[PATCH v2] ufs: core: Add HID support
Huan Tang
tanghuan at vivo.com
Mon May 12 06:15:19 PDT 2025
Follow JESD220G, support HID(Host Initiated Defragmentation)
through sysfs, the relevant sysfs nodes are as follows:
1.hid_analysis_trigger
2.hid_defrag_trigger
3.hid_fragmented_size
4.hid_defrag_size
5.hid_progress_ratio
6.hid_state
The detailed definition of the six nodes can be found in the sysfs
documentation.
HID's execution policy is given to user-space.
Changelog
===
v1 - > v2:
1.Refactor the HID code according to Bart and Peter and
Arvi's suggestions
v1
https://lore.kernel.org/all/20250417125008.123-1-tanghuan@vivo.com/
Signed-off-by: Huan Tang <tanghuan at vivo.com>
Signed-off-by: Wenxing Cheng <wenxing.cheng at vivo.com>
---
Documentation/ABI/testing/sysfs-driver-ufs | 83 +++++++++
drivers/ufs/core/ufs-sysfs.c | 195 +++++++++++++++++++++
drivers/ufs/core/ufshcd.c | 4 +
include/ufs/ufs.h | 25 +++
4 files changed, 307 insertions(+)
diff --git a/Documentation/ABI/testing/sysfs-driver-ufs b/Documentation/ABI/testing/sysfs-driver-ufs
index d4140dc6c5ba..8c4678305f94 100644
--- a/Documentation/ABI/testing/sysfs-driver-ufs
+++ b/Documentation/ABI/testing/sysfs-driver-ufs
@@ -1685,3 +1685,86 @@ Description:
================ ========================================
The file is read only.
+
+What: /sys/bus/platform/drivers/ufshcd/*/ufs_hid/hid_analysis_trigger
+What: /sys/bus/platform/devices/*.ufs/ufs_hid/hid_analysis_trigger
+Date: April 2025
+Contact: Huan Tang <tanghuan at vivo.com>
+Description:
+ The host can enable or disable HID analysis operation.
+
+ ======= =========================================
+ disable disable HID analysis operation
+ enable enable HID analysis operation
+ ======= =========================================
+
+ The file is write only.
+
+What: /sys/bus/platform/drivers/ufshcd/*/ufs_hid/hid_defrag_trigger
+What: /sys/bus/platform/devices/*.ufs/ufs_hid/hid_defrag_trigger
+Date: April 2025
+Contact: Huan Tang <tanghuan at vivo.com>
+Description:
+ The host can enable or disable HID defragmentation operation.
+
+ ======= =========================================
+ disable disable HID defragmentation operation
+ enable enable HID defragmentation operation
+ ======= =========================================
+
+ The attribute is write only.
+
+What: /sys/bus/platform/drivers/ufshcd/*/ufs_hid/hid_fragmented_size
+What: /sys/bus/platform/devices/*.ufs/ufs_hid/hid_fragmented_size
+Date: April 2025
+Contact: Huan Tang <tanghuan at vivo.com>
+Description:
+ The total fragmented size in the device is reported through
+ this attribute.
+
+ The attribute is read only.
+
+What: /sys/bus/platform/drivers/ufshcd/*/ufs_hid/hid_defrag_size
+What: /sys/bus/platform/devices/*.ufs/ufs_hid/hid_defrag_size
+Date: April 2025
+Contact: Huan Tang <tanghuan at vivo.com>
+Description:
+ The host sets the size to be defragmented by an HID
+ defragmentation operation.
+
+ The attribute is read/write.
+
+What: /sys/bus/platform/drivers/ufshcd/*/ufs_hid/hid_progress_ratio
+What: /sys/bus/platform/devices/*.ufs/ufs_hid/hid_progress_ratio
+Date: April 2025
+Contact: Huan Tang <tanghuan at vivo.com>
+Description:
+ Defragmentation progress is reported by this attribute,
+ indicateds the ratio of the completed defragmentation size
+ over the requested defragmentation size.
+
+ ==== ============================================
+ 1 1%
+ ...
+ 100 100%
+ ==== ============================================
+
+ The attribute is read only.
+
+What: /sys/bus/platform/drivers/ufshcd/*/ufs_hid/hid_state
+What: /sys/bus/platform/devices/*.ufs/ufs_hid/hid_state
+Date: April 2025
+Contact: Huan Tang <tanghuan at vivo.com>
+Description:
+ The HID state is reported by this attribute.
+
+ ==================== ===========================
+ idle Idle(analysis required)
+ analysis_in_progress Analysis in progress
+ defrag_required Defrag required
+ defrag_in_progress Defrag in progress
+ defrag_completed Defrag completed
+ defrag_not_required Defrag is not required
+ ==================== ===========================
+
+ The attribute is read only.
diff --git a/drivers/ufs/core/ufs-sysfs.c b/drivers/ufs/core/ufs-sysfs.c
index de8b6acd4058..e993468b6a4e 100644
--- a/drivers/ufs/core/ufs-sysfs.c
+++ b/drivers/ufs/core/ufs-sysfs.c
@@ -87,6 +87,26 @@ static const char *ufs_wb_resize_status_to_string(enum wb_resize_status status)
}
}
+static const char *ufs_hid_state_to_string(enum ufs_hid_state state)
+{
+ switch (state) {
+ case HID_IDLE:
+ return "idle";
+ case ANALYSIS_IN_PROGRESS:
+ return "analysis_in_progress";
+ case DEFRAG_REQUIRED:
+ return "defrag_required";
+ case DEFRAG_IN_PROGRESS:
+ return "defrag_in_progress";
+ case DEFRAG_COMPLETED:
+ return "defrag_completed";
+ case DEFRAG_IS_NOT_REQUIRED:
+ return "defrag_not_required";
+ default:
+ return "unknown";
+ }
+}
+
static const char *ufshcd_uic_link_state_to_string(
enum uic_link_state state)
{
@@ -1780,6 +1800,167 @@ static const struct attribute_group *ufs_sysfs_groups[] = {
NULL,
};
+static int hid_query_attr(struct ufs_hba *hba, enum query_opcode opcode,
+ enum attr_idn idn, u32 *attr_val)
+{
+ int ret;
+
+ down(&hba->host_sem);
+ if (!ufshcd_is_user_access_allowed(hba)) {
+ up(&hba->host_sem);
+ return -EBUSY;
+ }
+
+ ufshcd_rpm_get_sync(hba);
+ ret = ufshcd_query_attr(hba, opcode, idn, 0, 0, attr_val);
+ ufshcd_rpm_put_sync(hba);
+
+ up(&hba->host_sem);
+ return ret;
+}
+
+static const char * const hid_trigger_mode[] = {"disable", "enable"};
+
+static ssize_t hid_analysis_trigger_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+ int mode;
+ int ret;
+
+ mode = sysfs_match_string(hid_trigger_mode, buf);
+ if (mode < 0)
+ return -EINVAL;
+
+ ret = hid_query_attr(hba, UPIU_QUERY_OPCODE_WRITE_ATTR,
+ QUERY_ATTR_IDN_HID_DEFRAG_OPERATION, &mode);
+
+ return ret < 0 ? ret : count;
+}
+
+static DEVICE_ATTR_WO(hid_analysis_trigger);
+
+static ssize_t hid_defrag_trigger_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+ int mode;
+ int ret;
+
+ mode = sysfs_match_string(hid_trigger_mode, buf);
+ if (mode < 0)
+ return -EINVAL;
+
+ if (mode)
+ mode = HID_ANALYSIS_AND_DEFRAG_ENABLE;
+
+ ret = hid_query_attr(hba, UPIU_QUERY_OPCODE_WRITE_ATTR,
+ QUERY_ATTR_IDN_HID_DEFRAG_OPERATION, &mode);
+
+ return ret < 0 ? ret : count;
+}
+
+static DEVICE_ATTR_WO(hid_defrag_trigger);
+
+static ssize_t hid_fragmented_size_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+ u32 value;
+ int ret;
+
+ ret = hid_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR,
+ QUERY_ATTR_IDN_HID_AVAILABLE_SIZE, &value);
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%u\n", value);
+}
+
+static DEVICE_ATTR_RO(hid_fragmented_size);
+
+static ssize_t hid_defrag_size_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+ u32 value;
+ int ret;
+
+ ret = hid_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR,
+ QUERY_ATTR_IDN_HID_SIZE, &value);
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%u\n", value);
+}
+
+static ssize_t hid_defrag_size_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+ u32 value;
+ int ret;
+
+ if (kstrtou32(buf, 0, &value))
+ return -EINVAL;
+
+ ret = hid_query_attr(hba, UPIU_QUERY_OPCODE_WRITE_ATTR,
+ QUERY_ATTR_IDN_HID_SIZE, &value);
+
+ return ret < 0 ? ret : count;
+}
+
+static DEVICE_ATTR_RW(hid_defrag_size);
+
+static ssize_t hid_progress_ratio_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+ u32 value;
+ int ret;
+
+ ret = hid_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR,
+ QUERY_ATTR_IDN_HID_PROGRESS_RATIO, &value);
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%u\n", value);
+}
+
+static DEVICE_ATTR_RO(hid_progress_ratio);
+
+static ssize_t hid_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+ u32 value;
+ int ret;
+
+ ret = hid_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR,
+ QUERY_ATTR_IDN_HID_STATE, &value);
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%s\n", ufs_hid_state_to_string(value));
+}
+
+static DEVICE_ATTR_RO(hid_state);
+
+static struct attribute *ufs_sysfs_ufs_hid[] = {
+ &dev_attr_hid_analysis_trigger.attr,
+ &dev_attr_hid_defrag_trigger.attr,
+ &dev_attr_hid_fragmented_size.attr,
+ &dev_attr_hid_defrag_size.attr,
+ &dev_attr_hid_progress_ratio.attr,
+ &dev_attr_hid_state.attr,
+ NULL,
+};
+
+static const struct attribute_group ufs_sysfs_ufs_hid_group = {
+ .name = "ufs_hid",
+ .attrs = ufs_sysfs_ufs_hid,
+};
+
#define UFS_LUN_DESC_PARAM(_pname, _puname, _duname, _size) \
static ssize_t _pname##_show(struct device *dev, \
struct device_attribute *attr, char *buf) \
@@ -1898,6 +2079,7 @@ const struct attribute_group ufs_sysfs_lun_attributes_group = {
void ufs_sysfs_add_nodes(struct device *dev)
{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
int ret;
ret = sysfs_create_groups(&dev->kobj, ufs_sysfs_groups);
@@ -1905,9 +2087,22 @@ void ufs_sysfs_add_nodes(struct device *dev)
dev_err(dev,
"%s: sysfs groups creation failed (err = %d)\n",
__func__, ret);
+
+ if (hba->dev_info.hid_sup) {
+ ret = sysfs_create_group(&dev->kobj, &ufs_sysfs_ufs_hid_group);
+ if (ret)
+ dev_err(dev,
+ "%s: sysfs ufs_hid group creation failed (err = %d)\n",
+ __func__, ret);
+ }
}
void ufs_sysfs_remove_nodes(struct device *dev)
{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+
sysfs_remove_groups(&dev->kobj, ufs_sysfs_groups);
+
+ if (hba->dev_info.hid_sup)
+ sysfs_remove_group(&dev->kobj, &ufs_sysfs_ufs_hid_group);
}
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index dc55c94fa45e..fb67eb582826 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -8392,6 +8392,10 @@ static int ufs_get_device_desc(struct ufs_hba *hba)
dev_info->rtt_cap = desc_buf[DEVICE_DESC_PARAM_RTT_CAP];
+ dev_info->hid_sup = get_unaligned_be32(desc_buf +
+ DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP) &
+ UFS_DEV_HID_SUPPORT;
+
model_index = desc_buf[DEVICE_DESC_PARAM_PRDCT_NAME];
err = ufshcd_read_string_desc(hba, model_index,
diff --git a/include/ufs/ufs.h b/include/ufs/ufs.h
index c0c59a8f7256..e61caa40f7cd 100644
--- a/include/ufs/ufs.h
+++ b/include/ufs/ufs.h
@@ -182,6 +182,11 @@ enum attr_idn {
QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE = 0x1F,
QUERY_ATTR_IDN_TIMESTAMP = 0x30,
QUERY_ATTR_IDN_DEV_LVL_EXCEPTION_ID = 0x34,
+ QUERY_ATTR_IDN_HID_DEFRAG_OPERATION = 0x35,
+ QUERY_ATTR_IDN_HID_AVAILABLE_SIZE = 0x36,
+ QUERY_ATTR_IDN_HID_SIZE = 0x37,
+ QUERY_ATTR_IDN_HID_PROGRESS_RATIO = 0x38,
+ QUERY_ATTR_IDN_HID_STATE = 0x39,
QUERY_ATTR_IDN_WB_BUF_RESIZE_HINT = 0x3C,
QUERY_ATTR_IDN_WB_BUF_RESIZE_EN = 0x3D,
QUERY_ATTR_IDN_WB_BUF_RESIZE_STATUS = 0x3E,
@@ -401,6 +406,7 @@ enum {
UFS_DEV_HPB_SUPPORT = BIT(7),
UFS_DEV_WRITE_BOOSTER_SUP = BIT(8),
UFS_DEV_LVL_EXCEPTION_SUP = BIT(12),
+ UFS_DEV_HID_SUPPORT = BIT(13),
};
#define UFS_DEV_HPB_SUPPORT_VERSION 0x310
@@ -466,6 +472,23 @@ enum ufs_ref_clk_freq {
REF_CLK_FREQ_INVAL = -1,
};
+/* bDefragOperation attribute values */
+enum ufs_hid_defrag_operation {
+ HID_ANALYSIS_AND_DEFRAG_DISABLE = 0,
+ HID_ANALYSIS_ENABLE = 1,
+ HID_ANALYSIS_AND_DEFRAG_ENABLE = 2,
+};
+
+/* bHIDState attribute values */
+enum ufs_hid_state {
+ HID_IDLE = 0,
+ ANALYSIS_IN_PROGRESS = 1,
+ DEFRAG_REQUIRED = 2,
+ DEFRAG_IN_PROGRESS = 3,
+ DEFRAG_COMPLETED = 4,
+ DEFRAG_IS_NOT_REQUIRED = 5,
+};
+
/* bWriteBoosterBufferResizeEn attribute */
enum wb_resize_en {
WB_RESIZE_EN_IDLE = 0,
@@ -625,6 +648,8 @@ struct ufs_dev_info {
u32 rtc_update_period;
u8 rtt_cap; /* bDeviceRTTCap */
+
+ bool hid_sup;
};
/*
--
2.39.0
More information about the Linux-mediatek
mailing list