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