diff --git a/Documentation/ABI/testing/sysfs-driver-ufs b/Documentation/ABI/testing/sysfs-driver-ufs index 0397664e869a..5bfccd6343f7 100644 --- a/Documentation/ABI/testing/sysfs-driver-ufs +++ b/Documentation/ABI/testing/sysfs-driver-ufs @@ -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 +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 +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 +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 +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 +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 +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 964983bceb8f..354e82512357 100644 --- a/drivers/ufs/core/ufs-sysfs.c +++ b/drivers/ufs/core/ufs-sysfs.c @@ -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, }; diff --git a/drivers/ufs/core/ufshcd-priv.h b/drivers/ufs/core/ufshcd-priv.h index 95dd7ebde98e..d169ec9e5711 100644 --- a/drivers/ufs/core/ufshcd-priv.h +++ b/drivers/ufs/core/ufshcd-priv.h @@ -6,6 +6,16 @@ #include #include +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; diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 1a782409a30a..0e6a198e19e9 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -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; diff --git a/include/ufs/ufs.h b/include/ufs/ufs.h index f923463595ac..9ec986341249 100644 --- a/include/ufs/ufs.h +++ b/include/ufs/ufs.h @@ -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,