From 12b336a8b4ea8652372f88521bf55cef7f5d5283 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 27 Jan 2010 20:43:21 +0000 Subject: wm97xx_battery: Handle missing platform data gracefully Don't unconditionally dereference the WM97xx core platform data since it may not be present, causing an oops. Signed-off-by: Mark Brown Signed-off-by: Anton Vorontsov --- drivers/power/wm97xx_battery.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/wm97xx_battery.c b/drivers/power/wm97xx_battery.c index fa39e759a275..6ea3cb5837c7 100644 --- a/drivers/power/wm97xx_battery.c +++ b/drivers/power/wm97xx_battery.c @@ -175,8 +175,14 @@ static int __devinit wm97xx_bat_probe(struct platform_device *dev) dev_err(&dev->dev, "Do not pass platform_data through " "wm97xx_bat_set_pdata!\n"); return -EINVAL; - } else - pdata = wmdata->batt_pdata; + } + + if (!wmdata) { + dev_err(&dev->dev, "No platform data supplied\n"); + return -EINVAL; + } + + pdata = wmdata->batt_pdata; if (dev->id != -1) return -EINVAL; -- cgit v1.2.3-59-g8ed1b From f8d94eb77e4ef6d80c6b7e5e02b92180d9a6f37b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 27 Jan 2010 20:45:35 +0000 Subject: wm97xx_battery: Clean up some warnings Staticise work_lock (nothing outside the driver has any reason to see it) and specify dev when requesting the charger IRQ (since that's what we pass in when we free it). Signed-off-by: Mark Brown Acked-by: Marek Vasut Signed-off-by: Anton Vorontsov --- drivers/power/wm97xx_battery.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/wm97xx_battery.c b/drivers/power/wm97xx_battery.c index 6ea3cb5837c7..23eed356a854 100644 --- a/drivers/power/wm97xx_battery.c +++ b/drivers/power/wm97xx_battery.c @@ -26,7 +26,7 @@ static DEFINE_MUTEX(bat_lock); static struct work_struct bat_work; -struct mutex work_lock; +static struct mutex work_lock; static int bat_status = POWER_SUPPLY_STATUS_UNKNOWN; static struct wm97xx_batt_info *gpdata; static enum power_supply_property *prop; @@ -203,7 +203,7 @@ static int __devinit wm97xx_bat_probe(struct platform_device *dev) goto err2; ret = request_irq(gpio_to_irq(pdata->charge_gpio), wm97xx_chrg_irq, IRQF_DISABLED, - "AC Detect", 0); + "AC Detect", dev); if (ret) goto err2; props++; /* POWER_SUPPLY_PROP_STATUS */ -- cgit v1.2.3-59-g8ed1b From 5324dc0e3872324ed0bf372bce7bc437436910b6 Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Tue, 2 Feb 2010 14:45:04 -0800 Subject: da9030_battery: fix spelling in comment platfrom -> platform monotor -> monitor Signed-off-by: Stefan Weil Cc: Anton Vorontsov Acked-by: Mike Rapoport Signed-off-by: Andrew Morton Signed-off-by: Anton Vorontsov --- drivers/power/da9030_battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/da9030_battery.c b/drivers/power/da9030_battery.c index 3364198134a1..a2e71f7b27fb 100644 --- a/drivers/power/da9030_battery.c +++ b/drivers/power/da9030_battery.c @@ -509,7 +509,7 @@ static int da9030_battery_probe(struct platform_device *pdev) charger->master = pdev->dev.parent; - /* 10 seconds between monotor runs unless platfrom defines other + /* 10 seconds between monitor runs unless platform defines other interval */ charger->interval = msecs_to_jiffies( (pdata->batmon_interval ? : 10) * 1000); -- cgit v1.2.3-59-g8ed1b From 97f70c23a1fa1bffe79187563dc5c6f2c72420a2 Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Fri, 12 Feb 2010 23:56:46 +0200 Subject: power_supply: bq27x00: remove double endian swap The bq27x00 registers are little endian, so just read them as such and don't do double endian swap on LE machines. This also gets rid of strange looking shift that was done after reading 8bit register because endian swap in bq27x00_read() was messing it up. Signed-off-by: Grazvydas Ignotas Acked-by: Rodolfo Giometti Signed-off-by: Anton Vorontsov --- drivers/power/bq27x00_battery.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 62bb98124e26..537915cc491e 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -74,12 +74,7 @@ static enum power_supply_property bq27x00_battery_props[] = { 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 di->bus->read(reg, rt_value, b_single, di); } /* @@ -161,7 +156,7 @@ static int bq27x00_battery_rsoc(struct bq27x00_device_info *di) return ret; } - return rsoc >> 8; + return rsoc; } #define to_bq27x00_device_info(x) container_of((x), \ @@ -238,7 +233,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 = get_unaligned_be16(data); + *rt_value = get_unaligned_le16(data); else *rt_value = data[0]; -- cgit v1.2.3-59-g8ed1b From ba96f8717e77f1ef68c160f6e9a454b3ce2c045b Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Fri, 12 Feb 2010 23:57:02 +0200 Subject: power_supply: bq27x00: remove unused struct fields These are probably leftover from old TI code. Signed-off-by: Grazvydas Ignotas Acked-by: Rodolfo Giometti Signed-off-by: Anton Vorontsov --- drivers/power/bq27x00_battery.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 537915cc491e..3ae3e08a1f4c 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -49,10 +49,6 @@ struct bq27x00_access_methods { 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; -- cgit v1.2.3-59-g8ed1b From b4de3608156311b615da4bcc4c095913abc44825 Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Fri, 12 Feb 2010 23:57:13 +0200 Subject: power_supply: bq27x00: fix temperature conversion The power supply class requires tenths of degree Celsius. Signed-off-by: Grazvydas Ignotas Acked-by: Rodolfo Giometti Signed-off-by: Anton Vorontsov --- drivers/power/bq27x00_battery.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 3ae3e08a1f4c..3da9e0aa705a 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -74,7 +74,7 @@ static int bq27x00_read(u8 reg, int *rt_value, int b_single, } /* - * Return the battery temperature in Celsius degrees + * Return the battery temperature in tenths of degree Celsius * Or < 0 if something fails. */ static int bq27x00_battery_temperature(struct bq27x00_device_info *di) @@ -88,7 +88,7 @@ static int bq27x00_battery_temperature(struct bq27x00_device_info *di) return ret; } - return (temp >> 2) - 273; + return ((temp >> 2) - 273) * 10; } /* -- cgit v1.2.3-59-g8ed1b From e20908d95e89d04b9537aefecc0734d2e4f7faed Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Fri, 12 Feb 2010 23:57:23 +0200 Subject: power_supply: bq27x00: add BQ27500 support BQ27500 is newer fuel gauge chip from TI with slightly changed register layout and some different register semantics. Add new i2c ID for it and handle differences where needed. Also rename bq27200 functions to bq27x00, as they are no longer bq27200 specific. Signed-off-by: Grazvydas Ignotas Acked-by: Rodolfo Giometti Signed-off-by: Anton Vorontsov --- drivers/power/Kconfig | 4 +-- drivers/power/bq27x00_battery.c | 77 ++++++++++++++++++++++++++--------------- 2 files changed, 51 insertions(+), 30 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index d4b3d67f0548..bf1467213954 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -98,10 +98,10 @@ config BATTERY_WM97XX Say Y to enable support for battery measured by WM97xx aux port. config BATTERY_BQ27x00 - tristate "BQ27200 battery driver" + tristate "BQ27x00 battery driver" depends on I2C help - Say Y here to enable support for batteries with BQ27200(I2C) chip. + Say Y here to enable support for batteries with BQ27x00 (I2C) chips. config BATTERY_DA9030 tristate "DA9030 battery driver" diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 3da9e0aa705a..1526d020cbc6 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -26,14 +26,17 @@ #include #include -#define DRIVER_VERSION "1.0.0" +#define DRIVER_VERSION "1.1.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 BQ27000_REG_RSOC 0x0B /* Relative State-of-Charge */ + +#define BQ27500_REG_SOC 0x2c + /* If the system has several batteries we need a different name for each * of them... */ @@ -46,11 +49,14 @@ struct bq27x00_access_methods { struct bq27x00_device_info *di); }; +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; }; @@ -88,7 +94,10 @@ static int bq27x00_battery_temperature(struct bq27x00_device_info *di) return ret; } - return ((temp >> 2) - 273) * 10; + if (di->chip == BQ27500) + return temp - 2731; + else + return ((temp >> 2) - 273) * 10; } /* @@ -125,15 +134,22 @@ static int bq27x00_battery_current(struct bq27x00_device_info *di) 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; + + if (di->chip == BQ27500) { + /* bq27500 returns signed value */ + curr = (int)(s16)curr; + } else { + 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; } @@ -146,7 +162,10 @@ 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 (di->chip == BQ27500) + ret = bq27x00_read(BQ27500_REG_SOC, &rsoc, 0, di); + else + ret = bq27x00_read(BQ27000_REG_RSOC, &rsoc, 1, di); if (ret) { dev_err(di->dev, "error reading relative State-of-Charge\n"); return ret; @@ -197,10 +216,10 @@ static void bq27x00_powersupply_init(struct bq27x00_device_info *di) } /* - * BQ27200 specific code + * i2c specific code */ -static int bq27200_read(u8 reg, int *rt_value, int b_single, +static int bq27x00_read_i2c(u8 reg, int *rt_value, int b_single, struct bq27x00_device_info *di) { struct i2c_client *client = di->client; @@ -239,7 +258,7 @@ static int bq27200_read(u8 reg, int *rt_value, int b_single, return err; } -static int bq27200_battery_probe(struct i2c_client *client, +static int bq27x00_battery_probe(struct i2c_client *client, const struct i2c_device_id *id) { char *name; @@ -258,7 +277,7 @@ static int bq27200_battery_probe(struct i2c_client *client, if (retval < 0) return retval; - name = kasprintf(GFP_KERNEL, "bq27200-%d", num); + name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num); if (!name) { dev_err(&client->dev, "failed to allocate device name\n"); retval = -ENOMEM; @@ -272,6 +291,7 @@ static int bq27200_battery_probe(struct i2c_client *client, goto batt_failed_2; } di->id = num; + di->chip = id->driver_data; bus = kzalloc(sizeof(*bus), GFP_KERNEL); if (!bus) { @@ -284,7 +304,7 @@ static int bq27200_battery_probe(struct i2c_client *client, i2c_set_clientdata(client, di); di->dev = &client->dev; di->bat.name = name; - bus->read = &bq27200_read; + bus->read = &bq27x00_read_i2c; di->bus = bus; di->client = client; @@ -314,7 +334,7 @@ batt_failed_1: return retval; } -static int bq27200_battery_remove(struct i2c_client *client) +static int bq27x00_battery_remove(struct i2c_client *client) { struct bq27x00_device_info *di = i2c_get_clientdata(client); @@ -335,27 +355,28 @@ static int bq27200_battery_remove(struct i2c_client *client) * Module stuff */ -static const struct i2c_device_id bq27200_id[] = { - { "bq27200", 0 }, +static const struct i2c_device_id bq27x00_id[] = { + { "bq27200", BQ27000 }, /* bq27200 is same as bq27000, but with i2c */ + { "bq27500", BQ27500 }, {}, }; -static struct i2c_driver bq27200_battery_driver = { +static struct i2c_driver bq27x00_battery_driver = { .driver = { - .name = "bq27200-battery", + .name = "bq27x00-battery", }, - .probe = bq27200_battery_probe, - .remove = bq27200_battery_remove, - .id_table = bq27200_id, + .probe = bq27x00_battery_probe, + .remove = bq27x00_battery_remove, + .id_table = bq27x00_id, }; static int __init bq27x00_battery_init(void) { int ret; - ret = i2c_add_driver(&bq27200_battery_driver); + ret = i2c_add_driver(&bq27x00_battery_driver); if (ret) - printk(KERN_ERR "Unable to register BQ27200 driver\n"); + printk(KERN_ERR "Unable to register BQ27x00 driver\n"); return ret; } @@ -363,7 +384,7 @@ module_init(bq27x00_battery_init); static void __exit bq27x00_battery_exit(void) { - i2c_del_driver(&bq27200_battery_driver); + i2c_del_driver(&bq27x00_battery_driver); } module_exit(bq27x00_battery_exit); -- cgit v1.2.3-59-g8ed1b From 4e924a814ac78e325653840190e9e4762e6f4b7c Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Sat, 27 Feb 2010 17:06:09 +0200 Subject: power_supply: bq27x00: add status and time properties The BQ27x00 series of chips can report time-to-empty and time-to-full, so let's add corresponding properties. Also report charge status based on status flag register. Signed-off-by: Grazvydas Ignotas Signed-off-by: Anton Vorontsov --- drivers/power/bq27x00_battery.c | 81 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 2 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 1526d020cbc6..5d940fad8d43 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -32,10 +32,16 @@ #define BQ27x00_REG_VOLT 0x08 #define BQ27x00_REG_AI 0x14 #define BQ27x00_REG_FLAGS 0x0A +#define BQ27x00_REG_TTE 0x16 +#define BQ27x00_REG_TTF 0x18 +#define BQ27x00_REG_TTECP 0x26 #define BQ27000_REG_RSOC 0x0B /* Relative State-of-Charge */ +#define BQ27000_FLAG_CHGS BIT(7) #define BQ27500_REG_SOC 0x2c +#define BQ27500_FLAG_DSC BIT(0) +#define BQ27500_FLAG_FC BIT(9) /* If the system has several batteries we need a different name for each * of them... @@ -62,11 +68,15 @@ struct bq27x00_device_info { }; static enum power_supply_property bq27x00_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, + POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, }; /* @@ -144,7 +154,7 @@ static int bq27x00_battery_current(struct bq27x00_device_info *di) dev_err(di->dev, "error reading flags\n"); return 0; } - if ((flags & (1 << 7)) != 0) { + if (flags & BQ27000_FLAG_CHGS) { dev_dbg(di->dev, "negative current!\n"); return -curr; } @@ -174,6 +184,60 @@ static int bq27x00_battery_rsoc(struct bq27x00_device_info *di) return rsoc; } +static int bq27x00_battery_status(struct bq27x00_device_info *di, + union power_supply_propval *val) +{ + int flags = 0; + int status; + int ret; + + ret = bq27x00_read(BQ27x00_REG_FLAGS, &flags, 0, di); + if (ret < 0) { + dev_err(di->dev, "error reading flags\n"); + return ret; + } + + if (di->chip == BQ27500) { + if (flags & BQ27500_FLAG_FC) + status = POWER_SUPPLY_STATUS_FULL; + else if (flags & BQ27500_FLAG_DSC) + status = POWER_SUPPLY_STATUS_DISCHARGING; + else + status = POWER_SUPPLY_STATUS_CHARGING; + } else { + if (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. + */ +static int bq27x00_battery_time(struct bq27x00_device_info *di, int reg, + union power_supply_propval *val) +{ + int tval = 0; + int ret; + + ret = bq27x00_read(reg, &tval, 0, di); + if (ret) { + dev_err(di->dev, "error reading register %02x\n", reg); + return ret; + } + + if (tval == 65535) + return -ENODATA; + + val->intval = tval * 60; + return 0; +} + #define to_bq27x00_device_info(x) container_of((x), \ struct bq27x00_device_info, bat); @@ -181,9 +245,13 @@ static int bq27x00_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { + int ret = 0; struct bq27x00_device_info *di = to_bq27x00_device_info(psy); switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + ret = bq27x00_battery_status(di, val); + break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: case POWER_SUPPLY_PROP_PRESENT: val->intval = bq27x00_battery_voltage(di); @@ -199,11 +267,20 @@ static int bq27x00_battery_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_TEMP: val->intval = bq27x00_battery_temperature(di); break; + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: + ret = bq27x00_battery_time(di, BQ27x00_REG_TTE, val); + break; + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: + ret = bq27x00_battery_time(di, BQ27x00_REG_TTECP, val); + break; + case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: + ret = bq27x00_battery_time(di, BQ27x00_REG_TTF, val); + break; default: return -EINVAL; } - return 0; + return ret; } static void bq27x00_powersupply_init(struct bq27x00_device_info *di) -- cgit v1.2.3-59-g8ed1b From afbc74fdc5b96552c384e57119b3cc13c840bf06 Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Sat, 27 Feb 2010 17:06:44 +0200 Subject: power_supply: bq27x00: fix voltage and current units The chip returns voltage and current in mV and mA, but power supply class uses uV and uA, so add missing conversion. Signed-off-by: Grazvydas Ignotas Signed-off-by: Anton Vorontsov --- drivers/power/bq27x00_battery.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 5d940fad8d43..bece33ed873c 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -125,7 +125,7 @@ static int bq27x00_battery_voltage(struct bq27x00_device_info *di) return ret; } - return volt; + return volt * 1000; } /* @@ -156,11 +156,11 @@ static int bq27x00_battery_current(struct bq27x00_device_info *di) } if (flags & BQ27000_FLAG_CHGS) { dev_dbg(di->dev, "negative current!\n"); - return -curr; + curr = -curr; } } - return curr; + return curr * 1000; } /* -- cgit v1.2.3-59-g8ed1b From f99344fc69c3df46786a39ea4283a4175ea40b3f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 5 Jan 2010 13:59:07 +0000 Subject: mfd: Add a data argument to the WM8350 IRQ free function To better match genirq. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm8350-irq.c | 2 +- drivers/power/wm8350_power.c | 24 ++++++++++++------------ drivers/regulator/wm8350-regulator.c | 2 +- drivers/rtc/rtc-wm8350.c | 4 ++-- include/linux/mfd/wm8350/core.h | 3 ++- sound/soc/codecs/wm8350.c | 4 ++-- 6 files changed, 20 insertions(+), 19 deletions(-) (limited to 'drivers/power') diff --git a/drivers/mfd/wm8350-irq.c b/drivers/mfd/wm8350-irq.c index 9025f29e2707..655836bc69a0 100644 --- a/drivers/mfd/wm8350-irq.c +++ b/drivers/mfd/wm8350-irq.c @@ -451,7 +451,7 @@ int wm8350_register_irq(struct wm8350 *wm8350, int irq, } EXPORT_SYMBOL_GPL(wm8350_register_irq); -int wm8350_free_irq(struct wm8350 *wm8350, int irq) +int wm8350_free_irq(struct wm8350 *wm8350, int irq, void *data) { if (irq < 0 || irq >= WM8350_NUM_IRQ) return -EINVAL; diff --git a/drivers/power/wm8350_power.c b/drivers/power/wm8350_power.c index ad4f071e1287..3839a5e1c4a7 100644 --- a/drivers/power/wm8350_power.c +++ b/drivers/power/wm8350_power.c @@ -428,18 +428,18 @@ static void wm8350_init_charger(struct wm8350 *wm8350) static void free_charger_irq(struct wm8350 *wm8350) { - wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT); - wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD); - wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL); - wm8350_free_irq(wm8350, WM8350_IRQ_CHG_TO); - wm8350_free_irq(wm8350, WM8350_IRQ_CHG_END); - wm8350_free_irq(wm8350, WM8350_IRQ_CHG_START); - wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9); - wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1); - wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85); - wm8350_free_irq(wm8350, WM8350_IRQ_EXT_USB_FB); - wm8350_free_irq(wm8350, WM8350_IRQ_EXT_WALL_FB); - wm8350_free_irq(wm8350, WM8350_IRQ_EXT_BAT_FB); + wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_HOT, wm8350); + wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_COLD, wm8350); + wm8350_free_irq(wm8350, WM8350_IRQ_CHG_BAT_FAIL, wm8350); + wm8350_free_irq(wm8350, WM8350_IRQ_CHG_TO, wm8350); + wm8350_free_irq(wm8350, WM8350_IRQ_CHG_END, wm8350); + wm8350_free_irq(wm8350, WM8350_IRQ_CHG_START, wm8350); + wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P9, wm8350); + wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_3P1, wm8350); + wm8350_free_irq(wm8350, WM8350_IRQ_CHG_VBATT_LT_2P85, wm8350); + wm8350_free_irq(wm8350, WM8350_IRQ_EXT_USB_FB, wm8350); + wm8350_free_irq(wm8350, WM8350_IRQ_EXT_WALL_FB, wm8350); + wm8350_free_irq(wm8350, WM8350_IRQ_EXT_BAT_FB, wm8350); } static __devinit int wm8350_power_probe(struct platform_device *pdev) diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index 94227dd6ba7b..723cd1fb4867 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -1453,7 +1453,7 @@ static int wm8350_regulator_remove(struct platform_device *pdev) struct regulator_dev *rdev = platform_get_drvdata(pdev); struct wm8350 *wm8350 = rdev_get_drvdata(rdev); - wm8350_free_irq(wm8350, wm8350_reg[pdev->id].irq); + wm8350_free_irq(wm8350, wm8350_reg[pdev->id].irq, rdev); regulator_unregister(rdev); diff --git a/drivers/rtc/rtc-wm8350.c b/drivers/rtc/rtc-wm8350.c index f1e440521c54..a5512f515998 100644 --- a/drivers/rtc/rtc-wm8350.c +++ b/drivers/rtc/rtc-wm8350.c @@ -478,8 +478,8 @@ static int __devexit wm8350_rtc_remove(struct platform_device *pdev) struct wm8350 *wm8350 = platform_get_drvdata(pdev); struct wm8350_rtc *wm_rtc = &wm8350->rtc; - wm8350_free_irq(wm8350, WM8350_IRQ_RTC_SEC); - wm8350_free_irq(wm8350, WM8350_IRQ_RTC_ALM); + wm8350_free_irq(wm8350, WM8350_IRQ_RTC_SEC, wm8350); + wm8350_free_irq(wm8350, WM8350_IRQ_RTC_ALM, wm8350); rtc_device_unregister(wm_rtc->rtc); diff --git a/include/linux/mfd/wm8350/core.h b/include/linux/mfd/wm8350/core.h index 43868899bf49..8883125ddea1 100644 --- a/include/linux/mfd/wm8350/core.h +++ b/include/linux/mfd/wm8350/core.h @@ -680,7 +680,8 @@ int wm8350_block_write(struct wm8350 *wm8350, int reg, int size, u16 *src); int wm8350_register_irq(struct wm8350 *wm8350, int irq, irq_handler_t handler, unsigned long flags, const char *name, void *data); -int wm8350_free_irq(struct wm8350 *wm8350, int irq); +int wm8350_free_irq(struct wm8350 *wm8350, int irq, void *data); + int wm8350_mask_irq(struct wm8350 *wm8350, int irq); int wm8350_unmask_irq(struct wm8350 *wm8350, int irq); int wm8350_irq_init(struct wm8350 *wm8350, int irq, diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index 718ef912e758..079bf745bf05 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -1521,8 +1521,8 @@ static int wm8350_remove(struct platform_device *pdev) WM8350_JDL_ENA | WM8350_JDR_ENA); wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA); - wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L); - wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R); + wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L, priv); + wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R, priv); priv->hpl.jack = NULL; priv->hpr.jack = NULL; -- cgit v1.2.3-59-g8ed1b From 59f25070df0325067d7916b467ad15725657fedc Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 6 Jan 2010 19:24:25 +0000 Subject: mfd: Update WM8350 drivers for changed interrupt numbers The headphone detect and charger are using the IRQ numbers so need to take account of irq_base with the genirq conversion. I obviously picked the wrong system for initial testing. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/power/wm8350_power.c | 2 +- sound/soc/codecs/wm8350.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/wm8350_power.c b/drivers/power/wm8350_power.c index 3839a5e1c4a7..0693902d6151 100644 --- a/drivers/power/wm8350_power.c +++ b/drivers/power/wm8350_power.c @@ -190,7 +190,7 @@ static irqreturn_t wm8350_charger_handler(int irq, void *data) struct wm8350_power *power = &wm8350->power; struct wm8350_charger_policy *policy = power->policy; - switch (irq) { + switch (irq - wm8350->irq_base) { case WM8350_IRQ_CHG_BAT_FAIL: dev_err(wm8350->dev, "battery failed\n"); break; diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index 079bf745bf05..df2c6d9617fb 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -1349,7 +1349,7 @@ static irqreturn_t wm8350_hp_jack_handler(int irq, void *data) int mask; struct wm8350_jack_data *jack = NULL; - switch (irq) { + switch (irq - wm8350->irq_base) { case WM8350_IRQ_CODEC_JCK_DET_L: jack = &priv->hpl; mask = WM8350_JACK_L_LVL; @@ -1424,7 +1424,7 @@ int wm8350_hp_jack_detect(struct snd_soc_codec *codec, enum wm8350_jack which, wm8350_set_bits(wm8350, WM8350_JACK_DETECT, ena); /* Sync status */ - wm8350_hp_jack_handler(irq, priv); + wm8350_hp_jack_handler(irq + wm8350->irq_base, priv); return 0; } -- cgit v1.2.3-59-g8ed1b From 28db38888b2a53b6bdd70104c2d2a199632ca507 Mon Sep 17 00:00:00 2001 From: Haojian Zhuang Date: Mon, 25 Jan 2010 10:38:35 -0500 Subject: power_supply: Enable power supply of max8925 MAX8925 is a PMIC that contains charger component Signed-off-by: Haojian Zhuang Signed-off-by: Samuel Ortiz --- drivers/power/Kconfig | 7 + drivers/power/Makefile | 1 + drivers/power/max8925_power.c | 534 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 542 insertions(+) create mode 100644 drivers/power/max8925_power.c (limited to 'drivers/power') diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index bf1467213954..faaa9b4d0d07 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -29,6 +29,13 @@ config APM_POWER Say Y here to enable support APM status emulation using battery class devices. +config MAX8925_POWER + tristate "MAX8925 battery charger support" + depends on MFD_MAX8925 + help + Say Y here to enable support for the battery charger in the Maxim + MAX8925 PMIC. + config WM831X_BACKUP tristate "WM831X backup battery charger support" depends on MFD_WM831X diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 573597c683b4..a2ba7c85c97a 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_POWER_SUPPLY) += power_supply.o obj-$(CONFIG_PDA_POWER) += pda_power.o obj-$(CONFIG_APM_POWER) += apm_power.o +obj-$(CONFIG_MAX8925_POWER) += max8925_power.o obj-$(CONFIG_WM831X_BACKUP) += wm831x_backup.o obj-$(CONFIG_WM831X_POWER) += wm831x_power.o obj-$(CONFIG_WM8350_POWER) += wm8350_power.o diff --git a/drivers/power/max8925_power.c b/drivers/power/max8925_power.c new file mode 100644 index 000000000000..a1b4410544d7 --- /dev/null +++ b/drivers/power/max8925_power.c @@ -0,0 +1,534 @@ +/* + * Battery driver for Maxim MAX8925 + * + * Copyright (c) 2009-2010 Marvell International Ltd. + * Haojian Zhuang + * + * 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 +#include +#include +#include +#include +#include +#include + +/* registers in GPM */ +#define MAX8925_OUT5VEN 0x54 +#define MAX8925_OUT3VEN 0x58 +#define MAX8925_CHG_CNTL1 0x7c + +/* bits definition */ +#define MAX8925_CHG_STAT_VSYSLOW (1 << 0) +#define MAX8925_CHG_STAT_MODE_MASK (3 << 2) +#define MAX8925_CHG_STAT_EN_MASK (1 << 4) +#define MAX8925_CHG_MBDET (1 << 1) +#define MAX8925_CHG_AC_RANGE_MASK (3 << 6) + +/* registers in ADC */ +#define MAX8925_ADC_RES_CNFG1 0x06 +#define MAX8925_ADC_AVG_CNFG1 0x07 +#define MAX8925_ADC_ACQ_CNFG1 0x08 +#define MAX8925_ADC_ACQ_CNFG2 0x09 +/* 2 bytes registers in below. MSB is 1st, LSB is 2nd. */ +#define MAX8925_ADC_AUX2 0x62 +#define MAX8925_ADC_VCHG 0x64 +#define MAX8925_ADC_VBBATT 0x66 +#define MAX8925_ADC_VMBATT 0x68 +#define MAX8925_ADC_ISNS 0x6a +#define MAX8925_ADC_THM 0x6c +#define MAX8925_ADC_TDIE 0x6e +#define MAX8925_CMD_AUX2 0xc8 +#define MAX8925_CMD_VCHG 0xd0 +#define MAX8925_CMD_VBBATT 0xd8 +#define MAX8925_CMD_VMBATT 0xe0 +#define MAX8925_CMD_ISNS 0xe8 +#define MAX8925_CMD_THM 0xf0 +#define MAX8925_CMD_TDIE 0xf8 + +enum { + MEASURE_AUX2, + MEASURE_VCHG, + MEASURE_VBBATT, + MEASURE_VMBATT, + MEASURE_ISNS, + MEASURE_THM, + MEASURE_TDIE, + MEASURE_MAX, +}; + +struct max8925_power_info { + struct max8925_chip *chip; + struct i2c_client *gpm; + struct i2c_client *adc; + + struct power_supply ac; + struct power_supply usb; + struct power_supply battery; + int irq_base; + unsigned ac_online:1; + unsigned usb_online:1; + unsigned bat_online:1; + unsigned chg_mode:2; + unsigned batt_detect:1; /* detecing MB by ID pin */ + unsigned topoff_threshold:2; + unsigned fast_charge:3; + + int (*set_charger) (int); +}; + +static int __set_charger(struct max8925_power_info *info, int enable) +{ + struct max8925_chip *chip = info->chip; + if (enable) { + /* enable charger in platform */ + if (info->set_charger) + info->set_charger(1); + /* enable charger */ + max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 1 << 7, 0); + } else { + /* disable charge */ + max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 1 << 7, 1 << 7); + if (info->set_charger) + info->set_charger(0); + } + dev_dbg(chip->dev, "%s\n", (enable) ? "Enable charger" + : "Disable charger"); + return 0; +} + +static irqreturn_t max8925_charger_handler(int irq, void *data) +{ + struct max8925_power_info *info = (struct max8925_power_info *)data; + struct max8925_chip *chip = info->chip; + + switch (irq - chip->irq_base) { + case MAX8925_IRQ_VCHG_DC_R: + info->ac_online = 1; + __set_charger(info, 1); + dev_dbg(chip->dev, "Adapter inserted\n"); + break; + case MAX8925_IRQ_VCHG_DC_F: + info->ac_online = 0; + __set_charger(info, 0); + dev_dbg(chip->dev, "Adapter is removal\n"); + break; + case MAX8925_IRQ_VCHG_USB_R: + info->usb_online = 1; + __set_charger(info, 1); + dev_dbg(chip->dev, "USB inserted\n"); + break; + case MAX8925_IRQ_VCHG_USB_F: + info->usb_online = 0; + __set_charger(info, 0); + dev_dbg(chip->dev, "USB is removal\n"); + break; + case MAX8925_IRQ_VCHG_THM_OK_F: + /* Battery is not ready yet */ + dev_dbg(chip->dev, "Battery temperature is out of range\n"); + case MAX8925_IRQ_VCHG_DC_OVP: + dev_dbg(chip->dev, "Error detection\n"); + __set_charger(info, 0); + break; + case MAX8925_IRQ_VCHG_THM_OK_R: + /* Battery is ready now */ + dev_dbg(chip->dev, "Battery temperature is in range\n"); + break; + case MAX8925_IRQ_VCHG_SYSLOW_R: + /* VSYS is low */ + dev_info(chip->dev, "Sys power is too low\n"); + break; + case MAX8925_IRQ_VCHG_SYSLOW_F: + dev_dbg(chip->dev, "Sys power is above low threshold\n"); + break; + case MAX8925_IRQ_VCHG_DONE: + __set_charger(info, 0); + dev_dbg(chip->dev, "Charging is done\n"); + break; + case MAX8925_IRQ_VCHG_TOPOFF: + dev_dbg(chip->dev, "Charging in top-off mode\n"); + break; + case MAX8925_IRQ_VCHG_TMR_FAULT: + __set_charger(info, 0); + dev_dbg(chip->dev, "Safe timer is expired\n"); + break; + case MAX8925_IRQ_VCHG_RST: + __set_charger(info, 0); + dev_dbg(chip->dev, "Charger is reset\n"); + break; + } + return IRQ_HANDLED; +} + +static int start_measure(struct max8925_power_info *info, int type) +{ + unsigned char buf[2] = {0, 0}; + int meas_reg = 0, ret; + + switch (type) { + case MEASURE_VCHG: + meas_reg = MAX8925_ADC_VCHG; + break; + case MEASURE_VBBATT: + meas_reg = MAX8925_ADC_VBBATT; + break; + case MEASURE_VMBATT: + meas_reg = MAX8925_ADC_VMBATT; + break; + case MEASURE_ISNS: + meas_reg = MAX8925_ADC_ISNS; + break; + default: + return -EINVAL; + } + + max8925_bulk_read(info->adc, meas_reg, 2, buf); + ret = (buf[0] << 4) | (buf[1] >> 4); + + return ret; +} + +static int max8925_ac_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct max8925_power_info *info = dev_get_drvdata(psy->dev->parent); + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = info->ac_online; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + if (info->ac_online) { + ret = start_measure(info, MEASURE_VCHG); + if (ret >= 0) { + val->intval = ret << 1; /* unit is mV */ + goto out; + } + } + ret = -ENODATA; + break; + default: + ret = -ENODEV; + break; + } +out: + return ret; +} + +static enum power_supply_property max8925_ac_props[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_NOW, +}; + +static int max8925_usb_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct max8925_power_info *info = dev_get_drvdata(psy->dev->parent); + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = info->usb_online; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + if (info->usb_online) { + ret = start_measure(info, MEASURE_VCHG); + if (ret >= 0) { + val->intval = ret << 1; /* unit is mV */ + goto out; + } + } + ret = -ENODATA; + break; + default: + ret = -ENODEV; + break; + } +out: + return ret; +} + +static enum power_supply_property max8925_usb_props[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_NOW, +}; + +static int max8925_bat_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct max8925_power_info *info = dev_get_drvdata(psy->dev->parent); + long long int tmp = 0; + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = info->bat_online; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + if (info->bat_online) { + ret = start_measure(info, MEASURE_VMBATT); + if (ret >= 0) { + val->intval = ret << 1; /* unit is mV */ + ret = 0; + break; + } + } + ret = -ENODATA; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + if (info->bat_online) { + ret = start_measure(info, MEASURE_ISNS); + if (ret >= 0) { + tmp = (long long int)ret * 6250 / 4096 - 3125; + ret = (int)tmp; + val->intval = 0; + if (ret > 0) + val->intval = ret; /* unit is mA */ + ret = 0; + break; + } + } + ret = -ENODATA; + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + if (!info->bat_online) { + ret = -ENODATA; + break; + } + ret = max8925_reg_read(info->gpm, MAX8925_CHG_STATUS); + ret = (ret & MAX8925_CHG_STAT_MODE_MASK) >> 2; + switch (ret) { + case 1: + val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; + break; + case 0: + case 2: + val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + break; + case 3: + val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; + break; + } + ret = 0; + break; + case POWER_SUPPLY_PROP_STATUS: + if (!info->bat_online) { + ret = -ENODATA; + break; + } + ret = max8925_reg_read(info->gpm, MAX8925_CHG_STATUS); + if (info->usb_online || info->ac_online) { + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + if (ret & MAX8925_CHG_STAT_EN_MASK) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + } else + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + ret = 0; + break; + default: + ret = -ENODEV; + break; + } + return ret; +} + +static enum power_supply_property max8925_battery_props[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_STATUS, +}; + +#define REQUEST_IRQ(_irq, _name) \ +do { \ + ret = request_threaded_irq(chip->irq_base + _irq, NULL, \ + max8925_charger_handler, \ + IRQF_ONESHOT, _name, info); \ + if (ret) \ + dev_err(chip->dev, "Failed to request IRQ #%d: %d\n", \ + _irq, ret); \ +} while (0) + +static __devinit int max8925_init_charger(struct max8925_chip *chip, + struct max8925_power_info *info) +{ + int ret; + + REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_OVP, "ac-ovp"); + REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_F, "ac-remove"); + REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_R, "ac-insert"); + REQUEST_IRQ(MAX8925_IRQ_VCHG_USB_OVP, "usb-ovp"); + REQUEST_IRQ(MAX8925_IRQ_VCHG_USB_F, "usb-remove"); + REQUEST_IRQ(MAX8925_IRQ_VCHG_USB_R, "usb-insert"); + REQUEST_IRQ(MAX8925_IRQ_VCHG_THM_OK_R, "batt-temp-in-range"); + REQUEST_IRQ(MAX8925_IRQ_VCHG_THM_OK_F, "batt-temp-out-range"); + REQUEST_IRQ(MAX8925_IRQ_VCHG_SYSLOW_F, "vsys-high"); + REQUEST_IRQ(MAX8925_IRQ_VCHG_SYSLOW_R, "vsys-low"); + REQUEST_IRQ(MAX8925_IRQ_VCHG_RST, "charger-reset"); + REQUEST_IRQ(MAX8925_IRQ_VCHG_DONE, "charger-done"); + REQUEST_IRQ(MAX8925_IRQ_VCHG_TOPOFF, "charger-topoff"); + REQUEST_IRQ(MAX8925_IRQ_VCHG_TMR_FAULT, "charger-timer-expire"); + + info->ac_online = 0; + info->usb_online = 0; + info->bat_online = 0; + ret = max8925_reg_read(info->gpm, MAX8925_CHG_STATUS); + if (ret >= 0) { + /* + * If battery detection is enabled, ID pin of battery is + * connected to MBDET pin of MAX8925. It could be used to + * detect battery presence. + * Otherwise, we have to assume that battery is always on. + */ + if (info->batt_detect) + info->bat_online = (ret & MAX8925_CHG_MBDET) ? 0 : 1; + else + info->bat_online = 1; + if (ret & MAX8925_CHG_AC_RANGE_MASK) + info->ac_online = 1; + else + info->ac_online = 0; + } + /* disable charge */ + max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 1 << 7, 1 << 7); + /* set charging current in charge topoff mode */ + max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 3 << 5, + info->topoff_threshold << 5); + /* set charing current in fast charge mode */ + max8925_set_bits(info->gpm, MAX8925_CHG_CNTL1, 7, info->fast_charge); + + return 0; +} + +static __devexit int max8925_deinit_charger(struct max8925_power_info *info) +{ + struct max8925_chip *chip = info->chip; + int irq; + + irq = chip->irq_base + MAX8925_IRQ_VCHG_DC_OVP; + for (; irq <= chip->irq_base + MAX8925_IRQ_VCHG_TMR_FAULT; irq++) + free_irq(irq, info); + + return 0; +} + +static __devinit int max8925_power_probe(struct platform_device *pdev) +{ + struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct max8925_platform_data *max8925_pdata; + struct max8925_power_pdata *pdata = NULL; + struct max8925_power_info *info; + int ret; + + if (pdev->dev.parent->platform_data) { + max8925_pdata = pdev->dev.parent->platform_data; + pdata = max8925_pdata->power; + } + + if (!pdata) { + dev_err(&pdev->dev, "platform data isn't assigned to " + "power supply\n"); + return -EINVAL; + } + + info = kzalloc(sizeof(struct max8925_power_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + info->chip = chip; + info->gpm = chip->i2c; + info->adc = chip->adc; + + info->ac.name = "max8925-ac"; + info->ac.type = POWER_SUPPLY_TYPE_MAINS; + info->ac.properties = max8925_ac_props; + info->ac.num_properties = ARRAY_SIZE(max8925_ac_props); + info->ac.get_property = max8925_ac_get_prop; + ret = power_supply_register(&pdev->dev, &info->ac); + if (ret) + goto out; + info->ac.dev->parent = &pdev->dev; + + info->usb.name = "max8925-usb"; + info->usb.type = POWER_SUPPLY_TYPE_USB; + info->usb.properties = max8925_usb_props; + info->usb.num_properties = ARRAY_SIZE(max8925_usb_props); + info->usb.get_property = max8925_usb_get_prop; + ret = power_supply_register(&pdev->dev, &info->usb); + if (ret) + goto out_usb; + info->usb.dev->parent = &pdev->dev; + + info->battery.name = "max8925-battery"; + info->battery.type = POWER_SUPPLY_TYPE_BATTERY; + info->battery.properties = max8925_battery_props; + info->battery.num_properties = ARRAY_SIZE(max8925_battery_props); + info->battery.get_property = max8925_bat_get_prop; + ret = power_supply_register(&pdev->dev, &info->battery); + if (ret) + goto out_battery; + info->battery.dev->parent = &pdev->dev; + + info->batt_detect = pdata->batt_detect; + info->topoff_threshold = pdata->topoff_threshold; + info->fast_charge = pdata->fast_charge; + info->set_charger = pdata->set_charger; + dev_set_drvdata(&pdev->dev, info); + platform_set_drvdata(pdev, info); + + max8925_init_charger(chip, info); + return 0; +out_battery: + power_supply_unregister(&info->battery); +out_usb: + power_supply_unregister(&info->ac); +out: + kfree(info); + return ret; +} + +static __devexit int max8925_power_remove(struct platform_device *pdev) +{ + struct max8925_power_info *info = platform_get_drvdata(pdev); + + if (info) { + power_supply_unregister(&info->ac); + power_supply_unregister(&info->usb); + power_supply_unregister(&info->battery); + max8925_deinit_charger(info); + kfree(info); + } + return 0; +} + +static struct platform_driver max8925_power_driver = { + .probe = max8925_power_probe, + .remove = __devexit_p(max8925_power_remove), + .driver = { + .name = "max8925-power", + }, +}; + +static int __init max8925_power_init(void) +{ + return platform_driver_register(&max8925_power_driver); +} +module_init(max8925_power_init); + +static void __exit max8925_power_exit(void) +{ + platform_driver_unregister(&max8925_power_driver); +} +module_exit(max8925_power_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Power supply driver for MAX8925"); +MODULE_ALIAS("platform:max8925-power"); -- cgit v1.2.3-59-g8ed1b