From 6cf62a3b97e78ba41d31390e59a1ddc98a9e3622 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 14 Apr 2017 18:52:33 +0200 Subject: [PATCH 01/34] power: supply: bq24190_charger: Add disable-reset device-property Allow platform-code to disable the reset on probe and suspend/resume by setting a "disable-reset" boolean device property on the device. There are several reasons why the platform-code may want to disable the reset on probe and suspend/resume: 1) Resetting the charger should never be necessary it should always have sane values programmed. If it is running with invalid values while we are not running (system turned off or suspended) there is a big problem as that may lead to overcharging the battery. 2) The reset in suspend() is meant to put the charger back into default mode, but this is not necessary and not a good idea. If the charger has been programmed with a higher max charge_current / charge_voltage then putting it back in default-mode will reset those to the safe power-on defaults, leading to slower charging, or charging to a lower voltage (and thus not using the full capacity) while suspended which is undesirable. Reprogramming the max charge_current / charge_voltage after the reset will not help here as that will put the charger back in host mode and start the i2c watchdog if the host then does not do anything for 40s (iow if we're suspended for more then 40s) the watchdog expires resetting the device to default-mode, including resetting all the registers to there safe power-on defaults. So the only way to keep using custom charge settings while suspending is to keep the charger in its normal running state with the i2c watchdog disabled. This is fine as the charger will still automatically switch from constant current to constant voltage and stop charging when the battery is full. 3) Besides never being necessary resetting the charger also causes problems on systems where the charge voltage limit is set higher then the reset value, if this is the case and the charger is reset while charging and the battery voltage is between the 2 voltages, then about half the time the charger gets confused and claims to be charging (REG08 contains 0x64) but in reality the charger has decoupled itself from VBUS (Q1 off) and is drawing 0A from VBUS, leaving the system running from the battery. This last problem is happening on a GPD-win mini PC with a bq24292i charger chip combined with a max17047 fuel-gauge and a LiHV battery. I've checked and TI does not list any errata for the bq24292i which could explain this (there are no errata at all). Cc: Liam Breck Cc: Tony Lindgren Signed-off-by: Hans de Goede Acked-by: Liam Breck Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq24190_charger.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c index bd9e5c3d8cc2..2d379a2f99f1 100644 --- a/drivers/power/supply/bq24190_charger.c +++ b/drivers/power/supply/bq24190_charger.c @@ -533,6 +533,9 @@ static int bq24190_register_reset(struct bq24190_dev_info *bdi) int ret, limit = 100; u8 v; + if (device_property_read_bool(bdi->dev, "disable-reset")) + return 0; + /* Reset the registers */ ret = bq24190_write_mask(bdi, BQ24190_REG_POC, BQ24190_REG_POC_RESET_MASK, From 3fb319c2cdcd10f775f4d4a05a7d12fa1a5679c1 Mon Sep 17 00:00:00 2001 From: "H. Nikolaus Schaller" Date: Fri, 14 Apr 2017 20:25:56 +0200 Subject: [PATCH 02/34] power: supply: twl4030-charger: add writable INPUT_CURRENT_LIMIT property Currently, the twl4030 charger defines its own max_current by directly creating sysfs nodes. It should use the input_current_limit property which is e.g. used by the bq24257 driver. This patch adds the input_current_property with the same semantics as the max_current property. The code to manage the max_current property is removed by a separate patch. Signed-off-by: H. Nikolaus Schaller Signed-off-by: Sebastian Reichel --- drivers/power/supply/twl4030_charger.c | 59 ++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/drivers/power/supply/twl4030_charger.c b/drivers/power/supply/twl4030_charger.c index 990ff3d218bc..0b84f336fe39 100644 --- a/drivers/power/supply/twl4030_charger.c +++ b/drivers/power/supply/twl4030_charger.c @@ -922,6 +922,28 @@ static int twl4030_bci_get_property(struct power_supply *psy, twl4030_bci_state_to_status(state) != POWER_SUPPLY_STATUS_NOT_CHARGING; break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + val->intval = -1; + if (psy->desc->type != POWER_SUPPLY_TYPE_USB) { + if (!bci->ac_is_active) + val->intval = bci->ac_cur; + } else { + if (bci->ac_is_active) + val->intval = bci->usb_cur_target; + } + if (val->intval < 0) { + u8 bcictl1; + + val->intval = twl4030bci_read_adc_val(TWL4030_BCIIREF1); + if (val->intval < 0) + return val->intval; + ret = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1); + if (ret < 0) + return ret; + val->intval = regval2ua(val->intval, bcictl1 & + TWL4030_CGAIN); + } + break; default: return -EINVAL; } @@ -929,11 +951,44 @@ static int twl4030_bci_get_property(struct power_supply *psy, return 0; } +static int twl4030_bci_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct twl4030_bci *bci = dev_get_drvdata(psy->dev.parent); + + switch (psp) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + if (psy->desc->type == POWER_SUPPLY_TYPE_USB) + bci->usb_cur_target = val->intval; + else + bci->ac_cur = val->intval; + twl4030_charger_update_current(bci); + break; + default: + return -EINVAL; + } + + return 0; +} + +int twl4030_bci_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + return true; + default: + return false; + } +} + static enum power_supply_property twl4030_charger_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, }; #ifdef CONFIG_OF @@ -970,6 +1025,8 @@ static const struct power_supply_desc twl4030_bci_ac_desc = { .properties = twl4030_charger_props, .num_properties = ARRAY_SIZE(twl4030_charger_props), .get_property = twl4030_bci_get_property, + .set_property = twl4030_bci_set_property, + .property_is_writeable = twl4030_bci_property_is_writeable, }; static const struct power_supply_desc twl4030_bci_usb_desc = { @@ -978,6 +1035,8 @@ static const struct power_supply_desc twl4030_bci_usb_desc = { .properties = twl4030_charger_props, .num_properties = ARRAY_SIZE(twl4030_charger_props), .get_property = twl4030_bci_get_property, + .set_property = twl4030_bci_set_property, + .property_is_writeable = twl4030_bci_property_is_writeable, }; static int twl4030_bci_probe(struct platform_device *pdev) From 19a2ee69c20255157738402b6fdbcf5d708dad80 Mon Sep 17 00:00:00 2001 From: "H. Nikolaus Schaller" Date: Fri, 14 Apr 2017 21:29:38 +0200 Subject: [PATCH 03/34] power: supply: twl4030-charger: don't check if battery is present We can't assume that the battery is or stays present after probing on devices with replaceable battery. On some devices (e.g. GTA04 or OpenPanodra) it can be removed and even be hot swapped by the user while device continues to operate through external AC or USB power (as long as system power consumption remains below ca. 500mA as provided by USB). Under certain conditions it is possible to boot without battery. So it makes no sense to check for this situation during probe and make the charger driver (and its status reports) completely non-operational if the battery can be inserted later. Tested on: GTA04 and OpenPandora. Signed-off-by: H. Nikolaus Schaller Signed-off-by: Sebastian Reichel --- drivers/power/supply/twl4030_charger.c | 36 -------------------------- 1 file changed, 36 deletions(-) diff --git a/drivers/power/supply/twl4030_charger.c b/drivers/power/supply/twl4030_charger.c index 0b84f336fe39..abd10f6861ae 100644 --- a/drivers/power/supply/twl4030_charger.c +++ b/drivers/power/supply/twl4030_charger.c @@ -205,35 +205,6 @@ static int twl4030bci_read_adc_val(u8 reg) return temp | val; } -/* - * Check if Battery Pack was present - */ -static int twl4030_is_battery_present(struct twl4030_bci *bci) -{ - int ret; - u8 val = 0; - - /* Battery presence in Main charge? */ - ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, TWL4030_BCIMFSTS3); - if (ret) - return ret; - if (val & TWL4030_BATSTSMCHG) - return 0; - - /* - * OK, It could be that bootloader did not enable main charger, - * pre-charge is h/w auto. So, Battery presence in Pre-charge? - */ - ret = twl_i2c_read_u8(TWL4030_MODULE_PRECHARGE, &val, - TWL4030_BCIMFSTS1); - if (ret) - return ret; - if (val & TWL4030_BATSTSPCHG) - return 0; - - return -ENODEV; -} - /* * TI provided formulas: * CGAIN == 0: ICHG = (BCIICHG * 1.7) / (2^10 - 1) - 0.85 @@ -1068,13 +1039,6 @@ static int twl4030_bci_probe(struct platform_device *pdev) bci->irq_chg = platform_get_irq(pdev, 0); bci->irq_bci = platform_get_irq(pdev, 1); - /* Only proceed further *IF* battery is physically present */ - ret = twl4030_is_battery_present(bci); - if (ret) { - dev_crit(&pdev->dev, "Battery was not detected:%d\n", ret); - return ret; - } - platform_set_drvdata(pdev, bci); bci->ac = devm_power_supply_register(&pdev->dev, &twl4030_bci_ac_desc, From 2848e039c562a5278cd2ebc5b005c0f1a381b34e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 16 Apr 2017 17:30:31 +0200 Subject: [PATCH 04/34] power: supply: Make power_supply_am_i_supplied return -ENODEV if there are no suppliers It is sensible to assume that the hardware actually always has a way of charging the battery so when power_supply_am_i_supplied does not find any suppliers, that does not mean that there are none, but simply that no power_supply-drivers are registered / bound for any suppliers for the supply calling power_supply_am_i_supplied. At which point a fuel-gauge driver calling power_supply_am_i_supplied() cannot determine whether the battery is being charged or not. Allow a caller of power_supply_am_i_supplied to differentiate between there not being any suppliers, vs no suppliers being online by returning -ENODEV if there are no suppliers matching supplied_to / supplied_from, which allows fuel-gauge drivers to return POWER_SUPPLY_STATUS_UNKNOWN rather then POWER_SUPPLY_STATUS_DISCHARGING if there are no suppliers. Signed-off-by: Hans de Goede Reviewed-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/supply/power_supply_core.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index 1e0960b646e8..7ec7c7c202bd 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -280,13 +280,19 @@ static inline int power_supply_check_supplies(struct power_supply *psy) } #endif -static int __power_supply_am_i_supplied(struct device *dev, void *data) +struct psy_am_i_supplied_data { + struct power_supply *psy; + unsigned int count; +}; + +static int __power_supply_am_i_supplied(struct device *dev, void *_data) { union power_supply_propval ret = {0,}; - struct power_supply *psy = data; struct power_supply *epsy = dev_get_drvdata(dev); + struct psy_am_i_supplied_data *data = _data; - if (__power_supply_is_supplied_by(epsy, psy)) + data->count++; + if (__power_supply_is_supplied_by(epsy, data->psy)) if (!epsy->desc->get_property(epsy, POWER_SUPPLY_PROP_ONLINE, &ret)) return ret.intval; @@ -296,12 +302,16 @@ static int __power_supply_am_i_supplied(struct device *dev, void *data) int power_supply_am_i_supplied(struct power_supply *psy) { + struct psy_am_i_supplied_data data = { psy, 0 }; int error; - error = class_for_each_device(power_supply_class, NULL, psy, + error = class_for_each_device(power_supply_class, NULL, &data, __power_supply_am_i_supplied); - dev_dbg(&psy->dev, "%s %d\n", __func__, error); + dev_dbg(&psy->dev, "%s count %u err %d\n", __func__, data.count, error); + + if (data.count == 0) + return -ENODEV; return error; } From 1eb2869b3c90f4e7971515ddbe92f9e0d18827f5 Mon Sep 17 00:00:00 2001 From: Liam Breck Date: Sun, 16 Apr 2017 16:06:26 -0700 Subject: [PATCH 05/34] power: supply: bq24190_charger: Deprecate battery class and replicate its features in charger The driver was registering two classes, bq24190-battery & -charger. Because the power supply framework cannot surface features from multiple drivers in a single class, a fuel gauge driver would create a third class, which some power management utilities cannot see. Deprecate the -battery class for future removal and replicate its features in -charger. Set /sys/class...-charger/online = pg_stat && !batfet_disable. If device_property "omit-battery-class" is set, don't register -battery. Cc: Tony Lindgren Cc: Hans de Goede Signed-off-by: Liam Breck Reviewed-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq24190_charger.c | 146 +++++++++++++++++++------ 1 file changed, 111 insertions(+), 35 deletions(-) diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c index 2d379a2f99f1..d5a707e14526 100644 --- a/drivers/power/supply/bq24190_charger.c +++ b/drivers/power/supply/bq24190_charger.c @@ -662,22 +662,25 @@ static int bq24190_charger_get_health(struct bq24190_dev_info *bdi, v = bdi->f_reg; mutex_unlock(&bdi->f_reg_lock); - if (v & BQ24190_REG_F_BOOST_FAULT_MASK) { - /* - * This could be over-current or over-voltage but there's - * no way to tell which. Return 'OVERVOLTAGE' since there - * isn't an 'OVERCURRENT' value defined that we can return - * even if it was over-current. - */ - health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; - } else { - v &= BQ24190_REG_F_CHRG_FAULT_MASK; - v >>= BQ24190_REG_F_CHRG_FAULT_SHIFT; - - switch (v) { - case 0x0: /* Normal */ - health = POWER_SUPPLY_HEALTH_GOOD; + if (v & BQ24190_REG_F_NTC_FAULT_MASK) { + switch (v >> BQ24190_REG_F_NTC_FAULT_SHIFT & 0x7) { + case 0x1: /* TS1 Cold */ + case 0x3: /* TS2 Cold */ + case 0x5: /* Both Cold */ + health = POWER_SUPPLY_HEALTH_COLD; break; + case 0x2: /* TS1 Hot */ + case 0x4: /* TS2 Hot */ + case 0x6: /* Both Hot */ + health = POWER_SUPPLY_HEALTH_OVERHEAT; + break; + default: + health = POWER_SUPPLY_HEALTH_UNKNOWN; + } + } else if (v & BQ24190_REG_F_BAT_FAULT_MASK) { + health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + } else if (v & BQ24190_REG_F_CHRG_FAULT_MASK) { + switch (v >> BQ24190_REG_F_CHRG_FAULT_SHIFT & 0x3) { case 0x1: /* Input Fault (VBUS OVP or VBATintval = health; @@ -707,19 +720,59 @@ static int bq24190_charger_get_health(struct bq24190_dev_info *bdi, static int bq24190_charger_get_online(struct bq24190_dev_info *bdi, union power_supply_propval *val) { - u8 v; + u8 pg_stat, batfet_disable; int ret; ret = bq24190_read_mask(bdi, BQ24190_REG_SS, BQ24190_REG_SS_PG_STAT_MASK, - BQ24190_REG_SS_PG_STAT_SHIFT, &v); + BQ24190_REG_SS_PG_STAT_SHIFT, &pg_stat); if (ret < 0) return ret; - val->intval = v; + ret = bq24190_read_mask(bdi, BQ24190_REG_MOC, + BQ24190_REG_MOC_BATFET_DISABLE_MASK, + BQ24190_REG_MOC_BATFET_DISABLE_SHIFT, &batfet_disable); + if (ret < 0) + return ret; + + val->intval = pg_stat && !batfet_disable; + return 0; } +static int bq24190_battery_set_online(struct bq24190_dev_info *bdi, + const union power_supply_propval *val); +static int bq24190_battery_get_status(struct bq24190_dev_info *bdi, + union power_supply_propval *val); +static int bq24190_battery_get_temp_alert_max(struct bq24190_dev_info *bdi, + union power_supply_propval *val); +static int bq24190_battery_set_temp_alert_max(struct bq24190_dev_info *bdi, + const union power_supply_propval *val); + +static int bq24190_charger_set_online(struct bq24190_dev_info *bdi, + const union power_supply_propval *val) +{ + return bq24190_battery_set_online(bdi, val); +} + +static int bq24190_charger_get_status(struct bq24190_dev_info *bdi, + union power_supply_propval *val) +{ + return bq24190_battery_get_status(bdi, val); +} + +static int bq24190_charger_get_temp_alert_max(struct bq24190_dev_info *bdi, + union power_supply_propval *val) +{ + return bq24190_battery_get_temp_alert_max(bdi, val); +} + +static int bq24190_charger_set_temp_alert_max(struct bq24190_dev_info *bdi, + const union power_supply_propval *val) +{ + return bq24190_battery_set_temp_alert_max(bdi, val); +} + static int bq24190_charger_get_current(struct bq24190_dev_info *bdi, union power_supply_propval *val) { @@ -834,6 +887,12 @@ static int bq24190_charger_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_ONLINE: ret = bq24190_charger_get_online(bdi, val); break; + case POWER_SUPPLY_PROP_STATUS: + ret = bq24190_charger_get_status(bdi, val); + break; + case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: + ret = bq24190_charger_get_temp_alert_max(bdi, val); + break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: ret = bq24190_charger_get_current(bdi, val); break; @@ -882,6 +941,12 @@ static int bq24190_charger_set_property(struct power_supply *psy, return ret; switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + ret = bq24190_charger_set_online(bdi, val); + break; + case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: + ret = bq24190_charger_set_temp_alert_max(bdi, val); + break; case POWER_SUPPLY_PROP_CHARGE_TYPE: ret = bq24190_charger_set_charge_type(bdi, val); break; @@ -907,6 +972,8 @@ static int bq24190_charger_property_is_writeable(struct power_supply *psy, int ret; switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: case POWER_SUPPLY_PROP_CHARGE_TYPE: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: @@ -923,6 +990,8 @@ static enum power_supply_property bq24190_charger_properties[] = { POWER_SUPPLY_PROP_CHARGE_TYPE, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_TEMP_ALERT_MAX, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, @@ -1096,6 +1165,7 @@ static int bq24190_battery_get_property(struct power_supply *psy, struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy); int ret; + dev_warn(bdi->dev, "warning: /sys/class/power_supply/bq24190-battery is deprecated\n"); dev_dbg(bdi->dev, "prop: %d\n", psp); ret = pm_runtime_get_sync(bdi->dev); @@ -1141,6 +1211,7 @@ static int bq24190_battery_set_property(struct power_supply *psy, struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy); int ret; + dev_warn(bdi->dev, "warning: /sys/class/power_supply/bq24190-battery is deprecated\n"); dev_dbg(bdi->dev, "prop: %d\n", psp); ret = pm_runtime_get_sync(bdi->dev); @@ -1269,9 +1340,9 @@ static void bq24190_check_status(struct bq24190_dev_info *bdi) bdi->ss_reg = ss_reg; } - if (alert_charger) + if (alert_charger || alert_battery) power_supply_changed(bdi->charger); - if (alert_battery) + if (alert_battery && bdi->battery) power_supply_changed(bdi->battery); dev_dbg(bdi->dev, "ss_reg: 0x%02x, f_reg: 0x%02x\n", ss_reg, f_reg); @@ -1476,19 +1547,23 @@ static int bq24190_probe(struct i2c_client *client, goto out_pmrt; } - battery_cfg.drv_data = bdi; - bdi->battery = power_supply_register(dev, &bq24190_battery_desc, - &battery_cfg); - if (IS_ERR(bdi->battery)) { - dev_err(dev, "Can't register battery\n"); - ret = PTR_ERR(bdi->battery); - goto out_charger; + /* the battery class is deprecated and will be removed. */ + /* in the interim, this property hides it. */ + if (!device_property_read_bool(dev, "omit-battery-class")) { + battery_cfg.drv_data = bdi; + bdi->battery = power_supply_register(dev, &bq24190_battery_desc, + &battery_cfg); + if (IS_ERR(bdi->battery)) { + dev_err(dev, "Can't register battery\n"); + ret = PTR_ERR(bdi->battery); + goto out_charger; + } } ret = bq24190_sysfs_create_group(bdi); if (ret) { dev_err(dev, "Can't create sysfs entries\n"); - goto out_battery; + goto out_charger; } bdi->initialized = true; @@ -1526,10 +1601,9 @@ static int bq24190_probe(struct i2c_client *client, out_sysfs: bq24190_sysfs_remove_group(bdi); -out_battery: - power_supply_unregister(bdi->battery); - out_charger: + if (!IS_ERR_OR_NULL(bdi->battery)) + power_supply_unregister(bdi->battery); power_supply_unregister(bdi->charger); out_pmrt: @@ -1552,7 +1626,8 @@ static int bq24190_remove(struct i2c_client *client) bq24190_register_reset(bdi); bq24190_sysfs_remove_group(bdi); - power_supply_unregister(bdi->battery); + if (bdi->battery) + power_supply_unregister(bdi->battery); power_supply_unregister(bdi->charger); if (error >= 0) pm_runtime_put_sync(bdi->dev); @@ -1639,7 +1714,8 @@ static __maybe_unused int bq24190_pm_resume(struct device *dev) /* Things may have changed while suspended so alert upper layer */ power_supply_changed(bdi->charger); - power_supply_changed(bdi->battery); + if (bdi->battery) + power_supply_changed(bdi->battery); return 0; } From 7221675c70d147f2ae6f044697dff046f3805224 Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Tue, 18 Apr 2017 09:34:16 +0200 Subject: [PATCH 06/34] dt-bindings: power: supply: add AXP20X/AXP22X battery DT binding The X-Powers AXP20X and AXP22X PMICs can have a battery as power supply. This patch adds the DT binding documentation for the battery power supply which gets various data from the PMIC, such as the battery status (charging, discharging, full, dead), current max limit, current current, battery capacity (in percentage), voltage max and min limits, current voltage and battery capacity (in Ah). Signed-off-by: Quentin Schulz Acked-by: Chen-Yu Tsai Acked-by: Maxime Ripard Acked-by: Rob Herring Acked-by: Chen-Yu Tsai Signed-off-by: Sebastian Reichel --- .../bindings/power/supply/axp20x_battery.txt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Documentation/devicetree/bindings/power/supply/axp20x_battery.txt diff --git a/Documentation/devicetree/bindings/power/supply/axp20x_battery.txt b/Documentation/devicetree/bindings/power/supply/axp20x_battery.txt new file mode 100644 index 000000000000..c24886676a60 --- /dev/null +++ b/Documentation/devicetree/bindings/power/supply/axp20x_battery.txt @@ -0,0 +1,20 @@ +AXP20x and AXP22x battery power supply + +Required Properties: + - compatible, one of: + "x-powers,axp209-battery-power-supply" + "x-powers,axp221-battery-power-supply" + +This node is a subnode of the axp20x/axp22x PMIC. + +The AXP20X and AXP22X can read the battery voltage, charge and discharge +currents of the battery by reading ADC channels from the AXP20X/AXP22X +ADC. + +Example: + +&axp209 { + battery_power_supply: battery-power-supply { + compatible = "x-powers,axp209-battery-power-supply"; + } +}; From 46c202b5f25fb6fbd4af60ded133fa745b3601b3 Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Tue, 18 Apr 2017 09:34:17 +0200 Subject: [PATCH 07/34] power: supply: add battery driver for AXP20X and AXP22X PMICs The X-Powers AXP20X and AXP22X PMICs can have a battery as power supply. This patch adds the battery power supply driver to get various data from the PMIC, such as the battery status (charging, discharging, full, dead), current max limit, current current, battery capacity (in percentage), voltage max and min limits, current voltage and battery capacity (in Ah). This battery driver uses the AXP20X/AXP22X ADC driver as PMIC data provider. Signed-off-by: Quentin Schulz Acked-by: Jonathan Cameron Acked-by: Maxime Ripard Acked-by: Chen-Yu Tsai Signed-off-by: Sebastian Reichel --- drivers/power/supply/Kconfig | 12 + drivers/power/supply/Makefile | 1 + drivers/power/supply/axp20x_battery.c | 502 ++++++++++++++++++++++++++ 3 files changed, 515 insertions(+) create mode 100644 drivers/power/supply/axp20x_battery.c diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index da922756149f..4b718e90b3ca 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -238,6 +238,18 @@ config CHARGER_AXP20X This driver can also be built as a module. If so, the module will be called axp20x_ac_power. +config BATTERY_AXP20X + tristate "X-Powers AXP20X battery driver" + depends on MFD_AXP20X + depends on AXP20X_ADC + depends on IIO + help + Say Y here to enable support for X-Powers AXP20X PMICs' battery power + supply. + + This driver can also be built as a module. If so, the module will be + called axp20x_battery. + config AXP288_CHARGER tristate "X-Powers AXP288 Charger" depends on MFD_AXP20X && EXTCON_AXP288 diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index 39fc733e6cc4..a39126d7a6ce 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_TEST_POWER) += test_power.o obj-$(CONFIG_BATTERY_88PM860X) += 88pm860x_battery.o obj-$(CONFIG_BATTERY_ACT8945A) += act8945a_charger.o +obj-$(CONFIG_BATTERY_AXP20X) += axp20x_battery.o obj-$(CONFIG_CHARGER_AXP20X) += axp20x_ac_power.o obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o diff --git a/drivers/power/supply/axp20x_battery.c b/drivers/power/supply/axp20x_battery.c new file mode 100644 index 000000000000..5d29b2eab8fc --- /dev/null +++ b/drivers/power/supply/axp20x_battery.c @@ -0,0 +1,502 @@ +/* + * Battery power supply driver for X-Powers AXP20X and AXP22X PMICs + * + * Copyright 2016 Free Electrons NextThing Co. + * Quentin Schulz + * + * This driver is based on a previous upstreaming attempt by: + * Bruno Prémont + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AXP20X_PWR_STATUS_BAT_CHARGING BIT(2) + +#define AXP20X_PWR_OP_BATT_PRESENT BIT(5) +#define AXP20X_PWR_OP_BATT_ACTIVATED BIT(3) + +#define AXP209_FG_PERCENT GENMASK(6, 0) +#define AXP22X_FG_VALID BIT(7) + +#define AXP20X_CHRG_CTRL1_TGT_VOLT GENMASK(6, 5) +#define AXP20X_CHRG_CTRL1_TGT_4_1V (0 << 5) +#define AXP20X_CHRG_CTRL1_TGT_4_15V (1 << 5) +#define AXP20X_CHRG_CTRL1_TGT_4_2V (2 << 5) +#define AXP20X_CHRG_CTRL1_TGT_4_36V (3 << 5) + +#define AXP22X_CHRG_CTRL1_TGT_4_22V (1 << 5) +#define AXP22X_CHRG_CTRL1_TGT_4_24V (3 << 5) + +#define AXP20X_CHRG_CTRL1_TGT_CURR GENMASK(3, 0) + +#define AXP20X_V_OFF_MASK GENMASK(2, 0) + +struct axp20x_batt_ps { + struct regmap *regmap; + struct power_supply *batt; + struct device *dev; + struct iio_channel *batt_chrg_i; + struct iio_channel *batt_dischrg_i; + struct iio_channel *batt_v; + u8 axp_id; +}; + +static int axp20x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt, + int *val) +{ + int ret, reg; + + ret = regmap_read(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, ®); + if (ret) + return ret; + + switch (reg & AXP20X_CHRG_CTRL1_TGT_VOLT) { + case AXP20X_CHRG_CTRL1_TGT_4_1V: + *val = 4100000; + break; + case AXP20X_CHRG_CTRL1_TGT_4_15V: + *val = 4150000; + break; + case AXP20X_CHRG_CTRL1_TGT_4_2V: + *val = 4200000; + break; + case AXP20X_CHRG_CTRL1_TGT_4_36V: + *val = 4360000; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int axp22x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt, + int *val) +{ + int ret, reg; + + ret = regmap_read(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, ®); + if (ret) + return ret; + + switch (reg & AXP20X_CHRG_CTRL1_TGT_VOLT) { + case AXP20X_CHRG_CTRL1_TGT_4_1V: + *val = 4100000; + break; + case AXP20X_CHRG_CTRL1_TGT_4_2V: + *val = 4200000; + break; + case AXP22X_CHRG_CTRL1_TGT_4_22V: + *val = 4220000; + break; + case AXP22X_CHRG_CTRL1_TGT_4_24V: + *val = 4240000; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void raw_to_constant_charge_current(struct axp20x_batt_ps *axp, int *val) +{ + if (axp->axp_id == AXP209_ID) + *val = *val * 100000 + 300000; + else + *val = *val * 150000 + 300000; +} + +static int axp20x_get_constant_charge_current(struct axp20x_batt_ps *axp, + int *val) +{ + int ret; + + ret = regmap_read(axp->regmap, AXP20X_CHRG_CTRL1, val); + if (ret) + return ret; + + *val &= AXP20X_CHRG_CTRL1_TGT_CURR; + + raw_to_constant_charge_current(axp, val); + + return 0; +} + +static int axp20x_battery_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct axp20x_batt_ps *axp20x_batt = power_supply_get_drvdata(psy); + struct iio_channel *chan; + int ret = 0, reg, val1; + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + case POWER_SUPPLY_PROP_ONLINE: + ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_OP_MODE, + ®); + if (ret) + return ret; + + val->intval = !!(reg & AXP20X_PWR_OP_BATT_PRESENT); + break; + + case POWER_SUPPLY_PROP_STATUS: + ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_INPUT_STATUS, + ®); + if (ret) + return ret; + + if (reg & AXP20X_PWR_STATUS_BAT_CHARGING) { + val->intval = POWER_SUPPLY_STATUS_CHARGING; + return 0; + } + + ret = iio_read_channel_processed(axp20x_batt->batt_dischrg_i, + &val1); + if (ret) + return ret; + + if (val1) { + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + return 0; + } + + ret = regmap_read(axp20x_batt->regmap, AXP20X_FG_RES, &val1); + if (ret) + return ret; + + /* + * Fuel Gauge data takes 7 bits but the stored value seems to be + * directly the raw percentage without any scaling to 7 bits. + */ + if ((val1 & AXP209_FG_PERCENT) == 100) + val->intval = POWER_SUPPLY_STATUS_FULL; + else + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + + case POWER_SUPPLY_PROP_HEALTH: + ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_OP_MODE, + &val1); + if (ret) + return ret; + + if (val1 & AXP20X_PWR_OP_BATT_ACTIVATED) { + val->intval = POWER_SUPPLY_HEALTH_DEAD; + return 0; + } + + val->intval = POWER_SUPPLY_HEALTH_GOOD; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + ret = axp20x_get_constant_charge_current(axp20x_batt, + &val->intval); + if (ret) + return ret; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + val->intval = AXP20X_CHRG_CTRL1_TGT_CURR; + raw_to_constant_charge_current(axp20x_batt, &val->intval); + + break; + + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_INPUT_STATUS, + ®); + if (ret) + return ret; + + if (reg & AXP20X_PWR_STATUS_BAT_CHARGING) + chan = axp20x_batt->batt_chrg_i; + else + chan = axp20x_batt->batt_dischrg_i; + + ret = iio_read_channel_processed(chan, &val->intval); + if (ret) + return ret; + + /* IIO framework gives mA but Power Supply framework gives uA */ + val->intval *= 1000; + break; + + case POWER_SUPPLY_PROP_CAPACITY: + /* When no battery is present, return capacity is 100% */ + ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_OP_MODE, + ®); + if (ret) + return ret; + + if (!(reg & AXP20X_PWR_OP_BATT_PRESENT)) { + val->intval = 100; + return 0; + } + + ret = regmap_read(axp20x_batt->regmap, AXP20X_FG_RES, ®); + if (ret) + return ret; + + if (axp20x_batt->axp_id == AXP221_ID && + !(reg & AXP22X_FG_VALID)) + return -EINVAL; + + /* + * Fuel Gauge data takes 7 bits but the stored value seems to be + * directly the raw percentage without any scaling to 7 bits. + */ + val->intval = reg & AXP209_FG_PERCENT; + break; + + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + if (axp20x_batt->axp_id == AXP209_ID) + return axp20x_battery_get_max_voltage(axp20x_batt, + &val->intval); + return axp22x_battery_get_max_voltage(axp20x_batt, + &val->intval); + + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + ret = regmap_read(axp20x_batt->regmap, AXP20X_V_OFF, ®); + if (ret) + return ret; + + val->intval = 2600000 + 100000 * (reg & AXP20X_V_OFF_MASK); + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = iio_read_channel_processed(axp20x_batt->batt_v, + &val->intval); + if (ret) + return ret; + + /* IIO framework gives mV but Power Supply framework gives uV */ + val->intval *= 1000; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int axp20x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt, + int val) +{ + switch (val) { + case 4100000: + val = AXP20X_CHRG_CTRL1_TGT_4_1V; + break; + + case 4150000: + if (axp20x_batt->axp_id == AXP221_ID) + return -EINVAL; + + val = AXP20X_CHRG_CTRL1_TGT_4_15V; + break; + + case 4200000: + val = AXP20X_CHRG_CTRL1_TGT_4_2V; + break; + + default: + /* + * AXP20x max voltage can be set to 4.36V and AXP22X max voltage + * can be set to 4.22V and 4.24V, but these voltages are too + * high for Lithium based batteries (AXP PMICs are supposed to + * be used with these kinds of battery). + */ + return -EINVAL; + } + + return regmap_update_bits(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, + AXP20X_CHRG_CTRL1_TGT_VOLT, val); +} + +static int axp20x_set_constant_charge_current(struct axp20x_batt_ps *axp_batt, + int charge_current) +{ + if (axp_batt->axp_id == AXP209_ID) + charge_current = (charge_current - 300000) / 100000; + else + charge_current = (charge_current - 300000) / 150000; + + if (charge_current > AXP20X_CHRG_CTRL1_TGT_CURR || charge_current < 0) + return -EINVAL; + + return regmap_update_bits(axp_batt->regmap, AXP20X_CHRG_CTRL1, + AXP20X_CHRG_CTRL1_TGT_CURR, charge_current); +} + +static int axp20x_set_voltage_min_design(struct axp20x_batt_ps *axp_batt, + int min_voltage) +{ + int val1 = (min_voltage - 2600000) / 100000; + + if (val1 < 0 || val1 > AXP20X_V_OFF_MASK) + return -EINVAL; + + return regmap_update_bits(axp_batt->regmap, AXP20X_V_OFF, + AXP20X_V_OFF_MASK, val1); +} + +static int axp20x_battery_set_prop(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct axp20x_batt_ps *axp20x_batt = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + return axp20x_set_voltage_min_design(axp20x_batt, val->intval); + + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + return axp20x_battery_set_max_voltage(axp20x_batt, val->intval); + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + return axp20x_set_constant_charge_current(axp20x_batt, + val->intval); + + default: + return -EINVAL; + } +} + +static enum power_supply_property axp20x_battery_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_CAPACITY, +}; + +static int axp20x_battery_prop_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN || + psp == POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN || + psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT; +} + +static const struct power_supply_desc axp20x_batt_ps_desc = { + .name = "axp20x-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = axp20x_battery_props, + .num_properties = ARRAY_SIZE(axp20x_battery_props), + .property_is_writeable = axp20x_battery_prop_writeable, + .get_property = axp20x_battery_get_prop, + .set_property = axp20x_battery_set_prop, +}; + +static const struct of_device_id axp20x_battery_ps_id[] = { + { + .compatible = "x-powers,axp209-battery-power-supply", + .data = (void *)AXP209_ID, + }, { + .compatible = "x-powers,axp221-battery-power-supply", + .data = (void *)AXP221_ID, + }, { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, axp20x_battery_ps_id); + +static int axp20x_power_probe(struct platform_device *pdev) +{ + struct axp20x_batt_ps *axp20x_batt; + struct power_supply_config psy_cfg = {}; + + if (!of_device_is_available(pdev->dev.of_node)) + return -ENODEV; + + axp20x_batt = devm_kzalloc(&pdev->dev, sizeof(*axp20x_batt), + GFP_KERNEL); + if (!axp20x_batt) + return -ENOMEM; + + axp20x_batt->dev = &pdev->dev; + + axp20x_batt->batt_v = devm_iio_channel_get(&pdev->dev, "batt_v"); + if (IS_ERR(axp20x_batt->batt_v)) { + if (PTR_ERR(axp20x_batt->batt_v) == -ENODEV) + return -EPROBE_DEFER; + return PTR_ERR(axp20x_batt->batt_v); + } + + axp20x_batt->batt_chrg_i = devm_iio_channel_get(&pdev->dev, + "batt_chrg_i"); + if (IS_ERR(axp20x_batt->batt_chrg_i)) { + if (PTR_ERR(axp20x_batt->batt_chrg_i) == -ENODEV) + return -EPROBE_DEFER; + return PTR_ERR(axp20x_batt->batt_chrg_i); + } + + axp20x_batt->batt_dischrg_i = devm_iio_channel_get(&pdev->dev, + "batt_dischrg_i"); + if (IS_ERR(axp20x_batt->batt_dischrg_i)) { + if (PTR_ERR(axp20x_batt->batt_dischrg_i) == -ENODEV) + return -EPROBE_DEFER; + return PTR_ERR(axp20x_batt->batt_dischrg_i); + } + + axp20x_batt->regmap = dev_get_regmap(pdev->dev.parent, NULL); + platform_set_drvdata(pdev, axp20x_batt); + + psy_cfg.drv_data = axp20x_batt; + psy_cfg.of_node = pdev->dev.of_node; + + axp20x_batt->axp_id = (uintptr_t)of_device_get_match_data(&pdev->dev); + + axp20x_batt->batt = devm_power_supply_register(&pdev->dev, + &axp20x_batt_ps_desc, + &psy_cfg); + if (IS_ERR(axp20x_batt->batt)) { + dev_err(&pdev->dev, "failed to register power supply: %ld\n", + PTR_ERR(axp20x_batt->batt)); + return PTR_ERR(axp20x_batt->batt); + } + + return 0; +} + +static struct platform_driver axp20x_batt_driver = { + .probe = axp20x_power_probe, + .driver = { + .name = "axp20x-battery-power-supply", + .of_match_table = axp20x_battery_ps_id, + }, +}; + +module_platform_driver(axp20x_batt_driver); + +MODULE_DESCRIPTION("Battery power supply driver for AXP20X and AXP22X PMICs"); +MODULE_AUTHOR("Quentin Schulz "); +MODULE_LICENSE("GPL"); From bb8fe8ea0067083e0452d5c67a4ab70ad72cc52f Mon Sep 17 00:00:00 2001 From: Ryosuke Saito Date: Fri, 21 Apr 2017 12:13:17 +0900 Subject: [PATCH 08/34] power: supply: sbs-battery: fix the sbs interrupt request Since we use the default primary handler for the irq, IRQF_ONESHOT must be set. Otherwise the request fails and the following errors are displayed: genirq: Threaded irq requested with handler=NULL and !ONESHOT for irq 129 sbs-battery 0-000b: Failed to request irq: -22 Signed-off-by: Ryosuke Saito Signed-off-by: Sebastian Reichel --- drivers/power/supply/sbs-battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index 8bb2eb38eb1c..e07d570f504f 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -820,7 +820,7 @@ static int sbs_probe(struct i2c_client *client, } rc = devm_request_threaded_irq(&client->dev, irq, NULL, sbs_irq, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, dev_name(&client->dev), chip); if (rc) { dev_warn(&client->dev, "Failed to request irq: %d\n", rc); From 756e142a4b3b25aac44eaa54ded125fce0e8fcc7 Mon Sep 17 00:00:00 2001 From: Rahul Bedarkar Date: Fri, 21 Apr 2017 16:37:57 +0530 Subject: [PATCH 09/34] power: supply: generic-adc-battery: use SIMPLE_DEV_PM_OPS helper macro Replace ifdefs with SIMPLE_DEV_PM_OPS helper macro. Signed-off-by: Rahul Bedarkar Signed-off-by: Sebastian Reichel --- drivers/power/supply/generic-adc-battery.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/drivers/power/supply/generic-adc-battery.c b/drivers/power/supply/generic-adc-battery.c index edb36bf781b0..37e523374fe0 100644 --- a/drivers/power/supply/generic-adc-battery.c +++ b/drivers/power/supply/generic-adc-battery.c @@ -383,8 +383,7 @@ static int gab_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int gab_suspend(struct device *dev) +static int __maybe_unused gab_suspend(struct device *dev) { struct gab *adc_bat = dev_get_drvdata(dev); @@ -393,7 +392,7 @@ static int gab_suspend(struct device *dev) return 0; } -static int gab_resume(struct device *dev) +static int __maybe_unused gab_resume(struct device *dev) { struct gab *adc_bat = dev_get_drvdata(dev); struct gab_platform_data *pdata = adc_bat->pdata; @@ -407,20 +406,12 @@ static int gab_resume(struct device *dev) return 0; } -static const struct dev_pm_ops gab_pm_ops = { - .suspend = gab_suspend, - .resume = gab_resume, -}; - -#define GAB_PM_OPS (&gab_pm_ops) -#else -#define GAB_PM_OPS (NULL) -#endif +static SIMPLE_DEV_PM_OPS(gab_pm_ops, gab_suspend, gab_resume); static struct platform_driver gab_driver = { .driver = { .name = "generic-adc-battery", - .pm = GAB_PM_OPS + .pm = &gab_pm_ops, }, .probe = gab_probe, .remove = gab_remove, From 8b20839988f1ed5e534b270f3776709b640dc7e0 Mon Sep 17 00:00:00 2001 From: Pan Bian Date: Mon, 24 Apr 2017 16:22:08 +0800 Subject: [PATCH 10/34] power: supply: isp1704: Fix unchecked return value of devm_kzalloc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Function devm_kzalloc() will return a NULL pointer. However, in function isp1704_charger_probe(), the return value of devm_kzalloc() is directly used without validation. This may result in a bad memory access bug. Fixes: 34a109610e2a ("isp1704_charger: Add DT support") Signed-off-by: Pan Bian Reviewed-by: Pali Rohár Signed-off-by: Sebastian Reichel --- drivers/power/supply/isp1704_charger.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/power/supply/isp1704_charger.c b/drivers/power/supply/isp1704_charger.c index 4cd6899b961e..95af5f305838 100644 --- a/drivers/power/supply/isp1704_charger.c +++ b/drivers/power/supply/isp1704_charger.c @@ -418,6 +418,10 @@ static int isp1704_charger_probe(struct platform_device *pdev) pdata = devm_kzalloc(&pdev->dev, sizeof(struct isp1704_charger_data), GFP_KERNEL); + if (!pdata) { + ret = -ENOMEM; + goto fail0; + } pdata->enable_gpio = gpio; dev_info(&pdev->dev, "init gpio %d\n", pdata->enable_gpio); From 6254a6a94489c4be717f757bec7d3a372cba1b6e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 27 Apr 2017 21:11:48 +0200 Subject: [PATCH 11/34] power: supply: axp20x_usb_power: add IIO dependency When CONFIG_IIO=m and the axp20x_usb_power driver is built-in, we get a link time error: drivers/power/built-in.o: In function `axp20x_usb_power_get_property': undefined reference to `iio_read_channel_processed' drivers/power/built-in.o: In function `axp20x_usb_power_probe': undefined reference to `devm_iio_channel_get' undefined reference to `devm_iio_channel_get' This adds the same dependency that we already have for the AC power driver to the USB power driver. For consistency, I'm also moving the two closer together in the Kconfig file. Fixes: 33863c938caa ("power: supply: axp20x_usb_power: use IIO channels when available") Signed-off-by: Arnd Bergmann Signed-off-by: Sebastian Reichel --- drivers/power/supply/Kconfig | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 4b718e90b3ca..86f40bf37c34 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -250,6 +250,14 @@ config BATTERY_AXP20X This driver can also be built as a module. If so, the module will be called axp20x_battery. +config AXP20X_POWER + tristate "AXP20x power supply driver" + depends on MFD_AXP20X + depends on IIO + help + This driver provides support for the power supply features of + AXP20x PMIC. + config AXP288_CHARGER tristate "X-Powers AXP288 Charger" depends on MFD_AXP20X && EXTCON_AXP288 @@ -553,11 +561,4 @@ config CHARGER_RT9455 help Say Y to enable support for Richtek RT9455 battery charger. -config AXP20X_POWER - tristate "AXP20x power supply driver" - depends on MFD_AXP20X - help - This driver provides support for the power supply features of - AXP20x PMIC. - endif # POWER_SUPPLY From 7f232af35620fe68746cb3da582062de965b286c Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 29 Apr 2017 22:56:49 +0100 Subject: [PATCH 12/34] power: supply: ab8500_charger: spelling: "prechage" -> "precharge" trivial fix to spelling mistake in dev_error message. Signed-off-by: Colin Ian King Signed-off-by: Sebastian Reichel --- drivers/power/supply/ab8500_charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c index 5cee9aa87aa3..4ebbcce45c48 100644 --- a/drivers/power/supply/ab8500_charger.c +++ b/drivers/power/supply/ab8500_charger.c @@ -3238,7 +3238,7 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di) BUS_PP_PRECHG_CURRENT_MASK, 0); if (ret) { dev_err(di->dev, - "failed to setup usb power path prechage current\n"); + "failed to setup usb power path precharge current\n"); goto out; } } From c67c06939ee4a24fcad1235dd9632153533fdd4d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 14 Apr 2017 20:32:47 +0200 Subject: [PATCH 13/34] power: supply: max17042_battery: Use sign_extend32 instead of DIY code Use sign_extend32 to sign-extend variables where necessary instead of DIY code. Signed-off-by: Hans de Goede Reviewed-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index da7a75f82489..790dfa9d0a2e 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -106,13 +106,7 @@ static int max17042_get_temperature(struct max17042_chip *chip, int *temp) if (ret < 0) return ret; - *temp = data; - /* The value is signed. */ - if (*temp & 0x8000) { - *temp = (0x7fff & ~*temp) + 1; - *temp *= -1; - } - + *temp = sign_extend32(data, 15); /* The value is converted into deci-centigrade scale */ /* Units of LSB = 1 / 256 degree Celsius */ *temp = *temp * 10 / 256; @@ -302,13 +296,7 @@ static int max17042_get_property(struct power_supply *psy, if (ret < 0) return ret; - val->intval = data; - if (val->intval & 0x8000) { - /* Negative */ - val->intval = ~val->intval & 0x7fff; - val->intval++; - val->intval *= -1; - } + val->intval = sign_extend32(data, 15); val->intval *= 1562500 / chip->pdata->r_sns; } else { return -EINVAL; @@ -320,13 +308,7 @@ static int max17042_get_property(struct power_supply *psy, if (ret < 0) return ret; - val->intval = data; - if (val->intval & 0x8000) { - /* Negative */ - val->intval = ~val->intval & 0x7fff; - val->intval++; - val->intval *= -1; - } + val->intval = sign_extend32(data, 15); val->intval *= 1562500 / chip->pdata->r_sns; } else { return -EINVAL; From 2814913c3136b41084a896c90062bd9b87672dff Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 14 Apr 2017 20:32:48 +0200 Subject: [PATCH 14/34] power: supply: max17047_battery: The temp alert values are 8-bit 2's complement The temp alert values are 8-bit 2's complement, so sign-extend them before reporting them back to the caller. Signed-off-by: Hans de Goede Reviewed-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 790dfa9d0a2e..a51b2965cd5c 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -270,14 +270,14 @@ static int max17042_get_property(struct power_supply *psy, if (ret < 0) return ret; /* LSB is Alert Minimum. In deci-centigrade */ - val->intval = (data & 0xff) * 10; + val->intval = sign_extend32(data & 0xff, 7) * 10; break; case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: ret = regmap_read(map, MAX17042_TALRT_Th, &data); if (ret < 0) return ret; /* MSB is Alert Maximum. In deci-centigrade */ - val->intval = (data >> 8) * 10; + val->intval = sign_extend32(data >> 8, 7) * 10; break; case POWER_SUPPLY_PROP_TEMP_MIN: val->intval = chip->pdata->temp_min; From 917362135b8a5c0680acf08807e9fc6179eb6c79 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 14 Apr 2017 20:32:49 +0200 Subject: [PATCH 15/34] power: supply: max17042_battery: Add default platform_data fallback data Some x86 machines use a max17047 fuel-gauge and x86 might be missing platform_data if not provided by SFI. This commit adds default platform_data as fallback option so that the driver can work on boards where no platform_data is provided. Since not all boards have a thermistor hooked up, set temp_min to 0 and change the health checks from temp <= temp_min to temp < temp_min to not trigger on such boards (where temp reads 0). Signed-off-by: Hans de Goede Reviewed-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 60 ++++++++++++++++++++++--- include/linux/power/max17042_battery.h | 6 ++- 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index a51b2965cd5c..f0ff6e880ff2 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -150,12 +150,12 @@ static int max17042_get_battery_health(struct max17042_chip *chip, int *health) if (ret < 0) goto health_error; - if (temp <= chip->pdata->temp_min) { + if (temp < chip->pdata->temp_min) { *health = POWER_SUPPLY_HEALTH_COLD; goto out; } - if (temp >= chip->pdata->temp_max) { + if (temp > chip->pdata->temp_max) { *health = POWER_SUPPLY_HEALTH_OVERHEAT; goto out; } @@ -772,8 +772,9 @@ static void max17042_init_worker(struct work_struct *work) #ifdef CONFIG_OF static struct max17042_platform_data * -max17042_get_pdata(struct device *dev) +max17042_get_pdata(struct max17042_chip *chip) { + struct device *dev = &chip->client->dev; struct device_node *np = dev->of_node; u32 prop; struct max17042_platform_data *pdata; @@ -806,10 +807,55 @@ max17042_get_pdata(struct device *dev) return pdata; } #else +static struct max17042_reg_data max17047_default_pdata_init_regs[] = { + /* + * Some firmwares do not set FullSOCThr, Enable End-of-Charge Detection + * when the voltage FG reports 95%, as recommended in the datasheet. + */ + { MAX17047_FullSOCThr, MAX17042_BATTERY_FULL << 8 }, +}; + static struct max17042_platform_data * -max17042_get_pdata(struct device *dev) +max17042_get_pdata(struct max17042_chip *chip) { - return dev->platform_data; + struct device *dev = &chip->client->dev; + struct max17042_platform_data *pdata; + int ret, misc_cfg; + + if (dev->platform_data) + return dev->platform_data; + + /* + * The MAX17047 gets used on x86 where we might not have pdata, assume + * the firmware will already have initialized the fuel-gauge and provide + * default values for the non init bits to make things work. + */ + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return pdata; + + if (chip->chip_type != MAXIM_DEVICE_TYPE_MAX17042) { + pdata->init_data = max17047_default_pdata_init_regs; + pdata->num_init_data = + ARRAY_SIZE(max17047_default_pdata_init_regs); + } + + ret = regmap_read(chip->regmap, MAX17042_MiscCFG, &misc_cfg); + if (ret < 0) + return NULL; + + /* If bits 0-1 are set to 3 then only Voltage readings are used */ + if ((misc_cfg & 0x3) == 0x3) + pdata->enable_current_sense = false; + else + pdata->enable_current_sense = true; + + pdata->vmin = MAX17042_DEFAULT_VMIN; + pdata->vmax = MAX17042_DEFAULT_VMAX; + pdata->temp_min = MAX17042_DEFAULT_TEMP_MIN; + pdata->temp_max = MAX17042_DEFAULT_TEMP_MAX; + + return pdata; } #endif @@ -858,20 +904,20 @@ static int max17042_probe(struct i2c_client *client, return -ENOMEM; chip->client = client; + chip->chip_type = id->driver_data; chip->regmap = devm_regmap_init_i2c(client, &max17042_regmap_config); if (IS_ERR(chip->regmap)) { dev_err(&client->dev, "Failed to initialize regmap\n"); return -EINVAL; } - chip->pdata = max17042_get_pdata(&client->dev); + chip->pdata = max17042_get_pdata(chip); if (!chip->pdata) { dev_err(&client->dev, "no platform data provided\n"); return -EINVAL; } i2c_set_clientdata(client, chip); - chip->chip_type = id->driver_data; psy_cfg.drv_data = chip; /* When current is not measured, diff --git a/include/linux/power/max17042_battery.h b/include/linux/power/max17042_battery.h index 522757ac9cd4..3489fb0f9099 100644 --- a/include/linux/power/max17042_battery.h +++ b/include/linux/power/max17042_battery.h @@ -24,8 +24,12 @@ #define __MAX17042_BATTERY_H_ #define MAX17042_STATUS_BattAbsent (1 << 3) -#define MAX17042_BATTERY_FULL (100) +#define MAX17042_BATTERY_FULL (95) /* Recommend. FullSOCThr value */ #define MAX17042_DEFAULT_SNS_RESISTOR (10000) +#define MAX17042_DEFAULT_VMIN (3000) +#define MAX17042_DEFAULT_VMAX (4500) /* LiHV cell max */ +#define MAX17042_DEFAULT_TEMP_MIN (0) /* For sys without temp sensor */ +#define MAX17042_DEFAULT_TEMP_MAX (700) /* 70 degrees Celcius */ #define MAX17042_CHARACTERIZATION_DATA_SIZE 48 From a9df22c00d7c2c9c2944c62f1b819de6c214660f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 14 Apr 2017 20:32:51 +0200 Subject: [PATCH 16/34] power: supply: max17042_battery: Add support for the STATUS property Userspace prefers the driver having a status property over having to guess itself. Specifically this will properly make the GNOME3 UI (and likely others) properly show discharging / charging / full status, instead of always showing discharging as status. Note that in the case there is no charger driver supplying the max17042, then a status of unknown will get returned. At least upower treats this the same as not having a status attribute, so in this case nothing changes from a userspace pov. Signed-off-by: Hans de Goede Reviewed-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 46 +++++++++++++++++++++++++ include/linux/power/max17042_battery.h | 3 ++ 2 files changed, 49 insertions(+) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index f0ff6e880ff2..62efe7eeb3f8 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -76,6 +76,7 @@ struct max17042_chip { }; static enum power_supply_property max17042_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_VOLTAGE_MAX, @@ -113,6 +114,46 @@ static int max17042_get_temperature(struct max17042_chip *chip, int *temp) return 0; } +static int max17042_get_status(struct max17042_chip *chip, int *status) +{ + int ret, charge_full, charge_now; + + ret = power_supply_am_i_supplied(chip->battery); + if (ret < 0) { + *status = POWER_SUPPLY_STATUS_UNKNOWN; + return 0; + } + if (ret == 0) { + *status = POWER_SUPPLY_STATUS_DISCHARGING; + return 0; + } + + /* + * The MAX170xx has builtin end-of-charge detection and will update + * FullCAP to match RepCap when it detects end of charging. + * + * When this cycle the battery gets charged to a higher (calculated) + * capacity then the previous cycle then FullCAP will get updated + * contineously once end-of-charge detection kicks in, so allow the + * 2 to differ a bit. + */ + + ret = regmap_read(chip->regmap, MAX17042_FullCAP, &charge_full); + if (ret < 0) + return ret; + + ret = regmap_read(chip->regmap, MAX17042_RepCap, &charge_now); + if (ret < 0) + return ret; + + if ((charge_full - charge_now) <= MAX17042_FULL_THRESHOLD) + *status = POWER_SUPPLY_STATUS_FULL; + else + *status = POWER_SUPPLY_STATUS_CHARGING; + + return 0; +} + static int max17042_get_battery_health(struct max17042_chip *chip, int *health) { int temp, vavg, vbatt, ret; @@ -182,6 +223,11 @@ static int max17042_get_property(struct power_supply *psy, return -EAGAIN; switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + ret = max17042_get_status(chip, &val->intval); + if (ret < 0) + return ret; + break; case POWER_SUPPLY_PROP_PRESENT: ret = regmap_read(map, MAX17042_STATUS, &data); if (ret < 0) diff --git a/include/linux/power/max17042_battery.h b/include/linux/power/max17042_battery.h index 3489fb0f9099..a7ed29baf44a 100644 --- a/include/linux/power/max17042_battery.h +++ b/include/linux/power/max17042_battery.h @@ -31,6 +31,9 @@ #define MAX17042_DEFAULT_TEMP_MIN (0) /* For sys without temp sensor */ #define MAX17042_DEFAULT_TEMP_MAX (700) /* 70 degrees Celcius */ +/* Consider RepCap which is less then 10 units below FullCAP full */ +#define MAX17042_FULL_THRESHOLD 10 + #define MAX17042_CHARACTERIZATION_DATA_SIZE 48 enum max17042_register { From dcdddda8fdd23048f2eaf54a18a33e9f225d8fb6 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 14 Apr 2017 20:32:52 +0200 Subject: [PATCH 17/34] power: supply: max17042_battery: Add external_power_changed callback If our supplier changes status, chances are we've changed status too, let any listeners know about this. Signed-off-by: Hans de Goede Reviewed-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 62efe7eeb3f8..d355ee54b06d 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -429,6 +429,11 @@ static int max17042_property_is_writeable(struct power_supply *psy, return ret; } +static void max17042_external_power_changed(struct power_supply *psy) +{ + power_supply_changed(psy); +} + static int max17042_write_verify_reg(struct regmap *map, u8 reg, u32 value) { int retries = 8; @@ -917,6 +922,7 @@ static const struct power_supply_desc max17042_psy_desc = { .get_property = max17042_get_property, .set_property = max17042_set_property, .property_is_writeable = max17042_property_is_writeable, + .external_power_changed = max17042_external_power_changed, .properties = max17042_battery_props, .num_properties = ARRAY_SIZE(max17042_battery_props), }; From ef7fcdaebfce56bd7f1ac3b916b5d587a3666f10 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 14 Apr 2017 20:32:53 +0200 Subject: [PATCH 18/34] power: supply: max17042_battery: Add support for the TECHNOLOGY attribute The max17042 is intended for Li-Ion or Li-Po batteries, add a TECHNOLOGY attribute to reflect this. Note this is hardcoded to Li-Ion as there is no way to tell the difference, and Lithium-Ion Polymer batteries are a sub-family of Lithium-Ion so Li-Ion technically is correct for both. Using Li-Ion for both is already done by many drivers and is much better then not providing any technology info at all. Suggested-by: Wolfgang Wiedmeyer Signed-off-by: Hans de Goede Reviewed-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index d355ee54b06d..254550038455 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -78,6 +78,7 @@ struct max17042_chip { static enum power_supply_property max17042_battery_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_VOLTAGE_MAX, POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, @@ -238,6 +239,9 @@ static int max17042_get_property(struct power_supply *psy, else val->intval = 1; break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; case POWER_SUPPLY_PROP_CYCLE_COUNT: ret = regmap_read(map, MAX17042_Cycles, &data); if (ret < 0) From 7bfc9397ff9690fbb0da44e1a0aeab953cb73b77 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 14 Apr 2017 20:32:54 +0200 Subject: [PATCH 19/34] power: supply: max17042_battery: Add support for the VOLT_MIN property The info is there, so lets export it, like we already do for VOLT_MAX. Signed-off-by: Hans de Goede Reviewed-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 254550038455..0e94349b3855 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -81,6 +81,7 @@ static enum power_supply_property max17042_battery_props[] = { POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_VOLTAGE_MIN, POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_VOLTAGE_AVG, @@ -257,6 +258,13 @@ static int max17042_get_property(struct power_supply *psy, val->intval = data >> 8; val->intval *= 20000; /* Units of LSB = 20mV */ break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN: + ret = regmap_read(map, MAX17042_MinMaxVolt, &data); + if (ret < 0) + return ret; + + val->intval = (data & 0xff) * 20000; /* Units of 20mV */ + break; case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) ret = regmap_read(map, MAX17042_V_empty, &data); From d7d15fc67791c0e255f36345d02441c1716c2a1f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 14 Apr 2017 20:32:55 +0200 Subject: [PATCH 20/34] power: supply: max17042_battery: mAh readings depend on r_sns value The PROP_CHARGE_FULL code was hardcoded for the default sense resistor of 0.010 Ohm, make it use r_sns which contains the actual sense resistor value in micro-Ohms instead. Signed-off-by: Hans de Goede Reviewed-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 0e94349b3855..37e987f55833 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -220,6 +220,7 @@ static int max17042_get_property(struct power_supply *psy, struct regmap *map = chip->regmap; int ret; u32 data; + u64 data64; if (!chip->init_complete) return -EAGAIN; @@ -309,7 +310,9 @@ static int max17042_get_property(struct power_supply *psy, if (ret < 0) return ret; - val->intval = data * 1000 / 2; + data64 = data * 5000000ll; + do_div(data64, chip->pdata->r_sns); + val->intval = data64; break; case POWER_SUPPLY_PROP_CHARGE_COUNTER: ret = regmap_read(map, MAX17042_QH, &data); From 2e015412a320e49aab4beaeb6262e93fdc0a42f2 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 14 Apr 2017 20:32:56 +0200 Subject: [PATCH 21/34] power: supply: max17042_battery: Add support for the CHARGE_FULL_DESIGN property The info is there, lets export it as a property. Signed-off-by: Hans de Goede Reviewed-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 37e987f55833..3838f09e013c 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -87,6 +87,7 @@ static enum power_supply_property max17042_battery_props[] = { POWER_SUPPLY_PROP_VOLTAGE_AVG, POWER_SUPPLY_PROP_VOLTAGE_OCV, POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_COUNTER, POWER_SUPPLY_PROP_TEMP, @@ -305,6 +306,15 @@ static int max17042_get_property(struct power_supply *psy, val->intval = data >> 8; break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + ret = regmap_read(map, MAX17042_DesignCap, &data); + if (ret < 0) + return ret; + + data64 = data * 5000000ll; + do_div(data64, chip->pdata->r_sns); + val->intval = data64; + break; case POWER_SUPPLY_PROP_CHARGE_FULL: ret = regmap_read(map, MAX17042_FullCAP, &data); if (ret < 0) From 6d6b61eafca94146cedf7709974a7423df16a6f1 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 14 Apr 2017 20:32:57 +0200 Subject: [PATCH 22/34] power: supply: max17042_battery: Add support for the CHARGE_NOW property At least upower prefers the more precise charge_now sysfs value over capacity and the max17042 has the info, so lets export it. Signed-off-by: Hans de Goede Reviewed-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 3838f09e013c..3e19797fec4b 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -89,6 +89,7 @@ static enum power_supply_property max17042_battery_props[] = { POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CHARGE_COUNTER, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TEMP_ALERT_MIN, @@ -320,6 +321,15 @@ static int max17042_get_property(struct power_supply *psy, if (ret < 0) return ret; + data64 = data * 5000000ll; + do_div(data64, chip->pdata->r_sns); + val->intval = data64; + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + ret = regmap_read(map, MAX17042_RepCap, &data); + if (ret < 0) + return ret; + data64 = data * 5000000ll; do_div(data64, chip->pdata->r_sns); val->intval = data64; From adb69a3c4524693a32ba72533ee6d7a52e54989e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 14 Apr 2017 20:32:58 +0200 Subject: [PATCH 23/34] power: supply: max17042_battery: Add support for the SCOPE property Add support for the SCOPE property, always return SCOPE_SYSTEM, as the max170xx is used for the main battery on all known systems with a max170xx. Signed-off-by: Hans de Goede Reviewed-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 3e19797fec4b..aecaaa2b0586 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -97,6 +97,7 @@ static enum power_supply_property max17042_battery_props[] = { POWER_SUPPLY_PROP_TEMP_MIN, POWER_SUPPLY_PROP_TEMP_MAX, POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_SCOPE, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CURRENT_AVG, }; @@ -371,6 +372,9 @@ static int max17042_get_property(struct power_supply *psy, if (ret < 0) return ret; break; + case POWER_SUPPLY_PROP_SCOPE: + val->intval = POWER_SUPPLY_SCOPE_SYSTEM; + break; case POWER_SUPPLY_PROP_CURRENT_NOW: if (chip->pdata->enable_current_sense) { ret = regmap_read(map, MAX17042_Current, &data); From 633e8799ddc09431be2744c4a1efdbda13af2b0b Mon Sep 17 00:00:00 2001 From: Michael Trimarchi Date: Tue, 25 Apr 2017 15:18:05 +0200 Subject: [PATCH 24/34] power: supply: pda_power: move from timer to delayed_work This changed is needed to avoid locking problem during boot as shown: <5>[ 8.824096] Registering SWP/SWPB emulation handler <6>[ 8.977294] clock: disabling unused clocks to save power <3>[ 9.108154] BUG: sleeping function called from invalid context at kernel_albert/kernel/mutex.c:269 <3>[ 9.122894] in_atomic(): 1, irqs_disabled(): 0, pid: 1, name: swapper/0 <4>[ 9.130249] 3 locks held by swapper/0/1: <4>[ 9.134613] #0: (&__lockdep_no_validate__){......}, at: [] __driver_attach+0x58/0xa8 <4>[ 9.144500] #1: (&__lockdep_no_validate__){......}, at: [] __driver_attach+0x68/0xa8 <4>[ 9.154357] #2: (&polling_timer){......}, at: [] run_timer_softirq+0x108/0x3ec <4>[ 9.163726] Backtrace: <4>[ 9.166473] [] (dump_backtrace+0x0/0x114) from [] (dump_stack+0x20/0x24) <4>[ 9.175811] r6:00203230 r5:0000010d r4:d782e000 r3:60000113 <4>[ 9.182250] [] (dump_stack+0x0/0x24) from [] (__might_sleep+0x10c/0x128) <4>[ 9.191650] [] (__might_sleep+0x0/0x128) from [] (mutex_lock_nested+0x34/0x36c) <4>[ 9.201660] r5:c02d5350 r4:d79a0c64 <4>[ 9.205688] [] (mutex_lock_nested+0x0/0x36c) from [] (regulator_set_current_limit+0x30/0x118) <4>[ 9.217071] [] (regulator_set_current_limit+0x0/0x118) from [] (update_charger+0x84/0xc4) <4>[ 9.228027] r7:d782fb20 r6:00000101 r5:c1767e94 r4:00000000 <4>[ 9.234436] [] (update_charger+0x0/0xc4) from [] (psy_changed+0x20/0x48) <4>[ 9.243804] r5:d782e000 r4:c1767e94 <4>[ 9.247802] [] (psy_changed+0x0/0x48) from [] (polling_timer_func+0x84/0xb8) <4>[ 9.257537] r4:c1767e94 r3:00000002 <4>[ 9.261566] [] (polling_timer_func+0x0/0xb8) from [] (run_timer_softirq+0x17c/0x3ec) <4>[ 9.272033] r4:c1767eb0 r3:00000000 <4>[ 9.276062] [] (run_timer_softirq+0x0/0x3ec) from [] (__do_softirq+0xf0/0x298) <4>[ 9.286010] [] (__do_softirq+0x0/0x298) from [] (irq_exit+0x98/0xa0) <4>[ 9.295013] [] (irq_exit+0x0/0xa0) from [] (handle_IRQ+0x60/0xc0) <4>[ 9.303680] r4:c1194e98 r3:c00bc778 <4>[ 9.307708] [] (handle_IRQ+0x0/0xc0) from [] (gic_handle_irq+0x34/0x68) <4>[ 9.316955] r8:000ac383 r7:d782fc3c r6:d782fc08 r5:c11936c4 r4:e0802100 <4>[ 9.324310] r3:c026ba48 <4>[ 9.327301] [] (gic_handle_irq+0x0/0x68) from [] (__irq_svc+0x40/0x74) <4>[ 9.336456] Exception stack(0xd782fc08 to 0xd782fc50) <4>[ 9.342041] fc00: d6e30e6c ac383627 00000000 ac383417 ea19c000 ea200000 <4>[ 9.351104] fc20: beffffff 00000667 000ac383 d6e30670 d6e3066c d782fc94 d782fbe8 d782fc50 <4>[ 9.360168] fc40: c026ba48 c001d1f0 00000113 ffffffff Fixes: b2998049cfae ("[BATTERY] pda_power platform driver") Signed-off-by: Michael Trimarchi Signed-off-by: Anthony Brandon Signed-off-by: Sebastian Reichel --- drivers/power/supply/pda_power.c | 49 ++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/drivers/power/supply/pda_power.c b/drivers/power/supply/pda_power.c index dfe1ee89f7c7..922a86787c5c 100644 --- a/drivers/power/supply/pda_power.c +++ b/drivers/power/supply/pda_power.c @@ -30,9 +30,9 @@ static inline unsigned int get_irq_flags(struct resource *res) static struct device *dev; static struct pda_power_pdata *pdata; static struct resource *ac_irq, *usb_irq; -static struct timer_list charger_timer; -static struct timer_list supply_timer; -static struct timer_list polling_timer; +static struct delayed_work charger_work; +static struct delayed_work polling_work; +static struct delayed_work supply_work; static int polling; static struct power_supply *pda_psy_ac, *pda_psy_usb; @@ -140,7 +140,7 @@ static void update_charger(void) } } -static void supply_timer_func(unsigned long unused) +static void supply_work_func(struct work_struct *work) { if (ac_status == PDA_PSY_TO_CHANGE) { ac_status = new_ac_status; @@ -161,11 +161,12 @@ static void psy_changed(void) * Okay, charger set. Now wait a bit before notifying supplicants, * charge power should stabilize. */ - mod_timer(&supply_timer, - jiffies + msecs_to_jiffies(pdata->wait_for_charger)); + cancel_delayed_work(&supply_work); + schedule_delayed_work(&supply_work, + msecs_to_jiffies(pdata->wait_for_charger)); } -static void charger_timer_func(unsigned long unused) +static void charger_work_func(struct work_struct *work) { update_status(); psy_changed(); @@ -184,13 +185,14 @@ static irqreturn_t power_changed_isr(int irq, void *power_supply) * Wait a bit before reading ac/usb line status and setting charger, * because ac/usb status readings may lag from irq. */ - mod_timer(&charger_timer, - jiffies + msecs_to_jiffies(pdata->wait_for_status)); + cancel_delayed_work(&charger_work); + schedule_delayed_work(&charger_work, + msecs_to_jiffies(pdata->wait_for_status)); return IRQ_HANDLED; } -static void polling_timer_func(unsigned long unused) +static void polling_work_func(struct work_struct *work) { int changed = 0; @@ -211,8 +213,9 @@ static void polling_timer_func(unsigned long unused) if (changed) psy_changed(); - mod_timer(&polling_timer, - jiffies + msecs_to_jiffies(pdata->polling_interval)); + cancel_delayed_work(&polling_work); + schedule_delayed_work(&polling_work, + msecs_to_jiffies(pdata->polling_interval)); } #if IS_ENABLED(CONFIG_USB_PHY) @@ -250,8 +253,9 @@ static int otg_handle_notification(struct notifier_block *nb, * Wait a bit before reading ac/usb line status and setting charger, * because ac/usb status readings may lag from irq. */ - mod_timer(&charger_timer, - jiffies + msecs_to_jiffies(pdata->wait_for_status)); + cancel_delayed_work(&charger_work); + schedule_delayed_work(&charger_work, + msecs_to_jiffies(pdata->wait_for_status)); return NOTIFY_OK; } @@ -300,8 +304,8 @@ static int pda_power_probe(struct platform_device *pdev) if (!pdata->ac_max_uA) pdata->ac_max_uA = 500000; - setup_timer(&charger_timer, charger_timer_func, 0); - setup_timer(&supply_timer, supply_timer_func, 0); + INIT_DELAYED_WORK(&charger_work, charger_work_func); + INIT_DELAYED_WORK(&supply_work, supply_work_func); ac_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ac"); usb_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "usb"); @@ -385,9 +389,10 @@ static int pda_power_probe(struct platform_device *pdev) if (polling) { dev_dbg(dev, "will poll for status\n"); - setup_timer(&polling_timer, polling_timer_func, 0); - mod_timer(&polling_timer, - jiffies + msecs_to_jiffies(pdata->polling_interval)); + INIT_DELAYED_WORK(&polling_work, polling_work_func); + cancel_delayed_work(&polling_work); + schedule_delayed_work(&polling_work, + msecs_to_jiffies(pdata->polling_interval)); } if (ac_irq || usb_irq) @@ -433,9 +438,9 @@ static int pda_power_remove(struct platform_device *pdev) free_irq(ac_irq->start, pda_psy_ac); if (polling) - del_timer_sync(&polling_timer); - del_timer_sync(&charger_timer); - del_timer_sync(&supply_timer); + cancel_delayed_work_sync(&polling_work); + cancel_delayed_work_sync(&charger_work); + cancel_delayed_work_sync(&supply_work); if (pdata->is_usb_online) power_supply_unregister(pda_psy_usb); From 4df2cce4722d35eb35bb9371e7c9a600a84558f9 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Tue, 25 Apr 2017 17:09:04 +0200 Subject: [PATCH 25/34] power: supply: sbs-battery: Don't ignore the first external power change A mechanism to ignore the first external power change notification was put in place years ago to ignore the power_supply_register notification. However, this doesn't apply to the current situation anymore, as the first notification is always the result of a legitimate power change. This removes this deprecated mechanism, which puts back the driver's state machine to a sane state (an ignored first notification previously caused a charging/discharging status inversion). Signed-off-by: Paul Kocialkowski Signed-off-by: Sebastian Reichel --- drivers/power/supply/sbs-battery.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index e07d570f504f..7fd0f308189f 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -171,7 +171,6 @@ struct sbs_info { u32 i2c_retry_count; u32 poll_retry_count; struct delayed_work work; - int ignore_changes; }; static char model_name[I2C_SMBUS_BLOCK_MAX + 1]; @@ -694,11 +693,6 @@ static void sbs_external_power_changed(struct power_supply *psy) { struct sbs_info *chip = power_supply_get_drvdata(psy); - if (chip->ignore_changes > 0) { - chip->ignore_changes--; - return; - } - /* cancel outstanding work */ cancel_delayed_work_sync(&chip->work); @@ -775,10 +769,6 @@ static int sbs_probe(struct i2c_client *client, chip->enable_detection = false; psy_cfg.of_node = client->dev.of_node; psy_cfg.drv_data = chip; - /* ignore first notification of external change, it is generated - * from the power_supply_register call back - */ - chip->ignore_changes = 1; chip->last_state = POWER_SUPPLY_STATUS_UNKNOWN; /* use pdata if available, fall back to DT properties, From 7f93e1fa032bb5ee19b868b9649bc98c82553003 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Tue, 25 Apr 2017 17:09:05 +0200 Subject: [PATCH 26/34] power: supply: sbs-battery: Correct supply status with current draw The status reported directly by the battery controller is not always reliable and should be corrected based on the current draw information. This implements such a correction with a dedicated function, called where the supply status is retrieved. Signed-off-by: Paul Kocialkowski Signed-off-by: Sebastian Reichel --- drivers/power/supply/sbs-battery.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index 7fd0f308189f..171c701a2d49 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -295,6 +295,31 @@ static int sbs_write_word_data(struct i2c_client *client, u8 address, return 0; } +static int sbs_status_correct(struct i2c_client *client, int *intval) +{ + int ret; + + ret = sbs_read_word_data(client, sbs_data[REG_CURRENT].addr); + if (ret < 0) + return ret; + + ret = (s16)ret; + + /* Not drawing current means full (cannot be not charging) */ + if (ret == 0) + *intval = POWER_SUPPLY_STATUS_FULL; + + if (*intval == POWER_SUPPLY_STATUS_FULL) { + /* Drawing or providing current when full */ + if (ret > 0) + *intval = POWER_SUPPLY_STATUS_CHARGING; + else if (ret < 0) + *intval = POWER_SUPPLY_STATUS_DISCHARGING; + } + + return 0; +} + static int sbs_get_battery_presence_and_health( struct i2c_client *client, enum power_supply_property psp, union power_supply_propval *val) @@ -401,6 +426,8 @@ static int sbs_get_battery_property(struct i2c_client *client, else val->intval = POWER_SUPPLY_STATUS_CHARGING; + sbs_status_correct(client, &val->intval); + if (chip->poll_time == 0) chip->last_state = val->intval; else if (chip->last_state != val->intval) { @@ -721,6 +748,8 @@ static void sbs_delayed_work(struct work_struct *work) else ret = POWER_SUPPLY_STATUS_CHARGING; + sbs_status_correct(chip->client, &ret); + if (chip->last_state != ret) { chip->poll_time = 0; power_supply_changed(chip->power_supply); From e9574c14fd11ba254633342ff8ea58664c2938e1 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Thu, 27 Apr 2017 17:30:07 +0200 Subject: [PATCH 27/34] power: supply: avoid unused twl4030-madc.h Avoid inclusion of unused twl4030-madc.h. This will allow twl4030-madc.h to be merged into the iio driver. Signed-off-by: Sebastian Reichel --- drivers/power/supply/rx51_battery.c | 1 - drivers/power/supply/twl4030_madc_battery.c | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/power/supply/rx51_battery.c b/drivers/power/supply/rx51_battery.c index af9383d23d12..5654708b1279 100644 --- a/drivers/power/supply/rx51_battery.c +++ b/drivers/power/supply/rx51_battery.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include diff --git a/drivers/power/supply/twl4030_madc_battery.c b/drivers/power/supply/twl4030_madc_battery.c index f5817e422d64..4d41acb98576 100644 --- a/drivers/power/supply/twl4030_madc_battery.c +++ b/drivers/power/supply/twl4030_madc_battery.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include From cad87f097ca2d56093145c4becd487f5c186a612 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Mon, 1 May 2017 13:55:25 +0200 Subject: [PATCH 28/34] mailmap: add Sebastian Reichel Add two entries to map to my primary kernel address. Signed-off-by: Sebastian Reichel --- .mailmap | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.mailmap b/.mailmap index 67dc22ffc9a8..979c2c96636e 100644 --- a/.mailmap +++ b/.mailmap @@ -143,6 +143,8 @@ Santosh Shilimkar Santosh Shilimkar Sascha Hauer S.Çağlar Onur +Sebastian Reichel +Sebastian Reichel Shiraz Hashim Shuah Khan Shuah Khan From cda3b01368391b48728b7edbbac0ca873b514367 Mon Sep 17 00:00:00 2001 From: Phil Reid Date: Mon, 1 May 2017 16:49:58 +0800 Subject: [PATCH 29/34] power: supply: sbs-battery: Add alert callback To simplify the sbs-manager code and notification of battery removal use the i2c alert callback to notify the sbs-battery driver that an event has occurred. Signed-off-by: Phil Reid Signed-off-by: Sebastian Reichel --- drivers/power/supply/sbs-battery.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index 171c701a2d49..e3a114e60f1a 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -701,21 +701,30 @@ done: return 0; } -static irqreturn_t sbs_irq(int irq, void *devid) +static void sbs_supply_changed(struct sbs_info *chip) { - struct sbs_info *chip = devid; struct power_supply *battery = chip->power_supply; int ret; ret = gpiod_get_value_cansleep(chip->gpio_detect); if (ret < 0) - return ret; + return; chip->is_present = ret; power_supply_changed(battery); +} +static irqreturn_t sbs_irq(int irq, void *devid) +{ + sbs_supply_changed(devid); return IRQ_HANDLED; } +static void sbs_alert(struct i2c_client *client, enum i2c_alert_protocol prot, + unsigned int data) +{ + sbs_supply_changed(i2c_get_clientdata(client)); +} + static void sbs_external_power_changed(struct power_supply *psy) { struct sbs_info *chip = power_supply_get_drvdata(psy); @@ -936,6 +945,7 @@ MODULE_DEVICE_TABLE(of, sbs_dt_ids); static struct i2c_driver sbs_battery_driver = { .probe = sbs_probe, .remove = sbs_remove, + .alert = sbs_alert, .id_table = sbs_id, .driver = { .name = "sbs-battery", From 50730eb367147dc96394d25b3f55c33595663b6f Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Wed, 3 May 2017 11:33:57 +0100 Subject: [PATCH 30/34] power: supply: twl4030-charger: make twl4030_bci_property_is_writeable static The function twl4030_bci_property_is_writeable can be made static as it does not need to be in global scope. Signed-off-by: Colin Ian King Tested-by: "H. Nikolaus Schaller" Signed-off-by: Sebastian Reichel --- drivers/power/supply/twl4030_charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/supply/twl4030_charger.c b/drivers/power/supply/twl4030_charger.c index abd10f6861ae..2f82d0e9ec1b 100644 --- a/drivers/power/supply/twl4030_charger.c +++ b/drivers/power/supply/twl4030_charger.c @@ -943,7 +943,7 @@ static int twl4030_bci_set_property(struct power_supply *psy, return 0; } -int twl4030_bci_property_is_writeable(struct power_supply *psy, +static int twl4030_bci_property_is_writeable(struct power_supply *psy, enum power_supply_property psp) { switch (psp) { From 4f700a5285dc8f9dde4ad02542bc42fe9f97cfcf Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 3 May 2017 17:15:29 -0700 Subject: [PATCH 31/34] power: supply: cpcap-charger: Fix charger name I noticed we have a different name from what Android is using. Let's not break the /sys/class/power user space interface here and use the same naming as Android has. On Android we have the following for droid 4: $ ls /sys/class/power_supply/ ac battery usb So let's use the usb naming here for charger too. Fixes: 0c9888e3c192 ("power: supply: cpcap-charger: Add minimal CPCAP PMIC battery charger") Cc: Marcel Partap Cc: Michael Scott Signed-off-by: Tony Lindgren Signed-off-by: Sebastian Reichel --- drivers/power/supply/cpcap-charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/supply/cpcap-charger.c b/drivers/power/supply/cpcap-charger.c index 543a1bd21ab9..17907fcaebc3 100644 --- a/drivers/power/supply/cpcap-charger.c +++ b/drivers/power/supply/cpcap-charger.c @@ -566,7 +566,7 @@ out_err: } static const struct power_supply_desc cpcap_charger_usb_desc = { - .name = "cpcap_usb", + .name = "usb", .type = POWER_SUPPLY_TYPE_USB, .properties = cpcap_charger_props, .num_properties = ARRAY_SIZE(cpcap_charger_props), From 3ae5f06681fc0e9ce6a0ac4f909aa7695e1f3228 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 3 May 2017 17:15:30 -0700 Subject: [PATCH 32/34] power: supply: cpcap-charger: Fix charge voltage configuration We have the charge voltage wrong, it should be 4.35V instead of 4.2V. This will cause the battery to never get fully charged. I noticed this when looking at the Andoid kernel battery and charger status for a fully charged battery: POWER_SUPPLY_VOLTAGE_NOW=4351000 Also the battery on droid 4 says "4.35, 1735/1785mAh (min/typ), 6.6/6.8 Wh (min/typ)". Presumably the 4.35 on the battery is the charge voltage. And finally, on Android the CPCAP CRM register is set to 0x03b5 where the b is the charge voltage. Let's fix the charge voltage define and update the charge configuration to use the 4.35V setting. Fixes: 0c9888e3c192 ("power: supply: cpcap-charger: Add minimal CPCAP PMIC battery charger") Cc: Marcel Partap Cc: Michael Scott Signed-off-by: Tony Lindgren Signed-off-by: Sebastian Reichel --- drivers/power/supply/cpcap-charger.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/power/supply/cpcap-charger.c b/drivers/power/supply/cpcap-charger.c index 17907fcaebc3..33ca1174070b 100644 --- a/drivers/power/supply/cpcap-charger.c +++ b/drivers/power/supply/cpcap-charger.c @@ -76,7 +76,7 @@ #define CPCAP_REG_CRM_VCHRG_4V30 CPCAP_REG_CRM_VCHRG(0x8) #define CPCAP_REG_CRM_VCHRG_4V32 CPCAP_REG_CRM_VCHRG(0x9) #define CPCAP_REG_CRM_VCHRG_4V34 CPCAP_REG_CRM_VCHRG(0xa) -#define CPCAP_REG_CRM_VCHRG_4V36 CPCAP_REG_CRM_VCHRG(0xb) +#define CPCAP_REG_CRM_VCHRG_4V35 CPCAP_REG_CRM_VCHRG(0xb) #define CPCAP_REG_CRM_VCHRG_4V38 CPCAP_REG_CRM_VCHRG(0xc) #define CPCAP_REG_CRM_VCHRG_4V40 CPCAP_REG_CRM_VCHRG(0xd) #define CPCAP_REG_CRM_VCHRG_4V42 CPCAP_REG_CRM_VCHRG(0xe) @@ -433,7 +433,7 @@ static void cpcap_usb_detect(struct work_struct *work) max_current = CPCAP_REG_CRM_ICHRG_0A528; error = cpcap_charger_set_state(ddata, - CPCAP_REG_CRM_VCHRG_4V20, + CPCAP_REG_CRM_VCHRG_4V35, max_current, CPCAP_REG_CRM_TR_0A72); if (error) From 6ffa8ace7092a5414d5d387c54a66118eb90482b Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 3 May 2017 17:15:31 -0700 Subject: [PATCH 33/34] power: supply: cpcap-charger: Fix enable for 3.8V charge setting Zero is a valid register value for for 3.8V charging. Fixes: 0c9888e3c192 ("power: supply: cpcap-charger: Add minimal CPCAP PMIC battery charger") Cc: Marcel Partap Cc: Michael Scott Signed-off-by: Tony Lindgren Signed-off-by: Sebastian Reichel --- drivers/power/supply/cpcap-charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/supply/cpcap-charger.c b/drivers/power/supply/cpcap-charger.c index 33ca1174070b..378fbdaba85b 100644 --- a/drivers/power/supply/cpcap-charger.c +++ b/drivers/power/supply/cpcap-charger.c @@ -262,7 +262,7 @@ static int cpcap_charger_set_state(struct cpcap_charger_ddata *ddata, bool enable; int error; - enable = max_voltage && (charge_current || trickle_current); + enable = (charge_current || trickle_current); dev_dbg(ddata->dev, "%s enable: %i\n", __func__, enable); if (!enable) { From 35f4f99ccbc1ccf3a1f1157a5f2f14d6a7d1848f Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 3 May 2017 17:15:32 -0700 Subject: [PATCH 34/34] power: supply: cpcap-charger: Keep trickle charger bits disabled Android does not seem to set the trickle charger bits, and these seem to be only used by the bootloader when bringing up a completely discharged battery. So let's keep the trickle charging bits disabled and avoid misconfiguring the hardware. Fixes: 0c9888e3c192 ("power: supply: cpcap-charger: Add minimal CPCAP PMIC battery charger") Cc: Marcel Partap Cc: Michael Scott Signed-off-by: Tony Lindgren Signed-off-by: Sebastian Reichel --- drivers/power/supply/cpcap-charger.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/power/supply/cpcap-charger.c b/drivers/power/supply/cpcap-charger.c index 378fbdaba85b..26a2dc7ac9a2 100644 --- a/drivers/power/supply/cpcap-charger.c +++ b/drivers/power/supply/cpcap-charger.c @@ -434,8 +434,7 @@ static void cpcap_usb_detect(struct work_struct *work) error = cpcap_charger_set_state(ddata, CPCAP_REG_CRM_VCHRG_4V35, - max_current, - CPCAP_REG_CRM_TR_0A72); + max_current, 0); if (error) goto out_err; } else {