summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/scsi/ufs/ufs.h11
-rw-r--r--drivers/scsi/ufs/ufshcd.c91
-rw-r--r--include/uapi/scsi/ufs/ufs.h3
3 files changed, 104 insertions, 1 deletions
diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index e4b2c95350ef..7fe25d526dbe 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -156,6 +156,7 @@ enum ufs_desc_max_size {
QUERY_DESC_STRING_MAX_SIZE = 0xFE,
QUERY_DESC_GEOMETRY_MAZ_SIZE = 0x44,
QUERY_DESC_POWER_MAX_SIZE = 0x62,
+ QUERY_DESC_HEALTH_MAX_SIZE = 0x25,
QUERY_DESC_RFU_MAX_SIZE = 0x00,
};
@@ -209,6 +210,16 @@ enum device_desc_param {
DEVICE_DESC_PARAM_RTT_CAP = 0x1C,
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 433e93f08956..94edba9de9fc 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -43,6 +43,7 @@
#include <linux/nls.h>
#include <linux/of.h>
#include <linux/blkdev.h>
+#include <asm/unaligned.h>
#include "ufshcd.h"
#include "ufshci.h"
@@ -249,6 +250,7 @@ static u32 ufs_query_desc_max_size[] = {
QUERY_DESC_RFU_MAX_SIZE,
QUERY_DESC_GEOMETRY_MAZ_SIZE,
QUERY_DESC_POWER_MAX_SIZE,
+ QUERY_DESC_HEALTH_MAX_SIZE,
QUERY_DESC_RFU_MAX_SIZE,
};
@@ -9234,10 +9236,99 @@ 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 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 void ufshcd_shutdown_clkscaling(struct ufs_hba *hba)
diff --git a/include/uapi/scsi/ufs/ufs.h b/include/uapi/scsi/ufs/ufs.h
index cd82b760bd92..9da634db9e9f 100644
--- a/include/uapi/scsi/ufs/ufs.h
+++ b/include/uapi/scsi/ufs/ufs.h
@@ -51,7 +51,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,
};