[PATCH] ufs: core: Add HID support

Huan Tang posted 1 patch 8 months ago
There is a newer version of this series
Documentation/ABI/testing/sysfs-driver-ufs |  82 ++++++++++++
drivers/ufs/core/Kconfig                   |   9 ++
drivers/ufs/core/Makefile                  |   1 +
drivers/ufs/core/ufs-hid.c                 | 130 ++++++++++++++++++
drivers/ufs/core/ufs-sysfs.c               | 149 ++++++++++++++++++++-
drivers/ufs/core/ufshcd-priv.h             |   8 ++
drivers/ufs/core/ufshcd.c                  |   7 +
include/ufs/ufs.h                          |  28 +++-
include/ufs/ufshcd.h                       |   2 +
9 files changed, 414 insertions(+), 2 deletions(-)
create mode 100644 drivers/ufs/core/ufs-hid.c
[PATCH] ufs: core: Add HID support
Posted by Huan Tang 8 months ago
Follow JESD220G, support HID(Host Initiated Defragmentation)
through sysfs, the host can perform HID related operations
through these nodes:1.It can use hid_trigger to enable kworkers
to automatically perform complete HID operations;2.manual HID
operation can also be achieved through other nodes.
The relevant sysfs nodes are as follows:
	1.hid_trigger
	2.defrag_operation
	3.hid_available_size
	4.hid_size
	5.hid_progress_ratio
	6.hid_state
The detailed definition of the six nodes can be	found in the sysfs
documentation.

Signed-off-by: Huan Tang <tanghuan@vivo.com>
Signed-off-by: Wenxing Cheng <wenxing.cheng@vivo.com>
---
 Documentation/ABI/testing/sysfs-driver-ufs |  82 ++++++++++++
 drivers/ufs/core/Kconfig                   |   9 ++
 drivers/ufs/core/Makefile                  |   1 +
 drivers/ufs/core/ufs-hid.c                 | 130 ++++++++++++++++++
 drivers/ufs/core/ufs-sysfs.c               | 149 ++++++++++++++++++++-
 drivers/ufs/core/ufshcd-priv.h             |   8 ++
 drivers/ufs/core/ufshcd.c                  |   7 +
 include/ufs/ufs.h                          |  28 +++-
 include/ufs/ufshcd.h                       |   2 +
 9 files changed, 414 insertions(+), 2 deletions(-)
 create mode 100644 drivers/ufs/core/ufs-hid.c

diff --git a/Documentation/ABI/testing/sysfs-driver-ufs b/Documentation/ABI/testing/sysfs-driver-ufs
index ae0191295d29..80350d1f8662 100644
--- a/Documentation/ABI/testing/sysfs-driver-ufs
+++ b/Documentation/ABI/testing/sysfs-driver-ufs
@@ -1604,3 +1604,85 @@ Description:
 		prevent the UFS from frequently performing clock gating/ungating.
 
 		The attribute is read/write.
+
+What:		/sys/bus/platform/drivers/ufshcd/*/hid_trigger
+What:		/sys/bus/platform/devices/*.ufs/hid_trigger
+Date:		April 2025
+Contact:	Huan Tang <tanghuan@vivo.com>
+Description:
+		The host can enable or disable complete HID.
+
+		========  ============
+		enable    Let kworker(ufs_hid_enable_work) execute the complete HID
+		disable   Cancel kworker(ufs_hid_enable_work) and disable HID
+		========  ============
+
+		The file is write only.
+
+What:		/sys/bus/platform/drivers/ufshcd/*/attributes/defrag_operation
+What:		/sys/bus/platform/devices/*.ufs/attributes/defrag_operation
+Date:		April 2025
+Contact:	Huan Tang <tanghuan@vivo.com>
+Description:
+		The host can enable or disable HID analysis and HID defrag operations.
+
+		===============  ==================================================
+		all_disable      HID analysis and HID defrag operation are disabled
+		analysis_enable  HID analysis is enabled
+		all_enable       HID analysis and HID defrag operation are enabled
+		===============  ==================================================
+
+		The attribute is read/write.
+
+What:		/sys/bus/platform/drivers/ufshcd/*/attributes/hid_available_size
+What:		/sys/bus/platform/devices/*.ufs/attributes/hid_available_size
+Date:		April 2025
+Contact:	Huan Tang <tanghuan@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/*/attributes/hid_size
+What:		/sys/bus/platform/devices/*.ufs/attributes/hid_size
+Date:		April 2025
+Contact:	Huan Tang <tanghuan@vivo.com>
+Description:
+		The host sets the size to be defragmented by an HID defrag operation.
+
+		The attribute is read/write.
+
+What:		/sys/bus/platform/drivers/ufshcd/*/attributes/hid_progress_ratio
+What:		/sys/bus/platform/devices/*.ufs/attributes/hid_progress_ratio
+Date:		April 2025
+Contact:	Huan Tang <tanghuan@vivo.com>
+Description:
+		Defrag progress is reported by this attribute,indicates the ratio of
+		the completed defrag size over the requested defrag size.
+
+		====  ======================================
+		01h   1%
+		...
+		64h   100%
+		====  ======================================
+
+		The attribute is read only.
+
+What:		/sys/bus/platform/drivers/ufshcd/*/attributes/hid_state
+What:		/sys/bus/platform/devices/*.ufs/attributes/hid_state
+Date:		April 2025
+Contact:	Huan Tang <tanghuan@vivo.com>
+Description:
+		The HID state is reported by this attribute.
+
+		====   ======================================
+		00h    Idle(analysis required)
+		01h    Analysis in progress
+		02h    Defrag required
+		03h    Defrag in progress
+		04h    Defrag completed
+		05h    Defrag is not required
+		====  ======================================
+
+		The attribute is read only.
\ No newline at end of file
diff --git a/drivers/ufs/core/Kconfig b/drivers/ufs/core/Kconfig
index 817208ee64ec..9ac04ba18061 100644
--- a/drivers/ufs/core/Kconfig
+++ b/drivers/ufs/core/Kconfig
@@ -50,3 +50,12 @@ config SCSI_UFS_HWMON
 	  a hardware monitoring device will be created for the UFS device.
 
 	  If unsure, say N.
+
+config SCSI_UFS_HID
+	bool "Support UFS Host Initiated Defrag"
+	help
+	  The UFS HID feature allows the host to check the status of whether
+	  defragmentation is needed or not and enable the device's internal
+	  defrag operation explicitly.
+
+	  If unsure, say N.
diff --git a/drivers/ufs/core/Makefile b/drivers/ufs/core/Makefile
index cf820fa09a04..2e11f4bdb80d 100644
--- a/drivers/ufs/core/Makefile
+++ b/drivers/ufs/core/Makefile
@@ -7,3 +7,4 @@ ufshcd-core-$(CONFIG_SCSI_UFS_BSG)	+= ufs_bsg.o
 ufshcd-core-$(CONFIG_SCSI_UFS_CRYPTO)	+= ufshcd-crypto.o
 ufshcd-core-$(CONFIG_SCSI_UFS_FAULT_INJECTION) += ufs-fault-injection.o
 ufshcd-core-$(CONFIG_SCSI_UFS_HWMON)	+= ufs-hwmon.o
+ufshcd-core-$(CONFIG_SCSI_UFS_HID)	+= ufs-hid.o
diff --git a/drivers/ufs/core/ufs-hid.c b/drivers/ufs/core/ufs-hid.c
new file mode 100644
index 000000000000..98c117d21622
--- /dev/null
+++ b/drivers/ufs/core/ufs-hid.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0
+ /*
+  * Universal Flash Storage Host Initiated Defragmentation
+  * Authors:
+  *	Huan Tang <tanghuan@vivo.com>
+  *	Wenxing Cheng <wenxing.cheng@vivo.com>
+  */
+
+#include <linux/unaligned.h>
+#include <linux/device.h>
+#include <linux/stddef.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/container_of.h>
+#include "ufshcd-priv.h"
+
+#define HID_SCHED_COUNT_LIMIT	300
+static int hid_sched_cnt;
+static void ufs_hid_enable_work_fn(struct work_struct *work)
+{
+	struct ufs_hba *hba;
+	int ret = 0;
+	enum ufs_hid_defrag_operation defrag_op;
+	u32 hid_ahit = 0;
+	bool hid_flag = false;
+
+	hba = container_of(work, struct ufs_hba, ufs_hid_enable_work.work);
+
+	if (!hba->dev_info.hid_sup)
+		return;
+
+	down(&hba->host_sem);
+
+	if (!ufshcd_is_user_access_allowed(hba)) {
+		up(&hba->host_sem);
+		return;
+	}
+
+	ufshcd_rpm_get_sync(hba);
+	hid_ahit = hba->ahit;
+	ufshcd_auto_hibern8_update(hba, 0);
+
+	ret = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR,
+			QUERY_ATTR_IDN_HID_STATE, 0, 0, &hba->dev_info.hid_state);
+	if (ret)
+		hba->dev_info.hid_state = HID_IDLE;
+
+	switch (hba->dev_info.hid_state) {
+	case HID_IDLE:
+		defrag_op = HID_ANALYSIS_ENABLE;
+		hid_flag = true;
+		break;
+	case DEFRAG_REQUIRED:
+		defrag_op = HID_ANALYSIS_AND_DEFRAG_ENABLE;
+		hid_flag = true;
+		break;
+	case DEFRAG_COMPLETED:
+	case DEFRAG_IS_NOT_REQUIRED:
+		defrag_op = HID_ANALYSIS_AND_DEFRAG_DISABLE;
+		hid_flag = true;
+		break;
+	case ANALYSIS_IN_PROGRESS:
+	case DEFRAG_IN_PROGRESS:
+	default:
+		break;
+	}
+
+	if (hid_flag) {
+		ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_WRITE_ATTR,
+				QUERY_ATTR_IDN_HID_DEFRAG_OPERATION, 0, 0, &defrag_op);
+		hid_flag = false;
+	}
+	ret = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR,
+				QUERY_ATTR_IDN_HID_STATE, 0, 0, &hba->dev_info.hid_state);
+	if (ret)
+		hba->dev_info.hid_state = HID_IDLE;
+
+	ufshcd_auto_hibern8_update(hba, hid_ahit);
+	ufshcd_rpm_put_sync(hba);
+	up(&hba->host_sem);
+
+	if (hba->dev_info.hid_state != HID_IDLE &&
+			hid_sched_cnt++ < HID_SCHED_COUNT_LIMIT)
+		schedule_delayed_work(&hba->ufs_hid_enable_work,
+					msecs_to_jiffies(1000));
+	else
+		hid_sched_cnt = 0;
+}
+
+int ufs_hid_disable(struct ufs_hba *hba)
+{
+	enum ufs_hid_defrag_operation defrag_op = HID_ANALYSIS_AND_DEFRAG_DISABLE;
+	u32 hid_ahit;
+	int ret;
+
+	down(&hba->host_sem);
+
+	if (!ufshcd_is_user_access_allowed(hba)) {
+		up(&hba->host_sem);
+		return -EBUSY;
+	}
+
+	ufshcd_rpm_get_sync(hba);
+	hid_ahit = hba->ahit;
+	ufshcd_auto_hibern8_update(hba, 0);
+
+	ret = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_WRITE_ATTR,
+				QUERY_ATTR_IDN_HID_DEFRAG_OPERATION, 0, 0, &defrag_op);
+
+	ufshcd_auto_hibern8_update(hba, hid_ahit);
+	ufshcd_rpm_put_sync(hba);
+	up(&hba->host_sem);
+
+	return ret;
+}
+
+void ufs_hid_init(struct ufs_hba *hba, const u8 *desc_buf)
+{
+	u32 ext_ufs_feature;
+	struct ufs_dev_info *dev_info = &hba->dev_info;
+
+	ext_ufs_feature = get_unaligned_be32(desc_buf +
+				DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP);
+
+	if (ext_ufs_feature & UFS_DEV_HID_SUPPORT) {
+		dev_info->hid_sup = true;
+		INIT_DELAYED_WORK(&hba->ufs_hid_enable_work,
+				ufs_hid_enable_work_fn);
+	}
+}
diff --git a/drivers/ufs/core/ufs-sysfs.c b/drivers/ufs/core/ufs-sysfs.c
index 90b5ab60f5ae..0a56751ab7dc 100644
--- a/drivers/ufs/core/ufs-sysfs.c
+++ b/drivers/ufs/core/ufs-sysfs.c
@@ -81,6 +81,21 @@ static const char *ufshcd_ufs_dev_pwr_mode_to_string(
 	}
 }
 
+static const char *ufs_hid_defrag_operation_to_string(
+			enum ufs_hid_defrag_operation op)
+{
+	switch (op) {
+	case HID_ANALYSIS_AND_DEFRAG_DISABLE:
+		return "all_disable";
+	case HID_ANALYSIS_ENABLE:
+		return "analysis_enable";
+	case HID_ANALYSIS_AND_DEFRAG_ENABLE:
+		return "all_enable";
+	default:
+		return "unknown";
+	}
+}
+
 static inline ssize_t ufs_sysfs_pm_lvl_store(struct device *dev,
 					     struct device_attribute *attr,
 					     const char *buf, size_t count,
@@ -466,6 +481,34 @@ static ssize_t critical_health_show(struct device *dev,
 	return sysfs_emit(buf, "%d\n", hba->critical_health_count);
 }
 
+static const char * const hid_trigger_mode[] = {
+	"disable", "enable"
+};
+
+static ssize_t hid_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 = 0;
+
+	if (!hba->dev_info.hid_sup)
+		return -EOPNOTSUPP;
+
+	mode = sysfs_match_string(hid_trigger_mode, buf);
+	if (mode < 0)
+		return -EINVAL;
+
+	if (mode)
+		schedule_delayed_work(&hba->ufs_hid_enable_work, 0);
+	else {
+		cancel_delayed_work_sync(&hba->ufs_hid_enable_work);
+		ret = ufs_hid_disable(hba);
+	}
+
+	return ret < 0 ? ret : count;
+}
+
 static DEVICE_ATTR_RW(rpm_lvl);
 static DEVICE_ATTR_RO(rpm_target_dev_state);
 static DEVICE_ATTR_RO(rpm_target_link_state);
@@ -479,6 +522,7 @@ static DEVICE_ATTR_RW(wb_flush_threshold);
 static DEVICE_ATTR_RW(rtc_update_ms);
 static DEVICE_ATTR_RW(pm_qos_enable);
 static DEVICE_ATTR_RO(critical_health);
+static DEVICE_ATTR_WO(hid_trigger);
 
 static struct attribute *ufs_sysfs_ufshcd_attrs[] = {
 	&dev_attr_rpm_lvl.attr,
@@ -494,6 +538,7 @@ static struct attribute *ufs_sysfs_ufshcd_attrs[] = {
 	&dev_attr_rtc_update_ms.attr,
 	&dev_attr_pm_qos_enable.attr,
 	&dev_attr_critical_health.attr,
+	&dev_attr_hid_trigger.attr,
 	NULL
 };
 
@@ -1495,6 +1540,101 @@ static inline bool ufshcd_is_wb_attrs(enum attr_idn idn)
 		idn <= QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE;
 }
 
+static int hid_query_attr(struct ufs_hba *hba, enum query_opcode opcode,
+				enum attr_idn idn, u32 *attr_val)
+{
+	int ret;
+
+	if (!hba->dev_info.hid_sup)
+		return -EOPNOTSUPP;
+
+	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 ssize_t defrag_operation_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_DEFRAG_OPERATION, &value);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%s\n", ufs_hid_defrag_operation_to_string(value));
+}
+
+static const char * const hid_defrag_operation[] = {
+	[HID_ANALYSIS_AND_DEFRAG_DISABLE]	= "all_disable",
+	[HID_ANALYSIS_ENABLE]	= "analysis_enable",
+	[HID_ANALYSIS_AND_DEFRAG_ENABLE]	= "all_enable",
+};
+
+static ssize_t defrag_operation_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct ufs_hba *hba = dev_get_drvdata(dev);
+	int defrag_op;
+	int ret;
+
+	defrag_op = sysfs_match_string(hid_defrag_operation, buf);
+	if (defrag_op < 0)
+		return -EINVAL;
+
+	ret = hid_query_attr(hba, UPIU_QUERY_OPCODE_WRITE_ATTR,
+				QUERY_ATTR_IDN_HID_DEFRAG_OPERATION, &defrag_op);
+
+	return ret < 0 ? ret : count;
+}
+
+static DEVICE_ATTR_RW(defrag_operation);
+
+static ssize_t hid_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, "0x%08X\n", value);
+}
+
+static ssize_t hid_size_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct ufs_hba *hba = dev_get_drvdata(dev);
+	int 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_size);
+
 #define UFS_ATTRIBUTE(_name, _uname)					\
 static ssize_t _name##_show(struct device *dev,				\
 	struct device_attribute *attr, char *buf)			\
@@ -1545,7 +1685,9 @@ UFS_ATTRIBUTE(wb_flush_status, _WB_FLUSH_STATUS);
 UFS_ATTRIBUTE(wb_avail_buf, _AVAIL_WB_BUFF_SIZE);
 UFS_ATTRIBUTE(wb_life_time_est, _WB_BUFF_LIFE_TIME_EST);
 UFS_ATTRIBUTE(wb_cur_buf, _CURR_WB_BUFF_SIZE);
-
+UFS_ATTRIBUTE(hid_available_size, _HID_AVAILABLE_SIZE);
+UFS_ATTRIBUTE(hid_progress_ratio, _HID_PROGRESS_RATIO);
+UFS_ATTRIBUTE(hid_state, _HID_STATE);
 
 static struct attribute *ufs_sysfs_attributes[] = {
 	&dev_attr_boot_lun_enabled.attr,
@@ -1568,6 +1710,11 @@ static struct attribute *ufs_sysfs_attributes[] = {
 	&dev_attr_wb_avail_buf.attr,
 	&dev_attr_wb_life_time_est.attr,
 	&dev_attr_wb_cur_buf.attr,
+	&dev_attr_defrag_operation.attr,
+	&dev_attr_hid_available_size.attr,
+	&dev_attr_hid_size.attr,
+	&dev_attr_hid_progress_ratio.attr,
+	&dev_attr_hid_state.attr,
 	NULL,
 };
 
diff --git a/drivers/ufs/core/ufshcd-priv.h b/drivers/ufs/core/ufshcd-priv.h
index 10b4a19a70f1..cc4c817dad9f 100644
--- a/drivers/ufs/core/ufshcd-priv.h
+++ b/drivers/ufs/core/ufshcd-priv.h
@@ -42,6 +42,14 @@ static inline void ufs_hwmon_remove(struct ufs_hba *hba) {}
 static inline void ufs_hwmon_notify_event(struct ufs_hba *hba, u8 ee_mask) {}
 #endif
 
+#ifdef CONFIG_SCSI_UFS_HID
+void ufs_hid_init(struct ufs_hba *hba, const u8 *desc_buf);
+int ufs_hid_disable(struct ufs_hba *hba);
+#else
+static inline void ufs_hid_init(struct ufs_hba *hba, const u8 *desc_buf) {}
+static inline int ufs_hid_disable(struct ufs_hba *hba) {return 0; }
+#endif
+
 int ufshcd_query_descriptor_retry(struct ufs_hba *hba,
 				  enum query_opcode opcode,
 				  enum desc_idn idn, u8 index,
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index 83b092cbb864..93eee2774c1b 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -6536,6 +6536,9 @@ static void ufshcd_err_handler(struct work_struct *work)
 		 hba->saved_uic_err, hba->force_reset,
 		 ufshcd_is_link_broken(hba) ? "; link is broken" : "");
 
+	if (hba->dev_info.hid_sup)
+		cancel_delayed_work_sync(&hba->ufs_hid_enable_work);
+
 	down(&hba->host_sem);
 	spin_lock_irqsave(hba->host->host_lock, flags);
 	if (ufshcd_err_handling_should_stop(hba)) {
@@ -8317,6 +8320,8 @@ static int ufs_get_device_desc(struct ufs_hba *hba)
 
 	ufshcd_temp_notif_probe(hba, desc_buf);
 
+	ufs_hid_init(hba, desc_buf);
+
 	if (dev_info->wspecversion >= 0x410) {
 		hba->critical_health_count = 0;
 		ufshcd_enable_ee(hba, MASK_EE_HEALTH_CRITICAL);
@@ -9614,6 +9619,8 @@ static int __ufshcd_wl_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
 		req_link_state = UIC_LINK_OFF_STATE;
 	}
 
+	if (hba->dev_info.hid_sup)
+		cancel_delayed_work_sync(&hba->ufs_hid_enable_work);
 	/*
 	 * If we can't transition into any of the low power modes
 	 * just gate the clocks.
diff --git a/include/ufs/ufs.h b/include/ufs/ufs.h
index 8a24ed59ec46..6e8546024e09 100644
--- a/include/ufs/ufs.h
+++ b/include/ufs/ufs.h
@@ -180,7 +180,12 @@ enum attr_idn {
 	QUERY_ATTR_IDN_AVAIL_WB_BUFF_SIZE       = 0x1D,
 	QUERY_ATTR_IDN_WB_BUFF_LIFE_TIME_EST    = 0x1E,
 	QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE        = 0x1F,
-	QUERY_ATTR_IDN_TIMESTAMP		= 0x30
+	QUERY_ATTR_IDN_TIMESTAMP		= 0x30,
+	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,
 };
 
 /* Descriptor idn for Query requests */
@@ -390,6 +395,7 @@ enum {
 	UFS_DEV_EXT_TEMP_NOTIF		= BIT(6),
 	UFS_DEV_HPB_SUPPORT		= BIT(7),
 	UFS_DEV_WRITE_BOOSTER_SUP	= BIT(8),
+	UFS_DEV_HID_SUPPORT	= BIT(13),
 };
 #define UFS_DEV_HPB_SUPPORT_VERSION		0x310
 
@@ -454,6 +460,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,
+};
+
 /* Query response result code */
 enum {
 	QUERY_RESULT_SUCCESS                    = 0x00,
@@ -590,6 +613,9 @@ struct ufs_dev_info {
 	u32 rtc_update_period;
 
 	u8 rtt_cap; /* bDeviceRTTCap */
+
+	bool hid_sup;
+	enum ufs_hid_state hid_state;
 };
 
 /*
diff --git a/include/ufs/ufshcd.h b/include/ufs/ufshcd.h
index f56050ce9445..8a8c7268d667 100644
--- a/include/ufs/ufshcd.h
+++ b/include/ufs/ufshcd.h
@@ -1138,6 +1138,8 @@ struct ufs_hba {
 	bool pm_qos_enabled;
 
 	int critical_health_count;
+
+	struct delayed_work ufs_hid_enable_work;
 };
 
 /**
-- 
2.39.0
Re: [PATCH] ufs: core: Add HID support
Posted by Peter Wang (王信友) 7 months, 3 weeks ago
On Thu, 2025-04-17 at 20:50 +0800, Huan Tang wrote:
> +
> +#define HID_SCHED_COUNT_LIMIT  300
> +static int hid_sched_cnt;
> +static void ufs_hid_enable_work_fn(struct work_struct *work)
> +{
> +       struct ufs_hba *hba;
> +       int ret = 0;
> +       enum ufs_hid_defrag_operation defrag_op;
> +       u32 hid_ahit = 0;
> +       bool hid_flag = false;
> +
> +       hba = container_of(work, struct ufs_hba,
> ufs_hid_enable_work.work);
> +
> +       if (!hba->dev_info.hid_sup)
> +               return;
> +
> +       down(&hba->host_sem);
> +
> +       if (!ufshcd_is_user_access_allowed(hba)) {
> +               up(&hba->host_sem);
> +               return;
> +       }
> +
> +       ufshcd_rpm_get_sync(hba);
> +       hid_ahit = hba->ahit;
> +       ufshcd_auto_hibern8_update(hba, 0);
> +
> +       ret = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR,
> +                       QUERY_ATTR_IDN_HID_STATE, 0, 0, &hba-
> >dev_info.hid_state);
> +       if (ret)
> +               hba->dev_info.hid_state = HID_IDLE;
> +
> +       switch (hba->dev_info.hid_state) {
> +       case HID_IDLE:
> +               defrag_op = HID_ANALYSIS_ENABLE;
> 

Hi Huan,

Can change to HID_ANALYSIS_AND_DEFRAG_ENABLE to save defragment time?


> +               hid_flag = true;
> +               break;
> +       case DEFRAG_REQUIRED:
> +               defrag_op = HID_ANALYSIS_AND_DEFRAG_ENABLE;
> +               hid_flag = true;
> +               break;
> +       case DEFRAG_COMPLETED:
> +       case DEFRAG_IS_NOT_REQUIRED:
> +               defrag_op = HID_ANALYSIS_AND_DEFRAG_DISABLE;
> +               hid_flag = true;
> +               break;
> 

Can just break? Because according the spec. 
After the host reads the bHIDState value when it is 04h (Defrag
Completion) or 05h (Defrag Not Required), the following parameters
shall be initialized: 
bHIDState value to 00h (Idle)


> 
> @@ -9614,6 +9619,8 @@ static int __ufshcd_wl_suspend(struct ufs_hba
> *hba, enum ufs_pm_op pm_op)
>                 req_link_state = UIC_LINK_OFF_STATE;
>         }
> 
> +       if (hba->dev_info.hid_sup)
> +               cancel_delayed_work_sync(&hba->ufs_hid_enable_work);
> 

Will have dead-lock when ufs_hid_enable_work_fn invoke
ufshcd_rpm_get_sync?


>         /*
>          * If we can't transition into any of the low power modes
>          * just gate the clocks.
> diff --git a/include/ufs/ufs.h b/include/ufs/ufs.h
> index 8a24ed59ec46..6e8546024e09 100644
> --- a/include/ufs/ufs.h
> +++ b/include/ufs/ufs.h
> 
>  /* Descriptor idn for Query requests */
> @@ -390,6 +395,7 @@ enum {
>         UFS_DEV_EXT_TEMP_NOTIF          = BIT(6),
>         UFS_DEV_HPB_SUPPORT             = BIT(7),
>         UFS_DEV_WRITE_BOOSTER_SUP       = BIT(8),
> +       UFS_DEV_HID_SUPPORT     = BIT(13),
> 

Please align the arrangement of these enums.


>  };
>  #define UFS_DEV_HPB_SUPPORT_VERSION            0x310
> 
> @@ -454,6 +460,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,
> 

Please align the arrangement of these enums.


> +};
> +
> +/* 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,
> 

Please align the arrangement of these enums.

Thanks.
Peter




Re: Re: [PATCH] ufs: core: Add HID support
Posted by Huan Tang 7 months ago
Hi peter sir,

Sorry for not replying in time. Thank you for your comments and guidance!
Based on your, Bart's and Arvi's suggestions, I submitted the v2 patch:

https://lore.kernel.org/all/20250512131519.138-1-tanghuan@vivo.com/

> Can just break? Because according the spec. 
> After the host reads the bHIDState value when it is 04h (Defrag
> Completion) or 05h (Defrag Not Required), the following parameters
> shall be initialized: 
> bHIDState value to 00h (Idle)
v2 patch does not use this

> Will have dead-lock when ufs_hid_enable_work_fn invoke
> ufshcd_rpm_get_sync?
v2 patch does not use this

> Please align the arrangement of these enums.
Already modified in v2 patch

> Please align the arrangement of these enums.
Already modified in v2 patch

> Please align the arrangement of these enums.
Already modified in v2 patch


Thanks
Huan


Re: [PATCH] ufs: core: Add HID support
Posted by Bart Van Assche 8 months ago
On 4/17/25 5:50 AM, Huan Tang wrote:
> +What:		/sys/bus/platform/drivers/ufshcd/*/hid_trigger
> +What:		/sys/bus/platform/devices/*.ufs/hid_trigger
> +Date:		April 2025
> +Contact:	Huan Tang <tanghuan@vivo.com>
> +Description:
> +		The host can enable or disable complete HID.
> +
> +		========  ============
> +		enable    Let kworker(ufs_hid_enable_work) execute the complete HID
> +		disable   Cancel kworker(ufs_hid_enable_work) and disable HID
> +		========  ============
> +		The file is write only.
> +
> +What:		/sys/bus/platform/drivers/ufshcd/*/attributes/defrag_operation
> +What:		/sys/bus/platform/devices/*.ufs/attributes/defrag_operation
> +Date:		April 2025
> +Contact:	Huan Tang <tanghuan@vivo.com>
> +Description:
> +		The host can enable or disable HID analysis and HID defrag operations.
> +
> +		===============  ==================================================
> +		all_disable      HID analysis and HID defrag operation are disabled
> +		analysis_enable  HID analysis is enabled
> +		all_enable       HID analysis and HID defrag operation are enabled
> +		===============  ==================================================
> +
> +		The attribute is read/write.

Combining HID analysis and HID defragmentation controls into a single
sysfs attribute seems weird to me. Please replace the above two
attributes with two different attributes: one for controlling HID
analysis and another one for controlling HID defragmentation.

> +
> +What:		/sys/bus/platform/drivers/ufshcd/*/attributes/hid_available_size
> +What:		/sys/bus/platform/devices/*.ufs/attributes/hid_available_size
> +Date:		April 2025
> +Contact:	Huan Tang <tanghuan@vivo.com>
> +Description:
> +		The total fragmented size in the device is reported through this
> +		attribute.
> +
> +		The attribute is read only.

Please change the name of this attribute into "hid_fragmented_size". I
think that this alternative name is much more clear.

> +
> +What:		/sys/bus/platform/drivers/ufshcd/*/attributes/hid_size
> +What:		/sys/bus/platform/devices/*.ufs/attributes/hid_size
> +Date:		April 2025
> +Contact:	Huan Tang <tanghuan@vivo.com>
> +Description:
> +		The host sets the size to be defragmented by an HID defrag operation.
> +
> +		The attribute is read/write.

Please make the name of this attribute more clear, e.g. by renaming it
into "hid_defrag_size". Additionally, please change "defrag" into
"defragmentation".

> +What:		/sys/bus/platform/drivers/ufshcd/*/attributes/hid_progress_ratio
> +What:		/sys/bus/platform/devices/*.ufs/attributes/hid_progress_ratio
> +Date:		April 2025
> +Contact:	Huan Tang <tanghuan@vivo.com>
> +Description:
> +		Defrag progress is reported by this attribute,indicates the ratio of
> +		the completed defrag size over the requested defrag size.
> +
> +		====  ======================================
> +		01h   1%
> +		...
> +		64h   100%
> +		====  ======================================
> +
> +		The attribute is read only.

Please change the format of this attribute from hexadecimal into
decimal (64h -> 100).

> +
> +What:		/sys/bus/platform/drivers/ufshcd/*/attributes/hid_state
> +What:		/sys/bus/platform/devices/*.ufs/attributes/hid_state
> +Date:		April 2025
> +Contact:	Huan Tang <tanghuan@vivo.com>
> +Description:
> +		The HID state is reported by this attribute.
> +
> +		====   ======================================
> +		00h    Idle(analysis required)
> +		01h    Analysis in progress
> +		02h    Defrag required
> +		03h    Defrag in progress
> +		04h    Defrag completed
> +		05h    Defrag is not required
> +		====  ======================================
> +
> +		The attribute is read only.

Please change the format of this attribute from hexadecimal into
decimal. Please add an additional sysfs attribute that provides the
textual meaning of the HID state such that users don't have to look up
the documentation of these codes.

> +config SCSI_UFS_HID
> +	bool "Support UFS Host Initiated Defrag"
> +	help
> +	  The UFS HID feature allows the host to check the status of whether
> +	  defragmentation is needed or not and enable the device's internal
> +	  defrag operation explicitly.
> +
> +	  If unsure, say N.

Here and everywhere else in documentation that is intended for humans,
please change "defrag" into "defragmentation". Please also make the
Kconfig description more clear, e.g. by changing it into the following:

	help
	  In NAND-based storage devices garbage collection is
	  inevitable. Garbage collection may cause latency spikes.
	  The UFS Host-Initiated Defragmentation (HID) functionality
	  gives the host control over when defragmentation (garbage
	  collection) happens and hence can be used to avoid latency
	  spikes.

> +#define HID_SCHED_COUNT_LIMIT	300
> +static int hid_sched_cnt;
> +static void ufs_hid_enable_work_fn(struct work_struct *work)

A blank line is required after the macro definition and also between the
declaration of the static variable and the function definition.

> +{
> +	struct ufs_hba *hba;
> +	int ret = 0;
> +	enum ufs_hid_defrag_operation defrag_op;
> +	u32 hid_ahit = 0;
> +	bool hid_flag = false;

In new code, please order declarations such that the longest declaration
occurs first ("reverse Christmas tree").

> +	hba = container_of(work, struct ufs_hba, ufs_hid_enable_work.work);
> +
> +	if (!hba->dev_info.hid_sup)
> +		return;
> +
> +	down(&hba->host_sem);
> +
> +	if (!ufshcd_is_user_access_allowed(hba)) {
> +		up(&hba->host_sem);
> +		return;
> +	}
> +
> +	ufshcd_rpm_get_sync(hba);
> +	hid_ahit = hba->ahit;
> +	ufshcd_auto_hibern8_update(hba, 0);

Why is auto-hibernation disabled here? Please add a comment.

> +int ufs_hid_disable(struct ufs_hba *hba)
> +{
> +	enum ufs_hid_defrag_operation defrag_op = HID_ANALYSIS_AND_DEFRAG_DISABLE;
> +	u32 hid_ahit;
> +	int ret;
> +
> +	down(&hba->host_sem);
> +
> +	if (!ufshcd_is_user_access_allowed(hba)) {
> +		up(&hba->host_sem);
> +		return -EBUSY;
> +	}
> +
> +	ufshcd_rpm_get_sync(hba);
> +	hid_ahit = hba->ahit;
> +	ufshcd_auto_hibern8_update(hba, 0);
> +
> +	ret = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_WRITE_ATTR,
> +				QUERY_ATTR_IDN_HID_DEFRAG_OPERATION, 0, 0, &defrag_op);
> +
> +	ufshcd_auto_hibern8_update(hba, hid_ahit);
> +	ufshcd_rpm_put_sync(hba);
> +	up(&hba->host_sem);
> +
> +	return ret;
> +}

Please add a comment that explains why the ufshcd_auto_hibern8_update()
call is present.

> +static ssize_t hid_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, "0x%08X\n", value);
> +}

Why the hexadecimal format? All other sysfs size attributes I know of
use the decimal format.

Thanks,

Bart.
Re: Re: [PATCH] ufs: core: Add HID support
Posted by Huan Tang 7 months ago
Hi  Bart sir,

Sorry for not replying in time. Thank you for your comments and guidance!
Based on your, Peter's and Arvi's suggestions, I submitted the v2 patch:

https://lore.kernel.org/all/20250512131519.138-1-tanghuan@vivo.com/

> Combining HID analysis and HID defragmentation controls into a single
> sysfs attribute seems weird to me. Please replace the above two
> attributes with two different attributes: one for controlling HID
> analysis and another one for controlling HID defragmentation.

In the v2 patch,  use hid_analysis_trigger and hid_defrag_trigger

> Please change the name of this attribute into "hid_fragmented_size". I
> think that this alternative name is much more clear.

Already modified in v2 patch

> Please make the name of this attribute more clear, e.g. by renaming it
> into "hid_defrag_size". Additionally, please change "defrag" into
> "defragmentation".

Already modified in v2 patch

> Please change the format of this attribute from hexadecimal into
> decimal (64h -> 100).

Already modified in v2 patch

> Please change the format of this attribute from hexadecimal into
> decimal. Please add an additional sysfs attribute that provides the
> textual meaning of the HID state such that users don't have to look up
> the documentation of these codes.

Already modified in v2 patch

> Here and everywhere else in documentation that is intended for humans,
> please change "defrag" into "defragmentation". Please also make the
> Kconfig description more clear, e.g. by changing it into the following:

Already modified in v2 patch

> A blank line is required after the macro definition and also between the
> declaration of the static variable and the function definition.

v2 patch does not use this

> In new code, please order declarations such that the longest declaration
> occurs first ("reverse Christmas tree").

Already modified in v2 patch

> Why is auto-hibernation disabled here? Please add a comment.

Some UFS vendors say that AH8 needs to be disabled to run HID

> Please add a comment that explains why the ufshcd_auto_hibern8_update()
> call is present.

Some UFS vendors say that AH8 needs to be disabled to run HID

> Why the hexadecimal format? All other sysfs size attributes I know of
> use the decimal format.

Already modified in v2 patch

Thanks
Huan