From 8fada2d4c213e7625124a24784c460cefc17c700 Mon Sep 17 00:00:00 2001 From: Alex Dewar <alex.dewar90@gmail.com> Date: Sun, 9 Aug 2020 19:54:44 +0100 Subject: power: supply: Add dependency to lego-ev3-battery Kconfig options This battery appears only to be used by a single board (DA850), so it makes sense to add this to the Kconfig file so that users don't build the module unnecessarily. It currently seems to be built for the x86 Arch Linux kernel where it's probably not doing much good. Signed-off-by: Alex Dewar <alex.dewar90@gmail.com> Acked-by: David Lechner <david@lechnology.com> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index faf2830aa152..9f76e2f47ac6 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -164,7 +164,7 @@ config BATTERY_DS2782 config BATTERY_LEGO_EV3 tristate "LEGO MINDSTORMS EV3 battery" - depends on OF && IIO && GPIOLIB + depends on OF && IIO && GPIOLIB && (ARCH_DAVINCI_DA850 || COMPILE_TEST) help Say Y here to enable support for the LEGO MINDSTORMS EV3 battery. -- cgit From 93d660de3f3103d7a337bdb6fc912f1d88cb23b4 Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn <rikard.falkeborn@gmail.com> Date: Tue, 25 Aug 2020 01:22:28 +0200 Subject: power: supply: bq2515x: Constify static variables Constify a number of static variables that are not modified to allow the compiler to put them in read-only memory. Signed-off-by: Rikard Falkeborn <rikard.falkeborn@gmail.com> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/bq2515x_charger.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/bq2515x_charger.c b/drivers/power/supply/bq2515x_charger.c index 36b0c8c98d40..9dcb61ea4cf2 100644 --- a/drivers/power/supply/bq2515x_charger.c +++ b/drivers/power/supply/bq2515x_charger.c @@ -188,7 +188,7 @@ struct bq2515x_device { struct bq2515x_init_data init_data; }; -static struct reg_default bq25150_reg_defaults[] = { +static const struct reg_default bq25150_reg_defaults[] = { {BQ2515X_FLAG0, 0x0}, {BQ2515X_FLAG1, 0x0}, {BQ2515X_FLAG2, 0x0}, @@ -227,7 +227,7 @@ static struct reg_default bq25150_reg_defaults[] = { {BQ2515X_DEVICE_ID, 0x20}, }; -static struct reg_default bq25155_reg_defaults[] = { +static const struct reg_default bq25155_reg_defaults[] = { {BQ2515X_FLAG0, 0x0}, {BQ2515X_FLAG1, 0x0}, {BQ2515X_FLAG2, 0x0}, @@ -886,14 +886,14 @@ static int bq2515x_battery_get_property(struct power_supply *psy, return 0; } -static enum power_supply_property bq2515x_battery_properties[] = { +static const enum power_supply_property bq2515x_battery_properties[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, }; -static enum power_supply_property bq2515x_mains_properties[] = { +static const enum power_supply_property bq2515x_mains_properties[] = { POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_HEALTH, @@ -905,7 +905,7 @@ static enum power_supply_property bq2515x_mains_properties[] = { POWER_SUPPLY_PROP_PRECHARGE_CURRENT, }; -static struct power_supply_desc bq2515x_mains_desc = { +static const struct power_supply_desc bq2515x_mains_desc = { .name = "bq2515x-mains", .type = POWER_SUPPLY_TYPE_MAINS, .get_property = bq2515x_mains_get_property, @@ -915,7 +915,7 @@ static struct power_supply_desc bq2515x_mains_desc = { .property_is_writeable = bq2515x_power_supply_property_is_writeable, }; -static struct power_supply_desc bq2515x_battery_desc = { +static const struct power_supply_desc bq2515x_battery_desc = { .name = "bq2515x-battery", .type = POWER_SUPPLY_TYPE_BATTERY, .get_property = bq2515x_battery_get_property, -- cgit From 2d52f7102b1db9494dc104cacf2d964dfb996857 Mon Sep 17 00:00:00 2001 From: David Heidelberg <david@ixit.cz> Date: Fri, 14 Aug 2020 00:34:03 +0300 Subject: power: supply: smb347-charger: Use resource-managed API Simplify code, more convenient to use with Device Tree. Reviewed-by: Dmitry Osipenko <digetx@gmail.com> Signed-off-by: David Heidelberg <david@ixit.cz> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/smb347-charger.c | 75 ++++++++++++++--------------------- 1 file changed, 29 insertions(+), 46 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/smb347-charger.c b/drivers/power/supply/smb347-charger.c index f99026d81f2a..60894105fcbd 100644 --- a/drivers/power/supply/smb347-charger.c +++ b/drivers/power/supply/smb347-charger.c @@ -836,21 +836,31 @@ static int smb347_irq_init(struct smb347_charger *smb, struct i2c_client *client) { const struct smb347_charger_platform_data *pdata = smb->pdata; - int ret, irq = gpio_to_irq(pdata->irq_gpio); + unsigned long irqflags = IRQF_ONESHOT; + int ret; - ret = gpio_request_one(pdata->irq_gpio, GPIOF_IN, client->name); - if (ret < 0) - goto fail; + /* Requesting GPIO for IRQ is only needed in non-DT way */ + if (!client->irq) { + int irq = gpio_to_irq(pdata->irq_gpio); + + ret = devm_gpio_request_one(smb->dev, pdata->irq_gpio, + GPIOF_IN, client->name); + if (ret < 0) + return ret; + + irqflags |= IRQF_TRIGGER_FALLING; + client->irq = irq; + } - ret = request_threaded_irq(irq, NULL, smb347_interrupt, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - client->name, smb); + ret = devm_request_threaded_irq(smb->dev, client->irq, NULL, + smb347_interrupt, irqflags, + client->name, smb); if (ret < 0) - goto fail_gpio; + return ret; ret = smb347_set_writable(smb, true); if (ret < 0) - goto fail_irq; + return ret; /* * Configure the STAT output to be suitable for interrupts: disable @@ -860,20 +870,10 @@ static int smb347_irq_init(struct smb347_charger *smb, CFG_STAT_ACTIVE_HIGH | CFG_STAT_DISABLED, CFG_STAT_DISABLED); if (ret < 0) - goto fail_readonly; + client->irq = 0; smb347_set_writable(smb, false); - client->irq = irq; - return 0; -fail_readonly: - smb347_set_writable(smb, false); -fail_irq: - free_irq(irq, smb); -fail_gpio: - gpio_free(pdata->irq_gpio); -fail: - client->irq = 0; return ret; } @@ -1251,32 +1251,24 @@ static int smb347_probe(struct i2c_client *client, mains_usb_cfg.num_supplicants = ARRAY_SIZE(battery); mains_usb_cfg.drv_data = smb; if (smb->pdata->use_mains) { - smb->mains = power_supply_register(dev, &smb347_mains_desc, - &mains_usb_cfg); + smb->mains = devm_power_supply_register(dev, &smb347_mains_desc, + &mains_usb_cfg); if (IS_ERR(smb->mains)) return PTR_ERR(smb->mains); } if (smb->pdata->use_usb) { - smb->usb = power_supply_register(dev, &smb347_usb_desc, - &mains_usb_cfg); - if (IS_ERR(smb->usb)) { - if (smb->pdata->use_mains) - power_supply_unregister(smb->mains); + smb->usb = devm_power_supply_register(dev, &smb347_usb_desc, + &mains_usb_cfg); + if (IS_ERR(smb->usb)) return PTR_ERR(smb->usb); - } } battery_cfg.drv_data = smb; - smb->battery = power_supply_register(dev, &smb347_battery_desc, - &battery_cfg); - if (IS_ERR(smb->battery)) { - if (smb->pdata->use_usb) - power_supply_unregister(smb->usb); - if (smb->pdata->use_mains) - power_supply_unregister(smb->mains); + smb->battery = devm_power_supply_register(dev, &smb347_battery_desc, + &battery_cfg); + if (IS_ERR(smb->battery)) return PTR_ERR(smb->battery); - } /* * Interrupt pin is optional. If it is connected, we setup the @@ -1299,17 +1291,8 @@ static int smb347_remove(struct i2c_client *client) { struct smb347_charger *smb = i2c_get_clientdata(client); - if (client->irq) { + if (client->irq) smb347_irq_disable(smb); - free_irq(client->irq, smb); - gpio_free(smb->pdata->irq_gpio); - } - - power_supply_unregister(smb->battery); - if (smb->pdata->use_usb) - power_supply_unregister(smb->usb); - if (smb->pdata->use_mains) - power_supply_unregister(smb->mains); return 0; } -- cgit From 00cda13e339c9f4956a6f036675df2ca5b5a552e Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko <digetx@gmail.com> Date: Fri, 14 Aug 2020 00:34:02 +0300 Subject: power: supply: Support battery temperature device-tree properties The generic battery temperature properties are already supported by the power-supply core. Let's support parsing of the common battery temperature properties from a device-tree. Signed-off-by: Dmitry Osipenko <digetx@gmail.com> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/power_supply_core.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index ccbad435ed12..38e3aa642131 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -579,6 +579,12 @@ int power_supply_get_battery_info(struct power_supply *psy, info->charge_term_current_ua = -EINVAL; info->constant_charge_current_max_ua = -EINVAL; info->constant_charge_voltage_max_uv = -EINVAL; + info->temp_ambient_alert_min = INT_MIN; + info->temp_ambient_alert_max = INT_MAX; + info->temp_alert_min = INT_MIN; + info->temp_alert_max = INT_MAX; + info->temp_min = INT_MIN; + info->temp_max = INT_MAX; info->factory_internal_resistance_uohm = -EINVAL; info->resist_table = NULL; @@ -639,6 +645,19 @@ int power_supply_get_battery_info(struct power_supply *psy, of_property_read_u32(battery_np, "factory-internal-resistance-micro-ohms", &info->factory_internal_resistance_uohm); + of_property_read_u32_index(battery_np, "ambient-celsius", + 0, &info->temp_ambient_alert_min); + of_property_read_u32_index(battery_np, "ambient-celsius", + 1, &info->temp_ambient_alert_max); + of_property_read_u32_index(battery_np, "alert-celsius", + 0, &info->temp_alert_min); + of_property_read_u32_index(battery_np, "alert-celsius", + 1, &info->temp_alert_max); + of_property_read_u32_index(battery_np, "operating-range-celsius", + 0, &info->temp_min); + of_property_read_u32_index(battery_np, "operating-range-celsius", + 1, &info->temp_max); + len = of_property_count_u32_elems(battery_np, "ocv-capacity-celsius"); if (len < 0 && len != -EINVAL) { err = len; -- cgit From 364bec7557ecff4ef959c536abd19d00d94a854d Mon Sep 17 00:00:00 2001 From: David Heidelberg <david@ixit.cz> Date: Fri, 14 Aug 2020 00:34:04 +0300 Subject: power: supply: smb347-charger: Implement device-tree support This patch adds device-tree support to the SMB347 charger driver. All legacy platform data now can be parsed from DT. Because of that and since SMB347 is an I2C client driver, the IRQ number can be passed automatically through client's IRQ variable if it's defined in DT. There is no need to map GPIO to IRQ manually in the case of DT. This patch is based on the original work made by: Jonghwa Lee <jonghwa3.lee@samsung.com> Link: https://patchwork.kernel.org/patch/4284731/ Signed-off-by: Dmitry Osipenko <digetx@gmail.com> Signed-off-by: David Heidelberg <david@ixit.cz> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/smb347-charger.c | 151 ++++++++++++++++++++++++++++++---- 1 file changed, 137 insertions(+), 14 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/smb347-charger.c b/drivers/power/supply/smb347-charger.c index 60894105fcbd..da2c337107bf 100644 --- a/drivers/power/supply/smb347-charger.c +++ b/drivers/power/supply/smb347-charger.c @@ -1180,6 +1180,119 @@ static bool smb347_readable_reg(struct device *dev, unsigned int reg) return smb347_volatile_reg(dev, reg); } +static void smb347_dt_parse_pdata(struct device_node *np, + struct smb347_charger_platform_data *pdata) +{ + pdata->soft_temp_limit_compensation = + SMB347_SOFT_TEMP_COMPENSATE_DEFAULT; + /* + * These properties come from the battery info, still we need to + * pre-initialize the values. See smb347_get_battery_info() below. + */ + pdata->soft_cold_temp_limit = SMB347_TEMP_USE_DEFAULT; + pdata->hard_cold_temp_limit = SMB347_TEMP_USE_DEFAULT; + pdata->soft_hot_temp_limit = SMB347_TEMP_USE_DEFAULT; + pdata->hard_hot_temp_limit = SMB347_TEMP_USE_DEFAULT; + + /* Charging constraints */ + of_property_read_u32(np, "summit,fast-voltage-threshold-microvolt", + &pdata->pre_to_fast_voltage); + of_property_read_u32(np, "summit,mains-current-limit-microamp", + &pdata->mains_current_limit); + of_property_read_u32(np, "summit,usb-current-limit-microamp", + &pdata->usb_hc_current_limit); + + /* For thermometer monitoring */ + of_property_read_u32(np, "summit,chip-temperature-threshold-celsius", + &pdata->chip_temp_threshold); + of_property_read_u32(np, "summit,soft-compensation-method", + &pdata->soft_temp_limit_compensation); + of_property_read_u32(np, "summit,charge-current-compensation-microamp", + &pdata->charge_current_compensation); + + /* Supported charging mode */ + pdata->use_mains = + of_property_read_bool(np, "summit,enable-mains-charging"); + pdata->use_usb = + of_property_read_bool(np, "summit,enable-usb-charging"); + pdata->use_usb_otg = + of_property_read_bool(np, "summit,enable-otg-charging"); + + /* Select charging control */ + of_property_read_u32(np, "summit,enable-charge-control", + &pdata->enable_control); + + /* Interrupt support is optional */ + if (!of_find_property(np, "interrupts", NULL)) + pdata->irq_gpio = -1; +} + +static int smb347_get_battery_info(struct smb347_charger *smb) +{ + struct smb347_charger_platform_data *pdata = (void *)smb->pdata; + struct power_supply_battery_info info = {}; + struct power_supply *supply; + int err; + + if (smb->mains) + supply = smb->mains; + else + supply = smb->usb; + + err = power_supply_get_battery_info(supply, &info); + if (err == -ENXIO || err == -ENODEV) + return 0; + if (err) + return err; + + if (info.constant_charge_current_max_ua != -EINVAL) + pdata->max_charge_current = info.constant_charge_current_max_ua; + + if (info.constant_charge_voltage_max_uv != -EINVAL) + pdata->max_charge_voltage = info.constant_charge_voltage_max_uv; + + if (info.precharge_current_ua != -EINVAL) + pdata->pre_charge_current = info.precharge_current_ua; + + if (info.charge_term_current_ua != -EINVAL) + pdata->termination_current = info.charge_term_current_ua; + + if (info.temp_alert_min != INT_MIN) + pdata->soft_cold_temp_limit = info.temp_alert_min; + + if (info.temp_alert_max != INT_MAX) + pdata->soft_hot_temp_limit = info.temp_alert_max; + + if (info.temp_min != INT_MIN) + pdata->hard_cold_temp_limit = info.temp_min; + + if (info.temp_max != INT_MAX) + pdata->hard_hot_temp_limit = info.temp_max; + + /* Suspend when battery temperature is outside hard limits */ + if (pdata->hard_cold_temp_limit != SMB347_TEMP_USE_DEFAULT || + pdata->hard_hot_temp_limit != SMB347_TEMP_USE_DEFAULT) + pdata->suspend_on_hard_temp_limit = true; + + return 0; +} + +static struct smb347_charger_platform_data + *smb347_get_platdata(struct device *dev) +{ + struct smb347_charger_platform_data *pdata; + + if (dev->of_node) { + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (pdata) + smb347_dt_parse_pdata(dev->of_node, pdata); + } else { + pdata = dev_get_platdata(dev); + } + + return pdata; +} + static const struct regmap_config smb347_regmap = { .reg_bits = 8, .val_bits = 8, @@ -1216,40 +1329,35 @@ static int smb347_probe(struct i2c_client *client, const struct i2c_device_id *id) { static char *battery[] = { "smb347-battery" }; - const struct smb347_charger_platform_data *pdata; struct power_supply_config mains_usb_cfg = {}, battery_cfg = {}; struct device *dev = &client->dev; struct smb347_charger *smb; int ret; - pdata = dev->platform_data; - if (!pdata) - return -EINVAL; - - if (!pdata->use_mains && !pdata->use_usb) - return -EINVAL; - smb = devm_kzalloc(dev, sizeof(*smb), GFP_KERNEL); if (!smb) return -ENOMEM; + smb->pdata = smb347_get_platdata(dev); + if (!smb->pdata) + return -ENODEV; + + if (!smb->pdata->use_mains && !smb->pdata->use_usb) + return -EINVAL; + i2c_set_clientdata(client, smb); mutex_init(&smb->lock); smb->dev = &client->dev; - smb->pdata = pdata; smb->regmap = devm_regmap_init_i2c(client, &smb347_regmap); if (IS_ERR(smb->regmap)) return PTR_ERR(smb->regmap); - ret = smb347_hw_init(smb); - if (ret < 0) - return ret; - mains_usb_cfg.supplied_to = battery; mains_usb_cfg.num_supplicants = ARRAY_SIZE(battery); mains_usb_cfg.drv_data = smb; + mains_usb_cfg.of_node = dev->of_node; if (smb->pdata->use_mains) { smb->mains = devm_power_supply_register(dev, &smb347_mains_desc, &mains_usb_cfg); @@ -1270,11 +1378,19 @@ static int smb347_probe(struct i2c_client *client, if (IS_ERR(smb->battery)) return PTR_ERR(smb->battery); + ret = smb347_get_battery_info(smb); + if (ret) + return ret; + + ret = smb347_hw_init(smb); + if (ret < 0) + return ret; + /* * Interrupt pin is optional. If it is connected, we setup the * interrupt support here. */ - if (pdata->irq_gpio >= 0) { + if (smb->pdata->irq_gpio >= 0) { ret = smb347_irq_init(smb, client); if (ret < 0) { dev_warn(dev, "failed to initialize IRQ: %d\n", ret); @@ -1302,9 +1418,16 @@ static const struct i2c_device_id smb347_id[] = { }; MODULE_DEVICE_TABLE(i2c, smb347_id); +static const struct of_device_id smb3xx_of_match[] = { + { .compatible = "summit,smb347" }, + { }, +}; +MODULE_DEVICE_TABLE(of, smb3xx_of_match); + static struct i2c_driver smb347_driver = { .driver = { .name = "smb347", + .of_match_table = smb3xx_of_match, }, .probe = smb347_probe, .remove = smb347_remove, -- cgit From de76fd29a7dc4f6b96f7777bc5c0d92ed561ff5b Mon Sep 17 00:00:00 2001 From: David Heidelberg <david@ixit.cz> Date: Fri, 14 Aug 2020 00:34:05 +0300 Subject: power: supply: smb347-charger: Support SMB345 and SMB358 SMB345 tested on Nexus 7 2013. Based on: - https://patchwork.kernel.org/patch/4922431/ - https://patchwork.ozlabs.org/patch/666877/ Signed-off-by: David Heidelberg <david@ixit.cz> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/Kconfig | 6 +- drivers/power/supply/smb347-charger.c | 109 ++++++++++++++++++---------------- 2 files changed, 62 insertions(+), 53 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 9f76e2f47ac6..d1ccf17df42e 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -632,12 +632,12 @@ config CHARGER_BQ25890 Say Y to enable support for the TI BQ25890 battery charger. config CHARGER_SMB347 - tristate "Summit Microelectronics SMB347 Battery Charger" + tristate "Summit Microelectronics SMB3XX Battery Charger" depends on I2C select REGMAP_I2C help - Say Y to include support for Summit Microelectronics SMB347 - Battery Charger. + Say Y to include support for Summit Microelectronics SMB345, + SMB347 or SMB358 Battery Charger. config CHARGER_TPS65090 tristate "TPS65090 battery charger driver" diff --git a/drivers/power/supply/smb347-charger.c b/drivers/power/supply/smb347-charger.c index da2c337107bf..90a87e0624a6 100644 --- a/drivers/power/supply/smb347-charger.c +++ b/drivers/power/supply/smb347-charger.c @@ -128,6 +128,7 @@ * @mains: power_supply instance for AC/DC power * @usb: power_supply instance for USB power * @battery: power_supply instance for battery + * @id: SMB charger ID * @mains_online: is AC/DC input connected * @usb_online: is USB input connected * @charging_enabled: is charging enabled @@ -140,64 +141,61 @@ struct smb347_charger { struct power_supply *mains; struct power_supply *usb; struct power_supply *battery; + unsigned int id; bool mains_online; bool usb_online; bool charging_enabled; const struct smb347_charger_platform_data *pdata; }; -/* Fast charge current in uA */ -static const unsigned int fcc_tbl[] = { - 700000, - 900000, - 1200000, - 1500000, - 1800000, - 2000000, - 2200000, - 2500000, +enum smb_charger_chipid { + SMB345, + SMB347, + SMB358, + NUM_CHIP_TYPES, }; +/* Fast charge current in uA */ +static const unsigned int fcc_tbl[NUM_CHIP_TYPES][8] = { + [SMB345] = { 200000, 450000, 600000, 900000, + 1300000, 1500000, 1800000, 2000000 }, + [SMB347] = { 700000, 900000, 1200000, 1500000, + 1800000, 2000000, 2200000, 2500000 }, + [SMB358] = { 200000, 450000, 600000, 900000, + 1300000, 1500000, 1800000, 2000000 }, +}; /* Pre-charge current in uA */ -static const unsigned int pcc_tbl[] = { - 100000, - 150000, - 200000, - 250000, +static const unsigned int pcc_tbl[NUM_CHIP_TYPES][4] = { + [SMB345] = { 150000, 250000, 350000, 450000 }, + [SMB347] = { 100000, 150000, 200000, 250000 }, + [SMB358] = { 150000, 250000, 350000, 450000 }, }; /* Termination current in uA */ -static const unsigned int tc_tbl[] = { - 37500, - 50000, - 100000, - 150000, - 200000, - 250000, - 500000, - 600000, +static const unsigned int tc_tbl[NUM_CHIP_TYPES][8] = { + [SMB345] = { 30000, 40000, 60000, 80000, + 100000, 125000, 150000, 200000 }, + [SMB347] = { 37500, 50000, 100000, 150000, + 200000, 250000, 500000, 600000 }, + [SMB358] = { 30000, 40000, 60000, 80000, + 100000, 125000, 150000, 200000 }, }; /* Input current limit in uA */ -static const unsigned int icl_tbl[] = { - 300000, - 500000, - 700000, - 900000, - 1200000, - 1500000, - 1800000, - 2000000, - 2200000, - 2500000, +static const unsigned int icl_tbl[NUM_CHIP_TYPES][10] = { + [SMB345] = { 300000, 500000, 700000, 1000000, 1500000, + 1800000, 2000000, 2000000, 2000000, 2000000 }, + [SMB347] = { 300000, 500000, 700000, 900000, 1200000, + 1500000, 1800000, 2000000, 2200000, 2500000 }, + [SMB358] = { 300000, 500000, 700000, 1000000, 1500000, + 1800000, 2000000, 2000000, 2000000, 2000000 }, }; /* Charge current compensation in uA */ -static const unsigned int ccc_tbl[] = { - 250000, - 700000, - 900000, - 1200000, +static const unsigned int ccc_tbl[NUM_CHIP_TYPES][4] = { + [SMB345] = { 200000, 450000, 600000, 900000 }, + [SMB347] = { 250000, 700000, 900000, 1200000 }, + [SMB358] = { 200000, 450000, 600000, 900000 }, }; /* Convert register value to current using lookup table */ @@ -352,10 +350,11 @@ static int smb347_start_stop_charging(struct smb347_charger *smb) static int smb347_set_charge_current(struct smb347_charger *smb) { + unsigned int id = smb->id; int ret; if (smb->pdata->max_charge_current) { - ret = current_to_hw(fcc_tbl, ARRAY_SIZE(fcc_tbl), + ret = current_to_hw(fcc_tbl[id], ARRAY_SIZE(fcc_tbl[id]), smb->pdata->max_charge_current); if (ret < 0) return ret; @@ -368,7 +367,7 @@ static int smb347_set_charge_current(struct smb347_charger *smb) } if (smb->pdata->pre_charge_current) { - ret = current_to_hw(pcc_tbl, ARRAY_SIZE(pcc_tbl), + ret = current_to_hw(pcc_tbl[id], ARRAY_SIZE(pcc_tbl[id]), smb->pdata->pre_charge_current); if (ret < 0) return ret; @@ -381,7 +380,7 @@ static int smb347_set_charge_current(struct smb347_charger *smb) } if (smb->pdata->termination_current) { - ret = current_to_hw(tc_tbl, ARRAY_SIZE(tc_tbl), + ret = current_to_hw(tc_tbl[id], ARRAY_SIZE(tc_tbl[id]), smb->pdata->termination_current); if (ret < 0) return ret; @@ -397,10 +396,11 @@ static int smb347_set_charge_current(struct smb347_charger *smb) static int smb347_set_current_limits(struct smb347_charger *smb) { + unsigned int id = smb->id; int ret; if (smb->pdata->mains_current_limit) { - ret = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl), + ret = current_to_hw(icl_tbl[id], ARRAY_SIZE(icl_tbl[id]), smb->pdata->mains_current_limit); if (ret < 0) return ret; @@ -413,7 +413,7 @@ static int smb347_set_current_limits(struct smb347_charger *smb) } if (smb->pdata->usb_hc_current_limit) { - ret = current_to_hw(icl_tbl, ARRAY_SIZE(icl_tbl), + ret = current_to_hw(icl_tbl[id], ARRAY_SIZE(icl_tbl[id]), smb->pdata->usb_hc_current_limit); if (ret < 0) return ret; @@ -463,6 +463,7 @@ static int smb347_set_voltage_limits(struct smb347_charger *smb) static int smb347_set_temp_limits(struct smb347_charger *smb) { + unsigned int id = smb->id; bool enable_therm_monitor = false; int ret = 0; int val; @@ -587,7 +588,7 @@ static int smb347_set_temp_limits(struct smb347_charger *smb) } if (smb->pdata->charge_current_compensation) { - val = current_to_hw(ccc_tbl, ARRAY_SIZE(ccc_tbl), + val = current_to_hw(ccc_tbl[id], ARRAY_SIZE(ccc_tbl[id]), smb->pdata->charge_current_compensation); if (val < 0) return val; @@ -883,6 +884,7 @@ static int smb347_irq_init(struct smb347_charger *smb, */ static int get_const_charge_current(struct smb347_charger *smb) { + unsigned int id = smb->id; int ret, intval; unsigned int v; @@ -898,10 +900,12 @@ static int get_const_charge_current(struct smb347_charger *smb) * and we can detect which table to use from bit 5. */ if (v & 0x20) { - intval = hw_to_current(fcc_tbl, ARRAY_SIZE(fcc_tbl), v & 7); + intval = hw_to_current(fcc_tbl[id], + ARRAY_SIZE(fcc_tbl[id]), v & 7); } else { v >>= 3; - intval = hw_to_current(pcc_tbl, ARRAY_SIZE(pcc_tbl), v & 7); + intval = hw_to_current(pcc_tbl[id], + ARRAY_SIZE(pcc_tbl[id]), v & 7); } return intval; @@ -1349,6 +1353,7 @@ static int smb347_probe(struct i2c_client *client, mutex_init(&smb->lock); smb->dev = &client->dev; + smb->id = id->driver_data; smb->regmap = devm_regmap_init_i2c(client, &smb347_regmap); if (IS_ERR(smb->regmap)) @@ -1413,13 +1418,17 @@ static int smb347_remove(struct i2c_client *client) } static const struct i2c_device_id smb347_id[] = { - { "smb347", 0 }, - { } + { "smb345", SMB345 }, + { "smb347", SMB347 }, + { "smb358", SMB358 }, + { }, }; MODULE_DEVICE_TABLE(i2c, smb347_id); static const struct of_device_id smb3xx_of_match[] = { + { .compatible = "summit,smb345" }, { .compatible = "summit,smb347" }, + { .compatible = "summit,smb358" }, { }, }; MODULE_DEVICE_TABLE(of, smb3xx_of_match); -- cgit From db14d3b45b4da39a02746237d6006fddc3bf241b Mon Sep 17 00:00:00 2001 From: David Heidelberg <david@ixit.cz> Date: Fri, 14 Aug 2020 00:34:06 +0300 Subject: power: supply: smb347-charger: Remove virtual smb347-battery SMB347 is a charger and not a battery driver. Secondly, power-supply core now supports monitored-battery. So the 'fake' battery doesn't do anything useful for us, and thus, it should be removed. Transfer smb347-battery functionality into smb347-mains and smb347-usb. Reviewed-by: Dmitry Osipenko <digetx@gmail.com> Signed-off-by: David Heidelberg <david@ixit.cz> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/smb347-charger.c | 206 ++++++++++------------------------ 1 file changed, 60 insertions(+), 146 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/smb347-charger.c b/drivers/power/supply/smb347-charger.c index 90a87e0624a6..335b6ee494e4 100644 --- a/drivers/power/supply/smb347-charger.c +++ b/drivers/power/supply/smb347-charger.c @@ -127,7 +127,6 @@ * @regmap: pointer to driver regmap * @mains: power_supply instance for AC/DC power * @usb: power_supply instance for USB power - * @battery: power_supply instance for battery * @id: SMB charger ID * @mains_online: is AC/DC input connected * @usb_online: is USB input connected @@ -140,7 +139,6 @@ struct smb347_charger { struct regmap *regmap; struct power_supply *mains; struct power_supply *usb; - struct power_supply *battery; unsigned int id; bool mains_online; bool usb_online; @@ -743,7 +741,10 @@ static irqreturn_t smb347_interrupt(int irq, void *data) */ if (stat_c & STAT_C_CHARGER_ERROR) { dev_err(smb->dev, "charging stopped due to charger error\n"); - power_supply_changed(smb->battery); + if (smb->pdata->use_mains) + power_supply_changed(smb->mains); + if (smb->pdata->use_usb) + power_supply_changed(smb->usb); handled = true; } @@ -753,8 +754,12 @@ static irqreturn_t smb347_interrupt(int irq, void *data) * disabled by the hardware. */ if (irqstat_c & (IRQSTAT_C_TERMINATION_IRQ | IRQSTAT_C_TAPER_IRQ)) { - if (irqstat_c & IRQSTAT_C_TERMINATION_STAT) - power_supply_changed(smb->battery); + if (irqstat_c & IRQSTAT_C_TERMINATION_STAT) { + if (smb->pdata->use_mains) + power_supply_changed(smb->mains); + if (smb->pdata->use_usb) + power_supply_changed(smb->usb); + } dev_dbg(smb->dev, "going to HW maintenance mode\n"); handled = true; } @@ -768,7 +773,10 @@ static irqreturn_t smb347_interrupt(int irq, void *data) if (irqstat_d & IRQSTAT_D_CHARGE_TIMEOUT_STAT) dev_warn(smb->dev, "charging stopped due to timeout\n"); - power_supply_changed(smb->battery); + if (smb->pdata->use_mains) + power_supply_changed(smb->mains); + if (smb->pdata->use_usb) + power_supply_changed(smb->usb); handled = true; } @@ -936,95 +944,19 @@ static int get_const_charge_voltage(struct smb347_charger *smb) return intval; } -static int smb347_mains_get_property(struct power_supply *psy, - enum power_supply_property prop, - union power_supply_propval *val) -{ - struct smb347_charger *smb = power_supply_get_drvdata(psy); - int ret; - - switch (prop) { - case POWER_SUPPLY_PROP_ONLINE: - val->intval = smb->mains_online; - break; - - case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: - ret = get_const_charge_voltage(smb); - if (ret < 0) - return ret; - else - val->intval = ret; - break; - - case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: - ret = get_const_charge_current(smb); - if (ret < 0) - return ret; - else - val->intval = ret; - break; - - default: - return -EINVAL; - } - - return 0; -} - -static enum power_supply_property smb347_mains_properties[] = { - POWER_SUPPLY_PROP_ONLINE, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, -}; - -static int smb347_usb_get_property(struct power_supply *psy, - enum power_supply_property prop, - union power_supply_propval *val) -{ - struct smb347_charger *smb = power_supply_get_drvdata(psy); - int ret; - - switch (prop) { - case POWER_SUPPLY_PROP_ONLINE: - val->intval = smb->usb_online; - break; - - case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: - ret = get_const_charge_voltage(smb); - if (ret < 0) - return ret; - else - val->intval = ret; - break; - - case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: - ret = get_const_charge_current(smb); - if (ret < 0) - return ret; - else - val->intval = ret; - break; - - default: - return -EINVAL; - } - - return 0; -} - -static enum power_supply_property smb347_usb_properties[] = { - POWER_SUPPLY_PROP_ONLINE, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, - POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, -}; - -static int smb347_get_charging_status(struct smb347_charger *smb) +static int smb347_get_charging_status(struct smb347_charger *smb, + struct power_supply *psy) { int ret, status; unsigned int val; - if (!smb347_is_ps_online(smb)) - return POWER_SUPPLY_STATUS_DISCHARGING; + if (psy->desc->type == POWER_SUPPLY_TYPE_USB) { + if (!smb->usb_online) + return POWER_SUPPLY_STATUS_DISCHARGING; + } else { + if (!smb->mains_online) + return POWER_SUPPLY_STATUS_DISCHARGING; + } ret = regmap_read(smb->regmap, STAT_C, &val); if (ret < 0) @@ -1063,29 +995,29 @@ static int smb347_get_charging_status(struct smb347_charger *smb) return status; } -static int smb347_battery_get_property(struct power_supply *psy, - enum power_supply_property prop, - union power_supply_propval *val) +static int smb347_get_property(struct power_supply *psy, + enum power_supply_property prop, + union power_supply_propval *val) { struct smb347_charger *smb = power_supply_get_drvdata(psy); - const struct smb347_charger_platform_data *pdata = smb->pdata; int ret; - ret = smb347_update_ps_status(smb); - if (ret < 0) - return ret; - switch (prop) { case POWER_SUPPLY_PROP_STATUS: - ret = smb347_get_charging_status(smb); + ret = smb347_get_charging_status(smb, psy); if (ret < 0) return ret; val->intval = ret; break; case POWER_SUPPLY_PROP_CHARGE_TYPE: - if (!smb347_is_ps_online(smb)) - return -ENODATA; + if (psy->desc->type == POWER_SUPPLY_TYPE_USB) { + if (!smb->usb_online) + return -ENODATA; + } else { + if (!smb->mains_online) + return -ENODATA; + } /* * We handle trickle and pre-charging the same, and taper @@ -1104,24 +1036,25 @@ static int smb347_battery_get_property(struct power_supply *psy, } break; - case POWER_SUPPLY_PROP_TECHNOLOGY: - val->intval = pdata->battery_info.technology; - break; - - case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: - val->intval = pdata->battery_info.voltage_min_design; - break; - - case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: - val->intval = pdata->battery_info.voltage_max_design; + case POWER_SUPPLY_PROP_ONLINE: + if (psy->desc->type == POWER_SUPPLY_TYPE_USB) + val->intval = smb->usb_online; + else + val->intval = smb->mains_online; break; - case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: - val->intval = pdata->battery_info.charge_full_design; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + ret = get_const_charge_voltage(smb); + if (ret < 0) + return ret; + val->intval = ret; break; - case POWER_SUPPLY_PROP_MODEL_NAME: - val->strval = pdata->battery_info.name; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + ret = get_const_charge_current(smb); + if (ret < 0) + return ret; + val->intval = ret; break; default: @@ -1131,14 +1064,12 @@ static int smb347_battery_get_property(struct power_supply *psy, return 0; } -static enum power_supply_property smb347_battery_properties[] = { +static enum power_supply_property smb347_properties[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_CHARGE_TYPE, - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, - POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, - POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, - POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, }; static bool smb347_volatile_reg(struct device *dev, unsigned int reg) @@ -1308,32 +1239,23 @@ static const struct regmap_config smb347_regmap = { static const struct power_supply_desc smb347_mains_desc = { .name = "smb347-mains", .type = POWER_SUPPLY_TYPE_MAINS, - .get_property = smb347_mains_get_property, - .properties = smb347_mains_properties, - .num_properties = ARRAY_SIZE(smb347_mains_properties), + .get_property = smb347_get_property, + .properties = smb347_properties, + .num_properties = ARRAY_SIZE(smb347_properties), }; static const struct power_supply_desc smb347_usb_desc = { .name = "smb347-usb", .type = POWER_SUPPLY_TYPE_USB, - .get_property = smb347_usb_get_property, - .properties = smb347_usb_properties, - .num_properties = ARRAY_SIZE(smb347_usb_properties), -}; - -static const struct power_supply_desc smb347_battery_desc = { - .name = "smb347-battery", - .type = POWER_SUPPLY_TYPE_BATTERY, - .get_property = smb347_battery_get_property, - .properties = smb347_battery_properties, - .num_properties = ARRAY_SIZE(smb347_battery_properties), + .get_property = smb347_get_property, + .properties = smb347_properties, + .num_properties = ARRAY_SIZE(smb347_properties), }; static int smb347_probe(struct i2c_client *client, const struct i2c_device_id *id) { - static char *battery[] = { "smb347-battery" }; - struct power_supply_config mains_usb_cfg = {}, battery_cfg = {}; + struct power_supply_config mains_usb_cfg = {}; struct device *dev = &client->dev; struct smb347_charger *smb; int ret; @@ -1359,8 +1281,6 @@ static int smb347_probe(struct i2c_client *client, if (IS_ERR(smb->regmap)) return PTR_ERR(smb->regmap); - mains_usb_cfg.supplied_to = battery; - mains_usb_cfg.num_supplicants = ARRAY_SIZE(battery); mains_usb_cfg.drv_data = smb; mains_usb_cfg.of_node = dev->of_node; if (smb->pdata->use_mains) { @@ -1377,12 +1297,6 @@ static int smb347_probe(struct i2c_client *client, return PTR_ERR(smb->usb); } - battery_cfg.drv_data = smb; - smb->battery = devm_power_supply_register(dev, &smb347_battery_desc, - &battery_cfg); - if (IS_ERR(smb->battery)) - return PTR_ERR(smb->battery); - ret = smb347_get_battery_info(smb); if (ret) return ret; -- cgit From 99298de5df92699b1f709dc35c8636d15be21e2e Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko <digetx@gmail.com> Date: Fri, 14 Aug 2020 00:34:07 +0300 Subject: power: supply: smb347-charger: Replace mutex with IRQ disable/enable Let's simply disable/enable IRQ rather than use a mutex that protects from racing with the interrupt handler. The result of this patch is that it's a bit easier now to follow the driver's code. Tested-by: David Heidelberg <david@ixit.cz> Signed-off-by: Dmitry Osipenko <digetx@gmail.com> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/smb347-charger.c | 38 ++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 18 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/smb347-charger.c b/drivers/power/supply/smb347-charger.c index 335b6ee494e4..ec68ab2bce27 100644 --- a/drivers/power/supply/smb347-charger.c +++ b/drivers/power/supply/smb347-charger.c @@ -16,7 +16,6 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/i2c.h> -#include <linux/mutex.h> #include <linux/power_supply.h> #include <linux/power/smb347-charger.h> #include <linux/regmap.h> @@ -122,7 +121,6 @@ /** * struct smb347_charger - smb347 charger instance - * @lock: protects concurrent access to online variables * @dev: pointer to device * @regmap: pointer to driver regmap * @mains: power_supply instance for AC/DC power @@ -134,7 +132,6 @@ * @pdata: pointer to platform data */ struct smb347_charger { - struct mutex lock; struct device *dev; struct regmap *regmap; struct power_supply *mains; @@ -243,11 +240,9 @@ static int smb347_update_ps_status(struct smb347_charger *smb) if (smb->pdata->use_usb) usb = !(val & IRQSTAT_E_USBIN_UV_STAT); - mutex_lock(&smb->lock); ret = smb->mains_online != dc || smb->usb_online != usb; smb->mains_online = dc; smb->usb_online = usb; - mutex_unlock(&smb->lock); return ret; } @@ -263,13 +258,7 @@ static int smb347_update_ps_status(struct smb347_charger *smb) */ static bool smb347_is_ps_online(struct smb347_charger *smb) { - bool ret; - - mutex_lock(&smb->lock); - ret = smb->usb_online || smb->mains_online; - mutex_unlock(&smb->lock); - - return ret; + return smb->usb_online || smb->mains_online; } /** @@ -303,14 +292,13 @@ static int smb347_charging_set(struct smb347_charger *smb, bool enable) return 0; } - mutex_lock(&smb->lock); if (smb->charging_enabled != enable) { ret = regmap_update_bits(smb->regmap, CMD_A, CMD_A_CHG_ENABLED, enable ? CMD_A_CHG_ENABLED : 0); if (!ret) smb->charging_enabled = enable; } - mutex_unlock(&smb->lock); + return ret; } @@ -995,9 +983,9 @@ static int smb347_get_charging_status(struct smb347_charger *smb, return status; } -static int smb347_get_property(struct power_supply *psy, - enum power_supply_property prop, - union power_supply_propval *val) +static int smb347_get_property_locked(struct power_supply *psy, + enum power_supply_property prop, + union power_supply_propval *val) { struct smb347_charger *smb = power_supply_get_drvdata(psy); int ret; @@ -1064,6 +1052,21 @@ static int smb347_get_property(struct power_supply *psy, return 0; } +static int smb347_get_property(struct power_supply *psy, + enum power_supply_property prop, + union power_supply_propval *val) +{ + struct smb347_charger *smb = power_supply_get_drvdata(psy); + struct i2c_client *client = to_i2c_client(smb->dev); + int ret; + + disable_irq(client->irq); + ret = smb347_get_property_locked(psy, prop, val); + enable_irq(client->irq); + + return ret; +} + static enum power_supply_property smb347_properties[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_CHARGE_TYPE, @@ -1273,7 +1276,6 @@ static int smb347_probe(struct i2c_client *client, i2c_set_clientdata(client, smb); - mutex_init(&smb->lock); smb->dev = &client->dev; smb->id = id->driver_data; -- cgit From 5ca937fb5d6870735341d8fdacdd2b49618c35dc Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy <subbaram@codeaurora.org> Date: Thu, 13 Aug 2020 11:34:08 -0700 Subject: power: supply: add wireless type Currently, power_supply framework supports only Battery, UPS, Mains and USB power_supply_type. Add wireless power_supply_type so that the drivers which supports wireless can register a power supply class device with POWER_SUPPLY_TYPE_WIRELESS. Signed-off-by: Subbaraman Narayanamurthy <subbaram@codeaurora.org> Signed-off-by: Guru Das Srinagesh <gurus@codeaurora.org> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/power_supply_sysfs.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/power') diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index 3d383086018c..a616b9d8f43c 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -56,6 +56,7 @@ static const char * const POWER_SUPPLY_TYPE_TEXT[] = { [POWER_SUPPLY_TYPE_USB_PD] = "USB_PD", [POWER_SUPPLY_TYPE_USB_PD_DRP] = "USB_PD_DRP", [POWER_SUPPLY_TYPE_APPLE_BRICK_ID] = "BrickID", + [POWER_SUPPLY_TYPE_WIRELESS] = "Wireless", }; static const char * const POWER_SUPPLY_USB_TYPE_TEXT[] = { -- cgit From 4024810c5aad6b63a7e4de26850ea0c8ea870850 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski <krzk@kernel.org> Date: Wed, 26 Aug 2020 16:48:54 +0200 Subject: power: supply: bq27xxx: Simplify with dev_err_probe() Common pattern of handling deferred probe can be simplified with dev_err_probe(). Less code and also it prints the error value. Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/bq27xxx_battery.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c index a123f6e21f08..617689084ded 100644 --- a/drivers/power/supply/bq27xxx_battery.c +++ b/drivers/power/supply/bq27xxx_battery.c @@ -1992,13 +1992,9 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di) psy_desc->external_power_changed = bq27xxx_external_power_changed; di->bat = power_supply_register_no_ws(di->dev, psy_desc, &psy_cfg); - if (IS_ERR(di->bat)) { - if (PTR_ERR(di->bat) == -EPROBE_DEFER) - dev_dbg(di->dev, "failed to register battery, deferring probe\n"); - else - dev_err(di->dev, "failed to register battery\n"); - return PTR_ERR(di->bat); - } + if (IS_ERR(di->bat)) + return dev_err_probe(di->dev, PTR_ERR(di->bat), + "failed to register battery\n"); bq27xxx_battery_settings(di); bq27xxx_battery_update(di); -- cgit From ec744270c92785772b111ba21a6bb23a8cc2cb4d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski <krzk@kernel.org> Date: Wed, 26 Aug 2020 16:48:55 +0200 Subject: power: supply: cpcap: Simplify with dev_err_probe() Common pattern of handling deferred probe can be simplified with dev_err_probe(). Less code and also it prints the error value. Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/cpcap-battery.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 90eba364664b..295611b3b15e 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -747,11 +747,8 @@ static int cpcap_battery_init_iio(struct cpcap_battery_ddata *ddata) return 0; out_err: - if (error != -EPROBE_DEFER) - dev_err(ddata->dev, "could not initialize VBUS or ID IIO: %i\n", - error); - - return error; + return dev_err_probe(ddata->dev, error, + "could not initialize VBUS or ID IIO\n"); } /* Calibrate coulomb counter */ -- cgit From 52a023a14ea90da4356395f175cf055d6ad8afee Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski <krzk@kernel.org> Date: Wed, 26 Aug 2020 16:48:56 +0200 Subject: power: supply: gpio-charger: Simplify with dev_err_probe() Common pattern of handling deferred probe can be simplified with dev_err_probe(). Less code and also it prints the error value. Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/gpio-charger.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/gpio-charger.c b/drivers/power/supply/gpio-charger.c index 875735d50716..557f879a6499 100644 --- a/drivers/power/supply/gpio-charger.c +++ b/drivers/power/supply/gpio-charger.c @@ -173,10 +173,8 @@ static int gpio_charger_probe(struct platform_device *pdev) gpio_charger->gpiod = gpio_to_desc(pdata->gpio); } else if (IS_ERR(gpio_charger->gpiod)) { /* Just try again if this happens */ - if (PTR_ERR(gpio_charger->gpiod) == -EPROBE_DEFER) - return -EPROBE_DEFER; - dev_err(dev, "error getting GPIO descriptor\n"); - return PTR_ERR(gpio_charger->gpiod); + return dev_err_probe(dev, PTR_ERR(gpio_charger->gpiod), + "error getting GPIO descriptor\n"); } if (gpio_charger->gpiod) { -- cgit From 31873dc23c3fa19b86fb543935f284ebbddf486e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski <krzk@kernel.org> Date: Wed, 26 Aug 2020 16:48:57 +0200 Subject: power: supply: ingenic: Simplify with dev_err_probe() Common pattern of handling deferred probe can be simplified with dev_err_probe(). Less code and also it prints the error value. Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/ingenic-battery.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/ingenic-battery.c b/drivers/power/supply/ingenic-battery.c index dd3d93dfe3eb..32dc77fd9a95 100644 --- a/drivers/power/supply/ingenic-battery.c +++ b/drivers/power/supply/ingenic-battery.c @@ -147,11 +147,9 @@ static int ingenic_battery_probe(struct platform_device *pdev) psy_cfg.of_node = dev->of_node; bat->battery = devm_power_supply_register(dev, desc, &psy_cfg); - if (IS_ERR(bat->battery)) { - if (PTR_ERR(bat->battery) != -EPROBE_DEFER) - dev_err(dev, "Unable to register battery\n"); - return PTR_ERR(bat->battery); - } + if (IS_ERR(bat->battery)) + return dev_err_probe(dev, PTR_ERR(bat->battery), + "Unable to register battery\n"); ret = power_supply_get_battery_info(bat->battery, &bat->info); if (ret) { -- cgit From e03e3601fb9fc2aa5dc8b483944006c01ffa6aa5 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski <krzk@kernel.org> Date: Wed, 26 Aug 2020 16:48:58 +0200 Subject: power: supply: lego_ev3: Simplify with dev_err_probe() Common pattern of handling deferred probe can be simplified with dev_err_probe(). Less code and also it prints the error value. Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org> Reviewed-by: David Lechner <david@lechnology.com> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/lego_ev3_battery.c | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/lego_ev3_battery.c b/drivers/power/supply/lego_ev3_battery.c index 1ae3710909b7..ccb00be38e2c 100644 --- a/drivers/power/supply/lego_ev3_battery.c +++ b/drivers/power/supply/lego_ev3_battery.c @@ -166,27 +166,21 @@ static int lego_ev3_battery_probe(struct platform_device *pdev) batt->iio_v = devm_iio_channel_get(dev, "voltage"); err = PTR_ERR_OR_ZERO(batt->iio_v); - if (err) { - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get voltage iio channel\n"); - return err; - } + if (err) + return dev_err_probe(dev, err, + "Failed to get voltage iio channel\n"); batt->iio_i = devm_iio_channel_get(dev, "current"); err = PTR_ERR_OR_ZERO(batt->iio_i); - if (err) { - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get current iio channel\n"); - return err; - } + if (err) + return dev_err_probe(dev, err, + "Failed to get current iio channel\n"); batt->rechargeable_gpio = devm_gpiod_get(dev, "rechargeable", GPIOD_IN); err = PTR_ERR_OR_ZERO(batt->rechargeable_gpio); - if (err) { - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get rechargeable gpio\n"); - return err; - } + if (err) + return dev_err_probe(dev, err, + "Failed to get rechargeable gpio\n"); /* * The rechargeable battery indication switch cannot be changed without -- cgit From 17529bcf0ae20f1ac6d7846762bf0c6ad91dbb7f Mon Sep 17 00:00:00 2001 From: Linus Walleij <linus.walleij@linaro.org> Date: Thu, 27 Aug 2020 10:48:28 +0200 Subject: power: supply: gpio-charger: Convert to GPIO descriptors This converts the GPIO charger to use exclusively GPIO descriptors, moving the two remaining platforms passing global GPIO numbers over to using a GPIO descriptor table. Signed-off-by: Linus Walleij <linus.walleij@linaro.org> Cc: Robert Jarzmik <robert.jarzmik@free.fr> Cc: Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/gpio-charger.c | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/gpio-charger.c b/drivers/power/supply/gpio-charger.c index 875735d50716..ae778f110101 100644 --- a/drivers/power/supply/gpio-charger.c +++ b/drivers/power/supply/gpio-charger.c @@ -5,7 +5,6 @@ */ #include <linux/device.h> -#include <linux/gpio.h> /* For legacy platform data */ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/kernel.h> @@ -131,7 +130,6 @@ static int gpio_charger_probe(struct platform_device *pdev) struct power_supply_desc *charger_desc; struct gpio_desc *charge_status; int charge_status_irq; - unsigned long flags; int ret; int num_props = 0; @@ -149,29 +147,7 @@ static int gpio_charger_probe(struct platform_device *pdev) * boardfile descriptor tables. It's good to try this first. */ gpio_charger->gpiod = devm_gpiod_get_optional(dev, NULL, GPIOD_IN); - - /* - * Fallback to legacy platform data method, if no GPIO is specified - * using boardfile descriptor tables. - */ - if (!gpio_charger->gpiod && pdata) { - /* Non-DT: use legacy GPIO numbers */ - if (!gpio_is_valid(pdata->gpio)) { - dev_err(dev, "Invalid gpio pin in pdata\n"); - return -EINVAL; - } - flags = GPIOF_IN; - if (pdata->gpio_active_low) - flags |= GPIOF_ACTIVE_LOW; - ret = devm_gpio_request_one(dev, pdata->gpio, flags, - dev_name(dev)); - if (ret) { - dev_err(dev, "Failed to request gpio pin: %d\n", ret); - return ret; - } - /* Then convert this to gpiod for now */ - gpio_charger->gpiod = gpio_to_desc(pdata->gpio); - } else if (IS_ERR(gpio_charger->gpiod)) { + if (IS_ERR(gpio_charger->gpiod)) { /* Just try again if this happens */ if (PTR_ERR(gpio_charger->gpiod) == -EPROBE_DEFER) return -EPROBE_DEFER; -- cgit From be2919d8355e4651386ad2fb61ddb6efe4533b1b Mon Sep 17 00:00:00 2001 From: Sebastian Reichel <sebastian.reichel@collabora.com> Date: Sat, 6 Jun 2020 00:44:00 +0200 Subject: power: supply: gpio-charger: add charge-current-limit feature Add new charge-current-limit feature to gpio-charger. Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/gpio-charger.c | 140 ++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/supply/gpio-charger.c b/drivers/power/supply/gpio-charger.c index d7aff28f7e7e..68212b39785b 100644 --- a/drivers/power/supply/gpio-charger.c +++ b/drivers/power/supply/gpio-charger.c @@ -17,7 +17,13 @@ #include <linux/power/gpio-charger.h> +struct gpio_mapping { + u32 limit_ua; + u32 gpiodata; +} __packed; + struct gpio_charger { + struct device *dev; unsigned int irq; unsigned int charge_status_irq; bool wakeup_enabled; @@ -26,6 +32,11 @@ struct gpio_charger { struct power_supply_desc charger_desc; struct gpio_desc *gpiod; struct gpio_desc *charge_status; + + struct gpio_descs *current_limit_gpios; + struct gpio_mapping *current_limit_map; + u32 current_limit_map_size; + u32 charge_current_limit; }; static irqreturn_t gpio_charger_irq(int irq, void *devid) @@ -42,6 +53,35 @@ static inline struct gpio_charger *psy_to_gpio_charger(struct power_supply *psy) return power_supply_get_drvdata(psy); } +static int set_charge_current_limit(struct gpio_charger *gpio_charger, int val) +{ + struct gpio_mapping mapping; + int ndescs = gpio_charger->current_limit_gpios->ndescs; + struct gpio_desc **gpios = gpio_charger->current_limit_gpios->desc; + int i; + + if (!gpio_charger->current_limit_map_size) + return -EINVAL; + + for (i = 0; i < gpio_charger->current_limit_map_size; i++) { + if (gpio_charger->current_limit_map[i].limit_ua <= val) + break; + } + mapping = gpio_charger->current_limit_map[i]; + + for (i = 0; i < ndescs; i++) { + bool val = (mapping.gpiodata >> i) & 1; + gpiod_set_value_cansleep(gpios[ndescs-i-1], val); + } + + gpio_charger->charge_current_limit = mapping.limit_ua; + + dev_dbg(gpio_charger->dev, "set charge current limit to %d (requested: %d)\n", + gpio_charger->charge_current_limit, val); + + return 0; +} + static int gpio_charger_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { @@ -57,6 +97,9 @@ static int gpio_charger_get_property(struct power_supply *psy, else val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + val->intval = gpio_charger->charge_current_limit; + break; default: return -EINVAL; } @@ -64,6 +107,34 @@ static int gpio_charger_get_property(struct power_supply *psy, return 0; } +static int gpio_charger_set_property(struct power_supply *psy, + enum power_supply_property psp, const union power_supply_propval *val) +{ + struct gpio_charger *gpio_charger = psy_to_gpio_charger(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + return set_charge_current_limit(gpio_charger, val->intval); + default: + return -EINVAL; + } + + return 0; +} + +static int gpio_charger_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + return 1; + default: + break; + } + + return 0; +} + static enum power_supply_type gpio_charger_get_type(struct device *dev) { const char *chargetype; @@ -111,6 +182,61 @@ static int gpio_charger_get_irq(struct device *dev, void *dev_id, return irq; } +static int init_charge_current_limit(struct device *dev, + struct gpio_charger *gpio_charger) +{ + int i, len; + u32 cur_limit = U32_MAX; + + gpio_charger->current_limit_gpios = devm_gpiod_get_array_optional(dev, + "charge-current-limit", GPIOD_OUT_LOW); + if (IS_ERR(gpio_charger->current_limit_gpios)) { + dev_err(dev, "error getting current-limit GPIOs\n"); + return PTR_ERR(gpio_charger->current_limit_gpios); + } + + if (!gpio_charger->current_limit_gpios) + return 0; + + len = device_property_read_u32_array(dev, "charge-current-limit-mapping", + NULL, 0); + if (len < 0) + return len; + + if (len == 0 || len % 2) { + dev_err(dev, "invalid charge-current-limit-mapping length\n"); + return -EINVAL; + } + + gpio_charger->current_limit_map = devm_kmalloc_array(dev, + len / 2, sizeof(*gpio_charger->current_limit_map), GFP_KERNEL); + if (!gpio_charger->current_limit_map) + return -ENOMEM; + + gpio_charger->current_limit_map_size = len / 2; + + len = device_property_read_u32_array(dev, "charge-current-limit-mapping", + (u32*) gpio_charger->current_limit_map, len); + if (len < 0) + return len; + + for (i=0; i < gpio_charger->current_limit_map_size; i++) { + if (gpio_charger->current_limit_map[i].limit_ua > cur_limit) { + dev_err(dev, "charge-current-limit-mapping not sorted by current in descending order\n"); + return -EINVAL; + } + + cur_limit = gpio_charger->current_limit_map[i].limit_ua; + } + + /* default to smallest current limitation for safety reasons */ + len = gpio_charger->current_limit_map_size - 1; + set_charge_current_limit(gpio_charger, + gpio_charger->current_limit_map[len].limit_ua); + + return 0; +} + /* * The entries will be overwritten by driver's probe routine depending * on the available features. This list ensures, that the array is big @@ -119,6 +245,7 @@ static int gpio_charger_get_irq(struct device *dev, void *dev_id, static enum power_supply_property gpio_charger_properties[] = { POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, }; static int gpio_charger_probe(struct platform_device *pdev) @@ -141,6 +268,7 @@ static int gpio_charger_probe(struct platform_device *pdev) gpio_charger = devm_kzalloc(dev, sizeof(*gpio_charger), GFP_KERNEL); if (!gpio_charger) return -ENOMEM; + gpio_charger->dev = dev; /* * This will fetch a GPIO descriptor from device tree, ACPI or @@ -167,10 +295,22 @@ static int gpio_charger_probe(struct platform_device *pdev) num_props++; } + ret = init_charge_current_limit(dev, gpio_charger); + if (ret < 0) + return ret; + if (gpio_charger->current_limit_map) { + gpio_charger_properties[num_props] = + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX; + num_props++; + } + charger_desc = &gpio_charger->charger_desc; charger_desc->properties = gpio_charger_properties; charger_desc->num_properties = num_props; charger_desc->get_property = gpio_charger_get_property; + charger_desc->set_property = gpio_charger_set_property; + charger_desc->property_is_writeable = + gpio_charger_property_is_writeable; psy_cfg.of_node = dev->of_node; psy_cfg.drv_data = gpio_charger; -- cgit From 52bef41f606b036541f71f529819e5889ab1bbeb Mon Sep 17 00:00:00 2001 From: Ikjoon Jang <ikjn@chromium.org> Date: Tue, 11 Aug 2020 12:41:41 +0800 Subject: power: supply: sbs-battery: remove unused enable_detection flags Remove unused enable_detection flag which is always true after the device is proved. Signed-off-by: Ikjoon Jang <ikjn@chromium.org> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/sbs-battery.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index 49c3508a6b79..5dfb87291c52 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -193,7 +193,6 @@ struct sbs_info { struct power_supply *power_supply; bool is_present; struct gpio_desc *gpio_detect; - bool enable_detection; bool charger_broadcasts; int last_state; int poll_time; @@ -961,9 +960,6 @@ static int sbs_get_property(struct power_supply *psy, return -EINVAL; } - if (!chip->enable_detection) - goto done; - if (!chip->gpio_detect && chip->is_present != (ret >= 0)) { sbs_update_presence(chip, (ret >= 0)); @@ -1092,7 +1088,6 @@ static int sbs_probe(struct i2c_client *client) chip->flags = (u32)(uintptr_t)device_get_match_data(&client->dev); chip->client = client; - chip->enable_detection = false; psy_cfg.of_node = client->dev.of_node; psy_cfg.drv_data = chip; chip->last_state = POWER_SUPPLY_STATUS_UNKNOWN; @@ -1162,6 +1157,8 @@ skip_gpio: } } + INIT_DELAYED_WORK(&chip->work, sbs_delayed_work); + chip->power_supply = devm_power_supply_register(&client->dev, sbs_desc, &psy_cfg); if (IS_ERR(chip->power_supply)) { @@ -1174,10 +1171,6 @@ skip_gpio: dev_info(&client->dev, "%s: battery gas gauge device registered\n", client->name); - INIT_DELAYED_WORK(&chip->work, sbs_delayed_work); - - chip->enable_detection = true; - return 0; exit_psupply: -- cgit From 2c4bf6983402660569241991b86b6ca72af41701 Mon Sep 17 00:00:00 2001 From: Ikjoon Jang <ikjn@chromium.org> Date: Thu, 13 Aug 2020 13:10:07 +0800 Subject: power: supply: sbs-battery: combine get_presence_and_health This patch enables calling sbs_get_battery_presence_and_health() without checking its chip type. No functional changes. Signed-off-by: Ikjoon Jang <ikjn@chromium.org> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/sbs-battery.c | 73 +++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 37 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index 5dfb87291c52..6273211cd673 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -473,37 +473,6 @@ static bool sbs_bat_needs_calibration(struct i2c_client *client) return !!(ret & BIT(7)); } -static int sbs_get_battery_presence_and_health( - struct i2c_client *client, enum power_supply_property psp, - union power_supply_propval *val) -{ - int ret; - - /* Dummy command; if it succeeds, battery is present. */ - ret = sbs_read_word_data(client, sbs_data[REG_STATUS].addr); - - if (ret < 0) { /* battery not present*/ - if (psp == POWER_SUPPLY_PROP_PRESENT) { - val->intval = 0; - return 0; - } - return ret; - } - - if (psp == POWER_SUPPLY_PROP_PRESENT) - val->intval = 1; /* battery present */ - else { /* POWER_SUPPLY_PROP_HEALTH */ - if (sbs_bat_needs_calibration(client)) { - val->intval = POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED; - } else { - /* SBS spec doesn't have a general health command. */ - val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; - } - } - - return 0; -} - static int sbs_get_ti_battery_presence_and_health( struct i2c_client *client, enum power_supply_property psp, union power_supply_propval *val) @@ -562,6 +531,41 @@ static int sbs_get_ti_battery_presence_and_health( return 0; } +static int sbs_get_battery_presence_and_health( + struct i2c_client *client, enum power_supply_property psp, + union power_supply_propval *val) +{ + struct sbs_info *chip = i2c_get_clientdata(client); + int ret; + + if (chip->flags & SBS_FLAGS_TI_BQ20ZX5) + return sbs_get_ti_battery_presence_and_health(client, psp, val); + + /* Dummy command; if it succeeds, battery is present. */ + ret = sbs_read_word_data(client, sbs_data[REG_STATUS].addr); + + if (ret < 0) { /* battery not present*/ + if (psp == POWER_SUPPLY_PROP_PRESENT) { + val->intval = 0; + return 0; + } + return ret; + } + + if (psp == POWER_SUPPLY_PROP_PRESENT) + val->intval = 1; /* battery present */ + else { /* POWER_SUPPLY_PROP_HEALTH */ + if (sbs_bat_needs_calibration(client)) { + val->intval = POWER_SUPPLY_HEALTH_CALIBRATION_REQUIRED; + } else { + /* SBS spec doesn't have a general health command. */ + val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; + } + } + + return 0; +} + static int sbs_get_battery_property(struct i2c_client *client, int reg_offset, enum power_supply_property psp, union power_supply_propval *val) @@ -864,12 +868,7 @@ static int sbs_get_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_PRESENT: case POWER_SUPPLY_PROP_HEALTH: - if (chip->flags & SBS_FLAGS_TI_BQ20ZX5) - ret = sbs_get_ti_battery_presence_and_health(client, - psp, val); - else - ret = sbs_get_battery_presence_and_health(client, psp, - val); + ret = sbs_get_battery_presence_and_health(client, psp, val); /* this can only be true if no gpio is used */ if (psp == POWER_SUPPLY_PROP_PRESENT) -- cgit From c24b9a741b5f292112f2f75091f06a561b665770 Mon Sep 17 00:00:00 2001 From: Colin Ian King <colin.king@canonical.com> Date: Wed, 5 Aug 2020 11:38:57 +0100 Subject: power: supply: pm2301_charger: fix spelling mistake "chargind" -> "charging" There is a spelling mistake in a dev_dbg message. Fix it. Signed-off-by: Colin Ian King <colin.king@canonical.com> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/pm2301_charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/pm2301_charger.c b/drivers/power/supply/pm2301_charger.c index 17749fc90e16..787867805944 100644 --- a/drivers/power/supply/pm2301_charger.c +++ b/drivers/power/supply/pm2301_charger.c @@ -396,7 +396,7 @@ static int pm2_int_reg3(void *pm2_data, int val) if (val & (PM2XXX_INT4_ITCHARGINGON)) { dev_dbg(pm2->dev , - "chargind operation has started\n"); + "charging operation has started\n"); } if (val & (PM2XXX_INT4_ITVRESUME)) { -- cgit From 44ff56c022c03e69c0eae1d25cf4107be3875630 Mon Sep 17 00:00:00 2001 From: Dan Murphy <dmurphy@ti.com> Date: Thu, 30 Jul 2020 09:31:21 -0500 Subject: power: bq27xxx: Update to SPDX licensing Update the license to the SPDX licensing format. Signed-off-by: Dan Murphy <dmurphy@ti.com> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/bq27xxx_battery.c | 9 +-------- drivers/power/supply/bq27xxx_battery_hdq.c | 9 +-------- drivers/power/supply/bq27xxx_battery_i2c.c | 10 +--------- 3 files changed, 3 insertions(+), 25 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c index 617689084ded..92de7b720182 100644 --- a/drivers/power/supply/bq27xxx_battery.c +++ b/drivers/power/supply/bq27xxx_battery.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * BQ27xxx battery driver * @@ -9,14 +10,6 @@ * * Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc. * - * This package is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. - * * Datasheets: * https://www.ti.com/product/bq27000 * https://www.ti.com/product/bq27200 diff --git a/drivers/power/supply/bq27xxx_battery_hdq.c b/drivers/power/supply/bq27xxx_battery_hdq.c index 29771967df2e..12b10dad77d3 100644 --- a/drivers/power/supply/bq27xxx_battery_hdq.c +++ b/drivers/power/supply/bq27xxx_battery_hdq.c @@ -1,16 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * BQ27xxx battery monitor HDQ/1-wire driver * * Copyright (C) 2007-2017 Texas Instruments Incorporated - https://www.ti.com/ * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/kernel.h> diff --git a/drivers/power/supply/bq27xxx_battery_i2c.c b/drivers/power/supply/bq27xxx_battery_i2c.c index ab02456d69e5..765873dfc495 100644 --- a/drivers/power/supply/bq27xxx_battery_i2c.c +++ b/drivers/power/supply/bq27xxx_battery_i2c.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * BQ27xxx battery monitor I2C driver * * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/ * Andrew F. Davis <afd@ti.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/i2c.h> -- cgit From b6f3e21b928a8ae7959a0d79203b80bd70120768 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel <sebastian.reichel@collabora.com> Date: Wed, 26 Aug 2020 16:41:58 +0200 Subject: power: supply: smb347-charger: Drop pdata support There are no platforms using the pdata support, so let's drop it to simplify the driver. Reviewed-by: Dmitry Osipenko <digetx@gmail.com> Tested-by: Dmitry Osipenko <digetx@gmail.com> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/smb347-charger.c | 287 +++++++++++++++++++--------------- 1 file changed, 159 insertions(+), 128 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/smb347-charger.c b/drivers/power/supply/smb347-charger.c index ec68ab2bce27..b182727dfc90 100644 --- a/drivers/power/supply/smb347-charger.c +++ b/drivers/power/supply/smb347-charger.c @@ -17,9 +17,16 @@ #include <linux/interrupt.h> #include <linux/i2c.h> #include <linux/power_supply.h> -#include <linux/power/smb347-charger.h> #include <linux/regmap.h> +#include <dt-bindings/power/summit,smb347-charger.h> + +/* Use the default compensation method */ +#define SMB3XX_SOFT_TEMP_COMPENSATE_DEFAULT -1 + +/* Use default factory programmed value for hard/soft temperature limit */ +#define SMB3XX_TEMP_USE_DEFAULT -273 + /* * Configuration registers. These are mirrored to volatile RAM and can be * written once %CMD_A_ALLOW_WRITE is set in %CMD_A register. They will be @@ -129,7 +136,52 @@ * @mains_online: is AC/DC input connected * @usb_online: is USB input connected * @charging_enabled: is charging enabled - * @pdata: pointer to platform data + * @max_charge_current: maximum current (in uA) the battery can be charged + * @max_charge_voltage: maximum voltage (in uV) the battery can be charged + * @pre_charge_current: current (in uA) to use in pre-charging phase + * @termination_current: current (in uA) used to determine when the + * charging cycle terminates + * @pre_to_fast_voltage: voltage (in uV) treshold used for transitioning to + * pre-charge to fast charge mode + * @mains_current_limit: maximum input current drawn from AC/DC input (in uA) + * @usb_hc_current_limit: maximum input high current (in uA) drawn from USB + * input + * @chip_temp_threshold: die temperature where device starts limiting charge + * current [%100 - %130] (in degree C) + * @soft_cold_temp_limit: soft cold temperature limit [%0 - %15] (in degree C), + * granularity is 5 deg C. + * @soft_hot_temp_limit: soft hot temperature limit [%40 - %55] (in degree C), + * granularity is 5 deg C. + * @hard_cold_temp_limit: hard cold temperature limit [%-5 - %10] (in degree C), + * granularity is 5 deg C. + * @hard_hot_temp_limit: hard hot temperature limit [%50 - %65] (in degree C), + * granularity is 5 deg C. + * @suspend_on_hard_temp_limit: suspend charging when hard limit is hit + * @soft_temp_limit_compensation: compensation method when soft temperature + * limit is hit + * @charge_current_compensation: current (in uA) for charging compensation + * current when temperature hits soft limits + * @use_mains: AC/DC input can be used + * @use_usb: USB input can be used + * @use_usb_otg: USB OTG output can be used (not implemented yet) + * @enable_control: how charging enable/disable is controlled + * (driver/pin controls) + * + * @use_main, @use_usb, and @use_usb_otg are means to enable/disable + * hardware support for these. This is useful when we want to have for + * example OTG charging controlled via OTG transceiver driver and not by + * the SMB347 hardware. + * + * Hard and soft temperature limit values are given as described in the + * device data sheet and assuming NTC beta value is %3750. Even if this is + * not the case, these values should be used. They can be mapped to the + * corresponding NTC beta values with the help of table %2 in the data + * sheet. So for example if NTC beta is %3375 and we want to program hard + * hot limit to be %53 deg C, @hard_hot_temp_limit should be set to %50. + * + * If zero value is given in any of the current and voltage values, the + * factory programmed default will be used. For soft/hard temperature + * values, pass in %SMB3XX_TEMP_USE_DEFAULT instead. */ struct smb347_charger { struct device *dev; @@ -140,7 +192,26 @@ struct smb347_charger { bool mains_online; bool usb_online; bool charging_enabled; - const struct smb347_charger_platform_data *pdata; + + unsigned int max_charge_current; + unsigned int max_charge_voltage; + unsigned int pre_charge_current; + unsigned int termination_current; + unsigned int pre_to_fast_voltage; + unsigned int mains_current_limit; + unsigned int usb_hc_current_limit; + unsigned int chip_temp_threshold; + int soft_cold_temp_limit; + int soft_hot_temp_limit; + int hard_cold_temp_limit; + int hard_hot_temp_limit; + bool suspend_on_hard_temp_limit; + unsigned int soft_temp_limit_compensation; + unsigned int charge_current_compensation; + bool use_mains; + bool use_usb; + bool use_usb_otg; + unsigned int enable_control; }; enum smb_charger_chipid { @@ -235,9 +306,9 @@ static int smb347_update_ps_status(struct smb347_charger *smb) * Dc and usb are set depending on whether they are enabled in * platform data _and_ whether corresponding undervoltage is set. */ - if (smb->pdata->use_mains) + if (smb->use_mains) dc = !(val & IRQSTAT_E_DCIN_UV_STAT); - if (smb->pdata->use_usb) + if (smb->use_usb) usb = !(val & IRQSTAT_E_USBIN_UV_STAT); ret = smb->mains_online != dc || smb->usb_online != usb; @@ -287,7 +358,7 @@ static int smb347_charging_set(struct smb347_charger *smb, bool enable) { int ret = 0; - if (smb->pdata->enable_control != SMB347_CHG_ENABLE_SW) { + if (smb->enable_control != SMB3XX_CHG_ENABLE_SW) { dev_dbg(smb->dev, "charging enable/disable in SW disabled\n"); return 0; } @@ -339,9 +410,9 @@ static int smb347_set_charge_current(struct smb347_charger *smb) unsigned int id = smb->id; int ret; - if (smb->pdata->max_charge_current) { + if (smb->max_charge_current) { ret = current_to_hw(fcc_tbl[id], ARRAY_SIZE(fcc_tbl[id]), - smb->pdata->max_charge_current); + smb->max_charge_current); if (ret < 0) return ret; @@ -352,9 +423,9 @@ static int smb347_set_charge_current(struct smb347_charger *smb) return ret; } - if (smb->pdata->pre_charge_current) { + if (smb->pre_charge_current) { ret = current_to_hw(pcc_tbl[id], ARRAY_SIZE(pcc_tbl[id]), - smb->pdata->pre_charge_current); + smb->pre_charge_current); if (ret < 0) return ret; @@ -365,9 +436,9 @@ static int smb347_set_charge_current(struct smb347_charger *smb) return ret; } - if (smb->pdata->termination_current) { + if (smb->termination_current) { ret = current_to_hw(tc_tbl[id], ARRAY_SIZE(tc_tbl[id]), - smb->pdata->termination_current); + smb->termination_current); if (ret < 0) return ret; @@ -385,9 +456,9 @@ static int smb347_set_current_limits(struct smb347_charger *smb) unsigned int id = smb->id; int ret; - if (smb->pdata->mains_current_limit) { + if (smb->mains_current_limit) { ret = current_to_hw(icl_tbl[id], ARRAY_SIZE(icl_tbl[id]), - smb->pdata->mains_current_limit); + smb->mains_current_limit); if (ret < 0) return ret; @@ -398,9 +469,9 @@ static int smb347_set_current_limits(struct smb347_charger *smb) return ret; } - if (smb->pdata->usb_hc_current_limit) { + if (smb->usb_hc_current_limit) { ret = current_to_hw(icl_tbl[id], ARRAY_SIZE(icl_tbl[id]), - smb->pdata->usb_hc_current_limit); + smb->usb_hc_current_limit); if (ret < 0) return ret; @@ -417,8 +488,8 @@ static int smb347_set_voltage_limits(struct smb347_charger *smb) { int ret; - if (smb->pdata->pre_to_fast_voltage) { - ret = smb->pdata->pre_to_fast_voltage; + if (smb->pre_to_fast_voltage) { + ret = smb->pre_to_fast_voltage; /* uV */ ret = clamp_val(ret, 2400000, 3000000) - 2400000; @@ -431,8 +502,8 @@ static int smb347_set_voltage_limits(struct smb347_charger *smb) return ret; } - if (smb->pdata->max_charge_voltage) { - ret = smb->pdata->max_charge_voltage; + if (smb->max_charge_voltage) { + ret = smb->max_charge_voltage; /* uV */ ret = clamp_val(ret, 3500000, 4500000) - 3500000; @@ -454,8 +525,8 @@ static int smb347_set_temp_limits(struct smb347_charger *smb) int ret = 0; int val; - if (smb->pdata->chip_temp_threshold) { - val = smb->pdata->chip_temp_threshold; + if (smb->chip_temp_threshold) { + val = smb->chip_temp_threshold; /* degree C */ val = clamp_val(val, 100, 130) - 100; @@ -468,8 +539,8 @@ static int smb347_set_temp_limits(struct smb347_charger *smb) return ret; } - if (smb->pdata->soft_cold_temp_limit != SMB347_TEMP_USE_DEFAULT) { - val = smb->pdata->soft_cold_temp_limit; + if (smb->soft_cold_temp_limit != SMB3XX_TEMP_USE_DEFAULT) { + val = smb->soft_cold_temp_limit; val = clamp_val(val, 0, 15); val /= 5; @@ -485,8 +556,8 @@ static int smb347_set_temp_limits(struct smb347_charger *smb) enable_therm_monitor = true; } - if (smb->pdata->soft_hot_temp_limit != SMB347_TEMP_USE_DEFAULT) { - val = smb->pdata->soft_hot_temp_limit; + if (smb->soft_hot_temp_limit != SMB3XX_TEMP_USE_DEFAULT) { + val = smb->soft_hot_temp_limit; val = clamp_val(val, 40, 55) - 40; val /= 5; @@ -500,8 +571,8 @@ static int smb347_set_temp_limits(struct smb347_charger *smb) enable_therm_monitor = true; } - if (smb->pdata->hard_cold_temp_limit != SMB347_TEMP_USE_DEFAULT) { - val = smb->pdata->hard_cold_temp_limit; + if (smb->hard_cold_temp_limit != SMB3XX_TEMP_USE_DEFAULT) { + val = smb->hard_cold_temp_limit; val = clamp_val(val, -5, 10) + 5; val /= 5; @@ -517,8 +588,8 @@ static int smb347_set_temp_limits(struct smb347_charger *smb) enable_therm_monitor = true; } - if (smb->pdata->hard_hot_temp_limit != SMB347_TEMP_USE_DEFAULT) { - val = smb->pdata->hard_hot_temp_limit; + if (smb->hard_hot_temp_limit != SMB3XX_TEMP_USE_DEFAULT) { + val = smb->hard_hot_temp_limit; val = clamp_val(val, 50, 65) - 50; val /= 5; @@ -549,16 +620,16 @@ static int smb347_set_temp_limits(struct smb347_charger *smb) return ret; } - if (smb->pdata->suspend_on_hard_temp_limit) { + if (smb->suspend_on_hard_temp_limit) { ret = regmap_update_bits(smb->regmap, CFG_SYSOK, CFG_SYSOK_SUSPEND_HARD_LIMIT_DISABLED, 0); if (ret < 0) return ret; } - if (smb->pdata->soft_temp_limit_compensation != - SMB347_SOFT_TEMP_COMPENSATE_DEFAULT) { - val = smb->pdata->soft_temp_limit_compensation & 0x3; + if (smb->soft_temp_limit_compensation != + SMB3XX_SOFT_TEMP_COMPENSATE_DEFAULT) { + val = smb->soft_temp_limit_compensation & 0x3; ret = regmap_update_bits(smb->regmap, CFG_THERM, CFG_THERM_SOFT_HOT_COMPENSATION_MASK, @@ -573,9 +644,9 @@ static int smb347_set_temp_limits(struct smb347_charger *smb) return ret; } - if (smb->pdata->charge_current_compensation) { + if (smb->charge_current_compensation) { val = current_to_hw(ccc_tbl[id], ARRAY_SIZE(ccc_tbl[id]), - smb->pdata->charge_current_compensation); + smb->charge_current_compensation); if (val < 0) return val; @@ -634,7 +705,7 @@ static int smb347_hw_init(struct smb347_charger *smb) goto fail; /* If USB charging is disabled we put the USB in suspend mode */ - if (!smb->pdata->use_usb) { + if (!smb->use_usb) { ret = regmap_update_bits(smb->regmap, CMD_A, CMD_A_SUSPEND_ENABLED, CMD_A_SUSPEND_ENABLED); @@ -647,7 +718,7 @@ static int smb347_hw_init(struct smb347_charger *smb) * support for driving VBUS. Otherwise we disable it. */ ret = regmap_update_bits(smb->regmap, CFG_OTHER, CFG_OTHER_RID_MASK, - smb->pdata->use_usb_otg ? CFG_OTHER_RID_ENABLED_AUTO_OTG : 0); + smb->use_usb_otg ? CFG_OTHER_RID_ENABLED_AUTO_OTG : 0); if (ret < 0) goto fail; @@ -656,11 +727,11 @@ static int smb347_hw_init(struct smb347_charger *smb) * command register unless pin control is specified in the platform * data. */ - switch (smb->pdata->enable_control) { - case SMB347_CHG_ENABLE_PIN_ACTIVE_LOW: + switch (smb->enable_control) { + case SMB3XX_CHG_ENABLE_PIN_ACTIVE_LOW: val = CFG_PIN_EN_CTRL_ACTIVE_LOW; break; - case SMB347_CHG_ENABLE_PIN_ACTIVE_HIGH: + case SMB3XX_CHG_ENABLE_PIN_ACTIVE_HIGH: val = CFG_PIN_EN_CTRL_ACTIVE_HIGH; break; default: @@ -729,9 +800,9 @@ static irqreturn_t smb347_interrupt(int irq, void *data) */ if (stat_c & STAT_C_CHARGER_ERROR) { dev_err(smb->dev, "charging stopped due to charger error\n"); - if (smb->pdata->use_mains) + if (smb->use_mains) power_supply_changed(smb->mains); - if (smb->pdata->use_usb) + if (smb->use_usb) power_supply_changed(smb->usb); handled = true; } @@ -743,9 +814,9 @@ static irqreturn_t smb347_interrupt(int irq, void *data) */ if (irqstat_c & (IRQSTAT_C_TERMINATION_IRQ | IRQSTAT_C_TAPER_IRQ)) { if (irqstat_c & IRQSTAT_C_TERMINATION_STAT) { - if (smb->pdata->use_mains) + if (smb->use_mains) power_supply_changed(smb->mains); - if (smb->pdata->use_usb) + if (smb->use_usb) power_supply_changed(smb->usb); } dev_dbg(smb->dev, "going to HW maintenance mode\n"); @@ -761,9 +832,9 @@ static irqreturn_t smb347_interrupt(int irq, void *data) if (irqstat_d & IRQSTAT_D_CHARGE_TIMEOUT_STAT) dev_warn(smb->dev, "charging stopped due to timeout\n"); - if (smb->pdata->use_mains) + if (smb->use_mains) power_supply_changed(smb->mains); - if (smb->pdata->use_usb) + if (smb->use_usb) power_supply_changed(smb->usb); handled = true; } @@ -775,9 +846,9 @@ static irqreturn_t smb347_interrupt(int irq, void *data) if (irqstat_e & (IRQSTAT_E_USBIN_UV_IRQ | IRQSTAT_E_DCIN_UV_IRQ)) { if (smb347_update_ps_status(smb) > 0) { smb347_start_stop_charging(smb); - if (smb->pdata->use_mains) + if (smb->use_mains) power_supply_changed(smb->mains); - if (smb->pdata->use_usb) + if (smb->use_usb) power_supply_changed(smb->usb); } handled = true; @@ -832,25 +903,10 @@ static inline int smb347_irq_disable(struct smb347_charger *smb) static int smb347_irq_init(struct smb347_charger *smb, struct i2c_client *client) { - const struct smb347_charger_platform_data *pdata = smb->pdata; - unsigned long irqflags = IRQF_ONESHOT; int ret; - /* Requesting GPIO for IRQ is only needed in non-DT way */ - if (!client->irq) { - int irq = gpio_to_irq(pdata->irq_gpio); - - ret = devm_gpio_request_one(smb->dev, pdata->irq_gpio, - GPIOF_IN, client->name); - if (ret < 0) - return ret; - - irqflags |= IRQF_TRIGGER_FALLING; - client->irq = irq; - } - ret = devm_request_threaded_irq(smb->dev, client->irq, NULL, - smb347_interrupt, irqflags, + smb347_interrupt, IRQF_ONESHOT, client->name, smb); if (ret < 0) return ret; @@ -1118,56 +1174,52 @@ static bool smb347_readable_reg(struct device *dev, unsigned int reg) return smb347_volatile_reg(dev, reg); } -static void smb347_dt_parse_pdata(struct device_node *np, - struct smb347_charger_platform_data *pdata) +static void smb347_dt_parse_dev_info(struct smb347_charger *smb) { - pdata->soft_temp_limit_compensation = - SMB347_SOFT_TEMP_COMPENSATE_DEFAULT; + struct device_node *np = smb->dev->of_node; + + smb->soft_temp_limit_compensation = + SMB3XX_SOFT_TEMP_COMPENSATE_DEFAULT; /* * These properties come from the battery info, still we need to * pre-initialize the values. See smb347_get_battery_info() below. */ - pdata->soft_cold_temp_limit = SMB347_TEMP_USE_DEFAULT; - pdata->hard_cold_temp_limit = SMB347_TEMP_USE_DEFAULT; - pdata->soft_hot_temp_limit = SMB347_TEMP_USE_DEFAULT; - pdata->hard_hot_temp_limit = SMB347_TEMP_USE_DEFAULT; + smb->soft_cold_temp_limit = SMB3XX_TEMP_USE_DEFAULT; + smb->hard_cold_temp_limit = SMB3XX_TEMP_USE_DEFAULT; + smb->soft_hot_temp_limit = SMB3XX_TEMP_USE_DEFAULT; + smb->hard_hot_temp_limit = SMB3XX_TEMP_USE_DEFAULT; /* Charging constraints */ of_property_read_u32(np, "summit,fast-voltage-threshold-microvolt", - &pdata->pre_to_fast_voltage); + &smb->pre_to_fast_voltage); of_property_read_u32(np, "summit,mains-current-limit-microamp", - &pdata->mains_current_limit); + &smb->mains_current_limit); of_property_read_u32(np, "summit,usb-current-limit-microamp", - &pdata->usb_hc_current_limit); + &smb->usb_hc_current_limit); /* For thermometer monitoring */ of_property_read_u32(np, "summit,chip-temperature-threshold-celsius", - &pdata->chip_temp_threshold); + &smb->chip_temp_threshold); of_property_read_u32(np, "summit,soft-compensation-method", - &pdata->soft_temp_limit_compensation); + &smb->soft_temp_limit_compensation); of_property_read_u32(np, "summit,charge-current-compensation-microamp", - &pdata->charge_current_compensation); + &smb->charge_current_compensation); /* Supported charging mode */ - pdata->use_mains = + smb->use_mains = of_property_read_bool(np, "summit,enable-mains-charging"); - pdata->use_usb = + smb->use_usb = of_property_read_bool(np, "summit,enable-usb-charging"); - pdata->use_usb_otg = + smb->use_usb_otg = of_property_read_bool(np, "summit,enable-otg-charging"); /* Select charging control */ of_property_read_u32(np, "summit,enable-charge-control", - &pdata->enable_control); - - /* Interrupt support is optional */ - if (!of_find_property(np, "interrupts", NULL)) - pdata->irq_gpio = -1; + &smb->enable_control); } static int smb347_get_battery_info(struct smb347_charger *smb) { - struct smb347_charger_platform_data *pdata = (void *)smb->pdata; struct power_supply_battery_info info = {}; struct power_supply *supply; int err; @@ -1184,53 +1236,37 @@ static int smb347_get_battery_info(struct smb347_charger *smb) return err; if (info.constant_charge_current_max_ua != -EINVAL) - pdata->max_charge_current = info.constant_charge_current_max_ua; + smb->max_charge_current = info.constant_charge_current_max_ua; if (info.constant_charge_voltage_max_uv != -EINVAL) - pdata->max_charge_voltage = info.constant_charge_voltage_max_uv; + smb->max_charge_voltage = info.constant_charge_voltage_max_uv; if (info.precharge_current_ua != -EINVAL) - pdata->pre_charge_current = info.precharge_current_ua; + smb->pre_charge_current = info.precharge_current_ua; if (info.charge_term_current_ua != -EINVAL) - pdata->termination_current = info.charge_term_current_ua; + smb->termination_current = info.charge_term_current_ua; if (info.temp_alert_min != INT_MIN) - pdata->soft_cold_temp_limit = info.temp_alert_min; + smb->soft_cold_temp_limit = info.temp_alert_min; if (info.temp_alert_max != INT_MAX) - pdata->soft_hot_temp_limit = info.temp_alert_max; + smb->soft_hot_temp_limit = info.temp_alert_max; if (info.temp_min != INT_MIN) - pdata->hard_cold_temp_limit = info.temp_min; + smb->hard_cold_temp_limit = info.temp_min; if (info.temp_max != INT_MAX) - pdata->hard_hot_temp_limit = info.temp_max; + smb->hard_hot_temp_limit = info.temp_max; /* Suspend when battery temperature is outside hard limits */ - if (pdata->hard_cold_temp_limit != SMB347_TEMP_USE_DEFAULT || - pdata->hard_hot_temp_limit != SMB347_TEMP_USE_DEFAULT) - pdata->suspend_on_hard_temp_limit = true; + if (smb->hard_cold_temp_limit != SMB3XX_TEMP_USE_DEFAULT || + smb->hard_hot_temp_limit != SMB3XX_TEMP_USE_DEFAULT) + smb->suspend_on_hard_temp_limit = true; return 0; } -static struct smb347_charger_platform_data - *smb347_get_platdata(struct device *dev) -{ - struct smb347_charger_platform_data *pdata; - - if (dev->of_node) { - pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (pdata) - smb347_dt_parse_pdata(dev->of_node, pdata); - } else { - pdata = dev_get_platdata(dev); - } - - return pdata; -} - static const struct regmap_config smb347_regmap = { .reg_bits = 8, .val_bits = 8, @@ -1266,18 +1302,13 @@ static int smb347_probe(struct i2c_client *client, smb = devm_kzalloc(dev, sizeof(*smb), GFP_KERNEL); if (!smb) return -ENOMEM; - - smb->pdata = smb347_get_platdata(dev); - if (!smb->pdata) - return -ENODEV; - - if (!smb->pdata->use_mains && !smb->pdata->use_usb) - return -EINVAL; - - i2c_set_clientdata(client, smb); - smb->dev = &client->dev; smb->id = id->driver_data; + i2c_set_clientdata(client, smb); + + smb347_dt_parse_dev_info(smb); + if (!smb->use_mains && !smb->use_usb) + return -EINVAL; smb->regmap = devm_regmap_init_i2c(client, &smb347_regmap); if (IS_ERR(smb->regmap)) @@ -1285,14 +1316,14 @@ static int smb347_probe(struct i2c_client *client, mains_usb_cfg.drv_data = smb; mains_usb_cfg.of_node = dev->of_node; - if (smb->pdata->use_mains) { + if (smb->use_mains) { smb->mains = devm_power_supply_register(dev, &smb347_mains_desc, &mains_usb_cfg); if (IS_ERR(smb->mains)) return PTR_ERR(smb->mains); } - if (smb->pdata->use_usb) { + if (smb->use_usb) { smb->usb = devm_power_supply_register(dev, &smb347_usb_desc, &mains_usb_cfg); if (IS_ERR(smb->usb)) @@ -1311,7 +1342,7 @@ static int smb347_probe(struct i2c_client *client, * Interrupt pin is optional. If it is connected, we setup the * interrupt support here. */ - if (smb->pdata->irq_gpio >= 0) { + if (client->irq) { ret = smb347_irq_init(smb, client); if (ret < 0) { dev_warn(dev, "failed to initialize IRQ: %d\n", ret); -- cgit From f385e2fcc596f0c4428d3c519e524f3709654f3b Mon Sep 17 00:00:00 2001 From: Sebastian Reichel <sebastian.reichel@collabora.com> Date: Wed, 26 Aug 2020 16:41:59 +0200 Subject: power: supply: smb347-charger: Use generic property framework Simplify the driver and remove the DT specific code by using the generic device property framework. Reviewed-by: Dmitry Osipenko <digetx@gmail.com> Tested-by: Dmitry Osipenko <digetx@gmail.com> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/smb347-charger.c | 40 +++++++++++++++++------------------ 1 file changed, 19 insertions(+), 21 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/smb347-charger.c b/drivers/power/supply/smb347-charger.c index b182727dfc90..d3bf35ed12ce 100644 --- a/drivers/power/supply/smb347-charger.c +++ b/drivers/power/supply/smb347-charger.c @@ -17,6 +17,7 @@ #include <linux/interrupt.h> #include <linux/i2c.h> #include <linux/power_supply.h> +#include <linux/property.h> #include <linux/regmap.h> #include <dt-bindings/power/summit,smb347-charger.h> @@ -1176,7 +1177,7 @@ static bool smb347_readable_reg(struct device *dev, unsigned int reg) static void smb347_dt_parse_dev_info(struct smb347_charger *smb) { - struct device_node *np = smb->dev->of_node; + struct device *dev = smb->dev; smb->soft_temp_limit_compensation = SMB3XX_SOFT_TEMP_COMPENSATE_DEFAULT; @@ -1190,32 +1191,29 @@ static void smb347_dt_parse_dev_info(struct smb347_charger *smb) smb->hard_hot_temp_limit = SMB3XX_TEMP_USE_DEFAULT; /* Charging constraints */ - of_property_read_u32(np, "summit,fast-voltage-threshold-microvolt", - &smb->pre_to_fast_voltage); - of_property_read_u32(np, "summit,mains-current-limit-microamp", - &smb->mains_current_limit); - of_property_read_u32(np, "summit,usb-current-limit-microamp", - &smb->usb_hc_current_limit); + device_property_read_u32(dev, "summit,fast-voltage-threshold-microvolt", + &smb->pre_to_fast_voltage); + device_property_read_u32(dev, "summit,mains-current-limit-microamp", + &smb->mains_current_limit); + device_property_read_u32(dev, "summit,usb-current-limit-microamp", + &smb->usb_hc_current_limit); /* For thermometer monitoring */ - of_property_read_u32(np, "summit,chip-temperature-threshold-celsius", - &smb->chip_temp_threshold); - of_property_read_u32(np, "summit,soft-compensation-method", - &smb->soft_temp_limit_compensation); - of_property_read_u32(np, "summit,charge-current-compensation-microamp", - &smb->charge_current_compensation); + device_property_read_u32(dev, "summit,chip-temperature-threshold-celsius", + &smb->chip_temp_threshold); + device_property_read_u32(dev, "summit,soft-compensation-method", + &smb->soft_temp_limit_compensation); + device_property_read_u32(dev, "summit,charge-current-compensation-microamp", + &smb->charge_current_compensation); /* Supported charging mode */ - smb->use_mains = - of_property_read_bool(np, "summit,enable-mains-charging"); - smb->use_usb = - of_property_read_bool(np, "summit,enable-usb-charging"); - smb->use_usb_otg = - of_property_read_bool(np, "summit,enable-otg-charging"); + smb->use_mains = device_property_read_bool(dev, "summit,enable-mains-charging"); + smb->use_usb = device_property_read_bool(dev, "summit,enable-usb-charging"); + smb->use_usb_otg = device_property_read_bool(dev, "summit,enable-otg-charging"); /* Select charging control */ - of_property_read_u32(np, "summit,enable-charge-control", - &smb->enable_control); + device_property_read_u32(dev, "summit,enable-charge-control", + &smb->enable_control); } static int smb347_get_battery_info(struct smb347_charger *smb) -- cgit From 395a7251dc2bf5eed2aab6960720a270b364871e Mon Sep 17 00:00:00 2001 From: Ikjoon Jang <ikjn@chromium.org> Date: Fri, 28 Aug 2020 12:36:26 +0800 Subject: power: supply: sbs-battery: don't assume i2c errors as battery disconnect Current sbs-battery considers all smbus errors as disconnection events when battery-detect pin isn't supplied, and restored to present state back when any successful transaction is made. This can lead to unwanted state changes between present and !present when there's one i2c error and other following commands were successful. This patch provides a unified way of checking presence by calling sbs_get_battery_presence_and_health() when detect pin is not used. Signed-off-by: Ikjoon Jang <ikjn@chromium.org> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/sbs-battery.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index 6273211cd673..dacc4bc1c013 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -959,10 +959,17 @@ static int sbs_get_property(struct power_supply *psy, return -EINVAL; } - if (!chip->gpio_detect && - chip->is_present != (ret >= 0)) { - sbs_update_presence(chip, (ret >= 0)); - power_supply_changed(chip->power_supply); + if (!chip->gpio_detect && chip->is_present != (ret >= 0)) { + bool old_present = chip->is_present; + union power_supply_propval val; + + ret = sbs_get_battery_presence_and_health( + client, POWER_SUPPLY_PROP_PRESENT, &val); + + sbs_update_presence(chip, !ret && val.intval); + + if (old_present != chip->is_present) + power_supply_changed(chip->power_supply); } done: @@ -1147,11 +1154,13 @@ skip_gpio: * to the battery. */ if (!(force_load || chip->gpio_detect)) { - rc = sbs_read_word_data(client, sbs_data[REG_STATUS].addr); + union power_supply_propval val; - if (rc < 0) { - dev_err(&client->dev, "%s: Failed to get device status\n", - __func__); + rc = sbs_get_battery_presence_and_health( + client, POWER_SUPPLY_PROP_PRESENT, &val); + if (rc < 0 || !val.intval) { + dev_err(&client->dev, "Failed to get present status\n"); + rc = -ENODEV; goto exit_psupply; } } -- cgit From 1426dffad0dd64af9b1fee810171feb485f9c2b2 Mon Sep 17 00:00:00 2001 From: Andreas Kemnade <andreas@kemnade.info> Date: Fri, 28 Aug 2020 13:49:06 +0200 Subject: power: supply: Add support for RN5T618/RC5T619 charger and fuel gauge Both chips have charger and a fuel gauge. This adds basic support for displaying the state of the battery and the input power, settings are not modified. There are some defaults set via OTP. Charging also starts after plugging USB. Known issues of the fuel gauge: There are drivers in the wild which disable the fuel gauge at shutdown. If a kernel is booted without fuel gauge support, after such a driver has been used, the fuel gauge will stay off and decalibrate. If this driver is used after that, it might display wrong values for charge level. Signed-off-by: Andreas Kemnade <andreas@kemnade.info> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/Kconfig | 8 + drivers/power/supply/Makefile | 1 + drivers/power/supply/rn5t618_power.c | 556 +++++++++++++++++++++++++++++++++++ 3 files changed, 565 insertions(+) create mode 100644 drivers/power/supply/rn5t618_power.c (limited to 'drivers/power') diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index d1ccf17df42e..a4657484f38b 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -752,4 +752,12 @@ config CHARGER_WILCO information can be found in Documentation/ABI/testing/sysfs-class-power-wilco +config RN5T618_POWER + tristate "RN5T618 charger/fuel gauge support" + depends on MFD_RN5T618 + help + Say Y here to have support for RN5T618 PMIC family fuel gauge and charger. + This driver can also be built as a module. If so, the module will be + called rn5t618_power. + endif # POWER_SUPPLY diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index b3c694a65114..293d4a5d80d3 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -96,3 +96,4 @@ obj-$(CONFIG_CHARGER_UCS1002) += ucs1002_power.o obj-$(CONFIG_CHARGER_BD70528) += bd70528-charger.o obj-$(CONFIG_CHARGER_BD99954) += bd99954-charger.o obj-$(CONFIG_CHARGER_WILCO) += wilco-charger.o +obj-$(CONFIG_RN5T618_POWER) += rn5t618_power.o diff --git a/drivers/power/supply/rn5t618_power.c b/drivers/power/supply/rn5t618_power.c new file mode 100644 index 000000000000..424d2817bee5 --- /dev/null +++ b/drivers/power/supply/rn5t618_power.c @@ -0,0 +1,556 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Power supply driver for the RICOH RN5T618 power management chip family + * + * Copyright (C) 2020 Andreas Kemnade + */ + +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/bitops.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/mfd/rn5t618.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#define CHG_STATE_ADP_INPUT 0x40 +#define CHG_STATE_USB_INPUT 0x80 +#define CHG_STATE_MASK 0x1f +#define CHG_STATE_CHG_OFF 0 +#define CHG_STATE_CHG_READY_VADP 1 +#define CHG_STATE_CHG_TRICKLE 2 +#define CHG_STATE_CHG_RAPID 3 +#define CHG_STATE_CHG_COMPLETE 4 +#define CHG_STATE_SUSPEND 5 +#define CHG_STATE_VCHG_OVER_VOL 6 +#define CHG_STATE_BAT_ERROR 7 +#define CHG_STATE_NO_BAT 8 +#define CHG_STATE_BAT_OVER_VOL 9 +#define CHG_STATE_BAT_TEMP_ERR 10 +#define CHG_STATE_DIE_ERR 11 +#define CHG_STATE_DIE_SHUTDOWN 12 +#define CHG_STATE_NO_BAT2 13 +#define CHG_STATE_CHG_READY_VUSB 14 + +#define FG_ENABLE 1 + +struct rn5t618_power_info { + struct rn5t618 *rn5t618; + struct platform_device *pdev; + struct power_supply *battery; + struct power_supply *usb; + struct power_supply *adp; + int irq; +}; + +static enum power_supply_property rn5t618_usb_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, +}; + +static enum power_supply_property rn5t618_adp_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, +}; + + +static enum power_supply_property rn5t618_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, + POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_NOW, +}; + +static int rn5t618_battery_read_doublereg(struct rn5t618_power_info *info, + u8 reg, u16 *result) +{ + int ret, i; + u8 data[2]; + u16 old, new; + + old = 0; + /* Prevent races when registers are changing. */ + for (i = 0; i < 3; i++) { + ret = regmap_bulk_read(info->rn5t618->regmap, + reg, data, sizeof(data)); + if (ret) + return ret; + + new = data[0] << 8; + new |= data[1]; + if (new == old) + break; + + old = new; + } + + *result = new; + + return 0; +} + +static int rn5t618_decode_status(unsigned int status) +{ + switch (status & CHG_STATE_MASK) { + case CHG_STATE_CHG_OFF: + case CHG_STATE_SUSPEND: + case CHG_STATE_VCHG_OVER_VOL: + case CHG_STATE_DIE_SHUTDOWN: + return POWER_SUPPLY_STATUS_DISCHARGING; + + case CHG_STATE_CHG_TRICKLE: + case CHG_STATE_CHG_RAPID: + return POWER_SUPPLY_STATUS_CHARGING; + + case CHG_STATE_CHG_COMPLETE: + return POWER_SUPPLY_STATUS_FULL; + + default: + return POWER_SUPPLY_STATUS_NOT_CHARGING; + } +} + +static int rn5t618_battery_status(struct rn5t618_power_info *info, + union power_supply_propval *val) +{ + unsigned int v; + int ret; + + ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGSTATE, &v); + if (ret) + return ret; + + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + + if (v & 0xc0) { /* USB or ADP plugged */ + val->intval = rn5t618_decode_status(v); + } else + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + + return ret; +} + +static int rn5t618_battery_present(struct rn5t618_power_info *info, + union power_supply_propval *val) +{ + unsigned int v; + int ret; + + ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGSTATE, &v); + if (ret) + return ret; + + v &= CHG_STATE_MASK; + if ((v == CHG_STATE_NO_BAT) || (v == CHG_STATE_NO_BAT2)) + val->intval = 0; + else + val->intval = 1; + + return ret; +} + +static int rn5t618_battery_voltage_now(struct rn5t618_power_info *info, + union power_supply_propval *val) +{ + u16 res; + int ret; + + ret = rn5t618_battery_read_doublereg(info, RN5T618_VOLTAGE_1, &res); + if (ret) + return ret; + + val->intval = res * 2 * 2500 / 4095 * 1000; + + return 0; +} + +static int rn5t618_battery_current_now(struct rn5t618_power_info *info, + union power_supply_propval *val) +{ + u16 res; + int ret; + + ret = rn5t618_battery_read_doublereg(info, RN5T618_CC_AVEREG1, &res); + if (ret) + return ret; + + /* current is negative when discharging */ + val->intval = sign_extend32(res, 13) * 1000; + + return 0; +} + +static int rn5t618_battery_capacity(struct rn5t618_power_info *info, + union power_supply_propval *val) +{ + unsigned int v; + int ret; + + ret = regmap_read(info->rn5t618->regmap, RN5T618_SOC, &v); + if (ret) + return ret; + + val->intval = v; + + return 0; +} + +static int rn5t618_battery_temp(struct rn5t618_power_info *info, + union power_supply_propval *val) +{ + u16 res; + int ret; + + ret = rn5t618_battery_read_doublereg(info, RN5T618_TEMP_1, &res); + if (ret) + return ret; + + val->intval = sign_extend32(res, 11) * 10 / 16; + + return 0; +} + +static int rn5t618_battery_tte(struct rn5t618_power_info *info, + union power_supply_propval *val) +{ + u16 res; + int ret; + + ret = rn5t618_battery_read_doublereg(info, RN5T618_TT_EMPTY_H, &res); + if (ret) + return ret; + + if (res == 65535) + return -ENODATA; + + val->intval = res * 60; + + return 0; +} + +static int rn5t618_battery_ttf(struct rn5t618_power_info *info, + union power_supply_propval *val) +{ + u16 res; + int ret; + + ret = rn5t618_battery_read_doublereg(info, RN5T618_TT_FULL_H, &res); + if (ret) + return ret; + + if (res == 65535) + return -ENODATA; + + val->intval = res * 60; + + return 0; +} + +static int rn5t618_battery_charge_full(struct rn5t618_power_info *info, + union power_supply_propval *val) +{ + u16 res; + int ret; + + ret = rn5t618_battery_read_doublereg(info, RN5T618_FA_CAP_H, &res); + if (ret) + return ret; + + val->intval = res * 1000; + + return 0; +} + +static int rn5t618_battery_charge_now(struct rn5t618_power_info *info, + union power_supply_propval *val) +{ + u16 res; + int ret; + + ret = rn5t618_battery_read_doublereg(info, RN5T618_RE_CAP_H, &res); + if (ret) + return ret; + + val->intval = res * 1000; + + return 0; +} + +static int rn5t618_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret = 0; + struct rn5t618_power_info *info = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + ret = rn5t618_battery_status(info, val); + break; + case POWER_SUPPLY_PROP_PRESENT: + ret = rn5t618_battery_present(info, val); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = rn5t618_battery_voltage_now(info, val); + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = rn5t618_battery_current_now(info, val); + break; + case POWER_SUPPLY_PROP_CAPACITY: + ret = rn5t618_battery_capacity(info, val); + break; + case POWER_SUPPLY_PROP_TEMP: + ret = rn5t618_battery_temp(info, val); + break; + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: + ret = rn5t618_battery_tte(info, val); + break; + case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: + ret = rn5t618_battery_ttf(info, val); + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + ret = rn5t618_battery_charge_full(info, val); + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + ret = rn5t618_battery_charge_now(info, val); + break; + default: + return -EINVAL; + } + + return ret; +} + +static int rn5t618_adp_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct rn5t618_power_info *info = power_supply_get_drvdata(psy); + unsigned int chgstate; + bool online; + int ret; + + ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGSTATE, &chgstate); + if (ret) + return ret; + + online = !!(chgstate & CHG_STATE_ADP_INPUT); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = online; + break; + case POWER_SUPPLY_PROP_STATUS: + if (!online) { + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + } + val->intval = rn5t618_decode_status(chgstate); + if (val->intval != POWER_SUPPLY_STATUS_CHARGING) + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static int rn5t618_usb_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct rn5t618_power_info *info = power_supply_get_drvdata(psy); + unsigned int chgstate; + bool online; + int ret; + + ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGSTATE, &chgstate); + if (ret) + return ret; + + online = !!(chgstate & CHG_STATE_USB_INPUT); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = online; + break; + case POWER_SUPPLY_PROP_STATUS: + if (!online) { + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + } + val->intval = rn5t618_decode_status(chgstate); + if (val->intval != POWER_SUPPLY_STATUS_CHARGING) + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct power_supply_desc rn5t618_battery_desc = { + .name = "rn5t618-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = rn5t618_battery_props, + .num_properties = ARRAY_SIZE(rn5t618_battery_props), + .get_property = rn5t618_battery_get_property, +}; + +static const struct power_supply_desc rn5t618_adp_desc = { + .name = "rn5t618-adp", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = rn5t618_adp_props, + .num_properties = ARRAY_SIZE(rn5t618_adp_props), + .get_property = rn5t618_adp_get_property, +}; + +static const struct power_supply_desc rn5t618_usb_desc = { + .name = "rn5t618-usb", + .type = POWER_SUPPLY_TYPE_USB, + .properties = rn5t618_usb_props, + .num_properties = ARRAY_SIZE(rn5t618_usb_props), + .get_property = rn5t618_usb_get_property, +}; + +static irqreturn_t rn5t618_charger_irq(int irq, void *data) +{ + struct device *dev = data; + struct rn5t618_power_info *info = dev_get_drvdata(dev); + + unsigned int ctrl, stat1, stat2, err; + + regmap_read(info->rn5t618->regmap, RN5T618_CHGERR_IRR, &err); + regmap_read(info->rn5t618->regmap, RN5T618_CHGCTRL_IRR, &ctrl); + regmap_read(info->rn5t618->regmap, RN5T618_CHGSTAT_IRR1, &stat1); + regmap_read(info->rn5t618->regmap, RN5T618_CHGSTAT_IRR2, &stat2); + + regmap_write(info->rn5t618->regmap, RN5T618_CHGERR_IRR, 0); + regmap_write(info->rn5t618->regmap, RN5T618_CHGCTRL_IRR, 0); + regmap_write(info->rn5t618->regmap, RN5T618_CHGSTAT_IRR1, 0); + regmap_write(info->rn5t618->regmap, RN5T618_CHGSTAT_IRR2, 0); + + dev_dbg(dev, "chgerr: %x chgctrl: %x chgstat: %x chgstat2: %x\n", + err, ctrl, stat1, stat2); + + power_supply_changed(info->usb); + power_supply_changed(info->adp); + power_supply_changed(info->battery); + + return IRQ_HANDLED; +} + +static int rn5t618_power_probe(struct platform_device *pdev) +{ + int ret = 0; + unsigned int v; + struct power_supply_config psy_cfg = {}; + struct rn5t618_power_info *info; + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->pdev = pdev; + info->rn5t618 = dev_get_drvdata(pdev->dev.parent); + info->irq = -1; + + platform_set_drvdata(pdev, info); + + ret = regmap_read(info->rn5t618->regmap, RN5T618_CONTROL, &v); + if (ret) + return ret; + + if (!(v & FG_ENABLE)) { + /* E.g. the vendor kernels of various Kobo and Tolino Ebook + * readers disable the fuel gauge on shutdown. If a kernel + * without fuel gauge support is booted after that, the fuel + * gauge will get decalibrated. + */ + dev_info(&pdev->dev, "Fuel gauge not enabled, enabling now\n"); + dev_info(&pdev->dev, "Expect unprecise results\n"); + regmap_update_bits(info->rn5t618->regmap, RN5T618_CONTROL, + FG_ENABLE, FG_ENABLE); + } + + psy_cfg.drv_data = info; + info->battery = devm_power_supply_register(&pdev->dev, + &rn5t618_battery_desc, + &psy_cfg); + if (IS_ERR(info->battery)) { + ret = PTR_ERR(info->battery); + dev_err(&pdev->dev, "failed to register battery: %d\n", ret); + return ret; + } + + info->adp = devm_power_supply_register(&pdev->dev, + &rn5t618_adp_desc, + &psy_cfg); + if (IS_ERR(info->adp)) { + ret = PTR_ERR(info->adp); + dev_err(&pdev->dev, "failed to register adp: %d\n", ret); + return ret; + } + + info->usb = devm_power_supply_register(&pdev->dev, + &rn5t618_usb_desc, + &psy_cfg); + if (IS_ERR(info->usb)) { + ret = PTR_ERR(info->usb); + dev_err(&pdev->dev, "failed to register usb: %d\n", ret); + return ret; + } + + if (info->rn5t618->irq_data) + info->irq = regmap_irq_get_virq(info->rn5t618->irq_data, + RN5T618_IRQ_CHG); + + if (info->irq < 0) + info->irq = -1; + else { + ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL, + rn5t618_charger_irq, + IRQF_ONESHOT, + "rn5t618_power", + &pdev->dev); + + if (ret < 0) { + dev_err(&pdev->dev, "request IRQ:%d fail\n", + info->irq); + info->irq = -1; + } + } + + return 0; +} + +static struct platform_driver rn5t618_power_driver = { + .driver = { + .name = "rn5t618-power", + }, + .probe = rn5t618_power_probe, +}; + +module_platform_driver(rn5t618_power_driver); +MODULE_ALIAS("platform:rn5t618-power"); +MODULE_DESCRIPTION("Power supply driver for RICOH RN5T618"); +MODULE_LICENSE("GPL"); -- cgit From 033dea165c8aa4a64217b050bb7cdd551aa286fc Mon Sep 17 00:00:00 2001 From: Jonghwa Lee <jonghwa3.lee@samsung.com> Date: Thu, 14 May 2020 16:04:26 -0700 Subject: power: supply: charger-manager: Swap private uevent for power_supply_changed Whenever the battery status is changed, charger manager triggers a uevent through a private interface. Modify it to use power_supply_changed() since it belongs to the power supply subsystem. Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com> Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com> Signed-off-by: Jonathan Bakker <xc-racer2@live.ca> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/charger-manager.c | 91 ++++------------------------------ 1 file changed, 11 insertions(+), 80 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index 2ef53dc1f2fb..1adaeefd90a4 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -33,18 +33,6 @@ #define CM_DEFAULT_RECHARGE_TEMP_DIFF 50 #define CM_DEFAULT_CHARGE_TEMP_MAX 500 -static const char * const default_event_names[] = { - [CM_EVENT_UNKNOWN] = "Unknown", - [CM_EVENT_BATT_FULL] = "Battery Full", - [CM_EVENT_BATT_IN] = "Battery Inserted", - [CM_EVENT_BATT_OUT] = "Battery Pulled Out", - [CM_EVENT_BATT_OVERHEAT] = "Battery Overheat", - [CM_EVENT_BATT_COLD] = "Battery Cold", - [CM_EVENT_EXT_PWR_IN_OUT] = "External Power Attach/Detach", - [CM_EVENT_CHG_START_STOP] = "Charging Start/Stop", - [CM_EVENT_OTHERS] = "Other battery events" -}; - /* * Regard CM_JIFFIES_SMALL jiffies is small enough to ignore for * delayed works so that we can run delayed works with CM_JIFFIES_SMALL @@ -61,8 +49,6 @@ static const char * const default_event_names[] = { */ #define CM_RTC_SMALL (2) -#define UEVENT_BUF_SIZE 32 - static LIST_HEAD(cm_list); static DEFINE_MUTEX(cm_list_mtx); @@ -446,61 +432,6 @@ static int try_charger_restart(struct charger_manager *cm) return try_charger_enable(cm, true); } -/** - * uevent_notify - Let users know something has changed. - * @cm: the Charger Manager representing the battery. - * @event: the event string. - * - * If @event is null, it implies that uevent_notify is called - * by resume function. When called in the resume function, cm_suspended - * should be already reset to false in order to let uevent_notify - * notify the recent event during the suspend to users. While - * suspended, uevent_notify does not notify users, but tracks - * events so that uevent_notify can notify users later after resumed. - */ -static void uevent_notify(struct charger_manager *cm, const char *event) -{ - static char env_str[UEVENT_BUF_SIZE + 1] = ""; - static char env_str_save[UEVENT_BUF_SIZE + 1] = ""; - - if (cm_suspended) { - /* Nothing in suspended-event buffer */ - if (env_str_save[0] == 0) { - if (!strncmp(env_str, event, UEVENT_BUF_SIZE)) - return; /* status not changed */ - strncpy(env_str_save, event, UEVENT_BUF_SIZE); - return; - } - - if (!strncmp(env_str_save, event, UEVENT_BUF_SIZE)) - return; /* Duplicated. */ - strncpy(env_str_save, event, UEVENT_BUF_SIZE); - return; - } - - if (event == NULL) { - /* No messages pending */ - if (!env_str_save[0]) - return; - - strncpy(env_str, env_str_save, UEVENT_BUF_SIZE); - kobject_uevent(&cm->dev->kobj, KOBJ_CHANGE); - env_str_save[0] = 0; - - return; - } - - /* status not changed */ - if (!strncmp(env_str, event, UEVENT_BUF_SIZE)) - return; - - /* save the status and notify the update */ - strncpy(env_str, event, UEVENT_BUF_SIZE); - kobject_uevent(&cm->dev->kobj, KOBJ_CHANGE); - - dev_info(cm->dev, "%s\n", event); -} - /** * fullbatt_vchk - Check voltage drop some times after "FULL" event. * @work: the work_struct appointing the function @@ -538,7 +469,7 @@ static void fullbatt_vchk(struct work_struct *work) if (diff > desc->fullbatt_vchkdrop_uV) { try_charger_restart(cm); - uevent_notify(cm, "Recharging"); + power_supply_changed(cm->charger_psy); } } @@ -569,7 +500,7 @@ static int check_charging_duration(struct charger_manager *cm) if (duration > desc->charging_max_duration_ms) { dev_info(cm->dev, "Charging duration exceed %ums\n", desc->charging_max_duration_ms); - uevent_notify(cm, "Discharging"); + power_supply_changed(cm->charger_psy); try_charger_enable(cm, false); ret = true; } @@ -580,7 +511,7 @@ static int check_charging_duration(struct charger_manager *cm) is_ext_pwr_online(cm)) { dev_info(cm->dev, "Discharging duration exceed %ums\n", desc->discharging_max_duration_ms); - uevent_notify(cm, "Recharging"); + power_supply_changed(cm->charger_psy); try_charger_enable(cm, true); ret = true; } @@ -688,7 +619,7 @@ static bool _cm_monitor(struct charger_manager *cm) if (temp_alrt) { cm->emergency_stop = temp_alrt; if (!try_charger_enable(cm, false)) - uevent_notify(cm, default_event_names[temp_alrt]); + power_supply_changed(cm->charger_psy); /* * Check whole charging duration and discharging duration @@ -713,7 +644,7 @@ static bool _cm_monitor(struct charger_manager *cm) } else if (!cm->emergency_stop && is_full_charged(cm) && cm->charger_enabled) { dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged\n"); - uevent_notify(cm, default_event_names[CM_EVENT_BATT_FULL]); + power_supply_changed(cm->charger_psy); try_charger_enable(cm, false); @@ -722,7 +653,7 @@ static bool _cm_monitor(struct charger_manager *cm) cm->emergency_stop = 0; if (is_ext_pwr_online(cm)) { if (!try_charger_enable(cm, true)) - uevent_notify(cm, "CHARGING"); + power_supply_changed(cm->charger_psy); } } @@ -843,7 +774,7 @@ static void fullbatt_handler(struct charger_manager *cm) out: dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged\n"); - uevent_notify(cm, default_event_names[CM_EVENT_BATT_FULL]); + power_supply_changed(cm->charger_psy); } /** @@ -857,9 +788,9 @@ static void battout_handler(struct charger_manager *cm) if (!is_batt_present(cm)) { dev_emerg(cm->dev, "Battery Pulled Out!\n"); - uevent_notify(cm, default_event_names[CM_EVENT_BATT_OUT]); + power_supply_changed(cm->charger_psy); } else { - uevent_notify(cm, "Battery Reinserted?"); + power_supply_changed(cm->charger_psy); } } @@ -876,7 +807,7 @@ static void misc_event_handler(struct charger_manager *cm, if (is_polling_required(cm) && cm->desc->polling_interval_ms) schedule_work(&setup_polling); - uevent_notify(cm, default_event_names[type]); + power_supply_changed(cm->charger_psy); } static int charger_get_property(struct power_supply *psy, @@ -2048,7 +1979,7 @@ void cm_notify_event(struct power_supply *psy, enum cm_event_types type, break; case CM_EVENT_UNKNOWN: case CM_EVENT_OTHERS: - uevent_notify(cm, msg ? msg : default_event_names[type]); + power_supply_changed(cm->charger_psy); break; default: dev_err(cm->dev, "%s: type not specified\n", __func__); -- cgit From 9584051f3cf3af181e577960956bb7c085879b67 Mon Sep 17 00:00:00 2001 From: Jonghwa Lee <jonghwa3.lee@samsung.com> Date: Thu, 14 May 2020 16:04:27 -0700 Subject: power: supply: charger-manager: Remove cm_notify_event function cm_notify_event() was introduced to get an event associated with the battery status externally (ie in board files), but no one ever used it. Moreover it makes charger manager driver more complicated. Drop the function and all data related to it to simplify the driver. Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com> Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com> Signed-off-by: Jonathan Bakker <xc-racer2@live.ca> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/charger-manager.c | 183 ++------------------------------- 1 file changed, 10 insertions(+), 173 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index 1adaeefd90a4..c18dc8d3ae1c 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -434,25 +434,18 @@ static int try_charger_restart(struct charger_manager *cm) /** * fullbatt_vchk - Check voltage drop some times after "FULL" event. - * @work: the work_struct appointing the function * - * If a user has designated "fullbatt_vchkdrop_ms/uV" values with + * If a user has designated "fullbatt_vchkdrop_uV" values with * charger_desc, Charger Manager checks voltage drop after the battery * "FULL" event. It checks whether the voltage has dropped more than * fullbatt_vchkdrop_uV by calling this function after fullbatt_vchkrop_ms. */ -static void fullbatt_vchk(struct work_struct *work) +static void fullbatt_vchk(struct charger_manager *cm) { - struct delayed_work *dwork = to_delayed_work(work); - struct charger_manager *cm = container_of(dwork, - struct charger_manager, fullbatt_vchk_work); struct charger_desc *desc = cm->desc; int batt_uV, err, diff; - /* remove the appointment for fullbatt_vchk */ - cm->fullbatt_vchk_jiffies_at = 0; - - if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms) + if (!desc->fullbatt_vchkdrop_uV) return; err = get_batt_uV(cm, &batt_uV); @@ -588,9 +581,11 @@ static int cm_check_thermal_status(struct charger_manager *cm) } if (temp > upper_limit) - ret = CM_EVENT_BATT_OVERHEAT; + ret = CM_BATT_OVERHEAT; else if (temp < lower_limit) - ret = CM_EVENT_BATT_COLD; + ret = CM_BATT_COLD; + else + ret = CM_BATT_OK; return ret; } @@ -635,7 +630,7 @@ static bool _cm_monitor(struct charger_manager *cm) */ } else if (!cm->emergency_stop && is_ext_pwr_online(cm) && !cm->charger_enabled) { - fullbatt_vchk(&cm->fullbatt_vchk_work.work); + fullbatt_vchk(cm); /* * Check whether fully charged state to protect overcharge @@ -648,7 +643,7 @@ static bool _cm_monitor(struct charger_manager *cm) try_charger_enable(cm, false); - fullbatt_vchk(&cm->fullbatt_vchk_work.work); + fullbatt_vchk(cm); } else { cm->emergency_stop = 0; if (is_ext_pwr_online(cm)) { @@ -750,66 +745,6 @@ static void cm_monitor_poller(struct work_struct *work) schedule_work(&setup_polling); } -/** - * fullbatt_handler - Event handler for CM_EVENT_BATT_FULL - * @cm: the Charger Manager representing the battery. - */ -static void fullbatt_handler(struct charger_manager *cm) -{ - struct charger_desc *desc = cm->desc; - - if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms) - goto out; - - if (cm_suspended) - device_set_wakeup_capable(cm->dev, true); - - mod_delayed_work(cm_wq, &cm->fullbatt_vchk_work, - msecs_to_jiffies(desc->fullbatt_vchkdrop_ms)); - cm->fullbatt_vchk_jiffies_at = jiffies + msecs_to_jiffies( - desc->fullbatt_vchkdrop_ms); - - if (cm->fullbatt_vchk_jiffies_at == 0) - cm->fullbatt_vchk_jiffies_at = 1; - -out: - dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged\n"); - power_supply_changed(cm->charger_psy); -} - -/** - * battout_handler - Event handler for CM_EVENT_BATT_OUT - * @cm: the Charger Manager representing the battery. - */ -static void battout_handler(struct charger_manager *cm) -{ - if (cm_suspended) - device_set_wakeup_capable(cm->dev, true); - - if (!is_batt_present(cm)) { - dev_emerg(cm->dev, "Battery Pulled Out!\n"); - power_supply_changed(cm->charger_psy); - } else { - power_supply_changed(cm->charger_psy); - } -} - -/** - * misc_event_handler - Handler for other events - * @cm: the Charger Manager representing the battery. - * @type: the Charger Manager representing the battery. - */ -static void misc_event_handler(struct charger_manager *cm, - enum cm_event_types type) -{ - if (cm_suspended) - device_set_wakeup_capable(cm->dev, true); - - if (is_polling_required(cm) && cm->desc->polling_interval_ms) - schedule_work(&setup_polling); - power_supply_changed(cm->charger_psy); -} - static int charger_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -1000,21 +935,6 @@ static bool cm_setup_timer(void) mutex_lock(&cm_list_mtx); list_for_each_entry(cm, &cm_list, entry) { - unsigned int fbchk_ms = 0; - - /* fullbatt_vchk is required. setup timer for that */ - if (cm->fullbatt_vchk_jiffies_at) { - fbchk_ms = jiffies_to_msecs(cm->fullbatt_vchk_jiffies_at - - jiffies); - if (time_is_before_eq_jiffies( - cm->fullbatt_vchk_jiffies_at) || - msecs_to_jiffies(fbchk_ms) < CM_JIFFIES_SMALL) { - fullbatt_vchk(&cm->fullbatt_vchk_work.work); - fbchk_ms = 0; - } - } - CM_MIN_VALID(wakeup_ms, fbchk_ms); - /* Skip if polling is not required for this CM */ if (!is_polling_required(cm) && !cm->emergency_stop) continue; @@ -1422,8 +1342,6 @@ static struct charger_desc *of_cm_parse_desc(struct device *dev) of_property_read_u32(np, "cm-poll-interval", &desc->polling_interval_ms); - of_property_read_u32(np, "cm-fullbatt-vchkdrop-ms", - &desc->fullbatt_vchkdrop_ms); of_property_read_u32(np, "cm-fullbatt-vchkdrop-volt", &desc->fullbatt_vchkdrop_uV); of_property_read_u32(np, "cm-fullbatt-voltage", &desc->fullbatt_uV); @@ -1585,9 +1503,8 @@ static int charger_manager_probe(struct platform_device *pdev) if (desc->fullbatt_uV == 0) { dev_info(&pdev->dev, "Ignoring full-battery voltage threshold as it is not supplied\n"); } - if (!desc->fullbatt_vchkdrop_ms || !desc->fullbatt_vchkdrop_uV) { + if (!desc->fullbatt_vchkdrop_uV) { dev_info(&pdev->dev, "Disabling full-battery voltage drop checking mechanism as it is not supplied\n"); - desc->fullbatt_vchkdrop_ms = 0; desc->fullbatt_vchkdrop_uV = 0; } if (desc->fullbatt_soc == 0) { @@ -1693,8 +1610,6 @@ static int charger_manager_probe(struct platform_device *pdev) cm->charger_psy_desc.properties = properties; cm->charger_psy_desc.num_properties = num_properties; - INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk); - /* Register sysfs entry for charger(regulator) */ ret = charger_manager_prepare_sysfs(cm); if (ret < 0) { @@ -1834,8 +1749,6 @@ static bool cm_need_to_awake(void) static int cm_suspend_prepare(struct device *dev) { - struct charger_manager *cm = dev_get_drvdata(dev); - if (cm_need_to_awake()) return -EBUSY; @@ -1847,7 +1760,6 @@ static int cm_suspend_prepare(struct device *dev) if (cm_timer_set) { cancel_work_sync(&setup_polling); cancel_delayed_work_sync(&cm_monitor_work); - cancel_delayed_work(&cm->fullbatt_vchk_work); } return 0; @@ -1872,31 +1784,6 @@ static void cm_suspend_complete(struct device *dev) _cm_monitor(cm); - /* Re-enqueue delayed work (fullbatt_vchk_work) */ - if (cm->fullbatt_vchk_jiffies_at) { - unsigned long delay = 0; - unsigned long now = jiffies + CM_JIFFIES_SMALL; - - if (time_after_eq(now, cm->fullbatt_vchk_jiffies_at)) { - delay = (unsigned long)((long)now - - (long)(cm->fullbatt_vchk_jiffies_at)); - delay = jiffies_to_msecs(delay); - } else { - delay = 0; - } - - /* - * Account for cm_suspend_duration_ms with assuming that - * timer stops in suspend. - */ - if (delay > cm_suspend_duration_ms) - delay -= cm_suspend_duration_ms; - else - delay = 0; - - queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work, - msecs_to_jiffies(delay)); - } device_set_wakeup_capable(cm->dev, false); } @@ -1938,56 +1825,6 @@ static void __exit charger_manager_cleanup(void) } module_exit(charger_manager_cleanup); -/** - * cm_notify_event - charger driver notify Charger Manager of charger event - * @psy: pointer to instance of charger's power_supply - * @type: type of charger event - * @msg: optional message passed to uevent_notify function - */ -void cm_notify_event(struct power_supply *psy, enum cm_event_types type, - char *msg) -{ - struct charger_manager *cm; - bool found_power_supply = false; - - if (psy == NULL) - return; - - mutex_lock(&cm_list_mtx); - list_for_each_entry(cm, &cm_list, entry) { - if (match_string(cm->desc->psy_charger_stat, -1, - psy->desc->name) >= 0) { - found_power_supply = true; - break; - } - } - mutex_unlock(&cm_list_mtx); - - if (!found_power_supply) - return; - - switch (type) { - case CM_EVENT_BATT_FULL: - fullbatt_handler(cm); - break; - case CM_EVENT_BATT_OUT: - battout_handler(cm); - break; - case CM_EVENT_BATT_IN: - case CM_EVENT_EXT_PWR_IN_OUT ... CM_EVENT_CHG_START_STOP: - misc_event_handler(cm, type); - break; - case CM_EVENT_UNKNOWN: - case CM_EVENT_OTHERS: - power_supply_changed(cm->charger_psy); - break; - default: - dev_err(cm->dev, "%s: type not specified\n", __func__); - break; - } -} -EXPORT_SYMBOL_GPL(cm_notify_event); - MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); MODULE_DESCRIPTION("Charger Manager"); MODULE_LICENSE("GPL"); -- cgit From cdaeb15157cd3be32fcf05c668a70c8f7e009d5e Mon Sep 17 00:00:00 2001 From: Jonathan Bakker <xc-racer2@live.ca> Date: Thu, 14 May 2020 16:04:28 -0700 Subject: power: supply: charger-manager: Always use POWER_SUPPLY_PROP_TEMP We were using POWER_SUPPLY_PROP_TEMP if the temperature was coming via the fuel gauge and POWER_SUPPLY_PROP_TEMP_AMBIENT if it was coming via the thermal framework. Since they're mutually exclusive in the driver and we don't know if the thermal framework is ambient or not, unify them both to use POWER_SUPPLY_PROP_TEMP. Signed-off-by: Jonathan Bakker <xc-racer2@live.ca> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/charger-manager.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index c18dc8d3ae1c..1edeb5625f29 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -791,7 +791,6 @@ static int charger_get_property(struct power_supply *psy, POWER_SUPPLY_PROP_CURRENT_NOW, val); break; case POWER_SUPPLY_PROP_TEMP: - case POWER_SUPPLY_PROP_TEMP_AMBIENT: return cm_get_battery_temperature(cm, &val->intval); case POWER_SUPPLY_PROP_CAPACITY: if (!is_batt_present(cm)) { @@ -899,8 +898,7 @@ static enum power_supply_property default_charger_props[] = { * Optional properties are: * POWER_SUPPLY_PROP_CHARGE_NOW, * POWER_SUPPLY_PROP_CURRENT_NOW, - * POWER_SUPPLY_PROP_TEMP, and - * POWER_SUPPLY_PROP_TEMP_AMBIENT, + * POWER_SUPPLY_PROP_TEMP, */ }; @@ -1298,7 +1296,7 @@ static int cm_init_thermal_data(struct charger_manager *cm, return PTR_ERR(cm->tzd_batt); /* Use external thermometer */ - properties[*num_properties] = POWER_SUPPLY_PROP_TEMP_AMBIENT; + properties[*num_properties] = POWER_SUPPLY_PROP_TEMP; (*num_properties)++; cm->desc->measure_battery_temp = true; ret = 0; -- cgit From 0a9e0f94bfcfbaf0bd124fa7af7b281bbda3bcca Mon Sep 17 00:00:00 2001 From: Jonghwa Lee <jonghwa3.lee@samsung.com> Date: Thu, 14 May 2020 16:04:29 -0700 Subject: power: supply: charger-manager: Correct usage of CHARGE_NOW/FULL The POWER_SUPPLY_CHARGE_NOW/FULL property reflects battery's charges in uAh unit, but charger-manager has been used it wrongly as a status field. Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com> Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com> Signed-off-by: Jonathan Bakker <xc-racer2@live.ca> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/charger-manager.c | 40 ++++++++++------------------------ 1 file changed, 12 insertions(+), 28 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index 1edeb5625f29..34817df76020 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -846,35 +846,13 @@ static int charger_get_property(struct power_supply *psy, val->intval = 0; break; case POWER_SUPPLY_PROP_CHARGE_FULL: - if (is_full_charged(cm)) - val->intval = 1; - else - val->intval = 0; - ret = 0; - break; case POWER_SUPPLY_PROP_CHARGE_NOW: - if (is_charging(cm)) { - fuel_gauge = power_supply_get_by_name( - cm->desc->psy_fuel_gauge); - if (!fuel_gauge) { - ret = -ENODEV; - break; - } - - ret = power_supply_get_property(fuel_gauge, - POWER_SUPPLY_PROP_CHARGE_NOW, - val); - if (ret) { - val->intval = 1; - ret = 0; - } else { - /* If CHARGE_NOW is supplied, use it */ - val->intval = (val->intval > 0) ? - val->intval : 1; - } - } else { - val->intval = 0; + fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); + if (!fuel_gauge) { + ret = -ENODEV; + break; } + ret = power_supply_get_property(fuel_gauge, psp, val); break; default: return -EINVAL; @@ -893,9 +871,9 @@ static enum power_supply_property default_charger_props[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_ONLINE, - POWER_SUPPLY_PROP_CHARGE_FULL, /* * Optional properties are: + * POWER_SUPPLY_PROP_CHARGE_FULL, * POWER_SUPPLY_PROP_CHARGE_NOW, * POWER_SUPPLY_PROP_CURRENT_NOW, * POWER_SUPPLY_PROP_TEMP, @@ -1584,6 +1562,12 @@ static int charger_manager_probe(struct platform_device *pdev) desc->psy_fuel_gauge); return -ENODEV; } + if (!power_supply_get_property(fuel_gauge, + POWER_SUPPLY_PROP_CHARGE_FULL, &val)) { + properties[num_properties] = + POWER_SUPPLY_PROP_CHARGE_FULL; + num_properties++; + } if (!power_supply_get_property(fuel_gauge, POWER_SUPPLY_PROP_CHARGE_NOW, &val)) { properties[num_properties] = -- cgit From dfc63825aa15daa1d335e2291741949fd001b782 Mon Sep 17 00:00:00 2001 From: Jonghwa Lee <jonghwa3.lee@samsung.com> Date: Thu, 14 May 2020 16:04:30 -0700 Subject: power: supply: charger-manager: Collect all power_supply_changed() calls Current charger-manager calls power_supply_changed() whenever charging status is changed. Remove the separated power_supply_changed() calls and let it be called at end of try_charger_enable() function which is called to set charging/discharging. Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com> Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com> Signed-off-by: Jonathan Bakker <xc-racer2@live.ca> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/charger-manager.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index 34817df76020..cc8feb68694d 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -406,8 +406,10 @@ static int try_charger_enable(struct charger_manager *cm, bool enable) } } - if (!err) + if (!err) { cm->charger_enabled = enable; + power_supply_changed(cm->charger_psy); + } return err; } @@ -460,10 +462,8 @@ static void fullbatt_vchk(struct charger_manager *cm) dev_info(cm->dev, "VBATT dropped %duV after full-batt\n", diff); - if (diff > desc->fullbatt_vchkdrop_uV) { + if (diff > desc->fullbatt_vchkdrop_uV) try_charger_restart(cm); - power_supply_changed(cm->charger_psy); - } } /** @@ -493,7 +493,6 @@ static int check_charging_duration(struct charger_manager *cm) if (duration > desc->charging_max_duration_ms) { dev_info(cm->dev, "Charging duration exceed %ums\n", desc->charging_max_duration_ms); - power_supply_changed(cm->charger_psy); try_charger_enable(cm, false); ret = true; } @@ -504,7 +503,6 @@ static int check_charging_duration(struct charger_manager *cm) is_ext_pwr_online(cm)) { dev_info(cm->dev, "Discharging duration exceed %ums\n", desc->discharging_max_duration_ms); - power_supply_changed(cm->charger_psy); try_charger_enable(cm, true); ret = true; } @@ -613,8 +611,7 @@ static bool _cm_monitor(struct charger_manager *cm) */ if (temp_alrt) { cm->emergency_stop = temp_alrt; - if (!try_charger_enable(cm, false)) - power_supply_changed(cm->charger_psy); + try_charger_enable(cm, false); /* * Check whole charging duration and discharging duration @@ -639,16 +636,13 @@ static bool _cm_monitor(struct charger_manager *cm) } else if (!cm->emergency_stop && is_full_charged(cm) && cm->charger_enabled) { dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged\n"); - power_supply_changed(cm->charger_psy); - try_charger_enable(cm, false); fullbatt_vchk(cm); } else { cm->emergency_stop = 0; if (is_ext_pwr_online(cm)) { - if (!try_charger_enable(cm, true)) - power_supply_changed(cm->charger_psy); + try_charger_enable(cm, true); } } -- cgit From e132fc6bb89bd307cfcdb8ba24afcd1985261485 Mon Sep 17 00:00:00 2001 From: Jonghwa Lee <jonghwa3.lee@samsung.com> Date: Thu, 14 May 2020 16:04:31 -0700 Subject: power: supply: charger-manager: Make decisions focussed on battery status cm_monitor(), where charging management starts, checks various charging condition sequentially to decide next charging operation. However, as it follows sequential process, cascaded if statements, it does some jobs which have already done in the previous stage. This results in a delay in decision making. Moreover, starting point of charging is spread all around which makes maintain code and debugging difficult. Both of the problems mentioned above become clean if it manages battery charging focusing on battery status not following sequential condition checking. Now, cm_monitor() moves battery state diagram and does the optimal operation for current state. As a result, it reduces whole monitoring time almost in half. Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com> Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com> Signed-off-by: Jonathan Bakker <xc-racer2@live.ca> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/charger-manager.c | 181 +++++++++++---------------------- 1 file changed, 61 insertions(+), 120 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index cc8feb68694d..d10bf89328a0 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -271,6 +271,19 @@ static bool is_full_charged(struct charger_manager *cm) if (!fuel_gauge) return false; + /* Full, if it's over the fullbatt voltage */ + if (desc->fullbatt_uV > 0) { + ret = get_batt_uV(cm, &uV); + if (!ret) { + /* Battery is already full, checks voltage drop. */ + if (cm->battery_status == POWER_SUPPLY_STATUS_FULL + && desc->fullbatt_vchkdrop_uV) + uV += desc->fullbatt_vchkdrop_uV; + if (uV >= desc->fullbatt_uV) + return true; + } + } + if (desc->fullbatt_full_capacity > 0) { val.intval = 0; @@ -283,15 +296,6 @@ static bool is_full_charged(struct charger_manager *cm) } } - /* Full, if it's over the fullbatt voltage */ - if (desc->fullbatt_uV > 0) { - ret = get_batt_uV(cm, &uV); - if (!ret && uV >= desc->fullbatt_uV) { - is_full = true; - goto out; - } - } - /* Full, if the capacity is more than fullbatt_soc */ if (desc->fullbatt_soc > 0) { val.intval = 0; @@ -406,66 +410,12 @@ static int try_charger_enable(struct charger_manager *cm, bool enable) } } - if (!err) { + if (!err) cm->charger_enabled = enable; - power_supply_changed(cm->charger_psy); - } return err; } -/** - * try_charger_restart - Restart charging. - * @cm: the Charger Manager representing the battery. - * - * Restart charging by turning off and on the charger. - */ -static int try_charger_restart(struct charger_manager *cm) -{ - int err; - - if (cm->emergency_stop) - return -EAGAIN; - - err = try_charger_enable(cm, false); - if (err) - return err; - - return try_charger_enable(cm, true); -} - -/** - * fullbatt_vchk - Check voltage drop some times after "FULL" event. - * - * If a user has designated "fullbatt_vchkdrop_uV" values with - * charger_desc, Charger Manager checks voltage drop after the battery - * "FULL" event. It checks whether the voltage has dropped more than - * fullbatt_vchkdrop_uV by calling this function after fullbatt_vchkrop_ms. - */ -static void fullbatt_vchk(struct charger_manager *cm) -{ - struct charger_desc *desc = cm->desc; - int batt_uV, err, diff; - - if (!desc->fullbatt_vchkdrop_uV) - return; - - err = get_batt_uV(cm, &batt_uV); - if (err) { - dev_err(cm->dev, "%s: get_batt_uV error(%d)\n", __func__, err); - return; - } - - diff = desc->fullbatt_uV - batt_uV; - if (diff < 0) - return; - - dev_info(cm->dev, "VBATT dropped %duV after full-batt\n", diff); - - if (diff > desc->fullbatt_vchkdrop_uV) - try_charger_restart(cm); -} - /** * check_charging_duration - Monitor charging/discharging duration * @cm: the Charger Manager representing the battery. @@ -493,17 +443,14 @@ static int check_charging_duration(struct charger_manager *cm) if (duration > desc->charging_max_duration_ms) { dev_info(cm->dev, "Charging duration exceed %ums\n", desc->charging_max_duration_ms); - try_charger_enable(cm, false); ret = true; } - } else if (is_ext_pwr_online(cm) && !cm->charger_enabled) { + } else if (cm->battery_status == POWER_SUPPLY_STATUS_NOT_CHARGING) { duration = curr - cm->charging_end_time; - if (duration > desc->discharging_max_duration_ms && - is_ext_pwr_online(cm)) { + if (duration > desc->charging_max_duration_ms) { dev_info(cm->dev, "Discharging duration exceed %ums\n", desc->discharging_max_duration_ms); - try_charger_enable(cm, true); ret = true; } } @@ -585,9 +532,46 @@ static int cm_check_thermal_status(struct charger_manager *cm) else ret = CM_BATT_OK; + cm->emergency_stop = ret; + return ret; } +/** + * cm_get_target_status - Check current status and get next target status. + * @cm: the Charger Manager representing the battery. + */ +static int cm_get_target_status(struct charger_manager *cm) +{ + if (!is_ext_pwr_online(cm)) + return POWER_SUPPLY_STATUS_DISCHARGING; + + if (cm_check_thermal_status(cm)) { + /* Check if discharging duration exeeds limit. */ + if (check_charging_duration(cm)) + goto charging_ok; + return POWER_SUPPLY_STATUS_NOT_CHARGING; + } + + switch (cm->battery_status) { + case POWER_SUPPLY_STATUS_CHARGING: + /* Check if charging duration exeeds limit. */ + if (check_charging_duration(cm)) + return POWER_SUPPLY_STATUS_FULL; + fallthrough; + case POWER_SUPPLY_STATUS_FULL: + if (is_full_charged(cm)) + return POWER_SUPPLY_STATUS_FULL; + fallthrough; + default: + break; + } + +charging_ok: + /* Charging is allowed. */ + return POWER_SUPPLY_STATUS_CHARGING; +} + /** * _cm_monitor - Monitor the temperature and return true for exceptions. * @cm: the Charger Manager representing the battery. @@ -597,56 +581,18 @@ static int cm_check_thermal_status(struct charger_manager *cm) */ static bool _cm_monitor(struct charger_manager *cm) { - int temp_alrt; + int target; - temp_alrt = cm_check_thermal_status(cm); + target = cm_get_target_status(cm); - /* It has been stopped already */ - if (temp_alrt && cm->emergency_stop) - return false; - - /* - * Check temperature whether overheat or cold. - * If temperature is out of range normal state, stop charging. - */ - if (temp_alrt) { - cm->emergency_stop = temp_alrt; - try_charger_enable(cm, false); - - /* - * Check whole charging duration and discharging duration - * after full-batt. - */ - } else if (!cm->emergency_stop && check_charging_duration(cm)) { - dev_dbg(cm->dev, - "Charging/Discharging duration is out of range\n"); - /* - * Check dropped voltage of battery. If battery voltage is more - * dropped than fullbatt_vchkdrop_uV after fully charged state, - * charger-manager have to recharge battery. - */ - } else if (!cm->emergency_stop && is_ext_pwr_online(cm) && - !cm->charger_enabled) { - fullbatt_vchk(cm); + try_charger_enable(cm, (target == POWER_SUPPLY_STATUS_CHARGING)); - /* - * Check whether fully charged state to protect overcharge - * if charger-manager is charging for battery. - */ - } else if (!cm->emergency_stop && is_full_charged(cm) && - cm->charger_enabled) { - dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged\n"); - try_charger_enable(cm, false); - - fullbatt_vchk(cm); - } else { - cm->emergency_stop = 0; - if (is_ext_pwr_online(cm)) { - try_charger_enable(cm, true); - } + if (cm->battery_status != target) { + cm->battery_status = target; + power_supply_changed(cm->charger_psy); } - return true; + return (cm->battery_status == POWER_SUPPLY_STATUS_NOT_CHARGING); } /** @@ -751,12 +697,7 @@ static int charger_get_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_STATUS: - if (is_charging(cm)) - val->intval = POWER_SUPPLY_STATUS_CHARGING; - else if (is_ext_pwr_online(cm)) - val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; - else - val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + val->intval = cm->battery_status; break; case POWER_SUPPLY_PROP_HEALTH: if (cm->emergency_stop > 0) -- cgit From 9434e4530d4690d3c5c869b4528364d7636108b3 Mon Sep 17 00:00:00 2001 From: Jonghwa Lee <jonghwa3.lee@samsung.com> Date: Thu, 14 May 2020 16:04:32 -0700 Subject: power: supply: charger-manager: Don't start charging in cable nofitication Prevents direct charging control in cable notification and only set the input current limit according to cable type. Leave the enabling of charing to cm_monitor() where charging management proceeds. We may lose a few ms to enable charging compared to before, but it's more important that charging is enabled always in safe context. Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com> Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com> Signed-off-by: Jonathan Bakker <xc-racer2@live.ca> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/charger-manager.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index d10bf89328a0..d3b2ed3ef720 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -907,7 +907,8 @@ static void charger_extcon_work(struct work_struct *work) cable->min_uA, cable->max_uA); } - try_charger_enable(cable->cm, cable->attached); + cancel_delayed_work(&cm_monitor_work); + queue_delayed_work(cm_wq, &cm_monitor_work, 0); } /** @@ -930,15 +931,6 @@ static int charger_extcon_notifier(struct notifier_block *self, */ cable->attached = event; - /* - * Setup monitoring to check battery state - * when charger cable is attached. - */ - if (cable->attached && is_polling_required(cable->cm)) { - cancel_work_sync(&setup_polling); - schedule_work(&setup_polling); - } - /* * Setup work for controlling charger(regulator) * according to charger cable. -- cgit From c1f73028f75df43689feda4bc70573b7d18a618e Mon Sep 17 00:00:00 2001 From: Jonathan Bakker <xc-racer2@live.ca> Date: Thu, 14 May 2020 16:04:33 -0700 Subject: power: supply: charger-manager: Update extcon functions In commit 830ae442202e ("extcon: Remove the deprecated extcon functions") the function extcon_register_interest became a no-op returning an error, leading to non-functional behaviour in charger-manager. Additionally, a translation table is needed between the text representation of the extcon cable names and their IDs is needed. In order to retain DT compatibility, TA and CHARGE-DOWNSTREAM are added as they were present up until commit 11eecf910bd8 ("extcon: Modify the id and name of external connector") Signed-off-by: Jonathan Bakker <xc-racer2@live.ca> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/charger-manager.c | 82 +++++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 22 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index d3b2ed3ef720..5f536284d2ca 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -26,6 +26,29 @@ #include <linux/of.h> #include <linux/thermal.h> +static struct { + const char *name; + u64 extcon_type; +} extcon_mapping[] = { + /* Current textual representations */ + { "USB", EXTCON_USB }, + { "USB-HOST", EXTCON_USB_HOST }, + { "SDP", EXTCON_CHG_USB_SDP }, + { "DCP", EXTCON_CHG_USB_DCP }, + { "CDP", EXTCON_CHG_USB_CDP }, + { "ACA", EXTCON_CHG_USB_ACA }, + { "FAST-CHARGER", EXTCON_CHG_USB_FAST }, + { "SLOW-CHARGER", EXTCON_CHG_USB_SLOW }, + { "WPT", EXTCON_CHG_WPT }, + { "PD", EXTCON_CHG_USB_PD }, + { "DOCK", EXTCON_DOCK }, + { "JIG", EXTCON_JIG }, + { "MECHANICAL", EXTCON_MECHANICAL }, + /* Deprecated textual representations */ + { "TA", EXTCON_CHG_USB_SDP }, + { "CHARGE-DOWNSTREAM", EXTCON_CHG_USB_CDP }, +}; + /* * Default temperature threshold for charging. * Every temperature units are in tenth of centigrade. @@ -950,7 +973,8 @@ static int charger_extcon_notifier(struct notifier_block *self, static int charger_extcon_init(struct charger_manager *cm, struct charger_cable *cable) { - int ret; + int ret, i; + u64 extcon_type = EXTCON_NONE; /* * Charger manager use Extcon framework to identify @@ -959,14 +983,39 @@ static int charger_extcon_init(struct charger_manager *cm, */ INIT_WORK(&cable->wq, charger_extcon_work); cable->nb.notifier_call = charger_extcon_notifier; - ret = extcon_register_interest(&cable->extcon_dev, - cable->extcon_name, cable->name, &cable->nb); + + cable->extcon_dev = extcon_get_extcon_dev(cable->extcon_name); + if (IS_ERR_OR_NULL(cable->extcon_dev)) { + pr_err("Cannot find extcon_dev for %s (cable: %s)\n", + cable->extcon_name, cable->name); + if (cable->extcon_dev == NULL) + return -EPROBE_DEFER; + else + return PTR_ERR(cable->extcon_dev); + } + + for (i = 0; i < ARRAY_SIZE(extcon_mapping); i++) { + if (!strcmp(cable->name, extcon_mapping[i].name)) { + extcon_type = extcon_mapping[i].extcon_type; + break; + } + } + if (extcon_type == EXTCON_NONE) { + pr_err("Cannot find cable for type %s", cable->name); + return -EINVAL; + } + + cable->extcon_type = extcon_type; + + ret = devm_extcon_register_notifier(cm->dev, cable->extcon_dev, + cable->extcon_type, &cable->nb); if (ret < 0) { - pr_info("Cannot register extcon_dev for %s(cable: %s)\n", + pr_err("Cannot register extcon_dev for %s (cable: %s)\n", cable->extcon_name, cable->name); + return ret; } - return ret; + return 0; } /** @@ -983,6 +1032,7 @@ static int charger_manager_register_extcon(struct charger_manager *cm) { struct charger_desc *desc = cm->desc; struct charger_regulator *charger; + unsigned long event; int ret; int i; int j; @@ -1010,6 +1060,11 @@ static int charger_manager_register_extcon(struct charger_manager *cm) } cable->charger = charger; cable->cm = cm; + + event = extcon_get_state(cable->extcon_dev, + cable->extcon_type); + charger_extcon_notifier(&cable->nb, + event, NULL); } } @@ -1370,7 +1425,6 @@ static int charger_manager_probe(struct platform_device *pdev) struct charger_desc *desc = cm_get_drv_data(pdev); struct charger_manager *cm; int ret, i = 0; - int j = 0; union power_supply_propval val; struct power_supply *fuel_gauge; enum power_supply_property *properties; @@ -1572,12 +1626,6 @@ err_reg_extcon: struct charger_regulator *charger; charger = &desc->charger_regulators[i]; - for (j = 0; j < charger->num_cables; j++) { - struct charger_cable *cable = &charger->cables[j]; - /* Remove notifier block if only edev exists */ - if (cable->extcon_dev.edev) - extcon_unregister_interest(&cable->extcon_dev); - } regulator_put(desc->charger_regulators[i].consumer); } @@ -1592,7 +1640,6 @@ static int charger_manager_remove(struct platform_device *pdev) struct charger_manager *cm = platform_get_drvdata(pdev); struct charger_desc *desc = cm->desc; int i = 0; - int j = 0; /* Remove from the list */ mutex_lock(&cm_list_mtx); @@ -1602,15 +1649,6 @@ static int charger_manager_remove(struct platform_device *pdev) cancel_work_sync(&setup_polling); cancel_delayed_work_sync(&cm_monitor_work); - for (i = 0 ; i < desc->num_charger_regulators ; i++) { - struct charger_regulator *charger - = &desc->charger_regulators[i]; - for (j = 0 ; j < charger->num_cables ; j++) { - struct charger_cable *cable = &charger->cables[j]; - extcon_unregister_interest(&cable->extcon_dev); - } - } - for (i = 0 ; i < desc->num_charger_regulators ; i++) regulator_put(desc->charger_regulators[i].consumer); -- cgit From 683aa86eb16ab8cf91075654d08ef7409f160e35 Mon Sep 17 00:00:00 2001 From: Jonathan Bakker <xc-racer2@live.ca> Date: Thu, 14 May 2020 16:04:34 -0700 Subject: power: supply: charger-manager: Count cm-chargers property directly Rather than having a cm-chargers and a separate cm-num-chargers property, simply count the entries in cm-chargers. Signed-off-by: Jonathan Bakker <xc-racer2@live.ca> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/charger-manager.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index 5f536284d2ca..07992821e252 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -1311,8 +1311,8 @@ static struct charger_desc *of_cm_parse_desc(struct device *dev) desc->battery_present = battery_stat; /* chargers */ - of_property_read_u32(np, "cm-num-chargers", &num_chgs); - if (num_chgs) { + num_chgs = of_property_count_strings(np, "cm-chargers"); + if (num_chgs > 0) { int i; /* Allocate empty bin at the tail of array */ -- cgit From 3f41e742ee9413b4d1f5d2edf3c71a5db0c87b81 Mon Sep 17 00:00:00 2001 From: Wang Qing <wangqing@vivo.com> Date: Sat, 26 Sep 2020 10:41:07 +0800 Subject: power: supply: ds278x: fix spelling typo Modify the comment typo: "compliment" -> "complement". Signed-off-by: Wang Qing <wangqing@vivo.com> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/ds2780_battery.c | 6 +++--- drivers/power/supply/ds2781_battery.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/ds2780_battery.c b/drivers/power/supply/ds2780_battery.c index db3a25404c9f..dd57a472e878 100644 --- a/drivers/power/supply/ds2780_battery.c +++ b/drivers/power/supply/ds2780_battery.c @@ -160,7 +160,7 @@ static int ds2780_get_voltage(struct ds2780_device_info *dev_info, /* * The voltage value is located in 10 bits across the voltage MSB - * and LSB registers in two's compliment form + * and LSB registers in two's complement form * Sign bit of the voltage value is in bit 7 of the voltage MSB register * Bits 9 - 3 of the voltage value are in bits 6 - 0 of the * voltage MSB register @@ -188,7 +188,7 @@ static int ds2780_get_temperature(struct ds2780_device_info *dev_info, /* * The temperature value is located in 10 bits across the temperature - * MSB and LSB registers in two's compliment form + * MSB and LSB registers in two's complement form * Sign bit of the temperature value is in bit 7 of the temperature * MSB register * Bits 9 - 3 of the temperature value are in bits 6 - 0 of the @@ -241,7 +241,7 @@ static int ds2780_get_current(struct ds2780_device_info *dev_info, /* * The current value is located in 16 bits across the current MSB - * and LSB registers in two's compliment form + * and LSB registers in two's complement form * Sign bit of the current value is in bit 7 of the current MSB register * Bits 14 - 8 of the current value are in bits 6 - 0 of the current * MSB register diff --git a/drivers/power/supply/ds2781_battery.c b/drivers/power/supply/ds2781_battery.c index 130cbdfc14eb..3df3c820b38c 100644 --- a/drivers/power/supply/ds2781_battery.c +++ b/drivers/power/supply/ds2781_battery.c @@ -168,7 +168,7 @@ static int ds2781_get_voltage(struct ds2781_device_info *dev_info, return ret; /* * The voltage value is located in 10 bits across the voltage MSB - * and LSB registers in two's compliment form + * and LSB registers in two's complement form * Sign bit of the voltage value is in bit 7 of the voltage MSB register * Bits 9 - 3 of the voltage value are in bits 6 - 0 of the * voltage MSB register @@ -197,7 +197,7 @@ static int ds2781_get_temperature(struct ds2781_device_info *dev_info, return ret; /* * The temperature value is located in 10 bits across the temperature - * MSB and LSB registers in two's compliment form + * MSB and LSB registers in two's complement form * Sign bit of the temperature value is in bit 7 of the temperature * MSB register * Bits 9 - 3 of the temperature value are in bits 6 - 0 of the @@ -242,7 +242,7 @@ static int ds2781_get_current(struct ds2781_device_info *dev_info, /* * The current value is located in 16 bits across the current MSB - * and LSB registers in two's compliment form + * and LSB registers in two's complement form * Sign bit of the current value is in bit 7 of the current MSB register * Bits 14 - 8 of the current value are in bits 6 - 0 of the current * MSB register -- cgit From 724083293e18adf805ce5aa6dda2fe6c12071537 Mon Sep 17 00:00:00 2001 From: MichaÅ‚ MirosÅ‚aw <mirq-linux@rere.qmqm.pl> Date: Sat, 26 Sep 2020 21:05:34 +0200 Subject: power: supply: bq25890: support IBAT compensation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add configuration for compensation of IBAT measuring resistor in series with the battery. Signed-off-by: MichaÅ‚ MirosÅ‚aw <mirq-linux@rere.qmqm.pl> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/bq25890_charger.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index 77150667e36b..ab8398f935c5 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -83,6 +83,8 @@ struct bq25890_init_data { u8 boostf; /* boost frequency */ u8 ilim_en; /* enable ILIM pin */ u8 treg; /* thermal regulation threshold */ + u8 rbatcomp; /* IBAT sense resistor value */ + u8 vclamp; /* IBAT compensation voltage limit */ }; struct bq25890_state { @@ -258,6 +260,8 @@ enum bq25890_table_ids { TBL_VREG, TBL_BOOSTV, TBL_SYSVMIN, + TBL_VBATCOMP, + TBL_RBATCOMP, /* lookup tables */ TBL_TREG, @@ -299,6 +303,8 @@ static const union { [TBL_VREG] = { .rt = {3840000, 4608000, 16000} }, /* uV */ [TBL_BOOSTV] = { .rt = {4550000, 5510000, 64000} }, /* uV */ [TBL_SYSVMIN] = { .rt = {3000000, 3700000, 100000} }, /* uV */ + [TBL_VBATCOMP] ={ .rt = {0, 224000, 32000} }, /* uV */ + [TBL_RBATCOMP] ={ .rt = {0, 140000, 20000} }, /* uOhm */ /* lookup tables */ [TBL_TREG] = { .lt = {bq25890_treg_tbl, BQ25890_TREG_TBL_SIZE} }, @@ -648,7 +654,9 @@ static int bq25890_hw_init(struct bq25890_device *bq) {F_BOOSTI, bq->init_data.boosti}, {F_BOOSTF, bq->init_data.boostf}, {F_EN_ILIM, bq->init_data.ilim_en}, - {F_TREG, bq->init_data.treg} + {F_TREG, bq->init_data.treg}, + {F_BATCMP, bq->init_data.rbatcomp}, + {F_VCLAMP, bq->init_data.vclamp}, }; ret = bq25890_chip_reset(bq); @@ -859,11 +867,14 @@ static int bq25890_fw_read_u32_props(struct bq25890_device *bq) {"ti,boost-max-current", false, TBL_BOOSTI, &init->boosti}, /* optional properties */ - {"ti,thermal-regulation-threshold", true, TBL_TREG, &init->treg} + {"ti,thermal-regulation-threshold", true, TBL_TREG, &init->treg}, + {"ti,ibatcomp-micro-ohms", true, TBL_RBATCOMP, &init->rbatcomp}, + {"ti,ibatcomp-clamp-microvolt", true, TBL_VBATCOMP, &init->vclamp}, }; /* initialize data for optional properties */ init->treg = 3; /* 120 degrees Celsius */ + init->rbatcomp = init->vclamp = 0; /* IBAT compensation disabled */ for (i = 0; i < ARRAY_SIZE(props); i++) { ret = device_property_read_u32(bq->dev, props[i].name, -- cgit From ec3af53aa4edf9c770a1b31e3ee6fa6602e98ee2 Mon Sep 17 00:00:00 2001 From: Wang Qing <wangqing@vivo.com> Date: Thu, 24 Sep 2020 14:37:56 +0800 Subject: power: supply: ab8500-fg: fix spelling typo Modify the comment typo: "compliment" -> "complement". Signed-off-by: Wang Qing <wangqing@vivo.com> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/ab8500_fg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index 751c4f6c7487..3abd2cd1ee92 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -653,7 +653,7 @@ int ab8500_fg_inst_curr_finalize(struct ab8500_fg *di, int *res) /* * negative value for Discharging - * convert 2's compliment into decimal + * convert 2's complement into decimal */ if (high & 0x10) val = (low | (high << 8) | 0xFFFFE000); @@ -781,7 +781,7 @@ static void ab8500_fg_acc_cur_work(struct work_struct *work) if (ret < 0) goto exit; - /* Check for sign bit in case of negative value, 2's compliment */ + /* Check for sign bit in case of negative value, 2's complement */ if (high & 0x10) val = (low | (med << 8) | (high << 16) | 0xFFE00000); else -- cgit From 81196e2e57fc5b88f6b8ca98372a3dde047aa49d Mon Sep 17 00:00:00 2001 From: Lucas Stach <l.stach@pengutronix.de> Date: Wed, 30 Sep 2020 10:40:47 +0200 Subject: power: supply: ucs1002: fix some health status issues Some fault events like the over-current condition will get resolved by the hardware, by e.g. disabling the port. As the status in the interrupt status register is cleared on read when the fault is resolved, the sysfs health property will only contain the correct health status for the first time it is read after such an event, even if the actual fault condition (like a VBUS short) still persists. To reflect this properly in the property we cache the last health status and only update the cache when a actual change happens, i.e. the ERR bit in the status register flips, as this one properly reflects a continued fault condition. The ALERT pin however, is not driven by the ERR status, but by the actual fault status, so the pin will change back to it's default state when the hardware has automatically resolved the fault by cutting the power. Thus we never get an IRQ when the actual fault condition has been resolved and the ERR status bit has been cleared in auto-recovery mode. To get this information we need to poll the interrupt status register after some time to see if the fault is gone and update our cache in that case. To avoid any additional locking, we handle both paths (IRQ firing and delayed polling) through the same single-threaded delayed work. Signed-off-by: Lucas Stach <l.stach@pengutronix.de> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/ucs1002_power.c | 75 +++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 32 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/ucs1002_power.c b/drivers/power/supply/ucs1002_power.c index cdb9a23d825f..ef673ec3db56 100644 --- a/drivers/power/supply/ucs1002_power.c +++ b/drivers/power/supply/ucs1002_power.c @@ -38,6 +38,7 @@ /* Interrupt Status */ #define UCS1002_REG_INTERRUPT_STATUS 0x10 +# define F_ERR BIT(7) # define F_DISCHARGE_ERR BIT(6) # define F_RESET BIT(5) # define F_MIN_KEEP_OUT BIT(4) @@ -103,6 +104,9 @@ struct ucs1002_info { struct regulator_dev *rdev; bool present; bool output_disable; + struct delayed_work health_poll; + int health; + }; static enum power_supply_property ucs1002_props[] = { @@ -362,32 +366,6 @@ static int ucs1002_get_usb_type(struct ucs1002_info *info, return 0; } -static int ucs1002_get_health(struct ucs1002_info *info, - union power_supply_propval *val) -{ - unsigned int reg; - int ret, health; - - ret = regmap_read(info->regmap, UCS1002_REG_INTERRUPT_STATUS, ®); - if (ret) - return ret; - - if (reg & F_TSD) - health = POWER_SUPPLY_HEALTH_OVERHEAT; - else if (reg & (F_OVER_VOLT | F_BACK_VOLT)) - health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; - else if (reg & F_OVER_ILIM) - health = POWER_SUPPLY_HEALTH_OVERCURRENT; - else if (reg & (F_DISCHARGE_ERR | F_MIN_KEEP_OUT)) - health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; - else - health = POWER_SUPPLY_HEALTH_GOOD; - - val->intval = health; - - return 0; -} - static int ucs1002_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -406,7 +384,7 @@ static int ucs1002_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_USB_TYPE: return ucs1002_get_usb_type(info, val); case POWER_SUPPLY_PROP_HEALTH: - return ucs1002_get_health(info, val); + return val->intval = info->health; case POWER_SUPPLY_PROP_PRESENT: val->intval = info->present; return 0; @@ -458,6 +436,38 @@ static const struct power_supply_desc ucs1002_charger_desc = { .num_properties = ARRAY_SIZE(ucs1002_props), }; +static void ucs1002_health_poll(struct work_struct *work) +{ + struct ucs1002_info *info = container_of(work, struct ucs1002_info, + health_poll.work); + int ret; + u32 reg; + + ret = regmap_read(info->regmap, UCS1002_REG_INTERRUPT_STATUS, ®); + if (ret) + return; + + /* bad health and no status change, just schedule us again in a while */ + if ((reg & F_ERR) && info->health != POWER_SUPPLY_HEALTH_GOOD) { + schedule_delayed_work(&info->health_poll, + msecs_to_jiffies(2000)); + return; + } + + if (reg & F_TSD) + info->health = POWER_SUPPLY_HEALTH_OVERHEAT; + else if (reg & (F_OVER_VOLT | F_BACK_VOLT)) + info->health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + else if (reg & F_OVER_ILIM) + info->health = POWER_SUPPLY_HEALTH_OVERCURRENT; + else if (reg & (F_DISCHARGE_ERR | F_MIN_KEEP_OUT)) + info->health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + else + info->health = POWER_SUPPLY_HEALTH_GOOD; + + sysfs_notify(&info->charger->dev.kobj, NULL, "health"); +} + static irqreturn_t ucs1002_charger_irq(int irq, void *data) { int ret, regval; @@ -484,7 +494,7 @@ static irqreturn_t ucs1002_alert_irq(int irq, void *data) { struct ucs1002_info *info = data; - power_supply_changed(info->charger); + mod_delayed_work(system_wq, &info->health_poll, 0); return IRQ_HANDLED; } @@ -632,6 +642,9 @@ static int ucs1002_probe(struct i2c_client *client, return ret; } + info->health = POWER_SUPPLY_HEALTH_GOOD; + INIT_DELAYED_WORK(&info->health_poll, ucs1002_health_poll); + if (irq_a_det > 0) { ret = devm_request_threaded_irq(dev, irq_a_det, NULL, ucs1002_charger_irq, @@ -645,10 +658,8 @@ static int ucs1002_probe(struct i2c_client *client, } if (irq_alert > 0) { - ret = devm_request_threaded_irq(dev, irq_alert, NULL, - ucs1002_alert_irq, - IRQF_ONESHOT, - "ucs1002-alert", info); + ret = devm_request_irq(dev, irq_alert, ucs1002_alert_irq, + 0,"ucs1002-alert", info); if (ret) { dev_err(dev, "Failed to request ALERT threaded irq: %d\n", ret); -- cgit From 7bf738ba110722b63e9dc8af760d3fb2aef25593 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski <krzk@kernel.org> Date: Sat, 19 Sep 2020 16:04:14 +0200 Subject: power: supply: bq27xxx: report "not charging" on all types Commit 6f24ff97e323 ("power: supply: bq27xxx_battery: Add the BQ27Z561 Battery monitor") and commit d74534c27775 ("power: bq27xxx_battery: Add support for additional bq27xxx family devices") added support for new device types by copying most of the code and adding necessary quirks. However they did not copy the code in bq27xxx_battery_status() responsible for returning POWER_SUPPLY_STATUS_NOT_CHARGING. Unify the bq27xxx_battery_status() so for all types when charger is supplied, it will return "not charging" status. Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/bq27xxx_battery.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c index 92de7b720182..65806f668b1f 100644 --- a/drivers/power/supply/bq27xxx_battery.c +++ b/drivers/power/supply/bq27xxx_battery.c @@ -1765,8 +1765,6 @@ static int bq27xxx_battery_status(struct bq27xxx_device_info *di, status = POWER_SUPPLY_STATUS_FULL; else if (di->cache.flags & BQ27000_FLAG_CHGS) status = POWER_SUPPLY_STATUS_CHARGING; - else if (power_supply_am_i_supplied(di->bat) > 0) - status = POWER_SUPPLY_STATUS_NOT_CHARGING; else status = POWER_SUPPLY_STATUS_DISCHARGING; } else if (di->opts & BQ27Z561_O_BITS) { @@ -1785,6 +1783,10 @@ static int bq27xxx_battery_status(struct bq27xxx_device_info *di, status = POWER_SUPPLY_STATUS_CHARGING; } + if ((status == POWER_SUPPLY_STATUS_DISCHARGING) && + (power_supply_am_i_supplied(di->bat) > 0)) + status = POWER_SUPPLY_STATUS_NOT_CHARGING; + val->intval = status; return 0; -- cgit From bffa569fc9858aa04a763be6f4e6c91c76efb6d5 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski <krzk@kernel.org> Date: Sat, 19 Sep 2020 16:04:15 +0200 Subject: power: supply: bq27xxx: adjust whitespace and use BIT() for bitflags BIT() is a preferred way to toggle bit-like flags: no problems with 32/64 bit systems, less chances for mistakes. Remove also unneeded whitespace. Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/bq27xxx_battery.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c index 65806f668b1f..e971af43dd45 100644 --- a/drivers/power/supply/bq27xxx_battery.c +++ b/drivers/power/supply/bq27xxx_battery.c @@ -76,7 +76,7 @@ /* BQ27Z561 has different layout for Flags register */ #define BQ27Z561_FLAG_FDC BIT(4) /* Battery fully discharged */ -#define BQ27Z561_FLAG_FC BIT(5) /* Battery fully charged */ +#define BQ27Z561_FLAG_FC BIT(5) /* Battery fully charged */ #define BQ27Z561_FLAG_DIS_CH BIT(6) /* Battery is discharging */ /* control register params */ @@ -847,13 +847,14 @@ static struct bq27xxx_dm_reg bq27621_dm_regs[] = { #define bq27z561_dm_regs 0 #define bq28z610_dm_regs 0 - -#define BQ27XXX_O_ZERO 0x00000001 -#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 -#define BQ27Z561_O_BITS 0x00000020 +#define bq34z100_dm_regs 0 + +#define BQ27XXX_O_ZERO BIT(0) +#define BQ27XXX_O_OTDC BIT(1) /* has OTC/OTD overtemperature flags */ +#define BQ27XXX_O_UTOT BIT(2) /* has OT overtemperature flag */ +#define BQ27XXX_O_CFGUP BIT(3) +#define BQ27XXX_O_RAM BIT(4) +#define BQ27Z561_O_BITS BIT(5) #define BQ27XXX_DATA(ref, key, opt) { \ .opts = (opt), \ -- cgit From c02ca2019866726e951176ac2fca2a9b516eaacd Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski <krzk@kernel.org> Date: Sat, 19 Sep 2020 16:04:16 +0200 Subject: power: supply: bq27xxx: add separate flag for single SoC register bq27000, bq27010 and upcoming bq34z100 have a single byte SoC register. However except this similarity, bq34z100 is quite different than bq27000/bq27010, so flag BQ27XXX_O_ZERO cannot be reused here. Add a new bit flag describing that SoC is a single byte register. No functional change for bq27000 and bq27010. Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/bq27xxx_battery.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c index e971af43dd45..88cdad0ecb08 100644 --- a/drivers/power/supply/bq27xxx_battery.c +++ b/drivers/power/supply/bq27xxx_battery.c @@ -855,6 +855,7 @@ static struct bq27xxx_dm_reg bq27621_dm_regs[] = { #define BQ27XXX_O_CFGUP BIT(3) #define BQ27XXX_O_RAM BIT(4) #define BQ27Z561_O_BITS BIT(5) +#define BQ27XXX_O_SOC_SI BIT(6) /* SoC is single register */ #define BQ27XXX_DATA(ref, key, opt) { \ .opts = (opt), \ @@ -872,8 +873,8 @@ static struct { enum power_supply_property *props; size_t props_size; } bq27xxx_chip_data[] = { - [BQ27000] = BQ27XXX_DATA(bq27000, 0 , BQ27XXX_O_ZERO), - [BQ27010] = BQ27XXX_DATA(bq27010, 0 , BQ27XXX_O_ZERO), + [BQ27000] = BQ27XXX_DATA(bq27000, 0 , BQ27XXX_O_ZERO | BQ27XXX_O_SOC_SI), + [BQ27010] = BQ27XXX_DATA(bq27010, 0 , BQ27XXX_O_ZERO | BQ27XXX_O_SOC_SI), [BQ2750X] = BQ27XXX_DATA(bq2750x, 0 , BQ27XXX_O_OTDC), [BQ2751X] = BQ27XXX_DATA(bq2751x, 0 , BQ27XXX_O_OTDC), [BQ2752X] = BQ27XXX_DATA(bq2752x, 0 , BQ27XXX_O_OTDC), @@ -1420,7 +1421,7 @@ static int bq27xxx_battery_read_soc(struct bq27xxx_device_info *di) { int soc; - if (di->opts & BQ27XXX_O_ZERO) + if (di->opts & BQ27XXX_O_SOC_SI) soc = bq27xxx_read(di, BQ27XXX_REG_SOC, true); else soc = bq27xxx_read(di, BQ27XXX_REG_SOC, false); -- cgit From 7be64ae0bf369e035597691c1766d67e56f26ea1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski <krzk@kernel.org> Date: Sat, 19 Sep 2020 16:04:17 +0200 Subject: power: supply: bq27xxx: add separate flag for capacity inaccurate bq27000, bq27010 and upcoming bq34z100 have a Capacity Inaccurate flag. However except this similarity, bq34z100 is quite different than bq27000/bq27010, so flag BQ27XXX_O_ZERO cannot be reused here. Add a new bit flag describing this capability. No functional change for bq27000 and bq27010. Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/bq27xxx_battery.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c index 88cdad0ecb08..cda03e1f9586 100644 --- a/drivers/power/supply/bq27xxx_battery.c +++ b/drivers/power/supply/bq27xxx_battery.c @@ -856,6 +856,7 @@ static struct bq27xxx_dm_reg bq27621_dm_regs[] = { #define BQ27XXX_O_RAM BIT(4) #define BQ27Z561_O_BITS BIT(5) #define BQ27XXX_O_SOC_SI BIT(6) /* SoC is single register */ +#define BQ27XXX_O_HAS_CI BIT(7) /* has Capacity Inaccurate flag */ #define BQ27XXX_DATA(ref, key, opt) { \ .opts = (opt), \ @@ -873,8 +874,8 @@ static struct { enum power_supply_property *props; size_t props_size; } bq27xxx_chip_data[] = { - [BQ27000] = BQ27XXX_DATA(bq27000, 0 , BQ27XXX_O_ZERO | BQ27XXX_O_SOC_SI), - [BQ27010] = BQ27XXX_DATA(bq27010, 0 , BQ27XXX_O_ZERO | BQ27XXX_O_SOC_SI), + [BQ27000] = BQ27XXX_DATA(bq27000, 0 , BQ27XXX_O_ZERO | BQ27XXX_O_SOC_SI | BQ27XXX_O_HAS_CI), + [BQ27010] = BQ27XXX_DATA(bq27010, 0 , BQ27XXX_O_ZERO | BQ27XXX_O_SOC_SI | BQ27XXX_O_HAS_CI), [BQ2750X] = BQ27XXX_DATA(bq2750x, 0 , BQ27XXX_O_OTDC), [BQ2751X] = BQ27XXX_DATA(bq2751x, 0 , BQ27XXX_O_OTDC), [BQ2752X] = BQ27XXX_DATA(bq2752x, 0 , BQ27XXX_O_OTDC), @@ -1659,7 +1660,7 @@ static int bq27xxx_battery_read_health(struct bq27xxx_device_info *di) void bq27xxx_battery_update(struct bq27xxx_device_info *di) { struct bq27xxx_reg_cache cache = {0, }; - bool has_ci_flag = di->opts & BQ27XXX_O_ZERO; + bool has_ci_flag = di->opts & BQ27XXX_O_HAS_CI; bool has_singe_flag = di->opts & BQ27XXX_O_ZERO; cache.flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, has_singe_flag); -- cgit From 41a7431dbaa37533c3b732cdea425a7b8f2d4162 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski <krzk@kernel.org> Date: Sat, 19 Sep 2020 16:04:18 +0200 Subject: power: supply: bq27xxx: add support for TI bq34z100 Add support for new device: the TI bq34z100-G1, a Wide Range Fuel Gauge for Li-Ion, PbA, NiMH, and NiCd batteries. The device shares a lot with other models, although it has its own differences requiring new quirks. This patch was tested on a system equipped with NiMH batteries. Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/bq27xxx_battery.c | 50 +++++++++++++++++++++++++++++- drivers/power/supply/bq27xxx_battery_i2c.c | 2 ++ 2 files changed, 51 insertions(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c index cda03e1f9586..315e0909e6a4 100644 --- a/drivers/power/supply/bq27xxx_battery.c +++ b/drivers/power/supply/bq27xxx_battery.c @@ -38,6 +38,7 @@ * https://www.ti.com/product/bq27621-g1 * https://www.ti.com/product/bq27z561 * https://www.ti.com/product/bq28z610 + * https://www.ti.com/product/bq34z100-g1 */ #include <linux/device.h> @@ -476,6 +477,26 @@ static u8 [BQ27XXX_REG_DCAP] = 0x3c, [BQ27XXX_REG_AP] = 0x22, BQ27XXX_DM_REG_ROWS, + }, + bq34z100_regs[BQ27XXX_REG_MAX] = { + [BQ27XXX_REG_CTRL] = 0x00, + [BQ27XXX_REG_TEMP] = 0x0c, + [BQ27XXX_REG_INT_TEMP] = 0x2a, + [BQ27XXX_REG_VOLT] = 0x08, + [BQ27XXX_REG_AI] = 0x0a, + [BQ27XXX_REG_FLAGS] = 0x0e, + [BQ27XXX_REG_TTE] = 0x18, + [BQ27XXX_REG_TTF] = 0x1a, + [BQ27XXX_REG_TTES] = 0x1e, + [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, + [BQ27XXX_REG_NAC] = INVALID_REG_ADDR, + [BQ27XXX_REG_FCC] = 0x06, + [BQ27XXX_REG_CYCT] = 0x2c, + [BQ27XXX_REG_AE] = 0x24, + [BQ27XXX_REG_SOC] = 0x02, + [BQ27XXX_REG_DCAP] = 0x3c, + [BQ27XXX_REG_AP] = 0x22, + BQ27XXX_DM_REG_ROWS, }; static enum power_supply_property bq27000_props[] = { @@ -750,6 +771,27 @@ static enum power_supply_property bq28z610_props[] = { POWER_SUPPLY_PROP_MANUFACTURER, }; +static enum power_supply_property bq34z100_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, + POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_ENERGY_NOW, + POWER_SUPPLY_PROP_POWER_AVG, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_MANUFACTURER, +}; + struct bq27xxx_dm_reg { u8 subclass_id; u8 offset; @@ -857,6 +899,7 @@ static struct bq27xxx_dm_reg bq27621_dm_regs[] = { #define BQ27Z561_O_BITS BIT(5) #define BQ27XXX_O_SOC_SI BIT(6) /* SoC is single register */ #define BQ27XXX_O_HAS_CI BIT(7) /* has Capacity Inaccurate flag */ +#define BQ27XXX_O_MUL_CHEM BIT(8) /* multiple chemistries supported */ #define BQ27XXX_DATA(ref, key, opt) { \ .opts = (opt), \ @@ -903,6 +946,8 @@ static struct { [BQ27621] = BQ27XXX_DATA(bq27621, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM), [BQ27Z561] = BQ27XXX_DATA(bq27z561, 0 , BQ27Z561_O_BITS), [BQ28Z610] = BQ27XXX_DATA(bq28z610, 0 , BQ27Z561_O_BITS), + [BQ34Z100] = BQ27XXX_DATA(bq34z100, 0 , BQ27XXX_O_OTDC | BQ27XXX_O_SOC_SI | \ + BQ27XXX_O_HAS_CI | BQ27XXX_O_MUL_CHEM), }; static DEFINE_MUTEX(bq27xxx_list_lock); @@ -1914,7 +1959,10 @@ static int bq27xxx_battery_get_property(struct power_supply *psy, ret = bq27xxx_simple_value(di->cache.time_to_full, val); break; case POWER_SUPPLY_PROP_TECHNOLOGY: - val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + if (di->opts & BQ27XXX_O_MUL_CHEM) + val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; + else + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; break; case POWER_SUPPLY_PROP_CHARGE_NOW: ret = bq27xxx_simple_value(bq27xxx_battery_read_nac(di), val); diff --git a/drivers/power/supply/bq27xxx_battery_i2c.c b/drivers/power/supply/bq27xxx_battery_i2c.c index 765873dfc495..eb4f4284982f 100644 --- a/drivers/power/supply/bq27xxx_battery_i2c.c +++ b/drivers/power/supply/bq27xxx_battery_i2c.c @@ -247,6 +247,7 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = { { "bq27621", BQ27621 }, { "bq27z561", BQ27Z561 }, { "bq28z610", BQ28Z610 }, + { "bq34z100", BQ34Z100 }, {}, }; MODULE_DEVICE_TABLE(i2c, bq27xxx_i2c_id_table); @@ -282,6 +283,7 @@ static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = { { .compatible = "ti,bq27621" }, { .compatible = "ti,bq27z561" }, { .compatible = "ti,bq28z610" }, + { .compatible = "ti,bq34z100" }, {}, }; MODULE_DEVICE_TABLE(of, bq27xxx_battery_i2c_of_match_table); -- cgit From e55a50613d91515acaddaca8de46162feb9adb45 Mon Sep 17 00:00:00 2001 From: Iskren Chernev <iskren.chernev@gmail.com> Date: Tue, 22 Sep 2020 14:42:31 +0300 Subject: power: supply: max17040: Use devm_ to automate remove Two actions were performed during remove - power supply dereg and delayed work cleanup. Power supply dereg can be handled by using the devm_ version of the registration function. Work cleanup can be added as a devm_action. If probe fails after psy registration it will now be cleaned up properly. Signed-off-by: Iskren Chernev <iskren.chernev@gmail.com> Tested-by: Jonathan Bakker <xc-racer2@live.ca> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/max17040_battery.c | 39 ++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 18 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c index 6cb31b9a958d..19b9e710bbd2 100644 --- a/drivers/power/supply/max17040_battery.c +++ b/drivers/power/supply/max17040_battery.c @@ -207,6 +207,19 @@ static void max17040_check_changes(struct i2c_client *client) max17040_get_status(client); } +static void max17040_queue_work(struct max17040_chip *chip) +{ + queue_delayed_work(system_power_efficient_wq, &chip->work, + MAX17040_DELAY); +} + +static void max17040_stop_work(void *data) +{ + struct max17040_chip *chip = data; + + cancel_delayed_work_sync(&chip->work); +} + static void max17040_work(struct work_struct *work) { struct max17040_chip *chip; @@ -223,8 +236,7 @@ static void max17040_work(struct work_struct *work) if (last_soc != chip->soc || last_status != chip->status) power_supply_changed(chip->battery); - queue_delayed_work(system_power_efficient_wq, &chip->work, - MAX17040_DELAY); + max17040_queue_work(chip); } static irqreturn_t max17040_thread_handler(int id, void *dev) @@ -339,7 +351,7 @@ static int max17040_probe(struct i2c_client *client, i2c_set_clientdata(client, chip); psy_cfg.drv_data = chip; - chip->battery = power_supply_register(&client->dev, + chip->battery = devm_power_supply_register(&client->dev, &max17040_battery_desc, &psy_cfg); if (IS_ERR(chip->battery)) { dev_err(&client->dev, "failed: power supply register\n"); @@ -368,18 +380,11 @@ static int max17040_probe(struct i2c_client *client, } INIT_DEFERRABLE_WORK(&chip->work, max17040_work); - queue_delayed_work(system_power_efficient_wq, &chip->work, - MAX17040_DELAY); - - return 0; -} - -static int max17040_remove(struct i2c_client *client) -{ - struct max17040_chip *chip = i2c_get_clientdata(client); + ret = devm_add_action(&client->dev, max17040_stop_work, chip); + if (ret) + return ret; + max17040_queue_work(chip); - power_supply_unregister(chip->battery); - cancel_delayed_work(&chip->work); return 0; } @@ -403,12 +408,11 @@ static int max17040_resume(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct max17040_chip *chip = i2c_get_clientdata(client); - queue_delayed_work(system_power_efficient_wq, &chip->work, - MAX17040_DELAY); - if (client->irq && device_may_wakeup(dev)) disable_irq_wake(client->irq); + max17040_queue_work(chip); + return 0; } @@ -442,7 +446,6 @@ static struct i2c_driver max17040_i2c_driver = { .pm = MAX17040_PM_OPS, }, .probe = max17040_probe, - .remove = max17040_remove, .id_table = max17040_id, }; module_i2c_driver(max17040_i2c_driver); -- cgit From 6455a8a84bdfd34829d23328b9d106c2652885f2 Mon Sep 17 00:00:00 2001 From: Iskren Chernev <iskren.chernev@gmail.com> Date: Tue, 22 Sep 2020 14:42:32 +0300 Subject: power: supply: max17040: Use regmap i2c Rewrite i2c operations from i2c client read/write to regmap i2c. As a result, most private functions now accept the private driver data instead of an i2c client pointer. Signed-off-by: Iskren Chernev <iskren.chernev@gmail.com> Tested-by: Jonathan Bakker <xc-racer2@live.ca> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/Kconfig | 1 + drivers/power/supply/max17040_battery.c | 219 ++++++++++++++------------------ 2 files changed, 93 insertions(+), 127 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index a4657484f38b..47a4e1d363fc 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -367,6 +367,7 @@ config AXP288_FUEL_GAUGE config BATTERY_MAX17040 tristate "Maxim MAX17040 Fuel Gauge" depends on I2C + select REGMAP_I2C help MAX17040 is fuel-gauge systems for lithium-ion (Li+) batteries in handheld and portable equipment. The MAX17040 is configured diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c index 19b9e710bbd2..fae421796076 100644 --- a/drivers/power/supply/max17040_battery.c +++ b/drivers/power/supply/max17040_battery.c @@ -16,32 +16,30 @@ #include <linux/interrupt.h> #include <linux/power_supply.h> #include <linux/max17040_battery.h> +#include <linux/regmap.h> #include <linux/slab.h> #define MAX17040_VCELL 0x02 #define MAX17040_SOC 0x04 #define MAX17040_MODE 0x06 #define MAX17040_VER 0x08 -#define MAX17040_RCOMP 0x0C +#define MAX17040_CONFIG 0x0C #define MAX17040_CMD 0xFE #define MAX17040_DELAY 1000 #define MAX17040_BATTERY_FULL 95 -#define MAX17040_ATHD_MASK 0xFFC0 +#define MAX17040_ATHD_MASK 0x3f #define MAX17040_ATHD_DEFAULT_POWER_UP 4 struct max17040_chip { struct i2c_client *client; + struct regmap *regmap; struct delayed_work work; struct power_supply *battery; struct max17040_platform_data *pdata; - /* State Of Connect */ - int online; - /* battery voltage */ - int vcell; /* battery capacity */ int soc; /* State Of Charge */ @@ -50,138 +48,68 @@ struct max17040_chip { u32 low_soc_alert; }; -static int max17040_get_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) +static int max17040_reset(struct max17040_chip *chip) { - struct max17040_chip *chip = power_supply_get_drvdata(psy); - - switch (psp) { - case POWER_SUPPLY_PROP_STATUS: - val->intval = chip->status; - break; - case POWER_SUPPLY_PROP_ONLINE: - val->intval = chip->online; - break; - case POWER_SUPPLY_PROP_VOLTAGE_NOW: - val->intval = chip->vcell; - break; - case POWER_SUPPLY_PROP_CAPACITY: - val->intval = chip->soc; - break; - case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: - val->intval = chip->low_soc_alert; - break; - default: - return -EINVAL; - } - return 0; + return regmap_write(chip->regmap, MAX17040_CMD, 0x0054); } -static int max17040_write_reg(struct i2c_client *client, int reg, u16 value) +static int max17040_set_low_soc_alert(struct max17040_chip *chip, u32 level) { - int ret; - - ret = i2c_smbus_write_word_swapped(client, reg, value); - - if (ret < 0) - dev_err(&client->dev, "%s: err %d\n", __func__, ret); - - return ret; -} - -static int max17040_read_reg(struct i2c_client *client, int reg) -{ - int ret; - - ret = i2c_smbus_read_word_swapped(client, reg); - - if (ret < 0) - dev_err(&client->dev, "%s: err %d\n", __func__, ret); - - return ret; -} - -static void max17040_reset(struct i2c_client *client) -{ - max17040_write_reg(client, MAX17040_CMD, 0x0054); -} - -static int max17040_set_low_soc_alert(struct i2c_client *client, u32 level) -{ - int ret; - u16 data; - level = 32 - level; - data = max17040_read_reg(client, MAX17040_RCOMP); - /* clear the alrt bit and set LSb 5 bits */ - data &= MAX17040_ATHD_MASK; - data |= level; - ret = max17040_write_reg(client, MAX17040_RCOMP, data); - - return ret; + return regmap_update_bits(chip->regmap, MAX17040_CONFIG, + MAX17040_ATHD_MASK, level); } -static void max17040_get_vcell(struct i2c_client *client) +static int max17040_get_vcell(struct max17040_chip *chip) { - struct max17040_chip *chip = i2c_get_clientdata(client); - u16 vcell; + u32 vcell; - vcell = max17040_read_reg(client, MAX17040_VCELL); + regmap_read(chip->regmap, MAX17040_VCELL, &vcell); - chip->vcell = (vcell >> 4) * 1250; + return (vcell >> 4) * 1250; } -static void max17040_get_soc(struct i2c_client *client) +static int max17040_get_soc(struct max17040_chip *chip) { - struct max17040_chip *chip = i2c_get_clientdata(client); - u16 soc; + u32 soc; - soc = max17040_read_reg(client, MAX17040_SOC); + regmap_read(chip->regmap, MAX17040_SOC, &soc); - chip->soc = (soc >> 8); + return soc >> 8; } -static void max17040_get_version(struct i2c_client *client) +static int max17040_get_version(struct max17040_chip *chip) { - u16 version; + int ret; + u32 version; - version = max17040_read_reg(client, MAX17040_VER); + ret = regmap_read(chip->regmap, MAX17040_VER, &version); - dev_info(&client->dev, "MAX17040 Fuel-Gauge Ver 0x%x\n", version); + return ret ? ret : version; } -static void max17040_get_online(struct i2c_client *client) +static int max17040_get_online(struct max17040_chip *chip) { - struct max17040_chip *chip = i2c_get_clientdata(client); - - if (chip->pdata && chip->pdata->battery_online) - chip->online = chip->pdata->battery_online(); - else - chip->online = 1; + return chip->pdata && chip->pdata->battery_online ? + chip->pdata->battery_online() : 1; } -static void max17040_get_status(struct i2c_client *client) +static int max17040_get_status(struct max17040_chip *chip) { - struct max17040_chip *chip = i2c_get_clientdata(client); - if (!chip->pdata || !chip->pdata->charger_online - || !chip->pdata->charger_enable) { - chip->status = POWER_SUPPLY_STATUS_UNKNOWN; - return; - } + || !chip->pdata->charger_enable) + return POWER_SUPPLY_STATUS_UNKNOWN; + + if (max17040_get_soc(chip) > MAX17040_BATTERY_FULL) + return POWER_SUPPLY_STATUS_FULL; - if (chip->pdata->charger_online()) { + if (chip->pdata->charger_online()) if (chip->pdata->charger_enable()) - chip->status = POWER_SUPPLY_STATUS_CHARGING; + return POWER_SUPPLY_STATUS_CHARGING; else - chip->status = POWER_SUPPLY_STATUS_NOT_CHARGING; - } else { - chip->status = POWER_SUPPLY_STATUS_DISCHARGING; - } - - if (chip->soc > MAX17040_BATTERY_FULL) - chip->status = POWER_SUPPLY_STATUS_FULL; + return POWER_SUPPLY_STATUS_NOT_CHARGING; + else + return POWER_SUPPLY_STATUS_DISCHARGING; } static int max17040_get_of_data(struct max17040_chip *chip) @@ -193,18 +121,18 @@ static int max17040_get_of_data(struct max17040_chip *chip) "maxim,alert-low-soc-level", &chip->low_soc_alert); - if (chip->low_soc_alert <= 0 || chip->low_soc_alert >= 33) + if (chip->low_soc_alert <= 0 || chip->low_soc_alert >= 33) { + dev_err(dev, "maxim,alert-low-soc-level out of bounds\n"); return -EINVAL; + } return 0; } -static void max17040_check_changes(struct i2c_client *client) +static void max17040_check_changes(struct max17040_chip *chip) { - max17040_get_vcell(client); - max17040_get_soc(client); - max17040_get_online(client); - max17040_get_status(client); + chip->soc = max17040_get_soc(chip); + chip->status = max17040_get_status(chip); } static void max17040_queue_work(struct max17040_chip *chip) @@ -230,7 +158,7 @@ static void max17040_work(struct work_struct *work) /* store SOC and status to check changes */ last_soc = chip->soc; last_status = chip->status; - max17040_check_changes(chip->client); + max17040_check_changes(chip); /* check changes and send uevent */ if (last_soc != chip->soc || last_status != chip->status) @@ -242,17 +170,17 @@ static void max17040_work(struct work_struct *work) static irqreturn_t max17040_thread_handler(int id, void *dev) { struct max17040_chip *chip = dev; - struct i2c_client *client = chip->client; - dev_warn(&client->dev, "IRQ: Alert battery low level"); + dev_warn(&chip->client->dev, "IRQ: Alert battery low level"); + /* read registers */ - max17040_check_changes(chip->client); + max17040_check_changes(chip); /* send uevent */ power_supply_changed(chip->battery); /* reset alert bit */ - max17040_set_low_soc_alert(client, chip->low_soc_alert); + max17040_set_low_soc_alert(chip, chip->low_soc_alert); return IRQ_HANDLED; } @@ -296,7 +224,7 @@ static int max17040_set_property(struct power_supply *psy, ret = -EINVAL; break; } - ret = max17040_set_low_soc_alert(chip->client, val->intval); + ret = max17040_set_low_soc_alert(chip, val->intval); chip->low_soc_alert = val->intval; break; default: @@ -306,6 +234,41 @@ static int max17040_set_property(struct power_supply *psy, return ret; } +static int max17040_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct max17040_chip *chip = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = max17040_get_status(chip); + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = max17040_get_online(chip); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = max17040_get_vcell(chip); + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = max17040_get_soc(chip); + break; + case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: + val->intval = chip->low_soc_alert; + break; + default: + return -EINVAL; + } + return 0; +} + +static const struct regmap_config max17040_regmap = { + .reg_bits = 8, + .reg_stride = 2, + .val_bits = 16, + .val_format_endian = REGMAP_ENDIAN_BIG, +}; + static enum power_supply_property max17040_battery_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_ONLINE, @@ -340,13 +303,11 @@ static int max17040_probe(struct i2c_client *client, return -ENOMEM; chip->client = client; + chip->regmap = devm_regmap_init_i2c(client, &max17040_regmap); chip->pdata = client->dev.platform_data; ret = max17040_get_of_data(chip); - if (ret) { - dev_err(&client->dev, - "failed: low SOC alert OF data out of bounds\n"); + if (ret) return ret; - } i2c_set_clientdata(client, chip); psy_cfg.drv_data = chip; @@ -358,13 +319,17 @@ static int max17040_probe(struct i2c_client *client, return PTR_ERR(chip->battery); } - max17040_reset(client); - max17040_get_version(client); + ret = max17040_get_version(chip); + if (ret < 0) + return ret; + dev_dbg(&chip->client->dev, "MAX17040 Fuel-Gauge Ver 0x%x\n", ret); + + max17040_reset(chip); /* check interrupt */ if (client->irq && of_device_is_compatible(client->dev.of_node, "maxim,max77836-battery")) { - ret = max17040_set_low_soc_alert(client, chip->low_soc_alert); + ret = max17040_set_low_soc_alert(chip, chip->low_soc_alert); if (ret) { dev_err(&client->dev, "Failed to set low SOC alert: err %d\n", ret); -- cgit From 4f7f8e87c49e029a52318dd76d561860119d0372 Mon Sep 17 00:00:00 2001 From: Iskren Chernev <iskren.chernev@gmail.com> Date: Tue, 22 Sep 2020 14:42:34 +0300 Subject: power: supply: max17040: Support compatible devices The max17040 fuel gauge is part of a family of 8 chips that have very similar mode of operations and registers. This change adds: - compatible strings for all supported devices and handling for the minor differences between them; - handling for devices reporting double capacity via maxim,double-soc; The datasheets of the supported devices are linked [0] [1] [2] [3]. [0] https://datasheets.maximintegrated.com/en/ds/MAX17040-MAX17041.pdf [1] https://datasheets.maximintegrated.com/en/ds/MAX17043-MAX17044.pdf [2] https://datasheets.maximintegrated.com/en/ds/MAX17048-MAX17049.pdf [3] https://datasheets.maximintegrated.com/en/ds/MAX17058-MAX17059.pdf Signed-off-by: Iskren Chernev <iskren.chernev@gmail.com> Tested-by: Jonathan Bakker <xc-racer2@live.ca> Reported-by: kernel test robot <lkp@intel.com> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/Kconfig | 10 ++- drivers/power/supply/max17040_battery.c | 155 ++++++++++++++++++++++++++++---- 2 files changed, 143 insertions(+), 22 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 47a4e1d363fc..de9e0fa9a861 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -369,9 +369,13 @@ config BATTERY_MAX17040 depends on I2C select REGMAP_I2C help - MAX17040 is fuel-gauge systems for lithium-ion (Li+) batteries - in handheld and portable equipment. The MAX17040 is configured - to operate with a single lithium cell + Maxim models with ModelGauge are fuel-gauge systems for lithium-ion + (Li+) batteries in handheld and portable equipment, including + max17040, max17041, max17043, max17044, max17048, max17049, max17058, + max17059. It is also included in some batteries like max77836. + + Driver supports reporting SOC (State of Charge, i.e capacity), + voltage and configurable low-SOC wakeup interrupt. config BATTERY_MAX17042 tristate "Maxim MAX17042/17047/17050/8997/8966 Fuel Gauge" diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c index fae421796076..4bcec86d9209 100644 --- a/drivers/power/supply/max17040_battery.c +++ b/drivers/power/supply/max17040_battery.c @@ -15,6 +15,7 @@ #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/power_supply.h> +#include <linux/of_device.h> #include <linux/max17040_battery.h> #include <linux/regmap.h> #include <linux/slab.h> @@ -33,12 +34,92 @@ #define MAX17040_ATHD_MASK 0x3f #define MAX17040_ATHD_DEFAULT_POWER_UP 4 +enum chip_id { + ID_MAX17040, + ID_MAX17041, + ID_MAX17043, + ID_MAX17044, + ID_MAX17048, + ID_MAX17049, + ID_MAX17058, + ID_MAX17059, +}; + +/* values that differ by chip_id */ +struct chip_data { + u16 reset_val; + u16 vcell_shift; + u16 vcell_mul; + u16 vcell_div; + u8 has_low_soc_alert; +}; + +static struct chip_data max17040_family[] = { + [ID_MAX17040] = { + .reset_val = 0x0054, + .vcell_shift = 4, + .vcell_mul = 1250, + .vcell_div = 1, + .has_low_soc_alert = 0, + }, + [ID_MAX17041] = { + .reset_val = 0x0054, + .vcell_shift = 4, + .vcell_mul = 2500, + .vcell_div = 1, + .has_low_soc_alert = 0, + }, + [ID_MAX17043] = { + .reset_val = 0x0054, + .vcell_shift = 4, + .vcell_mul = 1250, + .vcell_div = 1, + .has_low_soc_alert = 1, + }, + [ID_MAX17044] = { + .reset_val = 0x0054, + .vcell_shift = 4, + .vcell_mul = 2500, + .vcell_div = 1, + .has_low_soc_alert = 1, + }, + [ID_MAX17048] = { + .reset_val = 0x5400, + .vcell_shift = 0, + .vcell_mul = 625, + .vcell_div = 8, + .has_low_soc_alert = 1, + }, + [ID_MAX17049] = { + .reset_val = 0x5400, + .vcell_shift = 0, + .vcell_mul = 625, + .vcell_div = 4, + .has_low_soc_alert = 1, + }, + [ID_MAX17058] = { + .reset_val = 0x5400, + .vcell_shift = 0, + .vcell_mul = 625, + .vcell_div = 8, + .has_low_soc_alert = 1, + }, + [ID_MAX17059] = { + .reset_val = 0x5400, + .vcell_shift = 0, + .vcell_mul = 625, + .vcell_div = 4, + .has_low_soc_alert = 1, + }, +}; + struct max17040_chip { struct i2c_client *client; struct regmap *regmap; struct delayed_work work; struct power_supply *battery; struct max17040_platform_data *pdata; + struct chip_data data; /* battery capacity */ int soc; @@ -46,27 +127,37 @@ struct max17040_chip { int status; /* Low alert threshold from 32% to 1% of the State of Charge */ u32 low_soc_alert; + /* some devices return twice the capacity */ + bool quirk_double_soc; }; static int max17040_reset(struct max17040_chip *chip) { - return regmap_write(chip->regmap, MAX17040_CMD, 0x0054); + return regmap_write(chip->regmap, MAX17040_CMD, chip->data.reset_val); } static int max17040_set_low_soc_alert(struct max17040_chip *chip, u32 level) { - level = 32 - level; + level = 32 - level * (chip->quirk_double_soc ? 2 : 1); return regmap_update_bits(chip->regmap, MAX17040_CONFIG, MAX17040_ATHD_MASK, level); } +static int max17040_raw_vcell_to_uvolts(struct max17040_chip *chip, u16 vcell) +{ + struct chip_data *d = &chip->data; + + return (vcell >> d->vcell_shift) * d->vcell_mul / d->vcell_div; +} + + static int max17040_get_vcell(struct max17040_chip *chip) { u32 vcell; regmap_read(chip->regmap, MAX17040_VCELL, &vcell); - return (vcell >> 4) * 1250; + return max17040_raw_vcell_to_uvolts(chip, vcell); } static int max17040_get_soc(struct max17040_chip *chip) @@ -75,7 +166,7 @@ static int max17040_get_soc(struct max17040_chip *chip) regmap_read(chip->regmap, MAX17040_SOC, &soc); - return soc >> 8; + return soc >> (chip->quirk_double_soc ? 9 : 8); } static int max17040_get_version(struct max17040_chip *chip) @@ -116,12 +207,16 @@ static int max17040_get_of_data(struct max17040_chip *chip) { struct device *dev = &chip->client->dev; + chip->quirk_double_soc = device_property_read_bool(dev, + "maxim,double-soc"); + chip->low_soc_alert = MAX17040_ATHD_DEFAULT_POWER_UP; device_property_read_u32(dev, "maxim,alert-low-soc-level", &chip->low_soc_alert); - if (chip->low_soc_alert <= 0 || chip->low_soc_alert >= 33) { + if (chip->low_soc_alert <= 0 || + chip->low_soc_alert > (chip->quirk_double_soc ? 16 : 32)) { dev_err(dev, "maxim,alert-low-soc-level out of bounds\n"); return -EINVAL; } @@ -219,8 +314,9 @@ static int max17040_set_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: - /* alert threshold can be programmed from 1% up to 32% */ - if ((val->intval < 1) || (val->intval > 32)) { + /* alert threshold can be programmed from 1% up to 16/32% */ + if ((val->intval < 1) || + (val->intval > (chip->quirk_double_soc ? 16 : 32))) { ret = -EINVAL; break; } @@ -293,6 +389,7 @@ static int max17040_probe(struct i2c_client *client, struct i2c_adapter *adapter = client->adapter; struct power_supply_config psy_cfg = {}; struct max17040_chip *chip; + enum chip_id chip_id; int ret; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) @@ -305,9 +402,15 @@ static int max17040_probe(struct i2c_client *client, chip->client = client; chip->regmap = devm_regmap_init_i2c(client, &max17040_regmap); chip->pdata = client->dev.platform_data; - ret = max17040_get_of_data(chip); - if (ret) - return ret; + chip_id = (enum chip_id) id->driver_data; + if (client->dev.of_node) { + ret = max17040_get_of_data(chip); + if (ret) + return ret; + chip_id = (enum chip_id) (uintptr_t) + of_device_get_match_data(&client->dev); + } + chip->data = max17040_family[chip_id]; i2c_set_clientdata(client, chip); psy_cfg.drv_data = chip; @@ -324,11 +427,11 @@ static int max17040_probe(struct i2c_client *client, return ret; dev_dbg(&chip->client->dev, "MAX17040 Fuel-Gauge Ver 0x%x\n", ret); - max17040_reset(chip); + if (chip_id == ID_MAX17040 || chip_id == ID_MAX17041) + max17040_reset(chip); /* check interrupt */ - if (client->irq && of_device_is_compatible(client->dev.of_node, - "maxim,max77836-battery")) { + if (client->irq && chip->data.has_low_soc_alert) { ret = max17040_set_low_soc_alert(chip, chip->low_soc_alert); if (ret) { dev_err(&client->dev, @@ -391,16 +494,30 @@ static SIMPLE_DEV_PM_OPS(max17040_pm_ops, max17040_suspend, max17040_resume); #endif /* CONFIG_PM_SLEEP */ static const struct i2c_device_id max17040_id[] = { - { "max17040" }, - { "max77836-battery" }, - { } + { "max17040", ID_MAX17040 }, + { "max17041", ID_MAX17041 }, + { "max17043", ID_MAX17043 }, + { "max77836-battery", ID_MAX17043 }, + { "max17044", ID_MAX17044 }, + { "max17048", ID_MAX17048 }, + { "max17049", ID_MAX17049 }, + { "max17058", ID_MAX17058 }, + { "max17059", ID_MAX17059 }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, max17040_id); static const struct of_device_id max17040_of_match[] = { - { .compatible = "maxim,max17040" }, - { .compatible = "maxim,max77836-battery" }, - { }, + { .compatible = "maxim,max17040", .data = (void *) ID_MAX17040 }, + { .compatible = "maxim,max17041", .data = (void *) ID_MAX17041 }, + { .compatible = "maxim,max17043", .data = (void *) ID_MAX17043 }, + { .compatible = "maxim,max77836-battery", .data = (void *) ID_MAX17043 }, + { .compatible = "maxim,max17044", .data = (void *) ID_MAX17044 }, + { .compatible = "maxim,max17048", .data = (void *) ID_MAX17048 }, + { .compatible = "maxim,max17049", .data = (void *) ID_MAX17049 }, + { .compatible = "maxim,max17058", .data = (void *) ID_MAX17058 }, + { .compatible = "maxim,max17059", .data = (void *) ID_MAX17059 }, + { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, max17040_of_match); -- cgit From 1779c6e3496a53d9cbbbcd248b234b2b6a07435c Mon Sep 17 00:00:00 2001 From: Iskren Chernev <iskren.chernev@gmail.com> Date: Tue, 22 Sep 2020 14:42:36 +0300 Subject: power: supply: max17040: Support setting rcomp The Maxim ModelGauge family supports fine-tuning by setting a compensation value (named rcomp in the docs). The value is affected by battery chemistry and ambient temperature. Add support for reading maxim,rcomp from DT and configuring the device with the supplied value. Temperature adjustment is not implemented at the moment, because there is no provision for receiving the ambient temperature at the moment. Signed-off-by: Iskren Chernev <iskren.chernev@gmail.com> Tested-by: Jonathan Bakker <xc-racer2@live.ca> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/max17040_battery.c | 40 +++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c index 4bcec86d9209..ae39ca5c6753 100644 --- a/drivers/power/supply/max17040_battery.c +++ b/drivers/power/supply/max17040_battery.c @@ -30,9 +30,11 @@ #define MAX17040_DELAY 1000 #define MAX17040_BATTERY_FULL 95 +#define MAX17040_RCOMP_DEFAULT 0x9700 #define MAX17040_ATHD_MASK 0x3f #define MAX17040_ATHD_DEFAULT_POWER_UP 4 +#define MAX17040_CFG_RCOMP_MASK 0xff00 enum chip_id { ID_MAX17040, @@ -52,6 +54,7 @@ struct chip_data { u16 vcell_mul; u16 vcell_div; u8 has_low_soc_alert; + u8 rcomp_bytes; }; static struct chip_data max17040_family[] = { @@ -61,6 +64,7 @@ static struct chip_data max17040_family[] = { .vcell_mul = 1250, .vcell_div = 1, .has_low_soc_alert = 0, + .rcomp_bytes = 2, }, [ID_MAX17041] = { .reset_val = 0x0054, @@ -68,6 +72,7 @@ static struct chip_data max17040_family[] = { .vcell_mul = 2500, .vcell_div = 1, .has_low_soc_alert = 0, + .rcomp_bytes = 2, }, [ID_MAX17043] = { .reset_val = 0x0054, @@ -75,6 +80,7 @@ static struct chip_data max17040_family[] = { .vcell_mul = 1250, .vcell_div = 1, .has_low_soc_alert = 1, + .rcomp_bytes = 1, }, [ID_MAX17044] = { .reset_val = 0x0054, @@ -82,6 +88,7 @@ static struct chip_data max17040_family[] = { .vcell_mul = 2500, .vcell_div = 1, .has_low_soc_alert = 1, + .rcomp_bytes = 1, }, [ID_MAX17048] = { .reset_val = 0x5400, @@ -89,6 +96,7 @@ static struct chip_data max17040_family[] = { .vcell_mul = 625, .vcell_div = 8, .has_low_soc_alert = 1, + .rcomp_bytes = 1, }, [ID_MAX17049] = { .reset_val = 0x5400, @@ -96,6 +104,7 @@ static struct chip_data max17040_family[] = { .vcell_mul = 625, .vcell_div = 4, .has_low_soc_alert = 1, + .rcomp_bytes = 1, }, [ID_MAX17058] = { .reset_val = 0x5400, @@ -103,6 +112,7 @@ static struct chip_data max17040_family[] = { .vcell_mul = 625, .vcell_div = 8, .has_low_soc_alert = 1, + .rcomp_bytes = 1, }, [ID_MAX17059] = { .reset_val = 0x5400, @@ -110,6 +120,7 @@ static struct chip_data max17040_family[] = { .vcell_mul = 625, .vcell_div = 4, .has_low_soc_alert = 1, + .rcomp_bytes = 1, }, }; @@ -129,6 +140,8 @@ struct max17040_chip { u32 low_soc_alert; /* some devices return twice the capacity */ bool quirk_double_soc; + /* higher 8 bits for 17043+, 16 bits for 17040,41 */ + u16 rcomp; }; static int max17040_reset(struct max17040_chip *chip) @@ -143,6 +156,14 @@ static int max17040_set_low_soc_alert(struct max17040_chip *chip, u32 level) MAX17040_ATHD_MASK, level); } +static int max17040_set_rcomp(struct max17040_chip *chip, u16 rcomp) +{ + u16 mask = chip->data.rcomp_bytes == 2 ? + 0xffff : MAX17040_CFG_RCOMP_MASK; + + return regmap_update_bits(chip->regmap, MAX17040_CONFIG, mask, rcomp); +} + static int max17040_raw_vcell_to_uvolts(struct max17040_chip *chip, u16 vcell) { struct chip_data *d = &chip->data; @@ -206,6 +227,10 @@ static int max17040_get_status(struct max17040_chip *chip) static int max17040_get_of_data(struct max17040_chip *chip) { struct device *dev = &chip->client->dev; + struct chip_data *data = &max17040_family[ + (enum chip_id) of_device_get_match_data(dev)]; + int rcomp_len; + u8 rcomp[2]; chip->quirk_double_soc = device_property_read_bool(dev, "maxim,double-soc"); @@ -221,6 +246,19 @@ static int max17040_get_of_data(struct max17040_chip *chip) return -EINVAL; } + rcomp_len = device_property_count_u8(dev, "maxim,rcomp"); + chip->rcomp = MAX17040_RCOMP_DEFAULT; + if (rcomp_len == data->rcomp_bytes) { + device_property_read_u8_array(dev, "maxim,rcomp", + rcomp, rcomp_len); + chip->rcomp = rcomp_len == 2 ? + rcomp[0] << 8 | rcomp[1] : + rcomp[0] << 8; + } else if (rcomp_len > 0) { + dev_err(dev, "maxim,rcomp has incorrect length\n"); + return -EINVAL; + } + return 0; } @@ -430,6 +468,8 @@ static int max17040_probe(struct i2c_client *client, if (chip_id == ID_MAX17040 || chip_id == ID_MAX17041) max17040_reset(chip); + max17040_set_rcomp(chip, chip->rcomp); + /* check interrupt */ if (client->irq && chip->data.has_low_soc_alert) { ret = max17040_set_low_soc_alert(chip, chip->low_soc_alert); -- cgit From ee2d433cb47338cd40af800cdab5133f62edca10 Mon Sep 17 00:00:00 2001 From: Iskren Chernev <iskren.chernev@gmail.com> Date: Tue, 22 Sep 2020 14:42:37 +0300 Subject: power: supply: max17040: Support soc alert max17048 and max17049 support SOC alerts (interrupts when battery capacity changes by +/- 1%). At the moment the driver polls for changes every second. Using the alerts removes the need for polling. Signed-off-by: Iskren Chernev <iskren.chernev@gmail.com> Tested-by: Jonathan Bakker <xc-racer2@live.ca> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/max17040_battery.c | 82 +++++++++++++++++++++++++++++---- 1 file changed, 73 insertions(+), 9 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c index ae39ca5c6753..1d7510a59295 100644 --- a/drivers/power/supply/max17040_battery.c +++ b/drivers/power/supply/max17040_battery.c @@ -25,6 +25,7 @@ #define MAX17040_MODE 0x06 #define MAX17040_VER 0x08 #define MAX17040_CONFIG 0x0C +#define MAX17040_STATUS 0x1A #define MAX17040_CMD 0xFE @@ -33,7 +34,10 @@ #define MAX17040_RCOMP_DEFAULT 0x9700 #define MAX17040_ATHD_MASK 0x3f +#define MAX17040_ALSC_MASK 0x40 #define MAX17040_ATHD_DEFAULT_POWER_UP 4 +#define MAX17040_STATUS_HD_MASK 0x1000 +#define MAX17040_STATUS_SC_MASK 0x2000 #define MAX17040_CFG_RCOMP_MASK 0xff00 enum chip_id { @@ -55,6 +59,7 @@ struct chip_data { u16 vcell_div; u8 has_low_soc_alert; u8 rcomp_bytes; + u8 has_soc_alert; }; static struct chip_data max17040_family[] = { @@ -65,6 +70,7 @@ static struct chip_data max17040_family[] = { .vcell_div = 1, .has_low_soc_alert = 0, .rcomp_bytes = 2, + .has_soc_alert = 0, }, [ID_MAX17041] = { .reset_val = 0x0054, @@ -73,6 +79,7 @@ static struct chip_data max17040_family[] = { .vcell_div = 1, .has_low_soc_alert = 0, .rcomp_bytes = 2, + .has_soc_alert = 0, }, [ID_MAX17043] = { .reset_val = 0x0054, @@ -81,6 +88,7 @@ static struct chip_data max17040_family[] = { .vcell_div = 1, .has_low_soc_alert = 1, .rcomp_bytes = 1, + .has_soc_alert = 0, }, [ID_MAX17044] = { .reset_val = 0x0054, @@ -89,6 +97,7 @@ static struct chip_data max17040_family[] = { .vcell_div = 1, .has_low_soc_alert = 1, .rcomp_bytes = 1, + .has_soc_alert = 0, }, [ID_MAX17048] = { .reset_val = 0x5400, @@ -97,6 +106,7 @@ static struct chip_data max17040_family[] = { .vcell_div = 8, .has_low_soc_alert = 1, .rcomp_bytes = 1, + .has_soc_alert = 1, }, [ID_MAX17049] = { .reset_val = 0x5400, @@ -105,6 +115,7 @@ static struct chip_data max17040_family[] = { .vcell_div = 4, .has_low_soc_alert = 1, .rcomp_bytes = 1, + .has_soc_alert = 1, }, [ID_MAX17058] = { .reset_val = 0x5400, @@ -113,6 +124,7 @@ static struct chip_data max17040_family[] = { .vcell_div = 8, .has_low_soc_alert = 1, .rcomp_bytes = 1, + .has_soc_alert = 0, }, [ID_MAX17059] = { .reset_val = 0x5400, @@ -121,6 +133,7 @@ static struct chip_data max17040_family[] = { .vcell_div = 4, .has_low_soc_alert = 1, .rcomp_bytes = 1, + .has_soc_alert = 0, }, }; @@ -156,6 +169,12 @@ static int max17040_set_low_soc_alert(struct max17040_chip *chip, u32 level) MAX17040_ATHD_MASK, level); } +static int max17040_set_soc_alert(struct max17040_chip *chip, bool enable) +{ + return regmap_update_bits(chip->regmap, MAX17040_CONFIG, + MAX17040_ALSC_MASK, enable ? MAX17040_ALSC_MASK : 0); +} + static int max17040_set_rcomp(struct max17040_chip *chip, u16 rcomp) { u16 mask = chip->data.rcomp_bytes == 2 ? @@ -300,11 +319,33 @@ static void max17040_work(struct work_struct *work) max17040_queue_work(chip); } +/* Returns true if alert cause was SOC change, not low SOC */ +static bool max17040_handle_soc_alert(struct max17040_chip *chip) +{ + bool ret = true; + u32 data; + + regmap_read(chip->regmap, MAX17040_STATUS, &data); + + if (data & MAX17040_STATUS_HD_MASK) { + // this alert was caused by low soc + ret = false; + } + if (data & MAX17040_STATUS_SC_MASK) { + // soc change bit -- deassert to mark as handled + regmap_write(chip->regmap, MAX17040_STATUS, + data & ~MAX17040_STATUS_SC_MASK); + } + + return ret; +} + static irqreturn_t max17040_thread_handler(int id, void *dev) { struct max17040_chip *chip = dev; - dev_warn(&chip->client->dev, "IRQ: Alert battery low level"); + if (!(chip->data.has_soc_alert && max17040_handle_soc_alert(chip))) + dev_warn(&chip->client->dev, "IRQ: Alert battery low level\n"); /* read registers */ max17040_check_changes(chip); @@ -428,6 +469,7 @@ static int max17040_probe(struct i2c_client *client, struct power_supply_config psy_cfg = {}; struct max17040_chip *chip; enum chip_id chip_id; + bool enable_irq = false; int ret; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) @@ -479,6 +521,27 @@ static int max17040_probe(struct i2c_client *client, return ret; } + enable_irq = true; + } + + if (client->irq && chip->data.has_soc_alert) { + ret = max17040_set_soc_alert(chip, 1); + if (ret) { + dev_err(&client->dev, + "Failed to set SOC alert: err %d\n", ret); + return ret; + } + enable_irq = true; + } else { + /* soc alerts negate the need for polling */ + INIT_DEFERRABLE_WORK(&chip->work, max17040_work); + ret = devm_add_action(&client->dev, max17040_stop_work, chip); + if (ret) + return ret; + max17040_queue_work(chip); + } + + if (enable_irq) { ret = max17040_enable_alert_irq(chip); if (ret) { client->irq = 0; @@ -487,12 +550,6 @@ static int max17040_probe(struct i2c_client *client, } } - INIT_DEFERRABLE_WORK(&chip->work, max17040_work); - ret = devm_add_action(&client->dev, max17040_stop_work, chip); - if (ret) - return ret; - max17040_queue_work(chip); - return 0; } @@ -503,7 +560,11 @@ static int max17040_suspend(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct max17040_chip *chip = i2c_get_clientdata(client); - cancel_delayed_work(&chip->work); + if (client->irq && chip->data.has_soc_alert) + // disable soc alert to prevent wakeup + max17040_set_soc_alert(chip, 0); + else + cancel_delayed_work(&chip->work); if (client->irq && device_may_wakeup(dev)) enable_irq_wake(client->irq); @@ -519,7 +580,10 @@ static int max17040_resume(struct device *dev) if (client->irq && device_may_wakeup(dev)) disable_irq_wake(client->irq); - max17040_queue_work(chip); + if (client->irq && chip->data.has_soc_alert) + max17040_set_soc_alert(chip, 1); + else + max17040_queue_work(chip); return 0; } -- cgit From 7da9f17fc2b9a3493c6fd55e96521485035bc670 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski <krzk@kernel.org> Date: Fri, 11 Sep 2020 18:27:23 +0200 Subject: power: supply: bq24257: skip 'struct acpi_device_id' when !CONFIG_ACPI Since ACPI_PTR() is used to NULLify the value when !CONFIG_ACPI, the struct acpi_device_id becomes unused: drivers/power/supply/bq24257_charger.c:1155:36: warning: 'bq24257_acpi_match' defined but not used [-Wunused-const-variable=] Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/bq24257_charger.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/supply/bq24257_charger.c b/drivers/power/supply/bq24257_charger.c index 8e60cb0f3c3f..96cb3290bcaa 100644 --- a/drivers/power/supply/bq24257_charger.c +++ b/drivers/power/supply/bq24257_charger.c @@ -1152,6 +1152,7 @@ static const struct of_device_id bq24257_of_match[] = { }; MODULE_DEVICE_TABLE(of, bq24257_of_match); +#ifdef CONFIG_ACPI static const struct acpi_device_id bq24257_acpi_match[] = { { "BQ242500", BQ24250 }, { "BQ242510", BQ24251 }, @@ -1159,6 +1160,7 @@ static const struct acpi_device_id bq24257_acpi_match[] = { {}, }; MODULE_DEVICE_TABLE(acpi, bq24257_acpi_match); +#endif static struct i2c_driver bq24257_driver = { .driver = { -- cgit From 66ac8df5e0fd6cad6821e0cbd4bc172afdd784f7 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski <krzk@kernel.org> Date: Fri, 11 Sep 2020 18:27:24 +0200 Subject: power: supply: bq2515x: fix kerneldoc Fix kerneldoc W=1 warning: drivers/power/supply/bq2515x_charger.c:189: warning: Function parameter or member 'init_data' not described in 'bq2515x_device' Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/bq2515x_charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/bq2515x_charger.c b/drivers/power/supply/bq2515x_charger.c index 9dcb61ea4cf2..374b112f712a 100644 --- a/drivers/power/supply/bq2515x_charger.c +++ b/drivers/power/supply/bq2515x_charger.c @@ -168,7 +168,7 @@ enum bq2515x_id { * @device_id: value of device_id * @mains_online: boolean value indicating power supply online * - * @bq2515x_init_data init_data: charger initialization data structure + * @init_data: charger initialization data structure */ struct bq2515x_device { struct power_supply *mains; -- cgit From 02067dc96c6098b5489636ccf54db1c81d9bada9 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski <krzk@kernel.org> Date: Fri, 11 Sep 2020 18:27:25 +0200 Subject: power: supply: bq25890: skip 'struct acpi_device_id' when !CONFIG_ACPI Since ACPI_PTR() is used to NULLify the value when !CONFIG_ACPI, the struct acpi_device_id becomes unused: drivers/power/supply/bq25890_charger.c:1076:36: warning: 'bq25890_acpi_match' defined but not used [-Wunused-const-variable=] Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/bq25890_charger.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index ab8398f935c5..34c21c51bac1 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -1084,11 +1084,13 @@ static const struct of_device_id bq25890_of_match[] = { }; MODULE_DEVICE_TABLE(of, bq25890_of_match); +#ifdef CONFIG_ACPI static const struct acpi_device_id bq25890_acpi_match[] = { {"BQ258900", 0}, {}, }; MODULE_DEVICE_TABLE(acpi, bq25890_acpi_match); +#endif static struct i2c_driver bq25890_driver = { .driver = { -- cgit From 439cd7eddc77638eda29874ec84c8fafb69e1fde Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski <krzk@kernel.org> Date: Fri, 11 Sep 2020 18:27:26 +0200 Subject: power: supply: goldfish: skip 'struct acpi_device_id' when !CONFIG_ACPI Since ACPI_PTR() is used to NULLify the value when !CONFIG_ACPI, the struct acpi_device_id becomes unused: drivers/power/supply/goldfish_battery.c:269:36: warning: 'goldfish_battery_acpi_match' defined but not used [-Wunused-const-variable=] Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/goldfish_battery.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/supply/goldfish_battery.c b/drivers/power/supply/goldfish_battery.c index c2644a9fe80f..bf1754355c9f 100644 --- a/drivers/power/supply/goldfish_battery.c +++ b/drivers/power/supply/goldfish_battery.c @@ -266,11 +266,13 @@ static const struct of_device_id goldfish_battery_of_match[] = { }; MODULE_DEVICE_TABLE(of, goldfish_battery_of_match); +#ifdef CONFIG_ACPI static const struct acpi_device_id goldfish_battery_acpi_match[] = { { "GFSH0001", 0 }, { }, }; MODULE_DEVICE_TABLE(acpi, goldfish_battery_acpi_match); +#endif static struct platform_driver goldfish_battery_device = { .probe = goldfish_battery_probe, -- cgit From 5bcb3dae01da624e353ba3acdcd045a2fe540675 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski <krzk@kernel.org> Date: Fri, 11 Sep 2020 18:27:27 +0200 Subject: power: supply: rt9455: skip 'struct acpi_device_id' when !CONFIG_ACPI Since ACPI_PTR() is used to NULLify the value when !CONFIG_ACPI, the struct acpi_device_id becomes unused: drivers/power/supply/rt9455_charger.c:1734:36: warning: 'rt9455_i2c_acpi_match' defined but not used [-Wunused-const-variable=] Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/rt9455_charger.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/supply/rt9455_charger.c b/drivers/power/supply/rt9455_charger.c index 29161ae90245..594bb3b8a4d1 100644 --- a/drivers/power/supply/rt9455_charger.c +++ b/drivers/power/supply/rt9455_charger.c @@ -1731,11 +1731,13 @@ static const struct of_device_id rt9455_of_match[] = { }; MODULE_DEVICE_TABLE(of, rt9455_of_match); +#ifdef CONFIG_ACPI static const struct acpi_device_id rt9455_i2c_acpi_match[] = { { "RT945500", 0 }, { } }; MODULE_DEVICE_TABLE(acpi, rt9455_i2c_acpi_match); +#endif static struct i2c_driver rt9455_driver = { .probe = rt9455_probe, -- cgit From c22b90db54513ec240e892fd82c4b98f5e5a7d2e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski <krzk@kernel.org> Date: Fri, 11 Sep 2020 18:27:28 +0200 Subject: power: supply: charger-manager: drop unused charger assignment The 'charger' variable in error path is assigned but never used: drivers/power/supply/charger-manager.c: In function 'charger_manager_probe': drivers/power/supply/charger-manager.c:1626:29: warning: variable 'charger' set but not used [-Wunused-but-set-variable] Fixes: c1f73028f75d ("power: supply: charger-manager: Update extcon functions") Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/charger-manager.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index 07992821e252..b2ca79173f95 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -1622,13 +1622,8 @@ static int charger_manager_probe(struct platform_device *pdev) return 0; err_reg_extcon: - for (i = 0; i < desc->num_charger_regulators; i++) { - struct charger_regulator *charger; - - charger = &desc->charger_regulators[i]; - + for (i = 0; i < desc->num_charger_regulators; i++) regulator_put(desc->charger_regulators[i].consumer); - } power_supply_unregister(cm->charger_psy); -- cgit From f87d092fe4d45ec36e88778c5836b1c3db80fc8c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski <krzk@kernel.org> Date: Fri, 11 Sep 2020 18:27:29 +0200 Subject: power: supply: pm2301: drop duplicated i2c_device_id The driver defines two of 'struct i2c_device_id' but uses only one: drivers/power/supply/pm2301_charger.c:107:35: warning: 'pm2xxx_ident' defined but not used [-Wunused-const-variable=] Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/pm2301_charger.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/pm2301_charger.c b/drivers/power/supply/pm2301_charger.c index 787867805944..2df6a2459d1f 100644 --- a/drivers/power/supply/pm2301_charger.c +++ b/drivers/power/supply/pm2301_charger.c @@ -104,11 +104,6 @@ static int pm2xxx_charger_current_map[] = { 3000, }; -static const struct i2c_device_id pm2xxx_ident[] = { - { "pm2301", 0 }, - { } -}; - static void set_lpn_pin(struct pm2xxx_charger *pm2) { if (!pm2->ac.charger_connected && gpio_is_valid(pm2->lpn_pin)) { -- cgit From c07fa6c1631333f02750cf59f22b615d768b4d8f Mon Sep 17 00:00:00 2001 From: Xiongfeng Wang <wangxiongfeng2@huawei.com> Date: Fri, 4 Sep 2020 14:09:58 +0800 Subject: power: supply: test_power: add missing newlines when printing parameters by sysfs When I cat some module parameters by sysfs, it displays as follows. It's better to add a newline for easy reading. root@syzkaller:~# cd /sys/module/test_power/parameters/ root@syzkaller:/sys/module/test_power/parameters# cat ac_online onroot@syzkaller:/sys/module/test_power/parameters# cat battery_present trueroot@syzkaller:/sys/module/test_power/parameters# cat battery_health goodroot@syzkaller:/sys/module/test_power/parameters# cat battery_status dischargingroot@syzkaller:/sys/module/test_power/parameters# cat battery_technology LIONroot@syzkaller:/sys/module/test_power/parameters# cat usb_online onroot@syzkaller:/sys/module/test_power/parameters# Signed-off-by: Xiongfeng Wang <wangxiongfeng2@huawei.com> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/test_power.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/supply/test_power.c b/drivers/power/supply/test_power.c index 04acd76bbaa1..4895ee5e63a9 100644 --- a/drivers/power/supply/test_power.c +++ b/drivers/power/supply/test_power.c @@ -353,6 +353,7 @@ static int param_set_ac_online(const char *key, const struct kernel_param *kp) static int param_get_ac_online(char *buffer, const struct kernel_param *kp) { strcpy(buffer, map_get_key(map_ac_online, ac_online, "unknown")); + strcat(buffer, "\n"); return strlen(buffer); } @@ -366,6 +367,7 @@ static int param_set_usb_online(const char *key, const struct kernel_param *kp) static int param_get_usb_online(char *buffer, const struct kernel_param *kp) { strcpy(buffer, map_get_key(map_ac_online, usb_online, "unknown")); + strcat(buffer, "\n"); return strlen(buffer); } @@ -380,6 +382,7 @@ static int param_set_battery_status(const char *key, static int param_get_battery_status(char *buffer, const struct kernel_param *kp) { strcpy(buffer, map_get_key(map_status, battery_status, "unknown")); + strcat(buffer, "\n"); return strlen(buffer); } @@ -394,6 +397,7 @@ static int param_set_battery_health(const char *key, static int param_get_battery_health(char *buffer, const struct kernel_param *kp) { strcpy(buffer, map_get_key(map_health, battery_health, "unknown")); + strcat(buffer, "\n"); return strlen(buffer); } @@ -409,6 +413,7 @@ static int param_get_battery_present(char *buffer, const struct kernel_param *kp) { strcpy(buffer, map_get_key(map_present, battery_present, "unknown")); + strcat(buffer, "\n"); return strlen(buffer); } @@ -426,6 +431,7 @@ static int param_get_battery_technology(char *buffer, { strcpy(buffer, map_get_key(map_technology, battery_technology, "unknown")); + strcat(buffer, "\n"); return strlen(buffer); } -- cgit From 304bff2fb8e9ad0047c11ae0ef2ca6c3d74cd52b Mon Sep 17 00:00:00 2001 From: Colin Ian King <colin.king@canonical.com> Date: Wed, 2 Sep 2020 11:16:56 +0100 Subject: power: supply: fix spelling mistake "unprecise" -> "imprecise" There is a spelling mistake in a dev_info message. Fix it. Signed-off-by: Colin Ian King <colin.king@canonical.com> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/rn5t618_power.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/rn5t618_power.c b/drivers/power/supply/rn5t618_power.c index 424d2817bee5..dee520f0fdf5 100644 --- a/drivers/power/supply/rn5t618_power.c +++ b/drivers/power/supply/rn5t618_power.c @@ -487,7 +487,7 @@ static int rn5t618_power_probe(struct platform_device *pdev) * gauge will get decalibrated. */ dev_info(&pdev->dev, "Fuel gauge not enabled, enabling now\n"); - dev_info(&pdev->dev, "Expect unprecise results\n"); + dev_info(&pdev->dev, "Expect imprecise results\n"); regmap_update_bits(info->rn5t618->regmap, RN5T618_CONTROL, FG_ENABLE, FG_ENABLE); } -- cgit From 5069185fc18e810715a91d80fcd075e03add600c Mon Sep 17 00:00:00 2001 From: Dan Murphy <dmurphy@ti.com> Date: Mon, 31 Aug 2020 11:48:49 -0500 Subject: power: supply: bq25980: Add support for the BQ259xx family Add support for the BQ25980, BQ25975 and BQ25960 family of flash chargers. Signed-off-by: Dan Murphy <dmurphy@ti.com> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/Kconfig | 9 + drivers/power/supply/Makefile | 1 + drivers/power/supply/bq25980_charger.c | 1316 ++++++++++++++++++++++++++++++++ drivers/power/supply/bq25980_charger.h | 178 +++++ 4 files changed, 1504 insertions(+) create mode 100644 drivers/power/supply/bq25980_charger.c create mode 100644 drivers/power/supply/bq25980_charger.h (limited to 'drivers/power') diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index de9e0fa9a861..eec646c568b7 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -636,6 +636,15 @@ config CHARGER_BQ25890 help Say Y to enable support for the TI BQ25890 battery charger. +config CHARGER_BQ25980 + tristate "TI BQ25980 battery charger driver" + depends on I2C + depends on GPIOLIB || COMPILE_TEST + select REGMAP_I2C + help + Say Y to enable support for the TI BQ25980, BQ25975 and BQ25960 + series of fast battery chargers. + config CHARGER_SMB347 tristate "Summit Microelectronics SMB3XX Battery Charger" depends on I2C diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index 293d4a5d80d3..dd4b86318cd9 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -84,6 +84,7 @@ obj-$(CONFIG_CHARGER_BQ24257) += bq24257_charger.o obj-$(CONFIG_CHARGER_BQ24735) += bq24735-charger.o obj-$(CONFIG_CHARGER_BQ2515X) += bq2515x_charger.o obj-$(CONFIG_CHARGER_BQ25890) += bq25890_charger.o +obj-$(CONFIG_CHARGER_BQ25980) += bq25980_charger.o obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o obj-$(CONFIG_CHARGER_TPS65217) += tps65217_charger.o diff --git a/drivers/power/supply/bq25980_charger.c b/drivers/power/supply/bq25980_charger.c new file mode 100644 index 000000000000..3995fb7cf060 --- /dev/null +++ b/drivers/power/supply/bq25980_charger.c @@ -0,0 +1,1316 @@ +// SPDX-License-Identifier: GPL-2.0 +// BQ25980 Battery Charger Driver +// Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/gpio/consumer.h> +#include <linux/power_supply.h> +#include <linux/regmap.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/moduleparam.h> +#include <linux/slab.h> + +#include "bq25980_charger.h" + +struct bq25980_state { + bool dischg; + bool ovp; + bool ocp; + bool wdt; + bool tflt; + bool online; + bool ce; + bool hiz; + bool bypass; + + u32 vbat_adc; + u32 vsys_adc; + u32 ibat_adc; +}; + +enum bq25980_id { + BQ25980, + BQ25975, + BQ25960, +}; + +struct bq25980_chip_info { + + int model_id; + + const struct regmap_config *regmap_config; + + int busocp_def; + int busocp_sc_max; + int busocp_byp_max; + int busocp_sc_min; + int busocp_byp_min; + + int busovp_sc_def; + int busovp_byp_def; + int busovp_sc_step; + + int busovp_sc_offset; + int busovp_byp_step; + int busovp_byp_offset; + int busovp_sc_min; + int busovp_sc_max; + int busovp_byp_min; + int busovp_byp_max; + + int batovp_def; + int batovp_max; + int batovp_min; + int batovp_step; + int batovp_offset; + + int batocp_def; + int batocp_max; +}; + +struct bq25980_init_data { + u32 ichg; + u32 bypass_ilim; + u32 sc_ilim; + u32 vreg; + u32 iterm; + u32 iprechg; + u32 bypass_vlim; + u32 sc_vlim; + u32 ichg_max; + u32 vreg_max; +}; + +struct bq25980_device { + struct i2c_client *client; + struct device *dev; + struct power_supply *charger; + struct power_supply *battery; + struct mutex lock; + struct regmap *regmap; + + char model_name[I2C_NAME_SIZE]; + + struct bq25980_init_data init_data; + const struct bq25980_chip_info *chip_info; + struct bq25980_state state; + int watchdog_timer; +}; + +static struct reg_default bq25980_reg_defs[] = { + {BQ25980_BATOVP, 0x5A}, + {BQ25980_BATOVP_ALM, 0x46}, + {BQ25980_BATOCP, 0x51}, + {BQ25980_BATOCP_ALM, 0x50}, + {BQ25980_BATUCP_ALM, 0x28}, + {BQ25980_CHRGR_CTRL_1, 0x0}, + {BQ25980_BUSOVP, 0x26}, + {BQ25980_BUSOVP_ALM, 0x22}, + {BQ25980_BUSOCP, 0xD}, + {BQ25980_BUSOCP_ALM, 0xC}, + {BQ25980_TEMP_CONTROL, 0x30}, + {BQ25980_TDIE_ALM, 0xC8}, + {BQ25980_TSBUS_FLT, 0x15}, + {BQ25980_TSBAT_FLG, 0x15}, + {BQ25980_VAC_CONTROL, 0x0}, + {BQ25980_CHRGR_CTRL_2, 0x0}, + {BQ25980_CHRGR_CTRL_3, 0x20}, + {BQ25980_CHRGR_CTRL_4, 0x1D}, + {BQ25980_CHRGR_CTRL_5, 0x18}, + {BQ25980_STAT1, 0x0}, + {BQ25980_STAT2, 0x0}, + {BQ25980_STAT3, 0x0}, + {BQ25980_STAT4, 0x0}, + {BQ25980_STAT5, 0x0}, + {BQ25980_FLAG1, 0x0}, + {BQ25980_FLAG2, 0x0}, + {BQ25980_FLAG3, 0x0}, + {BQ25980_FLAG4, 0x0}, + {BQ25980_FLAG5, 0x0}, + {BQ25980_MASK1, 0x0}, + {BQ25980_MASK2, 0x0}, + {BQ25980_MASK3, 0x0}, + {BQ25980_MASK4, 0x0}, + {BQ25980_MASK5, 0x0}, + {BQ25980_DEVICE_INFO, 0x8}, + {BQ25980_ADC_CONTROL1, 0x0}, + {BQ25980_ADC_CONTROL2, 0x0}, + {BQ25980_IBUS_ADC_LSB, 0x0}, + {BQ25980_IBUS_ADC_MSB, 0x0}, + {BQ25980_VBUS_ADC_LSB, 0x0}, + {BQ25980_VBUS_ADC_MSB, 0x0}, + {BQ25980_VAC1_ADC_LSB, 0x0}, + {BQ25980_VAC2_ADC_LSB, 0x0}, + {BQ25980_VOUT_ADC_LSB, 0x0}, + {BQ25980_VBAT_ADC_LSB, 0x0}, + {BQ25980_IBAT_ADC_MSB, 0x0}, + {BQ25980_IBAT_ADC_LSB, 0x0}, + {BQ25980_TSBUS_ADC_LSB, 0x0}, + {BQ25980_TSBAT_ADC_LSB, 0x0}, + {BQ25980_TDIE_ADC_LSB, 0x0}, + {BQ25980_DEGLITCH_TIME, 0x0}, + {BQ25980_CHRGR_CTRL_6, 0x0}, +}; + +static struct reg_default bq25975_reg_defs[] = { + {BQ25980_BATOVP, 0x5A}, + {BQ25980_BATOVP_ALM, 0x46}, + {BQ25980_BATOCP, 0x51}, + {BQ25980_BATOCP_ALM, 0x50}, + {BQ25980_BATUCP_ALM, 0x28}, + {BQ25980_CHRGR_CTRL_1, 0x0}, + {BQ25980_BUSOVP, 0x26}, + {BQ25980_BUSOVP_ALM, 0x22}, + {BQ25980_BUSOCP, 0xD}, + {BQ25980_BUSOCP_ALM, 0xC}, + {BQ25980_TEMP_CONTROL, 0x30}, + {BQ25980_TDIE_ALM, 0xC8}, + {BQ25980_TSBUS_FLT, 0x15}, + {BQ25980_TSBAT_FLG, 0x15}, + {BQ25980_VAC_CONTROL, 0x0}, + {BQ25980_CHRGR_CTRL_2, 0x0}, + {BQ25980_CHRGR_CTRL_3, 0x20}, + {BQ25980_CHRGR_CTRL_4, 0x1D}, + {BQ25980_CHRGR_CTRL_5, 0x18}, + {BQ25980_STAT1, 0x0}, + {BQ25980_STAT2, 0x0}, + {BQ25980_STAT3, 0x0}, + {BQ25980_STAT4, 0x0}, + {BQ25980_STAT5, 0x0}, + {BQ25980_FLAG1, 0x0}, + {BQ25980_FLAG2, 0x0}, + {BQ25980_FLAG3, 0x0}, + {BQ25980_FLAG4, 0x0}, + {BQ25980_FLAG5, 0x0}, + {BQ25980_MASK1, 0x0}, + {BQ25980_MASK2, 0x0}, + {BQ25980_MASK3, 0x0}, + {BQ25980_MASK4, 0x0}, + {BQ25980_MASK5, 0x0}, + {BQ25980_DEVICE_INFO, 0x8}, + {BQ25980_ADC_CONTROL1, 0x0}, + {BQ25980_ADC_CONTROL2, 0x0}, + {BQ25980_IBUS_ADC_LSB, 0x0}, + {BQ25980_IBUS_ADC_MSB, 0x0}, + {BQ25980_VBUS_ADC_LSB, 0x0}, + {BQ25980_VBUS_ADC_MSB, 0x0}, + {BQ25980_VAC1_ADC_LSB, 0x0}, + {BQ25980_VAC2_ADC_LSB, 0x0}, + {BQ25980_VOUT_ADC_LSB, 0x0}, + {BQ25980_VBAT_ADC_LSB, 0x0}, + {BQ25980_IBAT_ADC_MSB, 0x0}, + {BQ25980_IBAT_ADC_LSB, 0x0}, + {BQ25980_TSBUS_ADC_LSB, 0x0}, + {BQ25980_TSBAT_ADC_LSB, 0x0}, + {BQ25980_TDIE_ADC_LSB, 0x0}, + {BQ25980_DEGLITCH_TIME, 0x0}, + {BQ25980_CHRGR_CTRL_6, 0x0}, +}; + +static struct reg_default bq25960_reg_defs[] = { + {BQ25980_BATOVP, 0x5A}, + {BQ25980_BATOVP_ALM, 0x46}, + {BQ25980_BATOCP, 0x51}, + {BQ25980_BATOCP_ALM, 0x50}, + {BQ25980_BATUCP_ALM, 0x28}, + {BQ25980_CHRGR_CTRL_1, 0x0}, + {BQ25980_BUSOVP, 0x26}, + {BQ25980_BUSOVP_ALM, 0x22}, + {BQ25980_BUSOCP, 0xD}, + {BQ25980_BUSOCP_ALM, 0xC}, + {BQ25980_TEMP_CONTROL, 0x30}, + {BQ25980_TDIE_ALM, 0xC8}, + {BQ25980_TSBUS_FLT, 0x15}, + {BQ25980_TSBAT_FLG, 0x15}, + {BQ25980_VAC_CONTROL, 0x0}, + {BQ25980_CHRGR_CTRL_2, 0x0}, + {BQ25980_CHRGR_CTRL_3, 0x20}, + {BQ25980_CHRGR_CTRL_4, 0x1D}, + {BQ25980_CHRGR_CTRL_5, 0x18}, + {BQ25980_STAT1, 0x0}, + {BQ25980_STAT2, 0x0}, + {BQ25980_STAT3, 0x0}, + {BQ25980_STAT4, 0x0}, + {BQ25980_STAT5, 0x0}, + {BQ25980_FLAG1, 0x0}, + {BQ25980_FLAG2, 0x0}, + {BQ25980_FLAG3, 0x0}, + {BQ25980_FLAG4, 0x0}, + {BQ25980_FLAG5, 0x0}, + {BQ25980_MASK1, 0x0}, + {BQ25980_MASK2, 0x0}, + {BQ25980_MASK3, 0x0}, + {BQ25980_MASK4, 0x0}, + {BQ25980_MASK5, 0x0}, + {BQ25980_DEVICE_INFO, 0x8}, + {BQ25980_ADC_CONTROL1, 0x0}, + {BQ25980_ADC_CONTROL2, 0x0}, + {BQ25980_IBUS_ADC_LSB, 0x0}, + {BQ25980_IBUS_ADC_MSB, 0x0}, + {BQ25980_VBUS_ADC_LSB, 0x0}, + {BQ25980_VBUS_ADC_MSB, 0x0}, + {BQ25980_VAC1_ADC_LSB, 0x0}, + {BQ25980_VAC2_ADC_LSB, 0x0}, + {BQ25980_VOUT_ADC_LSB, 0x0}, + {BQ25980_VBAT_ADC_LSB, 0x0}, + {BQ25980_IBAT_ADC_MSB, 0x0}, + {BQ25980_IBAT_ADC_LSB, 0x0}, + {BQ25980_TSBUS_ADC_LSB, 0x0}, + {BQ25980_TSBAT_ADC_LSB, 0x0}, + {BQ25980_TDIE_ADC_LSB, 0x0}, + {BQ25980_DEGLITCH_TIME, 0x0}, + {BQ25980_CHRGR_CTRL_6, 0x0}, +}; + +static int bq25980_watchdog_time[BQ25980_NUM_WD_VAL] = {5000, 10000, 50000, + 300000}; + +static int bq25980_get_input_curr_lim(struct bq25980_device *bq) +{ + unsigned int busocp_reg_code; + int ret; + + ret = regmap_read(bq->regmap, BQ25980_BUSOCP, &busocp_reg_code); + if (ret) + return ret; + + return (busocp_reg_code * BQ25980_BUSOCP_STEP_uA) + BQ25980_BUSOCP_OFFSET_uA; +} + +static int bq25980_set_hiz(struct bq25980_device *bq, int setting) +{ + return regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2, + BQ25980_EN_HIZ, setting); +} + +static int bq25980_set_input_curr_lim(struct bq25980_device *bq, int busocp) +{ + unsigned int busocp_reg_code; + int ret; + + if (!busocp) + return bq25980_set_hiz(bq, BQ25980_ENABLE_HIZ); + + bq25980_set_hiz(bq, BQ25980_DISABLE_HIZ); + + if (busocp < BQ25980_BUSOCP_MIN_uA) + busocp = BQ25980_BUSOCP_MIN_uA; + + if (bq->state.bypass) + busocp = min(busocp, bq->chip_info->busocp_sc_max); + else + busocp = min(busocp, bq->chip_info->busocp_byp_max); + + busocp_reg_code = (busocp - BQ25980_BUSOCP_OFFSET_uA) + / BQ25980_BUSOCP_STEP_uA; + + ret = regmap_write(bq->regmap, BQ25980_BUSOCP, busocp_reg_code); + if (ret) + return ret; + + return regmap_write(bq->regmap, BQ25980_BUSOCP_ALM, busocp_reg_code); +} + +static int bq25980_get_input_volt_lim(struct bq25980_device *bq) +{ + unsigned int busovp_reg_code; + unsigned int busovp_offset; + unsigned int busovp_step; + int ret; + + if (bq->state.bypass) { + busovp_step = bq->chip_info->busovp_byp_step; + busovp_offset = bq->chip_info->busovp_byp_offset; + } else { + busovp_step = bq->chip_info->busovp_sc_step; + busovp_offset = bq->chip_info->busovp_sc_offset; + } + + ret = regmap_read(bq->regmap, BQ25980_BUSOVP, &busovp_reg_code); + if (ret) + return ret; + + return (busovp_reg_code * busovp_step) + busovp_offset; +} + +static int bq25980_set_input_volt_lim(struct bq25980_device *bq, int busovp) +{ + unsigned int busovp_reg_code; + unsigned int busovp_step; + unsigned int busovp_offset; + int ret; + + if (bq->state.bypass) { + busovp_step = bq->chip_info->busovp_byp_step; + busovp_offset = bq->chip_info->busovp_byp_offset; + if (busovp > bq->chip_info->busovp_byp_max) + busovp = bq->chip_info->busovp_byp_max; + else if (busovp < bq->chip_info->busovp_byp_min) + busovp = bq->chip_info->busovp_byp_min; + } else { + busovp_step = bq->chip_info->busovp_sc_step; + busovp_offset = bq->chip_info->busovp_sc_offset; + if (busovp > bq->chip_info->busovp_sc_max) + busovp = bq->chip_info->busovp_sc_max; + else if (busovp < bq->chip_info->busovp_sc_min) + busovp = bq->chip_info->busovp_sc_min; + } + + busovp_reg_code = (busovp - busovp_offset) / busovp_step; + + ret = regmap_write(bq->regmap, BQ25980_BUSOVP, busovp_reg_code); + if (ret) + return ret; + + return regmap_write(bq->regmap, BQ25980_BUSOVP_ALM, busovp_reg_code); +} + +static int bq25980_get_const_charge_curr(struct bq25980_device *bq) +{ + unsigned int batocp_reg_code; + int ret; + + ret = regmap_read(bq->regmap, BQ25980_BATOCP, &batocp_reg_code); + if (ret) + return ret; + + return (batocp_reg_code & BQ25980_BATOCP_MASK) * + BQ25980_BATOCP_STEP_uA; +} + +static int bq25980_set_const_charge_curr(struct bq25980_device *bq, int batocp) +{ + unsigned int batocp_reg_code; + int ret; + + batocp = max(batocp, BQ25980_BATOCP_MIN_uA); + batocp = min(batocp, bq->chip_info->batocp_max); + + batocp_reg_code = batocp / BQ25980_BATOCP_STEP_uA; + + ret = regmap_update_bits(bq->regmap, BQ25980_BATOCP, + BQ25980_BATOCP_MASK, batocp_reg_code); + if (ret) + return ret; + + return regmap_update_bits(bq->regmap, BQ25980_BATOCP_ALM, + BQ25980_BATOCP_MASK, batocp_reg_code); +} + +static int bq25980_get_const_charge_volt(struct bq25980_device *bq) +{ + unsigned int batovp_reg_code; + int ret; + + ret = regmap_read(bq->regmap, BQ25980_BATOVP, &batovp_reg_code); + if (ret) + return ret; + + return ((batovp_reg_code * bq->chip_info->batovp_step) + + bq->chip_info->batovp_offset); +} + +static int bq25980_set_const_charge_volt(struct bq25980_device *bq, int batovp) +{ + unsigned int batovp_reg_code; + int ret; + + if (batovp < bq->chip_info->batovp_min) + batovp = bq->chip_info->batovp_min; + + if (batovp > bq->chip_info->batovp_max) + batovp = bq->chip_info->batovp_max; + + batovp_reg_code = (batovp - bq->chip_info->batovp_offset) / + bq->chip_info->batovp_step; + + ret = regmap_write(bq->regmap, BQ25980_BATOVP, batovp_reg_code); + if (ret) + return ret; + + return regmap_write(bq->regmap, BQ25980_BATOVP_ALM, batovp_reg_code); +} + +static int bq25980_set_bypass(struct bq25980_device *bq, bool en_bypass) +{ + int ret; + + if (en_bypass) + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2, + BQ25980_EN_BYPASS, BQ25980_EN_BYPASS); + else + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2, + BQ25980_EN_BYPASS, en_bypass); + if (ret) + return ret; + + bq->state.bypass = en_bypass; + + return bq->state.bypass; +} + +static int bq25980_set_chg_en(struct bq25980_device *bq, bool en_chg) +{ + int ret; + + if (en_chg) + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2, + BQ25980_CHG_EN, BQ25980_CHG_EN); + else + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2, + BQ25980_CHG_EN, en_chg); + if (ret) + return ret; + + bq->state.ce = en_chg; + + return 0; +} + +static int bq25980_get_adc_ibus(struct bq25980_device *bq) +{ + int ibus_adc_lsb, ibus_adc_msb; + u16 ibus_adc; + int ret; + + ret = regmap_read(bq->regmap, BQ25980_IBUS_ADC_MSB, &ibus_adc_msb); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_IBUS_ADC_LSB, &ibus_adc_lsb); + if (ret) + return ret; + + ibus_adc = (ibus_adc_msb << 8) | ibus_adc_lsb; + + if (ibus_adc_msb & BQ25980_ADC_POLARITY_BIT) + return ((ibus_adc ^ 0xffff) + 1) * BQ25980_ADC_CURR_STEP_uA; + + return ibus_adc * BQ25980_ADC_CURR_STEP_uA; +} + +static int bq25980_get_adc_vbus(struct bq25980_device *bq) +{ + int vbus_adc_lsb, vbus_adc_msb; + u16 vbus_adc; + int ret; + + ret = regmap_read(bq->regmap, BQ25980_VBUS_ADC_MSB, &vbus_adc_msb); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_VBUS_ADC_LSB, &vbus_adc_lsb); + if (ret) + return ret; + + vbus_adc = (vbus_adc_msb << 8) | vbus_adc_lsb; + + return vbus_adc * BQ25980_ADC_VOLT_STEP_uV; +} + +static int bq25980_get_ibat_adc(struct bq25980_device *bq) +{ + int ret; + int ibat_adc_lsb, ibat_adc_msb; + int ibat_adc; + + ret = regmap_read(bq->regmap, BQ25980_IBAT_ADC_MSB, &ibat_adc_msb); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_IBAT_ADC_LSB, &ibat_adc_lsb); + if (ret) + return ret; + + ibat_adc = (ibat_adc_msb << 8) | ibat_adc_lsb; + + if (ibat_adc_msb & BQ25980_ADC_POLARITY_BIT) + return ((ibat_adc ^ 0xffff) + 1) * BQ25980_ADC_CURR_STEP_uA; + + return ibat_adc * BQ25980_ADC_CURR_STEP_uA; +} + +static int bq25980_get_adc_vbat(struct bq25980_device *bq) +{ + int vsys_adc_lsb, vsys_adc_msb; + u16 vsys_adc; + int ret; + + ret = regmap_read(bq->regmap, BQ25980_VBAT_ADC_MSB, &vsys_adc_msb); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_VBAT_ADC_LSB, &vsys_adc_lsb); + if (ret) + return ret; + + vsys_adc = (vsys_adc_msb << 8) | vsys_adc_lsb; + + return vsys_adc * BQ25980_ADC_VOLT_STEP_uV; +} + +static int bq25980_get_state(struct bq25980_device *bq, + struct bq25980_state *state) +{ + unsigned int chg_ctrl_2; + unsigned int stat1; + unsigned int stat2; + unsigned int stat3; + unsigned int stat4; + unsigned int ibat_adc_msb; + int ret; + + ret = regmap_read(bq->regmap, BQ25980_STAT1, &stat1); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_STAT2, &stat2); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_STAT3, &stat3); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_STAT4, &stat4); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_CHRGR_CTRL_2, &chg_ctrl_2); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_IBAT_ADC_MSB, &ibat_adc_msb); + if (ret) + return ret; + + state->dischg = ibat_adc_msb & BQ25980_ADC_POLARITY_BIT; + state->ovp = (stat1 & BQ25980_STAT1_OVP_MASK) | + (stat3 & BQ25980_STAT3_OVP_MASK); + state->ocp = (stat1 & BQ25980_STAT1_OCP_MASK) | + (stat2 & BQ25980_STAT2_OCP_MASK); + state->tflt = stat4 & BQ25980_STAT4_TFLT_MASK; + state->wdt = stat4 & BQ25980_WD_STAT; + state->online = stat3 & BQ25980_PRESENT_MASK; + state->ce = chg_ctrl_2 & BQ25980_CHG_EN; + state->hiz = chg_ctrl_2 & BQ25980_EN_HIZ; + state->bypass = chg_ctrl_2 & BQ25980_EN_BYPASS; + + return 0; +} + +static int bq25980_set_battery_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct bq25980_device *bq = power_supply_get_drvdata(psy); + int ret = 0; + + if (ret) + return ret; + + switch (psp) { + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + ret = bq25980_set_const_charge_curr(bq, val->intval); + if (ret) + return ret; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + ret = bq25980_set_const_charge_volt(bq, val->intval); + if (ret) + return ret; + break; + + default: + return -EINVAL; + } + + return ret; +} + +static int bq25980_get_battery_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct bq25980_device *bq = power_supply_get_drvdata(psy); + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + val->intval = bq->init_data.ichg_max; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: + val->intval = bq->init_data.vreg_max; + break; + + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = bq25980_get_ibat_adc(bq); + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = bq25980_get_adc_vbat(bq); + if (ret < 0) + return ret; + + val->intval = ret; + break; + + default: + return -EINVAL; + } + + return ret; +} + +static int bq25980_set_charger_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct bq25980_device *bq = power_supply_get_drvdata(psy); + int ret = -EINVAL; + + switch (prop) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = bq25980_set_input_curr_lim(bq, val->intval); + if (ret) + return ret; + break; + + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + ret = bq25980_set_input_volt_lim(bq, val->intval); + if (ret) + return ret; + break; + + case POWER_SUPPLY_PROP_CHARGE_TYPE: + ret = bq25980_set_bypass(bq, val->intval); + if (ret) + return ret; + break; + + case POWER_SUPPLY_PROP_STATUS: + ret = bq25980_set_chg_en(bq, val->intval); + if (ret) + return ret; + break; + + default: + return -EINVAL; + } + + return ret; +} + +static int bq25980_get_charger_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct bq25980_device *bq = power_supply_get_drvdata(psy); + struct bq25980_state state; + int ret = 0; + + mutex_lock(&bq->lock); + ret = bq25980_get_state(bq, &state); + mutex_unlock(&bq->lock); + if (ret) + return ret; + + switch (psp) { + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = BQ25980_MANUFACTURER; + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = bq->model_name; + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = state.online; + break; + + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + ret = bq25980_get_input_volt_lim(bq); + if (ret < 0) + return ret; + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = bq25980_get_input_curr_lim(bq); + if (ret < 0) + return ret; + + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_HEALTH: + val->intval = POWER_SUPPLY_HEALTH_GOOD; + + if (state.tflt) + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + else if (state.ovp) + val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + else if (state.ocp) + val->intval = POWER_SUPPLY_HEALTH_OVERCURRENT; + else if (state.wdt) + val->intval = + POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE; + break; + + case POWER_SUPPLY_PROP_STATUS: + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + + if ((state.ce) && (!state.hiz)) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else if (state.dischg) + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + else if (!state.ce) + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + + case POWER_SUPPLY_PROP_CHARGE_TYPE: + val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + + if (!state.ce) + val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; + else if (state.bypass) + val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; + else if (!state.bypass) + val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD; + break; + + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = bq25980_get_adc_ibus(bq); + if (ret < 0) + return ret; + + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = bq25980_get_adc_vbus(bq); + if (ret < 0) + return ret; + + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + ret = bq25980_get_const_charge_curr(bq); + if (ret < 0) + return ret; + + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + ret = bq25980_get_const_charge_volt(bq); + if (ret < 0) + return ret; + + val->intval = ret; + break; + + default: + return -EINVAL; + } + + return ret; +} + +static bool bq25980_state_changed(struct bq25980_device *bq, + struct bq25980_state *new_state) +{ + struct bq25980_state old_state; + + mutex_lock(&bq->lock); + old_state = bq->state; + mutex_unlock(&bq->lock); + + return (old_state.dischg != new_state->dischg || + old_state.ovp != new_state->ovp || + old_state.ocp != new_state->ocp || + old_state.online != new_state->online || + old_state.wdt != new_state->wdt || + old_state.tflt != new_state->tflt || + old_state.ce != new_state->ce || + old_state.hiz != new_state->hiz || + old_state.bypass != new_state->bypass); +} + +static irqreturn_t bq25980_irq_handler_thread(int irq, void *private) +{ + struct bq25980_device *bq = private; + struct bq25980_state state; + int ret; + + ret = bq25980_get_state(bq, &state); + if (ret < 0) + goto irq_out; + + if (!bq25980_state_changed(bq, &state)) + goto irq_out; + + mutex_lock(&bq->lock); + bq->state = state; + mutex_unlock(&bq->lock); + + power_supply_changed(bq->charger); + +irq_out: + return IRQ_HANDLED; +} + +static enum power_supply_property bq25980_power_supply_props[] = { + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, +}; + +static enum power_supply_property bq25980_battery_props[] = { + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, +}; + +static char *bq25980_charger_supplied_to[] = { + "main-battery", +}; + +static int bq25980_property_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + switch (prop) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + case POWER_SUPPLY_PROP_CHARGE_TYPE: + case POWER_SUPPLY_PROP_STATUS: + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + return true; + default: + return false; + } +} + +static const struct power_supply_desc bq25980_power_supply_desc = { + .name = "bq25980-charger", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = bq25980_power_supply_props, + .num_properties = ARRAY_SIZE(bq25980_power_supply_props), + .get_property = bq25980_get_charger_property, + .set_property = bq25980_set_charger_property, + .property_is_writeable = bq25980_property_is_writeable, +}; + +static struct power_supply_desc bq25980_battery_desc = { + .name = "bq25980-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = bq25980_get_battery_property, + .set_property = bq25980_set_battery_property, + .properties = bq25980_battery_props, + .num_properties = ARRAY_SIZE(bq25980_battery_props), + .property_is_writeable = bq25980_property_is_writeable, +}; + + +static bool bq25980_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case BQ25980_CHRGR_CTRL_2: + case BQ25980_STAT1...BQ25980_FLAG5: + case BQ25980_ADC_CONTROL1...BQ25980_TDIE_ADC_LSB: + return true; + default: + return false; + } +} + +static const struct regmap_config bq25980_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = BQ25980_CHRGR_CTRL_6, + .reg_defaults = bq25980_reg_defs, + .num_reg_defaults = ARRAY_SIZE(bq25980_reg_defs), + .cache_type = REGCACHE_RBTREE, + .volatile_reg = bq25980_is_volatile_reg, +}; + +static const struct regmap_config bq25975_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = BQ25980_CHRGR_CTRL_6, + .reg_defaults = bq25975_reg_defs, + .num_reg_defaults = ARRAY_SIZE(bq25975_reg_defs), + .cache_type = REGCACHE_RBTREE, + .volatile_reg = bq25980_is_volatile_reg, +}; + +static const struct regmap_config bq25960_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = BQ25980_CHRGR_CTRL_6, + .reg_defaults = bq25960_reg_defs, + .num_reg_defaults = ARRAY_SIZE(bq25960_reg_defs), + .cache_type = REGCACHE_RBTREE, + .volatile_reg = bq25980_is_volatile_reg, +}; + +static const struct bq25980_chip_info bq25980_chip_info_tbl[] = { + [BQ25980] = { + .model_id = BQ25980, + .regmap_config = &bq25980_regmap_config, + + .busocp_def = BQ25980_BUSOCP_DFLT_uA, + .busocp_sc_min = BQ25960_BUSOCP_SC_MAX_uA, + .busocp_sc_max = BQ25980_BUSOCP_SC_MAX_uA, + .busocp_byp_max = BQ25980_BUSOCP_BYP_MAX_uA, + .busocp_byp_min = BQ25980_BUSOCP_MIN_uA, + + .busovp_sc_def = BQ25980_BUSOVP_DFLT_uV, + .busovp_byp_def = BQ25980_BUSOVP_BYPASS_DFLT_uV, + .busovp_sc_step = BQ25980_BUSOVP_SC_STEP_uV, + .busovp_sc_offset = BQ25980_BUSOVP_SC_OFFSET_uV, + .busovp_byp_step = BQ25980_BUSOVP_BYP_STEP_uV, + .busovp_byp_offset = BQ25980_BUSOVP_BYP_OFFSET_uV, + .busovp_sc_min = BQ25980_BUSOVP_SC_MIN_uV, + .busovp_sc_max = BQ25980_BUSOVP_SC_MAX_uV, + .busovp_byp_min = BQ25980_BUSOVP_BYP_MIN_uV, + .busovp_byp_max = BQ25980_BUSOVP_BYP_MAX_uV, + + .batovp_def = BQ25980_BATOVP_DFLT_uV, + .batovp_max = BQ25980_BATOVP_MAX_uV, + .batovp_min = BQ25980_BATOVP_MIN_uV, + .batovp_step = BQ25980_BATOVP_STEP_uV, + .batovp_offset = BQ25980_BATOVP_OFFSET_uV, + + .batocp_def = BQ25980_BATOCP_DFLT_uA, + .batocp_max = BQ25980_BATOCP_MAX_uA, + }, + + [BQ25975] = { + .model_id = BQ25975, + .regmap_config = &bq25975_regmap_config, + + .busocp_def = BQ25975_BUSOCP_DFLT_uA, + .busocp_sc_min = BQ25975_BUSOCP_SC_MAX_uA, + .busocp_sc_max = BQ25975_BUSOCP_SC_MAX_uA, + .busocp_byp_min = BQ25980_BUSOCP_MIN_uA, + .busocp_byp_max = BQ25975_BUSOCP_BYP_MAX_uA, + + .busovp_sc_def = BQ25975_BUSOVP_DFLT_uV, + .busovp_byp_def = BQ25975_BUSOVP_BYPASS_DFLT_uV, + .busovp_sc_step = BQ25975_BUSOVP_SC_STEP_uV, + .busovp_sc_offset = BQ25975_BUSOVP_SC_OFFSET_uV, + .busovp_byp_step = BQ25975_BUSOVP_BYP_STEP_uV, + .busovp_byp_offset = BQ25975_BUSOVP_BYP_OFFSET_uV, + .busovp_sc_min = BQ25975_BUSOVP_SC_MIN_uV, + .busovp_sc_max = BQ25975_BUSOVP_SC_MAX_uV, + .busovp_byp_min = BQ25975_BUSOVP_BYP_MIN_uV, + .busovp_byp_max = BQ25975_BUSOVP_BYP_MAX_uV, + + .batovp_def = BQ25975_BATOVP_DFLT_uV, + .batovp_max = BQ25975_BATOVP_MAX_uV, + .batovp_min = BQ25975_BATOVP_MIN_uV, + .batovp_step = BQ25975_BATOVP_STEP_uV, + .batovp_offset = BQ25975_BATOVP_OFFSET_uV, + + .batocp_def = BQ25980_BATOCP_DFLT_uA, + .batocp_max = BQ25980_BATOCP_MAX_uA, + }, + + [BQ25960] = { + .model_id = BQ25960, + .regmap_config = &bq25960_regmap_config, + + .busocp_def = BQ25960_BUSOCP_DFLT_uA, + .busocp_sc_min = BQ25960_BUSOCP_SC_MAX_uA, + .busocp_sc_max = BQ25960_BUSOCP_SC_MAX_uA, + .busocp_byp_min = BQ25960_BUSOCP_SC_MAX_uA, + .busocp_byp_max = BQ25960_BUSOCP_BYP_MAX_uA, + + .busovp_sc_def = BQ25975_BUSOVP_DFLT_uV, + .busovp_byp_def = BQ25975_BUSOVP_BYPASS_DFLT_uV, + .busovp_sc_step = BQ25960_BUSOVP_SC_STEP_uV, + .busovp_sc_offset = BQ25960_BUSOVP_SC_OFFSET_uV, + .busovp_byp_step = BQ25960_BUSOVP_BYP_STEP_uV, + .busovp_byp_offset = BQ25960_BUSOVP_BYP_OFFSET_uV, + .busovp_sc_min = BQ25960_BUSOVP_SC_MIN_uV, + .busovp_sc_max = BQ25960_BUSOVP_SC_MAX_uV, + .busovp_byp_min = BQ25960_BUSOVP_BYP_MIN_uV, + .busovp_byp_max = BQ25960_BUSOVP_BYP_MAX_uV, + + .batovp_def = BQ25960_BATOVP_DFLT_uV, + .batovp_max = BQ25960_BATOVP_MAX_uV, + .batovp_min = BQ25960_BATOVP_MIN_uV, + .batovp_step = BQ25960_BATOVP_STEP_uV, + .batovp_offset = BQ25960_BATOVP_OFFSET_uV, + + .batocp_def = BQ25960_BATOCP_DFLT_uA, + .batocp_max = BQ25960_BATOCP_MAX_uA, + }, +}; + +static int bq25980_power_supply_init(struct bq25980_device *bq, + struct device *dev) +{ + struct power_supply_config psy_cfg = { .drv_data = bq, + .of_node = dev->of_node, }; + + psy_cfg.supplied_to = bq25980_charger_supplied_to; + psy_cfg.num_supplicants = ARRAY_SIZE(bq25980_charger_supplied_to); + + bq->charger = devm_power_supply_register(bq->dev, + &bq25980_power_supply_desc, + &psy_cfg); + if (IS_ERR(bq->charger)) + return -EINVAL; + + bq->battery = devm_power_supply_register(bq->dev, + &bq25980_battery_desc, + &psy_cfg); + if (IS_ERR(bq->battery)) + return -EINVAL; + + return 0; +} + +static int bq25980_hw_init(struct bq25980_device *bq) +{ + struct power_supply_battery_info bat_info = { }; + int wd_reg_val; + int ret = 0; + int curr_val; + int volt_val; + int i; + + if (!bq->watchdog_timer) { + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_3, + BQ25980_WATCHDOG_DIS, + BQ25980_WATCHDOG_DIS); + } else { + for (i = 0; i < BQ25980_NUM_WD_VAL; i++) { + if (bq->watchdog_timer > bq25980_watchdog_time[i] && + bq->watchdog_timer < bq25980_watchdog_time[i + 1]) { + wd_reg_val = i; + break; + } + } + + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_3, + BQ25980_WATCHDOG_MASK, wd_reg_val); + } + if (ret) + return ret; + + ret = power_supply_get_battery_info(bq->charger, &bat_info); + if (ret) { + dev_warn(bq->dev, "battery info missing\n"); + return -EINVAL; + } + + bq->init_data.ichg_max = bat_info.constant_charge_current_max_ua; + bq->init_data.vreg_max = bat_info.constant_charge_voltage_max_uv; + + if (bq->state.bypass) { + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2, + BQ25980_EN_BYPASS, BQ25980_EN_BYPASS); + if (ret) + return ret; + + curr_val = bq->init_data.bypass_ilim; + volt_val = bq->init_data.bypass_vlim; + } else { + curr_val = bq->init_data.sc_ilim; + volt_val = bq->init_data.sc_vlim; + } + + ret = bq25980_set_input_curr_lim(bq, curr_val); + if (ret) + return ret; + + ret = bq25980_set_input_volt_lim(bq, volt_val); + if (ret) + return ret; + + return regmap_update_bits(bq->regmap, BQ25980_ADC_CONTROL1, + BQ25980_ADC_EN, BQ25980_ADC_EN); +} + +static int bq25980_parse_dt(struct bq25980_device *bq) +{ + int ret; + + ret = device_property_read_u32(bq->dev, "ti,watchdog-timeout-ms", + &bq->watchdog_timer); + if (ret) + bq->watchdog_timer = BQ25980_WATCHDOG_MIN; + + if (bq->watchdog_timer > BQ25980_WATCHDOG_MAX || + bq->watchdog_timer < BQ25980_WATCHDOG_MIN) + return -EINVAL; + + ret = device_property_read_u32(bq->dev, + "ti,sc-ovp-limit-microvolt", + &bq->init_data.sc_vlim); + if (ret) + bq->init_data.sc_vlim = bq->chip_info->busovp_sc_def; + + if (bq->init_data.sc_vlim > bq->chip_info->busovp_sc_max || + bq->init_data.sc_vlim < bq->chip_info->busovp_sc_min) { + dev_err(bq->dev, "SC ovp limit is out of range\n"); + return -EINVAL; + } + + ret = device_property_read_u32(bq->dev, + "ti,sc-ocp-limit-microamp", + &bq->init_data.sc_ilim); + if (ret) + bq->init_data.sc_ilim = bq->chip_info->busocp_def; + + if (bq->init_data.sc_ilim > bq->chip_info->busocp_sc_max || + bq->init_data.sc_ilim < bq->chip_info->busocp_sc_min) { + dev_err(bq->dev, "SC ocp limit is out of range\n"); + return -EINVAL; + } + + ret = device_property_read_u32(bq->dev, + "ti,bypass-ovp-limit-microvolt", + &bq->init_data.bypass_vlim); + if (ret) + bq->init_data.bypass_vlim = bq->chip_info->busovp_byp_def; + + if (bq->init_data.bypass_vlim > bq->chip_info->busovp_byp_max || + bq->init_data.bypass_vlim < bq->chip_info->busovp_byp_min) { + dev_err(bq->dev, "Bypass ovp limit is out of range\n"); + return -EINVAL; + } + + ret = device_property_read_u32(bq->dev, + "ti,bypass-ocp-limit-microamp", + &bq->init_data.bypass_ilim); + if (ret) + bq->init_data.bypass_ilim = bq->chip_info->busocp_def; + + if (bq->init_data.bypass_ilim > bq->chip_info->busocp_byp_max || + bq->init_data.bypass_ilim < bq->chip_info->busocp_byp_min) { + dev_err(bq->dev, "Bypass ocp limit is out of range\n"); + return -EINVAL; + } + + + bq->state.bypass = device_property_read_bool(bq->dev, + "ti,bypass-enable"); + return 0; +} + +static int bq25980_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct bq25980_device *bq; + int ret; + + bq = devm_kzalloc(dev, sizeof(*bq), GFP_KERNEL); + if (!bq) + return -ENOMEM; + + bq->client = client; + bq->dev = dev; + + mutex_init(&bq->lock); + + strncpy(bq->model_name, id->name, I2C_NAME_SIZE); + bq->chip_info = &bq25980_chip_info_tbl[id->driver_data]; + + bq->regmap = devm_regmap_init_i2c(client, + bq->chip_info->regmap_config); + if (IS_ERR(bq->regmap)) { + dev_err(dev, "Failed to allocate register map\n"); + return PTR_ERR(bq->regmap); + } + + i2c_set_clientdata(client, bq); + + ret = bq25980_parse_dt(bq); + if (ret) { + dev_err(dev, "Failed to read device tree properties%d\n", ret); + return ret; + } + + if (client->irq) { + ret = devm_request_threaded_irq(dev, client->irq, NULL, + bq25980_irq_handler_thread, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + dev_name(&client->dev), bq); + if (ret) + return ret; + } + + ret = bq25980_power_supply_init(bq, dev); + if (ret) { + dev_err(dev, "Failed to register power supply\n"); + return ret; + } + + ret = bq25980_hw_init(bq); + if (ret) { + dev_err(dev, "Cannot initialize the chip.\n"); + return ret; + } + + return 0; +} + +static const struct i2c_device_id bq25980_i2c_ids[] = { + { "bq25980", BQ25980 }, + { "bq25975", BQ25975 }, + { "bq25975", BQ25975 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, bq25980_i2c_ids); + +static const struct of_device_id bq25980_of_match[] = { + { .compatible = "ti,bq25980", .data = (void *)BQ25980 }, + { .compatible = "ti,bq25975", .data = (void *)BQ25975 }, + { .compatible = "ti,bq25960", .data = (void *)BQ25960 }, + { }, +}; +MODULE_DEVICE_TABLE(of, bq25980_of_match); + +static struct i2c_driver bq25980_driver = { + .driver = { + .name = "bq25980-charger", + .of_match_table = bq25980_of_match, + }, + .probe = bq25980_probe, + .id_table = bq25980_i2c_ids, +}; +module_i2c_driver(bq25980_driver); + +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>"); +MODULE_AUTHOR("Ricardo Rivera-Matos <r-rivera-matos@ti.com>"); +MODULE_DESCRIPTION("bq25980 charger driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/supply/bq25980_charger.h b/drivers/power/supply/bq25980_charger.h new file mode 100644 index 000000000000..39f94eba5f6c --- /dev/null +++ b/drivers/power/supply/bq25980_charger.h @@ -0,0 +1,178 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ */ + +#ifndef BQ25980_CHARGER_H +#define BQ25980_CHARGER_H + +#define BQ25980_MANUFACTURER "Texas Instruments" + +#define BQ25980_BATOVP 0x0 +#define BQ25980_BATOVP_ALM 0x1 +#define BQ25980_BATOCP 0x2 +#define BQ25980_BATOCP_ALM 0x3 +#define BQ25980_BATUCP_ALM 0x4 +#define BQ25980_CHRGR_CTRL_1 0x5 +#define BQ25980_BUSOVP 0x6 +#define BQ25980_BUSOVP_ALM 0x7 +#define BQ25980_BUSOCP 0x8 +#define BQ25980_BUSOCP_ALM 0x9 +#define BQ25980_TEMP_CONTROL 0xA +#define BQ25980_TDIE_ALM 0xB +#define BQ25980_TSBUS_FLT 0xC +#define BQ25980_TSBAT_FLG 0xD +#define BQ25980_VAC_CONTROL 0xE +#define BQ25980_CHRGR_CTRL_2 0xF +#define BQ25980_CHRGR_CTRL_3 0x10 +#define BQ25980_CHRGR_CTRL_4 0x11 +#define BQ25980_CHRGR_CTRL_5 0x12 +#define BQ25980_STAT1 0x13 +#define BQ25980_STAT2 0x14 +#define BQ25980_STAT3 0x15 +#define BQ25980_STAT4 0x16 +#define BQ25980_STAT5 0x17 +#define BQ25980_FLAG1 0x18 +#define BQ25980_FLAG2 0x19 +#define BQ25980_FLAG3 0x1A +#define BQ25980_FLAG4 0x1B +#define BQ25980_FLAG5 0x1C +#define BQ25980_MASK1 0x1D +#define BQ25980_MASK2 0x1E +#define BQ25980_MASK3 0x1F +#define BQ25980_MASK4 0x20 +#define BQ25980_MASK5 0x21 +#define BQ25980_DEVICE_INFO 0x22 +#define BQ25980_ADC_CONTROL1 0x23 +#define BQ25980_ADC_CONTROL2 0x24 +#define BQ25980_IBUS_ADC_MSB 0x25 +#define BQ25980_IBUS_ADC_LSB 0x26 +#define BQ25980_VBUS_ADC_MSB 0x27 +#define BQ25980_VBUS_ADC_LSB 0x28 +#define BQ25980_VAC1_ADC_MSB 0x29 +#define BQ25980_VAC1_ADC_LSB 0x2A +#define BQ25980_VAC2_ADC_MSB 0x2B +#define BQ25980_VAC2_ADC_LSB 0x2C +#define BQ25980_VOUT_ADC_MSB 0x2D +#define BQ25980_VOUT_ADC_LSB 0x2E +#define BQ25980_VBAT_ADC_MSB 0x2F +#define BQ25980_VBAT_ADC_LSB 0x30 +#define BQ25980_IBAT_ADC_MSB 0x31 +#define BQ25980_IBAT_ADC_LSB 0x32 +#define BQ25980_TSBUS_ADC_MSB 0x33 +#define BQ25980_TSBUS_ADC_LSB 0x34 +#define BQ25980_TSBAT_ADC_MSB 0x35 +#define BQ25980_TSBAT_ADC_LSB 0x36 +#define BQ25980_TDIE_ADC_MSB 0x37 +#define BQ25980_TDIE_ADC_LSB 0x38 +#define BQ25980_DEGLITCH_TIME 0x39 +#define BQ25980_CHRGR_CTRL_6 0x3A + +#define BQ25980_BUSOCP_STEP_uA 250000 +#define BQ25980_BUSOCP_OFFSET_uA 1000000 + +#define BQ25980_BUSOCP_DFLT_uA 4250000 +#define BQ25975_BUSOCP_DFLT_uA 4250000 +#define BQ25960_BUSOCP_DFLT_uA 3250000 + +#define BQ25980_BUSOCP_MIN_uA 1000000 + +#define BQ25980_BUSOCP_SC_MAX_uA 5750000 +#define BQ25975_BUSOCP_SC_MAX_uA 5750000 +#define BQ25960_BUSOCP_SC_MAX_uA 3750000 + +#define BQ25980_BUSOCP_BYP_MAX_uA 8500000 +#define BQ25975_BUSOCP_BYP_MAX_uA 8500000 +#define BQ25960_BUSOCP_BYP_MAX_uA 5750000 + +#define BQ25980_BUSOVP_SC_STEP_uV 100000 +#define BQ25975_BUSOVP_SC_STEP_uV 50000 +#define BQ25960_BUSOVP_SC_STEP_uV 50000 +#define BQ25980_BUSOVP_SC_OFFSET_uV 14000000 +#define BQ25975_BUSOVP_SC_OFFSET_uV 7000000 +#define BQ25960_BUSOVP_SC_OFFSET_uV 7000000 + +#define BQ25980_BUSOVP_BYP_STEP_uV 50000 +#define BQ25975_BUSOVP_BYP_STEP_uV 25000 +#define BQ25960_BUSOVP_BYP_STEP_uV 25000 +#define BQ25980_BUSOVP_BYP_OFFSET_uV 7000000 +#define BQ25975_BUSOVP_BYP_OFFSET_uV 3500000 +#define BQ25960_BUSOVP_BYP_OFFSET_uV 3500000 + +#define BQ25980_BUSOVP_DFLT_uV 17800000 +#define BQ25980_BUSOVP_BYPASS_DFLT_uV 8900000 +#define BQ25975_BUSOVP_DFLT_uV 8900000 +#define BQ25975_BUSOVP_BYPASS_DFLT_uV 4450000 +#define BQ25960_BUSOVP_DFLT_uV 8900000 + +#define BQ25980_BUSOVP_SC_MIN_uV 14000000 +#define BQ25975_BUSOVP_SC_MIN_uV 7000000 +#define BQ25960_BUSOVP_SC_MIN_uV 7000000 +#define BQ25980_BUSOVP_BYP_MIN_uV 7000000 +#define BQ25975_BUSOVP_BYP_MIN_uV 3500000 +#define BQ25960_BUSOVP_BYP_MIN_uV 3500000 + +#define BQ25980_BUSOVP_SC_MAX_uV 22000000 +#define BQ25975_BUSOVP_SC_MAX_uV 12750000 +#define BQ25960_BUSOVP_SC_MAX_uV 12750000 + +#define BQ25980_BUSOVP_BYP_MAX_uV 12750000 +#define BQ25975_BUSOVP_BYP_MAX_uV 6500000 +#define BQ25960_BUSOVP_BYP_MAX_uV 6500000 + +#define BQ25980_BATOVP_STEP_uV 20000 +#define BQ25975_BATOVP_STEP_uV 10000 +#define BQ25960_BATOVP_STEP_uV 10000 + +#define BQ25980_BATOVP_OFFSET_uV 7000000 +#define BQ25975_BATOVP_OFFSET_uV 3500000 +#define BQ25960_BATOVP_OFFSET_uV 3500000 + +#define BQ25980_BATOVP_DFLT_uV 14000000 +#define BQ25975_BATOVP_DFLT_uV 8900000 +#define BQ25960_BATOVP_DFLT_uV 8900000 + +#define BQ25980_BATOVP_MIN_uV 7000000 +#define BQ25975_BATOVP_MIN_uV 3500000 +#define BQ25960_BATOVP_MIN_uV 3500000 + +#define BQ25980_BATOVP_MAX_uV 9540000 +#define BQ25975_BATOVP_MAX_uV 4770000 +#define BQ25960_BATOVP_MAX_uV 4770000 + +#define BQ25980_BATOCP_STEP_uA 100000 + +#define BQ25980_BATOCP_MASK GENMASK(6, 0) + +#define BQ25980_BATOCP_DFLT_uA 8100000 +#define BQ25960_BATOCP_DFLT_uA 6100000 + +#define BQ25980_BATOCP_MIN_uA 2000000 + +#define BQ25980_BATOCP_MAX_uA 11000000 +#define BQ25975_BATOCP_MAX_uA 11000000 +#define BQ25960_BATOCP_MAX_uA 7000000 + +#define BQ25980_ENABLE_HIZ 0xff +#define BQ25980_DISABLE_HIZ 0x0 +#define BQ25980_EN_BYPASS BIT(3) +#define BQ25980_STAT1_OVP_MASK (BIT(6) | BIT(5) | BIT(0)) +#define BQ25980_STAT3_OVP_MASK (BIT(7) | BIT(6)) +#define BQ25980_STAT1_OCP_MASK BIT(3) +#define BQ25980_STAT2_OCP_MASK (BIT(6) | BIT(1)) +#define BQ25980_STAT4_TFLT_MASK GENMASK(5, 1) +#define BQ25980_WD_STAT BIT(0) +#define BQ25980_PRESENT_MASK GENMASK(4, 2) +#define BQ25980_CHG_EN BIT(4) +#define BQ25980_EN_HIZ BIT(6) +#define BQ25980_ADC_EN BIT(7) + +#define BQ25980_ADC_VOLT_STEP_uV 1000 +#define BQ25980_ADC_CURR_STEP_uA 1000 +#define BQ25980_ADC_POLARITY_BIT BIT(7) + +#define BQ25980_WATCHDOG_MASK GENMASK(4, 3) +#define BQ25980_WATCHDOG_DIS BIT(2) +#define BQ25980_WATCHDOG_MAX 300000 +#define BQ25980_WATCHDOG_MIN 0 +#define BQ25980_NUM_WD_VAL 4 + +#endif /* BQ25980_CHARGER_H */ -- cgit From 8ae237ec0af9f754c1da00913646f3c46a99a1cb Mon Sep 17 00:00:00 2001 From: Ikjoon Jang <ikjn@chromium.org> Date: Thu, 3 Sep 2020 11:04:40 +0800 Subject: power: supply: sbs-battery: keep error code when get_property() fails Commit 395a7251dc2b (power: supply: sbs-battery: don't assume i2c errors as battery disconnect) overwrites the original error code returned from internal functions. On such a sporadic i2c error, a user will get a wrong value without errors. Fixes: 395a7251dc2b (power: supply: sbs-battery: don't assume i2c errors as battery disconnect) Signed-off-by: Ikjoon Jang <ikjn@chromium.org> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/sbs-battery.c | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index dacc4bc1c013..13192cbcce71 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -962,11 +962,10 @@ static int sbs_get_property(struct power_supply *psy, if (!chip->gpio_detect && chip->is_present != (ret >= 0)) { bool old_present = chip->is_present; union power_supply_propval val; - - ret = sbs_get_battery_presence_and_health( + int err = sbs_get_battery_presence_and_health( client, POWER_SUPPLY_PROP_PRESENT, &val); - sbs_update_presence(chip, !ret && val.intval); + sbs_update_presence(chip, !err && val.intval); if (old_present != chip->is_present) power_supply_changed(chip->power_supply); @@ -976,19 +975,14 @@ done: if (!ret) { /* Convert units to match requirements for power supply class */ sbs_unit_adjustment(client, psp, val); + dev_dbg(&client->dev, + "%s: property = %d, value = %x\n", __func__, + psp, val->intval); + } else if (!chip->is_present) { + /* battery not present, so return NODATA for properties */ + ret = -ENODATA; } - - dev_dbg(&client->dev, - "%s: property = %d, value = %x\n", __func__, psp, val->intval); - - if (ret && chip->is_present) - return ret; - - /* battery not present, so return NODATA for properties */ - if (ret) - return -ENODATA; - - return 0; + return ret; } static void sbs_supply_changed(struct sbs_info *chip) -- cgit From ec871696b77767672cae1fe3158dbe1a86abcfdd Mon Sep 17 00:00:00 2001 From: Lars Povlsen <lars.povlsen@microchip.com> Date: Tue, 6 Oct 2020 22:03:15 +0200 Subject: power: reset: ocelot: Add support for Sparx5 This adds reset support for Sparx5 in the ocelot-reset driver. Signed-off-by: Lars Povlsen <lars.povlsen@microchip.com> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/reset/Kconfig | 3 +-- drivers/power/reset/ocelot-reset.c | 55 +++++++++++++++++++++++++++++--------- 2 files changed, 44 insertions(+), 14 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 0a1fb5c74f83..6361569aacb7 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -129,10 +129,9 @@ config POWER_RESET_QCOM_PON config POWER_RESET_OCELOT_RESET bool "Microsemi Ocelot reset driver" - depends on MSCC_OCELOT || COMPILE_TEST select MFD_SYSCON help - This driver supports restart for Microsemi Ocelot SoC. + This driver supports restart for Microsemi Ocelot SoC and similar. config POWER_RESET_OXNAS bool "OXNAS SoC restart driver" diff --git a/drivers/power/reset/ocelot-reset.c b/drivers/power/reset/ocelot-reset.c index 419952c61fd0..f74e1dbb4ba3 100644 --- a/drivers/power/reset/ocelot-reset.c +++ b/drivers/power/reset/ocelot-reset.c @@ -15,15 +15,20 @@ #include <linux/reboot.h> #include <linux/regmap.h> +struct reset_props { + const char *syscon; + u32 protect_reg; + u32 vcore_protect; + u32 if_si_owner_bit; +}; + struct ocelot_reset_context { void __iomem *base; struct regmap *cpu_ctrl; + const struct reset_props *props; struct notifier_block restart_handler; }; -#define ICPU_CFG_CPU_SYSTEM_CTRL_RESET 0x20 -#define CORE_RST_PROTECT BIT(2) - #define SOFT_CHIP_RST BIT(0) #define ICPU_CFG_CPU_SYSTEM_CTRL_GENERAL_CTRL 0x24 @@ -31,7 +36,6 @@ struct ocelot_reset_context { #define IF_SI_OWNER_SISL 0 #define IF_SI_OWNER_SIBM 1 #define IF_SI_OWNER_SIMC 2 -#define IF_SI_OWNER_OFFSET 4 static int ocelot_restart_handle(struct notifier_block *this, unsigned long mode, void *cmd) @@ -39,15 +43,18 @@ static int ocelot_restart_handle(struct notifier_block *this, struct ocelot_reset_context *ctx = container_of(this, struct ocelot_reset_context, restart_handler); + u32 if_si_owner_bit = ctx->props->if_si_owner_bit; /* Make sure the core is not protected from reset */ - regmap_update_bits(ctx->cpu_ctrl, ICPU_CFG_CPU_SYSTEM_CTRL_RESET, - CORE_RST_PROTECT, 0); + regmap_update_bits(ctx->cpu_ctrl, ctx->props->protect_reg, + ctx->props->vcore_protect, 0); /* Make the SI back to boot mode */ regmap_update_bits(ctx->cpu_ctrl, ICPU_CFG_CPU_SYSTEM_CTRL_GENERAL_CTRL, - IF_SI_OWNER_MASK << IF_SI_OWNER_OFFSET, - IF_SI_OWNER_SIBM << IF_SI_OWNER_OFFSET); + IF_SI_OWNER_MASK << if_si_owner_bit, + IF_SI_OWNER_SIBM << if_si_owner_bit); + + pr_emerg("Resetting SoC\n"); writel(SOFT_CHIP_RST, ctx->base); @@ -72,9 +79,13 @@ static int ocelot_reset_probe(struct platform_device *pdev) if (IS_ERR(ctx->base)) return PTR_ERR(ctx->base); - ctx->cpu_ctrl = syscon_regmap_lookup_by_compatible("mscc,ocelot-cpu-syscon"); - if (IS_ERR(ctx->cpu_ctrl)) + ctx->props = device_get_match_data(dev); + + ctx->cpu_ctrl = syscon_regmap_lookup_by_compatible(ctx->props->syscon); + if (IS_ERR(ctx->cpu_ctrl)) { + dev_err(dev, "No syscon map: %s\n", ctx->props->syscon); return PTR_ERR(ctx->cpu_ctrl); + } ctx->restart_handler.notifier_call = ocelot_restart_handle; ctx->restart_handler.priority = 192; @@ -85,9 +96,29 @@ static int ocelot_reset_probe(struct platform_device *pdev) return err; } +static const struct reset_props reset_props_ocelot = { + .syscon = "mscc,ocelot-cpu-syscon", + .protect_reg = 0x20, + .vcore_protect = BIT(2), + .if_si_owner_bit = 4, +}; + +static const struct reset_props reset_props_sparx5 = { + .syscon = "microchip,sparx5-cpu-syscon", + .protect_reg = 0x84, + .vcore_protect = BIT(10), + .if_si_owner_bit = 6, +}; + static const struct of_device_id ocelot_reset_of_match[] = { - { .compatible = "mscc,ocelot-chip-reset" }, - {} + { + .compatible = "mscc,ocelot-chip-reset", + .data = &reset_props_ocelot + }, { + .compatible = "microchip,sparx5-chip-reset", + .data = &reset_props_sparx5 + }, + { /*sentinel*/ } }; static struct platform_driver ocelot_reset_driver = { -- cgit From 4b464bad37c65492f03fe6cc15bdb6a9a6a5f5e7 Mon Sep 17 00:00:00 2001 From: Colin Ian King <colin.king@canonical.com> Date: Tue, 6 Oct 2020 18:06:00 +0100 Subject: power: supply: bq25980: remove redundant zero check on ret Currently ret is assigned to zero and the following statement checks if it is non-zero. This check is redundant and can be removed Addresses-Coverity: ("Logically dead code") Fixes: 5069185fc18e ("power: supply: bq25980: Add support for the BQ259xx family") Signed-off-by: Colin Ian King <colin.king@canonical.com> Acked-by: Dan Murphy <dmurphy@ti.com> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/bq25980_charger.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/bq25980_charger.c b/drivers/power/supply/bq25980_charger.c index 3995fb7cf060..f04f9acdb13b 100644 --- a/drivers/power/supply/bq25980_charger.c +++ b/drivers/power/supply/bq25980_charger.c @@ -613,9 +613,6 @@ static int bq25980_set_battery_property(struct power_supply *psy, struct bq25980_device *bq = power_supply_get_drvdata(psy); int ret = 0; - if (ret) - return ret; - switch (psp) { case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: ret = bq25980_set_const_charge_curr(bq, val->intval); -- cgit From d4fbca833b72cb58297df35a32cce77cd6409707 Mon Sep 17 00:00:00 2001 From: Dan Murphy <dmurphy@ti.com> Date: Tue, 6 Oct 2020 12:30:07 -0500 Subject: power: supply: bq25980: Fix uninitialized wd_reg_val Fix the uninitialized wd_reg_val if the for..loop was not successful in finding an appropriate match. Fixes: 5069185fc18e ("power: supply: bq25980: Add support for the BQ259xx family") Reported-by: kernel test robot <lkp@intel.com> Signed-off-by: Dan Murphy <dmurphy@ti.com> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/bq25980_charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/bq25980_charger.c b/drivers/power/supply/bq25980_charger.c index f04f9acdb13b..db69ec412a67 100644 --- a/drivers/power/supply/bq25980_charger.c +++ b/drivers/power/supply/bq25980_charger.c @@ -1096,7 +1096,7 @@ static int bq25980_power_supply_init(struct bq25980_device *bq, static int bq25980_hw_init(struct bq25980_device *bq) { struct power_supply_battery_info bat_info = { }; - int wd_reg_val; + int wd_reg_val = 0; int ret = 0; int curr_val; int volt_val; -- cgit From 7f977e64370dad07b01601bb2ba959678a149ff3 Mon Sep 17 00:00:00 2001 From: Iskren Chernev <iskren.chernev@gmail.com> Date: Mon, 5 Oct 2020 23:00:38 +0300 Subject: power: supply: max17040: Fix ptr to enum cast clang complains about casting pointers to smaller enum types. Reported-by: kernel test robot <lkp@intel.com> Signed-off-by: Iskren Chernev <iskren.chernev@gmail.com> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/max17040_battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c index 1d7510a59295..d956c67d5155 100644 --- a/drivers/power/supply/max17040_battery.c +++ b/drivers/power/supply/max17040_battery.c @@ -247,7 +247,7 @@ static int max17040_get_of_data(struct max17040_chip *chip) { struct device *dev = &chip->client->dev; struct chip_data *data = &max17040_family[ - (enum chip_id) of_device_get_match_data(dev)]; + (uintptr_t) of_device_get_match_data(dev)]; int rcomp_len; u8 rcomp[2]; -- cgit From 10a4357f4aebb30d20fe5f0297930b292862fed9 Mon Sep 17 00:00:00 2001 From: Colin Ian King <colin.king@canonical.com> Date: Wed, 2 Sep 2020 14:31:17 +0100 Subject: power: supply: charger-manager: fix incorrect check on charging_duration_ms Currently the duration check on the discharging duration setting is checking the charging duration rather than the discharging duration due to a cut-n-paste coding error. Fix this by checking the value desc->charging_max_duration_ms. Addresses-Coverity: ("Copy-paste-error") Fixes: 8fcfe088e21a ("charger-manager: Support limit of maximum possible") Signed-off-by: Colin Ian King <colin.king@canonical.com> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/charger-manager.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index b2ca79173f95..6fcebe441552 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -471,7 +471,7 @@ static int check_charging_duration(struct charger_manager *cm) } else if (cm->battery_status == POWER_SUPPLY_STATUS_NOT_CHARGING) { duration = curr - cm->charging_end_time; - if (duration > desc->charging_max_duration_ms) { + if (duration > desc->discharging_max_duration_ms) { dev_info(cm->dev, "Discharging duration exceed %ums\n", desc->discharging_max_duration_ms); ret = true; -- cgit From 411643e949f4e616f758e2c6079f333b0e704c49 Mon Sep 17 00:00:00 2001 From: "Harley A.W. Lorenzo" <hl1998@protonmail.com> Date: Mon, 5 Oct 2020 04:44:17 +0000 Subject: power: supply: test-power: revise parameter printing to use sprintf Simplify code by using sprintf instead of strcpy+strcat+strlen. Signed-off-by: Harley A.W. Lorenzo <hl1998@protonmail.com> Suggested-by: Joe Perches <joe@perches.com> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/test_power.c | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/test_power.c b/drivers/power/supply/test_power.c index 4895ee5e63a9..5f510ddc946d 100644 --- a/drivers/power/supply/test_power.c +++ b/drivers/power/supply/test_power.c @@ -352,9 +352,8 @@ static int param_set_ac_online(const char *key, const struct kernel_param *kp) static int param_get_ac_online(char *buffer, const struct kernel_param *kp) { - strcpy(buffer, map_get_key(map_ac_online, ac_online, "unknown")); - strcat(buffer, "\n"); - return strlen(buffer); + return sprintf(buffer, "%s\n", + map_get_key(map_ac_online, ac_online, "unknown")); } static int param_set_usb_online(const char *key, const struct kernel_param *kp) @@ -366,9 +365,8 @@ static int param_set_usb_online(const char *key, const struct kernel_param *kp) static int param_get_usb_online(char *buffer, const struct kernel_param *kp) { - strcpy(buffer, map_get_key(map_ac_online, usb_online, "unknown")); - strcat(buffer, "\n"); - return strlen(buffer); + return sprintf(buffer, "%s\n", + map_get_key(map_ac_online, usb_online, "unknown")); } static int param_set_battery_status(const char *key, @@ -381,9 +379,8 @@ static int param_set_battery_status(const char *key, static int param_get_battery_status(char *buffer, const struct kernel_param *kp) { - strcpy(buffer, map_get_key(map_status, battery_status, "unknown")); - strcat(buffer, "\n"); - return strlen(buffer); + return sprintf(buffer, "%s\n", + map_get_key(map_ac_online, battery_status, "unknown")); } static int param_set_battery_health(const char *key, @@ -396,9 +393,8 @@ static int param_set_battery_health(const char *key, static int param_get_battery_health(char *buffer, const struct kernel_param *kp) { - strcpy(buffer, map_get_key(map_health, battery_health, "unknown")); - strcat(buffer, "\n"); - return strlen(buffer); + return sprintf(buffer, "%s\n", + map_get_key(map_ac_online, battery_health, "unknown")); } static int param_set_battery_present(const char *key, @@ -412,9 +408,8 @@ static int param_set_battery_present(const char *key, static int param_get_battery_present(char *buffer, const struct kernel_param *kp) { - strcpy(buffer, map_get_key(map_present, battery_present, "unknown")); - strcat(buffer, "\n"); - return strlen(buffer); + return sprintf(buffer, "%s\n", + map_get_key(map_ac_online, battery_present, "unknown")); } static int param_set_battery_technology(const char *key, @@ -429,10 +424,9 @@ static int param_set_battery_technology(const char *key, static int param_get_battery_technology(char *buffer, const struct kernel_param *kp) { - strcpy(buffer, - map_get_key(map_technology, battery_technology, "unknown")); - strcat(buffer, "\n"); - return strlen(buffer); + return sprintf(buffer, "%s\n", + map_get_key(map_ac_online, battery_technology, + "unknown")); } static int param_set_battery_capacity(const char *key, -- cgit From 58d1620c4ded303b9d94fc68b23e5af1ec507de6 Mon Sep 17 00:00:00 2001 From: Iskren Chernev <iskren.chernev@gmail.com> Date: Sat, 10 Oct 2020 09:55:26 +0300 Subject: power: supply: ltc2941: Fix ptr to enum cast clang complains about casting pointers to smaller enum types. Signed-off-by: Iskren Chernev <iskren.chernev@gmail.com> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/ltc2941-battery-gauge.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/ltc2941-battery-gauge.c b/drivers/power/supply/ltc2941-battery-gauge.c index 30a9014b2f95..10cd617516ec 100644 --- a/drivers/power/supply/ltc2941-battery-gauge.c +++ b/drivers/power/supply/ltc2941-battery-gauge.c @@ -473,7 +473,8 @@ static int ltc294x_i2c_probe(struct i2c_client *client, np = of_node_get(client->dev.of_node); - info->id = (enum ltc294x_id)of_device_get_match_data(&client->dev); + info->id = (enum ltc294x_id) (uintptr_t) of_device_get_match_data( + &client->dev); info->supply_desc.name = np->name; /* r_sense can be negative, when sense+ is connected to the battery -- cgit From 6c59a17b0d59dbffa62d0b3b8e648ccad02ea82f Mon Sep 17 00:00:00 2001 From: Dan Murphy <dmurphy@ti.com> Date: Fri, 9 Oct 2020 07:12:05 -0500 Subject: power: supply: bq25980: Fix uninitialized wd_reg_val and overrun Fix the issue when 'i' is equal to array size then array index over runs the array when checking for the watch dog value. Fixes: 5069185fc18e ("power: supply: bq25980: Add support for the BQ259xx family") Signed-off-by: Dan Murphy <dmurphy@ti.com> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/supply/bq25980_charger.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/supply/bq25980_charger.c b/drivers/power/supply/bq25980_charger.c index db69ec412a67..c936f311eb4f 100644 --- a/drivers/power/supply/bq25980_charger.c +++ b/drivers/power/supply/bq25980_charger.c @@ -1096,28 +1096,29 @@ static int bq25980_power_supply_init(struct bq25980_device *bq, static int bq25980_hw_init(struct bq25980_device *bq) { struct power_supply_battery_info bat_info = { }; - int wd_reg_val = 0; + int wd_reg_val = BQ25980_WATCHDOG_DIS; + int wd_max_val = BQ25980_NUM_WD_VAL - 1; int ret = 0; int curr_val; int volt_val; int i; - if (!bq->watchdog_timer) { - ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_3, - BQ25980_WATCHDOG_DIS, - BQ25980_WATCHDOG_DIS); - } else { - for (i = 0; i < BQ25980_NUM_WD_VAL; i++) { - if (bq->watchdog_timer > bq25980_watchdog_time[i] && - bq->watchdog_timer < bq25980_watchdog_time[i + 1]) { - wd_reg_val = i; - break; + if (bq->watchdog_timer) { + if (bq->watchdog_timer >= bq25980_watchdog_time[wd_max_val]) + wd_reg_val = wd_max_val; + else { + for (i = 0; i < wd_max_val; i++) { + if (bq->watchdog_timer > bq25980_watchdog_time[i] && + bq->watchdog_timer < bq25980_watchdog_time[i + 1]) { + wd_reg_val = i; + break; + } } } - - ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_3, - BQ25980_WATCHDOG_MASK, wd_reg_val); } + + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_3, + BQ25980_WATCHDOG_MASK, wd_reg_val); if (ret) return ret; -- cgit From 7007fab4ae82c092cb52691c338f1b776005e32b Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven <geert+renesas@glider.be> Date: Wed, 14 Oct 2020 15:14:15 +0200 Subject: power: reset: POWER_RESET_OCELOT_RESET should depend on Ocelot or Sparx5 To add support for Sparx5, the dependency on MSCC_OCELOT was removed. However, this increases exposure of the driver question not only to Sparx5 platforms, but to everyone. Hence re-add the dependency on MSCC_OCELOT, and extend it with ARCH_SPARX5, to prevent asking the user about this driver when configuring a kernel without Ocelot and Sparx5 support. Fixes: ec871696b7776767 ("power: reset: ocelot: Add support for Sparx5") Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> --- drivers/power/reset/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/power') diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 6361569aacb7..d55b3727e00e 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -129,6 +129,7 @@ config POWER_RESET_QCOM_PON config POWER_RESET_OCELOT_RESET bool "Microsemi Ocelot reset driver" + depends on MSCC_OCELOT || ARCH_SPARX5 || COMPILE_TEST select MFD_SYSCON help This driver supports restart for Microsemi Ocelot SoC and similar. -- cgit