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
...
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 @@
* "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: 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 @@
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_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 {
- struct shdwc_config *cfg;
+ const struct shdwc_config *cfg;
void __iomem *at91_shdwc_base;
};
@@ -260,7 +260,7 @@
}
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 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 @@
},
};
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 @@
}
/* 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 @@
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 @@
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 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 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 @@
/* 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 @@
return ret;
}
+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 >>= 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;
+ }
+}
+
static inline int axp288_charger_set_vbus_inlmt(struct axp288_chrg_info *info,
int inlmt)
{
int ret;
- unsigned int val;
u8 reg_val;
- /* 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;
- }
-
- 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;
+ 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
+ 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 @@
if (ret < 0)
dev_err(&info->pdev->dev, "axp288 vbus path select %d\n", ret);
-
return ret;
}
@@ -292,9 +281,6 @@
{
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
platform_set_drvdata(pdev, info);
- mutex_init(&info->lock);
ret = charger_init_hw_regs(info);
if (ret)
@@ -836,19 +780,19 @@
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 @@
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 @@
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 @@
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 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 @@
"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 @@
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);
+ /* 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;
- ret = pmic_read_adc_val("axp288-chrg-d-curr", &discharge, info);
- 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 @@
}
}
+/*
+ * 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 @@
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 @@
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 @@
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 @@
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 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 @@
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_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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
[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 @@
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 @@
#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 @@
#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 @@
[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 @@
{ "bq27520g2", BQ27520G2 },
{ "bq27520g3", BQ27520G3 },
{ "bq27520g4", BQ27520G4 },
+ { "bq27521", BQ27521 },
{ "bq27530", BQ27530 },
{ "bq27531", BQ27531 },
{ "bq27541", BQ27541 },
@@ -269,6 +270,7 @@
{ .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 @@
} 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 @@
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 @@
#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 @@
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 @@
},
.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_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 @@
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 @@
#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 @@
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_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 @@
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 @@
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 @@
BQ27520G2, /* bq27520G2 */
BQ27520G3, /* bq27520G3 */
BQ27520G4, /* bq27520G4 */
+ BQ27521, /* bq27521 */
BQ27530, /* bq27530, bq27531 */
BQ27531,
BQ27541, /* bq27541, bq27542, bq27546, bq27742 */