466 lines
16 KiB
C
466 lines
16 KiB
C
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
/*
|
||
|
* Samsung Specific feature : sysfs-nodes
|
||
|
*
|
||
|
* Copyright (C) 2021 Samsung Electronics Co., Ltd.
|
||
|
*
|
||
|
* Authors:
|
||
|
* Storage Driver <storage.sec@samsung.com>
|
||
|
*/
|
||
|
|
||
|
#include <linux/sysfs.h>
|
||
|
|
||
|
#include "ufs-sec-sysfs.h"
|
||
|
|
||
|
/* sec specific vendor sysfs nodes */
|
||
|
static struct device *sec_ufs_cmd_dev;
|
||
|
|
||
|
#ifdef CONFIG_BLK_TURBO_WRITE
|
||
|
#define SEC_UFS_TW_INFO_DIFF(t, n, o, member) ({ \
|
||
|
(t)->member = (n)->member - (o)->member; \
|
||
|
(o)->member = (n)->member; })
|
||
|
#endif
|
||
|
|
||
|
/* UFS info nodes : begin */
|
||
|
static ssize_t ufs_sec_unique_number_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
||
|
|
||
|
return snprintf(buf, PAGE_SIZE, "%s\n", hba->unique_number);
|
||
|
}
|
||
|
static DEVICE_ATTR(un, 0440, ufs_sec_unique_number_show, NULL);
|
||
|
|
||
|
static ssize_t ufs_sec_manufacturer_id_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
||
|
struct ufs_dev_info *dev_info = &hba->dev_info;
|
||
|
|
||
|
return snprintf(buf, PAGE_SIZE, "%04x\n", dev_info->wmanufacturerid);
|
||
|
}
|
||
|
static DEVICE_ATTR(man_id, 0444, ufs_sec_manufacturer_id_show, NULL);
|
||
|
|
||
|
static ssize_t ufs_sec_lt_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
||
|
u8 lt;
|
||
|
|
||
|
if (!hba) {
|
||
|
dev_info(dev, "skipping ufs lt read\n");
|
||
|
lt = 0;
|
||
|
} else if (hba->ufshcd_state == UFSHCD_STATE_OPERATIONAL) {
|
||
|
pm_runtime_get_sync(hba->dev);
|
||
|
ufs_sec_get_health_info(hba);
|
||
|
pm_runtime_put(hba->dev);
|
||
|
lt = hba->lifetime;
|
||
|
} else {
|
||
|
/* return previous LT value if not operational */
|
||
|
dev_info(hba->dev, "ufshcd_state : %d, old LT: %01x\n",
|
||
|
hba->ufshcd_state, hba->lifetime);
|
||
|
lt = hba->lifetime;
|
||
|
}
|
||
|
|
||
|
return snprintf(buf, PAGE_SIZE, "%01x\n", lt);
|
||
|
}
|
||
|
static DEVICE_ATTR(lt, 0444, ufs_sec_lt_show, NULL);
|
||
|
|
||
|
static ssize_t ufs_sec_lc_show(struct device *dev,
|
||
|
struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
||
|
|
||
|
return sprintf(buf, "%u\n", hba->lc_info);
|
||
|
}
|
||
|
|
||
|
static ssize_t ufs_sec_lc_store(struct device *dev,
|
||
|
struct device_attribute *attr, const char *buf, size_t count)
|
||
|
{
|
||
|
struct ufs_hba *hba = dev_get_drvdata(dev);
|
||
|
unsigned int value;
|
||
|
|
||
|
if (kstrtou32(buf, 0, &value))
|
||
|
return -EINVAL;
|
||
|
|
||
|
hba->lc_info = value;
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
static DEVICE_ATTR(lc, 0664, ufs_sec_lc_show, ufs_sec_lc_store);
|
||
|
|
||
|
static struct attribute *sec_ufs_info_attributes[] = {
|
||
|
&dev_attr_un.attr,
|
||
|
&dev_attr_man_id.attr,
|
||
|
&dev_attr_lt.attr,
|
||
|
&dev_attr_lc.attr,
|
||
|
NULL
|
||
|
};
|
||
|
|
||
|
static struct attribute_group sec_ufs_info_attribute_group = {
|
||
|
.attrs = sec_ufs_info_attributes,
|
||
|
};
|
||
|
|
||
|
void ufs_sec_create_sysfs(struct ufs_hba *hba)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
/* sec specific vendor sysfs nodes */
|
||
|
if (!sec_ufs_cmd_dev)
|
||
|
sec_ufs_cmd_dev = sec_device_create(hba, "ufs");
|
||
|
|
||
|
if (IS_ERR(sec_ufs_cmd_dev))
|
||
|
pr_err("Fail to create sysfs dev\n");
|
||
|
else {
|
||
|
ret = sysfs_create_group(&sec_ufs_cmd_dev->kobj,
|
||
|
&sec_ufs_info_attribute_group);
|
||
|
if (ret)
|
||
|
dev_err(hba->dev, "%s: Failed to create sec_ufs_info sysfs group, %d\n",
|
||
|
__func__, ret);
|
||
|
}
|
||
|
}
|
||
|
/* UFS info nodes : end */
|
||
|
|
||
|
#ifdef CONFIG_BLK_TURBO_WRITE
|
||
|
static void SEC_UFS_TW_info_get_diff(struct SEC_UFS_TW_info *target,
|
||
|
struct SEC_UFS_TW_info *new, struct SEC_UFS_TW_info *old)
|
||
|
{
|
||
|
SEC_UFS_TW_INFO_DIFF(target, new, old, tw_enable_count);
|
||
|
SEC_UFS_TW_INFO_DIFF(target, new, old, tw_disable_count);
|
||
|
SEC_UFS_TW_INFO_DIFF(target, new, old, tw_amount_W_kb);
|
||
|
SEC_UFS_TW_INFO_DIFF(target, new, old, hibern8_enter_count);
|
||
|
SEC_UFS_TW_INFO_DIFF(target, new, old, hibern8_enter_count_100ms);
|
||
|
SEC_UFS_TW_INFO_DIFF(target, new, old, hibern8_amount_ms);
|
||
|
SEC_UFS_TW_INFO_DIFF(target, new, old, hibern8_amount_ms_100ms);
|
||
|
|
||
|
target->hibern8_max_ms = new->hibern8_max_ms;
|
||
|
|
||
|
// initialize min, max h8 time values.
|
||
|
new->hibern8_max_ms = 0;
|
||
|
get_monotonic_boottime(&(old->timestamp)); /* update timestamp */
|
||
|
}
|
||
|
|
||
|
static ssize_t SEC_UFS_TW_info_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||
|
{
|
||
|
struct Scsi_Host *Shost = container_of(dev, struct Scsi_Host, shost_dev);
|
||
|
struct ufs_hba *hba = shost_priv(Shost);
|
||
|
struct SEC_UFS_TW_info tw_info;
|
||
|
struct SEC_UFS_TW_info *tw_info_old = &(hba->SEC_tw_info_old);
|
||
|
struct SEC_UFS_TW_info *tw_info_new = &(hba->SEC_tw_info);
|
||
|
long hours = 0;
|
||
|
|
||
|
get_monotonic_boottime(&(tw_info_new->timestamp));
|
||
|
hours = (tw_info_new->timestamp.tv_sec - tw_info_old->timestamp.tv_sec) / 60; /* min */
|
||
|
hours = (hours + 30) / 60; /* round up to hours */
|
||
|
|
||
|
SEC_UFS_TW_info_get_diff(&tw_info, tw_info_new, tw_info_old);
|
||
|
|
||
|
return sprintf(buf, "\"TWCTRLCNT\":\"%llu\","
|
||
|
"\"TWCTRLERRCNT\":\"%llu\","
|
||
|
"\"TWDAILYMB\":\"%llu\","
|
||
|
"\"TWTOTALMB\":\"%llu\","
|
||
|
"\"H8CNT\":\"%llu\",\"H8MS\":\"%llu\","
|
||
|
"\"H8CNT100MS\":\"%llu\",\"H8MS100MS\":\"%llu\","
|
||
|
"\"H8MAXMS\":\"%llu\","
|
||
|
"\"TWhours\":\"%ld\"\n",
|
||
|
(tw_info.tw_enable_count + tw_info.tw_disable_count),
|
||
|
tw_info_new->tw_setflag_error_count, /* total error count */
|
||
|
(tw_info.tw_amount_W_kb >> 10), /* TW write daily : MB */
|
||
|
(tw_info_new->tw_amount_W_kb >> 10), /* TW write total : MB */
|
||
|
tw_info.hibern8_enter_count, tw_info.hibern8_amount_ms,
|
||
|
tw_info.hibern8_enter_count_100ms,
|
||
|
tw_info.hibern8_amount_ms_100ms,
|
||
|
tw_info.hibern8_max_ms,
|
||
|
hours);
|
||
|
}
|
||
|
static DEVICE_ATTR(SEC_UFS_TW_info, 0444, SEC_UFS_TW_info_show, NULL);
|
||
|
#endif
|
||
|
|
||
|
/* UFS error info : begin */
|
||
|
static ssize_t SEC_UFS_op_cnt_store(struct device *dev, struct device_attribute *attr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
if ((buf[0] != 'C' && buf[0] != 'c') || (count != 1))
|
||
|
return -EINVAL;
|
||
|
|
||
|
SEC_UFS_ERR_INFO_BACKUP(op_count, HW_RESET_count);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(op_count, link_startup_count);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(op_count, Hibern8_enter_count);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(op_count, Hibern8_exit_count);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t SEC_UFS_uic_cmd_cnt_store(struct device *dev, struct device_attribute *attr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
if ((buf[0] != 'C' && buf[0] != 'c') || (count != 1))
|
||
|
return -EINVAL;
|
||
|
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UIC_cmd_count, DME_TEST_MODE_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UIC_cmd_count, DME_GET_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UIC_cmd_count, DME_SET_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UIC_cmd_count, DME_PEER_GET_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UIC_cmd_count, DME_PEER_SET_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UIC_cmd_count, DME_POWERON_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UIC_cmd_count, DME_POWEROFF_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UIC_cmd_count, DME_ENABLE_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UIC_cmd_count, DME_RESET_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UIC_cmd_count, DME_END_PT_RST_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UIC_cmd_count, DME_LINK_STARTUP_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UIC_cmd_count, DME_HIBER_ENTER_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UIC_cmd_count, DME_HIBER_EXIT_err);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t SEC_UFS_uic_err_cnt_store(struct device *dev, struct device_attribute *attr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
if ((buf[0] != 'C' && buf[0] != 'c') || (count != 1))
|
||
|
return -EINVAL;
|
||
|
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UIC_err_count, PA_ERR_cnt);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UIC_err_count, DL_PA_INIT_ERROR_cnt);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UIC_err_count, DL_NAC_RECEIVED_ERROR_cnt);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UIC_err_count, DL_TC_REPLAY_ERROR_cnt);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UIC_err_count, NL_ERROR_cnt);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UIC_err_count, TL_ERROR_cnt);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UIC_err_count, DME_ERROR_cnt);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t SEC_UFS_fatal_cnt_store(struct device *dev, struct device_attribute *attr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
if ((buf[0] != 'C' && buf[0] != 'c') || (count != 1))
|
||
|
return -EINVAL;
|
||
|
|
||
|
SEC_UFS_ERR_INFO_BACKUP(Fatal_err_count, DFE);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(Fatal_err_count, CFE);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(Fatal_err_count, SBFE);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(Fatal_err_count, CEFE);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(Fatal_err_count, LLE);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t SEC_UFS_utp_cnt_store(struct device *dev, struct device_attribute *attr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
if ((buf[0] != 'C' && buf[0] != 'c') || (count != 1))
|
||
|
return -EINVAL;
|
||
|
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UTP_count, UTMR_query_task_count);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UTP_count, UTMR_abort_task_count);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UTP_count, UTR_read_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UTP_count, UTR_write_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UTP_count, UTR_sync_cache_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UTP_count, UTR_unmap_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UTP_count, UTR_etc_err);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t SEC_UFS_query_cnt_store(struct device *dev, struct device_attribute *attr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
if ((buf[0] != 'C' && buf[0] != 'c') || (count != 1))
|
||
|
return -EINVAL;
|
||
|
|
||
|
SEC_UFS_ERR_INFO_BACKUP(query_count, NOP_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(query_count, R_Desc_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(query_count, W_Desc_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(query_count, R_Attr_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(query_count, W_Attr_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(query_count, R_Flag_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(query_count, Set_Flag_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(query_count, Clear_Flag_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(query_count, Toggle_Flag_err);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t SEC_UFS_err_sum_store(struct device *dev, struct device_attribute *attr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
if ((buf[0] != 'C' && buf[0] != 'c') || (count != 1))
|
||
|
return -EINVAL;
|
||
|
|
||
|
SEC_UFS_ERR_INFO_BACKUP(op_count, op_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UIC_cmd_count, UIC_cmd_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UIC_err_count, UIC_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(Fatal_err_count, Fatal_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(UTP_count, UTP_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(query_count, Query_err);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
static ssize_t sense_err_count_store(struct device *dev, struct device_attribute *attr,
|
||
|
const char *buf, size_t count)
|
||
|
{
|
||
|
if ((buf[0] != 'C' && buf[0] != 'c') || (count != 1))
|
||
|
return -EINVAL;
|
||
|
|
||
|
SEC_UFS_ERR_INFO_BACKUP(sense_count, scsi_medium_err);
|
||
|
SEC_UFS_ERR_INFO_BACKUP(sense_count, scsi_hw_err);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
SEC_UFS_DATA_ATTR_RW(SEC_UFS_op_cnt, "\"HWRESET\":\"%u\",\"LINKFAIL\":\"%u\",\"H8ENTERFAIL\":\"%u\""
|
||
|
",\"H8EXITFAIL\":\"%u\"\n",
|
||
|
ufs_err_info.op_count.HW_RESET_count,
|
||
|
ufs_err_info.op_count.link_startup_count,
|
||
|
ufs_err_info.op_count.Hibern8_enter_count,
|
||
|
ufs_err_info.op_count.Hibern8_exit_count);
|
||
|
|
||
|
SEC_UFS_DATA_ATTR_RW(SEC_UFS_uic_cmd_cnt, "\"TESTMODE\":\"%u\",\"DME_GET\":\"%u\",\"DME_SET\":\"%u\""
|
||
|
",\"DME_PGET\":\"%u\",\"DME_PSET\":\"%u\",\"PWRON\":\"%u\",\"PWROFF\":\"%u\""
|
||
|
",\"DME_EN\":\"%u\",\"DME_RST\":\"%u\",\"EPRST\":\"%u\",\"LINKSTARTUP\":\"%u\""
|
||
|
",\"H8ENTER\":\"%u\",\"H8EXIT\":\"%u\"\n",
|
||
|
ufs_err_info.UIC_cmd_count.DME_TEST_MODE_err,
|
||
|
ufs_err_info.UIC_cmd_count.DME_GET_err,
|
||
|
ufs_err_info.UIC_cmd_count.DME_SET_err,
|
||
|
ufs_err_info.UIC_cmd_count.DME_PEER_GET_err,
|
||
|
ufs_err_info.UIC_cmd_count.DME_PEER_SET_err,
|
||
|
ufs_err_info.UIC_cmd_count.DME_POWERON_err,
|
||
|
ufs_err_info.UIC_cmd_count.DME_POWEROFF_err,
|
||
|
ufs_err_info.UIC_cmd_count.DME_ENABLE_err,
|
||
|
ufs_err_info.UIC_cmd_count.DME_RESET_err,
|
||
|
ufs_err_info.UIC_cmd_count.DME_END_PT_RST_err,
|
||
|
ufs_err_info.UIC_cmd_count.DME_LINK_STARTUP_err,
|
||
|
ufs_err_info.UIC_cmd_count.DME_HIBER_ENTER_err,
|
||
|
ufs_err_info.UIC_cmd_count.DME_HIBER_EXIT_err);
|
||
|
|
||
|
SEC_UFS_DATA_ATTR_RW(SEC_UFS_uic_err_cnt, "\"PAERR\":\"%u\",\"DLPAINITERROR\":\"%u\",\"DLNAC\":\"%u\""
|
||
|
",\"DLTCREPLAY\":\"%u\",\"NLERR\":\"%u\",\"TLERR\":\"%u\",\"DMEERR\":\"%u\"\n",
|
||
|
ufs_err_info.UIC_err_count.PA_ERR_cnt,
|
||
|
ufs_err_info.UIC_err_count.DL_PA_INIT_ERROR_cnt,
|
||
|
ufs_err_info.UIC_err_count.DL_NAC_RECEIVED_ERROR_cnt,
|
||
|
ufs_err_info.UIC_err_count.DL_TC_REPLAY_ERROR_cnt,
|
||
|
ufs_err_info.UIC_err_count.NL_ERROR_cnt,
|
||
|
ufs_err_info.UIC_err_count.TL_ERROR_cnt,
|
||
|
ufs_err_info.UIC_err_count.DME_ERROR_cnt);
|
||
|
|
||
|
SEC_UFS_DATA_ATTR_RW(SEC_UFS_fatal_cnt, "\"DFE\":\"%u\",\"CFE\":\"%u\",\"SBFE\":\"%u\""
|
||
|
",\"CEFE\":\"%u\",\"LLE\":\"%u\"\n",
|
||
|
ufs_err_info.Fatal_err_count.DFE, // Device_Fatal
|
||
|
ufs_err_info.Fatal_err_count.CFE, // Controller_Fatal
|
||
|
ufs_err_info.Fatal_err_count.SBFE, // System_Bus_Fatal
|
||
|
ufs_err_info.Fatal_err_count.CEFE, // Crypto_Engine_Fatal
|
||
|
ufs_err_info.Fatal_err_count.LLE); // Link_Lost
|
||
|
|
||
|
SEC_UFS_DATA_ATTR_RW(SEC_UFS_utp_cnt, "\"UTMRQTASK\":\"%u\",\"UTMRATASK\":\"%u\",\"UTRR\":\"%u\""
|
||
|
",\"UTRW\":\"%u\",\"UTRSYNCCACHE\":\"%u\",\"UTRUNMAP\":\"%u\",\"UTRETC\":\"%u\"\n",
|
||
|
ufs_err_info.UTP_count.UTMR_query_task_count,
|
||
|
ufs_err_info.UTP_count.UTMR_abort_task_count,
|
||
|
ufs_err_info.UTP_count.UTR_read_err,
|
||
|
ufs_err_info.UTP_count.UTR_write_err,
|
||
|
ufs_err_info.UTP_count.UTR_sync_cache_err,
|
||
|
ufs_err_info.UTP_count.UTR_unmap_err,
|
||
|
ufs_err_info.UTP_count.UTR_etc_err);
|
||
|
|
||
|
SEC_UFS_DATA_ATTR_RW(SEC_UFS_query_cnt, "\"NOPERR\":\"%u\",\"R_DESC\":\"%u\",\"W_DESC\":\"%u\""
|
||
|
",\"R_ATTR\":\"%u\",\"W_ATTR\":\"%u\",\"R_FLAG\":\"%u\",\"S_FLAG\":\"%u\""
|
||
|
",\"C_FLAG\":\"%u\",\"T_FLAG\":\"%u\"\n",
|
||
|
ufs_err_info.query_count.NOP_err,
|
||
|
ufs_err_info.query_count.R_Desc_err,
|
||
|
ufs_err_info.query_count.W_Desc_err,
|
||
|
ufs_err_info.query_count.R_Attr_err,
|
||
|
ufs_err_info.query_count.W_Attr_err,
|
||
|
ufs_err_info.query_count.R_Flag_err,
|
||
|
ufs_err_info.query_count.Set_Flag_err,
|
||
|
ufs_err_info.query_count.Clear_Flag_err,
|
||
|
ufs_err_info.query_count.Toggle_Flag_err);
|
||
|
|
||
|
SEC_UFS_DATA_ATTR_RW(sense_err_count, "\"MEDIUM\":\"%u\",\"HWERR\":\"%u\"\n",
|
||
|
ufs_err_info.sense_count.scsi_medium_err,
|
||
|
ufs_err_info.sense_count.scsi_hw_err);
|
||
|
|
||
|
SEC_UFS_DATA_ATTR_RO(sense_err_logging, "\"LBA0\":\"%lx\",\"LBA1\":\"%lx\",\"LBA2\":\"%lx\""
|
||
|
",\"LBA3\":\"%lx\",\"LBA4\":\"%lx\",\"LBA5\":\"%lx\""
|
||
|
",\"LBA6\":\"%lx\",\"LBA7\":\"%lx\",\"LBA8\":\"%lx\",\"LBA9\":\"%lx\""
|
||
|
",\"REGIONMAP\":\"%016llx\"\n",
|
||
|
ufs_err_info.sense_err_log.issue_LBA_list[0],
|
||
|
ufs_err_info.sense_err_log.issue_LBA_list[1],
|
||
|
ufs_err_info.sense_err_log.issue_LBA_list[2],
|
||
|
ufs_err_info.sense_err_log.issue_LBA_list[3],
|
||
|
ufs_err_info.sense_err_log.issue_LBA_list[4],
|
||
|
ufs_err_info.sense_err_log.issue_LBA_list[5],
|
||
|
ufs_err_info.sense_err_log.issue_LBA_list[6],
|
||
|
ufs_err_info.sense_err_log.issue_LBA_list[7],
|
||
|
ufs_err_info.sense_err_log.issue_LBA_list[8],
|
||
|
ufs_err_info.sense_err_log.issue_LBA_list[9],
|
||
|
ufs_err_info.sense_err_log.issue_region_map);
|
||
|
|
||
|
/* daily err sum */
|
||
|
SEC_UFS_DATA_ATTR_RW(SEC_UFS_err_sum, "\"OPERR\":\"%u\",\"UICCMD\":\"%u\",\"UICERR\":\"%u\""
|
||
|
",\"FATALERR\":\"%u\",\"UTPERR\":\"%u\",\"QUERYERR\":\"%u\"\n",
|
||
|
ufs_err_info.op_count.op_err,
|
||
|
ufs_err_info.UIC_cmd_count.UIC_cmd_err,
|
||
|
ufs_err_info.UIC_err_count.UIC_err,
|
||
|
ufs_err_info.Fatal_err_count.Fatal_err,
|
||
|
ufs_err_info.UTP_count.UTP_err,
|
||
|
ufs_err_info.query_count.Query_err);
|
||
|
|
||
|
/* accumulated err sum */
|
||
|
SEC_UFS_DATA_ATTR_RO(SEC_UFS_err_summary,
|
||
|
"OPERR : %u, UICCMD : %u, UICERR : %u, FATALERR : %u, UTPERR : %u, QUERYERR : %u\n"
|
||
|
"MEDIUM : %u, HWERR : %u\n",
|
||
|
SEC_UFS_ERR_INFO_GET_VALUE(op_count, op_err),
|
||
|
SEC_UFS_ERR_INFO_GET_VALUE(UIC_cmd_count, UIC_cmd_err),
|
||
|
SEC_UFS_ERR_INFO_GET_VALUE(UIC_err_count, UIC_err),
|
||
|
SEC_UFS_ERR_INFO_GET_VALUE(Fatal_err_count, Fatal_err),
|
||
|
SEC_UFS_ERR_INFO_GET_VALUE(UTP_count, UTP_err),
|
||
|
SEC_UFS_ERR_INFO_GET_VALUE(query_count, Query_err),
|
||
|
SEC_UFS_ERR_INFO_GET_VALUE(sense_count, scsi_medium_err),
|
||
|
SEC_UFS_ERR_INFO_GET_VALUE(sense_count, scsi_hw_err));
|
||
|
/* UFS error info : end */
|
||
|
|
||
|
static struct attribute *ufs_attributes[] = {
|
||
|
&dev_attr_SEC_UFS_op_cnt.attr,
|
||
|
&dev_attr_SEC_UFS_uic_cmd_cnt.attr,
|
||
|
&dev_attr_SEC_UFS_uic_err_cnt.attr,
|
||
|
&dev_attr_SEC_UFS_fatal_cnt.attr,
|
||
|
&dev_attr_SEC_UFS_utp_cnt.attr,
|
||
|
&dev_attr_SEC_UFS_query_cnt.attr,
|
||
|
&dev_attr_SEC_UFS_err_sum.attr,
|
||
|
&dev_attr_sense_err_count.attr,
|
||
|
&dev_attr_sense_err_logging.attr,
|
||
|
&dev_attr_SEC_UFS_err_summary.attr,
|
||
|
#ifdef CONFIG_BLK_TURBO_WRITE
|
||
|
&dev_attr_SEC_UFS_TW_info.attr,
|
||
|
#endif
|
||
|
NULL
|
||
|
};
|
||
|
|
||
|
static struct attribute_group ufs_attribute_group = {
|
||
|
.attrs = ufs_attributes,
|
||
|
};
|
||
|
|
||
|
void ufs_sec_create_err_sysfs(struct ufs_hba *hba)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
struct device *dev = &(hba->host->shost_dev);
|
||
|
|
||
|
/* scsi_host sysfs nodes */
|
||
|
ret = sysfs_create_group(&dev->kobj, &ufs_attribute_group);
|
||
|
if (ret)
|
||
|
pr_err("cannot create sysfs group err: %d\n", ret);
|
||
|
}
|
||
|
|
||
|
void ufs_sec_add_sysfs_nodes(struct ufs_hba *hba)
|
||
|
{
|
||
|
/* sec specific vendor sysfs nodes */
|
||
|
ufs_sec_create_sysfs(hba);
|
||
|
ufs_sec_create_err_sysfs(hba);
|
||
|
}
|
||
|
|
||
|
void ufs_sec_remove_sysfs_nodes(struct ufs_hba *hba)
|
||
|
{
|
||
|
sysfs_remove_group(&sec_ufs_cmd_dev->kobj, &sec_ufs_info_attribute_group);
|
||
|
}
|