1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Copyright (C) 2017 NXP |
4 | * Copyright (C) 2019 Boundary Devices |
5 | * Copyright (C) 2020 Amarula Solutions(India) |
6 | */ |
7 | |
8 | #include <linux/delay.h> |
9 | #include <linux/err.h> |
10 | #include <linux/gpio/consumer.h> |
11 | #include <linux/i2c.h> |
12 | #include <linux/module.h> |
13 | #include <linux/regmap.h> |
14 | #include <linux/regulator/driver.h> |
15 | #include <linux/regulator/machine.h> |
16 | |
17 | /* registers */ |
18 | #define PF8X00_DEVICEID 0x00 |
19 | #define PF8X00_REVID 0x01 |
20 | #define PF8X00_EMREV 0x02 |
21 | #define PF8X00_PROGID 0x03 |
22 | #define PF8X00_IMS_INT 0x04 |
23 | #define PF8X00_IMS_THERM 0x07 |
24 | #define PF8X00_SW_MODE_INT 0x0a |
25 | #define PF8X00_SW_MODE_MASK 0x0b |
26 | #define PF8X00_IMS_SW_ILIM 0x12 |
27 | #define PF8X00_IMS_LDO_ILIM 0x15 |
28 | #define PF8X00_IMS_SW_UV 0x18 |
29 | #define PF8X00_IMS_SW_OV 0x1b |
30 | #define PF8X00_IMS_LDO_UV 0x1e |
31 | #define PF8X00_IMS_LDO_OV 0x21 |
32 | #define PF8X00_IMS_PWRON 0x24 |
33 | #define PF8X00_SYS_INT 0x27 |
34 | #define PF8X00_HARD_FAULT 0x29 |
35 | #define PF8X00_FSOB_FLAGS 0x2a |
36 | #define PF8X00_FSOB_SELECT 0x2b |
37 | #define PF8X00_ABIST_OV1 0x2c |
38 | #define PF8X00_ABIST_OV2 0x2d |
39 | #define PF8X00_ABIST_UV1 0x2e |
40 | #define PF8X00_ABIST_UV2 0x2f |
41 | #define PF8X00_TEST_FLAGS 0x30 |
42 | #define PF8X00_ABIST_RUN 0x31 |
43 | #define PF8X00_RANDOM_GEN 0x33 |
44 | #define PF8X00_RANDOM_CHK 0x34 |
45 | #define PF8X00_VMONEN1 0x35 |
46 | #define PF8X00_VMONEN2 0x36 |
47 | #define PF8X00_CTRL1 0x37 |
48 | #define PF8X00_CTRL2 0x38 |
49 | #define PF8X00_CTRL3 0x39 |
50 | #define PF8X00_PWRUP_CTRL 0x3a |
51 | #define PF8X00_RESETBMCU 0x3c |
52 | #define PF8X00_PGOOD 0x3d |
53 | #define PF8X00_PWRDN_DLY1 0x3e |
54 | #define PF8X00_PWRDN_DLY2 0x3f |
55 | #define PF8X00_FREQ_CTRL 0x40 |
56 | #define PF8X00_COINCELL_CTRL 0x41 |
57 | #define PF8X00_PWRON 0x42 |
58 | #define PF8X00_WD_CONFIG 0x43 |
59 | #define PF8X00_WD_CLEAR 0x44 |
60 | #define PF8X00_WD_EXPIRE 0x45 |
61 | #define PF8X00_WD_COUNTER 0x46 |
62 | #define PF8X00_FAULT_COUNTER 0x47 |
63 | #define PF8X00_FSAFE_COUNTER 0x48 |
64 | #define PF8X00_FAULT_TIMER 0x49 |
65 | #define PF8X00_AMUX 0x4a |
66 | #define PF8X00_SW1_CONFIG1 0x4d |
67 | #define PF8X00_LDO1_CONFIG1 0x85 |
68 | #define PF8X00_VSNVS_CONFIG1 0x9d |
69 | #define PF8X00_PAGE_SELECT 0x9f |
70 | |
71 | /* regulators */ |
72 | enum pf8x00_regulators { |
73 | PF8X00_LDO1, |
74 | PF8X00_LDO2, |
75 | PF8X00_LDO3, |
76 | PF8X00_LDO4, |
77 | PF8X00_BUCK1, |
78 | PF8X00_BUCK2, |
79 | PF8X00_BUCK3, |
80 | PF8X00_BUCK4, |
81 | PF8X00_BUCK5, |
82 | PF8X00_BUCK6, |
83 | PF8X00_BUCK7, |
84 | PF8X00_VSNVS, |
85 | |
86 | PF8X00_MAX_REGULATORS, |
87 | }; |
88 | |
89 | enum pf8x00_buck_states { |
90 | SW_CONFIG1, |
91 | SW_CONFIG2, |
92 | SW_PWRUP, |
93 | SW_MODE1, |
94 | SW_RUN_VOLT, |
95 | SW_STBY_VOLT, |
96 | }; |
97 | #define PF8X00_SW_BASE(i) (8 * (i - PF8X00_BUCK1) + PF8X00_SW1_CONFIG1) |
98 | |
99 | enum pf8x00_ldo_states { |
100 | LDO_CONFIG1, |
101 | LDO_CONFIG2, |
102 | LDO_PWRUP, |
103 | LDO_RUN_VOLT, |
104 | LDO_STBY_VOLT, |
105 | }; |
106 | #define PF8X00_LDO_BASE(i) (6 * (i - PF8X00_LDO1) + PF8X00_LDO1_CONFIG1) |
107 | |
108 | enum swxilim_bits { |
109 | SWXILIM_2100_MA, |
110 | SWXILIM_2600_MA, |
111 | SWXILIM_3000_MA, |
112 | SWXILIM_4500_MA, |
113 | }; |
114 | #define PF8X00_SWXILIM_SHIFT 3 |
115 | #define PF8X00_SWXILIM_MASK GENMASK(4, 3) |
116 | #define PF8X00_SWXPHASE_MASK GENMASK(2, 0) |
117 | #define PF8X00_SWXPHASE_SHIFT 7 |
118 | |
119 | enum pf8x00_devid { |
120 | PF8100 = 0x0, |
121 | PF8121A = BIT(1), |
122 | PF8200 = BIT(3), |
123 | }; |
124 | #define PF8X00_FAM BIT(6) |
125 | #define PF8X00_DEVICE_FAM_MASK GENMASK(7, 4) |
126 | #define PF8X00_DEVICE_ID_MASK GENMASK(3, 0) |
127 | |
128 | struct pf8x00_regulator_data { |
129 | struct regulator_desc desc; |
130 | unsigned int suspend_enable_reg; |
131 | unsigned int suspend_enable_mask; |
132 | unsigned int suspend_voltage_reg; |
133 | unsigned int suspend_voltage_cache; |
134 | }; |
135 | |
136 | struct pf8x00_chip { |
137 | struct regmap *regmap; |
138 | struct device *dev; |
139 | }; |
140 | |
141 | static const struct regmap_config pf8x00_regmap_config = { |
142 | .reg_bits = 8, |
143 | .val_bits = 8, |
144 | .max_register = PF8X00_PAGE_SELECT, |
145 | .cache_type = REGCACHE_RBTREE, |
146 | }; |
147 | |
148 | /* VLDOx output: 1.5V to 5.0V */ |
149 | static const int pf8x00_ldo_voltages[] = { |
150 | 1500000, 1600000, 1800000, 1850000, 2150000, 2500000, 2800000, 3000000, |
151 | 3100000, 3150000, 3200000, 3300000, 3350000, 1650000, 1700000, 5000000, |
152 | }; |
153 | |
154 | /* Output: 2.1A to 4.5A */ |
155 | static const unsigned int pf8x00_sw_current_table[] = { |
156 | 2100000, 2600000, 3000000, 4500000, |
157 | }; |
158 | |
159 | /* Output: 0.4V to 1.8V */ |
160 | #define PF8XOO_SW1_6_VOLTAGE_NUM 0xB2 |
161 | static const struct linear_range pf8x00_sw1_to_6_voltages[] = { |
162 | REGULATOR_LINEAR_RANGE(400000, 0x00, 0xB0, 6250), |
163 | REGULATOR_LINEAR_RANGE(1800000, 0xB1, 0xB1, 0), |
164 | }; |
165 | |
166 | /* Output: 1.0V to 4.1V */ |
167 | static const int pf8x00_sw7_voltages[] = { |
168 | 1000000, 1100000, 1200000, 1250000, 1300000, 1350000, 1500000, 1600000, |
169 | 1800000, 1850000, 2000000, 2100000, 2150000, 2250000, 2300000, 2400000, |
170 | 2500000, 2800000, 3150000, 3200000, 3250000, 3300000, 3350000, 3400000, |
171 | 3500000, 3800000, 4000000, 4100000, 4100000, 4100000, 4100000, 4100000, |
172 | }; |
173 | |
174 | /* Output: 1.8V, 3.0V, or 3.3V */ |
175 | static const int pf8x00_vsnvs_voltages[] = { |
176 | 0, 1800000, 3000000, 3300000, |
177 | }; |
178 | |
179 | static void swxilim_select(struct pf8x00_chip *chip, int id, int ilim) |
180 | { |
181 | u8 ilim_sel; |
182 | u8 reg = PF8X00_SW_BASE(id) + SW_CONFIG2; |
183 | |
184 | switch (ilim) { |
185 | case 2100: |
186 | ilim_sel = SWXILIM_2100_MA; |
187 | break; |
188 | case 2600: |
189 | ilim_sel = SWXILIM_2600_MA; |
190 | break; |
191 | case 3000: |
192 | ilim_sel = SWXILIM_3000_MA; |
193 | break; |
194 | case 4500: |
195 | ilim_sel = SWXILIM_4500_MA; |
196 | break; |
197 | default: |
198 | ilim_sel = SWXILIM_2100_MA; |
199 | break; |
200 | } |
201 | |
202 | regmap_update_bits(map: chip->regmap, reg, |
203 | PF8X00_SWXILIM_MASK, |
204 | val: ilim_sel << PF8X00_SWXILIM_SHIFT); |
205 | } |
206 | |
207 | static void handle_ilim_property(struct device_node *np, |
208 | const struct regulator_desc *desc, |
209 | struct regulator_config *config) |
210 | { |
211 | struct pf8x00_chip *chip = config->driver_data; |
212 | int ret; |
213 | int val; |
214 | |
215 | if ((desc->id >= PF8X00_BUCK1) && (desc->id <= PF8X00_BUCK7)) { |
216 | ret = of_property_read_u32(np, propname: "nxp,ilim-ma" , out_value: &val); |
217 | if (ret) { |
218 | dev_dbg(chip->dev, "unspecified ilim for BUCK%d, use value stored in OTP\n" , |
219 | desc->id - PF8X00_LDO4); |
220 | return; |
221 | } |
222 | |
223 | dev_warn(chip->dev, "nxp,ilim-ma is deprecated, please use regulator-max-microamp\n" ); |
224 | swxilim_select(chip, id: desc->id, ilim: val); |
225 | |
226 | } else |
227 | dev_warn(chip->dev, "nxp,ilim-ma used with incorrect regulator (%d)\n" , desc->id); |
228 | } |
229 | |
230 | static void handle_shift_property(struct device_node *np, |
231 | const struct regulator_desc *desc, |
232 | struct regulator_config *config) |
233 | { |
234 | unsigned char id = desc->id - PF8X00_LDO4; |
235 | unsigned char reg = PF8X00_SW_BASE(id) + SW_CONFIG2; |
236 | struct pf8x00_chip *chip = config->driver_data; |
237 | |
238 | int phase; |
239 | int val; |
240 | int ret; |
241 | if ((desc->id >= PF8X00_BUCK1) && (desc->id <= PF8X00_BUCK7)) { |
242 | ret = of_property_read_u32(np, propname: "nxp,phase-shift" , out_value: &val); |
243 | if (ret) { |
244 | dev_dbg(chip->dev, |
245 | "unspecified phase-shift for BUCK%d, using OTP configuration\n" , |
246 | id); |
247 | return; |
248 | } |
249 | |
250 | if (val < 0 || val > 315 || val % 45 != 0) { |
251 | dev_warn(config->dev, |
252 | "invalid phase_shift %d for BUCK%d, using OTP configuration\n" , |
253 | val, id); |
254 | return; |
255 | } |
256 | |
257 | phase = val / 45; |
258 | |
259 | if (phase >= 1) |
260 | phase -= 1; |
261 | else |
262 | phase = PF8X00_SWXPHASE_SHIFT; |
263 | |
264 | regmap_update_bits(map: chip->regmap, reg, |
265 | PF8X00_SWXPHASE_MASK, |
266 | val: phase); |
267 | } else |
268 | dev_warn(chip->dev, "nxp,phase-shift used with incorrect regulator (%d)\n" , id); |
269 | |
270 | } |
271 | |
272 | static int pf8x00_of_parse_cb(struct device_node *np, |
273 | const struct regulator_desc *desc, |
274 | struct regulator_config *config) |
275 | { |
276 | |
277 | handle_ilim_property(np, desc, config); |
278 | handle_shift_property(np, desc, config); |
279 | |
280 | return 0; |
281 | } |
282 | |
283 | static int pf8x00_suspend_enable(struct regulator_dev *rdev) |
284 | { |
285 | struct pf8x00_regulator_data *regl = rdev_get_drvdata(rdev); |
286 | struct regmap *rmap = rdev_get_regmap(rdev); |
287 | |
288 | return regmap_update_bits(map: rmap, reg: regl->suspend_enable_reg, |
289 | mask: regl->suspend_enable_mask, |
290 | val: regl->suspend_enable_mask); |
291 | } |
292 | |
293 | static int pf8x00_suspend_disable(struct regulator_dev *rdev) |
294 | { |
295 | struct pf8x00_regulator_data *regl = rdev_get_drvdata(rdev); |
296 | struct regmap *rmap = rdev_get_regmap(rdev); |
297 | |
298 | return regmap_update_bits(map: rmap, reg: regl->suspend_enable_reg, |
299 | mask: regl->suspend_enable_mask, val: 0); |
300 | } |
301 | |
302 | static int pf8x00_set_suspend_voltage(struct regulator_dev *rdev, int uV) |
303 | { |
304 | struct pf8x00_regulator_data *regl = rdev_get_drvdata(rdev); |
305 | int ret; |
306 | |
307 | if (regl->suspend_voltage_cache == uV) |
308 | return 0; |
309 | |
310 | ret = regulator_map_voltage_iterate(rdev, min_uV: uV, max_uV: uV); |
311 | if (ret < 0) { |
312 | dev_err(rdev_get_dev(rdev), "failed to map %i uV\n" , uV); |
313 | return ret; |
314 | } |
315 | |
316 | dev_dbg(rdev_get_dev(rdev), "uV: %i, reg: 0x%x, msk: 0x%x, val: 0x%x\n" , |
317 | uV, regl->suspend_voltage_reg, regl->desc.vsel_mask, ret); |
318 | ret = regmap_update_bits(map: rdev->regmap, reg: regl->suspend_voltage_reg, |
319 | mask: regl->desc.vsel_mask, val: ret); |
320 | if (ret < 0) { |
321 | dev_err(rdev_get_dev(rdev), "failed to set %i uV\n" , uV); |
322 | return ret; |
323 | } |
324 | |
325 | regl->suspend_voltage_cache = uV; |
326 | |
327 | return 0; |
328 | } |
329 | |
330 | static const struct regulator_ops pf8x00_ldo_ops = { |
331 | .enable = regulator_enable_regmap, |
332 | .disable = regulator_disable_regmap, |
333 | .is_enabled = regulator_is_enabled_regmap, |
334 | .list_voltage = regulator_list_voltage_table, |
335 | .set_voltage_sel = regulator_set_voltage_sel_regmap, |
336 | .get_voltage_sel = regulator_get_voltage_sel_regmap, |
337 | .set_suspend_enable = pf8x00_suspend_enable, |
338 | .set_suspend_disable = pf8x00_suspend_disable, |
339 | .set_suspend_voltage = pf8x00_set_suspend_voltage, |
340 | }; |
341 | |
342 | |
343 | static const struct regulator_ops pf8x00_buck1_6_ops = { |
344 | .enable = regulator_enable_regmap, |
345 | .disable = regulator_disable_regmap, |
346 | .is_enabled = regulator_is_enabled_regmap, |
347 | .list_voltage = regulator_list_voltage_linear_range, |
348 | .set_voltage_sel = regulator_set_voltage_sel_regmap, |
349 | .get_voltage_sel = regulator_get_voltage_sel_regmap, |
350 | .get_current_limit = regulator_get_current_limit_regmap, |
351 | .set_current_limit = regulator_set_current_limit_regmap, |
352 | .set_suspend_enable = pf8x00_suspend_enable, |
353 | .set_suspend_disable = pf8x00_suspend_disable, |
354 | .set_suspend_voltage = pf8x00_set_suspend_voltage, |
355 | }; |
356 | |
357 | static const struct regulator_ops pf8x00_buck7_ops = { |
358 | .enable = regulator_enable_regmap, |
359 | .disable = regulator_disable_regmap, |
360 | .is_enabled = regulator_is_enabled_regmap, |
361 | .list_voltage = regulator_list_voltage_table, |
362 | .map_voltage = regulator_map_voltage_ascend, |
363 | .set_voltage_sel = regulator_set_voltage_sel_regmap, |
364 | .get_voltage_sel = regulator_get_voltage_sel_regmap, |
365 | .get_current_limit = regulator_get_current_limit_regmap, |
366 | .set_current_limit = regulator_set_current_limit_regmap, |
367 | .set_suspend_enable = pf8x00_suspend_enable, |
368 | .set_suspend_disable = pf8x00_suspend_disable, |
369 | }; |
370 | |
371 | static const struct regulator_ops pf8x00_vsnvs_ops = { |
372 | .enable = regulator_enable_regmap, |
373 | .disable = regulator_disable_regmap, |
374 | .is_enabled = regulator_is_enabled_regmap, |
375 | .list_voltage = regulator_list_voltage_table, |
376 | .map_voltage = regulator_map_voltage_ascend, |
377 | .set_voltage_sel = regulator_set_voltage_sel_regmap, |
378 | .get_voltage_sel = regulator_get_voltage_sel_regmap, |
379 | }; |
380 | |
381 | #define PF8X00LDO(_id, _name, base, voltages) \ |
382 | [PF8X00_LDO ## _id] = { \ |
383 | .desc = { \ |
384 | .name = _name, \ |
385 | .of_match = _name, \ |
386 | .regulators_node = "regulators", \ |
387 | .n_voltages = ARRAY_SIZE(voltages), \ |
388 | .ops = &pf8x00_ldo_ops, \ |
389 | .type = REGULATOR_VOLTAGE, \ |
390 | .id = PF8X00_LDO ## _id, \ |
391 | .owner = THIS_MODULE, \ |
392 | .volt_table = voltages, \ |
393 | .vsel_reg = (base) + LDO_RUN_VOLT, \ |
394 | .vsel_mask = 0xff, \ |
395 | .enable_reg = (base) + LDO_CONFIG2, \ |
396 | .enable_val = 0x2, \ |
397 | .disable_val = 0x0, \ |
398 | .enable_mask = 2, \ |
399 | }, \ |
400 | .suspend_enable_reg = (base) + LDO_CONFIG2, \ |
401 | .suspend_enable_mask = 1, \ |
402 | .suspend_voltage_reg = (base) + LDO_STBY_VOLT, \ |
403 | } |
404 | |
405 | #define PF8X00BUCK(_id, _name, base, voltages) \ |
406 | [PF8X00_BUCK ## _id] = { \ |
407 | .desc = { \ |
408 | .name = _name, \ |
409 | .of_match = _name, \ |
410 | .regulators_node = "regulators", \ |
411 | .of_parse_cb = pf8x00_of_parse_cb, \ |
412 | .n_voltages = PF8XOO_SW1_6_VOLTAGE_NUM, \ |
413 | .ops = &pf8x00_buck1_6_ops, \ |
414 | .type = REGULATOR_VOLTAGE, \ |
415 | .id = PF8X00_BUCK ## _id, \ |
416 | .owner = THIS_MODULE, \ |
417 | .ramp_delay = 19000, \ |
418 | .linear_ranges = pf8x00_sw1_to_6_voltages, \ |
419 | .n_linear_ranges = \ |
420 | ARRAY_SIZE(pf8x00_sw1_to_6_voltages), \ |
421 | .vsel_reg = (base) + SW_RUN_VOLT, \ |
422 | .vsel_mask = 0xff, \ |
423 | .curr_table = pf8x00_sw_current_table, \ |
424 | .n_current_limits = \ |
425 | ARRAY_SIZE(pf8x00_sw_current_table), \ |
426 | .csel_reg = (base) + SW_CONFIG2, \ |
427 | .csel_mask = PF8X00_SWXILIM_MASK, \ |
428 | .enable_reg = (base) + SW_MODE1, \ |
429 | .enable_val = 0x3, \ |
430 | .disable_val = 0x0, \ |
431 | .enable_mask = 0x3, \ |
432 | .enable_time = 500, \ |
433 | }, \ |
434 | .suspend_enable_reg = (base) + SW_MODE1, \ |
435 | .suspend_enable_mask = 0xc, \ |
436 | .suspend_voltage_reg = (base) + SW_STBY_VOLT, \ |
437 | } |
438 | |
439 | #define PF8X00BUCK7(_name, base, voltages) \ |
440 | [PF8X00_BUCK7] = { \ |
441 | .desc = { \ |
442 | .name = _name, \ |
443 | .of_match = _name, \ |
444 | .regulators_node = "regulators", \ |
445 | .of_parse_cb = pf8x00_of_parse_cb, \ |
446 | .n_voltages = ARRAY_SIZE(voltages), \ |
447 | .ops = &pf8x00_buck7_ops, \ |
448 | .type = REGULATOR_VOLTAGE, \ |
449 | .id = PF8X00_BUCK7, \ |
450 | .owner = THIS_MODULE, \ |
451 | .ramp_delay = 19000, \ |
452 | .volt_table = voltages, \ |
453 | .vsel_reg = (base) + SW_RUN_VOLT, \ |
454 | .vsel_mask = 0xff, \ |
455 | .curr_table = pf8x00_sw_current_table, \ |
456 | .n_current_limits = \ |
457 | ARRAY_SIZE(pf8x00_sw_current_table), \ |
458 | .csel_reg = (base) + SW_CONFIG2, \ |
459 | .csel_mask = PF8X00_SWXILIM_MASK, \ |
460 | .enable_reg = (base) + SW_MODE1, \ |
461 | .enable_val = 0x3, \ |
462 | .disable_val = 0x0, \ |
463 | .enable_mask = 0x3, \ |
464 | .enable_time = 500, \ |
465 | }, \ |
466 | } |
467 | |
468 | |
469 | #define PF8X00VSNVS(_name, base, voltages) \ |
470 | [PF8X00_VSNVS] = { \ |
471 | .desc = { \ |
472 | .name = _name, \ |
473 | .of_match = _name, \ |
474 | .regulators_node = "regulators", \ |
475 | .n_voltages = ARRAY_SIZE(voltages), \ |
476 | .ops = &pf8x00_vsnvs_ops, \ |
477 | .type = REGULATOR_VOLTAGE, \ |
478 | .id = PF8X00_VSNVS, \ |
479 | .owner = THIS_MODULE, \ |
480 | .volt_table = voltages, \ |
481 | .vsel_reg = (base), \ |
482 | .vsel_mask = 0x3, \ |
483 | }, \ |
484 | } |
485 | |
486 | static struct pf8x00_regulator_data pf8x00_regs_data[PF8X00_MAX_REGULATORS] = { |
487 | PF8X00LDO(1, "ldo1" , PF8X00_LDO_BASE(PF8X00_LDO1), pf8x00_ldo_voltages), |
488 | PF8X00LDO(2, "ldo2" , PF8X00_LDO_BASE(PF8X00_LDO2), pf8x00_ldo_voltages), |
489 | PF8X00LDO(3, "ldo3" , PF8X00_LDO_BASE(PF8X00_LDO3), pf8x00_ldo_voltages), |
490 | PF8X00LDO(4, "ldo4" , PF8X00_LDO_BASE(PF8X00_LDO4), pf8x00_ldo_voltages), |
491 | PF8X00BUCK(1, "buck1" , PF8X00_SW_BASE(PF8X00_BUCK1), pf8x00_sw1_to_6_voltages), |
492 | PF8X00BUCK(2, "buck2" , PF8X00_SW_BASE(PF8X00_BUCK2), pf8x00_sw1_to_6_voltages), |
493 | PF8X00BUCK(3, "buck3" , PF8X00_SW_BASE(PF8X00_BUCK3), pf8x00_sw1_to_6_voltages), |
494 | PF8X00BUCK(4, "buck4" , PF8X00_SW_BASE(PF8X00_BUCK4), pf8x00_sw1_to_6_voltages), |
495 | PF8X00BUCK(5, "buck5" , PF8X00_SW_BASE(PF8X00_BUCK5), pf8x00_sw1_to_6_voltages), |
496 | PF8X00BUCK(6, "buck6" , PF8X00_SW_BASE(PF8X00_BUCK6), pf8x00_sw1_to_6_voltages), |
497 | PF8X00BUCK7("buck7" , PF8X00_SW_BASE(PF8X00_BUCK7), pf8x00_sw7_voltages), |
498 | PF8X00VSNVS("vsnvs" , PF8X00_VSNVS_CONFIG1, pf8x00_vsnvs_voltages), |
499 | }; |
500 | |
501 | static int pf8x00_identify(struct pf8x00_chip *chip) |
502 | { |
503 | unsigned int value; |
504 | u8 dev_fam, dev_id; |
505 | const char *name = NULL; |
506 | int ret; |
507 | |
508 | ret = regmap_read(map: chip->regmap, PF8X00_DEVICEID, val: &value); |
509 | if (ret) { |
510 | dev_err(chip->dev, "failed to read chip family\n" ); |
511 | return ret; |
512 | } |
513 | |
514 | dev_fam = value & PF8X00_DEVICE_FAM_MASK; |
515 | switch (dev_fam) { |
516 | case PF8X00_FAM: |
517 | break; |
518 | default: |
519 | dev_err(chip->dev, |
520 | "Chip 0x%x is not from PF8X00 family\n" , dev_fam); |
521 | return ret; |
522 | } |
523 | |
524 | dev_id = value & PF8X00_DEVICE_ID_MASK; |
525 | switch (dev_id) { |
526 | case PF8100: |
527 | name = "PF8100" ; |
528 | break; |
529 | case PF8121A: |
530 | name = "PF8121A" ; |
531 | break; |
532 | case PF8200: |
533 | name = "PF8200" ; |
534 | break; |
535 | default: |
536 | dev_err(chip->dev, "Unknown pf8x00 device id 0x%x\n" , dev_id); |
537 | return -ENODEV; |
538 | } |
539 | |
540 | dev_info(chip->dev, "%s PMIC found.\n" , name); |
541 | |
542 | return 0; |
543 | } |
544 | |
545 | static int pf8x00_i2c_probe(struct i2c_client *client) |
546 | { |
547 | struct regulator_config config = { NULL, }; |
548 | struct pf8x00_chip *chip; |
549 | int id; |
550 | int ret; |
551 | |
552 | chip = devm_kzalloc(dev: &client->dev, size: sizeof(*chip), GFP_KERNEL); |
553 | if (!chip) |
554 | return -ENOMEM; |
555 | |
556 | i2c_set_clientdata(client, data: chip); |
557 | chip->dev = &client->dev; |
558 | |
559 | chip->regmap = devm_regmap_init_i2c(client, &pf8x00_regmap_config); |
560 | if (IS_ERR(ptr: chip->regmap)) { |
561 | ret = PTR_ERR(ptr: chip->regmap); |
562 | dev_err(&client->dev, |
563 | "regmap allocation failed with err %d\n" , ret); |
564 | return ret; |
565 | } |
566 | |
567 | ret = pf8x00_identify(chip); |
568 | if (ret) |
569 | return ret; |
570 | |
571 | for (id = 0; id < ARRAY_SIZE(pf8x00_regs_data); id++) { |
572 | struct pf8x00_regulator_data *data = &pf8x00_regs_data[id]; |
573 | struct regulator_dev *rdev; |
574 | |
575 | config.dev = chip->dev; |
576 | config.driver_data = data; |
577 | config.regmap = chip->regmap; |
578 | |
579 | rdev = devm_regulator_register(dev: &client->dev, regulator_desc: &data->desc, config: &config); |
580 | if (IS_ERR(ptr: rdev)) { |
581 | dev_err(&client->dev, |
582 | "failed to register %s regulator\n" , data->desc.name); |
583 | return PTR_ERR(ptr: rdev); |
584 | } |
585 | } |
586 | |
587 | return 0; |
588 | } |
589 | |
590 | static const struct of_device_id pf8x00_dt_ids[] = { |
591 | { .compatible = "nxp,pf8100" ,}, |
592 | { .compatible = "nxp,pf8121a" ,}, |
593 | { .compatible = "nxp,pf8200" ,}, |
594 | { } |
595 | }; |
596 | MODULE_DEVICE_TABLE(of, pf8x00_dt_ids); |
597 | |
598 | static const struct i2c_device_id pf8x00_i2c_id[] = { |
599 | { "pf8100" , 0 }, |
600 | { "pf8121a" , 0 }, |
601 | { "pf8200" , 0 }, |
602 | {}, |
603 | }; |
604 | MODULE_DEVICE_TABLE(i2c, pf8x00_i2c_id); |
605 | |
606 | static struct i2c_driver pf8x00_regulator_driver = { |
607 | .id_table = pf8x00_i2c_id, |
608 | .driver = { |
609 | .name = "pf8x00" , |
610 | .probe_type = PROBE_PREFER_ASYNCHRONOUS, |
611 | .of_match_table = pf8x00_dt_ids, |
612 | }, |
613 | .probe = pf8x00_i2c_probe, |
614 | }; |
615 | module_i2c_driver(pf8x00_regulator_driver); |
616 | |
617 | MODULE_AUTHOR("Jagan Teki <jagan@amarulasolutions.com>" ); |
618 | MODULE_AUTHOR("Troy Kisky <troy.kisky@boundarydevices.com>" ); |
619 | MODULE_DESCRIPTION("Regulator Driver for NXP's PF8100/PF8121A/PF8200 PMIC" ); |
620 | MODULE_LICENSE("GPL v2" ); |
621 | |