1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * AXP20x PMIC USB power supply status driver |
4 | * |
5 | * Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com> |
6 | * Copyright (C) 2014 Bruno Prémont <bonbons@linux-vserver.org> |
7 | */ |
8 | |
9 | #include <linux/bitops.h> |
10 | #include <linux/device.h> |
11 | #include <linux/devm-helpers.h> |
12 | #include <linux/init.h> |
13 | #include <linux/interrupt.h> |
14 | #include <linux/kernel.h> |
15 | #include <linux/mfd/axp20x.h> |
16 | #include <linux/module.h> |
17 | #include <linux/of.h> |
18 | #include <linux/platform_device.h> |
19 | #include <linux/pm.h> |
20 | #include <linux/power_supply.h> |
21 | #include <linux/regmap.h> |
22 | #include <linux/slab.h> |
23 | #include <linux/iio/consumer.h> |
24 | #include <linux/workqueue.h> |
25 | |
26 | #define DRVNAME "axp20x-usb-power-supply" |
27 | |
28 | #define AXP192_USB_OTG_STATUS 0x04 |
29 | |
30 | #define AXP20X_PWR_STATUS_VBUS_PRESENT BIT(5) |
31 | #define AXP20X_PWR_STATUS_VBUS_USED BIT(4) |
32 | |
33 | #define AXP20X_USB_STATUS_VBUS_VALID BIT(2) |
34 | |
35 | #define AXP20X_VBUS_VHOLD_uV(b) (4000000 + (((b) >> 3) & 7) * 100000) |
36 | #define AXP20X_VBUS_VHOLD_MASK GENMASK(5, 3) |
37 | #define AXP20X_VBUS_VHOLD_OFFSET 3 |
38 | |
39 | #define AXP20X_ADC_EN1_VBUS_CURR BIT(2) |
40 | #define AXP20X_ADC_EN1_VBUS_VOLT BIT(3) |
41 | |
42 | /* |
43 | * Note do not raise the debounce time, we must report Vusb high within |
44 | * 100ms otherwise we get Vbus errors in musb. |
45 | */ |
46 | #define DEBOUNCE_TIME msecs_to_jiffies(50) |
47 | |
48 | struct axp_data { |
49 | const struct power_supply_desc *power_desc; |
50 | const char * const *irq_names; |
51 | unsigned int num_irq_names; |
52 | const int *curr_lim_table; |
53 | struct reg_field curr_lim_fld; |
54 | struct reg_field vbus_valid_bit; |
55 | struct reg_field vbus_mon_bit; |
56 | struct reg_field usb_bc_en_bit; |
57 | struct reg_field vbus_disable_bit; |
58 | bool vbus_needs_polling: 1; |
59 | }; |
60 | |
61 | struct axp20x_usb_power { |
62 | struct regmap *regmap; |
63 | struct regmap_field *curr_lim_fld; |
64 | struct regmap_field *vbus_valid_bit; |
65 | struct regmap_field *vbus_mon_bit; |
66 | struct regmap_field *usb_bc_en_bit; |
67 | struct regmap_field *vbus_disable_bit; |
68 | struct power_supply *supply; |
69 | const struct axp_data *axp_data; |
70 | struct iio_channel *vbus_v; |
71 | struct iio_channel *vbus_i; |
72 | struct delayed_work vbus_detect; |
73 | unsigned int old_status; |
74 | unsigned int online; |
75 | unsigned int num_irqs; |
76 | unsigned int irqs[] __counted_by(num_irqs); |
77 | }; |
78 | |
79 | static bool axp20x_usb_vbus_needs_polling(struct axp20x_usb_power *power) |
80 | { |
81 | /* |
82 | * Polling is only necessary while VBUS is offline. While online, a |
83 | * present->absent transition implies an online->offline transition |
84 | * and will trigger the VBUS_REMOVAL IRQ. |
85 | */ |
86 | if (power->axp_data->vbus_needs_polling && !power->online) |
87 | return true; |
88 | |
89 | return false; |
90 | } |
91 | |
92 | static irqreturn_t axp20x_usb_power_irq(int irq, void *devid) |
93 | { |
94 | struct axp20x_usb_power *power = devid; |
95 | |
96 | power_supply_changed(psy: power->supply); |
97 | |
98 | mod_delayed_work(wq: system_power_efficient_wq, dwork: &power->vbus_detect, DEBOUNCE_TIME); |
99 | |
100 | return IRQ_HANDLED; |
101 | } |
102 | |
103 | static void axp20x_usb_power_poll_vbus(struct work_struct *work) |
104 | { |
105 | struct axp20x_usb_power *power = |
106 | container_of(work, struct axp20x_usb_power, vbus_detect.work); |
107 | unsigned int val; |
108 | int ret; |
109 | |
110 | ret = regmap_read(map: power->regmap, AXP20X_PWR_INPUT_STATUS, val: &val); |
111 | if (ret) |
112 | goto out; |
113 | |
114 | val &= (AXP20X_PWR_STATUS_VBUS_PRESENT | AXP20X_PWR_STATUS_VBUS_USED); |
115 | if (val != power->old_status) |
116 | power_supply_changed(psy: power->supply); |
117 | |
118 | power->old_status = val; |
119 | power->online = val & AXP20X_PWR_STATUS_VBUS_USED; |
120 | |
121 | out: |
122 | if (axp20x_usb_vbus_needs_polling(power)) |
123 | mod_delayed_work(wq: system_power_efficient_wq, dwork: &power->vbus_detect, DEBOUNCE_TIME); |
124 | } |
125 | |
126 | static int axp20x_usb_power_get_property(struct power_supply *psy, |
127 | enum power_supply_property psp, union power_supply_propval *val) |
128 | { |
129 | struct axp20x_usb_power *power = power_supply_get_drvdata(psy); |
130 | unsigned int input, v; |
131 | int ret; |
132 | |
133 | switch (psp) { |
134 | case POWER_SUPPLY_PROP_VOLTAGE_MIN: |
135 | ret = regmap_read(map: power->regmap, AXP20X_VBUS_IPSOUT_MGMT, val: &v); |
136 | if (ret) |
137 | return ret; |
138 | |
139 | val->intval = AXP20X_VBUS_VHOLD_uV(v); |
140 | return 0; |
141 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
142 | if (IS_ENABLED(CONFIG_AXP20X_ADC)) { |
143 | ret = iio_read_channel_processed(chan: power->vbus_v, |
144 | val: &val->intval); |
145 | if (ret) |
146 | return ret; |
147 | |
148 | /* |
149 | * IIO framework gives mV but Power Supply framework |
150 | * gives uV. |
151 | */ |
152 | val->intval *= 1000; |
153 | return 0; |
154 | } |
155 | |
156 | ret = axp20x_read_variable_width(regmap: power->regmap, |
157 | AXP20X_VBUS_V_ADC_H, width: 12); |
158 | if (ret < 0) |
159 | return ret; |
160 | |
161 | val->intval = ret * 1700; /* 1 step = 1.7 mV */ |
162 | return 0; |
163 | case POWER_SUPPLY_PROP_CURRENT_MAX: |
164 | ret = regmap_field_read(field: power->curr_lim_fld, val: &v); |
165 | if (ret) |
166 | return ret; |
167 | |
168 | val->intval = power->axp_data->curr_lim_table[v]; |
169 | return 0; |
170 | case POWER_SUPPLY_PROP_CURRENT_NOW: |
171 | if (IS_ENABLED(CONFIG_AXP20X_ADC)) { |
172 | ret = iio_read_channel_processed(chan: power->vbus_i, |
173 | val: &val->intval); |
174 | if (ret) |
175 | return ret; |
176 | |
177 | /* |
178 | * IIO framework gives mA but Power Supply framework |
179 | * gives uA. |
180 | */ |
181 | val->intval *= 1000; |
182 | return 0; |
183 | } |
184 | |
185 | ret = axp20x_read_variable_width(regmap: power->regmap, |
186 | AXP20X_VBUS_I_ADC_H, width: 12); |
187 | if (ret < 0) |
188 | return ret; |
189 | |
190 | val->intval = ret * 375; /* 1 step = 0.375 mA */ |
191 | return 0; |
192 | default: |
193 | break; |
194 | } |
195 | |
196 | /* All the properties below need the input-status reg value */ |
197 | ret = regmap_read(map: power->regmap, AXP20X_PWR_INPUT_STATUS, val: &input); |
198 | if (ret) |
199 | return ret; |
200 | |
201 | switch (psp) { |
202 | case POWER_SUPPLY_PROP_HEALTH: |
203 | if (!(input & AXP20X_PWR_STATUS_VBUS_PRESENT)) { |
204 | val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; |
205 | break; |
206 | } |
207 | |
208 | val->intval = POWER_SUPPLY_HEALTH_GOOD; |
209 | |
210 | if (power->vbus_valid_bit) { |
211 | ret = regmap_field_read(field: power->vbus_valid_bit, val: &v); |
212 | if (ret) |
213 | return ret; |
214 | |
215 | if (v == 0) |
216 | val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; |
217 | } |
218 | |
219 | break; |
220 | case POWER_SUPPLY_PROP_PRESENT: |
221 | val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_PRESENT); |
222 | break; |
223 | case POWER_SUPPLY_PROP_ONLINE: |
224 | val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_USED); |
225 | break; |
226 | default: |
227 | return -EINVAL; |
228 | } |
229 | |
230 | return 0; |
231 | } |
232 | |
233 | static int axp20x_usb_power_set_voltage_min(struct axp20x_usb_power *power, |
234 | int intval) |
235 | { |
236 | int val; |
237 | |
238 | switch (intval) { |
239 | case 4000000: |
240 | case 4100000: |
241 | case 4200000: |
242 | case 4300000: |
243 | case 4400000: |
244 | case 4500000: |
245 | case 4600000: |
246 | case 4700000: |
247 | val = (intval - 4000000) / 100000; |
248 | return regmap_update_bits(map: power->regmap, |
249 | AXP20X_VBUS_IPSOUT_MGMT, |
250 | AXP20X_VBUS_VHOLD_MASK, |
251 | val: val << AXP20X_VBUS_VHOLD_OFFSET); |
252 | default: |
253 | return -EINVAL; |
254 | } |
255 | |
256 | return -EINVAL; |
257 | } |
258 | |
259 | static int axp20x_usb_power_set_current_max(struct axp20x_usb_power *power, int intval) |
260 | { |
261 | const unsigned int max = GENMASK(power->axp_data->curr_lim_fld.msb, |
262 | power->axp_data->curr_lim_fld.lsb); |
263 | |
264 | if (intval == -1) |
265 | return -EINVAL; |
266 | |
267 | for (unsigned int i = 0; i <= max; ++i) |
268 | if (power->axp_data->curr_lim_table[i] == intval) |
269 | return regmap_field_write(field: power->curr_lim_fld, val: i); |
270 | |
271 | return -EINVAL; |
272 | } |
273 | |
274 | static int axp20x_usb_power_set_property(struct power_supply *psy, |
275 | enum power_supply_property psp, |
276 | const union power_supply_propval *val) |
277 | { |
278 | struct axp20x_usb_power *power = power_supply_get_drvdata(psy); |
279 | |
280 | switch (psp) { |
281 | case POWER_SUPPLY_PROP_ONLINE: |
282 | if (!power->vbus_disable_bit) |
283 | return -EINVAL; |
284 | |
285 | return regmap_field_write(field: power->vbus_disable_bit, val: !val->intval); |
286 | |
287 | case POWER_SUPPLY_PROP_VOLTAGE_MIN: |
288 | return axp20x_usb_power_set_voltage_min(power, intval: val->intval); |
289 | |
290 | case POWER_SUPPLY_PROP_CURRENT_MAX: |
291 | return axp20x_usb_power_set_current_max(power, intval: val->intval); |
292 | |
293 | default: |
294 | return -EINVAL; |
295 | } |
296 | |
297 | return -EINVAL; |
298 | } |
299 | |
300 | static int axp20x_usb_power_prop_writeable(struct power_supply *psy, |
301 | enum power_supply_property psp) |
302 | { |
303 | struct axp20x_usb_power *power = power_supply_get_drvdata(psy); |
304 | |
305 | /* |
306 | * The VBUS path select flag works differently on AXP288 and newer: |
307 | * - On AXP20x and AXP22x, the flag enables VBUS (ignoring N_VBUSEN). |
308 | * - On AXP288 and AXP8xx, the flag disables VBUS (ignoring N_VBUSEN). |
309 | * We only expose the control on variants where it can be used to force |
310 | * the VBUS input offline. |
311 | */ |
312 | if (psp == POWER_SUPPLY_PROP_ONLINE) |
313 | return power->vbus_disable_bit != NULL; |
314 | |
315 | return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN || |
316 | psp == POWER_SUPPLY_PROP_CURRENT_MAX; |
317 | } |
318 | |
319 | static enum power_supply_property axp20x_usb_power_properties[] = { |
320 | POWER_SUPPLY_PROP_HEALTH, |
321 | POWER_SUPPLY_PROP_PRESENT, |
322 | POWER_SUPPLY_PROP_ONLINE, |
323 | POWER_SUPPLY_PROP_VOLTAGE_MIN, |
324 | POWER_SUPPLY_PROP_VOLTAGE_NOW, |
325 | POWER_SUPPLY_PROP_CURRENT_MAX, |
326 | POWER_SUPPLY_PROP_CURRENT_NOW, |
327 | }; |
328 | |
329 | static enum power_supply_property axp22x_usb_power_properties[] = { |
330 | POWER_SUPPLY_PROP_HEALTH, |
331 | POWER_SUPPLY_PROP_PRESENT, |
332 | POWER_SUPPLY_PROP_ONLINE, |
333 | POWER_SUPPLY_PROP_VOLTAGE_MIN, |
334 | POWER_SUPPLY_PROP_CURRENT_MAX, |
335 | }; |
336 | |
337 | static const struct power_supply_desc axp20x_usb_power_desc = { |
338 | .name = "axp20x-usb" , |
339 | .type = POWER_SUPPLY_TYPE_USB, |
340 | .properties = axp20x_usb_power_properties, |
341 | .num_properties = ARRAY_SIZE(axp20x_usb_power_properties), |
342 | .property_is_writeable = axp20x_usb_power_prop_writeable, |
343 | .get_property = axp20x_usb_power_get_property, |
344 | .set_property = axp20x_usb_power_set_property, |
345 | }; |
346 | |
347 | static const struct power_supply_desc axp22x_usb_power_desc = { |
348 | .name = "axp20x-usb" , |
349 | .type = POWER_SUPPLY_TYPE_USB, |
350 | .properties = axp22x_usb_power_properties, |
351 | .num_properties = ARRAY_SIZE(axp22x_usb_power_properties), |
352 | .property_is_writeable = axp20x_usb_power_prop_writeable, |
353 | .get_property = axp20x_usb_power_get_property, |
354 | .set_property = axp20x_usb_power_set_property, |
355 | }; |
356 | |
357 | static const char * const axp20x_irq_names[] = { |
358 | "VBUS_PLUGIN" , |
359 | "VBUS_REMOVAL" , |
360 | "VBUS_VALID" , |
361 | "VBUS_NOT_VALID" , |
362 | }; |
363 | |
364 | static const char * const axp22x_irq_names[] = { |
365 | "VBUS_PLUGIN" , |
366 | "VBUS_REMOVAL" , |
367 | }; |
368 | |
369 | static int axp192_usb_curr_lim_table[] = { |
370 | -1, |
371 | -1, |
372 | 500000, |
373 | 100000, |
374 | }; |
375 | |
376 | static int axp20x_usb_curr_lim_table[] = { |
377 | 900000, |
378 | 500000, |
379 | 100000, |
380 | -1, |
381 | }; |
382 | |
383 | static int axp221_usb_curr_lim_table[] = { |
384 | 900000, |
385 | 500000, |
386 | -1, |
387 | -1, |
388 | }; |
389 | |
390 | static int axp813_usb_curr_lim_table[] = { |
391 | 900000, |
392 | 1500000, |
393 | 2000000, |
394 | 2500000, |
395 | }; |
396 | |
397 | static const struct axp_data axp192_data = { |
398 | .power_desc = &axp20x_usb_power_desc, |
399 | .irq_names = axp20x_irq_names, |
400 | .num_irq_names = ARRAY_SIZE(axp20x_irq_names), |
401 | .curr_lim_table = axp192_usb_curr_lim_table, |
402 | .curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1), |
403 | .vbus_valid_bit = REG_FIELD(AXP192_USB_OTG_STATUS, 2, 2), |
404 | .vbus_mon_bit = REG_FIELD(AXP20X_VBUS_MON, 3, 3), |
405 | }; |
406 | |
407 | static const struct axp_data axp202_data = { |
408 | .power_desc = &axp20x_usb_power_desc, |
409 | .irq_names = axp20x_irq_names, |
410 | .num_irq_names = ARRAY_SIZE(axp20x_irq_names), |
411 | .curr_lim_table = axp20x_usb_curr_lim_table, |
412 | .curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1), |
413 | .vbus_valid_bit = REG_FIELD(AXP20X_USB_OTG_STATUS, 2, 2), |
414 | .vbus_mon_bit = REG_FIELD(AXP20X_VBUS_MON, 3, 3), |
415 | }; |
416 | |
417 | static const struct axp_data axp221_data = { |
418 | .power_desc = &axp22x_usb_power_desc, |
419 | .irq_names = axp22x_irq_names, |
420 | .num_irq_names = ARRAY_SIZE(axp22x_irq_names), |
421 | .curr_lim_table = axp221_usb_curr_lim_table, |
422 | .curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1), |
423 | .vbus_needs_polling = true, |
424 | }; |
425 | |
426 | static const struct axp_data axp223_data = { |
427 | .power_desc = &axp22x_usb_power_desc, |
428 | .irq_names = axp22x_irq_names, |
429 | .num_irq_names = ARRAY_SIZE(axp22x_irq_names), |
430 | .curr_lim_table = axp20x_usb_curr_lim_table, |
431 | .curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1), |
432 | .vbus_needs_polling = true, |
433 | }; |
434 | |
435 | static const struct axp_data axp813_data = { |
436 | .power_desc = &axp22x_usb_power_desc, |
437 | .irq_names = axp22x_irq_names, |
438 | .num_irq_names = ARRAY_SIZE(axp22x_irq_names), |
439 | .curr_lim_table = axp813_usb_curr_lim_table, |
440 | .curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1), |
441 | .usb_bc_en_bit = REG_FIELD(AXP288_BC_GLOBAL, 0, 0), |
442 | .vbus_disable_bit = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 7, 7), |
443 | .vbus_needs_polling = true, |
444 | }; |
445 | |
446 | #ifdef CONFIG_PM_SLEEP |
447 | static int axp20x_usb_power_suspend(struct device *dev) |
448 | { |
449 | struct axp20x_usb_power *power = dev_get_drvdata(dev); |
450 | int i = 0; |
451 | |
452 | /* |
453 | * Allow wake via VBUS_PLUGIN only. |
454 | * |
455 | * As nested threaded IRQs are not automatically disabled during |
456 | * suspend, we must explicitly disable the remainder of the IRQs. |
457 | */ |
458 | if (device_may_wakeup(dev: &power->supply->dev)) |
459 | enable_irq_wake(irq: power->irqs[i++]); |
460 | while (i < power->num_irqs) |
461 | disable_irq(irq: power->irqs[i++]); |
462 | |
463 | return 0; |
464 | } |
465 | |
466 | static int axp20x_usb_power_resume(struct device *dev) |
467 | { |
468 | struct axp20x_usb_power *power = dev_get_drvdata(dev); |
469 | int i = 0; |
470 | |
471 | if (device_may_wakeup(dev: &power->supply->dev)) |
472 | disable_irq_wake(irq: power->irqs[i++]); |
473 | while (i < power->num_irqs) |
474 | enable_irq(irq: power->irqs[i++]); |
475 | |
476 | mod_delayed_work(wq: system_power_efficient_wq, dwork: &power->vbus_detect, DEBOUNCE_TIME); |
477 | |
478 | return 0; |
479 | } |
480 | #endif |
481 | |
482 | static SIMPLE_DEV_PM_OPS(axp20x_usb_power_pm_ops, axp20x_usb_power_suspend, |
483 | axp20x_usb_power_resume); |
484 | |
485 | static int configure_iio_channels(struct platform_device *pdev, |
486 | struct axp20x_usb_power *power) |
487 | { |
488 | power->vbus_v = devm_iio_channel_get(dev: &pdev->dev, consumer_channel: "vbus_v" ); |
489 | if (IS_ERR(ptr: power->vbus_v)) { |
490 | if (PTR_ERR(ptr: power->vbus_v) == -ENODEV) |
491 | return -EPROBE_DEFER; |
492 | return PTR_ERR(ptr: power->vbus_v); |
493 | } |
494 | |
495 | power->vbus_i = devm_iio_channel_get(dev: &pdev->dev, consumer_channel: "vbus_i" ); |
496 | if (IS_ERR(ptr: power->vbus_i)) { |
497 | if (PTR_ERR(ptr: power->vbus_i) == -ENODEV) |
498 | return -EPROBE_DEFER; |
499 | return PTR_ERR(ptr: power->vbus_i); |
500 | } |
501 | |
502 | return 0; |
503 | } |
504 | |
505 | static int configure_adc_registers(struct axp20x_usb_power *power) |
506 | { |
507 | /* Enable vbus voltage and current measurement */ |
508 | return regmap_update_bits(map: power->regmap, AXP20X_ADC_EN1, |
509 | AXP20X_ADC_EN1_VBUS_CURR | |
510 | AXP20X_ADC_EN1_VBUS_VOLT, |
511 | AXP20X_ADC_EN1_VBUS_CURR | |
512 | AXP20X_ADC_EN1_VBUS_VOLT); |
513 | } |
514 | |
515 | static int axp20x_regmap_field_alloc_optional(struct device *dev, |
516 | struct regmap *regmap, |
517 | struct reg_field fdesc, |
518 | struct regmap_field **fieldp) |
519 | { |
520 | struct regmap_field *field; |
521 | |
522 | if (fdesc.reg == 0) { |
523 | *fieldp = NULL; |
524 | return 0; |
525 | } |
526 | |
527 | field = devm_regmap_field_alloc(dev, regmap, reg_field: fdesc); |
528 | if (IS_ERR(ptr: field)) |
529 | return PTR_ERR(ptr: field); |
530 | |
531 | *fieldp = field; |
532 | return 0; |
533 | } |
534 | |
535 | static int axp20x_usb_power_probe(struct platform_device *pdev) |
536 | { |
537 | struct axp20x_dev *axp20x = dev_get_drvdata(dev: pdev->dev.parent); |
538 | struct power_supply_config psy_cfg = {}; |
539 | struct axp20x_usb_power *power; |
540 | const struct axp_data *axp_data; |
541 | int i, irq, ret; |
542 | |
543 | if (!of_device_is_available(device: pdev->dev.of_node)) |
544 | return -ENODEV; |
545 | |
546 | if (!axp20x) { |
547 | dev_err(&pdev->dev, "Parent drvdata not set\n" ); |
548 | return -EINVAL; |
549 | } |
550 | |
551 | axp_data = of_device_get_match_data(dev: &pdev->dev); |
552 | |
553 | power = devm_kzalloc(dev: &pdev->dev, |
554 | struct_size(power, irqs, axp_data->num_irq_names), |
555 | GFP_KERNEL); |
556 | if (!power) |
557 | return -ENOMEM; |
558 | |
559 | platform_set_drvdata(pdev, data: power); |
560 | |
561 | power->axp_data = axp_data; |
562 | power->regmap = axp20x->regmap; |
563 | power->num_irqs = axp_data->num_irq_names; |
564 | |
565 | power->curr_lim_fld = devm_regmap_field_alloc(dev: &pdev->dev, regmap: power->regmap, |
566 | reg_field: axp_data->curr_lim_fld); |
567 | if (IS_ERR(ptr: power->curr_lim_fld)) |
568 | return PTR_ERR(ptr: power->curr_lim_fld); |
569 | |
570 | ret = axp20x_regmap_field_alloc_optional(dev: &pdev->dev, regmap: power->regmap, |
571 | fdesc: axp_data->vbus_valid_bit, |
572 | fieldp: &power->vbus_valid_bit); |
573 | if (ret) |
574 | return ret; |
575 | |
576 | ret = axp20x_regmap_field_alloc_optional(dev: &pdev->dev, regmap: power->regmap, |
577 | fdesc: axp_data->vbus_mon_bit, |
578 | fieldp: &power->vbus_mon_bit); |
579 | if (ret) |
580 | return ret; |
581 | |
582 | ret = axp20x_regmap_field_alloc_optional(dev: &pdev->dev, regmap: power->regmap, |
583 | fdesc: axp_data->usb_bc_en_bit, |
584 | fieldp: &power->usb_bc_en_bit); |
585 | if (ret) |
586 | return ret; |
587 | |
588 | ret = axp20x_regmap_field_alloc_optional(dev: &pdev->dev, regmap: power->regmap, |
589 | fdesc: axp_data->vbus_disable_bit, |
590 | fieldp: &power->vbus_disable_bit); |
591 | if (ret) |
592 | return ret; |
593 | |
594 | ret = devm_delayed_work_autocancel(dev: &pdev->dev, w: &power->vbus_detect, |
595 | worker: axp20x_usb_power_poll_vbus); |
596 | if (ret) |
597 | return ret; |
598 | |
599 | if (power->vbus_mon_bit) { |
600 | /* Enable vbus valid checking */ |
601 | ret = regmap_field_write(field: power->vbus_mon_bit, val: 1); |
602 | if (ret) |
603 | return ret; |
604 | |
605 | if (IS_ENABLED(CONFIG_AXP20X_ADC)) |
606 | ret = configure_iio_channels(pdev, power); |
607 | else |
608 | ret = configure_adc_registers(power); |
609 | |
610 | if (ret) |
611 | return ret; |
612 | } |
613 | |
614 | if (power->usb_bc_en_bit) { |
615 | /* Enable USB Battery Charging specification detection */ |
616 | ret = regmap_field_write(field: power->usb_bc_en_bit, val: 1); |
617 | if (ret) |
618 | return ret; |
619 | } |
620 | |
621 | psy_cfg.of_node = pdev->dev.of_node; |
622 | psy_cfg.drv_data = power; |
623 | |
624 | power->supply = devm_power_supply_register(parent: &pdev->dev, |
625 | desc: axp_data->power_desc, |
626 | cfg: &psy_cfg); |
627 | if (IS_ERR(ptr: power->supply)) |
628 | return PTR_ERR(ptr: power->supply); |
629 | |
630 | /* Request irqs after registering, as irqs may trigger immediately */ |
631 | for (i = 0; i < axp_data->num_irq_names; i++) { |
632 | irq = platform_get_irq_byname(pdev, axp_data->irq_names[i]); |
633 | if (irq < 0) |
634 | return irq; |
635 | |
636 | power->irqs[i] = regmap_irq_get_virq(data: axp20x->regmap_irqc, irq); |
637 | ret = devm_request_any_context_irq(dev: &pdev->dev, irq: power->irqs[i], |
638 | handler: axp20x_usb_power_irq, irqflags: 0, |
639 | DRVNAME, dev_id: power); |
640 | if (ret < 0) { |
641 | dev_err(&pdev->dev, "Error requesting %s IRQ: %d\n" , |
642 | axp_data->irq_names[i], ret); |
643 | return ret; |
644 | } |
645 | } |
646 | |
647 | if (axp20x_usb_vbus_needs_polling(power)) |
648 | queue_delayed_work(wq: system_power_efficient_wq, dwork: &power->vbus_detect, delay: 0); |
649 | |
650 | return 0; |
651 | } |
652 | |
653 | static const struct of_device_id axp20x_usb_power_match[] = { |
654 | { |
655 | .compatible = "x-powers,axp192-usb-power-supply" , |
656 | .data = &axp192_data, |
657 | }, { |
658 | .compatible = "x-powers,axp202-usb-power-supply" , |
659 | .data = &axp202_data, |
660 | }, { |
661 | .compatible = "x-powers,axp221-usb-power-supply" , |
662 | .data = &axp221_data, |
663 | }, { |
664 | .compatible = "x-powers,axp223-usb-power-supply" , |
665 | .data = &axp223_data, |
666 | }, { |
667 | .compatible = "x-powers,axp813-usb-power-supply" , |
668 | .data = &axp813_data, |
669 | }, { /* sentinel */ } |
670 | }; |
671 | MODULE_DEVICE_TABLE(of, axp20x_usb_power_match); |
672 | |
673 | static struct platform_driver axp20x_usb_power_driver = { |
674 | .probe = axp20x_usb_power_probe, |
675 | .driver = { |
676 | .name = DRVNAME, |
677 | .of_match_table = axp20x_usb_power_match, |
678 | .pm = &axp20x_usb_power_pm_ops, |
679 | }, |
680 | }; |
681 | |
682 | module_platform_driver(axp20x_usb_power_driver); |
683 | |
684 | MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>" ); |
685 | MODULE_DESCRIPTION("AXP20x PMIC USB power supply status driver" ); |
686 | MODULE_LICENSE("GPL" ); |
687 | |