]>
Commit | Line | Data |
---|---|---|
854dfbf9 FB |
1 | /* |
2 | * Copyright (C) EETS GmbH, 2017, Felix Brack <f.brack@eets.ch> | |
3 | * | |
4 | * SPDX-License-Identifier: GPL-2.0+ | |
5 | */ | |
6 | ||
7 | #include <common.h> | |
8 | #include <dm.h> | |
9 | #include <power/pmic.h> | |
10 | #include <power/regulator.h> | |
11 | #include <power/tps65910_pmic.h> | |
12 | ||
13 | #define VOUT_CHOICE_COUNT 4 | |
14 | ||
15 | /* | |
16 | * struct regulator_props - Properties of a LDO and VIO SMPS regulator | |
17 | * | |
18 | * All of these regulators allow setting one out of four output voltages. | |
19 | * These output voltages are only achievable when supplying the regulator | |
20 | * with a minimum input voltage. | |
21 | * | |
22 | * @vin_min[]: minimum supply input voltage in uV required to achieve the | |
23 | * corresponding vout[] voltage | |
24 | * @vout[]: regulator output voltage in uV | |
25 | * @reg: I2C register used to set regulator voltage | |
26 | */ | |
27 | struct regulator_props { | |
28 | int vin_min[VOUT_CHOICE_COUNT]; | |
29 | int vout[VOUT_CHOICE_COUNT]; | |
30 | int reg; | |
31 | }; | |
32 | ||
33 | static const struct regulator_props ldo_props_vdig1 = { | |
34 | .vin_min = { 1700000, 2100000, 2700000, 3200000 }, | |
35 | .vout = { 1200000, 1500000, 1800000, 2700000 }, | |
36 | .reg = TPS65910_REG_VDIG1 | |
37 | }; | |
38 | ||
39 | static const struct regulator_props ldo_props_vdig2 = { | |
40 | .vin_min = { 1700000, 1700000, 1700000, 2700000 }, | |
41 | .vout = { 1000000, 1100000, 1200000, 1800000 }, | |
42 | .reg = TPS65910_REG_VDIG2 | |
43 | }; | |
44 | ||
45 | static const struct regulator_props ldo_props_vpll = { | |
46 | .vin_min = { 2700000, 2700000, 2700000, 3000000 }, | |
47 | .vout = { 1000000, 1100000, 1800000, 2500000 }, | |
48 | .reg = TPS65910_REG_VPLL | |
49 | }; | |
50 | ||
51 | static const struct regulator_props ldo_props_vdac = { | |
52 | .vin_min = { 2700000, 3000000, 3200000, 3200000 }, | |
53 | .vout = { 1800000, 2600000, 2800000, 2850000 }, | |
54 | .reg = TPS65910_REG_VDAC | |
55 | }; | |
56 | ||
57 | static const struct regulator_props ldo_props_vaux1 = { | |
58 | .vin_min = { 2700000, 3200000, 3200000, 3200000 }, | |
59 | .vout = { 1800000, 2500000, 2800000, 2850000 }, | |
60 | .reg = TPS65910_REG_VAUX1 | |
61 | }; | |
62 | ||
63 | static const struct regulator_props ldo_props_vaux2 = { | |
64 | .vin_min = { 2700000, 3200000, 3200000, 3600000 }, | |
65 | .vout = { 1800000, 2800000, 2900000, 3300000 }, | |
66 | .reg = TPS65910_REG_VAUX2 | |
67 | }; | |
68 | ||
69 | static const struct regulator_props ldo_props_vaux33 = { | |
70 | .vin_min = { 2700000, 2700000, 3200000, 3600000 }, | |
71 | .vout = { 1800000, 2000000, 2800000, 3300000 }, | |
72 | .reg = TPS65910_REG_VAUX33 | |
73 | }; | |
74 | ||
75 | static const struct regulator_props ldo_props_vmmc = { | |
76 | .vin_min = { 2700000, 3200000, 3200000, 3600000 }, | |
77 | .vout = { 1800000, 2800000, 3000000, 3300000 }, | |
78 | .reg = TPS65910_REG_VMMC | |
79 | }; | |
80 | ||
81 | static const struct regulator_props smps_props_vio = { | |
82 | .vin_min = { 3200000, 3200000, 4000000, 4400000 }, | |
83 | .vout = { 1500000, 1800000, 2500000, 3300000 }, | |
84 | .reg = TPS65910_REG_VIO | |
85 | }; | |
86 | ||
87 | /* lookup table of control registers indexed by regulator unit number */ | |
88 | static const int ctrl_regs[] = { | |
89 | TPS65910_REG_VRTC, | |
90 | TPS65910_REG_VIO, | |
91 | TPS65910_REG_VDD1, | |
92 | TPS65910_REG_VDD2, | |
93 | TPS65910_REG_VDD3, | |
94 | TPS65910_REG_VDIG1, | |
95 | TPS65910_REG_VDIG2, | |
96 | TPS65910_REG_VPLL, | |
97 | TPS65910_REG_VDAC, | |
98 | TPS65910_REG_VAUX1, | |
99 | TPS65910_REG_VAUX2, | |
100 | TPS65910_REG_VAUX33, | |
101 | TPS65910_REG_VMMC | |
102 | }; | |
103 | ||
104 | /* supply names as used in DT */ | |
105 | static const char * const supply_names[] = { | |
106 | "vccio-supply", | |
107 | "vcc1-supply", | |
108 | "vcc2-supply", | |
109 | "vcc3-supply", | |
110 | "vcc4-supply", | |
111 | "vcc5-supply", | |
112 | "vcc6-supply", | |
113 | "vcc7-supply" | |
114 | }; | |
115 | ||
116 | /* lookup table of regulator supplies indexed by regulator unit number */ | |
117 | static const int regulator_supplies[] = { | |
118 | TPS65910_SUPPLY_VCC7, | |
119 | TPS65910_SUPPLY_VCCIO, | |
120 | TPS65910_SUPPLY_VCC1, | |
121 | TPS65910_SUPPLY_VCC2, | |
122 | TPS65910_SUPPLY_VCC7, | |
123 | TPS65910_SUPPLY_VCC6, | |
124 | TPS65910_SUPPLY_VCC6, | |
125 | TPS65910_SUPPLY_VCC5, | |
126 | TPS65910_SUPPLY_VCC5, | |
127 | TPS65910_SUPPLY_VCC4, | |
128 | TPS65910_SUPPLY_VCC4, | |
129 | TPS65910_SUPPLY_VCC3, | |
130 | TPS65910_SUPPLY_VCC3 | |
131 | }; | |
132 | ||
133 | static int get_ctrl_reg_from_unit_addr(const uint unit_addr) | |
134 | { | |
135 | if (unit_addr < ARRAY_SIZE(ctrl_regs)) | |
136 | return ctrl_regs[unit_addr]; | |
137 | return -ENXIO; | |
138 | } | |
139 | ||
140 | static int tps65910_regulator_get_value(struct udevice *dev, | |
141 | const struct regulator_props *rgp) | |
142 | { | |
143 | int sel, val, vout; | |
144 | struct tps65910_regulator_pdata *pdata = dev_get_platdata(dev); | |
145 | int vin = pdata->supply; | |
146 | ||
147 | val = pmic_reg_read(dev->parent, rgp->reg); | |
148 | if (val < 0) | |
149 | return val; | |
150 | sel = (val & TPS65910_SEL_MASK) >> 2; | |
151 | vout = (vin >= *(rgp->vin_min + sel)) ? *(rgp->vout + sel) : 0; | |
152 | vout = ((val & TPS65910_SUPPLY_STATE_MASK) == 1) ? vout : 0; | |
153 | ||
154 | return vout; | |
155 | } | |
156 | ||
157 | static int tps65910_ldo_get_value(struct udevice *dev) | |
158 | { | |
159 | struct tps65910_regulator_pdata *pdata = dev_get_platdata(dev); | |
160 | int vin; | |
161 | ||
162 | if (!pdata) | |
163 | return 0; | |
164 | vin = pdata->supply; | |
165 | ||
166 | switch (pdata->unit) { | |
167 | case TPS65910_UNIT_VRTC: | |
168 | /* VRTC is fixed and can't be turned off */ | |
169 | return (vin >= 2500000) ? 1830000 : 0; | |
170 | case TPS65910_UNIT_VDIG1: | |
171 | return tps65910_regulator_get_value(dev, &ldo_props_vdig1); | |
172 | case TPS65910_UNIT_VDIG2: | |
173 | return tps65910_regulator_get_value(dev, &ldo_props_vdig2); | |
174 | case TPS65910_UNIT_VPLL: | |
175 | return tps65910_regulator_get_value(dev, &ldo_props_vpll); | |
176 | case TPS65910_UNIT_VDAC: | |
177 | return tps65910_regulator_get_value(dev, &ldo_props_vdac); | |
178 | case TPS65910_UNIT_VAUX1: | |
179 | return tps65910_regulator_get_value(dev, &ldo_props_vaux1); | |
180 | case TPS65910_UNIT_VAUX2: | |
181 | return tps65910_regulator_get_value(dev, &ldo_props_vaux2); | |
182 | case TPS65910_UNIT_VAUX33: | |
183 | return tps65910_regulator_get_value(dev, &ldo_props_vaux33); | |
184 | case TPS65910_UNIT_VMMC: | |
185 | return tps65910_regulator_get_value(dev, &ldo_props_vmmc); | |
186 | default: | |
187 | return 0; | |
188 | } | |
189 | } | |
190 | ||
191 | static int tps65910_regulator_set_value(struct udevice *dev, | |
192 | const struct regulator_props *ldo, | |
193 | int uV) | |
194 | { | |
195 | int val; | |
196 | int sel = 0; | |
197 | struct tps65910_regulator_pdata *pdata = dev_get_platdata(dev); | |
198 | ||
199 | do { | |
200 | /* we only allow exact voltage matches */ | |
201 | if (uV == *(ldo->vout + sel)) | |
202 | break; | |
203 | } while (++sel < VOUT_CHOICE_COUNT); | |
204 | if (sel == VOUT_CHOICE_COUNT) | |
205 | return -EINVAL; | |
206 | if (pdata->supply < *(ldo->vin_min + sel)) | |
207 | return -EINVAL; | |
208 | ||
209 | val = pmic_reg_read(dev->parent, ldo->reg); | |
210 | if (val < 0) | |
211 | return val; | |
212 | val &= ~TPS65910_SEL_MASK; | |
213 | val |= sel << 2; | |
214 | return pmic_reg_write(dev->parent, ldo->reg, val); | |
215 | } | |
216 | ||
217 | static int tps65910_ldo_set_value(struct udevice *dev, int uV) | |
218 | { | |
219 | struct tps65910_regulator_pdata *pdata = dev_get_platdata(dev); | |
220 | int vin = pdata->supply; | |
221 | ||
222 | switch (pdata->unit) { | |
223 | case TPS65910_UNIT_VRTC: | |
224 | /* VRTC is fixed to 1.83V and can't be turned off */ | |
225 | if (vin < 2500000) | |
226 | return -EINVAL; | |
227 | return 0; | |
228 | case TPS65910_UNIT_VDIG1: | |
229 | return tps65910_regulator_set_value(dev, &ldo_props_vdig1, uV); | |
230 | case TPS65910_UNIT_VDIG2: | |
231 | return tps65910_regulator_set_value(dev, &ldo_props_vdig2, uV); | |
232 | case TPS65910_UNIT_VPLL: | |
233 | return tps65910_regulator_set_value(dev, &ldo_props_vpll, uV); | |
234 | case TPS65910_UNIT_VDAC: | |
235 | return tps65910_regulator_set_value(dev, &ldo_props_vdac, uV); | |
236 | case TPS65910_UNIT_VAUX1: | |
237 | return tps65910_regulator_set_value(dev, &ldo_props_vaux1, uV); | |
238 | case TPS65910_UNIT_VAUX2: | |
239 | return tps65910_regulator_set_value(dev, &ldo_props_vaux2, uV); | |
240 | case TPS65910_UNIT_VAUX33: | |
241 | return tps65910_regulator_set_value(dev, &ldo_props_vaux33, uV); | |
242 | case TPS65910_UNIT_VMMC: | |
243 | return tps65910_regulator_set_value(dev, &ldo_props_vmmc, uV); | |
244 | default: | |
245 | return 0; | |
246 | } | |
247 | } | |
248 | ||
249 | static int tps65910_get_enable(struct udevice *dev) | |
250 | { | |
251 | int reg, val; | |
252 | struct tps65910_regulator_pdata *pdata = dev_get_platdata(dev); | |
253 | ||
254 | reg = get_ctrl_reg_from_unit_addr(pdata->unit); | |
255 | if (reg < 0) | |
256 | return reg; | |
257 | ||
258 | val = pmic_reg_read(dev->parent, reg); | |
259 | if (val < 0) | |
260 | return val; | |
261 | ||
262 | /* bits 1:0 of regulator control register define state */ | |
263 | return ((val & TPS65910_SUPPLY_STATE_MASK) == 1); | |
264 | } | |
265 | ||
266 | static int tps65910_set_enable(struct udevice *dev, bool enable) | |
267 | { | |
268 | int reg; | |
269 | uint clr, set; | |
270 | struct tps65910_regulator_pdata *pdata = dev_get_platdata(dev); | |
271 | ||
272 | reg = get_ctrl_reg_from_unit_addr(pdata->unit); | |
273 | if (reg < 0) | |
274 | return reg; | |
275 | ||
276 | if (enable) { | |
277 | clr = TPS65910_SUPPLY_STATE_MASK & ~TPS65910_SUPPLY_STATE_ON; | |
278 | set = TPS65910_SUPPLY_STATE_MASK & TPS65910_SUPPLY_STATE_ON; | |
279 | } else { | |
280 | clr = TPS65910_SUPPLY_STATE_MASK & ~TPS65910_SUPPLY_STATE_OFF; | |
281 | set = TPS65910_SUPPLY_STATE_MASK & TPS65910_SUPPLY_STATE_OFF; | |
282 | } | |
283 | return pmic_clrsetbits(dev->parent, reg, clr, set); | |
284 | } | |
285 | ||
286 | static int buck_get_vdd1_vdd2_value(struct udevice *dev, int reg_vdd) | |
287 | { | |
288 | int gain; | |
289 | int val = pmic_reg_read(dev, reg_vdd); | |
290 | ||
291 | if (val < 0) | |
292 | return val; | |
293 | gain = (val & TPS65910_GAIN_SEL_MASK) >> 6; | |
294 | gain = (gain == 0) ? 1 : gain; | |
295 | val = pmic_reg_read(dev, reg_vdd + 1); | |
296 | if (val < 0) | |
297 | return val; | |
298 | if (val & TPS65910_VDD_SR_MASK) | |
299 | /* use smart reflex value instead */ | |
300 | val = pmic_reg_read(dev, reg_vdd + 2); | |
301 | if (val < 0) | |
302 | return val; | |
303 | return (562500 + (val & TPS65910_VDD_SEL_MASK) * 12500) * gain; | |
304 | } | |
305 | ||
306 | static int tps65910_buck_get_value(struct udevice *dev) | |
307 | { | |
308 | struct tps65910_regulator_pdata *pdata = dev_get_platdata(dev); | |
309 | ||
310 | switch (pdata->unit) { | |
311 | case TPS65910_UNIT_VIO: | |
312 | return tps65910_regulator_get_value(dev, &smps_props_vio); | |
313 | case TPS65910_UNIT_VDD1: | |
314 | return buck_get_vdd1_vdd2_value(dev->parent, TPS65910_REG_VDD1); | |
315 | case TPS65910_UNIT_VDD2: | |
316 | return buck_get_vdd1_vdd2_value(dev->parent, TPS65910_REG_VDD2); | |
317 | default: | |
318 | return 0; | |
319 | } | |
320 | } | |
321 | ||
322 | static int buck_set_vdd1_vdd2_value(struct udevice *dev, int uV) | |
323 | { | |
324 | int ret, reg_vdd, gain; | |
325 | int val; | |
326 | struct dm_regulator_uclass_platdata *uc_pdata; | |
327 | struct tps65910_regulator_pdata *pdata = dev_get_platdata(dev); | |
328 | ||
329 | switch (pdata->unit) { | |
330 | case TPS65910_UNIT_VDD1: | |
331 | reg_vdd = TPS65910_REG_VDD1; | |
332 | break; | |
333 | case TPS65910_UNIT_VDD2: | |
334 | reg_vdd = TPS65910_REG_VDD2; | |
335 | break; | |
336 | default: | |
337 | return -EINVAL; | |
338 | } | |
339 | uc_pdata = dev_get_uclass_platdata(dev); | |
340 | ||
341 | /* check setpoint is within limits */ | |
342 | if (uV < uc_pdata->min_uV) { | |
46caea7f | 343 | pr_err("voltage %duV for %s too low\n", uV, dev->name); |
854dfbf9 FB |
344 | return -EINVAL; |
345 | } | |
346 | if (uV > uc_pdata->max_uV) { | |
46caea7f | 347 | pr_err("voltage %duV for %s too high\n", uV, dev->name); |
854dfbf9 FB |
348 | return -EINVAL; |
349 | } | |
350 | ||
351 | val = pmic_reg_read(dev->parent, reg_vdd); | |
352 | if (val < 0) | |
353 | return val; | |
354 | gain = (val & TPS65910_GAIN_SEL_MASK) >> 6; | |
355 | gain = (gain == 0) ? 1 : gain; | |
356 | val = ((uV / gain) - 562500) / 12500; | |
357 | if (val < TPS65910_VDD_SEL_MIN || val > TPS65910_VDD_SEL_MAX) | |
358 | /* | |
359 | * Neither do we change the gain, nor do we allow shutdown or | |
360 | * any approximate value (for now) | |
361 | */ | |
362 | return -EPERM; | |
363 | val &= TPS65910_VDD_SEL_MASK; | |
364 | ret = pmic_reg_write(dev->parent, reg_vdd + 1, val); | |
365 | if (ret) | |
366 | return ret; | |
367 | return 0; | |
368 | } | |
369 | ||
370 | static int tps65910_buck_set_value(struct udevice *dev, int uV) | |
371 | { | |
372 | struct tps65910_regulator_pdata *pdata = dev_get_platdata(dev); | |
373 | ||
374 | if (pdata->unit == TPS65910_UNIT_VIO) | |
375 | return tps65910_regulator_set_value(dev, &smps_props_vio, uV); | |
376 | ||
377 | return buck_set_vdd1_vdd2_value(dev, uV); | |
378 | } | |
379 | ||
380 | static int tps65910_boost_get_value(struct udevice *dev) | |
381 | { | |
382 | int vout; | |
383 | struct tps65910_regulator_pdata *pdata = dev_get_platdata(dev); | |
384 | ||
385 | vout = (pdata->supply >= 3000000) ? 5000000 : 0; | |
386 | return vout; | |
387 | } | |
388 | ||
389 | static int tps65910_regulator_ofdata_to_platdata(struct udevice *dev) | |
390 | { | |
391 | struct udevice *supply; | |
392 | int ret; | |
393 | const char *supply_name; | |
394 | struct tps65910_regulator_pdata *pdata = dev_get_platdata(dev); | |
395 | ||
396 | pdata->unit = dev_get_driver_data(dev); | |
397 | if (pdata->unit > TPS65910_UNIT_VMMC) | |
398 | return -EINVAL; | |
399 | supply_name = supply_names[regulator_supplies[pdata->unit]]; | |
400 | ||
401 | debug("Looking up supply power %s\n", supply_name); | |
402 | ret = device_get_supply_regulator(dev->parent, supply_name, &supply); | |
403 | if (ret) { | |
404 | debug(" missing supply power %s\n", supply_name); | |
405 | return ret; | |
406 | } | |
407 | pdata->supply = regulator_get_value(supply); | |
408 | if (pdata->supply < 0) { | |
409 | debug(" invalid supply voltage for regulator %s\n", | |
410 | supply->name); | |
411 | return -EINVAL; | |
412 | } | |
413 | ||
414 | return 0; | |
415 | } | |
416 | ||
417 | static const struct dm_regulator_ops tps65910_boost_ops = { | |
418 | .get_value = tps65910_boost_get_value, | |
419 | .get_enable = tps65910_get_enable, | |
420 | .set_enable = tps65910_set_enable, | |
421 | }; | |
422 | ||
423 | U_BOOT_DRIVER(tps65910_boost) = { | |
424 | .name = TPS65910_BOOST_DRIVER, | |
425 | .id = UCLASS_REGULATOR, | |
426 | .ops = &tps65910_boost_ops, | |
427 | .platdata_auto_alloc_size = sizeof(struct tps65910_regulator_pdata), | |
428 | .ofdata_to_platdata = tps65910_regulator_ofdata_to_platdata, | |
429 | }; | |
430 | ||
431 | static const struct dm_regulator_ops tps65910_buck_ops = { | |
432 | .get_value = tps65910_buck_get_value, | |
433 | .set_value = tps65910_buck_set_value, | |
434 | .get_enable = tps65910_get_enable, | |
435 | .set_enable = tps65910_set_enable, | |
436 | }; | |
437 | ||
438 | U_BOOT_DRIVER(tps65910_buck) = { | |
439 | .name = TPS65910_BUCK_DRIVER, | |
440 | .id = UCLASS_REGULATOR, | |
441 | .ops = &tps65910_buck_ops, | |
442 | .platdata_auto_alloc_size = sizeof(struct tps65910_regulator_pdata), | |
443 | .ofdata_to_platdata = tps65910_regulator_ofdata_to_platdata, | |
444 | }; | |
445 | ||
446 | static const struct dm_regulator_ops tps65910_ldo_ops = { | |
447 | .get_value = tps65910_ldo_get_value, | |
448 | .set_value = tps65910_ldo_set_value, | |
449 | .get_enable = tps65910_get_enable, | |
450 | .set_enable = tps65910_set_enable, | |
451 | }; | |
452 | ||
453 | U_BOOT_DRIVER(tps65910_ldo) = { | |
454 | .name = TPS65910_LDO_DRIVER, | |
455 | .id = UCLASS_REGULATOR, | |
456 | .ops = &tps65910_ldo_ops, | |
457 | .platdata_auto_alloc_size = sizeof(struct tps65910_regulator_pdata), | |
458 | .ofdata_to_platdata = tps65910_regulator_ofdata_to_platdata, | |
459 | }; |