mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-09 09:01:36 -04:00
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>
This commit is contained in:
committed by
Martin K. Petersen
parent
ae82eaf4ae
commit
500d4b742e
@@ -1604,3 +1604,52 @@ Description:
|
|||||||
prevent the UFS from frequently performing clock gating/ungating.
|
prevent the UFS from frequently performing clock gating/ungating.
|
||||||
|
|
||||||
The attribute is read/write.
|
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(
|
static const char *ufshcd_uic_link_state_to_string(
|
||||||
enum uic_link_state state)
|
enum uic_link_state state)
|
||||||
{
|
{
|
||||||
@@ -411,6 +441,44 @@ static ssize_t wb_flush_threshold_store(struct device *dev,
|
|||||||
return count;
|
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
|
* pm_qos_enable_show - sysfs handler to show pm qos enable value
|
||||||
* @dev: device associated with the UFS controller
|
* @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(wb_on);
|
||||||
static DEVICE_ATTR_RW(enable_wb_buf_flush);
|
static DEVICE_ATTR_RW(enable_wb_buf_flush);
|
||||||
static DEVICE_ATTR_RW(wb_flush_threshold);
|
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(rtc_update_ms);
|
||||||
static DEVICE_ATTR_RW(pm_qos_enable);
|
static DEVICE_ATTR_RW(pm_qos_enable);
|
||||||
static DEVICE_ATTR_RO(critical_health);
|
static DEVICE_ATTR_RO(critical_health);
|
||||||
@@ -491,6 +560,7 @@ static struct attribute *ufs_sysfs_ufshcd_attrs[] = {
|
|||||||
&dev_attr_wb_on.attr,
|
&dev_attr_wb_on.attr,
|
||||||
&dev_attr_enable_wb_buf_flush.attr,
|
&dev_attr_enable_wb_buf_flush.attr,
|
||||||
&dev_attr_wb_flush_threshold.attr,
|
&dev_attr_wb_flush_threshold.attr,
|
||||||
|
&dev_attr_wb_resize_enable.attr,
|
||||||
&dev_attr_rtc_update_ms.attr,
|
&dev_attr_rtc_update_ms.attr,
|
||||||
&dev_attr_pm_qos_enable.attr,
|
&dev_attr_pm_qos_enable.attr,
|
||||||
&dev_attr_critical_health.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;
|
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) \
|
#define UFS_ATTRIBUTE(_name, _uname) \
|
||||||
static ssize_t _name##_show(struct device *dev, \
|
static ssize_t _name##_show(struct device *dev, \
|
||||||
struct device_attribute *attr, char *buf) \
|
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_avail_buf.attr,
|
||||||
&dev_attr_wb_life_time_est.attr,
|
&dev_attr_wb_life_time_est.attr,
|
||||||
&dev_attr_wb_cur_buf.attr,
|
&dev_attr_wb_cur_buf.attr,
|
||||||
|
&dev_attr_wb_resize_hint.attr,
|
||||||
|
&dev_attr_wb_resize_status.attr,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -6080,6 +6080,21 @@ int ufshcd_wb_toggle_buf_flush(struct ufs_hba *hba, bool enable)
|
|||||||
return ret;
|
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,
|
static bool ufshcd_wb_presrv_usrspc_keep_vcc_on(struct ufs_hba *hba,
|
||||||
u32 avail_buf)
|
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->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 =
|
dev_info->b_presrv_uspc_en =
|
||||||
desc_buf[DEVICE_DESC_PARAM_WB_PRESRV_USRSPC_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_AVAIL_WB_BUFF_SIZE = 0x1D,
|
||||||
QUERY_ATTR_IDN_WB_BUFF_LIFE_TIME_EST = 0x1E,
|
QUERY_ATTR_IDN_WB_BUFF_LIFE_TIME_EST = 0x1E,
|
||||||
QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE = 0x1F,
|
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 */
|
/* Descriptor idn for Query requests */
|
||||||
@@ -289,6 +292,7 @@ enum device_desc_param {
|
|||||||
DEVICE_DESC_PARAM_PRDCT_REV = 0x2A,
|
DEVICE_DESC_PARAM_PRDCT_REV = 0x2A,
|
||||||
DEVICE_DESC_PARAM_HPB_VER = 0x40,
|
DEVICE_DESC_PARAM_HPB_VER = 0x40,
|
||||||
DEVICE_DESC_PARAM_HPB_CONTROL = 0x42,
|
DEVICE_DESC_PARAM_HPB_CONTROL = 0x42,
|
||||||
|
DEVICE_DESC_PARAM_EXT_WB_SUP = 0x4D,
|
||||||
DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP = 0x4F,
|
DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP = 0x4F,
|
||||||
DEVICE_DESC_PARAM_WB_PRESRV_USRSPC_EN = 0x53,
|
DEVICE_DESC_PARAM_WB_PRESRV_USRSPC_EN = 0x53,
|
||||||
DEVICE_DESC_PARAM_WB_TYPE = 0x54,
|
DEVICE_DESC_PARAM_WB_TYPE = 0x54,
|
||||||
@@ -383,6 +387,11 @@ enum {
|
|||||||
UFSHCD_AMP = 3,
|
UFSHCD_AMP = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Possible values for wExtendedWriteBoosterSupport */
|
||||||
|
enum {
|
||||||
|
UFS_DEV_WB_BUF_RESIZE = BIT(0),
|
||||||
|
};
|
||||||
|
|
||||||
/* Possible values for dExtendedUFSFeaturesSupport */
|
/* Possible values for dExtendedUFSFeaturesSupport */
|
||||||
enum {
|
enum {
|
||||||
UFS_DEV_HIGH_TEMP_NOTIF = BIT(4),
|
UFS_DEV_HIGH_TEMP_NOTIF = BIT(4),
|
||||||
@@ -454,6 +463,28 @@ enum ufs_ref_clk_freq {
|
|||||||
REF_CLK_FREQ_INVAL = -1,
|
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 */
|
/* Query response result code */
|
||||||
enum {
|
enum {
|
||||||
QUERY_RESULT_SUCCESS = 0x00,
|
QUERY_RESULT_SUCCESS = 0x00,
|
||||||
@@ -578,6 +609,7 @@ struct ufs_dev_info {
|
|||||||
bool wb_buf_flush_enabled;
|
bool wb_buf_flush_enabled;
|
||||||
u8 wb_dedicated_lu;
|
u8 wb_dedicated_lu;
|
||||||
u8 wb_buffer_type;
|
u8 wb_buffer_type;
|
||||||
|
u16 ext_wb_sup;
|
||||||
|
|
||||||
bool b_rpm_dev_flush_capable;
|
bool b_rpm_dev_flush_capable;
|
||||||
u8 b_presrv_uspc_en;
|
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);
|
struct scatterlist *sg_list, enum dma_data_direction dir);
|
||||||
int ufshcd_wb_toggle(struct ufs_hba *hba, bool enable);
|
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_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);
|
||||||
int __ufshcd_suspend_prepare(struct device *dev, bool rpm_ok_for_spm);
|
int __ufshcd_suspend_prepare(struct device *dev, bool rpm_ok_for_spm);
|
||||||
void ufshcd_resume_complete(struct device *dev);
|
void ufshcd_resume_complete(struct device *dev);
|
||||||
|
|||||||
Reference in New Issue
Block a user