提交 972058ad 编写于 作者: L 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
  ...
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))
......
/* 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);
......
/*
* axp288_charger.c - X-power AXP288 PMIC Charger driver
*
* Copyright (C) 2016-2017 Hans de Goede <hdegoede@redhat.com>
* Copyright (C) 2014 Intel Corporation
* Author: Ramakrishna Pallala <ramakrishna.pallala@intel.com>
*
......@@ -98,28 +99,10 @@
#define CV_4200MV 4200 /* 4200mV */
#define CV_4350MV 4350 /* 4350mV */
#define CC_200MA 200 /* 200mA */
#define CC_600MA 600 /* 600mA */
#define CC_800MA 800 /* 800mA */
#define CC_1000MA 1000 /* 1000mA */
#define CC_1600MA 1600 /* 1600mA */
#define CC_2000MA 2000 /* 2000mA */
#define ILIM_100MA 100 /* 100mA */
#define ILIM_500MA 500 /* 500mA */
#define ILIM_900MA 900 /* 900mA */
#define ILIM_1500MA 1500 /* 1500mA */
#define ILIM_2000MA 2000 /* 2000mA */
#define ILIM_2500MA 2500 /* 2500mA */
#define ILIM_3000MA 3000 /* 3000mA */
#define AXP288_EXTCON_DEV_NAME "axp288_extcon"
#define USB_HOST_EXTCON_HID "INT3496"
#define USB_HOST_EXTCON_NAME "INT3496:00"
static const unsigned int cable_ids[] =
{ EXTCON_CHG_USB_SDP, EXTCON_CHG_USB_CDP, EXTCON_CHG_USB_DCP };
enum {
VBUS_OV_IRQ = 0,
CHARGE_DONE_IRQ,
......@@ -139,7 +122,6 @@ struct axp288_chrg_info {
struct regmap_irq_chip_data *regmap_irqc;
int irq[CHRG_INTR_END];
struct power_supply *psy_usb;
struct mutex lock;
/* OTG/Host mode */
struct {
......@@ -152,18 +134,14 @@ struct axp288_chrg_info {
/* SDP/CDP/DCP USB charging cable notifications */
struct {
struct extcon_dev *edev;
bool connected;
enum power_supply_type chg_type;
struct notifier_block nb[ARRAY_SIZE(cable_ids)];
struct notifier_block nb;
struct work_struct work;
} cable;
int inlmt;
int cc;
int cv;
int max_cc;
int max_cv;
int is_charger_enabled;
};
static inline int axp288_charger_set_cc(struct axp288_chrg_info *info, int cc)
......@@ -220,51 +198,63 @@ static inline int axp288_charger_set_cv(struct axp288_chrg_info *info, int cv)
return ret;
}
static inline int axp288_charger_set_vbus_inlmt(struct axp288_chrg_info *info,
int inlmt)
static int axp288_charger_get_vbus_inlmt(struct axp288_chrg_info *info)
{
int ret;
unsigned int val;
u8 reg_val;
int ret;
/* Read in limit register */
ret = regmap_read(info->regmap, AXP20X_CHRG_BAK_CTRL, &val);
if (ret < 0)
goto set_inlmt_fail;
if (inlmt <= ILIM_100MA) {
reg_val = CHRG_VBUS_ILIM_100MA;
inlmt = ILIM_100MA;
} else if (inlmt <= ILIM_500MA) {
reg_val = CHRG_VBUS_ILIM_500MA;
inlmt = ILIM_500MA;
} else if (inlmt <= ILIM_900MA) {
reg_val = CHRG_VBUS_ILIM_900MA;
inlmt = ILIM_900MA;
} else if (inlmt <= ILIM_1500MA) {
reg_val = CHRG_VBUS_ILIM_1500MA;
inlmt = ILIM_1500MA;
} else if (inlmt <= ILIM_2000MA) {
reg_val = CHRG_VBUS_ILIM_2000MA;
inlmt = ILIM_2000MA;
} else if (inlmt <= ILIM_2500MA) {
reg_val = CHRG_VBUS_ILIM_2500MA;
inlmt = ILIM_2500MA;
} else {
reg_val = CHRG_VBUS_ILIM_3000MA;
inlmt = ILIM_3000MA;
return ret;
val >>= CHRG_VBUS_ILIM_BIT_POS;
switch (val) {
case CHRG_VBUS_ILIM_100MA:
return 100000;
case CHRG_VBUS_ILIM_500MA:
return 500000;
case CHRG_VBUS_ILIM_900MA:
return 900000;
case CHRG_VBUS_ILIM_1500MA:
return 1500000;
case CHRG_VBUS_ILIM_2000MA:
return 2000000;
case CHRG_VBUS_ILIM_2500MA:
return 2500000;
case CHRG_VBUS_ILIM_3000MA:
return 3000000;
default:
dev_warn(&info->pdev->dev, "Unknown ilim reg val: %d\n", val);
return 0;
}
}
reg_val = (val & ~CHRG_VBUS_ILIM_MASK)
| (reg_val << CHRG_VBUS_ILIM_BIT_POS);
ret = regmap_write(info->regmap, AXP20X_CHRG_BAK_CTRL, reg_val);
if (ret >= 0)
info->inlmt = inlmt;
static inline int axp288_charger_set_vbus_inlmt(struct axp288_chrg_info *info,
int inlmt)
{
int ret;
u8 reg_val;
if (inlmt >= 3000000)
reg_val = CHRG_VBUS_ILIM_3000MA << CHRG_VBUS_ILIM_BIT_POS;
else if (inlmt >= 2500000)
reg_val = CHRG_VBUS_ILIM_2500MA << CHRG_VBUS_ILIM_BIT_POS;
else if (inlmt >= 2000000)
reg_val = CHRG_VBUS_ILIM_2000MA << CHRG_VBUS_ILIM_BIT_POS;
else if (inlmt >= 1500000)
reg_val = CHRG_VBUS_ILIM_1500MA << CHRG_VBUS_ILIM_BIT_POS;
else if (inlmt >= 900000)
reg_val = CHRG_VBUS_ILIM_900MA << CHRG_VBUS_ILIM_BIT_POS;
else if (inlmt >= 500000)
reg_val = CHRG_VBUS_ILIM_500MA << CHRG_VBUS_ILIM_BIT_POS;
else
dev_err(&info->pdev->dev, "charger BAK control %d\n", ret);
reg_val = CHRG_VBUS_ILIM_100MA << CHRG_VBUS_ILIM_BIT_POS;
ret = regmap_update_bits(info->regmap, AXP20X_CHRG_BAK_CTRL,
CHRG_VBUS_ILIM_MASK, reg_val);
if (ret < 0)
dev_err(&info->pdev->dev, "charger BAK control %d\n", ret);
set_inlmt_fail:
return ret;
}
......@@ -283,7 +273,6 @@ static int axp288_charger_vbus_path_select(struct axp288_chrg_info *info,
if (ret < 0)
dev_err(&info->pdev->dev, "axp288 vbus path select %d\n", ret);
return ret;
}
......@@ -292,9 +281,6 @@ static int axp288_charger_enable_charger(struct axp288_chrg_info *info,
{
int ret;
if ((int)enable == info->is_charger_enabled)
return 0;
if (enable)
ret = regmap_update_bits(info->regmap, AXP20X_CHRG_CTRL1,
CHRG_CCCV_CHG_EN, CHRG_CCCV_CHG_EN);
......@@ -303,8 +289,6 @@ static int axp288_charger_enable_charger(struct axp288_chrg_info *info,
CHRG_CCCV_CHG_EN, 0);
if (ret < 0)
dev_err(&info->pdev->dev, "axp288 enable charger %d\n", ret);
else
info->is_charger_enabled = enable;
return ret;
}
......@@ -376,8 +360,6 @@ static int axp288_charger_usb_set_property(struct power_supply *psy,
int ret = 0;
int scaled_val;
mutex_lock(&info->lock);
switch (psp) {
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
scaled_val = min(val->intval, info->max_cc);
......@@ -393,11 +375,15 @@ static int axp288_charger_usb_set_property(struct power_supply *psy,
if (ret < 0)
dev_warn(&info->pdev->dev, "set charge voltage failed\n");
break;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
ret = axp288_charger_set_vbus_inlmt(info, val->intval);
if (ret < 0)
dev_warn(&info->pdev->dev, "set input current limit failed\n");
break;
default:
ret = -EINVAL;
}
mutex_unlock(&info->lock);
return ret;
}
......@@ -406,9 +392,7 @@ static int axp288_charger_usb_get_property(struct power_supply *psy,
union power_supply_propval *val)
{
struct axp288_chrg_info *info = power_supply_get_drvdata(psy);
int ret = 0;
mutex_lock(&info->lock);
int ret;
switch (psp) {
case POWER_SUPPLY_PROP_PRESENT:
......@@ -419,7 +403,7 @@ static int axp288_charger_usb_get_property(struct power_supply *psy,
}
ret = axp288_charger_is_present(info);
if (ret < 0)
goto psy_get_prop_fail;
return ret;
val->intval = ret;
break;
case POWER_SUPPLY_PROP_ONLINE:
......@@ -430,7 +414,7 @@ static int axp288_charger_usb_get_property(struct power_supply *psy,
}
ret = axp288_charger_is_online(info);
if (ret < 0)
goto psy_get_prop_fail;
return ret;
val->intval = ret;
break;
case POWER_SUPPLY_PROP_HEALTH:
......@@ -448,17 +432,17 @@ static int axp288_charger_usb_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
val->intval = info->max_cv * 1000;
break;
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
val->intval = info->inlmt * 1000;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
ret = axp288_charger_get_vbus_inlmt(info);
if (ret < 0)
return ret;
val->intval = ret;
break;
default:
ret = -EINVAL;
goto psy_get_prop_fail;
return -EINVAL;
}
psy_get_prop_fail:
mutex_unlock(&info->lock);
return ret;
return 0;
}
static int axp288_charger_property_is_writeable(struct power_supply *psy,
......@@ -469,6 +453,7 @@ static int axp288_charger_property_is_writeable(struct power_supply *psy,
switch (psp) {
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
ret = 1;
break;
default:
......@@ -487,7 +472,7 @@ static enum power_supply_property axp288_usb_props[] = {
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
};
static const struct power_supply_desc axp288_charger_desc = {
......@@ -565,99 +550,53 @@ static void axp288_charger_extcon_evt_worker(struct work_struct *work)
container_of(work, struct axp288_chrg_info, cable.work);
int ret, current_limit;
struct extcon_dev *edev = info->cable.edev;
bool old_connected = info->cable.connected;
enum power_supply_type old_chg_type = info->cable.chg_type;
unsigned int val;
ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val);
if (ret < 0) {
dev_err(&info->pdev->dev, "Error reading status (%d)\n", ret);
return;
}
/* Offline? Disable charging and bail */
if (!(val & PS_STAT_VBUS_VALID)) {
dev_dbg(&info->pdev->dev, "USB charger disconnected\n");
axp288_charger_enable_charger(info, false);
power_supply_changed(info->psy_usb);
return;
}
/* Determine cable/charger type */
if (extcon_get_state(edev, EXTCON_CHG_USB_SDP) > 0) {
dev_dbg(&info->pdev->dev, "USB SDP charger is connected");
info->cable.connected = true;
info->cable.chg_type = POWER_SUPPLY_TYPE_USB;
dev_dbg(&info->pdev->dev, "USB SDP charger is connected\n");
current_limit = 500000;
} else if (extcon_get_state(edev, EXTCON_CHG_USB_CDP) > 0) {
dev_dbg(&info->pdev->dev, "USB CDP charger is connected");
info->cable.connected = true;
info->cable.chg_type = POWER_SUPPLY_TYPE_USB_CDP;
dev_dbg(&info->pdev->dev, "USB CDP charger is connected\n");
current_limit = 1500000;
} else if (extcon_get_state(edev, EXTCON_CHG_USB_DCP) > 0) {
dev_dbg(&info->pdev->dev, "USB DCP charger is connected");
info->cable.connected = true;
info->cable.chg_type = POWER_SUPPLY_TYPE_USB_DCP;
dev_dbg(&info->pdev->dev, "USB DCP charger is connected\n");
current_limit = 2000000;
} else {
if (old_connected)
dev_dbg(&info->pdev->dev, "USB charger disconnected");
info->cable.connected = false;
info->cable.chg_type = POWER_SUPPLY_TYPE_USB;
}
/* Cable status changed */
if (old_connected == info->cable.connected &&
old_chg_type == info->cable.chg_type)
/* Charger type detection still in progress, bail. */
return;
mutex_lock(&info->lock);
if (info->cable.connected) {
axp288_charger_enable_charger(info, false);
switch (info->cable.chg_type) {
case POWER_SUPPLY_TYPE_USB:
current_limit = ILIM_500MA;
break;
case POWER_SUPPLY_TYPE_USB_CDP:
current_limit = ILIM_1500MA;
break;
case POWER_SUPPLY_TYPE_USB_DCP:
current_limit = ILIM_2000MA;
break;
default:
/* Unknown */
current_limit = 0;
break;
}
/* Set vbus current limit first, then enable charger */
ret = axp288_charger_set_vbus_inlmt(info, current_limit);
if (ret == 0)
axp288_charger_enable_charger(info, true);
else
dev_err(&info->pdev->dev,
"error setting current limit (%d)", ret);
} else {
axp288_charger_enable_charger(info, false);
}
mutex_unlock(&info->lock);
/* Set vbus current limit first, then enable charger */
ret = axp288_charger_set_vbus_inlmt(info, current_limit);
if (ret == 0)
axp288_charger_enable_charger(info, true);
else
dev_err(&info->pdev->dev,
"error setting current limit (%d)\n", ret);
power_supply_changed(info->psy_usb);
}
/*
* We need 3 copies of this, because there is no way to find out for which
* cable id we are being called from the passed in arguments; and we must
* have a separate nb for each extcon_register_notifier call.
*/
static int axp288_charger_handle_cable0_evt(struct notifier_block *nb,
unsigned long event, void *param)
static int axp288_charger_handle_cable_evt(struct notifier_block *nb,
unsigned long event, void *param)
{
struct axp288_chrg_info *info =
container_of(nb, struct axp288_chrg_info, cable.nb[0]);
schedule_work(&info->cable.work);
return NOTIFY_OK;
}
static int axp288_charger_handle_cable1_evt(struct notifier_block *nb,
unsigned long event, void *param)
{
struct axp288_chrg_info *info =
container_of(nb, struct axp288_chrg_info, cable.nb[1]);
schedule_work(&info->cable.work);
return NOTIFY_OK;
}
static int axp288_charger_handle_cable2_evt(struct notifier_block *nb,
unsigned long event, void *param)
{
struct axp288_chrg_info *info =
container_of(nb, struct axp288_chrg_info, cable.nb[2]);
container_of(nb, struct axp288_chrg_info, cable.nb);
schedule_work(&info->cable.work);
return NOTIFY_OK;
}
......@@ -785,6 +724,14 @@ static int charger_init_hw_regs(struct axp288_chrg_info *info)
return 0;
}
static void axp288_charger_cancel_work(void *data)
{
struct axp288_chrg_info *info = data;
cancel_work_sync(&info->otg.work);
cancel_work_sync(&info->cable.work);
}
static int axp288_charger_probe(struct platform_device *pdev)
{
int ret, i, pirq;
......@@ -799,8 +746,6 @@ static int axp288_charger_probe(struct platform_device *pdev)
info->pdev = pdev;
info->regmap = axp20x->regmap;
info->regmap_irqc = axp20x->regmap_irqc;
info->cable.chg_type = -1;
info->is_charger_enabled = -1;
info->cable.edev = extcon_get_extcon_dev(AXP288_EXTCON_DEV_NAME);
if (info->cable.edev == NULL) {
......@@ -820,7 +765,6 @@ static int axp288_charger_probe(struct platform_device *pdev)
}
platform_set_drvdata(pdev, info);
mutex_init(&info->lock);
ret = charger_init_hw_regs(info);
if (ret)
......@@ -836,19 +780,19 @@ static int axp288_charger_probe(struct platform_device *pdev)
return ret;
}
/* Cancel our work on cleanup, register this before the notifiers */
ret = devm_add_action(dev, axp288_charger_cancel_work, info);
if (ret)
return ret;
/* Register for extcon notification */
INIT_WORK(&info->cable.work, axp288_charger_extcon_evt_worker);
info->cable.nb[0].notifier_call = axp288_charger_handle_cable0_evt;
info->cable.nb[1].notifier_call = axp288_charger_handle_cable1_evt;
info->cable.nb[2].notifier_call = axp288_charger_handle_cable2_evt;
for (i = 0; i < ARRAY_SIZE(cable_ids); i++) {
ret = devm_extcon_register_notifier(dev, info->cable.edev,
cable_ids[i], &info->cable.nb[i]);
if (ret) {
dev_err(dev, "failed to register extcon notifier for %u: %d\n",
cable_ids[i], ret);
return ret;
}
info->cable.nb.notifier_call = axp288_charger_handle_cable_evt;
ret = devm_extcon_register_notifier_all(dev, info->cable.edev,
&info->cable.nb);
if (ret) {
dev_err(dev, "failed to register cable extcon notifier\n");
return ret;
}
schedule_work(&info->cable.work);
......
/*
* 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 @@ 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)
{
int ret, value = 0;
int charge, discharge;
int ret, discharge;
ret = pmic_read_adc_val("axp288-chrg-curr", &charge, info);
if (ret < 0)
goto current_read_fail;
ret = pmic_read_adc_val("axp288-chrg-d-curr", &discharge, info);
/* First check discharge current, so that we do only 1 read on bat. */
ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &discharge);
if (ret < 0)
goto current_read_fail;
return ret;
if (charge > 0)
value = charge;
else if (discharge > 0)
value = -1 * discharge;
if (discharge > 0) {
*cur = -1 * discharge;
return 0;
}
*cur = value;
current_read_fail:
return ret;
return iio_read_channel_raw(info->iio_channel[BAT_CHRG_CURR], cur);
}
static int fuel_gauge_get_vocv(struct axp288_fg_info *info, int *vocv)
......@@ -698,12 +680,54 @@ 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)
{
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 */
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册