]>
Commit | Line | Data |
---|---|---|
326337fb MV |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* | |
3 | * NXP PCA9450 regulator driver | |
4 | * Copyright (C) 2022 Marek Vasut <[email protected]> | |
5 | * | |
6 | * Largely based on: | |
7 | * ROHM BD71837 regulator driver | |
8 | */ | |
9 | ||
326337fb MV |
10 | #include <dm.h> |
11 | #include <log.h> | |
12 | #include <linux/bitops.h> | |
13 | #include <power/pca9450.h> | |
14 | #include <power/pmic.h> | |
15 | #include <power/regulator.h> | |
16 | ||
17 | #define HW_STATE_CONTROL 0 | |
18 | #define DEBUG | |
19 | ||
20 | /** | |
21 | * struct pca9450_vrange - describe linear range of voltages | |
22 | * | |
23 | * @min_volt: smallest voltage in range | |
24 | * @step: how much voltage changes at each selector step | |
25 | * @min_sel: smallest selector in the range | |
26 | * @max_sel: maximum selector in the range | |
27 | */ | |
28 | struct pca9450_vrange { | |
29 | unsigned int min_volt; | |
30 | unsigned int step; | |
31 | u8 min_sel; | |
32 | u8 max_sel; | |
33 | }; | |
34 | ||
35 | /** | |
36 | * struct pca9450_plat - describe regulator control registers | |
37 | * | |
38 | * @name: name of the regulator. Used for matching the dt-entry | |
39 | * @enable_reg: register address used to enable/disable regulator | |
40 | * @enablemask: register mask used to enable/disable regulator | |
41 | * @volt_reg: register address used to configure regulator voltage | |
42 | * @volt_mask: register mask used to configure regulator voltage | |
43 | * @ranges: pointer to ranges of regulator voltages and matching register | |
44 | * values | |
45 | * @numranges: number of voltage ranges pointed by ranges | |
326337fb MV |
46 | */ |
47 | struct pca9450_plat { | |
48 | const char *name; | |
49 | u8 enable_reg; | |
50 | u8 enablemask; | |
51 | u8 volt_reg; | |
52 | u8 volt_mask; | |
53 | struct pca9450_vrange *ranges; | |
54 | unsigned int numranges; | |
326337fb MV |
55 | }; |
56 | ||
57 | #define PCA_RANGE(_min, _vstep, _sel_low, _sel_hi) \ | |
58 | { \ | |
59 | .min_volt = (_min), .step = (_vstep), \ | |
60 | .min_sel = (_sel_low), .max_sel = (_sel_hi), \ | |
61 | } | |
62 | ||
67b5663d | 63 | #define PCA_DATA(_name, enreg, enmask, vreg, vmask, _range) \ |
326337fb MV |
64 | { \ |
65 | .name = (_name), .enable_reg = (enreg), .enablemask = (enmask), \ | |
66 | .volt_reg = (vreg), .volt_mask = (vmask), .ranges = (_range), \ | |
67b5663d | 67 | .numranges = ARRAY_SIZE(_range) \ |
326337fb MV |
68 | } |
69 | ||
70 | static struct pca9450_vrange pca9450_buck123_vranges[] = { | |
71 | PCA_RANGE(600000, 12500, 0, 0x7f), | |
72 | }; | |
73 | ||
cd63c4a7 JZ |
74 | static struct pca9450_vrange pca9450_trim_buck13_vranges[] = { |
75 | PCA_RANGE(650000, 12500, 0, 0x7f), | |
76 | }; | |
77 | ||
326337fb MV |
78 | static struct pca9450_vrange pca9450_buck456_vranges[] = { |
79 | PCA_RANGE(600000, 25000, 0, 0x70), | |
80 | PCA_RANGE(3400000, 0, 0x71, 0x7f), | |
81 | }; | |
82 | ||
83 | static struct pca9450_vrange pca9450_ldo1_vranges[] = { | |
84 | PCA_RANGE(1600000, 100000, 0x0, 0x3), | |
85 | PCA_RANGE(3000000, 100000, 0x4, 0x7), | |
86 | }; | |
87 | ||
88 | static struct pca9450_vrange pca9450_ldo2_vranges[] = { | |
89 | PCA_RANGE(800000, 50000, 0x0, 0x7), | |
90 | }; | |
91 | ||
92 | static struct pca9450_vrange pca9450_ldo34_vranges[] = { | |
93 | PCA_RANGE(800000, 100000, 0x0, 0x19), | |
94 | PCA_RANGE(3300000, 0, 0x1a, 0x1f), | |
95 | }; | |
96 | ||
97 | static struct pca9450_vrange pca9450_ldo5_vranges[] = { | |
98 | PCA_RANGE(1800000, 100000, 0x0, 0xf), | |
99 | }; | |
100 | ||
101 | /* | |
102 | * We use enable mask 'HW_STATE_CONTROL' to indicate that this regulator | |
103 | * must not be enabled or disabled by SW. The typical use-case for PCA9450 | |
104 | * is powering NXP i.MX8. In this use-case we (for now) only allow control | |
105 | * for BUCK4, BUCK5, BUCK6 which are not boot critical. | |
106 | */ | |
107 | static struct pca9450_plat pca9450_reg_data[] = { | |
108 | /* Bucks 1-3 which support dynamic voltage scaling */ | |
109 | PCA_DATA("BUCK1", PCA9450_BUCK1CTRL, HW_STATE_CONTROL, | |
110 | PCA9450_BUCK1OUT_DVS0, PCA9450_DVS_BUCK_RUN_MASK, | |
67b5663d | 111 | pca9450_buck123_vranges), |
cd63c4a7 JZ |
112 | PCA_DATA("BUCK1_TRIM", PCA9450_BUCK1CTRL, HW_STATE_CONTROL, |
113 | PCA9450_BUCK1OUT_DVS0, PCA9450_DVS_BUCK_RUN_MASK, | |
114 | pca9450_trim_buck13_vranges), | |
326337fb MV |
115 | PCA_DATA("BUCK2", PCA9450_BUCK2CTRL, HW_STATE_CONTROL, |
116 | PCA9450_BUCK2OUT_DVS0, PCA9450_DVS_BUCK_RUN_MASK, | |
67b5663d | 117 | pca9450_buck123_vranges), |
326337fb MV |
118 | PCA_DATA("BUCK3", PCA9450_BUCK3CTRL, HW_STATE_CONTROL, |
119 | PCA9450_BUCK3OUT_DVS0, PCA9450_DVS_BUCK_RUN_MASK, | |
67b5663d | 120 | pca9450_buck123_vranges), |
cd63c4a7 JZ |
121 | PCA_DATA("BUCK3_TRIM", PCA9450_BUCK3CTRL, HW_STATE_CONTROL, |
122 | PCA9450_BUCK3OUT_DVS0, PCA9450_DVS_BUCK_RUN_MASK, | |
123 | pca9450_trim_buck13_vranges), | |
326337fb MV |
124 | /* Bucks 4-6 which do not support dynamic voltage scaling */ |
125 | PCA_DATA("BUCK4", PCA9450_BUCK4CTRL, HW_STATE_CONTROL, | |
126 | PCA9450_BUCK4OUT, PCA9450_DVS_BUCK_RUN_MASK, | |
67b5663d | 127 | pca9450_buck456_vranges), |
326337fb MV |
128 | PCA_DATA("BUCK5", PCA9450_BUCK5CTRL, HW_STATE_CONTROL, |
129 | PCA9450_BUCK5OUT, PCA9450_DVS_BUCK_RUN_MASK, | |
67b5663d | 130 | pca9450_buck456_vranges), |
326337fb MV |
131 | PCA_DATA("BUCK6", PCA9450_BUCK6CTRL, HW_STATE_CONTROL, |
132 | PCA9450_BUCK6OUT, PCA9450_DVS_BUCK_RUN_MASK, | |
67b5663d | 133 | pca9450_buck456_vranges), |
326337fb MV |
134 | /* LDOs */ |
135 | PCA_DATA("LDO1", PCA9450_LDO1CTRL, HW_STATE_CONTROL, | |
136 | PCA9450_LDO1CTRL, PCA9450_LDO12_MASK, | |
67b5663d | 137 | pca9450_ldo1_vranges), |
326337fb MV |
138 | PCA_DATA("LDO2", PCA9450_LDO2CTRL, HW_STATE_CONTROL, |
139 | PCA9450_LDO2CTRL, PCA9450_LDO12_MASK, | |
67b5663d | 140 | pca9450_ldo2_vranges), |
326337fb MV |
141 | PCA_DATA("LDO3", PCA9450_LDO3CTRL, HW_STATE_CONTROL, |
142 | PCA9450_LDO3CTRL, PCA9450_LDO34_MASK, | |
67b5663d | 143 | pca9450_ldo34_vranges), |
326337fb MV |
144 | PCA_DATA("LDO4", PCA9450_LDO4CTRL, HW_STATE_CONTROL, |
145 | PCA9450_LDO4CTRL, PCA9450_LDO34_MASK, | |
67b5663d | 146 | pca9450_ldo34_vranges), |
326337fb MV |
147 | PCA_DATA("LDO5", PCA9450_LDO5CTRL_H, HW_STATE_CONTROL, |
148 | PCA9450_LDO5CTRL_H, PCA9450_LDO5_MASK, | |
67b5663d | 149 | pca9450_ldo5_vranges), |
326337fb MV |
150 | }; |
151 | ||
152 | static int vrange_find_value(struct pca9450_vrange *r, unsigned int sel, | |
153 | unsigned int *val) | |
154 | { | |
155 | if (!val || sel < r->min_sel || sel > r->max_sel) | |
156 | return -EINVAL; | |
157 | ||
158 | *val = r->min_volt + r->step * (sel - r->min_sel); | |
159 | return 0; | |
160 | } | |
161 | ||
162 | static int vrange_find_selector(struct pca9450_vrange *r, int val, | |
163 | unsigned int *sel) | |
164 | { | |
165 | int ret = -EINVAL; | |
166 | int num_vals = r->max_sel - r->min_sel + 1; | |
167 | ||
168 | if (val >= r->min_volt && | |
169 | val <= r->min_volt + r->step * (num_vals - 1)) { | |
170 | if (r->step) { | |
171 | *sel = r->min_sel + ((val - r->min_volt) / r->step); | |
172 | ret = 0; | |
173 | } else { | |
174 | *sel = r->min_sel; | |
175 | ret = 0; | |
176 | } | |
177 | } | |
178 | return ret; | |
179 | } | |
180 | ||
181 | static int pca9450_get_enable(struct udevice *dev) | |
182 | { | |
183 | struct pca9450_plat *plat = dev_get_plat(dev); | |
184 | int val; | |
185 | ||
186 | /* | |
187 | * boot critical regulators on pca9450 must not be controlled by sw | |
188 | * due to the 'feature' which leaves power rails down if pca9450 is | |
189 | * reseted to snvs state. hence we can't get the state here. | |
190 | * | |
191 | * if we are alive it means we probably are on run state and | |
192 | * if the regulator can't be controlled we can assume it is | |
193 | * enabled. | |
194 | */ | |
195 | if (plat->enablemask == HW_STATE_CONTROL) | |
196 | return 1; | |
197 | ||
198 | val = pmic_reg_read(dev->parent, plat->enable_reg); | |
199 | if (val < 0) | |
200 | return val; | |
201 | ||
202 | return (val & plat->enablemask); | |
203 | } | |
204 | ||
205 | static int pca9450_set_enable(struct udevice *dev, bool enable) | |
206 | { | |
207 | int val = 0; | |
208 | struct pca9450_plat *plat = dev_get_plat(dev); | |
209 | ||
210 | /* | |
211 | * boot critical regulators on pca9450 must not be controlled by sw | |
212 | * due to the 'feature' which leaves power rails down if pca9450 is | |
213 | * reseted to snvs state. Hence we can't set the state here. | |
214 | */ | |
215 | if (plat->enablemask == HW_STATE_CONTROL) | |
216 | return enable ? 0 : -EINVAL; | |
217 | ||
218 | if (enable) | |
219 | val = plat->enablemask; | |
220 | ||
221 | return pmic_clrsetbits(dev->parent, plat->enable_reg, plat->enablemask, | |
222 | val); | |
223 | } | |
224 | ||
225 | static int pca9450_get_value(struct udevice *dev) | |
226 | { | |
227 | struct pca9450_plat *plat = dev_get_plat(dev); | |
228 | unsigned int reg, tmp; | |
229 | int i, ret; | |
230 | ||
231 | ret = pmic_reg_read(dev->parent, plat->volt_reg); | |
232 | if (ret < 0) | |
233 | return ret; | |
234 | ||
235 | reg = ret; | |
236 | reg &= plat->volt_mask; | |
237 | ||
238 | for (i = 0; i < plat->numranges; i++) { | |
239 | struct pca9450_vrange *r = &plat->ranges[i]; | |
240 | ||
241 | if (!vrange_find_value(r, reg, &tmp)) | |
242 | return tmp; | |
243 | } | |
244 | ||
245 | pr_err("Unknown voltage value read from pmic\n"); | |
246 | ||
247 | return -EINVAL; | |
248 | } | |
249 | ||
250 | static int pca9450_set_value(struct udevice *dev, int uvolt) | |
251 | { | |
252 | struct pca9450_plat *plat = dev_get_plat(dev); | |
253 | unsigned int sel; | |
254 | int i, found = 0; | |
255 | ||
326337fb MV |
256 | for (i = 0; i < plat->numranges; i++) { |
257 | struct pca9450_vrange *r = &plat->ranges[i]; | |
258 | ||
259 | found = !vrange_find_selector(r, uvolt, &sel); | |
260 | if (found) { | |
261 | unsigned int tmp; | |
262 | ||
263 | /* | |
264 | * We require exactly the requested value to be | |
265 | * supported - this can be changed later if needed | |
266 | */ | |
267 | found = !vrange_find_value(r, sel, &tmp); | |
268 | if (found && tmp == uvolt) | |
269 | break; | |
270 | found = 0; | |
271 | } | |
272 | } | |
273 | ||
274 | if (!found) | |
275 | return -EINVAL; | |
276 | ||
277 | return pmic_clrsetbits(dev->parent, plat->volt_reg, | |
278 | plat->volt_mask, sel); | |
279 | } | |
280 | ||
281 | static int pca9450_regulator_probe(struct udevice *dev) | |
282 | { | |
283 | struct pca9450_plat *plat = dev_get_plat(dev); | |
cd63c4a7 JZ |
284 | int i, type, ret; |
285 | unsigned int val; | |
286 | bool pmic_trim = false; | |
326337fb MV |
287 | |
288 | type = dev_get_driver_data(dev_get_parent(dev)); | |
289 | ||
69c573ca | 290 | if (type != NXP_CHIP_TYPE_PCA9450A && type != NXP_CHIP_TYPE_PCA9450BC && |
a857e472 | 291 | type != NXP_CHIP_TYPE_PCA9451A && type != NXP_CHIP_TYPE_PCA9452) { |
326337fb MV |
292 | debug("Unknown PMIC type\n"); |
293 | return -EINVAL; | |
294 | } | |
295 | ||
cd63c4a7 JZ |
296 | ret = pmic_reg_read(dev->parent, PCA9450_PWR_CTRL); |
297 | if (ret < 0) | |
298 | return ret; | |
299 | ||
300 | val = ret; | |
301 | ||
a857e472 JZ |
302 | if ((type == NXP_CHIP_TYPE_PCA9451A || type == NXP_CHIP_TYPE_PCA9452) && |
303 | (val & PCA9450_REG_PWRCTRL_TOFF_DEB)) | |
cd63c4a7 JZ |
304 | pmic_trim = true; |
305 | ||
326337fb MV |
306 | for (i = 0; i < ARRAY_SIZE(pca9450_reg_data); i++) { |
307 | if (strcmp(dev->name, pca9450_reg_data[i].name)) | |
308 | continue; | |
309 | ||
cd63c4a7 JZ |
310 | if (pmic_trim && (!strcmp(pca9450_reg_data[i].name, "BUCK1") || |
311 | !strcmp(pca9450_reg_data[i].name, "BUCK3"))) { | |
312 | *plat = pca9450_reg_data[i + 1]; | |
313 | return 0; | |
314 | } | |
315 | ||
326337fb MV |
316 | /* PCA9450B/PCA9450C uses BUCK1 and BUCK3 in dual-phase */ |
317 | if (type == NXP_CHIP_TYPE_PCA9450BC && | |
318 | !strcmp(pca9450_reg_data[i].name, "BUCK3")) { | |
319 | continue; | |
320 | } | |
321 | ||
69c573ca YL |
322 | /* PCA9451A uses BUCK3 in dual-phase and don't have LDO2 and LDO3 */ |
323 | if (type == NXP_CHIP_TYPE_PCA9451A && | |
324 | (!strcmp(pca9450_reg_data[i].name, "BUCK3") || | |
325 | !strcmp(pca9450_reg_data[i].name, "LDO2") || | |
326 | !strcmp(pca9450_reg_data[i].name, "LDO3"))) { | |
327 | continue; | |
328 | } | |
329 | ||
a857e472 JZ |
330 | if (type == NXP_CHIP_TYPE_PCA9452 && |
331 | (!strcmp(pca9450_reg_data[i].name, "BUCK3") || | |
332 | !strcmp(pca9450_reg_data[i].name, "LDO2"))) { | |
333 | continue; | |
334 | } | |
335 | ||
326337fb MV |
336 | *plat = pca9450_reg_data[i]; |
337 | ||
338 | return 0; | |
339 | } | |
340 | ||
341 | pr_err("Unknown regulator '%s'\n", dev->name); | |
342 | ||
343 | return -ENOENT; | |
344 | } | |
345 | ||
346 | static const struct dm_regulator_ops pca9450_regulator_ops = { | |
347 | .get_value = pca9450_get_value, | |
348 | .set_value = pca9450_set_value, | |
349 | .get_enable = pca9450_get_enable, | |
350 | .set_enable = pca9450_set_enable, | |
351 | }; | |
352 | ||
353 | U_BOOT_DRIVER(pca9450_regulator) = { | |
354 | .name = PCA9450_REGULATOR_DRIVER, | |
355 | .id = UCLASS_REGULATOR, | |
356 | .ops = &pca9450_regulator_ops, | |
357 | .probe = pca9450_regulator_probe, | |
358 | .plat_auto = sizeof(struct pca9450_plat), | |
359 | }; |