aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/regulator
diff options
context:
space:
mode:
authorHeiko Stübner <heiko@sntech.de>2011-10-05 06:27:05 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2011-10-09 07:36:21 -0400
commit3f0292ae8bb100cc8f96106a3de277df48134887 (patch)
tree6638f4fb724c40042690364d9f682f7350f32110 /drivers/regulator
parente3efe6669bf9c3cbd955b5a2976c70d79e8fd745 (diff)
regulator: Add driver for gpio-controlled regulators
This patch adds support for regulators that can be controlled via gpios. Examples for such regulators are the TI-tps65024x voltage regulators with 4 fixed and 1 runtime-switchable voltage regulators or the TI-bq240XX charger regulators. The number of controlling gpios is not limited, the mapping between voltage/current and target gpio state is done via the states map and the driver can be used for either voltage or current regulators. A mapping for a regulator with two GPIOs could look like: gpios = { { .gpio = GPIO1, .flags = GPIOF_OUT_INIT_HIGH, .label = "gpio name 1" }, { .gpio = GPIO2, .flags = GPIOF_OUT_INIT_LOW, .label = "gpio name 2" }, } The flags element of the gpios array determines the initial state of the gpio, set during probe. The initial state of the regulator is also calculated from these values states = { { .value = volt_or_cur1, .gpios = (0 << 1) | (0 << 0) }, { .value = volt_or_cur2, .gpios = (0 << 1) | (1 << 0) }, { .value = volt_or_cur3, .gpios = (1 << 1) | (0 << 0) }, { .value = volt_or_cur4, .gpios = (1 << 1) | (1 << 0) }, } The target-state for the n-th gpio is determined by the n-th bit in the bitfield of the target-value. Signed-off-by: Heiko Stuebner <heiko@sntech.de> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'drivers/regulator')
-rw-r--r--drivers/regulator/Kconfig9
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/gpio-regulator.c357
3 files changed, 367 insertions, 0 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index c7fd2c0e3f2b..5e0c7b664732 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -64,6 +64,15 @@ config REGULATOR_USERSPACE_CONSUMER
64 64
65 If unsure, say no. 65 If unsure, say no.
66 66
67config REGULATOR_GPIO
68 tristate "GPIO regulator support"
69 help
70 This driver provides support for regulators that can be
71 controlled via gpios.
72 It is capable of supporting current and voltage regulators
73 and the platform has to provide a mapping of GPIO-states
74 to target volts/amps.
75
67config REGULATOR_BQ24022 76config REGULATOR_BQ24022
68 tristate "TI bq24022 Dual Input 1-Cell Li-Ion Charger IC" 77 tristate "TI bq24022 Dual Input 1-Cell Li-Ion Charger IC"
69 help 78 help
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 040d5aa63535..93a6318f5328 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o
8obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o 8obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o
9obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o 9obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o
10 10
11obj-$(CONFIG_REGULATOR_GPIO) += gpio-regulator.o
11obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o 12obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o
12obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o 13obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o
13obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o 14obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o
diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c
new file mode 100644
index 000000000000..abf32ad6f573
--- /dev/null
+++ b/drivers/regulator/gpio-regulator.c
@@ -0,0 +1,357 @@
1/*
2 * gpio-regulator.c
3 *
4 * Copyright 2011 Heiko Stuebner <heiko@sntech.de>
5 *
6 * based on fixed.c
7 *
8 * Copyright 2008 Wolfson Microelectronics PLC.
9 *
10 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
11 *
12 * Copyright (c) 2009 Nokia Corporation
13 * Roger Quadros <ext-roger.quadros@nokia.com>
14 *
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License as
17 * published by the Free Software Foundation; either version 2 of the
18 * License, or (at your option) any later version.
19 *
20 * This is useful for systems with mixed controllable and
21 * non-controllable regulators, as well as for allowing testing on
22 * systems with no controllable regulators.
23 */
24
25#include <linux/err.h>
26#include <linux/mutex.h>
27#include <linux/platform_device.h>
28#include <linux/regulator/driver.h>
29#include <linux/regulator/machine.h>
30#include <linux/regulator/gpio-regulator.h>
31#include <linux/gpio.h>
32#include <linux/delay.h>
33#include <linux/slab.h>
34
35struct gpio_regulator_data {
36 struct regulator_desc desc;
37 struct regulator_dev *dev;
38
39 int enable_gpio;
40 bool enable_high;
41 bool is_enabled;
42 unsigned startup_delay;
43
44 struct gpio *gpios;
45 int nr_gpios;
46
47 struct gpio_regulator_state *states;
48 int nr_states;
49
50 int state;
51};
52
53static int gpio_regulator_is_enabled(struct regulator_dev *dev)
54{
55 struct gpio_regulator_data *data = rdev_get_drvdata(dev);
56
57 return data->is_enabled;
58}
59
60static int gpio_regulator_enable(struct regulator_dev *dev)
61{
62 struct gpio_regulator_data *data = rdev_get_drvdata(dev);
63
64 if (gpio_is_valid(data->enable_gpio)) {
65 gpio_set_value_cansleep(data->enable_gpio, data->enable_high);
66 data->is_enabled = true;
67 }
68
69 return 0;
70}
71
72static int gpio_regulator_disable(struct regulator_dev *dev)
73{
74 struct gpio_regulator_data *data = rdev_get_drvdata(dev);
75
76 if (gpio_is_valid(data->enable_gpio)) {
77 gpio_set_value_cansleep(data->enable_gpio, !data->enable_high);
78 data->is_enabled = false;
79 }
80
81 return 0;
82}
83
84static int gpio_regulator_enable_time(struct regulator_dev *dev)
85{
86 struct gpio_regulator_data *data = rdev_get_drvdata(dev);
87
88 return data->startup_delay;
89}
90
91static int gpio_regulator_get_value(struct regulator_dev *dev)
92{
93 struct gpio_regulator_data *data = rdev_get_drvdata(dev);
94 int ptr;
95
96 for (ptr = 0; ptr < data->nr_states; ptr++)
97 if (data->states[ptr].gpios == data->state)
98 return data->states[ptr].value;
99
100 return -EINVAL;
101}
102
103static int gpio_regulator_set_value(struct regulator_dev *dev,
104 int min, int max)
105{
106 struct gpio_regulator_data *data = rdev_get_drvdata(dev);
107 int ptr, target, state;
108
109 target = -1;
110 for (ptr = 0; ptr < data->nr_states; ptr++)
111 if (data->states[ptr].value >= min &&
112 data->states[ptr].value <= max)
113 target = data->states[ptr].gpios;
114
115 if (target < 0)
116 return -EINVAL;
117
118 for (ptr = 0; ptr < data->nr_gpios; ptr++) {
119 state = (target & (1 << ptr)) >> ptr;
120 gpio_set_value(data->gpios[ptr].gpio, state);
121 }
122 data->state = target;
123
124 return 0;
125}
126
127static int gpio_regulator_set_voltage(struct regulator_dev *dev,
128 int min_uV, int max_uV,
129 unsigned *selector)
130{
131 return gpio_regulator_set_value(dev, min_uV, max_uV);
132}
133
134static int gpio_regulator_list_voltage(struct regulator_dev *dev,
135 unsigned selector)
136{
137 struct gpio_regulator_data *data = rdev_get_drvdata(dev);
138
139 if (selector >= data->nr_states)
140 return -EINVAL;
141
142 return data->states[selector].value;
143}
144
145static int gpio_regulator_set_current_limit(struct regulator_dev *dev,
146 int min_uA, int max_uA)
147{
148 return gpio_regulator_set_value(dev, min_uA, max_uA);
149}
150
151static struct regulator_ops gpio_regulator_voltage_ops = {
152 .is_enabled = gpio_regulator_is_enabled,
153 .enable = gpio_regulator_enable,
154 .disable = gpio_regulator_disable,
155 .enable_time = gpio_regulator_enable_time,
156 .get_voltage = gpio_regulator_get_value,
157 .set_voltage = gpio_regulator_set_voltage,
158 .list_voltage = gpio_regulator_list_voltage,
159};
160
161static struct regulator_ops gpio_regulator_current_ops = {
162 .is_enabled = gpio_regulator_is_enabled,
163 .enable = gpio_regulator_enable,
164 .disable = gpio_regulator_disable,
165 .enable_time = gpio_regulator_enable_time,
166 .get_current_limit = gpio_regulator_get_value,
167 .set_current_limit = gpio_regulator_set_current_limit,
168};
169
170static int __devinit gpio_regulator_probe(struct platform_device *pdev)
171{
172 struct gpio_regulator_config *config = pdev->dev.platform_data;
173 struct gpio_regulator_data *drvdata;
174 int ptr, ret, state;
175
176 drvdata = kzalloc(sizeof(struct gpio_regulator_data), GFP_KERNEL);
177 if (drvdata == NULL) {
178 dev_err(&pdev->dev, "Failed to allocate device data\n");
179 return -ENOMEM;
180 }
181
182 drvdata->desc.name = kstrdup(config->supply_name, GFP_KERNEL);
183 if (drvdata->desc.name == NULL) {
184 dev_err(&pdev->dev, "Failed to allocate supply name\n");
185 ret = -ENOMEM;
186 goto err;
187 }
188
189 drvdata->gpios = kmemdup(config->gpios,
190 config->nr_gpios * sizeof(struct gpio),
191 GFP_KERNEL);
192 if (drvdata->gpios == NULL) {
193 dev_err(&pdev->dev, "Failed to allocate gpio data\n");
194 ret = -ENOMEM;
195 goto err_name;
196 }
197
198 drvdata->states = kmemdup(config->states,
199 config->nr_states *
200 sizeof(struct gpio_regulator_state),
201 GFP_KERNEL);
202 if (drvdata->states == NULL) {
203 dev_err(&pdev->dev, "Failed to allocate state data\n");
204 ret = -ENOMEM;
205 goto err_memgpio;
206 }
207 drvdata->nr_states = config->nr_states;
208
209 drvdata->desc.owner = THIS_MODULE;
210
211 /* handle regulator type*/
212 switch (config->type) {
213 case REGULATOR_VOLTAGE:
214 drvdata->desc.type = REGULATOR_VOLTAGE;
215 drvdata->desc.ops = &gpio_regulator_voltage_ops;
216 drvdata->desc.n_voltages = config->nr_states;
217 break;
218 case REGULATOR_CURRENT:
219 drvdata->desc.type = REGULATOR_CURRENT;
220 drvdata->desc.ops = &gpio_regulator_current_ops;
221 break;
222 default:
223 dev_err(&pdev->dev, "No regulator type set\n");
224 ret = -EINVAL;
225 goto err_memgpio;
226 break;
227 }
228
229 drvdata->enable_gpio = config->enable_gpio;
230 drvdata->startup_delay = config->startup_delay;
231
232 if (gpio_is_valid(config->enable_gpio)) {
233 drvdata->enable_high = config->enable_high;
234
235 ret = gpio_request(config->enable_gpio, config->supply_name);
236 if (ret) {
237 dev_err(&pdev->dev,
238 "Could not obtain regulator enable GPIO %d: %d\n",
239 config->enable_gpio, ret);
240 goto err_memstate;
241 }
242
243 /* set output direction without changing state
244 * to prevent glitch
245 */
246 if (config->enabled_at_boot) {
247 drvdata->is_enabled = true;
248 ret = gpio_direction_output(config->enable_gpio,
249 config->enable_high);
250 } else {
251 drvdata->is_enabled = false;
252 ret = gpio_direction_output(config->enable_gpio,
253 !config->enable_high);
254 }
255
256 if (ret) {
257 dev_err(&pdev->dev,
258 "Could not configure regulator enable GPIO %d direction: %d\n",
259 config->enable_gpio, ret);
260 goto err_enablegpio;
261 }
262 } else {
263 /* Regulator without GPIO control is considered
264 * always enabled
265 */
266 drvdata->is_enabled = true;
267 }
268
269 drvdata->nr_gpios = config->nr_gpios;
270 ret = gpio_request_array(drvdata->gpios, drvdata->nr_gpios);
271 if (ret) {
272 dev_err(&pdev->dev,
273 "Could not obtain regulator setting GPIOs: %d\n", ret);
274 goto err_enablegpio;
275 }
276
277 /* build initial state from gpio init data. */
278 state = 0;
279 for (ptr = 0; ptr < drvdata->nr_gpios; ptr++) {
280 if (config->gpios[ptr].flags & GPIOF_OUT_INIT_HIGH)
281 state |= (1 << ptr);
282 }
283 drvdata->state = state;
284
285 drvdata->dev = regulator_register(&drvdata->desc, &pdev->dev,
286 config->init_data, drvdata);
287 if (IS_ERR(drvdata->dev)) {
288 ret = PTR_ERR(drvdata->dev);
289 dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret);
290 goto err_stategpio;
291 }
292
293 platform_set_drvdata(pdev, drvdata);
294
295 return 0;
296
297err_stategpio:
298 gpio_free_array(drvdata->gpios, drvdata->nr_gpios);
299err_enablegpio:
300 if (gpio_is_valid(config->enable_gpio))
301 gpio_free(config->enable_gpio);
302err_memstate:
303 kfree(drvdata->states);
304err_memgpio:
305 kfree(drvdata->gpios);
306err_name:
307 kfree(drvdata->desc.name);
308err:
309 kfree(drvdata);
310 return ret;
311}
312
313static int __devexit gpio_regulator_remove(struct platform_device *pdev)
314{
315 struct gpio_regulator_data *drvdata = platform_get_drvdata(pdev);
316
317 regulator_unregister(drvdata->dev);
318
319 gpio_free_array(drvdata->gpios, drvdata->nr_gpios);
320
321 kfree(drvdata->states);
322 kfree(drvdata->gpios);
323
324 if (gpio_is_valid(drvdata->enable_gpio))
325 gpio_free(drvdata->enable_gpio);
326
327 kfree(drvdata->desc.name);
328 kfree(drvdata);
329
330 return 0;
331}
332
333static struct platform_driver gpio_regulator_driver = {
334 .probe = gpio_regulator_probe,
335 .remove = __devexit_p(gpio_regulator_remove),
336 .driver = {
337 .name = "gpio-regulator",
338 .owner = THIS_MODULE,
339 },
340};
341
342static int __init gpio_regulator_init(void)
343{
344 return platform_driver_register(&gpio_regulator_driver);
345}
346subsys_initcall(gpio_regulator_init);
347
348static void __exit gpio_regulator_exit(void)
349{
350 platform_driver_unregister(&gpio_regulator_driver);
351}
352module_exit(gpio_regulator_exit);
353
354MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
355MODULE_DESCRIPTION("gpio voltage regulator");
356MODULE_LICENSE("GPL");
357MODULE_ALIAS("platform:gpio-regulator");