aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/ufs
diff options
context:
space:
mode:
authorSujit Reddy Thumma <sthumma@codeaurora.org>2014-09-25 08:32:22 -0400
committerChristoph Hellwig <hch@lst.de>2014-10-01 07:11:19 -0400
commitaa497613093412ee26ef4bfa4ffec8391553dfca (patch)
tree1287812030e74c5555c66d5f36b7981517d99538 /drivers/scsi/ufs
parent5c0c28a84af9f9b6061bb4855a30e13d289b4ae1 (diff)
ufs: Add regulator enable support
UFS devices are powered by at most three external power supplies - - VCC - The flash memory core power supply, 2.7V to 3.6V or 1.70V to 1.95V - VCCQ - The controller and I/O power supply, 1.1V to 1.3V - VCCQ2 - Secondary controller and/or I/O power supply, 1.65V to 1.95V For some devices VCCQ or VCCQ2 are optional as they can be generated using internal LDO inside the UFS device. Add DT bindings for voltage regulators that can be controlled from host driver. Signed-off-by: Sujit Reddy Thumma <sthumma@codeaurora.org> Signed-off-by: Dolev Raviv <draviv@codeaurora.org> Signed-off-by: Christoph Hellwig <hch@lst.de>
Diffstat (limited to 'drivers/scsi/ufs')
-rw-r--r--drivers/scsi/ufs/ufs.h25
-rw-r--r--drivers/scsi/ufs/ufshcd-pltfrm.c100
-rw-r--r--drivers/scsi/ufs/ufshcd.c193
-rw-r--r--drivers/scsi/ufs/ufshcd.h3
4 files changed, 318 insertions, 3 deletions
diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index fafcf5e354c6..729ce7d62d1e 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -362,4 +362,29 @@ struct ufs_query_res {
362 struct utp_upiu_query upiu_res; 362 struct utp_upiu_query upiu_res;
363}; 363};
364 364
365#define UFS_VREG_VCC_MIN_UV 2700000 /* uV */
366#define UFS_VREG_VCC_MAX_UV 3600000 /* uV */
367#define UFS_VREG_VCC_1P8_MIN_UV 1700000 /* uV */
368#define UFS_VREG_VCC_1P8_MAX_UV 1950000 /* uV */
369#define UFS_VREG_VCCQ_MIN_UV 1100000 /* uV */
370#define UFS_VREG_VCCQ_MAX_UV 1300000 /* uV */
371#define UFS_VREG_VCCQ2_MIN_UV 1650000 /* uV */
372#define UFS_VREG_VCCQ2_MAX_UV 1950000 /* uV */
373
374struct ufs_vreg {
375 struct regulator *reg;
376 const char *name;
377 bool enabled;
378 int min_uV;
379 int max_uV;
380 int min_uA;
381 int max_uA;
382};
383
384struct ufs_vreg_info {
385 struct ufs_vreg *vcc;
386 struct ufs_vreg *vccq;
387 struct ufs_vreg *vccq2;
388};
389
365#endif /* End of Header */ 390#endif /* End of Header */
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c
index d727b1a83e95..51e47c429a1f 100644
--- a/drivers/scsi/ufs/ufshcd-pltfrm.c
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
@@ -53,6 +53,99 @@ static struct ufs_hba_variant_ops *get_variant_ops(struct device *dev)
53 return NULL; 53 return NULL;
54} 54}
55 55
56#define MAX_PROP_SIZE 32
57static int ufshcd_populate_vreg(struct device *dev, const char *name,
58 struct ufs_vreg **out_vreg)
59{
60 int ret = 0;
61 char prop_name[MAX_PROP_SIZE];
62 struct ufs_vreg *vreg = NULL;
63 struct device_node *np = dev->of_node;
64
65 if (!np) {
66 dev_err(dev, "%s: non DT initialization\n", __func__);
67 goto out;
68 }
69
70 snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", name);
71 if (!of_parse_phandle(np, prop_name, 0)) {
72 dev_info(dev, "%s: Unable to find %s regulator, assuming enabled\n",
73 __func__, prop_name);
74 goto out;
75 }
76
77 vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
78 if (!vreg) {
79 dev_err(dev, "No memory for %s regulator\n", name);
80 goto out;
81 }
82
83 vreg->name = kstrdup(name, GFP_KERNEL);
84
85 snprintf(prop_name, MAX_PROP_SIZE, "%s-max-microamp", name);
86 ret = of_property_read_u32(np, prop_name, &vreg->max_uA);
87 if (ret) {
88 dev_err(dev, "%s: unable to find %s err %d\n",
89 __func__, prop_name, ret);
90 goto out_free;
91 }
92
93 vreg->min_uA = 0;
94 if (!strcmp(name, "vcc")) {
95 if (of_property_read_bool(np, "vcc-supply-1p8")) {
96 vreg->min_uV = UFS_VREG_VCC_1P8_MIN_UV;
97 vreg->max_uV = UFS_VREG_VCC_1P8_MAX_UV;
98 } else {
99 vreg->min_uV = UFS_VREG_VCC_MIN_UV;
100 vreg->max_uV = UFS_VREG_VCC_MAX_UV;
101 }
102 } else if (!strcmp(name, "vccq")) {
103 vreg->min_uV = UFS_VREG_VCCQ_MIN_UV;
104 vreg->max_uV = UFS_VREG_VCCQ_MAX_UV;
105 } else if (!strcmp(name, "vccq2")) {
106 vreg->min_uV = UFS_VREG_VCCQ2_MIN_UV;
107 vreg->max_uV = UFS_VREG_VCCQ2_MAX_UV;
108 }
109
110 goto out;
111
112out_free:
113 devm_kfree(dev, vreg);
114 vreg = NULL;
115out:
116 if (!ret)
117 *out_vreg = vreg;
118 return ret;
119}
120
121/**
122 * ufshcd_parse_regulator_info - get regulator info from device tree
123 * @hba: per adapter instance
124 *
125 * Get regulator info from device tree for vcc, vccq, vccq2 power supplies.
126 * If any of the supplies are not defined it is assumed that they are always-on
127 * and hence return zero. If the property is defined but parsing is failed
128 * then return corresponding error.
129 */
130static int ufshcd_parse_regulator_info(struct ufs_hba *hba)
131{
132 int err;
133 struct device *dev = hba->dev;
134 struct ufs_vreg_info *info = &hba->vreg_info;
135
136 err = ufshcd_populate_vreg(dev, "vcc", &info->vcc);
137 if (err)
138 goto out;
139
140 err = ufshcd_populate_vreg(dev, "vccq", &info->vccq);
141 if (err)
142 goto out;
143
144 err = ufshcd_populate_vreg(dev, "vccq2", &info->vccq2);
145out:
146 return err;
147}
148
56#ifdef CONFIG_PM 149#ifdef CONFIG_PM
57/** 150/**
58 * ufshcd_pltfrm_suspend - suspend power management function 151 * ufshcd_pltfrm_suspend - suspend power management function
@@ -173,6 +266,13 @@ static int ufshcd_pltfrm_probe(struct platform_device *pdev)
173 266
174 hba->vops = get_variant_ops(&pdev->dev); 267 hba->vops = get_variant_ops(&pdev->dev);
175 268
269 err = ufshcd_parse_regulator_info(hba);
270 if (err) {
271 dev_err(&pdev->dev, "%s: regulator init failed %d\n",
272 __func__, err);
273 goto out;
274 }
275
176 pm_runtime_set_active(&pdev->dev); 276 pm_runtime_set_active(&pdev->dev);
177 pm_runtime_enable(&pdev->dev); 277 pm_runtime_enable(&pdev->dev);
178 278
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index d0565b07dc8a..ef8519e70e97 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -68,6 +68,16 @@
68/* Interrupt aggregation default timeout, unit: 40us */ 68/* Interrupt aggregation default timeout, unit: 40us */
69#define INT_AGGR_DEF_TO 0x02 69#define INT_AGGR_DEF_TO 0x02
70 70
71#define ufshcd_toggle_vreg(_dev, _vreg, _on) \
72 ({ \
73 int _ret; \
74 if (_on) \
75 _ret = ufshcd_enable_vreg(_dev, _vreg); \
76 else \
77 _ret = ufshcd_disable_vreg(_dev, _vreg); \
78 _ret; \
79 })
80
71enum { 81enum {
72 UFSHCD_MAX_CHANNEL = 0, 82 UFSHCD_MAX_CHANNEL = 0,
73 UFSHCD_MAX_ID = 1, 83 UFSHCD_MAX_ID = 1,
@@ -3193,6 +3203,153 @@ static struct scsi_host_template ufshcd_driver_template = {
3193 .can_queue = UFSHCD_CAN_QUEUE, 3203 .can_queue = UFSHCD_CAN_QUEUE,
3194}; 3204};
3195 3205
3206static int ufshcd_config_vreg(struct device *dev,
3207 struct ufs_vreg *vreg, bool on)
3208{
3209 int ret = 0;
3210 struct regulator *reg = vreg->reg;
3211 const char *name = vreg->name;
3212 int min_uV, uA_load;
3213
3214 BUG_ON(!vreg);
3215
3216 if (regulator_count_voltages(reg) > 0) {
3217 min_uV = on ? vreg->min_uV : 0;
3218 ret = regulator_set_voltage(reg, min_uV, vreg->max_uV);
3219 if (ret) {
3220 dev_err(dev, "%s: %s set voltage failed, err=%d\n",
3221 __func__, name, ret);
3222 goto out;
3223 }
3224
3225 uA_load = on ? vreg->max_uA : 0;
3226 ret = regulator_set_optimum_mode(reg, uA_load);
3227 if (ret >= 0) {
3228 /*
3229 * regulator_set_optimum_mode() returns new regulator
3230 * mode upon success.
3231 */
3232 ret = 0;
3233 } else {
3234 dev_err(dev, "%s: %s set optimum mode(uA_load=%d) failed, err=%d\n",
3235 __func__, name, uA_load, ret);
3236 goto out;
3237 }
3238 }
3239out:
3240 return ret;
3241}
3242
3243static int ufshcd_enable_vreg(struct device *dev, struct ufs_vreg *vreg)
3244{
3245 int ret = 0;
3246
3247 if (!vreg || vreg->enabled)
3248 goto out;
3249
3250 ret = ufshcd_config_vreg(dev, vreg, true);
3251 if (!ret)
3252 ret = regulator_enable(vreg->reg);
3253
3254 if (!ret)
3255 vreg->enabled = true;
3256 else
3257 dev_err(dev, "%s: %s enable failed, err=%d\n",
3258 __func__, vreg->name, ret);
3259out:
3260 return ret;
3261}
3262
3263static int ufshcd_disable_vreg(struct device *dev, struct ufs_vreg *vreg)
3264{
3265 int ret = 0;
3266
3267 if (!vreg || !vreg->enabled)
3268 goto out;
3269
3270 ret = regulator_disable(vreg->reg);
3271
3272 if (!ret) {
3273 /* ignore errors on applying disable config */
3274 ufshcd_config_vreg(dev, vreg, false);
3275 vreg->enabled = false;
3276 } else {
3277 dev_err(dev, "%s: %s disable failed, err=%d\n",
3278 __func__, vreg->name, ret);
3279 }
3280out:
3281 return ret;
3282}
3283
3284static int ufshcd_setup_vreg(struct ufs_hba *hba, bool on)
3285{
3286 int ret = 0;
3287 struct device *dev = hba->dev;
3288 struct ufs_vreg_info *info = &hba->vreg_info;
3289
3290 if (!info)
3291 goto out;
3292
3293 ret = ufshcd_toggle_vreg(dev, info->vcc, on);
3294 if (ret)
3295 goto out;
3296
3297 ret = ufshcd_toggle_vreg(dev, info->vccq, on);
3298 if (ret)
3299 goto out;
3300
3301 ret = ufshcd_toggle_vreg(dev, info->vccq2, on);
3302 if (ret)
3303 goto out;
3304
3305out:
3306 if (ret) {
3307 ufshcd_toggle_vreg(dev, info->vccq2, false);
3308 ufshcd_toggle_vreg(dev, info->vccq, false);
3309 ufshcd_toggle_vreg(dev, info->vcc, false);
3310 }
3311 return ret;
3312}
3313
3314static int ufshcd_get_vreg(struct device *dev, struct ufs_vreg *vreg)
3315{
3316 int ret = 0;
3317
3318 if (!vreg)
3319 goto out;
3320
3321 vreg->reg = devm_regulator_get(dev, vreg->name);
3322 if (IS_ERR(vreg->reg)) {
3323 ret = PTR_ERR(vreg->reg);
3324 dev_err(dev, "%s: %s get failed, err=%d\n",
3325 __func__, vreg->name, ret);
3326 }
3327out:
3328 return ret;
3329}
3330
3331static int ufshcd_init_vreg(struct ufs_hba *hba)
3332{
3333 int ret = 0;
3334 struct device *dev = hba->dev;
3335 struct ufs_vreg_info *info = &hba->vreg_info;
3336
3337 if (!info)
3338 goto out;
3339
3340 ret = ufshcd_get_vreg(dev, info->vcc);
3341 if (ret)
3342 goto out;
3343
3344 ret = ufshcd_get_vreg(dev, info->vccq);
3345 if (ret)
3346 goto out;
3347
3348 ret = ufshcd_get_vreg(dev, info->vccq2);
3349out:
3350 return ret;
3351}
3352
3196static int ufshcd_variant_hba_init(struct ufs_hba *hba) 3353static int ufshcd_variant_hba_init(struct ufs_hba *hba)
3197{ 3354{
3198 int err = 0; 3355 int err = 0;
@@ -3248,6 +3405,36 @@ static void ufshcd_variant_hba_exit(struct ufs_hba *hba)
3248 hba->vops->exit(hba); 3405 hba->vops->exit(hba);
3249} 3406}
3250 3407
3408static int ufshcd_hba_init(struct ufs_hba *hba)
3409{
3410 int err;
3411
3412 err = ufshcd_init_vreg(hba);
3413 if (err)
3414 goto out;
3415
3416 err = ufshcd_setup_vreg(hba, true);
3417 if (err)
3418 goto out;
3419
3420 err = ufshcd_variant_hba_init(hba);
3421 if (err)
3422 goto out_disable_vreg;
3423
3424 goto out;
3425
3426out_disable_vreg:
3427 ufshcd_setup_vreg(hba, false);
3428out:
3429 return err;
3430}
3431
3432static void ufshcd_hba_exit(struct ufs_hba *hba)
3433{
3434 ufshcd_variant_hba_exit(hba);
3435 ufshcd_setup_vreg(hba, false);
3436}
3437
3251/** 3438/**
3252 * ufshcd_suspend - suspend power management function 3439 * ufshcd_suspend - suspend power management function
3253 * @hba: per adapter instance 3440 * @hba: per adapter instance
@@ -3333,7 +3520,7 @@ void ufshcd_remove(struct ufs_hba *hba)
3333 3520
3334 scsi_host_put(hba->host); 3521 scsi_host_put(hba->host);
3335 3522
3336 ufshcd_variant_hba_exit(hba); 3523 ufshcd_hba_exit(hba);
3337} 3524}
3338EXPORT_SYMBOL_GPL(ufshcd_remove); 3525EXPORT_SYMBOL_GPL(ufshcd_remove);
3339 3526
@@ -3412,7 +3599,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
3412 hba->mmio_base = mmio_base; 3599 hba->mmio_base = mmio_base;
3413 hba->irq = irq; 3600 hba->irq = irq;
3414 3601
3415 err = ufshcd_variant_hba_init(hba); 3602 err = ufshcd_hba_init(hba);
3416 if (err) 3603 if (err)
3417 goto out_error; 3604 goto out_error;
3418 3605
@@ -3504,7 +3691,7 @@ out_remove_scsi_host:
3504 scsi_remove_host(hba->host); 3691 scsi_remove_host(hba->host);
3505out_disable: 3692out_disable:
3506 scsi_host_put(host); 3693 scsi_host_put(host);
3507 ufshcd_variant_hba_exit(hba); 3694 ufshcd_hba_exit(hba);
3508out_error: 3695out_error:
3509 return err; 3696 return err;
3510} 3697}
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 8c6bec05283e..c0232f95f5b5 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -52,6 +52,7 @@
52#include <linux/pm_runtime.h> 52#include <linux/pm_runtime.h>
53#include <linux/clk.h> 53#include <linux/clk.h>
54#include <linux/completion.h> 54#include <linux/completion.h>
55#include <linux/regulator/consumer.h>
55 56
56#include <asm/irq.h> 57#include <asm/irq.h>
57#include <asm/byteorder.h> 58#include <asm/byteorder.h>
@@ -219,6 +220,7 @@ struct ufs_hba_variant_ops {
219 * @saved_uic_err: sticky UIC error mask 220 * @saved_uic_err: sticky UIC error mask
220 * @dev_cmd: ufs device management command information 221 * @dev_cmd: ufs device management command information
221 * @auto_bkops_enabled: to track whether bkops is enabled in device 222 * @auto_bkops_enabled: to track whether bkops is enabled in device
223 * @vreg_info: UFS device voltage regulator information
222 */ 224 */
223struct ufs_hba { 225struct ufs_hba {
224 void __iomem *mmio_base; 226 void __iomem *mmio_base;
@@ -279,6 +281,7 @@ struct ufs_hba {
279 struct ufs_dev_cmd dev_cmd; 281 struct ufs_dev_cmd dev_cmd;
280 282
281 bool auto_bkops_enabled; 283 bool auto_bkops_enabled;
284 struct ufs_vreg_info vreg_info;
282}; 285};
283 286
284#define ufshcd_writel(hba, val, reg) \ 287#define ufshcd_writel(hba, val, reg) \