aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2010-01-25 14:41:57 -0500
committerLiam Girdwood <lrg@slimlogic.co.uk>2010-03-03 09:49:25 -0500
commit69dc16c325bef32b0a1a1abf15ae4047174cafc1 (patch)
treebb41933b94da89526efabaead5d693c46d904b70
parenta71b797fdc672714bfff1fdc142042a95e97d7ba (diff)
regulator: Add WM8994 regulator support
The WM8994 contains two LDOs with mixed hardware/software control to minimise the number of external supplies required while delivering optimal voltages to minimise power consumption. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk>
-rw-r--r--drivers/regulator/Kconfig7
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/wm8994-regulator.c304
3 files changed, 312 insertions, 0 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 2bc01ee9d9f2..3c07169498cf 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -111,6 +111,13 @@ config REGULATOR_WM8400
111 This driver provides support for the voltage regulators of the 111 This driver provides support for the voltage regulators of the
112 WM8400 AudioPlus PMIC. 112 WM8400 AudioPlus PMIC.
113 113
114config REGULATOR_WM8994
115 tristate "Wolfson Microelectronics WM8994 CODEC"
116 depends on MFD_WM8994
117 help
118 This driver provides support for the voltage regulators on the
119 WM8994 CODEC.
120
114config REGULATOR_DA903X 121config REGULATOR_DA903X
115 tristate "Support regulators on Dialog Semiconductor DA9030/DA9034 PMIC" 122 tristate "Support regulators on Dialog Semiconductor DA9030/DA9034 PMIC"
116 depends on PMIC_DA903X 123 depends on PMIC_DA903X
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 075835be4396..7c59bcb10613 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o
19obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o 19obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o
20obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o 20obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o
21obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o 21obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o
22obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o
22obj-$(CONFIG_REGULATOR_DA903X) += da903x.o 23obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
23obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o 24obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
24obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o 25obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o
diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c
new file mode 100644
index 000000000000..d09e018b3177
--- /dev/null
+++ b/drivers/regulator/wm8994-regulator.c
@@ -0,0 +1,304 @@
1/*
2 * wm8994-regulator.c -- Regulator driver for the WM8994
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/platform_device.h>
20#include <linux/regulator/driver.h>
21#include <linux/gpio.h>
22
23#include <linux/mfd/wm8994/core.h>
24#include <linux/mfd/wm8994/registers.h>
25#include <linux/mfd/wm8994/pdata.h>
26
27struct wm8994_ldo {
28 int enable;
29 int is_enabled;
30 struct regulator_dev *regulator;
31 struct wm8994 *wm8994;
32};
33
34#define WM8994_LDO1_MAX_SELECTOR 0x7
35#define WM8994_LDO2_MAX_SELECTOR 0x3
36
37static int wm8994_ldo_enable(struct regulator_dev *rdev)
38{
39 struct wm8994_ldo *ldo = rdev_get_drvdata(rdev);
40
41 /* If we have no soft control assume that the LDO is always enabled. */
42 if (!ldo->enable)
43 return 0;
44
45 gpio_set_value(ldo->enable, 1);
46 ldo->is_enabled = 1;
47
48 return 0;
49}
50
51static int wm8994_ldo_disable(struct regulator_dev *rdev)
52{
53 struct wm8994_ldo *ldo = rdev_get_drvdata(rdev);
54
55 /* If we have no soft control assume that the LDO is always enabled. */
56 if (!ldo->enable)
57 return -EINVAL;
58
59 gpio_set_value(ldo->enable, 0);
60 ldo->is_enabled = 0;
61
62 return 0;
63}
64
65static int wm8994_ldo_is_enabled(struct regulator_dev *rdev)
66{
67 struct wm8994_ldo *ldo = rdev_get_drvdata(rdev);
68
69 return ldo->is_enabled;
70}
71
72static int wm8994_ldo_enable_time(struct regulator_dev *rdev)
73{
74 /* 3ms is fairly conservative but this shouldn't be too performance
75 * critical; can be tweaked per-system if required. */
76 return 3000;
77}
78
79static int wm8994_ldo1_list_voltage(struct regulator_dev *rdev,
80 unsigned int selector)
81{
82 if (selector > WM8994_LDO1_MAX_SELECTOR)
83 return -EINVAL;
84
85 return (selector * 100000) + 2400000;
86}
87
88static int wm8994_ldo1_get_voltage(struct regulator_dev *rdev)
89{
90 struct wm8994_ldo *ldo = rdev_get_drvdata(rdev);
91 int val;
92
93 val = wm8994_reg_read(ldo->wm8994, WM8994_LDO_1);
94 if (val < 0)
95 return val;
96
97 val = (val & WM8994_LDO1_VSEL_MASK) >> WM8994_LDO1_VSEL_SHIFT;
98
99 return wm8994_ldo1_list_voltage(rdev, val);
100}
101
102static int wm8994_ldo1_set_voltage(struct regulator_dev *rdev,
103 int min_uV, int max_uV)
104{
105 struct wm8994_ldo *ldo = rdev_get_drvdata(rdev);
106 int selector, v;
107
108 selector = (min_uV - 2400000) / 100000;
109 v = wm8994_ldo1_list_voltage(rdev, selector);
110 if (v < 0 || v > max_uV)
111 return -EINVAL;
112
113 selector <<= WM8994_LDO1_VSEL_SHIFT;
114
115 return wm8994_set_bits(ldo->wm8994, WM8994_LDO_1,
116 WM8994_LDO1_VSEL_MASK, selector);
117}
118
119static struct regulator_ops wm8994_ldo1_ops = {
120 .enable = wm8994_ldo_enable,
121 .disable = wm8994_ldo_disable,
122 .is_enabled = wm8994_ldo_is_enabled,
123 .enable_time = wm8994_ldo_enable_time,
124
125 .list_voltage = wm8994_ldo1_list_voltage,
126 .get_voltage = wm8994_ldo1_get_voltage,
127 .set_voltage = wm8994_ldo1_set_voltage,
128};
129
130static int wm8994_ldo2_list_voltage(struct regulator_dev *rdev,
131 unsigned int selector)
132{
133 if (selector > WM8994_LDO2_MAX_SELECTOR)
134 return -EINVAL;
135
136 return (selector * 100000) + 900000;
137}
138
139static int wm8994_ldo2_get_voltage(struct regulator_dev *rdev)
140{
141 struct wm8994_ldo *ldo = rdev_get_drvdata(rdev);
142 int val;
143
144 val = wm8994_reg_read(ldo->wm8994, WM8994_LDO_2);
145 if (val < 0)
146 return val;
147
148 val = (val & WM8994_LDO2_VSEL_MASK) >> WM8994_LDO2_VSEL_SHIFT;
149
150 return wm8994_ldo2_list_voltage(rdev, val);
151}
152
153static int wm8994_ldo2_set_voltage(struct regulator_dev *rdev,
154 int min_uV, int max_uV)
155{
156 struct wm8994_ldo *ldo = rdev_get_drvdata(rdev);
157 int selector, v;
158
159 selector = (min_uV - 900000) / 100000;
160 v = wm8994_ldo2_list_voltage(rdev, selector);
161 if (v < 0 || v > max_uV)
162 return -EINVAL;
163
164 selector <<= WM8994_LDO2_VSEL_SHIFT;
165
166 return wm8994_set_bits(ldo->wm8994, WM8994_LDO_2,
167 WM8994_LDO2_VSEL_MASK, selector);
168}
169
170static struct regulator_ops wm8994_ldo2_ops = {
171 .enable = wm8994_ldo_enable,
172 .disable = wm8994_ldo_disable,
173 .is_enabled = wm8994_ldo_is_enabled,
174 .enable_time = wm8994_ldo_enable_time,
175
176 .list_voltage = wm8994_ldo2_list_voltage,
177 .get_voltage = wm8994_ldo2_get_voltage,
178 .set_voltage = wm8994_ldo2_set_voltage,
179};
180
181static struct regulator_desc wm8994_ldo_desc[] = {
182 {
183 .name = "LDO1",
184 .id = 1,
185 .type = REGULATOR_VOLTAGE,
186 .n_voltages = WM8994_LDO1_MAX_SELECTOR + 1,
187 .ops = &wm8994_ldo1_ops,
188 .owner = THIS_MODULE,
189 },
190 {
191 .name = "LDO2",
192 .id = 2,
193 .type = REGULATOR_VOLTAGE,
194 .n_voltages = WM8994_LDO2_MAX_SELECTOR + 1,
195 .ops = &wm8994_ldo2_ops,
196 .owner = THIS_MODULE,
197 },
198};
199
200static __devinit int wm8994_ldo_probe(struct platform_device *pdev)
201{
202 struct wm8994 *wm8994 = dev_get_drvdata(pdev->dev.parent);
203 struct wm8994_pdata *pdata = wm8994->dev->platform_data;
204 int id = pdev->id % ARRAY_SIZE(pdata->ldo);
205 struct wm8994_ldo *ldo;
206 int ret;
207
208 dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1);
209
210 if (!pdata)
211 return -ENODEV;
212
213 ldo = kzalloc(sizeof(struct wm8994_ldo), GFP_KERNEL);
214 if (ldo == NULL) {
215 dev_err(&pdev->dev, "Unable to allocate private data\n");
216 return -ENOMEM;
217 }
218
219 ldo->wm8994 = wm8994;
220
221 ldo->is_enabled = 1;
222
223 if (pdata->ldo[id].enable && gpio_is_valid(pdata->ldo[id].enable)) {
224 ldo->enable = pdata->ldo[id].enable;
225
226 ret = gpio_request(ldo->enable, "WM8994 LDO enable");
227 if (ret < 0) {
228 dev_err(&pdev->dev, "Failed to get enable GPIO: %d\n",
229 ret);
230 goto err;
231 }
232
233 ret = gpio_direction_output(ldo->enable, ldo->is_enabled);
234 if (ret < 0) {
235 dev_err(&pdev->dev, "Failed to set GPIO up: %d\n",
236 ret);
237 goto err_gpio;
238 }
239 }
240
241 ldo->regulator = regulator_register(&wm8994_ldo_desc[id], &pdev->dev,
242 pdata->ldo[id].init_data, ldo);
243 if (IS_ERR(ldo->regulator)) {
244 ret = PTR_ERR(ldo->regulator);
245 dev_err(wm8994->dev, "Failed to register LDO%d: %d\n",
246 id + 1, ret);
247 goto err_gpio;
248 }
249
250 platform_set_drvdata(pdev, ldo);
251
252 return 0;
253
254err_gpio:
255 if (gpio_is_valid(ldo->enable))
256 gpio_free(ldo->enable);
257err:
258 kfree(ldo);
259 return ret;
260}
261
262static __devexit int wm8994_ldo_remove(struct platform_device *pdev)
263{
264 struct wm8994_ldo *ldo = platform_get_drvdata(pdev);
265
266 regulator_unregister(ldo->regulator);
267 if (gpio_is_valid(ldo->enable))
268 gpio_free(ldo->enable);
269 kfree(ldo);
270
271 return 0;
272}
273
274static struct platform_driver wm8994_ldo_driver = {
275 .probe = wm8994_ldo_probe,
276 .remove = __devexit_p(wm8994_ldo_remove),
277 .driver = {
278 .name = "wm8994-ldo",
279 },
280};
281
282static int __init wm8994_ldo_init(void)
283{
284 int ret;
285
286 ret = platform_driver_register(&wm8994_ldo_driver);
287 if (ret != 0)
288 pr_err("Failed to register Wm8994 GP LDO driver: %d\n", ret);
289
290 return ret;
291}
292subsys_initcall(wm8994_ldo_init);
293
294static void __exit wm8994_ldo_exit(void)
295{
296 platform_driver_unregister(&wm8994_ldo_driver);
297}
298module_exit(wm8994_ldo_exit);
299
300/* Module information */
301MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
302MODULE_DESCRIPTION("WM8994 LDO driver");
303MODULE_LICENSE("GPL");
304MODULE_ALIAS("platform:wm8994-ldo");