mirror of https://gitee.com/openkylin/linux.git
power supply and reset changes for the v4.12 series (part 2)
* New battery driver for AXP20X and AXP22X PMICs * Improve max17042_battery for usage on x86 * Misc small cleanups & fixes -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE72YNB0Y/i3JqeVQT2O7X88g7+poFAlkU2QYACgkQ2O7X88g7 +poudxAAjAzrD6WGMtH+++53j8sKTel1eyuKImsv/4TgkWjyBeXzeeEtHX4T8qCJ HtfGf/JB7sYCWdSFXCxYmOy051r6InGd3VxC2mMvkuz6p8ya1YAR41oHwXEBdsa5 TcYhIe9GNHEhdENTLsKywXOA3gh1REjU9ljCUJJwAxUpqDfU6pRSoFqwB0SAu6zC /FjGAcooyOyKT9tsZuJ68Xw01thtsowMLNKD6gkXSiMfH2wVhlEijoQ3OwJzW423 mvxukptrDaMiXWjjwY46/ExfEmoEmDo8le5vnB6m3OUOkTmsj2A7RI0GwYPjUM6S ZCrGn4yNeq3c277rAixY8twhS9zLVM3vuFWOKeT9zSIleJ7eFu9er6U2jJZvA17C BDoZg4RWFQrYv6YcMsADS8Wk4jYwhahdApNbG2RcUrC3qH+QTqGw6/7LCJ9X3dXs tSWszwSw9pGIPNnzzhwI7nNREFTfy8W+0qRRyUw2LWEVTJBxzUYmF8L3hFnhr/94 JqFdDWqSCEZpi0sgjp3IxDAlZJ9p/lTFN7iA8UdK912kBA+1Mo4JQqd6piXxYvJM yCjv4zDclpDa7QbV286y/FOLy/qnUrQ+8qIGvSgAJh5v32mJc8uJqP+4g8401D5T 6YfzboSUQ1s4hofFh1wIO+swq5iTcLJrhZyaCWC9qgKQVWx8tSw= =lF9I -----END PGP SIGNATURE----- Merge tag 'for-v4.12-2' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply Pull more power-supply updates from Sebastian Reichel: "The power-supply subsystem has a few more changes for the v4.12 merge window: - New battery driver for AXP20X and AXP22X PMICs - Improve max17042_battery for usage on x86 - Misc small cleanups & fixes" * tag 'for-v4.12-2' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (34 commits) power: supply: cpcap-charger: Keep trickle charger bits disabled power: supply: cpcap-charger: Fix enable for 3.8V charge setting power: supply: cpcap-charger: Fix charge voltage configuration power: supply: cpcap-charger: Fix charger name power: supply: twl4030-charger: make twl4030_bci_property_is_writeable static power: supply: sbs-battery: Add alert callback mailmap: add Sebastian Reichel power: supply: avoid unused twl4030-madc.h power: supply: sbs-battery: Correct supply status with current draw power: supply: sbs-battery: Don't ignore the first external power change power: supply: pda_power: move from timer to delayed_work power: supply: max17042_battery: Add support for the SCOPE property power: supply: max17042_battery: Add support for the CHARGE_NOW property power: supply: max17042_battery: Add support for the CHARGE_FULL_DESIGN property power: supply: max17042_battery: mAh readings depend on r_sns value power: supply: max17042_battery: Add support for the VOLT_MIN property power: supply: max17042_battery: Add support for the TECHNOLOGY attribute power: supply: max17042_battery: Add external_power_changed callback power: supply: max17042_battery: Add support for the STATUS property power: supply: max17042_battery: Add default platform_data fallback data ...
This commit is contained in:
commit
6b402bdfdb
2
.mailmap
2
.mailmap
|
@ -146,6 +146,8 @@ Santosh Shilimkar <ssantosh@kernel.org>
|
|||
Santosh Shilimkar <santosh.shilimkar@oracle.org>
|
||||
Sascha Hauer <s.hauer@pengutronix.de>
|
||||
S.Çağlar Onur <caglar@pardus.org.tr>
|
||||
Sebastian Reichel <sre@kernel.org> <sre@debian.org>
|
||||
Sebastian Reichel <sre@kernel.org> <sebastian.reichel@collabora.co.uk>
|
||||
Shiraz Hashim <shiraz.linux.kernel@gmail.com> <shiraz.hashim@st.com>
|
||||
Shuah Khan <shuah@kernel.org> <shuahkhan@gmail.com>
|
||||
Shuah Khan <shuah@kernel.org> <shuah.khan@hp.com>
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
AXP20x and AXP22x battery power supply
|
||||
|
||||
Required Properties:
|
||||
- compatible, one of:
|
||||
"x-powers,axp209-battery-power-supply"
|
||||
"x-powers,axp221-battery-power-supply"
|
||||
|
||||
This node is a subnode of the axp20x/axp22x PMIC.
|
||||
|
||||
The AXP20X and AXP22X can read the battery voltage, charge and discharge
|
||||
currents of the battery by reading ADC channels from the AXP20X/AXP22X
|
||||
ADC.
|
||||
|
||||
Example:
|
||||
|
||||
&axp209 {
|
||||
battery_power_supply: battery-power-supply {
|
||||
compatible = "x-powers,axp209-battery-power-supply";
|
||||
}
|
||||
};
|
|
@ -238,6 +238,26 @@ config CHARGER_AXP20X
|
|||
This driver can also be built as a module. If so, the module will be
|
||||
called axp20x_ac_power.
|
||||
|
||||
config BATTERY_AXP20X
|
||||
tristate "X-Powers AXP20X battery driver"
|
||||
depends on MFD_AXP20X
|
||||
depends on AXP20X_ADC
|
||||
depends on IIO
|
||||
help
|
||||
Say Y here to enable support for X-Powers AXP20X PMICs' battery power
|
||||
supply.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called axp20x_battery.
|
||||
|
||||
config AXP20X_POWER
|
||||
tristate "AXP20x power supply driver"
|
||||
depends on MFD_AXP20X
|
||||
depends on IIO
|
||||
help
|
||||
This driver provides support for the power supply features of
|
||||
AXP20x PMIC.
|
||||
|
||||
config AXP288_CHARGER
|
||||
tristate "X-Powers AXP288 Charger"
|
||||
depends on MFD_AXP20X && EXTCON_AXP288
|
||||
|
@ -541,11 +561,4 @@ config CHARGER_RT9455
|
|||
help
|
||||
Say Y to enable support for Richtek RT9455 battery charger.
|
||||
|
||||
config AXP20X_POWER
|
||||
tristate "AXP20x power supply driver"
|
||||
depends on MFD_AXP20X
|
||||
help
|
||||
This driver provides support for the power supply features of
|
||||
AXP20x PMIC.
|
||||
|
||||
endif # POWER_SUPPLY
|
||||
|
|
|
@ -18,6 +18,7 @@ obj-$(CONFIG_TEST_POWER) += test_power.o
|
|||
|
||||
obj-$(CONFIG_BATTERY_88PM860X) += 88pm860x_battery.o
|
||||
obj-$(CONFIG_BATTERY_ACT8945A) += act8945a_charger.o
|
||||
obj-$(CONFIG_BATTERY_AXP20X) += axp20x_battery.o
|
||||
obj-$(CONFIG_CHARGER_AXP20X) += axp20x_ac_power.o
|
||||
obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
|
||||
obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o
|
||||
|
|
|
@ -3238,7 +3238,7 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
|
|||
BUS_PP_PRECHG_CURRENT_MASK, 0);
|
||||
if (ret) {
|
||||
dev_err(di->dev,
|
||||
"failed to setup usb power path prechage current\n");
|
||||
"failed to setup usb power path precharge current\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,502 @@
|
|||
/*
|
||||
* Battery power supply driver for X-Powers AXP20X and AXP22X PMICs
|
||||
*
|
||||
* Copyright 2016 Free Electrons NextThing Co.
|
||||
* Quentin Schulz <quentin.schulz@free-electrons.com>
|
||||
*
|
||||
* This driver is based on a previous upstreaming attempt by:
|
||||
* Bruno Prémont <bonbons@linux-vserver.org>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General
|
||||
* Public License. See the file "COPYING" in the main directory of this
|
||||
* archive for more details.
|
||||
*
|
||||
* 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/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
#include <linux/mfd/axp20x.h>
|
||||
|
||||
#define AXP20X_PWR_STATUS_BAT_CHARGING BIT(2)
|
||||
|
||||
#define AXP20X_PWR_OP_BATT_PRESENT BIT(5)
|
||||
#define AXP20X_PWR_OP_BATT_ACTIVATED BIT(3)
|
||||
|
||||
#define AXP209_FG_PERCENT GENMASK(6, 0)
|
||||
#define AXP22X_FG_VALID BIT(7)
|
||||
|
||||
#define AXP20X_CHRG_CTRL1_TGT_VOLT GENMASK(6, 5)
|
||||
#define AXP20X_CHRG_CTRL1_TGT_4_1V (0 << 5)
|
||||
#define AXP20X_CHRG_CTRL1_TGT_4_15V (1 << 5)
|
||||
#define AXP20X_CHRG_CTRL1_TGT_4_2V (2 << 5)
|
||||
#define AXP20X_CHRG_CTRL1_TGT_4_36V (3 << 5)
|
||||
|
||||
#define AXP22X_CHRG_CTRL1_TGT_4_22V (1 << 5)
|
||||
#define AXP22X_CHRG_CTRL1_TGT_4_24V (3 << 5)
|
||||
|
||||
#define AXP20X_CHRG_CTRL1_TGT_CURR GENMASK(3, 0)
|
||||
|
||||
#define AXP20X_V_OFF_MASK GENMASK(2, 0)
|
||||
|
||||
struct axp20x_batt_ps {
|
||||
struct regmap *regmap;
|
||||
struct power_supply *batt;
|
||||
struct device *dev;
|
||||
struct iio_channel *batt_chrg_i;
|
||||
struct iio_channel *batt_dischrg_i;
|
||||
struct iio_channel *batt_v;
|
||||
u8 axp_id;
|
||||
};
|
||||
|
||||
static int axp20x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt,
|
||||
int *val)
|
||||
{
|
||||
int ret, reg;
|
||||
|
||||
ret = regmap_read(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, ®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (reg & AXP20X_CHRG_CTRL1_TGT_VOLT) {
|
||||
case AXP20X_CHRG_CTRL1_TGT_4_1V:
|
||||
*val = 4100000;
|
||||
break;
|
||||
case AXP20X_CHRG_CTRL1_TGT_4_15V:
|
||||
*val = 4150000;
|
||||
break;
|
||||
case AXP20X_CHRG_CTRL1_TGT_4_2V:
|
||||
*val = 4200000;
|
||||
break;
|
||||
case AXP20X_CHRG_CTRL1_TGT_4_36V:
|
||||
*val = 4360000;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int axp22x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt,
|
||||
int *val)
|
||||
{
|
||||
int ret, reg;
|
||||
|
||||
ret = regmap_read(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, ®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (reg & AXP20X_CHRG_CTRL1_TGT_VOLT) {
|
||||
case AXP20X_CHRG_CTRL1_TGT_4_1V:
|
||||
*val = 4100000;
|
||||
break;
|
||||
case AXP20X_CHRG_CTRL1_TGT_4_2V:
|
||||
*val = 4200000;
|
||||
break;
|
||||
case AXP22X_CHRG_CTRL1_TGT_4_22V:
|
||||
*val = 4220000;
|
||||
break;
|
||||
case AXP22X_CHRG_CTRL1_TGT_4_24V:
|
||||
*val = 4240000;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void raw_to_constant_charge_current(struct axp20x_batt_ps *axp, int *val)
|
||||
{
|
||||
if (axp->axp_id == AXP209_ID)
|
||||
*val = *val * 100000 + 300000;
|
||||
else
|
||||
*val = *val * 150000 + 300000;
|
||||
}
|
||||
|
||||
static int axp20x_get_constant_charge_current(struct axp20x_batt_ps *axp,
|
||||
int *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(axp->regmap, AXP20X_CHRG_CTRL1, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val &= AXP20X_CHRG_CTRL1_TGT_CURR;
|
||||
|
||||
raw_to_constant_charge_current(axp, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int axp20x_battery_get_prop(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct axp20x_batt_ps *axp20x_batt = power_supply_get_drvdata(psy);
|
||||
struct iio_channel *chan;
|
||||
int ret = 0, reg, val1;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_OP_MODE,
|
||||
®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val->intval = !!(reg & AXP20X_PWR_OP_BATT_PRESENT);
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_INPUT_STATUS,
|
||||
®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (reg & AXP20X_PWR_STATUS_BAT_CHARGING) {
|
||||
val->intval = POWER_SUPPLY_STATUS_CHARGING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = iio_read_channel_processed(axp20x_batt->batt_dischrg_i,
|
||||
&val1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val1) {
|
||||
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = regmap_read(axp20x_batt->regmap, AXP20X_FG_RES, &val1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Fuel Gauge data takes 7 bits but the stored value seems to be
|
||||
* directly the raw percentage without any scaling to 7 bits.
|
||||
*/
|
||||
if ((val1 & AXP209_FG_PERCENT) == 100)
|
||||
val->intval = POWER_SUPPLY_STATUS_FULL;
|
||||
else
|
||||
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_HEALTH:
|
||||
ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_OP_MODE,
|
||||
&val1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val1 & AXP20X_PWR_OP_BATT_ACTIVATED) {
|
||||
val->intval = POWER_SUPPLY_HEALTH_DEAD;
|
||||
return 0;
|
||||
}
|
||||
|
||||
val->intval = POWER_SUPPLY_HEALTH_GOOD;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||
ret = axp20x_get_constant_charge_current(axp20x_batt,
|
||||
&val->intval);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
|
||||
val->intval = AXP20X_CHRG_CTRL1_TGT_CURR;
|
||||
raw_to_constant_charge_current(axp20x_batt, &val->intval);
|
||||
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
||||
ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_INPUT_STATUS,
|
||||
®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (reg & AXP20X_PWR_STATUS_BAT_CHARGING)
|
||||
chan = axp20x_batt->batt_chrg_i;
|
||||
else
|
||||
chan = axp20x_batt->batt_dischrg_i;
|
||||
|
||||
ret = iio_read_channel_processed(chan, &val->intval);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* IIO framework gives mA but Power Supply framework gives uA */
|
||||
val->intval *= 1000;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
/* When no battery is present, return capacity is 100% */
|
||||
ret = regmap_read(axp20x_batt->regmap, AXP20X_PWR_OP_MODE,
|
||||
®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!(reg & AXP20X_PWR_OP_BATT_PRESENT)) {
|
||||
val->intval = 100;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = regmap_read(axp20x_batt->regmap, AXP20X_FG_RES, ®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (axp20x_batt->axp_id == AXP221_ID &&
|
||||
!(reg & AXP22X_FG_VALID))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Fuel Gauge data takes 7 bits but the stored value seems to be
|
||||
* directly the raw percentage without any scaling to 7 bits.
|
||||
*/
|
||||
val->intval = reg & AXP209_FG_PERCENT;
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
|
||||
if (axp20x_batt->axp_id == AXP209_ID)
|
||||
return axp20x_battery_get_max_voltage(axp20x_batt,
|
||||
&val->intval);
|
||||
return axp22x_battery_get_max_voltage(axp20x_batt,
|
||||
&val->intval);
|
||||
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
|
||||
ret = regmap_read(axp20x_batt->regmap, AXP20X_V_OFF, ®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val->intval = 2600000 + 100000 * (reg & AXP20X_V_OFF_MASK);
|
||||
break;
|
||||
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
ret = iio_read_channel_processed(axp20x_batt->batt_v,
|
||||
&val->intval);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* IIO framework gives mV but Power Supply framework gives uV */
|
||||
val->intval *= 1000;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int axp20x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt,
|
||||
int val)
|
||||
{
|
||||
switch (val) {
|
||||
case 4100000:
|
||||
val = AXP20X_CHRG_CTRL1_TGT_4_1V;
|
||||
break;
|
||||
|
||||
case 4150000:
|
||||
if (axp20x_batt->axp_id == AXP221_ID)
|
||||
return -EINVAL;
|
||||
|
||||
val = AXP20X_CHRG_CTRL1_TGT_4_15V;
|
||||
break;
|
||||
|
||||
case 4200000:
|
||||
val = AXP20X_CHRG_CTRL1_TGT_4_2V;
|
||||
break;
|
||||
|
||||
default:
|
||||
/*
|
||||
* AXP20x max voltage can be set to 4.36V and AXP22X max voltage
|
||||
* can be set to 4.22V and 4.24V, but these voltages are too
|
||||
* high for Lithium based batteries (AXP PMICs are supposed to
|
||||
* be used with these kinds of battery).
|
||||
*/
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return regmap_update_bits(axp20x_batt->regmap, AXP20X_CHRG_CTRL1,
|
||||
AXP20X_CHRG_CTRL1_TGT_VOLT, val);
|
||||
}
|
||||
|
||||
static int axp20x_set_constant_charge_current(struct axp20x_batt_ps *axp_batt,
|
||||
int charge_current)
|
||||
{
|
||||
if (axp_batt->axp_id == AXP209_ID)
|
||||
charge_current = (charge_current - 300000) / 100000;
|
||||
else
|
||||
charge_current = (charge_current - 300000) / 150000;
|
||||
|
||||
if (charge_current > AXP20X_CHRG_CTRL1_TGT_CURR || charge_current < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return regmap_update_bits(axp_batt->regmap, AXP20X_CHRG_CTRL1,
|
||||
AXP20X_CHRG_CTRL1_TGT_CURR, charge_current);
|
||||
}
|
||||
|
||||
static int axp20x_set_voltage_min_design(struct axp20x_batt_ps *axp_batt,
|
||||
int min_voltage)
|
||||
{
|
||||
int val1 = (min_voltage - 2600000) / 100000;
|
||||
|
||||
if (val1 < 0 || val1 > AXP20X_V_OFF_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
return regmap_update_bits(axp_batt->regmap, AXP20X_V_OFF,
|
||||
AXP20X_V_OFF_MASK, val1);
|
||||
}
|
||||
|
||||
static int axp20x_battery_set_prop(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
struct axp20x_batt_ps *axp20x_batt = power_supply_get_drvdata(psy);
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
|
||||
return axp20x_set_voltage_min_design(axp20x_batt, val->intval);
|
||||
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
|
||||
return axp20x_battery_set_max_voltage(axp20x_batt, val->intval);
|
||||
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||
return axp20x_set_constant_charge_current(axp20x_batt,
|
||||
val->intval);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static enum power_supply_property axp20x_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
|
||||
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
};
|
||||
|
||||
static int axp20x_battery_prop_writeable(struct power_supply *psy,
|
||||
enum power_supply_property psp)
|
||||
{
|
||||
return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN ||
|
||||
psp == POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN ||
|
||||
psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT;
|
||||
}
|
||||
|
||||
static const struct power_supply_desc axp20x_batt_ps_desc = {
|
||||
.name = "axp20x-battery",
|
||||
.type = POWER_SUPPLY_TYPE_BATTERY,
|
||||
.properties = axp20x_battery_props,
|
||||
.num_properties = ARRAY_SIZE(axp20x_battery_props),
|
||||
.property_is_writeable = axp20x_battery_prop_writeable,
|
||||
.get_property = axp20x_battery_get_prop,
|
||||
.set_property = axp20x_battery_set_prop,
|
||||
};
|
||||
|
||||
static const struct of_device_id axp20x_battery_ps_id[] = {
|
||||
{
|
||||
.compatible = "x-powers,axp209-battery-power-supply",
|
||||
.data = (void *)AXP209_ID,
|
||||
}, {
|
||||
.compatible = "x-powers,axp221-battery-power-supply",
|
||||
.data = (void *)AXP221_ID,
|
||||
}, { /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, axp20x_battery_ps_id);
|
||||
|
||||
static int axp20x_power_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct axp20x_batt_ps *axp20x_batt;
|
||||
struct power_supply_config psy_cfg = {};
|
||||
|
||||
if (!of_device_is_available(pdev->dev.of_node))
|
||||
return -ENODEV;
|
||||
|
||||
axp20x_batt = devm_kzalloc(&pdev->dev, sizeof(*axp20x_batt),
|
||||
GFP_KERNEL);
|
||||
if (!axp20x_batt)
|
||||
return -ENOMEM;
|
||||
|
||||
axp20x_batt->dev = &pdev->dev;
|
||||
|
||||
axp20x_batt->batt_v = devm_iio_channel_get(&pdev->dev, "batt_v");
|
||||
if (IS_ERR(axp20x_batt->batt_v)) {
|
||||
if (PTR_ERR(axp20x_batt->batt_v) == -ENODEV)
|
||||
return -EPROBE_DEFER;
|
||||
return PTR_ERR(axp20x_batt->batt_v);
|
||||
}
|
||||
|
||||
axp20x_batt->batt_chrg_i = devm_iio_channel_get(&pdev->dev,
|
||||
"batt_chrg_i");
|
||||
if (IS_ERR(axp20x_batt->batt_chrg_i)) {
|
||||
if (PTR_ERR(axp20x_batt->batt_chrg_i) == -ENODEV)
|
||||
return -EPROBE_DEFER;
|
||||
return PTR_ERR(axp20x_batt->batt_chrg_i);
|
||||
}
|
||||
|
||||
axp20x_batt->batt_dischrg_i = devm_iio_channel_get(&pdev->dev,
|
||||
"batt_dischrg_i");
|
||||
if (IS_ERR(axp20x_batt->batt_dischrg_i)) {
|
||||
if (PTR_ERR(axp20x_batt->batt_dischrg_i) == -ENODEV)
|
||||
return -EPROBE_DEFER;
|
||||
return PTR_ERR(axp20x_batt->batt_dischrg_i);
|
||||
}
|
||||
|
||||
axp20x_batt->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
platform_set_drvdata(pdev, axp20x_batt);
|
||||
|
||||
psy_cfg.drv_data = axp20x_batt;
|
||||
psy_cfg.of_node = pdev->dev.of_node;
|
||||
|
||||
axp20x_batt->axp_id = (uintptr_t)of_device_get_match_data(&pdev->dev);
|
||||
|
||||
axp20x_batt->batt = devm_power_supply_register(&pdev->dev,
|
||||
&axp20x_batt_ps_desc,
|
||||
&psy_cfg);
|
||||
if (IS_ERR(axp20x_batt->batt)) {
|
||||
dev_err(&pdev->dev, "failed to register power supply: %ld\n",
|
||||
PTR_ERR(axp20x_batt->batt));
|
||||
return PTR_ERR(axp20x_batt->batt);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver axp20x_batt_driver = {
|
||||
.probe = axp20x_power_probe,
|
||||
.driver = {
|
||||
.name = "axp20x-battery-power-supply",
|
||||
.of_match_table = axp20x_battery_ps_id,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(axp20x_batt_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Battery power supply driver for AXP20X and AXP22X PMICs");
|
||||
MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -533,6 +533,9 @@ static int bq24190_register_reset(struct bq24190_dev_info *bdi)
|
|||
int ret, limit = 100;
|
||||
u8 v;
|
||||
|
||||
if (device_property_read_bool(bdi->dev, "disable-reset"))
|
||||
return 0;
|
||||
|
||||
/* Reset the registers */
|
||||
ret = bq24190_write_mask(bdi, BQ24190_REG_POC,
|
||||
BQ24190_REG_POC_RESET_MASK,
|
||||
|
@ -659,22 +662,25 @@ static int bq24190_charger_get_health(struct bq24190_dev_info *bdi,
|
|||
v = bdi->f_reg;
|
||||
mutex_unlock(&bdi->f_reg_lock);
|
||||
|
||||
if (v & BQ24190_REG_F_BOOST_FAULT_MASK) {
|
||||
/*
|
||||
* This could be over-current or over-voltage but there's
|
||||
* no way to tell which. Return 'OVERVOLTAGE' since there
|
||||
* isn't an 'OVERCURRENT' value defined that we can return
|
||||
* even if it was over-current.
|
||||
*/
|
||||
health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
|
||||
} else {
|
||||
v &= BQ24190_REG_F_CHRG_FAULT_MASK;
|
||||
v >>= BQ24190_REG_F_CHRG_FAULT_SHIFT;
|
||||
|
||||
switch (v) {
|
||||
case 0x0: /* Normal */
|
||||
health = POWER_SUPPLY_HEALTH_GOOD;
|
||||
if (v & BQ24190_REG_F_NTC_FAULT_MASK) {
|
||||
switch (v >> BQ24190_REG_F_NTC_FAULT_SHIFT & 0x7) {
|
||||
case 0x1: /* TS1 Cold */
|
||||
case 0x3: /* TS2 Cold */
|
||||
case 0x5: /* Both Cold */
|
||||
health = POWER_SUPPLY_HEALTH_COLD;
|
||||
break;
|
||||
case 0x2: /* TS1 Hot */
|
||||
case 0x4: /* TS2 Hot */
|
||||
case 0x6: /* Both Hot */
|
||||
health = POWER_SUPPLY_HEALTH_OVERHEAT;
|
||||
break;
|
||||
default:
|
||||
health = POWER_SUPPLY_HEALTH_UNKNOWN;
|
||||
}
|
||||
} else if (v & BQ24190_REG_F_BAT_FAULT_MASK) {
|
||||
health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
|
||||
} else if (v & BQ24190_REG_F_CHRG_FAULT_MASK) {
|
||||
switch (v >> BQ24190_REG_F_CHRG_FAULT_SHIFT & 0x3) {
|
||||
case 0x1: /* Input Fault (VBUS OVP or VBAT<VBUS<3.8V) */
|
||||
/*
|
||||
* This could be over-voltage or under-voltage
|
||||
|
@ -691,9 +697,19 @@ static int bq24190_charger_get_health(struct bq24190_dev_info *bdi,
|
|||
case 0x3: /* Charge Safety Timer Expiration */
|
||||
health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
|
||||
break;
|
||||
default:
|
||||
health = POWER_SUPPLY_HEALTH_UNKNOWN;
|
||||
default: /* prevent compiler warning */
|
||||
health = -1;
|
||||
}
|
||||
} else if (v & BQ24190_REG_F_BOOST_FAULT_MASK) {
|
||||
/*
|
||||
* This could be over-current or over-voltage but there's
|
||||
* no way to tell which. Return 'OVERVOLTAGE' since there
|
||||
* isn't an 'OVERCURRENT' value defined that we can return
|
||||
* even if it was over-current.
|
||||
*/
|
||||
health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
|
||||
} else {
|
||||
health = POWER_SUPPLY_HEALTH_GOOD;
|
||||
}
|
||||
|
||||
val->intval = health;
|
||||
|
@ -704,19 +720,59 @@ static int bq24190_charger_get_health(struct bq24190_dev_info *bdi,
|
|||
static int bq24190_charger_get_online(struct bq24190_dev_info *bdi,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
u8 v;
|
||||
u8 pg_stat, batfet_disable;
|
||||
int ret;
|
||||
|
||||
ret = bq24190_read_mask(bdi, BQ24190_REG_SS,
|
||||
BQ24190_REG_SS_PG_STAT_MASK,
|
||||
BQ24190_REG_SS_PG_STAT_SHIFT, &v);
|
||||
BQ24190_REG_SS_PG_STAT_SHIFT, &pg_stat);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = v;
|
||||
ret = bq24190_read_mask(bdi, BQ24190_REG_MOC,
|
||||
BQ24190_REG_MOC_BATFET_DISABLE_MASK,
|
||||
BQ24190_REG_MOC_BATFET_DISABLE_SHIFT, &batfet_disable);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = pg_stat && !batfet_disable;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bq24190_battery_set_online(struct bq24190_dev_info *bdi,
|
||||
const union power_supply_propval *val);
|
||||
static int bq24190_battery_get_status(struct bq24190_dev_info *bdi,
|
||||
union power_supply_propval *val);
|
||||
static int bq24190_battery_get_temp_alert_max(struct bq24190_dev_info *bdi,
|
||||
union power_supply_propval *val);
|
||||
static int bq24190_battery_set_temp_alert_max(struct bq24190_dev_info *bdi,
|
||||
const union power_supply_propval *val);
|
||||
|
||||
static int bq24190_charger_set_online(struct bq24190_dev_info *bdi,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
return bq24190_battery_set_online(bdi, val);
|
||||
}
|
||||
|
||||
static int bq24190_charger_get_status(struct bq24190_dev_info *bdi,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
return bq24190_battery_get_status(bdi, val);
|
||||
}
|
||||
|
||||
static int bq24190_charger_get_temp_alert_max(struct bq24190_dev_info *bdi,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
return bq24190_battery_get_temp_alert_max(bdi, val);
|
||||
}
|
||||
|
||||
static int bq24190_charger_set_temp_alert_max(struct bq24190_dev_info *bdi,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
return bq24190_battery_set_temp_alert_max(bdi, val);
|
||||
}
|
||||
|
||||
static int bq24190_charger_get_current(struct bq24190_dev_info *bdi,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
|
@ -831,6 +887,12 @@ static int bq24190_charger_get_property(struct power_supply *psy,
|
|||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
ret = bq24190_charger_get_online(bdi, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
ret = bq24190_charger_get_status(bdi, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
|
||||
ret = bq24190_charger_get_temp_alert_max(bdi, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||
ret = bq24190_charger_get_current(bdi, val);
|
||||
break;
|
||||
|
@ -879,6 +941,12 @@ static int bq24190_charger_set_property(struct power_supply *psy,
|
|||
return ret;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
ret = bq24190_charger_set_online(bdi, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
|
||||
ret = bq24190_charger_set_temp_alert_max(bdi, val);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_TYPE:
|
||||
ret = bq24190_charger_set_charge_type(bdi, val);
|
||||
break;
|
||||
|
@ -904,6 +972,8 @@ static int bq24190_charger_property_is_writeable(struct power_supply *psy,
|
|||
int ret;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
|
||||
case POWER_SUPPLY_PROP_CHARGE_TYPE:
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
|
||||
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
|
||||
|
@ -920,6 +990,8 @@ static enum power_supply_property bq24190_charger_properties[] = {
|
|||
POWER_SUPPLY_PROP_CHARGE_TYPE,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
|
||||
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
|
||||
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
|
||||
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
|
||||
|
@ -1093,6 +1165,7 @@ static int bq24190_battery_get_property(struct power_supply *psy,
|
|||
struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy);
|
||||
int ret;
|
||||
|
||||
dev_warn(bdi->dev, "warning: /sys/class/power_supply/bq24190-battery is deprecated\n");
|
||||
dev_dbg(bdi->dev, "prop: %d\n", psp);
|
||||
|
||||
ret = pm_runtime_get_sync(bdi->dev);
|
||||
|
@ -1138,6 +1211,7 @@ static int bq24190_battery_set_property(struct power_supply *psy,
|
|||
struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy);
|
||||
int ret;
|
||||
|
||||
dev_warn(bdi->dev, "warning: /sys/class/power_supply/bq24190-battery is deprecated\n");
|
||||
dev_dbg(bdi->dev, "prop: %d\n", psp);
|
||||
|
||||
ret = pm_runtime_get_sync(bdi->dev);
|
||||
|
@ -1266,9 +1340,9 @@ static void bq24190_check_status(struct bq24190_dev_info *bdi)
|
|||
bdi->ss_reg = ss_reg;
|
||||
}
|
||||
|
||||
if (alert_charger)
|
||||
if (alert_charger || alert_battery)
|
||||
power_supply_changed(bdi->charger);
|
||||
if (alert_battery)
|
||||
if (alert_battery && bdi->battery)
|
||||
power_supply_changed(bdi->battery);
|
||||
|
||||
dev_dbg(bdi->dev, "ss_reg: 0x%02x, f_reg: 0x%02x\n", ss_reg, f_reg);
|
||||
|
@ -1473,19 +1547,23 @@ static int bq24190_probe(struct i2c_client *client,
|
|||
goto out_pmrt;
|
||||
}
|
||||
|
||||
battery_cfg.drv_data = bdi;
|
||||
bdi->battery = power_supply_register(dev, &bq24190_battery_desc,
|
||||
&battery_cfg);
|
||||
if (IS_ERR(bdi->battery)) {
|
||||
dev_err(dev, "Can't register battery\n");
|
||||
ret = PTR_ERR(bdi->battery);
|
||||
goto out_charger;
|
||||
/* the battery class is deprecated and will be removed. */
|
||||
/* in the interim, this property hides it. */
|
||||
if (!device_property_read_bool(dev, "omit-battery-class")) {
|
||||
battery_cfg.drv_data = bdi;
|
||||
bdi->battery = power_supply_register(dev, &bq24190_battery_desc,
|
||||
&battery_cfg);
|
||||
if (IS_ERR(bdi->battery)) {
|
||||
dev_err(dev, "Can't register battery\n");
|
||||
ret = PTR_ERR(bdi->battery);
|
||||
goto out_charger;
|
||||
}
|
||||
}
|
||||
|
||||
ret = bq24190_sysfs_create_group(bdi);
|
||||
if (ret) {
|
||||
dev_err(dev, "Can't create sysfs entries\n");
|
||||
goto out_battery;
|
||||
goto out_charger;
|
||||
}
|
||||
|
||||
bdi->initialized = true;
|
||||
|
@ -1523,10 +1601,9 @@ static int bq24190_probe(struct i2c_client *client,
|
|||
out_sysfs:
|
||||
bq24190_sysfs_remove_group(bdi);
|
||||
|
||||
out_battery:
|
||||
power_supply_unregister(bdi->battery);
|
||||
|
||||
out_charger:
|
||||
if (!IS_ERR_OR_NULL(bdi->battery))
|
||||
power_supply_unregister(bdi->battery);
|
||||
power_supply_unregister(bdi->charger);
|
||||
|
||||
out_pmrt:
|
||||
|
@ -1549,7 +1626,8 @@ static int bq24190_remove(struct i2c_client *client)
|
|||
|
||||
bq24190_register_reset(bdi);
|
||||
bq24190_sysfs_remove_group(bdi);
|
||||
power_supply_unregister(bdi->battery);
|
||||
if (bdi->battery)
|
||||
power_supply_unregister(bdi->battery);
|
||||
power_supply_unregister(bdi->charger);
|
||||
if (error >= 0)
|
||||
pm_runtime_put_sync(bdi->dev);
|
||||
|
@ -1636,7 +1714,8 @@ static __maybe_unused int bq24190_pm_resume(struct device *dev)
|
|||
|
||||
/* Things may have changed while suspended so alert upper layer */
|
||||
power_supply_changed(bdi->charger);
|
||||
power_supply_changed(bdi->battery);
|
||||
if (bdi->battery)
|
||||
power_supply_changed(bdi->battery);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@
|
|||
#define CPCAP_REG_CRM_VCHRG_4V30 CPCAP_REG_CRM_VCHRG(0x8)
|
||||
#define CPCAP_REG_CRM_VCHRG_4V32 CPCAP_REG_CRM_VCHRG(0x9)
|
||||
#define CPCAP_REG_CRM_VCHRG_4V34 CPCAP_REG_CRM_VCHRG(0xa)
|
||||
#define CPCAP_REG_CRM_VCHRG_4V36 CPCAP_REG_CRM_VCHRG(0xb)
|
||||
#define CPCAP_REG_CRM_VCHRG_4V35 CPCAP_REG_CRM_VCHRG(0xb)
|
||||
#define CPCAP_REG_CRM_VCHRG_4V38 CPCAP_REG_CRM_VCHRG(0xc)
|
||||
#define CPCAP_REG_CRM_VCHRG_4V40 CPCAP_REG_CRM_VCHRG(0xd)
|
||||
#define CPCAP_REG_CRM_VCHRG_4V42 CPCAP_REG_CRM_VCHRG(0xe)
|
||||
|
@ -262,7 +262,7 @@ static int cpcap_charger_set_state(struct cpcap_charger_ddata *ddata,
|
|||
bool enable;
|
||||
int error;
|
||||
|
||||
enable = max_voltage && (charge_current || trickle_current);
|
||||
enable = (charge_current || trickle_current);
|
||||
dev_dbg(ddata->dev, "%s enable: %i\n", __func__, enable);
|
||||
|
||||
if (!enable) {
|
||||
|
@ -433,9 +433,8 @@ static void cpcap_usb_detect(struct work_struct *work)
|
|||
max_current = CPCAP_REG_CRM_ICHRG_0A528;
|
||||
|
||||
error = cpcap_charger_set_state(ddata,
|
||||
CPCAP_REG_CRM_VCHRG_4V20,
|
||||
max_current,
|
||||
CPCAP_REG_CRM_TR_0A72);
|
||||
CPCAP_REG_CRM_VCHRG_4V35,
|
||||
max_current, 0);
|
||||
if (error)
|
||||
goto out_err;
|
||||
} else {
|
||||
|
@ -566,7 +565,7 @@ static int cpcap_charger_init_iio(struct cpcap_charger_ddata *ddata)
|
|||
}
|
||||
|
||||
static const struct power_supply_desc cpcap_charger_usb_desc = {
|
||||
.name = "cpcap_usb",
|
||||
.name = "usb",
|
||||
.type = POWER_SUPPLY_TYPE_USB,
|
||||
.properties = cpcap_charger_props,
|
||||
.num_properties = ARRAY_SIZE(cpcap_charger_props),
|
||||
|
|
|
@ -383,8 +383,7 @@ static int gab_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int gab_suspend(struct device *dev)
|
||||
static int __maybe_unused gab_suspend(struct device *dev)
|
||||
{
|
||||
struct gab *adc_bat = dev_get_drvdata(dev);
|
||||
|
||||
|
@ -393,7 +392,7 @@ static int gab_suspend(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int gab_resume(struct device *dev)
|
||||
static int __maybe_unused gab_resume(struct device *dev)
|
||||
{
|
||||
struct gab *adc_bat = dev_get_drvdata(dev);
|
||||
struct gab_platform_data *pdata = adc_bat->pdata;
|
||||
|
@ -407,20 +406,12 @@ static int gab_resume(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops gab_pm_ops = {
|
||||
.suspend = gab_suspend,
|
||||
.resume = gab_resume,
|
||||
};
|
||||
|
||||
#define GAB_PM_OPS (&gab_pm_ops)
|
||||
#else
|
||||
#define GAB_PM_OPS (NULL)
|
||||
#endif
|
||||
static SIMPLE_DEV_PM_OPS(gab_pm_ops, gab_suspend, gab_resume);
|
||||
|
||||
static struct platform_driver gab_driver = {
|
||||
.driver = {
|
||||
.name = "generic-adc-battery",
|
||||
.pm = GAB_PM_OPS
|
||||
.pm = &gab_pm_ops,
|
||||
},
|
||||
.probe = gab_probe,
|
||||
.remove = gab_remove,
|
||||
|
|
|
@ -418,6 +418,10 @@ static int isp1704_charger_probe(struct platform_device *pdev)
|
|||
|
||||
pdata = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct isp1704_charger_data), GFP_KERNEL);
|
||||
if (!pdata) {
|
||||
ret = -ENOMEM;
|
||||
goto fail0;
|
||||
}
|
||||
pdata->enable_gpio = gpio;
|
||||
|
||||
dev_info(&pdev->dev, "init gpio %d\n", pdata->enable_gpio);
|
||||
|
|
|
@ -76,15 +76,20 @@ struct max17042_chip {
|
|||
};
|
||||
|
||||
static enum power_supply_property max17042_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_TECHNOLOGY,
|
||||
POWER_SUPPLY_PROP_CYCLE_COUNT,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_MAX,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_MIN,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_AVG,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_OCV,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL,
|
||||
POWER_SUPPLY_PROP_CHARGE_NOW,
|
||||
POWER_SUPPLY_PROP_CHARGE_COUNTER,
|
||||
POWER_SUPPLY_PROP_TEMP,
|
||||
POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
|
||||
|
@ -92,6 +97,7 @@ static enum power_supply_property max17042_battery_props[] = {
|
|||
POWER_SUPPLY_PROP_TEMP_MIN,
|
||||
POWER_SUPPLY_PROP_TEMP_MAX,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_SCOPE,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_AVG,
|
||||
};
|
||||
|
@ -106,19 +112,53 @@ static int max17042_get_temperature(struct max17042_chip *chip, int *temp)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*temp = data;
|
||||
/* The value is signed. */
|
||||
if (*temp & 0x8000) {
|
||||
*temp = (0x7fff & ~*temp) + 1;
|
||||
*temp *= -1;
|
||||
}
|
||||
|
||||
*temp = sign_extend32(data, 15);
|
||||
/* The value is converted into deci-centigrade scale */
|
||||
/* Units of LSB = 1 / 256 degree Celsius */
|
||||
*temp = *temp * 10 / 256;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max17042_get_status(struct max17042_chip *chip, int *status)
|
||||
{
|
||||
int ret, charge_full, charge_now;
|
||||
|
||||
ret = power_supply_am_i_supplied(chip->battery);
|
||||
if (ret < 0) {
|
||||
*status = POWER_SUPPLY_STATUS_UNKNOWN;
|
||||
return 0;
|
||||
}
|
||||
if (ret == 0) {
|
||||
*status = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The MAX170xx has builtin end-of-charge detection and will update
|
||||
* FullCAP to match RepCap when it detects end of charging.
|
||||
*
|
||||
* When this cycle the battery gets charged to a higher (calculated)
|
||||
* capacity then the previous cycle then FullCAP will get updated
|
||||
* contineously once end-of-charge detection kicks in, so allow the
|
||||
* 2 to differ a bit.
|
||||
*/
|
||||
|
||||
ret = regmap_read(chip->regmap, MAX17042_FullCAP, &charge_full);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read(chip->regmap, MAX17042_RepCap, &charge_now);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if ((charge_full - charge_now) <= MAX17042_FULL_THRESHOLD)
|
||||
*status = POWER_SUPPLY_STATUS_FULL;
|
||||
else
|
||||
*status = POWER_SUPPLY_STATUS_CHARGING;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max17042_get_battery_health(struct max17042_chip *chip, int *health)
|
||||
{
|
||||
int temp, vavg, vbatt, ret;
|
||||
|
@ -156,12 +196,12 @@ static int max17042_get_battery_health(struct max17042_chip *chip, int *health)
|
|||
if (ret < 0)
|
||||
goto health_error;
|
||||
|
||||
if (temp <= chip->pdata->temp_min) {
|
||||
if (temp < chip->pdata->temp_min) {
|
||||
*health = POWER_SUPPLY_HEALTH_COLD;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (temp >= chip->pdata->temp_max) {
|
||||
if (temp > chip->pdata->temp_max) {
|
||||
*health = POWER_SUPPLY_HEALTH_OVERHEAT;
|
||||
goto out;
|
||||
}
|
||||
|
@ -183,11 +223,17 @@ static int max17042_get_property(struct power_supply *psy,
|
|||
struct regmap *map = chip->regmap;
|
||||
int ret;
|
||||
u32 data;
|
||||
u64 data64;
|
||||
|
||||
if (!chip->init_complete)
|
||||
return -EAGAIN;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
ret = max17042_get_status(chip, &val->intval);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
ret = regmap_read(map, MAX17042_STATUS, &data);
|
||||
if (ret < 0)
|
||||
|
@ -198,6 +244,9 @@ static int max17042_get_property(struct power_supply *psy,
|
|||
else
|
||||
val->intval = 1;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TECHNOLOGY:
|
||||
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CYCLE_COUNT:
|
||||
ret = regmap_read(map, MAX17042_Cycles, &data);
|
||||
if (ret < 0)
|
||||
|
@ -213,6 +262,13 @@ static int max17042_get_property(struct power_supply *psy,
|
|||
val->intval = data >> 8;
|
||||
val->intval *= 20000; /* Units of LSB = 20mV */
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MIN:
|
||||
ret = regmap_read(map, MAX17042_MinMaxVolt, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = (data & 0xff) * 20000; /* Units of 20mV */
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
|
||||
if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042)
|
||||
ret = regmap_read(map, MAX17042_V_empty, &data);
|
||||
|
@ -252,12 +308,32 @@ static int max17042_get_property(struct power_supply *psy,
|
|||
|
||||
val->intval = data >> 8;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
|
||||
ret = regmap_read(map, MAX17042_DesignCap, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data64 = data * 5000000ll;
|
||||
do_div(data64, chip->pdata->r_sns);
|
||||
val->intval = data64;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_FULL:
|
||||
ret = regmap_read(map, MAX17042_FullCAP, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = data * 1000 / 2;
|
||||
data64 = data * 5000000ll;
|
||||
do_div(data64, chip->pdata->r_sns);
|
||||
val->intval = data64;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_NOW:
|
||||
ret = regmap_read(map, MAX17042_RepCap, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data64 = data * 5000000ll;
|
||||
do_div(data64, chip->pdata->r_sns);
|
||||
val->intval = data64;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
|
||||
ret = regmap_read(map, MAX17042_QH, &data);
|
||||
|
@ -276,14 +352,14 @@ static int max17042_get_property(struct power_supply *psy,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
/* LSB is Alert Minimum. In deci-centigrade */
|
||||
val->intval = (data & 0xff) * 10;
|
||||
val->intval = sign_extend32(data & 0xff, 7) * 10;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
|
||||
ret = regmap_read(map, MAX17042_TALRT_Th, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/* MSB is Alert Maximum. In deci-centigrade */
|
||||
val->intval = (data >> 8) * 10;
|
||||
val->intval = sign_extend32(data >> 8, 7) * 10;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TEMP_MIN:
|
||||
val->intval = chip->pdata->temp_min;
|
||||
|
@ -296,19 +372,16 @@ static int max17042_get_property(struct power_supply *psy,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_SCOPE:
|
||||
val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
||||
if (chip->pdata->enable_current_sense) {
|
||||
ret = regmap_read(map, MAX17042_Current, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = data;
|
||||
if (val->intval & 0x8000) {
|
||||
/* Negative */
|
||||
val->intval = ~val->intval & 0x7fff;
|
||||
val->intval++;
|
||||
val->intval *= -1;
|
||||
}
|
||||
val->intval = sign_extend32(data, 15);
|
||||
val->intval *= 1562500 / chip->pdata->r_sns;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
|
@ -320,13 +393,7 @@ static int max17042_get_property(struct power_supply *psy,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val->intval = data;
|
||||
if (val->intval & 0x8000) {
|
||||
/* Negative */
|
||||
val->intval = ~val->intval & 0x7fff;
|
||||
val->intval++;
|
||||
val->intval *= -1;
|
||||
}
|
||||
val->intval = sign_extend32(data, 15);
|
||||
val->intval *= 1562500 / chip->pdata->r_sns;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
|
@ -401,6 +468,11 @@ static int max17042_property_is_writeable(struct power_supply *psy,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void max17042_external_power_changed(struct power_supply *psy)
|
||||
{
|
||||
power_supply_changed(psy);
|
||||
}
|
||||
|
||||
static int max17042_write_verify_reg(struct regmap *map, u8 reg, u32 value)
|
||||
{
|
||||
int retries = 8;
|
||||
|
@ -790,8 +862,9 @@ static void max17042_init_worker(struct work_struct *work)
|
|||
|
||||
#ifdef CONFIG_OF
|
||||
static struct max17042_platform_data *
|
||||
max17042_get_pdata(struct device *dev)
|
||||
max17042_get_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;
|
||||
|
@ -824,10 +897,55 @@ max17042_get_pdata(struct device *dev)
|
|||
return pdata;
|
||||
}
|
||||
#else
|
||||
static struct max17042_reg_data max17047_default_pdata_init_regs[] = {
|
||||
/*
|
||||
* Some firmwares do not set FullSOCThr, Enable End-of-Charge Detection
|
||||
* when the voltage FG reports 95%, as recommended in the datasheet.
|
||||
*/
|
||||
{ MAX17047_FullSOCThr, MAX17042_BATTERY_FULL << 8 },
|
||||
};
|
||||
|
||||
static struct max17042_platform_data *
|
||||
max17042_get_pdata(struct device *dev)
|
||||
max17042_get_pdata(struct max17042_chip *chip)
|
||||
{
|
||||
return dev->platform_data;
|
||||
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
|
||||
* default values for the non init bits to make things work.
|
||||
*/
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return pdata;
|
||||
|
||||
if (chip->chip_type != MAXIM_DEVICE_TYPE_MAX17042) {
|
||||
pdata->init_data = max17047_default_pdata_init_regs;
|
||||
pdata->num_init_data =
|
||||
ARRAY_SIZE(max17047_default_pdata_init_regs);
|
||||
}
|
||||
|
||||
ret = regmap_read(chip->regmap, MAX17042_MiscCFG, &misc_cfg);
|
||||
if (ret < 0)
|
||||
return NULL;
|
||||
|
||||
/* If bits 0-1 are set to 3 then only Voltage readings are used */
|
||||
if ((misc_cfg & 0x3) == 0x3)
|
||||
pdata->enable_current_sense = false;
|
||||
else
|
||||
pdata->enable_current_sense = true;
|
||||
|
||||
pdata->vmin = MAX17042_DEFAULT_VMIN;
|
||||
pdata->vmax = MAX17042_DEFAULT_VMAX;
|
||||
pdata->temp_min = MAX17042_DEFAULT_TEMP_MIN;
|
||||
pdata->temp_max = MAX17042_DEFAULT_TEMP_MAX;
|
||||
|
||||
return pdata;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -843,6 +961,7 @@ static const struct power_supply_desc max17042_psy_desc = {
|
|||
.get_property = max17042_get_property,
|
||||
.set_property = max17042_set_property,
|
||||
.property_is_writeable = max17042_property_is_writeable,
|
||||
.external_power_changed = max17042_external_power_changed,
|
||||
.properties = max17042_battery_props,
|
||||
.num_properties = ARRAY_SIZE(max17042_battery_props),
|
||||
};
|
||||
|
@ -876,20 +995,20 @@ static int max17042_probe(struct i2c_client *client,
|
|||
return -ENOMEM;
|
||||
|
||||
chip->client = client;
|
||||
chip->chip_type = id->driver_data;
|
||||
chip->regmap = devm_regmap_init_i2c(client, &max17042_regmap_config);
|
||||
if (IS_ERR(chip->regmap)) {
|
||||
dev_err(&client->dev, "Failed to initialize regmap\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chip->pdata = max17042_get_pdata(&client->dev);
|
||||
chip->pdata = max17042_get_pdata(chip);
|
||||
if (!chip->pdata) {
|
||||
dev_err(&client->dev, "no platform data provided\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, chip);
|
||||
chip->chip_type = id->driver_data;
|
||||
psy_cfg.drv_data = chip;
|
||||
|
||||
/* When current is not measured,
|
||||
|
|
|
@ -30,9 +30,9 @@ static inline unsigned int get_irq_flags(struct resource *res)
|
|||
static struct device *dev;
|
||||
static struct pda_power_pdata *pdata;
|
||||
static struct resource *ac_irq, *usb_irq;
|
||||
static struct timer_list charger_timer;
|
||||
static struct timer_list supply_timer;
|
||||
static struct timer_list polling_timer;
|
||||
static struct delayed_work charger_work;
|
||||
static struct delayed_work polling_work;
|
||||
static struct delayed_work supply_work;
|
||||
static int polling;
|
||||
static struct power_supply *pda_psy_ac, *pda_psy_usb;
|
||||
|
||||
|
@ -140,7 +140,7 @@ static void update_charger(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void supply_timer_func(unsigned long unused)
|
||||
static void supply_work_func(struct work_struct *work)
|
||||
{
|
||||
if (ac_status == PDA_PSY_TO_CHANGE) {
|
||||
ac_status = new_ac_status;
|
||||
|
@ -161,11 +161,12 @@ static void psy_changed(void)
|
|||
* Okay, charger set. Now wait a bit before notifying supplicants,
|
||||
* charge power should stabilize.
|
||||
*/
|
||||
mod_timer(&supply_timer,
|
||||
jiffies + msecs_to_jiffies(pdata->wait_for_charger));
|
||||
cancel_delayed_work(&supply_work);
|
||||
schedule_delayed_work(&supply_work,
|
||||
msecs_to_jiffies(pdata->wait_for_charger));
|
||||
}
|
||||
|
||||
static void charger_timer_func(unsigned long unused)
|
||||
static void charger_work_func(struct work_struct *work)
|
||||
{
|
||||
update_status();
|
||||
psy_changed();
|
||||
|
@ -184,13 +185,14 @@ static irqreturn_t power_changed_isr(int irq, void *power_supply)
|
|||
* Wait a bit before reading ac/usb line status and setting charger,
|
||||
* because ac/usb status readings may lag from irq.
|
||||
*/
|
||||
mod_timer(&charger_timer,
|
||||
jiffies + msecs_to_jiffies(pdata->wait_for_status));
|
||||
cancel_delayed_work(&charger_work);
|
||||
schedule_delayed_work(&charger_work,
|
||||
msecs_to_jiffies(pdata->wait_for_status));
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void polling_timer_func(unsigned long unused)
|
||||
static void polling_work_func(struct work_struct *work)
|
||||
{
|
||||
int changed = 0;
|
||||
|
||||
|
@ -211,8 +213,9 @@ static void polling_timer_func(unsigned long unused)
|
|||
if (changed)
|
||||
psy_changed();
|
||||
|
||||
mod_timer(&polling_timer,
|
||||
jiffies + msecs_to_jiffies(pdata->polling_interval));
|
||||
cancel_delayed_work(&polling_work);
|
||||
schedule_delayed_work(&polling_work,
|
||||
msecs_to_jiffies(pdata->polling_interval));
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_PHY)
|
||||
|
@ -250,8 +253,9 @@ static int otg_handle_notification(struct notifier_block *nb,
|
|||
* Wait a bit before reading ac/usb line status and setting charger,
|
||||
* because ac/usb status readings may lag from irq.
|
||||
*/
|
||||
mod_timer(&charger_timer,
|
||||
jiffies + msecs_to_jiffies(pdata->wait_for_status));
|
||||
cancel_delayed_work(&charger_work);
|
||||
schedule_delayed_work(&charger_work,
|
||||
msecs_to_jiffies(pdata->wait_for_status));
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
@ -300,8 +304,8 @@ static int pda_power_probe(struct platform_device *pdev)
|
|||
if (!pdata->ac_max_uA)
|
||||
pdata->ac_max_uA = 500000;
|
||||
|
||||
setup_timer(&charger_timer, charger_timer_func, 0);
|
||||
setup_timer(&supply_timer, supply_timer_func, 0);
|
||||
INIT_DELAYED_WORK(&charger_work, charger_work_func);
|
||||
INIT_DELAYED_WORK(&supply_work, supply_work_func);
|
||||
|
||||
ac_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ac");
|
||||
usb_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "usb");
|
||||
|
@ -385,9 +389,10 @@ static int pda_power_probe(struct platform_device *pdev)
|
|||
|
||||
if (polling) {
|
||||
dev_dbg(dev, "will poll for status\n");
|
||||
setup_timer(&polling_timer, polling_timer_func, 0);
|
||||
mod_timer(&polling_timer,
|
||||
jiffies + msecs_to_jiffies(pdata->polling_interval));
|
||||
INIT_DELAYED_WORK(&polling_work, polling_work_func);
|
||||
cancel_delayed_work(&polling_work);
|
||||
schedule_delayed_work(&polling_work,
|
||||
msecs_to_jiffies(pdata->polling_interval));
|
||||
}
|
||||
|
||||
if (ac_irq || usb_irq)
|
||||
|
@ -433,9 +438,9 @@ static int pda_power_remove(struct platform_device *pdev)
|
|||
free_irq(ac_irq->start, pda_psy_ac);
|
||||
|
||||
if (polling)
|
||||
del_timer_sync(&polling_timer);
|
||||
del_timer_sync(&charger_timer);
|
||||
del_timer_sync(&supply_timer);
|
||||
cancel_delayed_work_sync(&polling_work);
|
||||
cancel_delayed_work_sync(&charger_work);
|
||||
cancel_delayed_work_sync(&supply_work);
|
||||
|
||||
if (pdata->is_usb_online)
|
||||
power_supply_unregister(pda_psy_usb);
|
||||
|
|
|
@ -280,13 +280,19 @@ static inline int power_supply_check_supplies(struct power_supply *psy)
|
|||
}
|
||||
#endif
|
||||
|
||||
static int __power_supply_am_i_supplied(struct device *dev, void *data)
|
||||
struct psy_am_i_supplied_data {
|
||||
struct power_supply *psy;
|
||||
unsigned int count;
|
||||
};
|
||||
|
||||
static int __power_supply_am_i_supplied(struct device *dev, void *_data)
|
||||
{
|
||||
union power_supply_propval ret = {0,};
|
||||
struct power_supply *psy = data;
|
||||
struct power_supply *epsy = dev_get_drvdata(dev);
|
||||
struct psy_am_i_supplied_data *data = _data;
|
||||
|
||||
if (__power_supply_is_supplied_by(epsy, psy))
|
||||
data->count++;
|
||||
if (__power_supply_is_supplied_by(epsy, data->psy))
|
||||
if (!epsy->desc->get_property(epsy, POWER_SUPPLY_PROP_ONLINE,
|
||||
&ret))
|
||||
return ret.intval;
|
||||
|
@ -296,12 +302,16 @@ static int __power_supply_am_i_supplied(struct device *dev, void *data)
|
|||
|
||||
int power_supply_am_i_supplied(struct power_supply *psy)
|
||||
{
|
||||
struct psy_am_i_supplied_data data = { psy, 0 };
|
||||
int error;
|
||||
|
||||
error = class_for_each_device(power_supply_class, NULL, psy,
|
||||
error = class_for_each_device(power_supply_class, NULL, &data,
|
||||
__power_supply_am_i_supplied);
|
||||
|
||||
dev_dbg(&psy->dev, "%s %d\n", __func__, error);
|
||||
dev_dbg(&psy->dev, "%s count %u err %d\n", __func__, data.count, error);
|
||||
|
||||
if (data.count == 0)
|
||||
return -ENODEV;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c/twl4030-madc.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
|
|
|
@ -171,7 +171,6 @@ struct sbs_info {
|
|||
u32 i2c_retry_count;
|
||||
u32 poll_retry_count;
|
||||
struct delayed_work work;
|
||||
int ignore_changes;
|
||||
};
|
||||
|
||||
static char model_name[I2C_SMBUS_BLOCK_MAX + 1];
|
||||
|
@ -296,6 +295,31 @@ static int sbs_write_word_data(struct i2c_client *client, u8 address,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int sbs_status_correct(struct i2c_client *client, int *intval)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sbs_read_word_data(client, sbs_data[REG_CURRENT].addr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = (s16)ret;
|
||||
|
||||
/* Not drawing current means full (cannot be not charging) */
|
||||
if (ret == 0)
|
||||
*intval = POWER_SUPPLY_STATUS_FULL;
|
||||
|
||||
if (*intval == POWER_SUPPLY_STATUS_FULL) {
|
||||
/* Drawing or providing current when full */
|
||||
if (ret > 0)
|
||||
*intval = POWER_SUPPLY_STATUS_CHARGING;
|
||||
else if (ret < 0)
|
||||
*intval = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sbs_get_battery_presence_and_health(
|
||||
struct i2c_client *client, enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
|
@ -402,6 +426,8 @@ static int sbs_get_battery_property(struct i2c_client *client,
|
|||
else
|
||||
val->intval = POWER_SUPPLY_STATUS_CHARGING;
|
||||
|
||||
sbs_status_correct(client, &val->intval);
|
||||
|
||||
if (chip->poll_time == 0)
|
||||
chip->last_state = val->intval;
|
||||
else if (chip->last_state != val->intval) {
|
||||
|
@ -675,30 +701,34 @@ static int sbs_get_property(struct power_supply *psy,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t sbs_irq(int irq, void *devid)
|
||||
static void sbs_supply_changed(struct sbs_info *chip)
|
||||
{
|
||||
struct sbs_info *chip = devid;
|
||||
struct power_supply *battery = chip->power_supply;
|
||||
int ret;
|
||||
|
||||
ret = gpiod_get_value_cansleep(chip->gpio_detect);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return;
|
||||
chip->is_present = ret;
|
||||
power_supply_changed(battery);
|
||||
}
|
||||
|
||||
static irqreturn_t sbs_irq(int irq, void *devid)
|
||||
{
|
||||
sbs_supply_changed(devid);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void sbs_alert(struct i2c_client *client, enum i2c_alert_protocol prot,
|
||||
unsigned int data)
|
||||
{
|
||||
sbs_supply_changed(i2c_get_clientdata(client));
|
||||
}
|
||||
|
||||
static void sbs_external_power_changed(struct power_supply *psy)
|
||||
{
|
||||
struct sbs_info *chip = power_supply_get_drvdata(psy);
|
||||
|
||||
if (chip->ignore_changes > 0) {
|
||||
chip->ignore_changes--;
|
||||
return;
|
||||
}
|
||||
|
||||
/* cancel outstanding work */
|
||||
cancel_delayed_work_sync(&chip->work);
|
||||
|
||||
|
@ -727,6 +757,8 @@ static void sbs_delayed_work(struct work_struct *work)
|
|||
else
|
||||
ret = POWER_SUPPLY_STATUS_CHARGING;
|
||||
|
||||
sbs_status_correct(chip->client, &ret);
|
||||
|
||||
if (chip->last_state != ret) {
|
||||
chip->poll_time = 0;
|
||||
power_supply_changed(chip->power_supply);
|
||||
|
@ -775,10 +807,6 @@ static int sbs_probe(struct i2c_client *client,
|
|||
chip->enable_detection = false;
|
||||
psy_cfg.of_node = client->dev.of_node;
|
||||
psy_cfg.drv_data = chip;
|
||||
/* ignore first notification of external change, it is generated
|
||||
* from the power_supply_register call back
|
||||
*/
|
||||
chip->ignore_changes = 1;
|
||||
chip->last_state = POWER_SUPPLY_STATUS_UNKNOWN;
|
||||
|
||||
/* use pdata if available, fall back to DT properties,
|
||||
|
@ -820,7 +848,7 @@ static int sbs_probe(struct i2c_client *client,
|
|||
}
|
||||
|
||||
rc = devm_request_threaded_irq(&client->dev, irq, NULL, sbs_irq,
|
||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
dev_name(&client->dev), chip);
|
||||
if (rc) {
|
||||
dev_warn(&client->dev, "Failed to request irq: %d\n", rc);
|
||||
|
@ -917,6 +945,7 @@ MODULE_DEVICE_TABLE(of, sbs_dt_ids);
|
|||
static struct i2c_driver sbs_battery_driver = {
|
||||
.probe = sbs_probe,
|
||||
.remove = sbs_remove,
|
||||
.alert = sbs_alert,
|
||||
.id_table = sbs_id,
|
||||
.driver = {
|
||||
.name = "sbs-battery",
|
||||
|
|
|
@ -205,35 +205,6 @@ static int twl4030bci_read_adc_val(u8 reg)
|
|||
return temp | val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if Battery Pack was present
|
||||
*/
|
||||
static int twl4030_is_battery_present(struct twl4030_bci *bci)
|
||||
{
|
||||
int ret;
|
||||
u8 val = 0;
|
||||
|
||||
/* Battery presence in Main charge? */
|
||||
ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, TWL4030_BCIMFSTS3);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (val & TWL4030_BATSTSMCHG)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* OK, It could be that bootloader did not enable main charger,
|
||||
* pre-charge is h/w auto. So, Battery presence in Pre-charge?
|
||||
*/
|
||||
ret = twl_i2c_read_u8(TWL4030_MODULE_PRECHARGE, &val,
|
||||
TWL4030_BCIMFSTS1);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (val & TWL4030_BATSTSPCHG)
|
||||
return 0;
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* TI provided formulas:
|
||||
* CGAIN == 0: ICHG = (BCIICHG * 1.7) / (2^10 - 1) - 0.85
|
||||
|
@ -922,6 +893,28 @@ static int twl4030_bci_get_property(struct power_supply *psy,
|
|||
twl4030_bci_state_to_status(state) !=
|
||||
POWER_SUPPLY_STATUS_NOT_CHARGING;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
val->intval = -1;
|
||||
if (psy->desc->type != POWER_SUPPLY_TYPE_USB) {
|
||||
if (!bci->ac_is_active)
|
||||
val->intval = bci->ac_cur;
|
||||
} else {
|
||||
if (bci->ac_is_active)
|
||||
val->intval = bci->usb_cur_target;
|
||||
}
|
||||
if (val->intval < 0) {
|
||||
u8 bcictl1;
|
||||
|
||||
val->intval = twl4030bci_read_adc_val(TWL4030_BCIIREF1);
|
||||
if (val->intval < 0)
|
||||
return val->intval;
|
||||
ret = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
val->intval = regval2ua(val->intval, bcictl1 &
|
||||
TWL4030_CGAIN);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -929,11 +922,44 @@ static int twl4030_bci_get_property(struct power_supply *psy,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int twl4030_bci_set_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
struct twl4030_bci *bci = dev_get_drvdata(psy->dev.parent);
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
if (psy->desc->type == POWER_SUPPLY_TYPE_USB)
|
||||
bci->usb_cur_target = val->intval;
|
||||
else
|
||||
bci->ac_cur = val->intval;
|
||||
twl4030_charger_update_current(bci);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int twl4030_bci_property_is_writeable(struct power_supply *psy,
|
||||
enum power_supply_property psp)
|
||||
{
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static enum power_supply_property twl4030_charger_props[] = {
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
@ -970,6 +996,8 @@ static const struct power_supply_desc twl4030_bci_ac_desc = {
|
|||
.properties = twl4030_charger_props,
|
||||
.num_properties = ARRAY_SIZE(twl4030_charger_props),
|
||||
.get_property = twl4030_bci_get_property,
|
||||
.set_property = twl4030_bci_set_property,
|
||||
.property_is_writeable = twl4030_bci_property_is_writeable,
|
||||
};
|
||||
|
||||
static const struct power_supply_desc twl4030_bci_usb_desc = {
|
||||
|
@ -978,6 +1006,8 @@ static const struct power_supply_desc twl4030_bci_usb_desc = {
|
|||
.properties = twl4030_charger_props,
|
||||
.num_properties = ARRAY_SIZE(twl4030_charger_props),
|
||||
.get_property = twl4030_bci_get_property,
|
||||
.set_property = twl4030_bci_set_property,
|
||||
.property_is_writeable = twl4030_bci_property_is_writeable,
|
||||
};
|
||||
|
||||
static int twl4030_bci_probe(struct platform_device *pdev)
|
||||
|
@ -1009,13 +1039,6 @@ static int twl4030_bci_probe(struct platform_device *pdev)
|
|||
bci->irq_chg = platform_get_irq(pdev, 0);
|
||||
bci->irq_bci = platform_get_irq(pdev, 1);
|
||||
|
||||
/* Only proceed further *IF* battery is physically present */
|
||||
ret = twl4030_is_battery_present(bci);
|
||||
if (ret) {
|
||||
dev_crit(&pdev->dev, "Battery was not detected:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, bci);
|
||||
|
||||
bci->ac = devm_power_supply_register(&pdev->dev, &twl4030_bci_ac_desc,
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include <linux/power_supply.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sort.h>
|
||||
#include <linux/i2c/twl4030-madc.h>
|
||||
#include <linux/power/twl4030_madc_battery.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
|
||||
|
|
|
@ -24,8 +24,15 @@
|
|||
#define __MAX17042_BATTERY_H_
|
||||
|
||||
#define MAX17042_STATUS_BattAbsent (1 << 3)
|
||||
#define MAX17042_BATTERY_FULL (100)
|
||||
#define MAX17042_BATTERY_FULL (95) /* Recommend. FullSOCThr value */
|
||||
#define MAX17042_DEFAULT_SNS_RESISTOR (10000)
|
||||
#define MAX17042_DEFAULT_VMIN (3000)
|
||||
#define MAX17042_DEFAULT_VMAX (4500) /* LiHV cell max */
|
||||
#define MAX17042_DEFAULT_TEMP_MIN (0) /* For sys without temp sensor */
|
||||
#define MAX17042_DEFAULT_TEMP_MAX (700) /* 70 degrees Celcius */
|
||||
|
||||
/* Consider RepCap which is less then 10 units below FullCAP full */
|
||||
#define MAX17042_FULL_THRESHOLD 10
|
||||
|
||||
#define MAX17042_CHARACTERIZATION_DATA_SIZE 48
|
||||
|
||||
|
|
Loading…
Reference in New Issue