From 51d07566045787b99219d809639c8724506fc78a Mon Sep 17 00:00:00 2001 From: Rhyland Klein Date: Tue, 25 Jan 2011 11:10:06 -0800 Subject: bq20z75: Add support for charge properties Adding support for charge properties for gas gauge. Also ensuring that battery mode is correct now for energy as well as charge properties by setting it on the fly. I also added 2 functions to power_supply.h to help identify the units for specific properties more easily by power supplies. Signed-off-by: Rhyland Klein Signed-off-by: Anton Vorontsov --- drivers/power/bq20z75.c | 98 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 87 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/power/bq20z75.c b/drivers/power/bq20z75.c index 492da27e1a47..4141775e5ff6 100644 --- a/drivers/power/bq20z75.c +++ b/drivers/power/bq20z75.c @@ -38,11 +38,22 @@ enum { REG_CYCLE_COUNT, REG_SERIAL_NUMBER, REG_REMAINING_CAPACITY, + REG_REMAINING_CAPACITY_CHARGE, REG_FULL_CHARGE_CAPACITY, + REG_FULL_CHARGE_CAPACITY_CHARGE, REG_DESIGN_CAPACITY, + REG_DESIGN_CAPACITY_CHARGE, REG_DESIGN_VOLTAGE, }; +/* Battery Mode defines */ +#define BATTERY_MODE_OFFSET 0x03 +#define BATTERY_MODE_MASK 0x8000 +enum bq20z75_battery_mode { + BATTERY_MODE_AMPS, + BATTERY_MODE_WATTS +}; + /* manufacturer access defines */ #define MANUFACTURER_ACCESS_STATUS 0x0006 #define MANUFACTURER_ACCESS_SLEEP 0x0011 @@ -78,8 +89,12 @@ static const struct bq20z75_device_data { BQ20Z75_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0E, 0, 100), [REG_REMAINING_CAPACITY] = BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_NOW, 0x0F, 0, 65535), + [REG_REMAINING_CAPACITY_CHARGE] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_CHARGE_NOW, 0x0F, 0, 65535), [REG_FULL_CHARGE_CAPACITY] = BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL, 0x10, 0, 65535), + [REG_FULL_CHARGE_CAPACITY_CHARGE] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_CHARGE_FULL, 0x10, 0, 65535), [REG_TIME_TO_EMPTY] = BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0, 65535), @@ -93,6 +108,9 @@ static const struct bq20z75_device_data { [REG_DESIGN_CAPACITY] = BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 0x18, 0, 65535), + [REG_DESIGN_CAPACITY_CHARGE] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 0x18, 0, + 65535), [REG_DESIGN_VOLTAGE] = BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 0x19, 0, 65535), @@ -117,6 +135,9 @@ static enum power_supply_property bq20z75_properties[] = { POWER_SUPPLY_PROP_ENERGY_NOW, POWER_SUPPLY_PROP_ENERGY_FULL, POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, }; struct bq20z75_info { @@ -260,6 +281,9 @@ static void bq20z75_unit_adjustment(struct i2c_client *client, case POWER_SUPPLY_PROP_VOLTAGE_NOW: case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: case POWER_SUPPLY_PROP_CURRENT_NOW: + case POWER_SUPPLY_PROP_CHARGE_NOW: + case POWER_SUPPLY_PROP_CHARGE_FULL: + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: val->intval *= BASE_UNIT_CONVERSION; break; @@ -281,11 +305,44 @@ static void bq20z75_unit_adjustment(struct i2c_client *client, } } +static enum bq20z75_battery_mode +bq20z75_set_battery_mode(struct i2c_client *client, + enum bq20z75_battery_mode mode) +{ + int ret, original_val; + + original_val = bq20z75_read_word_data(client, BATTERY_MODE_OFFSET); + if (original_val < 0) + return original_val; + + if ((original_val & BATTERY_MODE_MASK) == mode) + return mode; + + if (mode == BATTERY_MODE_AMPS) + ret = original_val & ~BATTERY_MODE_MASK; + else + ret = original_val | BATTERY_MODE_MASK; + + ret = bq20z75_write_word_data(client, BATTERY_MODE_OFFSET, ret); + if (ret < 0) + return ret; + + return original_val & BATTERY_MODE_MASK; +} + static int bq20z75_get_battery_capacity(struct i2c_client *client, int reg_offset, enum power_supply_property psp, union power_supply_propval *val) { s32 ret; + enum bq20z75_battery_mode mode = BATTERY_MODE_WATTS; + + if (power_supply_is_amp_property(psp)) + mode = BATTERY_MODE_AMPS; + + mode = bq20z75_set_battery_mode(client, mode); + if (mode < 0) + return mode; ret = bq20z75_read_word_data(client, bq20z75_data[reg_offset].addr); if (ret < 0) @@ -298,6 +355,10 @@ static int bq20z75_get_battery_capacity(struct i2c_client *client, } else val->intval = ret; + ret = bq20z75_set_battery_mode(client, mode); + if (ret < 0) + return ret; + return 0; } @@ -318,11 +379,25 @@ static int bq20z75_get_battery_serial_number(struct i2c_client *client, return 0; } +static int bq20z75_get_property_index(struct i2c_client *client, + enum power_supply_property psp) +{ + int count; + for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) + if (psp == bq20z75_data[count].psp) + return count; + + dev_warn(&client->dev, + "%s: Invalid Property - %d\n", __func__, psp); + + return -EINVAL; +} + static int bq20z75_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - int count; + int ps_index; int ret; struct bq20z75_info *bq20z75_device = container_of(psy, struct bq20z75_info, power_supply); @@ -343,13 +418,15 @@ static int bq20z75_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_ENERGY_NOW: case POWER_SUPPLY_PROP_ENERGY_FULL: case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: + case POWER_SUPPLY_PROP_CHARGE_NOW: + case POWER_SUPPLY_PROP_CHARGE_FULL: + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: case POWER_SUPPLY_PROP_CAPACITY: - for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) { - if (psp == bq20z75_data[count].psp) - break; - } + ps_index = bq20z75_get_property_index(client, psp); + if (ps_index < 0) + return ps_index; - ret = bq20z75_get_battery_capacity(client, count, psp, val); + ret = bq20z75_get_battery_capacity(client, ps_index, psp, val); if (ret) return ret; @@ -369,12 +446,11 @@ static int bq20z75_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: - for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) { - if (psp == bq20z75_data[count].psp) - break; - } + ps_index = bq20z75_get_property_index(client, psp); + if (ps_index < 0) + return ps_index; - ret = bq20z75_get_battery_property(client, count, psp, val); + ret = bq20z75_get_battery_property(client, ps_index, psp, val); if (ret) return ret; -- cgit v1.2.3 From 0b9536c957095eb1497828aa51b34ac695f67eae Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Fri, 7 Jan 2011 16:28:16 +0000 Subject: leds: Add ability to blink via simple trigger As blink API is now available, it's possible to add ability to blink via simple trigger. Signed-off-by: Vasily Khoruzhick Signed-off-by: Anton Vorontsov --- drivers/leds/led-triggers.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers') diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index c41eb6180c9c..4bebae733349 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -231,6 +231,26 @@ void led_trigger_event(struct led_trigger *trigger, } EXPORT_SYMBOL_GPL(led_trigger_event); +void led_trigger_blink(struct led_trigger *trigger, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct list_head *entry; + + if (!trigger) + return; + + read_lock(&trigger->leddev_list_lock); + list_for_each(entry, &trigger->led_cdevs) { + struct led_classdev *led_cdev; + + led_cdev = list_entry(entry, struct led_classdev, trig_list); + led_blink_set(led_cdev, delay_on, delay_off); + } + read_unlock(&trigger->leddev_list_lock); +} +EXPORT_SYMBOL_GPL(led_trigger_blink); + void led_trigger_register_simple(const char *name, struct led_trigger **tp) { struct led_trigger *trigger; -- cgit v1.2.3 From 6501f728c56f831626d52b236023e556bca37f51 Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Fri, 7 Jan 2011 18:28:17 +0200 Subject: power_supply: Add new LED trigger charging-blink-solid-full Add new trigger to power_supply LEDs. It will blink when battery is charging, and stay solid when battery is charged. It's usefull to indicate battery state when there's only one LED available. Signed-off-by: Vasily Khoruzhick Signed-off-by: Anton Vorontsov --- drivers/power/power_supply_leds.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers') diff --git a/drivers/power/power_supply_leds.c b/drivers/power/power_supply_leds.c index 031a554837f7..da25eb94e5c6 100644 --- a/drivers/power/power_supply_leds.c +++ b/drivers/power/power_supply_leds.c @@ -21,6 +21,8 @@ static void power_supply_update_bat_leds(struct power_supply *psy) { union power_supply_propval status; + unsigned long delay_on = 0; + unsigned long delay_off = 0; if (psy->get_property(psy, POWER_SUPPLY_PROP_STATUS, &status)) return; @@ -32,16 +34,22 @@ static void power_supply_update_bat_leds(struct power_supply *psy) led_trigger_event(psy->charging_full_trig, LED_FULL); led_trigger_event(psy->charging_trig, LED_OFF); led_trigger_event(psy->full_trig, LED_FULL); + led_trigger_event(psy->charging_blink_full_solid_trig, + LED_FULL); break; case POWER_SUPPLY_STATUS_CHARGING: led_trigger_event(psy->charging_full_trig, LED_FULL); led_trigger_event(psy->charging_trig, LED_FULL); led_trigger_event(psy->full_trig, LED_OFF); + led_trigger_blink(psy->charging_blink_full_solid_trig, + &delay_on, &delay_off); break; default: led_trigger_event(psy->charging_full_trig, LED_OFF); led_trigger_event(psy->charging_trig, LED_OFF); led_trigger_event(psy->full_trig, LED_OFF); + led_trigger_event(psy->charging_blink_full_solid_trig, + LED_OFF); break; } } @@ -64,15 +72,24 @@ static int power_supply_create_bat_triggers(struct power_supply *psy) if (!psy->full_trig_name) goto full_failed; + psy->charging_blink_full_solid_trig_name = kasprintf(GFP_KERNEL, + "%s-charging-blink-full-solid", psy->name); + if (!psy->charging_blink_full_solid_trig_name) + goto charging_blink_full_solid_failed; + led_trigger_register_simple(psy->charging_full_trig_name, &psy->charging_full_trig); led_trigger_register_simple(psy->charging_trig_name, &psy->charging_trig); led_trigger_register_simple(psy->full_trig_name, &psy->full_trig); + led_trigger_register_simple(psy->charging_blink_full_solid_trig_name, + &psy->charging_blink_full_solid_trig); goto success; +charging_blink_full_solid_failed: + kfree(psy->full_trig_name); full_failed: kfree(psy->charging_trig_name); charging_failed: @@ -88,6 +105,8 @@ static void power_supply_remove_bat_triggers(struct power_supply *psy) led_trigger_unregister_simple(psy->charging_full_trig); led_trigger_unregister_simple(psy->charging_trig); led_trigger_unregister_simple(psy->full_trig); + led_trigger_unregister_simple(psy->charging_blink_full_solid_trig); + kfree(psy->charging_blink_full_solid_trig_name); kfree(psy->full_trig_name); kfree(psy->charging_trig_name); kfree(psy->charging_full_trig_name); -- cgit v1.2.3 From f722e17fdb2c97bbec2563636dd88489cdb1428b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen 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 Tested-by: Grazvydas Ignotas --- drivers/power/power_supply_sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') 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 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 Signed-off-by: Lars-Peter Clausen Tested-by: Grazvydas Ignotas --- drivers/power/power_supply_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') 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 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 Acked-by: Rodolfo Giometti Tested-by: Grazvydas Ignotas --- drivers/power/bq27x00_battery.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') 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 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 Acked-by: Rodolfo Giometti Tested-by: Grazvydas Ignotas --- drivers/power/bq27x00_battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') 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: =?UTF-8?q?Pali=20Roh=C3=A1r?= 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 Signed-off-by: Lars-Peter Clausen Acked-by: Rodolfo Giometti Tested-by: Grazvydas Ignotas --- drivers/power/bq27x00_battery.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') 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 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 Acked-by: Rodolfo Giometti Tested-by: Grazvydas Ignotas --- drivers/power/bq27x00_battery.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') 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 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 Acked-by: Rodolfo Giometti Tested-by: Grazvydas Ignotas --- drivers/power/bq27x00_battery.c | 86 +++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 47 deletions(-) (limited to 'drivers') 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 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 Tested-by: Grazvydas Ignotas --- drivers/power/Kconfig | 14 +++ drivers/power/bq27x00_battery.c | 184 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 183 insertions(+), 15 deletions(-) (limited to 'drivers') 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 * Copyright (C) 2008 Eurotech S.p.A. + * Copyright (C) 2010-2011 Lars-Peter Clausen * * Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc. * @@ -27,6 +28,8 @@ #include #include +#include + #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); -- cgit v1.2.3 From 297a533b3e621be520d44d8baf3bb72ce15c4110 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen 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 Tested-by: Grazvydas Ignotas --- drivers/power/bq27x00_battery.c | 271 ++++++++++++++++++++++------------------ 1 file changed, 150 insertions(+), 121 deletions(-) (limited to 'drivers') 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 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 Tested-by: Grazvydas Ignotas --- drivers/power/bq27x00_battery.c | 58 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 5 deletions(-) (limited to 'drivers') 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: =?UTF-8?q?Pali=20Roh=C3=A1r?= 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 Signed-off-by: Lars-Peter Clausen Tested-by: Grazvydas Ignotas --- drivers/power/bq27x00_battery.c | 152 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 151 insertions(+), 1 deletion(-) (limited to 'drivers') 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 #include #include @@ -30,7 +37,7 @@ #include -#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: =?UTF-8?q?Pali=20Roh=C3=A1r?= 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 Tested-by: Pali Rohár Signed-off-by: Lars-Peter Clausen Acked-by: Rodolfo Giometti Tested-by: Grazvydas Ignotas --- drivers/power/bq27x00_battery.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') 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 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 Acked-by: Rodolfo Giometti Tested-by: Grazvydas Ignotas --- drivers/power/bq27x00_battery.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') 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: =?UTF-8?q?Pali=20Roh=C3=A1r?= 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 Signed-off-by: Lars-Peter Clausen Tested-by: Grazvydas Ignotas --- drivers/power/bq27x00_battery.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') 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 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 Tested-by: Grazvydas Ignotas --- drivers/power/bq27x00_battery.c | 45 ++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 21 deletions(-) (limited to 'drivers') 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 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 Signed-off-by: Lars-Peter Clausen --- drivers/power/bq27x00_battery.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) (limited to 'drivers') 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 From d6ccc442b12102414c1343f0adacaa8a1aaa516c Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Fri, 25 Feb 2011 13:56:36 +0200 Subject: twl4030_charger: Make the driver atomic notifier safe This queues work from the otg notification where the i2c operations can be safely made. Needed for atomic otg notifiers. Signed-off-by: Heikki Krogerus Tested-by: Grazvydas Ignotas Signed-off-by: Anton Vorontsov --- drivers/power/twl4030_charger.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index ff1f42398a2e..92c16e1677bd 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -71,8 +71,11 @@ struct twl4030_bci { struct power_supply usb; struct otg_transceiver *transceiver; struct notifier_block otg_nb; + struct work_struct work; int irq_chg; int irq_bci; + + unsigned long event; }; /* @@ -258,14 +261,11 @@ static irqreturn_t twl4030_bci_interrupt(int irq, void *arg) return IRQ_HANDLED; } -static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val, - void *priv) +static void twl4030_bci_usb_work(struct work_struct *data) { - struct twl4030_bci *bci = container_of(nb, struct twl4030_bci, otg_nb); + struct twl4030_bci *bci = container_of(data, struct twl4030_bci, work); - dev_dbg(bci->dev, "OTG notify %lu\n", val); - - switch (val) { + switch (bci->event) { case USB_EVENT_VBUS: case USB_EVENT_CHARGER: twl4030_charger_enable_usb(bci, true); @@ -274,6 +274,17 @@ static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val, twl4030_charger_enable_usb(bci, false); break; } +} + +static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val, + void *priv) +{ + struct twl4030_bci *bci = container_of(nb, struct twl4030_bci, otg_nb); + + dev_dbg(bci->dev, "OTG notify %lu\n", val); + + bci->event = val; + schedule_work(&bci->work); return NOTIFY_OK; } @@ -466,6 +477,8 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) goto fail_bci_irq; } + INIT_WORK(&bci->work, twl4030_bci_usb_work); + bci->transceiver = otg_get_transceiver(); if (bci->transceiver != NULL) { bci->otg_nb.notifier_call = twl4030_bci_usb_ncb; -- cgit v1.2.3 From bb879101606dd7235d8f4ecd0f707b63281d0838 Mon Sep 17 00:00:00 2001 From: Rhyland Klein Date: Mon, 28 Feb 2011 16:55:28 -0800 Subject: bq20z75: Add optional battery detect gpio Adding support for an optional gpio for battery detection. This is passed in through the i2c platform data. It also accepts another field, battery_detect_present to signify the gpio state which means the battery is present, either 0 (low) or 1 (high). Signed-off-by: Rhyland Klein Signed-off-by: Anton Vorontsov --- drivers/power/bq20z75.c | 160 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 129 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/power/bq20z75.c b/drivers/power/bq20z75.c index 4141775e5ff6..a51e98d74d34 100644 --- a/drivers/power/bq20z75.c +++ b/drivers/power/bq20z75.c @@ -25,6 +25,10 @@ #include #include #include +#include +#include + +#include enum { REG_MANUFACTURER_DATA, @@ -141,8 +145,13 @@ static enum power_supply_property bq20z75_properties[] = { }; struct bq20z75_info { - struct i2c_client *client; - struct power_supply power_supply; + struct i2c_client *client; + struct power_supply power_supply; + struct bq20z75_platform_data *pdata; + bool is_present; + bool gpio_detect; + bool enable_detection; + int irq; }; static int bq20z75_read_word_data(struct i2c_client *client, u8 address) @@ -179,6 +188,18 @@ static int bq20z75_get_battery_presence_and_health( union power_supply_propval *val) { s32 ret; + struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); + + if (psp == POWER_SUPPLY_PROP_PRESENT && + bq20z75_device->gpio_detect) { + ret = gpio_get_value( + bq20z75_device->pdata->battery_detect); + if (ret == bq20z75_device->pdata->battery_detect_present) + val->intval = 1; + else + val->intval = 0; + return ret; + } /* Write to ManufacturerAccess with * ManufacturerAccess command and then @@ -192,8 +213,11 @@ static int bq20z75_get_battery_presence_and_health( ret = bq20z75_read_word_data(client, bq20z75_data[REG_MANUFACTURER_DATA].addr); - if (ret < 0) + if (ret < 0) { + if (psp == POWER_SUPPLY_PROP_PRESENT) + val->intval = 0; /* battery removed */ return ret; + } if (ret < bq20z75_data[REG_MANUFACTURER_DATA].min_value || ret > bq20z75_data[REG_MANUFACTURER_DATA].max_value) { @@ -397,8 +421,7 @@ static int bq20z75_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - int ps_index; - int ret; + int ret = 0; struct bq20z75_info *bq20z75_device = container_of(psy, struct bq20z75_info, power_supply); struct i2c_client *client = bq20z75_device->client; @@ -407,8 +430,6 @@ static int bq20z75_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_PRESENT: case POWER_SUPPLY_PROP_HEALTH: ret = bq20z75_get_battery_presence_and_health(client, psp, val); - if (ret) - return ret; break; case POWER_SUPPLY_PROP_TECHNOLOGY: @@ -422,20 +443,15 @@ static int bq20z75_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CHARGE_FULL: case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: case POWER_SUPPLY_PROP_CAPACITY: - ps_index = bq20z75_get_property_index(client, psp); - if (ps_index < 0) - return ps_index; - - ret = bq20z75_get_battery_capacity(client, ps_index, psp, val); - if (ret) - return ret; + ret = bq20z75_get_property_index(client, psp); + if (ret < 0) + break; + ret = bq20z75_get_battery_capacity(client, ret, psp, val); break; case POWER_SUPPLY_PROP_SERIAL_NUMBER: ret = bq20z75_get_battery_serial_number(client, val); - if (ret) - return ret; break; case POWER_SUPPLY_PROP_STATUS: @@ -446,14 +462,11 @@ static int bq20z75_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: - ps_index = bq20z75_get_property_index(client, psp); - if (ps_index < 0) - return ps_index; - - ret = bq20z75_get_battery_property(client, ps_index, psp, val); - if (ret) - return ret; + ret = bq20z75_get_property_index(client, psp); + if (ret < 0) + break; + ret = bq20z75_get_battery_property(client, ret, psp, val); break; default: @@ -462,26 +475,51 @@ static int bq20z75_get_property(struct power_supply *psy, return -EINVAL; } - /* Convert units to match requirements for power supply class */ - bq20z75_unit_adjustment(client, psp, val); + if (!bq20z75_device->enable_detection) + goto done; + + if (!bq20z75_device->gpio_detect && + bq20z75_device->is_present != (ret >= 0)) { + bq20z75_device->is_present = (ret >= 0); + power_supply_changed(&bq20z75_device->power_supply); + } + +done: + if (!ret) { + /* Convert units to match requirements for power supply class */ + bq20z75_unit_adjustment(client, psp, val); + } dev_dbg(&client->dev, "%s: property = %d, value = %d\n", __func__, psp, val->intval); - return 0; + return ret; } -static int bq20z75_probe(struct i2c_client *client, +static irqreturn_t bq20z75_irq(int irq, void *devid) +{ + struct power_supply *battery = devid; + + power_supply_changed(battery); + + return IRQ_HANDLED; +} + +static int __devinit bq20z75_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct bq20z75_info *bq20z75_device; + struct bq20z75_platform_data *pdata = client->dev.platform_data; int rc; + int irq; bq20z75_device = kzalloc(sizeof(struct bq20z75_info), GFP_KERNEL); if (!bq20z75_device) return -ENOMEM; bq20z75_device->client = client; + bq20z75_device->enable_detection = false; + bq20z75_device->gpio_detect = false; bq20z75_device->power_supply.name = "battery"; bq20z75_device->power_supply.type = POWER_SUPPLY_TYPE_BATTERY; bq20z75_device->power_supply.properties = bq20z75_properties; @@ -489,26 +527,86 @@ static int bq20z75_probe(struct i2c_client *client, ARRAY_SIZE(bq20z75_properties); bq20z75_device->power_supply.get_property = bq20z75_get_property; + if (pdata) { + bq20z75_device->gpio_detect = + gpio_is_valid(pdata->battery_detect); + bq20z75_device->pdata = pdata; + } + i2c_set_clientdata(client, bq20z75_device); + if (!bq20z75_device->gpio_detect) + goto skip_gpio; + + rc = gpio_request(pdata->battery_detect, dev_name(&client->dev)); + if (rc) { + dev_warn(&client->dev, "Failed to request gpio: %d\n", rc); + bq20z75_device->gpio_detect = false; + goto skip_gpio; + } + + rc = gpio_direction_input(pdata->battery_detect); + if (rc) { + dev_warn(&client->dev, "Failed to get gpio as input: %d\n", rc); + gpio_free(pdata->battery_detect); + bq20z75_device->gpio_detect = false; + goto skip_gpio; + } + + irq = gpio_to_irq(pdata->battery_detect); + if (irq <= 0) { + dev_warn(&client->dev, "Failed to get gpio as irq: %d\n", irq); + gpio_free(pdata->battery_detect); + bq20z75_device->gpio_detect = false; + goto skip_gpio; + } + + rc = request_irq(irq, bq20z75_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + dev_name(&client->dev), &bq20z75_device->power_supply); + if (rc) { + dev_warn(&client->dev, "Failed to request irq: %d\n", rc); + gpio_free(pdata->battery_detect); + bq20z75_device->gpio_detect = false; + goto skip_gpio; + } + + bq20z75_device->irq = irq; + +skip_gpio: + rc = power_supply_register(&client->dev, &bq20z75_device->power_supply); if (rc) { dev_err(&client->dev, "%s: Failed to register power supply\n", __func__); - kfree(bq20z75_device); - return rc; + goto exit_psupply; } dev_info(&client->dev, "%s: battery gas gauge device registered\n", client->name); return 0; + +exit_psupply: + if (bq20z75_device->irq) + free_irq(bq20z75_device->irq, &bq20z75_device->power_supply); + if (bq20z75_device->gpio_detect) + gpio_free(pdata->battery_detect); + + kfree(bq20z75_device); + + return rc; } -static int bq20z75_remove(struct i2c_client *client) +static int __devexit bq20z75_remove(struct i2c_client *client) { struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); + if (bq20z75_device->irq) + free_irq(bq20z75_device->irq, &bq20z75_device->power_supply); + if (bq20z75_device->gpio_detect) + gpio_free(bq20z75_device->pdata->battery_detect); + power_supply_unregister(&bq20z75_device->power_supply); kfree(bq20z75_device); bq20z75_device = NULL; @@ -544,7 +642,7 @@ static const struct i2c_device_id bq20z75_id[] = { static struct i2c_driver bq20z75_battery_driver = { .probe = bq20z75_probe, - .remove = bq20z75_remove, + .remove = __devexit_p(bq20z75_remove), .suspend = bq20z75_suspend, .resume = bq20z75_resume, .id_table = bq20z75_id, -- cgit v1.2.3 From ff28fcef1bedcfbdf49500fee1573dc2f3eedb22 Mon Sep 17 00:00:00 2001 From: Rhyland Klein Date: Mon, 28 Feb 2011 16:55:29 -0800 Subject: bq20z75: Add i2c retry mechanism With the support of platform data, now adding support for option i2c retries on read/write failures. Ths is specified through the optional platform data. Signed-off-by: Rhyland Klein Signed-off-by: Anton Vorontsov --- drivers/power/bq20z75.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/power/bq20z75.c b/drivers/power/bq20z75.c index a51e98d74d34..e82d10e25e8c 100644 --- a/drivers/power/bq20z75.c +++ b/drivers/power/bq20z75.c @@ -156,30 +156,55 @@ struct bq20z75_info { static int bq20z75_read_word_data(struct i2c_client *client, u8 address) { - s32 ret; + struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); + s32 ret = 0; + int retries = 1; + + if (bq20z75_device->pdata) + retries = max(bq20z75_device->pdata->i2c_retry_count + 1, 1); + + while (retries > 0) { + ret = i2c_smbus_read_word_data(client, address); + if (ret >= 0) + break; + retries--; + } - ret = i2c_smbus_read_word_data(client, address); if (ret < 0) { - dev_err(&client->dev, + dev_warn(&client->dev, "%s: i2c read at address 0x%x failed\n", __func__, address); return ret; } + return le16_to_cpu(ret); } static int bq20z75_write_word_data(struct i2c_client *client, u8 address, u16 value) { - s32 ret; + struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); + s32 ret = 0; + int retries = 1; + + if (bq20z75_device->pdata) + retries = max(bq20z75_device->pdata->i2c_retry_count + 1, 1); + + while (retries > 0) { + ret = i2c_smbus_write_word_data(client, address, + le16_to_cpu(value)); + if (ret >= 0) + break; + retries--; + } - ret = i2c_smbus_write_word_data(client, address, le16_to_cpu(value)); if (ret < 0) { - dev_err(&client->dev, + dev_warn(&client->dev, "%s: i2c write to address 0x%x failed\n", __func__, address); return ret; } + return 0; } -- cgit v1.2.3 From f3da0dee31689130a06f12f06575c7a5b62421ad Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 1 Mar 2011 17:22:33 +0800 Subject: bq20z75: Add MODULE_DEVICE_TABLE The device table is required to load modules based on modaliases. After adding the MODULE_DEVICE_TABLE, below entry will be added to modules.alias: alias i2c:bq20z75 bq20z75 Signed-off-by: Axel Lin Signed-off-by: Anton Vorontsov --- drivers/power/bq20z75.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/power/bq20z75.c b/drivers/power/bq20z75.c index e82d10e25e8c..8018e8cc7a74 100644 --- a/drivers/power/bq20z75.c +++ b/drivers/power/bq20z75.c @@ -664,6 +664,7 @@ static const struct i2c_device_id bq20z75_id[] = { { "bq20z75", 0 }, {} }; +MODULE_DEVICE_TABLE(i2c, bq20z75_id); static struct i2c_driver bq20z75_battery_driver = { .probe = bq20z75_probe, -- cgit v1.2.3 From 84ab16f54bbde67c8c2aabc36b034c91044935cb Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 1 Mar 2011 17:23:34 +0800 Subject: ds2782_battery: Add MODULE_DEVICE_TABLE The device table is required to load modules based on modaliases. After adding the MODULE_DEVICE_TABLE, below entries will be added to modules.alias: alias i2c:ds2786 ds2782_battery alias i2c:ds2782 ds2782_battery Signed-off-by: Axel Lin Signed-off-by: Anton Vorontsov --- drivers/power/ds2782_battery.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/power/ds2782_battery.c b/drivers/power/ds2782_battery.c index 6957e8af6449..4d2dc4fa2888 100644 --- a/drivers/power/ds2782_battery.c +++ b/drivers/power/ds2782_battery.c @@ -393,6 +393,7 @@ static const struct i2c_device_id ds278x_id[] = { {"ds2786", DS2786}, {}, }; +MODULE_DEVICE_TABLE(i2c, ds278x_id); static struct i2c_driver ds278x_battery_driver = { .driver = { -- cgit v1.2.3 From dcbc9169ed3ce7cd12328fec8b9eb9f7b91ce130 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 1 Mar 2011 17:24:34 +0800 Subject: z2_battery: Add MODULE_DEVICE_TABLE The device table is required to load modules based on modaliases. After adding the MODULE_DEVICE_TABLE, below entry will be added to modules.alias: alias i2c:aer915 z2_battery Signed-off-by: Axel Lin Signed-off-by: Anton Vorontsov --- drivers/power/z2_battery.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/power/z2_battery.c b/drivers/power/z2_battery.c index e5ed52d71937..81304c0bb9d3 100644 --- a/drivers/power/z2_battery.c +++ b/drivers/power/z2_battery.c @@ -293,6 +293,7 @@ static const struct i2c_device_id z2_batt_id[] = { { "aer915", 0 }, { } }; +MODULE_DEVICE_TABLE(i2c, z2_batt_id); static struct i2c_driver z2_batt_driver = { .driver = { -- cgit v1.2.3 From f8d878ddbb3c08bc2f876975df674484208536ac Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Mon, 14 Mar 2011 21:12:39 +0100 Subject: s3c_adc_battery: Fix method names when PM not set s3c_adc_battery declares wrong method names when CONFIG_PM is not set. Signed-off-by: Heiko Stuebner Acked-by: Vasily Khoruzhick Signed-off-by: Anton Vorontsov --- drivers/power/s3c_adc_battery.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/power/s3c_adc_battery.c b/drivers/power/s3c_adc_battery.c index 4255f2358b13..d36c289aaef5 100644 --- a/drivers/power/s3c_adc_battery.c +++ b/drivers/power/s3c_adc_battery.c @@ -406,8 +406,8 @@ static int s3c_adc_bat_resume(struct platform_device *pdev) return 0; } #else -#define s3c_adc_battery_suspend NULL -#define s3c_adc_battery_resume NULL +#define s3c_adc_bat_suspend NULL +#define s3c_adc_bat_resume NULL #endif static struct platform_driver s3c_adc_bat_driver = { -- cgit v1.2.3 From a223246f7280422f77c15b71455fe3362d3604b4 Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Fri, 11 Mar 2011 15:09:32 +0200 Subject: z2_battery: Fix count of properties PROP_STATUS property was not counted, as result VOLTAGE_MIN property is missing in sysfs. Fix it. Signed-off-by: Vasily Khoruzhick Acked-by: Marek Vasut Signed-off-by: Anton Vorontsov --- drivers/power/z2_battery.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/power/z2_battery.c b/drivers/power/z2_battery.c index 81304c0bb9d3..2a9ab89f83b8 100644 --- a/drivers/power/z2_battery.c +++ b/drivers/power/z2_battery.c @@ -134,6 +134,8 @@ static int z2_batt_ps_init(struct z2_charger *charger, int props) enum power_supply_property *prop; struct z2_battery_info *info = charger->info; + if (info->charge_gpio >= 0) + props++; /* POWER_SUPPLY_PROP_STATUS */ if (info->batt_tech >= 0) props++; /* POWER_SUPPLY_PROP_TECHNOLOGY */ if (info->batt_I2C_reg >= 0) -- cgit v1.2.3 From a7d9ace4eb3a6a3c3fcb12fb05510c1893808263 Mon Sep 17 00:00:00 2001 From: Rhyland Klein Date: Wed, 9 Mar 2011 16:18:02 -0800 Subject: bq20z75: Fix issues with present and suspend There are a few issues found around the battery not being present. If the battery isn't present, then a few undesirable things happen. The first was excessive reporting of failed properties. This was fixed by instead returning ENODATA for all properties other than PRESENT if the battery isn't present. That way the callers can identify the difference between a failure and the battery not being there. The next issue was in the suspend logic. It was found that if the battery wasn't present, then it would return a failure, preventing the system from going into suspend. If there is no battery present, the io is expected to fail, so in that case, we shouldn't return the failure and just acknowledge that it was expected. I also found that when a gpio was used, i didn't maintain the internal is_present state properly. I added a set of that to fix that. Lastly, the code to see io's fail and figure out that the battery isn't present when not using a gpio had a problem. In that code, it looked for the read to fail and if it did, then handled it. The problem is that in function to get the property, it first writes a value and that write can fail, causing the code to never reach the logic after the read. Fix is to move the logic till after the write. Signed-off-by: Rhyland Klein Signed-off-by: Anton Vorontsov --- drivers/power/bq20z75.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/power/bq20z75.c b/drivers/power/bq20z75.c index 8018e8cc7a74..5615c92bef66 100644 --- a/drivers/power/bq20z75.c +++ b/drivers/power/bq20z75.c @@ -171,7 +171,7 @@ static int bq20z75_read_word_data(struct i2c_client *client, u8 address) } if (ret < 0) { - dev_warn(&client->dev, + dev_dbg(&client->dev, "%s: i2c read at address 0x%x failed\n", __func__, address); return ret; @@ -199,7 +199,7 @@ static int bq20z75_write_word_data(struct i2c_client *client, u8 address, } if (ret < 0) { - dev_warn(&client->dev, + dev_dbg(&client->dev, "%s: i2c write to address 0x%x failed\n", __func__, address); return ret; @@ -223,6 +223,7 @@ static int bq20z75_get_battery_presence_and_health( val->intval = 1; else val->intval = 0; + bq20z75_device->is_present = val->intval; return ret; } @@ -232,18 +233,17 @@ static int bq20z75_get_battery_presence_and_health( ret = bq20z75_write_word_data(client, bq20z75_data[REG_MANUFACTURER_DATA].addr, MANUFACTURER_ACCESS_STATUS); - if (ret < 0) - return ret; - - - ret = bq20z75_read_word_data(client, - bq20z75_data[REG_MANUFACTURER_DATA].addr); if (ret < 0) { if (psp == POWER_SUPPLY_PROP_PRESENT) val->intval = 0; /* battery removed */ return ret; } + ret = bq20z75_read_word_data(client, + bq20z75_data[REG_MANUFACTURER_DATA].addr); + if (ret < 0) + return ret; + if (ret < bq20z75_data[REG_MANUFACTURER_DATA].min_value || ret > bq20z75_data[REG_MANUFACTURER_DATA].max_value) { val->intval = 0; @@ -455,6 +455,8 @@ static int bq20z75_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_PRESENT: case POWER_SUPPLY_PROP_HEALTH: ret = bq20z75_get_battery_presence_and_health(client, psp, val); + if (psp == POWER_SUPPLY_PROP_PRESENT) + return 0; break; case POWER_SUPPLY_PROP_TECHNOLOGY: @@ -516,9 +518,16 @@ done: } dev_dbg(&client->dev, - "%s: property = %d, value = %d\n", __func__, psp, val->intval); + "%s: property = %d, value = %x\n", __func__, psp, val->intval); + + if (ret && bq20z75_device->is_present) + return ret; + + /* battery not present, so return NODATA for properties */ + if (ret) + return -ENODATA; - return ret; + return 0; } static irqreturn_t bq20z75_irq(int irq, void *devid) @@ -643,13 +652,14 @@ static int __devexit bq20z75_remove(struct i2c_client *client) static int bq20z75_suspend(struct i2c_client *client, pm_message_t state) { + struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); s32 ret; /* write to manufacturer access with sleep command */ ret = bq20z75_write_word_data(client, bq20z75_data[REG_MANUFACTURER_DATA].addr, MANUFACTURER_ACCESS_SLEEP); - if (ret < 0) + if (bq20z75_device->is_present && ret < 0) return ret; return 0; -- cgit v1.2.3 From 909a78b320b31ca9709d7ea5f602ec1bd2015192 Mon Sep 17 00:00:00 2001 From: Benson Leung Date: Sun, 27 Feb 2011 17:41:48 -0800 Subject: bq20z75: Fix time and temp units MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Corrected temperature and time to empty/full conversions. Temperature is in 0.1°C, time is in seconds. Corrected units in comment. "Convert to µWh." Signed-off-by: Benson Leung Acked-by: Rhyland Klein Signed-off-by: Anton Vorontsov --- drivers/power/bq20z75.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/power/bq20z75.c b/drivers/power/bq20z75.c index 5615c92bef66..506585e31a5b 100644 --- a/drivers/power/bq20z75.c +++ b/drivers/power/bq20z75.c @@ -318,12 +318,15 @@ static void bq20z75_unit_adjustment(struct i2c_client *client, { #define BASE_UNIT_CONVERSION 1000 #define BATTERY_MODE_CAP_MULT_WATT (10 * BASE_UNIT_CONVERSION) -#define TIME_UNIT_CONVERSION 600 -#define TEMP_KELVIN_TO_CELCIUS 2731 +#define TIME_UNIT_CONVERSION 60 +#define TEMP_KELVIN_TO_CELSIUS 2731 switch (psp) { case POWER_SUPPLY_PROP_ENERGY_NOW: case POWER_SUPPLY_PROP_ENERGY_FULL: case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: + /* bq20z75 provides energy in units of 10mWh. + * Convert to µWh + */ val->intval *= BATTERY_MODE_CAP_MULT_WATT; break; @@ -337,14 +340,17 @@ static void bq20z75_unit_adjustment(struct i2c_client *client, break; case POWER_SUPPLY_PROP_TEMP: - /* bq20z75 provides battery tempreture in 0.1°K - * so convert it to 0.1°C */ - val->intval -= TEMP_KELVIN_TO_CELCIUS; - val->intval *= 10; + /* bq20z75 provides battery temperature in 0.1K + * so convert it to 0.1°C + */ + val->intval -= TEMP_KELVIN_TO_CELSIUS; break; case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: + /* bq20z75 provides time to empty and time to full in minutes. + * Convert to seconds + */ val->intval *= TIME_UNIT_CONVERSION; break; -- cgit v1.2.3