]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
af41e8db PM |
2 | /* |
3 | * Copyright (C) 2014-2015 Samsung Electronics | |
4 | * Przemyslaw Marczak <[email protected]> | |
af41e8db | 5 | */ |
f15cd4f1 | 6 | |
af41e8db | 7 | #include <common.h> |
af41e8db PM |
8 | #include <errno.h> |
9 | #include <dm.h> | |
10 | #include <dm/uclass-internal.h> | |
11 | #include <power/pmic.h> | |
12 | #include <power/regulator.h> | |
13 | ||
af41e8db PM |
14 | int regulator_mode(struct udevice *dev, struct dm_regulator_mode **modep) |
15 | { | |
16 | struct dm_regulator_uclass_platdata *uc_pdata; | |
17 | ||
18 | *modep = NULL; | |
19 | ||
20 | uc_pdata = dev_get_uclass_platdata(dev); | |
21 | if (!uc_pdata) | |
22 | return -ENXIO; | |
23 | ||
24 | *modep = uc_pdata->mode; | |
25 | return uc_pdata->mode_count; | |
26 | } | |
27 | ||
28 | int regulator_get_value(struct udevice *dev) | |
29 | { | |
30 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
31 | ||
32 | if (!ops || !ops->get_value) | |
33 | return -ENOSYS; | |
34 | ||
35 | return ops->get_value(dev); | |
36 | } | |
37 | ||
38 | int regulator_set_value(struct udevice *dev, int uV) | |
39 | { | |
40 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
eaadcf38 K |
41 | struct dm_regulator_uclass_platdata *uc_pdata; |
42 | ||
43 | uc_pdata = dev_get_uclass_platdata(dev); | |
44 | if (uc_pdata->min_uV != -ENODATA && uV < uc_pdata->min_uV) | |
45 | return -EINVAL; | |
46 | if (uc_pdata->max_uV != -ENODATA && uV > uc_pdata->max_uV) | |
47 | return -EINVAL; | |
af41e8db PM |
48 | |
49 | if (!ops || !ops->set_value) | |
50 | return -ENOSYS; | |
51 | ||
52 | return ops->set_value(dev, uV); | |
53 | } | |
2f5d532f K |
54 | |
55 | /* | |
56 | * To be called with at most caution as there is no check | |
57 | * before setting the actual voltage value. | |
58 | */ | |
59 | int regulator_set_value_force(struct udevice *dev, int uV) | |
60 | { | |
61 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
62 | ||
63 | if (!ops || !ops->set_value) | |
64 | return -ENOSYS; | |
65 | ||
66 | return ops->set_value(dev, uV); | |
67 | } | |
af41e8db PM |
68 | |
69 | int regulator_get_current(struct udevice *dev) | |
70 | { | |
71 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
72 | ||
73 | if (!ops || !ops->get_current) | |
74 | return -ENOSYS; | |
75 | ||
76 | return ops->get_current(dev); | |
77 | } | |
78 | ||
79 | int regulator_set_current(struct udevice *dev, int uA) | |
80 | { | |
81 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
5483456e K |
82 | struct dm_regulator_uclass_platdata *uc_pdata; |
83 | ||
84 | uc_pdata = dev_get_uclass_platdata(dev); | |
85 | if (uc_pdata->min_uA != -ENODATA && uA < uc_pdata->min_uA) | |
86 | return -EINVAL; | |
87 | if (uc_pdata->max_uA != -ENODATA && uA > uc_pdata->max_uA) | |
88 | return -EINVAL; | |
af41e8db PM |
89 | |
90 | if (!ops || !ops->set_current) | |
91 | return -ENOSYS; | |
92 | ||
93 | return ops->set_current(dev, uA); | |
94 | } | |
95 | ||
06bdf600 | 96 | int regulator_get_enable(struct udevice *dev) |
af41e8db PM |
97 | { |
98 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
99 | ||
100 | if (!ops || !ops->get_enable) | |
101 | return -ENOSYS; | |
102 | ||
103 | return ops->get_enable(dev); | |
104 | } | |
105 | ||
106 | int regulator_set_enable(struct udevice *dev, bool enable) | |
107 | { | |
108 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
109 | ||
110 | if (!ops || !ops->set_enable) | |
111 | return -ENOSYS; | |
112 | ||
113 | return ops->set_enable(dev, enable); | |
114 | } | |
115 | ||
116 | int regulator_get_mode(struct udevice *dev) | |
117 | { | |
118 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
119 | ||
120 | if (!ops || !ops->get_mode) | |
121 | return -ENOSYS; | |
122 | ||
123 | return ops->get_mode(dev); | |
124 | } | |
125 | ||
126 | int regulator_set_mode(struct udevice *dev, int mode) | |
127 | { | |
128 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
129 | ||
130 | if (!ops || !ops->set_mode) | |
131 | return -ENOSYS; | |
132 | ||
133 | return ops->set_mode(dev, mode); | |
134 | } | |
135 | ||
3b880757 | 136 | int regulator_get_by_platname(const char *plat_name, struct udevice **devp) |
af41e8db PM |
137 | { |
138 | struct dm_regulator_uclass_platdata *uc_pdata; | |
139 | struct udevice *dev; | |
3b880757 | 140 | int ret; |
af41e8db PM |
141 | |
142 | *devp = NULL; | |
143 | ||
3b880757 PM |
144 | for (ret = uclass_find_first_device(UCLASS_REGULATOR, &dev); dev; |
145 | ret = uclass_find_next_device(&dev)) { | |
422f04b6 SG |
146 | if (ret) { |
147 | debug("regulator %s, ret=%d\n", dev->name, ret); | |
3b880757 | 148 | continue; |
422f04b6 | 149 | } |
3b880757 | 150 | |
af41e8db PM |
151 | uc_pdata = dev_get_uclass_platdata(dev); |
152 | if (!uc_pdata || strcmp(plat_name, uc_pdata->name)) | |
153 | continue; | |
154 | ||
155 | return uclass_get_device_tail(dev, 0, devp); | |
156 | } | |
157 | ||
422f04b6 | 158 | debug("%s: can't find: %s, ret=%d\n", __func__, plat_name, ret); |
af41e8db PM |
159 | |
160 | return -ENODEV; | |
161 | } | |
162 | ||
3b880757 | 163 | int regulator_get_by_devname(const char *devname, struct udevice **devp) |
af41e8db PM |
164 | { |
165 | return uclass_get_device_by_name(UCLASS_REGULATOR, devname, devp); | |
166 | } | |
167 | ||
7c816e24 PM |
168 | int device_get_supply_regulator(struct udevice *dev, const char *supply_name, |
169 | struct udevice **devp) | |
170 | { | |
171 | return uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, | |
172 | supply_name, devp); | |
173 | } | |
174 | ||
3b55d30f | 175 | int regulator_autoset(struct udevice *dev) |
af41e8db | 176 | { |
3b55d30f SG |
177 | struct dm_regulator_uclass_platdata *uc_pdata; |
178 | int ret = 0; | |
af41e8db | 179 | |
3b55d30f SG |
180 | uc_pdata = dev_get_uclass_platdata(dev); |
181 | if (!uc_pdata->always_on && !uc_pdata->boot_on) | |
182 | return -EMEDIUMTYPE; | |
af41e8db | 183 | |
3b55d30f SG |
184 | if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UV) |
185 | ret = regulator_set_value(dev, uc_pdata->min_uV); | |
186 | if (!ret && (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UA)) | |
187 | ret = regulator_set_current(dev, uc_pdata->min_uA); | |
af41e8db PM |
188 | |
189 | if (!ret) | |
3b55d30f | 190 | ret = regulator_set_enable(dev, true); |
af41e8db PM |
191 | |
192 | return ret; | |
193 | } | |
194 | ||
3b55d30f | 195 | static void regulator_show(struct udevice *dev, int ret) |
af41e8db PM |
196 | { |
197 | struct dm_regulator_uclass_platdata *uc_pdata; | |
af41e8db PM |
198 | |
199 | uc_pdata = dev_get_uclass_platdata(dev); | |
3b880757 | 200 | |
3b55d30f SG |
201 | printf("%s@%s: ", dev->name, uc_pdata->name); |
202 | if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UV) | |
203 | printf("set %d uV", uc_pdata->min_uV); | |
204 | if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UA) | |
205 | printf("; set %d uA", uc_pdata->min_uA); | |
206 | printf("; enabling"); | |
207 | if (ret) | |
7d577999 | 208 | printf(" (ret: %d)", ret); |
3b55d30f SG |
209 | printf("\n"); |
210 | } | |
af41e8db | 211 | |
3b55d30f SG |
212 | int regulator_autoset_by_name(const char *platname, struct udevice **devp) |
213 | { | |
214 | struct udevice *dev; | |
215 | int ret; | |
af41e8db | 216 | |
3b55d30f | 217 | ret = regulator_get_by_platname(platname, &dev); |
af41e8db PM |
218 | if (devp) |
219 | *devp = dev; | |
3b55d30f | 220 | if (ret) { |
422f04b6 | 221 | debug("Can get the regulator: %s (err=%d)\n", platname, ret); |
3b55d30f SG |
222 | return ret; |
223 | } | |
3b880757 | 224 | |
3b55d30f | 225 | return regulator_autoset(dev); |
af41e8db PM |
226 | } |
227 | ||
3b880757 PM |
228 | int regulator_list_autoset(const char *list_platname[], |
229 | struct udevice *list_devp[], | |
230 | bool verbose) | |
af41e8db PM |
231 | { |
232 | struct udevice *dev; | |
3b880757 | 233 | int error = 0, i = 0, ret; |
af41e8db | 234 | |
3b880757 | 235 | while (list_platname[i]) { |
3b55d30f SG |
236 | ret = regulator_autoset_by_name(list_platname[i], &dev); |
237 | if (ret != -EMEDIUMTYPE && verbose) | |
238 | regulator_show(dev, ret); | |
3b880757 PM |
239 | if (ret & !error) |
240 | error = ret; | |
241 | ||
242 | if (list_devp) | |
243 | list_devp[i] = dev; | |
244 | ||
245 | i++; | |
246 | } | |
af41e8db | 247 | |
3b880757 PM |
248 | return error; |
249 | } | |
250 | ||
251 | static bool regulator_name_is_unique(struct udevice *check_dev, | |
252 | const char *check_name) | |
253 | { | |
254 | struct dm_regulator_uclass_platdata *uc_pdata; | |
255 | struct udevice *dev; | |
256 | int check_len = strlen(check_name); | |
257 | int ret; | |
258 | int len; | |
259 | ||
260 | for (ret = uclass_find_first_device(UCLASS_REGULATOR, &dev); dev; | |
261 | ret = uclass_find_next_device(&dev)) { | |
262 | if (ret || dev == check_dev) | |
af41e8db PM |
263 | continue; |
264 | ||
3b880757 PM |
265 | uc_pdata = dev_get_uclass_platdata(dev); |
266 | len = strlen(uc_pdata->name); | |
267 | if (len != check_len) | |
268 | continue; | |
269 | ||
270 | if (!strcmp(uc_pdata->name, check_name)) | |
271 | return false; | |
af41e8db PM |
272 | } |
273 | ||
3b880757 | 274 | return true; |
af41e8db PM |
275 | } |
276 | ||
277 | static int regulator_post_bind(struct udevice *dev) | |
278 | { | |
279 | struct dm_regulator_uclass_platdata *uc_pdata; | |
3b880757 | 280 | const char *property = "regulator-name"; |
af41e8db PM |
281 | |
282 | uc_pdata = dev_get_uclass_platdata(dev); | |
af41e8db PM |
283 | |
284 | /* Regulator's mandatory constraint */ | |
f15cd4f1 | 285 | uc_pdata->name = dev_read_string(dev, property); |
af41e8db | 286 | if (!uc_pdata->name) { |
f15cd4f1 SG |
287 | debug("%s: dev '%s' has no property '%s'\n", |
288 | __func__, dev->name, property); | |
289 | uc_pdata->name = dev_read_name(dev); | |
cf260011 PF |
290 | if (!uc_pdata->name) |
291 | return -EINVAL; | |
af41e8db PM |
292 | } |
293 | ||
3b880757 PM |
294 | if (regulator_name_is_unique(dev, uc_pdata->name)) |
295 | return 0; | |
296 | ||
f15cd4f1 | 297 | debug("'%s' of dev: '%s', has nonunique value: '%s\n", |
3b880757 PM |
298 | property, dev->name, uc_pdata->name); |
299 | ||
300 | return -EINVAL; | |
af41e8db PM |
301 | } |
302 | ||
303 | static int regulator_pre_probe(struct udevice *dev) | |
304 | { | |
305 | struct dm_regulator_uclass_platdata *uc_pdata; | |
af41e8db PM |
306 | |
307 | uc_pdata = dev_get_uclass_platdata(dev); | |
308 | if (!uc_pdata) | |
309 | return -ENXIO; | |
310 | ||
311 | /* Regulator's optional constraints */ | |
f15cd4f1 SG |
312 | uc_pdata->min_uV = dev_read_u32_default(dev, "regulator-min-microvolt", |
313 | -ENODATA); | |
314 | uc_pdata->max_uV = dev_read_u32_default(dev, "regulator-max-microvolt", | |
315 | -ENODATA); | |
316 | uc_pdata->min_uA = dev_read_u32_default(dev, "regulator-min-microamp", | |
317 | -ENODATA); | |
318 | uc_pdata->max_uA = dev_read_u32_default(dev, "regulator-max-microamp", | |
319 | -ENODATA); | |
320 | uc_pdata->always_on = dev_read_bool(dev, "regulator-always-on"); | |
321 | uc_pdata->boot_on = dev_read_bool(dev, "regulator-boot-on"); | |
af41e8db | 322 | |
7837ceab SG |
323 | /* Those values are optional (-ENODATA if unset) */ |
324 | if ((uc_pdata->min_uV != -ENODATA) && | |
325 | (uc_pdata->max_uV != -ENODATA) && | |
326 | (uc_pdata->min_uV == uc_pdata->max_uV)) | |
327 | uc_pdata->flags |= REGULATOR_FLAG_AUTOSET_UV; | |
328 | ||
329 | /* Those values are optional (-ENODATA if unset) */ | |
330 | if ((uc_pdata->min_uA != -ENODATA) && | |
331 | (uc_pdata->max_uA != -ENODATA) && | |
332 | (uc_pdata->min_uA == uc_pdata->max_uA)) | |
333 | uc_pdata->flags |= REGULATOR_FLAG_AUTOSET_UA; | |
334 | ||
af41e8db PM |
335 | return 0; |
336 | } | |
337 | ||
083fc83a SG |
338 | int regulators_enable_boot_on(bool verbose) |
339 | { | |
340 | struct udevice *dev; | |
341 | struct uclass *uc; | |
342 | int ret; | |
343 | ||
344 | ret = uclass_get(UCLASS_REGULATOR, &uc); | |
345 | if (ret) | |
346 | return ret; | |
347 | for (uclass_first_device(UCLASS_REGULATOR, &dev); | |
3f603cbb | 348 | dev; |
083fc83a SG |
349 | uclass_next_device(&dev)) { |
350 | ret = regulator_autoset(dev); | |
d08504d1 SG |
351 | if (ret == -EMEDIUMTYPE) { |
352 | ret = 0; | |
083fc83a | 353 | continue; |
d08504d1 | 354 | } |
083fc83a SG |
355 | if (verbose) |
356 | regulator_show(dev, ret); | |
364809de SG |
357 | if (ret == -ENOSYS) |
358 | ret = 0; | |
083fc83a SG |
359 | } |
360 | ||
361 | return ret; | |
362 | } | |
363 | ||
af41e8db PM |
364 | UCLASS_DRIVER(regulator) = { |
365 | .id = UCLASS_REGULATOR, | |
366 | .name = "regulator", | |
367 | .post_bind = regulator_post_bind, | |
368 | .pre_probe = regulator_pre_probe, | |
369 | .per_device_platdata_auto_alloc_size = | |
370 | sizeof(struct dm_regulator_uclass_platdata), | |
371 | }; |