diff options
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/Kconfig | 16 | ||||
-rw-r--r-- | drivers/power/Makefile | 3 | ||||
-rw-r--r-- | drivers/power/bq27x00_battery.c | 381 | ||||
-rw-r--r-- | drivers/power/olpc_battery.c | 20 | ||||
-rw-r--r-- | drivers/power/palmtx_battery.c | 198 | ||||
-rw-r--r-- | drivers/power/pda_power.c | 11 | ||||
-rw-r--r-- | drivers/power/power_supply_core.c | 29 | ||||
-rw-r--r-- | drivers/power/power_supply_sysfs.c | 2 | ||||
-rw-r--r-- | drivers/power/wm97xx_battery.c | 272 |
9 files changed, 709 insertions, 223 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 9ce55850271a..8e0c2b47803c 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -51,15 +51,21 @@ 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. -config BATTERY_PALMTX - tristate "Palm T|X battery" - depends on MACH_PALMTX +config BATTERY_WM97XX + bool "WM97xx generic battery driver" + depends on TOUCHSCREEN_WM97XX=y help - Say Y to enable support for the battery in Palm T|X. + Say Y to enable support for battery measured by WM97xx aux port. + +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 4706bf8ff459..e8f1ecec5d8f 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -21,4 +21,5 @@ obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o 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_WM97XX) += wm97xx_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 000000000000..0c056fcc01ce --- /dev/null +++ b/drivers/power/bq27x00_battery.c @@ -0,0 +1,381 @@ +/* + * BQ27x00 battery driver + * + * Copyright (C) 2008 Rodolfo Giometti <giometti@linux.it> + * Copyright (C) 2008 Eurotech S.p.A. <info@eurotech.it> + * + * 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 <linux/module.h> +#include <linux/param.h> +#include <linux/jiffies.h> +#include <linux/workqueue.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/idr.h> +#include <linux/i2c.h> +#include <asm/unaligned.h> + +#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 + +/* 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 = get_unaligned_be16(data); + 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 <giometti@linux.it>"); +MODULE_DESCRIPTION("BQ27x00 battery monitor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/olpc_battery.c b/drivers/power/olpc_battery.c index 32570af3c5c9..5fbca2681baa 100644 --- a/drivers/power/olpc_battery.c +++ b/drivers/power/olpc_battery.c @@ -205,9 +205,9 @@ static int olpc_bat_get_property(struct power_supply *psy, union power_supply_propval *val) { int ret = 0; - int16_t ec_word; + __be16 ec_word; uint8_t ec_byte; - uint64_t ser_buf; + __be64 ser_buf; ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &ec_byte, 1); if (ret) @@ -257,16 +257,14 @@ static int olpc_bat_get_property(struct power_supply *psy, if (ret) return ret; - ec_word = be16_to_cpu(ec_word); - val->intval = ec_word * 9760L / 32; + val->intval = (int)be16_to_cpu(ec_word) * 9760L / 32; break; case POWER_SUPPLY_PROP_CURRENT_AVG: ret = olpc_ec_cmd(EC_BAT_CURRENT, NULL, 0, (void *)&ec_word, 2); if (ret) return ret; - ec_word = be16_to_cpu(ec_word); - val->intval = ec_word * 15625L / 120; + val->intval = (int)be16_to_cpu(ec_word) * 15625L / 120; break; case POWER_SUPPLY_PROP_CAPACITY: ret = olpc_ec_cmd(EC_BAT_SOC, NULL, 0, &ec_byte, 1); @@ -278,24 +276,22 @@ static int olpc_bat_get_property(struct power_supply *psy, ret = olpc_ec_cmd(EC_BAT_TEMP, NULL, 0, (void *)&ec_word, 2); if (ret) return ret; - ec_word = be16_to_cpu(ec_word); - val->intval = ec_word * 100 / 256; + + val->intval = (int)be16_to_cpu(ec_word) * 100 / 256; break; case POWER_SUPPLY_PROP_TEMP_AMBIENT: ret = olpc_ec_cmd(EC_AMB_TEMP, NULL, 0, (void *)&ec_word, 2); if (ret) return ret; - ec_word = be16_to_cpu(ec_word); - val->intval = ec_word * 100 / 256; + val->intval = (int)be16_to_cpu(ec_word) * 100 / 256; break; case POWER_SUPPLY_PROP_CHARGE_COUNTER: ret = olpc_ec_cmd(EC_BAT_ACR, NULL, 0, (void *)&ec_word, 2); if (ret) return ret; - ec_word = be16_to_cpu(ec_word); - val->intval = ec_word * 6250 / 15; + val->intval = (int)be16_to_cpu(ec_word) * 6250 / 15; break; case POWER_SUPPLY_PROP_SERIAL_NUMBER: ret = olpc_ec_cmd(EC_BAT_SERIAL, NULL, 0, (void *)&ser_buf, 8); diff --git a/drivers/power/palmtx_battery.c b/drivers/power/palmtx_battery.c deleted file mode 100644 index 7035bfa41c62..000000000000 --- a/drivers/power/palmtx_battery.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * linux/drivers/power/palmtx_battery.c - * - * Battery measurement code for Palm T|X Handheld computer - * - * based on tosa_battery.c - * - * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/power_supply.h> -#include <linux/wm97xx.h> -#include <linux/delay.h> -#include <linux/spinlock.h> -#include <linux/interrupt.h> -#include <linux/gpio.h> - -#include <asm/mach-types.h> -#include <mach/palmtx.h> - -static DEFINE_MUTEX(bat_lock); -static struct work_struct bat_work; -struct mutex work_lock; -int bat_status = POWER_SUPPLY_STATUS_DISCHARGING; - -static unsigned long palmtx_read_bat(struct power_supply *bat_ps) -{ - return wm97xx_read_aux_adc(bat_ps->dev->parent->driver_data, - WM97XX_AUX_ID3) * 1000 / 414; -} - -static unsigned long palmtx_read_temp(struct power_supply *bat_ps) -{ - return wm97xx_read_aux_adc(bat_ps->dev->parent->driver_data, - WM97XX_AUX_ID2); -} - -static int palmtx_bat_get_property(struct power_supply *bat_ps, - enum power_supply_property psp, - union power_supply_propval *val) -{ - switch (psp) { - case POWER_SUPPLY_PROP_STATUS: - val->intval = bat_status; - break; - case POWER_SUPPLY_PROP_TECHNOLOGY: - val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO; - break; - case POWER_SUPPLY_PROP_VOLTAGE_NOW: - val->intval = palmtx_read_bat(bat_ps); - break; - case POWER_SUPPLY_PROP_VOLTAGE_MAX: - case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: - val->intval = PALMTX_BAT_MAX_VOLTAGE; - break; - case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: - val->intval = PALMTX_BAT_MIN_VOLTAGE; - break; - case POWER_SUPPLY_PROP_TEMP: - val->intval = palmtx_read_temp(bat_ps); - break; - case POWER_SUPPLY_PROP_PRESENT: - val->intval = 1; - break; - default: - return -EINVAL; - } - return 0; -} - -static void palmtx_bat_external_power_changed(struct power_supply *bat_ps) -{ - schedule_work(&bat_work); -} - -static char *status_text[] = { - [POWER_SUPPLY_STATUS_UNKNOWN] = "Unknown", - [POWER_SUPPLY_STATUS_CHARGING] = "Charging", - [POWER_SUPPLY_STATUS_DISCHARGING] = "Discharging", -}; - -static void palmtx_bat_update(struct power_supply *bat_ps) -{ - int old_status = bat_status; - - mutex_lock(&work_lock); - - bat_status = gpio_get_value(GPIO_NR_PALMTX_POWER_DETECT) ? - POWER_SUPPLY_STATUS_CHARGING : - POWER_SUPPLY_STATUS_DISCHARGING; - - if (old_status != bat_status) { - pr_debug("%s %s -> %s\n", bat_ps->name, - status_text[old_status], - status_text[bat_status]); - power_supply_changed(bat_ps); - } - - mutex_unlock(&work_lock); -} - -static enum power_supply_property palmtx_bat_main_props[] = { - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_VOLTAGE_MAX, - POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_PRESENT, -}; - -struct power_supply bat_ps = { - .name = "main-battery", - .type = POWER_SUPPLY_TYPE_BATTERY, - .properties = palmtx_bat_main_props, - .num_properties = ARRAY_SIZE(palmtx_bat_main_props), - .get_property = palmtx_bat_get_property, - .external_power_changed = palmtx_bat_external_power_changed, - .use_for_apm = 1, -}; - -static void palmtx_bat_work(struct work_struct *work) -{ - palmtx_bat_update(&bat_ps); -} - -#ifdef CONFIG_PM -static int palmtx_bat_suspend(struct platform_device *dev, pm_message_t state) -{ - flush_scheduled_work(); - return 0; -} - -static int palmtx_bat_resume(struct platform_device *dev) -{ - schedule_work(&bat_work); - return 0; -} -#else -#define palmtx_bat_suspend NULL -#define palmtx_bat_resume NULL -#endif - -static int __devinit palmtx_bat_probe(struct platform_device *dev) -{ - int ret = 0; - - if (!machine_is_palmtx()) - return -ENODEV; - - mutex_init(&work_lock); - - INIT_WORK(&bat_work, palmtx_bat_work); - - ret = power_supply_register(&dev->dev, &bat_ps); - if (!ret) - schedule_work(&bat_work); - - return ret; -} - -static int __devexit palmtx_bat_remove(struct platform_device *dev) -{ - power_supply_unregister(&bat_ps); - return 0; -} - -static struct platform_driver palmtx_bat_driver = { - .driver.name = "wm97xx-battery", - .driver.owner = THIS_MODULE, - .probe = palmtx_bat_probe, - .remove = __devexit_p(palmtx_bat_remove), - .suspend = palmtx_bat_suspend, - .resume = palmtx_bat_resume, -}; - -static int __init palmtx_bat_init(void) -{ - return platform_driver_register(&palmtx_bat_driver); -} - -static void __exit palmtx_bat_exit(void) -{ - platform_driver_unregister(&palmtx_bat_driver); -} - -module_init(palmtx_bat_init); -module_exit(palmtx_bat_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); -MODULE_DESCRIPTION("Palm T|X battery driver"); diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c index 0471ec743ab9..d30bb766fcef 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); } diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index cb1ccb472921..5520040449c4 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -87,12 +87,36 @@ 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; - psy->dev = device_create_drvdata(power_supply_class, parent, 0, - psy, "%s", psy->name); + psy->dev = device_create(power_supply_class, parent, 0, psy, + "%s", psy->name); if (IS_ERR(psy->dev)) { rc = PTR_ERR(psy->dev); goto dev_create_failed; @@ -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/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index fe2aeb11939b..23ae8460f5c1 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -30,7 +30,7 @@ #define POWER_SUPPLY_ATTR(_name) \ { \ - .attr = { .name = #_name, .mode = 0444, .owner = THIS_MODULE }, \ + .attr = { .name = #_name, .mode = 0444 }, \ .show = power_supply_show_property, \ .store = NULL, \ } diff --git a/drivers/power/wm97xx_battery.c b/drivers/power/wm97xx_battery.c new file mode 100644 index 000000000000..8bde92126d34 --- /dev/null +++ b/drivers/power/wm97xx_battery.c @@ -0,0 +1,272 @@ +/* + * linux/drivers/power/wm97xx_battery.c + * + * Battery measurement code for WM97xx + * + * based on tosa_battery.c + * + * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/wm97xx.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/gpio.h> +#include <linux/wm97xx_batt.h> + +static DEFINE_MUTEX(bat_lock); +static struct work_struct bat_work; +struct mutex work_lock; +static int bat_status = POWER_SUPPLY_STATUS_UNKNOWN; +static struct wm97xx_batt_info *pdata; +static enum power_supply_property *prop; + +static unsigned long wm97xx_read_bat(struct power_supply *bat_ps) +{ + return wm97xx_read_aux_adc(bat_ps->dev->parent->driver_data, + pdata->batt_aux) * pdata->batt_mult / + pdata->batt_div; +} + +static unsigned long wm97xx_read_temp(struct power_supply *bat_ps) +{ + return wm97xx_read_aux_adc(bat_ps->dev->parent->driver_data, + pdata->temp_aux) * pdata->temp_mult / + pdata->temp_div; +} + +static int wm97xx_bat_get_property(struct power_supply *bat_ps, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = bat_status; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = pdata->batt_tech; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + if (pdata->batt_aux >= 0) + val->intval = wm97xx_read_bat(bat_ps); + else + return -EINVAL; + break; + case POWER_SUPPLY_PROP_TEMP: + if (pdata->temp_aux >= 0) + val->intval = wm97xx_read_temp(bat_ps); + else + return -EINVAL; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + if (pdata->max_voltage >= 0) + val->intval = pdata->max_voltage; + else + return -EINVAL; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN: + if (pdata->min_voltage >= 0) + val->intval = pdata->min_voltage; + else + return -EINVAL; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + default: + return -EINVAL; + } + return 0; +} + +static void wm97xx_bat_external_power_changed(struct power_supply *bat_ps) +{ + schedule_work(&bat_work); +} + +static void wm97xx_bat_update(struct power_supply *bat_ps) +{ + int old_status = bat_status; + + mutex_lock(&work_lock); + + bat_status = (pdata->charge_gpio >= 0) ? + (gpio_get_value(pdata->charge_gpio) ? + POWER_SUPPLY_STATUS_DISCHARGING : + POWER_SUPPLY_STATUS_CHARGING) : + POWER_SUPPLY_STATUS_UNKNOWN; + + if (old_status != bat_status) { + pr_debug("%s: %i -> %i\n", bat_ps->name, old_status, + bat_status); + power_supply_changed(bat_ps); + } + + mutex_unlock(&work_lock); +} + +static struct power_supply bat_ps = { + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = wm97xx_bat_get_property, + .external_power_changed = wm97xx_bat_external_power_changed, + .use_for_apm = 1, +}; + +static void wm97xx_bat_work(struct work_struct *work) +{ + wm97xx_bat_update(&bat_ps); +} + +#ifdef CONFIG_PM +static int wm97xx_bat_suspend(struct platform_device *dev, pm_message_t state) +{ + flush_scheduled_work(); + return 0; +} + +static int wm97xx_bat_resume(struct platform_device *dev) +{ + schedule_work(&bat_work); + return 0; +} +#else +#define wm97xx_bat_suspend NULL +#define wm97xx_bat_resume NULL +#endif + +static int __devinit wm97xx_bat_probe(struct platform_device *dev) +{ + int ret = 0; + int props = 1; /* POWER_SUPPLY_PROP_PRESENT */ + int i = 0; + + if (dev->id != -1) + return -EINVAL; + + mutex_init(&work_lock); + + if (!pdata) { + dev_err(&dev->dev, "Please use wm97xx_bat_set_pdata\n"); + return -EINVAL; + } + + if (pdata->charge_gpio >= 0 && gpio_is_valid(pdata->charge_gpio)) { + ret = gpio_request(pdata->charge_gpio, "BATT CHRG"); + if (ret) + goto err; + ret = gpio_direction_input(pdata->charge_gpio); + if (ret) + goto err2; + props++; /* POWER_SUPPLY_PROP_STATUS */ + } + + if (pdata->batt_tech >= 0) + props++; /* POWER_SUPPLY_PROP_TECHNOLOGY */ + if (pdata->temp_aux >= 0) + props++; /* POWER_SUPPLY_PROP_TEMP */ + if (pdata->batt_aux >= 0) + props++; /* POWER_SUPPLY_PROP_VOLTAGE_NOW */ + if (pdata->max_voltage >= 0) + props++; /* POWER_SUPPLY_PROP_VOLTAGE_MAX */ + if (pdata->min_voltage >= 0) + props++; /* POWER_SUPPLY_PROP_VOLTAGE_MIN */ + + prop = kzalloc(props * sizeof(*prop), GFP_KERNEL); + if (!prop) + goto err2; + + prop[i++] = POWER_SUPPLY_PROP_PRESENT; + if (pdata->charge_gpio >= 0) + prop[i++] = POWER_SUPPLY_PROP_STATUS; + if (pdata->batt_tech >= 0) + prop[i++] = POWER_SUPPLY_PROP_TECHNOLOGY; + if (pdata->temp_aux >= 0) + prop[i++] = POWER_SUPPLY_PROP_TEMP; + if (pdata->batt_aux >= 0) + prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_NOW; + if (pdata->max_voltage >= 0) + prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MAX; + if (pdata->min_voltage >= 0) + prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MIN; + + INIT_WORK(&bat_work, wm97xx_bat_work); + + if (!pdata->batt_name) { + dev_info(&dev->dev, "Please consider setting proper battery " + "name in platform definition file, falling " + "back to name \"wm97xx-batt\"\n"); + bat_ps.name = "wm97xx-batt"; + } else + bat_ps.name = pdata->batt_name; + + bat_ps.properties = prop; + bat_ps.num_properties = props; + + ret = power_supply_register(&dev->dev, &bat_ps); + if (!ret) + schedule_work(&bat_work); + else + goto err3; + + return 0; +err3: + kfree(prop); +err2: + gpio_free(pdata->charge_gpio); +err: + return ret; +} + +static int __devexit wm97xx_bat_remove(struct platform_device *dev) +{ + if (pdata && pdata->charge_gpio && pdata->charge_gpio >= 0) + gpio_free(pdata->charge_gpio); + flush_scheduled_work(); + power_supply_unregister(&bat_ps); + kfree(prop); + return 0; +} + +static struct platform_driver wm97xx_bat_driver = { + .driver = { + .name = "wm97xx-battery", + .owner = THIS_MODULE, + }, + .probe = wm97xx_bat_probe, + .remove = __devexit_p(wm97xx_bat_remove), + .suspend = wm97xx_bat_suspend, + .resume = wm97xx_bat_resume, +}; + +static int __init wm97xx_bat_init(void) +{ + return platform_driver_register(&wm97xx_bat_driver); +} + +static void __exit wm97xx_bat_exit(void) +{ + platform_driver_unregister(&wm97xx_bat_driver); +} + +void __init wm97xx_bat_set_pdata(struct wm97xx_batt_info *data) +{ + pdata = data; +} +EXPORT_SYMBOL_GPL(wm97xx_bat_set_pdata); + +module_init(wm97xx_bat_init); +module_exit(wm97xx_bat_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); +MODULE_DESCRIPTION("WM97xx battery driver"); |