Skip to content

Commit

Permalink
scsi: ufs: Factor out ufshcd_read_desc_param
Browse files Browse the repository at this point in the history
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: Michal' Potomski <michalx.potomski@intel.com>
Reviewed-by: Subhash Jadavani <subhashj@codeaurora.org>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
  • Loading branch information
Potomski, MichalX authored and Martin K. Petersen committed Mar 2, 2017
1 parent 6f8830f commit a4b0e8a
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 72 deletions.
22 changes: 8 additions & 14 deletions drivers/scsi/ufs/ufs.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ enum attr_idn {
/* Descriptor idn for Query requests */
enum desc_idn {
QUERY_DESC_IDN_DEVICE = 0x0,
QUERY_DESC_IDN_CONFIGURAION = 0x1,
QUERY_DESC_IDN_CONFIGURATION = 0x1,
QUERY_DESC_IDN_UNIT = 0x2,
QUERY_DESC_IDN_RFU_0 = 0x3,
QUERY_DESC_IDN_INTERCONNECT = 0x4,
Expand All @@ -162,19 +162,13 @@ enum desc_header_offset {
QUERY_DESC_DESC_TYPE_OFFSET = 0x01,
};

enum ufs_desc_max_size {
QUERY_DESC_DEVICE_MAX_SIZE = 0x40,
QUERY_DESC_CONFIGURAION_MAX_SIZE = 0x90,
QUERY_DESC_UNIT_MAX_SIZE = 0x23,
QUERY_DESC_INTERCONNECT_MAX_SIZE = 0x06,
/*
* Max. 126 UNICODE characters (2 bytes per character) plus 2 bytes
* 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,
enum ufs_desc_def_size {
QUERY_DESC_DEVICE_DEF_SIZE = 0x40,
QUERY_DESC_CONFIGURATION_DEF_SIZE = 0x90,
QUERY_DESC_UNIT_DEF_SIZE = 0x23,
QUERY_DESC_INTERCONNECT_DEF_SIZE = 0x06,
QUERY_DESC_GEOMETRY_DEF_SIZE = 0x44,
QUERY_DESC_POWER_DEF_SIZE = 0x62,
};

/* Unit descriptor parameters offsets in bytes*/
Expand Down
231 changes: 173 additions & 58 deletions drivers/scsi/ufs/ufshcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,19 +100,6 @@
#define ufshcd_hex_dump(prefix_str, buf, len) \
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 {
UFSHCD_MAX_CHANNEL = 0,
UFSHCD_MAX_ID = 1,
Expand Down Expand Up @@ -2857,7 +2844,7 @@ static int __ufshcd_query_descriptor(struct ufs_hba *hba,
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",
__func__, *buf_len);
err = -EINVAL;
Expand Down Expand Up @@ -2937,6 +2924,92 @@ static int ufshcd_query_descriptor_retry(struct ufs_hba *hba,
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
* @hba: Pointer to adapter instance
Expand All @@ -2951,42 +3024,49 @@ static int ufshcd_query_descriptor_retry(struct ufs_hba *hba,
static int ufshcd_read_desc_param(struct ufs_hba *hba,
enum desc_idn desc_id,
int desc_index,
u32 param_offset,
u8 param_offset,
u8 *param_read_buf,
u32 param_size)
u8 param_size)
{
int ret;
u8 *desc_buf;
u32 buff_len;
int buff_len;
bool is_kmalloc = true;

/* safety checks */
if (desc_id >= QUERY_DESC_IDN_MAX)
/* Safety check */
if (desc_id >= QUERY_DESC_IDN_MAX || !param_size)
return -EINVAL;

buff_len = ufs_query_desc_max_size[desc_id];
if ((param_offset + param_size) > buff_len)
return -EINVAL;
/* Get the max length of descriptor from structure filled up at probe
* time.
*/
ret = ufshcd_map_desc_id_to_length(hba, desc_id, &buff_len);

if (!param_offset && (param_size == buff_len)) {
/* memory space already available to hold full descriptor */
desc_buf = param_read_buf;
is_kmalloc = false;
} else {
/* allocate memory to hold full descriptor */
/* Sanity checks */
if (ret || !buff_len) {
dev_err(hba->dev, "%s: Failed to get full descriptor length",
__func__);
return ret;
}

/* Check whether we need temp memory */
if (param_offset != 0 || param_size < buff_len) {
desc_buf = kmalloc(buff_len, GFP_KERNEL);
if (!desc_buf)
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,
desc_id, desc_index, 0, desc_buf,
&buff_len);
desc_id, desc_index, 0,
desc_buf, &buff_len);

if (ret) {
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);

goto out;
}

Expand All @@ -2998,25 +3078,9 @@ static int ufshcd_read_desc_param(struct ufs_hba *hba,
goto out;
}

/*
* While reading variable size descriptors (like string descriptor),
* some UFS devices may report the "LENGTH" (field in "Transaction
* 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;
}
/* Check wherher we will not copy more data, than available */
if (is_kmalloc && param_size > buff_len)
param_size = buff_len;

if (is_kmalloc)
memcpy(param_read_buf, &desc_buf[param_offset], param_size);
Expand Down Expand Up @@ -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)
{
int ret;
int buff_len = QUERY_DESC_POWER_MAX_SIZE;
u8 desc_buf[QUERY_DESC_POWER_MAX_SIZE];
int buff_len = hba->desc_size.pwr_desc;
u8 desc_buf[hba->desc_size.pwr_desc];

ret = ufshcd_read_power_desc(hba, desc_buf, buff_len);
if (ret) {
Expand Down Expand Up @@ -6017,11 +6081,10 @@ static int ufs_get_device_desc(struct ufs_hba *hba,
{
int err;
u8 model_index;
u8 str_desc_buf[QUERY_DESC_STRING_MAX_SIZE + 1] = {0};
u8 desc_buf[QUERY_DESC_DEVICE_MAX_SIZE];
u8 str_desc_buf[QUERY_DESC_MAX_SIZE + 1] = {0};
u8 desc_buf[hba->desc_size.dev_desc];

err = ufshcd_read_device_desc(hba, desc_buf,
QUERY_DESC_DEVICE_MAX_SIZE);
err = ufshcd_read_device_desc(hba, desc_buf, hba->desc_size.dev_desc);
if (err) {
dev_err(hba->dev, "%s: Failed reading Device Desc. err = %d\n",
__func__, err);
Expand All @@ -6038,14 +6101,14 @@ static int ufs_get_device_desc(struct ufs_hba *hba,
model_index = desc_buf[DEVICE_DESC_PARAM_PRDCT_NAME];

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) {
dev_err(hba->dev, "%s: Failed reading Product Name. err = %d\n",
__func__, err);
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),
min_t(u8, str_desc_buf[QUERY_DESC_LENGTH_OFFSET],
MAX_MODEL_LEN));
Expand Down Expand Up @@ -6251,6 +6314,51 @@ static void ufshcd_clear_dbg_ufs_stats(struct ufs_hba *hba)
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
* @hba: per-adapter instance
Expand Down Expand Up @@ -6285,6 +6393,9 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
if (ret)
goto out;

/* Init check for device descriptor sizes */
ufshcd_init_desc_sizes(hba);

ret = ufs_get_device_desc(hba, &card);
if (ret) {
dev_err(hba->dev, "%s: Failed getting device info. err = %d\n",
Expand Down Expand Up @@ -6320,6 +6431,7 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)

/* set the state as operational after switching to desired gear */
hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;

/*
* If we are in error handling context or in power management callbacks
* context, no need to scan the host
Expand Down Expand Up @@ -7774,6 +7886,9 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
hba->mmio_base = mmio_base;
hba->irq = irq;

/* Set descriptor lengths to specification defaults */
ufshcd_def_desc_sizes(hba);

err = ufshcd_hba_init(hba);
if (err)
goto out_error;
Expand Down
Loading

0 comments on commit a4b0e8a

Please sign in to comment.