18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * AXP20x PMIC USB power supply status driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com> 68c2ecf20Sopenharmony_ci * Copyright (C) 2014 Bruno Prémont <bonbons@linux-vserver.org> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/bitops.h> 108c2ecf20Sopenharmony_ci#include <linux/device.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/mfd/axp20x.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/of.h> 178c2ecf20Sopenharmony_ci#include <linux/of_device.h> 188c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 198c2ecf20Sopenharmony_ci#include <linux/pm.h> 208c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 218c2ecf20Sopenharmony_ci#include <linux/regmap.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/iio/consumer.h> 248c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define DRVNAME "axp20x-usb-power-supply" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define AXP20X_PWR_STATUS_VBUS_PRESENT BIT(5) 298c2ecf20Sopenharmony_ci#define AXP20X_PWR_STATUS_VBUS_USED BIT(4) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define AXP20X_USB_STATUS_VBUS_VALID BIT(2) 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define AXP20X_VBUS_PATH_SEL BIT(7) 348c2ecf20Sopenharmony_ci#define AXP20X_VBUS_PATH_SEL_OFFSET 7 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define AXP20X_VBUS_VHOLD_uV(b) (4000000 + (((b) >> 3) & 7) * 100000) 378c2ecf20Sopenharmony_ci#define AXP20X_VBUS_VHOLD_MASK GENMASK(5, 3) 388c2ecf20Sopenharmony_ci#define AXP20X_VBUS_VHOLD_OFFSET 3 398c2ecf20Sopenharmony_ci#define AXP20X_VBUS_CLIMIT_MASK 3 408c2ecf20Sopenharmony_ci#define AXP20X_VBUS_CLIMIT_900mA 0 418c2ecf20Sopenharmony_ci#define AXP20X_VBUS_CLIMIT_500mA 1 428c2ecf20Sopenharmony_ci#define AXP20X_VBUS_CLIMIT_100mA 2 438c2ecf20Sopenharmony_ci#define AXP20X_VBUS_CLIMIT_NONE 3 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define AXP813_VBUS_CLIMIT_900mA 0 468c2ecf20Sopenharmony_ci#define AXP813_VBUS_CLIMIT_1500mA 1 478c2ecf20Sopenharmony_ci#define AXP813_VBUS_CLIMIT_2000mA 2 488c2ecf20Sopenharmony_ci#define AXP813_VBUS_CLIMIT_2500mA 3 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define AXP20X_ADC_EN1_VBUS_CURR BIT(2) 518c2ecf20Sopenharmony_ci#define AXP20X_ADC_EN1_VBUS_VOLT BIT(3) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define AXP20X_VBUS_MON_VBUS_VALID BIT(3) 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define AXP813_BC_EN BIT(0) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* 588c2ecf20Sopenharmony_ci * Note do not raise the debounce time, we must report Vusb high within 598c2ecf20Sopenharmony_ci * 100ms otherwise we get Vbus errors in musb. 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_ci#define DEBOUNCE_TIME msecs_to_jiffies(50) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistruct axp20x_usb_power { 648c2ecf20Sopenharmony_ci struct regmap *regmap; 658c2ecf20Sopenharmony_ci struct power_supply *supply; 668c2ecf20Sopenharmony_ci enum axp20x_variants axp20x_id; 678c2ecf20Sopenharmony_ci struct iio_channel *vbus_v; 688c2ecf20Sopenharmony_ci struct iio_channel *vbus_i; 698c2ecf20Sopenharmony_ci struct delayed_work vbus_detect; 708c2ecf20Sopenharmony_ci unsigned int old_status; 718c2ecf20Sopenharmony_ci unsigned int online; 728c2ecf20Sopenharmony_ci unsigned int num_irqs; 738c2ecf20Sopenharmony_ci unsigned int irqs[]; 748c2ecf20Sopenharmony_ci}; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic bool axp20x_usb_vbus_needs_polling(struct axp20x_usb_power *power) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci /* 798c2ecf20Sopenharmony_ci * Polling is only necessary while VBUS is offline. While online, a 808c2ecf20Sopenharmony_ci * present->absent transition implies an online->offline transition 818c2ecf20Sopenharmony_ci * and will trigger the VBUS_REMOVAL IRQ. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci if (power->axp20x_id >= AXP221_ID && !power->online) 848c2ecf20Sopenharmony_ci return true; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci return false; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic irqreturn_t axp20x_usb_power_irq(int irq, void *devid) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct axp20x_usb_power *power = devid; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci power_supply_changed(power->supply); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci mod_delayed_work(system_wq, &power->vbus_detect, DEBOUNCE_TIME); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci return IRQ_HANDLED; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic void axp20x_usb_power_poll_vbus(struct work_struct *work) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct axp20x_usb_power *power = 1038c2ecf20Sopenharmony_ci container_of(work, struct axp20x_usb_power, vbus_detect.work); 1048c2ecf20Sopenharmony_ci unsigned int val; 1058c2ecf20Sopenharmony_ci int ret; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &val); 1088c2ecf20Sopenharmony_ci if (ret) 1098c2ecf20Sopenharmony_ci goto out; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci val &= (AXP20X_PWR_STATUS_VBUS_PRESENT | AXP20X_PWR_STATUS_VBUS_USED); 1128c2ecf20Sopenharmony_ci if (val != power->old_status) 1138c2ecf20Sopenharmony_ci power_supply_changed(power->supply); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci power->old_status = val; 1168c2ecf20Sopenharmony_ci power->online = val & AXP20X_PWR_STATUS_VBUS_USED; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ciout: 1198c2ecf20Sopenharmony_ci if (axp20x_usb_vbus_needs_polling(power)) 1208c2ecf20Sopenharmony_ci mod_delayed_work(system_wq, &power->vbus_detect, DEBOUNCE_TIME); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int axp20x_get_current_max(struct axp20x_usb_power *power, int *val) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci unsigned int v; 1268c2ecf20Sopenharmony_ci int ret = regmap_read(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, &v); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (ret) 1298c2ecf20Sopenharmony_ci return ret; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci switch (v & AXP20X_VBUS_CLIMIT_MASK) { 1328c2ecf20Sopenharmony_ci case AXP20X_VBUS_CLIMIT_100mA: 1338c2ecf20Sopenharmony_ci if (power->axp20x_id == AXP221_ID) 1348c2ecf20Sopenharmony_ci *val = -1; /* No 100mA limit */ 1358c2ecf20Sopenharmony_ci else 1368c2ecf20Sopenharmony_ci *val = 100000; 1378c2ecf20Sopenharmony_ci break; 1388c2ecf20Sopenharmony_ci case AXP20X_VBUS_CLIMIT_500mA: 1398c2ecf20Sopenharmony_ci *val = 500000; 1408c2ecf20Sopenharmony_ci break; 1418c2ecf20Sopenharmony_ci case AXP20X_VBUS_CLIMIT_900mA: 1428c2ecf20Sopenharmony_ci *val = 900000; 1438c2ecf20Sopenharmony_ci break; 1448c2ecf20Sopenharmony_ci case AXP20X_VBUS_CLIMIT_NONE: 1458c2ecf20Sopenharmony_ci *val = -1; 1468c2ecf20Sopenharmony_ci break; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic int axp813_get_current_max(struct axp20x_usb_power *power, int *val) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci unsigned int v; 1558c2ecf20Sopenharmony_ci int ret = regmap_read(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, &v); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (ret) 1588c2ecf20Sopenharmony_ci return ret; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci switch (v & AXP20X_VBUS_CLIMIT_MASK) { 1618c2ecf20Sopenharmony_ci case AXP813_VBUS_CLIMIT_900mA: 1628c2ecf20Sopenharmony_ci *val = 900000; 1638c2ecf20Sopenharmony_ci break; 1648c2ecf20Sopenharmony_ci case AXP813_VBUS_CLIMIT_1500mA: 1658c2ecf20Sopenharmony_ci *val = 1500000; 1668c2ecf20Sopenharmony_ci break; 1678c2ecf20Sopenharmony_ci case AXP813_VBUS_CLIMIT_2000mA: 1688c2ecf20Sopenharmony_ci *val = 2000000; 1698c2ecf20Sopenharmony_ci break; 1708c2ecf20Sopenharmony_ci case AXP813_VBUS_CLIMIT_2500mA: 1718c2ecf20Sopenharmony_ci *val = 2500000; 1728c2ecf20Sopenharmony_ci break; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci return 0; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic int axp20x_usb_power_get_property(struct power_supply *psy, 1788c2ecf20Sopenharmony_ci enum power_supply_property psp, union power_supply_propval *val) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct axp20x_usb_power *power = power_supply_get_drvdata(psy); 1818c2ecf20Sopenharmony_ci unsigned int input, v; 1828c2ecf20Sopenharmony_ci int ret; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci switch (psp) { 1858c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_MIN: 1868c2ecf20Sopenharmony_ci ret = regmap_read(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, &v); 1878c2ecf20Sopenharmony_ci if (ret) 1888c2ecf20Sopenharmony_ci return ret; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci val->intval = AXP20X_VBUS_VHOLD_uV(v); 1918c2ecf20Sopenharmony_ci return 0; 1928c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 1938c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_AXP20X_ADC)) { 1948c2ecf20Sopenharmony_ci ret = iio_read_channel_processed(power->vbus_v, 1958c2ecf20Sopenharmony_ci &val->intval); 1968c2ecf20Sopenharmony_ci if (ret) 1978c2ecf20Sopenharmony_ci return ret; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* 2008c2ecf20Sopenharmony_ci * IIO framework gives mV but Power Supply framework 2018c2ecf20Sopenharmony_ci * gives uV. 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ci val->intval *= 1000; 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci ret = axp20x_read_variable_width(power->regmap, 2088c2ecf20Sopenharmony_ci AXP20X_VBUS_V_ADC_H, 12); 2098c2ecf20Sopenharmony_ci if (ret < 0) 2108c2ecf20Sopenharmony_ci return ret; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci val->intval = ret * 1700; /* 1 step = 1.7 mV */ 2138c2ecf20Sopenharmony_ci return 0; 2148c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_MAX: 2158c2ecf20Sopenharmony_ci if (power->axp20x_id == AXP813_ID) 2168c2ecf20Sopenharmony_ci return axp813_get_current_max(power, &val->intval); 2178c2ecf20Sopenharmony_ci return axp20x_get_current_max(power, &val->intval); 2188c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_NOW: 2198c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_AXP20X_ADC)) { 2208c2ecf20Sopenharmony_ci ret = iio_read_channel_processed(power->vbus_i, 2218c2ecf20Sopenharmony_ci &val->intval); 2228c2ecf20Sopenharmony_ci if (ret) 2238c2ecf20Sopenharmony_ci return ret; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* 2268c2ecf20Sopenharmony_ci * IIO framework gives mA but Power Supply framework 2278c2ecf20Sopenharmony_ci * gives uA. 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_ci val->intval *= 1000; 2308c2ecf20Sopenharmony_ci return 0; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci ret = axp20x_read_variable_width(power->regmap, 2348c2ecf20Sopenharmony_ci AXP20X_VBUS_I_ADC_H, 12); 2358c2ecf20Sopenharmony_ci if (ret < 0) 2368c2ecf20Sopenharmony_ci return ret; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci val->intval = ret * 375; /* 1 step = 0.375 mA */ 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci default: 2418c2ecf20Sopenharmony_ci break; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* All the properties below need the input-status reg value */ 2458c2ecf20Sopenharmony_ci ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &input); 2468c2ecf20Sopenharmony_ci if (ret) 2478c2ecf20Sopenharmony_ci return ret; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci switch (psp) { 2508c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_HEALTH: 2518c2ecf20Sopenharmony_ci if (!(input & AXP20X_PWR_STATUS_VBUS_PRESENT)) { 2528c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; 2538c2ecf20Sopenharmony_ci break; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_GOOD; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (power->axp20x_id == AXP202_ID) { 2598c2ecf20Sopenharmony_ci ret = regmap_read(power->regmap, 2608c2ecf20Sopenharmony_ci AXP20X_USB_OTG_STATUS, &v); 2618c2ecf20Sopenharmony_ci if (ret) 2628c2ecf20Sopenharmony_ci return ret; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (!(v & AXP20X_USB_STATUS_VBUS_VALID)) 2658c2ecf20Sopenharmony_ci val->intval = 2668c2ecf20Sopenharmony_ci POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci break; 2698c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 2708c2ecf20Sopenharmony_ci val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_PRESENT); 2718c2ecf20Sopenharmony_ci break; 2728c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 2738c2ecf20Sopenharmony_ci val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_USED); 2748c2ecf20Sopenharmony_ci break; 2758c2ecf20Sopenharmony_ci default: 2768c2ecf20Sopenharmony_ci return -EINVAL; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci return 0; 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic int axp813_usb_power_set_online(struct axp20x_usb_power *power, 2838c2ecf20Sopenharmony_ci int intval) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci int val = !intval << AXP20X_VBUS_PATH_SEL_OFFSET; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return regmap_update_bits(power->regmap, 2888c2ecf20Sopenharmony_ci AXP20X_VBUS_IPSOUT_MGMT, 2898c2ecf20Sopenharmony_ci AXP20X_VBUS_PATH_SEL, val); 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic int axp20x_usb_power_set_voltage_min(struct axp20x_usb_power *power, 2938c2ecf20Sopenharmony_ci int intval) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci int val; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci switch (intval) { 2988c2ecf20Sopenharmony_ci case 4000000: 2998c2ecf20Sopenharmony_ci case 4100000: 3008c2ecf20Sopenharmony_ci case 4200000: 3018c2ecf20Sopenharmony_ci case 4300000: 3028c2ecf20Sopenharmony_ci case 4400000: 3038c2ecf20Sopenharmony_ci case 4500000: 3048c2ecf20Sopenharmony_ci case 4600000: 3058c2ecf20Sopenharmony_ci case 4700000: 3068c2ecf20Sopenharmony_ci val = (intval - 4000000) / 100000; 3078c2ecf20Sopenharmony_ci return regmap_update_bits(power->regmap, 3088c2ecf20Sopenharmony_ci AXP20X_VBUS_IPSOUT_MGMT, 3098c2ecf20Sopenharmony_ci AXP20X_VBUS_VHOLD_MASK, 3108c2ecf20Sopenharmony_ci val << AXP20X_VBUS_VHOLD_OFFSET); 3118c2ecf20Sopenharmony_ci default: 3128c2ecf20Sopenharmony_ci return -EINVAL; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci return -EINVAL; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic int axp813_usb_power_set_current_max(struct axp20x_usb_power *power, 3198c2ecf20Sopenharmony_ci int intval) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci int val; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci switch (intval) { 3248c2ecf20Sopenharmony_ci case 900000: 3258c2ecf20Sopenharmony_ci return regmap_update_bits(power->regmap, 3268c2ecf20Sopenharmony_ci AXP20X_VBUS_IPSOUT_MGMT, 3278c2ecf20Sopenharmony_ci AXP20X_VBUS_CLIMIT_MASK, 3288c2ecf20Sopenharmony_ci AXP813_VBUS_CLIMIT_900mA); 3298c2ecf20Sopenharmony_ci case 1500000: 3308c2ecf20Sopenharmony_ci case 2000000: 3318c2ecf20Sopenharmony_ci case 2500000: 3328c2ecf20Sopenharmony_ci val = (intval - 1000000) / 500000; 3338c2ecf20Sopenharmony_ci return regmap_update_bits(power->regmap, 3348c2ecf20Sopenharmony_ci AXP20X_VBUS_IPSOUT_MGMT, 3358c2ecf20Sopenharmony_ci AXP20X_VBUS_CLIMIT_MASK, val); 3368c2ecf20Sopenharmony_ci default: 3378c2ecf20Sopenharmony_ci return -EINVAL; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci return -EINVAL; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic int axp20x_usb_power_set_current_max(struct axp20x_usb_power *power, 3448c2ecf20Sopenharmony_ci int intval) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci int val; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci switch (intval) { 3498c2ecf20Sopenharmony_ci case 100000: 3508c2ecf20Sopenharmony_ci if (power->axp20x_id == AXP221_ID) 3518c2ecf20Sopenharmony_ci return -EINVAL; 3528c2ecf20Sopenharmony_ci fallthrough; 3538c2ecf20Sopenharmony_ci case 500000: 3548c2ecf20Sopenharmony_ci case 900000: 3558c2ecf20Sopenharmony_ci val = (900000 - intval) / 400000; 3568c2ecf20Sopenharmony_ci return regmap_update_bits(power->regmap, 3578c2ecf20Sopenharmony_ci AXP20X_VBUS_IPSOUT_MGMT, 3588c2ecf20Sopenharmony_ci AXP20X_VBUS_CLIMIT_MASK, val); 3598c2ecf20Sopenharmony_ci default: 3608c2ecf20Sopenharmony_ci return -EINVAL; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci return -EINVAL; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic int axp20x_usb_power_set_property(struct power_supply *psy, 3678c2ecf20Sopenharmony_ci enum power_supply_property psp, 3688c2ecf20Sopenharmony_ci const union power_supply_propval *val) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci struct axp20x_usb_power *power = power_supply_get_drvdata(psy); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci switch (psp) { 3738c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 3748c2ecf20Sopenharmony_ci if (power->axp20x_id != AXP813_ID) 3758c2ecf20Sopenharmony_ci return -EINVAL; 3768c2ecf20Sopenharmony_ci return axp813_usb_power_set_online(power, val->intval); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_MIN: 3798c2ecf20Sopenharmony_ci return axp20x_usb_power_set_voltage_min(power, val->intval); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_MAX: 3828c2ecf20Sopenharmony_ci if (power->axp20x_id == AXP813_ID) 3838c2ecf20Sopenharmony_ci return axp813_usb_power_set_current_max(power, 3848c2ecf20Sopenharmony_ci val->intval); 3858c2ecf20Sopenharmony_ci return axp20x_usb_power_set_current_max(power, val->intval); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci default: 3888c2ecf20Sopenharmony_ci return -EINVAL; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci return -EINVAL; 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic int axp20x_usb_power_prop_writeable(struct power_supply *psy, 3958c2ecf20Sopenharmony_ci enum power_supply_property psp) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci struct axp20x_usb_power *power = power_supply_get_drvdata(psy); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci /* 4008c2ecf20Sopenharmony_ci * The VBUS path select flag works differently on on AXP288 and newer: 4018c2ecf20Sopenharmony_ci * - On AXP20x and AXP22x, the flag enables VBUS (ignoring N_VBUSEN). 4028c2ecf20Sopenharmony_ci * - On AXP288 and AXP8xx, the flag disables VBUS (ignoring N_VBUSEN). 4038c2ecf20Sopenharmony_ci * We only expose the control on variants where it can be used to force 4048c2ecf20Sopenharmony_ci * the VBUS input offline. 4058c2ecf20Sopenharmony_ci */ 4068c2ecf20Sopenharmony_ci if (psp == POWER_SUPPLY_PROP_ONLINE) 4078c2ecf20Sopenharmony_ci return power->axp20x_id == AXP813_ID; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN || 4108c2ecf20Sopenharmony_ci psp == POWER_SUPPLY_PROP_CURRENT_MAX; 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cistatic enum power_supply_property axp20x_usb_power_properties[] = { 4148c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_HEALTH, 4158c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 4168c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 4178c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_MIN, 4188c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 4198c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_MAX, 4208c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_NOW, 4218c2ecf20Sopenharmony_ci}; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic enum power_supply_property axp22x_usb_power_properties[] = { 4248c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_HEALTH, 4258c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 4268c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 4278c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_MIN, 4288c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_MAX, 4298c2ecf20Sopenharmony_ci}; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic const struct power_supply_desc axp20x_usb_power_desc = { 4328c2ecf20Sopenharmony_ci .name = "axp20x-usb", 4338c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_USB, 4348c2ecf20Sopenharmony_ci .properties = axp20x_usb_power_properties, 4358c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(axp20x_usb_power_properties), 4368c2ecf20Sopenharmony_ci .property_is_writeable = axp20x_usb_power_prop_writeable, 4378c2ecf20Sopenharmony_ci .get_property = axp20x_usb_power_get_property, 4388c2ecf20Sopenharmony_ci .set_property = axp20x_usb_power_set_property, 4398c2ecf20Sopenharmony_ci}; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cistatic const struct power_supply_desc axp22x_usb_power_desc = { 4428c2ecf20Sopenharmony_ci .name = "axp20x-usb", 4438c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_USB, 4448c2ecf20Sopenharmony_ci .properties = axp22x_usb_power_properties, 4458c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(axp22x_usb_power_properties), 4468c2ecf20Sopenharmony_ci .property_is_writeable = axp20x_usb_power_prop_writeable, 4478c2ecf20Sopenharmony_ci .get_property = axp20x_usb_power_get_property, 4488c2ecf20Sopenharmony_ci .set_property = axp20x_usb_power_set_property, 4498c2ecf20Sopenharmony_ci}; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cistatic const char * const axp20x_irq_names[] = { 4528c2ecf20Sopenharmony_ci "VBUS_PLUGIN", 4538c2ecf20Sopenharmony_ci "VBUS_REMOVAL", 4548c2ecf20Sopenharmony_ci "VBUS_VALID", 4558c2ecf20Sopenharmony_ci "VBUS_NOT_VALID", 4568c2ecf20Sopenharmony_ci}; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic const char * const axp22x_irq_names[] = { 4598c2ecf20Sopenharmony_ci "VBUS_PLUGIN", 4608c2ecf20Sopenharmony_ci "VBUS_REMOVAL", 4618c2ecf20Sopenharmony_ci}; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistruct axp_data { 4648c2ecf20Sopenharmony_ci const struct power_supply_desc *power_desc; 4658c2ecf20Sopenharmony_ci const char * const *irq_names; 4668c2ecf20Sopenharmony_ci unsigned int num_irq_names; 4678c2ecf20Sopenharmony_ci enum axp20x_variants axp20x_id; 4688c2ecf20Sopenharmony_ci}; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic const struct axp_data axp202_data = { 4718c2ecf20Sopenharmony_ci .power_desc = &axp20x_usb_power_desc, 4728c2ecf20Sopenharmony_ci .irq_names = axp20x_irq_names, 4738c2ecf20Sopenharmony_ci .num_irq_names = ARRAY_SIZE(axp20x_irq_names), 4748c2ecf20Sopenharmony_ci .axp20x_id = AXP202_ID, 4758c2ecf20Sopenharmony_ci}; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic const struct axp_data axp221_data = { 4788c2ecf20Sopenharmony_ci .power_desc = &axp22x_usb_power_desc, 4798c2ecf20Sopenharmony_ci .irq_names = axp22x_irq_names, 4808c2ecf20Sopenharmony_ci .num_irq_names = ARRAY_SIZE(axp22x_irq_names), 4818c2ecf20Sopenharmony_ci .axp20x_id = AXP221_ID, 4828c2ecf20Sopenharmony_ci}; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic const struct axp_data axp223_data = { 4858c2ecf20Sopenharmony_ci .power_desc = &axp22x_usb_power_desc, 4868c2ecf20Sopenharmony_ci .irq_names = axp22x_irq_names, 4878c2ecf20Sopenharmony_ci .num_irq_names = ARRAY_SIZE(axp22x_irq_names), 4888c2ecf20Sopenharmony_ci .axp20x_id = AXP223_ID, 4898c2ecf20Sopenharmony_ci}; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic const struct axp_data axp813_data = { 4928c2ecf20Sopenharmony_ci .power_desc = &axp22x_usb_power_desc, 4938c2ecf20Sopenharmony_ci .irq_names = axp22x_irq_names, 4948c2ecf20Sopenharmony_ci .num_irq_names = ARRAY_SIZE(axp22x_irq_names), 4958c2ecf20Sopenharmony_ci .axp20x_id = AXP813_ID, 4968c2ecf20Sopenharmony_ci}; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 4998c2ecf20Sopenharmony_cistatic int axp20x_usb_power_suspend(struct device *dev) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci struct axp20x_usb_power *power = dev_get_drvdata(dev); 5028c2ecf20Sopenharmony_ci int i = 0; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci /* 5058c2ecf20Sopenharmony_ci * Allow wake via VBUS_PLUGIN only. 5068c2ecf20Sopenharmony_ci * 5078c2ecf20Sopenharmony_ci * As nested threaded IRQs are not automatically disabled during 5088c2ecf20Sopenharmony_ci * suspend, we must explicitly disable the remainder of the IRQs. 5098c2ecf20Sopenharmony_ci */ 5108c2ecf20Sopenharmony_ci if (device_may_wakeup(&power->supply->dev)) 5118c2ecf20Sopenharmony_ci enable_irq_wake(power->irqs[i++]); 5128c2ecf20Sopenharmony_ci while (i < power->num_irqs) 5138c2ecf20Sopenharmony_ci disable_irq(power->irqs[i++]); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci return 0; 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_cistatic int axp20x_usb_power_resume(struct device *dev) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci struct axp20x_usb_power *power = dev_get_drvdata(dev); 5218c2ecf20Sopenharmony_ci int i = 0; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (device_may_wakeup(&power->supply->dev)) 5248c2ecf20Sopenharmony_ci disable_irq_wake(power->irqs[i++]); 5258c2ecf20Sopenharmony_ci while (i < power->num_irqs) 5268c2ecf20Sopenharmony_ci enable_irq(power->irqs[i++]); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci mod_delayed_work(system_wq, &power->vbus_detect, DEBOUNCE_TIME); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci return 0; 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci#endif 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(axp20x_usb_power_pm_ops, axp20x_usb_power_suspend, 5358c2ecf20Sopenharmony_ci axp20x_usb_power_resume); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_cistatic int configure_iio_channels(struct platform_device *pdev, 5388c2ecf20Sopenharmony_ci struct axp20x_usb_power *power) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci power->vbus_v = devm_iio_channel_get(&pdev->dev, "vbus_v"); 5418c2ecf20Sopenharmony_ci if (IS_ERR(power->vbus_v)) { 5428c2ecf20Sopenharmony_ci if (PTR_ERR(power->vbus_v) == -ENODEV) 5438c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 5448c2ecf20Sopenharmony_ci return PTR_ERR(power->vbus_v); 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci power->vbus_i = devm_iio_channel_get(&pdev->dev, "vbus_i"); 5488c2ecf20Sopenharmony_ci if (IS_ERR(power->vbus_i)) { 5498c2ecf20Sopenharmony_ci if (PTR_ERR(power->vbus_i) == -ENODEV) 5508c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 5518c2ecf20Sopenharmony_ci return PTR_ERR(power->vbus_i); 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci return 0; 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic int configure_adc_registers(struct axp20x_usb_power *power) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci /* Enable vbus voltage and current measurement */ 5608c2ecf20Sopenharmony_ci return regmap_update_bits(power->regmap, AXP20X_ADC_EN1, 5618c2ecf20Sopenharmony_ci AXP20X_ADC_EN1_VBUS_CURR | 5628c2ecf20Sopenharmony_ci AXP20X_ADC_EN1_VBUS_VOLT, 5638c2ecf20Sopenharmony_ci AXP20X_ADC_EN1_VBUS_CURR | 5648c2ecf20Sopenharmony_ci AXP20X_ADC_EN1_VBUS_VOLT); 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cistatic int axp20x_usb_power_probe(struct platform_device *pdev) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); 5708c2ecf20Sopenharmony_ci struct power_supply_config psy_cfg = {}; 5718c2ecf20Sopenharmony_ci struct axp20x_usb_power *power; 5728c2ecf20Sopenharmony_ci const struct axp_data *axp_data; 5738c2ecf20Sopenharmony_ci int i, irq, ret; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci if (!of_device_is_available(pdev->dev.of_node)) 5768c2ecf20Sopenharmony_ci return -ENODEV; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci if (!axp20x) { 5798c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Parent drvdata not set\n"); 5808c2ecf20Sopenharmony_ci return -EINVAL; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci axp_data = of_device_get_match_data(&pdev->dev); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci power = devm_kzalloc(&pdev->dev, 5868c2ecf20Sopenharmony_ci struct_size(power, irqs, axp_data->num_irq_names), 5878c2ecf20Sopenharmony_ci GFP_KERNEL); 5888c2ecf20Sopenharmony_ci if (!power) 5898c2ecf20Sopenharmony_ci return -ENOMEM; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, power); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci power->axp20x_id = axp_data->axp20x_id; 5948c2ecf20Sopenharmony_ci power->regmap = axp20x->regmap; 5958c2ecf20Sopenharmony_ci power->num_irqs = axp_data->num_irq_names; 5968c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&power->vbus_detect, axp20x_usb_power_poll_vbus); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci if (power->axp20x_id == AXP202_ID) { 5998c2ecf20Sopenharmony_ci /* Enable vbus valid checking */ 6008c2ecf20Sopenharmony_ci ret = regmap_update_bits(power->regmap, AXP20X_VBUS_MON, 6018c2ecf20Sopenharmony_ci AXP20X_VBUS_MON_VBUS_VALID, 6028c2ecf20Sopenharmony_ci AXP20X_VBUS_MON_VBUS_VALID); 6038c2ecf20Sopenharmony_ci if (ret) 6048c2ecf20Sopenharmony_ci return ret; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_AXP20X_ADC)) 6078c2ecf20Sopenharmony_ci ret = configure_iio_channels(pdev, power); 6088c2ecf20Sopenharmony_ci else 6098c2ecf20Sopenharmony_ci ret = configure_adc_registers(power); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci if (ret) 6128c2ecf20Sopenharmony_ci return ret; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci if (power->axp20x_id == AXP813_ID) { 6168c2ecf20Sopenharmony_ci /* Enable USB Battery Charging specification detection */ 6178c2ecf20Sopenharmony_ci regmap_update_bits(axp20x->regmap, AXP288_BC_GLOBAL, 6188c2ecf20Sopenharmony_ci AXP813_BC_EN, AXP813_BC_EN); 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci psy_cfg.of_node = pdev->dev.of_node; 6228c2ecf20Sopenharmony_ci psy_cfg.drv_data = power; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci power->supply = devm_power_supply_register(&pdev->dev, 6258c2ecf20Sopenharmony_ci axp_data->power_desc, 6268c2ecf20Sopenharmony_ci &psy_cfg); 6278c2ecf20Sopenharmony_ci if (IS_ERR(power->supply)) 6288c2ecf20Sopenharmony_ci return PTR_ERR(power->supply); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci /* Request irqs after registering, as irqs may trigger immediately */ 6318c2ecf20Sopenharmony_ci for (i = 0; i < axp_data->num_irq_names; i++) { 6328c2ecf20Sopenharmony_ci irq = platform_get_irq_byname(pdev, axp_data->irq_names[i]); 6338c2ecf20Sopenharmony_ci if (irq < 0) { 6348c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No IRQ for %s: %d\n", 6358c2ecf20Sopenharmony_ci axp_data->irq_names[i], irq); 6368c2ecf20Sopenharmony_ci return irq; 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci power->irqs[i] = regmap_irq_get_virq(axp20x->regmap_irqc, irq); 6398c2ecf20Sopenharmony_ci ret = devm_request_any_context_irq(&pdev->dev, power->irqs[i], 6408c2ecf20Sopenharmony_ci axp20x_usb_power_irq, 0, 6418c2ecf20Sopenharmony_ci DRVNAME, power); 6428c2ecf20Sopenharmony_ci if (ret < 0) { 6438c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Error requesting %s IRQ: %d\n", 6448c2ecf20Sopenharmony_ci axp_data->irq_names[i], ret); 6458c2ecf20Sopenharmony_ci return ret; 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ci } 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci if (axp20x_usb_vbus_needs_polling(power)) 6508c2ecf20Sopenharmony_ci queue_delayed_work(system_wq, &power->vbus_detect, 0); 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci return 0; 6538c2ecf20Sopenharmony_ci} 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_cistatic int axp20x_usb_power_remove(struct platform_device *pdev) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci struct axp20x_usb_power *power = platform_get_drvdata(pdev); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&power->vbus_detect); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci return 0; 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_cistatic const struct of_device_id axp20x_usb_power_match[] = { 6658c2ecf20Sopenharmony_ci { 6668c2ecf20Sopenharmony_ci .compatible = "x-powers,axp202-usb-power-supply", 6678c2ecf20Sopenharmony_ci .data = &axp202_data, 6688c2ecf20Sopenharmony_ci }, { 6698c2ecf20Sopenharmony_ci .compatible = "x-powers,axp221-usb-power-supply", 6708c2ecf20Sopenharmony_ci .data = &axp221_data, 6718c2ecf20Sopenharmony_ci }, { 6728c2ecf20Sopenharmony_ci .compatible = "x-powers,axp223-usb-power-supply", 6738c2ecf20Sopenharmony_ci .data = &axp223_data, 6748c2ecf20Sopenharmony_ci }, { 6758c2ecf20Sopenharmony_ci .compatible = "x-powers,axp813-usb-power-supply", 6768c2ecf20Sopenharmony_ci .data = &axp813_data, 6778c2ecf20Sopenharmony_ci }, { /* sentinel */ } 6788c2ecf20Sopenharmony_ci}; 6798c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, axp20x_usb_power_match); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_cistatic struct platform_driver axp20x_usb_power_driver = { 6828c2ecf20Sopenharmony_ci .probe = axp20x_usb_power_probe, 6838c2ecf20Sopenharmony_ci .remove = axp20x_usb_power_remove, 6848c2ecf20Sopenharmony_ci .driver = { 6858c2ecf20Sopenharmony_ci .name = DRVNAME, 6868c2ecf20Sopenharmony_ci .of_match_table = axp20x_usb_power_match, 6878c2ecf20Sopenharmony_ci .pm = &axp20x_usb_power_pm_ops, 6888c2ecf20Sopenharmony_ci }, 6898c2ecf20Sopenharmony_ci}; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_cimodule_platform_driver(axp20x_usb_power_driver); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 6948c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("AXP20x PMIC USB power supply status driver"); 6958c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 696