power supply and reset changes for the v4.16 series
* bq27xxx: add bq27521 support * drop unused imx-snvs-poweroff driver * improve axp288 driver * misc. fixes -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE72YNB0Y/i3JqeVQT2O7X88g7+poFAlpx13wACgkQ2O7X88g7 +pp6QBAAlYcyFR1s/NrQA42zbMdFSHhzTzjQOoOXrik9ajRQNo5XuOyOYoS1LztW aaIzZm/GSgZ00wMNv/NoUkUU+CVhj2mhlIj/uintLmK8jryEcnLYAnrRiV38qkQQ JwQEet4IPhHQ4ljw6jexnhiieSLhl5HqufF1jDpV+b959sG0WyH1skeHbMM033c9 giIgSn8lrgjG5of/bnoTIAnbsH+hummURQ7yox4Dqa+dqJ0oJK3U0uorbeyQtCuB 57aPEiDfoxBBohkPcwpCCMOxkreShST2caNRrmyKHif3dj+80ZBIsHOme1rVaP0c XG5z3qu1lHkvxthLcNKEXAZ9+PD9kCKFIi3YUA8FLBwDyeYvJi+4uQ7VkzFXxK0H hYt4nYA5vO9i0rNaRdFPK/RYr6esTW9aVw3IASi9ic3oHncaW1Q/kpU7hglkND+w 8MOPARgLR6G86D3FTsI8bxmkLuKr1k7Vae2MnhnX3jgPDKFF35yTh21LrLJQXDzX nQQ4YoLwdbU0dvhDc1vQMNc2t3wOwpZjfg5a8f2nd7xqFRM4uE4batN8MkefNkxv W0Dd+0H4n1Gy+Z9vvSmlwt1iGWWa9QqhNeLDrrtqpN43AJUfP3ucd4nFlDUNS921 Ilt4e34OoXJoDNDsm00iCPduTtme7QChSnkiGY+cFiwA0CSgs3A= =XIx5 -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
Коммит
972058ad79
|
@ -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>;
|
||||
};
|
||||
}
|
|
@ -15,6 +15,7 @@ Required properties:
|
|||
* "ti,bq27520g2" - BQ27520-g2
|
||||
* "ti,bq27520g3" - BQ27520-g3
|
||||
* "ti,bq27520g4" - BQ27520-g4
|
||||
* "ti,bq27521" - BQ27521
|
||||
* "ti,bq27530" - BQ27530
|
||||
* "ti,bq27531" - BQ27531
|
||||
* "ti,bq27541" - BQ27541
|
||||
|
|
|
@ -14909,6 +14909,12 @@ F: include/linux/workqueue.h
|
|||
F: kernel/workqueue.c
|
||||
F: Documentation/core-api/workqueue.rst
|
||||
|
||||
X-POWERS AXP288 PMIC DRIVERS
|
||||
M: Hans de Goede <hdegoede@redhat.com>
|
||||
S: Maintained
|
||||
N: axp288
|
||||
F: drivers/acpi/pmic/intel_pmic_xpower.c
|
||||
|
||||
X-POWERS MULTIFUNCTION PMIC DEVICE DRIVERS
|
||||
M: Chen-Yu Tsai <wens@csie.org>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
|
|
|
@ -98,15 +98,6 @@ config POWER_RESET_HISI
|
|||
help
|
||||
Reboot support for Hisilicon boards.
|
||||
|
||||
config POWER_RESET_IMX
|
||||
bool "IMX6 power-off driver"
|
||||
depends on POWER_RESET && SOC_IMX6
|
||||
help
|
||||
This driver support power off external PMIC by PMIC_ON_REQ on i.mx6
|
||||
boards.If you want to use other pin to control external power,please
|
||||
say N here or disable in dts to make sure pm_power_off never be
|
||||
overwrote wrongly by this driver.
|
||||
|
||||
config POWER_RESET_MSM
|
||||
bool "Qualcomm MSM power-off driver"
|
||||
depends on ARCH_QCOM
|
||||
|
|
|
@ -10,7 +10,6 @@ obj-$(CONFIG_POWER_RESET_GEMINI_POWEROFF) += gemini-poweroff.o
|
|||
obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o
|
||||
obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o
|
||||
obj-$(CONFIG_POWER_RESET_IMX) += imx-snvs-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_PIIX4_POWEROFF) += piix4-poweroff.o
|
||||
obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o
|
||||
|
|
|
@ -68,7 +68,7 @@ struct shdwc_config {
|
|||
};
|
||||
|
||||
struct shdwc {
|
||||
struct shdwc_config *cfg;
|
||||
const struct shdwc_config *cfg;
|
||||
void __iomem *at91_shdwc_base;
|
||||
};
|
||||
|
||||
|
@ -260,7 +260,7 @@ static int __init at91_shdwc_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
match = of_match_node(at91_shdwc_of_match, pdev->dev.of_node);
|
||||
at91_shdwc->cfg = (struct shdwc_config *)(match->data);
|
||||
at91_shdwc->cfg = match->data;
|
||||
|
||||
sclk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(sclk))
|
||||
|
|
|
@ -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);
|
|
@ -23,7 +23,7 @@
|
|||
#include <linux/pm.h>
|
||||
|
||||
static void __iomem *msm_ps_hold;
|
||||
static int do_msm_restart(struct notifier_block *nb, unsigned long action,
|
||||
static int deassert_pshold(struct notifier_block *nb, unsigned long action,
|
||||
void *data)
|
||||
{
|
||||
writel(0, msm_ps_hold);
|
||||
|
@ -33,14 +33,13 @@ static int do_msm_restart(struct notifier_block *nb, unsigned long action,
|
|||
}
|
||||
|
||||
static struct notifier_block restart_nb = {
|
||||
.notifier_call = do_msm_restart,
|
||||
.notifier_call = deassert_pshold,
|
||||
.priority = 128,
|
||||
};
|
||||
|
||||
static void do_msm_poweroff(void)
|
||||
{
|
||||
/* TODO: Add poweroff capability */
|
||||
do_msm_restart(&restart_nb, 0, NULL);
|
||||
deassert_pshold(&restart_nb, 0, NULL);
|
||||
}
|
||||
|
||||
static int msm_restart_probe(struct platform_device *pdev)
|
||||
|
|
|
@ -82,3 +82,7 @@ static struct platform_driver zx_reboot_driver = {
|
|||
},
|
||||
};
|
||||
module_platform_driver(zx_reboot_driver);
|
||||
|
||||
MODULE_DESCRIPTION("ZTE SoCs reset driver");
|
||||
MODULE_AUTHOR("Jun Nie <jun.nie@linaro.org>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -3218,11 +3218,13 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
|
|||
}
|
||||
|
||||
/* Enable backup battery charging */
|
||||
abx500_mask_and_set_register_interruptible(di->dev,
|
||||
ret = abx500_mask_and_set_register_interruptible(di->dev,
|
||||
AB8500_RTC, AB8500_RTC_CTRL_REG,
|
||||
RTC_BUP_CH_ENA, RTC_BUP_CH_ENA);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
dev_err(di->dev, "%s mask and set failed\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (is_ab8540(di->parent)) {
|
||||
ret = abx500_mask_and_set_register_interruptible(di->dev,
|
||||
|
|
|
@ -159,7 +159,7 @@ static int axp20x_ac_power_probe(struct platform_device *pdev)
|
|||
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
|
||||
struct power_supply_config psy_cfg = {};
|
||||
struct axp20x_ac_power *power;
|
||||
struct axp_data *axp_data;
|
||||
const struct axp_data *axp_data;
|
||||
static const char * const irq_names[] = { "ACIN_PLUGIN", "ACIN_REMOVAL",
|
||||
NULL };
|
||||
int i, irq, ret;
|
||||
|
@ -176,7 +176,7 @@ static int axp20x_ac_power_probe(struct platform_device *pdev)
|
|||
if (!power)
|
||||
return -ENOMEM;
|
||||
|
||||
axp_data = (struct axp_data *)of_device_get_match_data(&pdev->dev);
|
||||
axp_data = of_device_get_match_data(&pdev->dev);
|
||||
|
||||
if (axp_data->acin_adc) {
|
||||
power->acin_v = devm_iio_channel_get(&pdev->dev, "acin_v");
|
||||
|
@ -230,10 +230,10 @@ static int axp20x_ac_power_probe(struct platform_device *pdev)
|
|||
static const struct of_device_id axp20x_ac_power_match[] = {
|
||||
{
|
||||
.compatible = "x-powers,axp202-ac-power-supply",
|
||||
.data = (void *)&axp20x_data,
|
||||
.data = &axp20x_data,
|
||||
}, {
|
||||
.compatible = "x-powers,axp221-ac-power-supply",
|
||||
.data = (void *)&axp22x_data,
|
||||
.data = &axp22x_data,
|
||||
}, { /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, axp20x_ac_power_match);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* axp288_charger.c - X-power AXP288 PMIC Charger driver
|
||||
*
|
||||
* Copyright (C) 2016-2017 Hans de Goede <hdegoede@redhat.com>
|
||||
* Copyright (C) 2014 Intel Corporation
|
||||
* Author: Ramakrishna Pallala <ramakrishna.pallala@intel.com>
|
||||
*
|
||||
|
@ -98,28 +99,10 @@
|
|||
#define CV_4200MV 4200 /* 4200mV */
|
||||
#define CV_4350MV 4350 /* 4350mV */
|
||||
|
||||
#define CC_200MA 200 /* 200mA */
|
||||
#define CC_600MA 600 /* 600mA */
|
||||
#define CC_800MA 800 /* 800mA */
|
||||
#define CC_1000MA 1000 /* 1000mA */
|
||||
#define CC_1600MA 1600 /* 1600mA */
|
||||
#define CC_2000MA 2000 /* 2000mA */
|
||||
|
||||
#define ILIM_100MA 100 /* 100mA */
|
||||
#define ILIM_500MA 500 /* 500mA */
|
||||
#define ILIM_900MA 900 /* 900mA */
|
||||
#define ILIM_1500MA 1500 /* 1500mA */
|
||||
#define ILIM_2000MA 2000 /* 2000mA */
|
||||
#define ILIM_2500MA 2500 /* 2500mA */
|
||||
#define ILIM_3000MA 3000 /* 3000mA */
|
||||
|
||||
#define AXP288_EXTCON_DEV_NAME "axp288_extcon"
|
||||
#define USB_HOST_EXTCON_HID "INT3496"
|
||||
#define USB_HOST_EXTCON_NAME "INT3496:00"
|
||||
|
||||
static const unsigned int cable_ids[] =
|
||||
{ EXTCON_CHG_USB_SDP, EXTCON_CHG_USB_CDP, EXTCON_CHG_USB_DCP };
|
||||
|
||||
enum {
|
||||
VBUS_OV_IRQ = 0,
|
||||
CHARGE_DONE_IRQ,
|
||||
|
@ -139,7 +122,6 @@ struct axp288_chrg_info {
|
|||
struct regmap_irq_chip_data *regmap_irqc;
|
||||
int irq[CHRG_INTR_END];
|
||||
struct power_supply *psy_usb;
|
||||
struct mutex lock;
|
||||
|
||||
/* OTG/Host mode */
|
||||
struct {
|
||||
|
@ -152,18 +134,14 @@ struct axp288_chrg_info {
|
|||
/* SDP/CDP/DCP USB charging cable notifications */
|
||||
struct {
|
||||
struct extcon_dev *edev;
|
||||
bool connected;
|
||||
enum power_supply_type chg_type;
|
||||
struct notifier_block nb[ARRAY_SIZE(cable_ids)];
|
||||
struct notifier_block nb;
|
||||
struct work_struct work;
|
||||
} cable;
|
||||
|
||||
int inlmt;
|
||||
int cc;
|
||||
int cv;
|
||||
int max_cc;
|
||||
int max_cv;
|
||||
int is_charger_enabled;
|
||||
};
|
||||
|
||||
static inline int axp288_charger_set_cc(struct axp288_chrg_info *info, int cc)
|
||||
|
@ -220,51 +198,63 @@ static inline int axp288_charger_set_cv(struct axp288_chrg_info *info, int cv)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static 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 @@ static int axp288_charger_vbus_path_select(struct axp288_chrg_info *info,
|
|||
if (ret < 0)
|
||||
dev_err(&info->pdev->dev, "axp288 vbus path select %d\n", ret);
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -292,9 +281,6 @@ static int axp288_charger_enable_charger(struct axp288_chrg_info *info,
|
|||
{
|
||||
int ret;
|
||||
|
||||
if ((int)enable == info->is_charger_enabled)
|
||||
return 0;
|
||||
|
||||
if (enable)
|
||||
ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,
|
||||
CHRG_CCCV_CHG_EN, CHRG_CCCV_CHG_EN);
|
||||
|
@ -303,8 +289,6 @@ static int axp288_charger_enable_charger(struct axp288_chrg_info *info,
|
|||
CHRG_CCCV_CHG_EN, 0);
|
||||
if (ret < 0)
|
||||
dev_err(&info->pdev->dev, "axp288 enable charger %d\n", ret);
|
||||
else
|
||||
info->is_charger_enabled = enable;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -376,8 +360,6 @@ static int axp288_charger_usb_set_property(struct power_supply *psy,
|
|||
int ret = 0;
|
||||
int scaled_val;
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||
scaled_val = min(val->intval, info->max_cc);
|
||||
|
@ -393,11 +375,15 @@ static int axp288_charger_usb_set_property(struct power_supply *psy,
|
|||
if (ret < 0)
|
||||
dev_warn(&info->pdev->dev, "set charge voltage failed\n");
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
ret = axp288_charger_set_vbus_inlmt(info, val->intval);
|
||||
if (ret < 0)
|
||||
dev_warn(&info->pdev->dev, "set input current limit failed\n");
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
mutex_unlock(&info->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -406,9 +392,7 @@ static int axp288_charger_usb_get_property(struct power_supply *psy,
|
|||
union power_supply_propval *val)
|
||||
{
|
||||
struct axp288_chrg_info *info = power_supply_get_drvdata(psy);
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
int ret;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
|
@ -419,7 +403,7 @@ static int axp288_charger_usb_get_property(struct power_supply *psy,
|
|||
}
|
||||
ret = axp288_charger_is_present(info);
|
||||
if (ret < 0)
|
||||
goto psy_get_prop_fail;
|
||||
return ret;
|
||||
val->intval = ret;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
|
@ -430,7 +414,7 @@ static int axp288_charger_usb_get_property(struct power_supply *psy,
|
|||
}
|
||||
ret = axp288_charger_is_online(info);
|
||||
if (ret < 0)
|
||||
goto psy_get_prop_fail;
|
||||
return ret;
|
||||
val->intval = ret;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_HEALTH:
|
||||
|
@ -448,17 +432,17 @@ static int axp288_charger_usb_get_property(struct power_supply *psy,
|
|||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
|
||||
val->intval = info->max_cv * 1000;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
|
||||
val->intval = info->inlmt * 1000;
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
ret = axp288_charger_get_vbus_inlmt(info);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
val->intval = ret;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto psy_get_prop_fail;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
psy_get_prop_fail:
|
||||
mutex_unlock(&info->lock);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int axp288_charger_property_is_writeable(struct power_supply *psy,
|
||||
|
@ -469,6 +453,7 @@ static int axp288_charger_property_is_writeable(struct power_supply *psy,
|
|||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
ret = 1;
|
||||
break;
|
||||
default:
|
||||
|
@ -487,7 +472,7 @@ static enum power_supply_property axp288_usb_props[] = {
|
|||
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
|
||||
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
|
||||
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
|
||||
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
|
||||
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
|
||||
};
|
||||
|
||||
static const struct power_supply_desc axp288_charger_desc = {
|
||||
|
@ -565,99 +550,53 @@ static void axp288_charger_extcon_evt_worker(struct work_struct *work)
|
|||
container_of(work, struct axp288_chrg_info, cable.work);
|
||||
int ret, current_limit;
|
||||
struct extcon_dev *edev = info->cable.edev;
|
||||
bool old_connected = info->cable.connected;
|
||||
enum power_supply_type old_chg_type = info->cable.chg_type;
|
||||
unsigned int val;
|
||||
|
||||
ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(&info->pdev->dev, "Error reading status (%d)\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Offline? Disable charging and bail */
|
||||
if (!(val & PS_STAT_VBUS_VALID)) {
|
||||
dev_dbg(&info->pdev->dev, "USB charger disconnected\n");
|
||||
axp288_charger_enable_charger(info, false);
|
||||
power_supply_changed(info->psy_usb);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Determine cable/charger type */
|
||||
if (extcon_get_state(edev, EXTCON_CHG_USB_SDP) > 0) {
|
||||
dev_dbg(&info->pdev->dev, "USB SDP charger is connected");
|
||||
info->cable.connected = true;
|
||||
info->cable.chg_type = POWER_SUPPLY_TYPE_USB;
|
||||
dev_dbg(&info->pdev->dev, "USB SDP charger is connected\n");
|
||||
current_limit = 500000;
|
||||
} else if (extcon_get_state(edev, EXTCON_CHG_USB_CDP) > 0) {
|
||||
dev_dbg(&info->pdev->dev, "USB CDP charger is connected");
|
||||
info->cable.connected = true;
|
||||
info->cable.chg_type = POWER_SUPPLY_TYPE_USB_CDP;
|
||||
dev_dbg(&info->pdev->dev, "USB CDP charger is connected\n");
|
||||
current_limit = 1500000;
|
||||
} else if (extcon_get_state(edev, EXTCON_CHG_USB_DCP) > 0) {
|
||||
dev_dbg(&info->pdev->dev, "USB DCP charger is connected");
|
||||
info->cable.connected = true;
|
||||
info->cable.chg_type = POWER_SUPPLY_TYPE_USB_DCP;
|
||||
dev_dbg(&info->pdev->dev, "USB DCP charger is connected\n");
|
||||
current_limit = 2000000;
|
||||
} else {
|
||||
if (old_connected)
|
||||
dev_dbg(&info->pdev->dev, "USB charger disconnected");
|
||||
info->cable.connected = false;
|
||||
info->cable.chg_type = POWER_SUPPLY_TYPE_USB;
|
||||
}
|
||||
|
||||
/* Cable status changed */
|
||||
if (old_connected == info->cable.connected &&
|
||||
old_chg_type == info->cable.chg_type)
|
||||
/* Charger type detection still in progress, bail. */
|
||||
return;
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
|
||||
if (info->cable.connected) {
|
||||
axp288_charger_enable_charger(info, false);
|
||||
|
||||
switch (info->cable.chg_type) {
|
||||
case POWER_SUPPLY_TYPE_USB:
|
||||
current_limit = ILIM_500MA;
|
||||
break;
|
||||
case POWER_SUPPLY_TYPE_USB_CDP:
|
||||
current_limit = ILIM_1500MA;
|
||||
break;
|
||||
case POWER_SUPPLY_TYPE_USB_DCP:
|
||||
current_limit = ILIM_2000MA;
|
||||
break;
|
||||
default:
|
||||
/* Unknown */
|
||||
current_limit = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set vbus current limit first, then enable charger */
|
||||
ret = axp288_charger_set_vbus_inlmt(info, current_limit);
|
||||
if (ret == 0)
|
||||
axp288_charger_enable_charger(info, true);
|
||||
else
|
||||
dev_err(&info->pdev->dev,
|
||||
"error setting current limit (%d)", ret);
|
||||
} else {
|
||||
axp288_charger_enable_charger(info, false);
|
||||
}
|
||||
|
||||
mutex_unlock(&info->lock);
|
||||
/* Set vbus current limit first, then enable charger */
|
||||
ret = axp288_charger_set_vbus_inlmt(info, current_limit);
|
||||
if (ret == 0)
|
||||
axp288_charger_enable_charger(info, true);
|
||||
else
|
||||
dev_err(&info->pdev->dev,
|
||||
"error setting current limit (%d)\n", ret);
|
||||
|
||||
power_supply_changed(info->psy_usb);
|
||||
}
|
||||
|
||||
/*
|
||||
* We need 3 copies of this, because there is no way to find out for which
|
||||
* cable id we are being called from the passed in arguments; and we must
|
||||
* have a separate nb for each extcon_register_notifier call.
|
||||
*/
|
||||
static int axp288_charger_handle_cable0_evt(struct notifier_block *nb,
|
||||
unsigned long event, void *param)
|
||||
static int axp288_charger_handle_cable_evt(struct notifier_block *nb,
|
||||
unsigned long event, void *param)
|
||||
{
|
||||
struct axp288_chrg_info *info =
|
||||
container_of(nb, struct axp288_chrg_info, cable.nb[0]);
|
||||
schedule_work(&info->cable.work);
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int axp288_charger_handle_cable1_evt(struct notifier_block *nb,
|
||||
unsigned long event, void *param)
|
||||
{
|
||||
struct axp288_chrg_info *info =
|
||||
container_of(nb, struct axp288_chrg_info, cable.nb[1]);
|
||||
schedule_work(&info->cable.work);
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int axp288_charger_handle_cable2_evt(struct notifier_block *nb,
|
||||
unsigned long event, void *param)
|
||||
{
|
||||
struct axp288_chrg_info *info =
|
||||
container_of(nb, struct axp288_chrg_info, cable.nb[2]);
|
||||
container_of(nb, struct axp288_chrg_info, cable.nb);
|
||||
schedule_work(&info->cable.work);
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
@ -785,6 +724,14 @@ static int charger_init_hw_regs(struct axp288_chrg_info *info)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void axp288_charger_cancel_work(void *data)
|
||||
{
|
||||
struct axp288_chrg_info *info = data;
|
||||
|
||||
cancel_work_sync(&info->otg.work);
|
||||
cancel_work_sync(&info->cable.work);
|
||||
}
|
||||
|
||||
static int axp288_charger_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret, i, pirq;
|
||||
|
@ -799,8 +746,6 @@ static int axp288_charger_probe(struct platform_device *pdev)
|
|||
info->pdev = pdev;
|
||||
info->regmap = axp20x->regmap;
|
||||
info->regmap_irqc = axp20x->regmap_irqc;
|
||||
info->cable.chg_type = -1;
|
||||
info->is_charger_enabled = -1;
|
||||
|
||||
info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME);
|
||||
if (info->cable.edev == NULL) {
|
||||
|
@ -820,7 +765,6 @@ static int axp288_charger_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
mutex_init(&info->lock);
|
||||
|
||||
ret = charger_init_hw_regs(info);
|
||||
if (ret)
|
||||
|
@ -836,19 +780,19 @@ static int axp288_charger_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Cancel our work on cleanup, register this before the notifiers */
|
||||
ret = devm_add_action(dev, axp288_charger_cancel_work, info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Register for extcon notification */
|
||||
INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker);
|
||||
info->cable.nb[0].notifier_call = axp288_charger_handle_cable0_evt;
|
||||
info->cable.nb[1].notifier_call = axp288_charger_handle_cable1_evt;
|
||||
info->cable.nb[2].notifier_call = axp288_charger_handle_cable2_evt;
|
||||
for (i = 0; i < ARRAY_SIZE(cable_ids); i++) {
|
||||
ret = devm_extcon_register_notifier(dev, info->cable.edev,
|
||||
cable_ids[i], &info->cable.nb[i]);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register extcon notifier for %u: %d\n",
|
||||
cable_ids[i], ret);
|
||||
return ret;
|
||||
}
|
||||
info->cable.nb.notifier_call = axp288_charger_handle_cable_evt;
|
||||
ret = devm_extcon_register_notifier_all(dev, info->cable.edev,
|
||||
&info->cable.nb);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register cable extcon notifier\n");
|
||||
return ret;
|
||||
}
|
||||
schedule_work(&info->cable.work);
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* axp288_fuel_gauge.c - Xpower AXP288 PMIC Fuel Gauge Driver
|
||||
*
|
||||
* Copyright (C) 2016-2017 Hans de Goede <hdegoede@redhat.com>
|
||||
* Copyright (C) 2014 Intel Corporation
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -16,6 +17,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/device.h>
|
||||
|
@ -31,6 +33,12 @@
|
|||
#include <linux/seq_file.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define PS_STAT_VBUS_TRIGGER (1 << 0)
|
||||
#define PS_STAT_BAT_CHRG_DIR (1 << 2)
|
||||
#define PS_STAT_VBAT_ABOVE_VHOLD (1 << 3)
|
||||
#define PS_STAT_VBUS_VALID (1 << 4)
|
||||
#define PS_STAT_VBUS_PRESENT (1 << 5)
|
||||
|
||||
#define CHRG_STAT_BAT_SAFE_MODE (1 << 3)
|
||||
#define CHRG_STAT_BAT_VALID (1 << 4)
|
||||
#define CHRG_STAT_BAT_PRESENT (1 << 5)
|
||||
|
@ -100,11 +108,22 @@ enum {
|
|||
WL1_IRQ,
|
||||
};
|
||||
|
||||
enum {
|
||||
BAT_TEMP = 0,
|
||||
PMIC_TEMP,
|
||||
SYSTEM_TEMP,
|
||||
BAT_CHRG_CURR,
|
||||
BAT_D_CURR,
|
||||
BAT_VOLT,
|
||||
IIO_CHANNEL_NUM
|
||||
};
|
||||
|
||||
struct axp288_fg_info {
|
||||
struct platform_device *pdev;
|
||||
struct regmap *regmap;
|
||||
struct regmap_irq_chip_data *regmap_irqc;
|
||||
int irq[AXP288_FG_INTR_NUM];
|
||||
struct iio_channel *iio_channel[IIO_CHANNEL_NUM];
|
||||
struct power_supply *bat;
|
||||
struct mutex lock;
|
||||
int status;
|
||||
|
@ -199,33 +218,6 @@ static int fuel_gauge_read_12bit_word(struct axp288_fg_info *info, int reg)
|
|||
return (buf[0] << 4) | ((buf[1] >> 4) & 0x0f);
|
||||
}
|
||||
|
||||
static int pmic_read_adc_val(const char *name, int *raw_val,
|
||||
struct axp288_fg_info *info)
|
||||
{
|
||||
int ret, val = 0;
|
||||
struct iio_channel *indio_chan;
|
||||
|
||||
indio_chan = iio_channel_get(NULL, name);
|
||||
if (IS_ERR_OR_NULL(indio_chan)) {
|
||||
ret = PTR_ERR(indio_chan);
|
||||
goto exit;
|
||||
}
|
||||
ret = iio_read_channel_raw(indio_chan, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(&info->pdev->dev,
|
||||
"IIO channel read error: %x, %x\n", ret, val);
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
dev_dbg(&info->pdev->dev, "adc raw val=%x\n", val);
|
||||
*raw_val = val;
|
||||
|
||||
err_exit:
|
||||
iio_channel_release(indio_chan);
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static int fuel_gauge_debug_show(struct seq_file *s, void *data)
|
||||
{
|
||||
|
@ -296,22 +288,22 @@ static int fuel_gauge_debug_show(struct seq_file *s, void *data)
|
|||
AXP288_FG_TUNE5,
|
||||
fuel_gauge_reg_readb(info, AXP288_FG_TUNE5));
|
||||
|
||||
ret = pmic_read_adc_val("axp288-batt-temp", &raw_val, info);
|
||||
ret = iio_read_channel_raw(info->iio_channel[BAT_TEMP], &raw_val);
|
||||
if (ret >= 0)
|
||||
seq_printf(s, "axp288-batttemp : %d\n", raw_val);
|
||||
ret = pmic_read_adc_val("axp288-pmic-temp", &raw_val, info);
|
||||
ret = iio_read_channel_raw(info->iio_channel[PMIC_TEMP], &raw_val);
|
||||
if (ret >= 0)
|
||||
seq_printf(s, "axp288-pmictemp : %d\n", raw_val);
|
||||
ret = pmic_read_adc_val("axp288-system-temp", &raw_val, info);
|
||||
ret = iio_read_channel_raw(info->iio_channel[SYSTEM_TEMP], &raw_val);
|
||||
if (ret >= 0)
|
||||
seq_printf(s, "axp288-systtemp : %d\n", raw_val);
|
||||
ret = pmic_read_adc_val("axp288-chrg-curr", &raw_val, info);
|
||||
ret = iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], &raw_val);
|
||||
if (ret >= 0)
|
||||
seq_printf(s, "axp288-chrgcurr : %d\n", raw_val);
|
||||
ret = pmic_read_adc_val("axp288-chrg-d-curr", &raw_val, info);
|
||||
ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &raw_val);
|
||||
if (ret >= 0)
|
||||
seq_printf(s, "axp288-dchrgcur : %d\n", raw_val);
|
||||
ret = pmic_read_adc_val("axp288-batt-volt", &raw_val, info);
|
||||
ret = iio_read_channel_raw(info->iio_channel[BAT_VOLT], &raw_val);
|
||||
if (ret >= 0)
|
||||
seq_printf(s, "axp288-battvolt : %d\n", raw_val);
|
||||
|
||||
|
@ -351,8 +343,7 @@ static inline void fuel_gauge_remove_debugfs(struct axp288_fg_info *info)
|
|||
|
||||
static void fuel_gauge_get_status(struct axp288_fg_info *info)
|
||||
{
|
||||
int pwr_stat, ret;
|
||||
int charge, discharge;
|
||||
int pwr_stat, fg_res;
|
||||
|
||||
pwr_stat = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS);
|
||||
if (pwr_stat < 0) {
|
||||
|
@ -360,36 +351,32 @@ static void fuel_gauge_get_status(struct axp288_fg_info *info)
|
|||
"PWR STAT read failed:%d\n", pwr_stat);
|
||||
return;
|
||||
}
|
||||
ret = pmic_read_adc_val("axp288-chrg-curr", &charge, info);
|
||||
if (ret < 0) {
|
||||
dev_err(&info->pdev->dev,
|
||||
"ADC charge current read failed:%d\n", ret);
|
||||
return;
|
||||
}
|
||||
ret = pmic_read_adc_val("axp288-chrg-d-curr", &discharge, info);
|
||||
if (ret < 0) {
|
||||
dev_err(&info->pdev->dev,
|
||||
"ADC discharge current read failed:%d\n", ret);
|
||||
return;
|
||||
|
||||
/* Report full if Vbus is valid and the reported capacity is 100% */
|
||||
if (pwr_stat & PS_STAT_VBUS_VALID) {
|
||||
fg_res = fuel_gauge_reg_readb(info, AXP20X_FG_RES);
|
||||
if (fg_res < 0) {
|
||||
dev_err(&info->pdev->dev,
|
||||
"FG RES read failed: %d\n", fg_res);
|
||||
return;
|
||||
}
|
||||
if (fg_res == (FG_REP_CAP_VALID | 100)) {
|
||||
info->status = POWER_SUPPLY_STATUS_FULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (charge > 0)
|
||||
if (pwr_stat & PS_STAT_BAT_CHRG_DIR)
|
||||
info->status = POWER_SUPPLY_STATUS_CHARGING;
|
||||
else if (discharge > 0)
|
||||
else
|
||||
info->status = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
else {
|
||||
if (pwr_stat & CHRG_STAT_BAT_PRESENT)
|
||||
info->status = POWER_SUPPLY_STATUS_FULL;
|
||||
else
|
||||
info->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
}
|
||||
}
|
||||
|
||||
static int fuel_gauge_get_vbatt(struct axp288_fg_info *info, int *vbatt)
|
||||
{
|
||||
int ret = 0, raw_val;
|
||||
|
||||
ret = pmic_read_adc_val("axp288-batt-volt", &raw_val, info);
|
||||
ret = iio_read_channel_raw(info->iio_channel[BAT_VOLT], &raw_val);
|
||||
if (ret < 0)
|
||||
goto vbatt_read_fail;
|
||||
|
||||
|
@ -400,24 +387,19 @@ vbatt_read_fail:
|
|||
|
||||
static int fuel_gauge_get_current(struct axp288_fg_info *info, int *cur)
|
||||
{
|
||||
int ret, value = 0;
|
||||
int charge, discharge;
|
||||
int ret, discharge;
|
||||
|
||||
ret = pmic_read_adc_val("axp288-chrg-curr", &charge, info);
|
||||
/* 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 @@ intr_failed:
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Some devices have no battery (HDMI sticks) and the axp288 battery's
|
||||
* detection reports one despite it not being there.
|
||||
*/
|
||||
static const struct dmi_system_id axp288_fuel_gauge_blacklist[] = {
|
||||
{
|
||||
/* Intel Cherry Trail Compute Stick, Windows version */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "STK1AW32SC"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* Intel Cherry Trail Compute Stick, version without an OS */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "STK1A32SC"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* Meegopad T08 */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Default string"),
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "To be filled by OEM."),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "T3 MRD"),
|
||||
DMI_MATCH(DMI_BOARD_VERSION, "V1.1"),
|
||||
},
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static int axp288_fuel_gauge_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
int i, ret = 0;
|
||||
struct axp288_fg_info *info;
|
||||
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
|
||||
struct power_supply_config psy_cfg = {};
|
||||
static const char * const iio_chan_name[] = {
|
||||
[BAT_TEMP] = "axp288-batt-temp",
|
||||
[PMIC_TEMP] = "axp288-pmic-temp",
|
||||
[SYSTEM_TEMP] = "axp288-system-temp",
|
||||
[BAT_CHRG_CURR] = "axp288-chrg-curr",
|
||||
[BAT_D_CURR] = "axp288-chrg-d-curr",
|
||||
[BAT_VOLT] = "axp288-batt-volt",
|
||||
};
|
||||
|
||||
if (dmi_check_system(axp288_fuel_gauge_blacklist))
|
||||
return -ENODEV;
|
||||
|
||||
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
|
@ -719,18 +743,39 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
|
|||
mutex_init(&info->lock);
|
||||
INIT_DELAYED_WORK(&info->status_monitor, fuel_gauge_status_monitor);
|
||||
|
||||
for (i = 0; i < IIO_CHANNEL_NUM; i++) {
|
||||
/*
|
||||
* Note cannot use devm_iio_channel_get because x86 systems
|
||||
* lack the device<->channel maps which iio_channel_get will
|
||||
* try to use when passed a non NULL device pointer.
|
||||
*/
|
||||
info->iio_channel[i] =
|
||||
iio_channel_get(NULL, iio_chan_name[i]);
|
||||
if (IS_ERR(info->iio_channel[i])) {
|
||||
ret = PTR_ERR(info->iio_channel[i]);
|
||||
dev_dbg(&pdev->dev, "error getting iiochan %s: %d\n",
|
||||
iio_chan_name[i], ret);
|
||||
/* Wait for axp288_adc to load */
|
||||
if (ret == -ENODEV)
|
||||
ret = -EPROBE_DEFER;
|
||||
|
||||
goto out_free_iio_chan;
|
||||
}
|
||||
}
|
||||
|
||||
ret = fuel_gauge_reg_readb(info, AXP288_FG_DES_CAP1_REG);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto out_free_iio_chan;
|
||||
|
||||
if (!(ret & FG_DES_CAP1_VALID)) {
|
||||
dev_err(&pdev->dev, "axp288 not configured by firmware\n");
|
||||
return -ENODEV;
|
||||
ret = -ENODEV;
|
||||
goto out_free_iio_chan;
|
||||
}
|
||||
|
||||
ret = fuel_gauge_reg_readb(info, AXP20X_CHRG_CTRL1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto out_free_iio_chan;
|
||||
switch ((ret & CHRG_CCCV_CV_MASK) >> CHRG_CCCV_CV_BIT_POS) {
|
||||
case CHRG_CCCV_CV_4100MV:
|
||||
info->max_volt = 4100;
|
||||
|
@ -751,7 +796,7 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(info->bat)) {
|
||||
ret = PTR_ERR(info->bat);
|
||||
dev_err(&pdev->dev, "failed to register battery: %d\n", ret);
|
||||
return ret;
|
||||
goto out_free_iio_chan;
|
||||
}
|
||||
|
||||
fuel_gauge_create_debugfs(info);
|
||||
|
@ -759,6 +804,13 @@ static int axp288_fuel_gauge_probe(struct platform_device *pdev)
|
|||
schedule_delayed_work(&info->status_monitor, STATUS_MON_DELAY_JIFFIES);
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_iio_chan:
|
||||
for (i = 0; i < IIO_CHANNEL_NUM; i++)
|
||||
if (!IS_ERR_OR_NULL(info->iio_channel[i]))
|
||||
iio_channel_release(info->iio_channel[i]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct platform_device_id axp288_fg_id_table[] = {
|
||||
|
@ -780,6 +832,9 @@ static int axp288_fuel_gauge_remove(struct platform_device *pdev)
|
|||
if (info->irq[i] >= 0)
|
||||
free_irq(info->irq[i], info);
|
||||
|
||||
for (i = 0; i < IIO_CHANNEL_NUM; i++)
|
||||
iio_channel_release(info->iio_channel[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/extcon.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
@ -162,9 +161,6 @@ struct bq24190_dev_info {
|
|||
struct device *dev;
|
||||
struct power_supply *charger;
|
||||
struct power_supply *battery;
|
||||
struct extcon_dev *extcon;
|
||||
struct notifier_block extcon_nb;
|
||||
struct delayed_work extcon_work;
|
||||
struct delayed_work input_current_limit_work;
|
||||
char model_name[I2C_NAME_SIZE];
|
||||
bool initialized;
|
||||
|
@ -686,6 +682,16 @@ static int bq24190_register_reset(struct bq24190_dev_info *bdi)
|
|||
int ret, limit = 100;
|
||||
u8 v;
|
||||
|
||||
/*
|
||||
* This prop. can be passed on device instantiation from platform code:
|
||||
* struct property_entry pe[] =
|
||||
* { PROPERTY_ENTRY_BOOL("disable-reset"), ... };
|
||||
* struct i2c_board_info bi =
|
||||
* { .type = "bq24190", .addr = 0x6b, .properties = pe, .irq = irq };
|
||||
* struct i2c_adapter ad = { ... };
|
||||
* i2c_add_adapter(&ad);
|
||||
* i2c_new_device(&ad, &bi);
|
||||
*/
|
||||
if (device_property_read_bool(bdi->dev, "disable-reset"))
|
||||
return 0;
|
||||
|
||||
|
@ -1193,8 +1199,6 @@ static int bq24190_charger_set_property(struct power_supply *psy,
|
|||
static int bq24190_charger_property_is_writeable(struct power_supply *psy,
|
||||
enum power_supply_property psp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
|
||||
|
@ -1202,13 +1206,10 @@ static int bq24190_charger_property_is_writeable(struct power_supply *psy,
|
|||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
ret = 1;
|
||||
break;
|
||||
return 1;
|
||||
default:
|
||||
ret = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void bq24190_input_current_limit_work(struct work_struct *work)
|
||||
|
@ -1623,75 +1624,6 @@ static irqreturn_t bq24190_irq_handler_thread(int irq, void *data)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void bq24190_extcon_work(struct work_struct *work)
|
||||
{
|
||||
struct bq24190_dev_info *bdi =
|
||||
container_of(work, struct bq24190_dev_info, extcon_work.work);
|
||||
int error, iinlim = 0;
|
||||
u8 v;
|
||||
|
||||
error = pm_runtime_get_sync(bdi->dev);
|
||||
if (error < 0) {
|
||||
dev_warn(bdi->dev, "pm_runtime_get failed: %i\n", error);
|
||||
pm_runtime_put_noidle(bdi->dev);
|
||||
return;
|
||||
}
|
||||
|
||||
if (extcon_get_state(bdi->extcon, EXTCON_CHG_USB_SDP) == 1)
|
||||
iinlim = 500000;
|
||||
else if (extcon_get_state(bdi->extcon, EXTCON_CHG_USB_CDP) == 1 ||
|
||||
extcon_get_state(bdi->extcon, EXTCON_CHG_USB_ACA) == 1)
|
||||
iinlim = 1500000;
|
||||
else if (extcon_get_state(bdi->extcon, EXTCON_CHG_USB_DCP) == 1)
|
||||
iinlim = 2000000;
|
||||
|
||||
if (iinlim) {
|
||||
error = bq24190_set_field_val(bdi, BQ24190_REG_ISC,
|
||||
BQ24190_REG_ISC_IINLIM_MASK,
|
||||
BQ24190_REG_ISC_IINLIM_SHIFT,
|
||||
bq24190_isc_iinlim_values,
|
||||
ARRAY_SIZE(bq24190_isc_iinlim_values),
|
||||
iinlim);
|
||||
if (error < 0)
|
||||
dev_err(bdi->dev, "Can't set IINLIM: %d\n", error);
|
||||
}
|
||||
|
||||
/* if no charger found and in USB host mode, set OTG 5V boost, else normal */
|
||||
if (!iinlim && extcon_get_state(bdi->extcon, EXTCON_USB_HOST) == 1)
|
||||
v = BQ24190_REG_POC_CHG_CONFIG_OTG;
|
||||
else
|
||||
v = BQ24190_REG_POC_CHG_CONFIG_CHARGE;
|
||||
|
||||
error = bq24190_write_mask(bdi, BQ24190_REG_POC,
|
||||
BQ24190_REG_POC_CHG_CONFIG_MASK,
|
||||
BQ24190_REG_POC_CHG_CONFIG_SHIFT,
|
||||
v);
|
||||
if (error < 0)
|
||||
dev_err(bdi->dev, "Can't set CHG_CONFIG: %d\n", error);
|
||||
|
||||
pm_runtime_mark_last_busy(bdi->dev);
|
||||
pm_runtime_put_autosuspend(bdi->dev);
|
||||
}
|
||||
|
||||
static int bq24190_extcon_event(struct notifier_block *nb, unsigned long event,
|
||||
void *param)
|
||||
{
|
||||
struct bq24190_dev_info *bdi =
|
||||
container_of(nb, struct bq24190_dev_info, extcon_nb);
|
||||
|
||||
/*
|
||||
* The Power-Good detection may take up to 220ms, sometimes
|
||||
* the external charger detection is quicker, and the bq24190 will
|
||||
* reset to iinlim based on its own charger detection (which is not
|
||||
* hooked up when using external charger detection) resulting in
|
||||
* a too low default 500mA iinlim. Delay applying the extcon value
|
||||
* for 300ms to avoid this.
|
||||
*/
|
||||
queue_delayed_work(system_wq, &bdi->extcon_work, msecs_to_jiffies(300));
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int bq24190_hw_init(struct bq24190_dev_info *bdi)
|
||||
{
|
||||
u8 v;
|
||||
|
@ -1766,7 +1698,6 @@ static int bq24190_probe(struct i2c_client *client,
|
|||
struct device *dev = &client->dev;
|
||||
struct power_supply_config charger_cfg = {}, battery_cfg = {};
|
||||
struct bq24190_dev_info *bdi;
|
||||
const char *name;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
|
||||
|
@ -1796,25 +1727,6 @@ static int bq24190_probe(struct i2c_client *client,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Devicetree platforms should get extcon via phandle (not yet supported).
|
||||
* On ACPI platforms, extcon clients may invoke us with:
|
||||
* struct property_entry pe[] =
|
||||
* { PROPERTY_ENTRY_STRING("extcon-name", client_name), ... };
|
||||
* struct i2c_board_info bi =
|
||||
* { .type = "bq24190", .addr = 0x6b, .properties = pe, .irq = irq };
|
||||
* struct i2c_adapter ad = { ... };
|
||||
* i2c_add_adapter(&ad);
|
||||
* i2c_new_device(&ad, &bi);
|
||||
*/
|
||||
if (device_property_read_string(dev, "extcon-name", &name) == 0) {
|
||||
bdi->extcon = extcon_get_extcon_dev(name);
|
||||
if (!bdi->extcon)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
dev_info(bdi->dev, "using extcon device %s\n", name);
|
||||
}
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_set_autosuspend_delay(dev, 600);
|
||||
|
@ -1882,20 +1794,6 @@ static int bq24190_probe(struct i2c_client *client,
|
|||
if (ret < 0)
|
||||
goto out_sysfs;
|
||||
|
||||
if (bdi->extcon) {
|
||||
INIT_DELAYED_WORK(&bdi->extcon_work, bq24190_extcon_work);
|
||||
bdi->extcon_nb.notifier_call = bq24190_extcon_event;
|
||||
ret = devm_extcon_register_notifier_all(dev, bdi->extcon,
|
||||
&bdi->extcon_nb);
|
||||
if (ret) {
|
||||
dev_err(dev, "Can't register extcon\n");
|
||||
goto out_sysfs;
|
||||
}
|
||||
|
||||
/* Sync initial cable state */
|
||||
queue_delayed_work(system_wq, &bdi->extcon_work, 0);
|
||||
}
|
||||
|
||||
enable_irq_wake(client->irq);
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
|
|
|
@ -323,6 +323,30 @@ static u8
|
|||
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
|
||||
BQ27XXX_DM_REG_ROWS,
|
||||
},
|
||||
bq27521_regs[BQ27XXX_REG_MAX] = {
|
||||
[BQ27XXX_REG_CTRL] = 0x02,
|
||||
[BQ27XXX_REG_TEMP] = 0x0a,
|
||||
[BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_VOLT] = 0x0c,
|
||||
[BQ27XXX_REG_AI] = 0x0e,
|
||||
[BQ27XXX_REG_FLAGS] = 0x08,
|
||||
[BQ27XXX_REG_TTE] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_TTES] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_NAC] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_FCC] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_CYCT] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_AE] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_SOC] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_DM_CTRL] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_DM_CLASS] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
|
||||
[BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
|
||||
},
|
||||
bq27530_regs[BQ27XXX_REG_MAX] = {
|
||||
[BQ27XXX_REG_CTRL] = 0x00,
|
||||
[BQ27XXX_REG_TEMP] = 0x06,
|
||||
|
@ -557,6 +581,15 @@ static enum power_supply_property bq27520g4_props[] = {
|
|||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
};
|
||||
|
||||
static enum power_supply_property bq27521_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_TEMP,
|
||||
POWER_SUPPLY_PROP_TECHNOLOGY,
|
||||
};
|
||||
|
||||
static enum power_supply_property bq27530_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
|
@ -671,6 +704,7 @@ static struct bq27xxx_dm_reg bq27500_dm_regs[] = {
|
|||
#define bq27520g2_dm_regs 0
|
||||
#define bq27520g3_dm_regs 0
|
||||
#define bq27520g4_dm_regs 0
|
||||
#define bq27521_dm_regs 0
|
||||
#define bq27530_dm_regs 0
|
||||
#define bq27531_dm_regs 0
|
||||
#define bq27541_dm_regs 0
|
||||
|
@ -717,8 +751,8 @@ static struct bq27xxx_dm_reg bq27621_dm_regs[] = {
|
|||
#endif
|
||||
|
||||
#define BQ27XXX_O_ZERO 0x00000001
|
||||
#define BQ27XXX_O_OTDC 0x00000002
|
||||
#define BQ27XXX_O_UTOT 0x00000004
|
||||
#define BQ27XXX_O_OTDC 0x00000002 /* has OTC/OTD overtemperature flags */
|
||||
#define BQ27XXX_O_UTOT 0x00000004 /* has OT overtemperature flag */
|
||||
#define BQ27XXX_O_CFGUP 0x00000008
|
||||
#define BQ27XXX_O_RAM 0x00000010
|
||||
|
||||
|
@ -751,6 +785,7 @@ static struct {
|
|||
[BQ27520G2] = BQ27XXX_DATA(bq27520g2, 0 , BQ27XXX_O_OTDC),
|
||||
[BQ27520G3] = BQ27XXX_DATA(bq27520g3, 0 , BQ27XXX_O_OTDC),
|
||||
[BQ27520G4] = BQ27XXX_DATA(bq27520g4, 0 , BQ27XXX_O_OTDC),
|
||||
[BQ27521] = BQ27XXX_DATA(bq27521, 0 , 0),
|
||||
[BQ27530] = BQ27XXX_DATA(bq27530, 0 , BQ27XXX_O_UTOT),
|
||||
[BQ27531] = BQ27XXX_DATA(bq27531, 0 , BQ27XXX_O_UTOT),
|
||||
[BQ27541] = BQ27XXX_DATA(bq27541, 0 , BQ27XXX_O_OTDC),
|
||||
|
|
|
@ -239,6 +239,7 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
|
|||
{ "bq27520g2", BQ27520G2 },
|
||||
{ "bq27520g3", BQ27520G3 },
|
||||
{ "bq27520g4", BQ27520G4 },
|
||||
{ "bq27521", BQ27521 },
|
||||
{ "bq27530", BQ27530 },
|
||||
{ "bq27531", BQ27531 },
|
||||
{ "bq27541", BQ27541 },
|
||||
|
@ -269,6 +270,7 @@ static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = {
|
|||
{ .compatible = "ti,bq27520g2" },
|
||||
{ .compatible = "ti,bq27520g3" },
|
||||
{ .compatible = "ti,bq27520g4" },
|
||||
{ .compatible = "ti,bq27521" },
|
||||
{ .compatible = "ti,bq27530" },
|
||||
{ .compatible = "ti,bq27531" },
|
||||
{ .compatible = "ti,bq27541" },
|
||||
|
|
|
@ -578,7 +578,7 @@ static int check_charging_duration(struct charger_manager *cm)
|
|||
} else if (is_ext_pwr_online(cm) && !cm->charger_enabled) {
|
||||
duration = curr - cm->charging_end_time;
|
||||
|
||||
if (duration > desc->charging_max_duration_ms &&
|
||||
if (duration > desc->discharging_max_duration_ms &&
|
||||
is_ext_pwr_online(cm)) {
|
||||
dev_info(cm->dev, "Discharging duration exceed %ums\n",
|
||||
desc->discharging_max_duration_ms);
|
||||
|
|
|
@ -586,8 +586,8 @@ static int cpcap_battery_init_irq(struct platform_device *pdev,
|
|||
int irq, error;
|
||||
|
||||
irq = platform_get_irq_byname(pdev, name);
|
||||
if (!irq)
|
||||
return -ENODEV;
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
error = devm_request_threaded_irq(ddata->dev, irq, NULL,
|
||||
cpcap_battery_irq_thread,
|
||||
|
|
|
@ -60,6 +60,7 @@ enum ltc294x_id {
|
|||
#define LTC294X_REG_CONTROL_PRESCALER_SET(x) \
|
||||
((x << 3) & LTC294X_REG_CONTROL_PRESCALER_MASK)
|
||||
#define LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED 0
|
||||
#define LTC294X_REG_CONTROL_ADC_DISABLE(x) ((x) & ~(BIT(7) | BIT(6)))
|
||||
|
||||
struct ltc294x_info {
|
||||
struct i2c_client *client; /* I2C Client pointer */
|
||||
|
@ -523,6 +524,29 @@ static int ltc294x_i2c_probe(struct i2c_client *client,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void ltc294x_i2c_shutdown(struct i2c_client *client)
|
||||
{
|
||||
struct ltc294x_info *info = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
u8 value;
|
||||
u8 control;
|
||||
|
||||
/* The LTC2941 does not need any special handling */
|
||||
if (info->id == LTC2941_ID)
|
||||
return;
|
||||
|
||||
/* Read control register */
|
||||
ret = ltc294x_read_regs(info->client, LTC294X_REG_CONTROL, &value, 1);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
/* Disable continuous ADC conversion as this drains the battery */
|
||||
control = LTC294X_REG_CONTROL_ADC_DISABLE(value);
|
||||
if (control != value)
|
||||
ltc294x_write_regs(info->client, LTC294X_REG_CONTROL,
|
||||
&control, 1);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
static int ltc294x_suspend(struct device *dev)
|
||||
|
@ -589,6 +613,7 @@ static struct i2c_driver ltc294x_driver = {
|
|||
},
|
||||
.probe = ltc294x_i2c_probe,
|
||||
.remove = ltc294x_i2c_remove,
|
||||
.shutdown = ltc294x_i2c_shutdown,
|
||||
.id_table = ltc294x_i2c_id,
|
||||
};
|
||||
module_i2c_driver(ltc294x_driver);
|
||||
|
|
|
@ -123,6 +123,8 @@ static int max17042_get_temperature(struct max17042_chip *chip, int *temp)
|
|||
static int max17042_get_status(struct max17042_chip *chip, int *status)
|
||||
{
|
||||
int ret, charge_full, charge_now;
|
||||
int avg_current;
|
||||
u32 data;
|
||||
|
||||
ret = power_supply_am_i_supplied(chip->battery);
|
||||
if (ret < 0) {
|
||||
|
@ -152,10 +154,31 @@ static int max17042_get_status(struct max17042_chip *chip, int *status)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if ((charge_full - charge_now) <= MAX17042_FULL_THRESHOLD)
|
||||
if ((charge_full - charge_now) <= MAX17042_FULL_THRESHOLD) {
|
||||
*status = POWER_SUPPLY_STATUS_FULL;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Even though we are supplied, we may still be discharging if the
|
||||
* supply is e.g. only delivering 5V 0.5A. Check current if available.
|
||||
*/
|
||||
if (!chip->pdata->enable_current_sense) {
|
||||
*status = POWER_SUPPLY_STATUS_CHARGING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = regmap_read(chip->regmap, MAX17042_AvgCurrent, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
avg_current = sign_extend32(data, 15);
|
||||
avg_current *= 1562500 / chip->pdata->r_sns;
|
||||
|
||||
if (avg_current > 0)
|
||||
*status = POWER_SUPPLY_STATUS_CHARGING;
|
||||
else
|
||||
*status = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -863,16 +886,13 @@ static void max17042_init_worker(struct work_struct *work)
|
|||
|
||||
#ifdef CONFIG_OF
|
||||
static struct max17042_platform_data *
|
||||
max17042_get_pdata(struct max17042_chip *chip)
|
||||
max17042_get_of_pdata(struct max17042_chip *chip)
|
||||
{
|
||||
struct device *dev = &chip->client->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
u32 prop;
|
||||
struct max17042_platform_data *pdata;
|
||||
|
||||
if (!np)
|
||||
return dev->platform_data;
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return NULL;
|
||||
|
@ -897,7 +917,8 @@ max17042_get_pdata(struct max17042_chip *chip)
|
|||
|
||||
return pdata;
|
||||
}
|
||||
#else
|
||||
#endif
|
||||
|
||||
static struct max17042_reg_data max17047_default_pdata_init_regs[] = {
|
||||
/*
|
||||
* Some firmwares do not set FullSOCThr, Enable End-of-Charge Detection
|
||||
|
@ -907,15 +928,12 @@ static struct max17042_reg_data max17047_default_pdata_init_regs[] = {
|
|||
};
|
||||
|
||||
static struct max17042_platform_data *
|
||||
max17042_get_pdata(struct max17042_chip *chip)
|
||||
max17042_get_default_pdata(struct max17042_chip *chip)
|
||||
{
|
||||
struct device *dev = &chip->client->dev;
|
||||
struct max17042_platform_data *pdata;
|
||||
int ret, misc_cfg;
|
||||
|
||||
if (dev->platform_data)
|
||||
return dev->platform_data;
|
||||
|
||||
/*
|
||||
* The MAX17047 gets used on x86 where we might not have pdata, assume
|
||||
* the firmware will already have initialized the fuel-gauge and provide
|
||||
|
@ -948,7 +966,21 @@ max17042_get_pdata(struct max17042_chip *chip)
|
|||
|
||||
return pdata;
|
||||
}
|
||||
|
||||
static struct max17042_platform_data *
|
||||
max17042_get_pdata(struct max17042_chip *chip)
|
||||
{
|
||||
struct device *dev = &chip->client->dev;
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
if (dev->of_node)
|
||||
return max17042_get_of_pdata(chip);
|
||||
#endif
|
||||
if (dev->platform_data)
|
||||
return dev->platform_data;
|
||||
|
||||
return max17042_get_default_pdata(chip);
|
||||
}
|
||||
|
||||
static const struct regmap_config max17042_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
|
|
|
@ -183,7 +183,7 @@ static int sbsm_select(struct i2c_mux_core *muxc, u32 chan)
|
|||
return ret;
|
||||
|
||||
/* chan goes from 1 ... 4 */
|
||||
reg = 1 << BIT(SBSM_SMB_BAT_OFFSET + chan);
|
||||
reg = BIT(SBSM_SMB_BAT_OFFSET + chan);
|
||||
ret = sbsm_write_word(data->client, SBSM_CMD_BATSYSSTATE, reg);
|
||||
if (ret)
|
||||
dev_err(dev, "Failed to select channel %i\n", chan);
|
||||
|
|
|
@ -16,6 +16,7 @@ enum bq27xxx_chip {
|
|||
BQ27520G2, /* bq27520G2 */
|
||||
BQ27520G3, /* bq27520G3 */
|
||||
BQ27520G4, /* bq27520G4 */
|
||||
BQ27521, /* bq27521 */
|
||||
BQ27530, /* bq27530, bq27531 */
|
||||
BQ27531,
|
||||
BQ27541, /* bq27541, bq27542, bq27546, bq27742 */
|
||||
|
|
Загрузка…
Ссылка в новой задаче