aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYaniv Gardi <ygardi@codeaurora.org>2014-09-25 08:32:27 -0400
committerChristoph Hellwig <hch@lst.de>2014-10-01 07:11:21 -0400
commit3a4bf06d1f6d7de17528b962bc91fcbb2e568b4e (patch)
treef57dcec75893f4b3505755e34b5b6a911bd236b4
parent1d337ec2f35e69a046dab0cc77e64e68d1cdcd8b (diff)
ufs: Active Power Mode - configuring bActiveICCLevel
The maximum power consumption in active is determined by bActiveICCLevel. The configuration is done by reading max current supported by the regulators connected to VCC, VCCQ and VCCQ2 rails on the boards, and reading the current consumption levels from the device for each rails (vcc/vccq/vccq2) using power descriptor. We configure the bActiveICCLevel attribute, with the max value that correspond to the minimum-of(VCC-current-level,VCCQ-current-level, VCCQ2-current-level). In order to minimize resume latency, pre-fetch icc levels and reference clock during initialization and avoid reading them each link startup during resume. Signed-off-by: Raviv Shvili <rshvili@codeaurora.org> Signed-off-by: Yaniv Gardi <ygardi@codeaurora.org> Signed-off-by: Dolev Raviv <draviv@codeaurora.org> Signed-off-by: Christoph Hellwig <hch@lst.de>
-rw-r--r--drivers/scsi/ufs/ufs.h26
-rw-r--r--drivers/scsi/ufs/ufshcd.c126
-rw-r--r--drivers/scsi/ufs/ufshcd.h13
3 files changed, 165 insertions, 0 deletions
diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index f76a304e7408..4ca99ed8ee4a 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -115,6 +115,7 @@ enum flag_idn {
115 115
116/* Attribute idn for Query requests */ 116/* Attribute idn for Query requests */
117enum attr_idn { 117enum attr_idn {
118 QUERY_ATTR_IDN_ACTIVE_ICC_LVL = 0x03,
118 QUERY_ATTR_IDN_BKOPS_STATUS = 0x05, 119 QUERY_ATTR_IDN_BKOPS_STATUS = 0x05,
119 QUERY_ATTR_IDN_EE_CONTROL = 0x0D, 120 QUERY_ATTR_IDN_EE_CONTROL = 0x0D,
120 QUERY_ATTR_IDN_EE_STATUS = 0x0E, 121 QUERY_ATTR_IDN_EE_STATUS = 0x0E,
@@ -174,6 +175,31 @@ enum unit_desc_param {
174 UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1 = 0x22, 175 UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1 = 0x22,
175}; 176};
176 177
178/* bActiveICCLevel parameter current units */
179enum {
180 UFSHCD_NANO_AMP = 0,
181 UFSHCD_MICRO_AMP = 1,
182 UFSHCD_MILI_AMP = 2,
183 UFSHCD_AMP = 3,
184};
185
186#define POWER_DESC_MAX_SIZE 0x62
187#define POWER_DESC_MAX_ACTV_ICC_LVLS 16
188
189/* Attribute bActiveICCLevel parameter bit masks definitions */
190#define ATTR_ICC_LVL_UNIT_OFFSET 14
191#define ATTR_ICC_LVL_UNIT_MASK (0x3 << ATTR_ICC_LVL_UNIT_OFFSET)
192#define ATTR_ICC_LVL_VALUE_MASK 0x3FF
193
194/* Power descriptor parameters offsets in bytes */
195enum power_desc_param_offset {
196 PWR_DESC_LEN = 0x0,
197 PWR_DESC_TYPE = 0x1,
198 PWR_DESC_ACTIVE_LVLS_VCC_0 = 0x2,
199 PWR_DESC_ACTIVE_LVLS_VCCQ_0 = 0x22,
200 PWR_DESC_ACTIVE_LVLS_VCCQ2_0 = 0x42,
201};
202
177/* Exception event mask values */ 203/* Exception event mask values */
178enum { 204enum {
179 MASK_EE_STATUS = 0xFFFF, 205 MASK_EE_STATUS = 0xFFFF,
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index af29d4c0a416..dc89c486d40a 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -3265,6 +3265,125 @@ static int ufshcd_eh_host_reset_handler(struct scsi_cmnd *cmd)
3265} 3265}
3266 3266
3267/** 3267/**
3268 * ufshcd_get_max_icc_level - calculate the ICC level
3269 * @sup_curr_uA: max. current supported by the regulator
3270 * @start_scan: row at the desc table to start scan from
3271 * @buff: power descriptor buffer
3272 *
3273 * Returns calculated max ICC level for specific regulator
3274 */
3275static u32 ufshcd_get_max_icc_level(int sup_curr_uA, u32 start_scan, char *buff)
3276{
3277 int i;
3278 int curr_uA;
3279 u16 data;
3280 u16 unit;
3281
3282 for (i = start_scan; i >= 0; i--) {
3283 data = be16_to_cpu(*((u16 *)(buff + 2*i)));
3284 unit = (data & ATTR_ICC_LVL_UNIT_MASK) >>
3285 ATTR_ICC_LVL_UNIT_OFFSET;
3286 curr_uA = data & ATTR_ICC_LVL_VALUE_MASK;
3287 switch (unit) {
3288 case UFSHCD_NANO_AMP:
3289 curr_uA = curr_uA / 1000;
3290 break;
3291 case UFSHCD_MILI_AMP:
3292 curr_uA = curr_uA * 1000;
3293 break;
3294 case UFSHCD_AMP:
3295 curr_uA = curr_uA * 1000 * 1000;
3296 break;
3297 case UFSHCD_MICRO_AMP:
3298 default:
3299 break;
3300 }
3301 if (sup_curr_uA >= curr_uA)
3302 break;
3303 }
3304 if (i < 0) {
3305 i = 0;
3306 pr_err("%s: Couldn't find valid icc_level = %d", __func__, i);
3307 }
3308
3309 return (u32)i;
3310}
3311
3312/**
3313 * ufshcd_calc_icc_level - calculate the max ICC level
3314 * In case regulators are not initialized we'll return 0
3315 * @hba: per-adapter instance
3316 * @desc_buf: power descriptor buffer to extract ICC levels from.
3317 * @len: length of desc_buff
3318 *
3319 * Returns calculated ICC level
3320 */
3321static u32 ufshcd_find_max_sup_active_icc_level(struct ufs_hba *hba,
3322 u8 *desc_buf, int len)
3323{
3324 u32 icc_level = 0;
3325
3326 if (!hba->vreg_info.vcc || !hba->vreg_info.vccq ||
3327 !hba->vreg_info.vccq2) {
3328 dev_err(hba->dev,
3329 "%s: Regulator capability was not set, actvIccLevel=%d",
3330 __func__, icc_level);
3331 goto out;
3332 }
3333
3334 if (hba->vreg_info.vcc)
3335 icc_level = ufshcd_get_max_icc_level(
3336 hba->vreg_info.vcc->max_uA,
3337 POWER_DESC_MAX_ACTV_ICC_LVLS - 1,
3338 &desc_buf[PWR_DESC_ACTIVE_LVLS_VCC_0]);
3339
3340 if (hba->vreg_info.vccq)
3341 icc_level = ufshcd_get_max_icc_level(
3342 hba->vreg_info.vccq->max_uA,
3343 icc_level,
3344 &desc_buf[PWR_DESC_ACTIVE_LVLS_VCCQ_0]);
3345
3346 if (hba->vreg_info.vccq2)
3347 icc_level = ufshcd_get_max_icc_level(
3348 hba->vreg_info.vccq2->max_uA,
3349 icc_level,
3350 &desc_buf[PWR_DESC_ACTIVE_LVLS_VCCQ2_0]);
3351out:
3352 return icc_level;
3353}
3354
3355static void ufshcd_init_icc_levels(struct ufs_hba *hba)
3356{
3357 int ret;
3358 int buff_len = QUERY_DESC_POWER_MAX_SIZE;
3359 u8 desc_buf[QUERY_DESC_POWER_MAX_SIZE];
3360
3361 ret = ufshcd_read_power_desc(hba, desc_buf, buff_len);
3362 if (ret) {
3363 dev_err(hba->dev,
3364 "%s: Failed reading power descriptor.len = %d ret = %d",
3365 __func__, buff_len, ret);
3366 return;
3367 }
3368
3369 hba->init_prefetch_data.icc_level =
3370 ufshcd_find_max_sup_active_icc_level(hba,
3371 desc_buf, buff_len);
3372 dev_dbg(hba->dev, "%s: setting icc_level 0x%x",
3373 __func__, hba->init_prefetch_data.icc_level);
3374
3375 ret = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_WRITE_ATTR,
3376 QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0,
3377 &hba->init_prefetch_data.icc_level);
3378
3379 if (ret)
3380 dev_err(hba->dev,
3381 "%s: Failed configuring bActiveICCLevel = %d ret = %d",
3382 __func__, hba->init_prefetch_data.icc_level , ret);
3383
3384}
3385
3386/**
3268 * ufshcd_probe_hba - probe hba to detect device and initialize 3387 * ufshcd_probe_hba - probe hba to detect device and initialize
3269 * @hba: per-adapter instance 3388 * @hba: per-adapter instance
3270 * 3389 *
@@ -3293,9 +3412,16 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
3293 3412
3294 /* If we are in error handling context no need to scan the host */ 3413 /* If we are in error handling context no need to scan the host */
3295 if (!ufshcd_eh_in_progress(hba)) { 3414 if (!ufshcd_eh_in_progress(hba)) {
3415 if (!hba->is_init_prefetch)
3416 ufshcd_init_icc_levels(hba);
3417
3296 scsi_scan_host(hba->host); 3418 scsi_scan_host(hba->host);
3297 pm_runtime_put_sync(hba->dev); 3419 pm_runtime_put_sync(hba->dev);
3298 } 3420 }
3421
3422 if (!hba->is_init_prefetch)
3423 hba->is_init_prefetch = true;
3424
3299out: 3425out:
3300 /* 3426 /*
3301 * If we failed to initialize the device or the device is not 3427 * If we failed to initialize the device or the device is not
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index eddb3f3b4139..8365ad437aa6 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -196,6 +196,15 @@ struct ufs_hba_variant_ops {
196}; 196};
197 197
198/** 198/**
199 * struct ufs_init_prefetch - contains data that is pre-fetched once during
200 * initialization
201 * @icc_level: icc level which was read during initialization
202 */
203struct ufs_init_prefetch {
204 u32 icc_level;
205};
206
207/**
199 * struct ufs_hba - per adapter private structure 208 * struct ufs_hba - per adapter private structure
200 * @mmio_base: UFSHCI base register address 209 * @mmio_base: UFSHCI base register address
201 * @ucdl_base_addr: UFS Command Descriptor base address 210 * @ucdl_base_addr: UFS Command Descriptor base address
@@ -229,6 +238,8 @@ struct ufs_hba_variant_ops {
229 * @intr_mask: Interrupt Mask Bits 238 * @intr_mask: Interrupt Mask Bits
230 * @ee_ctrl_mask: Exception event control mask 239 * @ee_ctrl_mask: Exception event control mask
231 * @is_powered: flag to check if HBA is powered 240 * @is_powered: flag to check if HBA is powered
241 * @is_init_prefetch: flag to check if data was pre-fetched in initialization
242 * @init_prefetch_data: data pre-fetched during initialization
232 * @eh_work: Worker to handle UFS errors that require s/w attention 243 * @eh_work: Worker to handle UFS errors that require s/w attention
233 * @eeh_work: Worker to handle exception events 244 * @eeh_work: Worker to handle exception events
234 * @errors: HBA errors 245 * @errors: HBA errors
@@ -285,6 +296,8 @@ struct ufs_hba {
285 u32 intr_mask; 296 u32 intr_mask;
286 u16 ee_ctrl_mask; 297 u16 ee_ctrl_mask;
287 bool is_powered; 298 bool is_powered;
299 bool is_init_prefetch;
300 struct ufs_init_prefetch init_prefetch_data;
288 301
289 /* Work Queues */ 302 /* Work Queues */
290 struct work_struct eh_work; 303 struct work_struct eh_work;