From 7d54d0d38ec42559c891526f079f1e035cd4b3ae Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 7 Nov 2017 15:43:22 +0300 Subject: [PATCH 01/32] power: supply: sbs-message: double left shift bug in sbsm_select() The original code does this: "1 << (1 << 11)" which is undefined in C. Fixes: dbc4deda03fe ("power: Adds support for Smart Battery System Manager") Signed-off-by: Dan Carpenter Signed-off-by: Sebastian Reichel --- drivers/power/supply/sbs-manager.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/supply/sbs-manager.c b/drivers/power/supply/sbs-manager.c index ccb4217b9638..cb6e8f66c7a2 100644 --- a/drivers/power/supply/sbs-manager.c +++ b/drivers/power/supply/sbs-manager.c @@ -183,7 +183,7 @@ static int sbsm_select(struct i2c_mux_core *muxc, u32 chan) return ret; /* chan goes from 1 ... 4 */ - reg = 1 << BIT(SBSM_SMB_BAT_OFFSET + chan); + reg = BIT(SBSM_SMB_BAT_OFFSET + chan); ret = sbsm_write_word(data->client, SBSM_CMD_BATSYSSTATE, reg); if (ret) dev_err(dev, "Failed to select channel %i\n", chan); From 348c7cf5fcbcb68838255759d4cb45d039af36d2 Mon Sep 17 00:00:00 2001 From: Jesse Chan Date: Mon, 20 Nov 2017 12:58:27 -0800 Subject: [PATCH 02/32] power: reset: zx-reboot: add missing MODULE_DESCRIPTION/AUTHOR/LICENSE This change resolves a new compile-time warning when built as a loadable module: WARNING: modpost: missing MODULE_LICENSE() in drivers/power/reset/zx-reboot.o see include/linux/module.h for more information This adds the license as "GPL v2", which matches the header of the file. MODULE_DESCRIPTION and MODULE_AUTHOR are also added. Signed-off-by: Jesse Chan Signed-off-by: Sebastian Reichel --- drivers/power/reset/zx-reboot.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/power/reset/zx-reboot.c b/drivers/power/reset/zx-reboot.c index 7549c7f74a3c..c03e96e6a041 100644 --- a/drivers/power/reset/zx-reboot.c +++ b/drivers/power/reset/zx-reboot.c @@ -82,3 +82,7 @@ static struct platform_driver zx_reboot_driver = { }, }; module_platform_driver(zx_reboot_driver); + +MODULE_DESCRIPTION("ZTE SoCs reset driver"); +MODULE_AUTHOR("Jun Nie "); +MODULE_LICENSE("GPL v2"); From f46b151ede8668cb824a836e82303e1123f5f9f6 Mon Sep 17 00:00:00 2001 From: Ryosuke Saito Date: Tue, 21 Nov 2017 13:27:12 +0900 Subject: [PATCH 03/32] power: supply: charger-manager: Fix typo in condition Should be discharging_max_duration_ms, not charging_max_duration_ms. Signed-off-by: Ryosuke Saito Signed-off-by: Sebastian Reichel --- drivers/power/supply/charger-manager.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index 6502fa7c2106..1de4b4493824 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -578,7 +578,7 @@ static int check_charging_duration(struct charger_manager *cm) } else if (is_ext_pwr_online(cm) && !cm->charger_enabled) { duration = curr - cm->charging_end_time; - if (duration > desc->charging_max_duration_ms && + if (duration > desc->discharging_max_duration_ms && is_ext_pwr_online(cm)) { dev_info(cm->dev, "Discharging duration exceed %ums\n", desc->discharging_max_duration_ms); From bf59fddde1c3eab89eb8dca8f3d3dc097887d2bb Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Wed, 22 Nov 2017 21:27:31 +0100 Subject: [PATCH 04/32] power: supply: ab8500_charger: Fix an error handling path 'ret' is know to be 0 at this point, because it has not been updated by the the previous call to 'abx500_mask_and_set_register_interruptible()'. Fix it by updating 'ret' before checking if an error occurred. Fixes: 84edbeeab67c ("ab8500-charger: AB8500 charger driver") Signed-off-by: Christophe JAILLET 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 4ebbcce45c48..1cdbe7a0738d 100644 --- a/drivers/power/supply/ab8500_charger.c +++ b/drivers/power/supply/ab8500_charger.c @@ -3218,7 +3218,7 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di) } /* Enable backup battery charging */ - abx500_mask_and_set_register_interruptible(di->dev, + ret = abx500_mask_and_set_register_interruptible(di->dev, AB8500_RTC, AB8500_RTC_CTRL_REG, RTC_BUP_CH_ENA, RTC_BUP_CH_ENA); if (ret < 0) From 09edcb647542487864e23aa8d2ef26be3e08978a Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Wed, 22 Nov 2017 21:31:20 +0100 Subject: [PATCH 05/32] power: supply: ab8500_charger: Bail out in case of error in 'ab8500_charger_init_hw_registers()' If an error occurs when we enable the backup battery charging, we should go through the error handling path directly. Before commit db43e6c473b5 ("ab8500-bm: Add usb power path support") this was the case, but this commit has added some code between the last test and the 'out' label. So, in case of error, this added code is executed and the error may be silently ignored. Fix it by adding the missing 'goto out', as done in all other error handling paths. Fixes: db43e6c473b5 ("ab8500-bm: Add usb power path support") Signed-off-by: Christophe JAILLET Signed-off-by: Sebastian Reichel --- drivers/power/supply/ab8500_charger.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c index 1cdbe7a0738d..5a76c6d343de 100644 --- a/drivers/power/supply/ab8500_charger.c +++ b/drivers/power/supply/ab8500_charger.c @@ -3221,8 +3221,10 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di) ret = abx500_mask_and_set_register_interruptible(di->dev, AB8500_RTC, AB8500_RTC_CTRL_REG, RTC_BUP_CH_ENA, RTC_BUP_CH_ENA); - if (ret < 0) + if (ret < 0) { dev_err(di->dev, "%s mask and set failed\n", __func__); + goto out; + } if (is_ab8540(di->parent)) { ret = abx500_mask_and_set_register_interruptible(di->dev, From aac799040731ee338b84d27c27fa2d903e89c857 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Fri, 24 Nov 2017 22:31:40 +0530 Subject: [PATCH 06/32] power: supply: cpcap-battery: Fix platform_get_irq_byname's error checking The platform_get_irq_byname() function returns -1 if an error occurs. zero or positive number on success. platform_get_irq_byname() error checking for zero is not correct. Signed-off-by: Arvind Yadav Signed-off-by: Sebastian Reichel --- drivers/power/supply/cpcap-battery.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index ee71a2b37b12..98ba07869c3b 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -586,8 +586,8 @@ static int cpcap_battery_init_irq(struct platform_device *pdev, int irq, error; irq = platform_get_irq_byname(pdev, name); - if (!irq) - return -ENODEV; + if (irq < 0) + return irq; error = devm_request_threaded_irq(ddata->dev, irq, NULL, cpcap_battery_irq_thread, From 5f2f0d610578d380123d5c5906bbfa1ec5542b1a Mon Sep 17 00:00:00 2001 From: Mike Looijmans Date: Thu, 23 Nov 2017 15:41:05 +0100 Subject: [PATCH 07/32] power: ltc2941-battery-gauge: Disable continuous monitoring on shutdown The driver sets the fuel gauge to continuous monitoring on startup, for the models that support this. When the board shuts down, the chip remains in that mode, causing a few mA drain on the battery every 2 or 10 seconds. This patch registers a shutdown handler that turns off the monitoring to prevent this battery drain. Signed-off-by: Mike Looijmans Signed-off-by: Sebastian Reichel --- drivers/power/supply/ltc2941-battery-gauge.c | 25 ++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/power/supply/ltc2941-battery-gauge.c b/drivers/power/supply/ltc2941-battery-gauge.c index 08e4fd9ee607..4cfa3f0cd689 100644 --- a/drivers/power/supply/ltc2941-battery-gauge.c +++ b/drivers/power/supply/ltc2941-battery-gauge.c @@ -60,6 +60,7 @@ enum ltc294x_id { #define LTC294X_REG_CONTROL_PRESCALER_SET(x) \ ((x << 3) & LTC294X_REG_CONTROL_PRESCALER_MASK) #define LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED 0 +#define LTC294X_REG_CONTROL_ADC_DISABLE(x) ((x) & ~(BIT(7) | BIT(6))) struct ltc294x_info { struct i2c_client *client; /* I2C Client pointer */ @@ -523,6 +524,29 @@ static int ltc294x_i2c_probe(struct i2c_client *client, return 0; } +static void ltc294x_i2c_shutdown(struct i2c_client *client) +{ + struct ltc294x_info *info = i2c_get_clientdata(client); + int ret; + u8 value; + u8 control; + + /* The LTC2941 does not need any special handling */ + if (info->id == LTC2941_ID) + return; + + /* Read control register */ + ret = ltc294x_read_regs(info->client, LTC294X_REG_CONTROL, &value, 1); + if (ret < 0) + return; + + /* Disable continuous ADC conversion as this drains the battery */ + control = LTC294X_REG_CONTROL_ADC_DISABLE(value); + if (control != value) + ltc294x_write_regs(info->client, LTC294X_REG_CONTROL, + &control, 1); +} + #ifdef CONFIG_PM_SLEEP static int ltc294x_suspend(struct device *dev) @@ -589,6 +613,7 @@ static struct i2c_driver ltc294x_driver = { }, .probe = ltc294x_i2c_probe, .remove = ltc294x_i2c_remove, + .shutdown = ltc294x_i2c_shutdown, .id_table = ltc294x_i2c_id, }; module_i2c_driver(ltc294x_driver); From d16002755dc8169d080d23f6df04ba165df66586 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 28 Nov 2017 10:08:30 +0100 Subject: [PATCH 08/32] power: supply: bq24190_charger: Remove extcon handling Now that drivers/i2c/busses/i2c-cht-wc.c uses "input-current-limit-from-supplier" instead of "extcon-name" the last user of the bq24190 extcon code is gone, remove it. Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq24190_charger.c | 117 +++---------------------- 1 file changed, 10 insertions(+), 107 deletions(-) diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c index 35ff406aca48..c4335ff70232 100644 --- a/drivers/power/supply/bq24190_charger.c +++ b/drivers/power/supply/bq24190_charger.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -162,9 +161,6 @@ struct bq24190_dev_info { struct device *dev; struct power_supply *charger; struct power_supply *battery; - struct extcon_dev *extcon; - struct notifier_block extcon_nb; - struct delayed_work extcon_work; struct delayed_work input_current_limit_work; char model_name[I2C_NAME_SIZE]; bool initialized; @@ -686,6 +682,16 @@ static int bq24190_register_reset(struct bq24190_dev_info *bdi) int ret, limit = 100; u8 v; + /* + * This prop. can be passed on device instantiation from platform code: + * struct property_entry pe[] = + * { PROPERTY_ENTRY_BOOL("disable-reset"), ... }; + * struct i2c_board_info bi = + * { .type = "bq24190", .addr = 0x6b, .properties = pe, .irq = irq }; + * struct i2c_adapter ad = { ... }; + * i2c_add_adapter(&ad); + * i2c_new_device(&ad, &bi); + */ if (device_property_read_bool(bdi->dev, "disable-reset")) return 0; @@ -1623,75 +1629,6 @@ static irqreturn_t bq24190_irq_handler_thread(int irq, void *data) return IRQ_HANDLED; } -static void bq24190_extcon_work(struct work_struct *work) -{ - struct bq24190_dev_info *bdi = - container_of(work, struct bq24190_dev_info, extcon_work.work); - int error, iinlim = 0; - u8 v; - - error = pm_runtime_get_sync(bdi->dev); - if (error < 0) { - dev_warn(bdi->dev, "pm_runtime_get failed: %i\n", error); - pm_runtime_put_noidle(bdi->dev); - return; - } - - if (extcon_get_state(bdi->extcon, EXTCON_CHG_USB_SDP) == 1) - iinlim = 500000; - else if (extcon_get_state(bdi->extcon, EXTCON_CHG_USB_CDP) == 1 || - extcon_get_state(bdi->extcon, EXTCON_CHG_USB_ACA) == 1) - iinlim = 1500000; - else if (extcon_get_state(bdi->extcon, EXTCON_CHG_USB_DCP) == 1) - iinlim = 2000000; - - if (iinlim) { - error = bq24190_set_field_val(bdi, BQ24190_REG_ISC, - BQ24190_REG_ISC_IINLIM_MASK, - BQ24190_REG_ISC_IINLIM_SHIFT, - bq24190_isc_iinlim_values, - ARRAY_SIZE(bq24190_isc_iinlim_values), - iinlim); - if (error < 0) - dev_err(bdi->dev, "Can't set IINLIM: %d\n", error); - } - - /* if no charger found and in USB host mode, set OTG 5V boost, else normal */ - if (!iinlim && extcon_get_state(bdi->extcon, EXTCON_USB_HOST) == 1) - v = BQ24190_REG_POC_CHG_CONFIG_OTG; - else - v = BQ24190_REG_POC_CHG_CONFIG_CHARGE; - - error = bq24190_write_mask(bdi, BQ24190_REG_POC, - BQ24190_REG_POC_CHG_CONFIG_MASK, - BQ24190_REG_POC_CHG_CONFIG_SHIFT, - v); - if (error < 0) - dev_err(bdi->dev, "Can't set CHG_CONFIG: %d\n", error); - - pm_runtime_mark_last_busy(bdi->dev); - pm_runtime_put_autosuspend(bdi->dev); -} - -static int bq24190_extcon_event(struct notifier_block *nb, unsigned long event, - void *param) -{ - struct bq24190_dev_info *bdi = - container_of(nb, struct bq24190_dev_info, extcon_nb); - - /* - * The Power-Good detection may take up to 220ms, sometimes - * the external charger detection is quicker, and the bq24190 will - * reset to iinlim based on its own charger detection (which is not - * hooked up when using external charger detection) resulting in - * a too low default 500mA iinlim. Delay applying the extcon value - * for 300ms to avoid this. - */ - queue_delayed_work(system_wq, &bdi->extcon_work, msecs_to_jiffies(300)); - - return NOTIFY_OK; -} - static int bq24190_hw_init(struct bq24190_dev_info *bdi) { u8 v; @@ -1766,7 +1703,6 @@ static int bq24190_probe(struct i2c_client *client, struct device *dev = &client->dev; struct power_supply_config charger_cfg = {}, battery_cfg = {}; struct bq24190_dev_info *bdi; - const char *name; int ret; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { @@ -1796,25 +1732,6 @@ static int bq24190_probe(struct i2c_client *client, return -EINVAL; } - /* - * Devicetree platforms should get extcon via phandle (not yet supported). - * On ACPI platforms, extcon clients may invoke us with: - * struct property_entry pe[] = - * { PROPERTY_ENTRY_STRING("extcon-name", client_name), ... }; - * struct i2c_board_info bi = - * { .type = "bq24190", .addr = 0x6b, .properties = pe, .irq = irq }; - * struct i2c_adapter ad = { ... }; - * i2c_add_adapter(&ad); - * i2c_new_device(&ad, &bi); - */ - if (device_property_read_string(dev, "extcon-name", &name) == 0) { - bdi->extcon = extcon_get_extcon_dev(name); - if (!bdi->extcon) - return -EPROBE_DEFER; - - dev_info(bdi->dev, "using extcon device %s\n", name); - } - pm_runtime_enable(dev); pm_runtime_use_autosuspend(dev); pm_runtime_set_autosuspend_delay(dev, 600); @@ -1882,20 +1799,6 @@ static int bq24190_probe(struct i2c_client *client, if (ret < 0) goto out_sysfs; - if (bdi->extcon) { - INIT_DELAYED_WORK(&bdi->extcon_work, bq24190_extcon_work); - bdi->extcon_nb.notifier_call = bq24190_extcon_event; - ret = devm_extcon_register_notifier_all(dev, bdi->extcon, - &bdi->extcon_nb); - if (ret) { - dev_err(dev, "Can't register extcon\n"); - goto out_sysfs; - } - - /* Sync initial cable state */ - queue_delayed_work(system_wq, &bdi->extcon_work, 0); - } - enable_irq_wake(client->irq); pm_runtime_mark_last_busy(dev); From 70a39e1075019eef753649ac01ed594ac9016be6 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Fri, 10 Nov 2017 11:39:37 +0100 Subject: [PATCH 09/32] Add support for bq27521 battery monitor This adds basic support for BQ27521 battery monitor, used in Nokia N9 and N950. In particular, battery voltage is important to be able to tell when the battery is almost empty. Emptying battery on N950 is pretty painful, as flasher needs to be used to recover phone in such case. Signed-off-by: Pavel Machek Acked-by: Andrew F. Davis Acked-by: Rob Herring Signed-off-by: Sebastian Reichel --- .../bindings/power/supply/bq27xxx.txt | 1 + drivers/power/supply/bq27xxx_battery.c | 39 ++++++++++++++++++- drivers/power/supply/bq27xxx_battery_i2c.c | 2 + include/linux/power/bq27xxx_battery.h | 1 + 4 files changed, 41 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/power/supply/bq27xxx.txt b/Documentation/devicetree/bindings/power/supply/bq27xxx.txt index 6858e1a804ad..615c1cb6889f 100644 --- a/Documentation/devicetree/bindings/power/supply/bq27xxx.txt +++ b/Documentation/devicetree/bindings/power/supply/bq27xxx.txt @@ -15,6 +15,7 @@ Required properties: * "ti,bq27520g2" - BQ27520-g2 * "ti,bq27520g3" - BQ27520-g3 * "ti,bq27520g4" - BQ27520-g4 + * "ti,bq27521" - BQ27521 * "ti,bq27530" - BQ27530 * "ti,bq27531" - BQ27531 * "ti,bq27541" - BQ27541 diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c index 51f0961ecf3e..d99981542a46 100644 --- a/drivers/power/supply/bq27xxx_battery.c +++ b/drivers/power/supply/bq27xxx_battery.c @@ -323,6 +323,30 @@ static u8 [BQ27XXX_REG_AP] = INVALID_REG_ADDR, BQ27XXX_DM_REG_ROWS, }, + bq27521_regs[BQ27XXX_REG_MAX] = { + [BQ27XXX_REG_CTRL] = 0x02, + [BQ27XXX_REG_TEMP] = 0x0a, + [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, + [BQ27XXX_REG_VOLT] = 0x0c, + [BQ27XXX_REG_AI] = 0x0e, + [BQ27XXX_REG_FLAGS] = 0x08, + [BQ27XXX_REG_TTE] = INVALID_REG_ADDR, + [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, + [BQ27XXX_REG_TTES] = INVALID_REG_ADDR, + [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, + [BQ27XXX_REG_NAC] = INVALID_REG_ADDR, + [BQ27XXX_REG_FCC] = INVALID_REG_ADDR, + [BQ27XXX_REG_CYCT] = INVALID_REG_ADDR, + [BQ27XXX_REG_AE] = INVALID_REG_ADDR, + [BQ27XXX_REG_SOC] = INVALID_REG_ADDR, + [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR, + [BQ27XXX_REG_AP] = INVALID_REG_ADDR, + [BQ27XXX_DM_CTRL] = INVALID_REG_ADDR, + [BQ27XXX_DM_CLASS] = INVALID_REG_ADDR, + [BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR, + [BQ27XXX_DM_DATA] = INVALID_REG_ADDR, + [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR, + }, bq27530_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x06, @@ -557,6 +581,15 @@ static enum power_supply_property bq27520g4_props[] = { POWER_SUPPLY_PROP_MANUFACTURER, }; +static enum power_supply_property bq27521_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TECHNOLOGY, +}; + static enum power_supply_property bq27530_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, @@ -671,6 +704,7 @@ static struct bq27xxx_dm_reg bq27500_dm_regs[] = { #define bq27520g2_dm_regs 0 #define bq27520g3_dm_regs 0 #define bq27520g4_dm_regs 0 +#define bq27521_dm_regs 0 #define bq27530_dm_regs 0 #define bq27531_dm_regs 0 #define bq27541_dm_regs 0 @@ -717,8 +751,8 @@ static struct bq27xxx_dm_reg bq27621_dm_regs[] = { #endif #define BQ27XXX_O_ZERO 0x00000001 -#define BQ27XXX_O_OTDC 0x00000002 -#define BQ27XXX_O_UTOT 0x00000004 +#define BQ27XXX_O_OTDC 0x00000002 /* has OTC/OTD overtemperature flags */ +#define BQ27XXX_O_UTOT 0x00000004 /* has OT overtemperature flag */ #define BQ27XXX_O_CFGUP 0x00000008 #define BQ27XXX_O_RAM 0x00000010 @@ -751,6 +785,7 @@ static struct { [BQ27520G2] = BQ27XXX_DATA(bq27520g2, 0 , BQ27XXX_O_OTDC), [BQ27520G3] = BQ27XXX_DATA(bq27520g3, 0 , BQ27XXX_O_OTDC), [BQ27520G4] = BQ27XXX_DATA(bq27520g4, 0 , BQ27XXX_O_OTDC), + [BQ27521] = BQ27XXX_DATA(bq27521, 0 , 0), [BQ27530] = BQ27XXX_DATA(bq27530, 0 , BQ27XXX_O_UTOT), [BQ27531] = BQ27XXX_DATA(bq27531, 0 , BQ27XXX_O_UTOT), [BQ27541] = BQ27XXX_DATA(bq27541, 0 , BQ27XXX_O_OTDC), diff --git a/drivers/power/supply/bq27xxx_battery_i2c.c b/drivers/power/supply/bq27xxx_battery_i2c.c index 0b11ed472f33..6b25e5f2337e 100644 --- a/drivers/power/supply/bq27xxx_battery_i2c.c +++ b/drivers/power/supply/bq27xxx_battery_i2c.c @@ -239,6 +239,7 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = { { "bq27520g2", BQ27520G2 }, { "bq27520g3", BQ27520G3 }, { "bq27520g4", BQ27520G4 }, + { "bq27521", BQ27521 }, { "bq27530", BQ27530 }, { "bq27531", BQ27531 }, { "bq27541", BQ27541 }, @@ -269,6 +270,7 @@ static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = { { .compatible = "ti,bq27520g2" }, { .compatible = "ti,bq27520g3" }, { .compatible = "ti,bq27520g4" }, + { .compatible = "ti,bq27521" }, { .compatible = "ti,bq27530" }, { .compatible = "ti,bq27531" }, { .compatible = "ti,bq27541" }, diff --git a/include/linux/power/bq27xxx_battery.h b/include/linux/power/bq27xxx_battery.h index e6187f524f2c..01fbf1b16258 100644 --- a/include/linux/power/bq27xxx_battery.h +++ b/include/linux/power/bq27xxx_battery.h @@ -16,6 +16,7 @@ enum bq27xxx_chip { BQ27520G2, /* bq27520G2 */ BQ27520G3, /* bq27520G3 */ BQ27520G4, /* bq27520G4 */ + BQ27521, /* bq27521 */ BQ27530, /* bq27530, bq27531 */ BQ27531, BQ27541, /* bq27541, bq27542, bq27546, bq27742 */ From fd8b8f17d820328c0a2a46653e44e12a8cd450ba Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Sun, 7 Jan 2018 19:33:13 -0800 Subject: [PATCH 10/32] power: reset: msm: Clarify restart and poweroff When PSHOLD in a Qualcomm platform is deasserted the PMIC will perform either a power off or a restart of the system. The action to take is configured in the PON block, which is controlled by a separate driver. As the configuration logic was added to the pm8941-pwrkey driver the comment in do_msm_poweroff() is no longer valid and the name do_msm_restart() is misleading. Update the naming and drop the comment. Signed-off-by: Bjorn Andersson Signed-off-by: Sebastian Reichel --- drivers/power/reset/msm-poweroff.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c index 4702efdfe466..01b8c71697cb 100644 --- a/drivers/power/reset/msm-poweroff.c +++ b/drivers/power/reset/msm-poweroff.c @@ -23,7 +23,7 @@ #include static void __iomem *msm_ps_hold; -static int do_msm_restart(struct notifier_block *nb, unsigned long action, +static int deassert_pshold(struct notifier_block *nb, unsigned long action, void *data) { writel(0, msm_ps_hold); @@ -33,14 +33,13 @@ static int do_msm_restart(struct notifier_block *nb, unsigned long action, } static struct notifier_block restart_nb = { - .notifier_call = do_msm_restart, + .notifier_call = deassert_pshold, .priority = 128, }; static void do_msm_poweroff(void) { - /* TODO: Add poweroff capability */ - do_msm_restart(&restart_nb, 0, NULL); + deassert_pshold(&restart_nb, 0, NULL); } static int msm_restart_probe(struct platform_device *pdev) From baf61639b808307bd6e5be27e5cbef9cc3acd80e Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Fri, 22 Dec 2017 18:28:59 +0800 Subject: [PATCH 11/32] power: reset: remove unused imx-snvs-poweroff driver There's no user of it in kernel now and it basically functions the same as the generic syscon-poweroff.c to which we have already switched. So let's remove it. Cc: Robin Gong Cc: Shawn Guo Signed-off-by: Dong Aisheng Acked-by: Rob Herring Reviewed-by: Fabio Estevam Signed-off-by: Sebastian Reichel --- .../power/reset/imx-snvs-poweroff.txt | 23 ------- drivers/power/reset/Kconfig | 9 --- drivers/power/reset/Makefile | 1 - drivers/power/reset/imx-snvs-poweroff.c | 66 ------------------- 4 files changed, 99 deletions(-) delete mode 100644 Documentation/devicetree/bindings/power/reset/imx-snvs-poweroff.txt delete mode 100644 drivers/power/reset/imx-snvs-poweroff.c diff --git a/Documentation/devicetree/bindings/power/reset/imx-snvs-poweroff.txt b/Documentation/devicetree/bindings/power/reset/imx-snvs-poweroff.txt deleted file mode 100644 index 1b81fcd9fb72..000000000000 --- a/Documentation/devicetree/bindings/power/reset/imx-snvs-poweroff.txt +++ /dev/null @@ -1,23 +0,0 @@ -i.mx6 Poweroff Driver - -SNVS_LPCR in SNVS module can power off the whole system by pull -PMIC_ON_REQ low if PMIC_ON_REQ is connected with external PMIC. -If you don't want to use PMIC_ON_REQ as power on/off control, -please set status='disabled' to disable this driver. - -Required Properties: --compatible: "fsl,sec-v4.0-poweroff" --reg: Specifies the physical address of the SNVS_LPCR register - -Example: - snvs@20cc000 { - compatible = "fsl,sec-v4.0-mon", "simple-bus"; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0 0x020cc000 0x4000>; - ..... - snvs_poweroff: snvs-poweroff@38 { - compatible = "fsl,sec-v4.0-poweroff"; - reg = <0x38 0x4>; - }; - } diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index ca0de1a78e85..a102e74ab24e 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -98,15 +98,6 @@ config POWER_RESET_HISI help Reboot support for Hisilicon boards. -config POWER_RESET_IMX - bool "IMX6 power-off driver" - depends on POWER_RESET && SOC_IMX6 - help - This driver support power off external PMIC by PMIC_ON_REQ on i.mx6 - boards.If you want to use other pin to control external power,please - say N here or disable in dts to make sure pm_power_off never be - overwrote wrongly by this driver. - config POWER_RESET_MSM bool "Qualcomm MSM power-off driver" depends on ARCH_QCOM diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index aeb65edb17b7..dcc92f5f7a37 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -10,7 +10,6 @@ obj-$(CONFIG_POWER_RESET_GEMINI_POWEROFF) += gemini-poweroff.o obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o -obj-$(CONFIG_POWER_RESET_IMX) += imx-snvs-poweroff.o obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o obj-$(CONFIG_POWER_RESET_PIIX4_POWEROFF) += piix4-poweroff.o obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o diff --git a/drivers/power/reset/imx-snvs-poweroff.c b/drivers/power/reset/imx-snvs-poweroff.c deleted file mode 100644 index ad6ce5020ea7..000000000000 --- a/drivers/power/reset/imx-snvs-poweroff.c +++ /dev/null @@ -1,66 +0,0 @@ -/* Power off driver for i.mx6 - * Copyright (c) 2014, FREESCALE CORPORATION. All rights reserved. - * - * based on msm-poweroff.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * 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 - -static void __iomem *snvs_base; - -static void do_imx_poweroff(void) -{ - u32 value = readl(snvs_base); - - /* set TOP and DP_EN bit */ - writel(value | 0x60, snvs_base); -} - -static int imx_poweroff_probe(struct platform_device *pdev) -{ - snvs_base = of_iomap(pdev->dev.of_node, 0); - if (!snvs_base) { - dev_err(&pdev->dev, "failed to get memory\n"); - return -ENODEV; - } - - pm_power_off = do_imx_poweroff; - return 0; -} - -static const struct of_device_id of_imx_poweroff_match[] = { - { .compatible = "fsl,sec-v4.0-poweroff", }, - {}, -}; -MODULE_DEVICE_TABLE(of, of_imx_poweroff_match); - -static struct platform_driver imx_poweroff_driver = { - .probe = imx_poweroff_probe, - .driver = { - .name = "imx-snvs-poweroff", - .of_match_table = of_match_ptr(of_imx_poweroff_match), - }, -}; - -static int __init imx_poweroff_init(void) -{ - return platform_driver_register(&imx_poweroff_driver); -} -device_initcall(imx_poweroff_init); From d8e651953595403eb3504c1306135c24f243c7d9 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 26 Dec 2017 13:58:59 +0100 Subject: [PATCH 12/32] power: supply: axp288_charger: Do not stop + restart charging at boot Before this commit we were relying solely on the extcon interface for cable detection, including to determine if a cable providing vbus is connected at all. This caused us to turn off charging at boot, because when we run the initial state processing the axp288-extcon driver is still running charger-type detection most of the time, so all charger cable types read as disconnected when we run the initial state processing. This commit reworks the axp288_charger_extcon_evt_worker flow to use the VBUS_VALID bit from the PWR_INPUT_STATUS register to determine if we should turn charging on/off. Note this is the same bit as we use for the online property. If VBUS_VALID is set, but the extcon code has not completed the charger type detection yet, we now simply bail leaving things as configured by the BIOS (we will get a notifier call when the extcon code is done and reschedule the axp288_charger_extcon_evt_worker). The extcon code is the only one to trigger the worker (outside of the initial run) and we can rely on it to only call us if things have changed, so while we are completely refactoring axp288_charger_extcon_evt_worker, also remove the code to check if the state has changed. Reviewed-by: Chen-Yu Tsai Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_charger.c | 81 ++++++++++----------------- 1 file changed, 30 insertions(+), 51 deletions(-) diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c index d51ebd1da65e..54a95d196306 100644 --- a/drivers/power/supply/axp288_charger.c +++ b/drivers/power/supply/axp288_charger.c @@ -1,6 +1,7 @@ /* * axp288_charger.c - X-power AXP288 PMIC Charger driver * + * Copyright (C) 2016-2017 Hans de Goede * Copyright (C) 2014 Intel Corporation * Author: Ramakrishna Pallala * @@ -152,8 +153,6 @@ struct axp288_chrg_info { /* SDP/CDP/DCP USB charging cable notifications */ struct { struct extcon_dev *edev; - bool connected; - enum power_supply_type chg_type; struct notifier_block nb[ARRAY_SIZE(cable_ids)]; struct work_struct work; } cable; @@ -565,66 +564,47 @@ static void axp288_charger_extcon_evt_worker(struct work_struct *work) container_of(work, struct axp288_chrg_info, cable.work); int ret, current_limit; struct extcon_dev *edev = info->cable.edev; - bool old_connected = info->cable.connected; - enum power_supply_type old_chg_type = info->cable.chg_type; + unsigned int val; + + ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val); + if (ret < 0) { + dev_err(&info->pdev->dev, "Error reading status (%d)\n", ret); + return; + } + + /* Offline? Disable charging and bail */ + if (!(val & PS_STAT_VBUS_VALID)) { + dev_dbg(&info->pdev->dev, "USB charger disconnected\n"); + mutex_lock(&info->lock); + axp288_charger_enable_charger(info, false); + mutex_unlock(&info->lock); + power_supply_changed(info->psy_usb); + return; + } /* Determine cable/charger type */ if (extcon_get_state(edev, EXTCON_CHG_USB_SDP) > 0) { dev_dbg(&info->pdev->dev, "USB SDP charger is connected"); - info->cable.connected = true; - info->cable.chg_type = POWER_SUPPLY_TYPE_USB; + current_limit = ILIM_500MA; } else if (extcon_get_state(edev, EXTCON_CHG_USB_CDP) > 0) { dev_dbg(&info->pdev->dev, "USB CDP charger is connected"); - info->cable.connected = true; - info->cable.chg_type = POWER_SUPPLY_TYPE_USB_CDP; + current_limit = ILIM_1500MA; } else if (extcon_get_state(edev, EXTCON_CHG_USB_DCP) > 0) { dev_dbg(&info->pdev->dev, "USB DCP charger is connected"); - info->cable.connected = true; - info->cable.chg_type = POWER_SUPPLY_TYPE_USB_DCP; + current_limit = ILIM_2000MA; } else { - if (old_connected) - dev_dbg(&info->pdev->dev, "USB charger disconnected"); - info->cable.connected = false; - info->cable.chg_type = POWER_SUPPLY_TYPE_USB; - } - - /* Cable status changed */ - if (old_connected == info->cable.connected && - old_chg_type == info->cable.chg_type) + /* Charger type detection still in progress, bail. */ return; + } mutex_lock(&info->lock); - - if (info->cable.connected) { - axp288_charger_enable_charger(info, false); - - switch (info->cable.chg_type) { - case POWER_SUPPLY_TYPE_USB: - current_limit = ILIM_500MA; - break; - case POWER_SUPPLY_TYPE_USB_CDP: - current_limit = ILIM_1500MA; - break; - case POWER_SUPPLY_TYPE_USB_DCP: - current_limit = ILIM_2000MA; - break; - default: - /* Unknown */ - current_limit = 0; - break; - } - - /* Set vbus current limit first, then enable charger */ - ret = axp288_charger_set_vbus_inlmt(info, current_limit); - if (ret == 0) - axp288_charger_enable_charger(info, true); - else - dev_err(&info->pdev->dev, - "error setting current limit (%d)", ret); - } else { - axp288_charger_enable_charger(info, false); - } - + /* Set vbus current limit first, then enable charger */ + ret = axp288_charger_set_vbus_inlmt(info, current_limit); + if (ret == 0) + axp288_charger_enable_charger(info, true); + else + dev_err(&info->pdev->dev, + "error setting current limit (%d)\n", ret); mutex_unlock(&info->lock); power_supply_changed(info->psy_usb); @@ -799,7 +779,6 @@ static int axp288_charger_probe(struct platform_device *pdev) info->pdev = pdev; info->regmap = axp20x->regmap; info->regmap_irqc = axp20x->regmap_irqc; - info->cable.chg_type = -1; info->is_charger_enabled = -1; info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME); From a59943f8f6a74460254515b359dfd045615adc9a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 26 Dec 2017 13:59:00 +0100 Subject: [PATCH 13/32] power: supply: axp288_charger: Add missing newlines to some messages Add missing (terminating) "\n"-s to some dev_dbg messages. Reviewed-by: Chen-Yu Tsai Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_charger.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c index 54a95d196306..d398f8ee296d 100644 --- a/drivers/power/supply/axp288_charger.c +++ b/drivers/power/supply/axp288_charger.c @@ -584,13 +584,13 @@ static void axp288_charger_extcon_evt_worker(struct work_struct *work) /* Determine cable/charger type */ if (extcon_get_state(edev, EXTCON_CHG_USB_SDP) > 0) { - dev_dbg(&info->pdev->dev, "USB SDP charger is connected"); + dev_dbg(&info->pdev->dev, "USB SDP charger is connected\n"); current_limit = ILIM_500MA; } else if (extcon_get_state(edev, EXTCON_CHG_USB_CDP) > 0) { - dev_dbg(&info->pdev->dev, "USB CDP charger is connected"); + dev_dbg(&info->pdev->dev, "USB CDP charger is connected\n"); current_limit = ILIM_1500MA; } else if (extcon_get_state(edev, EXTCON_CHG_USB_DCP) > 0) { - dev_dbg(&info->pdev->dev, "USB DCP charger is connected"); + dev_dbg(&info->pdev->dev, "USB DCP charger is connected\n"); current_limit = ILIM_2000MA; } else { /* Charger type detection still in progress, bail. */ From 672b4b00603ec8e4e4fbfabbb2fd353c5f8523c4 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 26 Dec 2017 13:59:01 +0100 Subject: [PATCH 14/32] power: supply: axp288_charger: Remove charger-enabled state tracking The extcon code is the only one to trigger our worker (outside of the initial run) and we can rely on it to only call us if things have changed, so there is no need to track the charger-enabled state. Reviewed-by: Chen-Yu Tsai Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_charger.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c index d398f8ee296d..5d8308c8835d 100644 --- a/drivers/power/supply/axp288_charger.c +++ b/drivers/power/supply/axp288_charger.c @@ -162,7 +162,6 @@ struct axp288_chrg_info { int cv; int max_cc; int max_cv; - int is_charger_enabled; }; static inline int axp288_charger_set_cc(struct axp288_chrg_info *info, int cc) @@ -291,9 +290,6 @@ static int axp288_charger_enable_charger(struct axp288_chrg_info *info, { int ret; - if ((int)enable == info->is_charger_enabled) - return 0; - if (enable) ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1, CHRG_CCCV_CHG_EN, CHRG_CCCV_CHG_EN); @@ -302,8 +298,6 @@ static int axp288_charger_enable_charger(struct axp288_chrg_info *info, CHRG_CCCV_CHG_EN, 0); if (ret < 0) dev_err(&info->pdev->dev, "axp288 enable charger %d\n", ret); - else - info->is_charger_enabled = enable; return ret; } @@ -779,7 +773,6 @@ static int axp288_charger_probe(struct platform_device *pdev) info->pdev = pdev; info->regmap = axp20x->regmap; info->regmap_irqc = axp20x->regmap_irqc; - info->is_charger_enabled = -1; info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME); if (info->cable.edev == NULL) { From a9904aa82806143641a04521f6d296a13f636834 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 26 Dec 2017 13:59:02 +0100 Subject: [PATCH 15/32] power: supply: axp288_charger: Cleanup some double empty lines While we are doing cleanups, also remove some double blank lines. Reviewed-by: Chen-Yu Tsai Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_charger.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c index 5d8308c8835d..03f502e012c3 100644 --- a/drivers/power/supply/axp288_charger.c +++ b/drivers/power/supply/axp288_charger.c @@ -261,7 +261,6 @@ static inline int axp288_charger_set_vbus_inlmt(struct axp288_chrg_info *info, else dev_err(&info->pdev->dev, "charger BAK control %d\n", ret); - set_inlmt_fail: return ret; } @@ -281,7 +280,6 @@ static int axp288_charger_vbus_path_select(struct axp288_chrg_info *info, if (ret < 0) dev_err(&info->pdev->dev, "axp288 vbus path select %d\n", ret); - return ret; } From bbafa111caa06339a1ca50a7230c56dea0dffbde Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 26 Dec 2017 13:59:03 +0100 Subject: [PATCH 16/32] power: supply: axp288_charger: Use regmap_update_bits to set the input limits Use regmap_update_bits in axp288_charger_set_vbus_inlmt, instead of DIY code. Reviewed-by: Chen-Yu Tsai Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_charger.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c index 03f502e012c3..7b48afca1a1f 100644 --- a/drivers/power/supply/axp288_charger.c +++ b/drivers/power/supply/axp288_charger.c @@ -222,14 +222,8 @@ static inline int axp288_charger_set_vbus_inlmt(struct axp288_chrg_info *info, int inlmt) { int ret; - unsigned int val; u8 reg_val; - /* Read in limit register */ - ret = regmap_read(info->regmap, AXP20X_CHRG_BAK_CTRL, &val); - if (ret < 0) - goto set_inlmt_fail; - if (inlmt <= ILIM_100MA) { reg_val = CHRG_VBUS_ILIM_100MA; inlmt = ILIM_100MA; @@ -253,15 +247,15 @@ static inline int axp288_charger_set_vbus_inlmt(struct axp288_chrg_info *info, inlmt = ILIM_3000MA; } - reg_val = (val & ~CHRG_VBUS_ILIM_MASK) - | (reg_val << CHRG_VBUS_ILIM_BIT_POS); - ret = regmap_write(info->regmap, AXP20X_CHRG_BAK_CTRL, reg_val); + reg_val = reg_val << CHRG_VBUS_ILIM_BIT_POS; + + ret = regmap_update_bits(info->regmap, AXP20X_CHRG_BAK_CTRL, + CHRG_VBUS_ILIM_MASK, reg_val); if (ret >= 0) info->inlmt = inlmt; else dev_err(&info->pdev->dev, "charger BAK control %d\n", ret); -set_inlmt_fail: return ret; } From c28185bd3af43bbcf95d659deaef73754b3623fc Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 26 Dec 2017 13:59:04 +0100 Subject: [PATCH 17/32] power: supply: axp288_charger: Remove no longer needed locking Now that we use regmap to do read-modify-write ops everywhere, we can rely on the regmap lock and no longer need our own lock. Reviewed-by: Chen-Yu Tsai Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_charger.c | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c index 7b48afca1a1f..070dd79e606f 100644 --- a/drivers/power/supply/axp288_charger.c +++ b/drivers/power/supply/axp288_charger.c @@ -140,7 +140,6 @@ struct axp288_chrg_info { struct regmap_irq_chip_data *regmap_irqc; int irq[CHRG_INTR_END]; struct power_supply *psy_usb; - struct mutex lock; /* OTG/Host mode */ struct { @@ -361,8 +360,6 @@ static int axp288_charger_usb_set_property(struct power_supply *psy, int ret = 0; int scaled_val; - mutex_lock(&info->lock); - switch (psp) { case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: scaled_val = min(val->intval, info->max_cc); @@ -382,7 +379,6 @@ static int axp288_charger_usb_set_property(struct power_supply *psy, ret = -EINVAL; } - mutex_unlock(&info->lock); return ret; } @@ -391,9 +387,7 @@ static int axp288_charger_usb_get_property(struct power_supply *psy, union power_supply_propval *val) { struct axp288_chrg_info *info = power_supply_get_drvdata(psy); - int ret = 0; - - mutex_lock(&info->lock); + int ret; switch (psp) { case POWER_SUPPLY_PROP_PRESENT: @@ -404,7 +398,7 @@ static int axp288_charger_usb_get_property(struct power_supply *psy, } ret = axp288_charger_is_present(info); if (ret < 0) - goto psy_get_prop_fail; + return ret; val->intval = ret; break; case POWER_SUPPLY_PROP_ONLINE: @@ -415,7 +409,7 @@ static int axp288_charger_usb_get_property(struct power_supply *psy, } ret = axp288_charger_is_online(info); if (ret < 0) - goto psy_get_prop_fail; + return ret; val->intval = ret; break; case POWER_SUPPLY_PROP_HEALTH: @@ -437,13 +431,10 @@ static int axp288_charger_usb_get_property(struct power_supply *psy, val->intval = info->inlmt * 1000; break; default: - ret = -EINVAL; - goto psy_get_prop_fail; + return -EINVAL; } -psy_get_prop_fail: - mutex_unlock(&info->lock); - return ret; + return 0; } static int axp288_charger_property_is_writeable(struct power_supply *psy, @@ -561,9 +552,7 @@ static void axp288_charger_extcon_evt_worker(struct work_struct *work) /* Offline? Disable charging and bail */ if (!(val & PS_STAT_VBUS_VALID)) { dev_dbg(&info->pdev->dev, "USB charger disconnected\n"); - mutex_lock(&info->lock); axp288_charger_enable_charger(info, false); - mutex_unlock(&info->lock); power_supply_changed(info->psy_usb); return; } @@ -583,7 +572,6 @@ static void axp288_charger_extcon_evt_worker(struct work_struct *work) return; } - mutex_lock(&info->lock); /* Set vbus current limit first, then enable charger */ ret = axp288_charger_set_vbus_inlmt(info, current_limit); if (ret == 0) @@ -591,7 +579,6 @@ static void axp288_charger_extcon_evt_worker(struct work_struct *work) else dev_err(&info->pdev->dev, "error setting current limit (%d)\n", ret); - mutex_unlock(&info->lock); power_supply_changed(info->psy_usb); } @@ -784,7 +771,6 @@ static int axp288_charger_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, info); - mutex_init(&info->lock); ret = charger_init_hw_regs(info); if (ret) From d1ce7e5853dcb9f9a13e4b57701e5133391454a7 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 26 Dec 2017 13:59:05 +0100 Subject: [PATCH 18/32] power: supply: axp288_charger: Do not cache input current limit value The hardware may change this underneath us. Reviewed-by: Chen-Yu Tsai Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_charger.c | 48 ++++++++++++++++++++------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c index 070dd79e606f..2c7b0efbbf3d 100644 --- a/drivers/power/supply/axp288_charger.c +++ b/drivers/power/supply/axp288_charger.c @@ -156,7 +156,6 @@ struct axp288_chrg_info { struct work_struct work; } cable; - int inlmt; int cc; int cv; int max_cc; @@ -217,6 +216,37 @@ static inline int axp288_charger_set_cv(struct axp288_chrg_info *info, int cv) return ret; } +static int axp288_charger_get_vbus_inlmt(struct axp288_chrg_info *info) +{ + unsigned int val; + int ret; + + ret = regmap_read(info->regmap, AXP20X_CHRG_BAK_CTRL, &val); + if (ret < 0) + return ret; + + val >>= CHRG_VBUS_ILIM_BIT_POS; + switch (val) { + case CHRG_VBUS_ILIM_100MA: + return 100000; + case CHRG_VBUS_ILIM_500MA: + return 500000; + case CHRG_VBUS_ILIM_900MA: + return 900000; + case CHRG_VBUS_ILIM_1500MA: + return 1500000; + case CHRG_VBUS_ILIM_2000MA: + return 2000000; + case CHRG_VBUS_ILIM_2500MA: + return 2500000; + case CHRG_VBUS_ILIM_3000MA: + return 3000000; + default: + dev_warn(&info->pdev->dev, "Unknown ilim reg val: %d\n", val); + return 0; + } +} + static inline int axp288_charger_set_vbus_inlmt(struct axp288_chrg_info *info, int inlmt) { @@ -225,34 +255,25 @@ static inline int axp288_charger_set_vbus_inlmt(struct axp288_chrg_info *info, if (inlmt <= ILIM_100MA) { reg_val = CHRG_VBUS_ILIM_100MA; - inlmt = ILIM_100MA; } else if (inlmt <= ILIM_500MA) { reg_val = CHRG_VBUS_ILIM_500MA; - inlmt = ILIM_500MA; } else if (inlmt <= ILIM_900MA) { reg_val = CHRG_VBUS_ILIM_900MA; - inlmt = ILIM_900MA; } else if (inlmt <= ILIM_1500MA) { reg_val = CHRG_VBUS_ILIM_1500MA; - inlmt = ILIM_1500MA; } else if (inlmt <= ILIM_2000MA) { reg_val = CHRG_VBUS_ILIM_2000MA; - inlmt = ILIM_2000MA; } else if (inlmt <= ILIM_2500MA) { reg_val = CHRG_VBUS_ILIM_2500MA; - inlmt = ILIM_2500MA; } else { reg_val = CHRG_VBUS_ILIM_3000MA; - inlmt = ILIM_3000MA; } reg_val = reg_val << CHRG_VBUS_ILIM_BIT_POS; ret = regmap_update_bits(info->regmap, AXP20X_CHRG_BAK_CTRL, CHRG_VBUS_ILIM_MASK, reg_val); - if (ret >= 0) - info->inlmt = inlmt; - else + if (ret < 0) dev_err(&info->pdev->dev, "charger BAK control %d\n", ret); return ret; @@ -428,7 +449,10 @@ static int axp288_charger_usb_get_property(struct power_supply *psy, val->intval = info->max_cv * 1000; break; case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: - val->intval = info->inlmt * 1000; + ret = axp288_charger_get_vbus_inlmt(info); + if (ret < 0) + return ret; + val->intval = ret; break; default: return -EINVAL; From 9563d054168c475b43bd5165dd952ea3187cac6a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 26 Dec 2017 13:59:06 +0100 Subject: [PATCH 19/32] power: supply: axp288_charger: Pick lower input current limit not higher The code before this commit would pick 900 mA when asking for an input current limit of 600mA, rather then 500 mA, not good. While touching almost all code using the silly xxxMA defines anyways, also get rid of these simply typing out the numbers and switch the unit to uA as that is the psy class standard unit for currents. Reviewed-by: Chen-Yu Tsai Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_charger.c | 52 +++++++++------------------ 1 file changed, 17 insertions(+), 35 deletions(-) diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c index 2c7b0efbbf3d..fdca0a4a1c8e 100644 --- a/drivers/power/supply/axp288_charger.c +++ b/drivers/power/supply/axp288_charger.c @@ -99,21 +99,6 @@ #define CV_4200MV 4200 /* 4200mV */ #define CV_4350MV 4350 /* 4350mV */ -#define CC_200MA 200 /* 200mA */ -#define CC_600MA 600 /* 600mA */ -#define CC_800MA 800 /* 800mA */ -#define CC_1000MA 1000 /* 1000mA */ -#define CC_1600MA 1600 /* 1600mA */ -#define CC_2000MA 2000 /* 2000mA */ - -#define ILIM_100MA 100 /* 100mA */ -#define ILIM_500MA 500 /* 500mA */ -#define ILIM_900MA 900 /* 900mA */ -#define ILIM_1500MA 1500 /* 1500mA */ -#define ILIM_2000MA 2000 /* 2000mA */ -#define ILIM_2500MA 2500 /* 2500mA */ -#define ILIM_3000MA 3000 /* 3000mA */ - #define AXP288_EXTCON_DEV_NAME "axp288_extcon" #define USB_HOST_EXTCON_HID "INT3496" #define USB_HOST_EXTCON_NAME "INT3496:00" @@ -253,23 +238,20 @@ static inline int axp288_charger_set_vbus_inlmt(struct axp288_chrg_info *info, int ret; u8 reg_val; - if (inlmt <= ILIM_100MA) { - reg_val = CHRG_VBUS_ILIM_100MA; - } else if (inlmt <= ILIM_500MA) { - reg_val = CHRG_VBUS_ILIM_500MA; - } else if (inlmt <= ILIM_900MA) { - reg_val = CHRG_VBUS_ILIM_900MA; - } else if (inlmt <= ILIM_1500MA) { - reg_val = CHRG_VBUS_ILIM_1500MA; - } else if (inlmt <= ILIM_2000MA) { - reg_val = CHRG_VBUS_ILIM_2000MA; - } else if (inlmt <= ILIM_2500MA) { - reg_val = CHRG_VBUS_ILIM_2500MA; - } else { - reg_val = CHRG_VBUS_ILIM_3000MA; - } - - reg_val = reg_val << CHRG_VBUS_ILIM_BIT_POS; + if (inlmt >= 3000000) + reg_val = CHRG_VBUS_ILIM_3000MA << CHRG_VBUS_ILIM_BIT_POS; + else if (inlmt >= 2500000) + reg_val = CHRG_VBUS_ILIM_2500MA << CHRG_VBUS_ILIM_BIT_POS; + else if (inlmt >= 2000000) + reg_val = CHRG_VBUS_ILIM_2000MA << CHRG_VBUS_ILIM_BIT_POS; + else if (inlmt >= 1500000) + reg_val = CHRG_VBUS_ILIM_1500MA << CHRG_VBUS_ILIM_BIT_POS; + else if (inlmt >= 900000) + reg_val = CHRG_VBUS_ILIM_900MA << CHRG_VBUS_ILIM_BIT_POS; + else if (inlmt >= 500000) + reg_val = CHRG_VBUS_ILIM_500MA << CHRG_VBUS_ILIM_BIT_POS; + else + reg_val = CHRG_VBUS_ILIM_100MA << CHRG_VBUS_ILIM_BIT_POS; ret = regmap_update_bits(info->regmap, AXP20X_CHRG_BAK_CTRL, CHRG_VBUS_ILIM_MASK, reg_val); @@ -584,13 +566,13 @@ static void axp288_charger_extcon_evt_worker(struct work_struct *work) /* Determine cable/charger type */ if (extcon_get_state(edev, EXTCON_CHG_USB_SDP) > 0) { dev_dbg(&info->pdev->dev, "USB SDP charger is connected\n"); - current_limit = ILIM_500MA; + current_limit = 500000; } else if (extcon_get_state(edev, EXTCON_CHG_USB_CDP) > 0) { dev_dbg(&info->pdev->dev, "USB CDP charger is connected\n"); - current_limit = ILIM_1500MA; + current_limit = 1500000; } else if (extcon_get_state(edev, EXTCON_CHG_USB_DCP) > 0) { dev_dbg(&info->pdev->dev, "USB DCP charger is connected\n"); - current_limit = ILIM_2000MA; + current_limit = 2000000; } else { /* Charger type detection still in progress, bail. */ return; From 81d56dd3d5854b22a4fa724bffd37a8cf673093d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 26 Dec 2017 13:59:07 +0100 Subject: [PATCH 20/32] power: supply: axp288_charger: Use the right property for the input current limit Use the right property for the input current limit and make it writable. Reviewed-by: Chen-Yu Tsai Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_charger.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c index fdca0a4a1c8e..8f4ac2d38b3f 100644 --- a/drivers/power/supply/axp288_charger.c +++ b/drivers/power/supply/axp288_charger.c @@ -378,6 +378,11 @@ static int axp288_charger_usb_set_property(struct power_supply *psy, if (ret < 0) dev_warn(&info->pdev->dev, "set charge voltage failed\n"); break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = axp288_charger_set_vbus_inlmt(info, val->intval); + if (ret < 0) + dev_warn(&info->pdev->dev, "set input current limit failed\n"); + break; default: ret = -EINVAL; } @@ -430,7 +435,7 @@ static int axp288_charger_usb_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: val->intval = info->max_cv * 1000; break; - case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: ret = axp288_charger_get_vbus_inlmt(info); if (ret < 0) return ret; @@ -451,6 +456,7 @@ static int axp288_charger_property_is_writeable(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: ret = 1; break; default: @@ -469,7 +475,7 @@ static enum power_supply_property axp288_usb_props[] = { POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, - POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, }; static const struct power_supply_desc axp288_charger_desc = { From 8c0a0a2959565d39b0f1b9685dc05f00caa04789 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 26 Dec 2017 13:59:08 +0100 Subject: [PATCH 21/32] power: supply: axp288_charger: Simplify extcon cable handling Simplify extcon cable handling using the new devm_extcon_register_notifier_all function to listen to all cables in one go. Reviewed-by: Chen-Yu Tsai Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_charger.c | 51 ++++++--------------------- 1 file changed, 10 insertions(+), 41 deletions(-) diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c index 8f4ac2d38b3f..4ec7b023c881 100644 --- a/drivers/power/supply/axp288_charger.c +++ b/drivers/power/supply/axp288_charger.c @@ -103,9 +103,6 @@ #define USB_HOST_EXTCON_HID "INT3496" #define USB_HOST_EXTCON_NAME "INT3496:00" -static const unsigned int cable_ids[] = - { EXTCON_CHG_USB_SDP, EXTCON_CHG_USB_CDP, EXTCON_CHG_USB_DCP }; - enum { VBUS_OV_IRQ = 0, CHARGE_DONE_IRQ, @@ -137,7 +134,7 @@ struct axp288_chrg_info { /* SDP/CDP/DCP USB charging cable notifications */ struct { struct extcon_dev *edev; - struct notifier_block nb[ARRAY_SIZE(cable_ids)]; + struct notifier_block nb; struct work_struct work; } cable; @@ -595,34 +592,11 @@ static void axp288_charger_extcon_evt_worker(struct work_struct *work) power_supply_changed(info->psy_usb); } -/* - * We need 3 copies of this, because there is no way to find out for which - * cable id we are being called from the passed in arguments; and we must - * have a separate nb for each extcon_register_notifier call. - */ -static int axp288_charger_handle_cable0_evt(struct notifier_block *nb, - unsigned long event, void *param) +static int axp288_charger_handle_cable_evt(struct notifier_block *nb, + unsigned long event, void *param) { struct axp288_chrg_info *info = - container_of(nb, struct axp288_chrg_info, cable.nb[0]); - schedule_work(&info->cable.work); - return NOTIFY_OK; -} - -static int axp288_charger_handle_cable1_evt(struct notifier_block *nb, - unsigned long event, void *param) -{ - struct axp288_chrg_info *info = - container_of(nb, struct axp288_chrg_info, cable.nb[1]); - schedule_work(&info->cable.work); - return NOTIFY_OK; -} - -static int axp288_charger_handle_cable2_evt(struct notifier_block *nb, - unsigned long event, void *param) -{ - struct axp288_chrg_info *info = - container_of(nb, struct axp288_chrg_info, cable.nb[2]); + container_of(nb, struct axp288_chrg_info, cable.nb); schedule_work(&info->cable.work); return NOTIFY_OK; } @@ -800,17 +774,12 @@ static int axp288_charger_probe(struct platform_device *pdev) /* Register for extcon notification */ INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker); - info->cable.nb[0].notifier_call = axp288_charger_handle_cable0_evt; - info->cable.nb[1].notifier_call = axp288_charger_handle_cable1_evt; - info->cable.nb[2].notifier_call = axp288_charger_handle_cable2_evt; - for (i = 0; i < ARRAY_SIZE(cable_ids); i++) { - ret = devm_extcon_register_notifier(dev, info->cable.edev, - cable_ids[i], &info->cable.nb[i]); - if (ret) { - dev_err(dev, "failed to register extcon notifier for %u: %d\n", - cable_ids[i], ret); - return ret; - } + info->cable.nb.notifier_call = axp288_charger_handle_cable_evt; + ret = devm_extcon_register_notifier_all(dev, info->cable.edev, + &info->cable.nb); + if (ret) { + dev_err(dev, "failed to register cable extcon notifier\n"); + return ret; } schedule_work(&info->cable.work); From 165c2357744e41391902a2a72dd170beb60c28d5 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 26 Dec 2017 13:59:09 +0100 Subject: [PATCH 22/32] power: supply: axp288_charger: Properly stop work on probe-error / remove Properly stop any work we may have queued on probe-errors / remove. Rather then adding a remove driver callback for this, and goto style error handling to probe, use a devm_action for this. The devm_action gets registered before we register any of the extcon notifiers which may queue the work, devm does cleanup in reverse order, so this ensures that the notifiers are removed before we cancel the work. Reviewed-by: Chen-Yu Tsai Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_charger.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c index 4ec7b023c881..9bfbde15b07d 100644 --- a/drivers/power/supply/axp288_charger.c +++ b/drivers/power/supply/axp288_charger.c @@ -724,6 +724,14 @@ static int charger_init_hw_regs(struct axp288_chrg_info *info) return 0; } +static void axp288_charger_cancel_work(void *data) +{ + struct axp288_chrg_info *info = data; + + cancel_work_sync(&info->otg.work); + cancel_work_sync(&info->cable.work); +} + static int axp288_charger_probe(struct platform_device *pdev) { int ret, i, pirq; @@ -772,6 +780,11 @@ static int axp288_charger_probe(struct platform_device *pdev) return ret; } + /* Cancel our work on cleanup, register this before the notifiers */ + ret = devm_add_action(dev, axp288_charger_cancel_work, info); + if (ret) + return ret; + /* Register for extcon notification */ INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker); info->cable.nb.notifier_call = axp288_charger_handle_cable_evt; From 331645e165a9b40b49d9cb495bb5ea37850b57a7 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 26 Dec 2017 13:59:10 +0100 Subject: [PATCH 23/32] power: supply: axp288_fuel_gauge: Get iio-channels once during boot Get iio-channels once during boot, delaying the probe if the axp288_adc drivers has not loaded yet, instead of getting them on demand each time we need them. This fixes the following errors in dmesg: axp288_fuel_gauge axp288_fuel_gauge: ADC charge current read failed:-19 Which were caused by the ondemand iio-channel read code not finding the channel when the axp288_adc driver had not loaded yet. Reviewed-by: Chen-Yu Tsai Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_fuel_gauge.c | 110 ++++++++++++++--------- 1 file changed, 67 insertions(+), 43 deletions(-) diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index a8dcabc32721..eb60f75f00d7 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -1,6 +1,7 @@ /* * axp288_fuel_gauge.c - Xpower AXP288 PMIC Fuel Gauge Driver * + * Copyright (C) 2016-2017 Hans de Goede * Copyright (C) 2014 Intel Corporation * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -100,11 +101,22 @@ enum { WL1_IRQ, }; +enum { + BAT_TEMP = 0, + PMIC_TEMP, + SYSTEM_TEMP, + BAT_CHRG_CURR, + BAT_D_CURR, + BAT_VOLT, + IIO_CHANNEL_NUM +}; + struct axp288_fg_info { struct platform_device *pdev; struct regmap *regmap; struct regmap_irq_chip_data *regmap_irqc; int irq[AXP288_FG_INTR_NUM]; + struct iio_channel *iio_channel[IIO_CHANNEL_NUM]; struct power_supply *bat; struct mutex lock; int status; @@ -199,33 +211,6 @@ static int fuel_gauge_read_12bit_word(struct axp288_fg_info *info, int reg) return (buf[0] << 4) | ((buf[1] >> 4) & 0x0f); } -static int pmic_read_adc_val(const char *name, int *raw_val, - struct axp288_fg_info *info) -{ - int ret, val = 0; - struct iio_channel *indio_chan; - - indio_chan = iio_channel_get(NULL, name); - if (IS_ERR_OR_NULL(indio_chan)) { - ret = PTR_ERR(indio_chan); - goto exit; - } - ret = iio_read_channel_raw(indio_chan, &val); - if (ret < 0) { - dev_err(&info->pdev->dev, - "IIO channel read error: %x, %x\n", ret, val); - goto err_exit; - } - - dev_dbg(&info->pdev->dev, "adc raw val=%x\n", val); - *raw_val = val; - -err_exit: - iio_channel_release(indio_chan); -exit: - return ret; -} - #ifdef CONFIG_DEBUG_FS static int fuel_gauge_debug_show(struct seq_file *s, void *data) { @@ -296,22 +281,22 @@ static int fuel_gauge_debug_show(struct seq_file *s, void *data) AXP288_FG_TUNE5, fuel_gauge_reg_readb(info, AXP288_FG_TUNE5)); - ret = pmic_read_adc_val("axp288-batt-temp", &raw_val, info); + ret = iio_read_channel_raw(info->iio_channel[BAT_TEMP], &raw_val); if (ret >= 0) seq_printf(s, "axp288-batttemp : %d\n", raw_val); - ret = pmic_read_adc_val("axp288-pmic-temp", &raw_val, info); + ret = iio_read_channel_raw(info->iio_channel[PMIC_TEMP], &raw_val); if (ret >= 0) seq_printf(s, "axp288-pmictemp : %d\n", raw_val); - ret = pmic_read_adc_val("axp288-system-temp", &raw_val, info); + ret = iio_read_channel_raw(info->iio_channel[SYSTEM_TEMP], &raw_val); if (ret >= 0) seq_printf(s, "axp288-systtemp : %d\n", raw_val); - ret = pmic_read_adc_val("axp288-chrg-curr", &raw_val, info); + ret = iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], &raw_val); if (ret >= 0) seq_printf(s, "axp288-chrgcurr : %d\n", raw_val); - ret = pmic_read_adc_val("axp288-chrg-d-curr", &raw_val, info); + ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &raw_val); if (ret >= 0) seq_printf(s, "axp288-dchrgcur : %d\n", raw_val); - ret = pmic_read_adc_val("axp288-batt-volt", &raw_val, info); + ret = iio_read_channel_raw(info->iio_channel[BAT_VOLT], &raw_val); if (ret >= 0) seq_printf(s, "axp288-battvolt : %d\n", raw_val); @@ -360,13 +345,13 @@ static void fuel_gauge_get_status(struct axp288_fg_info *info) "PWR STAT read failed:%d\n", pwr_stat); return; } - ret = pmic_read_adc_val("axp288-chrg-curr", &charge, info); + ret = iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], &charge); if (ret < 0) { dev_err(&info->pdev->dev, "ADC charge current read failed:%d\n", ret); return; } - ret = pmic_read_adc_val("axp288-chrg-d-curr", &discharge, info); + ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &discharge); if (ret < 0) { dev_err(&info->pdev->dev, "ADC discharge current read failed:%d\n", ret); @@ -389,7 +374,7 @@ static int fuel_gauge_get_vbatt(struct axp288_fg_info *info, int *vbatt) { int ret = 0, raw_val; - ret = pmic_read_adc_val("axp288-batt-volt", &raw_val, info); + ret = iio_read_channel_raw(info->iio_channel[BAT_VOLT], &raw_val); if (ret < 0) goto vbatt_read_fail; @@ -403,10 +388,10 @@ static int fuel_gauge_get_current(struct axp288_fg_info *info, int *cur) int ret, value = 0; int charge, discharge; - ret = pmic_read_adc_val("axp288-chrg-curr", &charge, info); + ret = iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], &charge); if (ret < 0) goto current_read_fail; - ret = pmic_read_adc_val("axp288-chrg-d-curr", &discharge, info); + ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &discharge); if (ret < 0) goto current_read_fail; @@ -700,10 +685,18 @@ intr_failed: static int axp288_fuel_gauge_probe(struct platform_device *pdev) { - int ret = 0; + int i, ret = 0; struct axp288_fg_info *info; struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); struct power_supply_config psy_cfg = {}; + static const char * const iio_chan_name[] = { + [BAT_TEMP] = "axp288-batt-temp", + [PMIC_TEMP] = "axp288-pmic-temp", + [SYSTEM_TEMP] = "axp288-system-temp", + [BAT_CHRG_CURR] = "axp288-chrg-curr", + [BAT_D_CURR] = "axp288-chrg-d-curr", + [BAT_VOLT] = "axp288-batt-volt", + }; info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) @@ -719,18 +712,39 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev) mutex_init(&info->lock); INIT_DELAYED_WORK(&info->status_monitor, fuel_gauge_status_monitor); + for (i = 0; i < IIO_CHANNEL_NUM; i++) { + /* + * Note cannot use devm_iio_channel_get because x86 systems + * lack the device<->channel maps which iio_channel_get will + * try to use when passed a non NULL device pointer. + */ + info->iio_channel[i] = + iio_channel_get(NULL, iio_chan_name[i]); + if (IS_ERR(info->iio_channel[i])) { + ret = PTR_ERR(info->iio_channel[i]); + dev_dbg(&pdev->dev, "error getting iiochan %s: %d\n", + iio_chan_name[i], ret); + /* Wait for axp288_adc to load */ + if (ret == -ENODEV) + ret = -EPROBE_DEFER; + + goto out_free_iio_chan; + } + } + ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG); if (ret < 0) - return ret; + goto out_free_iio_chan; if (!(ret & FG_DES_CAP1_VALID)) { dev_err(&pdev->dev, "axp288 not configured by firmware\n"); - return -ENODEV; + ret = -ENODEV; + goto out_free_iio_chan; } ret = fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1); if (ret < 0) - return ret; + goto out_free_iio_chan; switch ((ret & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS) { case CHRG_CCCV_CV_4100MV: info->max_volt = 4100; @@ -751,7 +765,7 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev) if (IS_ERR(info->bat)) { ret = PTR_ERR(info->bat); dev_err(&pdev->dev, "failed to register battery: %d\n", ret); - return ret; + goto out_free_iio_chan; } fuel_gauge_create_debugfs(info); @@ -759,6 +773,13 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev) schedule_delayed_work(&info->status_monitor, STATUS_MON_DELAY_JIFFIES); return 0; + +out_free_iio_chan: + for (i = 0; i < IIO_CHANNEL_NUM; i++) + if (!IS_ERR_OR_NULL(info->iio_channel[i])) + iio_channel_release(info->iio_channel[i]); + + return ret; } static const struct platform_device_id axp288_fg_id_table[] = { @@ -780,6 +801,9 @@ static int axp288_fuel_gauge_remove(struct platform_device *pdev) if (info->irq[i] >= 0) free_irq(info->irq[i], info); + for (i = 0; i < IIO_CHANNEL_NUM; i++) + iio_channel_release(info->iio_channel[i]); + return 0; } From 31381042a614034386abb7fe7efeef392563e3b2 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Wed, 27 Dec 2017 10:20:42 +0100 Subject: [PATCH 24/32] bq24190: Simplify code in property_is_writeable Simplify function that should be trivial. Signed-off-by: Pavel machek Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq24190_charger.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c index c4335ff70232..b58df04d03b3 100644 --- a/drivers/power/supply/bq24190_charger.c +++ b/drivers/power/supply/bq24190_charger.c @@ -1199,8 +1199,6 @@ static int bq24190_charger_set_property(struct power_supply *psy, static int bq24190_charger_property_is_writeable(struct power_supply *psy, enum power_supply_property psp) { - int ret; - switch (psp) { case POWER_SUPPLY_PROP_ONLINE: case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: @@ -1208,13 +1206,10 @@ static int bq24190_charger_property_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: - ret = 1; - break; + return 1; default: - ret = 0; + return 0; } - - return ret; } static void bq24190_input_current_limit_work(struct work_struct *work) From a488ca6e707b07018633200b29e2de568bdd55cb Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 2 Jan 2018 14:28:02 +0100 Subject: [PATCH 25/32] power: supply: account for const type of of_device_id.data This driver creates two const structures that it stores in the data field of an of_device_id array. The data field of an of_device_id structure has type const void *, so there is no need for a const-discarding cast when putting const values into such a structure. Furthermore, adding const to the declaration of the location that receives a const value from such a field ensures that the compiler will continue to check that the value is not modified. The const-discarding cast on the extraction from the data field is thus no longer needed. Done using Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp20x_ac_power.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/power/supply/axp20x_ac_power.c b/drivers/power/supply/axp20x_ac_power.c index 38f4e87cf24d..0771f951b11f 100644 --- a/drivers/power/supply/axp20x_ac_power.c +++ b/drivers/power/supply/axp20x_ac_power.c @@ -159,7 +159,7 @@ static int axp20x_ac_power_probe(struct platform_device *pdev) struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); struct power_supply_config psy_cfg = {}; struct axp20x_ac_power *power; - struct axp_data *axp_data; + const struct axp_data *axp_data; static const char * const irq_names[] = { "ACIN_PLUGIN", "ACIN_REMOVAL", NULL }; int i, irq, ret; @@ -176,7 +176,7 @@ static int axp20x_ac_power_probe(struct platform_device *pdev) if (!power) return -ENOMEM; - axp_data = (struct axp_data *)of_device_get_match_data(&pdev->dev); + axp_data = of_device_get_match_data(&pdev->dev); if (axp_data->acin_adc) { power->acin_v = devm_iio_channel_get(&pdev->dev, "acin_v"); @@ -230,10 +230,10 @@ static int axp20x_ac_power_probe(struct platform_device *pdev) static const struct of_device_id axp20x_ac_power_match[] = { { .compatible = "x-powers,axp202-ac-power-supply", - .data = (void *)&axp20x_data, + .data = &axp20x_data, }, { .compatible = "x-powers,axp221-ac-power-supply", - .data = (void *)&axp22x_data, + .data = &axp22x_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, axp20x_ac_power_match); From 8eb96f136fe12c3c475326a581098ff3a590acbb Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 2 Jan 2018 14:28:08 +0100 Subject: [PATCH 26/32] power: reset: account for const type of of_device_id.data This driver creates a const structure that it stores in the data field of an of_device_id array. Add const to the declaration of the location that receives a value from the data field to ensure that the compiler will continue to check that the value is not modified and remove the const-dropping cast on the access to the data field. Done using Coccinelle. Signed-off-by: Julia Lawall Acked-by: Alexandre Belloni Signed-off-by: Sebastian Reichel --- drivers/power/reset/at91-sama5d2_shdwc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/power/reset/at91-sama5d2_shdwc.c b/drivers/power/reset/at91-sama5d2_shdwc.c index 31080c254124..0206cce328b3 100644 --- a/drivers/power/reset/at91-sama5d2_shdwc.c +++ b/drivers/power/reset/at91-sama5d2_shdwc.c @@ -68,7 +68,7 @@ struct shdwc_config { }; struct shdwc { - struct shdwc_config *cfg; + const struct shdwc_config *cfg; void __iomem *at91_shdwc_base; }; @@ -260,7 +260,7 @@ static int __init at91_shdwc_probe(struct platform_device *pdev) } match = of_match_node(at91_shdwc_of_match, pdev->dev.of_node); - at91_shdwc->cfg = (struct shdwc_config *)(match->data); + at91_shdwc->cfg = match->data; sclk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(sclk)) From 2b5a4b4bf2224f4f6b6631091bd51cb08d3094be Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 26 Dec 2017 13:59:11 +0100 Subject: [PATCH 27/32] power: supply: axp288_fuel_gauge: Rework get_status() Relying on the (dis)charge current reporting for reporting FULL back to userspace does not work really well and often leads to the reported status getting stuck at e.g. 98/99% (the fuelgauge is not perfect) for hours. What happens is that when the battery is full the axp288 keeps charging it with a very low current. Until it is really really full and once really really full, some inaccuracies in the adc lead to it then sometimes reporting a small discharging rate, even though an external pwr source is used. So we end up with a status of "charging" for hours after the battery is actually already full and sometimes this then flip-flops to discharging. This commit fixes this by first checking if a valid Vbus is present and if it is present using the fuel-gauge's reported percentage to check for a full battery. This commit also changes how get_status() determines if the battery is charging or discharging when not reporting it as full. We still use the current direction for this, but instead of reading 4 extra registers for this (2 16 bit regs), simplify things by using the current-direction bit in the power-status register, which already gets read anyways. This also reduces the amount of i2c reads to 1 when on battery and 2 when a valid Vbus is present. Signed-off-by: Hans de Goede Reviewed-by: Chen-Yu Tsai Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_fuel_gauge.c | 43 ++++++++++++------------ 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index eb60f75f00d7..c0b5e40b23e1 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -32,6 +32,12 @@ #include #include +#define PS_STAT_VBUS_TRIGGER (1 << 0) +#define PS_STAT_BAT_CHRG_DIR (1 << 2) +#define PS_STAT_VBAT_ABOVE_VHOLD (1 << 3) +#define PS_STAT_VBUS_VALID (1 << 4) +#define PS_STAT_VBUS_PRESENT (1 << 5) + #define CHRG_STAT_BAT_SAFE_MODE (1 << 3) #define CHRG_STAT_BAT_VALID (1 << 4) #define CHRG_STAT_BAT_PRESENT (1 << 5) @@ -336,8 +342,7 @@ static inline void fuel_gauge_remove_debugfs(struct axp288_fg_info *info) static void fuel_gauge_get_status(struct axp288_fg_info *info) { - int pwr_stat, ret; - int charge, discharge; + int pwr_stat, fg_res; pwr_stat = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS); if (pwr_stat < 0) { @@ -345,29 +350,25 @@ static void fuel_gauge_get_status(struct axp288_fg_info *info) "PWR STAT read failed:%d\n", pwr_stat); return; } - ret = iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], &charge); - if (ret < 0) { - dev_err(&info->pdev->dev, - "ADC charge current read failed:%d\n", ret); - return; - } - ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &discharge); - if (ret < 0) { - dev_err(&info->pdev->dev, - "ADC discharge current read failed:%d\n", ret); - return; + + /* Report full if Vbus is valid and the reported capacity is 100% */ + if (pwr_stat & PS_STAT_VBUS_VALID) { + fg_res = fuel_gauge_reg_readb(info, AXP20X_FG_RES); + if (fg_res < 0) { + dev_err(&info->pdev->dev, + "FG RES read failed: %d\n", fg_res); + return; + } + if (fg_res == (FG_REP_CAP_VALID | 100)) { + info->status = POWER_SUPPLY_STATUS_FULL; + return; + } } - if (charge > 0) + if (pwr_stat & PS_STAT_BAT_CHRG_DIR) info->status = POWER_SUPPLY_STATUS_CHARGING; - else if (discharge > 0) + else info->status = POWER_SUPPLY_STATUS_DISCHARGING; - else { - if (pwr_stat & CHRG_STAT_BAT_PRESENT) - info->status = POWER_SUPPLY_STATUS_FULL; - else - info->status = POWER_SUPPLY_STATUS_NOT_CHARGING; - } } static int fuel_gauge_get_vbatt(struct axp288_fg_info *info, int *vbatt) From ceb40831c94115134581ee6eaaa26001e00def5f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 26 Dec 2017 13:59:12 +0100 Subject: [PATCH 28/32] power: supply: axp288_fuel_gauge: Optimize get_current() First check the discharge current, and when that is non 0 use that without also checking the charge current (which will be 0 then). This makes get_current() do only 1 i2c read instead of 2 when on battery. This is esp. important given the pmic i2c bus mutex stuff used on boards with an axp288 because the SoC's own punit also may access the axp288, which makes i2c accesses more expensive then normal. Signed-off-by: Hans de Goede Reviewed-by: Chen-Yu Tsai Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_fuel_gauge.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index c0b5e40b23e1..e0f3965b6964 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -386,24 +386,19 @@ vbatt_read_fail: static int fuel_gauge_get_current(struct axp288_fg_info *info, int *cur) { - int ret, value = 0; - int charge, discharge; + int ret, discharge; - ret = iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], &charge); - if (ret < 0) - goto current_read_fail; + /* First check discharge current, so that we do only 1 read on bat. */ ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &discharge); if (ret < 0) - goto current_read_fail; + return ret; - if (charge > 0) - value = charge; - else if (discharge > 0) - value = -1 * discharge; + if (discharge > 0) { + *cur = -1 * discharge; + return 0; + } - *cur = value; -current_read_fail: - return ret; + return iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], cur); } static int fuel_gauge_get_vocv(struct axp288_fg_info *info, int *vocv) From b60c75b6a502077f0edb10c274eb5ac8e72ba342 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 26 Dec 2017 13:59:13 +0100 Subject: [PATCH 29/32] power: supply: axp288_fuel_gauge: Do not register our psy on (some) HDMI sticks The Intel Compute Stick (Cherry Trail version) and the Meegopad T08 HDMI stick, both use an axp288 PMIC. They also both have this wired up in such a way that the detection logic in the PMIC claims that a valid battery is present, resuling in GNOME and KDE showing a full-battery in their status bar and power-settings, while these devices do not have a battery. For lack of a better fix add a DMI blacklist and do not register the axp288_fuel_gauge psy on devices on the blacklist. Reviewed-by: Chen-Yu Tsai Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp288_fuel_gauge.c | 35 ++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index e0f3965b6964..4cc6e038dfdd 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -17,6 +17,7 @@ * */ +#include #include #include #include @@ -679,6 +680,37 @@ intr_failed: } } +/* + * Some devices have no battery (HDMI sticks) and the axp288 battery's + * detection reports one despite it not being there. + */ +static const struct dmi_system_id axp288_fuel_gauge_blacklist[] = { + { + /* Intel Cherry Trail Compute Stick, Windows version */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "STK1AW32SC"), + }, + }, + { + /* Intel Cherry Trail Compute Stick, version without an OS */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "STK1A32SC"), + }, + }, + { + /* Meegopad T08 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Default string"), + DMI_MATCH(DMI_BOARD_VENDOR, "To be filled by OEM."), + DMI_MATCH(DMI_BOARD_NAME, "T3 MRD"), + DMI_MATCH(DMI_BOARD_VERSION, "V1.1"), + }, + }, + {} +}; + static int axp288_fuel_gauge_probe(struct platform_device *pdev) { int i, ret = 0; @@ -694,6 +726,9 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev) [BAT_VOLT] = "axp288-batt-volt", }; + if (dmi_check_system(axp288_fuel_gauge_blacklist)) + return -ENODEV; + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; From 8be4c3667cf117b8f8a2b0b73a34258d45992867 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 26 Dec 2017 13:59:14 +0100 Subject: [PATCH 30/32] MAINTAINERS: Add AXP288 PMIC entry Add an entry for the AXP288 PMIC drivers with myself as maintainer. Signed-off-by: Hans de Goede Acked-by: Chen-Yu Tsai Signed-off-by: Sebastian Reichel --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index aa71ab52fd76..5693b8970e50 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14822,6 +14822,12 @@ F: include/linux/workqueue.h F: kernel/workqueue.c F: Documentation/core-api/workqueue.rst +X-POWERS AXP288 PMIC DRIVERS +M: Hans de Goede +S: Maintained +N: axp288 +F: drivers/acpi/pmic/intel_pmic_xpower.c + X-POWERS MULTIFUNCTION PMIC DEVICE DRIVERS M: Chen-Yu Tsai L: linux-kernel@vger.kernel.org From 6e5ab19d54e8cd7bdc779db0b807403558dbea62 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 20 Jan 2018 14:50:24 +0100 Subject: [PATCH 31/32] power: supply: max17042_battery: Check battery current for status when supplied Even though the system is supplied, it may still be discharging if the supply is e.g. only delivering 5V 0.5A. Check the avg battery current if available for more accurate status reporting. Cc: James Suggested-by: James Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 27 +++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 5b556a13f517..102b3f71e9a4 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -123,6 +123,8 @@ static int max17042_get_temperature(struct max17042_chip *chip, int *temp) static int max17042_get_status(struct max17042_chip *chip, int *status) { int ret, charge_full, charge_now; + int avg_current; + u32 data; ret = power_supply_am_i_supplied(chip->battery); if (ret < 0) { @@ -152,10 +154,31 @@ static int max17042_get_status(struct max17042_chip *chip, int *status) if (ret < 0) return ret; - if ((charge_full - charge_now) <= MAX17042_FULL_THRESHOLD) + if ((charge_full - charge_now) <= MAX17042_FULL_THRESHOLD) { *status = POWER_SUPPLY_STATUS_FULL; - else + return 0; + } + + /* + * Even though we are supplied, we may still be discharging if the + * supply is e.g. only delivering 5V 0.5A. Check current if available. + */ + if (!chip->pdata->enable_current_sense) { *status = POWER_SUPPLY_STATUS_CHARGING; + return 0; + } + + ret = regmap_read(chip->regmap, MAX17042_AvgCurrent, &data); + if (ret < 0) + return ret; + + avg_current = sign_extend32(data, 15); + avg_current *= 1562500 / chip->pdata->r_sns; + + if (avg_current > 0) + *status = POWER_SUPPLY_STATUS_CHARGING; + else + *status = POWER_SUPPLY_STATUS_DISCHARGING; return 0; } From 2d7e6a8376c0eaf260f54b265acadd2e185d7b82 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 22 Jan 2018 18:42:18 +0100 Subject: [PATCH 32/32] power: supply: max17042_battery: Always fall back to default platform-data It is possible to have CONFIG_OF enabled on x86 builds, where we have no firmware provided max17042_platform_data. The CONFIG_OF implementation of max17042_get_pdata would return NULL in this case, causing the probe to fail. Instead always fallback to the default platform-data, as used on x86 sofar, when there is no firmware provided pdata, independent of CONFIG_OF. Signed-off-by: Hans de Goede Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 27 ++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 102b3f71e9a4..35dde81b1c9b 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -886,16 +886,13 @@ static void max17042_init_worker(struct work_struct *work) #ifdef CONFIG_OF static struct max17042_platform_data * -max17042_get_pdata(struct max17042_chip *chip) +max17042_get_of_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; - if (!np) - return dev->platform_data; - pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return NULL; @@ -920,7 +917,8 @@ max17042_get_pdata(struct max17042_chip *chip) return pdata; } -#else +#endif + static struct max17042_reg_data max17047_default_pdata_init_regs[] = { /* * Some firmwares do not set FullSOCThr, Enable End-of-Charge Detection @@ -930,15 +928,12 @@ static struct max17042_reg_data max17047_default_pdata_init_regs[] = { }; static struct max17042_platform_data * -max17042_get_pdata(struct max17042_chip *chip) +max17042_get_default_pdata(struct max17042_chip *chip) { 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 @@ -971,7 +966,21 @@ max17042_get_pdata(struct max17042_chip *chip) return pdata; } + +static struct max17042_platform_data * +max17042_get_pdata(struct max17042_chip *chip) +{ + struct device *dev = &chip->client->dev; + +#ifdef CONFIG_OF + if (dev->of_node) + return max17042_get_of_pdata(chip); #endif + if (dev->platform_data) + return dev->platform_data; + + return max17042_get_default_pdata(chip); +} static const struct regmap_config max17042_regmap_config = { .reg_bits = 8,