BACKPORT: FROMGIT: scsi: ufs: core: Add HID support

Follow JESD220G, support HID(Host Initiated Defragmentation) through
sysfs, the relevant sysfs nodes are as follows:

	1. analysis_trigger
	2. defrag_trigger
	3. fragmented_size
	4. defrag_size
	5. progress_ratio
	6. state

The detailed definition of the six nodes can be found in the sysfs
documentation.

HID's execution policy is given to user-space.

Signed-off-by: Huan Tang <tanghuan@vivo.com>
Signed-off-by: Wenxing Cheng <wenxing.cheng@vivo.com>
Link: https://lore.kernel.org/r/20250523064604.800-1-tanghuan@vivo.com
Suggested-by: Bart Van Assche <bvanassche@acm.org>
Reviewed-by: Peter Wang <peter.wang@mediatek.com>
Reviewed-by: Bean Huo <huobean@gmail.com>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Reviewed-by: Yangtao Li <frank.li@vivo.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Bug: 435744113
Change-Id: Ib2348157694d3b57dab2da9f8b47be5b0bccf42d
(cherry picked from commit ae7795a8c2582b5fb7971132753810a3f158e7b2 https://git.kernel.org/pub/scm/linux/kernel/git/mkp/scsi.git for-next)
[ bvanassche: introduced struct ufs_hba_priv ]
Signed-off-by: Bart Van Assche <bvanassche@google.com>
This commit is contained in:
Huan Tang
2025-05-23 14:46:04 +08:00
committed by Bart Van Assche
parent 1ccd114e35
commit ba125a42ce
5 changed files with 316 additions and 1 deletions
@@ -1636,3 +1636,86 @@ Description:
attribute value.
The attribute is read only.
What: /sys/bus/platform/drivers/ufshcd/*/hid/analysis_trigger
What: /sys/bus/platform/devices/*.ufs/hid/analysis_trigger
Date: May 2025
Contact: Huan Tang <tanghuan@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/*/hid/defrag_trigger
What: /sys/bus/platform/devices/*.ufs/hid/defrag_trigger
Date: May 2025
Contact: Huan Tang <tanghuan@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/*/hid/fragmented_size
What: /sys/bus/platform/devices/*.ufs/hid/fragmented_size
Date: May 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/*/hid/defrag_size
What: /sys/bus/platform/devices/*.ufs/hid/defrag_size
Date: May 2025
Contact: Huan Tang <tanghuan@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/*/hid/progress_ratio
What: /sys/bus/platform/devices/*.ufs/hid/progress_ratio
Date: May 2025
Contact: Huan Tang <tanghuan@vivo.com>
Description:
Defragmentation progress is reported by this attribute,
indicates 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/*/hid/state
What: /sys/bus/platform/devices/*.ufs/hid/state
Date: May 2025
Contact: Huan Tang <tanghuan@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.
+190
View File
@@ -57,6 +57,23 @@ static const char *ufs_hs_gear_to_string(enum ufs_hs_gear_tag gear)
}
}
static const char * const ufs_hid_states[] = {
[HID_IDLE] = "idle",
[ANALYSIS_IN_PROGRESS] = "analysis_in_progress",
[DEFRAG_REQUIRED] = "defrag_required",
[DEFRAG_IN_PROGRESS] = "defrag_in_progress",
[DEFRAG_COMPLETED] = "defrag_completed",
[DEFRAG_NOT_REQUIRED] = "defrag_not_required",
};
static const char *ufs_hid_state_to_string(enum ufs_hid_state state)
{
if (state < NUM_UFS_HID_STATES)
return ufs_hid_states[state];
return "unknown";
}
static const char *ufshcd_uic_link_state_to_string(
enum uic_link_state state)
{
@@ -1627,6 +1644,178 @@ static const struct attribute_group ufs_sysfs_attributes_group = {
.attrs = ufs_sysfs_attributes,
};
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 ssize_t 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;
if (sysfs_streq(buf, "enable"))
mode = HID_ANALYSIS_ENABLE;
else if (sysfs_streq(buf, "disable"))
mode = HID_ANALYSIS_AND_DEFRAG_DISABLE;
else
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(analysis_trigger);
static ssize_t 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;
if (sysfs_streq(buf, "enable"))
mode = HID_ANALYSIS_AND_DEFRAG_ENABLE;
else if (sysfs_streq(buf, "disable"))
mode = HID_ANALYSIS_AND_DEFRAG_DISABLE;
else
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(defrag_trigger);
static ssize_t 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(fragmented_size);
static ssize_t 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 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(defrag_size);
static ssize_t 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(progress_ratio);
static ssize_t 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(state);
static struct attribute *ufs_sysfs_hid[] = {
&dev_attr_analysis_trigger.attr,
&dev_attr_defrag_trigger.attr,
&dev_attr_fragmented_size.attr,
&dev_attr_defrag_size.attr,
&dev_attr_progress_ratio.attr,
&dev_attr_state.attr,
NULL,
};
static umode_t ufs_sysfs_hid_is_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct ufs_hba *hba = dev_get_drvdata(dev);
return to_hba_priv(hba)->hid_sup ? attr->mode : 0;
}
static const struct attribute_group ufs_sysfs_hid_group = {
.name = "hid",
.attrs = ufs_sysfs_hid,
.is_visible = ufs_sysfs_hid_is_visible,
};
static const struct attribute_group *ufs_sysfs_groups[] = {
&ufs_sysfs_default_group,
&ufs_sysfs_capabilities_group,
@@ -1641,6 +1830,7 @@ static const struct attribute_group *ufs_sysfs_groups[] = {
&ufs_sysfs_string_descriptors_group,
&ufs_sysfs_flags_group,
&ufs_sysfs_attributes_group,
&ufs_sysfs_hid_group,
NULL,
};
+10
View File
@@ -6,6 +6,16 @@
#include <linux/pm_runtime.h>
#include <ufs/ufshcd.h>
struct ufs_hba_priv {
struct ufs_hba hba;
bool hid_sup;
};
static inline struct ufs_hba_priv *to_hba_priv(struct ufs_hba *hba)
{
return container_of(hba, struct ufs_hba_priv, hba);
}
static inline bool ufshcd_is_user_access_allowed(struct ufs_hba *hba)
{
return !hba->shutting_down;
+5 -1
View File
@@ -8533,6 +8533,10 @@ static int ufs_get_device_desc(struct ufs_hba *hba)
dev_info->rtt_cap = desc_buf[DEVICE_DESC_PARAM_RTT_CAP];
to_hba_priv(hba)->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,
@@ -10607,7 +10611,7 @@ int ufshcd_alloc_host(struct device *dev, struct ufs_hba **hba_handle)
}
host = scsi_host_alloc(&ufshcd_driver_template,
sizeof(struct ufs_hba));
sizeof(struct ufs_hba_priv));
if (!host) {
dev_err(dev, "scsi_host_alloc failed\n");
err = -ENOMEM;
+28
View File
@@ -187,6 +187,15 @@ enum attr_idn {
QUERY_ATTR_IDN_DEV_LVL_EXCEPTION_ID = 0x34,
};
/* The constants below are not in enum attr_idn to preseve the GKI ABI. */
enum {
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 */
enum desc_idn {
QUERY_DESC_IDN_DEVICE = 0x0,
@@ -396,6 +405,7 @@ enum {
UFS_DEV_WRITE_BOOSTER_SUP = BIT(8),
UFS_DEV_EXT_IID_SUP = BIT(16),
UFS_DEV_LVL_EXCEPTION_SUP = BIT(12),
UFS_DEV_HID_SUPPORT = BIT(13),
};
#define UFS_DEV_HPB_SUPPORT_VERSION 0x310
@@ -461,6 +471,24 @@ 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_NOT_REQUIRED = 5,
NUM_UFS_HID_STATES = 6,
};
/* Query response result code */
enum {
QUERY_RESULT_SUCCESS = 0x00,