Loading drivers/scsi/ufs/ufs.h +26 −0 Original line number Diff line number Diff line Loading @@ -113,6 +113,7 @@ enum flag_idn { /* Attribute idn for Query requests */ enum attr_idn { QUERY_ATTR_IDN_ACTIVE_ICC_LVL = 0x03, QUERY_ATTR_IDN_BKOPS_STATUS = 0x05, QUERY_ATTR_IDN_EE_CONTROL = 0x0D, QUERY_ATTR_IDN_EE_STATUS = 0x0E, Loading Loading @@ -153,6 +154,31 @@ enum unit_desc_param { UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1 = 0x22, }; /* bActiveICCLevel parameter current units */ enum { UFSHCD_NANO_AMP = 0, UFSHCD_MICRO_AMP = 1, UFSHCD_MILI_AMP = 2, UFSHCD_AMP = 3, }; #define POWER_DESC_MAX_SIZE 0x62 #define POWER_DESC_MAX_ACTV_ICC_LVLS 16 /* Attribute bActiveICCLevel parameter bit masks definitions */ #define ATTR_ICC_LVL_UNIT_OFFSET 14 #define ATTR_ICC_LVL_UNIT_MASK (0x3 << ATTR_ICC_LVL_UNIT_OFFSET) #define ATTR_ICC_LVL_VALUE_MASK 0x3FF /* Power descriptor parameters offsets in bytes */ enum power_desc_param_offset { PWR_DESC_LEN = 0x0, PWR_DESC_TYPE = 0x1, PWR_DESC_ACTIVE_LVLS_VCC_0 = 0x2, PWR_DESC_ACTIVE_LVLS_VCCQ_0 = 0x22, PWR_DESC_ACTIVE_LVLS_VCCQ2_0 = 0x42, }; /* Exception event mask values */ enum { MASK_EE_STATUS = 0xFFFF, Loading drivers/scsi/ufs/ufshcd.c +113 −0 Original line number Diff line number Diff line Loading @@ -3188,6 +3188,118 @@ out: return ret; } /** * ufshcd_get_max_icc_level - calculate the ICC level * @sup_curr_uA: max. current supported by the regulator * @start_scan: row at the desc table to start scan from * @buff: power descriptor buffer * * Returns calculated max ICC level for specific regulator */ static u8 ufshcd_get_max_icc_level(int sup_curr_uA, u8 start_scan, char *buff) { u8 i; int curr_uA; u16 data; u16 unit; for (i = start_scan; i >= 0; i--) { data = be16_to_cpu(*((u16 *)(buff + 2*i))); unit = (data & ATTR_ICC_LVL_UNIT_MASK) >> ATTR_ICC_LVL_UNIT_OFFSET; curr_uA = data & ATTR_ICC_LVL_VALUE_MASK; switch (unit) { case UFSHCD_NANO_AMP: curr_uA = curr_uA / 1000; break; case UFSHCD_MILI_AMP: curr_uA = curr_uA * 1000; break; case UFSHCD_AMP: curr_uA = curr_uA * 1000 * 1000; break; case UFSHCD_MICRO_AMP: default: break; } if (sup_curr_uA >= curr_uA) break; } return i; } /** * ufshcd_calc_icc_level - calculate the max ICC level * In case regulators are not initialized we'll return 0 * @hba: per-adapter instance * @desc_buf: power descriptor buffer to extract ICC levels from. * @len: length of desc_buff * * Returns calculated ICC level */ static u8 ufshcd_find_max_sup_active_icc_level(struct ufs_hba *hba, u8 *desc_buf, int len) { u8 icc_level = 0; if (!hba->vreg_info.vcc || !hba->vreg_info.vccq || !hba->vreg_info.vccq2) { dev_err(hba->dev, "%s: Regulator capability was not set, actvIccLevel=%d", __func__, icc_level); goto out; } if (hba->vreg_info.vcc) icc_level = ufshcd_get_max_icc_level( hba->vreg_info.vcc->max_uA, POWER_DESC_MAX_ACTV_ICC_LVLS - 1, &desc_buf[PWR_DESC_ACTIVE_LVLS_VCC_0]); if (hba->vreg_info.vccq) icc_level = ufshcd_get_max_icc_level( hba->vreg_info.vccq->max_uA, icc_level, &desc_buf[PWR_DESC_ACTIVE_LVLS_VCCQ_0]); if (hba->vreg_info.vccq2) icc_level = ufshcd_get_max_icc_level( hba->vreg_info.vccq2->max_uA, icc_level, &desc_buf[PWR_DESC_ACTIVE_LVLS_VCCQ2_0]); out: return icc_level; } static void ufshcd_init_icc_levels(struct ufs_hba *hba) { u32 icc_level; int ret; int buff_len = POWER_DESC_MAX_SIZE; u8 desc_buf[POWER_DESC_MAX_SIZE]; ret = ufshcd_query_descriptor(hba, UPIU_QUERY_OPCODE_READ_DESC, QUERY_DESC_IDN_POWER, 0, 0, desc_buf, &buff_len); if (ret || (buff_len < POWER_DESC_MAX_SIZE)) { dev_err(hba->dev, "%s: Failed reading power descriptor.len = %d ret = %d", __func__, buff_len, ret); return; } icc_level = ufshcd_find_max_sup_active_icc_level(hba, desc_buf, buff_len); ret = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_WRITE_ATTR, QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, &icc_level); if (ret) dev_err(hba->dev, "%s: Failed configuring bActiveICCLevel = %d ret = %d", __func__, icc_level , ret); } /** * ufshcd_async_scan - asynchronous execution for link startup * @data: data pointer to pass to this function Loading Loading @@ -3217,6 +3329,7 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie) /* If we are in error handling context no need to scan the host */ if (!ufshcd_eh_in_progress(hba)) { ufshcd_init_icc_levels(hba); scsi_scan_host(hba->host); pm_runtime_put_sync(hba->dev); } Loading Loading
drivers/scsi/ufs/ufs.h +26 −0 Original line number Diff line number Diff line Loading @@ -113,6 +113,7 @@ enum flag_idn { /* Attribute idn for Query requests */ enum attr_idn { QUERY_ATTR_IDN_ACTIVE_ICC_LVL = 0x03, QUERY_ATTR_IDN_BKOPS_STATUS = 0x05, QUERY_ATTR_IDN_EE_CONTROL = 0x0D, QUERY_ATTR_IDN_EE_STATUS = 0x0E, Loading Loading @@ -153,6 +154,31 @@ enum unit_desc_param { UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1 = 0x22, }; /* bActiveICCLevel parameter current units */ enum { UFSHCD_NANO_AMP = 0, UFSHCD_MICRO_AMP = 1, UFSHCD_MILI_AMP = 2, UFSHCD_AMP = 3, }; #define POWER_DESC_MAX_SIZE 0x62 #define POWER_DESC_MAX_ACTV_ICC_LVLS 16 /* Attribute bActiveICCLevel parameter bit masks definitions */ #define ATTR_ICC_LVL_UNIT_OFFSET 14 #define ATTR_ICC_LVL_UNIT_MASK (0x3 << ATTR_ICC_LVL_UNIT_OFFSET) #define ATTR_ICC_LVL_VALUE_MASK 0x3FF /* Power descriptor parameters offsets in bytes */ enum power_desc_param_offset { PWR_DESC_LEN = 0x0, PWR_DESC_TYPE = 0x1, PWR_DESC_ACTIVE_LVLS_VCC_0 = 0x2, PWR_DESC_ACTIVE_LVLS_VCCQ_0 = 0x22, PWR_DESC_ACTIVE_LVLS_VCCQ2_0 = 0x42, }; /* Exception event mask values */ enum { MASK_EE_STATUS = 0xFFFF, Loading
drivers/scsi/ufs/ufshcd.c +113 −0 Original line number Diff line number Diff line Loading @@ -3188,6 +3188,118 @@ out: return ret; } /** * ufshcd_get_max_icc_level - calculate the ICC level * @sup_curr_uA: max. current supported by the regulator * @start_scan: row at the desc table to start scan from * @buff: power descriptor buffer * * Returns calculated max ICC level for specific regulator */ static u8 ufshcd_get_max_icc_level(int sup_curr_uA, u8 start_scan, char *buff) { u8 i; int curr_uA; u16 data; u16 unit; for (i = start_scan; i >= 0; i--) { data = be16_to_cpu(*((u16 *)(buff + 2*i))); unit = (data & ATTR_ICC_LVL_UNIT_MASK) >> ATTR_ICC_LVL_UNIT_OFFSET; curr_uA = data & ATTR_ICC_LVL_VALUE_MASK; switch (unit) { case UFSHCD_NANO_AMP: curr_uA = curr_uA / 1000; break; case UFSHCD_MILI_AMP: curr_uA = curr_uA * 1000; break; case UFSHCD_AMP: curr_uA = curr_uA * 1000 * 1000; break; case UFSHCD_MICRO_AMP: default: break; } if (sup_curr_uA >= curr_uA) break; } return i; } /** * ufshcd_calc_icc_level - calculate the max ICC level * In case regulators are not initialized we'll return 0 * @hba: per-adapter instance * @desc_buf: power descriptor buffer to extract ICC levels from. * @len: length of desc_buff * * Returns calculated ICC level */ static u8 ufshcd_find_max_sup_active_icc_level(struct ufs_hba *hba, u8 *desc_buf, int len) { u8 icc_level = 0; if (!hba->vreg_info.vcc || !hba->vreg_info.vccq || !hba->vreg_info.vccq2) { dev_err(hba->dev, "%s: Regulator capability was not set, actvIccLevel=%d", __func__, icc_level); goto out; } if (hba->vreg_info.vcc) icc_level = ufshcd_get_max_icc_level( hba->vreg_info.vcc->max_uA, POWER_DESC_MAX_ACTV_ICC_LVLS - 1, &desc_buf[PWR_DESC_ACTIVE_LVLS_VCC_0]); if (hba->vreg_info.vccq) icc_level = ufshcd_get_max_icc_level( hba->vreg_info.vccq->max_uA, icc_level, &desc_buf[PWR_DESC_ACTIVE_LVLS_VCCQ_0]); if (hba->vreg_info.vccq2) icc_level = ufshcd_get_max_icc_level( hba->vreg_info.vccq2->max_uA, icc_level, &desc_buf[PWR_DESC_ACTIVE_LVLS_VCCQ2_0]); out: return icc_level; } static void ufshcd_init_icc_levels(struct ufs_hba *hba) { u32 icc_level; int ret; int buff_len = POWER_DESC_MAX_SIZE; u8 desc_buf[POWER_DESC_MAX_SIZE]; ret = ufshcd_query_descriptor(hba, UPIU_QUERY_OPCODE_READ_DESC, QUERY_DESC_IDN_POWER, 0, 0, desc_buf, &buff_len); if (ret || (buff_len < POWER_DESC_MAX_SIZE)) { dev_err(hba->dev, "%s: Failed reading power descriptor.len = %d ret = %d", __func__, buff_len, ret); return; } icc_level = ufshcd_find_max_sup_active_icc_level(hba, desc_buf, buff_len); ret = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_WRITE_ATTR, QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, &icc_level); if (ret) dev_err(hba->dev, "%s: Failed configuring bActiveICCLevel = %d ret = %d", __func__, icc_level , ret); } /** * ufshcd_async_scan - asynchronous execution for link startup * @data: data pointer to pass to this function Loading Loading @@ -3217,6 +3329,7 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie) /* If we are in error handling context no need to scan the host */ if (!ufshcd_eh_in_progress(hba)) { ufshcd_init_icc_levels(hba); scsi_scan_host(hba->host); pm_runtime_put_sync(hba->dev); } Loading