1
0
Fork 0

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
  ...
hifive-unleashed-5.1
Linus Torvalds 2017-05-12 12:02:21 -07:00
commit 6b402bdfdb
18 changed files with 975 additions and 173 deletions

View File

@ -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>

View File

@ -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";
}
};

View File

@ -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

View File

@ -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

View File

@ -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;
}
}

View File

@ -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, &reg);
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, &reg);
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,
&reg);
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,
&reg);
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,
&reg);
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,
&reg);
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, &reg);
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, &reg);
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");

View File

@ -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;
}

View File

@ -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 @@ out_err:
}
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),

View File

@ -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,

View File

@ -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);

View File

@ -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,

View File

@ -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);

View File

@ -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;
}

View File

@ -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>

View File

@ -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 @@ done:
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",

View File

@ -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,

View File

@ -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>

View File

@ -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