power supply and reset changes for the v4.14 series

* New chip/feature support
  - bq27xxx: support updating battery config from DT
  - bq24190: support loading battery charge info from DT
  - LTC2941: add LTC2942/LTC2944 support
  - max17042: add ACPI support
  - max1721x: new driver
 * Misc
  - Move bq27xxx w1 driver from w1 into power-supply subsystem
  - Introduce power_supply_set_input_current_limit_from_supplier
  - constify stuff
  - some minor fixes
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE72YNB0Y/i3JqeVQT2O7X88g7+poFAlmxSSQACgkQ2O7X88g7
 +ppf2xAAliDRdNPuxpKu69syiX+iNPaPHTqRXd41sAJlEX2IYv/avcoNFcGySplR
 gJYdlbx2Qd86aBaMf92h4tGihk2iNa87It9xkA+JgO3wNoKiZ+b5lk///IDk8aDF
 8sknSTXJtGYse0jwBRKTT4cL1ZW/g10heUxPr8AiX8nL4QhMf0TIxXawWQteAZDE
 dDz/UEAqbINooD5HtP716Z0e3U6FyWTqFc6UzuR+n8c6wC6n/JA6h91ASAHUmExT
 aH1JuQyrmucBp8cLhP01ThSkoIdGbcZM7nJ3WQBlfDricFJXa/yThug/mHvjztJX
 3U1UOZBqsb/LL9Dh5hl/D3+i+7qI29KCyUZiYUrwoiSjxM/AtOB2t4cAWmU7+PnJ
 V3coYC1s2DlwYcLIUiyE2zmVWIDcOuuOJhQjCdpUBTPvz83nQ0v5oXMn7VnQmWlO
 8Z/IRWnW90v2D2iHmSPAwFG3ATfH0pAP8G/4Wrudti+ymSxAhXaLqQcj3HHjKjvI
 3CEGOPAN9Opuof7SfkIVuoOjPzazx5GUpAZQKi8LS87KRdXN4ics3vbNB6VBDR50
 FzVhJJZBTeAOJCDogInuu4gDa0L5Ikfdkhqc+nvNfcdsE2k3H8Nb//+UPiPnfgEA
 mEFyp9gaY08/3ylDEHxp3N/W7POrL4QelTjv0dvdKW70i7KtWpA=
 =iy5f
 -----END PGP SIGNATURE-----

Merge tag 'for-v4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply

Pull power supply and reset changes from Sebastian Reichel:
 "New chip/feature support:
   - bq27xxx: support updating battery config from DT
   - bq24190: support loading battery charge info from DT
   - LTC2941: add LTC2942/LTC2944 support
   - max17042: add ACPI support
   - max1721x: new driver

  Misc:
   - Move bq27xxx w1 driver from w1 into power-supply subsystem
   - Introduce power_supply_set_input_current_limit_from_supplier
   - constify stuff
   - some minor fixes"

* tag 'for-v4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (39 commits)
  power: supply: bq27xxx: enable writing capacity values for bq27421
  power: supply: bq24190_charger: Get input_current_limit from our supplier
  power: supply: bq24190_charger: Export 5V boost converter as regulator
  power: supply: bq24190_charger: Add power_supply_battery_info support
  power: supply: bq24190_charger: Add property system-minimum-microvolt
  power: supply: bq24190_charger: Enable devicetree config
  dt-bindings: power: supply: Add docs for TI BQ24190 battery charger
  power: supply: bq27xxx: Remove duplicate chip data arrays
  power: supply: bq27xxx: Enable data memory update for certain chips
  power: supply: bq27xxx: Add chip IDs for previously shadowed chips
  power: supply: bq27xxx: Create single chip data table
  power: supply: bq24190_charger: Add ti,bq24192i to devicetree table
  power: supply: bq24190_charger: Add input_current_limit property
  power: supply: Add power_supply_set_input_current_limit_from_supplier helper
  power: supply: max17042_battery: Fix compiler warning
  power: supply: core: Delete two error messages for a failed memory allocation in power_supply_check_supplies()
  power: supply: make device_attribute const
  power: supply: max17042_battery: Fix ACPI interrupt issues
  power: supply: max17042_battery: Add support for ACPI enumeration
  power: supply: lp8788: Make several arrays static const * const
  ...
This commit is contained in:
Linus Torvalds 2017-09-09 14:44:39 -07:00
commit 0ce5c79f38
27 changed files with 1471 additions and 638 deletions

View file

@ -171,8 +171,8 @@ static u32 at91_shdwc_get_wakeup_input(struct platform_device *pdev,
for_each_child_of_node(np, cnp) {
if (of_property_read_u32(cnp, "reg", &wk_input)) {
dev_warn(&pdev->dev, "reg property is missing for %s\n",
cnp->full_name);
dev_warn(&pdev->dev, "reg property is missing for %pOF\n",
cnp);
continue;
}

View file

@ -198,6 +198,15 @@ config BATTERY_BQ27XXX_I2C
Say Y here to enable support for batteries with BQ27xxx chips
connected over an I2C bus.
config BATTERY_BQ27XXX_HDQ
tristate "BQ27xxx HDQ support"
depends on BATTERY_BQ27XXX
depends on W1
default y
help
Say Y here to enable support for batteries with BQ27xxx chips
connected over an HDQ bus.
config BATTERY_BQ27XXX_DT_UPDATES_NVM
bool "BQ27xxx support for update of NVM/flash data memory"
depends on BATTERY_BQ27XXX_I2C
@ -313,6 +322,19 @@ config BATTERY_MAX17042
with MAX17042. This driver also supports max17047/50 chips which are
improved version of max17042.
config BATTERY_MAX1721X
tristate "MAX17211/MAX17215 standalone gas-gauge"
depends on W1
select REGMAP_W1
help
MAX1721x is fuel-gauge systems for lithium-ion (Li+) batteries
in handheld and portable equipment. MAX17211 used with single cell
battery. MAX17215 designed for muticell battery. Both them have
OneWire (W1) host interface.
Say Y here to enable support for the MAX17211/MAX17215 standalone
battery gas-gauge.
config BATTERY_Z2
tristate "Z2 battery driver"
depends on I2C && MACH_ZIPIT2
@ -365,6 +387,7 @@ config BATTERY_RX51
config CHARGER_CPCAP
tristate "CPCAP PMIC Charger Driver"
depends on MFD_CPCAP && IIO
depends on OMAP_USB2 || (!OMAP_USB2 && COMPILE_TEST)
default MFD_CPCAP
help
Say Y to enable support for CPCAP PMIC charger driver for Motorola

View file

@ -38,12 +38,14 @@ obj-$(CONFIG_BATTERY_SBS) += sbs-battery.o
obj-$(CONFIG_CHARGER_SBS) += sbs-charger.o
obj-$(CONFIG_BATTERY_BQ27XXX) += bq27xxx_battery.o
obj-$(CONFIG_BATTERY_BQ27XXX_I2C) += bq27xxx_battery_i2c.o
obj-$(CONFIG_BATTERY_BQ27XXX_HDQ) += bq27xxx_battery_hdq.o
obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
obj-$(CONFIG_BATTERY_DA9052) += da9052-battery.o
obj-$(CONFIG_CHARGER_DA9150) += da9150-charger.o
obj-$(CONFIG_BATTERY_DA9150) += da9150-fg.o
obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o
obj-$(CONFIG_BATTERY_MAX1721X) += max1721x_battery.o
obj-$(CONFIG_BATTERY_Z2) += z2_battery.o
obj-$(CONFIG_BATTERY_RT5033) += rt5033_battery.o
obj-$(CONFIG_CHARGER_RT9455) += rt9455_charger.o

View file

@ -596,9 +596,9 @@ static int act8945a_charger_probe(struct platform_device *pdev)
return ret;
irq = of_irq_get(pdev->dev.of_node, 0);
if (irq == -EPROBE_DEFER) {
if (irq <= 0) {
dev_err(&pdev->dev, "failed to find IRQ number\n");
return -EPROBE_DEFER;
return irq ?: -ENXIO;
}
ret = devm_request_irq(&pdev->dev, irq, act8945a_status_changed,

View file

@ -16,6 +16,9 @@
#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <linux/power_supply.h>
#include <linux/power/bq24190_charger.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/workqueue.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
@ -43,6 +46,8 @@
#define BQ24190_REG_POC_CHG_CONFIG_OTG 0x2
#define BQ24190_REG_POC_SYS_MIN_MASK (BIT(3) | BIT(2) | BIT(1))
#define BQ24190_REG_POC_SYS_MIN_SHIFT 1
#define BQ24190_REG_POC_SYS_MIN_MIN 3000
#define BQ24190_REG_POC_SYS_MIN_MAX 3700
#define BQ24190_REG_POC_BOOST_LIM_MASK BIT(0)
#define BQ24190_REG_POC_BOOST_LIM_SHIFT 0
@ -57,9 +62,13 @@
#define BQ24190_REG_PCTCC_IPRECHG_MASK (BIT(7) | BIT(6) | BIT(5) | \
BIT(4))
#define BQ24190_REG_PCTCC_IPRECHG_SHIFT 4
#define BQ24190_REG_PCTCC_IPRECHG_MIN 128
#define BQ24190_REG_PCTCC_IPRECHG_MAX 2048
#define BQ24190_REG_PCTCC_ITERM_MASK (BIT(3) | BIT(2) | BIT(1) | \
BIT(0))
#define BQ24190_REG_PCTCC_ITERM_SHIFT 0
#define BQ24190_REG_PCTCC_ITERM_MIN 128
#define BQ24190_REG_PCTCC_ITERM_MAX 2048
#define BQ24190_REG_CVC 0x04 /* Charge Voltage Control */
#define BQ24190_REG_CVC_VREG_MASK (BIT(7) | BIT(6) | BIT(5) | \
@ -156,9 +165,13 @@ struct bq24190_dev_info {
struct extcon_dev *extcon;
struct notifier_block extcon_nb;
struct delayed_work extcon_work;
struct delayed_work input_current_limit_work;
char model_name[I2C_NAME_SIZE];
bool initialized;
bool irq_event;
u16 sys_min;
u16 iprechg;
u16 iterm;
struct mutex f_reg_lock;
u8 f_reg;
u8 ss_reg;
@ -504,15 +517,112 @@ static int bq24190_sysfs_create_group(struct bq24190_dev_info *bdi)
static inline void bq24190_sysfs_remove_group(struct bq24190_dev_info *bdi) {}
#endif
/*
* According to the "Host Mode and default Mode" section of the
* manual, a write to any register causes the bq24190 to switch
* from default mode to host mode. It will switch back to default
* mode after a WDT timeout unless the WDT is turned off as well.
* So, by simply turning off the WDT, we accomplish both with the
* same write.
*/
static int bq24190_set_mode_host(struct bq24190_dev_info *bdi)
#ifdef CONFIG_REGULATOR
static int bq24190_set_charge_mode(struct regulator_dev *dev, u8 val)
{
struct bq24190_dev_info *bdi = rdev_get_drvdata(dev);
int ret;
ret = pm_runtime_get_sync(bdi->dev);
if (ret < 0) {
dev_warn(bdi->dev, "pm_runtime_get failed: %i\n", ret);
pm_runtime_put_noidle(bdi->dev);
return ret;
}
ret = bq24190_write_mask(bdi, BQ24190_REG_POC,
BQ24190_REG_POC_CHG_CONFIG_MASK,
BQ24190_REG_POC_CHG_CONFIG_SHIFT, val);
pm_runtime_mark_last_busy(bdi->dev);
pm_runtime_put_autosuspend(bdi->dev);
return ret;
}
static int bq24190_vbus_enable(struct regulator_dev *dev)
{
return bq24190_set_charge_mode(dev, BQ24190_REG_POC_CHG_CONFIG_OTG);
}
static int bq24190_vbus_disable(struct regulator_dev *dev)
{
return bq24190_set_charge_mode(dev, BQ24190_REG_POC_CHG_CONFIG_CHARGE);
}
static int bq24190_vbus_is_enabled(struct regulator_dev *dev)
{
struct bq24190_dev_info *bdi = rdev_get_drvdata(dev);
int ret;
u8 val;
ret = pm_runtime_get_sync(bdi->dev);
if (ret < 0) {
dev_warn(bdi->dev, "pm_runtime_get failed: %i\n", ret);
pm_runtime_put_noidle(bdi->dev);
return ret;
}
ret = bq24190_read_mask(bdi, BQ24190_REG_POC,
BQ24190_REG_POC_CHG_CONFIG_MASK,
BQ24190_REG_POC_CHG_CONFIG_SHIFT, &val);
pm_runtime_mark_last_busy(bdi->dev);
pm_runtime_put_autosuspend(bdi->dev);
return ret ? ret : val == BQ24190_REG_POC_CHG_CONFIG_OTG;
}
static const struct regulator_ops bq24190_vbus_ops = {
.enable = bq24190_vbus_enable,
.disable = bq24190_vbus_disable,
.is_enabled = bq24190_vbus_is_enabled,
};
static const struct regulator_desc bq24190_vbus_desc = {
.name = "usb_otg_vbus",
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.ops = &bq24190_vbus_ops,
.fixed_uV = 5000000,
.n_voltages = 1,
};
static const struct regulator_init_data bq24190_vbus_init_data = {
.constraints = {
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
};
static int bq24190_register_vbus_regulator(struct bq24190_dev_info *bdi)
{
struct bq24190_platform_data *pdata = bdi->dev->platform_data;
struct regulator_config cfg = { };
struct regulator_dev *reg;
int ret = 0;
cfg.dev = bdi->dev;
if (pdata && pdata->regulator_init_data)
cfg.init_data = pdata->regulator_init_data;
else
cfg.init_data = &bq24190_vbus_init_data;
cfg.driver_data = bdi;
reg = devm_regulator_register(bdi->dev, &bq24190_vbus_desc, &cfg);
if (IS_ERR(reg)) {
ret = PTR_ERR(reg);
dev_err(bdi->dev, "Can't register regulator: %d\n", ret);
}
return ret;
}
#else
static int bq24190_register_vbus_regulator(struct bq24190_dev_info *bdi)
{
return 0;
}
#endif
static int bq24190_set_config(struct bq24190_dev_info *bdi)
{
int ret;
u8 v;
@ -523,9 +633,52 @@ static int bq24190_set_mode_host(struct bq24190_dev_info *bdi)
bdi->watchdog = ((v & BQ24190_REG_CTTC_WATCHDOG_MASK) >>
BQ24190_REG_CTTC_WATCHDOG_SHIFT);
/*
* According to the "Host Mode and default Mode" section of the
* manual, a write to any register causes the bq24190 to switch
* from default mode to host mode. It will switch back to default
* mode after a WDT timeout unless the WDT is turned off as well.
* So, by simply turning off the WDT, we accomplish both with the
* same write.
*/
v &= ~BQ24190_REG_CTTC_WATCHDOG_MASK;
return bq24190_write(bdi, BQ24190_REG_CTTC, v);
ret = bq24190_write(bdi, BQ24190_REG_CTTC, v);
if (ret < 0)
return ret;
if (bdi->sys_min) {
v = bdi->sys_min / 100 - 30; // manual section 9.5.1.2, table 9
ret = bq24190_write_mask(bdi, BQ24190_REG_POC,
BQ24190_REG_POC_SYS_MIN_MASK,
BQ24190_REG_POC_SYS_MIN_SHIFT,
v);
if (ret < 0)
return ret;
}
if (bdi->iprechg) {
v = bdi->iprechg / 128 - 1; // manual section 9.5.1.4, table 11
ret = bq24190_write_mask(bdi, BQ24190_REG_PCTCC,
BQ24190_REG_PCTCC_IPRECHG_MASK,
BQ24190_REG_PCTCC_IPRECHG_SHIFT,
v);
if (ret < 0)
return ret;
}
if (bdi->iterm) {
v = bdi->iterm / 128 - 1; // manual section 9.5.1.4, table 11
ret = bq24190_write_mask(bdi, BQ24190_REG_PCTCC,
BQ24190_REG_PCTCC_ITERM_MASK,
BQ24190_REG_PCTCC_ITERM_SHIFT,
v);
if (ret < 0)
return ret;
}
return 0;
}
static int bq24190_register_reset(struct bq24190_dev_info *bdi)
@ -773,6 +926,38 @@ static int bq24190_charger_set_temp_alert_max(struct bq24190_dev_info *bdi,
return bq24190_battery_set_temp_alert_max(bdi, val);
}
static int bq24190_charger_get_precharge(struct bq24190_dev_info *bdi,
union power_supply_propval *val)
{
u8 v;
int ret;
ret = bq24190_read_mask(bdi, BQ24190_REG_PCTCC,
BQ24190_REG_PCTCC_IPRECHG_MASK,
BQ24190_REG_PCTCC_IPRECHG_SHIFT, &v);
if (ret < 0)
return ret;
val->intval = ++v * 128 * 1000;
return 0;
}
static int bq24190_charger_get_charge_term(struct bq24190_dev_info *bdi,
union power_supply_propval *val)
{
u8 v;
int ret;
ret = bq24190_read_mask(bdi, BQ24190_REG_PCTCC,
BQ24190_REG_PCTCC_ITERM_MASK,
BQ24190_REG_PCTCC_ITERM_SHIFT, &v);
if (ret < 0)
return ret;
val->intval = ++v * 128 * 1000;
return 0;
}
static int bq24190_charger_get_current(struct bq24190_dev_info *bdi,
union power_supply_propval *val)
{
@ -865,6 +1050,33 @@ static int bq24190_charger_set_voltage(struct bq24190_dev_info *bdi,
ARRAY_SIZE(bq24190_cvc_vreg_values), val->intval);
}
static int bq24190_charger_get_iinlimit(struct bq24190_dev_info *bdi,
union power_supply_propval *val)
{
int iinlimit, ret;
ret = bq24190_get_field_val(bdi, BQ24190_REG_ISC,
BQ24190_REG_ISC_IINLIM_MASK,
BQ24190_REG_ISC_IINLIM_SHIFT,
bq24190_isc_iinlim_values,
ARRAY_SIZE(bq24190_isc_iinlim_values), &iinlimit);
if (ret < 0)
return ret;
val->intval = iinlimit;
return 0;
}
static int bq24190_charger_set_iinlimit(struct bq24190_dev_info *bdi,
const union power_supply_propval *val)
{
return bq24190_set_field_val(bdi, BQ24190_REG_ISC,
BQ24190_REG_ISC_IINLIM_MASK,
BQ24190_REG_ISC_IINLIM_SHIFT,
bq24190_isc_iinlim_values,
ARRAY_SIZE(bq24190_isc_iinlim_values), val->intval);
}
static int bq24190_charger_get_property(struct power_supply *psy,
enum power_supply_property psp, union power_supply_propval *val)
{
@ -893,6 +1105,12 @@ static int bq24190_charger_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
ret = bq24190_charger_get_temp_alert_max(bdi, val);
break;
case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
ret = bq24190_charger_get_precharge(bdi, val);
break;
case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
ret = bq24190_charger_get_charge_term(bdi, val);
break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
ret = bq24190_charger_get_current(bdi, val);
break;
@ -905,6 +1123,9 @@ static int bq24190_charger_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
ret = bq24190_charger_get_voltage_max(bdi, val);
break;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
ret = bq24190_charger_get_iinlimit(bdi, val);
break;
case POWER_SUPPLY_PROP_SCOPE:
val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
ret = 0;
@ -956,6 +1177,9 @@ static int bq24190_charger_set_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
ret = bq24190_charger_set_voltage(bdi, val);
break;
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
ret = bq24190_charger_set_iinlimit(bdi, val);
break;
default:
ret = -EINVAL;
}
@ -977,6 +1201,7 @@ static int bq24190_charger_property_is_writeable(struct power_supply *psy,
case POWER_SUPPLY_PROP_CHARGE_TYPE:
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
ret = 1;
break;
default:
@ -986,16 +1211,45 @@ static int bq24190_charger_property_is_writeable(struct power_supply *psy,
return ret;
}
static void bq24190_input_current_limit_work(struct work_struct *work)
{
struct bq24190_dev_info *bdi =
container_of(work, struct bq24190_dev_info,
input_current_limit_work.work);
power_supply_set_input_current_limit_from_supplier(bdi->charger);
}
/* Sync the input-current-limit with our parent supply (if we have one) */
static void bq24190_charger_external_power_changed(struct power_supply *psy)
{
struct bq24190_dev_info *bdi = power_supply_get_drvdata(psy);
/*
* The Power-Good detection may take up to 220ms, sometimes
* the external charger detection is quicker, and the bq24190 will
* reset to iinlim based on its own charger detection (which is not
* hooked up when using external charger detection) resulting in a
* too low default 500mA iinlim. Delay setting the input-current-limit
* for 300ms to avoid this.
*/
queue_delayed_work(system_wq, &bdi->input_current_limit_work,
msecs_to_jiffies(300));
}
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_PRECHARGE_CURRENT,
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
POWER_SUPPLY_PROP_SCOPE,
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
@ -1013,6 +1267,7 @@ static const struct power_supply_desc bq24190_charger_desc = {
.get_property = bq24190_charger_get_property,
.set_property = bq24190_charger_set_property,
.property_is_writeable = bq24190_charger_property_is_writeable,
.external_power_changed = bq24190_charger_external_power_changed,
};
/* Battery power supply property routines */
@ -1460,13 +1715,50 @@ static int bq24190_hw_init(struct bq24190_dev_info *bdi)
if (ret < 0)
return ret;
ret = bq24190_set_mode_host(bdi);
ret = bq24190_set_config(bdi);
if (ret < 0)
return ret;
return bq24190_read(bdi, BQ24190_REG_SS, &bdi->ss_reg);
}
static int bq24190_get_config(struct bq24190_dev_info *bdi)
{
const char * const s = "ti,system-minimum-microvolt";
struct power_supply_battery_info info = {};
int v;
if (device_property_read_u32(bdi->dev, s, &v) == 0) {
v /= 1000;
if (v >= BQ24190_REG_POC_SYS_MIN_MIN
&& v <= BQ24190_REG_POC_SYS_MIN_MAX)
bdi->sys_min = v;
else
dev_warn(bdi->dev, "invalid value for %s: %u\n", s, v);
}
if (bdi->dev->of_node &&
!power_supply_get_battery_info(bdi->charger, &info)) {
v = info.precharge_current_ua / 1000;
if (v >= BQ24190_REG_PCTCC_IPRECHG_MIN
&& v <= BQ24190_REG_PCTCC_IPRECHG_MAX)
bdi->iprechg = v;
else
dev_warn(bdi->dev, "invalid value for battery:precharge-current-microamp: %d\n",
v);
v = info.charge_term_current_ua / 1000;
if (v >= BQ24190_REG_PCTCC_ITERM_MIN
&& v <= BQ24190_REG_PCTCC_ITERM_MAX)
bdi->iterm = v;
else
dev_warn(bdi->dev, "invalid value for battery:charge-term-current-microamp: %d\n",
v);
}
return 0;
}
static int bq24190_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@ -1494,10 +1786,12 @@ static int bq24190_probe(struct i2c_client *client,
mutex_init(&bdi->f_reg_lock);
bdi->f_reg = 0;
bdi->ss_reg = BQ24190_REG_SS_VBUS_STAT_MASK; /* impossible state */
INIT_DELAYED_WORK(&bdi->input_current_limit_work,
bq24190_input_current_limit_work);
i2c_set_clientdata(client, bdi);
if (!client->irq) {
if (client->irq <= 0) {
dev_err(dev, "Can't get irq info\n");
return -EINVAL;
}
@ -1530,13 +1824,8 @@ static int bq24190_probe(struct i2c_client *client,
goto out_pmrt;
}
ret = bq24190_hw_init(bdi);
if (ret < 0) {
dev_err(dev, "Hardware init failed\n");
goto out_pmrt;
}
charger_cfg.drv_data = bdi;
charger_cfg.of_node = dev->of_node;
charger_cfg.supplied_to = bq24190_charger_supplied_to;
charger_cfg.num_supplicants = ARRAY_SIZE(bq24190_charger_supplied_to),
bdi->charger = power_supply_register(dev, &bq24190_charger_desc,
@ -1560,8 +1849,20 @@ static int bq24190_probe(struct i2c_client *client,
}
}
ret = bq24190_get_config(bdi);
if (ret < 0) {
dev_err(dev, "Can't get devicetree config\n");
goto out_charger;
}
ret = bq24190_hw_init(bdi);
if (ret < 0) {
dev_err(dev, "Hardware init failed\n");
goto out_charger;
}
ret = bq24190_sysfs_create_group(bdi);
if (ret) {
if (ret < 0) {
dev_err(dev, "Can't create sysfs entries\n");
goto out_charger;
}
@ -1577,6 +1878,10 @@ static int bq24190_probe(struct i2c_client *client,
goto out_sysfs;
}
ret = bq24190_register_vbus_regulator(bdi);
if (ret < 0)
goto out_sysfs;
if (bdi->extcon) {
INIT_DELAYED_WORK(&bdi->extcon_work, bq24190_extcon_work);
bdi->extcon_nb.notifier_call = bq24190_extcon_event;
@ -1704,7 +2009,7 @@ static __maybe_unused int bq24190_pm_resume(struct device *dev)
}
bq24190_register_reset(bdi);
bq24190_set_mode_host(bdi);
bq24190_set_config(bdi);
bq24190_read(bdi, BQ24190_REG_SS, &bdi->ss_reg);
if (error >= 0) {
@ -1736,6 +2041,7 @@ MODULE_DEVICE_TABLE(i2c, bq24190_i2c_ids);
#ifdef CONFIG_OF
static const struct of_device_id bq24190_of_match[] = {
{ .compatible = "ti,bq24190", },
{ .compatible = "ti,bq24192i", },
{ },
};
MODULE_DEVICE_TABLE(of, bq24190_of_match);

View file

@ -58,8 +58,6 @@
#include <linux/power/bq27xxx_battery.h>
#define DRIVER_VERSION "1.2.0"
#define BQ27XXX_MANUFACTURER "Texas Instruments"
/* BQ27XXX Flags */
@ -132,8 +130,8 @@ enum bq27xxx_reg_index {
[BQ27XXX_DM_CKSUM] = 0x60
/* Register mappings */
static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
[BQ27000] = {
static u8
bq27000_regs[BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_CTRL] = 0x00,
[BQ27XXX_REG_TEMP] = 0x06,
[BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
@ -157,7 +155,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
[BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
[BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
},
[BQ27010] = {
bq27010_regs[BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_CTRL] = 0x00,
[BQ27XXX_REG_TEMP] = 0x06,
[BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
@ -181,7 +179,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
[BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
[BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
},
[BQ2750X] = {
bq2750x_regs[BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_CTRL] = 0x00,
[BQ27XXX_REG_TEMP] = 0x06,
[BQ27XXX_REG_INT_TEMP] = 0x28,
@ -201,7 +199,31 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
BQ27XXX_DM_REG_ROWS,
},
[BQ2751X] = {
#define bq2751x_regs bq27510g3_regs
#define bq2752x_regs bq27510g3_regs
bq27500_regs[BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_CTRL] = 0x00,
[BQ27XXX_REG_TEMP] = 0x06,
[BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
[BQ27XXX_REG_VOLT] = 0x08,
[BQ27XXX_REG_AI] = 0x14,
[BQ27XXX_REG_FLAGS] = 0x0a,
[BQ27XXX_REG_TTE] = 0x16,
[BQ27XXX_REG_TTF] = 0x18,
[BQ27XXX_REG_TTES] = 0x1c,
[BQ27XXX_REG_TTECP] = 0x26,
[BQ27XXX_REG_NAC] = 0x0c,
[BQ27XXX_REG_FCC] = 0x12,
[BQ27XXX_REG_CYCT] = 0x2a,
[BQ27XXX_REG_AE] = 0x22,
[BQ27XXX_REG_SOC] = 0x2c,
[BQ27XXX_REG_DCAP] = 0x3c,
[BQ27XXX_REG_AP] = 0x24,
BQ27XXX_DM_REG_ROWS,
},
#define bq27510g1_regs bq27500_regs
#define bq27510g2_regs bq27500_regs
bq27510g3_regs[BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_CTRL] = 0x00,
[BQ27XXX_REG_TEMP] = 0x06,
[BQ27XXX_REG_INT_TEMP] = 0x28,
@ -221,87 +243,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
BQ27XXX_DM_REG_ROWS,
},
[BQ27500] = {
[BQ27XXX_REG_CTRL] = 0x00,
[BQ27XXX_REG_TEMP] = 0x06,
[BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
[BQ27XXX_REG_VOLT] = 0x08,
[BQ27XXX_REG_AI] = 0x14,
[BQ27XXX_REG_FLAGS] = 0x0a,
[BQ27XXX_REG_TTE] = 0x16,
[BQ27XXX_REG_TTF] = 0x18,
[BQ27XXX_REG_TTES] = 0x1c,
[BQ27XXX_REG_TTECP] = 0x26,
[BQ27XXX_REG_NAC] = 0x0c,
[BQ27XXX_REG_FCC] = 0x12,
[BQ27XXX_REG_CYCT] = 0x2a,
[BQ27XXX_REG_AE] = 0x22,
[BQ27XXX_REG_SOC] = 0x2c,
[BQ27XXX_REG_DCAP] = 0x3c,
[BQ27XXX_REG_AP] = 0x24,
BQ27XXX_DM_REG_ROWS,
},
[BQ27510G1] = {
[BQ27XXX_REG_CTRL] = 0x00,
[BQ27XXX_REG_TEMP] = 0x06,
[BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
[BQ27XXX_REG_VOLT] = 0x08,
[BQ27XXX_REG_AI] = 0x14,
[BQ27XXX_REG_FLAGS] = 0x0a,
[BQ27XXX_REG_TTE] = 0x16,
[BQ27XXX_REG_TTF] = 0x18,
[BQ27XXX_REG_TTES] = 0x1c,
[BQ27XXX_REG_TTECP] = 0x26,
[BQ27XXX_REG_NAC] = 0x0c,
[BQ27XXX_REG_FCC] = 0x12,
[BQ27XXX_REG_CYCT] = 0x2a,
[BQ27XXX_REG_AE] = 0x22,
[BQ27XXX_REG_SOC] = 0x2c,
[BQ27XXX_REG_DCAP] = 0x3c,
[BQ27XXX_REG_AP] = 0x24,
BQ27XXX_DM_REG_ROWS,
},
[BQ27510G2] = {
[BQ27XXX_REG_CTRL] = 0x00,
[BQ27XXX_REG_TEMP] = 0x06,
[BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
[BQ27XXX_REG_VOLT] = 0x08,
[BQ27XXX_REG_AI] = 0x14,
[BQ27XXX_REG_FLAGS] = 0x0a,
[BQ27XXX_REG_TTE] = 0x16,
[BQ27XXX_REG_TTF] = 0x18,
[BQ27XXX_REG_TTES] = 0x1c,
[BQ27XXX_REG_TTECP] = 0x26,
[BQ27XXX_REG_NAC] = 0x0c,
[BQ27XXX_REG_FCC] = 0x12,
[BQ27XXX_REG_CYCT] = 0x2a,
[BQ27XXX_REG_AE] = 0x22,
[BQ27XXX_REG_SOC] = 0x2c,
[BQ27XXX_REG_DCAP] = 0x3c,
[BQ27XXX_REG_AP] = 0x24,
BQ27XXX_DM_REG_ROWS,
},
[BQ27510G3] = {
[BQ27XXX_REG_CTRL] = 0x00,
[BQ27XXX_REG_TEMP] = 0x06,
[BQ27XXX_REG_INT_TEMP] = 0x28,
[BQ27XXX_REG_VOLT] = 0x08,
[BQ27XXX_REG_AI] = 0x14,
[BQ27XXX_REG_FLAGS] = 0x0a,
[BQ27XXX_REG_TTE] = 0x16,
[BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
[BQ27XXX_REG_TTES] = 0x1a,
[BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
[BQ27XXX_REG_NAC] = 0x0c,
[BQ27XXX_REG_FCC] = 0x12,
[BQ27XXX_REG_CYCT] = 0x1e,
[BQ27XXX_REG_AE] = INVALID_REG_ADDR,
[BQ27XXX_REG_SOC] = 0x20,
[BQ27XXX_REG_DCAP] = 0x2e,
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
BQ27XXX_DM_REG_ROWS,
},
[BQ27520G1] = {
bq27520g1_regs[BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_CTRL] = 0x00,
[BQ27XXX_REG_TEMP] = 0x06,
[BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
@ -321,7 +263,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_AP] = 0x24,
BQ27XXX_DM_REG_ROWS,
},
[BQ27520G2] = {
bq27520g2_regs[BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_CTRL] = 0x00,
[BQ27XXX_REG_TEMP] = 0x06,
[BQ27XXX_REG_INT_TEMP] = 0x36,
@ -341,7 +283,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_AP] = 0x24,
BQ27XXX_DM_REG_ROWS,
},
[BQ27520G3] = {
bq27520g3_regs[BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_CTRL] = 0x00,
[BQ27XXX_REG_TEMP] = 0x06,
[BQ27XXX_REG_INT_TEMP] = 0x36,
@ -361,7 +303,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_AP] = 0x24,
BQ27XXX_DM_REG_ROWS,
},
[BQ27520G4] = {
bq27520g4_regs[BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_CTRL] = 0x00,
[BQ27XXX_REG_TEMP] = 0x06,
[BQ27XXX_REG_INT_TEMP] = 0x28,
@ -381,7 +323,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
BQ27XXX_DM_REG_ROWS,
},
[BQ27530] = {
bq27530_regs[BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_CTRL] = 0x00,
[BQ27XXX_REG_TEMP] = 0x06,
[BQ27XXX_REG_INT_TEMP] = 0x32,
@ -401,7 +343,8 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_AP] = 0x24,
BQ27XXX_DM_REG_ROWS,
},
[BQ27541] = {
#define bq27531_regs bq27530_regs
bq27541_regs[BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_CTRL] = 0x00,
[BQ27XXX_REG_TEMP] = 0x06,
[BQ27XXX_REG_INT_TEMP] = 0x28,
@ -421,7 +364,10 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_AP] = 0x24,
BQ27XXX_DM_REG_ROWS,
},
[BQ27545] = {
#define bq27542_regs bq27541_regs
#define bq27546_regs bq27541_regs
#define bq27742_regs bq27541_regs
bq27545_regs[BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_CTRL] = 0x00,
[BQ27XXX_REG_TEMP] = 0x06,
[BQ27XXX_REG_INT_TEMP] = 0x28,
@ -441,7 +387,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_AP] = 0x24,
BQ27XXX_DM_REG_ROWS,
},
[BQ27421] = {
bq27421_regs[BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_CTRL] = 0x00,
[BQ27XXX_REG_TEMP] = 0x02,
[BQ27XXX_REG_INT_TEMP] = 0x1e,
@ -460,10 +406,12 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_DCAP] = 0x3c,
[BQ27XXX_REG_AP] = 0x18,
BQ27XXX_DM_REG_ROWS,
},
};
};
#define bq27425_regs bq27421_regs
#define bq27441_regs bq27421_regs
#define bq27621_regs bq27421_regs
static enum power_supply_property bq27000_battery_props[] = {
static enum power_supply_property bq27000_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
@ -485,7 +433,7 @@ static enum power_supply_property bq27000_battery_props[] = {
POWER_SUPPLY_PROP_MANUFACTURER,
};
static enum power_supply_property bq27010_battery_props[] = {
static enum power_supply_property bq27010_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
@ -505,43 +453,11 @@ static enum power_supply_property bq27010_battery_props[] = {
POWER_SUPPLY_PROP_MANUFACTURER,
};
static enum power_supply_property bq2750x_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CHARGE_NOW,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_CYCLE_COUNT,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_MANUFACTURER,
};
#define bq2750x_props bq27510g3_props
#define bq2751x_props bq27510g3_props
#define bq2752x_props bq27510g3_props
static enum power_supply_property bq2751x_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CHARGE_NOW,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_CYCLE_COUNT,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_MANUFACTURER,
};
static enum power_supply_property bq27500_battery_props[] = {
static enum power_supply_property bq27500_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
@ -561,50 +477,10 @@ static enum power_supply_property bq27500_battery_props[] = {
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_MANUFACTURER,
};
#define bq27510g1_props bq27500_props
#define bq27510g2_props bq27500_props
static enum power_supply_property bq27510g1_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CHARGE_NOW,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_CYCLE_COUNT,
POWER_SUPPLY_PROP_ENERGY_NOW,
POWER_SUPPLY_PROP_POWER_AVG,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_MANUFACTURER,
};
static enum power_supply_property bq27510g2_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CHARGE_NOW,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_CYCLE_COUNT,
POWER_SUPPLY_PROP_ENERGY_NOW,
POWER_SUPPLY_PROP_POWER_AVG,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_MANUFACTURER,
};
static enum power_supply_property bq27510g3_battery_props[] = {
static enum power_supply_property bq27510g3_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
@ -622,7 +498,7 @@ static enum power_supply_property bq27510g3_battery_props[] = {
POWER_SUPPLY_PROP_MANUFACTURER,
};
static enum power_supply_property bq27520g1_battery_props[] = {
static enum power_supply_property bq27520g1_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
@ -642,28 +518,9 @@ static enum power_supply_property bq27520g1_battery_props[] = {
POWER_SUPPLY_PROP_MANUFACTURER,
};
static enum power_supply_property bq27520g2_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CHARGE_NOW,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_CYCLE_COUNT,
POWER_SUPPLY_PROP_ENERGY_NOW,
POWER_SUPPLY_PROP_POWER_AVG,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_MANUFACTURER,
};
#define bq27520g2_props bq27500_props
static enum power_supply_property bq27520g3_battery_props[] = {
static enum power_supply_property bq27520g3_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
@ -683,7 +540,7 @@ static enum power_supply_property bq27520g3_battery_props[] = {
POWER_SUPPLY_PROP_MANUFACTURER,
};
static enum power_supply_property bq27520g4_battery_props[] = {
static enum power_supply_property bq27520g4_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
@ -700,7 +557,7 @@ static enum power_supply_property bq27520g4_battery_props[] = {
POWER_SUPPLY_PROP_MANUFACTURER,
};
static enum power_supply_property bq27530_battery_props[] = {
static enum power_supply_property bq27530_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
@ -717,8 +574,9 @@ static enum power_supply_property bq27530_battery_props[] = {
POWER_SUPPLY_PROP_CYCLE_COUNT,
POWER_SUPPLY_PROP_MANUFACTURER,
};
#define bq27531_props bq27530_props
static enum power_supply_property bq27541_battery_props[] = {
static enum power_supply_property bq27541_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
@ -736,8 +594,11 @@ static enum power_supply_property bq27541_battery_props[] = {
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_MANUFACTURER,
};
#define bq27542_props bq27541_props
#define bq27546_props bq27541_props
#define bq27742_props bq27541_props
static enum power_supply_property bq27545_battery_props[] = {
static enum power_supply_property bq27545_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
@ -755,7 +616,7 @@ static enum power_supply_property bq27545_battery_props[] = {
POWER_SUPPLY_PROP_MANUFACTURER,
};
static enum power_supply_property bq27421_battery_props[] = {
static enum power_supply_property bq27421_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
@ -769,33 +630,138 @@ static enum power_supply_property bq27421_battery_props[] = {
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_MANUFACTURER,
};
#define bq27425_props bq27421_props
#define bq27441_props bq27421_props
#define bq27621_props bq27421_props
#define BQ27XXX_PROP(_id, _prop) \
[_id] = { \
.props = _prop, \
.size = ARRAY_SIZE(_prop), \
}
struct bq27xxx_dm_reg {
u8 subclass_id;
u8 offset;
u8 bytes;
u16 min, max;
};
enum bq27xxx_dm_reg_id {
BQ27XXX_DM_DESIGN_CAPACITY = 0,
BQ27XXX_DM_DESIGN_ENERGY,
BQ27XXX_DM_TERMINATE_VOLTAGE,
};
#define bq27000_dm_regs 0
#define bq27010_dm_regs 0
#define bq2750x_dm_regs 0
#define bq2751x_dm_regs 0
#define bq2752x_dm_regs 0
#if 0 /* not yet tested */
static struct bq27xxx_dm_reg bq27500_dm_regs[] = {
[BQ27XXX_DM_DESIGN_CAPACITY] = { 48, 10, 2, 0, 65535 },
[BQ27XXX_DM_DESIGN_ENERGY] = { }, /* missing on chip */
[BQ27XXX_DM_TERMINATE_VOLTAGE] = { 80, 48, 2, 1000, 32767 },
};
#else
#define bq27500_dm_regs 0
#endif
/* todo create data memory definitions from datasheets and test on chips */
#define bq27510g1_dm_regs 0
#define bq27510g2_dm_regs 0
#define bq27510g3_dm_regs 0
#define bq27520g1_dm_regs 0
#define bq27520g2_dm_regs 0
#define bq27520g3_dm_regs 0
#define bq27520g4_dm_regs 0
#define bq27530_dm_regs 0
#define bq27531_dm_regs 0
#define bq27541_dm_regs 0
#define bq27542_dm_regs 0
#define bq27546_dm_regs 0
#define bq27742_dm_regs 0
#if 0 /* not yet tested */
static struct bq27xxx_dm_reg bq27545_dm_regs[] = {
[BQ27XXX_DM_DESIGN_CAPACITY] = { 48, 23, 2, 0, 32767 },
[BQ27XXX_DM_DESIGN_ENERGY] = { 48, 25, 2, 0, 32767 },
[BQ27XXX_DM_TERMINATE_VOLTAGE] = { 80, 67, 2, 2800, 3700 },
};
#else
#define bq27545_dm_regs 0
#endif
static struct bq27xxx_dm_reg bq27421_dm_regs[] = {
[BQ27XXX_DM_DESIGN_CAPACITY] = { 82, 10, 2, 0, 8000 },
[BQ27XXX_DM_DESIGN_ENERGY] = { 82, 12, 2, 0, 32767 },
[BQ27XXX_DM_TERMINATE_VOLTAGE] = { 82, 16, 2, 2500, 3700 },
};
static struct bq27xxx_dm_reg bq27425_dm_regs[] = {
[BQ27XXX_DM_DESIGN_CAPACITY] = { 82, 12, 2, 0, 32767 },
[BQ27XXX_DM_DESIGN_ENERGY] = { 82, 14, 2, 0, 32767 },
[BQ27XXX_DM_TERMINATE_VOLTAGE] = { 82, 18, 2, 2800, 3700 },
};
#if 0 /* not yet tested */
#define bq27441_dm_regs bq27421_dm_regs
#else
#define bq27441_dm_regs 0
#endif
#if 0 /* not yet tested */
static struct bq27xxx_dm_reg bq27621_dm_regs[] = {
[BQ27XXX_DM_DESIGN_CAPACITY] = { 82, 3, 2, 0, 8000 },
[BQ27XXX_DM_DESIGN_ENERGY] = { 82, 5, 2, 0, 32767 },
[BQ27XXX_DM_TERMINATE_VOLTAGE] = { 82, 9, 2, 2500, 3700 },
};
#else
#define bq27621_dm_regs 0
#endif
#define BQ27XXX_O_ZERO 0x00000001
#define BQ27XXX_O_OTDC 0x00000002
#define BQ27XXX_O_UTOT 0x00000004
#define BQ27XXX_O_CFGUP 0x00000008
#define BQ27XXX_O_RAM 0x00000010
#define BQ27XXX_DATA(ref, key, opt) { \
.opts = (opt), \
.unseal_key = key, \
.regs = ref##_regs, \
.dm_regs = ref##_dm_regs, \
.props = ref##_props, \
.props_size = ARRAY_SIZE(ref##_props) }
static struct {
u32 opts;
u32 unseal_key;
u8 *regs;
struct bq27xxx_dm_reg *dm_regs;
enum power_supply_property *props;
size_t size;
} bq27xxx_battery_props[] = {
BQ27XXX_PROP(BQ27000, bq27000_battery_props),
BQ27XXX_PROP(BQ27010, bq27010_battery_props),
BQ27XXX_PROP(BQ2750X, bq2750x_battery_props),
BQ27XXX_PROP(BQ2751X, bq2751x_battery_props),
BQ27XXX_PROP(BQ27500, bq27500_battery_props),
BQ27XXX_PROP(BQ27510G1, bq27510g1_battery_props),
BQ27XXX_PROP(BQ27510G2, bq27510g2_battery_props),
BQ27XXX_PROP(BQ27510G3, bq27510g3_battery_props),
BQ27XXX_PROP(BQ27520G1, bq27520g1_battery_props),
BQ27XXX_PROP(BQ27520G2, bq27520g2_battery_props),
BQ27XXX_PROP(BQ27520G3, bq27520g3_battery_props),
BQ27XXX_PROP(BQ27520G4, bq27520g4_battery_props),
BQ27XXX_PROP(BQ27530, bq27530_battery_props),
BQ27XXX_PROP(BQ27541, bq27541_battery_props),
BQ27XXX_PROP(BQ27545, bq27545_battery_props),
BQ27XXX_PROP(BQ27421, bq27421_battery_props),
size_t props_size;
} bq27xxx_chip_data[] = {
[BQ27000] = BQ27XXX_DATA(bq27000, 0 , BQ27XXX_O_ZERO),
[BQ27010] = BQ27XXX_DATA(bq27010, 0 , BQ27XXX_O_ZERO),
[BQ2750X] = BQ27XXX_DATA(bq2750x, 0 , BQ27XXX_O_OTDC),
[BQ2751X] = BQ27XXX_DATA(bq2751x, 0 , BQ27XXX_O_OTDC),
[BQ2752X] = BQ27XXX_DATA(bq2752x, 0 , BQ27XXX_O_OTDC),
[BQ27500] = BQ27XXX_DATA(bq27500, 0x04143672, BQ27XXX_O_OTDC),
[BQ27510G1] = BQ27XXX_DATA(bq27510g1, 0 , BQ27XXX_O_OTDC),
[BQ27510G2] = BQ27XXX_DATA(bq27510g2, 0 , BQ27XXX_O_OTDC),
[BQ27510G3] = BQ27XXX_DATA(bq27510g3, 0 , BQ27XXX_O_OTDC),
[BQ27520G1] = BQ27XXX_DATA(bq27520g1, 0 , BQ27XXX_O_OTDC),
[BQ27520G2] = BQ27XXX_DATA(bq27520g2, 0 , BQ27XXX_O_OTDC),
[BQ27520G3] = BQ27XXX_DATA(bq27520g3, 0 , BQ27XXX_O_OTDC),
[BQ27520G4] = BQ27XXX_DATA(bq27520g4, 0 , BQ27XXX_O_OTDC),
[BQ27530] = BQ27XXX_DATA(bq27530, 0 , BQ27XXX_O_UTOT),
[BQ27531] = BQ27XXX_DATA(bq27531, 0 , BQ27XXX_O_UTOT),
[BQ27541] = BQ27XXX_DATA(bq27541, 0 , BQ27XXX_O_OTDC),
[BQ27542] = BQ27XXX_DATA(bq27542, 0 , BQ27XXX_O_OTDC),
[BQ27546] = BQ27XXX_DATA(bq27546, 0 , BQ27XXX_O_OTDC),
[BQ27742] = BQ27XXX_DATA(bq27742, 0 , BQ27XXX_O_OTDC),
[BQ27545] = BQ27XXX_DATA(bq27545, 0x04143672, BQ27XXX_O_OTDC),
[BQ27421] = BQ27XXX_DATA(bq27421, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM),
[BQ27425] = BQ27XXX_DATA(bq27425, 0x04143672, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP),
[BQ27441] = BQ27XXX_DATA(bq27441, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM),
[BQ27621] = BQ27XXX_DATA(bq27621, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM),
};
static DEFINE_MUTEX(bq27xxx_list_lock);
@ -805,13 +771,6 @@ static LIST_HEAD(bq27xxx_battery_devices);
#define BQ27XXX_DM_SZ 32
struct bq27xxx_dm_reg {
u8 subclass_id;
u8 offset;
u8 bytes;
u16 min, max;
};
/**
* struct bq27xxx_dm_buf - chip data memory buffer
* @class: data memory subclass_id
@ -844,12 +803,6 @@ static inline u16 *bq27xxx_dm_reg_ptr(struct bq27xxx_dm_buf *buf,
return NULL;
}
enum bq27xxx_dm_reg_id {
BQ27XXX_DM_DESIGN_CAPACITY = 0,
BQ27XXX_DM_DESIGN_ENERGY,
BQ27XXX_DM_TERMINATE_VOLTAGE,
};
static const char * const bq27xxx_dm_reg_name[] = {
[BQ27XXX_DM_DESIGN_CAPACITY] = "design-capacity",
[BQ27XXX_DM_DESIGN_ENERGY] = "design-energy",
@ -1092,9 +1045,9 @@ static void bq27xxx_battery_update_dm_block(struct bq27xxx_device_info *di,
}
#ifdef CONFIG_BATTERY_BQ27XXX_DT_UPDATES_NVM
if (!di->ram_chip && !bq27xxx_dt_to_nvm) {
if (!(di->opts & BQ27XXX_O_RAM) && !bq27xxx_dt_to_nvm) {
#else
if (!di->ram_chip) {
if (!(di->opts & BQ27XXX_O_RAM)) {
#endif
/* devicetree and NVM differ; defer to NVM */
dev_warn(di->dev, "%s has %u; update to %u disallowed "
@ -1130,7 +1083,7 @@ static int bq27xxx_battery_cfgupdate_priv(struct bq27xxx_device_info *di, bool a
return ret;
} while (!!(ret & BQ27XXX_FLAG_CFGUP) != active && --try);
if (!try) {
if (!try && di->chip != BQ27425) { // 425 has a bug
dev_err(di->dev, "timed out waiting for cfgupdate flag %d\n", active);
return -EINVAL;
}
@ -1162,7 +1115,7 @@ static inline int bq27xxx_battery_soft_reset(struct bq27xxx_device_info *di)
static int bq27xxx_battery_write_dm_block(struct bq27xxx_device_info *di,
struct bq27xxx_dm_buf *buf)
{
bool cfgup = di->chip == BQ27421; /* assume related chips need cfgupdate */
bool cfgup = di->opts & BQ27XXX_O_CFGUP;
int ret;
if (!buf->dirty)
@ -1261,7 +1214,7 @@ static void bq27xxx_battery_set_config(struct bq27xxx_device_info *di,
bq27xxx_battery_seal(di);
if (updated && di->chip != BQ27421) { /* not a cfgupdate chip, so reset */
if (updated && !(di->opts & BQ27XXX_O_CFGUP)) {
bq27xxx_write(di, BQ27XXX_REG_CTRL, BQ27XXX_RESET, false);
BQ27XXX_MSLEEP(300); /* reset time is not documented */
}
@ -1328,7 +1281,7 @@ static int bq27xxx_battery_read_soc(struct bq27xxx_device_info *di)
{
int soc;
if (di->chip == BQ27000 || di->chip == BQ27010)
if (di->opts & BQ27XXX_O_ZERO)
soc = bq27xxx_read(di, BQ27XXX_REG_SOC, true);
else
soc = bq27xxx_read(di, BQ27XXX_REG_SOC, false);
@ -1354,7 +1307,7 @@ static int bq27xxx_battery_read_charge(struct bq27xxx_device_info *di, u8 reg)
return charge;
}
if (di->chip == BQ27000 || di->chip == BQ27010)
if (di->opts & BQ27XXX_O_ZERO)
charge *= BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS;
else
charge *= 1000;
@ -1370,7 +1323,7 @@ static inline int bq27xxx_battery_read_nac(struct bq27xxx_device_info *di)
{
int flags;
if (di->chip == BQ27000 || di->chip == BQ27010) {
if (di->opts & BQ27XXX_O_ZERO) {
flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, true);
if (flags >= 0 && (flags & BQ27000_FLAG_CI))
return -ENODATA;
@ -1396,7 +1349,7 @@ static int bq27xxx_battery_read_dcap(struct bq27xxx_device_info *di)
{
int dcap;
if (di->chip == BQ27000 || di->chip == BQ27010)
if (di->opts & BQ27XXX_O_ZERO)
dcap = bq27xxx_read(di, BQ27XXX_REG_DCAP, true);
else
dcap = bq27xxx_read(di, BQ27XXX_REG_DCAP, false);
@ -1406,7 +1359,7 @@ static int bq27xxx_battery_read_dcap(struct bq27xxx_device_info *di)
return dcap;
}
if (di->chip == BQ27000 || di->chip == BQ27010)
if (di->opts & BQ27XXX_O_ZERO)
dcap = (dcap << 8) * BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS;
else
dcap *= 1000;
@ -1428,7 +1381,7 @@ static int bq27xxx_battery_read_energy(struct bq27xxx_device_info *di)
return ae;
}
if (di->chip == BQ27000 || di->chip == BQ27010)
if (di->opts & BQ27XXX_O_ZERO)
ae *= BQ27XXX_POWER_CONSTANT / BQ27XXX_RS;
else
ae *= 1000;
@ -1450,7 +1403,7 @@ static int bq27xxx_battery_read_temperature(struct bq27xxx_device_info *di)
return temp;
}
if (di->chip == BQ27000 || di->chip == BQ27010)
if (di->opts & BQ27XXX_O_ZERO)
temp = 5 * temp / 2;
return temp;
@ -1507,7 +1460,7 @@ static int bq27xxx_battery_read_pwr_avg(struct bq27xxx_device_info *di)
return tval;
}
if (di->chip == BQ27000 || di->chip == BQ27010)
if (di->opts & BQ27XXX_O_ZERO)
return (tval * BQ27XXX_POWER_CONSTANT) / BQ27XXX_RS;
else
return tval;
@ -1518,26 +1471,12 @@ static int bq27xxx_battery_read_pwr_avg(struct bq27xxx_device_info *di)
*/
static bool bq27xxx_battery_overtemp(struct bq27xxx_device_info *di, u16 flags)
{
switch (di->chip) {
case BQ2750X:
case BQ2751X:
case BQ27500:
case BQ27510G1:
case BQ27510G2:
case BQ27510G3:
case BQ27520G1:
case BQ27520G2:
case BQ27520G3:
case BQ27520G4:
case BQ27541:
case BQ27545:
if (di->opts & BQ27XXX_O_OTDC)
return flags & (BQ27XXX_FLAG_OTC | BQ27XXX_FLAG_OTD);
case BQ27530:
case BQ27421:
if (di->opts & BQ27XXX_O_UTOT)
return flags & BQ27XXX_FLAG_OT;
default:
return false;
}
return false;
}
/*
@ -1545,7 +1484,7 @@ static bool bq27xxx_battery_overtemp(struct bq27xxx_device_info *di, u16 flags)
*/
static bool bq27xxx_battery_undertemp(struct bq27xxx_device_info *di, u16 flags)
{
if (di->chip == BQ27530 || di->chip == BQ27421)
if (di->opts & BQ27XXX_O_UTOT)
return flags & BQ27XXX_FLAG_UT;
return false;
@ -1556,7 +1495,7 @@ static bool bq27xxx_battery_undertemp(struct bq27xxx_device_info *di, u16 flags)
*/
static bool bq27xxx_battery_dead(struct bq27xxx_device_info *di, u16 flags)
{
if (di->chip == BQ27000 || di->chip == BQ27010)
if (di->opts & BQ27XXX_O_ZERO)
return flags & (BQ27000_FLAG_EDV1 | BQ27000_FLAG_EDVF);
else
return flags & (BQ27XXX_FLAG_SOC1 | BQ27XXX_FLAG_SOCF);
@ -1569,7 +1508,7 @@ static bool bq27xxx_battery_dead(struct bq27xxx_device_info *di, u16 flags)
static int bq27xxx_battery_read_health(struct bq27xxx_device_info *di)
{
int flags;
bool has_singe_flag = di->chip == BQ27000 || di->chip == BQ27010;
bool has_singe_flag = di->opts & BQ27XXX_O_ZERO;
flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, has_singe_flag);
if (flags < 0) {
@ -1591,8 +1530,8 @@ static int bq27xxx_battery_read_health(struct bq27xxx_device_info *di)
void bq27xxx_battery_update(struct bq27xxx_device_info *di)
{
struct bq27xxx_reg_cache cache = {0, };
bool has_ci_flag = di->chip == BQ27000 || di->chip == BQ27010;
bool has_singe_flag = di->chip == BQ27000 || di->chip == BQ27010;
bool has_ci_flag = di->opts & BQ27XXX_O_ZERO;
bool has_singe_flag = di->opts & BQ27XXX_O_ZERO;
cache.flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, has_singe_flag);
if ((cache.flags & 0xff) == 0xff)
@ -1670,7 +1609,7 @@ static int bq27xxx_battery_current(struct bq27xxx_device_info *di,
return curr;
}
if (di->chip == BQ27000 || di->chip == BQ27010) {
if (di->opts & BQ27XXX_O_ZERO) {
flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, true);
if (flags & BQ27000_FLAG_CHGS) {
dev_dbg(di->dev, "negative current!\n");
@ -1691,7 +1630,7 @@ static int bq27xxx_battery_status(struct bq27xxx_device_info *di,
{
int status;
if (di->chip == BQ27000 || di->chip == BQ27010) {
if (di->opts & BQ27XXX_O_ZERO) {
if (di->cache.flags & BQ27000_FLAG_FC)
status = POWER_SUPPLY_STATUS_FULL;
else if (di->cache.flags & BQ27000_FLAG_CHGS)
@ -1719,7 +1658,7 @@ static int bq27xxx_battery_capacity_level(struct bq27xxx_device_info *di,
{
int level;
if (di->chip == BQ27000 || di->chip == BQ27010) {
if (di->opts & BQ27XXX_O_ZERO) {
if (di->cache.flags & BQ27000_FLAG_FC)
level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
else if (di->cache.flags & BQ27000_FLAG_EDV1)
@ -1884,7 +1823,11 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di)
INIT_DELAYED_WORK(&di->work, bq27xxx_battery_poll);
mutex_init(&di->lock);
di->regs = bq27xxx_regs[di->chip];
di->regs = bq27xxx_chip_data[di->chip].regs;
di->unseal_key = bq27xxx_chip_data[di->chip].unseal_key;
di->dm_regs = bq27xxx_chip_data[di->chip].dm_regs;
di->opts = bq27xxx_chip_data[di->chip].opts;
psy_desc = devm_kzalloc(di->dev, sizeof(*psy_desc), GFP_KERNEL);
if (!psy_desc)
@ -1892,8 +1835,8 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di)
psy_desc->name = di->name;
psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
psy_desc->properties = bq27xxx_battery_props[di->chip].props;
psy_desc->num_properties = bq27xxx_battery_props[di->chip].size;
psy_desc->properties = bq27xxx_chip_data[di->chip].props;
psy_desc->num_properties = bq27xxx_chip_data[di->chip].props_size;
psy_desc->get_property = bq27xxx_battery_get_property;
psy_desc->external_power_changed = bq27xxx_external_power_changed;
@ -1903,8 +1846,6 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di)
return PTR_ERR(di->bat);
}
dev_info(di->dev, "support ver. %s enabled\n", DRIVER_VERSION);
bq27xxx_battery_settings(di);
bq27xxx_battery_update(di);
@ -1938,110 +1879,6 @@ void bq27xxx_battery_teardown(struct bq27xxx_device_info *di)
}
EXPORT_SYMBOL_GPL(bq27xxx_battery_teardown);
static int bq27xxx_battery_platform_read(struct bq27xxx_device_info *di, u8 reg,
bool single)
{
struct device *dev = di->dev;
struct bq27xxx_platform_data *pdata = dev->platform_data;
unsigned int timeout = 3;
int upper, lower;
int temp;
if (!single) {
/* Make sure the value has not changed in between reading the
* lower and the upper part */
upper = pdata->read(dev, reg + 1);
do {
temp = upper;
if (upper < 0)
return upper;
lower = pdata->read(dev, reg);
if (lower < 0)
return lower;
upper = pdata->read(dev, reg + 1);
} while (temp != upper && --timeout);
if (timeout == 0)
return -EIO;
return (upper << 8) | lower;
}
return pdata->read(dev, reg);
}
static int bq27xxx_battery_platform_probe(struct platform_device *pdev)
{
struct bq27xxx_device_info *di;
struct bq27xxx_platform_data *pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "no platform_data supplied\n");
return -EINVAL;
}
if (!pdata->read) {
dev_err(&pdev->dev, "no hdq read callback supplied\n");
return -EINVAL;
}
if (!pdata->chip) {
dev_err(&pdev->dev, "no device supplied\n");
return -EINVAL;
}
di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
if (!di)
return -ENOMEM;
platform_set_drvdata(pdev, di);
di->dev = &pdev->dev;
di->chip = pdata->chip;
di->name = pdata->name ?: dev_name(&pdev->dev);
di->bus.read = bq27xxx_battery_platform_read;
return bq27xxx_battery_setup(di);
}
static int bq27xxx_battery_platform_remove(struct platform_device *pdev)
{
struct bq27xxx_device_info *di = platform_get_drvdata(pdev);
bq27xxx_battery_teardown(di);
return 0;
}
static const struct platform_device_id bq27xxx_battery_platform_id_table[] = {
{ "bq27000-battery", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, bq27xxx_battery_platform_id_table);
#ifdef CONFIG_OF
static const struct of_device_id bq27xxx_battery_platform_of_match_table[] = {
{ .compatible = "ti,bq27000" },
{},
};
MODULE_DEVICE_TABLE(of, bq27xxx_battery_platform_of_match_table);
#endif
static struct platform_driver bq27xxx_battery_platform_driver = {
.probe = bq27xxx_battery_platform_probe,
.remove = bq27xxx_battery_platform_remove,
.driver = {
.name = "bq27000-battery",
.of_match_table = of_match_ptr(bq27xxx_battery_platform_of_match_table),
},
.id_table = bq27xxx_battery_platform_id_table,
};
module_platform_driver(bq27xxx_battery_platform_driver);
MODULE_ALIAS("platform:bq27000-battery");
MODULE_AUTHOR("Rodolfo Giometti <[email protected]>");
MODULE_DESCRIPTION("BQ27xxx battery monitor driver");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,135 @@
/*
* BQ27xxx battery monitor HDQ/1-wire driver
*
* Copyright (C) 2007-2017 Texas Instruments Incorporated - http://www.ti.com/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/types.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/power/bq27xxx_battery.h>
#include <linux/w1.h>
#define W1_FAMILY_BQ27000 0x01
#define HDQ_CMD_READ (0 << 7)
#define HDQ_CMD_WRITE (1 << 7)
static int F_ID;
module_param(F_ID, int, S_IRUSR);
MODULE_PARM_DESC(F_ID, "1-wire slave FID for BQ27xxx device");
static int w1_bq27000_read(struct w1_slave *sl, unsigned int reg)
{
u8 val;
mutex_lock(&sl->master->bus_mutex);
w1_write_8(sl->master, HDQ_CMD_READ | reg);
val = w1_read_8(sl->master);
mutex_unlock(&sl->master->bus_mutex);
return val;
}
static int bq27xxx_battery_hdq_read(struct bq27xxx_device_info *di, u8 reg,
bool single)
{
struct w1_slave *sl = dev_to_w1_slave(di->dev);
unsigned int timeout = 3;
int upper, lower;
int temp;
if (!single) {
/*
* Make sure the value has not changed in between reading the
* lower and the upper part
*/
upper = w1_bq27000_read(sl, reg + 1);
do {
temp = upper;
if (upper < 0)
return upper;
lower = w1_bq27000_read(sl, reg);
if (lower < 0)
return lower;
upper = w1_bq27000_read(sl, reg + 1);
} while (temp != upper && --timeout);
if (timeout == 0)
return -EIO;
return (upper << 8) | lower;
}
return w1_bq27000_read(sl, reg);
}
static int bq27xxx_battery_hdq_add_slave(struct w1_slave *sl)
{
struct bq27xxx_device_info *di;
di = devm_kzalloc(&sl->dev, sizeof(*di), GFP_KERNEL);
if (!di)
return -ENOMEM;
dev_set_drvdata(&sl->dev, di);
di->dev = &sl->dev;
di->chip = BQ27000;
di->name = "bq27000-battery";
di->bus.read = bq27xxx_battery_hdq_read;
return bq27xxx_battery_setup(di);
}
static void bq27xxx_battery_hdq_remove_slave(struct w1_slave *sl)
{
struct bq27xxx_device_info *di = dev_get_drvdata(&sl->dev);
bq27xxx_battery_teardown(di);
}
static struct w1_family_ops bq27xxx_battery_hdq_fops = {
.add_slave = bq27xxx_battery_hdq_add_slave,
.remove_slave = bq27xxx_battery_hdq_remove_slave,
};
static struct w1_family bq27xxx_battery_hdq_family = {
.fid = W1_FAMILY_BQ27000,
.fops = &bq27xxx_battery_hdq_fops,
};
static int __init bq27xxx_battery_hdq_init(void)
{
if (F_ID)
bq27xxx_battery_hdq_family.fid = F_ID;
return w1_register_family(&bq27xxx_battery_hdq_family);
}
module_init(bq27xxx_battery_hdq_init);
static void __exit bq27xxx_battery_hdq_exit(void)
{
w1_unregister_family(&bq27xxx_battery_hdq_family);
}
module_exit(bq27xxx_battery_hdq_exit);
MODULE_AUTHOR("Texas Instruments Ltd");
MODULE_DESCRIPTION("BQ27xxx battery monitor HDQ/1-wire driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("w1-family-" __stringify(W1_FAMILY_BQ27000));

View file

@ -230,7 +230,7 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
{ "bq27210", BQ27010 },
{ "bq27500", BQ2750X },
{ "bq27510", BQ2751X },
{ "bq27520", BQ2751X },
{ "bq27520", BQ2752X },
{ "bq27500-1", BQ27500 },
{ "bq27510g1", BQ27510G1 },
{ "bq27510g2", BQ27510G2 },
@ -240,16 +240,16 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
{ "bq27520g3", BQ27520G3 },
{ "bq27520g4", BQ27520G4 },
{ "bq27530", BQ27530 },
{ "bq27531", BQ27530 },
{ "bq27531", BQ27531 },
{ "bq27541", BQ27541 },
{ "bq27542", BQ27541 },
{ "bq27546", BQ27541 },
{ "bq27742", BQ27541 },
{ "bq27542", BQ27542 },
{ "bq27546", BQ27546 },
{ "bq27742", BQ27742 },
{ "bq27545", BQ27545 },
{ "bq27421", BQ27421 },
{ "bq27425", BQ27421 },
{ "bq27441", BQ27421 },
{ "bq27621", BQ27421 },
{ "bq27425", BQ27425 },
{ "bq27441", BQ27441 },
{ "bq27621", BQ27621 },
{},
};
MODULE_DEVICE_TABLE(i2c, bq27xxx_i2c_id_table);

View file

@ -1632,8 +1632,7 @@ static int charger_manager_probe(struct platform_device *pdev)
return -ENODEV;
}
cm = devm_kzalloc(&pdev->dev,
sizeof(struct charger_manager), GFP_KERNEL);
cm = devm_kzalloc(&pdev->dev, sizeof(*cm), GFP_KERNEL);
if (!cm)
return -ENOMEM;
@ -1645,12 +1644,14 @@ static int charger_manager_probe(struct platform_device *pdev)
/* Initialize alarm timer */
if (alarmtimer_get_rtcdev()) {
cm_timer = devm_kzalloc(cm->dev, sizeof(*cm_timer), GFP_KERNEL);
if (!cm_timer)
return -ENOMEM;
alarm_init(cm_timer, ALARM_BOOTTIME, cm_timer_func);
}
/*
* The following two do not need to be errors.
* Users may intentionally ignore those two features.
* Some of the following do not need to be errors.
* Users may intentionally ignore those features.
*/
if (desc->fullbatt_uV == 0) {
dev_info(&pdev->dev, "Ignoring full-battery voltage threshold as it is not supplied\n");

View file

@ -663,7 +663,7 @@ static ssize_t ds2780_write_param_eeprom_bin(struct file *filp,
return count;
}
static struct bin_attribute ds2780_param_eeprom_bin_attr = {
static const struct bin_attribute ds2780_param_eeprom_bin_attr = {
.attr = {
.name = "param_eeprom",
.mode = S_IRUGO | S_IWUSR,
@ -708,7 +708,7 @@ static ssize_t ds2780_write_user_eeprom_bin(struct file *filp,
return count;
}
static struct bin_attribute ds2780_user_eeprom_bin_attr = {
static const struct bin_attribute ds2780_user_eeprom_bin_attr = {
.attr = {
.name = "user_eeprom",
.mode = S_IRUGO | S_IWUSR,

View file

@ -665,7 +665,7 @@ static ssize_t ds2781_write_param_eeprom_bin(struct file *filp,
return count;
}
static struct bin_attribute ds2781_param_eeprom_bin_attr = {
static const struct bin_attribute ds2781_param_eeprom_bin_attr = {
.attr = {
.name = "param_eeprom",
.mode = S_IRUGO | S_IWUSR,
@ -711,7 +711,7 @@ static ssize_t ds2781_write_user_eeprom_bin(struct file *filp,
return count;
}
static struct bin_attribute ds2781_user_eeprom_bin_attr = {
static const struct bin_attribute ds2781_user_eeprom_bin_attr = {
.attr = {
.name = "user_eeprom",
.mode = S_IRUGO | S_IWUSR,

View file

@ -626,7 +626,7 @@ static ssize_t lp8788_show_charger_status(struct device *dev,
{
struct lp8788_charger *pchg = dev_get_drvdata(dev);
enum lp8788_charging_state state;
char *desc[LP8788_MAX_CHG_STATE] = {
static const char * const desc[LP8788_MAX_CHG_STATE] = {
[LP8788_OFF] = "CHARGER OFF",
[LP8788_WARM_UP] = "WARM UP",
[LP8788_LOW_INPUT] = "LOW INPUT STATE",
@ -650,8 +650,10 @@ static ssize_t lp8788_show_eoc_time(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct lp8788_charger *pchg = dev_get_drvdata(dev);
char *stime[] = { "400ms", "5min", "10min", "15min",
"20min", "25min", "30min", "No timeout" };
static const char * const stime[] = {
"400ms", "5min", "10min", "15min",
"20min", "25min", "30min", "No timeout"
};
u8 val;
lp8788_read_byte(pchg->lp, LP8788_CHG_EOC, &val);
@ -665,9 +667,13 @@ static ssize_t lp8788_show_eoc_level(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct lp8788_charger *pchg = dev_get_drvdata(dev);
char *abs_level[] = { "25mA", "49mA", "75mA", "98mA" };
char *relative_level[] = { "5%", "10%", "15%", "20%" };
char *level;
static const char * const abs_level[] = {
"25mA", "49mA", "75mA", "98mA"
};
static const char * const relative_level[] = {
"5%", "10%", "15%", "20%"
};
const char *level;
u8 val;
u8 mode;

View file

@ -1,6 +1,6 @@
/*
* I2C client/driver for the Linear Technology LTC2941 and LTC2943
* Battery Gas Gauge IC
* I2C client/driver for the Linear Technology LTC2941, LTC2942, LTC2943
* and LTC2944 Battery Gas Gauge IC
*
* Copyright (C) 2014 Topic Embedded Systems
*
@ -34,35 +34,39 @@ enum ltc294x_reg {
LTC294X_REG_CONTROL = 0x01,
LTC294X_REG_ACC_CHARGE_MSB = 0x02,
LTC294X_REG_ACC_CHARGE_LSB = 0x03,
LTC294X_REG_THRESH_HIGH_MSB = 0x04,
LTC294X_REG_THRESH_HIGH_LSB = 0x05,
LTC294X_REG_THRESH_LOW_MSB = 0x06,
LTC294X_REG_THRESH_LOW_LSB = 0x07,
LTC294X_REG_VOLTAGE_MSB = 0x08,
LTC294X_REG_VOLTAGE_LSB = 0x09,
LTC294X_REG_CURRENT_MSB = 0x0E,
LTC294X_REG_CURRENT_LSB = 0x0F,
LTC294X_REG_TEMPERATURE_MSB = 0x14,
LTC294X_REG_TEMPERATURE_LSB = 0x15,
LTC294X_REG_VOLTAGE_MSB = 0x08,
LTC294X_REG_VOLTAGE_LSB = 0x09,
LTC2942_REG_TEMPERATURE_MSB = 0x0C,
LTC2942_REG_TEMPERATURE_LSB = 0x0D,
LTC2943_REG_CURRENT_MSB = 0x0E,
LTC2943_REG_CURRENT_LSB = 0x0F,
LTC2943_REG_TEMPERATURE_MSB = 0x14,
LTC2943_REG_TEMPERATURE_LSB = 0x15,
};
#define LTC2943_REG_CONTROL_MODE_MASK (BIT(7) | BIT(6))
#define LTC2943_REG_CONTROL_MODE_SCAN BIT(7)
enum ltc294x_id {
LTC2941_ID,
LTC2942_ID,
LTC2943_ID,
LTC2944_ID,
};
#define LTC2941_REG_STATUS_CHIP_ID BIT(7)
#define LTC2942_REG_CONTROL_MODE_SCAN (BIT(7) | BIT(6))
#define LTC2943_REG_CONTROL_MODE_SCAN BIT(7)
#define LTC294X_REG_CONTROL_PRESCALER_MASK (BIT(5) | BIT(4) | BIT(3))
#define LTC294X_REG_CONTROL_SHUTDOWN_MASK (BIT(0))
#define LTC294X_REG_CONTROL_PRESCALER_SET(x) \
((x << 3) & LTC294X_REG_CONTROL_PRESCALER_MASK)
#define LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED 0
#define LTC2941_NUM_REGS 0x08
#define LTC2943_NUM_REGS 0x18
struct ltc294x_info {
struct i2c_client *client; /* I2C Client pointer */
struct power_supply *supply; /* Supply pointer */
struct power_supply_desc supply_desc; /* Supply description */
struct delayed_work work; /* Work scheduler */
unsigned long num_regs; /* Number of registers (chip type) */
enum ltc294x_id id; /* Chip type */
int charge; /* Last charge register content */
int r_sense; /* mOhm */
int Qlsb; /* nAh */
@ -145,9 +149,18 @@ static int ltc294x_reset(const struct ltc294x_info *info, int prescaler_exp)
control = LTC294X_REG_CONTROL_PRESCALER_SET(prescaler_exp) |
LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED;
/* Put the 2943 into "monitor" mode, so it measures every 10 sec */
if (info->num_regs == LTC2943_NUM_REGS)
/* Put device into "monitor" mode */
switch (info->id) {
case LTC2942_ID: /* 2942 measures every 2 sec */
control |= LTC2942_REG_CONTROL_MODE_SCAN;
break;
case LTC2943_ID:
case LTC2944_ID: /* 2943 and 2944 measure every 10 sec */
control |= LTC2943_REG_CONTROL_MODE_SCAN;
break;
default:
break;
}
if (value != control) {
ret = ltc294x_write_regs(info->client,
@ -252,7 +265,24 @@ static int ltc294x_get_voltage(const struct ltc294x_info *info, int *val)
ret = ltc294x_read_regs(info->client,
LTC294X_REG_VOLTAGE_MSB, &datar[0], 2);
value = (datar[0] << 8) | datar[1];
*val = ((value * 23600) / 0xFFFF) * 1000; /* in uV */
switch (info->id) {
case LTC2943_ID:
value *= 23600 * 2;
value /= 0xFFFF;
value *= 1000 / 2;
break;
case LTC2944_ID:
value *= 70800 / 5*4;
value /= 0xFFFF;
value *= 1000 * 5/4;
break;
default:
value *= 6000 * 10;
value /= 0xFFFF;
value *= 1000 / 10;
break;
}
*val = value;
return ret;
}
@ -263,27 +293,38 @@ static int ltc294x_get_current(const struct ltc294x_info *info, int *val)
s32 value;
ret = ltc294x_read_regs(info->client,
LTC294X_REG_CURRENT_MSB, &datar[0], 2);
LTC2943_REG_CURRENT_MSB, &datar[0], 2);
value = (datar[0] << 8) | datar[1];
value -= 0x7FFF;
if (info->id == LTC2944_ID)
value *= 64000;
else
value *= 60000;
/* Value is in range -32k..+32k, r_sense is usually 10..50 mOhm,
* the formula below keeps everything in s32 range while preserving
* enough digits */
*val = 1000 * ((60000 * value) / (info->r_sense * 0x7FFF)); /* in uA */
*val = 1000 * (value / (info->r_sense * 0x7FFF)); /* in uA */
return ret;
}
static int ltc294x_get_temperature(const struct ltc294x_info *info, int *val)
{
enum ltc294x_reg reg;
int ret;
u8 datar[2];
u32 value;
ret = ltc294x_read_regs(info->client,
LTC294X_REG_TEMPERATURE_MSB, &datar[0], 2);
value = (datar[0] << 8) | datar[1];
/* Full-scale is 510 Kelvin, convert to centidegrees */
*val = (((51000 * value) / 0xFFFF) - 27215);
if (info->id == LTC2942_ID) {
reg = LTC2942_REG_TEMPERATURE_MSB;
value = 60000; /* Full-scale is 600 Kelvin */
} else {
reg = LTC2943_REG_TEMPERATURE_MSB;
value = 51000; /* Full-scale is 510 Kelvin */
}
ret = ltc294x_read_regs(info->client, reg, &datar[0], 2);
value *= (datar[0] << 8) | datar[1];
/* Convert to centidegrees */
*val = value / 0xFFFF - 27215;
return ret;
}
@ -357,8 +398,8 @@ static enum power_supply_property ltc294x_properties[] = {
POWER_SUPPLY_PROP_CHARGE_COUNTER,
POWER_SUPPLY_PROP_CHARGE_NOW,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_CURRENT_NOW,
};
static int ltc294x_i2c_remove(struct i2c_client *client)
@ -375,10 +416,11 @@ static int ltc294x_i2c_probe(struct i2c_client *client,
{
struct power_supply_config psy_cfg = {};
struct ltc294x_info *info;
struct device_node *np;
int ret;
u32 prescaler_exp;
s32 r_sense;
struct device_node *np;
u8 status;
info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
if (info == NULL)
@ -388,7 +430,7 @@ static int ltc294x_i2c_probe(struct i2c_client *client,
np = of_node_get(client->dev.of_node);
info->num_regs = (unsigned long)of_device_get_match_data(&client->dev);
info->id = (enum ltc294x_id)of_device_get_match_data(&client->dev);
info->supply_desc.name = np->name;
/* r_sense can be negative, when sense+ is connected to the battery
@ -409,7 +451,7 @@ static int ltc294x_i2c_probe(struct i2c_client *client,
prescaler_exp = LTC2941_MAX_PRESCALER_EXP;
}
if (info->num_regs == LTC2943_NUM_REGS) {
if (info->id == LTC2943_ID) {
if (prescaler_exp > LTC2943_MAX_PRESCALER_EXP)
prescaler_exp = LTC2943_MAX_PRESCALER_EXP;
info->Qlsb = ((340 * 50000) / r_sense) /
@ -421,21 +463,39 @@ static int ltc294x_i2c_probe(struct i2c_client *client,
(128 / (1 << prescaler_exp));
}
/* Read status register to check for LTC2942 */
if (info->id == LTC2941_ID || info->id == LTC2942_ID) {
ret = ltc294x_read_regs(client, LTC294X_REG_STATUS, &status, 1);
if (ret < 0) {
dev_err(&client->dev,
"Could not read status register\n");
return ret;
}
if (status & LTC2941_REG_STATUS_CHIP_ID)
info->id = LTC2941_ID;
else
info->id = LTC2942_ID;
}
info->client = client;
info->supply_desc.type = POWER_SUPPLY_TYPE_BATTERY;
info->supply_desc.properties = ltc294x_properties;
if (info->num_regs >= LTC294X_REG_TEMPERATURE_LSB)
switch (info->id) {
case LTC2944_ID:
case LTC2943_ID:
info->supply_desc.num_properties =
ARRAY_SIZE(ltc294x_properties);
else if (info->num_regs >= LTC294X_REG_CURRENT_LSB)
break;
case LTC2942_ID:
info->supply_desc.num_properties =
ARRAY_SIZE(ltc294x_properties) - 1;
else if (info->num_regs >= LTC294X_REG_VOLTAGE_LSB)
info->supply_desc.num_properties =
ARRAY_SIZE(ltc294x_properties) - 2;
else
break;
case LTC2941_ID:
default:
info->supply_desc.num_properties =
ARRAY_SIZE(ltc294x_properties) - 3;
break;
}
info->supply_desc.get_property = ltc294x_get_property;
info->supply_desc.set_property = ltc294x_set_property;
info->supply_desc.property_is_writeable = ltc294x_property_is_writeable;
@ -492,8 +552,10 @@ static SIMPLE_DEV_PM_OPS(ltc294x_pm_ops, ltc294x_suspend, ltc294x_resume);
static const struct i2c_device_id ltc294x_i2c_id[] = {
{"ltc2941", LTC2941_NUM_REGS},
{"ltc2943", LTC2943_NUM_REGS},
{ "ltc2941", LTC2941_ID, },
{ "ltc2942", LTC2942_ID, },
{ "ltc2943", LTC2943_ID, },
{ "ltc2944", LTC2944_ID, },
{ },
};
MODULE_DEVICE_TABLE(i2c, ltc294x_i2c_id);
@ -501,11 +563,19 @@ MODULE_DEVICE_TABLE(i2c, ltc294x_i2c_id);
static const struct of_device_id ltc294x_i2c_of_match[] = {
{
.compatible = "lltc,ltc2941",
.data = (void *)LTC2941_NUM_REGS
.data = (void *)LTC2941_ID,
},
{
.compatible = "lltc,ltc2942",
.data = (void *)LTC2942_ID,
},
{
.compatible = "lltc,ltc2943",
.data = (void *)LTC2943_NUM_REGS
.data = (void *)LTC2943_ID,
},
{
.compatible = "lltc,ltc2944",
.data = (void *)LTC2944_ID,
},
{ },
};
@ -525,5 +595,5 @@ module_i2c_driver(ltc294x_driver);
MODULE_AUTHOR("Auryn Verwegen, Topic Embedded Systems");
MODULE_AUTHOR("Mike Looijmans, Topic Embedded Products");
MODULE_DESCRIPTION("LTC2941/LTC2943 Battery Gas Gauge IC driver");
MODULE_DESCRIPTION("LTC2941/LTC2942/LTC2943/LTC2944 Battery Gas Gauge IC driver");
MODULE_LICENSE("GPL");

View file

@ -22,6 +22,7 @@
* This driver is based on max17040_battery.c
*/
#include <linux/acpi.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
@ -982,6 +983,8 @@ static int max17042_probe(struct i2c_client *client,
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
const struct power_supply_desc *max17042_desc = &max17042_psy_desc;
struct power_supply_config psy_cfg = {};
const struct acpi_device_id *acpi_id = NULL;
struct device *dev = &client->dev;
struct max17042_chip *chip;
int ret;
int i;
@ -995,7 +998,15 @@ static int max17042_probe(struct i2c_client *client,
return -ENOMEM;
chip->client = client;
chip->chip_type = id->driver_data;
if (id) {
chip->chip_type = id->driver_data;
} else {
acpi_id = acpi_match_device(dev->driver->acpi_match_table, dev);
if (!acpi_id)
return -ENODEV;
chip->chip_type = acpi_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");
@ -1039,11 +1050,18 @@ static int max17042_probe(struct i2c_client *client,
}
if (client->irq) {
unsigned int flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
/*
* On ACPI systems the IRQ may be handled by ACPI-event code,
* so we need to share (if the ACPI code is willing to share).
*/
if (acpi_id)
flags |= IRQF_SHARED | IRQF_PROBE_SHARED;
ret = devm_request_threaded_irq(&client->dev, client->irq,
NULL,
max17042_thread_handler,
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT,
max17042_thread_handler, flags,
chip->battery->desc->name,
chip);
if (!ret) {
@ -1053,10 +1071,13 @@ static int max17042_probe(struct i2c_client *client,
max17042_set_soc_threshold(chip, 1);
} else {
client->irq = 0;
dev_err(&client->dev, "%s(): cannot get IRQ\n",
__func__);
if (ret != -EBUSY)
dev_err(&client->dev, "Failed to get IRQ\n");
}
}
/* Not able to update the charge threshold when exceeded? -> disable */
if (!client->irq)
regmap_write(chip->regmap, MAX17042_SALRT_Th, 0xff00);
regmap_read(chip->regmap, MAX17042_STATUS, &val);
if (val & STATUS_POR_BIT) {
@ -1104,6 +1125,14 @@ static int max17042_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(max17042_pm_ops, max17042_suspend,
max17042_resume);
#ifdef CONFIG_ACPI
static const struct acpi_device_id max17042_acpi_match[] = {
{ "MAX17047", MAXIM_DEVICE_TYPE_MAX17047 },
{ }
};
MODULE_DEVICE_TABLE(acpi, max17042_acpi_match);
#endif
#ifdef CONFIG_OF
static const struct of_device_id max17042_dt_match[] = {
{ .compatible = "maxim,max17042" },
@ -1125,6 +1154,7 @@ MODULE_DEVICE_TABLE(i2c, max17042_id);
static struct i2c_driver max17042_i2c_driver = {
.driver = {
.name = "max17042",
.acpi_match_table = ACPI_PTR(max17042_acpi_match),
.of_match_table = of_match_ptr(max17042_dt_match),
.pm = &max17042_pm_ops,
},

View file

@ -0,0 +1,448 @@
/*
* 1-Wire implementation for Maxim Semiconductor
* MAX7211/MAX17215 stanalone fuel gauge chip
*
* Copyright (C) 2017 Radioavionica Corporation
* Author: Alex A. Mihaylov <minimumlaw@rambler.ru>
*
* Use consistent with the GNU GPL is permitted,
* provided that this copyright notice is
* preserved in its entirety in all copies and derived works.
*
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/w1.h>
#include <linux/regmap.h>
#include <linux/power_supply.h>
#define W1_MAX1721X_FAMILY_ID 0x26
#define DEF_DEV_NAME_MAX17211 "MAX17211"
#define DEF_DEV_NAME_MAX17215 "MAX17215"
#define DEF_DEV_NAME_UNKNOWN "UNKNOWN"
#define DEF_MFG_NAME "MAXIM"
#define PSY_MAX_NAME_LEN 32
/* Number of valid register addresses in W1 mode */
#define MAX1721X_MAX_REG_NR 0x1EF
/* Factory settings (nonvilatile registers) (W1 specific) */
#define MAX1721X_REG_NRSENSE 0x1CF /* RSense in 10^-5 Ohm */
/* Strings */
#define MAX1721X_REG_MFG_STR 0x1CC
#define MAX1721X_REG_MFG_NUMB 3
#define MAX1721X_REG_DEV_STR 0x1DB
#define MAX1721X_REG_DEV_NUMB 5
/* HEX Strings */
#define MAX1721X_REG_SER_HEX 0x1D8
/* MAX172XX Output Registers for W1 chips */
#define MAX172XX_REG_STATUS 0x000 /* status reg */
#define MAX172XX_BAT_PRESENT (1<<4) /* battery connected bit */
#define MAX172XX_REG_DEVNAME 0x021 /* chip config */
#define MAX172XX_DEV_MASK 0x000F /* chip type mask */
#define MAX172X1_DEV 0x0001
#define MAX172X5_DEV 0x0005
#define MAX172XX_REG_TEMP 0x008 /* Temperature */
#define MAX172XX_REG_BATT 0x0DA /* Battery voltage */
#define MAX172XX_REG_CURRENT 0x00A /* Actual current */
#define MAX172XX_REG_AVGCURRENT 0x00B /* Average current */
#define MAX172XX_REG_REPSOC 0x006 /* Percentage of charge */
#define MAX172XX_REG_DESIGNCAP 0x018 /* Design capacity */
#define MAX172XX_REG_REPCAP 0x005 /* Average capacity */
#define MAX172XX_REG_TTE 0x011 /* Time to empty */
#define MAX172XX_REG_TTF 0x020 /* Time to full */
struct max17211_device_info {
char name[PSY_MAX_NAME_LEN];
struct power_supply *bat;
struct power_supply_desc bat_desc;
struct device *w1_dev;
struct regmap *regmap;
/* battery design format */
unsigned int rsense; /* in tenths uOhm */
char DeviceName[2 * MAX1721X_REG_DEV_NUMB + 1];
char ManufacturerName[2 * MAX1721X_REG_MFG_NUMB + 1];
char SerialNumber[13]; /* see get_sn_str() later for comment */
};
/* Convert regs value to power_supply units */
static inline int max172xx_time_to_ps(unsigned int reg)
{
return reg * 5625 / 1000; /* in sec. */
}
static inline int max172xx_percent_to_ps(unsigned int reg)
{
return reg / 256; /* in percent from 0 to 100 */
}
static inline int max172xx_voltage_to_ps(unsigned int reg)
{
return reg * 1250; /* in uV */
}
static inline int max172xx_capacity_to_ps(unsigned int reg)
{
return reg * 500; /* in uAh */
}
/*
* Current and temperature is signed values, so unsigned regs
* value must be converted to signed type
*/
static inline int max172xx_temperature_to_ps(unsigned int reg)
{
int val = (int16_t)(reg);
return val * 10 / 256; /* in tenths of deg. C */
}
/*
* Calculating current registers resolution:
*
* RSense stored in 10^-5 Ohm, so mesaurment voltage must be
* in 10^-11 Volts for get current in uA.
* 16 bit current reg fullscale +/-51.2mV is 102400 uV.
* So: 102400 / 65535 * 10^5 = 156252
*/
static inline int max172xx_current_to_voltage(unsigned int reg)
{
int val = (int16_t)(reg);
return val * 156252;
}
static inline struct max17211_device_info *
to_device_info(struct power_supply *psy)
{
return power_supply_get_drvdata(psy);
}
static int max1721x_battery_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct max17211_device_info *info = to_device_info(psy);
unsigned int reg = 0;
int ret = 0;
switch (psp) {
case POWER_SUPPLY_PROP_PRESENT:
/*
* POWER_SUPPLY_PROP_PRESENT will always readable via
* sysfs interface. Value return 0 if battery not
* present or unaccesable via W1.
*/
val->intval =
regmap_read(info->regmap, MAX172XX_REG_STATUS,
&reg) ? 0 : !(reg & MAX172XX_BAT_PRESENT);
break;
case POWER_SUPPLY_PROP_CAPACITY:
ret = regmap_read(info->regmap, MAX172XX_REG_REPSOC, &reg);
val->intval = max172xx_percent_to_ps(reg);
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
ret = regmap_read(info->regmap, MAX172XX_REG_BATT, &reg);
val->intval = max172xx_voltage_to_ps(reg);
break;
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
ret = regmap_read(info->regmap, MAX172XX_REG_DESIGNCAP, &reg);
val->intval = max172xx_capacity_to_ps(reg);
break;
case POWER_SUPPLY_PROP_CHARGE_AVG:
ret = regmap_read(info->regmap, MAX172XX_REG_REPCAP, &reg);
val->intval = max172xx_capacity_to_ps(reg);
break;
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
ret = regmap_read(info->regmap, MAX172XX_REG_TTE, &reg);
val->intval = max172xx_time_to_ps(reg);
break;
case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
ret = regmap_read(info->regmap, MAX172XX_REG_TTF, &reg);
val->intval = max172xx_time_to_ps(reg);
break;
case POWER_SUPPLY_PROP_TEMP:
ret = regmap_read(info->regmap, MAX172XX_REG_TEMP, &reg);
val->intval = max172xx_temperature_to_ps(reg);
break;
/* We need signed current, so must cast info->rsense to signed type */
case POWER_SUPPLY_PROP_CURRENT_NOW:
ret = regmap_read(info->regmap, MAX172XX_REG_CURRENT, &reg);
val->intval =
max172xx_current_to_voltage(reg) / (int)info->rsense;
break;
case POWER_SUPPLY_PROP_CURRENT_AVG:
ret = regmap_read(info->regmap, MAX172XX_REG_AVGCURRENT, &reg);
val->intval =
max172xx_current_to_voltage(reg) / (int)info->rsense;
break;
/*
* Strings already received and inited by probe.
* We do dummy read for check battery still available.
*/
case POWER_SUPPLY_PROP_MODEL_NAME:
ret = regmap_read(info->regmap, MAX1721X_REG_DEV_STR, &reg);
val->strval = info->DeviceName;
break;
case POWER_SUPPLY_PROP_MANUFACTURER:
ret = regmap_read(info->regmap, MAX1721X_REG_MFG_STR, &reg);
val->strval = info->ManufacturerName;
break;
case POWER_SUPPLY_PROP_SERIAL_NUMBER:
ret = regmap_read(info->regmap, MAX1721X_REG_SER_HEX, &reg);
val->strval = info->SerialNumber;
break;
default:
ret = -EINVAL;
}
return ret;
}
static enum power_supply_property max1721x_battery_props[] = {
/* int */
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_CHARGE_AVG,
POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CURRENT_AVG,
/* strings */
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
POWER_SUPPLY_PROP_SERIAL_NUMBER,
};
static int get_string(struct max17211_device_info *info,
uint16_t reg, uint8_t nr, char *str)
{
unsigned int val;
if (!str || !(reg == MAX1721X_REG_MFG_STR ||
reg == MAX1721X_REG_DEV_STR))
return -EFAULT;
while (nr--) {
if (regmap_read(info->regmap, reg++, &val))
return -EFAULT;
*str++ = val>>8 & 0x00FF;
*str++ = val & 0x00FF;
}
return 0;
}
/* Maxim say: Serial number is a hex string up to 12 hex characters */
static int get_sn_string(struct max17211_device_info *info, char *str)
{
unsigned int val[3];
if (!str)
return -EFAULT;
if (regmap_read(info->regmap, MAX1721X_REG_SER_HEX, &val[0]))
return -EFAULT;
if (regmap_read(info->regmap, MAX1721X_REG_SER_HEX + 1, &val[1]))
return -EFAULT;
if (regmap_read(info->regmap, MAX1721X_REG_SER_HEX + 2, &val[2]))
return -EFAULT;
snprintf(str, 13, "%04X%04X%04X", val[0], val[1], val[2]);
return 0;
}
/*
* MAX1721x registers description for w1-regmap
*/
static const struct regmap_range max1721x_allow_range[] = {
regmap_reg_range(0, 0xDF), /* volatile data */
regmap_reg_range(0x180, 0x1DF), /* non-volatile memory */
regmap_reg_range(0x1E0, 0x1EF), /* non-volatile history (unused) */
};
static const struct regmap_range max1721x_deny_range[] = {
/* volatile data unused registers */
regmap_reg_range(0x24, 0x26),
regmap_reg_range(0x30, 0x31),
regmap_reg_range(0x33, 0x34),
regmap_reg_range(0x37, 0x37),
regmap_reg_range(0x3B, 0x3C),
regmap_reg_range(0x40, 0x41),
regmap_reg_range(0x43, 0x44),
regmap_reg_range(0x47, 0x49),
regmap_reg_range(0x4B, 0x4C),
regmap_reg_range(0x4E, 0xAF),
regmap_reg_range(0xB1, 0xB3),
regmap_reg_range(0xB5, 0xB7),
regmap_reg_range(0xBF, 0xD0),
regmap_reg_range(0xDB, 0xDB),
/* hole between volatile and non-volatile registers */
regmap_reg_range(0xE0, 0x17F),
};
static const struct regmap_access_table max1721x_regs = {
.yes_ranges = max1721x_allow_range,
.n_yes_ranges = ARRAY_SIZE(max1721x_allow_range),
.no_ranges = max1721x_deny_range,
.n_no_ranges = ARRAY_SIZE(max1721x_deny_range),
};
/*
* Model Gauge M5 Algorithm output register
* Volatile data (must not be cached)
*/
static const struct regmap_range max1721x_volatile_allow[] = {
regmap_reg_range(0, 0xDF),
};
static const struct regmap_access_table max1721x_volatile_regs = {
.yes_ranges = max1721x_volatile_allow,
.n_yes_ranges = ARRAY_SIZE(max1721x_volatile_allow),
};
/*
* W1-regmap config
*/
static const struct regmap_config max1721x_regmap_w1_config = {
.reg_bits = 16,
.val_bits = 16,
.rd_table = &max1721x_regs,
.volatile_table = &max1721x_volatile_regs,
.max_register = MAX1721X_MAX_REG_NR,
};
static int devm_w1_max1721x_add_device(struct w1_slave *sl)
{
struct power_supply_config psy_cfg = {};
struct max17211_device_info *info;
info = devm_kzalloc(&sl->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
sl->family_data = (void *)info;
info->w1_dev = &sl->dev;
/*
* power_supply class battery name translated from W1 slave device
* unical ID (look like 26-0123456789AB) to "max1721x-0123456789AB\0"
* so, 26 (device family) correcpondent to max1721x devices.
* Device name still unical for any numbers connected devices.
*/
snprintf(info->name, sizeof(info->name),
"max1721x-%012X", (unsigned int)sl->reg_num.id);
info->bat_desc.name = info->name;
/*
* FixMe: battery device name exceed max len for thermal_zone device
* name and translation to thermal_zone must be disabled.
*/
info->bat_desc.no_thermal = true;
info->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY;
info->bat_desc.properties = max1721x_battery_props;
info->bat_desc.num_properties = ARRAY_SIZE(max1721x_battery_props);
info->bat_desc.get_property = max1721x_battery_get_property;
psy_cfg.drv_data = info;
/* regmap init */
info->regmap = devm_regmap_init_w1(info->w1_dev,
&max1721x_regmap_w1_config);
if (IS_ERR(info->regmap)) {
int err = PTR_ERR(info->regmap);
dev_err(info->w1_dev, "Failed to allocate register map: %d\n",
err);
return err;
}
/* rsense init */
info->rsense = 0;
if (regmap_read(info->regmap, MAX1721X_REG_NRSENSE, &info->rsense)) {
dev_err(info->w1_dev, "Can't read RSense. Hardware error.\n");
return -ENODEV;
}
if (!info->rsense) {
dev_warn(info->w1_dev, "RSenese not calibrated, set 10 mOhms!\n");
info->rsense = 1000; /* in regs in 10^-5 */
}
dev_info(info->w1_dev, "RSense: %d mOhms.\n", info->rsense / 100);
if (get_string(info, MAX1721X_REG_MFG_STR,
MAX1721X_REG_MFG_NUMB, info->ManufacturerName)) {
dev_err(info->w1_dev, "Can't read manufacturer. Hardware error.\n");
return -ENODEV;
}
if (!info->ManufacturerName[0])
strncpy(info->ManufacturerName, DEF_MFG_NAME,
2 * MAX1721X_REG_MFG_NUMB);
if (get_string(info, MAX1721X_REG_DEV_STR,
MAX1721X_REG_DEV_NUMB, info->DeviceName)) {
dev_err(info->w1_dev, "Can't read device. Hardware error.\n");
return -ENODEV;
}
if (!info->DeviceName[0]) {
unsigned int dev_name;
if (regmap_read(info->regmap,
MAX172XX_REG_DEVNAME, &dev_name)) {
dev_err(info->w1_dev, "Can't read device name reg.\n");
return -ENODEV;
}
switch (dev_name & MAX172XX_DEV_MASK) {
case MAX172X1_DEV:
strncpy(info->DeviceName, DEF_DEV_NAME_MAX17211,
2 * MAX1721X_REG_DEV_NUMB);
break;
case MAX172X5_DEV:
strncpy(info->DeviceName, DEF_DEV_NAME_MAX17215,
2 * MAX1721X_REG_DEV_NUMB);
break;
default:
strncpy(info->DeviceName, DEF_DEV_NAME_UNKNOWN,
2 * MAX1721X_REG_DEV_NUMB);
}
}
if (get_sn_string(info, info->SerialNumber)) {
dev_err(info->w1_dev, "Can't read serial. Hardware error.\n");
return -ENODEV;
}
info->bat = devm_power_supply_register(&sl->dev, &info->bat_desc,
&psy_cfg);
if (IS_ERR(info->bat)) {
dev_err(info->w1_dev, "failed to register battery\n");
return PTR_ERR(info->bat);
}
return 0;
}
static struct w1_family_ops w1_max1721x_fops = {
.add_slave = devm_w1_max1721x_add_device,
};
static struct w1_family w1_max1721x_family = {
.fid = W1_MAX1721X_FAMILY_ID,
.fops = &w1_max1721x_fops,
};
module_w1_family(w1_max1721x_family);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alex A. Mihaylov <[email protected]>");
MODULE_DESCRIPTION("Maxim MAX17211/MAX17215 Fuel Gauage IC driver");
MODULE_ALIAS("w1-family-" __stringify(W1_MAX1721X_FAMILY_ID));

View file

@ -535,7 +535,7 @@ static ssize_t olpc_bat_eeprom_read(struct file *filp, struct kobject *kobj,
return count;
}
static struct bin_attribute olpc_bat_eeprom = {
static const struct bin_attribute olpc_bat_eeprom = {
.attr = {
.name = "eeprom",
.mode = S_IRUGO,
@ -559,7 +559,7 @@ static ssize_t olpc_bat_error_read(struct device *dev,
return sprintf(buf, "%d\n", ec_byte);
}
static struct device_attribute olpc_bat_error = {
static const struct device_attribute olpc_bat_error = {
.attr = {
.name = "error",
.mode = S_IRUGO,

View file

@ -254,7 +254,7 @@ static struct attribute *pcf50633_mbc_sysfs_entries[] = {
NULL,
};
static struct attribute_group mbc_attr_group = {
static const struct attribute_group mbc_attr_group = {
.name = NULL, /* put in device directory */
.attrs = pcf50633_mbc_sysfs_entries,
};

View file

@ -259,18 +259,14 @@ static int power_supply_check_supplies(struct power_supply *psy)
/* All supplies found, allocate char ** array for filling */
psy->supplied_from = devm_kzalloc(&psy->dev, sizeof(psy->supplied_from),
GFP_KERNEL);
if (!psy->supplied_from) {
dev_err(&psy->dev, "Couldn't allocate memory for supply list\n");
if (!psy->supplied_from)
return -ENOMEM;
}
*psy->supplied_from = devm_kzalloc(&psy->dev,
sizeof(char *) * (cnt - 1),
GFP_KERNEL);
if (!*psy->supplied_from) {
dev_err(&psy->dev, "Couldn't allocate memory for supply list\n");
if (!*psy->supplied_from)
return -ENOMEM;
}
return power_supply_populate_supplied_from(psy);
}
@ -314,11 +310,12 @@ static int __power_supply_am_i_supplied(struct device *dev, void *_data)
struct power_supply *epsy = dev_get_drvdata(dev);
struct psy_am_i_supplied_data *data = _data;
data->count++;
if (__power_supply_is_supplied_by(epsy, data->psy))
if (__power_supply_is_supplied_by(epsy, data->psy)) {
data->count++;
if (!epsy->desc->get_property(epsy, POWER_SUPPLY_PROP_ONLINE,
&ret))
return ret.intval;
}
return 0;
}
@ -374,6 +371,47 @@ int power_supply_is_system_supplied(void)
}
EXPORT_SYMBOL_GPL(power_supply_is_system_supplied);
static int __power_supply_get_supplier_max_current(struct device *dev,
void *data)
{
union power_supply_propval ret = {0,};
struct power_supply *epsy = dev_get_drvdata(dev);
struct power_supply *psy = data;
if (__power_supply_is_supplied_by(epsy, psy))
if (!epsy->desc->get_property(epsy,
POWER_SUPPLY_PROP_CURRENT_MAX,
&ret))
return ret.intval;
return 0;
}
int power_supply_set_input_current_limit_from_supplier(struct power_supply *psy)
{
union power_supply_propval val = {0,};
int curr;
if (!psy->desc->set_property)
return -EINVAL;
/*
* This function is not intended for use with a supply with multiple
* suppliers, we simply pick the first supply to report a non 0
* max-current.
*/
curr = class_for_each_device(power_supply_class, NULL, psy,
__power_supply_get_supplier_max_current);
if (curr <= 0)
return (curr == 0) ? -ENODEV : curr;
val.intval = curr;
return psy->desc->set_property(psy,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val);
}
EXPORT_SYMBOL_GPL(power_supply_set_input_current_limit_from_supplier);
int power_supply_set_battery_charged(struct power_supply *psy)
{
if (atomic_read(&psy->use_cnt) >= 0 &&

View file

@ -12,25 +12,21 @@
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/power_supply.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/stat.h>
#include <linux/power/sbs-battery.h>
#include <linux/power_supply.h>
#include <linux/slab.h>
#include <linux/stat.h>
enum {
REG_MANUFACTURER_DATA,
@ -60,8 +56,8 @@ enum {
#define BATTERY_MODE_OFFSET 0x03
#define BATTERY_MODE_MASK 0x8000
enum sbs_battery_mode {
BATTERY_MODE_AMPS,
BATTERY_MODE_WATTS
BATTERY_MODE_AMPS = 0,
BATTERY_MODE_WATTS = 0x8000
};
/* manufacturer access defines */
@ -532,6 +528,8 @@ static enum sbs_battery_mode sbs_set_battery_mode(struct i2c_client *client,
if (ret < 0)
return ret;
usleep_range(1000, 2000);
return original_val & BATTERY_MODE_MASK;
}