Commit a4b0e8a4 authored by Potomski, MichalX's avatar Potomski, MichalX Committed by Martin K. Petersen

scsi: ufs: Factor out ufshcd_read_desc_param

Since in UFS 2.1 specification some of the descriptor lengths differs
from 2.0 specification and some devices, which are reporting spec
version 2.0 have different descriptor lengths we can not rely on
hardcoded values taken from 2.0 specification. This patch introduces
reading these lengths per each device from descriptor headers at probe
time to ensure their correctness.
Signed-off-by: default avatarMichal' Potomski <michalx.potomski@intel.com>
Reviewed-by: default avatarSubhash Jadavani <subhashj@codeaurora.org>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 6f8830f5
...@@ -146,7 +146,7 @@ enum attr_idn { ...@@ -146,7 +146,7 @@ enum attr_idn {
/* Descriptor idn for Query requests */ /* Descriptor idn for Query requests */
enum desc_idn { enum desc_idn {
QUERY_DESC_IDN_DEVICE = 0x0, QUERY_DESC_IDN_DEVICE = 0x0,
QUERY_DESC_IDN_CONFIGURAION = 0x1, QUERY_DESC_IDN_CONFIGURATION = 0x1,
QUERY_DESC_IDN_UNIT = 0x2, QUERY_DESC_IDN_UNIT = 0x2,
QUERY_DESC_IDN_RFU_0 = 0x3, QUERY_DESC_IDN_RFU_0 = 0x3,
QUERY_DESC_IDN_INTERCONNECT = 0x4, QUERY_DESC_IDN_INTERCONNECT = 0x4,
...@@ -162,19 +162,13 @@ enum desc_header_offset { ...@@ -162,19 +162,13 @@ enum desc_header_offset {
QUERY_DESC_DESC_TYPE_OFFSET = 0x01, QUERY_DESC_DESC_TYPE_OFFSET = 0x01,
}; };
enum ufs_desc_max_size { enum ufs_desc_def_size {
QUERY_DESC_DEVICE_MAX_SIZE = 0x40, QUERY_DESC_DEVICE_DEF_SIZE = 0x40,
QUERY_DESC_CONFIGURAION_MAX_SIZE = 0x90, QUERY_DESC_CONFIGURATION_DEF_SIZE = 0x90,
QUERY_DESC_UNIT_MAX_SIZE = 0x23, QUERY_DESC_UNIT_DEF_SIZE = 0x23,
QUERY_DESC_INTERCONNECT_MAX_SIZE = 0x06, QUERY_DESC_INTERCONNECT_DEF_SIZE = 0x06,
/* QUERY_DESC_GEOMETRY_DEF_SIZE = 0x44,
* Max. 126 UNICODE characters (2 bytes per character) plus 2 bytes QUERY_DESC_POWER_DEF_SIZE = 0x62,
* of descriptor header.
*/
QUERY_DESC_STRING_MAX_SIZE = 0xFE,
QUERY_DESC_GEOMETRY_MAX_SIZE = 0x44,
QUERY_DESC_POWER_MAX_SIZE = 0x62,
QUERY_DESC_RFU_MAX_SIZE = 0x00,
}; };
/* Unit descriptor parameters offsets in bytes*/ /* Unit descriptor parameters offsets in bytes*/
......
...@@ -100,19 +100,6 @@ ...@@ -100,19 +100,6 @@
#define ufshcd_hex_dump(prefix_str, buf, len) \ #define ufshcd_hex_dump(prefix_str, buf, len) \
print_hex_dump(KERN_ERR, prefix_str, DUMP_PREFIX_OFFSET, 16, 4, buf, len, false) print_hex_dump(KERN_ERR, prefix_str, DUMP_PREFIX_OFFSET, 16, 4, buf, len, false)
static u32 ufs_query_desc_max_size[] = {
QUERY_DESC_DEVICE_MAX_SIZE,
QUERY_DESC_CONFIGURAION_MAX_SIZE,
QUERY_DESC_UNIT_MAX_SIZE,
QUERY_DESC_RFU_MAX_SIZE,
QUERY_DESC_INTERCONNECT_MAX_SIZE,
QUERY_DESC_STRING_MAX_SIZE,
QUERY_DESC_RFU_MAX_SIZE,
QUERY_DESC_GEOMETRY_MAX_SIZE,
QUERY_DESC_POWER_MAX_SIZE,
QUERY_DESC_RFU_MAX_SIZE,
};
enum { enum {
UFSHCD_MAX_CHANNEL = 0, UFSHCD_MAX_CHANNEL = 0,
UFSHCD_MAX_ID = 1, UFSHCD_MAX_ID = 1,
...@@ -2857,7 +2844,7 @@ static int __ufshcd_query_descriptor(struct ufs_hba *hba, ...@@ -2857,7 +2844,7 @@ static int __ufshcd_query_descriptor(struct ufs_hba *hba,
goto out; goto out;
} }
if (*buf_len <= QUERY_DESC_MIN_SIZE || *buf_len > QUERY_DESC_MAX_SIZE) { if (*buf_len < QUERY_DESC_MIN_SIZE || *buf_len > QUERY_DESC_MAX_SIZE) {
dev_err(hba->dev, "%s: descriptor buffer size (%d) is out of range\n", dev_err(hba->dev, "%s: descriptor buffer size (%d) is out of range\n",
__func__, *buf_len); __func__, *buf_len);
err = -EINVAL; err = -EINVAL;
...@@ -2937,6 +2924,92 @@ static int ufshcd_query_descriptor_retry(struct ufs_hba *hba, ...@@ -2937,6 +2924,92 @@ static int ufshcd_query_descriptor_retry(struct ufs_hba *hba,
return err; return err;
} }
/**
* ufshcd_read_desc_length - read the specified descriptor length from header
* @hba: Pointer to adapter instance
* @desc_id: descriptor idn value
* @desc_index: descriptor index
* @desc_length: pointer to variable to read the length of descriptor
*
* Return 0 in case of success, non-zero otherwise
*/
static int ufshcd_read_desc_length(struct ufs_hba *hba,
enum desc_idn desc_id,
int desc_index,
int *desc_length)
{
int ret;
u8 header[QUERY_DESC_HDR_SIZE];
int header_len = QUERY_DESC_HDR_SIZE;
if (desc_id >= QUERY_DESC_IDN_MAX)
return -EINVAL;
ret = ufshcd_query_descriptor_retry(hba, UPIU_QUERY_OPCODE_READ_DESC,
desc_id, desc_index, 0, header,
&header_len);
if (ret) {
dev_err(hba->dev, "%s: Failed to get descriptor header id %d",
__func__, desc_id);
return ret;
} else if (desc_id != header[QUERY_DESC_DESC_TYPE_OFFSET]) {
dev_warn(hba->dev, "%s: descriptor header id %d and desc_id %d mismatch",
__func__, header[QUERY_DESC_DESC_TYPE_OFFSET],
desc_id);
ret = -EINVAL;
}
*desc_length = header[QUERY_DESC_LENGTH_OFFSET];
return ret;
}
/**
* ufshcd_map_desc_id_to_length - map descriptor IDN to its length
* @hba: Pointer to adapter instance
* @desc_id: descriptor idn value
* @desc_len: mapped desc length (out)
*
* Return 0 in case of success, non-zero otherwise
*/
int ufshcd_map_desc_id_to_length(struct ufs_hba *hba,
enum desc_idn desc_id, int *desc_len)
{
switch (desc_id) {
case QUERY_DESC_IDN_DEVICE:
*desc_len = hba->desc_size.dev_desc;
break;
case QUERY_DESC_IDN_POWER:
*desc_len = hba->desc_size.pwr_desc;
break;
case QUERY_DESC_IDN_GEOMETRY:
*desc_len = hba->desc_size.geom_desc;
break;
case QUERY_DESC_IDN_CONFIGURATION:
*desc_len = hba->desc_size.conf_desc;
break;
case QUERY_DESC_IDN_UNIT:
*desc_len = hba->desc_size.unit_desc;
break;
case QUERY_DESC_IDN_INTERCONNECT:
*desc_len = hba->desc_size.interc_desc;
break;
case QUERY_DESC_IDN_STRING:
*desc_len = QUERY_DESC_MAX_SIZE;
break;
case QUERY_DESC_IDN_RFU_0:
case QUERY_DESC_IDN_RFU_1:
*desc_len = 0;
break;
default:
*desc_len = 0;
return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL(ufshcd_map_desc_id_to_length);
/** /**
* ufshcd_read_desc_param - read the specified descriptor parameter * ufshcd_read_desc_param - read the specified descriptor parameter
* @hba: Pointer to adapter instance * @hba: Pointer to adapter instance
...@@ -2951,42 +3024,49 @@ static int ufshcd_query_descriptor_retry(struct ufs_hba *hba, ...@@ -2951,42 +3024,49 @@ static int ufshcd_query_descriptor_retry(struct ufs_hba *hba,
static int ufshcd_read_desc_param(struct ufs_hba *hba, static int ufshcd_read_desc_param(struct ufs_hba *hba,
enum desc_idn desc_id, enum desc_idn desc_id,
int desc_index, int desc_index,
u32 param_offset, u8 param_offset,
u8 *param_read_buf, u8 *param_read_buf,
u32 param_size) u8 param_size)
{ {
int ret; int ret;
u8 *desc_buf; u8 *desc_buf;
u32 buff_len; int buff_len;
bool is_kmalloc = true; bool is_kmalloc = true;
/* safety checks */ /* Safety check */
if (desc_id >= QUERY_DESC_IDN_MAX) if (desc_id >= QUERY_DESC_IDN_MAX || !param_size)
return -EINVAL; return -EINVAL;
buff_len = ufs_query_desc_max_size[desc_id]; /* Get the max length of descriptor from structure filled up at probe
if ((param_offset + param_size) > buff_len) * time.
return -EINVAL; */
ret = ufshcd_map_desc_id_to_length(hba, desc_id, &buff_len);
if (!param_offset && (param_size == buff_len)) { /* Sanity checks */
/* memory space already available to hold full descriptor */ if (ret || !buff_len) {
desc_buf = param_read_buf; dev_err(hba->dev, "%s: Failed to get full descriptor length",
is_kmalloc = false; __func__);
} else { return ret;
/* allocate memory to hold full descriptor */ }
/* Check whether we need temp memory */
if (param_offset != 0 || param_size < buff_len) {
desc_buf = kmalloc(buff_len, GFP_KERNEL); desc_buf = kmalloc(buff_len, GFP_KERNEL);
if (!desc_buf) if (!desc_buf)
return -ENOMEM; return -ENOMEM;
} else {
desc_buf = param_read_buf;
is_kmalloc = false;
} }
/* Request for full descriptor */
ret = ufshcd_query_descriptor_retry(hba, UPIU_QUERY_OPCODE_READ_DESC, ret = ufshcd_query_descriptor_retry(hba, UPIU_QUERY_OPCODE_READ_DESC,
desc_id, desc_index, 0, desc_buf, desc_id, desc_index, 0,
&buff_len); desc_buf, &buff_len);
if (ret) { if (ret) {
dev_err(hba->dev, "%s: Failed reading descriptor. desc_id %d, desc_index %d, param_offset %d, ret %d", dev_err(hba->dev, "%s: Failed reading descriptor. desc_id %d, desc_index %d, param_offset %d, ret %d",
__func__, desc_id, desc_index, param_offset, ret); __func__, desc_id, desc_index, param_offset, ret);
goto out; goto out;
} }
...@@ -2998,25 +3078,9 @@ static int ufshcd_read_desc_param(struct ufs_hba *hba, ...@@ -2998,25 +3078,9 @@ static int ufshcd_read_desc_param(struct ufs_hba *hba,
goto out; goto out;
} }
/* /* Check wherher we will not copy more data, than available */
* While reading variable size descriptors (like string descriptor), if (is_kmalloc && param_size > buff_len)
* some UFS devices may report the "LENGTH" (field in "Transaction param_size = buff_len;
* Specific fields" of Query Response UPIU) same as what was requested
* in Query Request UPIU instead of reporting the actual size of the
* variable size descriptor.
* Although it's safe to ignore the "LENGTH" field for variable size
* descriptors as we can always derive the length of the descriptor from
* the descriptor header fields. Hence this change impose the length
* match check only for fixed size descriptors (for which we always
* request the correct size as part of Query Request UPIU).
*/
if ((desc_id != QUERY_DESC_IDN_STRING) &&
(buff_len != desc_buf[QUERY_DESC_LENGTH_OFFSET])) {
dev_err(hba->dev, "%s: desc_buf length mismatch: buff_len %d, buff_len(desc_header) %d",
__func__, buff_len, desc_buf[QUERY_DESC_LENGTH_OFFSET]);
ret = -EINVAL;
goto out;
}
if (is_kmalloc) if (is_kmalloc)
memcpy(param_read_buf, &desc_buf[param_offset], param_size); memcpy(param_read_buf, &desc_buf[param_offset], param_size);
...@@ -5919,8 +5983,8 @@ static int ufshcd_set_icc_levels_attr(struct ufs_hba *hba, u32 icc_level) ...@@ -5919,8 +5983,8 @@ static int ufshcd_set_icc_levels_attr(struct ufs_hba *hba, u32 icc_level)
static void ufshcd_init_icc_levels(struct ufs_hba *hba) static void ufshcd_init_icc_levels(struct ufs_hba *hba)
{ {
int ret; int ret;
int buff_len = QUERY_DESC_POWER_MAX_SIZE; int buff_len = hba->desc_size.pwr_desc;
u8 desc_buf[QUERY_DESC_POWER_MAX_SIZE]; u8 desc_buf[hba->desc_size.pwr_desc];
ret = ufshcd_read_power_desc(hba, desc_buf, buff_len); ret = ufshcd_read_power_desc(hba, desc_buf, buff_len);
if (ret) { if (ret) {
...@@ -6017,11 +6081,10 @@ static int ufs_get_device_desc(struct ufs_hba *hba, ...@@ -6017,11 +6081,10 @@ static int ufs_get_device_desc(struct ufs_hba *hba,
{ {
int err; int err;
u8 model_index; u8 model_index;
u8 str_desc_buf[QUERY_DESC_STRING_MAX_SIZE + 1] = {0}; u8 str_desc_buf[QUERY_DESC_MAX_SIZE + 1] = {0};
u8 desc_buf[QUERY_DESC_DEVICE_MAX_SIZE]; u8 desc_buf[hba->desc_size.dev_desc];
err = ufshcd_read_device_desc(hba, desc_buf, err = ufshcd_read_device_desc(hba, desc_buf, hba->desc_size.dev_desc);
QUERY_DESC_DEVICE_MAX_SIZE);
if (err) { if (err) {
dev_err(hba->dev, "%s: Failed reading Device Desc. err = %d\n", dev_err(hba->dev, "%s: Failed reading Device Desc. err = %d\n",
__func__, err); __func__, err);
...@@ -6038,14 +6101,14 @@ static int ufs_get_device_desc(struct ufs_hba *hba, ...@@ -6038,14 +6101,14 @@ static int ufs_get_device_desc(struct ufs_hba *hba,
model_index = desc_buf[DEVICE_DESC_PARAM_PRDCT_NAME]; model_index = desc_buf[DEVICE_DESC_PARAM_PRDCT_NAME];
err = ufshcd_read_string_desc(hba, model_index, str_desc_buf, err = ufshcd_read_string_desc(hba, model_index, str_desc_buf,
QUERY_DESC_STRING_MAX_SIZE, ASCII_STD); QUERY_DESC_MAX_SIZE, ASCII_STD);
if (err) { if (err) {
dev_err(hba->dev, "%s: Failed reading Product Name. err = %d\n", dev_err(hba->dev, "%s: Failed reading Product Name. err = %d\n",
__func__, err); __func__, err);
goto out; goto out;
} }
str_desc_buf[QUERY_DESC_STRING_MAX_SIZE] = '\0'; str_desc_buf[QUERY_DESC_MAX_SIZE] = '\0';
strlcpy(dev_desc->model, (str_desc_buf + QUERY_DESC_HDR_SIZE), strlcpy(dev_desc->model, (str_desc_buf + QUERY_DESC_HDR_SIZE),
min_t(u8, str_desc_buf[QUERY_DESC_LENGTH_OFFSET], min_t(u8, str_desc_buf[QUERY_DESC_LENGTH_OFFSET],
MAX_MODEL_LEN)); MAX_MODEL_LEN));
...@@ -6251,6 +6314,51 @@ static void ufshcd_clear_dbg_ufs_stats(struct ufs_hba *hba) ...@@ -6251,6 +6314,51 @@ static void ufshcd_clear_dbg_ufs_stats(struct ufs_hba *hba)
hba->req_abort_count = 0; hba->req_abort_count = 0;
} }
static void ufshcd_init_desc_sizes(struct ufs_hba *hba)
{
int err;
err = ufshcd_read_desc_length(hba, QUERY_DESC_IDN_DEVICE, 0,
&hba->desc_size.dev_desc);
if (err)
hba->desc_size.dev_desc = QUERY_DESC_DEVICE_DEF_SIZE;
err = ufshcd_read_desc_length(hba, QUERY_DESC_IDN_POWER, 0,
&hba->desc_size.pwr_desc);
if (err)
hba->desc_size.pwr_desc = QUERY_DESC_POWER_DEF_SIZE;
err = ufshcd_read_desc_length(hba, QUERY_DESC_IDN_INTERCONNECT, 0,
&hba->desc_size.interc_desc);
if (err)
hba->desc_size.interc_desc = QUERY_DESC_INTERCONNECT_DEF_SIZE;
err = ufshcd_read_desc_length(hba, QUERY_DESC_IDN_CONFIGURATION, 0,
&hba->desc_size.conf_desc);
if (err)
hba->desc_size.conf_desc = QUERY_DESC_CONFIGURATION_DEF_SIZE;
err = ufshcd_read_desc_length(hba, QUERY_DESC_IDN_UNIT, 0,
&hba->desc_size.unit_desc);
if (err)
hba->desc_size.unit_desc = QUERY_DESC_UNIT_DEF_SIZE;
err = ufshcd_read_desc_length(hba, QUERY_DESC_IDN_GEOMETRY, 0,
&hba->desc_size.geom_desc);
if (err)
hba->desc_size.geom_desc = QUERY_DESC_GEOMETRY_DEF_SIZE;
}
static void ufshcd_def_desc_sizes(struct ufs_hba *hba)
{
hba->desc_size.dev_desc = QUERY_DESC_DEVICE_DEF_SIZE;
hba->desc_size.pwr_desc = QUERY_DESC_POWER_DEF_SIZE;
hba->desc_size.interc_desc = QUERY_DESC_INTERCONNECT_DEF_SIZE;
hba->desc_size.conf_desc = QUERY_DESC_CONFIGURATION_DEF_SIZE;
hba->desc_size.unit_desc = QUERY_DESC_UNIT_DEF_SIZE;
hba->desc_size.geom_desc = QUERY_DESC_GEOMETRY_DEF_SIZE;
}
/** /**
* ufshcd_probe_hba - probe hba to detect device and initialize * ufshcd_probe_hba - probe hba to detect device and initialize
* @hba: per-adapter instance * @hba: per-adapter instance
...@@ -6285,6 +6393,9 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) ...@@ -6285,6 +6393,9 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
if (ret) if (ret)
goto out; goto out;
/* Init check for device descriptor sizes */
ufshcd_init_desc_sizes(hba);
ret = ufs_get_device_desc(hba, &card); ret = ufs_get_device_desc(hba, &card);
if (ret) { if (ret) {
dev_err(hba->dev, "%s: Failed getting device info. err = %d\n", dev_err(hba->dev, "%s: Failed getting device info. err = %d\n",
...@@ -6320,6 +6431,7 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) ...@@ -6320,6 +6431,7 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
/* set the state as operational after switching to desired gear */ /* set the state as operational after switching to desired gear */
hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL; hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;
/* /*
* If we are in error handling context or in power management callbacks * If we are in error handling context or in power management callbacks
* context, no need to scan the host * context, no need to scan the host
...@@ -7774,6 +7886,9 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) ...@@ -7774,6 +7886,9 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
hba->mmio_base = mmio_base; hba->mmio_base = mmio_base;
hba->irq = irq; hba->irq = irq;
/* Set descriptor lengths to specification defaults */
ufshcd_def_desc_sizes(hba);
err = ufshcd_hba_init(hba); err = ufshcd_hba_init(hba);
if (err) if (err)
goto out_error; goto out_error;
......
...@@ -220,6 +220,15 @@ struct ufs_dev_cmd { ...@@ -220,6 +220,15 @@ struct ufs_dev_cmd {
struct ufs_query query; struct ufs_query query;
}; };
struct ufs_desc_size {
int dev_desc;
int pwr_desc;
int geom_desc;
int interc_desc;
int unit_desc;
int conf_desc;
};
/** /**
* struct ufs_clk_info - UFS clock related info * struct ufs_clk_info - UFS clock related info
* @list: list headed by hba->clk_list_head * @list: list headed by hba->clk_list_head
...@@ -483,6 +492,7 @@ struct ufs_stats { ...@@ -483,6 +492,7 @@ struct ufs_stats {
* @clk_list_head: UFS host controller clocks list node head * @clk_list_head: UFS host controller clocks list node head
* @pwr_info: holds current power mode * @pwr_info: holds current power mode
* @max_pwr_info: keeps the device max valid pwm * @max_pwr_info: keeps the device max valid pwm
* @desc_size: descriptor sizes reported by device
* @urgent_bkops_lvl: keeps track of urgent bkops level for device * @urgent_bkops_lvl: keeps track of urgent bkops level for device
* @is_urgent_bkops_lvl_checked: keeps track if the urgent bkops level for * @is_urgent_bkops_lvl_checked: keeps track if the urgent bkops level for
* device is known or not. * device is known or not.
...@@ -666,6 +676,7 @@ struct ufs_hba { ...@@ -666,6 +676,7 @@ struct ufs_hba {
bool is_urgent_bkops_lvl_checked; bool is_urgent_bkops_lvl_checked;
struct rw_semaphore clk_scaling_lock; struct rw_semaphore clk_scaling_lock;
struct ufs_desc_size desc_size;
}; };
/* Returns true if clocks can be gated. Otherwise false */ /* Returns true if clocks can be gated. Otherwise false */
...@@ -832,6 +843,10 @@ int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode, ...@@ -832,6 +843,10 @@ int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode,
enum flag_idn idn, bool *flag_res); enum flag_idn idn, bool *flag_res);
int ufshcd_hold(struct ufs_hba *hba, bool async); int ufshcd_hold(struct ufs_hba *hba, bool async);
void ufshcd_release(struct ufs_hba *hba); void ufshcd_release(struct ufs_hba *hba);
int ufshcd_map_desc_id_to_length(struct ufs_hba *hba, enum desc_idn desc_id,
int *desc_length);
u32 ufshcd_get_local_unipro_ver(struct ufs_hba *hba); u32 ufshcd_get_local_unipro_ver(struct ufs_hba *hba);
/* Wrapper functions for safely calling variant operations */ /* Wrapper functions for safely calling variant operations */
......
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