diff options
author | Sebastian Reichel <sre@kernel.org> | 2022-02-01 15:33:24 +0100 |
---|---|---|
committer | Sebastian Reichel <sre@kernel.org> | 2022-02-01 15:33:24 +0100 |
commit | 28e54414a8901ec936e52f9293f52ff4ba90c2e0 (patch) | |
tree | 0722d7385b40dd158a118ada36d489ef3466a83c /drivers/power | |
parent | 210bc22c5d3d894cd032e0957035933945266d71 (diff) | |
parent | 21356ac14e054ee86a34008e2c59bd3f1b42d7bf (diff) | |
download | linux-stable-28e54414a8901ec936e52f9293f52ff4ba90c2e0.tar.gz linux-stable-28e54414a8901ec936e52f9293f52ff4ba90c2e0.tar.bz2 linux-stable-28e54414a8901ec936e52f9293f52ff4ba90c2e0.zip |
Merge tag 'psy-extcon-i2c-mfd-for-v5.18-signed' into psy-next
Immutable branch between power-supply, mfd, i2c and extcon for for 5.18
This immutable branch fixes the charger setup on Xiaomi Mi Pad 2 and
Lenovo Yogabook, which requires updates to multiple drivers throughout
the tree.
Signed-off-by: Sebastian Reichel <sre@kernel.org>
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/supply/bq24190_charger.c | 12 | ||||
-rw-r--r-- | drivers/power/supply/bq25890_charger.c | 396 | ||||
-rw-r--r-- | drivers/power/supply/power_supply_core.c | 57 |
3 files changed, 360 insertions, 105 deletions
diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c index 06c34b09349c..a1c957a26f07 100644 --- a/drivers/power/supply/bq24190_charger.c +++ b/drivers/power/supply/bq24190_charger.c @@ -1206,8 +1206,18 @@ static void bq24190_input_current_limit_work(struct work_struct *work) struct bq24190_dev_info *bdi = container_of(work, struct bq24190_dev_info, input_current_limit_work.work); + union power_supply_propval val; + int ret; + + ret = power_supply_get_property_from_supplier(bdi->charger, + POWER_SUPPLY_PROP_CURRENT_MAX, + &val); + if (ret) + return; - power_supply_set_input_current_limit_from_supplier(bdi->charger); + bq24190_charger_set_property(bdi->charger, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + &val); } /* Sync the input-current-limit with our parent supply (if we have one) */ diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index e62da9dc4f35..852a6fec4339 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -8,7 +8,9 @@ #include <linux/module.h> #include <linux/i2c.h> #include <linux/power_supply.h> +#include <linux/power/bq25890_charger.h> #include <linux/regmap.h> +#include <linux/regulator/driver.h> #include <linux/types.h> #include <linux/gpio/consumer.h> #include <linux/interrupt.h> @@ -25,6 +27,10 @@ #define BQ25895_ID 7 #define BQ25896_ID 0 +#define PUMP_EXPRESS_START_DELAY (5 * HZ) +#define PUMP_EXPRESS_MAX_TRIES 6 +#define PUMP_EXPRESS_VBUS_MARGIN_uV 1000000 + enum bq25890_chip_version { BQ25890, BQ25892, @@ -40,7 +46,7 @@ static const char *const bq25890_chip_name[] = { }; enum bq25890_fields { - F_EN_HIZ, F_EN_ILIM, F_IILIM, /* Reg00 */ + F_EN_HIZ, F_EN_ILIM, F_IINLIM, /* Reg00 */ F_BHOT, F_BCOLD, F_VINDPM_OFS, /* Reg01 */ F_CONV_START, F_CONV_RATE, F_BOOSTF, F_ICO_EN, F_HVDCP_EN, F_MAXC_EN, F_FORCE_DPM, F_AUTO_DPDM_EN, /* Reg02 */ @@ -94,6 +100,7 @@ struct bq25890_state { u8 vsys_status; u8 boost_fault; u8 bat_fault; + u8 ntc_fault; }; struct bq25890_device { @@ -104,11 +111,15 @@ struct bq25890_device { struct usb_phy *usb_phy; struct notifier_block usb_nb; struct work_struct usb_work; + struct delayed_work pump_express_work; unsigned long usb_event; struct regmap *rmap; struct regmap_field *rmap_fields[F_MAX_FIELDS]; + bool skip_reset; + bool read_back_init_data; + u32 pump_express_vbus_max; enum bq25890_chip_version chip_version; struct bq25890_init_data init_data; struct bq25890_state state; @@ -153,7 +164,7 @@ static const struct reg_field bq25890_reg_fields[] = { /* REG00 */ [F_EN_HIZ] = REG_FIELD(0x00, 7, 7), [F_EN_ILIM] = REG_FIELD(0x00, 6, 6), - [F_IILIM] = REG_FIELD(0x00, 0, 5), + [F_IINLIM] = REG_FIELD(0x00, 0, 5), /* REG01 */ [F_BHOT] = REG_FIELD(0x01, 6, 7), [F_BCOLD] = REG_FIELD(0x01, 5, 5), @@ -256,10 +267,11 @@ enum bq25890_table_ids { /* range tables */ TBL_ICHG, TBL_ITERM, - TBL_IILIM, + TBL_IINLIM, TBL_VREG, TBL_BOOSTV, TBL_SYSVMIN, + TBL_VBUSV, TBL_VBATCOMP, TBL_RBATCOMP, @@ -320,14 +332,15 @@ static const union { } bq25890_tables[] = { /* range tables */ /* TODO: BQ25896 has max ICHG 3008 mA */ - [TBL_ICHG] = { .rt = {0, 5056000, 64000} }, /* uA */ - [TBL_ITERM] = { .rt = {64000, 1024000, 64000} }, /* uA */ - [TBL_IILIM] = { .rt = {100000, 3250000, 50000} }, /* uA */ - [TBL_VREG] = { .rt = {3840000, 4608000, 16000} }, /* uV */ - [TBL_BOOSTV] = { .rt = {4550000, 5510000, 64000} }, /* uV */ - [TBL_SYSVMIN] = { .rt = {3000000, 3700000, 100000} }, /* uV */ - [TBL_VBATCOMP] ={ .rt = {0, 224000, 32000} }, /* uV */ - [TBL_RBATCOMP] ={ .rt = {0, 140000, 20000} }, /* uOhm */ + [TBL_ICHG] = { .rt = {0, 5056000, 64000} }, /* uA */ + [TBL_ITERM] = { .rt = {64000, 1024000, 64000} }, /* uA */ + [TBL_IINLIM] = { .rt = {100000, 3250000, 50000} }, /* uA */ + [TBL_VREG] = { .rt = {3840000, 4608000, 16000} }, /* uV */ + [TBL_BOOSTV] = { .rt = {4550000, 5510000, 64000} }, /* uV */ + [TBL_SYSVMIN] = { .rt = {3000000, 3700000, 100000} }, /* uV */ + [TBL_VBUSV] = { .rt = {2600000, 15300000, 100000} }, /* uV */ + [TBL_VBATCOMP] = { .rt = {0, 224000, 32000} }, /* uV */ + [TBL_RBATCOMP] = { .rt = {0, 140000, 20000} }, /* uOhm */ /* lookup tables */ [TBL_TREG] = { .lt = {bq25890_treg_tbl, BQ25890_TREG_TBL_SIZE} }, @@ -407,6 +420,14 @@ enum bq25890_chrg_fault { CHRG_FAULT_TIMER_EXPIRED, }; +enum bq25890_ntc_fault { + NTC_FAULT_NORMAL = 0, + NTC_FAULT_WARM = 2, + NTC_FAULT_COOL = 3, + NTC_FAULT_COLD = 5, + NTC_FAULT_HOT = 6, +}; + static bool bq25890_is_adc_property(enum power_supply_property psp) { switch (psp) { @@ -422,6 +443,17 @@ static bool bq25890_is_adc_property(enum power_supply_property psp) static irqreturn_t __bq25890_handle_irq(struct bq25890_device *bq); +static int bq25890_get_vbus_voltage(struct bq25890_device *bq) +{ + int ret; + + ret = bq25890_field_read(bq, F_VBUSV); + if (ret < 0) + return ret; + + return bq25890_find_val(ret, TBL_VBUSV); +} + static int bq25890_power_supply_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -499,6 +531,18 @@ static int bq25890_power_supply_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: val->intval = bq25890_find_val(bq->init_data.ichg, TBL_ICHG); + + /* When temperature is too low, charge current is decreased */ + if (bq->state.ntc_fault == NTC_FAULT_COOL) { + ret = bq25890_field_read(bq, F_JEITA_ISET); + if (ret < 0) + return ret; + + if (ret) + val->intval /= 5; + else + val->intval /= 2; + } break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: @@ -528,11 +572,11 @@ static int bq25890_power_supply_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: - ret = bq25890_field_read(bq, F_IILIM); + ret = bq25890_field_read(bq, F_IINLIM); if (ret < 0) return ret; - val->intval = bq25890_find_val(ret, TBL_IILIM); + val->intval = bq25890_find_val(ret, TBL_IINLIM); break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: @@ -569,6 +613,43 @@ static int bq25890_power_supply_get_property(struct power_supply *psy, return 0; } +/* On the BQ25892 try to get charger-type info from our supplier */ +static void bq25890_charger_external_power_changed(struct power_supply *psy) +{ + struct bq25890_device *bq = power_supply_get_drvdata(psy); + union power_supply_propval val; + int input_current_limit, ret; + + if (bq->chip_version != BQ25892) + return; + + ret = power_supply_get_property_from_supplier(bq->charger, + POWER_SUPPLY_PROP_USB_TYPE, + &val); + if (ret) + return; + + switch (val.intval) { + case POWER_SUPPLY_USB_TYPE_DCP: + input_current_limit = bq25890_find_idx(2000000, TBL_IINLIM); + if (bq->pump_express_vbus_max) { + queue_delayed_work(system_power_efficient_wq, + &bq->pump_express_work, + PUMP_EXPRESS_START_DELAY); + } + break; + case POWER_SUPPLY_USB_TYPE_CDP: + case POWER_SUPPLY_USB_TYPE_ACA: + input_current_limit = bq25890_find_idx(1500000, TBL_IINLIM); + break; + case POWER_SUPPLY_USB_TYPE_SDP: + default: + input_current_limit = bq25890_find_idx(500000, TBL_IINLIM); + } + + bq25890_field_write(bq, F_IINLIM, input_current_limit); +} + static int bq25890_get_chip_state(struct bq25890_device *bq, struct bq25890_state *state) { @@ -583,7 +664,8 @@ static int bq25890_get_chip_state(struct bq25890_device *bq, {F_VSYS_STAT, &state->vsys_status}, {F_BOOST_FAULT, &state->boost_fault}, {F_BAT_FAULT, &state->bat_fault}, - {F_CHG_FAULT, &state->chrg_fault} + {F_CHG_FAULT, &state->chrg_fault}, + {F_NTC_FAULT, &state->ntc_fault} }; for (i = 0; i < ARRAY_SIZE(state_fields); i++) { @@ -594,9 +676,10 @@ static int bq25890_get_chip_state(struct bq25890_device *bq, *state_fields[i].data = ret; } - dev_dbg(bq->dev, "S:CHG/PG/VSYS=%d/%d/%d, F:CHG/BOOST/BAT=%d/%d/%d\n", + dev_dbg(bq->dev, "S:CHG/PG/VSYS=%d/%d/%d, F:CHG/BOOST/BAT/NTC=%d/%d/%d/%d\n", state->chrg_status, state->online, state->vsys_status, - state->chrg_fault, state->boost_fault, state->bat_fault); + state->chrg_fault, state->boost_fault, state->bat_fault, + state->ntc_fault); return 0; } @@ -670,33 +753,69 @@ static int bq25890_chip_reset(struct bq25890_device *bq) return 0; } -static int bq25890_hw_init(struct bq25890_device *bq) +static int bq25890_rw_init_data(struct bq25890_device *bq) { + bool write = !bq->read_back_init_data; int ret; int i; const struct { enum bq25890_fields id; - u32 value; + u8 *value; } init_data[] = { - {F_ICHG, bq->init_data.ichg}, - {F_VREG, bq->init_data.vreg}, - {F_ITERM, bq->init_data.iterm}, - {F_IPRECHG, bq->init_data.iprechg}, - {F_SYSVMIN, bq->init_data.sysvmin}, - {F_BOOSTV, bq->init_data.boostv}, - {F_BOOSTI, bq->init_data.boosti}, - {F_BOOSTF, bq->init_data.boostf}, - {F_EN_ILIM, bq->init_data.ilim_en}, - {F_TREG, bq->init_data.treg}, - {F_BATCMP, bq->init_data.rbatcomp}, - {F_VCLAMP, bq->init_data.vclamp}, + {F_ICHG, &bq->init_data.ichg}, + {F_VREG, &bq->init_data.vreg}, + {F_ITERM, &bq->init_data.iterm}, + {F_IPRECHG, &bq->init_data.iprechg}, + {F_SYSVMIN, &bq->init_data.sysvmin}, + {F_BOOSTV, &bq->init_data.boostv}, + {F_BOOSTI, &bq->init_data.boosti}, + {F_BOOSTF, &bq->init_data.boostf}, + {F_EN_ILIM, &bq->init_data.ilim_en}, + {F_TREG, &bq->init_data.treg}, + {F_BATCMP, &bq->init_data.rbatcomp}, + {F_VCLAMP, &bq->init_data.vclamp}, }; - ret = bq25890_chip_reset(bq); - if (ret < 0) { - dev_dbg(bq->dev, "Reset failed %d\n", ret); - return ret; + for (i = 0; i < ARRAY_SIZE(init_data); i++) { + if (write) { + ret = bq25890_field_write(bq, init_data[i].id, + *init_data[i].value); + } else { + ret = bq25890_field_read(bq, init_data[i].id); + if (ret >= 0) + *init_data[i].value = ret; + } + if (ret < 0) { + dev_dbg(bq->dev, "Accessing init data failed %d\n", ret); + return ret; + } + } + + return 0; +} + +static int bq25890_hw_init(struct bq25890_device *bq) +{ + int ret; + + if (!bq->skip_reset) { + ret = bq25890_chip_reset(bq); + if (ret < 0) { + dev_dbg(bq->dev, "Reset failed %d\n", ret); + return ret; + } + } else { + /* + * Ensure charging is enabled, on some boards where the fw + * takes care of initalizition F_CHG_CFG is set to 0 before + * handing control over to the OS. + */ + ret = bq25890_field_write(bq, F_CHG_CFG, 1); + if (ret < 0) { + dev_dbg(bq->dev, "Enabling charging failed %d\n", ret); + return ret; + } } /* disable watchdog */ @@ -707,14 +826,9 @@ static int bq25890_hw_init(struct bq25890_device *bq) } /* initialize currents/voltages and other parameters */ - for (i = 0; i < ARRAY_SIZE(init_data); i++) { - ret = bq25890_field_write(bq, init_data[i].id, - init_data[i].value); - if (ret < 0) { - dev_dbg(bq->dev, "Writing init data failed %d\n", ret); - return ret; - } - } + ret = bq25890_rw_init_data(bq); + if (ret) + return ret; ret = bq25890_get_chip_state(bq, &bq->state); if (ret < 0) { @@ -760,6 +874,7 @@ static const struct power_supply_desc bq25890_power_supply_desc = { .properties = bq25890_power_supply_props, .num_properties = ARRAY_SIZE(bq25890_power_supply_props), .get_property = bq25890_power_supply_get_property, + .external_power_changed = bq25890_charger_external_power_changed, }; static int bq25890_power_supply_init(struct bq25890_device *bq) @@ -776,6 +891,64 @@ static int bq25890_power_supply_init(struct bq25890_device *bq) return PTR_ERR_OR_ZERO(bq->charger); } +static int bq25890_set_otg_cfg(struct bq25890_device *bq, u8 val) +{ + int ret; + + ret = bq25890_field_write(bq, F_OTG_CFG, val); + if (ret < 0) + dev_err(bq->dev, "Error switching to boost/charger mode: %d\n", ret); + + return ret; +} + +static void bq25890_pump_express_work(struct work_struct *data) +{ + struct bq25890_device *bq = + container_of(data, struct bq25890_device, pump_express_work.work); + int voltage, i, ret; + + dev_dbg(bq->dev, "Start to request input voltage increasing\n"); + + /* Enable current pulse voltage control protocol */ + ret = bq25890_field_write(bq, F_PUMPX_EN, 1); + if (ret < 0) + goto error_print; + + for (i = 0; i < PUMP_EXPRESS_MAX_TRIES; i++) { + voltage = bq25890_get_vbus_voltage(bq); + if (voltage < 0) + goto error_print; + dev_dbg(bq->dev, "input voltage = %d uV\n", voltage); + + if ((voltage + PUMP_EXPRESS_VBUS_MARGIN_uV) > + bq->pump_express_vbus_max) + break; + + ret = bq25890_field_write(bq, F_PUMPX_UP, 1); + if (ret < 0) + goto error_print; + + /* Note a single PUMPX up pulse-sequence takes 2.1s */ + ret = regmap_field_read_poll_timeout(bq->rmap_fields[F_PUMPX_UP], + ret, !ret, 100000, 3000000); + if (ret < 0) + goto error_print; + + /* Make sure ADC has sampled Vbus before checking again */ + msleep(1000); + } + + bq25890_field_write(bq, F_PUMPX_EN, 0); + + dev_info(bq->dev, "Hi-voltage charging requested, input voltage is %d mV\n", + voltage); + + return; +error_print: + dev_err(bq->dev, "Failed to request hi-voltage charging\n"); +} + static void bq25890_usb_work(struct work_struct *data) { int ret; @@ -785,25 +958,16 @@ static void bq25890_usb_work(struct work_struct *data) switch (bq->usb_event) { case USB_EVENT_ID: /* Enable boost mode */ - ret = bq25890_field_write(bq, F_OTG_CFG, 1); - if (ret < 0) - goto error; + bq25890_set_otg_cfg(bq, 1); break; case USB_EVENT_NONE: /* Disable boost mode */ - ret = bq25890_field_write(bq, F_OTG_CFG, 0); - if (ret < 0) - goto error; - - power_supply_changed(bq->charger); + ret = bq25890_set_otg_cfg(bq, 0); + if (ret == 0) + power_supply_changed(bq->charger); break; } - - return; - -error: - dev_err(bq->dev, "Error switching to boost/charger mode.\n"); } static int bq25890_usb_notifier(struct notifier_block *nb, unsigned long val, @@ -818,6 +982,45 @@ static int bq25890_usb_notifier(struct notifier_block *nb, unsigned long val, return NOTIFY_OK; } +#ifdef CONFIG_REGULATOR +static int bq25890_vbus_enable(struct regulator_dev *rdev) +{ + struct bq25890_device *bq = rdev_get_drvdata(rdev); + + return bq25890_set_otg_cfg(bq, 1); +} + +static int bq25890_vbus_disable(struct regulator_dev *rdev) +{ + struct bq25890_device *bq = rdev_get_drvdata(rdev); + + return bq25890_set_otg_cfg(bq, 0); +} + +static int bq25890_vbus_is_enabled(struct regulator_dev *rdev) +{ + struct bq25890_device *bq = rdev_get_drvdata(rdev); + + return bq25890_field_read(bq, F_OTG_CFG); +} + +static const struct regulator_ops bq25890_vbus_ops = { + .enable = bq25890_vbus_enable, + .disable = bq25890_vbus_disable, + .is_enabled = bq25890_vbus_is_enabled, +}; + +static const struct regulator_desc bq25890_vbus_desc = { + .name = "usb_otg_vbus", + .of_match = "usb-otg-vbus", + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .ops = &bq25890_vbus_ops, + .fixed_uV = 5000000, + .n_voltages = 1, +}; +#endif + static int bq25890_get_chip_version(struct bq25890_device *bq) { int id, rev; @@ -936,6 +1139,16 @@ static int bq25890_fw_probe(struct bq25890_device *bq) int ret; struct bq25890_init_data *init = &bq->init_data; + /* Optional, left at 0 if property is not present */ + device_property_read_u32(bq->dev, "linux,pump-express-vbus-max", + &bq->pump_express_vbus_max); + + bq->skip_reset = device_property_read_bool(bq->dev, "linux,skip-reset"); + bq->read_back_init_data = device_property_read_bool(bq->dev, + "linux,read-back-settings"); + if (bq->read_back_init_data) + return 0; + ret = bq25890_fw_read_u32_props(bq); if (ret < 0) return ret; @@ -952,7 +1165,6 @@ static int bq25890_probe(struct i2c_client *client, struct device *dev = &client->dev; struct bq25890_device *bq; int ret; - int i; bq = devm_kzalloc(dev, sizeof(*bq), GFP_KERNEL); if (!bq) @@ -962,21 +1174,17 @@ static int bq25890_probe(struct i2c_client *client, bq->dev = dev; mutex_init(&bq->lock); + INIT_DELAYED_WORK(&bq->pump_express_work, bq25890_pump_express_work); bq->rmap = devm_regmap_init_i2c(client, &bq25890_regmap_config); if (IS_ERR(bq->rmap)) return dev_err_probe(dev, PTR_ERR(bq->rmap), "failed to allocate register map\n"); - for (i = 0; i < ARRAY_SIZE(bq25890_reg_fields); i++) { - const struct reg_field *reg_fields = bq25890_reg_fields; - - bq->rmap_fields[i] = devm_regmap_field_alloc(dev, bq->rmap, - reg_fields[i]); - if (IS_ERR(bq->rmap_fields[i])) - return dev_err_probe(dev, PTR_ERR(bq->rmap_fields[i]), - "cannot allocate regmap field\n"); - } + ret = devm_regmap_field_bulk_alloc(dev, bq->rmap, bq->rmap_fields, + bq25890_reg_fields, F_MAX_FIELDS); + if (ret) + return ret; i2c_set_clientdata(client, bq); @@ -986,16 +1194,9 @@ static int bq25890_probe(struct i2c_client *client, return ret; } - if (!dev->platform_data) { - ret = bq25890_fw_probe(bq); - if (ret < 0) { - dev_err(dev, "Cannot read device properties: %d\n", - ret); - return ret; - } - } else { - return -ENODEV; - } + ret = bq25890_fw_probe(bq); + if (ret < 0) + return dev_err_probe(dev, ret, "reading device properties\n"); ret = bq25890_hw_init(bq); if (ret < 0) { @@ -1018,6 +1219,22 @@ static int bq25890_probe(struct i2c_client *client, bq->usb_nb.notifier_call = bq25890_usb_notifier; usb_register_notifier(bq->usb_phy, &bq->usb_nb); } +#ifdef CONFIG_REGULATOR + else { + struct bq25890_platform_data *pdata = dev_get_platdata(dev); + struct regulator_config cfg = { }; + struct regulator_dev *reg; + + cfg.dev = dev; + cfg.driver_data = bq; + if (pdata) + cfg.init_data = pdata->regulator_init_data; + + reg = devm_regulator_register(dev, &bq25890_vbus_desc, &cfg); + if (IS_ERR(reg)) + return dev_err_probe(dev, PTR_ERR(reg), "registering regulator"); + } +#endif ret = bq25890_power_supply_init(bq); if (ret < 0) { @@ -1048,12 +1265,36 @@ static int bq25890_remove(struct i2c_client *client) if (!IS_ERR_OR_NULL(bq->usb_phy)) usb_unregister_notifier(bq->usb_phy, &bq->usb_nb); - /* reset all registers to default values */ - bq25890_chip_reset(bq); + if (!bq->skip_reset) { + /* reset all registers to default values */ + bq25890_chip_reset(bq); + } return 0; } +static void bq25890_shutdown(struct i2c_client *client) +{ + struct bq25890_device *bq = i2c_get_clientdata(client); + + /* + * TODO this if + return should probably be removed, but that would + * introduce a function change for boards using the usb-phy framework. + * This needs to be tested on such a board before making this change. + */ + if (!IS_ERR_OR_NULL(bq->usb_phy)) + return; + + /* + * Turn off the 5v Boost regulator which outputs Vbus to the device's + * Micro-USB or Type-C USB port. Leaving this on drains power and + * this avoids the PMIC on some device-models seeing this as Vbus + * getting inserted after shutdown, causing the device to immediately + * power-up again. + */ + bq25890_set_otg_cfg(bq, 0); +} + #ifdef CONFIG_PM_SLEEP static int bq25890_suspend(struct device *dev) { @@ -1133,6 +1374,7 @@ static struct i2c_driver bq25890_driver = { }, .probe = bq25890_probe, .remove = bq25890_remove, + .shutdown = bq25890_shutdown, .id_table = bq25890_i2c_ids, }; module_i2c_driver(bq25890_driver); diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index ec838c9bcc0a..df4471e50d33 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -376,46 +376,49 @@ int power_supply_is_system_supplied(void) } EXPORT_SYMBOL_GPL(power_supply_is_system_supplied); -static int __power_supply_get_supplier_max_current(struct device *dev, - void *data) +struct psy_get_supplier_prop_data { + struct power_supply *psy; + enum power_supply_property psp; + union power_supply_propval *val; +}; + +static int __power_supply_get_supplier_property(struct device *dev, void *_data) { - union power_supply_propval ret = {0,}; struct power_supply *epsy = dev_get_drvdata(dev); - struct power_supply *psy = data; + struct psy_get_supplier_prop_data *data = _data; - if (__power_supply_is_supplied_by(epsy, psy)) - if (!epsy->desc->get_property(epsy, - POWER_SUPPLY_PROP_CURRENT_MAX, - &ret)) - return ret.intval; + if (__power_supply_is_supplied_by(epsy, data->psy)) + if (!epsy->desc->get_property(epsy, data->psp, data->val)) + return 1; /* Success */ - return 0; + return 0; /* Continue iterating */ } -int power_supply_set_input_current_limit_from_supplier(struct power_supply *psy) +int power_supply_get_property_from_supplier(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) { - union power_supply_propval val = {0,}; - int curr; - - if (!psy->desc->set_property) - return -EINVAL; + struct psy_get_supplier_prop_data data = { + .psy = psy, + .psp = psp, + .val = val, + }; + int ret; /* * This function is not intended for use with a supply with multiple - * suppliers, we simply pick the first supply to report a non 0 - * max-current. + * suppliers, we simply pick the first supply to report the psp. */ - curr = class_for_each_device(power_supply_class, NULL, psy, - __power_supply_get_supplier_max_current); - if (curr <= 0) - return (curr == 0) ? -ENODEV : curr; - - val.intval = curr; + ret = class_for_each_device(power_supply_class, NULL, &data, + __power_supply_get_supplier_property); + if (ret < 0) + return ret; + if (ret == 0) + return -ENODEV; - return psy->desc->set_property(psy, - POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val); + return 0; } -EXPORT_SYMBOL_GPL(power_supply_set_input_current_limit_from_supplier); +EXPORT_SYMBOL_GPL(power_supply_get_property_from_supplier); int power_supply_set_battery_charged(struct power_supply *psy) { |