aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/regulator/wm8400-regulator.c
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2008-09-11 06:12:01 -0400
committerLiam Girdwood <lrg@slimlogic.co.uk>2008-10-13 16:51:53 -0400
commit42fad570b666256a3fd009e23e74cbb365a29ca8 (patch)
tree73257001bdb14a56ed69b504106e8fe392ef2f7c /drivers/regulator/wm8400-regulator.c
parent1d9f9f040035da73d6ee5d2b3b3a25483a980da3 (diff)
regulator: Add WM8400 regulator support
The WM8400 provides two programmable DCDC step-down (buck) convertors and four low-dropout (LDO) regulators. This driver provides support for runtime managment of these in the standard regulator API. Support for configuration of the suspend and hibernate mode behaviour of the regulators is not yet included. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk>
Diffstat (limited to 'drivers/regulator/wm8400-regulator.c')
-rw-r--r--drivers/regulator/wm8400-regulator.c368
1 files changed, 368 insertions, 0 deletions
diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c
new file mode 100644
index 000000000000..48b372e038a8
--- /dev/null
+++ b/drivers/regulator/wm8400-regulator.c
@@ -0,0 +1,368 @@
1/*
2 * Regulator support for WM8400
3 *
4 * Copyright 2008 Wolfson Microelectronics PLC.
5 *
6 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
12 *
13 */
14
15#include <linux/bug.h>
16#include <linux/err.h>
17#include <linux/kernel.h>
18#include <linux/regulator/driver.h>
19#include <linux/mfd/wm8400-private.h>
20
21static int wm8400_ldo_is_enabled(struct regulator_dev *dev)
22{
23 struct wm8400 *wm8400 = rdev_get_drvdata(dev);
24 u16 val;
25
26 val = wm8400_reg_read(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev));
27 return (val & WM8400_LDO1_ENA) != 0;
28}
29
30static int wm8400_ldo_enable(struct regulator_dev *dev)
31{
32 struct wm8400 *wm8400 = rdev_get_drvdata(dev);
33
34 return wm8400_set_bits(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev),
35 WM8400_LDO1_ENA, WM8400_LDO1_ENA);
36}
37
38static int wm8400_ldo_disable(struct regulator_dev *dev)
39{
40 struct wm8400 *wm8400 = rdev_get_drvdata(dev);
41
42 return wm8400_set_bits(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev),
43 WM8400_LDO1_ENA, 0);
44}
45
46static int wm8400_ldo_get_voltage(struct regulator_dev *dev)
47{
48 struct wm8400 *wm8400 = rdev_get_drvdata(dev);
49 u16 val;
50
51 val = wm8400_reg_read(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev));
52 val &= WM8400_LDO1_VSEL_MASK;
53
54 if (val < 15)
55 return 900000 + (val * 50000);
56 else
57 return 1600000 + ((val - 14) * 100000);
58}
59
60static int wm8400_ldo_set_voltage(struct regulator_dev *dev,
61 int min_uV, int max_uV)
62{
63 struct wm8400 *wm8400 = rdev_get_drvdata(dev);
64 u16 val;
65
66 if (min_uV < 900000 || min_uV > 3300000)
67 return -EINVAL;
68
69 if (min_uV < 1700000) {
70 /* Steps of 50mV from 900mV; */
71 val = (min_uV - 850001) / 50000;
72
73 if ((val * 50000) + 900000 > max_uV)
74 return -EINVAL;
75 BUG_ON((val * 50000) + 900000 < min_uV);
76 } else {
77 /* Steps of 100mV from 1700mV */
78 val = ((min_uV - 1600001) / 100000);
79
80 if ((val * 100000) + 1700000 > max_uV)
81 return -EINVAL;
82 BUG_ON((val * 100000) + 1700000 < min_uV);
83
84 val += 0xf;
85 }
86
87 return wm8400_set_bits(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev),
88 WM8400_LDO1_VSEL_MASK, val);
89}
90
91static struct regulator_ops wm8400_ldo_ops = {
92 .is_enabled = wm8400_ldo_is_enabled,
93 .enable = wm8400_ldo_enable,
94 .disable = wm8400_ldo_disable,
95 .get_voltage = wm8400_ldo_get_voltage,
96 .set_voltage = wm8400_ldo_set_voltage,
97};
98
99static int wm8400_dcdc_is_enabled(struct regulator_dev *dev)
100{
101 struct wm8400 *wm8400 = rdev_get_drvdata(dev);
102 int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
103 u16 val;
104
105 val = wm8400_reg_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset);
106 return (val & WM8400_DC1_ENA) != 0;
107}
108
109static int wm8400_dcdc_enable(struct regulator_dev *dev)
110{
111 struct wm8400 *wm8400 = rdev_get_drvdata(dev);
112 int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
113
114 return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
115 WM8400_DC1_ENA, WM8400_DC1_ENA);
116}
117
118static int wm8400_dcdc_disable(struct regulator_dev *dev)
119{
120 struct wm8400 *wm8400 = rdev_get_drvdata(dev);
121 int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
122
123 return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
124 WM8400_DC1_ENA, 0);
125}
126
127static int wm8400_dcdc_get_voltage(struct regulator_dev *dev)
128{
129 struct wm8400 *wm8400 = rdev_get_drvdata(dev);
130 u16 val;
131 int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
132
133 val = wm8400_reg_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset);
134 val &= WM8400_DC1_VSEL_MASK;
135
136 return 850000 + (25000 * val);
137}
138
139static int wm8400_dcdc_set_voltage(struct regulator_dev *dev,
140 int min_uV, int max_uV)
141{
142 struct wm8400 *wm8400 = rdev_get_drvdata(dev);
143 u16 val;
144 int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
145
146 if (min_uV < 850000)
147 return -EINVAL;
148
149 val = (min_uV - 825001) / 25000;
150
151 if (850000 + (25000 * val) > max_uV)
152 return -EINVAL;
153 BUG_ON(850000 + (25000 * val) < min_uV);
154
155 return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
156 WM8400_DC1_VSEL_MASK, val);
157}
158
159static unsigned int wm8400_dcdc_get_mode(struct regulator_dev *dev)
160{
161 struct wm8400 *wm8400 = rdev_get_drvdata(dev);
162 int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
163 u16 data[2];
164 int ret;
165
166 ret = wm8400_block_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset, 2,
167 data);
168 if (ret != 0)
169 return 0;
170
171 /* Datasheet: hibernate */
172 if (data[0] & WM8400_DC1_SLEEP)
173 return REGULATOR_MODE_STANDBY;
174
175 /* Datasheet: standby */
176 if (!(data[0] & WM8400_DC1_ACTIVE))
177 return REGULATOR_MODE_IDLE;
178
179 /* Datasheet: active with or without force PWM */
180 if (data[1] & WM8400_DC1_FRC_PWM)
181 return REGULATOR_MODE_FAST;
182 else
183 return REGULATOR_MODE_NORMAL;
184}
185
186static int wm8400_dcdc_set_mode(struct regulator_dev *dev, unsigned int mode)
187{
188 struct wm8400 *wm8400 = rdev_get_drvdata(dev);
189 int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
190 int ret;
191
192 switch (mode) {
193 case REGULATOR_MODE_FAST:
194 /* Datasheet: active with force PWM */
195 ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_2 + offset,
196 WM8400_DC1_FRC_PWM, WM8400_DC1_FRC_PWM);
197 if (ret != 0)
198 return ret;
199
200 return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
201 WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP,
202 WM8400_DC1_ACTIVE);
203
204 case REGULATOR_MODE_NORMAL:
205 /* Datasheet: active */
206 ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_2 + offset,
207 WM8400_DC1_FRC_PWM, 0);
208 if (ret != 0)
209 return ret;
210
211 return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
212 WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP,
213 WM8400_DC1_ACTIVE);
214
215 case REGULATOR_MODE_IDLE:
216 /* Datasheet: standby */
217 ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
218 WM8400_DC1_ACTIVE, 0);
219 if (ret != 0)
220 return ret;
221 return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
222 WM8400_DC1_SLEEP, 0);
223
224 default:
225 return -EINVAL;
226 }
227}
228
229static unsigned int wm8400_dcdc_get_optimum_mode(struct regulator_dev *dev,
230 int input_uV, int output_uV,
231 int load_uA)
232{
233 return REGULATOR_MODE_NORMAL;
234}
235
236static struct regulator_ops wm8400_dcdc_ops = {
237 .is_enabled = wm8400_dcdc_is_enabled,
238 .enable = wm8400_dcdc_enable,
239 .disable = wm8400_dcdc_disable,
240 .get_voltage = wm8400_dcdc_get_voltage,
241 .set_voltage = wm8400_dcdc_set_voltage,
242 .get_mode = wm8400_dcdc_get_mode,
243 .set_mode = wm8400_dcdc_set_mode,
244 .get_optimum_mode = wm8400_dcdc_get_optimum_mode,
245};
246
247static struct regulator_desc regulators[] = {
248 {
249 .name = "LDO1",
250 .id = WM8400_LDO1,
251 .ops = &wm8400_ldo_ops,
252 .type = REGULATOR_VOLTAGE,
253 .owner = THIS_MODULE,
254 },
255 {
256 .name = "LDO2",
257 .id = WM8400_LDO2,
258 .ops = &wm8400_ldo_ops,
259 .type = REGULATOR_VOLTAGE,
260 .owner = THIS_MODULE,
261 },
262 {
263 .name = "LDO3",
264 .id = WM8400_LDO3,
265 .ops = &wm8400_ldo_ops,
266 .type = REGULATOR_VOLTAGE,
267 .owner = THIS_MODULE,
268 },
269 {
270 .name = "LDO4",
271 .id = WM8400_LDO4,
272 .ops = &wm8400_ldo_ops,
273 .type = REGULATOR_VOLTAGE,
274 .owner = THIS_MODULE,
275 },
276 {
277 .name = "DCDC1",
278 .id = WM8400_DCDC1,
279 .ops = &wm8400_dcdc_ops,
280 .type = REGULATOR_VOLTAGE,
281 .owner = THIS_MODULE,
282 },
283 {
284 .name = "DCDC2",
285 .id = WM8400_DCDC2,
286 .ops = &wm8400_dcdc_ops,
287 .type = REGULATOR_VOLTAGE,
288 .owner = THIS_MODULE,
289 },
290};
291
292static int __init wm8400_regulator_probe(struct platform_device *pdev)
293{
294 struct regulator_dev *rdev;
295
296 rdev = regulator_register(&regulators[pdev->id], &pdev->dev,
297 pdev->dev.driver_data);
298
299 if (IS_ERR(rdev))
300 return PTR_ERR(rdev);
301
302 return 0;
303}
304
305static int __devexit wm8400_regulator_remove(struct platform_device *pdev)
306{
307 struct regulator_dev *rdev = platform_get_drvdata(pdev);
308
309 regulator_unregister(rdev);
310
311 return 0;
312}
313
314static struct platform_driver wm8400_regulator_driver = {
315 .driver = {
316 .name = "wm8400-regulator",
317 },
318 .probe = wm8400_regulator_probe,
319 .remove = __devexit_p(wm8400_regulator_remove),
320};
321
322/**
323 * wm8400_register_regulator - enable software control of a WM8400 regulator
324 *
325 * This function enables software control of a WM8400 regulator via
326 * the regulator API. It is intended to be called from the
327 * platform_init() callback of the WM8400 MFD driver.
328 *
329 * @param dev The WM8400 device to operate on.
330 * @param reg The regulator to control.
331 * @param initdata Regulator initdata for the regulator.
332 */
333int wm8400_register_regulator(struct device *dev, int reg,
334 struct regulator_init_data *initdata)
335{
336 struct wm8400 *wm8400 = dev->driver_data;
337
338 if (wm8400->regulators[reg].name)
339 return -EBUSY;
340
341 initdata->driver_data = wm8400;
342
343 wm8400->regulators[reg].name = "wm8400-regulator";
344 wm8400->regulators[reg].id = reg;
345 wm8400->regulators[reg].dev.parent = dev;
346 wm8400->regulators[reg].dev.driver_data = wm8400;
347 wm8400->regulators[reg].dev.platform_data = initdata;
348
349 return platform_device_register(&wm8400->regulators[reg]);
350}
351EXPORT_SYMBOL_GPL(wm8400_register_regulator);
352
353static int __init wm8400_regulator_init(void)
354{
355 return platform_driver_register(&wm8400_regulator_driver);
356}
357module_init(wm8400_regulator_init);
358
359static void __exit wm8400_regulator_exit(void)
360{
361 platform_driver_unregister(&wm8400_regulator_driver);
362}
363module_exit(wm8400_regulator_exit);
364
365MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
366MODULE_DESCRIPTION("WM8400 regulator driver");
367MODULE_LICENSE("GPL");
368MODULE_ALIAS("platform:wm8400-regulator");