[PATCH v4] ufs: core: Add HID support
ALOK TIWARI
alok.a.tiwari at oracle.com
Tue May 20 00:45:18 PDT 2025
> +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 at vivo.com>
> +Description:
> + Defragmentation progress is reported by this attribute,
> + indicateds the ratio of the completed defragmentation size
typo indicateds -> indicates
> + 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 at vivo.com>
> +Description:
> + The HID state is reported by this attribute.
> +
> + ==================== ===========================
> + idle Idle(analysis required)
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..f162eb36f46b 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)
> {
> @@ -1763,6 +1783,177 @@ 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 const char * const hid_trigger_mode[] = {"disable", "enable"};
> +
> +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;
> +
> + 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(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;
> +
> + 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(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,
remove extra ' ' after umode_t
> + struct attribute *attr, int n)
> +{
> + struct device *dev = container_of(kobj, struct device, kobj);
> + struct ufs_hba *hba = dev_get_drvdata(dev);
> +
> + return hba->dev_info.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,
> @@ -1777,6 +1968,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,
> };
>
> diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
> index 3e2097e65964..8ccd923a5761 100644
> --- a/drivers/ufs/core/ufshcd.c
> +++ b/drivers/ufs/core/ufshcd.c
> @@ -8390,6 +8390,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,
_IS_ is extra in an enum label, or sound redundant.
I think DEFRAG_NOT_REQUIRED sounds cleaner, especially since it's used
with return "defrag_not_required".
> +};
> +
> /* 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;
> };
>
> /*
Thanks,
Alok
More information about the Linux-mediatek
mailing list