From e82374fd1a804e197fc2a54c3930e70c5d300abc Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Mon, 11 Aug 2008 22:22:27 +0200 Subject: pda_power: Check and handle return value of set_irq_wake The recent change in commit 2db873211ba47ef704c301f9ecf4a33413a0b649 forces the calls enable_irq_wake() and disable_irq_wake() to be balanced. But if in pda_power_suspend() the call to enable_irq_wake() fails (because attached gpio cannot wake up the CPU), the corresponding disable_irq_wake will WARN(). Fix it by storing success/failure of enable_irq_wake(). Signed-off-by: Robert Jarzmik Signed-off-by: Anton Vorontsov --- drivers/power/pda_power.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c index 0471ec743ab..d30bb766fce 100644 --- a/drivers/power/pda_power.c +++ b/drivers/power/pda_power.c @@ -334,13 +334,16 @@ static int pda_power_remove(struct platform_device *pdev) } #ifdef CONFIG_PM +static int ac_wakeup_enabled; +static int usb_wakeup_enabled; + static int pda_power_suspend(struct platform_device *pdev, pm_message_t state) { if (device_may_wakeup(&pdev->dev)) { if (ac_irq) - enable_irq_wake(ac_irq->start); + ac_wakeup_enabled = !enable_irq_wake(ac_irq->start); if (usb_irq) - enable_irq_wake(usb_irq->start); + usb_wakeup_enabled = !enable_irq_wake(usb_irq->start); } return 0; @@ -349,9 +352,9 @@ static int pda_power_suspend(struct platform_device *pdev, pm_message_t state) static int pda_power_resume(struct platform_device *pdev) { if (device_may_wakeup(&pdev->dev)) { - if (usb_irq) + if (usb_irq && usb_wakeup_enabled) disable_irq_wake(usb_irq->start); - if (ac_irq) + if (ac_irq && ac_wakeup_enabled) disable_irq_wake(ac_irq->start); } -- cgit v1.2.3-70-g09d2 From 942ed161944b3476639916cf544e6975b29c985a Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Tue, 26 Aug 2008 21:09:59 +0100 Subject: power_supply: Add function to return system-wide power state Certain drivers benefit from knowing whether the system is on ac or battery, for instance when determining which backlight registers to read. This adds a simple call to determine whether there's an online power supply other than any batteries. Signed-off-by: Matthew Garrett Signed-off-by: Anton Vorontsov --- drivers/power/power_supply_core.c | 25 +++++++++++++++++++++++++ include/linux/power_supply.h | 6 ++++++ 2 files changed, 31 insertions(+) diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index cb1ccb47292..f44f5b608f6 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -87,6 +87,30 @@ int power_supply_am_i_supplied(struct power_supply *psy) return error; } +static int __power_supply_is_system_supplied(struct device *dev, void *data) +{ + union power_supply_propval ret = {0,}; + struct power_supply *psy = dev_get_drvdata(dev); + + if (psy->type != POWER_SUPPLY_TYPE_BATTERY) { + if (psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &ret)) + return 0; + if (ret.intval) + return ret.intval; + } + return 0; +} + +int power_supply_is_system_supplied(void) +{ + int error; + + error = class_for_each_device(power_supply_class, NULL, NULL, + __power_supply_is_system_supplied); + + return error; +} + int power_supply_register(struct device *parent, struct power_supply *psy) { int rc = 0; @@ -148,6 +172,7 @@ static void __exit power_supply_class_exit(void) EXPORT_SYMBOL_GPL(power_supply_changed); EXPORT_SYMBOL_GPL(power_supply_am_i_supplied); +EXPORT_SYMBOL_GPL(power_supply_is_system_supplied); EXPORT_SYMBOL_GPL(power_supply_register); EXPORT_SYMBOL_GPL(power_supply_unregister); diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index ea96ead1d39..f9348cba6dc 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -165,6 +165,12 @@ struct power_supply_info { extern void power_supply_changed(struct power_supply *psy); extern int power_supply_am_i_supplied(struct power_supply *psy); +#if defined(CONFIG_POWER_SUPPLY) || defined(CONFIG_POWER_SUPPLY_MODULE) +extern int power_supply_is_system_supplied(void); +#else +static inline int power_supply_is_system_supplied(void) { return -ENOSYS; } +#endif + extern int power_supply_register(struct device *parent, struct power_supply *psy); extern void power_supply_unregister(struct power_supply *psy); -- cgit v1.2.3-70-g09d2 From b996ad0e9fb15ca4acc60bcd0380912117a45d13 Mon Sep 17 00:00:00 2001 From: Rodolfo Giometti Date: Wed, 20 Aug 2008 16:52:58 -0700 Subject: power_supply: Support for Texas Instruments BQ27200 battery managers These battery managers came in two different packages: one for I2C busses (BQ27200) and one for HDQ busses (BQ27000). This driver currently supports only the I2C chip version but the code is designed in order to easily allow the HDQ chip version integration. [akpm@linux-foundation.org: coding-style fixes] [akpm@linux-foundation.org: make things static, use kasprintf()] Signed-off-by: Rodolfo Giometti Cc: David Woodhouse Signed-off-by: Andrew Morton Signed-off-by: Anton Vorontsov --- drivers/power/Kconfig | 6 + drivers/power/Makefile | 1 + drivers/power/bq27x00_battery.c | 382 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 389 insertions(+) create mode 100644 drivers/power/bq27x00_battery.c diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 9ce55850271..b2bd104b986 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -62,4 +62,10 @@ config BATTERY_PALMTX help Say Y to enable support for the battery in Palm T|X. +config BATTERY_BQ27x00 + tristate "BQ27200 battery driver" + depends on I2C + help + Say Y here to enable support for batteries with BQ27200(I2C) chip. + endif # POWER_SUPPLY diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 4706bf8ff45..6cb301b779a 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -22,3 +22,4 @@ obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o obj-$(CONFIG_BATTERY_PALMTX) += palmtx_battery.o +obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c new file mode 100644 index 00000000000..62d4948e820 --- /dev/null +++ b/drivers/power/bq27x00_battery.c @@ -0,0 +1,382 @@ +/* + * BQ27x00 battery driver + * + * Copyright (C) 2008 Rodolfo Giometti + * Copyright (C) 2008 Eurotech S.p.A. + * + * Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DRIVER_VERSION "1.0.0" + +#define BQ27x00_REG_TEMP 0x06 +#define BQ27x00_REG_VOLT 0x08 +#define BQ27x00_REG_RSOC 0x0B /* Relative State-of-Charge */ +#define BQ27x00_REG_AI 0x14 +#define BQ27x00_REG_FLAGS 0x0A +#define HIGH_BYTE(A) ((A) << 8) + +/* 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)(u8 reg, int *rt_value, int b_single, + struct bq27x00_device_info *di); +}; + +struct bq27x00_device_info { + struct device *dev; + int id; + int voltage_uV; + int current_uA; + int temp_C; + int charge_rsoc; + struct bq27x00_access_methods *bus; + struct power_supply bat; + + struct i2c_client *client; +}; + +static enum power_supply_property bq27x00_battery_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_TEMP, +}; + +/* + * Common code for BQ27x00 devices + */ + +static int bq27x00_read(u8 reg, int *rt_value, int b_single, + struct bq27x00_device_info *di) +{ + int ret; + + ret = di->bus->read(reg, rt_value, b_single, di); + *rt_value = be16_to_cpu(*rt_value); + + return ret; +} + +/* + * Return the battery temperature in Celcius degrees + * Or < 0 if something fails. + */ +static int bq27x00_battery_temperature(struct bq27x00_device_info *di) +{ + int ret; + int temp = 0; + + ret = bq27x00_read(BQ27x00_REG_TEMP, &temp, 0, di); + if (ret) { + dev_err(di->dev, "error reading temperature\n"); + return ret; + } + + return (temp >> 2) - 273; +} + +/* + * Return the battery Voltage in milivolts + * Or < 0 if something fails. + */ +static int bq27x00_battery_voltage(struct bq27x00_device_info *di) +{ + int ret; + int volt = 0; + + ret = bq27x00_read(BQ27x00_REG_VOLT, &volt, 0, di); + if (ret) { + dev_err(di->dev, "error reading voltage\n"); + return ret; + } + + return volt; +} + +/* + * Return the battery average current + * Note that current can be negative signed as well + * Or 0 if something fails. + */ +static int bq27x00_battery_current(struct bq27x00_device_info *di) +{ + int ret; + int curr = 0; + int flags = 0; + + ret = bq27x00_read(BQ27x00_REG_AI, &curr, 0, di); + if (ret) { + dev_err(di->dev, "error reading current\n"); + return 0; + } + ret = bq27x00_read(BQ27x00_REG_FLAGS, &flags, 0, di); + if (ret < 0) { + dev_err(di->dev, "error reading flags\n"); + return 0; + } + if ((flags & (1 << 7)) != 0) { + dev_dbg(di->dev, "negative current!\n"); + return -curr; + } + 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; + + ret = bq27x00_read(BQ27x00_REG_RSOC, &rsoc, 1, di); + if (ret) { + dev_err(di->dev, "error reading relative State-of-Charge\n"); + return ret; + } + + return rsoc >> 8; +} + +#define to_bq27x00_device_info(x) container_of((x), \ + struct bq27x00_device_info, bat); + +static int bq27x00_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct bq27x00_device_info *di = to_bq27x00_device_info(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + case POWER_SUPPLY_PROP_PRESENT: + val->intval = bq27x00_battery_voltage(di); + if (psp == POWER_SUPPLY_PROP_PRESENT) + val->intval = val->intval <= 0 ? 0 : 1; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = bq27x00_battery_current(di); + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = bq27x00_battery_rsoc(di); + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = bq27x00_battery_temperature(di); + break; + default: + return -EINVAL; + } + + return 0; +} + +static void bq27x00_powersupply_init(struct bq27x00_device_info *di) +{ + 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; +} + +/* + * BQ27200 specific code + */ + +static int bq27200_read(u8 reg, int *rt_value, int b_single, + struct bq27x00_device_info *di) +{ + struct i2c_client *client = di->client; + struct i2c_msg msg[1]; + unsigned char data[2]; + int err; + + if (!client->adapter) + return -ENODEV; + + msg->addr = client->addr; + msg->flags = 0; + msg->len = 1; + msg->buf = data; + + data[0] = reg; + err = i2c_transfer(client->adapter, msg, 1); + + if (err >= 0) { + if (!b_single) + msg->len = 2; + else + msg->len = 1; + + msg->flags = I2C_M_RD; + err = i2c_transfer(client->adapter, msg, 1); + if (err >= 0) { + if (!b_single) + *rt_value = data[1] | HIGH_BYTE(data[0]); + else + *rt_value = data[0]; + + return 0; + } + } + return err; +} + +static int bq27200_battery_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + char *name; + struct bq27x00_device_info *di; + struct bq27x00_access_methods *bus; + int num; + int retval = 0; + + /* Get new ID for the new battery device */ + retval = idr_pre_get(&battery_id, GFP_KERNEL); + if (retval == 0) + return -ENOMEM; + mutex_lock(&battery_mutex); + retval = idr_get_new(&battery_id, client, &num); + mutex_unlock(&battery_mutex); + if (retval < 0) + return retval; + + name = kasprintf(GFP_KERNEL, "bq27200-%d", num); + if (!name) { + dev_err(&client->dev, "failed to allocate device name\n"); + retval = -ENOMEM; + goto batt_failed_1; + } + + di = kzalloc(sizeof(*di), GFP_KERNEL); + if (!di) { + dev_err(&client->dev, "failed to allocate device info data\n"); + retval = -ENOMEM; + goto batt_failed_2; + } + di->id = num; + + bus = kzalloc(sizeof(*bus), GFP_KERNEL); + if (!bus) { + dev_err(&client->dev, "failed to allocate access method " + "data\n"); + retval = -ENOMEM; + goto batt_failed_3; + } + + i2c_set_clientdata(client, di); + di->dev = &client->dev; + di->bat.name = name; + bus->read = &bq27200_read; + 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: + kfree(name); +batt_failed_1: + mutex_lock(&battery_mutex); + idr_remove(&battery_id, num); + mutex_unlock(&battery_mutex); + + return retval; +} + +static int bq27200_battery_remove(struct i2c_client *client) +{ + struct bq27x00_device_info *di = i2c_get_clientdata(client); + + power_supply_unregister(&di->bat); + + kfree(di->bat.name); + + mutex_lock(&battery_mutex); + idr_remove(&battery_id, di->id); + mutex_unlock(&battery_mutex); + + kfree(di); + + return 0; +} + +/* + * Module stuff + */ + +static const struct i2c_device_id bq27200_id[] = { + { "bq27200", 0 }, + {}, +}; + +static struct i2c_driver bq27200_battery_driver = { + .driver = { + .name = "bq27200-battery", + }, + .probe = bq27200_battery_probe, + .remove = bq27200_battery_remove, + .id_table = bq27200_id, +}; + +static int __init bq27x00_battery_init(void) +{ + int ret; + + ret = i2c_add_driver(&bq27200_battery_driver); + if (ret) + printk(KERN_ERR "Unable to register BQ27200 driver\n"); + + return ret; +} +module_init(bq27x00_battery_init); + +static void __exit bq27x00_battery_exit(void) +{ + i2c_del_driver(&bq27200_battery_driver); +} +module_exit(bq27x00_battery_exit); + +MODULE_AUTHOR("Rodolfo Giometti "); +MODULE_DESCRIPTION("BQ27x00 battery monitor driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From 6a9037887ccea92152b034edeb15d453d1a98555 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Mon, 22 Sep 2008 14:06:01 -0700 Subject: power_supply: fix dependency of tosa_battery tosa_battery should also depend on wm97xx_ts as it uses dac-accessing functions from that module. Signed-off-by: Dmitry Baryshkov Signed-off-by: Andrew Morton Signed-off-by: Anton Vorontsov --- drivers/power/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index b2bd104b986..ae095a498db 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -51,7 +51,7 @@ config BATTERY_OLPC config BATTERY_TOSA tristate "Sharp SL-6000 (tosa) battery" - depends on MACH_TOSA && MFD_TC6393XB + depends on MACH_TOSA && MFD_TC6393XB && TOUCHSCREEN_WM97XX help Say Y to enable support for the battery on the Sharp Zaurus SL-6000 (tosa) models. -- cgit v1.2.3-70-g09d2 From 8aef7e8f8de2d900da892085edbf14ea35fe6881 Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Mon, 22 Sep 2008 14:53:50 -0700 Subject: bq27x00_battery: use unaligned access helper Remove hand-rolled get_unaligned_be16, this points to a possible bug as bq27x00_read does another endian byteswap which sparse notices: drivers/power/bq27x00_battery.c:81:14: warning: cast to restricted __be16 Which should probably be checked. Signed-off-by: Harvey Harrison Cc: Rodolfo Giometti Signed-off-by: Andrew Morton Signed-off-by: Anton Vorontsov --- drivers/power/bq27x00_battery.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 62d4948e820..0c056fcc01c 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -23,8 +23,8 @@ #include #include #include - #include +#include #define DRIVER_VERSION "1.0.0" @@ -33,7 +33,6 @@ #define BQ27x00_REG_RSOC 0x0B /* Relative State-of-Charge */ #define BQ27x00_REG_AI 0x14 #define BQ27x00_REG_FLAGS 0x0A -#define HIGH_BYTE(A) ((A) << 8) /* If the system has several batteries we need a different name for each * of them... @@ -239,7 +238,7 @@ static int bq27200_read(u8 reg, int *rt_value, int b_single, err = i2c_transfer(client->adapter, msg, 1); if (err >= 0) { if (!b_single) - *rt_value = data[1] | HIGH_BYTE(data[0]); + *rt_value = get_unaligned_be16(data); else *rt_value = data[0]; -- cgit v1.2.3-70-g09d2