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