From da1dcbff751eef490dd8ea5e7e42fcd25524f931 Mon Sep 17 00:00:00 2001 From: Stanislav Nijnikov Date: Thu, 15 Feb 2018 14:14:05 +0200 Subject: [PATCH] scsi: ufs: sysfs: health descriptor This patch introduces a sysfs group entry for the UFS health descriptor parameters. The group adds "health_descriptor" folder under the UFS driver sysfs entry (/sys/bus/platform/drivers/ufshcd/*). The parameters are shown as hexadecimal numbers. The full information about the parameters could be found at UFS specifications 2.1. Change-Id: I469c8cf579b2a660c6bfd2a1bd30d4a7f357c588 Signed-off-by: Stanislav Nijnikov Reviewed-by: Greg Kroah-Hartman Signed-off-by: Martin K. Petersen Git-commit: c648c2d27f168ae4faeb43f8c3074226aae3862c Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Anjana --- drivers/scsi/ufs/ufs.h | 12 +++- drivers/scsi/ufs/ufshcd.c | 106 ++++++++++++++++++++++++++++++++++++ drivers/scsi/ufs/ufshcd.h | 1 + include/uapi/scsi/ufs/ufs.h | 3 +- 4 files changed, 120 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 5ff6531c0b7d..988e20a583e6 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -151,8 +151,9 @@ enum ufs_desc_def_size { QUERY_DESC_CONFIGURATION_DEF_SIZE = 0x90, QUERY_DESC_UNIT_DEF_SIZE = 0x23, QUERY_DESC_INTERCONNECT_DEF_SIZE = 0x06, - QUERY_DESC_GEOMETRY_DEF_SIZE = 0x44, + QUERY_DESC_GEOMETRY_DEF_SIZE = 0x48, QUERY_DESC_POWER_DEF_SIZE = 0x62, + QUERY_DESC_HEALTH_DEF_SIZE = 0x25, }; /* Unit descriptor parameters offsets in bytes*/ @@ -206,6 +207,15 @@ enum device_desc_param { DEVICE_DESC_PARAM_FRQ_RTC = 0x1D, }; +/* Health descriptor parameters offsets in bytes*/ +enum health_desc_param { + HEALTH_DESC_PARAM_LEN = 0x0, + HEALTH_DESC_PARAM_TYPE = 0x1, + HEALTH_DESC_PARAM_EOL_INFO = 0x2, + HEALTH_DESC_PARAM_LIFE_TIME_EST_A = 0x3, + HEALTH_DESC_PARAM_LIFE_TIME_EST_B = 0x4, +}; + /* * Logical Unit Write Protect * 00h: LU not write protected diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index d51c17b11ae1..9544472b5b45 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -43,6 +43,8 @@ #include #include #include +#include + #include "ufshcd.h" #include "ufs_quirks.h" #include "unipro.h" @@ -4518,6 +4520,9 @@ int ufshcd_map_desc_id_to_length(struct ufs_hba *hba, case QUERY_DESC_IDN_STRING: *desc_len = QUERY_DESC_MAX_SIZE; break; + case QUERY_DESC_IDN_HEALTH: + *desc_len = hba->desc_size.hlth_desc; + break; case QUERY_DESC_IDN_RFU_0: case QUERY_DESC_IDN_RFU_1: *desc_len = 0; @@ -8538,6 +8543,11 @@ static void ufshcd_init_desc_sizes(struct ufs_hba *hba) &hba->desc_size.geom_desc); if (err) hba->desc_size.geom_desc = QUERY_DESC_GEOMETRY_DEF_SIZE; + + err = ufshcd_read_desc_length(hba, QUERY_DESC_IDN_HEALTH, 0, + &hba->desc_size.hlth_desc); + if (err) + hba->desc_size.hlth_desc = QUERY_DESC_HEALTH_DEF_SIZE; } static void ufshcd_def_desc_sizes(struct ufs_hba *hba) @@ -8548,6 +8558,7 @@ static void ufshcd_def_desc_sizes(struct ufs_hba *hba) hba->desc_size.conf_desc = QUERY_DESC_CONFIGURATION_DEF_SIZE; hba->desc_size.unit_desc = QUERY_DESC_UNIT_DEF_SIZE; hba->desc_size.geom_desc = QUERY_DESC_GEOMETRY_DEF_SIZE; + hba->desc_size.hlth_desc = QUERY_DESC_HEALTH_DEF_SIZE; } static void ufshcd_apply_pm_quirks(struct ufs_hba *hba) @@ -10656,16 +10667,111 @@ static void ufshcd_add_spm_lvl_sysfs_nodes(struct ufs_hba *hba) dev_err(hba->dev, "Failed to create sysfs for spm_lvl\n"); } +static ssize_t ufs_sysfs_read_desc_param(struct ufs_hba *hba, + enum desc_idn desc_id, + u8 desc_index, + u8 param_offset, + u8 *sysfs_buf, + u8 param_size) +{ + u8 desc_buf[8] = {0}; + int ret; + + if (param_size > 8) + return -EINVAL; + + pm_runtime_get_sync(hba->dev); + ret = ufshcd_read_desc_param(hba, desc_id, desc_index, + param_offset, desc_buf, param_size); + pm_runtime_put_sync(hba->dev); + + if (ret) + return -EINVAL; + switch (param_size) { + case 1: + ret = snprintf(sysfs_buf, PAGE_SIZE, "0x%02X\n", *desc_buf); + break; + case 2: + ret = snprintf(sysfs_buf, PAGE_SIZE, "0x%04X\n", + get_unaligned_be16(desc_buf)); + break; + case 4: + ret = snprintf(sysfs_buf, PAGE_SIZE, "0x%08X\n", + get_unaligned_be32(desc_buf)); + break; + case 8: + ret = snprintf(sysfs_buf, PAGE_SIZE, "0x%016llX\n", + get_unaligned_be64(desc_buf)); + break; + } + + return ret; +} + + +#define UFS_DESC_PARAM(_name, _puname, _duname, _size) \ + static ssize_t _name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct ufs_hba *hba = dev_get_drvdata(dev); \ + return ufs_sysfs_read_desc_param(hba, QUERY_DESC_IDN_##_duname, \ + 0, _duname##_DESC_PARAM##_puname, buf, _size); \ +} \ +static DEVICE_ATTR_RO(_name) + +#define UFS_HEALTH_DESC_PARAM(_name, _uname, _size) \ + UFS_DESC_PARAM(_name, _uname, HEALTH, _size) + +UFS_HEALTH_DESC_PARAM(eol_info, _EOL_INFO, 1); +UFS_HEALTH_DESC_PARAM(life_time_estimation_a, _LIFE_TIME_EST_A, 1); +UFS_HEALTH_DESC_PARAM(life_time_estimation_b, _LIFE_TIME_EST_B, 1); + +static struct attribute *ufs_sysfs_health_descriptor[] = { + &dev_attr_eol_info.attr, + &dev_attr_life_time_estimation_a.attr, + &dev_attr_life_time_estimation_b.attr, + NULL, +}; + +static const struct attribute_group ufs_sysfs_health_descriptor_group = { + .name = "health_descriptor", + .attrs = ufs_sysfs_health_descriptor, +}; + +static const struct attribute_group *ufs_sysfs_groups[] = { + &ufs_sysfs_health_descriptor_group, + NULL, +}; + + +static void ufshcd_add_desc_sysfs_nodes(struct device *dev) +{ + int ret; + + ret = sysfs_create_groups(&dev->kobj, ufs_sysfs_groups); + if (ret) + dev_err(dev, + "%s: sysfs groups creation failed (err = %d)\n", + __func__, ret); +} + +static void ufshcd_remove_desc_sysfs_nodes(struct device *dev) +{ + sysfs_remove_groups(&dev->kobj, ufs_sysfs_groups); +} + static inline void ufshcd_add_sysfs_nodes(struct ufs_hba *hba) { ufshcd_add_rpm_lvl_sysfs_nodes(hba); ufshcd_add_spm_lvl_sysfs_nodes(hba); + ufshcd_add_desc_sysfs_nodes(hba->dev); } static inline void ufshcd_remove_sysfs_nodes(struct ufs_hba *hba) { device_remove_file(hba->dev, &hba->rpm_lvl_attr); device_remove_file(hba->dev, &hba->spm_lvl_attr); + ufshcd_remove_desc_sysfs_nodes(hba->dev); } static void __ufshcd_shutdown_clkscaling(struct ufs_hba *hba) diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 76d5f89de303..b75240c70797 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -259,6 +259,7 @@ struct ufs_desc_size { int interc_desc; int unit_desc; int conf_desc; + int hlth_desc; }; /** diff --git a/include/uapi/scsi/ufs/ufs.h b/include/uapi/scsi/ufs/ufs.h index 246cd9abb339..38ab40c8685f 100644 --- a/include/uapi/scsi/ufs/ufs.h +++ b/include/uapi/scsi/ufs/ufs.h @@ -55,7 +55,8 @@ enum desc_idn { QUERY_DESC_IDN_RFU_1 = 0x6, QUERY_DESC_IDN_GEOMETRY = 0x7, QUERY_DESC_IDN_POWER = 0x8, - QUERY_DESC_IDN_RFU_2 = 0x9, + QUERY_DESC_IDN_HEALTH = 0x9, + QUERY_DESC_IDN_RFU_2 = 0x0A, QUERY_DESC_IDN_MAX, };