162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * AXP20x PMIC USB power supply status driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com> 662306a36Sopenharmony_ci * Copyright (C) 2014 Bruno Prémont <bonbons@linux-vserver.org> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/bitops.h> 1062306a36Sopenharmony_ci#include <linux/device.h> 1162306a36Sopenharmony_ci#include <linux/devm-helpers.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/interrupt.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/mfd/axp20x.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/of.h> 1862306a36Sopenharmony_ci#include <linux/platform_device.h> 1962306a36Sopenharmony_ci#include <linux/pm.h> 2062306a36Sopenharmony_ci#include <linux/power_supply.h> 2162306a36Sopenharmony_ci#include <linux/regmap.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <linux/iio/consumer.h> 2462306a36Sopenharmony_ci#include <linux/workqueue.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define DRVNAME "axp20x-usb-power-supply" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define AXP192_USB_OTG_STATUS 0x04 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define AXP20X_PWR_STATUS_VBUS_PRESENT BIT(5) 3162306a36Sopenharmony_ci#define AXP20X_PWR_STATUS_VBUS_USED BIT(4) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define AXP20X_USB_STATUS_VBUS_VALID BIT(2) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define AXP20X_VBUS_VHOLD_uV(b) (4000000 + (((b) >> 3) & 7) * 100000) 3662306a36Sopenharmony_ci#define AXP20X_VBUS_VHOLD_MASK GENMASK(5, 3) 3762306a36Sopenharmony_ci#define AXP20X_VBUS_VHOLD_OFFSET 3 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define AXP20X_ADC_EN1_VBUS_CURR BIT(2) 4062306a36Sopenharmony_ci#define AXP20X_ADC_EN1_VBUS_VOLT BIT(3) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* 4362306a36Sopenharmony_ci * Note do not raise the debounce time, we must report Vusb high within 4462306a36Sopenharmony_ci * 100ms otherwise we get Vbus errors in musb. 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_ci#define DEBOUNCE_TIME msecs_to_jiffies(50) 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistruct axp_data { 4962306a36Sopenharmony_ci const struct power_supply_desc *power_desc; 5062306a36Sopenharmony_ci const char * const *irq_names; 5162306a36Sopenharmony_ci unsigned int num_irq_names; 5262306a36Sopenharmony_ci const int *curr_lim_table; 5362306a36Sopenharmony_ci struct reg_field curr_lim_fld; 5462306a36Sopenharmony_ci struct reg_field vbus_valid_bit; 5562306a36Sopenharmony_ci struct reg_field vbus_mon_bit; 5662306a36Sopenharmony_ci struct reg_field usb_bc_en_bit; 5762306a36Sopenharmony_ci struct reg_field vbus_disable_bit; 5862306a36Sopenharmony_ci bool vbus_needs_polling: 1; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistruct axp20x_usb_power { 6262306a36Sopenharmony_ci struct regmap *regmap; 6362306a36Sopenharmony_ci struct regmap_field *curr_lim_fld; 6462306a36Sopenharmony_ci struct regmap_field *vbus_valid_bit; 6562306a36Sopenharmony_ci struct regmap_field *vbus_mon_bit; 6662306a36Sopenharmony_ci struct regmap_field *usb_bc_en_bit; 6762306a36Sopenharmony_ci struct regmap_field *vbus_disable_bit; 6862306a36Sopenharmony_ci struct power_supply *supply; 6962306a36Sopenharmony_ci const struct axp_data *axp_data; 7062306a36Sopenharmony_ci struct iio_channel *vbus_v; 7162306a36Sopenharmony_ci struct iio_channel *vbus_i; 7262306a36Sopenharmony_ci struct delayed_work vbus_detect; 7362306a36Sopenharmony_ci unsigned int old_status; 7462306a36Sopenharmony_ci unsigned int online; 7562306a36Sopenharmony_ci unsigned int num_irqs; 7662306a36Sopenharmony_ci unsigned int irqs[]; 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic bool axp20x_usb_vbus_needs_polling(struct axp20x_usb_power *power) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci /* 8262306a36Sopenharmony_ci * Polling is only necessary while VBUS is offline. While online, a 8362306a36Sopenharmony_ci * present->absent transition implies an online->offline transition 8462306a36Sopenharmony_ci * and will trigger the VBUS_REMOVAL IRQ. 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_ci if (power->axp_data->vbus_needs_polling && !power->online) 8762306a36Sopenharmony_ci return true; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci return false; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic irqreturn_t axp20x_usb_power_irq(int irq, void *devid) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci struct axp20x_usb_power *power = devid; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci power_supply_changed(power->supply); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci mod_delayed_work(system_power_efficient_wq, &power->vbus_detect, DEBOUNCE_TIME); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return IRQ_HANDLED; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic void axp20x_usb_power_poll_vbus(struct work_struct *work) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct axp20x_usb_power *power = 10662306a36Sopenharmony_ci container_of(work, struct axp20x_usb_power, vbus_detect.work); 10762306a36Sopenharmony_ci unsigned int val; 10862306a36Sopenharmony_ci int ret; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &val); 11162306a36Sopenharmony_ci if (ret) 11262306a36Sopenharmony_ci goto out; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci val &= (AXP20X_PWR_STATUS_VBUS_PRESENT | AXP20X_PWR_STATUS_VBUS_USED); 11562306a36Sopenharmony_ci if (val != power->old_status) 11662306a36Sopenharmony_ci power_supply_changed(power->supply); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci power->old_status = val; 11962306a36Sopenharmony_ci power->online = val & AXP20X_PWR_STATUS_VBUS_USED; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ciout: 12262306a36Sopenharmony_ci if (axp20x_usb_vbus_needs_polling(power)) 12362306a36Sopenharmony_ci mod_delayed_work(system_power_efficient_wq, &power->vbus_detect, DEBOUNCE_TIME); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic int axp20x_usb_power_get_property(struct power_supply *psy, 12762306a36Sopenharmony_ci enum power_supply_property psp, union power_supply_propval *val) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct axp20x_usb_power *power = power_supply_get_drvdata(psy); 13062306a36Sopenharmony_ci unsigned int input, v; 13162306a36Sopenharmony_ci int ret; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci switch (psp) { 13462306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_MIN: 13562306a36Sopenharmony_ci ret = regmap_read(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, &v); 13662306a36Sopenharmony_ci if (ret) 13762306a36Sopenharmony_ci return ret; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci val->intval = AXP20X_VBUS_VHOLD_uV(v); 14062306a36Sopenharmony_ci return 0; 14162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 14262306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_AXP20X_ADC)) { 14362306a36Sopenharmony_ci ret = iio_read_channel_processed(power->vbus_v, 14462306a36Sopenharmony_ci &val->intval); 14562306a36Sopenharmony_ci if (ret) 14662306a36Sopenharmony_ci return ret; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* 14962306a36Sopenharmony_ci * IIO framework gives mV but Power Supply framework 15062306a36Sopenharmony_ci * gives uV. 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_ci val->intval *= 1000; 15362306a36Sopenharmony_ci return 0; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci ret = axp20x_read_variable_width(power->regmap, 15762306a36Sopenharmony_ci AXP20X_VBUS_V_ADC_H, 12); 15862306a36Sopenharmony_ci if (ret < 0) 15962306a36Sopenharmony_ci return ret; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci val->intval = ret * 1700; /* 1 step = 1.7 mV */ 16262306a36Sopenharmony_ci return 0; 16362306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_MAX: 16462306a36Sopenharmony_ci ret = regmap_field_read(power->curr_lim_fld, &v); 16562306a36Sopenharmony_ci if (ret) 16662306a36Sopenharmony_ci return ret; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci val->intval = power->axp_data->curr_lim_table[v]; 16962306a36Sopenharmony_ci return 0; 17062306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_NOW: 17162306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_AXP20X_ADC)) { 17262306a36Sopenharmony_ci ret = iio_read_channel_processed(power->vbus_i, 17362306a36Sopenharmony_ci &val->intval); 17462306a36Sopenharmony_ci if (ret) 17562306a36Sopenharmony_ci return ret; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* 17862306a36Sopenharmony_ci * IIO framework gives mA but Power Supply framework 17962306a36Sopenharmony_ci * gives uA. 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_ci val->intval *= 1000; 18262306a36Sopenharmony_ci return 0; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci ret = axp20x_read_variable_width(power->regmap, 18662306a36Sopenharmony_ci AXP20X_VBUS_I_ADC_H, 12); 18762306a36Sopenharmony_ci if (ret < 0) 18862306a36Sopenharmony_ci return ret; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci val->intval = ret * 375; /* 1 step = 0.375 mA */ 19162306a36Sopenharmony_ci return 0; 19262306a36Sopenharmony_ci default: 19362306a36Sopenharmony_ci break; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* All the properties below need the input-status reg value */ 19762306a36Sopenharmony_ci ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &input); 19862306a36Sopenharmony_ci if (ret) 19962306a36Sopenharmony_ci return ret; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci switch (psp) { 20262306a36Sopenharmony_ci case POWER_SUPPLY_PROP_HEALTH: 20362306a36Sopenharmony_ci if (!(input & AXP20X_PWR_STATUS_VBUS_PRESENT)) { 20462306a36Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; 20562306a36Sopenharmony_ci break; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_GOOD; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (power->vbus_valid_bit) { 21162306a36Sopenharmony_ci ret = regmap_field_read(power->vbus_valid_bit, &v); 21262306a36Sopenharmony_ci if (ret) 21362306a36Sopenharmony_ci return ret; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (v == 0) 21662306a36Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci break; 22062306a36Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 22162306a36Sopenharmony_ci val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_PRESENT); 22262306a36Sopenharmony_ci break; 22362306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 22462306a36Sopenharmony_ci val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_USED); 22562306a36Sopenharmony_ci break; 22662306a36Sopenharmony_ci default: 22762306a36Sopenharmony_ci return -EINVAL; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci return 0; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic int axp20x_usb_power_set_voltage_min(struct axp20x_usb_power *power, 23462306a36Sopenharmony_ci int intval) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci int val; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci switch (intval) { 23962306a36Sopenharmony_ci case 4000000: 24062306a36Sopenharmony_ci case 4100000: 24162306a36Sopenharmony_ci case 4200000: 24262306a36Sopenharmony_ci case 4300000: 24362306a36Sopenharmony_ci case 4400000: 24462306a36Sopenharmony_ci case 4500000: 24562306a36Sopenharmony_ci case 4600000: 24662306a36Sopenharmony_ci case 4700000: 24762306a36Sopenharmony_ci val = (intval - 4000000) / 100000; 24862306a36Sopenharmony_ci return regmap_update_bits(power->regmap, 24962306a36Sopenharmony_ci AXP20X_VBUS_IPSOUT_MGMT, 25062306a36Sopenharmony_ci AXP20X_VBUS_VHOLD_MASK, 25162306a36Sopenharmony_ci val << AXP20X_VBUS_VHOLD_OFFSET); 25262306a36Sopenharmony_ci default: 25362306a36Sopenharmony_ci return -EINVAL; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return -EINVAL; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic int axp20x_usb_power_set_current_max(struct axp20x_usb_power *power, int intval) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci const unsigned int max = GENMASK(power->axp_data->curr_lim_fld.msb, 26262306a36Sopenharmony_ci power->axp_data->curr_lim_fld.lsb); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (intval == -1) 26562306a36Sopenharmony_ci return -EINVAL; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci for (unsigned int i = 0; i <= max; ++i) 26862306a36Sopenharmony_ci if (power->axp_data->curr_lim_table[i] == intval) 26962306a36Sopenharmony_ci return regmap_field_write(power->curr_lim_fld, i); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci return -EINVAL; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic int axp20x_usb_power_set_property(struct power_supply *psy, 27562306a36Sopenharmony_ci enum power_supply_property psp, 27662306a36Sopenharmony_ci const union power_supply_propval *val) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct axp20x_usb_power *power = power_supply_get_drvdata(psy); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci switch (psp) { 28162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 28262306a36Sopenharmony_ci if (!power->vbus_disable_bit) 28362306a36Sopenharmony_ci return -EINVAL; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci return regmap_field_write(power->vbus_disable_bit, !val->intval); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_MIN: 28862306a36Sopenharmony_ci return axp20x_usb_power_set_voltage_min(power, val->intval); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_MAX: 29162306a36Sopenharmony_ci return axp20x_usb_power_set_current_max(power, val->intval); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci default: 29462306a36Sopenharmony_ci return -EINVAL; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci return -EINVAL; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic int axp20x_usb_power_prop_writeable(struct power_supply *psy, 30162306a36Sopenharmony_ci enum power_supply_property psp) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci struct axp20x_usb_power *power = power_supply_get_drvdata(psy); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* 30662306a36Sopenharmony_ci * The VBUS path select flag works differently on AXP288 and newer: 30762306a36Sopenharmony_ci * - On AXP20x and AXP22x, the flag enables VBUS (ignoring N_VBUSEN). 30862306a36Sopenharmony_ci * - On AXP288 and AXP8xx, the flag disables VBUS (ignoring N_VBUSEN). 30962306a36Sopenharmony_ci * We only expose the control on variants where it can be used to force 31062306a36Sopenharmony_ci * the VBUS input offline. 31162306a36Sopenharmony_ci */ 31262306a36Sopenharmony_ci if (psp == POWER_SUPPLY_PROP_ONLINE) 31362306a36Sopenharmony_ci return power->vbus_disable_bit != NULL; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN || 31662306a36Sopenharmony_ci psp == POWER_SUPPLY_PROP_CURRENT_MAX; 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic enum power_supply_property axp20x_usb_power_properties[] = { 32062306a36Sopenharmony_ci POWER_SUPPLY_PROP_HEALTH, 32162306a36Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 32262306a36Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 32362306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_MIN, 32462306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 32562306a36Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_MAX, 32662306a36Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_NOW, 32762306a36Sopenharmony_ci}; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic enum power_supply_property axp22x_usb_power_properties[] = { 33062306a36Sopenharmony_ci POWER_SUPPLY_PROP_HEALTH, 33162306a36Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 33262306a36Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 33362306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_MIN, 33462306a36Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_MAX, 33562306a36Sopenharmony_ci}; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic const struct power_supply_desc axp20x_usb_power_desc = { 33862306a36Sopenharmony_ci .name = "axp20x-usb", 33962306a36Sopenharmony_ci .type = POWER_SUPPLY_TYPE_USB, 34062306a36Sopenharmony_ci .properties = axp20x_usb_power_properties, 34162306a36Sopenharmony_ci .num_properties = ARRAY_SIZE(axp20x_usb_power_properties), 34262306a36Sopenharmony_ci .property_is_writeable = axp20x_usb_power_prop_writeable, 34362306a36Sopenharmony_ci .get_property = axp20x_usb_power_get_property, 34462306a36Sopenharmony_ci .set_property = axp20x_usb_power_set_property, 34562306a36Sopenharmony_ci}; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic const struct power_supply_desc axp22x_usb_power_desc = { 34862306a36Sopenharmony_ci .name = "axp20x-usb", 34962306a36Sopenharmony_ci .type = POWER_SUPPLY_TYPE_USB, 35062306a36Sopenharmony_ci .properties = axp22x_usb_power_properties, 35162306a36Sopenharmony_ci .num_properties = ARRAY_SIZE(axp22x_usb_power_properties), 35262306a36Sopenharmony_ci .property_is_writeable = axp20x_usb_power_prop_writeable, 35362306a36Sopenharmony_ci .get_property = axp20x_usb_power_get_property, 35462306a36Sopenharmony_ci .set_property = axp20x_usb_power_set_property, 35562306a36Sopenharmony_ci}; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic const char * const axp20x_irq_names[] = { 35862306a36Sopenharmony_ci "VBUS_PLUGIN", 35962306a36Sopenharmony_ci "VBUS_REMOVAL", 36062306a36Sopenharmony_ci "VBUS_VALID", 36162306a36Sopenharmony_ci "VBUS_NOT_VALID", 36262306a36Sopenharmony_ci}; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic const char * const axp22x_irq_names[] = { 36562306a36Sopenharmony_ci "VBUS_PLUGIN", 36662306a36Sopenharmony_ci "VBUS_REMOVAL", 36762306a36Sopenharmony_ci}; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic int axp192_usb_curr_lim_table[] = { 37062306a36Sopenharmony_ci -1, 37162306a36Sopenharmony_ci -1, 37262306a36Sopenharmony_ci 500000, 37362306a36Sopenharmony_ci 100000, 37462306a36Sopenharmony_ci}; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic int axp20x_usb_curr_lim_table[] = { 37762306a36Sopenharmony_ci 900000, 37862306a36Sopenharmony_ci 500000, 37962306a36Sopenharmony_ci 100000, 38062306a36Sopenharmony_ci -1, 38162306a36Sopenharmony_ci}; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic int axp221_usb_curr_lim_table[] = { 38462306a36Sopenharmony_ci 900000, 38562306a36Sopenharmony_ci 500000, 38662306a36Sopenharmony_ci -1, 38762306a36Sopenharmony_ci -1, 38862306a36Sopenharmony_ci}; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic int axp813_usb_curr_lim_table[] = { 39162306a36Sopenharmony_ci 900000, 39262306a36Sopenharmony_ci 1500000, 39362306a36Sopenharmony_ci 2000000, 39462306a36Sopenharmony_ci 2500000, 39562306a36Sopenharmony_ci}; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic const struct axp_data axp192_data = { 39862306a36Sopenharmony_ci .power_desc = &axp20x_usb_power_desc, 39962306a36Sopenharmony_ci .irq_names = axp20x_irq_names, 40062306a36Sopenharmony_ci .num_irq_names = ARRAY_SIZE(axp20x_irq_names), 40162306a36Sopenharmony_ci .curr_lim_table = axp192_usb_curr_lim_table, 40262306a36Sopenharmony_ci .curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1), 40362306a36Sopenharmony_ci .vbus_valid_bit = REG_FIELD(AXP192_USB_OTG_STATUS, 2, 2), 40462306a36Sopenharmony_ci .vbus_mon_bit = REG_FIELD(AXP20X_VBUS_MON, 3, 3), 40562306a36Sopenharmony_ci}; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic const struct axp_data axp202_data = { 40862306a36Sopenharmony_ci .power_desc = &axp20x_usb_power_desc, 40962306a36Sopenharmony_ci .irq_names = axp20x_irq_names, 41062306a36Sopenharmony_ci .num_irq_names = ARRAY_SIZE(axp20x_irq_names), 41162306a36Sopenharmony_ci .curr_lim_table = axp20x_usb_curr_lim_table, 41262306a36Sopenharmony_ci .curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1), 41362306a36Sopenharmony_ci .vbus_valid_bit = REG_FIELD(AXP20X_USB_OTG_STATUS, 2, 2), 41462306a36Sopenharmony_ci .vbus_mon_bit = REG_FIELD(AXP20X_VBUS_MON, 3, 3), 41562306a36Sopenharmony_ci}; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic const struct axp_data axp221_data = { 41862306a36Sopenharmony_ci .power_desc = &axp22x_usb_power_desc, 41962306a36Sopenharmony_ci .irq_names = axp22x_irq_names, 42062306a36Sopenharmony_ci .num_irq_names = ARRAY_SIZE(axp22x_irq_names), 42162306a36Sopenharmony_ci .curr_lim_table = axp221_usb_curr_lim_table, 42262306a36Sopenharmony_ci .curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1), 42362306a36Sopenharmony_ci .vbus_needs_polling = true, 42462306a36Sopenharmony_ci}; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic const struct axp_data axp223_data = { 42762306a36Sopenharmony_ci .power_desc = &axp22x_usb_power_desc, 42862306a36Sopenharmony_ci .irq_names = axp22x_irq_names, 42962306a36Sopenharmony_ci .num_irq_names = ARRAY_SIZE(axp22x_irq_names), 43062306a36Sopenharmony_ci .curr_lim_table = axp20x_usb_curr_lim_table, 43162306a36Sopenharmony_ci .curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1), 43262306a36Sopenharmony_ci .vbus_needs_polling = true, 43362306a36Sopenharmony_ci}; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic const struct axp_data axp813_data = { 43662306a36Sopenharmony_ci .power_desc = &axp22x_usb_power_desc, 43762306a36Sopenharmony_ci .irq_names = axp22x_irq_names, 43862306a36Sopenharmony_ci .num_irq_names = ARRAY_SIZE(axp22x_irq_names), 43962306a36Sopenharmony_ci .curr_lim_table = axp813_usb_curr_lim_table, 44062306a36Sopenharmony_ci .curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1), 44162306a36Sopenharmony_ci .usb_bc_en_bit = REG_FIELD(AXP288_BC_GLOBAL, 0, 0), 44262306a36Sopenharmony_ci .vbus_disable_bit = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 7, 7), 44362306a36Sopenharmony_ci .vbus_needs_polling = true, 44462306a36Sopenharmony_ci}; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 44762306a36Sopenharmony_cistatic int axp20x_usb_power_suspend(struct device *dev) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci struct axp20x_usb_power *power = dev_get_drvdata(dev); 45062306a36Sopenharmony_ci int i = 0; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* 45362306a36Sopenharmony_ci * Allow wake via VBUS_PLUGIN only. 45462306a36Sopenharmony_ci * 45562306a36Sopenharmony_ci * As nested threaded IRQs are not automatically disabled during 45662306a36Sopenharmony_ci * suspend, we must explicitly disable the remainder of the IRQs. 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_ci if (device_may_wakeup(&power->supply->dev)) 45962306a36Sopenharmony_ci enable_irq_wake(power->irqs[i++]); 46062306a36Sopenharmony_ci while (i < power->num_irqs) 46162306a36Sopenharmony_ci disable_irq(power->irqs[i++]); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic int axp20x_usb_power_resume(struct device *dev) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci struct axp20x_usb_power *power = dev_get_drvdata(dev); 46962306a36Sopenharmony_ci int i = 0; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (device_may_wakeup(&power->supply->dev)) 47262306a36Sopenharmony_ci disable_irq_wake(power->irqs[i++]); 47362306a36Sopenharmony_ci while (i < power->num_irqs) 47462306a36Sopenharmony_ci enable_irq(power->irqs[i++]); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci mod_delayed_work(system_power_efficient_wq, &power->vbus_detect, DEBOUNCE_TIME); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci return 0; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci#endif 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(axp20x_usb_power_pm_ops, axp20x_usb_power_suspend, 48362306a36Sopenharmony_ci axp20x_usb_power_resume); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic int configure_iio_channels(struct platform_device *pdev, 48662306a36Sopenharmony_ci struct axp20x_usb_power *power) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci power->vbus_v = devm_iio_channel_get(&pdev->dev, "vbus_v"); 48962306a36Sopenharmony_ci if (IS_ERR(power->vbus_v)) { 49062306a36Sopenharmony_ci if (PTR_ERR(power->vbus_v) == -ENODEV) 49162306a36Sopenharmony_ci return -EPROBE_DEFER; 49262306a36Sopenharmony_ci return PTR_ERR(power->vbus_v); 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci power->vbus_i = devm_iio_channel_get(&pdev->dev, "vbus_i"); 49662306a36Sopenharmony_ci if (IS_ERR(power->vbus_i)) { 49762306a36Sopenharmony_ci if (PTR_ERR(power->vbus_i) == -ENODEV) 49862306a36Sopenharmony_ci return -EPROBE_DEFER; 49962306a36Sopenharmony_ci return PTR_ERR(power->vbus_i); 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci return 0; 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic int configure_adc_registers(struct axp20x_usb_power *power) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci /* Enable vbus voltage and current measurement */ 50862306a36Sopenharmony_ci return regmap_update_bits(power->regmap, AXP20X_ADC_EN1, 50962306a36Sopenharmony_ci AXP20X_ADC_EN1_VBUS_CURR | 51062306a36Sopenharmony_ci AXP20X_ADC_EN1_VBUS_VOLT, 51162306a36Sopenharmony_ci AXP20X_ADC_EN1_VBUS_CURR | 51262306a36Sopenharmony_ci AXP20X_ADC_EN1_VBUS_VOLT); 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic int axp20x_regmap_field_alloc_optional(struct device *dev, 51662306a36Sopenharmony_ci struct regmap *regmap, 51762306a36Sopenharmony_ci struct reg_field fdesc, 51862306a36Sopenharmony_ci struct regmap_field **fieldp) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci struct regmap_field *field; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (fdesc.reg == 0) { 52362306a36Sopenharmony_ci *fieldp = NULL; 52462306a36Sopenharmony_ci return 0; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci field = devm_regmap_field_alloc(dev, regmap, fdesc); 52862306a36Sopenharmony_ci if (IS_ERR(field)) 52962306a36Sopenharmony_ci return PTR_ERR(field); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci *fieldp = field; 53262306a36Sopenharmony_ci return 0; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic int axp20x_usb_power_probe(struct platform_device *pdev) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); 53862306a36Sopenharmony_ci struct power_supply_config psy_cfg = {}; 53962306a36Sopenharmony_ci struct axp20x_usb_power *power; 54062306a36Sopenharmony_ci const struct axp_data *axp_data; 54162306a36Sopenharmony_ci int i, irq, ret; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (!of_device_is_available(pdev->dev.of_node)) 54462306a36Sopenharmony_ci return -ENODEV; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (!axp20x) { 54762306a36Sopenharmony_ci dev_err(&pdev->dev, "Parent drvdata not set\n"); 54862306a36Sopenharmony_ci return -EINVAL; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci axp_data = of_device_get_match_data(&pdev->dev); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci power = devm_kzalloc(&pdev->dev, 55462306a36Sopenharmony_ci struct_size(power, irqs, axp_data->num_irq_names), 55562306a36Sopenharmony_ci GFP_KERNEL); 55662306a36Sopenharmony_ci if (!power) 55762306a36Sopenharmony_ci return -ENOMEM; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci platform_set_drvdata(pdev, power); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci power->axp_data = axp_data; 56262306a36Sopenharmony_ci power->regmap = axp20x->regmap; 56362306a36Sopenharmony_ci power->num_irqs = axp_data->num_irq_names; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci power->curr_lim_fld = devm_regmap_field_alloc(&pdev->dev, power->regmap, 56662306a36Sopenharmony_ci axp_data->curr_lim_fld); 56762306a36Sopenharmony_ci if (IS_ERR(power->curr_lim_fld)) 56862306a36Sopenharmony_ci return PTR_ERR(power->curr_lim_fld); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci ret = axp20x_regmap_field_alloc_optional(&pdev->dev, power->regmap, 57162306a36Sopenharmony_ci axp_data->vbus_valid_bit, 57262306a36Sopenharmony_ci &power->vbus_valid_bit); 57362306a36Sopenharmony_ci if (ret) 57462306a36Sopenharmony_ci return ret; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci ret = axp20x_regmap_field_alloc_optional(&pdev->dev, power->regmap, 57762306a36Sopenharmony_ci axp_data->vbus_mon_bit, 57862306a36Sopenharmony_ci &power->vbus_mon_bit); 57962306a36Sopenharmony_ci if (ret) 58062306a36Sopenharmony_ci return ret; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci ret = axp20x_regmap_field_alloc_optional(&pdev->dev, power->regmap, 58362306a36Sopenharmony_ci axp_data->usb_bc_en_bit, 58462306a36Sopenharmony_ci &power->usb_bc_en_bit); 58562306a36Sopenharmony_ci if (ret) 58662306a36Sopenharmony_ci return ret; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci ret = axp20x_regmap_field_alloc_optional(&pdev->dev, power->regmap, 58962306a36Sopenharmony_ci axp_data->vbus_disable_bit, 59062306a36Sopenharmony_ci &power->vbus_disable_bit); 59162306a36Sopenharmony_ci if (ret) 59262306a36Sopenharmony_ci return ret; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci ret = devm_delayed_work_autocancel(&pdev->dev, &power->vbus_detect, 59562306a36Sopenharmony_ci axp20x_usb_power_poll_vbus); 59662306a36Sopenharmony_ci if (ret) 59762306a36Sopenharmony_ci return ret; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci if (power->vbus_mon_bit) { 60062306a36Sopenharmony_ci /* Enable vbus valid checking */ 60162306a36Sopenharmony_ci ret = regmap_field_write(power->vbus_mon_bit, 1); 60262306a36Sopenharmony_ci if (ret) 60362306a36Sopenharmony_ci return ret; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_AXP20X_ADC)) 60662306a36Sopenharmony_ci ret = configure_iio_channels(pdev, power); 60762306a36Sopenharmony_ci else 60862306a36Sopenharmony_ci ret = configure_adc_registers(power); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci if (ret) 61162306a36Sopenharmony_ci return ret; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (power->usb_bc_en_bit) { 61562306a36Sopenharmony_ci /* Enable USB Battery Charging specification detection */ 61662306a36Sopenharmony_ci ret = regmap_field_write(power->usb_bc_en_bit, 1); 61762306a36Sopenharmony_ci if (ret) 61862306a36Sopenharmony_ci return ret; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci psy_cfg.of_node = pdev->dev.of_node; 62262306a36Sopenharmony_ci psy_cfg.drv_data = power; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci power->supply = devm_power_supply_register(&pdev->dev, 62562306a36Sopenharmony_ci axp_data->power_desc, 62662306a36Sopenharmony_ci &psy_cfg); 62762306a36Sopenharmony_ci if (IS_ERR(power->supply)) 62862306a36Sopenharmony_ci return PTR_ERR(power->supply); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci /* Request irqs after registering, as irqs may trigger immediately */ 63162306a36Sopenharmony_ci for (i = 0; i < axp_data->num_irq_names; i++) { 63262306a36Sopenharmony_ci irq = platform_get_irq_byname(pdev, axp_data->irq_names[i]); 63362306a36Sopenharmony_ci if (irq < 0) 63462306a36Sopenharmony_ci return irq; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci power->irqs[i] = regmap_irq_get_virq(axp20x->regmap_irqc, irq); 63762306a36Sopenharmony_ci ret = devm_request_any_context_irq(&pdev->dev, power->irqs[i], 63862306a36Sopenharmony_ci axp20x_usb_power_irq, 0, 63962306a36Sopenharmony_ci DRVNAME, power); 64062306a36Sopenharmony_ci if (ret < 0) { 64162306a36Sopenharmony_ci dev_err(&pdev->dev, "Error requesting %s IRQ: %d\n", 64262306a36Sopenharmony_ci axp_data->irq_names[i], ret); 64362306a36Sopenharmony_ci return ret; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci if (axp20x_usb_vbus_needs_polling(power)) 64862306a36Sopenharmony_ci queue_delayed_work(system_power_efficient_wq, &power->vbus_detect, 0); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci return 0; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic const struct of_device_id axp20x_usb_power_match[] = { 65462306a36Sopenharmony_ci { 65562306a36Sopenharmony_ci .compatible = "x-powers,axp192-usb-power-supply", 65662306a36Sopenharmony_ci .data = &axp192_data, 65762306a36Sopenharmony_ci }, { 65862306a36Sopenharmony_ci .compatible = "x-powers,axp202-usb-power-supply", 65962306a36Sopenharmony_ci .data = &axp202_data, 66062306a36Sopenharmony_ci }, { 66162306a36Sopenharmony_ci .compatible = "x-powers,axp221-usb-power-supply", 66262306a36Sopenharmony_ci .data = &axp221_data, 66362306a36Sopenharmony_ci }, { 66462306a36Sopenharmony_ci .compatible = "x-powers,axp223-usb-power-supply", 66562306a36Sopenharmony_ci .data = &axp223_data, 66662306a36Sopenharmony_ci }, { 66762306a36Sopenharmony_ci .compatible = "x-powers,axp813-usb-power-supply", 66862306a36Sopenharmony_ci .data = &axp813_data, 66962306a36Sopenharmony_ci }, { /* sentinel */ } 67062306a36Sopenharmony_ci}; 67162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, axp20x_usb_power_match); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic struct platform_driver axp20x_usb_power_driver = { 67462306a36Sopenharmony_ci .probe = axp20x_usb_power_probe, 67562306a36Sopenharmony_ci .driver = { 67662306a36Sopenharmony_ci .name = DRVNAME, 67762306a36Sopenharmony_ci .of_match_table = axp20x_usb_power_match, 67862306a36Sopenharmony_ci .pm = &axp20x_usb_power_pm_ops, 67962306a36Sopenharmony_ci }, 68062306a36Sopenharmony_ci}; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cimodule_platform_driver(axp20x_usb_power_driver); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ciMODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 68562306a36Sopenharmony_ciMODULE_DESCRIPTION("AXP20x PMIC USB power supply status driver"); 68662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 687