scsi: ufs: core: Add WB buffer resize support
authorHuan Tang <tanghuan@vivo.com>
Fri, 11 Apr 2025 09:29:24 +0000 (17:29 +0800)
committerMartin K. Petersen <martin.petersen@oracle.com>
Sat, 12 Apr 2025 01:38:06 +0000 (21:38 -0400)
Follow JESD220G, support a WB buffer resize function through sysfs. The
host can obtain resize hint and resize status, and enable the resize
operation. Add three sysfs nodes:

1. wb_resize_enable

2. wb_resize_hint

3. wb_resize_status

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

Signed-off-by: Huan Tang <tanghuan@vivo.com>
Signed-off-by: Lu Hongfei <luhongfei@vivo.com>
Link: https://lore.kernel.org/r/20250411092924.1116-1-tanghuan@vivo.com
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Documentation/ABI/testing/sysfs-driver-ufs
drivers/ufs/core/ufs-sysfs.c
drivers/ufs/core/ufshcd.c
include/ufs/ufs.h
include/ufs/ufshcd.h

index ae0191295d29b8135bc6d5c41986bf41eebbd3be..e7b49bc894f57af17c3b72a249130ab89d186387 100644 (file)
@@ -1604,3 +1604,52 @@ Description:
                prevent the UFS from frequently performing clock gating/ungating.
 
                The attribute is read/write.
+
+What:          /sys/bus/platform/drivers/ufshcd/*/wb_resize_enable
+What:          /sys/bus/platform/devices/*.ufs/wb_resize_enable
+Date:          April 2025
+Contact:       Huan Tang <tanghuan@vivo.com>
+Description:
+               The host can enable the WriteBooster buffer resize by setting this
+               attribute.
+
+               ========  ======================================
+               idle      There is no resize operation
+               decrease  Decrease WriteBooster buffer size
+               increase  Increase WriteBooster buffer size
+               ========  ======================================
+
+               The file is write only.
+
+What:          /sys/bus/platform/drivers/ufshcd/*/attributes/wb_resize_hint
+What:          /sys/bus/platform/devices/*.ufs/attributes/wb_resize_hint
+Date:          April 2025
+Contact:       Huan Tang <tanghuan@vivo.com>
+Description:
+               wb_resize_hint indicates hint information about which type of resize
+               for WriteBooster buffer is recommended by the device.
+
+               =========  ======================================
+               keep       Recommend keep the buffer size
+               decrease   Recommend to decrease the buffer size
+               increase   Recommend to increase the buffer size
+               =========  ======================================
+
+               The file is read only.
+
+What:          /sys/bus/platform/drivers/ufshcd/*/attributes/wb_resize_status
+What:          /sys/bus/platform/devices/*.ufs/attributes/wb_resize_status
+Date:          April 2025
+Contact:       Huan Tang <tanghuan@vivo.com>
+Description:
+               The host can check the resize operation status of the WriteBooster
+               buffer by reading this attribute.
+
+               ================  ========================================
+               idle              Resize operation is not issued
+               in_progress       Resize operation in progress
+               complete_success  Resize operation completed successfully
+               general_failure   Resize operation general failure
+               ================  ========================================
+
+               The file is read only.
index 90b5ab60f5ae4a5cea34a56e13e8a5307c39a633..a27a3980480ebe2d3b64ca1c1b997275ed8d65e7 100644 (file)
@@ -57,6 +57,36 @@ static const char *ufs_hs_gear_to_string(enum ufs_hs_gear_tag gear)
        }
 }
 
+static const char *ufs_wb_resize_hint_to_string(enum wb_resize_hint hint)
+{
+       switch (hint) {
+       case WB_RESIZE_HINT_KEEP:
+               return "keep";
+       case WB_RESIZE_HINT_DECREASE:
+               return "decrease";
+       case WB_RESIZE_HINT_INCREASE:
+               return "increase";
+       default:
+               return "unknown";
+       }
+}
+
+static const char *ufs_wb_resize_status_to_string(enum wb_resize_status status)
+{
+       switch (status) {
+       case WB_RESIZE_STATUS_IDLE:
+               return "idle";
+       case WB_RESIZE_STATUS_IN_PROGRESS:
+               return "in_progress";
+       case WB_RESIZE_STATUS_COMPLETE_SUCCESS:
+               return "complete_success";
+       case WB_RESIZE_STATUS_GENERAL_FAILURE:
+               return "general_failure";
+       default:
+               return "unknown";
+       }
+}
+
 static const char *ufshcd_uic_link_state_to_string(
                        enum uic_link_state state)
 {
@@ -411,6 +441,44 @@ static ssize_t wb_flush_threshold_store(struct device *dev,
        return count;
 }
 
+static const char * const wb_resize_en_mode[] = {
+       [WB_RESIZE_EN_IDLE]     = "idle",
+       [WB_RESIZE_EN_DECREASE] = "decrease",
+       [WB_RESIZE_EN_INCREASE] = "increase",
+};
+
+static ssize_t wb_resize_enable_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t count)
+{
+       struct ufs_hba *hba = dev_get_drvdata(dev);
+       int mode;
+       ssize_t res;
+
+       if (!ufshcd_is_wb_allowed(hba) || !hba->dev_info.wb_enabled
+               || !hba->dev_info.b_presrv_uspc_en
+               || !(hba->dev_info.ext_wb_sup & UFS_DEV_WB_BUF_RESIZE))
+               return -EOPNOTSUPP;
+
+       mode = sysfs_match_string(wb_resize_en_mode, buf);
+       if (mode < 0)
+               return -EINVAL;
+
+       down(&hba->host_sem);
+       if (!ufshcd_is_user_access_allowed(hba)) {
+               res = -EBUSY;
+               goto out;
+       }
+
+       ufshcd_rpm_get_sync(hba);
+       res = ufshcd_wb_set_resize_en(hba, mode);
+       ufshcd_rpm_put_sync(hba);
+
+out:
+       up(&hba->host_sem);
+       return res < 0 ? res : count;
+}
+
 /**
  * pm_qos_enable_show - sysfs handler to show pm qos enable value
  * @dev: device associated with the UFS controller
@@ -476,6 +544,7 @@ static DEVICE_ATTR_RW(auto_hibern8);
 static DEVICE_ATTR_RW(wb_on);
 static DEVICE_ATTR_RW(enable_wb_buf_flush);
 static DEVICE_ATTR_RW(wb_flush_threshold);
+static DEVICE_ATTR_WO(wb_resize_enable);
 static DEVICE_ATTR_RW(rtc_update_ms);
 static DEVICE_ATTR_RW(pm_qos_enable);
 static DEVICE_ATTR_RO(critical_health);
@@ -491,6 +560,7 @@ static struct attribute *ufs_sysfs_ufshcd_attrs[] = {
        &dev_attr_wb_on.attr,
        &dev_attr_enable_wb_buf_flush.attr,
        &dev_attr_wb_flush_threshold.attr,
+       &dev_attr_wb_resize_enable.attr,
        &dev_attr_rtc_update_ms.attr,
        &dev_attr_pm_qos_enable.attr,
        &dev_attr_critical_health.attr,
@@ -1495,6 +1565,67 @@ static inline bool ufshcd_is_wb_attrs(enum attr_idn idn)
                idn <= QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE;
 }
 
+static int wb_read_resize_attrs(struct ufs_hba *hba,
+                       enum attr_idn idn, u32 *attr_val)
+{
+       u8 index = 0;
+       int ret;
+
+       if (!ufshcd_is_wb_allowed(hba) || !hba->dev_info.wb_enabled
+               || !hba->dev_info.b_presrv_uspc_en
+               || !(hba->dev_info.ext_wb_sup & UFS_DEV_WB_BUF_RESIZE))
+               return -EOPNOTSUPP;
+
+       down(&hba->host_sem);
+       if (!ufshcd_is_user_access_allowed(hba)) {
+               up(&hba->host_sem);
+               return -EBUSY;
+       }
+
+       index = ufshcd_wb_get_query_index(hba);
+       ufshcd_rpm_get_sync(hba);
+       ret = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR,
+                       idn, index, 0, attr_val);
+       ufshcd_rpm_put_sync(hba);
+
+       up(&hba->host_sem);
+       return ret;
+}
+
+static ssize_t wb_resize_hint_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct ufs_hba *hba = dev_get_drvdata(dev);
+       int ret;
+       u32 value;
+
+       ret = wb_read_resize_attrs(hba,
+                       QUERY_ATTR_IDN_WB_BUF_RESIZE_HINT, &value);
+       if (ret)
+               return ret;
+
+       return sysfs_emit(buf, "%s\n", ufs_wb_resize_hint_to_string(value));
+}
+
+static DEVICE_ATTR_RO(wb_resize_hint);
+
+static ssize_t wb_resize_status_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct ufs_hba *hba = dev_get_drvdata(dev);
+       int ret;
+       u32 value;
+
+       ret = wb_read_resize_attrs(hba,
+                       QUERY_ATTR_IDN_WB_BUF_RESIZE_STATUS, &value);
+       if (ret)
+               return ret;
+
+       return sysfs_emit(buf, "%s\n", ufs_wb_resize_status_to_string(value));
+}
+
+static DEVICE_ATTR_RO(wb_resize_status);
+
 #define UFS_ATTRIBUTE(_name, _uname)                                   \
 static ssize_t _name##_show(struct device *dev,                                \
        struct device_attribute *attr, char *buf)                       \
@@ -1568,6 +1699,8 @@ 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_wb_resize_hint.attr,
+       &dev_attr_wb_resize_status.attr,
        NULL,
 };
 
index b40660ca2fa6b3488645bd26121752554a8d6a08..be65fc4b5ccd3a0d21e72a3b7a66f0dc6f8943c7 100644 (file)
@@ -6080,6 +6080,21 @@ int ufshcd_wb_toggle_buf_flush(struct ufs_hba *hba, bool enable)
        return ret;
 }
 
+int ufshcd_wb_set_resize_en(struct ufs_hba *hba, enum wb_resize_en en_mode)
+{
+       int ret;
+       u8 index;
+
+       index = ufshcd_wb_get_query_index(hba);
+       ret = ufshcd_query_attr_retry(hba, UPIU_QUERY_OPCODE_WRITE_ATTR,
+                               QUERY_ATTR_IDN_WB_BUF_RESIZE_EN, index, 0, &en_mode);
+       if (ret)
+               dev_err(hba->dev, "%s: Enable WB buf resize operation failed %d\n",
+                       __func__, ret);
+
+       return ret;
+}
+
 static bool ufshcd_wb_presrv_usrspc_keep_vcc_on(struct ufs_hba *hba,
                                                u32 avail_buf)
 {
@@ -8100,6 +8115,9 @@ static void ufshcd_wb_probe(struct ufs_hba *hba, const u8 *desc_buf)
         */
        dev_info->wb_buffer_type = desc_buf[DEVICE_DESC_PARAM_WB_TYPE];
 
+       dev_info->ext_wb_sup =  get_unaligned_be16(desc_buf +
+                                               DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP);
+
        dev_info->b_presrv_uspc_en =
                desc_buf[DEVICE_DESC_PARAM_WB_PRESRV_USRSPC_EN];
 
index 8a24ed59ec46f0b438d1f1d0069a7fe17f49000a..9188f7aa99dab2211466fe3f89425c09fc94382d 100644 (file)
@@ -180,7 +180,10 @@ 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_WB_BUF_RESIZE_HINT       = 0x3C,
+       QUERY_ATTR_IDN_WB_BUF_RESIZE_EN         = 0x3D,
+       QUERY_ATTR_IDN_WB_BUF_RESIZE_STATUS     = 0x3E,
 };
 
 /* Descriptor idn for Query requests */
@@ -289,6 +292,7 @@ enum device_desc_param {
        DEVICE_DESC_PARAM_PRDCT_REV             = 0x2A,
        DEVICE_DESC_PARAM_HPB_VER               = 0x40,
        DEVICE_DESC_PARAM_HPB_CONTROL           = 0x42,
+       DEVICE_DESC_PARAM_EXT_WB_SUP            = 0x4D,
        DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP   = 0x4F,
        DEVICE_DESC_PARAM_WB_PRESRV_USRSPC_EN   = 0x53,
        DEVICE_DESC_PARAM_WB_TYPE               = 0x54,
@@ -383,6 +387,11 @@ enum {
        UFSHCD_AMP              = 3,
 };
 
+/* Possible values for wExtendedWriteBoosterSupport */
+enum {
+       UFS_DEV_WB_BUF_RESIZE   = BIT(0),
+};
+
 /* Possible values for dExtendedUFSFeaturesSupport */
 enum {
        UFS_DEV_HIGH_TEMP_NOTIF         = BIT(4),
@@ -454,6 +463,28 @@ enum ufs_ref_clk_freq {
        REF_CLK_FREQ_INVAL      = -1,
 };
 
+/* bWriteBoosterBufferResizeEn attribute */
+enum wb_resize_en {
+       WB_RESIZE_EN_IDLE       = 0,
+       WB_RESIZE_EN_DECREASE   = 1,
+       WB_RESIZE_EN_INCREASE   = 2,
+};
+
+/* bWriteBoosterBufferResizeHint attribute */
+enum wb_resize_hint {
+       WB_RESIZE_HINT_KEEP     = 0,
+       WB_RESIZE_HINT_DECREASE = 1,
+       WB_RESIZE_HINT_INCREASE = 2,
+};
+
+/* bWriteBoosterBufferResizeStatus attribute */
+enum wb_resize_status {
+       WB_RESIZE_STATUS_IDLE   = 0,
+       WB_RESIZE_STATUS_IN_PROGRESS    = 1,
+       WB_RESIZE_STATUS_COMPLETE_SUCCESS       = 2,
+       WB_RESIZE_STATUS_GENERAL_FAILURE        = 3,
+};
+
 /* Query response result code */
 enum {
        QUERY_RESULT_SUCCESS                    = 0x00,
@@ -578,6 +609,7 @@ struct ufs_dev_info {
        bool    wb_buf_flush_enabled;
        u8      wb_dedicated_lu;
        u8      wb_buffer_type;
+       u16     ext_wb_sup;
 
        bool    b_rpm_dev_flush_capable;
        u8      b_presrv_uspc_en;
index ec999fa671240cb87bf540d339aa830b6847eb71..a2ffdc0b1044c71967dbd3d1a1ee2237ab171d0b 100644 (file)
@@ -1468,6 +1468,7 @@ int ufshcd_advanced_rpmb_req_handler(struct ufs_hba *hba, struct utp_upiu_req *r
                                     struct scatterlist *sg_list, enum dma_data_direction dir);
 int ufshcd_wb_toggle(struct ufs_hba *hba, bool enable);
 int ufshcd_wb_toggle_buf_flush(struct ufs_hba *hba, bool enable);
+int ufshcd_wb_set_resize_en(struct ufs_hba *hba, enum wb_resize_en en_mode);
 int ufshcd_suspend_prepare(struct device *dev);
 int __ufshcd_suspend_prepare(struct device *dev, bool rpm_ok_for_spm);
 void ufshcd_resume_complete(struct device *dev);