diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-01-31 12:55:31 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-01-31 12:55:31 -0800 |
commit | 972058ad79b124086d931b6a0a25fb9bb52502dc (patch) | |
tree | 1be7b08a433dea703c799d8c83796ba57d152fb1 | |
parent | a9e89e54483629ae68a44ced46cf590358beac2a (diff) | |
parent | 2d7e6a8376c0eaf260f54b265acadd2e185d7b82 (diff) | |
download | op-kernel-dev-972058ad79b124086d931b6a0a25fb9bb52502dc.zip op-kernel-dev-972058ad79b124086d931b6a0a25fb9bb52502dc.tar.gz |
Merge tag 'for-v4.16' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply
Pull power supply and reset updates from Sebastian Reichel:
- bq27xxx: add bq27521 support
- drop unused imx-snvs-poweroff driver
- improve axp288 driver
- misc fixes
* tag 'for-v4.16' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (32 commits)
power: supply: max17042_battery: Always fall back to default platform-data
power: supply: max17042_battery: Check battery current for status when supplied
MAINTAINERS: Add AXP288 PMIC entry
power: supply: axp288_fuel_gauge: Do not register our psy on (some) HDMI sticks
power: supply: axp288_fuel_gauge: Optimize get_current()
power: supply: axp288_fuel_gauge: Rework get_status()
power: reset: account for const type of of_device_id.data
power: supply: account for const type of of_device_id.data
bq24190: Simplify code in property_is_writeable
power: supply: axp288_fuel_gauge: Get iio-channels once during boot
power: supply: axp288_charger: Properly stop work on probe-error / remove
power: supply: axp288_charger: Simplify extcon cable handling
power: supply: axp288_charger: Use the right property for the input current limit
power: supply: axp288_charger: Pick lower input current limit not higher
power: supply: axp288_charger: Do not cache input current limit value
power: supply: axp288_charger: Remove no longer needed locking
power: supply: axp288_charger: Use regmap_update_bits to set the input limits
power: supply: axp288_charger: Cleanup some double empty lines
power: supply: axp288_charger: Remove charger-enabled state tracking
power: supply: axp288_charger: Add missing newlines to some messages
...
22 files changed, 394 insertions, 489 deletions
diff --git a/Documentation/devicetree/bindings/power/reset/imx-snvs-poweroff.txt b/Documentation/devicetree/bindings/power/reset/imx-snvs-poweroff.txt deleted file mode 100644 index 1b81fcd..0000000 --- a/Documentation/devicetree/bindings/power/reset/imx-snvs-poweroff.txt +++ /dev/null @@ -1,23 +0,0 @@ -i.mx6 Poweroff Driver - -SNVS_LPCR in SNVS module can power off the whole system by pull -PMIC_ON_REQ low if PMIC_ON_REQ is connected with external PMIC. -If you don't want to use PMIC_ON_REQ as power on/off control, -please set status='disabled' to disable this driver. - -Required Properties: --compatible: "fsl,sec-v4.0-poweroff" --reg: Specifies the physical address of the SNVS_LPCR register - -Example: - snvs@20cc000 { - compatible = "fsl,sec-v4.0-mon", "simple-bus"; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0 0x020cc000 0x4000>; - ..... - snvs_poweroff: snvs-poweroff@38 { - compatible = "fsl,sec-v4.0-poweroff"; - reg = <0x38 0x4>; - }; - } diff --git a/Documentation/devicetree/bindings/power/supply/bq27xxx.txt b/Documentation/devicetree/bindings/power/supply/bq27xxx.txt index 6858e1a..615c1cb 100644 --- a/Documentation/devicetree/bindings/power/supply/bq27xxx.txt +++ b/Documentation/devicetree/bindings/power/supply/bq27xxx.txt @@ -15,6 +15,7 @@ Required properties: * "ti,bq27520g2" - BQ27520-g2 * "ti,bq27520g3" - BQ27520-g3 * "ti,bq27520g4" - BQ27520-g4 + * "ti,bq27521" - BQ27521 * "ti,bq27530" - BQ27530 * "ti,bq27531" - BQ27531 * "ti,bq27541" - BQ27541 diff --git a/MAINTAINERS b/MAINTAINERS index e26e4c7..bc29978 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14909,6 +14909,12 @@ F: include/linux/workqueue.h F: kernel/workqueue.c F: Documentation/core-api/workqueue.rst +X-POWERS AXP288 PMIC DRIVERS +M: Hans de Goede <hdegoede@redhat.com> +S: Maintained +N: axp288 +F: drivers/acpi/pmic/intel_pmic_xpower.c + X-POWERS MULTIFUNCTION PMIC DEVICE DRIVERS M: Chen-Yu Tsai <wens@csie.org> L: linux-kernel@vger.kernel.org diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index ca0de1a..a102e74 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -98,15 +98,6 @@ config POWER_RESET_HISI help Reboot support for Hisilicon boards. -config POWER_RESET_IMX - bool "IMX6 power-off driver" - depends on POWER_RESET && SOC_IMX6 - help - This driver support power off external PMIC by PMIC_ON_REQ on i.mx6 - boards.If you want to use other pin to control external power,please - say N here or disable in dts to make sure pm_power_off never be - overwrote wrongly by this driver. - config POWER_RESET_MSM bool "Qualcomm MSM power-off driver" depends on ARCH_QCOM diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index aeb65ed..dcc92f5 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -10,7 +10,6 @@ obj-$(CONFIG_POWER_RESET_GEMINI_POWEROFF) += gemini-poweroff.o obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o -obj-$(CONFIG_POWER_RESET_IMX) += imx-snvs-poweroff.o obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o obj-$(CONFIG_POWER_RESET_PIIX4_POWEROFF) += piix4-poweroff.o obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o diff --git a/drivers/power/reset/at91-sama5d2_shdwc.c b/drivers/power/reset/at91-sama5d2_shdwc.c index 31080c2..0206cce 100644 --- a/drivers/power/reset/at91-sama5d2_shdwc.c +++ b/drivers/power/reset/at91-sama5d2_shdwc.c @@ -68,7 +68,7 @@ struct shdwc_config { }; struct shdwc { - struct shdwc_config *cfg; + const struct shdwc_config *cfg; void __iomem *at91_shdwc_base; }; @@ -260,7 +260,7 @@ static int __init at91_shdwc_probe(struct platform_device *pdev) } match = of_match_node(at91_shdwc_of_match, pdev->dev.of_node); - at91_shdwc->cfg = (struct shdwc_config *)(match->data); + at91_shdwc->cfg = match->data; sclk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(sclk)) diff --git a/drivers/power/reset/imx-snvs-poweroff.c b/drivers/power/reset/imx-snvs-poweroff.c deleted file mode 100644 index ad6ce50..0000000 --- a/drivers/power/reset/imx-snvs-poweroff.c +++ /dev/null @@ -1,66 +0,0 @@ -/* Power off driver for i.mx6 - * Copyright (c) 2014, FREESCALE CORPORATION. All rights reserved. - * - * based on msm-poweroff.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include <linux/err.h> -#include <linux/init.h> -#include <linux/io.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/platform_device.h> - -static void __iomem *snvs_base; - -static void do_imx_poweroff(void) -{ - u32 value = readl(snvs_base); - - /* set TOP and DP_EN bit */ - writel(value | 0x60, snvs_base); -} - -static int imx_poweroff_probe(struct platform_device *pdev) -{ - snvs_base = of_iomap(pdev->dev.of_node, 0); - if (!snvs_base) { - dev_err(&pdev->dev, "failed to get memory\n"); - return -ENODEV; - } - - pm_power_off = do_imx_poweroff; - return 0; -} - -static const struct of_device_id of_imx_poweroff_match[] = { - { .compatible = "fsl,sec-v4.0-poweroff", }, - {}, -}; -MODULE_DEVICE_TABLE(of, of_imx_poweroff_match); - -static struct platform_driver imx_poweroff_driver = { - .probe = imx_poweroff_probe, - .driver = { - .name = "imx-snvs-poweroff", - .of_match_table = of_match_ptr(of_imx_poweroff_match), - }, -}; - -static int __init imx_poweroff_init(void) -{ - return platform_driver_register(&imx_poweroff_driver); -} -device_initcall(imx_poweroff_init); diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c index 4702efd..01b8c71 100644 --- a/drivers/power/reset/msm-poweroff.c +++ b/drivers/power/reset/msm-poweroff.c @@ -23,7 +23,7 @@ #include <linux/pm.h> static void __iomem *msm_ps_hold; -static int do_msm_restart(struct notifier_block *nb, unsigned long action, +static int deassert_pshold(struct notifier_block *nb, unsigned long action, void *data) { writel(0, msm_ps_hold); @@ -33,14 +33,13 @@ static int do_msm_restart(struct notifier_block *nb, unsigned long action, } static struct notifier_block restart_nb = { - .notifier_call = do_msm_restart, + .notifier_call = deassert_pshold, .priority = 128, }; static void do_msm_poweroff(void) { - /* TODO: Add poweroff capability */ - do_msm_restart(&restart_nb, 0, NULL); + deassert_pshold(&restart_nb, 0, NULL); } static int msm_restart_probe(struct platform_device *pdev) diff --git a/drivers/power/reset/zx-reboot.c b/drivers/power/reset/zx-reboot.c index 7549c7f..c03e96e 100644 --- a/drivers/power/reset/zx-reboot.c +++ b/drivers/power/reset/zx-reboot.c @@ -82,3 +82,7 @@ static struct platform_driver zx_reboot_driver = { }, }; module_platform_driver(zx_reboot_driver); + +MODULE_DESCRIPTION("ZTE SoCs reset driver"); +MODULE_AUTHOR("Jun Nie <jun.nie@linaro.org>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c index 4ebbcce..5a76c6d 100644 --- a/drivers/power/supply/ab8500_charger.c +++ b/drivers/power/supply/ab8500_charger.c @@ -3218,11 +3218,13 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di) } /* Enable backup battery charging */ - abx500_mask_and_set_register_interruptible(di->dev, + ret = abx500_mask_and_set_register_interruptible(di->dev, AB8500_RTC, AB8500_RTC_CTRL_REG, RTC_BUP_CH_ENA, RTC_BUP_CH_ENA); - if (ret < 0) + if (ret < 0) { dev_err(di->dev, "%s mask and set failed\n", __func__); + goto out; + } if (is_ab8540(di->parent)) { ret = abx500_mask_and_set_register_interruptible(di->dev, diff --git a/drivers/power/supply/axp20x_ac_power.c b/drivers/power/supply/axp20x_ac_power.c index 38f4e87..0771f95 100644 --- a/drivers/power/supply/axp20x_ac_power.c +++ b/drivers/power/supply/axp20x_ac_power.c @@ -159,7 +159,7 @@ static int axp20x_ac_power_probe(struct platform_device *pdev) struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); struct power_supply_config psy_cfg = {}; struct axp20x_ac_power *power; - struct axp_data *axp_data; + const struct axp_data *axp_data; static const char * const irq_names[] = { "ACIN_PLUGIN", "ACIN_REMOVAL", NULL }; int i, irq, ret; @@ -176,7 +176,7 @@ static int axp20x_ac_power_probe(struct platform_device *pdev) if (!power) return -ENOMEM; - axp_data = (struct axp_data *)of_device_get_match_data(&pdev->dev); + axp_data = of_device_get_match_data(&pdev->dev); if (axp_data->acin_adc) { power->acin_v = devm_iio_channel_get(&pdev->dev, "acin_v"); @@ -230,10 +230,10 @@ static int axp20x_ac_power_probe(struct platform_device *pdev) static const struct of_device_id axp20x_ac_power_match[] = { { .compatible = "x-powers,axp202-ac-power-supply", - .data = (void *)&axp20x_data, + .data = &axp20x_data, }, { .compatible = "x-powers,axp221-ac-power-supply", - .data = (void *)&axp22x_data, + .data = &axp22x_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, axp20x_ac_power_match); diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c index d51ebd1..9bfbde1 100644 --- a/drivers/power/supply/axp288_charger.c +++ b/drivers/power/supply/axp288_charger.c @@ -1,6 +1,7 @@ /* * axp288_charger.c - X-power AXP288 PMIC Charger driver * + * Copyright (C) 2016-2017 Hans de Goede <hdegoede@redhat.com> * Copyright (C) 2014 Intel Corporation * Author: Ramakrishna Pallala <ramakrishna.pallala@intel.com> * @@ -98,28 +99,10 @@ #define CV_4200MV 4200 /* 4200mV */ #define CV_4350MV 4350 /* 4350mV */ -#define CC_200MA 200 /* 200mA */ -#define CC_600MA 600 /* 600mA */ -#define CC_800MA 800 /* 800mA */ -#define CC_1000MA 1000 /* 1000mA */ -#define CC_1600MA 1600 /* 1600mA */ -#define CC_2000MA 2000 /* 2000mA */ - -#define ILIM_100MA 100 /* 100mA */ -#define ILIM_500MA 500 /* 500mA */ -#define ILIM_900MA 900 /* 900mA */ -#define ILIM_1500MA 1500 /* 1500mA */ -#define ILIM_2000MA 2000 /* 2000mA */ -#define ILIM_2500MA 2500 /* 2500mA */ -#define ILIM_3000MA 3000 /* 3000mA */ - #define AXP288_EXTCON_DEV_NAME "axp288_extcon" #define USB_HOST_EXTCON_HID "INT3496" #define USB_HOST_EXTCON_NAME "INT3496:00" -static const unsigned int cable_ids[] = - { EXTCON_CHG_USB_SDP, EXTCON_CHG_USB_CDP, EXTCON_CHG_USB_DCP }; - enum { VBUS_OV_IRQ = 0, CHARGE_DONE_IRQ, @@ -139,7 +122,6 @@ struct axp288_chrg_info { struct regmap_irq_chip_data *regmap_irqc; int irq[CHRG_INTR_END]; struct power_supply *psy_usb; - struct mutex lock; /* OTG/Host mode */ struct { @@ -152,18 +134,14 @@ struct axp288_chrg_info { /* SDP/CDP/DCP USB charging cable notifications */ struct { struct extcon_dev *edev; - bool connected; - enum power_supply_type chg_type; - struct notifier_block nb[ARRAY_SIZE(cable_ids)]; + struct notifier_block nb; struct work_struct work; } cable; - int inlmt; int cc; int cv; int max_cc; int max_cv; - int is_charger_enabled; }; static inline int axp288_charger_set_cc(struct axp288_chrg_info *info, int cc) @@ -220,51 +198,63 @@ static inline int axp288_charger_set_cv(struct axp288_chrg_info *info, int cv) return ret; } -static inline int axp288_charger_set_vbus_inlmt(struct axp288_chrg_info *info, - int inlmt) +static int axp288_charger_get_vbus_inlmt(struct axp288_chrg_info *info) { - int ret; unsigned int val; - u8 reg_val; + int ret; - /* Read in limit register */ ret = regmap_read(info->regmap, AXP20X_CHRG_BAK_CTRL, &val); if (ret < 0) - goto set_inlmt_fail; - - if (inlmt <= ILIM_100MA) { - reg_val = CHRG_VBUS_ILIM_100MA; - inlmt = ILIM_100MA; - } else if (inlmt <= ILIM_500MA) { - reg_val = CHRG_VBUS_ILIM_500MA; - inlmt = ILIM_500MA; - } else if (inlmt <= ILIM_900MA) { - reg_val = CHRG_VBUS_ILIM_900MA; - inlmt = ILIM_900MA; - } else if (inlmt <= ILIM_1500MA) { - reg_val = CHRG_VBUS_ILIM_1500MA; - inlmt = ILIM_1500MA; - } else if (inlmt <= ILIM_2000MA) { - reg_val = CHRG_VBUS_ILIM_2000MA; - inlmt = ILIM_2000MA; - } else if (inlmt <= ILIM_2500MA) { - reg_val = CHRG_VBUS_ILIM_2500MA; - inlmt = ILIM_2500MA; - } else { - reg_val = CHRG_VBUS_ILIM_3000MA; - inlmt = ILIM_3000MA; + return ret; + + val >>= CHRG_VBUS_ILIM_BIT_POS; + switch (val) { + case CHRG_VBUS_ILIM_100MA: + return 100000; + case CHRG_VBUS_ILIM_500MA: + return 500000; + case CHRG_VBUS_ILIM_900MA: + return 900000; + case CHRG_VBUS_ILIM_1500MA: + return 1500000; + case CHRG_VBUS_ILIM_2000MA: + return 2000000; + case CHRG_VBUS_ILIM_2500MA: + return 2500000; + case CHRG_VBUS_ILIM_3000MA: + return 3000000; + default: + dev_warn(&info->pdev->dev, "Unknown ilim reg val: %d\n", val); + return 0; } +} - reg_val = (val & ~CHRG_VBUS_ILIM_MASK) - | (reg_val << CHRG_VBUS_ILIM_BIT_POS); - ret = regmap_write(info->regmap, AXP20X_CHRG_BAK_CTRL, reg_val); - if (ret >= 0) - info->inlmt = inlmt; +static inline int axp288_charger_set_vbus_inlmt(struct axp288_chrg_info *info, + int inlmt) +{ + int ret; + u8 reg_val; + + if (inlmt >= 3000000) + reg_val = CHRG_VBUS_ILIM_3000MA << CHRG_VBUS_ILIM_BIT_POS; + else if (inlmt >= 2500000) + reg_val = CHRG_VBUS_ILIM_2500MA << CHRG_VBUS_ILIM_BIT_POS; + else if (inlmt >= 2000000) + reg_val = CHRG_VBUS_ILIM_2000MA << CHRG_VBUS_ILIM_BIT_POS; + else if (inlmt >= 1500000) + reg_val = CHRG_VBUS_ILIM_1500MA << CHRG_VBUS_ILIM_BIT_POS; + else if (inlmt >= 900000) + reg_val = CHRG_VBUS_ILIM_900MA << CHRG_VBUS_ILIM_BIT_POS; + else if (inlmt >= 500000) + reg_val = CHRG_VBUS_ILIM_500MA << CHRG_VBUS_ILIM_BIT_POS; else - dev_err(&info->pdev->dev, "charger BAK control %d\n", ret); + reg_val = CHRG_VBUS_ILIM_100MA << CHRG_VBUS_ILIM_BIT_POS; + ret = regmap_update_bits(info->regmap, AXP20X_CHRG_BAK_CTRL, + CHRG_VBUS_ILIM_MASK, reg_val); + if (ret < 0) + dev_err(&info->pdev->dev, "charger BAK control %d\n", ret); -set_inlmt_fail: return ret; } @@ -283,7 +273,6 @@ static int axp288_charger_vbus_path_select(struct axp288_chrg_info *info, if (ret < 0) dev_err(&info->pdev->dev, "axp288 vbus path select %d\n", ret); - return ret; } @@ -292,9 +281,6 @@ static int axp288_charger_enable_charger(struct axp288_chrg_info *info, { int ret; - if ((int)enable == info->is_charger_enabled) - return 0; - if (enable) ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1, CHRG_CCCV_CHG_EN, CHRG_CCCV_CHG_EN); @@ -303,8 +289,6 @@ static int axp288_charger_enable_charger(struct axp288_chrg_info *info, CHRG_CCCV_CHG_EN, 0); if (ret < 0) dev_err(&info->pdev->dev, "axp288 enable charger %d\n", ret); - else - info->is_charger_enabled = enable; return ret; } @@ -376,8 +360,6 @@ static int axp288_charger_usb_set_property(struct power_supply *psy, int ret = 0; int scaled_val; - mutex_lock(&info->lock); - switch (psp) { case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: scaled_val = min(val->intval, info->max_cc); @@ -393,11 +375,15 @@ static int axp288_charger_usb_set_property(struct power_supply *psy, if (ret < 0) dev_warn(&info->pdev->dev, "set charge voltage failed\n"); break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = axp288_charger_set_vbus_inlmt(info, val->intval); + if (ret < 0) + dev_warn(&info->pdev->dev, "set input current limit failed\n"); + break; default: ret = -EINVAL; } - mutex_unlock(&info->lock); return ret; } @@ -406,9 +392,7 @@ static int axp288_charger_usb_get_property(struct power_supply *psy, union power_supply_propval *val) { struct axp288_chrg_info *info = power_supply_get_drvdata(psy); - int ret = 0; - - mutex_lock(&info->lock); + int ret; switch (psp) { case POWER_SUPPLY_PROP_PRESENT: @@ -419,7 +403,7 @@ static int axp288_charger_usb_get_property(struct power_supply *psy, } ret = axp288_charger_is_present(info); if (ret < 0) - goto psy_get_prop_fail; + return ret; val->intval = ret; break; case POWER_SUPPLY_PROP_ONLINE: @@ -430,7 +414,7 @@ static int axp288_charger_usb_get_property(struct power_supply *psy, } ret = axp288_charger_is_online(info); if (ret < 0) - goto psy_get_prop_fail; + return ret; val->intval = ret; break; case POWER_SUPPLY_PROP_HEALTH: @@ -448,17 +432,17 @@ static int axp288_charger_usb_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: val->intval = info->max_cv * 1000; break; - case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: - val->intval = info->inlmt * 1000; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = axp288_charger_get_vbus_inlmt(info); + if (ret < 0) + return ret; + val->intval = ret; break; default: - ret = -EINVAL; - goto psy_get_prop_fail; + return -EINVAL; } -psy_get_prop_fail: - mutex_unlock(&info->lock); - return ret; + return 0; } static int axp288_charger_property_is_writeable(struct power_supply *psy, @@ -469,6 +453,7 @@ static int axp288_charger_property_is_writeable(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: ret = 1; break; default: @@ -487,7 +472,7 @@ static enum power_supply_property axp288_usb_props[] = { POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, - POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, }; static const struct power_supply_desc axp288_charger_desc = { @@ -565,99 +550,53 @@ static void axp288_charger_extcon_evt_worker(struct work_struct *work) container_of(work, struct axp288_chrg_info, cable.work); int ret, current_limit; struct extcon_dev *edev = info->cable.edev; - bool old_connected = info->cable.connected; - enum power_supply_type old_chg_type = info->cable.chg_type; + unsigned int val; + + ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val); + if (ret < 0) { + dev_err(&info->pdev->dev, "Error reading status (%d)\n", ret); + return; + } + + /* Offline? Disable charging and bail */ + if (!(val & PS_STAT_VBUS_VALID)) { + dev_dbg(&info->pdev->dev, "USB charger disconnected\n"); + axp288_charger_enable_charger(info, false); + power_supply_changed(info->psy_usb); + return; + } /* Determine cable/charger type */ if (extcon_get_state(edev, EXTCON_CHG_USB_SDP) > 0) { - dev_dbg(&info->pdev->dev, "USB SDP charger is connected"); - info->cable.connected = true; - info->cable.chg_type = POWER_SUPPLY_TYPE_USB; + dev_dbg(&info->pdev->dev, "USB SDP charger is connected\n"); + current_limit = 500000; } else if (extcon_get_state(edev, EXTCON_CHG_USB_CDP) > 0) { - dev_dbg(&info->pdev->dev, "USB CDP charger is connected"); - info->cable.connected = true; - info->cable.chg_type = POWER_SUPPLY_TYPE_USB_CDP; + dev_dbg(&info->pdev->dev, "USB CDP charger is connected\n"); + current_limit = 1500000; } else if (extcon_get_state(edev, EXTCON_CHG_USB_DCP) > 0) { - dev_dbg(&info->pdev->dev, "USB DCP charger is connected"); - info->cable.connected = true; - info->cable.chg_type = POWER_SUPPLY_TYPE_USB_DCP; + dev_dbg(&info->pdev->dev, "USB DCP charger is connected\n"); + current_limit = 2000000; } else { - if (old_connected) - dev_dbg(&info->pdev->dev, "USB charger disconnected"); - info->cable.connected = false; - info->cable.chg_type = POWER_SUPPLY_TYPE_USB; - } - - /* Cable status changed */ - if (old_connected == info->cable.connected && - old_chg_type == info->cable.chg_type) + /* Charger type detection still in progress, bail. */ return; - - mutex_lock(&info->lock); - - if (info->cable.connected) { - axp288_charger_enable_charger(info, false); - - switch (info->cable.chg_type) { - case POWER_SUPPLY_TYPE_USB: - current_limit = ILIM_500MA; - break; - case POWER_SUPPLY_TYPE_USB_CDP: - current_limit = ILIM_1500MA; - break; - case POWER_SUPPLY_TYPE_USB_DCP: - current_limit = ILIM_2000MA; - break; - default: - /* Unknown */ - current_limit = 0; - break; - } - - /* Set vbus current limit first, then enable charger */ - ret = axp288_charger_set_vbus_inlmt(info, current_limit); - if (ret == 0) - axp288_charger_enable_charger(info, true); - else - dev_err(&info->pdev->dev, - "error setting current limit (%d)", ret); - } else { - axp288_charger_enable_charger(info, false); } - mutex_unlock(&info->lock); + /* Set vbus current limit first, then enable charger */ + ret = axp288_charger_set_vbus_inlmt(info, current_limit); + if (ret == 0) + axp288_charger_enable_charger(info, true); + else + dev_err(&info->pdev->dev, + "error setting current limit (%d)\n", ret); power_supply_changed(info->psy_usb); } -/* - * We need 3 copies of this, because there is no way to find out for which - * cable id we are being called from the passed in arguments; and we must - * have a separate nb for each extcon_register_notifier call. - */ -static int axp288_charger_handle_cable0_evt(struct notifier_block *nb, - unsigned long event, void *param) +static int axp288_charger_handle_cable_evt(struct notifier_block *nb, + unsigned long event, void *param) { struct axp288_chrg_info *info = - container_of(nb, struct axp288_chrg_info, cable.nb[0]); - schedule_work(&info->cable.work); - return NOTIFY_OK; -} - -static int axp288_charger_handle_cable1_evt(struct notifier_block *nb, - unsigned long event, void *param) -{ - struct axp288_chrg_info *info = - container_of(nb, struct axp288_chrg_info, cable.nb[1]); - schedule_work(&info->cable.work); - return NOTIFY_OK; -} - -static int axp288_charger_handle_cable2_evt(struct notifier_block *nb, - unsigned long event, void *param) -{ - struct axp288_chrg_info *info = - container_of(nb, struct axp288_chrg_info, cable.nb[2]); + container_of(nb, struct axp288_chrg_info, cable.nb); schedule_work(&info->cable.work); return NOTIFY_OK; } @@ -785,6 +724,14 @@ static int charger_init_hw_regs(struct axp288_chrg_info *info) return 0; } +static void axp288_charger_cancel_work(void *data) +{ + struct axp288_chrg_info *info = data; + + cancel_work_sync(&info->otg.work); + cancel_work_sync(&info->cable.work); +} + static int axp288_charger_probe(struct platform_device *pdev) { int ret, i, pirq; @@ -799,8 +746,6 @@ static int axp288_charger_probe(struct platform_device *pdev) info->pdev = pdev; info->regmap = axp20x->regmap; info->regmap_irqc = axp20x->regmap_irqc; - info->cable.chg_type = -1; - info->is_charger_enabled = -1; info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME); if (info->cable.edev == NULL) { @@ -820,7 +765,6 @@ static int axp288_charger_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, info); - mutex_init(&info->lock); ret = charger_init_hw_regs(info); if (ret) @@ -836,19 +780,19 @@ static int axp288_charger_probe(struct platform_device *pdev) return ret; } + /* Cancel our work on cleanup, register this before the notifiers */ + ret = devm_add_action(dev, axp288_charger_cancel_work, info); + if (ret) + return ret; + /* Register for extcon notification */ INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker); - info->cable.nb[0].notifier_call = axp288_charger_handle_cable0_evt; - info->cable.nb[1].notifier_call = axp288_charger_handle_cable1_evt; - info->cable.nb[2].notifier_call = axp288_charger_handle_cable2_evt; - for (i = 0; i < ARRAY_SIZE(cable_ids); i++) { - ret = devm_extcon_register_notifier(dev, info->cable.edev, - cable_ids[i], &info->cable.nb[i]); - if (ret) { - dev_err(dev, "failed to register extcon notifier for %u: %d\n", - cable_ids[i], ret); - return ret; - } + info->cable.nb.notifier_call = axp288_charger_handle_cable_evt; + ret = devm_extcon_register_notifier_all(dev, info->cable.edev, + &info->cable.nb); + if (ret) { + dev_err(dev, "failed to register cable extcon notifier\n"); + return ret; } schedule_work(&info->cable.work); diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c index a8dcabc..4cc6e03 100644 --- a/drivers/power/supply/axp288_fuel_gauge.c +++ b/drivers/power/supply/axp288_fuel_gauge.c @@ -1,6 +1,7 @@ /* * axp288_fuel_gauge.c - Xpower AXP288 PMIC Fuel Gauge Driver * + * Copyright (C) 2016-2017 Hans de Goede <hdegoede@redhat.com> * Copyright (C) 2014 Intel Corporation * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -16,6 +17,7 @@ * */ +#include <linux/dmi.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/device.h> @@ -31,6 +33,12 @@ #include <linux/seq_file.h> #include <asm/unaligned.h> +#define PS_STAT_VBUS_TRIGGER (1 << 0) +#define PS_STAT_BAT_CHRG_DIR (1 << 2) +#define PS_STAT_VBAT_ABOVE_VHOLD (1 << 3) +#define PS_STAT_VBUS_VALID (1 << 4) +#define PS_STAT_VBUS_PRESENT (1 << 5) + #define CHRG_STAT_BAT_SAFE_MODE (1 << 3) #define CHRG_STAT_BAT_VALID (1 << 4) #define CHRG_STAT_BAT_PRESENT (1 << 5) @@ -100,11 +108,22 @@ enum { WL1_IRQ, }; +enum { + BAT_TEMP = 0, + PMIC_TEMP, + SYSTEM_TEMP, + BAT_CHRG_CURR, + BAT_D_CURR, + BAT_VOLT, + IIO_CHANNEL_NUM +}; + struct axp288_fg_info { struct platform_device *pdev; struct regmap *regmap; struct regmap_irq_chip_data *regmap_irqc; int irq[AXP288_FG_INTR_NUM]; + struct iio_channel *iio_channel[IIO_CHANNEL_NUM]; struct power_supply *bat; struct mutex lock; int status; @@ -199,33 +218,6 @@ static int fuel_gauge_read_12bit_word(struct axp288_fg_info *info, int reg) return (buf[0] << 4) | ((buf[1] >> 4) & 0x0f); } -static int pmic_read_adc_val(const char *name, int *raw_val, - struct axp288_fg_info *info) -{ - int ret, val = 0; - struct iio_channel *indio_chan; - - indio_chan = iio_channel_get(NULL, name); - if (IS_ERR_OR_NULL(indio_chan)) { - ret = PTR_ERR(indio_chan); - goto exit; - } - ret = iio_read_channel_raw(indio_chan, &val); - if (ret < 0) { - dev_err(&info->pdev->dev, - "IIO channel read error: %x, %x\n", ret, val); - goto err_exit; - } - - dev_dbg(&info->pdev->dev, "adc raw val=%x\n", val); - *raw_val = val; - -err_exit: - iio_channel_release(indio_chan); -exit: - return ret; -} - #ifdef CONFIG_DEBUG_FS static int fuel_gauge_debug_show(struct seq_file *s, void *data) { @@ -296,22 +288,22 @@ static int fuel_gauge_debug_show(struct seq_file *s, void *data) AXP288_FG_TUNE5, fuel_gauge_reg_readb(info, AXP288_FG_TUNE5)); - ret = pmic_read_adc_val("axp288-batt-temp", &raw_val, info); + ret = iio_read_channel_raw(info->iio_channel[BAT_TEMP], &raw_val); if (ret >= 0) seq_printf(s, "axp288-batttemp : %d\n", raw_val); - ret = pmic_read_adc_val("axp288-pmic-temp", &raw_val, info); + ret = iio_read_channel_raw(info->iio_channel[PMIC_TEMP], &raw_val); if (ret >= 0) seq_printf(s, "axp288-pmictemp : %d\n", raw_val); - ret = pmic_read_adc_val("axp288-system-temp", &raw_val, info); + ret = iio_read_channel_raw(info->iio_channel[SYSTEM_TEMP], &raw_val); if (ret >= 0) seq_printf(s, "axp288-systtemp : %d\n", raw_val); - ret = pmic_read_adc_val("axp288-chrg-curr", &raw_val, info); + ret = iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], &raw_val); if (ret >= 0) seq_printf(s, "axp288-chrgcurr : %d\n", raw_val); - ret = pmic_read_adc_val("axp288-chrg-d-curr", &raw_val, info); + ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &raw_val); if (ret >= 0) seq_printf(s, "axp288-dchrgcur : %d\n", raw_val); - ret = pmic_read_adc_val("axp288-batt-volt", &raw_val, info); + ret = iio_read_channel_raw(info->iio_channel[BAT_VOLT], &raw_val); if (ret >= 0) seq_printf(s, "axp288-battvolt : %d\n", raw_val); @@ -351,8 +343,7 @@ static inline void fuel_gauge_remove_debugfs(struct axp288_fg_info *info) static void fuel_gauge_get_status(struct axp288_fg_info *info) { - int pwr_stat, ret; - int charge, discharge; + int pwr_stat, fg_res; pwr_stat = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS); if (pwr_stat < 0) { @@ -360,36 +351,32 @@ static void fuel_gauge_get_status(struct axp288_fg_info *info) "PWR STAT read failed:%d\n", pwr_stat); return; } - ret = pmic_read_adc_val("axp288-chrg-curr", &charge, info); - if (ret < 0) { - dev_err(&info->pdev->dev, - "ADC charge current read failed:%d\n", ret); - return; - } - ret = pmic_read_adc_val("axp288-chrg-d-curr", &discharge, info); - if (ret < 0) { - dev_err(&info->pdev->dev, - "ADC discharge current read failed:%d\n", ret); - return; + + /* Report full if Vbus is valid and the reported capacity is 100% */ + if (pwr_stat & PS_STAT_VBUS_VALID) { + fg_res = fuel_gauge_reg_readb(info, AXP20X_FG_RES); + if (fg_res < 0) { + dev_err(&info->pdev->dev, + "FG RES read failed: %d\n", fg_res); + return; + } + if (fg_res == (FG_REP_CAP_VALID | 100)) { + info->status = POWER_SUPPLY_STATUS_FULL; + return; + } } - if (charge > 0) + if (pwr_stat & PS_STAT_BAT_CHRG_DIR) info->status = POWER_SUPPLY_STATUS_CHARGING; - else if (discharge > 0) + else info->status = POWER_SUPPLY_STATUS_DISCHARGING; - else { - if (pwr_stat & CHRG_STAT_BAT_PRESENT) - info->status = POWER_SUPPLY_STATUS_FULL; - else - info->status = POWER_SUPPLY_STATUS_NOT_CHARGING; - } } static int fuel_gauge_get_vbatt(struct axp288_fg_info *info, int *vbatt) { int ret = 0, raw_val; - ret = pmic_read_adc_val("axp288-batt-volt", &raw_val, info); + ret = iio_read_channel_raw(info->iio_channel[BAT_VOLT], &raw_val); if (ret < 0) goto vbatt_read_fail; @@ -400,24 +387,19 @@ vbatt_read_fail: static int fuel_gauge_get_current(struct axp288_fg_info *info, int *cur) { - int ret, value = 0; - int charge, discharge; + int ret, discharge; - ret = pmic_read_adc_val("axp288-chrg-curr", &charge, info); - if (ret < 0) - goto current_read_fail; - ret = pmic_read_adc_val("axp288-chrg-d-curr", &discharge, info); + /* First check discharge current, so that we do only 1 read on bat. */ + ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &discharge); if (ret < 0) - goto current_read_fail; + return ret; - if (charge > 0) - value = charge; - else if (discharge > 0) - value = -1 * discharge; + if (discharge > 0) { + *cur = -1 * discharge; + return 0; + } - *cur = value; -current_read_fail: - return ret; + return iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], cur); } static int fuel_gauge_get_vocv(struct axp288_fg_info *info, int *vocv) @@ -698,12 +680,54 @@ intr_failed: } } +/* + * Some devices have no battery (HDMI sticks) and the axp288 battery's + * detection reports one despite it not being there. + */ +static const struct dmi_system_id axp288_fuel_gauge_blacklist[] = { + { + /* Intel Cherry Trail Compute Stick, Windows version */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "STK1AW32SC"), + }, + }, + { + /* Intel Cherry Trail Compute Stick, version without an OS */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "STK1A32SC"), + }, + }, + { + /* Meegopad T08 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Default string"), + DMI_MATCH(DMI_BOARD_VENDOR, "To be filled by OEM."), + DMI_MATCH(DMI_BOARD_NAME, "T3 MRD"), + DMI_MATCH(DMI_BOARD_VERSION, "V1.1"), + }, + }, + {} +}; + static int axp288_fuel_gauge_probe(struct platform_device *pdev) { - int ret = 0; + int i, ret = 0; struct axp288_fg_info *info; struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); struct power_supply_config psy_cfg = {}; + static const char * const iio_chan_name[] = { + [BAT_TEMP] = "axp288-batt-temp", + [PMIC_TEMP] = "axp288-pmic-temp", + [SYSTEM_TEMP] = "axp288-system-temp", + [BAT_CHRG_CURR] = "axp288-chrg-curr", + [BAT_D_CURR] = "axp288-chrg-d-curr", + [BAT_VOLT] = "axp288-batt-volt", + }; + + if (dmi_check_system(axp288_fuel_gauge_blacklist)) + return -ENODEV; info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) @@ -719,18 +743,39 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev) mutex_init(&info->lock); INIT_DELAYED_WORK(&info->status_monitor, fuel_gauge_status_monitor); + for (i = 0; i < IIO_CHANNEL_NUM; i++) { + /* + * Note cannot use devm_iio_channel_get because x86 systems + * lack the device<->channel maps which iio_channel_get will + * try to use when passed a non NULL device pointer. + */ + info->iio_channel[i] = + iio_channel_get(NULL, iio_chan_name[i]); + if (IS_ERR(info->iio_channel[i])) { + ret = PTR_ERR(info->iio_channel[i]); + dev_dbg(&pdev->dev, "error getting iiochan %s: %d\n", + iio_chan_name[i], ret); + /* Wait for axp288_adc to load */ + if (ret == -ENODEV) + ret = -EPROBE_DEFER; + + goto out_free_iio_chan; + } + } + ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG); if (ret < 0) - return ret; + goto out_free_iio_chan; if (!(ret & FG_DES_CAP1_VALID)) { dev_err(&pdev->dev, "axp288 not configured by firmware\n"); - return -ENODEV; + ret = -ENODEV; + goto out_free_iio_chan; } ret = fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1); if (ret < 0) - return ret; + goto out_free_iio_chan; switch ((ret & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS) { case CHRG_CCCV_CV_4100MV: info->max_volt = 4100; @@ -751,7 +796,7 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev) if (IS_ERR(info->bat)) { ret = PTR_ERR(info->bat); dev_err(&pdev->dev, "failed to register battery: %d\n", ret); - return ret; + goto out_free_iio_chan; } fuel_gauge_create_debugfs(info); @@ -759,6 +804,13 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev) schedule_delayed_work(&info->status_monitor, STATUS_MON_DELAY_JIFFIES); return 0; + +out_free_iio_chan: + for (i = 0; i < IIO_CHANNEL_NUM; i++) + if (!IS_ERR_OR_NULL(info->iio_channel[i])) + iio_channel_release(info->iio_channel[i]); + + return ret; } static const struct platform_device_id axp288_fg_id_table[] = { @@ -780,6 +832,9 @@ static int axp288_fuel_gauge_remove(struct platform_device *pdev) if (info->irq[i] >= 0) free_irq(info->irq[i], info); + for (i = 0; i < IIO_CHANNEL_NUM; i++) + iio_channel_release(info->iio_channel[i]); + return 0; } diff --git a/drivers/power/supply/bq24190_charger.c b/drivers/power/supply/bq24190_charger.c index 35ff406..b58df04 100644 --- a/drivers/power/supply/bq24190_charger.c +++ b/drivers/power/supply/bq24190_charger.c @@ -11,7 +11,6 @@ #include <linux/module.h> #include <linux/interrupt.h> #include <linux/delay.h> -#include <linux/extcon.h> #include <linux/of_irq.h> #include <linux/of_device.h> #include <linux/pm_runtime.h> @@ -162,9 +161,6 @@ struct bq24190_dev_info { struct device *dev; struct power_supply *charger; struct power_supply *battery; - struct extcon_dev *extcon; - struct notifier_block extcon_nb; - struct delayed_work extcon_work; struct delayed_work input_current_limit_work; char model_name[I2C_NAME_SIZE]; bool initialized; @@ -686,6 +682,16 @@ static int bq24190_register_reset(struct bq24190_dev_info *bdi) int ret, limit = 100; u8 v; + /* + * This prop. can be passed on device instantiation from platform code: + * struct property_entry pe[] = + * { PROPERTY_ENTRY_BOOL("disable-reset"), ... }; + * struct i2c_board_info bi = + * { .type = "bq24190", .addr = 0x6b, .properties = pe, .irq = irq }; + * struct i2c_adapter ad = { ... }; + * i2c_add_adapter(&ad); + * i2c_new_device(&ad, &bi); + */ if (device_property_read_bool(bdi->dev, "disable-reset")) return 0; @@ -1193,8 +1199,6 @@ static int bq24190_charger_set_property(struct power_supply *psy, static int bq24190_charger_property_is_writeable(struct power_supply *psy, enum power_supply_property psp) { - int ret; - switch (psp) { case POWER_SUPPLY_PROP_ONLINE: case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: @@ -1202,13 +1206,10 @@ static int bq24190_charger_property_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: - ret = 1; - break; + return 1; default: - ret = 0; + return 0; } - - return ret; } static void bq24190_input_current_limit_work(struct work_struct *work) @@ -1623,75 +1624,6 @@ static irqreturn_t bq24190_irq_handler_thread(int irq, void *data) return IRQ_HANDLED; } -static void bq24190_extcon_work(struct work_struct *work) -{ - struct bq24190_dev_info *bdi = - container_of(work, struct bq24190_dev_info, extcon_work.work); - int error, iinlim = 0; - u8 v; - - error = pm_runtime_get_sync(bdi->dev); - if (error < 0) { - dev_warn(bdi->dev, "pm_runtime_get failed: %i\n", error); - pm_runtime_put_noidle(bdi->dev); - return; - } - - if (extcon_get_state(bdi->extcon, EXTCON_CHG_USB_SDP) == 1) - iinlim = 500000; - else if (extcon_get_state(bdi->extcon, EXTCON_CHG_USB_CDP) == 1 || - extcon_get_state(bdi->extcon, EXTCON_CHG_USB_ACA) == 1) - iinlim = 1500000; - else if (extcon_get_state(bdi->extcon, EXTCON_CHG_USB_DCP) == 1) - iinlim = 2000000; - - if (iinlim) { - error = bq24190_set_field_val(bdi, BQ24190_REG_ISC, - BQ24190_REG_ISC_IINLIM_MASK, - BQ24190_REG_ISC_IINLIM_SHIFT, - bq24190_isc_iinlim_values, - ARRAY_SIZE(bq24190_isc_iinlim_values), - iinlim); - if (error < 0) - dev_err(bdi->dev, "Can't set IINLIM: %d\n", error); - } - - /* if no charger found and in USB host mode, set OTG 5V boost, else normal */ - if (!iinlim && extcon_get_state(bdi->extcon, EXTCON_USB_HOST) == 1) - v = BQ24190_REG_POC_CHG_CONFIG_OTG; - else - v = BQ24190_REG_POC_CHG_CONFIG_CHARGE; - - error = bq24190_write_mask(bdi, BQ24190_REG_POC, - BQ24190_REG_POC_CHG_CONFIG_MASK, - BQ24190_REG_POC_CHG_CONFIG_SHIFT, - v); - if (error < 0) - dev_err(bdi->dev, "Can't set CHG_CONFIG: %d\n", error); - - pm_runtime_mark_last_busy(bdi->dev); - pm_runtime_put_autosuspend(bdi->dev); -} - -static int bq24190_extcon_event(struct notifier_block *nb, unsigned long event, - void *param) -{ - struct bq24190_dev_info *bdi = - container_of(nb, struct bq24190_dev_info, extcon_nb); - - /* - * The Power-Good detection may take up to 220ms, sometimes - * the external charger detection is quicker, and the bq24190 will - * reset to iinlim based on its own charger detection (which is not - * hooked up when using external charger detection) resulting in - * a too low default 500mA iinlim. Delay applying the extcon value - * for 300ms to avoid this. - */ - queue_delayed_work(system_wq, &bdi->extcon_work, msecs_to_jiffies(300)); - - return NOTIFY_OK; -} - static int bq24190_hw_init(struct bq24190_dev_info *bdi) { u8 v; @@ -1766,7 +1698,6 @@ static int bq24190_probe(struct i2c_client *client, struct device *dev = &client->dev; struct power_supply_config charger_cfg = {}, battery_cfg = {}; struct bq24190_dev_info *bdi; - const char *name; int ret; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { @@ -1796,25 +1727,6 @@ static int bq24190_probe(struct i2c_client *client, return -EINVAL; } - /* - * Devicetree platforms should get extcon via phandle (not yet supported). - * On ACPI platforms, extcon clients may invoke us with: - * struct property_entry pe[] = - * { PROPERTY_ENTRY_STRING("extcon-name", client_name), ... }; - * struct i2c_board_info bi = - * { .type = "bq24190", .addr = 0x6b, .properties = pe, .irq = irq }; - * struct i2c_adapter ad = { ... }; - * i2c_add_adapter(&ad); - * i2c_new_device(&ad, &bi); - */ - if (device_property_read_string(dev, "extcon-name", &name) == 0) { - bdi->extcon = extcon_get_extcon_dev(name); - if (!bdi->extcon) - return -EPROBE_DEFER; - - dev_info(bdi->dev, "using extcon device %s\n", name); - } - pm_runtime_enable(dev); pm_runtime_use_autosuspend(dev); pm_runtime_set_autosuspend_delay(dev, 600); @@ -1882,20 +1794,6 @@ static int bq24190_probe(struct i2c_client *client, if (ret < 0) goto out_sysfs; - if (bdi->extcon) { - INIT_DELAYED_WORK(&bdi->extcon_work, bq24190_extcon_work); - bdi->extcon_nb.notifier_call = bq24190_extcon_event; - ret = devm_extcon_register_notifier_all(dev, bdi->extcon, - &bdi->extcon_nb); - if (ret) { - dev_err(dev, "Can't register extcon\n"); - goto out_sysfs; - } - - /* Sync initial cable state */ - queue_delayed_work(system_wq, &bdi->extcon_work, 0); - } - enable_irq_wake(client->irq); pm_runtime_mark_last_busy(dev); diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c index 51f0961..d999815 100644 --- a/drivers/power/supply/bq27xxx_battery.c +++ b/drivers/power/supply/bq27xxx_battery.c @@ -323,6 +323,30 @@ static u8 [BQ27XXX_REG_AP] = INVALID_REG_ADDR, BQ27XXX_DM_REG_ROWS, }, + bq27521_regs[BQ27XXX_REG_MAX] = { + [BQ27XXX_REG_CTRL] = 0x02, + [BQ27XXX_REG_TEMP] = 0x0a, + [BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR, + [BQ27XXX_REG_VOLT] = 0x0c, + [BQ27XXX_REG_AI] = 0x0e, + [BQ27XXX_REG_FLAGS] = 0x08, + [BQ27XXX_REG_TTE] = INVALID_REG_ADDR, + [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, + [BQ27XXX_REG_TTES] = INVALID_REG_ADDR, + [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, + [BQ27XXX_REG_NAC] = INVALID_REG_ADDR, + [BQ27XXX_REG_FCC] = INVALID_REG_ADDR, + [BQ27XXX_REG_CYCT] = INVALID_REG_ADDR, + [BQ27XXX_REG_AE] = INVALID_REG_ADDR, + [BQ27XXX_REG_SOC] = INVALID_REG_ADDR, + [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR, + [BQ27XXX_REG_AP] = INVALID_REG_ADDR, + [BQ27XXX_DM_CTRL] = INVALID_REG_ADDR, + [BQ27XXX_DM_CLASS] = INVALID_REG_ADDR, + [BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR, + [BQ27XXX_DM_DATA] = INVALID_REG_ADDR, + [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR, + }, bq27530_regs[BQ27XXX_REG_MAX] = { [BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_TEMP] = 0x06, @@ -557,6 +581,15 @@ static enum power_supply_property bq27520g4_props[] = { POWER_SUPPLY_PROP_MANUFACTURER, }; +static enum power_supply_property bq27521_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TECHNOLOGY, +}; + static enum power_supply_property bq27530_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, @@ -671,6 +704,7 @@ static struct bq27xxx_dm_reg bq27500_dm_regs[] = { #define bq27520g2_dm_regs 0 #define bq27520g3_dm_regs 0 #define bq27520g4_dm_regs 0 +#define bq27521_dm_regs 0 #define bq27530_dm_regs 0 #define bq27531_dm_regs 0 #define bq27541_dm_regs 0 @@ -717,8 +751,8 @@ static struct bq27xxx_dm_reg bq27621_dm_regs[] = { #endif #define BQ27XXX_O_ZERO 0x00000001 -#define BQ27XXX_O_OTDC 0x00000002 -#define BQ27XXX_O_UTOT 0x00000004 +#define BQ27XXX_O_OTDC 0x00000002 /* has OTC/OTD overtemperature flags */ +#define BQ27XXX_O_UTOT 0x00000004 /* has OT overtemperature flag */ #define BQ27XXX_O_CFGUP 0x00000008 #define BQ27XXX_O_RAM 0x00000010 @@ -751,6 +785,7 @@ static struct { [BQ27520G2] = BQ27XXX_DATA(bq27520g2, 0 , BQ27XXX_O_OTDC), [BQ27520G3] = BQ27XXX_DATA(bq27520g3, 0 , BQ27XXX_O_OTDC), [BQ27520G4] = BQ27XXX_DATA(bq27520g4, 0 , BQ27XXX_O_OTDC), + [BQ27521] = BQ27XXX_DATA(bq27521, 0 , 0), [BQ27530] = BQ27XXX_DATA(bq27530, 0 , BQ27XXX_O_UTOT), [BQ27531] = BQ27XXX_DATA(bq27531, 0 , BQ27XXX_O_UTOT), [BQ27541] = BQ27XXX_DATA(bq27541, 0 , BQ27XXX_O_OTDC), diff --git a/drivers/power/supply/bq27xxx_battery_i2c.c b/drivers/power/supply/bq27xxx_battery_i2c.c index 0b11ed4..6b25e5f 100644 --- a/drivers/power/supply/bq27xxx_battery_i2c.c +++ b/drivers/power/supply/bq27xxx_battery_i2c.c @@ -239,6 +239,7 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = { { "bq27520g2", BQ27520G2 }, { "bq27520g3", BQ27520G3 }, { "bq27520g4", BQ27520G4 }, + { "bq27521", BQ27521 }, { "bq27530", BQ27530 }, { "bq27531", BQ27531 }, { "bq27541", BQ27541 }, @@ -269,6 +270,7 @@ static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = { { .compatible = "ti,bq27520g2" }, { .compatible = "ti,bq27520g3" }, { .compatible = "ti,bq27520g4" }, + { .compatible = "ti,bq27521" }, { .compatible = "ti,bq27530" }, { .compatible = "ti,bq27531" }, { .compatible = "ti,bq27541" }, diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index 6502fa7..1de4b44 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -578,7 +578,7 @@ static int check_charging_duration(struct charger_manager *cm) } else if (is_ext_pwr_online(cm) && !cm->charger_enabled) { duration = curr - cm->charging_end_time; - if (duration > desc->charging_max_duration_ms && + if (duration > desc->discharging_max_duration_ms && is_ext_pwr_online(cm)) { dev_info(cm->dev, "Discharging duration exceed %ums\n", desc->discharging_max_duration_ms); diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index ee71a2b..98ba078 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -586,8 +586,8 @@ static int cpcap_battery_init_irq(struct platform_device *pdev, int irq, error; irq = platform_get_irq_byname(pdev, name); - if (!irq) - return -ENODEV; + if (irq < 0) + return irq; error = devm_request_threaded_irq(ddata->dev, irq, NULL, cpcap_battery_irq_thread, diff --git a/drivers/power/supply/ltc2941-battery-gauge.c b/drivers/power/supply/ltc2941-battery-gauge.c index 08e4fd9..4cfa3f0 100644 --- a/drivers/power/supply/ltc2941-battery-gauge.c +++ b/drivers/power/supply/ltc2941-battery-gauge.c @@ -60,6 +60,7 @@ enum ltc294x_id { #define LTC294X_REG_CONTROL_PRESCALER_SET(x) \ ((x << 3) & LTC294X_REG_CONTROL_PRESCALER_MASK) #define LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED 0 +#define LTC294X_REG_CONTROL_ADC_DISABLE(x) ((x) & ~(BIT(7) | BIT(6))) struct ltc294x_info { struct i2c_client *client; /* I2C Client pointer */ @@ -523,6 +524,29 @@ static int ltc294x_i2c_probe(struct i2c_client *client, return 0; } +static void ltc294x_i2c_shutdown(struct i2c_client *client) +{ + struct ltc294x_info *info = i2c_get_clientdata(client); + int ret; + u8 value; + u8 control; + + /* The LTC2941 does not need any special handling */ + if (info->id == LTC2941_ID) + return; + + /* Read control register */ + ret = ltc294x_read_regs(info->client, LTC294X_REG_CONTROL, &value, 1); + if (ret < 0) + return; + + /* Disable continuous ADC conversion as this drains the battery */ + control = LTC294X_REG_CONTROL_ADC_DISABLE(value); + if (control != value) + ltc294x_write_regs(info->client, LTC294X_REG_CONTROL, + &control, 1); +} + #ifdef CONFIG_PM_SLEEP static int ltc294x_suspend(struct device *dev) @@ -589,6 +613,7 @@ static struct i2c_driver ltc294x_driver = { }, .probe = ltc294x_i2c_probe, .remove = ltc294x_i2c_remove, + .shutdown = ltc294x_i2c_shutdown, .id_table = ltc294x_i2c_id, }; module_i2c_driver(ltc294x_driver); diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 5b556a1..35dde81b 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -123,6 +123,8 @@ static int max17042_get_temperature(struct max17042_chip *chip, int *temp) static int max17042_get_status(struct max17042_chip *chip, int *status) { int ret, charge_full, charge_now; + int avg_current; + u32 data; ret = power_supply_am_i_supplied(chip->battery); if (ret < 0) { @@ -152,10 +154,31 @@ static int max17042_get_status(struct max17042_chip *chip, int *status) if (ret < 0) return ret; - if ((charge_full - charge_now) <= MAX17042_FULL_THRESHOLD) + if ((charge_full - charge_now) <= MAX17042_FULL_THRESHOLD) { *status = POWER_SUPPLY_STATUS_FULL; - else + return 0; + } + + /* + * Even though we are supplied, we may still be discharging if the + * supply is e.g. only delivering 5V 0.5A. Check current if available. + */ + if (!chip->pdata->enable_current_sense) { *status = POWER_SUPPLY_STATUS_CHARGING; + return 0; + } + + ret = regmap_read(chip->regmap, MAX17042_AvgCurrent, &data); + if (ret < 0) + return ret; + + avg_current = sign_extend32(data, 15); + avg_current *= 1562500 / chip->pdata->r_sns; + + if (avg_current > 0) + *status = POWER_SUPPLY_STATUS_CHARGING; + else + *status = POWER_SUPPLY_STATUS_DISCHARGING; return 0; } @@ -863,16 +886,13 @@ static void max17042_init_worker(struct work_struct *work) #ifdef CONFIG_OF static struct max17042_platform_data * -max17042_get_pdata(struct max17042_chip *chip) +max17042_get_of_pdata(struct max17042_chip *chip) { struct device *dev = &chip->client->dev; struct device_node *np = dev->of_node; u32 prop; struct max17042_platform_data *pdata; - if (!np) - return dev->platform_data; - pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return NULL; @@ -897,7 +917,8 @@ max17042_get_pdata(struct max17042_chip *chip) return pdata; } -#else +#endif + static struct max17042_reg_data max17047_default_pdata_init_regs[] = { /* * Some firmwares do not set FullSOCThr, Enable End-of-Charge Detection @@ -907,15 +928,12 @@ static struct max17042_reg_data max17047_default_pdata_init_regs[] = { }; static struct max17042_platform_data * -max17042_get_pdata(struct max17042_chip *chip) +max17042_get_default_pdata(struct max17042_chip *chip) { struct device *dev = &chip->client->dev; struct max17042_platform_data *pdata; int ret, misc_cfg; - if (dev->platform_data) - return dev->platform_data; - /* * The MAX17047 gets used on x86 where we might not have pdata, assume * the firmware will already have initialized the fuel-gauge and provide @@ -948,7 +966,21 @@ max17042_get_pdata(struct max17042_chip *chip) return pdata; } + +static struct max17042_platform_data * +max17042_get_pdata(struct max17042_chip *chip) +{ + struct device *dev = &chip->client->dev; + +#ifdef CONFIG_OF + if (dev->of_node) + return max17042_get_of_pdata(chip); #endif + if (dev->platform_data) + return dev->platform_data; + + return max17042_get_default_pdata(chip); +} static const struct regmap_config max17042_regmap_config = { .reg_bits = 8, diff --git a/drivers/power/supply/sbs-manager.c b/drivers/power/supply/sbs-manager.c index ccb4217..cb6e8f6 100644 --- a/drivers/power/supply/sbs-manager.c +++ b/drivers/power/supply/sbs-manager.c @@ -183,7 +183,7 @@ static int sbsm_select(struct i2c_mux_core *muxc, u32 chan) return ret; /* chan goes from 1 ... 4 */ - reg = 1 << BIT(SBSM_SMB_BAT_OFFSET + chan); + reg = BIT(SBSM_SMB_BAT_OFFSET + chan); ret = sbsm_write_word(data->client, SBSM_CMD_BATSYSSTATE, reg); if (ret) dev_err(dev, "Failed to select channel %i\n", chan); diff --git a/include/linux/power/bq27xxx_battery.h b/include/linux/power/bq27xxx_battery.h index e6187f5..01fbf1b 100644 --- a/include/linux/power/bq27xxx_battery.h +++ b/include/linux/power/bq27xxx_battery.h @@ -16,6 +16,7 @@ enum bq27xxx_chip { BQ27520G2, /* bq27520G2 */ BQ27520G3, /* bq27520G3 */ BQ27520G4, /* bq27520G4 */ + BQ27521, /* bq27521 */ BQ27530, /* bq27530, bq27531 */ BQ27531, BQ27541, /* bq27541, bq27542, bq27546, bq27742 */ |