From f722e17fdb2c97bbec2563636dd88489cdb1428b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen <lars@metafoo.de> Date: Sat, 8 Jan 2011 19:12:26 +0100 Subject: power_supply: Ignore -ENODATA errors when generating uevents Sometimes a driver can not report a meaningful value for a certain property and returns -ENODATA. Currently when generating a uevent and a property return -ENODATA it is treated as an error an no uevent is generated at all. This is not an desirable behavior. This patch adds a special case for -ENODATA and ignores properties which return this error code when generating the uevent. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Tested-by: Grazvydas Ignotas <notasas@gmail.com> --- drivers/power/power_supply_sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index cd1f90754a3a..605514afc29f 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -270,7 +270,7 @@ int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env) attr = &power_supply_attrs[psy->properties[j]]; ret = power_supply_show_property(dev, attr, prop_buf); - if (ret == -ENODEV) { + if (ret == -ENODEV || ret == -ENODATA) { /* When a battery is absent, we expect -ENODEV. Don't abort; send the uevent with at least the the PRESENT=0 property */ ret = 0; -- cgit v1.2.3 From 97774672573ac4355bd12cf84b202555c1131b69 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen <lars@metafoo.de> Date: Mon, 21 Feb 2011 15:34:19 +0100 Subject: power_supply: Initialize changed_work before calling device_add Calling device_add causes an inital uevent for that device to be generated. The power_supply uevent function calls the drivers get_property function, which might causes the driver to update its state, which again might causes the driver to call power_supply_changed(). Since the power_supplys changed_work has not been initialized at this point the behavior is undefined and can result in an OOPS. This patch fixes the issue by initializing the power_supplys changed_work prior to adding the power_supplys device to the device tree. Reported-by: Grazvydas Ignotas <notasas@gmail.com> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Tested-by: Grazvydas Ignotas <notasas@gmail.com> --- drivers/power/power_supply_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 970f7335d3a7..329b46b2327d 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -171,6 +171,8 @@ int power_supply_register(struct device *parent, struct power_supply *psy) dev_set_drvdata(dev, psy); psy->dev = dev; + INIT_WORK(&psy->changed_work, power_supply_changed_work); + rc = kobject_set_name(&dev->kobj, "%s", psy->name); if (rc) goto kobject_set_name_failed; @@ -179,8 +181,6 @@ int power_supply_register(struct device *parent, struct power_supply *psy) if (rc) goto device_add_failed; - INIT_WORK(&psy->changed_work, power_supply_changed_work); - rc = power_supply_create_triggers(psy); if (rc) goto create_triggers_failed; -- cgit v1.2.3 From 5661f334e8a53848552c8763f3bd63d0672b2e7b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen <lars@metafoo.de> Date: Mon, 24 May 2010 20:36:52 +0200 Subject: bq27x00: Add type property This patch adds the type property to the bq27x00 battery driver. All bq27x00 are lithium ion batteries. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Acked-by: Rodolfo Giometti <giometti@linux.it> Tested-by: Grazvydas Ignotas <notasas@gmail.com> --- drivers/power/bq27x00_battery.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index eff0273d4030..bb043f93cead 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -78,6 +78,7 @@ static enum power_supply_property bq27x00_battery_props[] = { POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, + POWER_SUPPLY_PROP_TECHNOLOGY, }; /* @@ -277,6 +278,9 @@ static int bq27x00_battery_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: ret = bq27x00_battery_time(di, BQ27x00_REG_TTF, val); break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; default: return -EINVAL; } -- cgit v1.2.3 From 0e9f30497e88600ba52013665270f543bb94f0d8 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen <lars@metafoo.de> Date: Mon, 24 May 2010 20:20:57 +0200 Subject: bq27x00: Improve temperature property precession This patch improves the precession of the temperature property of the bq27x00 driver. By dividing before multiplying the current code effectively cuts of the last decimal digit. This patch fixes it by multiplying before dividing. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Acked-by: Rodolfo Giometti <giometti@linux.it> Tested-by: Grazvydas Ignotas <notasas@gmail.com> --- drivers/power/bq27x00_battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index bb043f93cead..4f7465999c86 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -109,7 +109,7 @@ static int bq27x00_battery_temperature(struct bq27x00_device_info *di) if (di->chip == BQ27500) return temp - 2731; else - return ((temp >> 2) - 273) * 10; + return ((temp * 5) - 5463) / 2; } /* -- cgit v1.2.3 From a2e5118c370e78c3841606e7edc7677c986325fa Mon Sep 17 00:00:00 2001 From: Pali Rohár <pali.rohar@gmail.com> Date: Mon, 24 May 2010 20:52:13 +0200 Subject: bq27x00: Fix CURRENT_NOW property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the bq27000 datasheet the current should be calculated by the following formula: current = AI * 3570 / 20 This patch adjust the drivers code accordingly. Signed-off-by: Pali Rohár <pali.rohar@gmail.com> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Acked-by: Rodolfo Giometti <giometti@linux.it> Tested-by: Grazvydas Ignotas <notasas@gmail.com> --- drivers/power/bq27x00_battery.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 4f7465999c86..1b06134e0ae8 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -44,6 +44,8 @@ #define BQ27500_FLAG_DSC BIT(0) #define BQ27500_FLAG_FC BIT(9) +#define BQ27000_RS 20 /* Resistor sense */ + /* If the system has several batteries we need a different name for each * of them... */ @@ -149,7 +151,7 @@ static int bq27x00_battery_current(struct bq27x00_device_info *di) if (di->chip == BQ27500) { /* bq27500 returns signed value */ - curr = (int)(s16)curr; + curr = (int)((s16)curr) * 1000; } else { ret = bq27x00_read(BQ27x00_REG_FLAGS, &flags, 0, di); if (ret < 0) { @@ -160,9 +162,10 @@ static int bq27x00_battery_current(struct bq27x00_device_info *di) dev_dbg(di->dev, "negative current!\n"); curr = -curr; } + curr = curr * 3570 / BQ27000_RS; } - return curr * 1000; + return curr; } /* -- cgit v1.2.3 From 3413b4ea250ff0b4142a154a2764e33979957e8b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen <lars@metafoo.de> Date: Mon, 24 May 2010 21:57:33 +0200 Subject: bq27x00: Return -ENODEV for properties if the battery is not present This patch changes get_property callback of the bq27x00 battery to return -ENODEV for properties other then the PROP_PRESENT if the battery is not present. The power subsystem core expects a driver to behave that way. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Acked-by: Rodolfo Giometti <giometti@linux.it> Tested-by: Grazvydas Ignotas <notasas@gmail.com> --- drivers/power/bq27x00_battery.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 1b06134e0ae8..9f16666dc466 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -252,16 +252,21 @@ static int bq27x00_battery_get_property(struct power_supply *psy, { int ret = 0; struct bq27x00_device_info *di = to_bq27x00_device_info(psy); + int voltage = bq27x00_battery_voltage(di); + + if (psp != POWER_SUPPLY_PROP_PRESENT && voltage <= 0) + return -ENODEV; switch (psp) { case POWER_SUPPLY_PROP_STATUS: ret = bq27x00_battery_status(di, val); break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = voltage; + break; case POWER_SUPPLY_PROP_PRESENT: - val->intval = bq27x00_battery_voltage(di); if (psp == POWER_SUPPLY_PROP_PRESENT) - val->intval = val->intval <= 0 ? 0 : 1; + val->intval = voltage <= 0 ? 0 : 1; break; case POWER_SUPPLY_PROP_CURRENT_NOW: val->intval = bq27x00_battery_current(di); -- cgit v1.2.3 From a40402ef0bf3b6e53a7dfd396e9487eb2f613e19 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen <lars@metafoo.de> Date: Mon, 24 May 2010 19:37:58 +0200 Subject: bq27x00: Prepare code for addition of bq27000 platform driver This patch simplifies the drivers data structure and moves code to be shared by the bq27000 and bq27200/bq27500 init functions into a common function. This patch has no functional changes, it only moves code around. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Acked-by: Rodolfo Giometti <giometti@linux.it> Tested-by: Grazvydas Ignotas <notasas@gmail.com> --- drivers/power/bq27x00_battery.c | 86 +++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 47 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 9f16666dc466..def951d8fc2b 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -54,8 +54,8 @@ static DEFINE_MUTEX(battery_mutex); struct bq27x00_device_info; struct bq27x00_access_methods { - int (*read)(u8 reg, int *rt_value, int b_single, - struct bq27x00_device_info *di); + int (*read)(struct bq27x00_device_info *, u8 reg, int *rt_value, + bool single); }; enum bq27x00_chip { BQ27000, BQ27500 }; @@ -63,11 +63,11 @@ enum bq27x00_chip { BQ27000, BQ27500 }; struct bq27x00_device_info { struct device *dev; int id; - struct bq27x00_access_methods *bus; - struct power_supply bat; enum bq27x00_chip chip; - struct i2c_client *client; + struct power_supply bat; + + struct bq27x00_access_methods bus; }; static enum power_supply_property bq27x00_battery_props[] = { @@ -87,10 +87,10 @@ static enum power_supply_property bq27x00_battery_props[] = { * Common code for BQ27x00 devices */ -static int bq27x00_read(u8 reg, int *rt_value, int b_single, - struct bq27x00_device_info *di) +static inline int bq27x00_read(struct bq27x00_device_info *di, u8 reg, + int *rt_value, bool single) { - return di->bus->read(reg, rt_value, b_single, di); + return di->bus.read(di, reg, rt_value, single); } /* @@ -102,7 +102,7 @@ static int bq27x00_battery_temperature(struct bq27x00_device_info *di) int ret; int temp = 0; - ret = bq27x00_read(BQ27x00_REG_TEMP, &temp, 0, di); + ret = bq27x00_read(di, BQ27x00_REG_TEMP, &temp, false); if (ret) { dev_err(di->dev, "error reading temperature\n"); return ret; @@ -123,7 +123,7 @@ static int bq27x00_battery_voltage(struct bq27x00_device_info *di) int ret; int volt = 0; - ret = bq27x00_read(BQ27x00_REG_VOLT, &volt, 0, di); + ret = bq27x00_read(di, BQ27x00_REG_VOLT, &volt, false); if (ret) { dev_err(di->dev, "error reading voltage\n"); return ret; @@ -143,7 +143,7 @@ static int bq27x00_battery_current(struct bq27x00_device_info *di) int curr = 0; int flags = 0; - ret = bq27x00_read(BQ27x00_REG_AI, &curr, 0, di); + ret = bq27x00_read(di, BQ27x00_REG_AI, &curr, false); if (ret) { dev_err(di->dev, "error reading current\n"); return 0; @@ -153,7 +153,7 @@ static int bq27x00_battery_current(struct bq27x00_device_info *di) /* bq27500 returns signed value */ curr = (int)((s16)curr) * 1000; } else { - ret = bq27x00_read(BQ27x00_REG_FLAGS, &flags, 0, di); + ret = bq27x00_read(di, BQ27x00_REG_FLAGS, &flags, false); if (ret < 0) { dev_err(di->dev, "error reading flags\n"); return 0; @@ -178,9 +178,9 @@ static int bq27x00_battery_rsoc(struct bq27x00_device_info *di) int rsoc = 0; if (di->chip == BQ27500) - ret = bq27x00_read(BQ27500_REG_SOC, &rsoc, 0, di); + ret = bq27x00_read(di, BQ27500_REG_SOC, &rsoc, false); else - ret = bq27x00_read(BQ27000_REG_RSOC, &rsoc, 1, di); + ret = bq27x00_read(di, BQ27000_REG_RSOC, &rsoc, true); if (ret) { dev_err(di->dev, "error reading relative State-of-Charge\n"); return ret; @@ -196,7 +196,7 @@ static int bq27x00_battery_status(struct bq27x00_device_info *di, int status; int ret; - ret = bq27x00_read(BQ27x00_REG_FLAGS, &flags, 0, di); + ret = bq27x00_read(di, BQ27x00_REG_FLAGS, &flags, false); if (ret < 0) { dev_err(di->dev, "error reading flags\n"); return ret; @@ -230,7 +230,7 @@ static int bq27x00_battery_time(struct bq27x00_device_info *di, int reg, int tval = 0; int ret; - ret = bq27x00_read(reg, &tval, 0, di); + ret = bq27x00_read(di, reg, &tval, false); if (ret) { dev_err(di->dev, "error reading register %02x\n", reg); return ret; @@ -296,23 +296,35 @@ static int bq27x00_battery_get_property(struct power_supply *psy, return ret; } -static void bq27x00_powersupply_init(struct bq27x00_device_info *di) +static int bq27x00_powersupply_init(struct bq27x00_device_info *di) { + int ret; + di->bat.type = POWER_SUPPLY_TYPE_BATTERY; di->bat.properties = bq27x00_battery_props; di->bat.num_properties = ARRAY_SIZE(bq27x00_battery_props); di->bat.get_property = bq27x00_battery_get_property; di->bat.external_power_changed = NULL; + + ret = power_supply_register(di->dev, &di->bat); + if (ret) { + dev_err(di->dev, "failed to register battery: %d\n", ret); + return ret; + } + + dev_info(di->dev, "support ver. %s enabled\n", DRIVER_VERSION); + + return 0; } /* * i2c specific code */ -static int bq27x00_read_i2c(u8 reg, int *rt_value, int b_single, - struct bq27x00_device_info *di) +static int bq27x00_read_i2c(struct bq27x00_device_info *di, u8 reg, + int *rt_value, bool single) { - struct i2c_client *client = di->client; + struct i2c_client *client = to_i2c_client(di->dev); struct i2c_msg msg[1]; unsigned char data[2]; int err; @@ -329,7 +341,7 @@ static int bq27x00_read_i2c(u8 reg, int *rt_value, int b_single, err = i2c_transfer(client->adapter, msg, 1); if (err >= 0) { - if (!b_single) + if (!single) msg->len = 2; else msg->len = 1; @@ -337,7 +349,7 @@ static int bq27x00_read_i2c(u8 reg, int *rt_value, int b_single, msg->flags = I2C_M_RD; err = i2c_transfer(client->adapter, msg, 1); if (err >= 0) { - if (!b_single) + if (!single) *rt_value = get_unaligned_le16(data); else *rt_value = data[0]; @@ -353,7 +365,6 @@ static int bq27x00_battery_probe(struct i2c_client *client, { char *name; struct bq27x00_device_info *di; - struct bq27x00_access_methods *bus; int num; int retval = 0; @@ -380,38 +391,20 @@ static int bq27x00_battery_probe(struct i2c_client *client, retval = -ENOMEM; goto batt_failed_2; } + di->id = num; + di->dev = &client->dev; di->chip = id->driver_data; + di->bat.name = name; + di->bus.read = &bq27x00_read_i2c; - bus = kzalloc(sizeof(*bus), GFP_KERNEL); - if (!bus) { - dev_err(&client->dev, "failed to allocate access method " - "data\n"); - retval = -ENOMEM; + if (bq27x00_powersupply_init(di)) goto batt_failed_3; - } i2c_set_clientdata(client, di); - di->dev = &client->dev; - di->bat.name = name; - bus->read = &bq27x00_read_i2c; - di->bus = bus; - di->client = client; - - bq27x00_powersupply_init(di); - - retval = power_supply_register(&client->dev, &di->bat); - if (retval) { - dev_err(&client->dev, "failed to register battery\n"); - goto batt_failed_4; - } - - dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION); return 0; -batt_failed_4: - kfree(bus); batt_failed_3: kfree(di); batt_failed_2: @@ -430,7 +423,6 @@ static int bq27x00_battery_remove(struct i2c_client *client) power_supply_unregister(&di->bat); - kfree(di->bus); kfree(di->bat.name); mutex_lock(&battery_mutex); -- cgit v1.2.3 From 7fb7ba588c0f276609609565b21fcc853284a9a0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen <lars@metafoo.de> Date: Mon, 24 May 2010 19:55:27 +0200 Subject: bq27x00: Add bq27000 support This patch adds support for the bq27000 battery to the bq27x00 driver. The bq27000 is similar to the bq27200 except that it uses the HDQ bus instead of I2C to communicate with the host system. The driver is implemented as a platform driver. The driver expects to be provided with a read callback function through its platform data. The read function is assumed to do the lowlevel HDQ handling and read out the value of a certain register. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Tested-by: Grazvydas Ignotas <notasas@gmail.com> --- drivers/power/Kconfig | 14 +++ drivers/power/bq27x00_battery.c | 184 +++++++++++++++++++++++++++++++--- include/linux/power/bq27x00_battery.h | 19 ++++ 3 files changed, 202 insertions(+), 15 deletions(-) create mode 100644 include/linux/power/bq27x00_battery.h (limited to 'drivers/power') diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 61bf5d724139..52a462fc6b84 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -117,10 +117,24 @@ config BATTERY_BQ20Z75 config BATTERY_BQ27x00 tristate "BQ27x00 battery driver" + help + Say Y here to enable support for batteries with BQ27x00 (I2C/HDQ) chips. + +config BATTERY_BQ27X00_I2C + bool "BQ27200/BQ27500 support" + depends on BATTERY_BQ27x00 depends on I2C + default y help Say Y here to enable support for batteries with BQ27x00 (I2C) chips. +config BATTERY_BQ27X00_PLATFORM + bool "BQ27000 support" + depends on BATTERY_BQ27x00 + default y + help + Say Y here to enable support for batteries with BQ27000 (HDQ) chips. + config BATTERY_DA9030 tristate "DA9030 battery driver" depends on PMIC_DA903X diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index def951d8fc2b..44bc76be0780 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -3,6 +3,7 @@ * * Copyright (C) 2008 Rodolfo Giometti <giometti@linux.it> * Copyright (C) 2008 Eurotech S.p.A. <info@eurotech.it> + * Copyright (C) 2010-2011 Lars-Peter Clausen <lars@metafoo.de> * * Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc. * @@ -27,6 +28,8 @@ #include <linux/slab.h> #include <asm/unaligned.h> +#include <linux/power/bq27x00_battery.h> + #define DRIVER_VERSION "1.1.0" #define BQ27x00_REG_TEMP 0x06 @@ -46,12 +49,6 @@ #define BQ27000_RS 20 /* Resistor sense */ -/* If the system has several batteries we need a different name for each - * of them... - */ -static DEFINE_IDR(battery_id); -static DEFINE_MUTEX(battery_mutex); - struct bq27x00_device_info; struct bq27x00_access_methods { int (*read)(struct bq27x00_device_info *, u8 reg, int *rt_value, @@ -317,9 +314,15 @@ static int bq27x00_powersupply_init(struct bq27x00_device_info *di) return 0; } -/* - * i2c specific code + +/* i2c specific code */ +#ifdef CONFIG_BATTERY_BQ27X00_I2C + +/* If the system has several batteries we need a different name for each + * of them... */ +static DEFINE_IDR(battery_id); +static DEFINE_MUTEX(battery_mutex); static int bq27x00_read_i2c(struct bq27x00_device_info *di, u8 reg, int *rt_value, bool single) @@ -434,10 +437,6 @@ static int bq27x00_battery_remove(struct i2c_client *client) return 0; } -/* - * Module stuff - */ - static const struct i2c_device_id bq27x00_id[] = { { "bq27200", BQ27000 }, /* bq27200 is same as bq27000, but with i2c */ { "bq27500", BQ27500 }, @@ -453,13 +452,167 @@ static struct i2c_driver bq27x00_battery_driver = { .id_table = bq27x00_id, }; +static inline int bq27x00_battery_i2c_init(void) +{ + int ret = i2c_add_driver(&bq27x00_battery_driver); + if (ret) + printk(KERN_ERR "Unable to register BQ27x00 i2c driver\n"); + + return ret; +} + +static inline void bq27x00_battery_i2c_exit(void) +{ + i2c_del_driver(&bq27x00_battery_driver); +} + +#else + +static inline int bq27x00_battery_i2c_init(void) { return 0; } +static inline void bq27x00_battery_i2c_exit(void) {}; + +#endif + +/* platform specific code */ +#ifdef CONFIG_BATTERY_BQ27X00_PLATFORM + +static int bq27000_read_platform(struct bq27x00_device_info *di, u8 reg, + int *rt_value, bool single) +{ + struct device *dev = di->dev; + struct bq27000_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; + + *rt_value = (upper << 8) | lower; + } else { + lower = pdata->read(dev, reg); + if (lower < 0) + return lower; + *rt_value = lower; + } + return 0; +} + +static int __devinit bq27000_battery_probe(struct platform_device *pdev) +{ + struct bq27x00_device_info *di; + struct bq27000_platform_data *pdata = pdev->dev.platform_data; + int ret; + + 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; + } + + di = kzalloc(sizeof(*di), GFP_KERNEL); + if (!di) { + dev_err(&pdev->dev, "failed to allocate device info data\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, di); + + di->dev = &pdev->dev; + di->chip = BQ27000; + + di->bat.name = pdata->name ?: dev_name(&pdev->dev); + di->bus.read = &bq27000_read_platform; + + ret = bq27x00_powersupply_init(di); + if (ret) + goto err_free; + + return 0; + +err_free: + platform_set_drvdata(pdev, NULL); + kfree(di); + + return ret; +} + +static int __devexit bq27000_battery_remove(struct platform_device *pdev) +{ + struct bq27x00_device_info *di = platform_get_drvdata(pdev); + + power_supply_unregister(&di->bat); + platform_set_drvdata(pdev, NULL); + kfree(di); + + return 0; +} + +static struct platform_driver bq27000_battery_driver = { + .probe = bq27000_battery_probe, + .remove = __devexit_p(bq27000_battery_remove), + .driver = { + .name = "bq27000-battery", + .owner = THIS_MODULE, + }, +}; + +static inline int bq27x00_battery_platform_init(void) +{ + int ret = platform_driver_register(&bq27000_battery_driver); + if (ret) + printk(KERN_ERR "Unable to register BQ27000 platform driver\n"); + + return ret; +} + +static inline void bq27x00_battery_platform_exit(void) +{ + platform_driver_unregister(&bq27000_battery_driver); +} + +#else + +static inline int bq27x00_battery_platform_init(void) { return 0; } +static inline void bq27x00_battery_platform_exit(void) {}; + +#endif + +/* + * Module stuff + */ + static int __init bq27x00_battery_init(void) { int ret; - ret = i2c_add_driver(&bq27x00_battery_driver); + ret = bq27x00_battery_i2c_init(); + if (ret) + return ret; + + ret = bq27x00_battery_platform_init(); if (ret) - printk(KERN_ERR "Unable to register BQ27x00 driver\n"); + bq27x00_battery_i2c_exit(); return ret; } @@ -467,7 +620,8 @@ module_init(bq27x00_battery_init); static void __exit bq27x00_battery_exit(void) { - i2c_del_driver(&bq27x00_battery_driver); + bq27x00_battery_platform_exit(); + bq27x00_battery_i2c_exit(); } module_exit(bq27x00_battery_exit); diff --git a/include/linux/power/bq27x00_battery.h b/include/linux/power/bq27x00_battery.h new file mode 100644 index 000000000000..a857f719bf40 --- /dev/null +++ b/include/linux/power/bq27x00_battery.h @@ -0,0 +1,19 @@ +#ifndef __LINUX_BQ27X00_BATTERY_H__ +#define __LINUX_BQ27X00_BATTERY_H__ + +/** + * struct bq27000_plaform_data - Platform data for bq27000 devices + * @name: Name of the battery. If NULL the driver will fallback to "bq27000". + * @read: HDQ read callback. + * This function should provide access to the HDQ bus the battery is + * connected to. + * The first parameter is a pointer to the battery device, the second the + * register to be read. The return value should either be the content of + * the passed register or an error value. + */ +struct bq27000_platform_data { + const char *name; + int (*read)(struct device *dev, unsigned int); +}; + +#endif -- cgit v1.2.3 From 297a533b3e621be520d44d8baf3bb72ce15c4110 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen <lars@metafoo.de> Date: Fri, 7 Jan 2011 20:12:47 +0100 Subject: bq27x00: Cache battery registers This patch adds a register cache to the bq27x00 battery driver. Usually multiple, if not all, power_supply properties are queried at once, for example when an uevent is generated. Since some registers are used by multiple properties caching the registers should reduce the number of reads. The cache is valid for 5 seconds this roughly matches the internal update interval of the current register for the bq27000/bq27200. Fast changing properties(*_NOW) which can be obtained by reading a single register are not cached. It will also be used in the follow up patch to check if the battery status has been changed since the last update to emit power_supply_changed events. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Tested-by: Grazvydas Ignotas <notasas@gmail.com> --- drivers/power/bq27x00_battery.c | 271 ++++++++++++++++++++++------------------ 1 file changed, 150 insertions(+), 121 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 44bc76be0780..dbe3fcb3b0ba 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -51,17 +51,30 @@ struct bq27x00_device_info; struct bq27x00_access_methods { - int (*read)(struct bq27x00_device_info *, u8 reg, int *rt_value, - bool single); + int (*read)(struct bq27x00_device_info *di, u8 reg, bool single); }; enum bq27x00_chip { BQ27000, BQ27500 }; +struct bq27x00_reg_cache { + int temperature; + int time_to_empty; + int time_to_empty_avg; + int time_to_full; + int capacity; + int flags; + + int current_now; +}; + struct bq27x00_device_info { struct device *dev; int id; enum bq27x00_chip chip; + struct bq27x00_reg_cache cache; + unsigned long last_update; + struct power_supply bat; struct bq27x00_access_methods bus; @@ -85,48 +98,93 @@ static enum power_supply_property bq27x00_battery_props[] = { */ static inline int bq27x00_read(struct bq27x00_device_info *di, u8 reg, - int *rt_value, bool single) + bool single) { - return di->bus.read(di, reg, rt_value, single); + return di->bus.read(di, reg, single); } /* - * Return the battery temperature in tenths of degree Celsius + * Return the battery Relative State-of-Charge * Or < 0 if something fails. */ -static int bq27x00_battery_temperature(struct bq27x00_device_info *di) +static int bq27x00_battery_read_rsoc(struct bq27x00_device_info *di) { - int ret; - int temp = 0; - - ret = bq27x00_read(di, BQ27x00_REG_TEMP, &temp, false); - if (ret) { - dev_err(di->dev, "error reading temperature\n"); - return ret; - } + int rsoc; if (di->chip == BQ27500) - return temp - 2731; + rsoc = bq27x00_read(di, BQ27500_REG_SOC, false); else - return ((temp * 5) - 5463) / 2; + rsoc = bq27x00_read(di, BQ27000_REG_RSOC, true); + + if (rsoc < 0) + dev_err(di->dev, "error reading relative State-of-Charge\n"); + + return rsoc; } /* - * Return the battery Voltage in milivolts - * Or < 0 if something fails. + * Read a time register. + * Return < 0 if something fails. */ -static int bq27x00_battery_voltage(struct bq27x00_device_info *di) +static int bq27x00_battery_read_time(struct bq27x00_device_info *di, u8 reg) { - int ret; - int volt = 0; + int tval; - ret = bq27x00_read(di, BQ27x00_REG_VOLT, &volt, false); - if (ret) { - dev_err(di->dev, "error reading voltage\n"); - return ret; + tval = bq27x00_read(di, reg, false); + if (tval < 0) { + dev_err(di->dev, "error reading register %02x: %d\n", reg, tval); + return tval; } - return volt * 1000; + if (tval == 65535) + return -ENODATA; + + return tval * 60; +} + +static void bq27x00_update(struct bq27x00_device_info *di) +{ + struct bq27x00_reg_cache cache = {0, }; + bool is_bq27500 = di->chip == BQ27500; + + cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, is_bq27500); + if (cache.flags >= 0) { + cache.capacity = bq27x00_battery_read_rsoc(di); + cache.temperature = bq27x00_read(di, BQ27x00_REG_TEMP, false); + cache.time_to_empty = bq27x00_battery_read_time(di, BQ27x00_REG_TTE); + cache.time_to_empty_avg = bq27x00_battery_read_time(di, BQ27x00_REG_TTECP); + cache.time_to_full = bq27x00_battery_read_time(di, BQ27x00_REG_TTF); + + if (!is_bq27500) + cache.current_now = bq27x00_read(di, BQ27x00_REG_AI, false); + } + + /* Ignore current_now which is a snapshot of the current battery state + * and is likely to be different even between two consecutive reads */ + if (memcmp(&di->cache, &cache, sizeof(cache) - sizeof(int)) != 0) { + di->cache = cache; + power_supply_changed(&di->bat); + } + + di->last_update = jiffies; +} + +/* + * Return the battery temperature in tenths of degree Celsius + * Or < 0 if something fails. + */ +static int bq27x00_battery_temperature(struct bq27x00_device_info *di, + union power_supply_propval *val) +{ + if (di->cache.temperature < 0) + return di->cache.temperature; + + if (di->chip == BQ27500) + val->intval = di->cache.temperature - 2731; + else + val->intval = ((di->cache.temperature * 5) - 5463) / 2; + + return 0; } /* @@ -134,109 +192,84 @@ static int bq27x00_battery_voltage(struct bq27x00_device_info *di) * Note that current can be negative signed as well * Or 0 if something fails. */ -static int bq27x00_battery_current(struct bq27x00_device_info *di) +static int bq27x00_battery_current(struct bq27x00_device_info *di, + union power_supply_propval *val) { - int ret; - int curr = 0; - int flags = 0; + int curr; - ret = bq27x00_read(di, BQ27x00_REG_AI, &curr, false); - if (ret) { - dev_err(di->dev, "error reading current\n"); - return 0; - } + if (di->chip == BQ27500) + curr = bq27x00_read(di, BQ27x00_REG_AI, false); + else + curr = di->cache.current_now; + + if (curr < 0) + return curr; if (di->chip == BQ27500) { /* bq27500 returns signed value */ - curr = (int)((s16)curr) * 1000; + val->intval = (int)((s16)curr) * 1000; } else { - ret = bq27x00_read(di, BQ27x00_REG_FLAGS, &flags, false); - if (ret < 0) { - dev_err(di->dev, "error reading flags\n"); - return 0; - } - if (flags & BQ27000_FLAG_CHGS) { + if (di->cache.flags & BQ27000_FLAG_CHGS) { dev_dbg(di->dev, "negative current!\n"); curr = -curr; } - curr = curr * 3570 / BQ27000_RS; - } - - return curr; -} - -/* - * Return the battery Relative State-of-Charge - * Or < 0 if something fails. - */ -static int bq27x00_battery_rsoc(struct bq27x00_device_info *di) -{ - int ret; - int rsoc = 0; - if (di->chip == BQ27500) - ret = bq27x00_read(di, BQ27500_REG_SOC, &rsoc, false); - else - ret = bq27x00_read(di, BQ27000_REG_RSOC, &rsoc, true); - if (ret) { - dev_err(di->dev, "error reading relative State-of-Charge\n"); - return ret; + val->intval = curr * 3570 / BQ27000_RS; } - return rsoc; + return 0; } static int bq27x00_battery_status(struct bq27x00_device_info *di, - union power_supply_propval *val) + union power_supply_propval *val) { - int flags = 0; int status; - int ret; - - ret = bq27x00_read(di, BQ27x00_REG_FLAGS, &flags, false); - if (ret < 0) { - dev_err(di->dev, "error reading flags\n"); - return ret; - } if (di->chip == BQ27500) { - if (flags & BQ27500_FLAG_FC) + if (di->cache.flags & BQ27500_FLAG_FC) status = POWER_SUPPLY_STATUS_FULL; - else if (flags & BQ27500_FLAG_DSC) + else if (di->cache.flags & BQ27500_FLAG_DSC) status = POWER_SUPPLY_STATUS_DISCHARGING; else status = POWER_SUPPLY_STATUS_CHARGING; } else { - if (flags & BQ27000_FLAG_CHGS) + if (di->cache.flags & BQ27000_FLAG_CHGS) status = POWER_SUPPLY_STATUS_CHARGING; else status = POWER_SUPPLY_STATUS_DISCHARGING; } val->intval = status; + return 0; } /* - * Read a time register. - * Return < 0 if something fails. + * Return the battery Voltage in milivolts + * Or < 0 if something fails. */ -static int bq27x00_battery_time(struct bq27x00_device_info *di, int reg, - union power_supply_propval *val) +static int bq27x00_battery_voltage(struct bq27x00_device_info *di, + union power_supply_propval *val) { - int tval = 0; - int ret; + int volt; - ret = bq27x00_read(di, reg, &tval, false); - if (ret) { - dev_err(di->dev, "error reading register %02x\n", reg); - return ret; - } + volt = bq27x00_read(di, BQ27x00_REG_VOLT, false); + if (volt < 0) + return volt; - if (tval == 65535) - return -ENODATA; + val->intval = volt * 1000; + + return 0; +} + +static int bq27x00_simple_value(int value, + union power_supply_propval *val) +{ + if (value < 0) + return value; + + val->intval = value; - val->intval = tval * 60; return 0; } @@ -249,9 +282,11 @@ static int bq27x00_battery_get_property(struct power_supply *psy, { int ret = 0; struct bq27x00_device_info *di = to_bq27x00_device_info(psy); - int voltage = bq27x00_battery_voltage(di); - if (psp != POWER_SUPPLY_PROP_PRESENT && voltage <= 0) + if (time_is_before_jiffies(di->last_update + 5 * HZ)) + bq27x00_update(di); + + if (psp != POWER_SUPPLY_PROP_PRESENT && di->cache.flags < 0) return -ENODEV; switch (psp) { @@ -259,29 +294,28 @@ static int bq27x00_battery_get_property(struct power_supply *psy, ret = bq27x00_battery_status(di, val); break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: - val->intval = voltage; + ret = bq27x00_battery_voltage(di, val); break; case POWER_SUPPLY_PROP_PRESENT: - if (psp == POWER_SUPPLY_PROP_PRESENT) - val->intval = voltage <= 0 ? 0 : 1; + val->intval = di->cache.flags < 0 ? 0 : 1; break; case POWER_SUPPLY_PROP_CURRENT_NOW: - val->intval = bq27x00_battery_current(di); + ret = bq27x00_battery_current(di, val); break; case POWER_SUPPLY_PROP_CAPACITY: - val->intval = bq27x00_battery_rsoc(di); + ret = bq27x00_simple_value(di->cache.capacity, val); break; case POWER_SUPPLY_PROP_TEMP: - val->intval = bq27x00_battery_temperature(di); + ret = bq27x00_battery_temperature(di, val); break; case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: - ret = bq27x00_battery_time(di, BQ27x00_REG_TTE, val); + ret = bq27x00_simple_value(di->cache.time_to_empty, val); break; case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: - ret = bq27x00_battery_time(di, BQ27x00_REG_TTECP, val); + ret = bq27x00_simple_value(di->cache.time_to_empty_avg, val); break; case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: - ret = bq27x00_battery_time(di, BQ27x00_REG_TTF, val); + ret = bq27x00_simple_value(di->cache.time_to_full, val); break; case POWER_SUPPLY_PROP_TECHNOLOGY: val->intval = POWER_SUPPLY_TECHNOLOGY_LION; @@ -311,6 +345,8 @@ static int bq27x00_powersupply_init(struct bq27x00_device_info *di) dev_info(di->dev, "support ver. %s enabled\n", DRIVER_VERSION); + bq27x00_update(di); + return 0; } @@ -324,13 +360,12 @@ static int bq27x00_powersupply_init(struct bq27x00_device_info *di) static DEFINE_IDR(battery_id); static DEFINE_MUTEX(battery_mutex); -static int bq27x00_read_i2c(struct bq27x00_device_info *di, u8 reg, - int *rt_value, bool single) +static int bq27x00_read_i2c(struct bq27x00_device_info *di, u8 reg, bool single) { struct i2c_client *client = to_i2c_client(di->dev); struct i2c_msg msg[1]; unsigned char data[2]; - int err; + int ret; if (!client->adapter) return -ENODEV; @@ -341,26 +376,24 @@ static int bq27x00_read_i2c(struct bq27x00_device_info *di, u8 reg, msg->buf = data; data[0] = reg; - err = i2c_transfer(client->adapter, msg, 1); + ret = i2c_transfer(client->adapter, msg, 1); - if (err >= 0) { + if (ret >= 0) { if (!single) msg->len = 2; else msg->len = 1; msg->flags = I2C_M_RD; - err = i2c_transfer(client->adapter, msg, 1); - if (err >= 0) { + ret = i2c_transfer(client->adapter, msg, 1); + if (ret >= 0) { if (!single) - *rt_value = get_unaligned_le16(data); + ret = get_unaligned_le16(data); else - *rt_value = data[0]; - - return 0; + ret = data[0]; } } - return err; + return ret; } static int bq27x00_battery_probe(struct i2c_client *client, @@ -477,7 +510,7 @@ static inline void bq27x00_battery_i2c_exit(void) {}; #ifdef CONFIG_BATTERY_BQ27X00_PLATFORM static int bq27000_read_platform(struct bq27x00_device_info *di, u8 reg, - int *rt_value, bool single) + bool single) { struct device *dev = di->dev; struct bq27000_platform_data *pdata = dev->platform_data; @@ -504,14 +537,10 @@ static int bq27000_read_platform(struct bq27x00_device_info *di, u8 reg, if (timeout == 0) return -EIO; - *rt_value = (upper << 8) | lower; - } else { - lower = pdata->read(dev, reg); - if (lower < 0) - return lower; - *rt_value = lower; + return (upper << 8) | lower; } - return 0; + + return pdata->read(dev, reg); } static int __devinit bq27000_battery_probe(struct platform_device *pdev) -- cgit v1.2.3 From 740b755a3b3433f5ee5f9ef54de5d1b4c08223cc Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen <lars@metafoo.de> Date: Fri, 7 Jan 2011 20:14:53 +0100 Subject: bq27x00: Poll battery state This patch adds support for polling the battery state and generating a power_supply_changed() event if it has changed. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Tested-by: Grazvydas Ignotas <notasas@gmail.com> --- drivers/power/bq27x00_battery.c | 58 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 5 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index dbe3fcb3b0ba..25350fcbadb3 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -74,10 +74,13 @@ struct bq27x00_device_info { struct bq27x00_reg_cache cache; unsigned long last_update; + struct delayed_work work; struct power_supply bat; struct bq27x00_access_methods bus; + + struct mutex lock; }; static enum power_supply_property bq27x00_battery_props[] = { @@ -93,6 +96,11 @@ static enum power_supply_property bq27x00_battery_props[] = { POWER_SUPPLY_PROP_TECHNOLOGY, }; +static unsigned int poll_interval = 360; +module_param(poll_interval, uint, 0644); +MODULE_PARM_DESC(poll_interval, "battery poll interval in seconds - " \ + "0 disables polling"); + /* * Common code for BQ27x00 devices */ @@ -169,6 +177,21 @@ static void bq27x00_update(struct bq27x00_device_info *di) di->last_update = jiffies; } +static void bq27x00_battery_poll(struct work_struct *work) +{ + struct bq27x00_device_info *di = + container_of(work, struct bq27x00_device_info, work.work); + + bq27x00_update(di); + + if (poll_interval > 0) { + /* The timer does not have to be accurate. */ + set_timer_slack(&di->work.timer, poll_interval * HZ / 4); + schedule_delayed_work(&di->work, poll_interval * HZ); + } +} + + /* * Return the battery temperature in tenths of degree Celsius * Or < 0 if something fails. @@ -283,8 +306,12 @@ static int bq27x00_battery_get_property(struct power_supply *psy, int ret = 0; struct bq27x00_device_info *di = to_bq27x00_device_info(psy); - if (time_is_before_jiffies(di->last_update + 5 * HZ)) - bq27x00_update(di); + mutex_lock(&di->lock); + if (time_is_before_jiffies(di->last_update + 5 * HZ)) { + cancel_delayed_work_sync(&di->work); + bq27x00_battery_poll(&di->work.work); + } + mutex_unlock(&di->lock); if (psp != POWER_SUPPLY_PROP_PRESENT && di->cache.flags < 0) return -ENODEV; @@ -327,6 +354,14 @@ static int bq27x00_battery_get_property(struct power_supply *psy, return ret; } +static void bq27x00_external_power_changed(struct power_supply *psy) +{ + struct bq27x00_device_info *di = to_bq27x00_device_info(psy); + + cancel_delayed_work_sync(&di->work); + schedule_delayed_work(&di->work, 0); +} + static int bq27x00_powersupply_init(struct bq27x00_device_info *di) { int ret; @@ -335,7 +370,10 @@ static int bq27x00_powersupply_init(struct bq27x00_device_info *di) di->bat.properties = bq27x00_battery_props; di->bat.num_properties = ARRAY_SIZE(bq27x00_battery_props); di->bat.get_property = bq27x00_battery_get_property; - di->bat.external_power_changed = NULL; + di->bat.external_power_changed = bq27x00_external_power_changed; + + INIT_DELAYED_WORK(&di->work, bq27x00_battery_poll); + mutex_init(&di->lock); ret = power_supply_register(di->dev, &di->bat); if (ret) { @@ -350,6 +388,15 @@ static int bq27x00_powersupply_init(struct bq27x00_device_info *di) return 0; } +static void bq27x00_powersupply_unregister(struct bq27x00_device_info *di) +{ + cancel_delayed_work_sync(&di->work); + + power_supply_unregister(&di->bat); + + mutex_destroy(&di->lock); +} + /* i2c specific code */ #ifdef CONFIG_BATTERY_BQ27X00_I2C @@ -457,7 +504,7 @@ static int bq27x00_battery_remove(struct i2c_client *client) { struct bq27x00_device_info *di = i2c_get_clientdata(client); - power_supply_unregister(&di->bat); + bq27x00_powersupply_unregister(di); kfree(di->bat.name); @@ -590,7 +637,8 @@ static int __devexit bq27000_battery_remove(struct platform_device *pdev) { struct bq27x00_device_info *di = platform_get_drvdata(pdev); - power_supply_unregister(&di->bat); + bq27x00_powersupply_unregister(di); + platform_set_drvdata(pdev, NULL); kfree(di); -- cgit v1.2.3 From 631c17ee5daf0ca73688cb5e5216fc0e3e340483 Mon Sep 17 00:00:00 2001 From: Pali Rohár <pali.rohar@gmail.com> Date: Tue, 1 Feb 2011 00:08:02 +0100 Subject: bq27x00: Add new properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch add support for reporting properties POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_CHARGE_COUNTER, POWER_SUPPLY_PROP_ENERGY_NOW in module bq27x00_battery. Signed-off-by: Pali Rohár <pali.rohar@gmail.com> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Tested-by: Grazvydas Ignotas <notasas@gmail.com> --- drivers/power/bq27x00_battery.c | 152 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 151 insertions(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 25350fcbadb3..a1bd1ff753b3 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -16,6 +16,13 @@ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * */ + +/* + * Datasheets: + * http://focus.ti.com/docs/prod/folders/print/bq27000.html + * http://focus.ti.com/docs/prod/folders/print/bq27500.html + */ + #include <linux/module.h> #include <linux/param.h> #include <linux/jiffies.h> @@ -30,7 +37,7 @@ #include <linux/power/bq27x00_battery.h> -#define DRIVER_VERSION "1.1.0" +#define DRIVER_VERSION "1.2.0" #define BQ27x00_REG_TEMP 0x06 #define BQ27x00_REG_VOLT 0x08 @@ -39,11 +46,17 @@ #define BQ27x00_REG_TTE 0x16 #define BQ27x00_REG_TTF 0x18 #define BQ27x00_REG_TTECP 0x26 +#define BQ27x00_REG_NAC 0x0C /* Nominal available capaciy */ +#define BQ27x00_REG_LMD 0x12 /* Last measured discharge */ +#define BQ27x00_REG_CYCT 0x2A /* Cycle count total */ +#define BQ27x00_REG_AE 0x22 /* Available enery */ #define BQ27000_REG_RSOC 0x0B /* Relative State-of-Charge */ +#define BQ27000_REG_ILMD 0x76 /* Initial last measured discharge */ #define BQ27000_FLAG_CHGS BIT(7) #define BQ27500_REG_SOC 0x2c +#define BQ27500_REG_DCAP 0x3C /* Design capacity */ #define BQ27500_FLAG_DSC BIT(0) #define BQ27500_FLAG_FC BIT(9) @@ -61,6 +74,8 @@ struct bq27x00_reg_cache { int time_to_empty; int time_to_empty_avg; int time_to_full; + int charge_full; + int charge_counter; int capacity; int flags; @@ -73,6 +88,8 @@ struct bq27x00_device_info { enum bq27x00_chip chip; struct bq27x00_reg_cache cache; + int charge_design_full; + unsigned long last_update; struct delayed_work work; @@ -94,6 +111,11 @@ static enum power_supply_property bq27x00_battery_props[] = { POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 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_CHARGE_COUNTER, + POWER_SUPPLY_PROP_ENERGY_NOW, }; static unsigned int poll_interval = 360; @@ -130,6 +152,87 @@ static int bq27x00_battery_read_rsoc(struct bq27x00_device_info *di) return rsoc; } +/* + * Return a battery charge value in µAh + * Or < 0 if something fails. + */ +static int bq27x00_battery_read_charge(struct bq27x00_device_info *di, u8 reg) +{ + int charge; + + charge = bq27x00_read(di, reg, false); + if (charge < 0) { + dev_err(di->dev, "error reading nominal available capacity\n"); + return charge; + } + + if (di->chip == BQ27500) + charge *= 1000; + else + charge = charge * 3570 / BQ27000_RS; + + return charge; +} + +/* + * Return the battery Nominal available capaciy in µAh + * Or < 0 if something fails. + */ +static inline int bq27x00_battery_read_nac(struct bq27x00_device_info *di) +{ + return bq27x00_battery_read_charge(di, BQ27x00_REG_NAC); +} + +/* + * Return the battery Last measured discharge in µAh + * Or < 0 if something fails. + */ +static inline int bq27x00_battery_read_lmd(struct bq27x00_device_info *di) +{ + return bq27x00_battery_read_charge(di, BQ27x00_REG_LMD); +} + +/* + * Return the battery Initial last measured discharge in µAh + * Or < 0 if something fails. + */ +static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di) +{ + int ilmd; + + if (di->chip == BQ27500) + ilmd = bq27x00_read(di, BQ27500_REG_DCAP, false); + else + ilmd = bq27x00_read(di, BQ27000_REG_ILMD, true); + + if (ilmd < 0) { + dev_err(di->dev, "error reading initial last measured discharge\n"); + return ilmd; + } + + if (di->chip == BQ27500) + ilmd *= 1000; + else + ilmd = ilmd * 256 * 3570 / BQ27000_RS; + + return ilmd; +} + +/* + * Return the battery Cycle count total + * Or < 0 if something fails. + */ +static int bq27x00_battery_read_cyct(struct bq27x00_device_info *di) +{ + int cyct; + + cyct = bq27x00_read(di, BQ27x00_REG_CYCT, false); + if (cyct < 0) + dev_err(di->dev, "error reading cycle count total\n"); + + return cyct; +} + /* * Read a time register. * Return < 0 if something fails. @@ -162,9 +265,15 @@ static void bq27x00_update(struct bq27x00_device_info *di) cache.time_to_empty = bq27x00_battery_read_time(di, BQ27x00_REG_TTE); cache.time_to_empty_avg = bq27x00_battery_read_time(di, BQ27x00_REG_TTECP); cache.time_to_full = bq27x00_battery_read_time(di, BQ27x00_REG_TTF); + cache.charge_full = bq27x00_battery_read_lmd(di); + cache.charge_counter = bq27x00_battery_read_cyct(di); if (!is_bq27500) cache.current_now = bq27x00_read(di, BQ27x00_REG_AI, false); + + /* We only have to read charge design full once */ + if (di->charge_design_full <= 0) + di->charge_design_full = bq27x00_battery_read_ilmd(di); } /* Ignore current_now which is a snapshot of the current battery state @@ -285,6 +394,32 @@ static int bq27x00_battery_voltage(struct bq27x00_device_info *di, return 0; } +/* + * Return the battery Available energy in µWh + * Or < 0 if something fails. + */ +static int bq27x00_battery_energy(struct bq27x00_device_info *di, + union power_supply_propval *val) +{ + int ae; + + ae = bq27x00_read(di, BQ27x00_REG_AE, false); + if (ae < 0) { + dev_err(di->dev, "error reading available energy\n"); + return ae; + } + + if (di->chip == BQ27500) + ae *= 1000; + else + ae = ae * 29200 / BQ27000_RS; + + val->intval = ae; + + return 0; +} + + static int bq27x00_simple_value(int value, union power_supply_propval *val) { @@ -347,6 +482,21 @@ static int bq27x00_battery_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_TECHNOLOGY: val->intval = POWER_SUPPLY_TECHNOLOGY_LION; break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + ret = bq27x00_simple_value(bq27x00_battery_read_nac(di), val); + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + ret = bq27x00_simple_value(di->cache.charge_full, val); + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + ret = bq27x00_simple_value(di->charge_design_full, val); + break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + ret = bq27x00_simple_value(di->cache.charge_counter, val); + break; + case POWER_SUPPLY_PROP_ENERGY_NOW: + ret = bq27x00_battery_energy(di, val); + break; default: return -EINVAL; } -- cgit v1.2.3 From fd9b958c5525024204e7e2c48fe5eae560d171bd Mon Sep 17 00:00:00 2001 From: Pali Rohár <pali.rohar@gmail.com> Date: Tue, 1 Feb 2011 00:10:41 +0100 Subject: bq27x00: Add MODULE_DEVICE_TABLE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds MODULE_DEVICE_TABLE for module bq27x00_battery. Signed-off-by: Pali Rohár <pali.rohar@gmail.com> Tested-by: Pali Rohár <pali.rohar@gmail.com> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Acked-by: Rodolfo Giometti <giometti@linux.it> Tested-by: Grazvydas Ignotas <notasas@gmail.com> --- drivers/power/bq27x00_battery.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index a1bd1ff753b3..0e99d02dea33 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -672,6 +672,7 @@ static const struct i2c_device_id bq27x00_id[] = { { "bq27500", BQ27500 }, {}, }; +MODULE_DEVICE_TABLE(i2c, bq27x00_id); static struct i2c_driver bq27x00_battery_driver = { .driver = { -- cgit v1.2.3 From c1b9ab67e7c7021be9d0db6345d6ccc5d70248f1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen <lars@metafoo.de> Date: Sat, 18 Sep 2010 17:31:16 +0200 Subject: bq27x00: Give more specific reports on battery status The current code only reports whether the battery is charging or discharging. But the battery also reports whether it is fully charged, furthermore by look at if the battery is supplied we can tell whether it is discharging or not charging. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Acked-by: Rodolfo Giometti <giometti@linux.it> Tested-by: Grazvydas Ignotas <notasas@gmail.com> --- drivers/power/bq27x00_battery.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 0e99d02dea33..003be5f1c785 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -54,6 +54,7 @@ #define BQ27000_REG_RSOC 0x0B /* Relative State-of-Charge */ #define BQ27000_REG_ILMD 0x76 /* Initial last measured discharge */ #define BQ27000_FLAG_CHGS BIT(7) +#define BQ27000_FLAG_FC BIT(5) #define BQ27500_REG_SOC 0x2c #define BQ27500_REG_DCAP 0x3C /* Design capacity */ @@ -365,8 +366,12 @@ static int bq27x00_battery_status(struct bq27x00_device_info *di, else status = POWER_SUPPLY_STATUS_CHARGING; } else { - if (di->cache.flags & BQ27000_FLAG_CHGS) + if (di->cache.flags & BQ27000_FLAG_FC) + status = POWER_SUPPLY_STATUS_FULL; + else if (di->cache.flags & BQ27000_FLAG_CHGS) status = POWER_SUPPLY_STATUS_CHARGING; + else if (power_supply_am_i_supplied(&di->bat)) + status = POWER_SUPPLY_STATUS_NOT_CHARGING; else status = POWER_SUPPLY_STATUS_DISCHARGING; } -- cgit v1.2.3 From bf7d4140450f6dbc019a8831223e08d778f90dd7 Mon Sep 17 00:00:00 2001 From: Pali Rohár <pali.rohar@gmail.com> Date: Tue, 1 Feb 2011 00:23:13 +0100 Subject: bq27x00: Minor cleanups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Consistently use uppercase for hexadecimal values. * Clarify/fix the unit of functions return value in its comment. Signed-off-by: Pali Rohár <pali.rohar@gmail.com> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Tested-by: Grazvydas Ignotas <notasas@gmail.com> --- drivers/power/bq27x00_battery.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 003be5f1c785..b44555f1b633 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -56,7 +56,7 @@ #define BQ27000_FLAG_CHGS BIT(7) #define BQ27000_FLAG_FC BIT(5) -#define BQ27500_REG_SOC 0x2c +#define BQ27500_REG_SOC 0x2C #define BQ27500_REG_DCAP 0x3C /* Design capacity */ #define BQ27500_FLAG_DSC BIT(0) #define BQ27500_FLAG_FC BIT(9) @@ -321,7 +321,7 @@ static int bq27x00_battery_temperature(struct bq27x00_device_info *di, } /* - * Return the battery average current + * Return the battery average current in µA * Note that current can be negative signed as well * Or 0 if something fails. */ -- cgit v1.2.3 From 2ec523a8238a117b082ca6b0cef3b9ae812364f9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen <lars@metafoo.de> Date: Wed, 2 Feb 2011 19:35:07 +0100 Subject: bq27x00: Cleanup bq27x00_i2c_read Some minor stylistic cleanups. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Tested-by: Grazvydas Ignotas <notasas@gmail.com> --- drivers/power/bq27x00_battery.c | 45 ++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 21 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index b44555f1b633..740a0ac29dd1 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -565,36 +565,39 @@ static DEFINE_MUTEX(battery_mutex); static int bq27x00_read_i2c(struct bq27x00_device_info *di, u8 reg, bool single) { struct i2c_client *client = to_i2c_client(di->dev); - struct i2c_msg msg[1]; + struct i2c_msg msg; unsigned char data[2]; int ret; if (!client->adapter) return -ENODEV; - msg->addr = client->addr; - msg->flags = 0; - msg->len = 1; - msg->buf = data; + msg.addr = client->addr; + msg.flags = 0; + msg.len = 1; + msg.buf = data; data[0] = reg; - ret = i2c_transfer(client->adapter, msg, 1); + ret = i2c_transfer(client->adapter, &msg, 1); + + if (ret < 0) + return ret; + + if (single) + msg.len = 1; + else + msg.len = 2; + + msg.flags = I2C_M_RD; + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) + return ret; + + if (!single) + ret = get_unaligned_le16(data); + else + ret = data[0]; - if (ret >= 0) { - if (!single) - msg->len = 2; - else - msg->len = 1; - - msg->flags = I2C_M_RD; - ret = i2c_transfer(client->adapter, msg, 1); - if (ret >= 0) { - if (!single) - ret = get_unaligned_le16(data); - else - ret = data[0]; - } - } return ret; } -- cgit v1.2.3 From 9e912f452931b3da9d13d716a381ba0d5fb74ced Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas <notasas@gmail.com> Date: Tue, 15 Feb 2011 23:27:35 +0200 Subject: bq27x00: Use single i2c_transfer call for property read Doing this by using 2 calls sometimes results in unexpected values being returned on OMAP3 i2c controller. Signed-off-by: Grazvydas Ignotas <notasas@gmail.com> Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> --- drivers/power/bq27x00_battery.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 740a0ac29dd1..59e68dbd028b 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -565,31 +565,26 @@ static DEFINE_MUTEX(battery_mutex); static int bq27x00_read_i2c(struct bq27x00_device_info *di, u8 reg, bool single) { struct i2c_client *client = to_i2c_client(di->dev); - struct i2c_msg msg; + struct i2c_msg msg[2]; unsigned char data[2]; int ret; if (!client->adapter) return -ENODEV; - msg.addr = client->addr; - msg.flags = 0; - msg.len = 1; - msg.buf = data; - - data[0] = reg; - ret = i2c_transfer(client->adapter, &msg, 1); - - if (ret < 0) - return ret; - + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].buf = ® + msg[0].len = sizeof(reg); + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = data; if (single) - msg.len = 1; + msg[1].len = 1; else - msg.len = 2; + msg[1].len = 2; - msg.flags = I2C_M_RD; - ret = i2c_transfer(client->adapter, &msg, 1); + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); if (ret < 0) return ret; -- cgit v1.2.3