scsi: ufs: core: Add WB buffer resize support
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>pull/982/merge
parent
ae82eaf4ae
commit
500d4b742e
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue