aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2009-07-28 10:23:46 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2009-09-17 03:47:11 -0400
commitd4d6b722e780f005f0d4e43a43909fa51cc33a11 (patch)
treee70606c2ff3c5f49238053b1fe882ef009a682b4
parent1304850d4c5d2f915bdcb8d547f3ef26c60cc825 (diff)
regulator: Add WM831x ISINK support
The WM831x series of PMICs provide two constant current sinks designed to drive strings of serially connected LEDs for applications such as backlights. This driver adds support for those regulators. 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>
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/wm831x-isink.c260
2 files changed, 261 insertions, 0 deletions
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index a0a635fdae87..3a4cdf5f7935 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -13,6 +13,7 @@ obj-$(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_WM831X) += wm831x-dcdc.o
16obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o
16obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o 17obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o
17obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o 18obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o
18obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o 19obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o
diff --git a/drivers/regulator/wm831x-isink.c b/drivers/regulator/wm831x-isink.c
new file mode 100644
index 000000000000..1d8d9879d3a1
--- /dev/null
+++ b/drivers/regulator/wm831x-isink.c
@@ -0,0 +1,260 @@
1/*
2 * wm831x-isink.c -- Current sink 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_ISINK_MAX_NAME 7
28
29struct wm831x_isink {
30 char name[WM831X_ISINK_MAX_NAME];
31 struct regulator_desc desc;
32 int reg;
33 struct wm831x *wm831x;
34 struct regulator_dev *regulator;
35};
36
37static int wm831x_isink_enable(struct regulator_dev *rdev)
38{
39 struct wm831x_isink *isink = rdev_get_drvdata(rdev);
40 struct wm831x *wm831x = isink->wm831x;
41 int ret;
42
43 /* We have a two stage enable: first start the ISINK... */
44 ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_ENA,
45 WM831X_CS1_ENA);
46 if (ret != 0)
47 return ret;
48
49 /* ...then enable drive */
50 ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_DRIVE,
51 WM831X_CS1_DRIVE);
52 if (ret != 0)
53 wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_ENA, 0);
54
55 return ret;
56
57}
58
59static int wm831x_isink_disable(struct regulator_dev *rdev)
60{
61 struct wm831x_isink *isink = rdev_get_drvdata(rdev);
62 struct wm831x *wm831x = isink->wm831x;
63 int ret;
64
65 ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_DRIVE, 0);
66 if (ret < 0)
67 return ret;
68
69 ret = wm831x_set_bits(wm831x, isink->reg, WM831X_CS1_ENA, 0);
70 if (ret < 0)
71 return ret;
72
73 return ret;
74
75}
76
77static int wm831x_isink_is_enabled(struct regulator_dev *rdev)
78{
79 struct wm831x_isink *isink = rdev_get_drvdata(rdev);
80 struct wm831x *wm831x = isink->wm831x;
81 int ret;
82
83 ret = wm831x_reg_read(wm831x, isink->reg);
84 if (ret < 0)
85 return ret;
86
87 if ((ret & (WM831X_CS1_ENA | WM831X_CS1_DRIVE)) ==
88 (WM831X_CS1_ENA | WM831X_CS1_DRIVE))
89 return 1;
90 else
91 return 0;
92}
93
94static int wm831x_isink_set_current(struct regulator_dev *rdev,
95 int min_uA, int max_uA)
96{
97 struct wm831x_isink *isink = rdev_get_drvdata(rdev);
98 struct wm831x *wm831x = isink->wm831x;
99 int ret, i;
100
101 for (i = 0; i < ARRAY_SIZE(wm831x_isinkv_values); i++) {
102 int val = wm831x_isinkv_values[i];
103 if (min_uA >= val && val <= max_uA) {
104 ret = wm831x_set_bits(wm831x, isink->reg,
105 WM831X_CS1_ISEL_MASK, i);
106 return ret;
107 }
108 }
109
110 return -EINVAL;
111}
112
113static int wm831x_isink_get_current(struct regulator_dev *rdev)
114{
115 struct wm831x_isink *isink = rdev_get_drvdata(rdev);
116 struct wm831x *wm831x = isink->wm831x;
117 int ret;
118
119 ret = wm831x_reg_read(wm831x, isink->reg);
120 if (ret < 0)
121 return ret;
122
123 ret &= WM831X_CS1_ISEL_MASK;
124 if (ret > WM831X_ISINK_MAX_ISEL)
125 ret = WM831X_ISINK_MAX_ISEL;
126
127 return wm831x_isinkv_values[ret];
128}
129
130static struct regulator_ops wm831x_isink_ops = {
131 .is_enabled = wm831x_isink_is_enabled,
132 .enable = wm831x_isink_enable,
133 .disable = wm831x_isink_disable,
134 .set_current_limit = wm831x_isink_set_current,
135 .get_current_limit = wm831x_isink_get_current,
136};
137
138static irqreturn_t wm831x_isink_irq(int irq, void *data)
139{
140 struct wm831x_isink *isink = data;
141
142 regulator_notifier_call_chain(isink->regulator,
143 REGULATOR_EVENT_OVER_CURRENT,
144 NULL);
145
146 return IRQ_HANDLED;
147}
148
149
150static __devinit int wm831x_isink_probe(struct platform_device *pdev)
151{
152 struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
153 struct wm831x_pdata *pdata = wm831x->dev->platform_data;
154 struct wm831x_isink *isink;
155 int id = pdev->id % ARRAY_SIZE(pdata->isink);
156 struct resource *res;
157 int ret, irq;
158
159 dev_dbg(&pdev->dev, "Probing ISINK%d\n", id + 1);
160
161 if (pdata == NULL || pdata->isink[id] == NULL)
162 return -ENODEV;
163
164 isink = kzalloc(sizeof(struct wm831x_isink), GFP_KERNEL);
165 if (isink == NULL) {
166 dev_err(&pdev->dev, "Unable to allocate private data\n");
167 return -ENOMEM;
168 }
169
170 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
171 if (res == NULL) {
172 dev_err(&pdev->dev, "No I/O resource\n");
173 ret = -EINVAL;
174 goto err;
175 }
176 isink->reg = res->start;
177
178 /* For current parts this is correct; probably need to revisit
179 * in future.
180 */
181 snprintf(isink->name, sizeof(isink->name), "ISINK%d", id + 1);
182 isink->desc.name = isink->name;
183 isink->desc.id = id;
184 isink->desc.ops = &wm831x_isink_ops;
185 isink->desc.type = REGULATOR_CURRENT;
186 isink->desc.owner = THIS_MODULE;
187
188 isink->regulator = regulator_register(&isink->desc, &pdev->dev,
189 pdata->isink[id], isink);
190 if (IS_ERR(isink->regulator)) {
191 ret = PTR_ERR(isink->regulator);
192 dev_err(wm831x->dev, "Failed to register ISINK%d: %d\n",
193 id + 1, ret);
194 goto err;
195 }
196
197 irq = platform_get_irq(pdev, 0);
198 ret = wm831x_request_irq(wm831x, irq, wm831x_isink_irq,
199 IRQF_TRIGGER_RISING, isink->name,
200 isink);
201 if (ret != 0) {
202 dev_err(&pdev->dev, "Failed to request ISINK IRQ %d: %d\n",
203 irq, ret);
204 goto err_regulator;
205 }
206
207 platform_set_drvdata(pdev, isink);
208
209 return 0;
210
211err_regulator:
212 regulator_unregister(isink->regulator);
213err:
214 kfree(isink);
215 return ret;
216}
217
218static __devexit int wm831x_isink_remove(struct platform_device *pdev)
219{
220 struct wm831x_isink *isink = platform_get_drvdata(pdev);
221 struct wm831x *wm831x = isink->wm831x;
222
223 wm831x_free_irq(wm831x, platform_get_irq(pdev, 0), isink);
224
225 regulator_unregister(isink->regulator);
226 kfree(isink);
227
228 return 0;
229}
230
231static struct platform_driver wm831x_isink_driver = {
232 .probe = wm831x_isink_probe,
233 .remove = __devexit_p(wm831x_isink_remove),
234 .driver = {
235 .name = "wm831x-isink",
236 },
237};
238
239static int __init wm831x_isink_init(void)
240{
241 int ret;
242 ret = platform_driver_register(&wm831x_isink_driver);
243 if (ret != 0)
244 pr_err("Failed to register WM831x ISINK driver: %d\n", ret);
245
246 return ret;
247}
248subsys_initcall(wm831x_isink_init);
249
250static void __exit wm831x_isink_exit(void)
251{
252 platform_driver_unregister(&wm831x_isink_driver);
253}
254module_exit(wm831x_isink_exit);
255
256/* Module information */
257MODULE_AUTHOR("Mark Brown");
258MODULE_DESCRIPTION("WM831x current sink driver");
259MODULE_LICENSE("GPL");
260MODULE_ALIAS("platform:wm831x-isink");