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) |
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 1b81fcd9fb72..000000000000 --- a/Documentation/devicetree/bindings/power/reset/imx-snvs-poweroff.txt +++ /dev/null @@ -1,23 +0,0 @@ -i.mx6 Poweroff Driver - -SNVS_LPCR in SNVS module can power off the whole system by pull -PMIC_ON_REQ low if PMIC_ON_REQ is connected with external PMIC. -If you don't want to use PMIC_ON_REQ as power on/off control, -please set status='disabled' to disable this driver. - -Required Properties: --compatible: "fsl,sec-v4.0-poweroff" --reg: Specifies the physical address of the SNVS_LPCR register - -Example: - snvs@20cc000 { - compatible = "fsl,sec-v4.0-mon", "simple-bus"; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0 0x020cc000 0x4000>; - ..... - snvs_poweroff: snvs-poweroff@38 { - compatible = "fsl,sec-v4.0-poweroff"; - reg = <0x38 0x4>; - }; - } diff --git a/Documentation/devicetree/bindings/power/supply/bq27xxx.txt b/Documentation/devicetree/bindings/power/supply/bq27xxx.txt index 6858e1a804ad..615c1cb6889f 100644 --- a/Documentation/devicetree/bindings/power/supply/bq27xxx.txt +++ b/Documentation/devicetree/bindings/power/supply/bq27xxx.txt @@ -15,6 +15,7 @@ Required properties: * "ti,bq27520g2" - BQ27520-g2 * "ti,bq27520g3" - BQ27520-g3 * "ti,bq27520g4" - BQ27520-g4 + * "ti,bq27521" - BQ27521 * "ti,bq27530" - BQ27530 * "ti,bq27531" - BQ27531 * "ti,bq27541" - BQ27541 diff --git a/MAINTAINERS b/MAINTAINERS index e26e4c7a5454..bc29978d263c 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 ca0de1a78e85..a102e74ab24e 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -98,15 +98,6 @@ config POWER_RESET_HISI help Reboot support for Hisilicon boards. -config POWER_RESET_IMX - bool "IMX6 power-off driver" - depends on POWER_RESET && SOC_IMX6 - help - This driver support power off external PMIC by PMIC_ON_REQ on i.mx6 - boards.If you want to use other pin to control external power,please - say N here or disable in dts to make sure pm_power_off never be - overwrote wrongly by this driver. - config POWER_RESET_MSM bool "Qualcomm MSM power-off driver" depends on ARCH_QCOM diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index aeb65edb17b7..dcc92f5f7a37 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -10,7 +10,6 @@ obj-$(CONFIG_POWER_RESET_GEMINI_POWEROFF) += gemini-poweroff.o obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o -obj-$(CONFIG_POWER_RESET_IMX) += imx-snvs-poweroff.o obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o obj-$(CONFIG_POWER_RESET_PIIX4_POWEROFF) += piix4-poweroff.o obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o diff --git a/drivers/power/reset/at91-sama5d2_shdwc.c b/drivers/power/reset/at91-sama5d2_shdwc.c index 31080c254124..0206cce328b3 100644 --- a/drivers/power/reset/at91-sama5d2_shdwc.c +++ b/drivers/power/reset/at91-sama5d2_shdwc.c @@ -68,7 +68,7 @@ struct shdwc_config { }; struct shdwc { - struct shdwc_config *cfg; + const struct shdwc_config *cfg; void __iomem *at91_shdwc_base; }; @@ -260,7 +260,7 @@ static int __init at91_shdwc_probe(struct platform_device *pdev) } match = of_match_node(at91_shdwc_of_match, pdev->dev.of_node); - at91_shdwc->cfg = (struct shdwc_config *)(match->data); + at91_shdwc->cfg = match->data; sclk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(sclk)) diff --git a/drivers/power/reset/imx-snvs-poweroff.c b/drivers/power/reset/imx-snvs-poweroff.c deleted file mode 100644 index ad6ce5020ea7..000000000000 --- a/drivers/power/reset/imx-snvs-poweroff.c +++ /dev/null @@ -1,66 +0,0 @@ -/* Power off driver for i.mx6 - * Copyright (c) 2014, FREESCALE CORPORATION. All rights reserved. - * - * based on msm-poweroff.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include <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 4702efdfe466..01b8c71697cb 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 7549c7f74a3c..c03e96e6a041 100644 --- a/drivers/power/reset/zx-reboot.c +++ b/drivers/power/reset/zx-reboot.c @@ -82,3 +82,7 @@ static struct platform_driver zx_reboot_driver = { }, }; module_platform_driver(zx_reboot_driver); + +MODULE_DESCRIPTION("ZTE SoCs reset driver"); +MODULE_AUTHOR("Jun Nie <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 4ebbcce45c48..5a76c6d343de 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 38f4e87cf24d..0771f951b11f 100644 --- a/drivers/power/supply/axp20x_ac_power.c +++ b/drivers/power/supply/axp20x_ac_power.c @@ -159,7 +159,7 @@ static int axp20x_ac_power_probe(struct platform_device *pdev) struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); struct power_supply_config psy_cfg = {}; struct axp20x_ac_power *power; - struct axp_data *axp_data; + const struct axp_data *axp_data; static const char * const irq_names[] = { "ACIN_PLUGIN", "ACIN_REMOVAL", NULL }; int i, irq, ret; @@ -176,7 +176,7 @@ static int axp20x_ac_power_probe(struct platform_device *pdev) if (!power) return -ENOMEM; - axp_data = (struct axp_data *)of_device_get_match_data(&pdev->dev); + axp_data = of_device_get_match_data(&pdev->dev); if (axp_data->acin_adc) { power->acin_v = devm_iio_channel_get(&pdev->dev, "acin_v"); @@ -230,10 +230,10 @@ static int axp20x_ac_power_probe(struct platform_device *pdev) static const struct of_device_id axp20x_ac_power_match[] = { { .compatible = "x-powers,axp202-ac-power-supply", - .data = (void *)&axp20x_data, + .data = &axp20x_data, }, { .compatible = "x-powers,axp221-ac-power-supply", - .data = (void *)&axp22x_data, + .data = &axp22x_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, axp20x_ac_power_match); diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c index d51ebd1da65e..9bfbde15b07d 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 a8dcabc32721..4cc6e038dfdd 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); |