diff options
22 files changed, 335 insertions, 191 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-power b/Documentation/ABI/testing/sysfs-class-power index ca830c6cd809..f7904efc4cfa 100644 --- a/Documentation/ABI/testing/sysfs-class-power +++ b/Documentation/ABI/testing/sysfs-class-power @@ -480,6 +480,19 @@ Description: Valid values: Represented in microvolts +What: /sys/class/power_supply/<supply_name>/cycle_count +Date: January 2010 +Contact: linux-pm@vger.kernel.org +Description: + Reports the number of full charge + discharge cycles the + battery has undergone. + + Access: Read + + Valid values: + Integer > 0: representing full cycles + Integer = 0: cycle_count info is not available + **USB Properties** What: /sys/class/power_supply/<supply_name>/input_current_limit diff --git a/Documentation/devicetree/bindings/power/supply/maxim,max17040.yaml b/Documentation/devicetree/bindings/power/supply/maxim,max17040.yaml index f792d06db413..ffb344987a7b 100644 --- a/Documentation/devicetree/bindings/power/supply/maxim,max17040.yaml +++ b/Documentation/devicetree/bindings/power/supply/maxim,max17040.yaml @@ -62,7 +62,7 @@ required: - compatible - reg -additionalProperties: false +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/power/supply/samsung,battery.yaml b/Documentation/devicetree/bindings/power/supply/samsung,battery.yaml new file mode 100644 index 000000000000..40292d581b10 --- /dev/null +++ b/Documentation/devicetree/bindings/power/supply/samsung,battery.yaml @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/power/supply/samsung,battery.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Samsung SDI Batteries + +maintainers: + - Linus Walleij <linus.walleij@linaro.org> + +description: | + Samsung SDI (Samsung Digital Interface) batteries are all different versions + of lithium ion chemistry devices used for mobile phones, laptops and other + portable electronics. The batteries are adapted to a specific product and + the physical restrictions make it impossible to use another battery with the + product, so product device trees can specify these batteries. Operating + systems should determine hardware characteristics of the batteries from the + compatible string. + +properties: + compatible: + oneOf: + - const: samsung,eb-l1m7flu + description: 3.8V 1500 mAh battery used in Samsung GT-I8190 + - const: samsung,eb425161la + description: 3.8V 1500 mAh battery used in Samsung SGH-T599 and SGH-I407 + - const: samsung,eb425161lu + description: 3.8V 1500 mAh battery used in Samsung GT-I8160 + - const: samsung,eb485159lu + description: 3.8V 1700 mAh battery used in Samsung GT-S7710 + - const: samsung,eb535151vu + description: 3.8V 1500 mAh battery used in Samsung GT-I9070 + - const: samsung,eb585157lu + description: 3.8V 2000 mAh battery used in Samsung GT-I8530 + +required: + - compatible + +additionalProperties: false + +examples: + - | + power { + #address-cells = <1>; + #size-cells = <0>; + + battery: battery { + compatible = "samsung,eb425161la"; + }; + + charger@11 { + reg = <0x11>; + monitored-battery = <&battery>; + }; + }; diff --git a/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-btemp.yaml b/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-btemp.yaml index 2f57aa5a5f4e..4b8a00cec39c 100644 --- a/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-btemp.yaml +++ b/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-btemp.yaml @@ -17,10 +17,14 @@ properties: compatible: const: stericsson,ab8500-btemp - battery: + monitored-battery: $ref: /schemas/types.yaml#/definitions/phandle description: phandle to battery node + battery: + $ref: /schemas/types.yaml#/definitions/phandle + deprecated: true + interrupts: maxItems: 5 @@ -42,7 +46,7 @@ properties: required: - compatible - - battery + - monitored-battery - interrupts - interrupt-names - io-channels @@ -56,7 +60,7 @@ examples: pmic { battery-temperature { compatible = "stericsson,ab8500-btemp"; - battery = <&ab8500_battery>; + monitored-battery = <&battery>; interrupts = <20 IRQ_TYPE_LEVEL_HIGH>, <80 IRQ_TYPE_LEVEL_HIGH>, <83 IRQ_TYPE_LEVEL_HIGH>, diff --git a/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-chargalg.yaml b/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-chargalg.yaml index 0897231c2f6e..6799224f7fb4 100644 --- a/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-chargalg.yaml +++ b/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-chargalg.yaml @@ -17,13 +17,17 @@ properties: compatible: const: stericsson,ab8500-chargalg - battery: + monitored-battery: $ref: /schemas/types.yaml#/definitions/phandle description: phandle to battery node + battery: + $ref: /schemas/types.yaml#/definitions/phandle + deprecated: true + required: - compatible - - battery + - monitored-battery additionalProperties: false @@ -32,6 +36,6 @@ examples: pmic { charging-algorithm { compatible = "stericsson,ab8500-chargalg"; - battery = <&ab8500_battery>; + monitored-battery = <&ab8500_battery>; }; }; diff --git a/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-charger.yaml b/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-charger.yaml index e13305afea69..9518eb7289d0 100644 --- a/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-charger.yaml +++ b/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-charger.yaml @@ -17,10 +17,14 @@ properties: compatible: const: stericsson,ab8500-charger - battery: + monitored-battery: $ref: /schemas/types.yaml#/definitions/phandle description: phandle to battery node + battery: + $ref: /schemas/types.yaml#/definitions/phandle + deprecated: true + vddadc-supply: description: Supply for USB and Main charger @@ -66,7 +70,7 @@ properties: required: - compatible - - battery + - monitored-battery - vddadc-supply - interrupts - interrupt-names @@ -81,7 +85,7 @@ examples: pmic { charger { compatible = "stericsson,ab8500-charger"; - battery = <&ab8500_battery>; + monitored-battery = <&battery>; vddadc-supply = <&ab8500_ldo_tvout_reg>; interrupts = <10 IRQ_TYPE_LEVEL_HIGH>, <11 IRQ_TYPE_LEVEL_HIGH>, diff --git a/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-fg.yaml b/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-fg.yaml index db342e5ac0d1..54ac42a9d354 100644 --- a/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-fg.yaml +++ b/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-fg.yaml @@ -17,10 +17,14 @@ properties: compatible: const: stericsson,ab8500-fg - battery: + monitored-battery: $ref: /schemas/types.yaml#/definitions/phandle description: phandle to battery node + battery: + $ref: /schemas/types.yaml#/definitions/phandle + deprecated: true + interrupts: maxItems: 5 @@ -41,7 +45,7 @@ properties: required: - compatible - - battery + - monitored-battery - interrupts - interrupt-names - io-channels @@ -55,7 +59,7 @@ examples: pmic { fuel-gauge { compatible = "stericsson,ab8500-fg"; - battery = <&ab8500_battery>; + monitored-battery = <&battery>; interrupts = <24 IRQ_TYPE_LEVEL_HIGH>, <8 IRQ_TYPE_LEVEL_HIGH>, <28 IRQ_TYPE_LEVEL_HIGH>, diff --git a/MAINTAINERS b/MAINTAINERS index 4eb537e015d1..886639f1fa0b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11508,6 +11508,27 @@ S: Maintained F: Documentation/devicetree/bindings/iio/proximity/maxbotix,mb1232.yaml F: drivers/iio/proximity/mb1232.c +MAXIM MAX17040 FAMILY FUEL GAUGE DRIVERS +R: Iskren Chernev <iskren.chernev@gmail.com> +R: Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com> +R: Marek Szyprowski <m.szyprowski@samsung.com> +R: Matheus Castello <matheus@castello.eng.br> +L: linux-pm@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/power/supply/maxim,max17040.yaml +F: drivers/power/supply/max17040_battery.c + +MAXIM MAX17042 FAMILY FUEL GAUGE DRIVERS +R: Hans de Goede <hdegoede@redhat.com> +R: Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com> +R: Marek Szyprowski <m.szyprowski@samsung.com> +R: Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm> +R: Purism Kernel Team <kernel@puri.sm> +L: linux-pm@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/power/supply/maxim,max17042.yaml +F: drivers/power/supply/max17042_battery.c + MAXIM MAX77650 PMIC MFD DRIVER M: Bartosz Golaszewski <brgl@bgdev.pl> L: linux-kernel@vger.kernel.org diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c index 026649409135..64def79d557a 100644 --- a/drivers/power/reset/at91-reset.c +++ b/drivers/power/reset/at91-reset.c @@ -193,7 +193,7 @@ static int __init at91_reset_probe(struct platform_device *pdev) return -ENOMEM; reset->rstc_base = devm_of_iomap(&pdev->dev, pdev->dev.of_node, 0, NULL); - if (!reset->rstc_base) { + if (IS_ERR(reset->rstc_base)) { dev_err(&pdev->dev, "Could not map reset controller address\n"); return -ENODEV; } @@ -203,7 +203,7 @@ static int __init at91_reset_probe(struct platform_device *pdev) for_each_matching_node_and_match(np, at91_ramc_of_match, &match) { reset->ramc_lpr = (u32)match->data; reset->ramc_base[idx] = devm_of_iomap(&pdev->dev, np, 0, NULL); - if (!reset->ramc_base[idx]) { + if (IS_ERR(reset->ramc_base[idx])) { dev_err(&pdev->dev, "Could not map ram controller address\n"); of_node_put(np); return -ENODEV; diff --git a/drivers/power/reset/ltc2952-poweroff.c b/drivers/power/reset/ltc2952-poweroff.c index 8688c8ba8894..fbb344353fe4 100644 --- a/drivers/power/reset/ltc2952-poweroff.c +++ b/drivers/power/reset/ltc2952-poweroff.c @@ -94,7 +94,6 @@ static struct ltc2952_poweroff *ltc2952_data; */ static enum hrtimer_restart ltc2952_poweroff_timer_wde(struct hrtimer *timer) { - ktime_t now; int state; struct ltc2952_poweroff *data = to_ltc2952(timer, timer_wde); @@ -104,8 +103,7 @@ static enum hrtimer_restart ltc2952_poweroff_timer_wde(struct hrtimer *timer) state = gpiod_get_value(data->gpio_watchdog); gpiod_set_value(data->gpio_watchdog, !state); - now = hrtimer_cb_get_time(timer); - hrtimer_forward(timer, now, data->wde_interval); + hrtimer_forward_now(timer, data->wde_interval); return HRTIMER_RESTART; } diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index fcc7534edcb2..5cf5bb56d2e3 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -351,7 +351,7 @@ config AXP20X_POWER config AXP288_CHARGER tristate "X-Powers AXP288 Charger" - depends on MFD_AXP20X && EXTCON_AXP288 + depends on MFD_AXP20X && EXTCON_AXP288 && IOSF_MBI help Say yes here to have support X-Power AXP288 power management IC (PMIC) integrated charger. @@ -366,20 +366,22 @@ config AXP288_FUEL_GAUGE over/under temperature. config BATTERY_MAX17040 - tristate "Maxim MAX17040 Fuel Gauge" + tristate "Maxim MAX17040/17041/17043 family Fuel Gauge" depends on I2C select REGMAP_I2C help - Maxim models with ModelGauge are fuel-gauge systems for lithium-ion - (Li+) batteries in handheld and portable equipment, including - max17040, max17041, max17043, max17044, max17048, max17049, max17058, - max17059. It is also included in some batteries like max77836. + Driver supports Maxim fuel-gauge systems for lithium-ion (Li+) + batteries used mainly in handheld and portable equipment. + Supported devices: max17040, max17041, max17043, max17044, max17048, + max17049, max17058, max17059, max77836. Driver supports reporting SOC (State of Charge, i.e capacity), voltage and configurable low-SOC wakeup interrupt. + Driver can be build as a module (max17040_battery). + config BATTERY_MAX17042 - tristate "Maxim MAX17042/17047/17050/8997/8966 Fuel Gauge" + tristate "Maxim MAX17042/17047/17050/8997/8966 family Fuel Gauge" depends on I2C select REGMAP_I2C help @@ -387,8 +389,11 @@ config BATTERY_MAX17042 in handheld and portable equipment. The MAX17042 is configured to operate with a single lithium cell. MAX8997 and MAX8966 are multi-function devices that include fuel gauages that are compatible - with MAX17042. This driver also supports max17047/50 chips which are - improved version of max17042. + with MAX17042. + Supported devices: max8966, max8997, max17042, max17047, max17050, + max17055, max77693, max77849. + + Driver can be build as a module (max17042_battery). config BATTERY_MAX1721X tristate "MAX17211/MAX17215 standalone gas-gauge" diff --git a/drivers/power/supply/ab8500_bmdata.c b/drivers/power/supply/ab8500_bmdata.c index 6f5fb794042c..bfc1245d7912 100644 --- a/drivers/power/supply/ab8500_bmdata.c +++ b/drivers/power/supply/ab8500_bmdata.c @@ -497,8 +497,7 @@ int ab8500_bm_of_probe(struct device *dev, const char *btech; int i; - /* get phandle to 'battery-info' node */ - battery_node = of_parse_phandle(np, "battery", 0); + battery_node = of_parse_phandle(np, "monitored-battery", 0); if (!battery_node) { dev_err(dev, "battery node or reference missing\n"); return -EINVAL; diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c index b9553be9bed5..ec41f6cd3f93 100644 --- a/drivers/power/supply/axp288_charger.c +++ b/drivers/power/supply/axp288_charger.c @@ -22,6 +22,7 @@ #include <linux/mfd/axp20x.h> #include <linux/extcon.h> #include <linux/dmi.h> +#include <asm/iosf_mbi.h> #define PS_STAT_VBUS_TRIGGER BIT(0) #define PS_STAT_BAT_CHRG_DIR BIT(2) @@ -95,6 +96,8 @@ #define CV_4200MV 4200 /* 4200mV */ #define CV_4350MV 4350 /* 4350mV */ +#define AXP288_REG_UPDATE_INTERVAL (60 * HZ) + #define AXP288_EXTCON_DEV_NAME "axp288_extcon" #define USB_HOST_EXTCON_HID "INT3496" #define USB_HOST_EXTCON_NAME "INT3496:00" @@ -118,6 +121,7 @@ struct axp288_chrg_info { struct regmap_irq_chip_data *regmap_irqc; int irq[CHRG_INTR_END]; struct power_supply *psy_usb; + struct mutex lock; /* OTG/Host mode */ struct { @@ -138,6 +142,12 @@ struct axp288_chrg_info { int cv; int max_cc; int max_cv; + + unsigned long last_updated; + unsigned int input_status; + unsigned int op_mode; + unsigned int backend_control; + bool valid; }; static inline int axp288_charger_set_cc(struct axp288_chrg_info *info, int cc) @@ -197,11 +207,8 @@ static inline int axp288_charger_set_cv(struct axp288_chrg_info *info, int cv) static int axp288_charger_get_vbus_inlmt(struct axp288_chrg_info *info) { unsigned int val; - int ret; - ret = regmap_read(info->regmap, AXP20X_CHRG_BAK_CTRL, &val); - if (ret < 0) - return ret; + val = info->backend_control; val >>= CHRG_VBUS_ILIM_BIT_POS; switch (val) { @@ -295,63 +302,19 @@ static int axp288_charger_enable_charger(struct axp288_chrg_info *info, return ret; } -static int axp288_charger_is_present(struct axp288_chrg_info *info) -{ - int ret, present = 0; - unsigned int val; - - ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val); - if (ret < 0) - return ret; - - if (val & PS_STAT_VBUS_PRESENT) - present = 1; - return present; -} - -static int axp288_charger_is_online(struct axp288_chrg_info *info) -{ - int ret, online = 0; - unsigned int val; - - ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val); - if (ret < 0) - return ret; - - if (val & PS_STAT_VBUS_VALID) - online = 1; - return online; -} - static int axp288_get_charger_health(struct axp288_chrg_info *info) { - int ret, pwr_stat, chrg_stat; - int health = POWER_SUPPLY_HEALTH_UNKNOWN; - unsigned int val; - - ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val); - if ((ret < 0) || !(val & PS_STAT_VBUS_PRESENT)) - goto health_read_fail; + if (!(info->input_status & PS_STAT_VBUS_PRESENT)) + return POWER_SUPPLY_HEALTH_UNKNOWN; + + if (!(info->input_status & PS_STAT_VBUS_VALID)) + return POWER_SUPPLY_HEALTH_DEAD; + else if (info->op_mode & CHRG_STAT_PMIC_OTP) + return POWER_SUPPLY_HEALTH_OVERHEAT; + else if (info->op_mode & CHRG_STAT_BAT_SAFE_MODE) + return POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; else - pwr_stat = val; - - ret = regmap_read(info->regmap, AXP20X_PWR_OP_MODE, &val); - if (ret < 0) - goto health_read_fail; - else - chrg_stat = val; - - if (!(pwr_stat & PS_STAT_VBUS_VALID)) - health = POWER_SUPPLY_HEALTH_DEAD; - else if (chrg_stat & CHRG_STAT_PMIC_OTP) - health = POWER_SUPPLY_HEALTH_OVERHEAT; - else if (chrg_stat & CHRG_STAT_BAT_SAFE_MODE) - health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; - else - health = POWER_SUPPLY_HEALTH_GOOD; - -health_read_fail: - return health; + return POWER_SUPPLY_HEALTH_GOOD; } static int axp288_charger_usb_set_property(struct power_supply *psy, @@ -362,30 +325,86 @@ static int axp288_charger_usb_set_property(struct power_supply *psy, int ret = 0; int scaled_val; + mutex_lock(&info->lock); switch (psp) { case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: scaled_val = min(val->intval, info->max_cc); scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000); ret = axp288_charger_set_cc(info, scaled_val); - if (ret < 0) + if (ret < 0) { dev_warn(&info->pdev->dev, "set charge current failed\n"); + goto out; + } break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: scaled_val = min(val->intval, info->max_cv); scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000); ret = axp288_charger_set_cv(info, scaled_val); - if (ret < 0) + if (ret < 0) { dev_warn(&info->pdev->dev, "set charge voltage failed\n"); + goto out; + } break; case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: ret = axp288_charger_set_vbus_inlmt(info, val->intval); - if (ret < 0) + if (ret < 0) { dev_warn(&info->pdev->dev, "set input current limit failed\n"); + goto out; + } + info->valid = false; break; default: ret = -EINVAL; } +out: + mutex_unlock(&info->lock); + return ret; +} + +static int axp288_charger_reg_readb(struct axp288_chrg_info *info, int reg, unsigned int *ret_val) +{ + int ret; + + ret = regmap_read(info->regmap, reg, ret_val); + if (ret < 0) { + dev_err(&info->pdev->dev, "Error %d on reading value from register 0x%04x\n", + ret, + reg); + return ret; + } + return 0; +} + +static int axp288_charger_usb_update_property(struct axp288_chrg_info *info) +{ + int ret = 0; + + if (info->valid && time_before(jiffies, info->last_updated + AXP288_REG_UPDATE_INTERVAL)) + return 0; + + dev_dbg(&info->pdev->dev, "Charger updating register values...\n"); + + ret = iosf_mbi_block_punit_i2c_access(); + if (ret < 0) + return ret; + + ret = axp288_charger_reg_readb(info, AXP20X_PWR_INPUT_STATUS, &info->input_status); + if (ret < 0) + goto out; + + ret = axp288_charger_reg_readb(info, AXP20X_PWR_OP_MODE, &info->op_mode); + if (ret < 0) + goto out; + + ret = axp288_charger_reg_readb(info, AXP20X_CHRG_BAK_CTRL, &info->backend_control); + if (ret < 0) + goto out; + + info->last_updated = jiffies; + info->valid = true; +out: + iosf_mbi_unblock_punit_i2c_access(); return ret; } @@ -396,6 +415,11 @@ static int axp288_charger_usb_get_property(struct power_supply *psy, struct axp288_chrg_info *info = power_supply_get_drvdata(psy); int ret; + mutex_lock(&info->lock); + ret = axp288_charger_usb_update_property(info); + if (ret < 0) + goto out; + switch (psp) { case POWER_SUPPLY_PROP_PRESENT: /* Check for OTG case first */ @@ -403,10 +427,7 @@ static int axp288_charger_usb_get_property(struct power_supply *psy, val->intval = 0; break; } - ret = axp288_charger_is_present(info); - if (ret < 0) - return ret; - val->intval = ret; + val->intval = (info->input_status & PS_STAT_VBUS_PRESENT) ? 1 : 0; break; case POWER_SUPPLY_PROP_ONLINE: /* Check for OTG case first */ @@ -414,10 +435,7 @@ static int axp288_charger_usb_get_property(struct power_supply *psy, val->intval = 0; break; } - ret = axp288_charger_is_online(info); - if (ret < 0) - return ret; - val->intval = ret; + val->intval = (info->input_status & PS_STAT_VBUS_VALID) ? 1 : 0; break; case POWER_SUPPLY_PROP_HEALTH: val->intval = axp288_get_charger_health(info); @@ -435,16 +453,15 @@ static int axp288_charger_usb_get_property(struct power_supply *psy, val->intval = info->max_cv * 1000; break; case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: - ret = axp288_charger_get_vbus_inlmt(info); - if (ret < 0) - return ret; - val->intval = ret; + val->intval = axp288_charger_get_vbus_inlmt(info); break; default: - return -EINVAL; + ret = -EINVAL; } - return 0; +out: + mutex_unlock(&info->lock); + return ret; } static int axp288_charger_property_is_writeable(struct power_supply *psy, @@ -540,7 +557,9 @@ static irqreturn_t axp288_charger_irq_thread_handler(int irq, void *dev) dev_warn(&info->pdev->dev, "Spurious Interrupt!!!\n"); goto out; } - + mutex_lock(&info->lock); + info->valid = false; + mutex_unlock(&info->lock); power_supply_changed(info->psy_usb); out: return IRQ_HANDLED; @@ -613,6 +632,9 @@ static void axp288_charger_extcon_evt_worker(struct work_struct *work) if (!(val & PS_STAT_VBUS_VALID)) { dev_dbg(&info->pdev->dev, "USB charger disconnected\n"); axp288_charger_enable_charger(info, false); + mutex_lock(&info->lock); + info->valid = false; + mutex_unlock(&info->lock); power_supply_changed(info->psy_usb); return; } @@ -644,6 +666,9 @@ static void axp288_charger_extcon_evt_worker(struct work_struct *work) dev_err(&info->pdev->dev, "error setting current limit (%d)\n", ret); + mutex_lock(&info->lock); + info->valid = false; + mutex_unlock(&info->lock); power_supply_changed(info->psy_usb); } @@ -817,6 +842,7 @@ static int axp288_charger_probe(struct platform_device *pdev) if (!info) return -ENOMEM; + mutex_init(&info->lock); info->pdev = pdev; info->regmap = axp20x->regmap; info->regmap_irqc = axp20x->regmap_irqc; diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index 945c3257ca93..34ec186a2e9a 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -682,16 +682,16 @@ static int bq25890_hw_init(struct bq25890_device *bq) } } - /* Configure ADC for continuous conversions when charging */ - ret = bq25890_field_write(bq, F_CONV_RATE, !!bq->state.online); + ret = bq25890_get_chip_state(bq, &bq->state); if (ret < 0) { - dev_dbg(bq->dev, "Config ADC failed %d\n", ret); + dev_dbg(bq->dev, "Get state failed %d\n", ret); return ret; } - ret = bq25890_get_chip_state(bq, &bq->state); + /* Configure ADC for continuous conversions when charging */ + ret = bq25890_field_write(bq, F_CONV_RATE, !!bq->state.online); if (ret < 0) { - dev_dbg(bq->dev, "Get state failed %d\n", ret); + dev_dbg(bq->dev, "Config ADC failed %d\n", ret); return ret; } @@ -734,8 +734,9 @@ static int bq25890_power_supply_init(struct bq25890_device *bq) psy_cfg.supplied_to = bq25890_charger_supplied_to; psy_cfg.num_supplicants = ARRAY_SIZE(bq25890_charger_supplied_to); - bq->charger = power_supply_register(bq->dev, &bq25890_power_supply_desc, - &psy_cfg); + bq->charger = devm_power_supply_register(bq->dev, + &bq25890_power_supply_desc, + &psy_cfg); return PTR_ERR_OR_ZERO(bq->charger); } @@ -788,13 +789,13 @@ static int bq25890_get_chip_version(struct bq25890_device *bq) id = bq25890_field_read(bq, F_PN); if (id < 0) { - dev_err(bq->dev, "Cannot read chip ID.\n"); + dev_err(bq->dev, "Cannot read chip ID: %d\n", id); return id; } rev = bq25890_field_read(bq, F_DEV_REV); if (rev < 0) { - dev_err(bq->dev, "Cannot read chip revision.\n"); + dev_err(bq->dev, "Cannot read chip revision: %d\n", rev); return rev; } @@ -837,10 +838,9 @@ static int bq25890_irq_probe(struct bq25890_device *bq) struct gpio_desc *irq; irq = devm_gpiod_get(bq->dev, BQ25890_IRQ_PIN, GPIOD_IN); - if (IS_ERR(irq)) { - dev_err(bq->dev, "Could not probe irq pin.\n"); - return PTR_ERR(irq); - } + if (IS_ERR(irq)) + return dev_err_probe(bq->dev, PTR_ERR(irq), + "Could not probe irq pin.\n"); return gpiod_to_irq(irq); } @@ -929,34 +929,33 @@ static int bq25890_probe(struct i2c_client *client, mutex_init(&bq->lock); bq->rmap = devm_regmap_init_i2c(client, &bq25890_regmap_config); - if (IS_ERR(bq->rmap)) { - dev_err(dev, "failed to allocate register map\n"); - return PTR_ERR(bq->rmap); - } + if (IS_ERR(bq->rmap)) + return dev_err_probe(dev, PTR_ERR(bq->rmap), + "failed to allocate register map\n"); for (i = 0; i < ARRAY_SIZE(bq25890_reg_fields); i++) { const struct reg_field *reg_fields = bq25890_reg_fields; bq->rmap_fields[i] = devm_regmap_field_alloc(dev, bq->rmap, reg_fields[i]); - if (IS_ERR(bq->rmap_fields[i])) { - dev_err(dev, "cannot allocate regmap field\n"); - return PTR_ERR(bq->rmap_fields[i]); - } + if (IS_ERR(bq->rmap_fields[i])) + return dev_err_probe(dev, PTR_ERR(bq->rmap_fields[i]), + "cannot allocate regmap field\n"); } i2c_set_clientdata(client, bq); ret = bq25890_get_chip_version(bq); if (ret) { - dev_err(dev, "Cannot read chip ID or unknown chip.\n"); + dev_err(dev, "Cannot read chip ID or unknown chip: %d\n", ret); return ret; } if (!dev->platform_data) { ret = bq25890_fw_probe(bq); if (ret < 0) { - dev_err(dev, "Cannot read device properties.\n"); + dev_err(dev, "Cannot read device properties: %d\n", + ret); return ret; } } else { @@ -965,7 +964,7 @@ static int bq25890_probe(struct i2c_client *client, ret = bq25890_hw_init(bq); if (ret < 0) { - dev_err(dev, "Cannot initialize the chip.\n"); + dev_err(dev, "Cannot initialize the chip: %d\n", ret); return ret; } @@ -985,22 +984,22 @@ static int bq25890_probe(struct i2c_client *client, usb_register_notifier(bq->usb_phy, &bq->usb_nb); } + ret = bq25890_power_supply_init(bq); + if (ret < 0) { + dev_err(dev, "Failed to register power supply\n"); + goto err_unregister_usb_notifier; + } + ret = devm_request_threaded_irq(dev, client->irq, NULL, bq25890_irq_handler_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, BQ25890_IRQ_PIN, bq); if (ret) - goto irq_fail; - - ret = bq25890_power_supply_init(bq); - if (ret < 0) { - dev_err(dev, "Failed to register power supply\n"); - goto irq_fail; - } + goto err_unregister_usb_notifier; return 0; -irq_fail: +err_unregister_usb_notifier: if (!IS_ERR_OR_NULL(bq->usb_phy)) usb_unregister_notifier(bq->usb_phy, &bq->usb_nb); @@ -1011,8 +1010,6 @@ static int bq25890_remove(struct i2c_client *client) { struct bq25890_device *bq = i2c_get_clientdata(client); - power_supply_unregister(bq->charger); - if (!IS_ERR_OR_NULL(bq->usb_phy)) usb_unregister_notifier(bq->usb_phy, &bq->usb_nb); diff --git a/drivers/power/supply/bq27xxx_battery_i2c.c b/drivers/power/supply/bq27xxx_battery_i2c.c index 46f078350fd3..cf38cbfe13e9 100644 --- a/drivers/power/supply/bq27xxx_battery_i2c.c +++ b/drivers/power/supply/bq27xxx_battery_i2c.c @@ -187,7 +187,8 @@ static int bq27xxx_battery_i2c_probe(struct i2c_client *client, dev_err(&client->dev, "Unable to register IRQ %d error %d\n", client->irq, ret); - return ret; + bq27xxx_battery_teardown(di); + goto err_failed; } } diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 8d62d4241da3..18e3ff0e15d5 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -1026,20 +1026,13 @@ static const struct power_supply_desc cpcap_charger_battery_desc = { static int cpcap_battery_probe(struct platform_device *pdev) { struct cpcap_battery_ddata *ddata; - const struct of_device_id *match; struct power_supply_config psy_cfg = {}; int error; + const struct cpcap_battery_config *cfg; - match = of_match_device(of_match_ptr(cpcap_battery_id_table), - &pdev->dev); - if (!match) - return -EINVAL; - - if (!match->data) { - dev_err(&pdev->dev, "no configuration data found\n"); - + cfg = device_get_match_data(&pdev->dev); + if (!cfg) return -ENODEV; - } ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); if (!ddata) @@ -1047,7 +1040,7 @@ static int cpcap_battery_probe(struct platform_device *pdev) INIT_LIST_HEAD(&ddata->irq_list); ddata->dev = &pdev->dev; - memcpy(&ddata->config, match->data, sizeof(ddata->config)); + memcpy(&ddata->config, cfg, sizeof(ddata->config)); ddata->reg = dev_get_regmap(ddata->dev->parent, NULL); if (!ddata->reg) diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c index 3cea92e28dc3..a9aef1e8b186 100644 --- a/drivers/power/supply/max17040_battery.c +++ b/drivers/power/supply/max17040_battery.c @@ -449,6 +449,8 @@ static int max17040_probe(struct i2c_client *client, chip->client = client; chip->regmap = devm_regmap_init_i2c(client, &max17040_regmap); + if (IS_ERR(chip->regmap)) + return PTR_ERR(chip->regmap); chip_id = (enum chip_id) id->driver_data; if (client->dev.of_node) { ret = max17040_get_of_data(chip); diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 8dffae76b6a3..87128cf0d577 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -313,7 +313,10 @@ static int max17042_get_property(struct power_supply *psy, val->intval = data * 625 / 8; break; case POWER_SUPPLY_PROP_CAPACITY: - ret = regmap_read(map, MAX17042_RepSOC, &data); + if (chip->pdata->enable_current_sense) + ret = regmap_read(map, MAX17042_RepSOC, &data); + else + ret = regmap_read(map, MAX17042_VFSOC, &data); if (ret < 0) return ret; @@ -783,7 +786,7 @@ static inline void max17042_override_por_values(struct max17042_chip *chip) if ((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_LAvg_empty, config->lavg_empty); + max17042_override_por(map, MAX17042_IAvg_empty, config->iavg_empty); max17042_override_por(map, MAX17042_TempNom, config->temp_nom); max17042_override_por(map, MAX17042_TempLim, config->temp_lim); max17042_override_por(map, MAX17042_FCTC, config->fctc); @@ -857,7 +860,8 @@ static void max17042_set_soc_threshold(struct max17042_chip *chip, u16 off) regmap_read(map, MAX17042_RepSOC, &soc); soc >>= 8; soc_tr = (soc + off) << 8; - soc_tr |= (soc - off); + if (off < soc) + soc_tr |= soc - off; regmap_write(map, MAX17042_SALRT_Th, soc_tr); } @@ -876,6 +880,10 @@ static irqreturn_t max17042_thread_handler(int id, void *dev) max17042_set_soc_threshold(chip, 1); } + /* we implicitly handle all alerts via power_supply_changed */ + regmap_clear_bits(chip->regmap, MAX17042_STATUS, + 0xFFFF & ~(STATUS_POR_BIT | STATUS_BST_BIT)); + power_supply_changed(chip->battery); return IRQ_HANDLED; } diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index 0c2132c7f5d4..fc12a4f407f4 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -951,6 +951,22 @@ void power_supply_unreg_notifier(struct notifier_block *nb) } EXPORT_SYMBOL_GPL(power_supply_unreg_notifier); +static bool psy_has_property(const struct power_supply_desc *psy_desc, + enum power_supply_property psp) +{ + bool found = false; + int i; + + for (i = 0; i < psy_desc->num_properties; i++) { + if (psy_desc->properties[i] == psp) { + found = true; + break; + } + } + + return found; +} + #ifdef CONFIG_THERMAL static int power_supply_read_temp(struct thermal_zone_device *tzd, int *temp) @@ -977,24 +993,23 @@ static struct thermal_zone_device_ops psy_tzd_ops = { static int psy_register_thermal(struct power_supply *psy) { - int i, ret; + int ret; if (psy->desc->no_thermal) return 0; /* Register battery zone device psy reports temperature */ - for (i = 0; i < psy->desc->num_properties; i++) { - if (psy->desc->properties[i] == POWER_SUPPLY_PROP_TEMP) { - psy->tzd = thermal_zone_device_register(psy->desc->name, - 0, 0, psy, &psy_tzd_ops, NULL, 0, 0); - if (IS_ERR(psy->tzd)) - return PTR_ERR(psy->tzd); - ret = thermal_zone_device_enable(psy->tzd); - if (ret) - thermal_zone_device_unregister(psy->tzd); - return ret; - } + if (psy_has_property(psy->desc, POWER_SUPPLY_PROP_TEMP)) { + psy->tzd = thermal_zone_device_register(psy->desc->name, + 0, 0, psy, &psy_tzd_ops, NULL, 0, 0); + if (IS_ERR(psy->tzd)) + return PTR_ERR(psy->tzd); + ret = thermal_zone_device_enable(psy->tzd); + if (ret) + thermal_zone_device_unregister(psy->tzd); + return ret; } + return 0; } @@ -1065,18 +1080,14 @@ static const struct thermal_cooling_device_ops psy_tcd_ops = { static int psy_register_cooler(struct power_supply *psy) { - int i; - /* Register for cooling device if psy can control charging */ - for (i = 0; i < psy->desc->num_properties; i++) { - if (psy->desc->properties[i] == - POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT) { - psy->tcd = thermal_cooling_device_register( - (char *)psy->desc->name, - psy, &psy_tcd_ops); - return PTR_ERR_OR_ZERO(psy->tcd); - } + if (psy_has_property(psy->desc, POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT)) { + psy->tcd = thermal_cooling_device_register( + (char *)psy->desc->name, + psy, &psy_tcd_ops); + return PTR_ERR_OR_ZERO(psy->tcd); } + return 0; } @@ -1114,7 +1125,7 @@ __power_supply_register(struct device *parent, { struct device *dev; struct power_supply *psy; - int i, rc; + int rc; if (!parent) pr_warn("%s: Expected proper parent device for '%s'\n", @@ -1123,11 +1134,9 @@ __power_supply_register(struct device *parent, if (!desc || !desc->name || !desc->properties || !desc->num_properties) return ERR_PTR(-EINVAL); - for (i = 0; i < desc->num_properties; ++i) { - if ((desc->properties[i] == POWER_SUPPLY_PROP_USB_TYPE) && - (!desc->usb_types || !desc->num_usb_types)) - return ERR_PTR(-EINVAL); - } + if (psy_has_property(desc, POWER_SUPPLY_PROP_USB_TYPE) && + (!desc->usb_types || !desc->num_usb_types)) + return ERR_PTR(-EINVAL); psy = kzalloc(sizeof(*psy), GFP_KERNEL); if (!psy) diff --git a/drivers/power/supply/rt5033_battery.c b/drivers/power/supply/rt5033_battery.c index 9ad0afe83d1b..7a23c70f4879 100644 --- a/drivers/power/supply/rt5033_battery.c +++ b/drivers/power/supply/rt5033_battery.c @@ -60,7 +60,7 @@ static int rt5033_battery_get_watt_prop(struct i2c_client *client, regmap_read(battery->regmap, regh, &msb); regmap_read(battery->regmap, regl, &lsb); - ret = ((msb << 4) + (lsb >> 4)) * 1250 / 1000; + ret = ((msb << 4) + (lsb >> 4)) * 1250; return ret; } diff --git a/drivers/power/supply/wm831x_power.c b/drivers/power/supply/wm831x_power.c index 4cd2dd870039..82e31066c746 100644 --- a/drivers/power/supply/wm831x_power.c +++ b/drivers/power/supply/wm831x_power.c @@ -234,7 +234,7 @@ static struct chg_map chg_times[] = { { 510, 15 << WM831X_CHG_TIME_SHIFT }, }; -static void wm831x_battey_apply_config(struct wm831x *wm831x, +static void wm831x_battery_apply_config(struct wm831x *wm831x, struct chg_map *map, int count, int val, int *reg, const char *name, const char *units) @@ -281,24 +281,24 @@ static void wm831x_config_battery(struct wm831x *wm831x) if (pdata->fast_enable) reg1 |= WM831X_CHG_FAST; - wm831x_battey_apply_config(wm831x, trickle_ilims, + wm831x_battery_apply_config(wm831x, trickle_ilims, ARRAY_SIZE(trickle_ilims), pdata->trickle_ilim, ®2, "trickle charge current limit", "mA"); - wm831x_battey_apply_config(wm831x, vsels, ARRAY_SIZE(vsels), + wm831x_battery_apply_config(wm831x, vsels, ARRAY_SIZE(vsels), pdata->vsel, ®2, "target voltage", "mV"); - wm831x_battey_apply_config(wm831x, fast_ilims, ARRAY_SIZE(fast_ilims), + wm831x_battery_apply_config(wm831x, fast_ilims, ARRAY_SIZE(fast_ilims), pdata->fast_ilim, ®2, "fast charge current limit", "mA"); - wm831x_battey_apply_config(wm831x, eoc_iterms, ARRAY_SIZE(eoc_iterms), + wm831x_battery_apply_config(wm831x, eoc_iterms, ARRAY_SIZE(eoc_iterms), pdata->eoc_iterm, ®1, "end of charge current threshold", "mA"); - wm831x_battey_apply_config(wm831x, chg_times, ARRAY_SIZE(chg_times), + wm831x_battery_apply_config(wm831x, chg_times, ARRAY_SIZE(chg_times), pdata->timeout, ®2, "charger timeout", "min"); diff --git a/include/linux/power/max17042_battery.h b/include/linux/power/max17042_battery.h index dd24756a8af7..c417abd2ab70 100644 --- a/include/linux/power/max17042_battery.h +++ b/include/linux/power/max17042_battery.h @@ -78,7 +78,7 @@ enum max17042_register { MAX17042_T_empty = 0x34, MAX17042_FullCAP0 = 0x35, - MAX17042_LAvg_empty = 0x36, + MAX17042_IAvg_empty = 0x36, MAX17042_FCTC = 0x37, MAX17042_RCOMP0 = 0x38, MAX17042_TempCo = 0x39, @@ -221,7 +221,7 @@ struct max17042_config_data { u16 fullcap; /* 0x10 */ u16 fullcapnom; /* 0x23 */ u16 socempty; /* 0x33 */ - u16 lavg_empty; /* 0x36 */ + u16 iavg_empty; /* 0x36 */ u16 dqacc; /* 0x45 */ u16 dpacc; /* 0x46 */ u16 qrtbl00; /* 0x12 */ |