aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/regulator
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2009-07-28 10:21:49 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2009-09-17 03:47:06 -0400
commite4ee831f949a7c7746a56bcf1e7ca057d6f69e2a (patch)
treec1d0967c8e9dfd71c7f330949aa4dfa5f2aab24f /drivers/regulator
parentbe721979dd6b335e4ab6f83abb5cc11c33662aa8 (diff)
regulator: Add WM831x DC-DC buck convertor support
The WM831x series of devices all have 3 DC-DC buck convertors. This driver implements software control for these regulators via the regulator API. Use with split hardware/software control of individual regulators is not supported, though regulators not controlled by software may be controlled via the hardware control interfaces. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Acked-by: Liam Girdwood <lrg@slimlogic.co.uk> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/regulator')
-rw-r--r--drivers/regulator/Kconfig7
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/wm831x-dcdc.c643
3 files changed, 651 insertions, 0 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index da7483ef32b3..38ea5dc8e143 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -82,6 +82,13 @@ config REGULATOR_TWL4030
82 This driver supports the voltage regulators provided by 82 This driver supports the voltage regulators provided by
83 this family of companion chips. 83 this family of companion chips.
84 84
85config REGULATOR_WM831X
86 tristate "Wolfson Microelcronics WM831x PMIC regulators"
87 depends on MFD_WM831X
88 help
89 Support the voltage and current regulators of the WM831x series
90 of PMIC devices.
91
85config REGULATOR_WM8350 92config REGULATOR_WM8350
86 tristate "Wolfson Microelectroncis WM8350 AudioPlus PMIC" 93 tristate "Wolfson Microelectroncis WM8350 AudioPlus PMIC"
87 depends on MFD_WM8350 94 depends on MFD_WM8350
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 3a9748ff860b..b1d2b826f532 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o
12obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o 12obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o
13obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o 13obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o
14obj-$(CONFIG_REGULATOR_TWL4030) += twl4030-regulator.o 14obj-$(CONFIG_REGULATOR_TWL4030) += twl4030-regulator.o
15obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
15obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o 16obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o
16obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o 17obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o
17obj-$(CONFIG_REGULATOR_DA903X) += da903x.o 18obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c
new file mode 100644
index 000000000000..fa5126e38acc
--- /dev/null
+++ b/drivers/regulator/wm831x-dcdc.c
@@ -0,0 +1,643 @@
1/*
2 * wm831x-dcdc.c -- DC-DC buck convertor driver for the WM831x series
3 *
4 * Copyright 2009 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 modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 */
13
14#include <linux/module.h>
15#include <linux/moduleparam.h>
16#include <linux/init.h>
17#include <linux/bitops.h>
18#include <linux/err.h>
19#include <linux/i2c.h>
20#include <linux/platform_device.h>
21#include <linux/regulator/driver.h>
22
23#include <linux/mfd/wm831x/core.h>
24#include <linux/mfd/wm831x/regulator.h>
25#include <linux/mfd/wm831x/pdata.h>
26
27#define WM831X_BUCKV_MAX_SELECTOR 0x68
28#define WM831X_BUCKP_MAX_SELECTOR 0x66
29
30#define WM831X_DCDC_MODE_FAST 0
31#define WM831X_DCDC_MODE_NORMAL 1
32#define WM831X_DCDC_MODE_IDLE 2
33#define WM831X_DCDC_MODE_STANDBY 3
34
35#define WM831X_DCDC_MAX_NAME 6
36
37/* Register offsets in control block */
38#define WM831X_DCDC_CONTROL_1 0
39#define WM831X_DCDC_CONTROL_2 1
40#define WM831X_DCDC_ON_CONFIG 2
41#define WM831X_DCDC_SLEEP_CONTROL 3
42
43/*
44 * Shared
45 */
46
47struct wm831x_dcdc {
48 char name[WM831X_DCDC_MAX_NAME];
49 struct regulator_desc desc;
50 int base;
51 struct wm831x *wm831x;
52 struct regulator_dev *regulator;
53};
54
55static int wm831x_dcdc_is_enabled(struct regulator_dev *rdev)
56{
57 struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
58 struct wm831x *wm831x = dcdc->wm831x;
59 int mask = 1 << rdev_get_id(rdev);
60 int reg;
61
62 reg = wm831x_reg_read(wm831x, WM831X_DCDC_ENABLE);
63 if (reg < 0)
64 return reg;
65
66 if (reg & mask)
67 return 1;
68 else
69 return 0;
70}
71
72static int wm831x_dcdc_enable(struct regulator_dev *rdev)
73{
74 struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
75 struct wm831x *wm831x = dcdc->wm831x;
76 int mask = 1 << rdev_get_id(rdev);
77
78 return wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE, mask, mask);
79}
80
81static int wm831x_dcdc_disable(struct regulator_dev *rdev)
82{
83 struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
84 struct wm831x *wm831x = dcdc->wm831x;
85 int mask = 1 << rdev_get_id(rdev);
86
87 return wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE, mask, 0);
88}
89
90static unsigned int wm831x_dcdc_get_mode(struct regulator_dev *rdev)
91
92{
93 struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
94 struct wm831x *wm831x = dcdc->wm831x;
95 u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG;
96 int val;
97
98 val = wm831x_reg_read(wm831x, reg);
99 if (val < 0)
100 return val;
101
102 val = (val & WM831X_DC1_ON_MODE_MASK) >> WM831X_DC1_ON_MODE_SHIFT;
103
104 switch (val) {
105 case WM831X_DCDC_MODE_FAST:
106 return REGULATOR_MODE_FAST;
107 case WM831X_DCDC_MODE_NORMAL:
108 return REGULATOR_MODE_NORMAL;
109 case WM831X_DCDC_MODE_STANDBY:
110 return REGULATOR_MODE_STANDBY;
111 case WM831X_DCDC_MODE_IDLE:
112 return REGULATOR_MODE_IDLE;
113 default:
114 BUG();
115 }
116}
117
118static int wm831x_dcdc_set_mode_int(struct wm831x *wm831x, int reg,
119 unsigned int mode)
120{
121 int val;
122
123 switch (mode) {
124 case REGULATOR_MODE_FAST:
125 val = WM831X_DCDC_MODE_FAST;
126 break;
127 case REGULATOR_MODE_NORMAL:
128 val = WM831X_DCDC_MODE_NORMAL;
129 break;
130 case REGULATOR_MODE_STANDBY:
131 val = WM831X_DCDC_MODE_STANDBY;
132 break;
133 case REGULATOR_MODE_IDLE:
134 val = WM831X_DCDC_MODE_IDLE;
135 break;
136 default:
137 return -EINVAL;
138 }
139
140 return wm831x_set_bits(wm831x, reg, WM831X_DC1_ON_MODE_MASK,
141 val << WM831X_DC1_ON_MODE_SHIFT);
142}
143
144static int wm831x_dcdc_set_mode(struct regulator_dev *rdev, unsigned int mode)
145{
146 struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
147 struct wm831x *wm831x = dcdc->wm831x;
148 u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG;
149
150 return wm831x_dcdc_set_mode_int(wm831x, reg, mode);
151}
152
153static int wm831x_dcdc_set_suspend_mode(struct regulator_dev *rdev,
154 unsigned int mode)
155{
156 struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
157 struct wm831x *wm831x = dcdc->wm831x;
158 u16 reg = dcdc->base + WM831X_DCDC_SLEEP_CONTROL;
159
160 return wm831x_dcdc_set_mode_int(wm831x, reg, mode);
161}
162
163static int wm831x_dcdc_get_status(struct regulator_dev *rdev)
164{
165 struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
166 struct wm831x *wm831x = dcdc->wm831x;
167 int ret;
168
169 /* First, check for errors */
170 ret = wm831x_reg_read(wm831x, WM831X_DCDC_UV_STATUS);
171 if (ret < 0)
172 return ret;
173
174 if (ret & (1 << rdev_get_id(rdev))) {
175 dev_dbg(wm831x->dev, "DCDC%d under voltage\n",
176 rdev_get_id(rdev) + 1);
177 return REGULATOR_STATUS_ERROR;
178 }
179
180 /* DCDC1 and DCDC2 can additionally detect high voltage/current */
181 if (rdev_get_id(rdev) < 2) {
182 if (ret & (WM831X_DC1_OV_STS << rdev_get_id(rdev))) {
183 dev_dbg(wm831x->dev, "DCDC%d over voltage\n",
184 rdev_get_id(rdev) + 1);
185 return REGULATOR_STATUS_ERROR;
186 }
187
188 if (ret & (WM831X_DC1_HC_STS << rdev_get_id(rdev))) {
189 dev_dbg(wm831x->dev, "DCDC%d over current\n",
190 rdev_get_id(rdev) + 1);
191 return REGULATOR_STATUS_ERROR;
192 }
193 }
194
195 /* Is the regulator on? */
196 ret = wm831x_reg_read(wm831x, WM831X_DCDC_STATUS);
197 if (ret < 0)
198 return ret;
199 if (!(ret & (1 << rdev_get_id(rdev))))
200 return REGULATOR_STATUS_OFF;
201
202 /* TODO: When we handle hardware control modes so we can report the
203 * current mode. */
204 return REGULATOR_STATUS_ON;
205}
206
207static irqreturn_t wm831x_dcdc_uv_irq(int irq, void *data)
208{
209 struct wm831x_dcdc *dcdc = data;
210
211 regulator_notifier_call_chain(dcdc->regulator,
212 REGULATOR_EVENT_UNDER_VOLTAGE,
213 NULL);
214
215 return IRQ_HANDLED;
216}
217
218static irqreturn_t wm831x_dcdc_oc_irq(int irq, void *data)
219{
220 struct wm831x_dcdc *dcdc = data;
221
222 regulator_notifier_call_chain(dcdc->regulator,
223 REGULATOR_EVENT_OVER_CURRENT,
224 NULL);
225
226 return IRQ_HANDLED;
227}
228
229/*
230 * BUCKV specifics
231 */
232
233static int wm831x_buckv_list_voltage(struct regulator_dev *rdev,
234 unsigned selector)
235{
236 if (selector <= 0x8)
237 return 600000;
238 if (selector <= WM831X_BUCKV_MAX_SELECTOR)
239 return 600000 + ((selector - 0x8) * 12500);
240 return -EINVAL;
241}
242
243static int wm831x_buckv_set_voltage_int(struct regulator_dev *rdev, int reg,
244 int min_uV, int max_uV)
245{
246 struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
247 struct wm831x *wm831x = dcdc->wm831x;
248 u16 vsel;
249
250 if (min_uV < 600000)
251 vsel = 0;
252 else if (min_uV <= 1800000)
253 vsel = ((min_uV - 600000) / 12500) + 8;
254 else
255 return -EINVAL;
256
257 if (wm831x_buckv_list_voltage(rdev, vsel) > max_uV)
258 return -EINVAL;
259
260 return wm831x_set_bits(wm831x, reg, WM831X_DC1_ON_VSEL_MASK, vsel);
261}
262
263static int wm831x_buckv_set_voltage(struct regulator_dev *rdev,
264 int min_uV, int max_uV)
265{
266 struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
267 u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG;
268
269 return wm831x_buckv_set_voltage_int(rdev, reg, min_uV, max_uV);
270}
271
272static int wm831x_buckv_set_suspend_voltage(struct regulator_dev *rdev,
273 int uV)
274{
275 struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
276 u16 reg = dcdc->base + WM831X_DCDC_SLEEP_CONTROL;
277
278 return wm831x_buckv_set_voltage_int(rdev, reg, uV, uV);
279}
280
281static int wm831x_buckv_get_voltage(struct regulator_dev *rdev)
282{
283 struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
284 struct wm831x *wm831x = dcdc->wm831x;
285 u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG;
286 int val;
287
288 val = wm831x_reg_read(wm831x, reg);
289 if (val < 0)
290 return val;
291
292 return wm831x_buckv_list_voltage(rdev, val & WM831X_DC1_ON_VSEL_MASK);
293}
294
295/* Current limit options */
296static u16 wm831x_dcdc_ilim[] = {
297 125, 250, 375, 500, 625, 750, 875, 1000
298};
299
300static int wm831x_buckv_set_current_limit(struct regulator_dev *rdev,
301 int min_uA, int max_uA)
302{
303 struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
304 struct wm831x *wm831x = dcdc->wm831x;
305 u16 reg = dcdc->base + WM831X_DCDC_CONTROL_2;
306 int i;
307
308 for (i = 0; i < ARRAY_SIZE(wm831x_dcdc_ilim); i++) {
309 if (max_uA <= wm831x_dcdc_ilim[i])
310 break;
311 }
312 if (i == ARRAY_SIZE(wm831x_dcdc_ilim))
313 return -EINVAL;
314
315 return wm831x_set_bits(wm831x, reg, WM831X_DC1_HC_THR_MASK, i);
316}
317
318static int wm831x_buckv_get_current_limit(struct regulator_dev *rdev)
319{
320 struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
321 struct wm831x *wm831x = dcdc->wm831x;
322 u16 reg = dcdc->base + WM831X_DCDC_CONTROL_2;
323 int val;
324
325 val = wm831x_reg_read(wm831x, reg);
326 if (val < 0)
327 return val;
328
329 return wm831x_dcdc_ilim[val & WM831X_DC1_HC_THR_MASK];
330}
331
332static struct regulator_ops wm831x_buckv_ops = {
333 .set_voltage = wm831x_buckv_set_voltage,
334 .get_voltage = wm831x_buckv_get_voltage,
335 .list_voltage = wm831x_buckv_list_voltage,
336 .set_suspend_voltage = wm831x_buckv_set_suspend_voltage,
337 .set_current_limit = wm831x_buckv_set_current_limit,
338 .get_current_limit = wm831x_buckv_get_current_limit,
339
340 .is_enabled = wm831x_dcdc_is_enabled,
341 .enable = wm831x_dcdc_enable,
342 .disable = wm831x_dcdc_disable,
343 .get_status = wm831x_dcdc_get_status,
344 .get_mode = wm831x_dcdc_get_mode,
345 .set_mode = wm831x_dcdc_set_mode,
346 .set_suspend_mode = wm831x_dcdc_set_suspend_mode,
347};
348
349static __devinit int wm831x_buckv_probe(struct platform_device *pdev)
350{
351 struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
352 struct wm831x_pdata *pdata = wm831x->dev->platform_data;
353 int id = pdev->id % ARRAY_SIZE(pdata->dcdc);
354 struct wm831x_dcdc *dcdc;
355 struct resource *res;
356 int ret, irq;
357
358 dev_dbg(&pdev->dev, "Probing DCDC%d\n", id + 1);
359
360 if (pdata == NULL || pdata->dcdc[id] == NULL)
361 return -ENODEV;
362
363 dcdc = kzalloc(sizeof(struct wm831x_dcdc), GFP_KERNEL);
364 if (dcdc == NULL) {
365 dev_err(&pdev->dev, "Unable to allocate private data\n");
366 return -ENOMEM;
367 }
368
369 dcdc->wm831x = wm831x;
370
371 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
372 if (res == NULL) {
373 dev_err(&pdev->dev, "No I/O resource\n");
374 ret = -EINVAL;
375 goto err;
376 }
377 dcdc->base = res->start;
378
379 snprintf(dcdc->name, sizeof(dcdc->name), "DCDC%d", id + 1);
380 dcdc->desc.name = dcdc->name;
381 dcdc->desc.id = id;
382 dcdc->desc.type = REGULATOR_VOLTAGE;
383 dcdc->desc.n_voltages = WM831X_BUCKV_MAX_SELECTOR + 1;
384 dcdc->desc.ops = &wm831x_buckv_ops;
385 dcdc->desc.owner = THIS_MODULE;
386
387 dcdc->regulator = regulator_register(&dcdc->desc, &pdev->dev,
388 pdata->dcdc[id], dcdc);
389 if (IS_ERR(dcdc->regulator)) {
390 ret = PTR_ERR(dcdc->regulator);
391 dev_err(wm831x->dev, "Failed to register DCDC%d: %d\n",
392 id + 1, ret);
393 goto err;
394 }
395
396 irq = platform_get_irq_byname(pdev, "UV");
397 ret = wm831x_request_irq(wm831x, irq, wm831x_dcdc_uv_irq,
398 IRQF_TRIGGER_RISING, dcdc->name,
399 dcdc);
400 if (ret != 0) {
401 dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
402 irq, ret);
403 goto err_regulator;
404 }
405
406 irq = platform_get_irq_byname(pdev, "HC");
407 ret = wm831x_request_irq(wm831x, irq, wm831x_dcdc_oc_irq,
408 IRQF_TRIGGER_RISING, dcdc->name,
409 dcdc);
410 if (ret != 0) {
411 dev_err(&pdev->dev, "Failed to request HC IRQ %d: %d\n",
412 irq, ret);
413 goto err_uv;
414 }
415
416 platform_set_drvdata(pdev, dcdc);
417
418 return 0;
419
420err_uv:
421 wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc);
422err_regulator:
423 regulator_unregister(dcdc->regulator);
424err:
425 kfree(dcdc);
426 return ret;
427}
428
429static __devexit int wm831x_buckv_remove(struct platform_device *pdev)
430{
431 struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
432 struct wm831x *wm831x = dcdc->wm831x;
433
434 wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "HC"), dcdc);
435 wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc);
436 regulator_unregister(dcdc->regulator);
437 kfree(dcdc);
438
439 return 0;
440}
441
442static struct platform_driver wm831x_buckv_driver = {
443 .probe = wm831x_buckv_probe,
444 .remove = __devexit_p(wm831x_buckv_remove),
445 .driver = {
446 .name = "wm831x-buckv",
447 },
448};
449
450/*
451 * BUCKP specifics
452 */
453
454static int wm831x_buckp_list_voltage(struct regulator_dev *rdev,
455 unsigned selector)
456{
457 if (selector <= WM831X_BUCKP_MAX_SELECTOR)
458 return 850000 + (selector * 25000);
459 else
460 return -EINVAL;
461}
462
463static int wm831x_buckp_set_voltage_int(struct regulator_dev *rdev, int reg,
464 int min_uV, int max_uV)
465{
466 struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
467 struct wm831x *wm831x = dcdc->wm831x;
468 u16 vsel;
469
470 if (min_uV <= 34000000)
471 vsel = (min_uV - 850000) / 25000;
472 else
473 return -EINVAL;
474
475 if (wm831x_buckp_list_voltage(rdev, vsel) > max_uV)
476 return -EINVAL;
477
478 return wm831x_set_bits(wm831x, reg, WM831X_DC3_ON_VSEL_MASK, vsel);
479}
480
481static int wm831x_buckp_set_voltage(struct regulator_dev *rdev,
482 int min_uV, int max_uV)
483{
484 struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
485 u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG;
486
487 return wm831x_buckp_set_voltage_int(rdev, reg, min_uV, max_uV);
488}
489
490static int wm831x_buckp_set_suspend_voltage(struct regulator_dev *rdev,
491 int uV)
492{
493 struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
494 u16 reg = dcdc->base + WM831X_DCDC_SLEEP_CONTROL;
495
496 return wm831x_buckp_set_voltage_int(rdev, reg, uV, uV);
497}
498
499static int wm831x_buckp_get_voltage(struct regulator_dev *rdev)
500{
501 struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
502 struct wm831x *wm831x = dcdc->wm831x;
503 u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG;
504 int val;
505
506 val = wm831x_reg_read(wm831x, reg);
507 if (val < 0)
508 return val;
509
510 return wm831x_buckp_list_voltage(rdev, val & WM831X_DC3_ON_VSEL_MASK);
511}
512
513static struct regulator_ops wm831x_buckp_ops = {
514 .set_voltage = wm831x_buckp_set_voltage,
515 .get_voltage = wm831x_buckp_get_voltage,
516 .list_voltage = wm831x_buckp_list_voltage,
517 .set_suspend_voltage = wm831x_buckp_set_suspend_voltage,
518
519 .is_enabled = wm831x_dcdc_is_enabled,
520 .enable = wm831x_dcdc_enable,
521 .disable = wm831x_dcdc_disable,
522 .get_status = wm831x_dcdc_get_status,
523 .get_mode = wm831x_dcdc_get_mode,
524 .set_mode = wm831x_dcdc_set_mode,
525 .set_suspend_mode = wm831x_dcdc_set_suspend_mode,
526};
527
528static __devinit int wm831x_buckp_probe(struct platform_device *pdev)
529{
530 struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
531 struct wm831x_pdata *pdata = wm831x->dev->platform_data;
532 int id = pdev->id % ARRAY_SIZE(pdata->dcdc);
533 struct wm831x_dcdc *dcdc;
534 struct resource *res;
535 int ret, irq;
536
537 dev_dbg(&pdev->dev, "Probing DCDC%d\n", id + 1);
538
539 if (pdata == NULL || pdata->dcdc[id] == NULL)
540 return -ENODEV;
541
542 dcdc = kzalloc(sizeof(struct wm831x_dcdc), GFP_KERNEL);
543 if (dcdc == NULL) {
544 dev_err(&pdev->dev, "Unable to allocate private data\n");
545 return -ENOMEM;
546 }
547
548 dcdc->wm831x = wm831x;
549
550 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
551 if (res == NULL) {
552 dev_err(&pdev->dev, "No I/O resource\n");
553 ret = -EINVAL;
554 goto err;
555 }
556 dcdc->base = res->start;
557
558 snprintf(dcdc->name, sizeof(dcdc->name), "DCDC%d", id + 1);
559 dcdc->desc.name = dcdc->name;
560 dcdc->desc.id = id;
561 dcdc->desc.type = REGULATOR_VOLTAGE;
562 dcdc->desc.n_voltages = WM831X_BUCKP_MAX_SELECTOR + 1;
563 dcdc->desc.ops = &wm831x_buckp_ops;
564 dcdc->desc.owner = THIS_MODULE;
565
566 dcdc->regulator = regulator_register(&dcdc->desc, &pdev->dev,
567 pdata->dcdc[id], dcdc);
568 if (IS_ERR(dcdc->regulator)) {
569 ret = PTR_ERR(dcdc->regulator);
570 dev_err(wm831x->dev, "Failed to register DCDC%d: %d\n",
571 id + 1, ret);
572 goto err;
573 }
574
575 irq = platform_get_irq_byname(pdev, "UV");
576 ret = wm831x_request_irq(wm831x, irq, wm831x_dcdc_uv_irq,
577 IRQF_TRIGGER_RISING, dcdc->name,
578 dcdc);
579 if (ret != 0) {
580 dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
581 irq, ret);
582 goto err_regulator;
583 }
584
585 platform_set_drvdata(pdev, dcdc);
586
587 return 0;
588
589err_regulator:
590 regulator_unregister(dcdc->regulator);
591err:
592 kfree(dcdc);
593 return ret;
594}
595
596static __devexit int wm831x_buckp_remove(struct platform_device *pdev)
597{
598 struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
599 struct wm831x *wm831x = dcdc->wm831x;
600
601 wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc);
602 regulator_unregister(dcdc->regulator);
603 kfree(dcdc);
604
605 return 0;
606}
607
608static struct platform_driver wm831x_buckp_driver = {
609 .probe = wm831x_buckp_probe,
610 .remove = __devexit_p(wm831x_buckp_remove),
611 .driver = {
612 .name = "wm831x-buckp",
613 },
614};
615
616static int __init wm831x_dcdc_init(void)
617{
618 int ret;
619 ret = platform_driver_register(&wm831x_buckv_driver);
620 if (ret != 0)
621 pr_err("Failed to register WM831x BUCKV driver: %d\n", ret);
622
623 ret = platform_driver_register(&wm831x_buckp_driver);
624 if (ret != 0)
625 pr_err("Failed to register WM831x BUCKP driver: %d\n", ret);
626
627 return 0;
628}
629subsys_initcall(wm831x_dcdc_init);
630
631static void __exit wm831x_dcdc_exit(void)
632{
633 platform_driver_unregister(&wm831x_buckp_driver);
634 platform_driver_unregister(&wm831x_buckv_driver);
635}
636module_exit(wm831x_dcdc_exit);
637
638/* Module information */
639MODULE_AUTHOR("Mark Brown");
640MODULE_DESCRIPTION("WM831x DC-DC convertor driver");
641MODULE_LICENSE("GPL");
642MODULE_ALIAS("platform:wm831x-buckv");
643MODULE_ALIAS("platform:wm831x-buckp");