]>
Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
3f0292ae HS |
2 | /* |
3 | * gpio-regulator.c | |
4 | * | |
5 | * Copyright 2011 Heiko Stuebner <heiko@sntech.de> | |
6 | * | |
7 | * based on fixed.c | |
8 | * | |
9 | * Copyright 2008 Wolfson Microelectronics PLC. | |
10 | * | |
11 | * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> | |
12 | * | |
13 | * Copyright (c) 2009 Nokia Corporation | |
14 | * Roger Quadros <ext-roger.quadros@nokia.com> | |
15 | * | |
3f0292ae HS |
16 | * This is useful for systems with mixed controllable and |
17 | * non-controllable regulators, as well as for allowing testing on | |
18 | * systems with no controllable regulators. | |
19 | */ | |
20 | ||
21 | #include <linux/err.h> | |
22 | #include <linux/mutex.h> | |
ecc37edf | 23 | #include <linux/module.h> |
3f0292ae HS |
24 | #include <linux/platform_device.h> |
25 | #include <linux/regulator/driver.h> | |
26 | #include <linux/regulator/machine.h> | |
006694d0 | 27 | #include <linux/regulator/of_regulator.h> |
3f0292ae | 28 | #include <linux/regulator/gpio-regulator.h> |
d6cd33ad | 29 | #include <linux/gpio/consumer.h> |
3f0292ae | 30 | #include <linux/slab.h> |
006694d0 | 31 | #include <linux/of.h> |
3f0292ae HS |
32 | |
33 | struct gpio_regulator_data { | |
34 | struct regulator_desc desc; | |
3f0292ae | 35 | |
d6cd33ad | 36 | struct gpio_desc **gpiods; |
3f0292ae HS |
37 | int nr_gpios; |
38 | ||
39 | struct gpio_regulator_state *states; | |
40 | int nr_states; | |
41 | ||
42 | int state; | |
43 | }; | |
44 | ||
3f0292ae HS |
45 | static int gpio_regulator_get_value(struct regulator_dev *dev) |
46 | { | |
47 | struct gpio_regulator_data *data = rdev_get_drvdata(dev); | |
48 | int ptr; | |
49 | ||
50 | for (ptr = 0; ptr < data->nr_states; ptr++) | |
51 | if (data->states[ptr].gpios == data->state) | |
52 | return data->states[ptr].value; | |
53 | ||
54 | return -EINVAL; | |
55 | } | |
56 | ||
eb0c5686 HS |
57 | static int gpio_regulator_set_voltage(struct regulator_dev *dev, |
58 | int min_uV, int max_uV, | |
59 | unsigned *selector) | |
3f0292ae HS |
60 | { |
61 | struct gpio_regulator_data *data = rdev_get_drvdata(dev); | |
00926369 | 62 | int ptr, target = 0, state, best_val = INT_MAX; |
3f0292ae | 63 | |
3f0292ae | 64 | for (ptr = 0; ptr < data->nr_states; ptr++) |
4dbd8f63 | 65 | if (data->states[ptr].value < best_val && |
eb0c5686 HS |
66 | data->states[ptr].value >= min_uV && |
67 | data->states[ptr].value <= max_uV) { | |
3f0292ae | 68 | target = data->states[ptr].gpios; |
00926369 | 69 | best_val = data->states[ptr].value; |
b0e4d7bf HS |
70 | if (selector) |
71 | *selector = ptr; | |
00926369 | 72 | } |
3f0292ae | 73 | |
4dbd8f63 | 74 | if (best_val == INT_MAX) |
3f0292ae HS |
75 | return -EINVAL; |
76 | ||
77 | for (ptr = 0; ptr < data->nr_gpios; ptr++) { | |
78 | state = (target & (1 << ptr)) >> ptr; | |
d6cd33ad | 79 | gpiod_set_value_cansleep(data->gpiods[ptr], state); |
3f0292ae HS |
80 | } |
81 | data->state = target; | |
82 | ||
83 | return 0; | |
84 | } | |
85 | ||
3f0292ae HS |
86 | static int gpio_regulator_list_voltage(struct regulator_dev *dev, |
87 | unsigned selector) | |
88 | { | |
89 | struct gpio_regulator_data *data = rdev_get_drvdata(dev); | |
90 | ||
91 | if (selector >= data->nr_states) | |
92 | return -EINVAL; | |
93 | ||
94 | return data->states[selector].value; | |
95 | } | |
96 | ||
97 | static int gpio_regulator_set_current_limit(struct regulator_dev *dev, | |
98 | int min_uA, int max_uA) | |
99 | { | |
eb0c5686 HS |
100 | struct gpio_regulator_data *data = rdev_get_drvdata(dev); |
101 | int ptr, target = 0, state, best_val = 0; | |
102 | ||
103 | for (ptr = 0; ptr < data->nr_states; ptr++) | |
104 | if (data->states[ptr].value > best_val && | |
105 | data->states[ptr].value >= min_uA && | |
106 | data->states[ptr].value <= max_uA) { | |
107 | target = data->states[ptr].gpios; | |
108 | best_val = data->states[ptr].value; | |
109 | } | |
110 | ||
111 | if (best_val == 0) | |
112 | return -EINVAL; | |
113 | ||
114 | for (ptr = 0; ptr < data->nr_gpios; ptr++) { | |
115 | state = (target & (1 << ptr)) >> ptr; | |
d6cd33ad | 116 | gpiod_set_value_cansleep(data->gpiods[ptr], state); |
eb0c5686 HS |
117 | } |
118 | data->state = target; | |
119 | ||
120 | return 0; | |
3f0292ae HS |
121 | } |
122 | ||
705e2a90 | 123 | static const struct regulator_ops gpio_regulator_voltage_ops = { |
3f0292ae HS |
124 | .get_voltage = gpio_regulator_get_value, |
125 | .set_voltage = gpio_regulator_set_voltage, | |
126 | .list_voltage = gpio_regulator_list_voltage, | |
127 | }; | |
128 | ||
a451405f | 129 | static struct gpio_regulator_config * |
072e78b1 JMC |
130 | of_get_gpio_regulator_config(struct device *dev, struct device_node *np, |
131 | const struct regulator_desc *desc) | |
006694d0 LJ |
132 | { |
133 | struct gpio_regulator_config *config; | |
006694d0 | 134 | const char *regtype; |
d6cd33ad LW |
135 | int proplen, i; |
136 | int ngpios; | |
251b9c21 | 137 | int ret; |
006694d0 LJ |
138 | |
139 | config = devm_kzalloc(dev, | |
140 | sizeof(struct gpio_regulator_config), | |
141 | GFP_KERNEL); | |
142 | if (!config) | |
143 | return ERR_PTR(-ENOMEM); | |
144 | ||
072e78b1 | 145 | config->init_data = of_get_regulator_init_data(dev, np, desc); |
006694d0 LJ |
146 | if (!config->init_data) |
147 | return ERR_PTR(-EINVAL); | |
148 | ||
149 | config->supply_name = config->init_data->constraints.name; | |
150 | ||
006694d0 LJ |
151 | if (of_property_read_bool(np, "enable-at-boot")) |
152 | config->enabled_at_boot = true; | |
153 | ||
154 | of_property_read_u32(np, "startup-delay-us", &config->startup_delay); | |
155 | ||
d6cd33ad LW |
156 | /* Fetch GPIO init levels */ |
157 | ngpios = gpiod_count(dev, NULL); | |
158 | if (ngpios > 0) { | |
159 | config->gflags = devm_kzalloc(dev, | |
160 | sizeof(enum gpiod_flags) | |
161 | * ngpios, | |
162 | GFP_KERNEL); | |
163 | if (!config->gflags) | |
9f946099 RF |
164 | return ERR_PTR(-ENOMEM); |
165 | ||
d6cd33ad LW |
166 | for (i = 0; i < ngpios; i++) { |
167 | u32 val; | |
9f946099 | 168 | |
d6cd33ad LW |
169 | ret = of_property_read_u32_index(np, "gpios-states", i, |
170 | &val); | |
0094050d | 171 | |
d6cd33ad LW |
172 | /* Default to high per specification */ |
173 | if (ret) | |
174 | config->gflags[i] = GPIOD_OUT_HIGH; | |
175 | else | |
176 | config->gflags[i] = | |
177 | val ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW; | |
1f5a9623 | 178 | } |
006694d0 | 179 | } |
d6cd33ad | 180 | config->ngpios = ngpios; |
006694d0 LJ |
181 | |
182 | /* Fetch states. */ | |
934624d6 HS |
183 | proplen = of_property_count_u32_elems(np, "states"); |
184 | if (proplen < 0) { | |
216f2b9c LJ |
185 | dev_err(dev, "No 'states' property found\n"); |
186 | return ERR_PTR(-EINVAL); | |
187 | } | |
188 | ||
a86854d0 KC |
189 | config->states = devm_kcalloc(dev, |
190 | proplen / 2, | |
191 | sizeof(struct gpio_regulator_state), | |
006694d0 LJ |
192 | GFP_KERNEL); |
193 | if (!config->states) | |
194 | return ERR_PTR(-ENOMEM); | |
195 | ||
196 | for (i = 0; i < proplen / 2; i++) { | |
934624d6 HS |
197 | of_property_read_u32_index(np, "states", i * 2, |
198 | &config->states[i].value); | |
199 | of_property_read_u32_index(np, "states", i * 2 + 1, | |
200 | &config->states[i].gpios); | |
006694d0 LJ |
201 | } |
202 | config->nr_states = i; | |
203 | ||
5b1ada83 | 204 | config->type = REGULATOR_VOLTAGE; |
251b9c21 | 205 | ret = of_property_read_string(np, "regulator-type", ®type); |
5b1ada83 MB |
206 | if (ret >= 0) { |
207 | if (!strncmp("voltage", regtype, 7)) | |
208 | config->type = REGULATOR_VOLTAGE; | |
209 | else if (!strncmp("current", regtype, 7)) | |
210 | config->type = REGULATOR_CURRENT; | |
9eb9d315 MB |
211 | else |
212 | dev_warn(dev, "Unknown regulator-type '%s'\n", | |
213 | regtype); | |
251b9c21 | 214 | } |
006694d0 | 215 | |
006694d0 LJ |
216 | return config; |
217 | } | |
218 | ||
705e2a90 | 219 | static const struct regulator_ops gpio_regulator_current_ops = { |
3f0292ae HS |
220 | .get_current_limit = gpio_regulator_get_value, |
221 | .set_current_limit = gpio_regulator_set_current_limit, | |
222 | }; | |
223 | ||
a5023574 | 224 | static int gpio_regulator_probe(struct platform_device *pdev) |
3f0292ae | 225 | { |
d162d041 LW |
226 | struct device *dev = &pdev->dev; |
227 | struct gpio_regulator_config *config = dev_get_platdata(dev); | |
228 | struct device_node *np = dev->of_node; | |
3f0292ae | 229 | struct gpio_regulator_data *drvdata; |
c172708d | 230 | struct regulator_config cfg = { }; |
7cdc2ee7 | 231 | struct regulator_dev *rdev; |
d6cd33ad LW |
232 | enum gpiod_flags gflags; |
233 | int ptr, ret, state, i; | |
3f0292ae | 234 | |
d162d041 | 235 | drvdata = devm_kzalloc(dev, sizeof(struct gpio_regulator_data), |
02b55216 | 236 | GFP_KERNEL); |
9c25960c | 237 | if (drvdata == NULL) |
3f0292ae | 238 | return -ENOMEM; |
3f0292ae | 239 | |
072e78b1 | 240 | if (np) { |
d162d041 | 241 | config = of_get_gpio_regulator_config(dev, np, |
072e78b1 JMC |
242 | &drvdata->desc); |
243 | if (IS_ERR(config)) | |
244 | return PTR_ERR(config); | |
245 | } | |
246 | ||
d162d041 | 247 | drvdata->desc.name = devm_kstrdup(dev, config->supply_name, GFP_KERNEL); |
3f0292ae | 248 | if (drvdata->desc.name == NULL) { |
d162d041 | 249 | dev_err(dev, "Failed to allocate supply name\n"); |
ed8cffda | 250 | return -ENOMEM; |
3f0292ae HS |
251 | } |
252 | ||
d162d041 | 253 | drvdata->gpiods = devm_kzalloc(dev, sizeof(struct gpio_desc *), |
d6cd33ad LW |
254 | GFP_KERNEL); |
255 | if (!drvdata->gpiods) | |
256 | return -ENOMEM; | |
257 | for (i = 0; i < config->ngpios; i++) { | |
d162d041 | 258 | drvdata->gpiods[i] = devm_gpiod_get_index(dev, |
d6cd33ad LW |
259 | NULL, |
260 | i, | |
261 | config->gflags[i]); | |
262 | if (IS_ERR(drvdata->gpiods[i])) | |
263 | return PTR_ERR(drvdata->gpiods[i]); | |
264 | /* This is good to know */ | |
265 | gpiod_set_consumer_name(drvdata->gpiods[i], drvdata->desc.name); | |
3f0292ae | 266 | } |
d6cd33ad | 267 | drvdata->nr_gpios = config->ngpios; |
3f0292ae | 268 | |
d162d041 LW |
269 | drvdata->states = devm_kmemdup(dev, |
270 | config->states, | |
271 | config->nr_states * | |
272 | sizeof(struct gpio_regulator_state), | |
273 | GFP_KERNEL); | |
3f0292ae | 274 | if (drvdata->states == NULL) { |
d162d041 LW |
275 | dev_err(dev, "Failed to allocate state data\n"); |
276 | return -ENOMEM; | |
3f0292ae HS |
277 | } |
278 | drvdata->nr_states = config->nr_states; | |
279 | ||
280 | drvdata->desc.owner = THIS_MODULE; | |
a2a8222b | 281 | drvdata->desc.enable_time = config->startup_delay; |
3f0292ae HS |
282 | |
283 | /* handle regulator type*/ | |
284 | switch (config->type) { | |
285 | case REGULATOR_VOLTAGE: | |
286 | drvdata->desc.type = REGULATOR_VOLTAGE; | |
287 | drvdata->desc.ops = &gpio_regulator_voltage_ops; | |
288 | drvdata->desc.n_voltages = config->nr_states; | |
289 | break; | |
290 | case REGULATOR_CURRENT: | |
291 | drvdata->desc.type = REGULATOR_CURRENT; | |
292 | drvdata->desc.ops = &gpio_regulator_current_ops; | |
293 | break; | |
294 | default: | |
d162d041 LW |
295 | dev_err(dev, "No regulator type set\n"); |
296 | return -EINVAL; | |
3f0292ae HS |
297 | } |
298 | ||
3f0292ae HS |
299 | /* build initial state from gpio init data. */ |
300 | state = 0; | |
301 | for (ptr = 0; ptr < drvdata->nr_gpios; ptr++) { | |
d6cd33ad | 302 | if (config->gflags[ptr] == GPIOD_OUT_HIGH) |
3f0292ae HS |
303 | state |= (1 << ptr); |
304 | } | |
305 | drvdata->state = state; | |
306 | ||
d162d041 | 307 | cfg.dev = dev; |
c172708d | 308 | cfg.init_data = config->init_data; |
7d4be2f5 | 309 | cfg.driver_data = drvdata; |
f8a9f757 | 310 | cfg.of_node = np; |
c172708d | 311 | |
d6cd33ad LW |
312 | /* |
313 | * The signal will be inverted by the GPIO core if flagged so in the | |
314 | * decriptor. | |
315 | */ | |
316 | if (config->enabled_at_boot) | |
317 | gflags = GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_NONEXCLUSIVE; | |
318 | else | |
319 | gflags = GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE; | |
320 | ||
d162d041 LW |
321 | cfg.ena_gpiod = gpiod_get_optional(dev, "enable", gflags); |
322 | if (IS_ERR(cfg.ena_gpiod)) | |
323 | return PTR_ERR(cfg.ena_gpiod); | |
4b7c948f | 324 | |
7cdc2ee7 AL |
325 | rdev = devm_regulator_register(dev, &drvdata->desc, &cfg); |
326 | if (IS_ERR(rdev)) { | |
327 | ret = PTR_ERR(rdev); | |
d162d041 LW |
328 | dev_err(dev, "Failed to register regulator: %d\n", ret); |
329 | return ret; | |
3f0292ae HS |
330 | } |
331 | ||
332 | platform_set_drvdata(pdev, drvdata); | |
333 | ||
334 | return 0; | |
3f0292ae HS |
335 | } |
336 | ||
ec4f7b88 | 337 | #if defined(CONFIG_OF) |
3d68dfe3 | 338 | static const struct of_device_id regulator_gpio_of_match[] = { |
006694d0 LJ |
339 | { .compatible = "regulator-gpio", }, |
340 | {}, | |
341 | }; | |
2f9481e7 | 342 | MODULE_DEVICE_TABLE(of, regulator_gpio_of_match); |
ec4f7b88 | 343 | #endif |
006694d0 | 344 | |
3f0292ae HS |
345 | static struct platform_driver gpio_regulator_driver = { |
346 | .probe = gpio_regulator_probe, | |
3f0292ae HS |
347 | .driver = { |
348 | .name = "gpio-regulator", | |
ec4f7b88 | 349 | .of_match_table = of_match_ptr(regulator_gpio_of_match), |
3f0292ae HS |
350 | }, |
351 | }; | |
352 | ||
353 | static int __init gpio_regulator_init(void) | |
354 | { | |
355 | return platform_driver_register(&gpio_regulator_driver); | |
356 | } | |
357 | subsys_initcall(gpio_regulator_init); | |
358 | ||
359 | static void __exit gpio_regulator_exit(void) | |
360 | { | |
361 | platform_driver_unregister(&gpio_regulator_driver); | |
362 | } | |
363 | module_exit(gpio_regulator_exit); | |
364 | ||
365 | MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); | |
366 | MODULE_DESCRIPTION("gpio voltage regulator"); | |
367 | MODULE_LICENSE("GPL"); | |
368 | MODULE_ALIAS("platform:gpio-regulator"); |