]>
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 | ||
3acff11c CYT |
151 | if (config->init_data->constraints.boot_on) |
152 | config->enabled_at_boot = true; | |
153 | ||
154 | /* | |
155 | * Do not use: undocumented device tree property. | |
156 | * This is kept around solely for device tree ABI stability. | |
157 | */ | |
006694d0 LJ |
158 | if (of_property_read_bool(np, "enable-at-boot")) |
159 | config->enabled_at_boot = true; | |
160 | ||
161 | of_property_read_u32(np, "startup-delay-us", &config->startup_delay); | |
162 | ||
d6cd33ad LW |
163 | /* Fetch GPIO init levels */ |
164 | ngpios = gpiod_count(dev, NULL); | |
165 | if (ngpios > 0) { | |
166 | config->gflags = devm_kzalloc(dev, | |
167 | sizeof(enum gpiod_flags) | |
168 | * ngpios, | |
169 | GFP_KERNEL); | |
170 | if (!config->gflags) | |
9f946099 RF |
171 | return ERR_PTR(-ENOMEM); |
172 | ||
d6cd33ad LW |
173 | for (i = 0; i < ngpios; i++) { |
174 | u32 val; | |
9f946099 | 175 | |
d6cd33ad LW |
176 | ret = of_property_read_u32_index(np, "gpios-states", i, |
177 | &val); | |
0094050d | 178 | |
d6cd33ad LW |
179 | /* Default to high per specification */ |
180 | if (ret) | |
181 | config->gflags[i] = GPIOD_OUT_HIGH; | |
182 | else | |
183 | config->gflags[i] = | |
184 | val ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW; | |
1f5a9623 | 185 | } |
006694d0 | 186 | } |
d6cd33ad | 187 | config->ngpios = ngpios; |
006694d0 LJ |
188 | |
189 | /* Fetch states. */ | |
934624d6 HS |
190 | proplen = of_property_count_u32_elems(np, "states"); |
191 | if (proplen < 0) { | |
216f2b9c LJ |
192 | dev_err(dev, "No 'states' property found\n"); |
193 | return ERR_PTR(-EINVAL); | |
194 | } | |
195 | ||
a86854d0 KC |
196 | config->states = devm_kcalloc(dev, |
197 | proplen / 2, | |
198 | sizeof(struct gpio_regulator_state), | |
006694d0 LJ |
199 | GFP_KERNEL); |
200 | if (!config->states) | |
201 | return ERR_PTR(-ENOMEM); | |
202 | ||
203 | for (i = 0; i < proplen / 2; i++) { | |
934624d6 HS |
204 | of_property_read_u32_index(np, "states", i * 2, |
205 | &config->states[i].value); | |
206 | of_property_read_u32_index(np, "states", i * 2 + 1, | |
207 | &config->states[i].gpios); | |
006694d0 LJ |
208 | } |
209 | config->nr_states = i; | |
210 | ||
5b1ada83 | 211 | config->type = REGULATOR_VOLTAGE; |
251b9c21 | 212 | ret = of_property_read_string(np, "regulator-type", ®type); |
5b1ada83 MB |
213 | if (ret >= 0) { |
214 | if (!strncmp("voltage", regtype, 7)) | |
215 | config->type = REGULATOR_VOLTAGE; | |
216 | else if (!strncmp("current", regtype, 7)) | |
217 | config->type = REGULATOR_CURRENT; | |
9eb9d315 MB |
218 | else |
219 | dev_warn(dev, "Unknown regulator-type '%s'\n", | |
220 | regtype); | |
251b9c21 | 221 | } |
006694d0 | 222 | |
006694d0 LJ |
223 | return config; |
224 | } | |
225 | ||
705e2a90 | 226 | static const struct regulator_ops gpio_regulator_current_ops = { |
3f0292ae HS |
227 | .get_current_limit = gpio_regulator_get_value, |
228 | .set_current_limit = gpio_regulator_set_current_limit, | |
229 | }; | |
230 | ||
a5023574 | 231 | static int gpio_regulator_probe(struct platform_device *pdev) |
3f0292ae | 232 | { |
d162d041 LW |
233 | struct device *dev = &pdev->dev; |
234 | struct gpio_regulator_config *config = dev_get_platdata(dev); | |
235 | struct device_node *np = dev->of_node; | |
3f0292ae | 236 | struct gpio_regulator_data *drvdata; |
c172708d | 237 | struct regulator_config cfg = { }; |
7cdc2ee7 | 238 | struct regulator_dev *rdev; |
d6cd33ad LW |
239 | enum gpiod_flags gflags; |
240 | int ptr, ret, state, i; | |
3f0292ae | 241 | |
d162d041 | 242 | drvdata = devm_kzalloc(dev, sizeof(struct gpio_regulator_data), |
02b55216 | 243 | GFP_KERNEL); |
9c25960c | 244 | if (drvdata == NULL) |
3f0292ae | 245 | return -ENOMEM; |
3f0292ae | 246 | |
072e78b1 | 247 | if (np) { |
d162d041 | 248 | config = of_get_gpio_regulator_config(dev, np, |
072e78b1 JMC |
249 | &drvdata->desc); |
250 | if (IS_ERR(config)) | |
251 | return PTR_ERR(config); | |
252 | } | |
253 | ||
d162d041 | 254 | drvdata->desc.name = devm_kstrdup(dev, config->supply_name, GFP_KERNEL); |
3f0292ae | 255 | if (drvdata->desc.name == NULL) { |
d162d041 | 256 | dev_err(dev, "Failed to allocate supply name\n"); |
ed8cffda | 257 | return -ENOMEM; |
3f0292ae HS |
258 | } |
259 | ||
d162d041 | 260 | drvdata->gpiods = devm_kzalloc(dev, sizeof(struct gpio_desc *), |
d6cd33ad LW |
261 | GFP_KERNEL); |
262 | if (!drvdata->gpiods) | |
263 | return -ENOMEM; | |
264 | for (i = 0; i < config->ngpios; i++) { | |
d162d041 | 265 | drvdata->gpiods[i] = devm_gpiod_get_index(dev, |
d6cd33ad LW |
266 | NULL, |
267 | i, | |
268 | config->gflags[i]); | |
269 | if (IS_ERR(drvdata->gpiods[i])) | |
270 | return PTR_ERR(drvdata->gpiods[i]); | |
271 | /* This is good to know */ | |
272 | gpiod_set_consumer_name(drvdata->gpiods[i], drvdata->desc.name); | |
3f0292ae | 273 | } |
d6cd33ad | 274 | drvdata->nr_gpios = config->ngpios; |
3f0292ae | 275 | |
d162d041 LW |
276 | drvdata->states = devm_kmemdup(dev, |
277 | config->states, | |
278 | config->nr_states * | |
279 | sizeof(struct gpio_regulator_state), | |
280 | GFP_KERNEL); | |
3f0292ae | 281 | if (drvdata->states == NULL) { |
d162d041 LW |
282 | dev_err(dev, "Failed to allocate state data\n"); |
283 | return -ENOMEM; | |
3f0292ae HS |
284 | } |
285 | drvdata->nr_states = config->nr_states; | |
286 | ||
287 | drvdata->desc.owner = THIS_MODULE; | |
a2a8222b | 288 | drvdata->desc.enable_time = config->startup_delay; |
3f0292ae HS |
289 | |
290 | /* handle regulator type*/ | |
291 | switch (config->type) { | |
292 | case REGULATOR_VOLTAGE: | |
293 | drvdata->desc.type = REGULATOR_VOLTAGE; | |
294 | drvdata->desc.ops = &gpio_regulator_voltage_ops; | |
295 | drvdata->desc.n_voltages = config->nr_states; | |
296 | break; | |
297 | case REGULATOR_CURRENT: | |
298 | drvdata->desc.type = REGULATOR_CURRENT; | |
299 | drvdata->desc.ops = &gpio_regulator_current_ops; | |
300 | break; | |
301 | default: | |
d162d041 LW |
302 | dev_err(dev, "No regulator type set\n"); |
303 | return -EINVAL; | |
3f0292ae HS |
304 | } |
305 | ||
3f0292ae HS |
306 | /* build initial state from gpio init data. */ |
307 | state = 0; | |
308 | for (ptr = 0; ptr < drvdata->nr_gpios; ptr++) { | |
d6cd33ad | 309 | if (config->gflags[ptr] == GPIOD_OUT_HIGH) |
3f0292ae HS |
310 | state |= (1 << ptr); |
311 | } | |
312 | drvdata->state = state; | |
313 | ||
d162d041 | 314 | cfg.dev = dev; |
c172708d | 315 | cfg.init_data = config->init_data; |
7d4be2f5 | 316 | cfg.driver_data = drvdata; |
f8a9f757 | 317 | cfg.of_node = np; |
c172708d | 318 | |
d6cd33ad LW |
319 | /* |
320 | * The signal will be inverted by the GPIO core if flagged so in the | |
1f0b7400 | 321 | * descriptor. |
d6cd33ad LW |
322 | */ |
323 | if (config->enabled_at_boot) | |
324 | gflags = GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_NONEXCLUSIVE; | |
325 | else | |
326 | gflags = GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE; | |
327 | ||
d162d041 LW |
328 | cfg.ena_gpiod = gpiod_get_optional(dev, "enable", gflags); |
329 | if (IS_ERR(cfg.ena_gpiod)) | |
330 | return PTR_ERR(cfg.ena_gpiod); | |
4b7c948f | 331 | |
7cdc2ee7 AL |
332 | rdev = devm_regulator_register(dev, &drvdata->desc, &cfg); |
333 | if (IS_ERR(rdev)) { | |
334 | ret = PTR_ERR(rdev); | |
d162d041 LW |
335 | dev_err(dev, "Failed to register regulator: %d\n", ret); |
336 | return ret; | |
3f0292ae HS |
337 | } |
338 | ||
339 | platform_set_drvdata(pdev, drvdata); | |
340 | ||
341 | return 0; | |
3f0292ae HS |
342 | } |
343 | ||
ec4f7b88 | 344 | #if defined(CONFIG_OF) |
3d68dfe3 | 345 | static const struct of_device_id regulator_gpio_of_match[] = { |
006694d0 LJ |
346 | { .compatible = "regulator-gpio", }, |
347 | {}, | |
348 | }; | |
2f9481e7 | 349 | MODULE_DEVICE_TABLE(of, regulator_gpio_of_match); |
ec4f7b88 | 350 | #endif |
006694d0 | 351 | |
3f0292ae HS |
352 | static struct platform_driver gpio_regulator_driver = { |
353 | .probe = gpio_regulator_probe, | |
3f0292ae HS |
354 | .driver = { |
355 | .name = "gpio-regulator", | |
ec4f7b88 | 356 | .of_match_table = of_match_ptr(regulator_gpio_of_match), |
3f0292ae HS |
357 | }, |
358 | }; | |
359 | ||
360 | static int __init gpio_regulator_init(void) | |
361 | { | |
362 | return platform_driver_register(&gpio_regulator_driver); | |
363 | } | |
364 | subsys_initcall(gpio_regulator_init); | |
365 | ||
366 | static void __exit gpio_regulator_exit(void) | |
367 | { | |
368 | platform_driver_unregister(&gpio_regulator_driver); | |
369 | } | |
370 | module_exit(gpio_regulator_exit); | |
371 | ||
372 | MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); | |
373 | MODULE_DESCRIPTION("gpio voltage regulator"); | |
374 | MODULE_LICENSE("GPL"); | |
375 | MODULE_ALIAS("platform:gpio-regulator"); |