From b0d0d3de246b1eda0b3050745848e78bbb15626b Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Wed, 30 Oct 2019 09:17:26 +1300 Subject: power: reset: gpio-restart: don't error on deferral Don't generate an error message when devm_gpiod_get fails with -EPROBE_DEFER. Signed-off-by: Chris Packham Signed-off-by: Sebastian Reichel --- drivers/power/reset/gpio-restart.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/power/reset/gpio-restart.c b/drivers/power/reset/gpio-restart.c index 308ca9d9d276..5466eeea261c 100644 --- a/drivers/power/reset/gpio-restart.c +++ b/drivers/power/reset/gpio-restart.c @@ -64,9 +64,11 @@ static int gpio_restart_probe(struct platform_device *pdev) gpio_restart->reset_gpio = devm_gpiod_get(&pdev->dev, NULL, open_source ? GPIOD_IN : GPIOD_OUT_LOW); - if (IS_ERR(gpio_restart->reset_gpio)) { - dev_err(&pdev->dev, "Could not get reset GPIO\n"); - return PTR_ERR(gpio_restart->reset_gpio); + ret = PTR_ERR_OR_ZERO(gpio_restart->reset_gpio); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "Could not get reset GPIO\n"); + return ret; } gpio_restart->restart_handler.notifier_call = gpio_restart_notify; -- cgit v1.2.3-58-ga151 From dd04defd11b78a73b302587575bbcda1019925d8 Mon Sep 17 00:00:00 2001 From: Chuhong Yuan Date: Fri, 15 Nov 2019 14:25:15 +0800 Subject: power: supply: pda_power: add missed usb_unregister_notifier The driver forgets to unregister the notifier in remove. Add the call to fix it. Signed-off-by: Chuhong Yuan Signed-off-by: Sebastian Reichel --- drivers/power/supply/pda_power.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/power/supply/pda_power.c b/drivers/power/supply/pda_power.c index 3ae5707d39fa..03a37fd6be27 100644 --- a/drivers/power/supply/pda_power.c +++ b/drivers/power/supply/pda_power.c @@ -429,6 +429,10 @@ wrongid: static int pda_power_remove(struct platform_device *pdev) { +#if IS_ENABLED(CONFIG_USB_PHY) + if (!IS_ERR_OR_NULL(transceiver) && pdata->use_otg_notifier) + usb_unregister_notifier(transceiver, &otg_nb); +#endif if (pdata->is_usb_online && usb_irq) free_irq(usb_irq->start, pda_psy_usb); if (pdata->is_ac_online && ac_irq) -- cgit v1.2.3-58-ga151 From 86b9182df8bb12610d4d6feac45a69f3ed57bfd2 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Sat, 16 Nov 2019 14:56:19 +0100 Subject: power/supply: ingenic-battery: Don't change scale if there's only one The ADC in the JZ4740 can work either in high-precision mode with a 2.5V range, or in low-precision mode with a 7.5V range. The code in place in this driver will select the proper scale according to the maximum voltage of the battery. The JZ4770 however only has one mode, with a 6.6V range. If only one scale is available, there's no need to change it (and nothing to change it to), and trying to do so will fail with -EINVAL. Fixes: fb24ccfbe1e0 ("power: supply: add Ingenic JZ47xx battery driver.") Signed-off-by: Paul Cercueil Acked-by: Artur Rojek Cc: stable@vger.kernel.org Signed-off-by: Sebastian Reichel --- drivers/power/supply/ingenic-battery.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/power/supply/ingenic-battery.c b/drivers/power/supply/ingenic-battery.c index 35816d4b3012..2748715c4c75 100644 --- a/drivers/power/supply/ingenic-battery.c +++ b/drivers/power/supply/ingenic-battery.c @@ -100,10 +100,17 @@ static int ingenic_battery_set_scale(struct ingenic_battery *bat) return -EINVAL; } - return iio_write_channel_attribute(bat->channel, - scale_raw[best_idx], - scale_raw[best_idx + 1], - IIO_CHAN_INFO_SCALE); + /* Only set scale if there is more than one (fractional) entry */ + if (scale_len > 2) { + ret = iio_write_channel_attribute(bat->channel, + scale_raw[best_idx], + scale_raw[best_idx + 1], + IIO_CHAN_INFO_SCALE); + if (ret) + return ret; + } + + return 0; } static enum power_supply_property ingenic_battery_properties[] = { -- cgit v1.2.3-58-ga151 From 3c9c2d08128a510f67c5443387af5e2176407596 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 20 Nov 2019 21:39:59 +0800 Subject: power: supply: Fix Kconfig indentation Adjust indentation from spaces to tab (+optional two spaces) as in coding style with command like: $ sed -e 's/^ /\t/' -i */Kconfig Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/supply/Kconfig | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 27164a1d3c7c..9a5591ab90d0 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -73,10 +73,10 @@ config WM831X_POWER provided by Wolfson Microelectronics WM831x PMICs. config WM8350_POWER - tristate "WM8350 PMU support" - depends on MFD_WM8350 - help - Say Y here to enable support for the power management unit + tristate "WM8350 PMU support" + depends on MFD_WM8350 + help + Say Y here to enable support for the power management unit provided by the Wolfson Microelectronics WM8350 PMIC. config TEST_POWER @@ -209,16 +209,16 @@ config BATTERY_WM97XX Say Y to enable support for battery measured by WM97xx aux port. config BATTERY_SBS - tristate "SBS Compliant gas gauge" - depends on I2C - help + tristate "SBS Compliant gas gauge" + depends on I2C + help Say Y to include support for SBS battery driver for SBS-compliant gas gauges. config CHARGER_SBS - tristate "SBS Compliant charger" - depends on I2C - help + tristate "SBS Compliant charger" + depends on I2C + help Say Y to include support for SBS compliant battery chargers. config MANAGER_SBS @@ -484,11 +484,11 @@ config CHARGER_MANAGER depends on REGULATOR select EXTCON help - Say Y to enable charger-manager support, which allows multiple - chargers attached to a battery and multiple batteries attached to a - system. The charger-manager also can monitor charging status in - runtime and in suspend-to-RAM by waking up the system periodically - with help of suspend_again support. + Say Y to enable charger-manager support, which allows multiple + chargers attached to a battery and multiple batteries attached to a + system. The charger-manager also can monitor charging status in + runtime and in suspend-to-RAM by waking up the system periodically + with help of suspend_again support. config CHARGER_LT3651 tristate "Analog Devices LT3651 charger" -- cgit v1.2.3-58-ga151 From a3d70dacc727ada212aecb8d131a3910622763d6 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Tue, 19 Nov 2019 18:00:04 +0100 Subject: power: suppy: ucs1002: disable power when max current is 0 For some devices userspace needs the ability to completely cut the power to the USB devices connected to the charge controller. An easy way to achieve this is by allowing 0 as a valid max current and forcibly disable the output in that case, as well as enable it again if the regulator is in use and a non-0 max current is set. Signed-off-by: Lucas Stach Tested-by: Chris Healy Signed-off-by: Sebastian Reichel --- drivers/power/supply/ucs1002_power.c | 42 ++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/power/supply/ucs1002_power.c b/drivers/power/supply/ucs1002_power.c index 1b80ae479e7d..0ca80d00b80a 100644 --- a/drivers/power/supply/ucs1002_power.c +++ b/drivers/power/supply/ucs1002_power.c @@ -100,7 +100,9 @@ struct ucs1002_info { struct i2c_client *client; struct regmap *regmap; struct regulator_desc *regulator_descriptor; + struct regulator_dev *rdev; bool present; + bool output_disable; }; static enum power_supply_property ucs1002_props[] = { @@ -233,6 +235,11 @@ static int ucs1002_get_max_current(struct ucs1002_info *info, unsigned int reg; int ret; + if (info->output_disable) { + val->intval = 0; + return 0; + } + ret = regmap_read(info->regmap, UCS1002_REG_ILIMIT, ®); if (ret) return ret; @@ -247,6 +254,12 @@ static int ucs1002_set_max_current(struct ucs1002_info *info, u32 val) unsigned int reg; int ret, idx; + if (val == 0) { + info->output_disable = true; + regulator_disable_regmap(info->rdev); + return 0; + } + for (idx = 0; idx < ARRAY_SIZE(ucs1002_current_limit_uA); idx++) { if (val == ucs1002_current_limit_uA[idx]) break; @@ -270,6 +283,12 @@ static int ucs1002_set_max_current(struct ucs1002_info *info, u32 val) if (reg != idx) return -EINVAL; + info->output_disable = false; + + if (info->rdev && info->rdev->use_count && + !regulator_is_enabled_regmap(info->rdev)) + regulator_enable_regmap(info->rdev); + return 0; } @@ -470,9 +489,24 @@ static irqreturn_t ucs1002_alert_irq(int irq, void *data) return IRQ_HANDLED; } +int ucs1002_regulator_enable(struct regulator_dev *rdev) +{ + struct ucs1002_info *info = rdev_get_drvdata(rdev); + + /* + * If the output is disabled due to 0 maximum current, just pretend the + * enable did work. The regulator will be enabled as soon as we get a + * a non-zero maximum current budget. + */ + if (info->output_disable) + return 0; + + return regulator_enable_regmap(rdev); +} + static const struct regulator_ops ucs1002_regulator_ops = { .is_enabled = regulator_is_enabled_regmap, - .enable = regulator_enable_regmap, + .enable = ucs1002_regulator_enable, .disable = regulator_disable_regmap, }; @@ -499,7 +533,6 @@ static int ucs1002_probe(struct i2c_client *client, }; struct regulator_config regulator_config = {}; int irq_a_det, irq_alert, ret; - struct regulator_dev *rdev; struct ucs1002_info *info; unsigned int regval; @@ -589,10 +622,11 @@ static int ucs1002_probe(struct i2c_client *client, regulator_config.dev = dev; regulator_config.of_node = dev->of_node; regulator_config.regmap = info->regmap; + regulator_config.driver_data = info; - rdev = devm_regulator_register(dev, info->regulator_descriptor, + info->rdev = devm_regulator_register(dev, info->regulator_descriptor, ®ulator_config); - ret = PTR_ERR_OR_ZERO(rdev); + ret = PTR_ERR_OR_ZERO(info->rdev); if (ret) { dev_err(dev, "Failed to register VBUS regulator: %d\n", ret); return ret; -- cgit v1.2.3-58-ga151 From bc90705bbb570b2507353ba10d6b6788cfb119b1 Mon Sep 17 00:00:00 2001 From: "Angus Ainslie (Purism)" Date: Sat, 14 Dec 2019 07:27:54 -0800 Subject: power: supply: max17042: add MAX17055 support The MAX17055 is very similar to the MAX17042 so extend the driver. Signed-off-by: Angus Ainslie (Purism) Reviewed-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17042_battery.c | 17 +++++++++--- include/linux/power/max17042_battery.h | 48 ++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 0dfad2cf13fe..69ec4295d55d 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -282,6 +282,8 @@ static int max17042_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) ret = regmap_read(map, MAX17042_V_empty, &data); + else if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055) + ret = regmap_read(map, MAX17055_V_empty, &data); else ret = regmap_read(map, MAX17047_V_empty, &data); if (ret < 0) @@ -627,7 +629,8 @@ static void max17042_write_config_regs(struct max17042_chip *chip) config->filter_cfg); regmap_write(map, MAX17042_RelaxCFG, config->relax_cfg); if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047 || - chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050) + chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050 || + chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055) regmap_write(map, MAX17047_FullSOCThr, config->full_soc_thresh); } @@ -758,6 +761,8 @@ static inline void max17042_override_por_values(struct max17042_chip *chip) if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) max17042_override_por(map, MAX17042_V_empty, config->vempty); + if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055) + max17042_override_por(map, MAX17055_V_empty, config->vempty); else max17042_override_por(map, MAX17047_V_empty, config->vempty); max17042_override_por(map, MAX17042_TempNom, config->temp_nom); @@ -765,7 +770,10 @@ static inline void max17042_override_por_values(struct max17042_chip *chip) max17042_override_por(map, MAX17042_FCTC, config->fctc); max17042_override_por(map, MAX17042_RCOMP0, config->rcomp0); max17042_override_por(map, MAX17042_TempCo, config->tcompc0); - if (chip->chip_type) { + if (chip->chip_type && + ((chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) || + (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047) || + (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050))) { max17042_override_por(map, MAX17042_EmptyTempCo, config->empty_tempco); max17042_override_por(map, MAX17042_K_empty0, @@ -929,7 +937,8 @@ max17042_get_default_pdata(struct max17042_chip *chip) if (!pdata) return pdata; - if (chip->chip_type != MAXIM_DEVICE_TYPE_MAX17042) { + if ((chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047) || + (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050)) { pdata->init_data = max17047_default_pdata_init_regs; pdata->num_init_data = ARRAY_SIZE(max17047_default_pdata_init_regs); @@ -1167,6 +1176,7 @@ static const struct of_device_id max17042_dt_match[] = { { .compatible = "maxim,max17042" }, { .compatible = "maxim,max17047" }, { .compatible = "maxim,max17050" }, + { .compatible = "maxim,max17055" }, { }, }; MODULE_DEVICE_TABLE(of, max17042_dt_match); @@ -1176,6 +1186,7 @@ static const struct i2c_device_id max17042_id[] = { { "max17042", MAXIM_DEVICE_TYPE_MAX17042 }, { "max17047", MAXIM_DEVICE_TYPE_MAX17047 }, { "max17050", MAXIM_DEVICE_TYPE_MAX17050 }, + { "max17055", MAXIM_DEVICE_TYPE_MAX17055 }, { } }; MODULE_DEVICE_TABLE(i2c, max17042_id); diff --git a/include/linux/power/max17042_battery.h b/include/linux/power/max17042_battery.h index 4badd5322949..d55c746ac56e 100644 --- a/include/linux/power/max17042_battery.h +++ b/include/linux/power/max17042_battery.h @@ -105,11 +105,56 @@ enum max17042_register { MAX17042_OCV = 0xEE, - MAX17042_OCVInternal = 0xFB, + MAX17042_OCVInternal = 0xFB, /* MAX17055 VFOCV */ MAX17042_VFSOC = 0xFF, }; +enum max17055_register { + MAX17055_QRes = 0x0C, + MAX17055_TTF = 0x20, + MAX17055_V_empty = 0x3A, + MAX17055_TIMER = 0x3E, + MAX17055_USER_MEM = 0x40, + MAX17055_RGAIN = 0x42, + + MAX17055_ConvgCfg = 0x49, + MAX17055_VFRemCap = 0x4A, + + MAX17055_STATUS2 = 0xB0, + MAX17055_POWER = 0xB1, + MAX17055_ID = 0xB2, + MAX17055_AvgPower = 0xB3, + MAX17055_IAlrtTh = 0xB4, + MAX17055_TTFCfg = 0xB5, + MAX17055_CVMixCap = 0xB6, + MAX17055_CVHalfTime = 0xB7, + MAX17055_CGTempCo = 0xB8, + MAX17055_Curve = 0xB9, + MAX17055_HibCfg = 0xBA, + MAX17055_Config2 = 0xBB, + MAX17055_VRipple = 0xBC, + MAX17055_RippleCfg = 0xBD, + MAX17055_TimerH = 0xBE, + + MAX17055_RSense = 0xD0, + MAX17055_ScOcvLim = 0xD1, + + MAX17055_SOCHold = 0xD3, + MAX17055_MaxPeakPwr = 0xD4, + MAX17055_SusPeakPwr = 0xD5, + MAX17055_PackResistance = 0xD6, + MAX17055_SysResistance = 0xD7, + MAX17055_MinSysV = 0xD8, + MAX17055_MPPCurrent = 0xD9, + MAX17055_SPPCurrent = 0xDA, + MAX17055_ModelCfg = 0xDB, + MAX17055_AtQResidual = 0xDC, + MAX17055_AtTTE = 0xDD, + MAX17055_AtAvSOC = 0xDE, + MAX17055_AtAvCap = 0xDF, +}; + /* Registers specific to max17047/50 */ enum max17047_register { MAX17047_QRTbl00 = 0x12, @@ -125,6 +170,7 @@ enum max170xx_chip_type { MAXIM_DEVICE_TYPE_MAX17042, MAXIM_DEVICE_TYPE_MAX17047, MAXIM_DEVICE_TYPE_MAX17050, + MAXIM_DEVICE_TYPE_MAX17055, MAXIM_DEVICE_TYPE_NUM }; -- cgit v1.2.3-58-ga151 From 5de1780181e7c36d49266a6d35fc4e9f376b2b87 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Dec 2019 11:08:55 +0100 Subject: power: supply: max77650: add of_match table We need the of_match table if we want to use the compatible string in the pmic's child node and get the charger driver loaded automatically. Signed-off-by: Bartosz Golaszewski Signed-off-by: Sebastian Reichel --- drivers/power/supply/max77650-charger.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/power/supply/max77650-charger.c b/drivers/power/supply/max77650-charger.c index 5f9477c5cf5a..d913428bedc0 100644 --- a/drivers/power/supply/max77650-charger.c +++ b/drivers/power/supply/max77650-charger.c @@ -354,9 +354,16 @@ static int max77650_charger_remove(struct platform_device *pdev) return max77650_charger_disable(chg); } +static const struct of_device_id max77650_charger_of_match[] = { + { .compatible = "maxim,max77650-charger" }, + { } +}; +MODULE_DEVICE_TABLE(of, max77650_charger_of_match); + static struct platform_driver max77650_charger_driver = { .driver = { .name = "max77650-charger", + .of_match_table = max77650_charger_of_match, }, .probe = max77650_charger_probe, .remove = max77650_charger_remove, -- cgit v1.2.3-58-ga151 From 65dbad713d5d6a8581921804ae3f5eb4a9bf032e Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Mon, 9 Dec 2019 11:56:22 +0800 Subject: power: supply: core: Add battery internal resistance temperature table support Since the battery internal resistance can be changed with the temperature changes, thus add a resistance temperature table support to look up the accurate battery internal resistance in a certain temperature. Signed-off-by: Baolin Wang Signed-off-by: Baolin Wang Signed-off-by: Sebastian Reichel --- drivers/power/supply/power_supply_core.c | 67 +++++++++++++++++++++++++++++++- include/linux/power_supply.h | 10 +++++ 2 files changed, 76 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index 5c36c430ce8b..1a9a9fae73d3 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -565,9 +565,11 @@ EXPORT_SYMBOL_GPL(devm_power_supply_get_by_phandle); int power_supply_get_battery_info(struct power_supply *psy, struct power_supply_battery_info *info) { + struct power_supply_resistance_temp_table *resist_table; struct device_node *battery_np; const char *value; int err, len, index; + const __be32 *list; info->energy_full_design_uwh = -EINVAL; info->charge_full_design_uah = -EINVAL; @@ -578,6 +580,7 @@ int power_supply_get_battery_info(struct power_supply *psy, info->constant_charge_current_max_ua = -EINVAL; info->constant_charge_voltage_max_uv = -EINVAL; info->factory_internal_resistance_uohm = -EINVAL; + info->resist_table = NULL; for (index = 0; index < POWER_SUPPLY_OCV_TEMP_MAX; index++) { info->ocv_table[index] = NULL; @@ -644,7 +647,6 @@ int power_supply_get_battery_info(struct power_supply *psy, for (index = 0; index < len; index++) { struct power_supply_battery_ocv_table *table; char *propname; - const __be32 *list; int i, tab_len, size; propname = kasprintf(GFP_KERNEL, "ocv-capacity-table-%d", index); @@ -677,6 +679,26 @@ int power_supply_get_battery_info(struct power_supply *psy, } } + list = of_get_property(battery_np, "resistance-temp-table", &len); + if (!list || !len) + goto out_put_node; + + info->resist_table_size = len / (2 * sizeof(__be32)); + resist_table = info->resist_table = devm_kcalloc(&psy->dev, + info->resist_table_size, + sizeof(*resist_table), + GFP_KERNEL); + if (!info->resist_table) { + power_supply_put_battery_info(psy, info); + err = -ENOMEM; + goto out_put_node; + } + + for (index = 0; index < info->resist_table_size; index++) { + resist_table[index].temp = be32_to_cpu(*list++); + resist_table[index].resistance = be32_to_cpu(*list++); + } + out_put_node: of_node_put(battery_np); return err; @@ -692,9 +714,52 @@ void power_supply_put_battery_info(struct power_supply *psy, if (info->ocv_table[i]) devm_kfree(&psy->dev, info->ocv_table[i]); } + + if (info->resist_table) + devm_kfree(&psy->dev, info->resist_table); } EXPORT_SYMBOL_GPL(power_supply_put_battery_info); +/** + * power_supply_temp2resist_simple() - find the battery internal resistance + * percent + * @table: Pointer to battery resistance temperature table + * @table_len: The table length + * @ocv: Current temperature + * + * This helper function is used to look up battery internal resistance percent + * according to current temperature value from the resistance temperature table, + * and the table must be ordered descending. Then the actual battery internal + * resistance = the ideal battery internal resistance * percent / 100. + * + * Return: the battery internal resistance percent + */ +int power_supply_temp2resist_simple(struct power_supply_resistance_temp_table *table, + int table_len, int temp) +{ + int i, resist; + + for (i = 0; i < table_len; i++) + if (temp > table[i].temp) + break; + + if (i > 0 && i < table_len) { + int tmp; + + tmp = (table[i - 1].resistance - table[i].resistance) * + (temp - table[i].temp); + tmp /= table[i - 1].temp - table[i].temp; + resist = tmp + table[i].resistance; + } else if (i == 0) { + resist = table[0].resistance; + } else { + resist = table[table_len - 1].resistance; + } + + return resist; +} +EXPORT_SYMBOL_GPL(power_supply_temp2resist_simple); + /** * power_supply_ocv2cap_simple() - find the battery capacity * @table: Pointer to battery OCV lookup table diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 28413f737e7d..dcd5a71e6c67 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -325,6 +325,11 @@ struct power_supply_battery_ocv_table { int capacity; /* percent */ }; +struct power_supply_resistance_temp_table { + int temp; /* celsius */ + int resistance; /* internal resistance percent */ +}; + #define POWER_SUPPLY_OCV_TEMP_MAX 20 /* @@ -349,6 +354,8 @@ struct power_supply_battery_info { int ocv_temp[POWER_SUPPLY_OCV_TEMP_MAX];/* celsius */ struct power_supply_battery_ocv_table *ocv_table[POWER_SUPPLY_OCV_TEMP_MAX]; int ocv_table_size[POWER_SUPPLY_OCV_TEMP_MAX]; + struct power_supply_resistance_temp_table *resist_table; + int resist_table_size; }; extern struct atomic_notifier_head power_supply_notifier; @@ -381,6 +388,9 @@ power_supply_find_ocv2cap_table(struct power_supply_battery_info *info, int temp, int *table_len); extern int power_supply_batinfo_ocv2cap(struct power_supply_battery_info *info, int ocv, int temp); +extern int +power_supply_temp2resist_simple(struct power_supply_resistance_temp_table *table, + int table_len, int temp); extern void power_supply_changed(struct power_supply *psy); extern int power_supply_am_i_supplied(struct power_supply *psy); extern int power_supply_set_input_current_limit_from_supplier( -- cgit v1.2.3-58-ga151 From 6af8288834b6a9dfcbe9847b675ff24d39d30679 Mon Sep 17 00:00:00 2001 From: Yuanjiang Yu Date: Mon, 9 Dec 2019 11:56:23 +0800 Subject: power: supply: sc27xx: Optimize the battery resistance with measuring temperature Optimize the battery internal resistance in a certain temerature to get a accurate battery internal resistance. Signed-off-by: Yuanjiang Yu Signed-off-by: Baolin Wang Signed-off-by: Baolin Wang Signed-off-by: Sebastian Reichel --- drivers/power/supply/sc27xx_fuel_gauge.c | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c index bc8f5bda5762..221b6fb31332 100644 --- a/drivers/power/supply/sc27xx_fuel_gauge.c +++ b/drivers/power/supply/sc27xx_fuel_gauge.c @@ -81,9 +81,11 @@ * @max_volt: the maximum constant input voltage in millivolt * @min_volt: the minimum drained battery voltage in microvolt * @table_len: the capacity table length + * @resist_table_len: the resistance table length * @cur_1000ma_adc: ADC value corresponding to 1000 mA * @vol_1000mv_adc: ADC value corresponding to 1000 mV * @cap_table: capacity table with corresponding ocv + * @resist_table: resistance percent table with corresponding temperature */ struct sc27xx_fgu_data { struct regmap *regmap; @@ -103,15 +105,18 @@ struct sc27xx_fgu_data { int max_volt; int min_volt; int table_len; + int resist_table_len; int cur_1000ma_adc; int vol_1000mv_adc; struct power_supply_battery_ocv_table *cap_table; + struct power_supply_resistance_temp_table *resist_table; }; static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity); static void sc27xx_fgu_capacity_calibration(struct sc27xx_fgu_data *data, int cap, bool int_mode); static void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap); +static int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp); static const char * const sc27xx_charger_supply_name[] = { "sc2731_charger", @@ -434,7 +439,7 @@ static int sc27xx_fgu_get_current(struct sc27xx_fgu_data *data, int *val) static int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val) { - int vol, cur, ret; + int vol, cur, ret, temp, resistance; ret = sc27xx_fgu_get_vbat_vol(data, &vol); if (ret) @@ -444,8 +449,19 @@ static int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val) if (ret) return ret; + resistance = data->internal_resist; + if (data->resist_table_len > 0) { + ret = sc27xx_fgu_get_temp(data, &temp); + if (ret) + return ret; + + resistance = power_supply_temp2resist_simple(data->resist_table, + data->resist_table_len, temp); + resistance = data->internal_resist * resistance / 100; + } + /* Return the battery OCV in micro volts. */ - *val = vol * 1000 - cur * data->internal_resist; + *val = vol * 1000 - cur * resistance; return 0; } @@ -929,6 +945,18 @@ static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data) if (!data->alarm_cap) data->alarm_cap += 1; + data->resist_table_len = info.resist_table_size; + if (data->resist_table_len > 0) { + data->resist_table = devm_kmemdup(data->dev, info.resist_table, + data->resist_table_len * + sizeof(struct power_supply_resistance_temp_table), + GFP_KERNEL); + if (!data->resist_table) { + power_supply_put_battery_info(data->battery, &info); + return -ENOMEM; + } + } + power_supply_put_battery_info(data->battery, &info); ret = sc27xx_fgu_calibration(data); -- cgit v1.2.3-58-ga151 From 058d42563a5692a4e663df0dc270f4ffd2c70274 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Mon, 9 Dec 2019 11:56:25 +0800 Subject: power: supply: sc27xx: Calibrate the resistance of coulomb counter There are some deviations between the real resistance and the ideal resistance of coulomb counter, which will affect the accuracy of the coulomb counter, thus calibrate the real resistance of coulomb counter to improve the accuracy. Signed-off-by: Baolin Wang Signed-off-by: Baolin Wang Signed-off-by: Sebastian Reichel --- drivers/power/supply/sc27xx_fuel_gauge.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c index 221b6fb31332..469c83fdaa8e 100644 --- a/drivers/power/supply/sc27xx_fuel_gauge.c +++ b/drivers/power/supply/sc27xx_fuel_gauge.c @@ -62,6 +62,8 @@ #define SC27XX_FGU_CUR_BASIC_ADC 8192 #define SC27XX_FGU_SAMPLE_HZ 2 +/* micro Ohms */ +#define SC27XX_FGU_IDEAL_RESISTANCE 20000 /* * struct sc27xx_fgu_data: describe the FGU device @@ -84,6 +86,7 @@ * @resist_table_len: the resistance table length * @cur_1000ma_adc: ADC value corresponding to 1000 mA * @vol_1000mv_adc: ADC value corresponding to 1000 mV + * @calib_resist: the real resistance of coulomb counter chip in uOhm * @cap_table: capacity table with corresponding ocv * @resist_table: resistance percent table with corresponding temperature */ @@ -108,6 +111,7 @@ struct sc27xx_fgu_data { int resist_table_len; int cur_1000ma_adc; int vol_1000mv_adc; + int calib_resist; struct power_supply_battery_ocv_table *cap_table; struct power_supply_resistance_temp_table *resist_table; }; @@ -900,7 +904,9 @@ static int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data) */ cal_4200mv = (calib_data & 0x1ff) + 6963 - 4096 - 256; data->vol_1000mv_adc = DIV_ROUND_CLOSEST(cal_4200mv * 10, 42); - data->cur_1000ma_adc = data->vol_1000mv_adc * 4; + data->cur_1000ma_adc = + DIV_ROUND_CLOSEST(data->vol_1000mv_adc * 4 * data->calib_resist, + SC27XX_FGU_IDEAL_RESISTANCE); kfree(buf); return 0; @@ -1079,6 +1085,15 @@ static int sc27xx_fgu_probe(struct platform_device *pdev) return ret; } + ret = device_property_read_u32(&pdev->dev, + "sprd,calib-resistance-micro-ohms", + &data->calib_resist); + if (ret) { + dev_err(&pdev->dev, + "failed to get fgu calibration resistance\n"); + return ret; + } + data->channel = devm_iio_channel_get(dev, "bat-temp"); if (IS_ERR(data->channel)) { dev_err(dev, "failed to get IIO channel\n"); -- cgit v1.2.3-58-ga151 From 2e17ed94de68953b17ed91b64f4bd176cdf38ad4 Mon Sep 17 00:00:00 2001 From: Matheus Castello Date: Thu, 5 Dec 2019 12:44:06 -0300 Subject: power: supply: max17040: Add IRQ handler for low SOC alert According datasheet max17040 has a pin for alert host for low SOC. This pin can be used as external interrupt, so we need to check for interrupts assigned for device and handle it. In handler we are checking and storing fuel gauge registers values and send an uevent to notificate user space, so user space can decide save work or turn off since the alert demonstrate that the battery may no have the power to keep the system turned on for much longer. Signed-off-by: Matheus Castello Reviewed-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17040_battery.c | 73 ++++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c index 62499018e68b..5a8cd365a61e 100644 --- a/drivers/power/supply/max17040_battery.c +++ b/drivers/power/supply/max17040_battery.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -160,21 +161,54 @@ static void max17040_get_status(struct i2c_client *client) chip->status = POWER_SUPPLY_STATUS_FULL; } +static void max17040_check_changes(struct i2c_client *client) +{ + max17040_get_vcell(client); + max17040_get_soc(client); + max17040_get_online(client); + max17040_get_status(client); +} + static void max17040_work(struct work_struct *work) { struct max17040_chip *chip; chip = container_of(work, struct max17040_chip, work.work); - - max17040_get_vcell(chip->client); - max17040_get_soc(chip->client); - max17040_get_online(chip->client); - max17040_get_status(chip->client); + max17040_check_changes(chip->client); queue_delayed_work(system_power_efficient_wq, &chip->work, MAX17040_DELAY); } +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"); + /* read registers */ + max17040_check_changes(chip->client); + + /* send uevent */ + power_supply_changed(chip->battery); + + return IRQ_HANDLED; +} + +static int max17040_enable_alert_irq(struct max17040_chip *chip) +{ + struct i2c_client *client = chip->client; + unsigned int flags; + int ret; + + flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, + max17040_thread_handler, flags, + chip->battery->desc->name, chip); + + return ret; +} + static enum power_supply_property max17040_battery_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_ONLINE, @@ -220,6 +254,19 @@ static int max17040_probe(struct i2c_client *client, max17040_reset(client); max17040_get_version(client); + /* check interrupt */ + if (client->irq && of_device_is_compatible(client->dev.of_node, + "maxim,max77836-battery")) { + int ret; + + ret = max17040_enable_alert_irq(chip); + if (ret) { + client->irq = 0; + dev_warn(&client->dev, + "Failed to get IRQ err %d\n", ret); + } + } + INIT_DEFERRABLE_WORK(&chip->work, max17040_work); queue_delayed_work(system_power_efficient_wq, &chip->work, MAX17040_DELAY); @@ -244,6 +291,14 @@ static int max17040_suspend(struct device *dev) struct max17040_chip *chip = i2c_get_clientdata(client); cancel_delayed_work(&chip->work); + + if (client->irq) { + if (device_may_wakeup(dev)) + enable_irq_wake(client->irq); + else + disable_irq_wake(client->irq); + } + return 0; } @@ -254,6 +309,14 @@ static int max17040_resume(struct device *dev) queue_delayed_work(system_power_efficient_wq, &chip->work, MAX17040_DELAY); + + if (client->irq) { + if (device_may_wakeup(dev)) + disable_irq_wake(client->irq); + else + enable_irq_wake(client->irq); + } + return 0; } -- cgit v1.2.3-58-ga151 From cccdd0ca1c0d985c3cf1dfe65a3b42387a6e3d22 Mon Sep 17 00:00:00 2001 From: Matheus Castello Date: Thu, 5 Dec 2019 12:44:09 -0300 Subject: power: supply: max17040: Config alert SOC low level threshold from FDT For configuration of fuel gauge alert for a low level state of charge interrupt we add a function to config level threshold and a device tree binding property to set it in flatned device tree node. Now we can use "maxim,alert-low-soc-level" property with the values from 1% up to 32% to configure alert interrupt threshold. Signed-off-by: Matheus Castello Reviewed-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17040_battery.c | 52 ++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c index 5a8cd365a61e..2a2a1fd8d4eb 100644 --- a/drivers/power/supply/max17040_battery.c +++ b/drivers/power/supply/max17040_battery.c @@ -29,6 +29,9 @@ #define MAX17040_DELAY 1000 #define MAX17040_BATTERY_FULL 95 +#define MAX17040_ATHD_MASK 0xFFC0 +#define MAX17040_ATHD_DEFAULT_POWER_UP 4 + struct max17040_chip { struct i2c_client *client; struct delayed_work work; @@ -43,6 +46,8 @@ struct max17040_chip { int soc; /* State Of Charge */ int status; + /* Low alert threshold from 32% to 1% of the State of Charge */ + u32 low_soc_alert; }; static int max17040_get_property(struct power_supply *psy, @@ -99,6 +104,21 @@ 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; +} + static void max17040_get_vcell(struct i2c_client *client) { struct max17040_chip *chip = i2c_get_clientdata(client); @@ -161,6 +181,21 @@ static void max17040_get_status(struct i2c_client *client) chip->status = POWER_SUPPLY_STATUS_FULL; } +static int max17040_get_of_data(struct max17040_chip *chip) +{ + struct device *dev = &chip->client->dev; + + 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) + return -EINVAL; + + return 0; +} + static void max17040_check_changes(struct i2c_client *client) { max17040_get_vcell(client); @@ -192,6 +227,9 @@ static irqreturn_t max17040_thread_handler(int id, void *dev) /* send uevent */ power_supply_changed(chip->battery); + /* reset alert bit */ + max17040_set_low_soc_alert(client, chip->low_soc_alert); + return IRQ_HANDLED; } @@ -230,6 +268,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; + int ret; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) return -EIO; @@ -240,6 +279,12 @@ static int max17040_probe(struct i2c_client *client, chip->client = client; 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"); + return ret; + } i2c_set_clientdata(client, chip); psy_cfg.drv_data = chip; @@ -257,7 +302,12 @@ static int max17040_probe(struct i2c_client *client, /* check interrupt */ if (client->irq && of_device_is_compatible(client->dev.of_node, "maxim,max77836-battery")) { - int ret; + ret = max17040_set_low_soc_alert(client, chip->low_soc_alert); + if (ret) { + dev_err(&client->dev, + "Failed to set low SOC alert: err %d\n", ret); + return ret; + } ret = max17040_enable_alert_irq(chip); if (ret) { -- cgit v1.2.3-58-ga151 From a08990ea11dc3386cc972c817a121525f7bc7321 Mon Sep 17 00:00:00 2001 From: Matheus Castello Date: Thu, 5 Dec 2019 12:44:10 -0300 Subject: power: supply: max17040: Send uevent in SOC and status change Notify core through power_supply_changed() in case of changes in state of charge and power supply status. This is useful for user-space to efficiently update current battery level. Signed-off-by: Matheus Castello Reviewed-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17040_battery.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c index 2a2a1fd8d4eb..c1188e94cf54 100644 --- a/drivers/power/supply/max17040_battery.c +++ b/drivers/power/supply/max17040_battery.c @@ -207,10 +207,19 @@ static void max17040_check_changes(struct i2c_client *client) static void max17040_work(struct work_struct *work) { struct max17040_chip *chip; + int last_soc, last_status; chip = container_of(work, struct max17040_chip, work.work); + + /* store SOC and status to check changes */ + last_soc = chip->soc; + last_status = chip->status; max17040_check_changes(chip->client); + /* check changes and send uevent */ + 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); } -- cgit v1.2.3-58-ga151 From 464aca16487c618534bf2efa4afbc9ece015553a Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Fri, 29 Nov 2019 22:59:16 +0100 Subject: power: supply: cros_usbpd: Remove dev_err() getting the number of ports When a device has no support to get the charger number of ports, it doesn't have to result in a dev_err(), print saying "Could not get charger port count" using a dev_info() would suffice. In such case, the dev_info() message is already printed but the dev_err() is annoying, specially, on those devices that doesn't support the command. So remove the dev_err(). Signed-off-by: Enric Balletbo i Serra Reviewed-by: Guenter Roeck Signed-off-by: Sebastian Reichel --- drivers/power/supply/cros_usbpd-charger.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/power/supply/cros_usbpd-charger.c b/drivers/power/supply/cros_usbpd-charger.c index 6cc7c3910e09..ffad9ee03a68 100644 --- a/drivers/power/supply/cros_usbpd-charger.c +++ b/drivers/power/supply/cros_usbpd-charger.c @@ -132,11 +132,8 @@ static int cros_usbpd_charger_get_num_ports(struct charger_data *charger) ret = cros_usbpd_charger_ec_command(charger, 0, EC_CMD_CHARGE_PORT_COUNT, NULL, 0, &resp, sizeof(resp)); - if (ret < 0) { - dev_err(charger->dev, - "Unable to get the number of ports (err:0x%x)\n", ret); + if (ret < 0) return ret; - } return resp.port_count; } @@ -148,11 +145,8 @@ static int cros_usbpd_charger_get_usbpd_num_ports(struct charger_data *charger) ret = cros_usbpd_charger_ec_command(charger, 0, EC_CMD_USB_PD_PORTS, NULL, 0, &resp, sizeof(resp)); - if (ret < 0) { - dev_err(charger->dev, - "Unable to get the number or ports (err:0x%x)\n", ret); + if (ret < 0) return ret; - } return resp.num_ports; } -- cgit v1.2.3-58-ga151 From 463881ac40176a9383dbc5108e260c4b46a6f428 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 21 Nov 2019 04:19:55 +0100 Subject: power: reset: Fix Kconfig indentation Adjust indentation from spaces to tab (+optional two spaces) as in coding style with command like: $ sed -e 's/^ /\t/' -i */Kconfig Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/reset/Kconfig | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index c721939767eb..0498363203e8 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -141,14 +141,14 @@ config POWER_RESET_LTC2952 down via the LTC2952. Bindings are made in the device tree. config POWER_RESET_MT6323 - bool "MediaTek MT6323 power-off driver" - depends on MFD_MT6397 - help - The power-off driver is responsible for externally shutdown down - the power of a remote MediaTek SoC MT6323 is connected to through - controlling a tiny circuit BBPU inside MT6323 RTC. - - Say Y if you have a board where MT6323 could be found. + bool "MediaTek MT6323 power-off driver" + depends on MFD_MT6397 + help + The power-off driver is responsible for externally shutdown down + the power of a remote MediaTek SoC MT6323 is connected to through + controlling a tiny circuit BBPU inside MT6323 RTC. + + Say Y if you have a board where MT6323 could be found. config POWER_RESET_QNAP bool "QNAP power-off driver" -- cgit v1.2.3-58-ga151 From 75d8a8423c877f2745cdbeaeaa06e8fa04dfdf27 Mon Sep 17 00:00:00 2001 From: Jean-Francois Dagenais Date: Fri, 1 Nov 2019 15:06:59 -0400 Subject: power: supply: sbs-battery: use octal permissions on module param Symbolic permissions 'S_IRUSR | S_IRGRP | S_IROTH' are not preferred. Use octal permissions '0444'. Signed-off-by: Jean-Francois Dagenais Signed-off-by: Sebastian Reichel --- drivers/power/supply/sbs-battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index f8d74e9f7931..6c1ddeadda09 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -1001,6 +1001,6 @@ module_i2c_driver(sbs_battery_driver); MODULE_DESCRIPTION("SBS battery monitor driver"); MODULE_LICENSE("GPL"); -module_param(force_load, bool, S_IRUSR | S_IRGRP | S_IROTH); +module_param(force_load, bool, 0444); MODULE_PARM_DESC(force_load, "Attempt to load the driver even if no battery is connected"); -- cgit v1.2.3-58-ga151 From e2ec6aef37e699998a9d8df8926a70305b74afd5 Mon Sep 17 00:00:00 2001 From: Jean-Francois Dagenais Date: Fri, 1 Nov 2019 15:07:03 -0400 Subject: power: supply: sbs-battery: fix CAPACITY_MODE bit naming "Battery mode" is the name of the register, the bit manipulated by this code is "CAPACITY_MODE" (Smart Battery System Specifications). Signed-off-by: Jean-Francois Dagenais Signed-off-by: Sebastian Reichel --- drivers/power/supply/sbs-battery.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index 6c1ddeadda09..c01599ecb9c8 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -5,6 +5,7 @@ * Copyright (c) 2010, NVIDIA Corporation. */ +#include #include #include #include @@ -46,10 +47,10 @@ enum { /* Battery Mode defines */ #define BATTERY_MODE_OFFSET 0x03 -#define BATTERY_MODE_MASK 0x8000 -enum sbs_battery_mode { - BATTERY_MODE_AMPS = 0, - BATTERY_MODE_WATTS = 0x8000 +#define BATTERY_MODE_CAPACITY_MASK BIT(15) +enum sbs_capacity_mode { + CAPACITY_MODE_AMPS = 0, + CAPACITY_MODE_WATTS = BATTERY_MODE_CAPACITY_MASK }; /* manufacturer access defines */ @@ -518,8 +519,8 @@ static void sbs_unit_adjustment(struct i2c_client *client, } } -static enum sbs_battery_mode sbs_set_battery_mode(struct i2c_client *client, - enum sbs_battery_mode mode) +static enum sbs_capacity_mode sbs_set_capacity_mode(struct i2c_client *client, + enum sbs_capacity_mode mode) { int ret, original_val; @@ -527,13 +528,13 @@ static enum sbs_battery_mode sbs_set_battery_mode(struct i2c_client *client, if (original_val < 0) return original_val; - if ((original_val & BATTERY_MODE_MASK) == mode) + if ((original_val & BATTERY_MODE_CAPACITY_MASK) == mode) return mode; - if (mode == BATTERY_MODE_AMPS) - ret = original_val & ~BATTERY_MODE_MASK; + if (mode == CAPACITY_MODE_AMPS) + ret = original_val & ~BATTERY_MODE_CAPACITY_MASK; else - ret = original_val | BATTERY_MODE_MASK; + ret = original_val | BATTERY_MODE_CAPACITY_MASK; ret = sbs_write_word_data(client, BATTERY_MODE_OFFSET, ret); if (ret < 0) @@ -541,7 +542,7 @@ static enum sbs_battery_mode sbs_set_battery_mode(struct i2c_client *client, usleep_range(1000, 2000); - return original_val & BATTERY_MODE_MASK; + return original_val & BATTERY_MODE_CAPACITY_MASK; } static int sbs_get_battery_capacity(struct i2c_client *client, @@ -549,12 +550,12 @@ static int sbs_get_battery_capacity(struct i2c_client *client, union power_supply_propval *val) { s32 ret; - enum sbs_battery_mode mode = BATTERY_MODE_WATTS; + enum sbs_capacity_mode mode = CAPACITY_MODE_WATTS; if (power_supply_is_amp_property(psp)) - mode = BATTERY_MODE_AMPS; + mode = CAPACITY_MODE_AMPS; - mode = sbs_set_battery_mode(client, mode); + mode = sbs_set_capacity_mode(client, mode); if (mode < 0) return mode; @@ -564,7 +565,7 @@ static int sbs_get_battery_capacity(struct i2c_client *client, val->intval = ret; - ret = sbs_set_battery_mode(client, mode); + ret = sbs_set_capacity_mode(client, mode); if (ret < 0) return ret; -- cgit v1.2.3-58-ga151 From c6ef5234efc07679921c20025a743aabfc62f2c0 Mon Sep 17 00:00:00 2001 From: Ma Feng Date: Thu, 19 Dec 2019 09:46:29 +0800 Subject: power: supply: ab8500: Remove unneeded semicolon Fixes coccicheck warning: drivers/power/supply/ab8500_fg.c:2224:5-6: Unneeded semicolon drivers/power/supply/ab8500_fg.c:2227:4-5: Unneeded semicolon drivers/power/supply/ab8500_fg.c:2334:3-4: Unneeded semicolon drivers/power/supply/ab8500_fg.c:2342:3-4: Unneeded semicolon drivers/power/supply/ab8500_fg.c:2350:3-4: Unneeded semicolon drivers/power/supply/ab8500_fg.c:2358:3-4: Unneeded semicolon drivers/power/supply/ab8500_fg.c:2366:3-4: Unneeded semicolon Reported-by: Hulk Robot Signed-off-by: Ma Feng Signed-off-by: Sebastian Reichel --- drivers/power/supply/ab8500_fg.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index c3912ee9eb99..b96f90a82ecf 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -2221,10 +2221,10 @@ static int ab8500_fg_get_ext_psy_data(struct device *dev, void *data) ab8500_fg_update_cap_scalers(di); queue_work(di->fg_wq, &di->fg_work); break; - }; + } default: break; - }; + } break; case POWER_SUPPLY_PROP_TECHNOLOGY: switch (ext->desc->type) { @@ -2331,7 +2331,7 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di) if (ret) { dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_MAX_TIME_REG\n", __func__); goto out; - }; + } ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, AB8505_RTC_PCUT_FLAG_TIME_REG, di->bm->fg_params->pcut_flag_time); @@ -2339,7 +2339,7 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di) if (ret) { dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_FLAG_TIME_REG\n", __func__); goto out; - }; + } ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, AB8505_RTC_PCUT_RESTART_REG, di->bm->fg_params->pcut_max_restart); @@ -2347,7 +2347,7 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di) if (ret) { dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_RESTART_REG\n", __func__); goto out; - }; + } ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, AB8505_RTC_PCUT_DEBOUNCE_REG, di->bm->fg_params->pcut_debounce_time); @@ -2355,7 +2355,7 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di) if (ret) { dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_DEBOUNCE_REG\n", __func__); goto out; - }; + } ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, AB8505_RTC_PCUT_CTL_STATUS_REG, di->bm->fg_params->pcut_enable); @@ -2363,7 +2363,7 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di) if (ret) { dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_CTL_STATUS_REG\n", __func__); goto out; - }; + } } out: return ret; -- cgit v1.2.3-58-ga151 From e15c54d208f933fe004b2e5fcdd741d3ea408df1 Mon Sep 17 00:00:00 2001 From: Ma Feng Date: Thu, 19 Dec 2019 09:46:30 +0800 Subject: power: supply: ab8500_charger: Remove unneeded semicolon Fixes coccicheck warning: drivers/power/supply/ab8500_charger.c:1082:2-3: Unneeded semicolon drivers/power/supply/ab8500_charger.c:792:2-3: Unneeded semicolon drivers/power/supply/ab8500_charger.c:2430:2-3: Unneeded semicolon Reported-by: Hulk Robot Signed-off-by: Ma Feng Signed-off-by: Sebastian Reichel --- drivers/power/supply/ab8500_charger.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c index 8a0f9d769690..f69550d64f09 100644 --- a/drivers/power/supply/ab8500_charger.c +++ b/drivers/power/supply/ab8500_charger.c @@ -789,7 +789,7 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di, di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05; ret = -ENXIO; break; - }; + } di->max_usb_in_curr.set_max = di->max_usb_in_curr.usb_type_max; dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d", @@ -1079,7 +1079,7 @@ static int ab8500_charger_get_usb_cur(struct ab8500_charger *di) di->max_usb_in_curr.usb_type_max = USB_CH_IP_CUR_LVL_0P05; ret = -EPERM; break; - }; + } di->max_usb_in_curr.set_max = di->max_usb_in_curr.usb_type_max; return ret; } @@ -2427,7 +2427,7 @@ static void ab8500_charger_usb_state_changed_work(struct work_struct *work) default: break; - }; + } } /** -- cgit v1.2.3-58-ga151 From 921377c0b4e8e240e0b56cb13bc22f2ecc4e59df Mon Sep 17 00:00:00 2001 From: Ma Feng Date: Thu, 19 Dec 2019 09:46:31 +0800 Subject: power: supply: abx500_chargalg: Remove unneeded semicolon Fixes coccicheck warning: drivers/power/supply/abx500_chargalg.c:1826:2-3: Unneeded semicolon Reported-by: Hulk Robot Signed-off-by: Ma Feng Signed-off-by: Sebastian Reichel --- drivers/power/supply/abx500_chargalg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/power/supply/abx500_chargalg.c b/drivers/power/supply/abx500_chargalg.c index e6e37d4f20e4..2fb33a07879a 100644 --- a/drivers/power/supply/abx500_chargalg.c +++ b/drivers/power/supply/abx500_chargalg.c @@ -1823,7 +1823,7 @@ static ssize_t abx500_chargalg_en_store(struct abx500_chargalg *di, "Enter 0. Disable AC/USB Charging\n" "1. Enable AC charging\n" "2. Enable USB Charging\n"); - }; + } return strlen(buf); } -- cgit v1.2.3-58-ga151 From 1c51aad8475d670ad58ae60adc9d32342381df8d Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 12 Jan 2020 21:53:03 -0600 Subject: power: supply: axp20x_ac_power: Fix reporting online status AXP803/AXP813 have a flag that enables/disables the AC power supply input. This flag does not affect the status bits in PWR_INPUT_STATUS. Its effect can be verified by checking the battery charge/discharge state (bit 2 of PWR_INPUT_STATUS), or by examining the current draw on the AC input. Take this flag into account when getting the ONLINE property of the AC input, on PMICs where this flag is present. Fixes: 7693b5643fd2 ("power: supply: add AC power supply driver for AXP813") Cc: stable@vger.kernel.org Signed-off-by: Samuel Holland Reviewed-by: Chen-Yu Tsai Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp20x_ac_power.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/power/supply/axp20x_ac_power.c b/drivers/power/supply/axp20x_ac_power.c index 0d34a932b6d5..f74b0556bb6b 100644 --- a/drivers/power/supply/axp20x_ac_power.c +++ b/drivers/power/supply/axp20x_ac_power.c @@ -23,6 +23,8 @@ #define AXP20X_PWR_STATUS_ACIN_PRESENT BIT(7) #define AXP20X_PWR_STATUS_ACIN_AVAIL BIT(6) +#define AXP813_ACIN_PATH_SEL BIT(7) + #define AXP813_VHOLD_MASK GENMASK(5, 3) #define AXP813_VHOLD_UV_TO_BIT(x) ((((x) / 100000) - 40) << 3) #define AXP813_VHOLD_REG_TO_UV(x) \ @@ -40,6 +42,7 @@ struct axp20x_ac_power { struct power_supply *supply; struct iio_channel *acin_v; struct iio_channel *acin_i; + bool has_acin_path_sel; }; static irqreturn_t axp20x_ac_power_irq(int irq, void *devid) @@ -86,6 +89,17 @@ static int axp20x_ac_power_get_property(struct power_supply *psy, return ret; val->intval = !!(reg & AXP20X_PWR_STATUS_ACIN_AVAIL); + + /* ACIN_PATH_SEL disables ACIN even if ACIN_AVAIL is set. */ + if (val->intval && power->has_acin_path_sel) { + ret = regmap_read(power->regmap, AXP813_ACIN_PATH_CTRL, + ®); + if (ret) + return ret; + + val->intval = !!(reg & AXP813_ACIN_PATH_SEL); + } + return 0; case POWER_SUPPLY_PROP_VOLTAGE_NOW: @@ -224,21 +238,25 @@ static const struct power_supply_desc axp813_ac_power_desc = { struct axp_data { const struct power_supply_desc *power_desc; bool acin_adc; + bool acin_path_sel; }; static const struct axp_data axp20x_data = { - .power_desc = &axp20x_ac_power_desc, - .acin_adc = true, + .power_desc = &axp20x_ac_power_desc, + .acin_adc = true, + .acin_path_sel = false, }; static const struct axp_data axp22x_data = { - .power_desc = &axp22x_ac_power_desc, - .acin_adc = false, + .power_desc = &axp22x_ac_power_desc, + .acin_adc = false, + .acin_path_sel = false, }; static const struct axp_data axp813_data = { - .power_desc = &axp813_ac_power_desc, - .acin_adc = false, + .power_desc = &axp813_ac_power_desc, + .acin_adc = false, + .acin_path_sel = true, }; static int axp20x_ac_power_probe(struct platform_device *pdev) @@ -282,6 +300,7 @@ static int axp20x_ac_power_probe(struct platform_device *pdev) } power->regmap = dev_get_regmap(pdev->dev.parent, NULL); + power->has_acin_path_sel = axp_data->acin_path_sel; platform_set_drvdata(pdev, power); -- cgit v1.2.3-58-ga151 From ddfec18b42c44c4792071d67ae139e5b1f211410 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 12 Jan 2020 21:53:04 -0600 Subject: power: supply: axp20x_ac_power: Allow offlining AXP803/AXP813 have a flag that enables/disables the AC power supply input. Allow control of this flag via the ONLINE property on those variants. Reviewed-by: Chen-Yu Tsai Signed-off-by: Samuel Holland Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp20x_ac_power.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/power/supply/axp20x_ac_power.c b/drivers/power/supply/axp20x_ac_power.c index f74b0556bb6b..3ba38f2f281c 100644 --- a/drivers/power/supply/axp20x_ac_power.c +++ b/drivers/power/supply/axp20x_ac_power.c @@ -24,6 +24,7 @@ #define AXP20X_PWR_STATUS_ACIN_AVAIL BIT(6) #define AXP813_ACIN_PATH_SEL BIT(7) +#define AXP813_ACIN_PATH_SEL_TO_BIT(x) (!!(x) << 7) #define AXP813_VHOLD_MASK GENMASK(5, 3) #define AXP813_VHOLD_UV_TO_BIT(x) ((((x) / 100000) - 40) << 3) @@ -157,6 +158,11 @@ static int axp813_ac_power_set_property(struct power_supply *psy, struct axp20x_ac_power *power = power_supply_get_drvdata(psy); switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + return regmap_update_bits(power->regmap, AXP813_ACIN_PATH_CTRL, + AXP813_ACIN_PATH_SEL, + AXP813_ACIN_PATH_SEL_TO_BIT(val->intval)); + case POWER_SUPPLY_PROP_VOLTAGE_MIN: if (val->intval < 4000000 || val->intval > 4700000) return -EINVAL; @@ -183,7 +189,8 @@ static int axp813_ac_power_set_property(struct power_supply *psy, static int axp813_ac_power_prop_writeable(struct power_supply *psy, enum power_supply_property psp) { - return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN || + return psp == POWER_SUPPLY_PROP_ONLINE || + psp == POWER_SUPPLY_PROP_VOLTAGE_MIN || psp == POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT; } -- cgit v1.2.3-58-ga151 From ca4c77bb43151e6569e7aae689a69b65a48c85f0 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 12 Jan 2020 21:53:05 -0600 Subject: power: supply: axp20x_ac_power: Add wakeup control The AC power supply input can be used as a wakeup source. Hook up the ACIN_PLUGIN IRQ to trigger wakeup based on userspace configuration. To do this, we must remember the list of IRQs for the life of the device. To know how much space to allocate for the flexible array member, we switch from using a NULL sentinel to using an array length. Because we now depend on the specific order of the IRQs (we assume ACIN_PLUGIN is first and always present), failing to acquire an IRQ during probe must be a fatal error. To avoid spuriously waking up the system when the AC power supply is not configured as a wakeup source, we must explicitly disable all non- wake IRQs during system suspend. This is because the SoC's NMI input is shared among all IRQs on the AXP PMIC. Due to the use of regmap-irq, the individual IRQs within the PMIC are nested threaded interrupts, and are therefore not automatically disabled during system suspend. The upshot is that if any other device within the MFD (such as the power key) is an enabled wakeup source, all enabled IRQs within the PMIC will cause wakeup. We still need to call enable_irq_wake() when we *do* want wakeup, in case those other wakeup sources on the PMIC are all disabled. Reviewed-by: Chen-Yu Tsai Signed-off-by: Samuel Holland Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp20x_ac_power.c | 91 +++++++++++++++++++++++++++------- 1 file changed, 74 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/power/supply/axp20x_ac_power.c b/drivers/power/supply/axp20x_ac_power.c index 3ba38f2f281c..ac360016b08a 100644 --- a/drivers/power/supply/axp20x_ac_power.c +++ b/drivers/power/supply/axp20x_ac_power.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,8 @@ struct axp20x_ac_power { struct iio_channel *acin_v; struct iio_channel *acin_i; bool has_acin_path_sel; + unsigned int num_irqs; + unsigned int irqs[]; }; static irqreturn_t axp20x_ac_power_irq(int irq, void *devid) @@ -242,38 +245,86 @@ static const struct power_supply_desc axp813_ac_power_desc = { .set_property = axp813_ac_power_set_property, }; +static const char * const axp20x_irq_names[] = { + "ACIN_PLUGIN", + "ACIN_REMOVAL", +}; + struct axp_data { const struct power_supply_desc *power_desc; + const char * const *irq_names; + unsigned int num_irq_names; bool acin_adc; bool acin_path_sel; }; static const struct axp_data axp20x_data = { .power_desc = &axp20x_ac_power_desc, + .irq_names = axp20x_irq_names, + .num_irq_names = ARRAY_SIZE(axp20x_irq_names), .acin_adc = true, .acin_path_sel = false, }; static const struct axp_data axp22x_data = { .power_desc = &axp22x_ac_power_desc, + .irq_names = axp20x_irq_names, + .num_irq_names = ARRAY_SIZE(axp20x_irq_names), .acin_adc = false, .acin_path_sel = false, }; static const struct axp_data axp813_data = { .power_desc = &axp813_ac_power_desc, + .irq_names = axp20x_irq_names, + .num_irq_names = ARRAY_SIZE(axp20x_irq_names), .acin_adc = false, .acin_path_sel = true, }; +#ifdef CONFIG_PM_SLEEP +static int axp20x_ac_power_suspend(struct device *dev) +{ + struct axp20x_ac_power *power = dev_get_drvdata(dev); + int i = 0; + + /* + * Allow wake via ACIN_PLUGIN only. + * + * As nested threaded IRQs are not automatically disabled during + * suspend, we must explicitly disable the remainder of the IRQs. + */ + if (device_may_wakeup(&power->supply->dev)) + enable_irq_wake(power->irqs[i++]); + while (i < power->num_irqs) + disable_irq(power->irqs[i++]); + + return 0; +} + +static int axp20x_ac_power_resume(struct device *dev) +{ + struct axp20x_ac_power *power = dev_get_drvdata(dev); + int i = 0; + + if (device_may_wakeup(&power->supply->dev)) + disable_irq_wake(power->irqs[i++]); + while (i < power->num_irqs) + enable_irq(power->irqs[i++]); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(axp20x_ac_power_pm_ops, axp20x_ac_power_suspend, + axp20x_ac_power_resume); + static int axp20x_ac_power_probe(struct platform_device *pdev) { struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); struct power_supply_config psy_cfg = {}; struct axp20x_ac_power *power; const struct axp_data *axp_data; - static const char * const irq_names[] = { "ACIN_PLUGIN", "ACIN_REMOVAL", - NULL }; int i, irq, ret; if (!of_device_is_available(pdev->dev.of_node)) @@ -284,12 +335,14 @@ static int axp20x_ac_power_probe(struct platform_device *pdev) return -EINVAL; } - power = devm_kzalloc(&pdev->dev, sizeof(*power), GFP_KERNEL); + axp_data = of_device_get_match_data(&pdev->dev); + + power = devm_kzalloc(&pdev->dev, + struct_size(power, irqs, axp_data->num_irq_names), + GFP_KERNEL); if (!power) return -ENOMEM; - axp_data = of_device_get_match_data(&pdev->dev); - if (axp_data->acin_adc) { power->acin_v = devm_iio_channel_get(&pdev->dev, "acin_v"); if (IS_ERR(power->acin_v)) { @@ -308,6 +361,7 @@ static int axp20x_ac_power_probe(struct platform_device *pdev) power->regmap = dev_get_regmap(pdev->dev.parent, NULL); power->has_acin_path_sel = axp_data->acin_path_sel; + power->num_irqs = axp_data->num_irq_names; platform_set_drvdata(pdev, power); @@ -321,20 +375,22 @@ static int axp20x_ac_power_probe(struct platform_device *pdev) return PTR_ERR(power->supply); /* Request irqs after registering, as irqs may trigger immediately */ - for (i = 0; irq_names[i]; i++) { - irq = platform_get_irq_byname(pdev, irq_names[i]); + for (i = 0; i < axp_data->num_irq_names; i++) { + irq = platform_get_irq_byname(pdev, axp_data->irq_names[i]); if (irq < 0) { - dev_warn(&pdev->dev, "No IRQ for %s: %d\n", - irq_names[i], irq); - continue; + dev_err(&pdev->dev, "No IRQ for %s: %d\n", + axp_data->irq_names[i], irq); + return irq; } - irq = regmap_irq_get_virq(axp20x->regmap_irqc, irq); - ret = devm_request_any_context_irq(&pdev->dev, irq, + power->irqs[i] = regmap_irq_get_virq(axp20x->regmap_irqc, irq); + ret = devm_request_any_context_irq(&pdev->dev, power->irqs[i], axp20x_ac_power_irq, 0, DRVNAME, power); - if (ret < 0) - dev_warn(&pdev->dev, "Error requesting %s IRQ: %d\n", - irq_names[i], ret); + if (ret < 0) { + dev_err(&pdev->dev, "Error requesting %s IRQ: %d\n", + axp_data->irq_names[i], ret); + return ret; + } } return 0; @@ -357,8 +413,9 @@ MODULE_DEVICE_TABLE(of, axp20x_ac_power_match); static struct platform_driver axp20x_ac_power_driver = { .probe = axp20x_ac_power_probe, .driver = { - .name = DRVNAME, - .of_match_table = axp20x_ac_power_match, + .name = DRVNAME, + .of_match_table = axp20x_ac_power_match, + .pm = &axp20x_ac_power_pm_ops, }, }; -- cgit v1.2.3-58-ga151 From f95526333abf6f486cda63abb38d0c1f99d9535a Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 12 Jan 2020 21:53:06 -0600 Subject: power: supply: axp20x_usb_power: Remove unused device_node This member of struct axp20x_usb_power is not used anywhere. Remove it. Reviewed-by: Chen-Yu Tsai Signed-off-by: Samuel Holland Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp20x_usb_power.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c index 5f0a5722b19e..dd3f3f12e41d 100644 --- a/drivers/power/supply/axp20x_usb_power.c +++ b/drivers/power/supply/axp20x_usb_power.c @@ -57,7 +57,6 @@ #define DEBOUNCE_TIME msecs_to_jiffies(50) struct axp20x_usb_power { - struct device_node *np; struct regmap *regmap; struct power_supply *supply; enum axp20x_variants axp20x_id; @@ -465,7 +464,6 @@ static int axp20x_usb_power_probe(struct platform_device *pdev) power->axp20x_id = (enum axp20x_variants)of_device_get_match_data( &pdev->dev); - power->np = pdev->dev.of_node; power->regmap = axp20x->regmap; if (power->axp20x_id == AXP202_ID) { -- cgit v1.2.3-58-ga151 From e29242ad813c71e8e6a22c4f13f420a2297ac716 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 10 Jan 2020 11:05:40 +0100 Subject: power: supply: max17040: Correct IRQ wake handling Don't disable IRQ wake feature without prior enabling it. This fixes following warning observed on Exynos3250-based Rinato board: ------------[ cut here ]------------ WARNING: CPU: 0 PID: 1288 at kernel/irq/manage.c:724 irq_set_irq_wake+0xfc/0x134 Unbalanced IRQ 83 wake disable Modules linked in: CPU: 0 PID: 1288 Comm: rtcwake Not tainted 5.5.0-rc5-next-20200110-00031-g6289fffbb3f5 #7266 Hardware name: Samsung Exynos (Flattened Device Tree) [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [] (show_stack) from [] (dump_stack+0xa4/0xd0) [] (dump_stack) from [] (__warn+0xf4/0x10c) [] (__warn) from [] (warn_slowpath_fmt+0x74/0xb8) [] (warn_slowpath_fmt) from [] (irq_set_irq_wake+0xfc/0x134) [] (irq_set_irq_wake) from [] (max17040_suspend+0x50/0x58) [] (max17040_suspend) from [] (dpm_run_callback+0xb4/0x400) [] (dpm_run_callback) from [] (__device_suspend+0x140/0x814) [] (__device_suspend) from [] (dpm_suspend+0x16c/0x564) [] (dpm_suspend) from [] (dpm_suspend_start+0x90/0x98) [] (dpm_suspend_start) from [] (suspend_devices_and_enter+0xec/0xc0c) [] (suspend_devices_and_enter) from [] (pm_suspend+0x318/0x3e8) [] (pm_suspend) from [] (state_store+0x68/0xc8) [] (state_store) from [] (kernfs_fop_write+0x10c/0x220) [] (kernfs_fop_write) from [] (__vfs_write+0x2c/0x1c4) [] (__vfs_write) from [] (vfs_write+0xa4/0x180) [] (vfs_write) from [] (ksys_write+0x58/0xcc) [] (ksys_write) from [] (ret_fast_syscall+0x0/0x28) Exception stack(0xd6e83fa8 to 0xd6e83ff0) ... irq event stamp: 18028 hardirqs last enabled at (18027): [] cancel_delayed_work+0x84/0xf8 hardirqs last disabled at (18028): [] _raw_spin_lock_irqsave+0x1c/0x58 softirqs last enabled at (17876): [] __do_softirq+0x4f0/0x5e4 softirqs last disabled at (17869): [] irq_exit+0x16c/0x170 ---[ end trace 0728005730004e60 ]--- Fixes: 2e17ed94de68 ("power: supply: max17040: Add IRQ handler for low SOC alert") Signed-off-by: Marek Szyprowski Reviewed-by: Krzysztof Kozlowski Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17040_battery.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c index c1188e94cf54..8a1f0ee493aa 100644 --- a/drivers/power/supply/max17040_battery.c +++ b/drivers/power/supply/max17040_battery.c @@ -351,12 +351,8 @@ static int max17040_suspend(struct device *dev) cancel_delayed_work(&chip->work); - if (client->irq) { - if (device_may_wakeup(dev)) - enable_irq_wake(client->irq); - else - disable_irq_wake(client->irq); - } + if (client->irq && device_may_wakeup(dev)) + enable_irq_wake(client->irq); return 0; } @@ -369,12 +365,8 @@ static int max17040_resume(struct device *dev) queue_delayed_work(system_power_efficient_wq, &chip->work, MAX17040_DELAY); - if (client->irq) { - if (device_may_wakeup(dev)) - disable_irq_wake(client->irq); - else - enable_irq_wake(client->irq); - } + if (client->irq && device_may_wakeup(dev)) + disable_irq_wake(client->irq); return 0; } -- cgit v1.2.3-58-ga151 From a60ec78d306c6548d4adbc7918b587a723c555cc Mon Sep 17 00:00:00 2001 From: Sven Van Asbroeck Date: Thu, 19 Sep 2019 11:11:37 -0400 Subject: power: supply: ltc2941-battery-gauge: fix use-after-free This driver's remove path calls cancel_delayed_work(). However, that function does not wait until the work function finishes. This could mean that the work function is still running after the driver's remove function has finished, which would result in a use-after-free. Fix by calling cancel_delayed_work_sync(), which ensures that that the work is properly cancelled, no longer running, and unable to re-schedule itself. This issue was detected with the help of Coccinelle. Cc: stable Signed-off-by: Sven Van Asbroeck Signed-off-by: Sebastian Reichel --- drivers/power/supply/ltc2941-battery-gauge.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/power/supply/ltc2941-battery-gauge.c b/drivers/power/supply/ltc2941-battery-gauge.c index da49436176cd..30a9014b2f95 100644 --- a/drivers/power/supply/ltc2941-battery-gauge.c +++ b/drivers/power/supply/ltc2941-battery-gauge.c @@ -449,7 +449,7 @@ static int ltc294x_i2c_remove(struct i2c_client *client) { struct ltc294x_info *info = i2c_get_clientdata(client); - cancel_delayed_work(&info->work); + cancel_delayed_work_sync(&info->work); power_supply_unregister(info->supply); return 0; } -- cgit v1.2.3-58-ga151 From eb368de6de32925c65a97c1e929a31cae2155aee Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 25 Sep 2019 14:01:28 +0300 Subject: power: supply: sbs-battery: Fix a signedness bug in sbs_get_battery_capacity() The "mode" variable is an enum and in this context GCC treats it as an unsigned int so the error handling is never triggered. Fixes: 51d075660457 ("bq20z75: Add support for charge properties") Signed-off-by: Dan Carpenter Signed-off-by: Sebastian Reichel --- drivers/power/supply/sbs-battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index c01599ecb9c8..6acd242eed48 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -556,7 +556,7 @@ static int sbs_get_battery_capacity(struct i2c_client *client, mode = CAPACITY_MODE_AMPS; mode = sbs_set_capacity_mode(client, mode); - if (mode < 0) + if ((int)mode < 0) return mode; ret = sbs_read_word_data(client, sbs_data[reg_offset].addr); -- cgit v1.2.3-58-ga151 From d20267c9a98e082b718873bffbdfb44b126c995b Mon Sep 17 00:00:00 2001 From: Yauhen Kharuzhy Date: Thu, 2 Jan 2020 01:46:25 +0300 Subject: power: supply: bq25890_charger: Add support of BQ25892 and BQ25896 chips Support BQ25892 and BQ25896 chips by this driver. They shared one chip ID 0, so distinquish them by device revisions (2 for 25896 and 1 for 25892). Signed-off-by: Yauhen Kharuzhy Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq25890_charger.c | 97 ++++++++++++++++++++++++++-------- 1 file changed, 76 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index 9d1ec8d677de..a92c86ff211f 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -25,12 +25,20 @@ #define BQ25895_ID 7 #define BQ25896_ID 0 +enum bq25890_chip_version { + BQ25890, + BQ25892, + BQ25895, + BQ25896, +}; + enum bq25890_fields { F_EN_HIZ, F_EN_ILIM, F_IILIM, /* Reg00 */ F_BHOT, F_BCOLD, F_VINDPM_OFS, /* Reg01 */ F_CONV_START, F_CONV_RATE, F_BOOSTF, F_ICO_EN, F_HVDCP_EN, F_MAXC_EN, F_FORCE_DPM, F_AUTO_DPDM_EN, /* Reg02 */ - F_BAT_LOAD_EN, F_WD_RST, F_OTG_CFG, F_CHG_CFG, F_SYSVMIN, /* Reg03 */ + F_BAT_LOAD_EN, F_WD_RST, F_OTG_CFG, F_CHG_CFG, F_SYSVMIN, + F_MIN_VBAT_SEL, /* Reg03 */ F_PUMPX_EN, F_ICHG, /* Reg04 */ F_IPRECHG, F_ITERM, /* Reg05 */ F_VREG, F_BATLOWV, F_VRECHG, /* Reg06 */ @@ -39,8 +47,9 @@ enum bq25890_fields { F_BATCMP, F_VCLAMP, F_TREG, /* Reg08 */ F_FORCE_ICO, F_TMR2X_EN, F_BATFET_DIS, F_JEITA_VSET, F_BATFET_DLY, F_BATFET_RST_EN, F_PUMPX_UP, F_PUMPX_DN, /* Reg09 */ - F_BOOSTV, F_BOOSTI, /* Reg0A */ - F_VBUS_STAT, F_CHG_STAT, F_PG_STAT, F_SDP_STAT, F_VSYS_STAT, /* Reg0B */ + F_BOOSTV, F_PFM_OTG_DIS, F_BOOSTI, /* Reg0A */ + F_VBUS_STAT, F_CHG_STAT, F_PG_STAT, F_SDP_STAT, F_0B_RSVD, + F_VSYS_STAT, /* Reg0B */ F_WD_FAULT, F_BOOST_FAULT, F_CHG_FAULT, F_BAT_FAULT, F_NTC_FAULT, /* Reg0C */ F_FORCE_VINDPM, F_VINDPM, /* Reg0D */ @@ -91,7 +100,7 @@ struct bq25890_device { struct regmap *rmap; struct regmap_field *rmap_fields[F_MAX_FIELDS]; - int chip_id; + enum bq25890_chip_version chip_version; struct bq25890_init_data init_data; struct bq25890_state state; @@ -111,8 +120,7 @@ static const struct regmap_access_table bq25890_writeable_regs = { static const struct regmap_range bq25890_volatile_reg_ranges[] = { regmap_reg_range(0x00, 0x00), regmap_reg_range(0x09, 0x09), - regmap_reg_range(0x0b, 0x0c), - regmap_reg_range(0x0e, 0x14), + regmap_reg_range(0x0b, 0x14), }; static const struct regmap_access_table bq25890_volatile_regs = { @@ -155,7 +163,7 @@ static const struct reg_field bq25890_reg_fields[] = { [F_OTG_CFG] = REG_FIELD(0x03, 5, 5), [F_CHG_CFG] = REG_FIELD(0x03, 4, 4), [F_SYSVMIN] = REG_FIELD(0x03, 1, 3), - /* MIN_VBAT_SEL on BQ25896 */ + [F_MIN_VBAT_SEL] = REG_FIELD(0x03, 0, 0), // BQ25896 only /* REG04 */ [F_PUMPX_EN] = REG_FIELD(0x04, 7, 7), [F_ICHG] = REG_FIELD(0x04, 0, 6), @@ -188,8 +196,8 @@ static const struct reg_field bq25890_reg_fields[] = { [F_PUMPX_DN] = REG_FIELD(0x09, 0, 0), /* REG0A */ [F_BOOSTV] = REG_FIELD(0x0A, 4, 7), - /* PFM_OTG_DIS 3 on BQ25896 */ [F_BOOSTI] = REG_FIELD(0x0A, 0, 2), // reserved on BQ25895 + [F_PFM_OTG_DIS] = REG_FIELD(0x0A, 3, 3), // BQ25896 only /* REG0B */ [F_VBUS_STAT] = REG_FIELD(0x0B, 5, 7), [F_CHG_STAT] = REG_FIELD(0x0B, 3, 4), @@ -275,6 +283,7 @@ static const union { struct bq25890_lookup lt; } bq25890_tables[] = { /* range tables */ + /* TODO: BQ25896 has max ICHG 3008 mA */ [TBL_ICHG] = { .rt = {0, 5056000, 64000} }, /* uA */ [TBL_ITERM] = { .rt = {64000, 1024000, 64000} }, /* uA */ [TBL_VREG] = { .rt = {3840000, 4608000, 16000} }, /* uV */ @@ -391,11 +400,13 @@ static int bq25890_power_supply_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_MODEL_NAME: - if (bq->chip_id == BQ25890_ID) + if (bq->chip_version == BQ25890) val->strval = "BQ25890"; - else if (bq->chip_id == BQ25895_ID) + else if (bq->chip_version == BQ25892) + val->strval = "BQ25892"; + else if (bq->chip_version == BQ25895) val->strval = "BQ25895"; - else if (bq->chip_id == BQ25896_ID) + else if (bq->chip_version == BQ25896) val->strval = "BQ25896"; else val->strval = "UNKNOWN"; @@ -741,6 +752,56 @@ static int bq25890_usb_notifier(struct notifier_block *nb, unsigned long val, return NOTIFY_OK; } +static int bq25890_get_chip_version(struct bq25890_device *bq) +{ + int id, rev; + + id = bq25890_field_read(bq, F_PN); + if (id < 0) { + dev_err(bq->dev, "Cannot read chip ID.\n"); + return id; + } + + rev = bq25890_field_read(bq, F_DEV_REV); + if (rev < 0) { + dev_err(bq->dev, "Cannot read chip revision.\n"); + return id; + } + + switch (id) { + case BQ25890_ID: + bq->chip_version = BQ25890; + break; + + /* BQ25892 and BQ25896 share same ID 0 */ + case BQ25896_ID: + switch (rev) { + case 2: + bq->chip_version = BQ25896; + break; + case 1: + bq->chip_version = BQ25892; + break; + default: + dev_err(bq->dev, + "Unknown device revision %d, assume BQ25892\n", + rev); + bq->chip_version = BQ25892; + } + break; + + case BQ25895_ID: + bq->chip_version = BQ25895; + break; + + default: + dev_err(bq->dev, "Unknown chip ID %d\n", id); + return -ENODEV; + } + + return 0; +} + static int bq25890_irq_probe(struct bq25890_device *bq) { struct gpio_desc *irq; @@ -859,16 +920,10 @@ static int bq25890_probe(struct i2c_client *client, i2c_set_clientdata(client, bq); - bq->chip_id = bq25890_field_read(bq, F_PN); - if (bq->chip_id < 0) { - dev_err(dev, "Cannot read chip ID.\n"); - return bq->chip_id; - } - - if ((bq->chip_id != BQ25890_ID) && (bq->chip_id != BQ25895_ID) - && (bq->chip_id != BQ25896_ID)) { - dev_err(dev, "Chip with ID=%d, not supported!\n", bq->chip_id); - return -ENODEV; + ret = bq25890_get_chip_version(bq); + if (ret) { + dev_err(dev, "Cannot read chip ID or unknown chip.\n"); + return ret; } if (!dev->platform_data) { -- cgit v1.2.3-58-ga151 From 46aa27e742082ca4b1a30886876c24d26c2fac49 Mon Sep 17 00:00:00 2001 From: Yauhen Kharuzhy Date: Thu, 2 Jan 2020 01:46:26 +0300 Subject: power: supply: bq25890_charger: Add DT and I2C ids for all supported chips Add bq25892, bq25895 and bq25896 to list of supported device IDs for DeviceTree and I2C. Signed-off-by: Yauhen Kharuzhy Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq25890_charger.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index a92c86ff211f..785dbc6307b0 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -1041,12 +1041,18 @@ static const struct dev_pm_ops bq25890_pm = { static const struct i2c_device_id bq25890_i2c_ids[] = { { "bq25890", 0 }, + { "bq25892", 0 }, + { "bq25895", 0 }, + { "bq25896", 0 }, {}, }; MODULE_DEVICE_TABLE(i2c, bq25890_i2c_ids); static const struct of_device_id bq25890_of_match[] = { { .compatible = "ti,bq25890", }, + { .compatible = "ti,bq25892", }, + { .compatible = "ti,bq25895", }, + { .compatible = "ti,bq25896", }, { }, }; MODULE_DEVICE_TABLE(of, bq25890_of_match); -- cgit v1.2.3-58-ga151 From a4f06df13db829d3e8615f9bf476f0ea0e25c9a7 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 20 Dec 2019 17:31:17 +0200 Subject: power: reset: at91-poweroff: introduce struct shdwc_reg_config This driver uses AT91_PMC_MCKR in poweroff() function. But the SAM9X60's PMC versions maps AT91_PMC_MCKR functionality at different offset compared to the SAMA5D2's one. This patch prepares the field so that different AT91_PMC_MCKR's offsets to be introduced in struct reg_config so that proper offset to be used for AT91_PMC_MCKR based on compatible string. Signed-off-by: Claudiu Beznea Acked-by: Nicolas Ferre Signed-off-by: Sebastian Reichel --- drivers/power/reset/at91-sama5d2_shdwc.c | 54 +++++++++++++++++++------------- 1 file changed, 32 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/power/reset/at91-sama5d2_shdwc.c b/drivers/power/reset/at91-sama5d2_shdwc.c index 1c18f465a245..84806d20846b 100644 --- a/drivers/power/reset/at91-sama5d2_shdwc.c +++ b/drivers/power/reset/at91-sama5d2_shdwc.c @@ -66,7 +66,7 @@ #define SHDW_CFG_NOT_USED (32) -struct shdwc_config { +struct shdwc_reg_config { u8 wkup_pin_input; u8 mr_rtcwk_shift; u8 mr_rttwk_shift; @@ -74,8 +74,12 @@ struct shdwc_config { u8 sr_rttwk_shift; }; +struct reg_config { + struct shdwc_reg_config shdwc; +}; + struct shdwc { - const struct shdwc_config *cfg; + const struct reg_config *rcfg; struct clk *sclk; void __iomem *shdwc_base; void __iomem *mpddrc_base; @@ -95,6 +99,7 @@ static const unsigned long long sdwc_dbc_period[] = { static void __init at91_wakeup_status(struct platform_device *pdev) { struct shdwc *shdw = platform_get_drvdata(pdev); + const struct reg_config *rcfg = shdw->rcfg; u32 reg; char *reason = "unknown"; @@ -106,11 +111,11 @@ static void __init at91_wakeup_status(struct platform_device *pdev) if (!reg) return; - if (SHDW_WK_PIN(reg, shdw->cfg)) + if (SHDW_WK_PIN(reg, &rcfg->shdwc)) reason = "WKUP pin"; - else if (SHDW_RTCWK(reg, shdw->cfg)) + else if (SHDW_RTCWK(reg, &rcfg->shdwc)) reason = "RTC"; - else if (SHDW_RTTWK(reg, shdw->cfg)) + else if (SHDW_RTTWK(reg, &rcfg->shdwc)) reason = "RTT"; pr_info("AT91: Wake-Up source: %s\n", reason); @@ -215,6 +220,7 @@ static u32 at91_shdwc_get_wakeup_input(struct platform_device *pdev, static void at91_shdwc_dt_configure(struct platform_device *pdev) { struct shdwc *shdw = platform_get_drvdata(pdev); + const struct reg_config *rcfg = shdw->rcfg; struct device_node *np = pdev->dev.of_node; u32 mode = 0, tmp, input; @@ -227,10 +233,10 @@ static void at91_shdwc_dt_configure(struct platform_device *pdev) mode |= AT91_SHDW_WKUPDBC(at91_shdwc_debouncer_value(pdev, tmp)); if (of_property_read_bool(np, "atmel,wakeup-rtc-timer")) - mode |= SHDW_RTCWKEN(shdw->cfg); + mode |= SHDW_RTCWKEN(&rcfg->shdwc); if (of_property_read_bool(np, "atmel,wakeup-rtt-timer")) - mode |= SHDW_RTTWKEN(shdw->cfg); + mode |= SHDW_RTTWKEN(&rcfg->shdwc); dev_dbg(&pdev->dev, "%s: mode = %#x\n", __func__, mode); writel(mode, shdw->shdwc_base + AT91_SHDW_MR); @@ -239,30 +245,34 @@ static void at91_shdwc_dt_configure(struct platform_device *pdev) writel(input, shdw->shdwc_base + AT91_SHDW_WUIR); } -static const struct shdwc_config sama5d2_shdwc_config = { - .wkup_pin_input = 0, - .mr_rtcwk_shift = 17, - .mr_rttwk_shift = SHDW_CFG_NOT_USED, - .sr_rtcwk_shift = 5, - .sr_rttwk_shift = SHDW_CFG_NOT_USED, +static const struct reg_config sama5d2_reg_config = { + .shdwc = { + .wkup_pin_input = 0, + .mr_rtcwk_shift = 17, + .mr_rttwk_shift = SHDW_CFG_NOT_USED, + .sr_rtcwk_shift = 5, + .sr_rttwk_shift = SHDW_CFG_NOT_USED, + }, }; -static const struct shdwc_config sam9x60_shdwc_config = { - .wkup_pin_input = 0, - .mr_rtcwk_shift = 17, - .mr_rttwk_shift = 16, - .sr_rtcwk_shift = 5, - .sr_rttwk_shift = 4, +static const struct reg_config sam9x60_reg_config = { + .shdwc = { + .wkup_pin_input = 0, + .mr_rtcwk_shift = 17, + .mr_rttwk_shift = 16, + .sr_rtcwk_shift = 5, + .sr_rttwk_shift = 4, + }, }; static const struct of_device_id at91_shdwc_of_match[] = { { .compatible = "atmel,sama5d2-shdwc", - .data = &sama5d2_shdwc_config, + .data = &sama5d2_reg_config, }, { .compatible = "microchip,sam9x60-shdwc", - .data = &sam9x60_shdwc_config, + .data = &sam9x60_reg_config, }, { /*sentinel*/ } @@ -303,7 +313,7 @@ static int __init at91_shdwc_probe(struct platform_device *pdev) } match = of_match_node(at91_shdwc_of_match, pdev->dev.of_node); - at91_shdwc->cfg = match->data; + at91_shdwc->rcfg = match->data; at91_shdwc->sclk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(at91_shdwc->sclk)) -- cgit v1.2.3-58-ga151 From d39284f21de799c8f23b19a048855ac208d59633 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 20 Dec 2019 17:31:18 +0200 Subject: power: reset: at91-poweroff: use proper master clock register offset SAM9X60's PMC uses different offset for master clock register. Add a member of type struct pmc_reg_config in struct reg_config, fill it correspondingly for SAMA5D2 and SAM9X60 and use it in poweroff() function. Signed-off-by: Claudiu Beznea Acked-by: Nicolas Ferre Signed-off-by: Sebastian Reichel --- drivers/power/reset/at91-sama5d2_shdwc.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/power/reset/at91-sama5d2_shdwc.c b/drivers/power/reset/at91-sama5d2_shdwc.c index 84806d20846b..2fe3a627cb53 100644 --- a/drivers/power/reset/at91-sama5d2_shdwc.c +++ b/drivers/power/reset/at91-sama5d2_shdwc.c @@ -74,8 +74,13 @@ struct shdwc_reg_config { u8 sr_rttwk_shift; }; +struct pmc_reg_config { + u8 mckr; +}; + struct reg_config { struct shdwc_reg_config shdwc; + struct pmc_reg_config pmc; }; struct shdwc { @@ -136,9 +141,9 @@ static void at91_poweroff(void) " str %1, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t" /* Switch the master clock source to slow clock. */ - "1: ldr r6, [%4, #" __stringify(AT91_PMC_MCKR) "]\n\t" + "1: ldr r6, [%4, %5]\n\t" " bic r6, r6, #" __stringify(AT91_PMC_CSS) "\n\t" - " str r6, [%4, #" __stringify(AT91_PMC_MCKR) "]\n\t" + " str r6, [%4, %5]\n\t" /* Wait for clock switch. */ "2: ldr r6, [%4, #" __stringify(AT91_PMC_SR) "]\n\t" " tst r6, #" __stringify(AT91_PMC_MCKRDY) "\n\t" @@ -153,7 +158,8 @@ static void at91_poweroff(void) "r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF), "r" (at91_shdwc->shdwc_base), "r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW), - "r" (at91_shdwc->pmc_base) + "r" (at91_shdwc->pmc_base), + "r" (at91_shdwc->rcfg->pmc.mckr) : "r6"); } @@ -253,6 +259,9 @@ static const struct reg_config sama5d2_reg_config = { .sr_rtcwk_shift = 5, .sr_rttwk_shift = SHDW_CFG_NOT_USED, }, + .pmc = { + .mckr = 0x30, + }, }; static const struct reg_config sam9x60_reg_config = { @@ -263,6 +272,9 @@ static const struct reg_config sam9x60_reg_config = { .sr_rtcwk_shift = 5, .sr_rttwk_shift = 4, }, + .pmc = { + .mckr = 0x28, + }, }; static const struct of_device_id at91_shdwc_of_match[] = { -- cgit v1.2.3-58-ga151 From 577233a3f5637aff40e376572d5ed24ff80204d0 Mon Sep 17 00:00:00 2001 From: Chen Wandun Date: Fri, 20 Dec 2019 17:41:44 +0800 Subject: power: suppy: ucs1002: Make the symbol 'ucs1002_regulator_enable' static Fix the following sparse warning: drivers/power/supply/ucs1002_power.c:492:5: warning: symbol 'ucs1002_regulator_enable' was not declared. Should it be static? Fixes: a3d70dacc727 ("power: suppy: ucs1002: disable power when max current is 0") Signed-off-by: Chen Wandun Signed-off-by: Sebastian Reichel --- drivers/power/supply/ucs1002_power.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/power/supply/ucs1002_power.c b/drivers/power/supply/ucs1002_power.c index 0ca80d00b80a..cdb9a23d825f 100644 --- a/drivers/power/supply/ucs1002_power.c +++ b/drivers/power/supply/ucs1002_power.c @@ -489,7 +489,7 @@ static irqreturn_t ucs1002_alert_irq(int irq, void *data) return IRQ_HANDLED; } -int ucs1002_regulator_enable(struct regulator_dev *rdev) +static int ucs1002_regulator_enable(struct regulator_dev *rdev) { struct ucs1002_info *info = rdev_get_drvdata(rdev); -- cgit v1.2.3-58-ga151 From 56900d4541a9e2e9263a2db9246e216edad5f928 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Tue, 14 Jan 2020 21:40:45 -0600 Subject: power: supply: axp20x_usb_power: Use a match structure Instead of ad-hoc variant ID checks throughout the code, let's start moving the variant-specific details to a match structure. This allows for future flexibility, and it better matches the other axp20x power supply drivers. This commit removes most variant checks from axp20x_usb_power_probe(). Other parts of the driver still do ID matching; they are left unchanged for now. Reviewed-by: Chen-Yu Tsai Signed-off-by: Samuel Holland Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp20x_usb_power.c | 86 ++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c index dd3f3f12e41d..cd7071057457 100644 --- a/drivers/power/supply/axp20x_usb_power.c +++ b/drivers/power/supply/axp20x_usb_power.c @@ -405,6 +405,50 @@ static const struct power_supply_desc axp22x_usb_power_desc = { .set_property = axp20x_usb_power_set_property, }; +static const char * const axp20x_irq_names[] = { + "VBUS_PLUGIN", + "VBUS_REMOVAL", + "VBUS_VALID", + "VBUS_NOT_VALID", + NULL +}; + +static const char * const axp22x_irq_names[] = { + "VBUS_PLUGIN", + "VBUS_REMOVAL", + NULL +}; + +struct axp_data { + const struct power_supply_desc *power_desc; + const char * const *irq_names; + enum axp20x_variants axp20x_id; +}; + +static const struct axp_data axp202_data = { + .power_desc = &axp20x_usb_power_desc, + .irq_names = axp20x_irq_names, + .axp20x_id = AXP202_ID, +}; + +static const struct axp_data axp221_data = { + .power_desc = &axp22x_usb_power_desc, + .irq_names = axp22x_irq_names, + .axp20x_id = AXP221_ID, +}; + +static const struct axp_data axp223_data = { + .power_desc = &axp22x_usb_power_desc, + .irq_names = axp22x_irq_names, + .axp20x_id = AXP223_ID, +}; + +static const struct axp_data axp813_data = { + .power_desc = &axp22x_usb_power_desc, + .irq_names = axp22x_irq_names, + .axp20x_id = AXP813_ID, +}; + static int configure_iio_channels(struct platform_device *pdev, struct axp20x_usb_power *power) { @@ -440,12 +484,7 @@ static int axp20x_usb_power_probe(struct platform_device *pdev) struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); struct power_supply_config psy_cfg = {}; struct axp20x_usb_power *power; - static const char * const axp20x_irq_names[] = { "VBUS_PLUGIN", - "VBUS_REMOVAL", "VBUS_VALID", "VBUS_NOT_VALID", NULL }; - static const char * const axp22x_irq_names[] = { - "VBUS_PLUGIN", "VBUS_REMOVAL", NULL }; - const char * const *irq_names; - const struct power_supply_desc *usb_power_desc; + const struct axp_data *axp_data; int i, irq, ret; if (!of_device_is_available(pdev->dev.of_node)) @@ -461,9 +500,9 @@ static int axp20x_usb_power_probe(struct platform_device *pdev) return -ENOMEM; platform_set_drvdata(pdev, power); - power->axp20x_id = (enum axp20x_variants)of_device_get_match_data( - &pdev->dev); + axp_data = of_device_get_match_data(&pdev->dev); + power->axp20x_id = axp_data->axp20x_id; power->regmap = axp20x->regmap; if (power->axp20x_id == AXP202_ID) { @@ -481,18 +520,6 @@ static int axp20x_usb_power_probe(struct platform_device *pdev) if (ret) return ret; - - usb_power_desc = &axp20x_usb_power_desc; - irq_names = axp20x_irq_names; - } else if (power->axp20x_id == AXP221_ID || - power->axp20x_id == AXP223_ID || - power->axp20x_id == AXP813_ID) { - usb_power_desc = &axp22x_usb_power_desc; - irq_names = axp22x_irq_names; - } else { - dev_err(&pdev->dev, "Unsupported AXP variant: %ld\n", - axp20x->variant); - return -EINVAL; } if (power->axp20x_id == AXP813_ID) { @@ -504,17 +531,18 @@ static int axp20x_usb_power_probe(struct platform_device *pdev) psy_cfg.of_node = pdev->dev.of_node; psy_cfg.drv_data = power; - power->supply = devm_power_supply_register(&pdev->dev, usb_power_desc, + power->supply = devm_power_supply_register(&pdev->dev, + axp_data->power_desc, &psy_cfg); if (IS_ERR(power->supply)) return PTR_ERR(power->supply); /* Request irqs after registering, as irqs may trigger immediately */ - for (i = 0; irq_names[i]; i++) { - irq = platform_get_irq_byname(pdev, irq_names[i]); + for (i = 0; axp_data->irq_names[i]; i++) { + irq = platform_get_irq_byname(pdev, axp_data->irq_names[i]); if (irq < 0) { dev_warn(&pdev->dev, "No IRQ for %s: %d\n", - irq_names[i], irq); + axp_data->irq_names[i], irq); continue; } irq = regmap_irq_get_virq(axp20x->regmap_irqc, irq); @@ -522,7 +550,7 @@ static int axp20x_usb_power_probe(struct platform_device *pdev) axp20x_usb_power_irq, 0, DRVNAME, power); if (ret < 0) dev_warn(&pdev->dev, "Error requesting %s IRQ: %d\n", - irq_names[i], ret); + axp_data->irq_names[i], ret); } INIT_DELAYED_WORK(&power->vbus_detect, axp20x_usb_power_poll_vbus); @@ -544,16 +572,16 @@ static int axp20x_usb_power_remove(struct platform_device *pdev) static const struct of_device_id axp20x_usb_power_match[] = { { .compatible = "x-powers,axp202-usb-power-supply", - .data = (void *)AXP202_ID, + .data = &axp202_data, }, { .compatible = "x-powers,axp221-usb-power-supply", - .data = (void *)AXP221_ID, + .data = &axp221_data, }, { .compatible = "x-powers,axp223-usb-power-supply", - .data = (void *)AXP223_ID, + .data = &axp223_data, }, { .compatible = "x-powers,axp813-usb-power-supply", - .data = (void *)AXP813_ID, + .data = &axp813_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, axp20x_usb_power_match); -- cgit v1.2.3-58-ga151 From ecbc8dd78ed9fab5bc02d8cf2e839a3a0f2cdc5f Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Tue, 14 Jan 2020 21:40:46 -0600 Subject: power: supply: axp20x_usb_power: Allow offlining AXP803/AXP813 have a flag that enables/disables the USB power supply input. Allow control of this flag via the ONLINE property on those variants. It may be necessary to offline the USB power supply input when using the USB port in OTG mode, or to allow userspace to disable charging. When the USB VBUS input is disabled via the PATH_SEL bit, the VBUS_USED bit in PWR_INPUT_STATUS is cleared, so there is no change needed when getting the property. Reviewed-by: Chen-Yu Tsai Signed-off-by: Samuel Holland Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp20x_usb_power.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'drivers') diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c index cd7071057457..f14736041c41 100644 --- a/drivers/power/supply/axp20x_usb_power.c +++ b/drivers/power/supply/axp20x_usb_power.c @@ -29,6 +29,9 @@ #define AXP20X_USB_STATUS_VBUS_VALID BIT(2) +#define AXP20X_VBUS_PATH_SEL BIT(7) +#define AXP20X_VBUS_PATH_SEL_OFFSET 7 + #define AXP20X_VBUS_VHOLD_uV(b) (4000000 + (((b) >> 3) & 7) * 100000) #define AXP20X_VBUS_VHOLD_MASK GENMASK(5, 3) #define AXP20X_VBUS_VHOLD_OFFSET 3 @@ -263,6 +266,16 @@ static int axp20x_usb_power_get_property(struct power_supply *psy, return 0; } +static int axp813_usb_power_set_online(struct axp20x_usb_power *power, + int intval) +{ + int val = !intval << AXP20X_VBUS_PATH_SEL_OFFSET; + + return regmap_update_bits(power->regmap, + AXP20X_VBUS_IPSOUT_MGMT, + AXP20X_VBUS_PATH_SEL, val); +} + static int axp20x_usb_power_set_voltage_min(struct axp20x_usb_power *power, int intval) { @@ -344,6 +357,11 @@ static int axp20x_usb_power_set_property(struct power_supply *psy, struct axp20x_usb_power *power = power_supply_get_drvdata(psy); switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + if (power->axp20x_id != AXP813_ID) + return -EINVAL; + return axp813_usb_power_set_online(power, val->intval); + case POWER_SUPPLY_PROP_VOLTAGE_MIN: return axp20x_usb_power_set_voltage_min(power, val->intval); @@ -363,6 +381,18 @@ static int axp20x_usb_power_set_property(struct power_supply *psy, static int axp20x_usb_power_prop_writeable(struct power_supply *psy, enum power_supply_property psp) { + struct axp20x_usb_power *power = power_supply_get_drvdata(psy); + + /* + * The VBUS path select flag works differently on on AXP288 and newer: + * - On AXP20x and AXP22x, the flag enables VBUS (ignoring N_VBUSEN). + * - On AXP288 and AXP8xx, the flag disables VBUS (ignoring N_VBUSEN). + * We only expose the control on variants where it can be used to force + * the VBUS input offline. + */ + if (psp == POWER_SUPPLY_PROP_ONLINE) + return power->axp20x_id == AXP813_ID; + return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN || psp == POWER_SUPPLY_PROP_CURRENT_MAX; } -- cgit v1.2.3-58-ga151 From 09aaaec5f658a7cf04c6318ac794eafdb23fa240 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Tue, 14 Jan 2020 21:40:47 -0600 Subject: power: supply: axp20x_usb_power: Add wakeup control The USB power supply input can be used as a wakeup source. Hook up the VBUS_PLUGIN IRQ to trigger wakeup based on userspace configuration. To do this, we must remember the list of IRQs for the life of the device. To know how much space to allocate for the flexible array member, we switch from using a NULL sentinel to using an array length. Because we now depend on the specific order of the IRQs (we assume VBUS_PLUGIN is first and always present), failing to acquire an IRQ during probe must be a fatal error. To avoid spuriously waking up the system when the USB power supply is not configured as a wakeup source, we must explicitly disable all non- wake IRQs during system suspend. This is because the SoC's NMI input is shared among all IRQs on the AXP PMIC. Due to the use of regmap-irq, the individual IRQs within the PMIC are nested threaded interrupts, and are therefore not automatically disabled during system suspend. The upshot is that if any other device within the MFD (such as the power key) is an enabled wakeup source, all enabled IRQs within the PMIC will cause wakeup. We still need to call enable_irq_wake() when we *do* want wakeup, in case those other wakeup sources on the PMIC are all disabled. Reviewed-by: Chen-Yu Tsai Signed-off-by: Samuel Holland Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp20x_usb_power.c | 83 ++++++++++++++++++++++++++------- 1 file changed, 67 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c index f14736041c41..1732304bc96e 100644 --- a/drivers/power/supply/axp20x_usb_power.c +++ b/drivers/power/supply/axp20x_usb_power.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -67,6 +68,8 @@ struct axp20x_usb_power { struct iio_channel *vbus_i; struct delayed_work vbus_detect; unsigned int old_status; + unsigned int num_irqs; + unsigned int irqs[]; }; static irqreturn_t axp20x_usb_power_irq(int irq, void *devid) @@ -440,45 +443,85 @@ static const char * const axp20x_irq_names[] = { "VBUS_REMOVAL", "VBUS_VALID", "VBUS_NOT_VALID", - NULL }; static const char * const axp22x_irq_names[] = { "VBUS_PLUGIN", "VBUS_REMOVAL", - NULL }; struct axp_data { const struct power_supply_desc *power_desc; const char * const *irq_names; + unsigned int num_irq_names; enum axp20x_variants axp20x_id; }; static const struct axp_data axp202_data = { .power_desc = &axp20x_usb_power_desc, .irq_names = axp20x_irq_names, + .num_irq_names = ARRAY_SIZE(axp20x_irq_names), .axp20x_id = AXP202_ID, }; static const struct axp_data axp221_data = { .power_desc = &axp22x_usb_power_desc, .irq_names = axp22x_irq_names, + .num_irq_names = ARRAY_SIZE(axp22x_irq_names), .axp20x_id = AXP221_ID, }; static const struct axp_data axp223_data = { .power_desc = &axp22x_usb_power_desc, .irq_names = axp22x_irq_names, + .num_irq_names = ARRAY_SIZE(axp22x_irq_names), .axp20x_id = AXP223_ID, }; static const struct axp_data axp813_data = { .power_desc = &axp22x_usb_power_desc, .irq_names = axp22x_irq_names, + .num_irq_names = ARRAY_SIZE(axp22x_irq_names), .axp20x_id = AXP813_ID, }; +#ifdef CONFIG_PM_SLEEP +static int axp20x_usb_power_suspend(struct device *dev) +{ + struct axp20x_usb_power *power = dev_get_drvdata(dev); + int i = 0; + + /* + * Allow wake via VBUS_PLUGIN only. + * + * As nested threaded IRQs are not automatically disabled during + * suspend, we must explicitly disable the remainder of the IRQs. + */ + if (device_may_wakeup(&power->supply->dev)) + enable_irq_wake(power->irqs[i++]); + while (i < power->num_irqs) + disable_irq(power->irqs[i++]); + + return 0; +} + +static int axp20x_usb_power_resume(struct device *dev) +{ + struct axp20x_usb_power *power = dev_get_drvdata(dev); + int i = 0; + + if (device_may_wakeup(&power->supply->dev)) + disable_irq_wake(power->irqs[i++]); + while (i < power->num_irqs) + enable_irq(power->irqs[i++]); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(axp20x_usb_power_pm_ops, axp20x_usb_power_suspend, + axp20x_usb_power_resume); + static int configure_iio_channels(struct platform_device *pdev, struct axp20x_usb_power *power) { @@ -525,15 +568,19 @@ static int axp20x_usb_power_probe(struct platform_device *pdev) return -EINVAL; } - power = devm_kzalloc(&pdev->dev, sizeof(*power), GFP_KERNEL); + axp_data = of_device_get_match_data(&pdev->dev); + + power = devm_kzalloc(&pdev->dev, + struct_size(power, irqs, axp_data->num_irq_names), + GFP_KERNEL); if (!power) return -ENOMEM; platform_set_drvdata(pdev, power); - axp_data = of_device_get_match_data(&pdev->dev); power->axp20x_id = axp_data->axp20x_id; power->regmap = axp20x->regmap; + power->num_irqs = axp_data->num_irq_names; if (power->axp20x_id == AXP202_ID) { /* Enable vbus valid checking */ @@ -568,19 +615,22 @@ static int axp20x_usb_power_probe(struct platform_device *pdev) return PTR_ERR(power->supply); /* Request irqs after registering, as irqs may trigger immediately */ - for (i = 0; axp_data->irq_names[i]; i++) { + for (i = 0; i < axp_data->num_irq_names; i++) { irq = platform_get_irq_byname(pdev, axp_data->irq_names[i]); if (irq < 0) { - dev_warn(&pdev->dev, "No IRQ for %s: %d\n", - axp_data->irq_names[i], irq); - continue; + dev_err(&pdev->dev, "No IRQ for %s: %d\n", + axp_data->irq_names[i], irq); + return irq; + } + power->irqs[i] = regmap_irq_get_virq(axp20x->regmap_irqc, irq); + ret = devm_request_any_context_irq(&pdev->dev, power->irqs[i], + axp20x_usb_power_irq, 0, + DRVNAME, power); + if (ret < 0) { + dev_err(&pdev->dev, "Error requesting %s IRQ: %d\n", + axp_data->irq_names[i], ret); + return ret; } - irq = regmap_irq_get_virq(axp20x->regmap_irqc, irq); - ret = devm_request_any_context_irq(&pdev->dev, irq, - axp20x_usb_power_irq, 0, DRVNAME, power); - if (ret < 0) - dev_warn(&pdev->dev, "Error requesting %s IRQ: %d\n", - axp_data->irq_names[i], ret); } INIT_DELAYED_WORK(&power->vbus_detect, axp20x_usb_power_poll_vbus); @@ -620,8 +670,9 @@ static struct platform_driver axp20x_usb_power_driver = { .probe = axp20x_usb_power_probe, .remove = axp20x_usb_power_remove, .driver = { - .name = DRVNAME, - .of_match_table = axp20x_usb_power_match, + .name = DRVNAME, + .of_match_table = axp20x_usb_power_match, + .pm = &axp20x_usb_power_pm_ops, }, }; -- cgit v1.2.3-58-ga151 From bcfb7ae3f50be4d51346a8cf69097cf59b29d05b Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Tue, 14 Jan 2020 21:40:48 -0600 Subject: power: supply: axp20x_usb_power: Only poll while offline Investigation on the AXP803 shows that VBUS_PLUGIN/VBUS_REMOVAL IRQs are triggered on the rising/falling edge of AXP20X_PWR_STATUS_VBUS_USED. The reason IRQs do not arrive while N_VBUSEN/DRIVEVBUS is high is because AXP20X_PWR_STATUS_VBUS_USED also never goes high. This also means that if VBUS is online, a VBUS_REMOVAL IRQ is received immediately on setting N_VBUSEN/DRIVEVBUS high (and VBUS_PLUGIN shortly after it is set back low). This was also verified to be the case when manually offlining VBUS through AXP20X_VBUS_PATH_SELECT. As long as VBUS is online, a present->absent transition necessarily implies an online->offline transition. Since will cause an IRQ, there is no need to poll while VBUS is online. To ensure the driver's view of VBUS online status remains accurate, unconditionally poll once when receiving an IRQ and when resuming. If VBUS is still online at that time, polling will cease until the next VBUS_REMOVAL IRQ. Reviewed-by: Chen-Yu Tsai Signed-off-by: Samuel Holland Signed-off-by: Sebastian Reichel --- drivers/power/supply/axp20x_usb_power.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c index 1732304bc96e..4fde24b5f35a 100644 --- a/drivers/power/supply/axp20x_usb_power.c +++ b/drivers/power/supply/axp20x_usb_power.c @@ -68,16 +68,32 @@ struct axp20x_usb_power { struct iio_channel *vbus_i; struct delayed_work vbus_detect; unsigned int old_status; + unsigned int online; unsigned int num_irqs; unsigned int irqs[]; }; +static bool axp20x_usb_vbus_needs_polling(struct axp20x_usb_power *power) +{ + /* + * Polling is only necessary while VBUS is offline. While online, a + * present->absent transition implies an online->offline transition + * and will triger the VBUS_REMOVAL IRQ. + */ + if (power->axp20x_id >= AXP221_ID && !power->online) + return true; + + return false; +} + static irqreturn_t axp20x_usb_power_irq(int irq, void *devid) { struct axp20x_usb_power *power = devid; power_supply_changed(power->supply); + mod_delayed_work(system_wq, &power->vbus_detect, DEBOUNCE_TIME); + return IRQ_HANDLED; } @@ -97,17 +113,11 @@ static void axp20x_usb_power_poll_vbus(struct work_struct *work) power_supply_changed(power->supply); power->old_status = val; + power->online = val & AXP20X_PWR_STATUS_VBUS_USED; out: - mod_delayed_work(system_wq, &power->vbus_detect, DEBOUNCE_TIME); -} - -static bool axp20x_usb_vbus_needs_polling(struct axp20x_usb_power *power) -{ - if (power->axp20x_id >= AXP221_ID) - return true; - - return false; + if (axp20x_usb_vbus_needs_polling(power)) + mod_delayed_work(system_wq, &power->vbus_detect, DEBOUNCE_TIME); } static int axp20x_get_current_max(struct axp20x_usb_power *power, int *val) @@ -515,6 +525,8 @@ static int axp20x_usb_power_resume(struct device *dev) while (i < power->num_irqs) enable_irq(power->irqs[i++]); + mod_delayed_work(system_wq, &power->vbus_detect, DEBOUNCE_TIME); + return 0; } #endif -- cgit v1.2.3-58-ga151 From cb619e80333acc59ce9eb29d56ef6e3b749bf474 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 16 Jan 2020 17:09:00 +0000 Subject: power: supply: bq25890_charger: fix incorrect error return when bq25890_field_read fails Currently a read failure by bq25890_field_read on F_DEV_REV is returning an error in id instead of rev. Fix this by returning the value in rev. Addresses-Coverity: ("Copy-paste error") Fixes: d20267c9a98e ("power: supply: bq25890_charger: Add support of BQ25892 and BQ25896 chips") Signed-off-by: Colin Ian King Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq25890_charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index 785dbc6307b0..aebd1253dbc9 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -765,7 +765,7 @@ static int bq25890_get_chip_version(struct bq25890_device *bq) rev = bq25890_field_read(bq, F_DEV_REV); if (rev < 0) { dev_err(bq->dev, "Cannot read chip revision.\n"); - return id; + return rev; } switch (id) { -- cgit v1.2.3-58-ga151 From 3d32a8437c051013352baf5cbd0844fcf5d2f409 Mon Sep 17 00:00:00 2001 From: Chen Zhou Date: Wed, 15 Jan 2020 21:40:10 +0800 Subject: power: supply: ipaq_micro_battery: remove unneeded semicolon Fixes coccicheck warning: ./drivers/power/supply/ipaq_micro_battery.c:188:2-3: Unneeded semicolon ./drivers/power/supply/ipaq_micro_battery.c:152:3-4: Unneeded semicolon ./drivers/power/supply/ipaq_micro_battery.c:171:2-3: Unneeded semicolon Signed-off-by: Chen Zhou Signed-off-by: Sebastian Reichel --- drivers/power/supply/ipaq_micro_battery.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/power/supply/ipaq_micro_battery.c b/drivers/power/supply/ipaq_micro_battery.c index 03592ceaca88..192d9db0fb00 100644 --- a/drivers/power/supply/ipaq_micro_battery.c +++ b/drivers/power/supply/ipaq_micro_battery.c @@ -149,7 +149,7 @@ static int micro_batt_get_property(struct power_supply *b, default: val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; break; - }; + } break; case POWER_SUPPLY_PROP_STATUS: val->intval = get_status(b); @@ -168,7 +168,7 @@ static int micro_batt_get_property(struct power_supply *b, break; default: return -EINVAL; - }; + } return 0; } @@ -185,7 +185,7 @@ static int micro_ac_get_property(struct power_supply *b, break; default: return -EINVAL; - }; + } return 0; } -- cgit v1.2.3-58-ga151