Skip to content

Commit 8501e75

Browse files
mmindRohan Somvanshi
authored andcommitted
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 <[email protected]> Signed-off-by: Mark Brown <[email protected]> (cherry picked from commit 3f0292a) regulator: Add module.h include to gpio-regulator Reported-by: Stephen Rothwell <[email protected]> Signed-off-by: Mark Brown <[email protected]> (cherry picked from commit ecc37ed) regulator: Fix compile break due to missing arguments to regulator_register The commit 2c043bc ("regulator: pass additional of_node to regulator_register()") caused a compile break because it missed updating the regulator_register() call in gpio-regulator.c with the additional parameter (NULL). The compile break as reported by Stephen Rothwell with the x86_64 allmodconfig looked like this drivers/regulator/gpio-regulator.c: In function 'gpio_regulator_probe': drivers/regulator/gpio-regulator.c:287:8: error: too few arguments to function 'regulator_register' include/linux/regulator/driver.h:215:23: note: declared here Reported-by: Stephen Rothwell <[email protected]> Signed-off-by: Rajendra Nayak <[email protected]> Signed-off-by: Mark Brown <[email protected]> (cherry picked from commit 1568434) Change-Id: I912886aae825ca440f4ad3e7a33fe4e84bde4e1b Reviewed-on: http://git-master/r/74924 Reviewed-by: Laxman Dewangan <[email protected]> Tested-by: Laxman Dewangan <[email protected]> Reviewed-on: http://git-master/r/75547 Reviewed-by: Varun Wadekar <[email protected]> Tested-by: Varun Wadekar <[email protected]>
1 parent 6887f3b commit 8501e75

File tree

4 files changed

+455
-0
lines changed

4 files changed

+455
-0
lines changed

drivers/regulator/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,15 @@ config REGULATOR_USERSPACE_CONSUMER
6464

6565
If unsure, say no.
6666

67+
config 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+
6776
config REGULATOR_BQ24022
6877
tristate "TI bq24022 Dual Input 1-Cell Li-Ion Charger IC"
6978
help

drivers/regulator/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o
88
obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o
99
obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o
1010

11+
obj-$(CONFIG_REGULATOR_GPIO) += gpio-regulator.o
1112
obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o
1213
obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o
1314
obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o

drivers/regulator/gpio-regulator.c

Lines changed: 358 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,358 @@
1+
/*
2+
* gpio-regulator.c
3+
*
4+
* Copyright 2011 Heiko Stuebner <[email protected]>
5+
*
6+
* based on fixed.c
7+
*
8+
* Copyright 2008 Wolfson Microelectronics PLC.
9+
*
10+
* Author: Mark Brown <[email protected]>
11+
*
12+
* Copyright (c) 2009 Nokia Corporation
13+
* Roger Quadros <[email protected]>
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/module.h>
28+
#include <linux/platform_device.h>
29+
#include <linux/regulator/driver.h>
30+
#include <linux/regulator/machine.h>
31+
#include <linux/regulator/gpio-regulator.h>
32+
#include <linux/gpio.h>
33+
#include <linux/delay.h>
34+
#include <linux/slab.h>
35+
36+
struct gpio_regulator_data {
37+
struct regulator_desc desc;
38+
struct regulator_dev *dev;
39+
40+
int enable_gpio;
41+
bool enable_high;
42+
bool is_enabled;
43+
unsigned startup_delay;
44+
45+
struct gpio *gpios;
46+
int nr_gpios;
47+
48+
struct gpio_regulator_state *states;
49+
int nr_states;
50+
51+
int state;
52+
};
53+
54+
static int gpio_regulator_is_enabled(struct regulator_dev *dev)
55+
{
56+
struct gpio_regulator_data *data = rdev_get_drvdata(dev);
57+
58+
return data->is_enabled;
59+
}
60+
61+
static int gpio_regulator_enable(struct regulator_dev *dev)
62+
{
63+
struct gpio_regulator_data *data = rdev_get_drvdata(dev);
64+
65+
if (gpio_is_valid(data->enable_gpio)) {
66+
gpio_set_value_cansleep(data->enable_gpio, data->enable_high);
67+
data->is_enabled = true;
68+
}
69+
70+
return 0;
71+
}
72+
73+
static int gpio_regulator_disable(struct regulator_dev *dev)
74+
{
75+
struct gpio_regulator_data *data = rdev_get_drvdata(dev);
76+
77+
if (gpio_is_valid(data->enable_gpio)) {
78+
gpio_set_value_cansleep(data->enable_gpio, !data->enable_high);
79+
data->is_enabled = false;
80+
}
81+
82+
return 0;
83+
}
84+
85+
static int gpio_regulator_enable_time(struct regulator_dev *dev)
86+
{
87+
struct gpio_regulator_data *data = rdev_get_drvdata(dev);
88+
89+
return data->startup_delay;
90+
}
91+
92+
static int gpio_regulator_get_value(struct regulator_dev *dev)
93+
{
94+
struct gpio_regulator_data *data = rdev_get_drvdata(dev);
95+
int ptr;
96+
97+
for (ptr = 0; ptr < data->nr_states; ptr++)
98+
if (data->states[ptr].gpios == data->state)
99+
return data->states[ptr].value;
100+
101+
return -EINVAL;
102+
}
103+
104+
static int gpio_regulator_set_value(struct regulator_dev *dev,
105+
int min, int max)
106+
{
107+
struct gpio_regulator_data *data = rdev_get_drvdata(dev);
108+
int ptr, target, state;
109+
110+
target = -1;
111+
for (ptr = 0; ptr < data->nr_states; ptr++)
112+
if (data->states[ptr].value >= min &&
113+
data->states[ptr].value <= max)
114+
target = data->states[ptr].gpios;
115+
116+
if (target < 0)
117+
return -EINVAL;
118+
119+
for (ptr = 0; ptr < data->nr_gpios; ptr++) {
120+
state = (target & (1 << ptr)) >> ptr;
121+
gpio_set_value(data->gpios[ptr].gpio, state);
122+
}
123+
data->state = target;
124+
125+
return 0;
126+
}
127+
128+
static int gpio_regulator_set_voltage(struct regulator_dev *dev,
129+
int min_uV, int max_uV,
130+
unsigned *selector)
131+
{
132+
return gpio_regulator_set_value(dev, min_uV, max_uV);
133+
}
134+
135+
static int gpio_regulator_list_voltage(struct regulator_dev *dev,
136+
unsigned selector)
137+
{
138+
struct gpio_regulator_data *data = rdev_get_drvdata(dev);
139+
140+
if (selector >= data->nr_states)
141+
return -EINVAL;
142+
143+
return data->states[selector].value;
144+
}
145+
146+
static int gpio_regulator_set_current_limit(struct regulator_dev *dev,
147+
int min_uA, int max_uA)
148+
{
149+
return gpio_regulator_set_value(dev, min_uA, max_uA);
150+
}
151+
152+
static struct regulator_ops gpio_regulator_voltage_ops = {
153+
.is_enabled = gpio_regulator_is_enabled,
154+
.enable = gpio_regulator_enable,
155+
.disable = gpio_regulator_disable,
156+
.enable_time = gpio_regulator_enable_time,
157+
.get_voltage = gpio_regulator_get_value,
158+
.set_voltage = gpio_regulator_set_voltage,
159+
.list_voltage = gpio_regulator_list_voltage,
160+
};
161+
162+
static struct regulator_ops gpio_regulator_current_ops = {
163+
.is_enabled = gpio_regulator_is_enabled,
164+
.enable = gpio_regulator_enable,
165+
.disable = gpio_regulator_disable,
166+
.enable_time = gpio_regulator_enable_time,
167+
.get_current_limit = gpio_regulator_get_value,
168+
.set_current_limit = gpio_regulator_set_current_limit,
169+
};
170+
171+
static int __devinit gpio_regulator_probe(struct platform_device *pdev)
172+
{
173+
struct gpio_regulator_config *config = pdev->dev.platform_data;
174+
struct gpio_regulator_data *drvdata;
175+
int ptr, ret, state;
176+
177+
drvdata = kzalloc(sizeof(struct gpio_regulator_data), GFP_KERNEL);
178+
if (drvdata == NULL) {
179+
dev_err(&pdev->dev, "Failed to allocate device data\n");
180+
return -ENOMEM;
181+
}
182+
183+
drvdata->desc.name = kstrdup(config->supply_name, GFP_KERNEL);
184+
if (drvdata->desc.name == NULL) {
185+
dev_err(&pdev->dev, "Failed to allocate supply name\n");
186+
ret = -ENOMEM;
187+
goto err;
188+
}
189+
190+
drvdata->gpios = kmemdup(config->gpios,
191+
config->nr_gpios * sizeof(struct gpio),
192+
GFP_KERNEL);
193+
if (drvdata->gpios == NULL) {
194+
dev_err(&pdev->dev, "Failed to allocate gpio data\n");
195+
ret = -ENOMEM;
196+
goto err_name;
197+
}
198+
199+
drvdata->states = kmemdup(config->states,
200+
config->nr_states *
201+
sizeof(struct gpio_regulator_state),
202+
GFP_KERNEL);
203+
if (drvdata->states == NULL) {
204+
dev_err(&pdev->dev, "Failed to allocate state data\n");
205+
ret = -ENOMEM;
206+
goto err_memgpio;
207+
}
208+
drvdata->nr_states = config->nr_states;
209+
210+
drvdata->desc.owner = THIS_MODULE;
211+
212+
/* handle regulator type*/
213+
switch (config->type) {
214+
case REGULATOR_VOLTAGE:
215+
drvdata->desc.type = REGULATOR_VOLTAGE;
216+
drvdata->desc.ops = &gpio_regulator_voltage_ops;
217+
drvdata->desc.n_voltages = config->nr_states;
218+
break;
219+
case REGULATOR_CURRENT:
220+
drvdata->desc.type = REGULATOR_CURRENT;
221+
drvdata->desc.ops = &gpio_regulator_current_ops;
222+
break;
223+
default:
224+
dev_err(&pdev->dev, "No regulator type set\n");
225+
ret = -EINVAL;
226+
goto err_memgpio;
227+
break;
228+
}
229+
230+
drvdata->enable_gpio = config->enable_gpio;
231+
drvdata->startup_delay = config->startup_delay;
232+
233+
if (gpio_is_valid(config->enable_gpio)) {
234+
drvdata->enable_high = config->enable_high;
235+
236+
ret = gpio_request(config->enable_gpio, config->supply_name);
237+
if (ret) {
238+
dev_err(&pdev->dev,
239+
"Could not obtain regulator enable GPIO %d: %d\n",
240+
config->enable_gpio, ret);
241+
goto err_memstate;
242+
}
243+
244+
/* set output direction without changing state
245+
* to prevent glitch
246+
*/
247+
if (config->enabled_at_boot) {
248+
drvdata->is_enabled = true;
249+
ret = gpio_direction_output(config->enable_gpio,
250+
config->enable_high);
251+
} else {
252+
drvdata->is_enabled = false;
253+
ret = gpio_direction_output(config->enable_gpio,
254+
!config->enable_high);
255+
}
256+
257+
if (ret) {
258+
dev_err(&pdev->dev,
259+
"Could not configure regulator enable GPIO %d direction: %d\n",
260+
config->enable_gpio, ret);
261+
goto err_enablegpio;
262+
}
263+
} else {
264+
/* Regulator without GPIO control is considered
265+
* always enabled
266+
*/
267+
drvdata->is_enabled = true;
268+
}
269+
270+
drvdata->nr_gpios = config->nr_gpios;
271+
ret = gpio_request_array(drvdata->gpios, drvdata->nr_gpios);
272+
if (ret) {
273+
dev_err(&pdev->dev,
274+
"Could not obtain regulator setting GPIOs: %d\n", ret);
275+
goto err_enablegpio;
276+
}
277+
278+
/* build initial state from gpio init data. */
279+
state = 0;
280+
for (ptr = 0; ptr < drvdata->nr_gpios; ptr++) {
281+
if (config->gpios[ptr].flags & GPIOF_OUT_INIT_HIGH)
282+
state |= (1 << ptr);
283+
}
284+
drvdata->state = state;
285+
286+
drvdata->dev = regulator_register(&drvdata->desc, &pdev->dev,
287+
config->init_data, drvdata, NULL);
288+
if (IS_ERR(drvdata->dev)) {
289+
ret = PTR_ERR(drvdata->dev);
290+
dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret);
291+
goto err_stategpio;
292+
}
293+
294+
platform_set_drvdata(pdev, drvdata);
295+
296+
return 0;
297+
298+
err_stategpio:
299+
gpio_free_array(drvdata->gpios, drvdata->nr_gpios);
300+
err_enablegpio:
301+
if (gpio_is_valid(config->enable_gpio))
302+
gpio_free(config->enable_gpio);
303+
err_memstate:
304+
kfree(drvdata->states);
305+
err_memgpio:
306+
kfree(drvdata->gpios);
307+
err_name:
308+
kfree(drvdata->desc.name);
309+
err:
310+
kfree(drvdata);
311+
return ret;
312+
}
313+
314+
static int __devexit gpio_regulator_remove(struct platform_device *pdev)
315+
{
316+
struct gpio_regulator_data *drvdata = platform_get_drvdata(pdev);
317+
318+
regulator_unregister(drvdata->dev);
319+
320+
gpio_free_array(drvdata->gpios, drvdata->nr_gpios);
321+
322+
kfree(drvdata->states);
323+
kfree(drvdata->gpios);
324+
325+
if (gpio_is_valid(drvdata->enable_gpio))
326+
gpio_free(drvdata->enable_gpio);
327+
328+
kfree(drvdata->desc.name);
329+
kfree(drvdata);
330+
331+
return 0;
332+
}
333+
334+
static struct platform_driver gpio_regulator_driver = {
335+
.probe = gpio_regulator_probe,
336+
.remove = __devexit_p(gpio_regulator_remove),
337+
.driver = {
338+
.name = "gpio-regulator",
339+
.owner = THIS_MODULE,
340+
},
341+
};
342+
343+
static int __init gpio_regulator_init(void)
344+
{
345+
return platform_driver_register(&gpio_regulator_driver);
346+
}
347+
subsys_initcall(gpio_regulator_init);
348+
349+
static void __exit gpio_regulator_exit(void)
350+
{
351+
platform_driver_unregister(&gpio_regulator_driver);
352+
}
353+
module_exit(gpio_regulator_exit);
354+
355+
MODULE_AUTHOR("Heiko Stuebner <[email protected]>");
356+
MODULE_DESCRIPTION("gpio voltage regulator");
357+
MODULE_LICENSE("GPL");
358+
MODULE_ALIAS("platform:gpio-regulator");

0 commit comments

Comments
 (0)