aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuennadi Liakhovetski <g.liakhovetski@gmx.de>2012-11-22 05:12:08 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2012-11-23 07:14:40 -0500
commitf1e64f90269c197a0619535917210543c0112fcc (patch)
treeebb06fb5729dca187b9d77094e2dcdd752e8a0db
parentf4a75d2eb7b1e2206094b901be09adb31ba63681 (diff)
regulator: add a regulator driver for the AS3711 PMIC
This driver supports the 4 DCDC and 8 LDO regulators on the AS3711 PMIC. Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
-rw-r--r--drivers/regulator/Kconfig7
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/as3711-regulator.c379
3 files changed, 387 insertions, 0 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 67d47b59a66d..f73d6f5ce6dd 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -450,5 +450,12 @@ config REGULATOR_WM8994
450 This driver provides support for the voltage regulators on the 450 This driver provides support for the voltage regulators on the
451 WM8994 CODEC. 451 WM8994 CODEC.
452 452
453config REGULATOR_AS3711
454 tristate "AS3711 PMIC"
455 depends on MFD_AS3711
456 help
457 This driver provides support for the voltage regulators on the
458 AS3711 PMIC
459
453endif 460endif
454 461
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index e431eed8a878..c1557ace9095 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o
16obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o 16obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o
17obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o 17obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o
18obj-$(CONFIG_REGULATOR_ARIZONA) += arizona-micsupp.o arizona-ldo1.o 18obj-$(CONFIG_REGULATOR_ARIZONA) += arizona-micsupp.o arizona-ldo1.o
19obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o
19obj-$(CONFIG_REGULATOR_DA903X) += da903x.o 20obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
20obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o 21obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o
21obj-$(CONFIG_REGULATOR_DBX500_PRCMU) += dbx500-prcmu.o 22obj-$(CONFIG_REGULATOR_DBX500_PRCMU) += dbx500-prcmu.o
diff --git a/drivers/regulator/as3711-regulator.c b/drivers/regulator/as3711-regulator.c
new file mode 100644
index 000000000000..81578bf7e352
--- /dev/null
+++ b/drivers/regulator/as3711-regulator.c
@@ -0,0 +1,379 @@
1/*
2 * AS3711 PMIC regulator driver, using DCDC Step Down and LDO supplies
3 *
4 * Copyright (C) 2012 Renesas Electronics Corporation
5 * Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the version 2 of the GNU General Public License as
9 * published by the Free Software Foundation
10 */
11
12#include <linux/err.h>
13#include <linux/init.h>
14#include <linux/mfd/as3711.h>
15#include <linux/module.h>
16#include <linux/platform_device.h>
17#include <linux/regmap.h>
18#include <linux/regulator/driver.h>
19#include <linux/slab.h>
20
21struct as3711_regulator_info {
22 struct regulator_desc desc;
23 unsigned int max_uV;
24};
25
26struct as3711_regulator {
27 struct as3711_regulator_info *reg_info;
28 struct regulator_dev *rdev;
29};
30
31static int as3711_list_voltage_sd(struct regulator_dev *rdev,
32 unsigned int selector)
33{
34 if (selector >= rdev->desc->n_voltages)
35 return -EINVAL;
36
37 if (!selector)
38 return 0;
39 if (selector < 0x41)
40 return 600000 + selector * 12500;
41 if (selector < 0x71)
42 return 1400000 + (selector - 0x40) * 25000;
43 return 2600000 + (selector - 0x70) * 50000;
44}
45
46static int as3711_list_voltage_aldo(struct regulator_dev *rdev,
47 unsigned int selector)
48{
49 if (selector >= rdev->desc->n_voltages)
50 return -EINVAL;
51
52 if (selector < 0x10)
53 return 1200000 + selector * 50000;
54 return 1800000 + (selector - 0x10) * 100000;
55}
56
57static int as3711_list_voltage_dldo(struct regulator_dev *rdev,
58 unsigned int selector)
59{
60 if (selector >= rdev->desc->n_voltages ||
61 (selector > 0x10 && selector < 0x20))
62 return -EINVAL;
63
64 if (selector < 0x11)
65 return 900000 + selector * 50000;
66 return 1750000 + (selector - 0x20) * 50000;
67}
68
69static int as3711_bound_check(struct regulator_dev *rdev,
70 int *min_uV, int *max_uV)
71{
72 struct as3711_regulator_info *info = container_of(rdev->desc,
73 struct as3711_regulator_info, desc);
74 struct as3711_regulator *reg = rdev->reg_data;
75
76 WARN_ON(reg->reg_info != info);
77
78 dev_dbg(&rdev->dev, "%s(), %d, %d, %d\n", __func__,
79 *min_uV, rdev->desc->min_uV, info->max_uV);
80
81 if (*max_uV < *min_uV ||
82 *min_uV >= info->max_uV || rdev->desc->min_uV >= *max_uV)
83 return -EINVAL;
84
85 if (rdev->desc->n_voltages == 1)
86 return 0;
87
88 if (*max_uV > info->max_uV)
89 *max_uV = info->max_uV;
90
91 if (*min_uV < rdev->desc->min_uV)
92 *min_uV = rdev->desc->min_uV;
93
94 return *min_uV;
95}
96
97static int as3711_sel_check(int min, int max, int bottom, int step)
98{
99 int ret, voltage;
100
101 /* Round up min, when dividing: keeps us within the range */
102 ret = (min - bottom + step - 1) / step;
103 voltage = ret * step + bottom;
104 pr_debug("%s(): select %d..%d in %d+N*%d: %d\n", __func__,
105 min, max, bottom, step, ret);
106 if (voltage > max) {
107 /*
108 * Try 1 down. It will take us below min, but as long we stay
109 * above bottom, we're fine.
110 */
111 ret--;
112 voltage = ret * step + bottom;
113 if (voltage < bottom)
114 return -EINVAL;
115 }
116 return ret;
117}
118
119static int as3711_map_voltage_sd(struct regulator_dev *rdev,
120 int min_uV, int max_uV)
121{
122 int ret;
123
124 ret = as3711_bound_check(rdev, &min_uV, &max_uV);
125 if (ret <= 0)
126 return ret;
127
128 if (min_uV <= 1400000)
129 return as3711_sel_check(min_uV, max_uV, 600000, 12500);
130
131 if (min_uV <= 2600000)
132 return as3711_sel_check(min_uV, max_uV, 1400000, 25000) + 0x40;
133
134 return as3711_sel_check(min_uV, max_uV, 2600000, 50000) + 0x70;
135}
136
137/*
138 * The regulator API supports 4 modes of operataion: FAST, NORMAL, IDLE and
139 * STANDBY. We map them in the following way to AS3711 SD1-4 DCDC modes:
140 * FAST: sdX_fast=1
141 * NORMAL: low_noise=1
142 * IDLE: low_noise=0
143 */
144
145static int as3711_set_mode_sd(struct regulator_dev *rdev, unsigned int mode)
146{
147 unsigned int fast_bit = rdev->desc->enable_mask,
148 low_noise_bit = fast_bit << 4;
149 u8 val;
150
151 switch (mode) {
152 case REGULATOR_MODE_FAST:
153 val = fast_bit | low_noise_bit;
154 break;
155 case REGULATOR_MODE_NORMAL:
156 val = low_noise_bit;
157 break;
158 case REGULATOR_MODE_IDLE:
159 val = 0;
160 break;
161 default:
162 return -EINVAL;
163 }
164
165 return regmap_update_bits(rdev->regmap, AS3711_SD_CONTROL_1,
166 low_noise_bit | fast_bit, val);
167}
168
169static unsigned int as3711_get_mode_sd(struct regulator_dev *rdev)
170{
171 unsigned int fast_bit = rdev->desc->enable_mask,
172 low_noise_bit = fast_bit << 4, mask = fast_bit | low_noise_bit;
173 unsigned int val;
174 int ret = regmap_read(rdev->regmap, AS3711_SD_CONTROL_1, &val);
175
176 if (ret < 0)
177 return ret;
178
179 if ((val & mask) == mask)
180 return REGULATOR_MODE_FAST;
181
182 if ((val & mask) == low_noise_bit)
183 return REGULATOR_MODE_NORMAL;
184
185 if (!(val & mask))
186 return REGULATOR_MODE_IDLE;
187
188 return -EINVAL;
189}
190
191static int as3711_map_voltage_aldo(struct regulator_dev *rdev,
192 int min_uV, int max_uV)
193{
194 int ret;
195
196 ret = as3711_bound_check(rdev, &min_uV, &max_uV);
197 if (ret <= 0)
198 return ret;
199
200 if (min_uV <= 1800000)
201 return as3711_sel_check(min_uV, max_uV, 1200000, 50000);
202
203 return as3711_sel_check(min_uV, max_uV, 1800000, 100000) + 0x10;
204}
205
206static int as3711_map_voltage_dldo(struct regulator_dev *rdev,
207 int min_uV, int max_uV)
208{
209 int ret;
210
211 ret = as3711_bound_check(rdev, &min_uV, &max_uV);
212 if (ret <= 0)
213 return ret;
214
215 if (min_uV <= 1700000)
216 return as3711_sel_check(min_uV, max_uV, 900000, 50000);
217
218 return as3711_sel_check(min_uV, max_uV, 1750000, 50000) + 0x20;
219}
220
221static struct regulator_ops as3711_sd_ops = {
222 .is_enabled = regulator_is_enabled_regmap,
223 .enable = regulator_enable_regmap,
224 .disable = regulator_disable_regmap,
225 .get_voltage_sel = regulator_get_voltage_sel_regmap,
226 .set_voltage_sel = regulator_set_voltage_sel_regmap,
227 .list_voltage = as3711_list_voltage_sd,
228 .map_voltage = as3711_map_voltage_sd,
229 .get_mode = as3711_get_mode_sd,
230 .set_mode = as3711_set_mode_sd,
231};
232
233static struct regulator_ops as3711_aldo_ops = {
234 .is_enabled = regulator_is_enabled_regmap,
235 .enable = regulator_enable_regmap,
236 .disable = regulator_disable_regmap,
237 .get_voltage_sel = regulator_get_voltage_sel_regmap,
238 .set_voltage_sel = regulator_set_voltage_sel_regmap,
239 .list_voltage = as3711_list_voltage_aldo,
240 .map_voltage = as3711_map_voltage_aldo,
241};
242
243static struct regulator_ops as3711_dldo_ops = {
244 .is_enabled = regulator_is_enabled_regmap,
245 .enable = regulator_enable_regmap,
246 .disable = regulator_disable_regmap,
247 .get_voltage_sel = regulator_get_voltage_sel_regmap,
248 .set_voltage_sel = regulator_set_voltage_sel_regmap,
249 .list_voltage = as3711_list_voltage_dldo,
250 .map_voltage = as3711_map_voltage_dldo,
251};
252
253#define AS3711_REG(_id, _en_reg, _en_bit, _vmask, _vshift, _min_uV, _max_uV, _sfx) \
254 [AS3711_REGULATOR_ ## _id] = { \
255 .desc = { \
256 .name = "as3711-regulator-" # _id, \
257 .id = AS3711_REGULATOR_ ## _id, \
258 .n_voltages = (_vmask + 1), \
259 .ops = &as3711_ ## _sfx ## _ops, \
260 .type = REGULATOR_VOLTAGE, \
261 .owner = THIS_MODULE, \
262 .vsel_reg = AS3711_ ## _id ## _VOLTAGE, \
263 .vsel_mask = _vmask << _vshift, \
264 .enable_reg = AS3711_ ## _en_reg, \
265 .enable_mask = BIT(_en_bit), \
266 .min_uV = _min_uV, \
267 }, \
268 .max_uV = _max_uV, \
269}
270
271static struct as3711_regulator_info as3711_reg_info[] = {
272 AS3711_REG(SD_1, SD_CONTROL, 0, 0x7f, 0, 612500, 3350000, sd),
273 AS3711_REG(SD_2, SD_CONTROL, 1, 0x7f, 0, 612500, 3350000, sd),
274 AS3711_REG(SD_3, SD_CONTROL, 2, 0x7f, 0, 612500, 3350000, sd),
275 AS3711_REG(SD_4, SD_CONTROL, 3, 0x7f, 0, 612500, 3350000, sd),
276 AS3711_REG(LDO_1, LDO_1_VOLTAGE, 7, 0x1f, 0, 1200000, 3300000, aldo),
277 AS3711_REG(LDO_2, LDO_2_VOLTAGE, 7, 0x1f, 0, 1200000, 3300000, aldo),
278 AS3711_REG(LDO_3, LDO_3_VOLTAGE, 7, 0x3f, 0, 900000, 3300000, dldo),
279 AS3711_REG(LDO_4, LDO_4_VOLTAGE, 7, 0x3f, 0, 900000, 3300000, dldo),
280 AS3711_REG(LDO_5, LDO_5_VOLTAGE, 7, 0x3f, 0, 900000, 3300000, dldo),
281 AS3711_REG(LDO_6, LDO_6_VOLTAGE, 7, 0x3f, 0, 900000, 3300000, dldo),
282 AS3711_REG(LDO_7, LDO_7_VOLTAGE, 7, 0x3f, 0, 900000, 3300000, dldo),
283 AS3711_REG(LDO_8, LDO_8_VOLTAGE, 7, 0x3f, 0, 900000, 3300000, dldo),
284 /* StepUp output voltage depends on supplying regulator */
285};
286
287#define AS3711_REGULATOR_NUM ARRAY_SIZE(as3711_reg_info)
288
289static int as3711_regulator_probe(struct platform_device *pdev)
290{
291 struct as3711_regulator_pdata *pdata = dev_get_platdata(&pdev->dev);
292 struct as3711 *as3711 = dev_get_drvdata(pdev->dev.parent);
293 struct regulator_init_data *reg_data;
294 struct regulator_config config = {.dev = &pdev->dev,};
295 struct as3711_regulator *reg = NULL;
296 struct as3711_regulator *regs;
297 struct regulator_dev *rdev;
298 struct as3711_regulator_info *ri;
299 int ret;
300 int id;
301
302 if (!pdata)
303 dev_dbg(&pdev->dev, "No platform data...\n");
304
305 regs = devm_kzalloc(&pdev->dev, AS3711_REGULATOR_NUM *
306 sizeof(struct as3711_regulator), GFP_KERNEL);
307 if (!regs) {
308 dev_err(&pdev->dev, "Memory allocation failed exiting..\n");
309 return -ENOMEM;
310 }
311
312 for (id = 0, ri = as3711_reg_info; id < AS3711_REGULATOR_NUM; ++id, ri++) {
313 reg_data = pdata ? pdata->init_data[id] : NULL;
314
315 /* No need to register if there is no regulator data */
316 if (!ri->desc.name)
317 continue;
318
319 reg = &regs[id];
320 reg->reg_info = ri;
321
322 config.init_data = reg_data;
323 config.driver_data = reg;
324 config.regmap = as3711->regmap;
325
326 rdev = regulator_register(&ri->desc, &config);
327 if (IS_ERR(rdev)) {
328 dev_err(&pdev->dev, "Failed to register regulator %s\n",
329 ri->desc.name);
330 ret = PTR_ERR(rdev);
331 goto eregreg;
332 }
333 reg->rdev = rdev;
334 }
335 platform_set_drvdata(pdev, regs);
336 return 0;
337
338eregreg:
339 while (--id >= 0)
340 regulator_unregister(regs[id].rdev);
341
342 return ret;
343}
344
345static int as3711_regulator_remove(struct platform_device *pdev)
346{
347 struct as3711_regulator *regs = platform_get_drvdata(pdev);
348 int id;
349
350 for (id = 0; id < AS3711_REGULATOR_NUM; ++id)
351 regulator_unregister(regs[id].rdev);
352 return 0;
353}
354
355static struct platform_driver as3711_regulator_driver = {
356 .driver = {
357 .name = "as3711-regulator",
358 .owner = THIS_MODULE,
359 },
360 .probe = as3711_regulator_probe,
361 .remove = as3711_regulator_remove,
362};
363
364static int __init as3711_regulator_init(void)
365{
366 return platform_driver_register(&as3711_regulator_driver);
367}
368subsys_initcall(as3711_regulator_init);
369
370static void __exit as3711_regulator_exit(void)
371{
372 platform_driver_unregister(&as3711_regulator_driver);
373}
374module_exit(as3711_regulator_exit);
375
376MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
377MODULE_DESCRIPTION("AS3711 regulator driver");
378MODULE_ALIAS("platform:as3711-regulator");
379MODULE_LICENSE("GPL v2");