Commit 972058ad authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents a9e89e54 2d7e6a83
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>;
};
}
...@@ -15,6 +15,7 @@ Required properties: ...@@ -15,6 +15,7 @@ Required properties:
* "ti,bq27520g2" - BQ27520-g2 * "ti,bq27520g2" - BQ27520-g2
* "ti,bq27520g3" - BQ27520-g3 * "ti,bq27520g3" - BQ27520-g3
* "ti,bq27520g4" - BQ27520-g4 * "ti,bq27520g4" - BQ27520-g4
* "ti,bq27521" - BQ27521
* "ti,bq27530" - BQ27530 * "ti,bq27530" - BQ27530
* "ti,bq27531" - BQ27531 * "ti,bq27531" - BQ27531
* "ti,bq27541" - BQ27541 * "ti,bq27541" - BQ27541
......
...@@ -14909,6 +14909,12 @@ F: include/linux/workqueue.h ...@@ -14909,6 +14909,12 @@ F: include/linux/workqueue.h
F: kernel/workqueue.c F: kernel/workqueue.c
F: Documentation/core-api/workqueue.rst 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 X-POWERS MULTIFUNCTION PMIC DEVICE DRIVERS
M: Chen-Yu Tsai <wens@csie.org> M: Chen-Yu Tsai <wens@csie.org>
L: linux-kernel@vger.kernel.org L: linux-kernel@vger.kernel.org
......
...@@ -98,15 +98,6 @@ config POWER_RESET_HISI ...@@ -98,15 +98,6 @@ config POWER_RESET_HISI
help help
Reboot support for Hisilicon boards. 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 config POWER_RESET_MSM
bool "Qualcomm MSM power-off driver" bool "Qualcomm MSM power-off driver"
depends on ARCH_QCOM depends on ARCH_QCOM
......
...@@ -10,7 +10,6 @@ obj-$(CONFIG_POWER_RESET_GEMINI_POWEROFF) += gemini-poweroff.o ...@@ -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) += gpio-poweroff.o
obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o
obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.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_MSM) += msm-poweroff.o
obj-$(CONFIG_POWER_RESET_PIIX4_POWEROFF) += piix4-poweroff.o obj-$(CONFIG_POWER_RESET_PIIX4_POWEROFF) += piix4-poweroff.o
obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o
......
...@@ -68,7 +68,7 @@ struct shdwc_config { ...@@ -68,7 +68,7 @@ struct shdwc_config {
}; };
struct shdwc { struct shdwc {
struct shdwc_config *cfg; const struct shdwc_config *cfg;
void __iomem *at91_shdwc_base; void __iomem *at91_shdwc_base;
}; };
...@@ -260,7 +260,7 @@ static int __init at91_shdwc_probe(struct platform_device *pdev) ...@@ -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); 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); sclk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(sclk)) if (IS_ERR(sclk))
......
/* 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);
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
#include <linux/pm.h> #include <linux/pm.h>
static void __iomem *msm_ps_hold; 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) void *data)
{ {
writel(0, msm_ps_hold); writel(0, msm_ps_hold);
...@@ -33,14 +33,13 @@ static int do_msm_restart(struct notifier_block *nb, unsigned long action, ...@@ -33,14 +33,13 @@ static int do_msm_restart(struct notifier_block *nb, unsigned long action,
} }
static struct notifier_block restart_nb = { static struct notifier_block restart_nb = {
.notifier_call = do_msm_restart, .notifier_call = deassert_pshold,
.priority = 128, .priority = 128,
}; };
static void do_msm_poweroff(void) static void do_msm_poweroff(void)
{ {
/* TODO: Add poweroff capability */ deassert_pshold(&restart_nb, 0, NULL);
do_msm_restart(&restart_nb, 0, NULL);
} }
static int msm_restart_probe(struct platform_device *pdev) static int msm_restart_probe(struct platform_device *pdev)
......
...@@ -82,3 +82,7 @@ static struct platform_driver zx_reboot_driver = { ...@@ -82,3 +82,7 @@ static struct platform_driver zx_reboot_driver = {
}, },
}; };
module_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");
...@@ -3218,11 +3218,13 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di) ...@@ -3218,11 +3218,13 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
} }
/* Enable backup battery charging */ /* 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, AB8500_RTC, AB8500_RTC_CTRL_REG,
RTC_BUP_CH_ENA, RTC_BUP_CH_ENA); 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__); dev_err(di->dev, "%s mask and set failed\n", __func__);
goto out;
}
if (is_ab8540(di->parent)) { if (is_ab8540(di->parent)) {
ret = abx500_mask_and_set_register_interruptible(di->dev, ret = abx500_mask_and_set_register_interruptible(di->dev,
......
...@@ -159,7 +159,7 @@ static int axp20x_ac_power_probe(struct platform_device *pdev) ...@@ -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 axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
struct power_supply_config psy_cfg = {}; struct power_supply_config psy_cfg = {};
struct axp20x_ac_power *power; 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", static const char * const irq_names[] = { "ACIN_PLUGIN", "ACIN_REMOVAL",
NULL }; NULL };
int i, irq, ret; int i, irq, ret;
...@@ -176,7 +176,7 @@ static int axp20x_ac_power_probe(struct platform_device *pdev) ...@@ -176,7 +176,7 @@ static int axp20x_ac_power_probe(struct platform_device *pdev)
if (!power) if (!power)
return -ENOMEM; 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) { if (axp_data->acin_adc) {
power->acin_v = devm_iio_channel_get(&pdev->dev, "acin_v"); 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) ...@@ -230,10 +230,10 @@ static int axp20x_ac_power_probe(struct platform_device *pdev)
static const struct of_device_id axp20x_ac_power_match[] = { static const struct of_device_id axp20x_ac_power_match[] = {
{ {
.compatible = "x-powers,axp202-ac-power-supply", .compatible = "x-powers,axp202-ac-power-supply",
.data = (void *)&axp20x_data, .data = &axp20x_data,
}, { }, {
.compatible = "x-powers,axp221-ac-power-supply", .compatible = "x-powers,axp221-ac-power-supply",
.data = (void *)&axp22x_data, .data = &axp22x_data,
}, { /* sentinel */ } }, { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, axp20x_ac_power_match); MODULE_DEVICE_TABLE(of, axp20x_ac_power_match);
......
This diff is collapsed.
/* /*
* axp288_fuel_gauge.c - Xpower AXP288 PMIC Fuel Gauge Driver * 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 * Copyright (C) 2014 Intel Corporation
* *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
...@@ -16,6 +17,7 @@ ...@@ -16,6 +17,7 @@
* *
*/ */
#include <linux/dmi.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/device.h> #include <linux/device.h>
...@@ -31,6 +33,12 @@ ...@@ -31,6 +33,12 @@
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <asm/unaligned.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_SAFE_MODE (1 << 3)
#define CHRG_STAT_BAT_VALID (1 << 4) #define CHRG_STAT_BAT_VALID (1 << 4)
#define CHRG_STAT_BAT_PRESENT (1 << 5) #define CHRG_STAT_BAT_PRESENT (1 << 5)
...@@ -100,11 +108,22 @@ enum { ...@@ -100,11 +108,22 @@ enum {
WL1_IRQ, 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 axp288_fg_info {
struct platform_device *pdev; struct platform_device *pdev;
struct regmap *regmap; struct regmap *regmap;
struct regmap_irq_chip_data *regmap_irqc; struct regmap_irq_chip_data *regmap_irqc;
int irq[AXP288_FG_INTR_NUM]; int irq[AXP288_FG_INTR_NUM];
struct iio_channel *iio_channel[IIO_CHANNEL_NUM];
struct power_supply *bat; struct power_supply *bat;
struct mutex lock; struct mutex lock;
int status; int status;
...@@ -199,33 +218,6 @@ static int fuel_gauge_read_12bit_word(struct axp288_fg_info *info, int reg) ...@@ -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); 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 #ifdef CONFIG_DEBUG_FS
static int fuel_gauge_debug_show(struct seq_file *s, void *data) 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) ...@@ -296,22 +288,22 @@ static int fuel_gauge_debug_show(struct seq_file *s, void *data)
AXP288_FG_TUNE5, AXP288_FG_TUNE5,
fuel_gauge_reg_readb(info, 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) if (ret >= 0)
seq_printf(s, "axp288-batttemp : %d\n", raw_val); 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) if (ret >= 0)
seq_printf(s, "axp288-pmictemp : %d\n", raw_val); 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) if (ret >= 0)
seq_printf(s, "axp288-systtemp : %d\n", raw_val); 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) if (ret >= 0)
seq_printf(s, "axp288-chrgcurr : %d\n", raw_val); 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) if (ret >= 0)
seq_printf(s, "axp288-dchrgcur : %d\n", raw_val); 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) if (ret >= 0)
seq_printf(s, "axp288-battvolt : %d\n", raw_val); 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) ...@@ -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) static void fuel_gauge_get_status(struct axp288_fg_info *info)
{ {
int pwr_stat, ret; int pwr_stat, fg_res;
int charge, discharge;
pwr_stat = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS); pwr_stat = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS);
if (pwr_stat < 0) { if (pwr_stat < 0) {
...@@ -360,36 +351,32 @@ static void fuel_gauge_get_status(struct axp288_fg_info *info) ...@@ -360,36 +351,32 @@ static void fuel_gauge_get_status(struct axp288_fg_info *info)
"PWR STAT read failed:%d\n", pwr_stat); "PWR STAT read failed:%d\n", pwr_stat);
return; return;
} }
ret = pmic_read_adc_val("axp288-chrg-curr", &charge, info);
if (ret < 0) { /* 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, dev_err(&info->pdev->dev,
"ADC charge current read failed:%d\n", ret); "FG RES read failed: %d\n", fg_res);
return; return;
} }
ret = pmic_read_adc_val("axp288-chrg-d-curr", &discharge, info); if (fg_res == (FG_REP_CAP_VALID | 100)) {
if (ret < 0) { info->status = POWER_SUPPLY_STATUS_FULL;
dev_err(&info->pdev->dev,
"ADC discharge current read failed:%d\n", ret);
return; return;
} }
}
if (charge > 0) if (pwr_stat & PS_STAT_BAT_CHRG_DIR)
info->status = POWER_SUPPLY_STATUS_CHARGING; info->status = POWER_SUPPLY_STATUS_CHARGING;
else if (discharge > 0)
info->status = POWER_SUPPLY_STATUS_DISCHARGING;
else {
if (pwr_stat & CHRG_STAT_BAT_PRESENT)
info->status = POWER_SUPPLY_STATUS_FULL;
else else
info->status = POWER_SUPPLY_STATUS_NOT_CHARGING; info->status = POWER_SUPPLY_STATUS_DISCHARGING;
}
} }
static int fuel_gauge_get_vbatt(struct axp288_fg_info *info, int *vbatt) static int fuel_gauge_get_vbatt(struct axp288_fg_info *info, int *vbatt)
{ {
int ret = 0, raw_val; 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) if (ret < 0)
goto vbatt_read_fail; goto vbatt_read_fail;
...@@ -400,24 +387,19 @@ static int fuel_gauge_get_vbatt(struct axp288_fg_info *info, int *vbatt) ...@@ -400,24 +387,19 @@ static int fuel_gauge_get_vbatt(struct axp288_fg_info *info, int *vbatt)
static int fuel_gauge_get_current(struct axp288_fg_info *info, int *cur) static int fuel_gauge_get_current(struct axp288_fg_info *info, int *cur)
{ {
int ret, value = 0; int ret, discharge;
int charge, discharge;
ret = pmic_read_adc_val("axp288-chrg-curr", &charge, info); /* First check discharge current, so that we do only 1 read on bat. */
if (ret < 0) ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &discharge);
goto current_read_fail;
ret = pmic_read_adc_val("axp288-chrg-d-curr", &discharge, info);
if (ret < 0) if (ret < 0)
goto current_read_fail; return ret;
if (charge > 0) if (discharge > 0) {
value = charge; *cur = -1 * discharge;
else if (discharge > 0) return 0;
value = -1 * discharge; }
*cur = value; return iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], cur);
current_read_fail:
return ret;
} }
static int fuel_gauge_get_vocv(struct axp288_fg_info *info, int *vocv) static int fuel_gauge_get_vocv(struct axp288_fg_info *info, int *vocv)
...@@ -698,12 +680,54 @@ static void fuel_gauge_init_irq(struct axp288_fg_info *info) ...@@ -698,12 +680,54 @@ static void fuel_gauge_init_irq(struct axp288_fg_info *info)
} }
} }
/*
* 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) static int axp288_fuel_gauge_probe(struct platform_device *pdev)
{ {
int ret = 0; int i, ret = 0;
struct axp288_fg_info *info; struct axp288_fg_info *info;
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
struct power_supply_config psy_cfg = {}; 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); info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info) if (!info)
...@@ -719,18 +743,39 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev) ...@@ -719,18 +743,39 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
mutex_init(&info->lock); mutex_init(&info->lock);
INIT_DELAYED_WORK(&info->status_monitor, fuel_gauge_status_monitor); 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); ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG);
if (ret < 0) if (ret < 0)
return ret; goto out_free_iio_chan;
if (!(ret & FG_DES_CAP1_VALID)) { if (!(ret & FG_DES_CAP1_VALID)) {
dev_err(&pdev->dev, "axp288 not configured by firmware\n"); 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); ret = fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1);
if (ret < 0) if (ret < 0)
return ret; goto out_free_iio_chan;
switch ((ret & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS) { switch ((ret & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS) {
case CHRG_CCCV_CV_4100MV: case CHRG_CCCV_CV_4100MV:
info->max_volt = 4100; info->max_volt = 4100;
...@@ -751,7 +796,7 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev) ...@@ -751,7 +796,7 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
if (IS_ERR(info->bat)) { if (IS_ERR(info->bat)) {
ret = PTR_ERR(info->bat); ret = PTR_ERR(info->bat);
dev_err(&pdev->dev, "failed to register battery: %d\n", ret); dev_err(&pdev->dev, "failed to register battery: %d\n", ret);
return ret; goto out_free_iio_chan;
} }
fuel_gauge_create_debugfs(info); fuel_gauge_create_debugfs(info);
...@@ -759,6 +804,13 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev) ...@@ -759,6 +804,13 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
schedule_delayed_work(&info->status_monitor, STATUS_MON_DELAY_JIFFIES); schedule_delayed_work(&info->status_monitor, STATUS_MON_DELAY_JIFFIES);
return 0; 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[] = { static const struct platform_device_id axp288_fg_id_table[] = {
...@@ -780,6 +832,9 @@ static int axp288_fuel_gauge_remove(struct platform_device *pdev) ...@@ -780,6 +832,9 @@ static int axp288_fuel_gauge_remove(struct platform_device *pdev)
if (info->irq[i] >= 0) if (info->irq[i] >= 0)
free_irq(info->irq[i], info); free_irq(info->irq[i], info);
for (i = 0; i < IIO_CHANNEL_NUM; i++)
iio_channel_release(info->iio_channel[i]);
return 0; return 0;
} }
......
...@@ -11,7 +11,6 @@ ...@@ -11,7 +11,6 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/extcon.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
...@@ -162,9 +161,6 @@ struct bq24190_dev_info { ...@@ -162,9 +161,6 @@ struct bq24190_dev_info {
struct device *dev; struct device *dev;
struct power_supply *charger; struct power_supply *charger;
struct power_supply *battery; 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; struct delayed_work input_current_limit_work;
char model_name[I2C_NAME_SIZE]; char model_name[I2C_NAME_SIZE];
bool initialized; bool initialized;
...@@ -686,6 +682,16 @@ static int bq24190_register_reset(struct bq24190_dev_info *bdi) ...@@ -686,6 +682,16 @@ static int bq24190_register_reset(struct bq24190_dev_info *bdi)
int ret, limit = 100; int ret, limit = 100;
u8 v; 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")) if (device_property_read_bool(bdi->dev, "disable-reset"))
return 0; return 0;
...@@ -1193,8 +1199,6 @@ static int bq24190_charger_set_property(struct power_supply *psy, ...@@ -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, static int bq24190_charger_property_is_writeable(struct power_supply *psy,
enum power_supply_property psp) enum power_supply_property psp)
{ {
int ret;
switch (psp) { switch (psp) {
case POWER_SUPPLY_PROP_ONLINE: case POWER_SUPPLY_PROP_ONLINE:
case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
...@@ -1202,13 +1206,10 @@ static int bq24190_charger_property_is_writeable(struct power_supply *psy, ...@@ -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_CURRENT:
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
ret = 1; return 1;
break;
default: default:
ret = 0; return 0;
} }
return ret;
} }
static void bq24190_input_current_limit_work(struct work_struct *work) 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) ...@@ -1623,75 +1624,6 @@ static irqreturn_t bq24190_irq_handler_thread(int irq, void *data)
return IRQ_HANDLED; 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) static int bq24190_hw_init(struct bq24190_dev_info *bdi)
{ {
u8 v; u8 v;
...@@ -1766,7 +1698,6 @@ static int bq24190_probe(struct i2c_client *client, ...@@ -1766,7 +1698,6 @@ static int bq24190_probe(struct i2c_client *client,
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct power_supply_config charger_cfg = {}, battery_cfg = {}; struct power_supply_config charger_cfg = {}, battery_cfg = {};
struct bq24190_dev_info *bdi; struct bq24190_dev_info *bdi;
const char *name;
int ret; int ret;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
...@@ -1796,25 +1727,6 @@ static int bq24190_probe(struct i2c_client *client, ...@@ -1796,25 +1727,6 @@ static int bq24190_probe(struct i2c_client *client,
return -EINVAL; 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_enable(dev);
pm_runtime_use_autosuspend(dev); pm_runtime_use_autosuspend(dev);
pm_runtime_set_autosuspend_delay(dev, 600); pm_runtime_set_autosuspend_delay(dev, 600);
...@@ -1882,20 +1794,6 @@ static int bq24190_probe(struct i2c_client *client, ...@@ -1882,20 +1794,6 @@ static int bq24190_probe(struct i2c_client *client,
if (ret < 0) if (ret < 0)
goto out_sysfs; 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); enable_irq_wake(client->irq);
pm_runtime_mark_last_busy(dev); pm_runtime_mark_last_busy(dev);
......
...@@ -323,6 +323,30 @@ static u8 ...@@ -323,6 +323,30 @@ static u8
[BQ27XXX_REG_AP] = INVALID_REG_ADDR, [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
BQ27XXX_DM_REG_ROWS, 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] = { bq27530_regs[BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_CTRL] = 0x00, [BQ27XXX_REG_CTRL] = 0x00,
[BQ27XXX_REG_TEMP] = 0x06, [BQ27XXX_REG_TEMP] = 0x06,
...@@ -557,6 +581,15 @@ static enum power_supply_property bq27520g4_props[] = { ...@@ -557,6 +581,15 @@ static enum power_supply_property bq27520g4_props[] = {
POWER_SUPPLY_PROP_MANUFACTURER, 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[] = { static enum power_supply_property bq27530_props[] = {
POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_PRESENT,
...@@ -671,6 +704,7 @@ static struct bq27xxx_dm_reg bq27500_dm_regs[] = { ...@@ -671,6 +704,7 @@ static struct bq27xxx_dm_reg bq27500_dm_regs[] = {
#define bq27520g2_dm_regs 0 #define bq27520g2_dm_regs 0
#define bq27520g3_dm_regs 0 #define bq27520g3_dm_regs 0
#define bq27520g4_dm_regs 0 #define bq27520g4_dm_regs 0
#define bq27521_dm_regs 0
#define bq27530_dm_regs 0 #define bq27530_dm_regs 0
#define bq27531_dm_regs 0 #define bq27531_dm_regs 0
#define bq27541_dm_regs 0 #define bq27541_dm_regs 0
...@@ -717,8 +751,8 @@ static struct bq27xxx_dm_reg bq27621_dm_regs[] = { ...@@ -717,8 +751,8 @@ static struct bq27xxx_dm_reg bq27621_dm_regs[] = {
#endif #endif
#define BQ27XXX_O_ZERO 0x00000001 #define BQ27XXX_O_ZERO 0x00000001
#define BQ27XXX_O_OTDC 0x00000002 #define BQ27XXX_O_OTDC 0x00000002 /* has OTC/OTD overtemperature flags */
#define BQ27XXX_O_UTOT 0x00000004 #define BQ27XXX_O_UTOT 0x00000004 /* has OT overtemperature flag */
#define BQ27XXX_O_CFGUP 0x00000008 #define BQ27XXX_O_CFGUP 0x00000008
#define BQ27XXX_O_RAM 0x00000010 #define BQ27XXX_O_RAM 0x00000010
...@@ -751,6 +785,7 @@ static struct { ...@@ -751,6 +785,7 @@ static struct {
[BQ27520G2] = BQ27XXX_DATA(bq27520g2, 0 , BQ27XXX_O_OTDC), [BQ27520G2] = BQ27XXX_DATA(bq27520g2, 0 , BQ27XXX_O_OTDC),
[BQ27520G3] = BQ27XXX_DATA(bq27520g3, 0 , BQ27XXX_O_OTDC), [BQ27520G3] = BQ27XXX_DATA(bq27520g3, 0 , BQ27XXX_O_OTDC),
[BQ27520G4] = BQ27XXX_DATA(bq27520g4, 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), [BQ27530] = BQ27XXX_DATA(bq27530, 0 , BQ27XXX_O_UTOT),
[BQ27531] = BQ27XXX_DATA(bq27531, 0 , BQ27XXX_O_UTOT), [BQ27531] = BQ27XXX_DATA(bq27531, 0 , BQ27XXX_O_UTOT),
[BQ27541] = BQ27XXX_DATA(bq27541, 0 , BQ27XXX_O_OTDC), [BQ27541] = BQ27XXX_DATA(bq27541, 0 , BQ27XXX_O_OTDC),
......
...@@ -239,6 +239,7 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = { ...@@ -239,6 +239,7 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
{ "bq27520g2", BQ27520G2 }, { "bq27520g2", BQ27520G2 },
{ "bq27520g3", BQ27520G3 }, { "bq27520g3", BQ27520G3 },
{ "bq27520g4", BQ27520G4 }, { "bq27520g4", BQ27520G4 },
{ "bq27521", BQ27521 },
{ "bq27530", BQ27530 }, { "bq27530", BQ27530 },
{ "bq27531", BQ27531 }, { "bq27531", BQ27531 },
{ "bq27541", BQ27541 }, { "bq27541", BQ27541 },
...@@ -269,6 +270,7 @@ static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = { ...@@ -269,6 +270,7 @@ static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = {
{ .compatible = "ti,bq27520g2" }, { .compatible = "ti,bq27520g2" },
{ .compatible = "ti,bq27520g3" }, { .compatible = "ti,bq27520g3" },
{ .compatible = "ti,bq27520g4" }, { .compatible = "ti,bq27520g4" },
{ .compatible = "ti,bq27521" },
{ .compatible = "ti,bq27530" }, { .compatible = "ti,bq27530" },
{ .compatible = "ti,bq27531" }, { .compatible = "ti,bq27531" },
{ .compatible = "ti,bq27541" }, { .compatible = "ti,bq27541" },
......
...@@ -578,7 +578,7 @@ static int check_charging_duration(struct charger_manager *cm) ...@@ -578,7 +578,7 @@ static int check_charging_duration(struct charger_manager *cm)
} else if (is_ext_pwr_online(cm) && !cm->charger_enabled) { } else if (is_ext_pwr_online(cm) && !cm->charger_enabled) {
duration = curr - cm->charging_end_time; 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)) { is_ext_pwr_online(cm)) {
dev_info(cm->dev, "Discharging duration exceed %ums\n", dev_info(cm->dev, "Discharging duration exceed %ums\n",
desc->discharging_max_duration_ms); desc->discharging_max_duration_ms);
......
...@@ -586,8 +586,8 @@ static int cpcap_battery_init_irq(struct platform_device *pdev, ...@@ -586,8 +586,8 @@ static int cpcap_battery_init_irq(struct platform_device *pdev,
int irq, error; int irq, error;
irq = platform_get_irq_byname(pdev, name); irq = platform_get_irq_byname(pdev, name);
if (!irq) if (irq < 0)
return -ENODEV; return irq;
error = devm_request_threaded_irq(ddata->dev, irq, NULL, error = devm_request_threaded_irq(ddata->dev, irq, NULL,
cpcap_battery_irq_thread, cpcap_battery_irq_thread,
......
...@@ -60,6 +60,7 @@ enum ltc294x_id { ...@@ -60,6 +60,7 @@ enum ltc294x_id {
#define LTC294X_REG_CONTROL_PRESCALER_SET(x) \ #define LTC294X_REG_CONTROL_PRESCALER_SET(x) \
((x << 3) & LTC294X_REG_CONTROL_PRESCALER_MASK) ((x << 3) & LTC294X_REG_CONTROL_PRESCALER_MASK)
#define LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED 0 #define LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED 0
#define LTC294X_REG_CONTROL_ADC_DISABLE(x) ((x) & ~(BIT(7) | BIT(6)))
struct ltc294x_info { struct ltc294x_info {
struct i2c_client *client; /* I2C Client pointer */ struct i2c_client *client; /* I2C Client pointer */
...@@ -523,6 +524,29 @@ static int ltc294x_i2c_probe(struct i2c_client *client, ...@@ -523,6 +524,29 @@ static int ltc294x_i2c_probe(struct i2c_client *client,
return 0; 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 #ifdef CONFIG_PM_SLEEP
static int ltc294x_suspend(struct device *dev) static int ltc294x_suspend(struct device *dev)
...@@ -589,6 +613,7 @@ static struct i2c_driver ltc294x_driver = { ...@@ -589,6 +613,7 @@ static struct i2c_driver ltc294x_driver = {
}, },
.probe = ltc294x_i2c_probe, .probe = ltc294x_i2c_probe,
.remove = ltc294x_i2c_remove, .remove = ltc294x_i2c_remove,
.shutdown = ltc294x_i2c_shutdown,
.id_table = ltc294x_i2c_id, .id_table = ltc294x_i2c_id,
}; };
module_i2c_driver(ltc294x_driver); module_i2c_driver(ltc294x_driver);
......
...@@ -123,6 +123,8 @@ static int max17042_get_temperature(struct max17042_chip *chip, int *temp) ...@@ -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) static int max17042_get_status(struct max17042_chip *chip, int *status)
{ {
int ret, charge_full, charge_now; int ret, charge_full, charge_now;
int avg_current;
u32 data;
ret = power_supply_am_i_supplied(chip->battery); ret = power_supply_am_i_supplied(chip->battery);
if (ret < 0) { if (ret < 0) {
...@@ -152,10 +154,31 @@ static int max17042_get_status(struct max17042_chip *chip, int *status) ...@@ -152,10 +154,31 @@ static int max17042_get_status(struct max17042_chip *chip, int *status)
if (ret < 0) if (ret < 0)
return ret; return ret;
if ((charge_full - charge_now) <= MAX17042_FULL_THRESHOLD) if ((charge_full - charge_now) <= MAX17042_FULL_THRESHOLD) {
*status = POWER_SUPPLY_STATUS_FULL; *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; *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; return 0;
} }
...@@ -863,16 +886,13 @@ static void max17042_init_worker(struct work_struct *work) ...@@ -863,16 +886,13 @@ static void max17042_init_worker(struct work_struct *work)
#ifdef CONFIG_OF #ifdef CONFIG_OF
static struct max17042_platform_data * 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 *dev = &chip->client->dev;
struct device_node *np = dev->of_node; struct device_node *np = dev->of_node;
u32 prop; u32 prop;
struct max17042_platform_data *pdata; struct max17042_platform_data *pdata;
if (!np)
return dev->platform_data;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) if (!pdata)
return NULL; return NULL;
...@@ -897,7 +917,8 @@ max17042_get_pdata(struct max17042_chip *chip) ...@@ -897,7 +917,8 @@ max17042_get_pdata(struct max17042_chip *chip)
return pdata; return pdata;
} }
#else #endif
static struct max17042_reg_data max17047_default_pdata_init_regs[] = { static struct max17042_reg_data max17047_default_pdata_init_regs[] = {
/* /*
* Some firmwares do not set FullSOCThr, Enable End-of-Charge Detection * 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[] = { ...@@ -907,15 +928,12 @@ static struct max17042_reg_data max17047_default_pdata_init_regs[] = {
}; };
static struct max17042_platform_data * 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 device *dev = &chip->client->dev;
struct max17042_platform_data *pdata; struct max17042_platform_data *pdata;
int ret, misc_cfg; 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 MAX17047 gets used on x86 where we might not have pdata, assume
* the firmware will already have initialized the fuel-gauge and provide * the firmware will already have initialized the fuel-gauge and provide
...@@ -948,7 +966,21 @@ max17042_get_pdata(struct max17042_chip *chip) ...@@ -948,7 +966,21 @@ max17042_get_pdata(struct max17042_chip *chip)
return pdata; 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 #endif
if (dev->platform_data)
return dev->platform_data;
return max17042_get_default_pdata(chip);
}
static const struct regmap_config max17042_regmap_config = { static const struct regmap_config max17042_regmap_config = {
.reg_bits = 8, .reg_bits = 8,
......
...@@ -183,7 +183,7 @@ static int sbsm_select(struct i2c_mux_core *muxc, u32 chan) ...@@ -183,7 +183,7 @@ static int sbsm_select(struct i2c_mux_core *muxc, u32 chan)
return ret; return ret;
/* chan goes from 1 ... 4 */ /* 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); ret = sbsm_write_word(data->client, SBSM_CMD_BATSYSSTATE, reg);
if (ret) if (ret)
dev_err(dev, "Failed to select channel %i\n", chan); dev_err(dev, "Failed to select channel %i\n", chan);
......
...@@ -16,6 +16,7 @@ enum bq27xxx_chip { ...@@ -16,6 +16,7 @@ enum bq27xxx_chip {
BQ27520G2, /* bq27520G2 */ BQ27520G2, /* bq27520G2 */
BQ27520G3, /* bq27520G3 */ BQ27520G3, /* bq27520G3 */
BQ27520G4, /* bq27520G4 */ BQ27520G4, /* bq27520G4 */
BQ27521, /* bq27521 */
BQ27530, /* bq27530, bq27531 */ BQ27530, /* bq27530, bq27531 */
BQ27531, BQ27531,
BQ27541, /* bq27541, bq27542, bq27546, bq27742 */ BQ27541, /* bq27541, bq27542, bq27546, bq27742 */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment