Commit 6f8d5a6a authored by Stanley Chu's avatar Stanley Chu Committed by Martin K. Petersen

scsi: ufs: Add LU Dedicated buffer mode support for WriteBooster

According to UFS specification, there are two WriteBooster mode of
operations: "LU dedicated buffer" mode and "shared buffer" mode.  In the
"LU dedicated buffer" mode, the WriteBooster Buffer is dedicated to a
logical unit.

If the device supports the "LU dedicated buffer" mode, this mode is
configured by setting bWriteBoosterBufferType to 00h. The logical unit
WriteBooster Buffer size is configured by setting the
dLUNumWriteBoosterBufferAllocUnits field of the related Unit
Descriptor. Only a value greater than zero enables the WriteBooster feature
in the logical unit.

Modify ufshcd_wb_probe() as above description to support LU Dedicated
buffer mode.

Note that according to UFS 3.1 specification, the valid value of
bDeviceMaxWriteBoosterLUs parameter in Geometry Descriptor is 1, which
means at most one LUN can have WriteBooster buffer in "LU dedicated buffer
mode". Therefore this patch supports only one LUN with WriteBooster
enabled. All WriteBooster related sysfs nodes are specifically mapped to
the LUN with WriteBooster enabled in LU Dedicated buffer mode.

Link: https://lore.kernel.org/r/20200508080115.24233-7-stanley.chu@mediatek.comReviewed-by: default avatarAvri Altman <avri.altman@wdc.com>
Reviewed-by: default avatarBean Huo <beanhuo@micron.com>
Reviewed-by: default avatarAsutosh Das <asutoshd@codeaurora.org>
Signed-off-by: default avatarStanley Chu <stanley.chu@mediatek.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 1f34eedf
......@@ -622,16 +622,25 @@ static const struct attribute_group ufs_sysfs_string_descriptors_group = {
.attrs = ufs_sysfs_string_descriptors,
};
static inline bool ufshcd_is_wb_flags(enum flag_idn idn)
{
return ((idn >= QUERY_FLAG_IDN_WB_EN) &&
(idn <= QUERY_FLAG_IDN_WB_BUFF_FLUSH_DURING_HIBERN8));
}
#define UFS_FLAG(_name, _uname) \
static ssize_t _name##_show(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
bool flag; \
u8 index = 0; \
int ret; \
struct ufs_hba *hba = dev_get_drvdata(dev); \
if (ufshcd_is_wb_flags(QUERY_FLAG_IDN##_uname)) \
index = ufshcd_wb_get_flag_index(hba); \
pm_runtime_get_sync(hba->dev); \
ret = ufshcd_query_flag(hba, UPIU_QUERY_OPCODE_READ_FLAG, \
QUERY_FLAG_IDN##_uname, 0, &flag); \
QUERY_FLAG_IDN##_uname, index, &flag); \
pm_runtime_put_sync(hba->dev); \
if (ret) \
return -EINVAL; \
......
......@@ -64,6 +64,9 @@
#define UFS_MAX_LUNS (SCSI_W_LUN_BASE + UFS_UPIU_MAX_UNIT_NUM_ID)
#define UFS_UPIU_WLUN_ID (1 << 7)
/* WriteBooster buffer is available only for the logical unit from 0 to 7 */
#define UFS_UPIU_MAX_WB_LUN_ID 8
/* Well known logical unit id in LUN field of UPIU */
enum {
UFS_UPIU_REPORT_LUNS_WLUN = 0x81,
......@@ -330,6 +333,12 @@ enum health_desc_param {
HEALTH_DESC_PARAM_LIFE_TIME_EST_B = 0x4,
};
/* WriteBooster buffer mode */
enum {
WB_BUF_MODE_LU_DEDICATED = 0x0,
WB_BUF_MODE_SHARED = 0x1,
};
/*
* Logical Unit Write Protect
* 00h: LU not write protected
......@@ -559,6 +568,7 @@ struct ufs_dev_info {
bool is_lu_power_on_wp;
/* Maximum number of general LU supported by the UFS device */
u8 max_lu_supported;
u8 wb_dedicated_lu;
u16 wmanufacturerid;
/*UFS device Product Name */
u8 *model;
......
......@@ -5205,6 +5205,7 @@ static bool ufshcd_wb_sup(struct ufs_hba *hba)
static int ufshcd_wb_ctrl(struct ufs_hba *hba, bool enable)
{
int ret;
u8 index;
enum query_opcode opcode;
if (!ufshcd_wb_sup(hba))
......@@ -5217,8 +5218,9 @@ static int ufshcd_wb_ctrl(struct ufs_hba *hba, bool enable)
else
opcode = UPIU_QUERY_OPCODE_CLEAR_FLAG;
index = ufshcd_wb_get_flag_index(hba);
ret = ufshcd_query_flag_retry(hba, opcode,
QUERY_FLAG_IDN_WB_EN, 0, NULL);
QUERY_FLAG_IDN_WB_EN, index, NULL);
if (ret) {
dev_err(hba->dev, "%s write booster %s failed %d\n",
__func__, enable ? "enable" : "disable", ret);
......@@ -5235,15 +5237,17 @@ static int ufshcd_wb_ctrl(struct ufs_hba *hba, bool enable)
static int ufshcd_wb_toggle_flush_during_h8(struct ufs_hba *hba, bool set)
{
int val;
u8 index;
if (set)
val = UPIU_QUERY_OPCODE_SET_FLAG;
else
val = UPIU_QUERY_OPCODE_CLEAR_FLAG;
index = ufshcd_wb_get_flag_index(hba);
return ufshcd_query_flag_retry(hba, val,
QUERY_FLAG_IDN_WB_BUFF_FLUSH_DURING_HIBERN8, 0,
NULL);
QUERY_FLAG_IDN_WB_BUFF_FLUSH_DURING_HIBERN8,
index, NULL);
}
static inline void ufshcd_wb_toggle_flush(struct ufs_hba *hba, bool enable)
......@@ -5258,13 +5262,15 @@ static inline void ufshcd_wb_toggle_flush(struct ufs_hba *hba, bool enable)
static int ufshcd_wb_buf_flush_enable(struct ufs_hba *hba)
{
int ret;
u8 index;
if (!ufshcd_wb_sup(hba) || hba->wb_buf_flush_enabled)
return 0;
index = ufshcd_wb_get_flag_index(hba);
ret = ufshcd_query_flag_retry(hba, UPIU_QUERY_OPCODE_SET_FLAG,
QUERY_FLAG_IDN_WB_BUFF_FLUSH_EN,
0, NULL);
index, NULL);
if (ret)
dev_err(hba->dev, "%s WB - buf flush enable failed %d\n",
__func__, ret);
......@@ -5278,12 +5284,15 @@ static int ufshcd_wb_buf_flush_enable(struct ufs_hba *hba)
static int ufshcd_wb_buf_flush_disable(struct ufs_hba *hba)
{
int ret;
u8 index;
if (!ufshcd_wb_sup(hba) || !hba->wb_buf_flush_enabled)
return 0;
index = ufshcd_wb_get_flag_index(hba);
ret = ufshcd_query_flag_retry(hba, UPIU_QUERY_OPCODE_CLEAR_FLAG,
QUERY_FLAG_IDN_WB_BUFF_FLUSH_EN, 0, NULL);
QUERY_FLAG_IDN_WB_BUFF_FLUSH_EN,
index, NULL);
if (ret) {
dev_warn(hba->dev, "%s: WB - buf flush disable failed %d\n",
__func__, ret);
......@@ -6802,6 +6811,9 @@ static int ufshcd_scsi_add_wlus(struct ufs_hba *hba)
static void ufshcd_wb_probe(struct ufs_hba *hba, u8 *desc_buf)
{
u8 lun;
u32 d_lu_wb_buf_alloc;
if (!ufshcd_is_wb_allowed(hba))
return;
......@@ -6824,16 +6836,32 @@ static void ufshcd_wb_probe(struct ufs_hba *hba, u8 *desc_buf)
hba->dev_info.b_wb_buffer_type =
desc_buf[DEVICE_DESC_PARAM_WB_TYPE];
hba->dev_info.d_wb_alloc_units =
get_unaligned_be32(desc_buf +
DEVICE_DESC_PARAM_WB_SHARED_ALLOC_UNITS);
hba->dev_info.b_presrv_uspc_en =
desc_buf[DEVICE_DESC_PARAM_WB_PRESRV_USRSPC_EN];
if (!(hba->dev_info.b_wb_buffer_type &&
hba->dev_info.d_wb_alloc_units))
goto wb_disabled;
if (hba->dev_info.b_wb_buffer_type == WB_BUF_MODE_SHARED) {
hba->dev_info.d_wb_alloc_units =
get_unaligned_be32(desc_buf +
DEVICE_DESC_PARAM_WB_SHARED_ALLOC_UNITS);
if (!hba->dev_info.d_wb_alloc_units)
goto wb_disabled;
} else {
for (lun = 0; lun < UFS_UPIU_MAX_WB_LUN_ID; lun++) {
d_lu_wb_buf_alloc = 0;
ufshcd_read_unit_desc_param(hba,
lun,
UNIT_DESC_PARAM_WB_BUF_ALLOC_UNITS,
(u8 *)&d_lu_wb_buf_alloc,
sizeof(d_lu_wb_buf_alloc));
if (d_lu_wb_buf_alloc) {
hba->dev_info.wb_dedicated_lu = lun;
break;
}
}
if (!d_lu_wb_buf_alloc)
goto wb_disabled;
}
return;
wb_disabled:
......
......@@ -861,6 +861,13 @@ static inline bool ufshcd_keep_autobkops_enabled_except_suspend(
return hba->caps & UFSHCD_CAP_KEEP_AUTO_BKOPS_ENABLED_EXCEPT_SUSPEND;
}
static inline u8 ufshcd_wb_get_flag_index(struct ufs_hba *hba)
{
if (hba->dev_info.b_wb_buffer_type == WB_BUF_MODE_LU_DEDICATED)
return hba->dev_info.wb_dedicated_lu;
return 0;
}
extern int ufshcd_runtime_suspend(struct ufs_hba *hba);
extern int ufshcd_runtime_resume(struct ufs_hba *hba);
extern int ufshcd_runtime_idle(struct ufs_hba *hba);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment