]>
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); | |
44 | ||
45 | if (!ops || !ops->set_value) | |
46 | return -ENOSYS; | |
47 | ||
48 | return ops->set_value(dev, uV); | |
49 | } | |
50 | ||
51 | int regulator_get_current(struct udevice *dev) | |
52 | { | |
53 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
54 | ||
55 | if (!ops || !ops->get_current) | |
56 | return -ENOSYS; | |
57 | ||
58 | return ops->get_current(dev); | |
59 | } | |
60 | ||
61 | int regulator_set_current(struct udevice *dev, int uA) | |
62 | { | |
63 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
64 | ||
65 | if (!ops || !ops->set_current) | |
66 | return -ENOSYS; | |
67 | ||
68 | return ops->set_current(dev, uA); | |
69 | } | |
70 | ||
71 | bool regulator_get_enable(struct udevice *dev) | |
72 | { | |
73 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
74 | ||
75 | if (!ops || !ops->get_enable) | |
76 | return -ENOSYS; | |
77 | ||
78 | return ops->get_enable(dev); | |
79 | } | |
80 | ||
81 | int regulator_set_enable(struct udevice *dev, bool enable) | |
82 | { | |
83 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
84 | ||
85 | if (!ops || !ops->set_enable) | |
86 | return -ENOSYS; | |
87 | ||
88 | return ops->set_enable(dev, enable); | |
89 | } | |
90 | ||
91 | int regulator_get_mode(struct udevice *dev) | |
92 | { | |
93 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
94 | ||
95 | if (!ops || !ops->get_mode) | |
96 | return -ENOSYS; | |
97 | ||
98 | return ops->get_mode(dev); | |
99 | } | |
100 | ||
101 | int regulator_set_mode(struct udevice *dev, int mode) | |
102 | { | |
103 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
104 | ||
105 | if (!ops || !ops->set_mode) | |
106 | return -ENOSYS; | |
107 | ||
108 | return ops->set_mode(dev, mode); | |
109 | } | |
110 | ||
3b880757 | 111 | int regulator_get_by_platname(const char *plat_name, struct udevice **devp) |
af41e8db PM |
112 | { |
113 | struct dm_regulator_uclass_platdata *uc_pdata; | |
114 | struct udevice *dev; | |
3b880757 | 115 | int ret; |
af41e8db PM |
116 | |
117 | *devp = NULL; | |
118 | ||
3b880757 PM |
119 | for (ret = uclass_find_first_device(UCLASS_REGULATOR, &dev); dev; |
120 | ret = uclass_find_next_device(&dev)) { | |
121 | if (ret) | |
122 | continue; | |
123 | ||
af41e8db PM |
124 | uc_pdata = dev_get_uclass_platdata(dev); |
125 | if (!uc_pdata || strcmp(plat_name, uc_pdata->name)) | |
126 | continue; | |
127 | ||
128 | return uclass_get_device_tail(dev, 0, devp); | |
129 | } | |
130 | ||
131 | debug("%s: can't find: %s\n", __func__, plat_name); | |
132 | ||
133 | return -ENODEV; | |
134 | } | |
135 | ||
3b880757 | 136 | int regulator_get_by_devname(const char *devname, struct udevice **devp) |
af41e8db PM |
137 | { |
138 | return uclass_get_device_by_name(UCLASS_REGULATOR, devname, devp); | |
139 | } | |
140 | ||
7c816e24 PM |
141 | int device_get_supply_regulator(struct udevice *dev, const char *supply_name, |
142 | struct udevice **devp) | |
143 | { | |
144 | return uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, | |
145 | supply_name, devp); | |
146 | } | |
147 | ||
3b55d30f | 148 | int regulator_autoset(struct udevice *dev) |
af41e8db | 149 | { |
3b55d30f SG |
150 | struct dm_regulator_uclass_platdata *uc_pdata; |
151 | int ret = 0; | |
af41e8db | 152 | |
3b55d30f SG |
153 | uc_pdata = dev_get_uclass_platdata(dev); |
154 | if (!uc_pdata->always_on && !uc_pdata->boot_on) | |
155 | return -EMEDIUMTYPE; | |
af41e8db | 156 | |
3b55d30f SG |
157 | if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UV) |
158 | ret = regulator_set_value(dev, uc_pdata->min_uV); | |
159 | if (!ret && (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UA)) | |
160 | ret = regulator_set_current(dev, uc_pdata->min_uA); | |
af41e8db PM |
161 | |
162 | if (!ret) | |
3b55d30f | 163 | ret = regulator_set_enable(dev, true); |
af41e8db PM |
164 | |
165 | return ret; | |
166 | } | |
167 | ||
3b55d30f | 168 | static void regulator_show(struct udevice *dev, int ret) |
af41e8db PM |
169 | { |
170 | struct dm_regulator_uclass_platdata *uc_pdata; | |
af41e8db PM |
171 | |
172 | uc_pdata = dev_get_uclass_platdata(dev); | |
3b880757 | 173 | |
3b55d30f SG |
174 | printf("%s@%s: ", dev->name, uc_pdata->name); |
175 | if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UV) | |
176 | printf("set %d uV", uc_pdata->min_uV); | |
177 | if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UA) | |
178 | printf("; set %d uA", uc_pdata->min_uA); | |
179 | printf("; enabling"); | |
180 | if (ret) | |
7d577999 | 181 | printf(" (ret: %d)", ret); |
3b55d30f SG |
182 | printf("\n"); |
183 | } | |
af41e8db | 184 | |
3b55d30f SG |
185 | int regulator_autoset_by_name(const char *platname, struct udevice **devp) |
186 | { | |
187 | struct udevice *dev; | |
188 | int ret; | |
af41e8db | 189 | |
3b55d30f | 190 | ret = regulator_get_by_platname(platname, &dev); |
af41e8db PM |
191 | if (devp) |
192 | *devp = dev; | |
3b55d30f SG |
193 | if (ret) { |
194 | debug("Can get the regulator: %s!", platname); | |
195 | return ret; | |
196 | } | |
3b880757 | 197 | |
3b55d30f | 198 | return regulator_autoset(dev); |
af41e8db PM |
199 | } |
200 | ||
3b880757 PM |
201 | int regulator_list_autoset(const char *list_platname[], |
202 | struct udevice *list_devp[], | |
203 | bool verbose) | |
af41e8db PM |
204 | { |
205 | struct udevice *dev; | |
3b880757 | 206 | int error = 0, i = 0, ret; |
af41e8db | 207 | |
3b880757 | 208 | while (list_platname[i]) { |
3b55d30f SG |
209 | ret = regulator_autoset_by_name(list_platname[i], &dev); |
210 | if (ret != -EMEDIUMTYPE && verbose) | |
211 | regulator_show(dev, ret); | |
3b880757 PM |
212 | if (ret & !error) |
213 | error = ret; | |
214 | ||
215 | if (list_devp) | |
216 | list_devp[i] = dev; | |
217 | ||
218 | i++; | |
219 | } | |
af41e8db | 220 | |
3b880757 PM |
221 | return error; |
222 | } | |
223 | ||
224 | static bool regulator_name_is_unique(struct udevice *check_dev, | |
225 | const char *check_name) | |
226 | { | |
227 | struct dm_regulator_uclass_platdata *uc_pdata; | |
228 | struct udevice *dev; | |
229 | int check_len = strlen(check_name); | |
230 | int ret; | |
231 | int len; | |
232 | ||
233 | for (ret = uclass_find_first_device(UCLASS_REGULATOR, &dev); dev; | |
234 | ret = uclass_find_next_device(&dev)) { | |
235 | if (ret || dev == check_dev) | |
af41e8db PM |
236 | continue; |
237 | ||
3b880757 PM |
238 | uc_pdata = dev_get_uclass_platdata(dev); |
239 | len = strlen(uc_pdata->name); | |
240 | if (len != check_len) | |
241 | continue; | |
242 | ||
243 | if (!strcmp(uc_pdata->name, check_name)) | |
244 | return false; | |
af41e8db PM |
245 | } |
246 | ||
3b880757 | 247 | return true; |
af41e8db PM |
248 | } |
249 | ||
250 | static int regulator_post_bind(struct udevice *dev) | |
251 | { | |
252 | struct dm_regulator_uclass_platdata *uc_pdata; | |
253 | int offset = dev->of_offset; | |
254 | const void *blob = gd->fdt_blob; | |
3b880757 | 255 | const char *property = "regulator-name"; |
af41e8db PM |
256 | |
257 | uc_pdata = dev_get_uclass_platdata(dev); | |
258 | if (!uc_pdata) | |
259 | return -ENXIO; | |
260 | ||
261 | /* Regulator's mandatory constraint */ | |
3b880757 | 262 | uc_pdata->name = fdt_getprop(blob, offset, property, NULL); |
af41e8db PM |
263 | if (!uc_pdata->name) { |
264 | debug("%s: dev: %s has no property 'regulator-name'\n", | |
265 | __func__, dev->name); | |
cf260011 PF |
266 | uc_pdata->name = fdt_get_name(blob, offset, NULL); |
267 | if (!uc_pdata->name) | |
268 | return -EINVAL; | |
af41e8db PM |
269 | } |
270 | ||
3b880757 PM |
271 | if (regulator_name_is_unique(dev, uc_pdata->name)) |
272 | return 0; | |
273 | ||
59c26a9c | 274 | debug("\"%s\" of dev: \"%s\", has nonunique value: \"%s\"", |
3b880757 PM |
275 | property, dev->name, uc_pdata->name); |
276 | ||
277 | return -EINVAL; | |
af41e8db PM |
278 | } |
279 | ||
280 | static int regulator_pre_probe(struct udevice *dev) | |
281 | { | |
282 | struct dm_regulator_uclass_platdata *uc_pdata; | |
283 | int offset = dev->of_offset; | |
284 | ||
285 | uc_pdata = dev_get_uclass_platdata(dev); | |
286 | if (!uc_pdata) | |
287 | return -ENXIO; | |
288 | ||
289 | /* Regulator's optional constraints */ | |
290 | uc_pdata->min_uV = fdtdec_get_int(gd->fdt_blob, offset, | |
291 | "regulator-min-microvolt", -ENODATA); | |
292 | uc_pdata->max_uV = fdtdec_get_int(gd->fdt_blob, offset, | |
293 | "regulator-max-microvolt", -ENODATA); | |
294 | uc_pdata->min_uA = fdtdec_get_int(gd->fdt_blob, offset, | |
295 | "regulator-min-microamp", -ENODATA); | |
296 | uc_pdata->max_uA = fdtdec_get_int(gd->fdt_blob, offset, | |
297 | "regulator-max-microamp", -ENODATA); | |
298 | uc_pdata->always_on = fdtdec_get_bool(gd->fdt_blob, offset, | |
299 | "regulator-always-on"); | |
300 | uc_pdata->boot_on = fdtdec_get_bool(gd->fdt_blob, offset, | |
301 | "regulator-boot-on"); | |
302 | ||
7837ceab SG |
303 | /* Those values are optional (-ENODATA if unset) */ |
304 | if ((uc_pdata->min_uV != -ENODATA) && | |
305 | (uc_pdata->max_uV != -ENODATA) && | |
306 | (uc_pdata->min_uV == uc_pdata->max_uV)) | |
307 | uc_pdata->flags |= REGULATOR_FLAG_AUTOSET_UV; | |
308 | ||
309 | /* Those values are optional (-ENODATA if unset) */ | |
310 | if ((uc_pdata->min_uA != -ENODATA) && | |
311 | (uc_pdata->max_uA != -ENODATA) && | |
312 | (uc_pdata->min_uA == uc_pdata->max_uA)) | |
313 | uc_pdata->flags |= REGULATOR_FLAG_AUTOSET_UA; | |
314 | ||
af41e8db PM |
315 | return 0; |
316 | } | |
317 | ||
083fc83a SG |
318 | int regulators_enable_boot_on(bool verbose) |
319 | { | |
320 | struct udevice *dev; | |
321 | struct uclass *uc; | |
322 | int ret; | |
323 | ||
324 | ret = uclass_get(UCLASS_REGULATOR, &uc); | |
325 | if (ret) | |
326 | return ret; | |
327 | for (uclass_first_device(UCLASS_REGULATOR, &dev); | |
328 | dev && !ret; | |
329 | uclass_next_device(&dev)) { | |
330 | ret = regulator_autoset(dev); | |
d08504d1 SG |
331 | if (ret == -EMEDIUMTYPE) { |
332 | ret = 0; | |
083fc83a | 333 | continue; |
d08504d1 | 334 | } |
083fc83a SG |
335 | if (verbose) |
336 | regulator_show(dev, ret); | |
364809de SG |
337 | if (ret == -ENOSYS) |
338 | ret = 0; | |
083fc83a SG |
339 | } |
340 | ||
341 | return ret; | |
342 | } | |
343 | ||
af41e8db PM |
344 | UCLASS_DRIVER(regulator) = { |
345 | .id = UCLASS_REGULATOR, | |
346 | .name = "regulator", | |
347 | .post_bind = regulator_post_bind, | |
348 | .pre_probe = regulator_pre_probe, | |
349 | .per_device_platdata_auto_alloc_size = | |
350 | sizeof(struct dm_regulator_uclass_platdata), | |
351 | }; |