From c03bfabb60f5070a1c7299eab38b7bb9df64cb11 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 1 Aug 2011 07:23:00 +0800 Subject: max8997_charger&max8998_charger: Fix unterminated platform_device_id tables The platform_device_id table is supposed to be zero-terminated. Signed-off-by: Axel Lin Acked-by: MyungJoo Ham Signed-off-by: Anton Vorontsov --- drivers/power/max8997_charger.c | 1 + drivers/power/max8998_charger.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/max8997_charger.c b/drivers/power/max8997_charger.c index ffc5033..e12b4a2 100644 --- a/drivers/power/max8997_charger.c +++ b/drivers/power/max8997_charger.c @@ -178,6 +178,7 @@ static int __devexit max8997_battery_remove(struct platform_device *pdev) static const struct platform_device_id max8997_battery_id[] = { { "max8997-battery", 0 }, + { } }; static struct platform_driver max8997_battery_driver = { diff --git a/drivers/power/max8998_charger.c b/drivers/power/max8998_charger.c index ef8efad..8ac5fab 100644 --- a/drivers/power/max8998_charger.c +++ b/drivers/power/max8998_charger.c @@ -189,6 +189,7 @@ static int __devexit max8998_battery_remove(struct platform_device *pdev) static const struct platform_device_id max8998_battery_id[] = { { "max8998-battery", TYPE_MAX8998 }, + { } }; static struct platform_driver max8998_battery_driver = { -- cgit v1.1 From bd19c756b1a69ec2c8f5f81624d66a1a0daad7c0 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 24 Jun 2011 22:26:36 +0800 Subject: max8903_charger: Add "platform:" prefix for platform modalias Since 43cc71eed1250755986da4c0f9898f9a635cb3bf (platform: prefix MODALIAS with "platform:"), the platform modalias is prefixed with "platform:". Signed-off-by: Axel Lin Signed-off-by: Anton Vorontsov --- drivers/power/max8903_charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/max8903_charger.c b/drivers/power/max8903_charger.c index a9b0209..3c030c9 100644 --- a/drivers/power/max8903_charger.c +++ b/drivers/power/max8903_charger.c @@ -388,4 +388,4 @@ module_exit(max8903_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("MAX8903 Charger Driver"); MODULE_AUTHOR("MyungJoo Ham "); -MODULE_ALIAS("max8903-charger"); +MODULE_ALIAS("platform:max8903-charger"); -- cgit v1.1 From 0bea4b866448af09051e357b139f598aa70b8614 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 7 Jul 2011 21:15:13 +0800 Subject: ds2780_battery&z2_battery: Add __devexit_p at necessary places According to the comments in include/linux/init.h: "Pointers to __devexit functions must use __devexit_p(function_name), the wrapper will insert either the function_name or NULL, depending on the config options." Signed-off-by: Axel Lin Cc: Clifton Barnes Cc: Peter Edwards Signed-off-by: Anton Vorontsov --- drivers/power/ds2780_battery.c | 2 +- drivers/power/z2_battery.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/ds2780_battery.c b/drivers/power/ds2780_battery.c index 1fefe82..ed25ef5 100644 --- a/drivers/power/ds2780_battery.c +++ b/drivers/power/ds2780_battery.c @@ -832,7 +832,7 @@ static struct platform_driver ds2780_battery_driver = { .name = "ds2780-battery", }, .probe = ds2780_battery_probe, - .remove = ds2780_battery_remove, + .remove = __devexit_p(ds2780_battery_remove), }; static int __init ds2780_battery_init(void) diff --git a/drivers/power/z2_battery.c b/drivers/power/z2_battery.c index d119c38..ae38c44 100644 --- a/drivers/power/z2_battery.c +++ b/drivers/power/z2_battery.c @@ -313,7 +313,7 @@ static struct i2c_driver z2_batt_driver = { .pm = Z2_BATTERY_PM_OPS }, .probe = z2_batt_probe, - .remove = z2_batt_remove, + .remove = __devexit_p(z2_batt_remove), .id_table = z2_batt_id, }; -- cgit v1.1 From 9ad63986c606c60e2e916b1b96f22991f966d9cc Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Sun, 10 Jul 2011 16:01:15 -0700 Subject: pda_power: Add support for using otg transceiver events If the platform data sets the use_otg_notifier flag, the driver will now register an otg notifier callback and listen to transceiver events for AC/USB plug-in events instead. This would normally be used by not specifying is_xx_online callbacks and not specifying any irqs so the state machine is completely driven from OTG xceiver events. Signed-off-by: Dima Zavin Signed-off-by: Anton Vorontsov --- drivers/power/pda_power.c | 71 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 12 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c index 69f8aa3..81b7201 100644 --- a/drivers/power/pda_power.c +++ b/drivers/power/pda_power.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -38,9 +39,8 @@ static struct timer_list supply_timer; static struct timer_list polling_timer; static int polling; -#ifdef CONFIG_USB_OTG_UTILS static struct otg_transceiver *transceiver; -#endif +static struct notifier_block otg_nb; static struct regulator *ac_draw; enum { @@ -222,7 +222,42 @@ static void polling_timer_func(unsigned long unused) #ifdef CONFIG_USB_OTG_UTILS static int otg_is_usb_online(void) { - return (transceiver->state == OTG_STATE_B_PERIPHERAL); + return (transceiver->last_event == USB_EVENT_VBUS || + transceiver->last_event == USB_EVENT_ENUMERATED); +} + +static int otg_is_ac_online(void) +{ + return (transceiver->last_event == USB_EVENT_CHARGER); +} + +static int otg_handle_notification(struct notifier_block *nb, + unsigned long event, void *unused) +{ + switch (event) { + case USB_EVENT_CHARGER: + ac_status = PDA_PSY_TO_CHANGE; + break; + case USB_EVENT_VBUS: + case USB_EVENT_ENUMERATED: + usb_status = PDA_PSY_TO_CHANGE; + break; + case USB_EVENT_NONE: + ac_status = PDA_PSY_TO_CHANGE; + usb_status = PDA_PSY_TO_CHANGE; + break; + default: + return NOTIFY_OK; + } + + /* + * Wait a bit before reading ac/usb line status and setting charger, + * because ac/usb status readings may lag from irq. + */ + mod_timer(&charger_timer, + jiffies + msecs_to_jiffies(pdata->wait_for_status)); + + return NOTIFY_OK; } #endif @@ -282,6 +317,14 @@ static int pda_power_probe(struct platform_device *pdev) ret = PTR_ERR(ac_draw); } + transceiver = otg_get_transceiver(); + if (transceiver && !pdata->is_usb_online) { + pdata->is_usb_online = otg_is_usb_online; + } + if (transceiver && !pdata->is_ac_online) { + pdata->is_ac_online = otg_is_ac_online; + } + if (pdata->is_ac_online) { ret = power_supply_register(&pdev->dev, &pda_psy_ac); if (ret) { @@ -303,13 +346,6 @@ static int pda_power_probe(struct platform_device *pdev) } } -#ifdef CONFIG_USB_OTG_UTILS - transceiver = otg_get_transceiver(); - if (transceiver && !pdata->is_usb_online) { - pdata->is_usb_online = otg_is_usb_online; - } -#endif - if (pdata->is_usb_online) { ret = power_supply_register(&pdev->dev, &pda_psy_usb); if (ret) { @@ -331,6 +367,16 @@ static int pda_power_probe(struct platform_device *pdev) } } + if (transceiver && pdata->use_otg_notifier) { + otg_nb.notifier_call = otg_handle_notification; + ret = otg_register_notifier(transceiver, &otg_nb); + if (ret) { + dev_err(dev, "failure to register otg notifier\n"); + goto otg_reg_notifier_failed; + } + polling = 0; + } + if (polling) { dev_dbg(dev, "will poll for status\n"); setup_timer(&polling_timer, polling_timer_func, 0); @@ -343,16 +389,17 @@ static int pda_power_probe(struct platform_device *pdev) return 0; +otg_reg_notifier_failed: + if (pdata->is_usb_online && usb_irq) + free_irq(usb_irq->start, &pda_psy_usb); usb_irq_failed: if (pdata->is_usb_online) power_supply_unregister(&pda_psy_usb); usb_supply_failed: if (pdata->is_ac_online && ac_irq) free_irq(ac_irq->start, &pda_psy_ac); -#ifdef CONFIG_USB_OTG_UTILS if (transceiver) otg_put_transceiver(transceiver); -#endif ac_irq_failed: if (pdata->is_ac_online) power_supply_unregister(&pda_psy_ac); -- cgit v1.1 From 35c3ae5eef3fc641c75c5e1e4307835c8efa5f6b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 16 Aug 2011 08:37:42 +0900 Subject: wm831x_power: Only register WM831x battery charger if enabled Don't tell the system about the battery charger if it's not enabled, we can reasonably assume that if the charger is not enabled then no battery will ever be connected so reporting state is at best going to waste time and at worst going to cause confusing information in the UI. For simplicity and robustness we continue to register and handle interrupts from the charger. Signed-off-by: Mark Brown Signed-off-by: Anton Vorontsov --- drivers/power/power_supply_sysfs.c | 4 ++-- drivers/power/wm831x_power.c | 44 ++++++++++++++++++++++++-------------- 2 files changed, 30 insertions(+), 18 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 605514a..eb9a986 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -77,8 +77,8 @@ static ssize_t power_supply_show_property(struct device *dev, dev_dbg(dev, "driver has no data for `%s' property\n", attr->attr.name); else if (ret != -ENODEV) - dev_err(dev, "driver failed to report `%s' property\n", - attr->attr.name); + dev_err(dev, "driver failed to report `%s' property: %d\n", + attr->attr.name, ret); return ret; } diff --git a/drivers/power/wm831x_power.c b/drivers/power/wm831x_power.c index 6cc2ca6..c32e6f8 100644 --- a/drivers/power/wm831x_power.c +++ b/drivers/power/wm831x_power.c @@ -27,6 +27,7 @@ struct wm831x_power { char wall_name[20]; char usb_name[20]; char battery_name[20]; + bool have_battery; }; static int wm831x_power_check_online(struct wm831x *wm831x, int supply, @@ -449,7 +450,8 @@ static irqreturn_t wm831x_bat_irq(int irq, void *data) /* The battery charger is autonomous so we don't need to do * anything except kick user space */ - power_supply_changed(&wm831x_power->battery); + if (wm831x_power->have_battery) + power_supply_changed(&wm831x_power->battery); return IRQ_HANDLED; } @@ -479,7 +481,8 @@ static irqreturn_t wm831x_pwr_src_irq(int irq, void *data) dev_dbg(wm831x->dev, "Power source changed\n"); /* Just notify for everything - little harm in overnotifying. */ - power_supply_changed(&wm831x_power->battery); + if (wm831x_power->have_battery) + power_supply_changed(&wm831x_power->battery); power_supply_changed(&wm831x_power->usb); power_supply_changed(&wm831x_power->wall); @@ -537,15 +540,6 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev) if (ret) goto err_kmalloc; - battery->name = power->battery_name; - battery->properties = wm831x_bat_props; - battery->num_properties = ARRAY_SIZE(wm831x_bat_props); - battery->get_property = wm831x_bat_get_prop; - battery->use_for_apm = 1; - ret = power_supply_register(&pdev->dev, battery); - if (ret) - goto err_wall; - usb->name = power->usb_name, usb->type = POWER_SUPPLY_TYPE_USB; usb->properties = wm831x_usb_props; @@ -553,7 +547,23 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev) usb->get_property = wm831x_usb_get_prop; ret = power_supply_register(&pdev->dev, usb); if (ret) - goto err_battery; + goto err_wall; + + ret = wm831x_reg_read(wm831x, WM831X_CHARGER_CONTROL_1); + if (ret < 0) + goto err_wall; + power->have_battery = ret & WM831X_CHG_ENA; + + if (power->have_battery) { + battery->name = power->battery_name; + battery->properties = wm831x_bat_props; + battery->num_properties = ARRAY_SIZE(wm831x_bat_props); + battery->get_property = wm831x_bat_get_prop; + battery->use_for_apm = 1; + ret = power_supply_register(&pdev->dev, battery); + if (ret) + goto err_usb; + } irq = platform_get_irq_byname(pdev, "SYSLO"); ret = request_threaded_irq(irq, NULL, wm831x_syslo_irq, @@ -562,7 +572,7 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev) if (ret != 0) { dev_err(&pdev->dev, "Failed to request SYSLO IRQ %d: %d\n", irq, ret); - goto err_usb; + goto err_battery; } irq = platform_get_irq_byname(pdev, "PWR SRC"); @@ -601,10 +611,11 @@ err_bat_irq: err_syslo: irq = platform_get_irq_byname(pdev, "SYSLO"); free_irq(irq, power); +err_battery: + if (power->have_battery) + power_supply_unregister(battery); err_usb: power_supply_unregister(usb); -err_battery: - power_supply_unregister(battery); err_wall: power_supply_unregister(wall); err_kmalloc: @@ -628,7 +639,8 @@ static __devexit int wm831x_power_remove(struct platform_device *pdev) irq = platform_get_irq_byname(pdev, "SYSLO"); free_irq(irq, wm831x_power); - power_supply_unregister(&wm831x_power->battery); + if (wm831x_power->have_battery) + power_supply_unregister(&wm831x_power->battery); power_supply_unregister(&wm831x_power->wall); power_supply_unregister(&wm831x_power->usb); kfree(wm831x_power); -- cgit v1.1 From c3503fd02558245ebbb0b48f3ae1d62416e3fd2a Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Wed, 10 Aug 2011 21:45:36 +0100 Subject: olpc_battery: Bind to device tree This is cleaner, and allows suspend/resume (wakeup) handlers to be added in an upcoming patch. Signed-off-by: Daniel Drake Acked-by: Andres Salomon Signed-off-by: Anton Vorontsov --- drivers/power/olpc_battery.c | 55 +++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 21 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/olpc_battery.c b/drivers/power/olpc_battery.c index 0b0ff3a..9972268 100644 --- a/drivers/power/olpc_battery.c +++ b/drivers/power/olpc_battery.c @@ -519,9 +519,8 @@ static struct device_attribute olpc_bat_error = { * Initialisation *********************************************************************/ -static struct platform_device *bat_pdev; - static struct power_supply olpc_bat = { + .name = "olpc-battery", .get_property = olpc_bat_get_property, .use_for_apm = 1, }; @@ -534,14 +533,11 @@ void olpc_battery_trigger_uevent(unsigned long cause) kobject_uevent(&olpc_bat.dev->kobj, KOBJ_CHANGE); } -static int __init olpc_bat_init(void) +static int __devinit olpc_battery_probe(struct platform_device *pdev) { - int ret = 0; + int ret; uint8_t status; - if (!olpc_platform_info.ecver) - return -ENXIO; - /* * We've seen a number of EC protocol changes; this driver requires * the latest EC protocol, supported by 0x44 and above. @@ -558,15 +554,10 @@ static int __init olpc_bat_init(void) /* Ignore the status. It doesn't actually matter */ - bat_pdev = platform_device_register_simple("olpc-battery", 0, NULL, 0); - if (IS_ERR(bat_pdev)) - return PTR_ERR(bat_pdev); - - ret = power_supply_register(&bat_pdev->dev, &olpc_ac); + ret = power_supply_register(&pdev->dev, &olpc_ac); if (ret) - goto ac_failed; + return ret; - olpc_bat.name = bat_pdev->name; if (olpc_board_at_least(olpc_board_pre(0xd0))) { /* XO-1.5 */ olpc_bat.properties = olpc_xo15_bat_props; olpc_bat.num_properties = ARRAY_SIZE(olpc_xo15_bat_props); @@ -575,7 +566,7 @@ static int __init olpc_bat_init(void) olpc_bat.num_properties = ARRAY_SIZE(olpc_xo1_bat_props); } - ret = power_supply_register(&bat_pdev->dev, &olpc_bat); + ret = power_supply_register(&pdev->dev, &olpc_bat); if (ret) goto battery_failed; @@ -587,7 +578,7 @@ static int __init olpc_bat_init(void) if (ret) goto error_failed; - goto success; + return 0; error_failed: device_remove_bin_file(olpc_bat.dev, &olpc_bat_eeprom); @@ -595,22 +586,44 @@ eeprom_failed: power_supply_unregister(&olpc_bat); battery_failed: power_supply_unregister(&olpc_ac); -ac_failed: - platform_device_unregister(bat_pdev); -success: return ret; } -static void __exit olpc_bat_exit(void) +static int __devexit olpc_battery_remove(struct platform_device *pdev) { device_remove_file(olpc_bat.dev, &olpc_bat_error); device_remove_bin_file(olpc_bat.dev, &olpc_bat_eeprom); power_supply_unregister(&olpc_bat); power_supply_unregister(&olpc_ac); - platform_device_unregister(bat_pdev); + return 0; } +static const struct of_device_id olpc_battery_ids[] __devinitconst = { + { .compatible = "olpc,xo1-battery" }, + {} +}; +MODULE_DEVICE_TABLE(of, olpc_battery_ids); + +static struct platform_driver olpc_battery_drv = { + .driver = { + .name = "olpc-battery", + .owner = THIS_MODULE, + .of_match_table = olpc_battery_ids, + }, + .probe = olpc_battery_probe, + .remove = __devexit_p(olpc_battery_remove), +}; + +static int __init olpc_bat_init(void) +{ + return platform_driver_register(&olpc_battery_drv); +} module_init(olpc_bat_init); + +static void __exit olpc_bat_exit(void) +{ + platform_driver_unregister(&olpc_battery_drv); +} module_exit(olpc_bat_exit); MODULE_AUTHOR("David Woodhouse "); -- cgit v1.1 From cae659af87288a4b1723cdf94d713ef7fb4c56d9 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Wed, 10 Aug 2011 21:46:02 +0100 Subject: olpc_battery: Add wakeup support Battery and AC events can now be used to wake up the system from suspend. Signed-off-by: Daniel Drake Acked-by: Andres Salomon Signed-off-by: Anton Vorontsov --- drivers/power/olpc_battery.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/olpc_battery.c b/drivers/power/olpc_battery.c index 9972268..46a2f07 100644 --- a/drivers/power/olpc_battery.c +++ b/drivers/power/olpc_battery.c @@ -533,6 +533,24 @@ void olpc_battery_trigger_uevent(unsigned long cause) kobject_uevent(&olpc_bat.dev->kobj, KOBJ_CHANGE); } +static int olpc_battery_suspend(struct platform_device *pdev, + pm_message_t state) +{ + if (device_may_wakeup(olpc_ac.dev)) + olpc_ec_wakeup_set(EC_SCI_SRC_ACPWR); + else + olpc_ec_wakeup_clear(EC_SCI_SRC_ACPWR); + + if (device_may_wakeup(olpc_bat.dev)) + olpc_ec_wakeup_set(EC_SCI_SRC_BATTERY | EC_SCI_SRC_BATSOC + | EC_SCI_SRC_BATERR); + else + olpc_ec_wakeup_clear(EC_SCI_SRC_BATTERY | EC_SCI_SRC_BATSOC + | EC_SCI_SRC_BATERR); + + return 0; +} + static int __devinit olpc_battery_probe(struct platform_device *pdev) { int ret; @@ -578,6 +596,11 @@ static int __devinit olpc_battery_probe(struct platform_device *pdev) if (ret) goto error_failed; + if (olpc_ec_wakeup_available()) { + device_set_wakeup_capable(olpc_ac.dev, true); + device_set_wakeup_capable(olpc_bat.dev, true); + } + return 0; error_failed: @@ -612,6 +635,7 @@ static struct platform_driver olpc_battery_drv = { }, .probe = olpc_battery_probe, .remove = __devexit_p(olpc_battery_remove), + .suspend = olpc_battery_suspend, }; static int __init olpc_bat_init(void) -- cgit v1.1 From 45d116ec2503c4255374e11c2c48f81cdf6d0f1d Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 1 Aug 2011 07:41:50 +0800 Subject: max8998_charger: Allow full timeout not set, leave it unchanged Add a missing break for case 0 of pdata->timeout, otherwise it will fall through to the default case and return error. Signed-off-by: Axel Lin Acked-by: MyungJoo Ham Signed-off-by: Anton Vorontsov --- drivers/power/max8998_charger.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/power') diff --git a/drivers/power/max8998_charger.c b/drivers/power/max8998_charger.c index 8ac5fab..6f5ffc3 100644 --- a/drivers/power/max8998_charger.c +++ b/drivers/power/max8998_charger.c @@ -153,6 +153,7 @@ static __devinit int max8998_battery_probe(struct platform_device *pdev) case 0: dev_dbg(max8998->dev, "Full Timeout not set: leave it unchanged.\n"); + break; default: dev_err(max8998->dev, "Invalid Full Timeout value\n"); ret = -EINVAL; -- cgit v1.1 From 4cfa892c03b35f3c61d93e0d8d97ff8db074818f Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Fri, 12 Aug 2011 21:18:18 -0700 Subject: max17042_battery: Divide by 0 crash because r_sns init too late On MMP2 brownstone divide by 0 error since probe sets r_sns after calling power_supply_register. Move the code up a few lines. r_sns comes from the platform data. PROP_CURRENT_AVG and PROP_CURRENT divide the result by r_sns. Signed-off-by: Philip Rakity Acked-by: MyungJoo Ham Signed-off-by: Anton Vorontsov --- drivers/power/max17042_battery.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index 98bfab3..4628450 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c @@ -209,6 +209,9 @@ static int __devinit max17042_probe(struct i2c_client *client, if (!chip->pdata->enable_current_sense) chip->battery.num_properties -= 2; + if (chip->pdata->r_sns == 0) + chip->pdata->r_sns = MAX17042_DEFAULT_SNS_RESISTOR; + ret = power_supply_register(&client->dev, &chip->battery); if (ret) { dev_err(&client->dev, "failed: power supply register\n"); @@ -225,9 +228,6 @@ static int __devinit max17042_probe(struct i2c_client *client, max17042_write_reg(client, MAX17042_CGAIN, 0x0000); max17042_write_reg(client, MAX17042_MiscCFG, 0x0003); max17042_write_reg(client, MAX17042_LearnCFG, 0x0007); - } else { - if (chip->pdata->r_sns == 0) - chip->pdata->r_sns = MAX17042_DEFAULT_SNS_RESISTOR; } return 0; -- cgit v1.1 From 91d8b0d6f81d2946962ee559090a34834dfd467b Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Fri, 12 Aug 2011 21:19:57 -0700 Subject: max17042_battery: Do not lose accuracy calculating current_now PROP_CURRENT_NOW value is first divided then multiplied up causing a lose of accuracy. Use the same method as PROP_CURRENT_AVG to do the calculation. Signed-off-by: Philip Rakity Acked-by: MyungJoo Ham Signed-off-by: Anton Vorontsov --- drivers/power/max17042_battery.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index 4628450..61fb6d7 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c @@ -152,8 +152,7 @@ static int max17042_get_property(struct power_supply *psy, val->intval++; val->intval *= -1; } - val->intval >>= 4; - val->intval *= 1000000 * 25 / chip->pdata->r_sns; + val->intval *= 1562500 / chip->pdata->r_sns; } else { return -EINVAL; } -- cgit v1.1 From cf7a8c03db792894f436db5f3ffc44d947b9b068 Mon Sep 17 00:00:00 2001 From: MyungJoo Ham Date: Wed, 17 Aug 2011 10:18:34 +0900 Subject: max17042_battery: Bugfix of incorrect voltage register value interpretation The calculation had error in getting voltage values from MAX17042 registers. The least bit denotes 78.125uV (625/8). Signed-off-by: MyungJoo Ham Signed-off-by: Philip Rakity Signed-off-by: Kyungmin Park Signed-off-by: Anton Vorontsov --- drivers/power/max17042_battery.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index 61fb6d7..a6dc9c7 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c @@ -111,12 +111,12 @@ static int max17042_get_property(struct power_supply *psy, val->intval *= 10000; /* Units of LSB = 10mV */ break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: - val->intval = max17042_read_reg(chip->client, - MAX17042_VCELL) * 83; /* 1000 / 12 = 83 */ + val->intval = max17042_read_reg(chip->client, MAX17042_VCELL) + * 625 / 8; break; case POWER_SUPPLY_PROP_VOLTAGE_AVG: - val->intval = max17042_read_reg(chip->client, - MAX17042_AvgVCELL) * 83; + val->intval = max17042_read_reg(chip->client, MAX17042_AvgVCELL) + * 625 / 8; break; case POWER_SUPPLY_PROP_CAPACITY: val->intval = max17042_read_reg(chip->client, -- cgit v1.1 From 1c74529dbecf8839d3ed360690f937d68c14e55b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 23 Aug 2011 20:34:33 +0800 Subject: pda_power: Fix build error if !CONFIG_USB_OTG_UTILS commit 9ad63986c606c60e2e916b1b96f22991f966d9cc "pda_power: Add support for using otg transceiver events" introduces below build error if !CONFIG_USB_OTG_UTILS. CC drivers/power/pda_power.o drivers/power/pda_power.c: In function 'pda_power_probe': drivers/power/pda_power.c:322: error: 'otg_is_usb_online' undeclared (first use in this function) drivers/power/pda_power.c:322: error: (Each undeclared identifier is reported only once drivers/power/pda_power.c:322: error: for each function it appears in.) drivers/power/pda_power.c:325: error: 'otg_is_ac_online' undeclared (first use in this function) drivers/power/pda_power.c:371: error: 'otg_handle_notification' undeclared (first use in this function) make[2]: *** [drivers/power/pda_power.o] Error 1 make[1]: *** [drivers/power] Error 2 make: *** [drivers] Error 2 This patch adds #ifdef CONFIG_USB_OTG_UTILS guards at necessary places to fix build errors. Signed-off-by: Axel Lin Signed-off-by: Anton Vorontsov --- drivers/power/pda_power.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c index 81b7201..87be679 100644 --- a/drivers/power/pda_power.c +++ b/drivers/power/pda_power.c @@ -39,8 +39,11 @@ static struct timer_list supply_timer; static struct timer_list polling_timer; static int polling; +#ifdef CONFIG_USB_OTG_UTILS static struct otg_transceiver *transceiver; static struct notifier_block otg_nb; +#endif + static struct regulator *ac_draw; enum { @@ -317,6 +320,7 @@ static int pda_power_probe(struct platform_device *pdev) ret = PTR_ERR(ac_draw); } +#ifdef CONFIG_USB_OTG_UTILS transceiver = otg_get_transceiver(); if (transceiver && !pdata->is_usb_online) { pdata->is_usb_online = otg_is_usb_online; @@ -324,6 +328,7 @@ static int pda_power_probe(struct platform_device *pdev) if (transceiver && !pdata->is_ac_online) { pdata->is_ac_online = otg_is_ac_online; } +#endif if (pdata->is_ac_online) { ret = power_supply_register(&pdev->dev, &pda_psy_ac); @@ -367,6 +372,7 @@ static int pda_power_probe(struct platform_device *pdev) } } +#ifdef CONFIG_USB_OTG_UTILS if (transceiver && pdata->use_otg_notifier) { otg_nb.notifier_call = otg_handle_notification; ret = otg_register_notifier(transceiver, &otg_nb); @@ -376,6 +382,7 @@ static int pda_power_probe(struct platform_device *pdev) } polling = 0; } +#endif if (polling) { dev_dbg(dev, "will poll for status\n"); @@ -389,17 +396,21 @@ static int pda_power_probe(struct platform_device *pdev) return 0; +#ifdef CONFIG_USB_OTG_UTILS otg_reg_notifier_failed: if (pdata->is_usb_online && usb_irq) free_irq(usb_irq->start, &pda_psy_usb); +#endif usb_irq_failed: if (pdata->is_usb_online) power_supply_unregister(&pda_psy_usb); usb_supply_failed: if (pdata->is_ac_online && ac_irq) free_irq(ac_irq->start, &pda_psy_ac); +#ifdef CONFIG_USB_OTG_UTILS if (transceiver) otg_put_transceiver(transceiver); +#endif ac_irq_failed: if (pdata->is_ac_online) power_supply_unregister(&pda_psy_ac); -- cgit v1.1 From 85b5fbf784810d80aa09646189848c9ebc8a4ba2 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 22 Aug 2011 11:30:47 -0700 Subject: power_supply: Fix sysfs format warning Fix format warning: drivers/power/power_supply_sysfs.c:82: warning: format '%d' expects type 'int', but argument 4 has type 'ssize_t' Signed-off-by: Randy Dunlap Signed-off-by: Anton Vorontsov --- drivers/power/power_supply_sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index eb9a986..58cc490 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -77,7 +77,7 @@ static ssize_t power_supply_show_property(struct device *dev, dev_dbg(dev, "driver has no data for `%s' property\n", attr->attr.name); else if (ret != -ENODEV) - dev_err(dev, "driver failed to report `%s' property: %d\n", + dev_err(dev, "driver failed to report `%s' property: %zd\n", attr->attr.name, ret); return ret; } -- cgit v1.1 From 6c75ea1e582d12120072cae742a0d63ea8ac8c65 Mon Sep 17 00:00:00 2001 From: Rhyland Klein Date: Wed, 14 Sep 2011 13:19:07 -0700 Subject: bq20z75: Devicetree init support Adding support to generate platform data when kernel is configured through device tree. Also adding the binding for the TI bq20z75 fuel gadge and the bq20z75 driver. Signed-off-by: Rhyland Klein Acked-by: Grant Likely Signed-off-by: Anton Vorontsov --- drivers/power/bq20z75.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/bq20z75.c b/drivers/power/bq20z75.c index 9c5e5be..ce95ff7 100644 --- a/drivers/power/bq20z75.c +++ b/drivers/power/bq20z75.c @@ -613,6 +613,80 @@ static void bq20z75_delayed_work(struct work_struct *work) } } +#if defined(CONFIG_OF) + +#include +#include + +static const struct of_device_id bq20z75_dt_ids[] = { + { .compatible = "ti,bq20z75" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, bq20z75_dt_ids); + +static struct bq20z75_platform_data *bq20z75_of_populate_pdata( + struct i2c_client *client) +{ + struct device_node *of_node = client->dev.of_node; + struct bq20z75_platform_data *pdata = client->dev.platform_data; + enum of_gpio_flags gpio_flags; + int rc; + u32 prop; + + /* verify this driver matches this device */ + if (!of_node) + return NULL; + + /* if platform data is set, honor it */ + if (pdata) + return pdata; + + /* first make sure at least one property is set, otherwise + * it won't change behavior from running without pdata. + */ + if (!of_get_property(of_node, "ti,i2c-retry-count", NULL) && + !of_get_property(of_node, "ti,poll-retry-count", NULL) && + !of_get_property(of_node, "ti,battery-detect-gpios", NULL)) + goto of_out; + + pdata = devm_kzalloc(&client->dev, sizeof(struct bq20z75_platform_data), + GFP_KERNEL); + if (!pdata) + goto of_out; + + rc = of_property_read_u32(of_node, "ti,i2c-retry-count", &prop); + if (!rc) + pdata->i2c_retry_count = prop; + + rc = of_property_read_u32(of_node, "ti,poll-retry-count", &prop); + if (!rc) + pdata->poll_retry_count = prop; + + if (!of_get_property(of_node, "ti,battery-detect-gpios", NULL)) { + pdata->battery_detect = -1; + goto of_out; + } + + pdata->battery_detect = of_get_named_gpio_flags(of_node, + "ti,battery-detect-gpios", 0, &gpio_flags); + + if (gpio_flags & OF_GPIO_ACTIVE_LOW) + pdata->battery_detect_present = 0; + else + pdata->battery_detect_present = 1; + +of_out: + return pdata; +} +#else +#define bq20z75_dt_ids NULL +static struct bq20z75_platform_data *bq20z75_of_populate_pdata( + struct i2c_client *client) +{ + return client->dev.platform_data; +} +#endif + static int __devinit bq20z75_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -642,6 +716,8 @@ static int __devinit bq20z75_probe(struct i2c_client *client, bq20z75_device->power_supply.external_power_changed = bq20z75_external_power_changed; + pdata = bq20z75_of_populate_pdata(client); + if (pdata) { bq20z75_device->gpio_detect = gpio_is_valid(pdata->battery_detect); @@ -775,6 +851,7 @@ static struct i2c_driver bq20z75_battery_driver = { .id_table = bq20z75_id, .driver = { .name = "bq20z75-battery", + .of_match_table = bq20z75_dt_ids, }, }; -- cgit v1.1 From 5519d00e6a5d99a163484722ef7cea8bff47bc58 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 24 Nov 2011 22:49:07 +0400 Subject: olpc_battery: Fix section mismatch noise This patch fixes the following noise (by renaming _drv to _driver): WARNING: drivers/power/olpc_battery.o(.data+0x100): Section mismatch in reference from the variable olpc_battery_drv to the function .devinit.text:olpc_battery_probe() The variable olpc_battery_drv references the function __devinit olpc_battery_probe() If the reference is valid then annotate the variable with __init* or __refdata (see linux/init.h) or name the variable: *driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console WARNING: drivers/power/olpc_battery.o(.data+0x104): Section mismatch in reference from the variable olpc_battery_drv to the function .devexit.text:olpc_battery_remove() The variable olpc_battery_drv references the function __devexit olpc_battery_remove() If the reference is valid then annotate the variable with __exit* (see linux/init.h) or name the variable: *driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console WARNING: drivers/power/olpc_battery.o(.data+0x128): Section mismatch in reference from the variable olpc_battery_drv to the variable .devinit.rodata:olpc_battery_ids The variable olpc_battery_drv references the variable __devinitconst olpc_battery_ids If the reference is valid then annotate the variable with __init* or __refdata (see linux/init.h) or name the variable: *driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one, *_console Signed-off-by: Anton Vorontsov --- drivers/power/olpc_battery.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/olpc_battery.c b/drivers/power/olpc_battery.c index 46a2f07..dc60f61 100644 --- a/drivers/power/olpc_battery.c +++ b/drivers/power/olpc_battery.c @@ -627,7 +627,7 @@ static const struct of_device_id olpc_battery_ids[] __devinitconst = { }; MODULE_DEVICE_TABLE(of, olpc_battery_ids); -static struct platform_driver olpc_battery_drv = { +static struct platform_driver olpc_battery_driver = { .driver = { .name = "olpc-battery", .owner = THIS_MODULE, @@ -640,13 +640,13 @@ static struct platform_driver olpc_battery_drv = { static int __init olpc_bat_init(void) { - return platform_driver_register(&olpc_battery_drv); + return platform_driver_register(&olpc_battery_driver); } module_init(olpc_bat_init); static void __exit olpc_bat_exit(void) { - platform_driver_unregister(&olpc_battery_drv); + platform_driver_unregister(&olpc_battery_driver); } module_exit(olpc_bat_exit); -- cgit v1.1 From 7ab2f0207dd493e9ac8bae78324cf5592fa92650 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 24 Nov 2011 22:52:48 +0400 Subject: olpc_battery: Remove unneeded 'olpc_battery_trigger_uevent' It is no longer used, so we can safely remove it. Signed-off-by: Anton Vorontsov --- drivers/power/olpc_battery.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/olpc_battery.c b/drivers/power/olpc_battery.c index dc60f61..21e7c06 100644 --- a/drivers/power/olpc_battery.c +++ b/drivers/power/olpc_battery.c @@ -525,14 +525,6 @@ static struct power_supply olpc_bat = { .use_for_apm = 1, }; -void olpc_battery_trigger_uevent(unsigned long cause) -{ - if (cause & EC_SCI_SRC_ACPWR) - kobject_uevent(&olpc_ac.dev->kobj, KOBJ_CHANGE); - if (cause & (EC_SCI_SRC_BATERR|EC_SCI_SRC_BATSOC|EC_SCI_SRC_BATTERY)) - kobject_uevent(&olpc_bat.dev->kobj, KOBJ_CHANGE); -} - static int olpc_battery_suspend(struct platform_device *pdev, pm_message_t state) { -- cgit v1.1 From 389cd203d611db376c8226e6a7363104ca3769de Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 26 Aug 2011 15:52:48 +0800 Subject: collie_battery: Convert to gpio_request_array() / gpio_free_array() As suggested by Igor Grinberg, this change make the implementation looks simpler. Signed-off-by: Axel Lin Signed-off-by: Anton Vorontsov --- drivers/power/collie_battery.c | 53 +++++++++++------------------------------- 1 file changed, 13 insertions(+), 40 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/collie_battery.c b/drivers/power/collie_battery.c index 548d263..1b4c17b 100644 --- a/drivers/power/collie_battery.c +++ b/drivers/power/collie_battery.c @@ -277,18 +277,13 @@ static struct collie_bat collie_bat_bu = { .adc_temp_divider = -1, }; -static struct { - int gpio; - char *name; - bool output; - int value; -} gpios[] = { - { COLLIE_GPIO_CO, "main battery full", 0, 0 }, - { COLLIE_GPIO_MAIN_BAT_LOW, "main battery low", 0, 0 }, - { COLLIE_GPIO_CHARGE_ON, "main charge on", 1, 0 }, - { COLLIE_GPIO_MBAT_ON, "main battery", 1, 0 }, - { COLLIE_GPIO_TMP_ON, "main battery temp", 1, 0 }, - { COLLIE_GPIO_BBAT_ON, "backup battery", 1, 0 }, +static struct gpio collie_batt_gpios[] = { + { COLLIE_GPIO_CO, GPIOF_IN, "main battery full" }, + { COLLIE_GPIO_MAIN_BAT_LOW, GPIOF_IN, "main battery low" }, + { COLLIE_GPIO_CHARGE_ON, GPIOF_OUT_INIT_LOW, "main charge on" }, + { COLLIE_GPIO_MBAT_ON, GPIOF_OUT_INIT_LOW, "main battery" }, + { COLLIE_GPIO_TMP_ON, GPIOF_OUT_INIT_LOW, "main battery temp" }, + { COLLIE_GPIO_BBAT_ON, GPIOF_OUT_INIT_LOW, "backup battery" }, }; #ifdef CONFIG_PM @@ -313,29 +308,16 @@ static int collie_bat_resume(struct ucb1x00_dev *dev) static int __devinit collie_bat_probe(struct ucb1x00_dev *dev) { int ret; - int i; if (!machine_is_collie()) return -ENODEV; ucb = dev->ucb; - for (i = 0; i < ARRAY_SIZE(gpios); i++) { - ret = gpio_request(gpios[i].gpio, gpios[i].name); - if (ret) { - i--; - goto err_gpio; - } - - if (gpios[i].output) - ret = gpio_direction_output(gpios[i].gpio, - gpios[i].value); - else - ret = gpio_direction_input(gpios[i].gpio); - - if (ret) - goto err_gpio; - } + ret = gpio_request_array(collie_batt_gpios, + ARRAY_SIZE(collie_batt_gpios)); + if (ret) + return ret; mutex_init(&collie_bat_main.work_lock); @@ -363,19 +345,12 @@ err_psy_reg_main: /* see comment in collie_bat_remove */ cancel_work_sync(&bat_work); - - i--; -err_gpio: - for (; i >= 0; i--) - gpio_free(gpios[i].gpio); - + gpio_free_array(collie_batt_gpios, ARRAY_SIZE(collie_batt_gpios)); return ret; } static void __devexit collie_bat_remove(struct ucb1x00_dev *dev) { - int i; - free_irq(gpio_to_irq(COLLIE_GPIO_CO), &collie_bat_main); power_supply_unregister(&collie_bat_bu.psy); @@ -387,9 +362,7 @@ static void __devexit collie_bat_remove(struct ucb1x00_dev *dev) * unregistered now. */ cancel_work_sync(&bat_work); - - for (i = ARRAY_SIZE(gpios) - 1; i >= 0; i--) - gpio_free(gpios[i].gpio); + gpio_free_array(collie_batt_gpios, ARRAY_SIZE(collie_batt_gpios)); } static struct ucb1x00_driver collie_bat_driver = { -- cgit v1.1 From 019370d35a671839f1df1d05794e15942cac002b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 26 Aug 2011 19:55:17 +0800 Subject: tosa_battery: Convert to gpio_request_array() / gpio_free_array() This change simplifies the implementation. Signed-off-by: Axel Lin Acked-by: Dmitry Eremin-Solenikov Signed-off-by: Anton Vorontsov --- drivers/power/tosa_battery.c | 66 +++++++++++++------------------------------- 1 file changed, 19 insertions(+), 47 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/tosa_battery.c b/drivers/power/tosa_battery.c index 53f0d35..ada86a4 100644 --- a/drivers/power/tosa_battery.c +++ b/drivers/power/tosa_battery.c @@ -307,25 +307,20 @@ static struct tosa_bat tosa_bat_bu = { .adc_temp_divider = -1, }; -static struct { - int gpio; - char *name; - bool output; - int value; -} gpios[] = { - { TOSA_GPIO_CHARGE_OFF, "main charge off", 1, 1 }, - { TOSA_GPIO_CHARGE_OFF_JC, "jacket charge off", 1, 1 }, - { TOSA_GPIO_BAT_SW_ON, "battery switch", 1, 0 }, - { TOSA_GPIO_BAT0_V_ON, "main battery", 1, 0 }, - { TOSA_GPIO_BAT1_V_ON, "jacket battery", 1, 0 }, - { TOSA_GPIO_BAT1_TH_ON, "main battery temp", 1, 0 }, - { TOSA_GPIO_BAT0_TH_ON, "jacket battery temp", 1, 0 }, - { TOSA_GPIO_BU_CHRG_ON, "backup battery", 1, 0 }, - { TOSA_GPIO_BAT0_CRG, "main battery full", 0, 0 }, - { TOSA_GPIO_BAT1_CRG, "jacket battery full", 0, 0 }, - { TOSA_GPIO_BAT0_LOW, "main battery low", 0, 0 }, - { TOSA_GPIO_BAT1_LOW, "jacket battery low", 0, 0 }, - { TOSA_GPIO_JACKET_DETECT, "jacket detect", 0, 0 }, +static struct gpio tosa_bat_gpios[] = { + { TOSA_GPIO_CHARGE_OFF, GPIOF_OUT_INIT_HIGH, "main charge off" }, + { TOSA_GPIO_CHARGE_OFF_JC, GPIOF_OUT_INIT_HIGH, "jacket charge off" }, + { TOSA_GPIO_BAT_SW_ON, GPIOF_OUT_INIT_LOW, "battery switch" }, + { TOSA_GPIO_BAT0_V_ON, GPIOF_OUT_INIT_LOW, "main battery" }, + { TOSA_GPIO_BAT1_V_ON, GPIOF_OUT_INIT_LOW, "jacket battery" }, + { TOSA_GPIO_BAT1_TH_ON, GPIOF_OUT_INIT_LOW, "main battery temp" }, + { TOSA_GPIO_BAT0_TH_ON, GPIOF_OUT_INIT_LOW, "jacket battery temp" }, + { TOSA_GPIO_BU_CHRG_ON, GPIOF_OUT_INIT_LOW, "backup battery" }, + { TOSA_GPIO_BAT0_CRG, GPIOF_IN, "main battery full" }, + { TOSA_GPIO_BAT1_CRG, GPIOF_IN, "jacket battery full" }, + { TOSA_GPIO_BAT0_LOW, GPIOF_IN, "main battery low" }, + { TOSA_GPIO_BAT1_LOW, GPIOF_IN, "jacket battery low" }, + { TOSA_GPIO_JACKET_DETECT, GPIOF_IN, "jacket detect" }, }; #ifdef CONFIG_PM @@ -350,27 +345,13 @@ static int tosa_bat_resume(struct platform_device *dev) static int __devinit tosa_bat_probe(struct platform_device *dev) { int ret; - int i; if (!machine_is_tosa()) return -ENODEV; - for (i = 0; i < ARRAY_SIZE(gpios); i++) { - ret = gpio_request(gpios[i].gpio, gpios[i].name); - if (ret) { - i--; - goto err_gpio; - } - - if (gpios[i].output) - ret = gpio_direction_output(gpios[i].gpio, - gpios[i].value); - else - ret = gpio_direction_input(gpios[i].gpio); - - if (ret) - goto err_gpio; - } + ret = gpio_request_array(tosa_bat_gpios, ARRAY_SIZE(tosa_bat_gpios)); + if (ret) + return ret; mutex_init(&tosa_bat_main.work_lock); mutex_init(&tosa_bat_jacket.work_lock); @@ -424,18 +405,12 @@ err_psy_reg_main: /* see comment in tosa_bat_remove */ cancel_work_sync(&bat_work); - i--; -err_gpio: - for (; i >= 0; i--) - gpio_free(gpios[i].gpio); - + gpio_free_array(tosa_bat_gpios, ARRAY_SIZE(tosa_bat_gpios)); return ret; } static int __devexit tosa_bat_remove(struct platform_device *dev) { - int i; - free_irq(gpio_to_irq(TOSA_GPIO_JACKET_DETECT), &tosa_bat_jacket); free_irq(gpio_to_irq(TOSA_GPIO_BAT1_CRG), &tosa_bat_jacket); free_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG), &tosa_bat_main); @@ -450,10 +425,7 @@ static int __devexit tosa_bat_remove(struct platform_device *dev) * unregistered now. */ cancel_work_sync(&bat_work); - - for (i = ARRAY_SIZE(gpios) - 1; i >= 0; i--) - gpio_free(gpios[i].gpio); - + gpio_free_array(tosa_bat_gpios, ARRAY_SIZE(tosa_bat_gpios)); return 0; } -- cgit v1.1 From 7925231037447d1a9036f31c823d362bf2ef4bb0 Mon Sep 17 00:00:00 2001 From: Major Lee Date: Fri, 26 Aug 2011 12:22:00 +0100 Subject: intel_mid_battery: Handle Over Current gracefully When DCDC input line over current detecting, PMIC will change charging current automatically. Logging event is enough. Signed-off-by: Major Lee Signed-off-by: Alan Cox Signed-off-by: Anton Vorontsov --- drivers/power/intel_mid_battery.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/intel_mid_battery.c b/drivers/power/intel_mid_battery.c index cffcb7c..01fa671 100644 --- a/drivers/power/intel_mid_battery.c +++ b/drivers/power/intel_mid_battery.c @@ -61,7 +61,8 @@ MODULE_PARM_DESC(debug, "Flag to enable PMIC Battery debug messages."); #define PMIC_BATT_CHR_SBATDET_MASK (1 << 5) #define PMIC_BATT_CHR_SDCLMT_MASK (1 << 6) #define PMIC_BATT_CHR_SUSBOVP_MASK (1 << 7) -#define PMIC_BATT_CHR_EXCPT_MASK 0xC6 +#define PMIC_BATT_CHR_EXCPT_MASK 0x86 + #define PMIC_BATT_ADC_ACCCHRG_MASK (1 << 31) #define PMIC_BATT_ADC_ACCCHRGVAL_MASK 0x7FFFFFFF @@ -304,11 +305,6 @@ static void pmic_battery_read_status(struct pmic_power_module_info *pbi) pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING; pmic_battery_log_event(BATT_EVENT_BATOVP_EXCPT); batt_exception = 1; - } else if (r8 & PMIC_BATT_CHR_SDCLMT_MASK) { - pbi->batt_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; - pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING; - pmic_battery_log_event(BATT_EVENT_DCLMT_EXCPT); - batt_exception = 1; } else if (r8 & PMIC_BATT_CHR_STEMP_MASK) { pbi->batt_health = POWER_SUPPLY_HEALTH_OVERHEAT; pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING; @@ -316,6 +312,10 @@ static void pmic_battery_read_status(struct pmic_power_module_info *pbi) batt_exception = 1; } else { pbi->batt_health = POWER_SUPPLY_HEALTH_GOOD; + if (r8 & PMIC_BATT_CHR_SDCLMT_MASK) { + /* PMIC will change charging current automatically */ + pmic_battery_log_event(BATT_EVENT_DCLMT_EXCPT); + } } } -- cgit v1.1 From 629bcb4b72d49b3631ae3dd0fe1d345820fadfcc Mon Sep 17 00:00:00 2001 From: Jochen Friedrich Date: Sat, 19 Nov 2011 19:26:37 +0100 Subject: collie_battery: Get rid of irq_to_gpio usage Since 9d08d5d77a355510c2f5657c86b0a4b25acfe72c, irq_to_gpio() is no longer available but still in use by collie_battery.c. As it's just for a debug message, just get rid of this call. Signed-off-by: Jochen Friedrich Signed-off-by: Anton Vorontsov --- drivers/power/collie_battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/collie_battery.c b/drivers/power/collie_battery.c index 548d263..7a40fce 100644 --- a/drivers/power/collie_battery.c +++ b/drivers/power/collie_battery.c @@ -146,7 +146,7 @@ static void collie_bat_external_power_changed(struct power_supply *psy) static irqreturn_t collie_bat_gpio_isr(int irq, void *data) { - pr_info("collie_bat_gpio irq: %d\n", gpio_get_value(irq_to_gpio(irq))); + pr_info("collie_bat_gpio irq\n"); schedule_work(&bat_work); return IRQ_HANDLED; } -- cgit v1.1 From 44abd774f52999125d499aed17a2f70211c34dcd Mon Sep 17 00:00:00 2001 From: Jonghwan Choi Date: Sat, 29 Oct 2011 07:56:32 +0900 Subject: max8997_charger: Fix unsigned value for less than zero The 'val' is a 'unsigned char', so it is never less than zero. Signed-off-by: Jonghwan Choi Acked-by: MyungJoo Ham Signed-off-by: Anton Vorontsov --- drivers/power/max8997_charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/max8997_charger.c b/drivers/power/max8997_charger.c index e12b4a2..6e88c5d 100644 --- a/drivers/power/max8997_charger.c +++ b/drivers/power/max8997_charger.c @@ -97,7 +97,7 @@ static __devinit int max8997_battery_probe(struct platform_device *pdev) return -EINVAL; if (pdata->eoc_mA) { - u8 val = (pdata->eoc_mA - 50) / 10; + int val = (pdata->eoc_mA - 50) / 10; if (val < 0) val = 0; if (val > 0xf) -- cgit v1.1 From 9c9cf9e26363d0c06d0e01d5304477be97ace5cb Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Fri, 25 Nov 2011 21:25:23 +0400 Subject: max8925_power: Fix incorrect voltage and current calculation The datasheet indicates a 12 bit value is returned for i2c registers for voltage and current. Code was assuming 8 bits. But default for chip is 12 bit return value. Voltage is returned in 2mV units -- adjust to return as uV per linux power spec Adjust current calculation to return units in uA. Signed-off-by: Philip Rakity Signed-off-by: Anton Vorontsov --- drivers/power/max8925_power.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/max8925_power.c b/drivers/power/max8925_power.c index a70e16d..b16bd71 100644 --- a/drivers/power/max8925_power.c +++ b/drivers/power/max8925_power.c @@ -188,7 +188,7 @@ static int start_measure(struct max8925_power_info *info, int type) } max8925_bulk_read(info->adc, meas_reg, 2, buf); - ret = (buf[0] << 4) | (buf[1] >> 4); + ret = ((buf[0]<<8) | buf[1]) >> 4; return ret; } @@ -208,7 +208,7 @@ static int max8925_ac_get_prop(struct power_supply *psy, if (info->ac_online) { ret = start_measure(info, MEASURE_VCHG); if (ret >= 0) { - val->intval = ret << 1; /* unit is mV */ + val->intval = ret * 2000; /* unit is uV */ goto out; } } @@ -242,7 +242,7 @@ static int max8925_usb_get_prop(struct power_supply *psy, if (info->usb_online) { ret = start_measure(info, MEASURE_VCHG); if (ret >= 0) { - val->intval = ret << 1; /* unit is mV */ + val->intval = ret * 2000; /* unit is uV */ goto out; } } @@ -266,7 +266,6 @@ static int max8925_bat_get_prop(struct power_supply *psy, 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) { @@ -277,7 +276,7 @@ static int max8925_bat_get_prop(struct power_supply *psy, if (info->bat_online) { ret = start_measure(info, MEASURE_VMBATT); if (ret >= 0) { - val->intval = ret << 1; /* unit is mV */ + val->intval = ret * 2000; /* unit is uV */ ret = 0; break; } @@ -288,8 +287,8 @@ static int max8925_bat_get_prop(struct power_supply *psy, if (info->bat_online) { ret = start_measure(info, MEASURE_ISNS); if (ret >= 0) { - tmp = (long long int)ret * 6250 / 4096 - 3125; - ret = (int)tmp; + /* assume r_sns is 0.02 */ + ret = ((ret * 6250) - 3125) /* uA */; val->intval = 0; if (ret > 0) val->intval = ret; /* unit is mA */ -- cgit v1.1 From 92de378b739115c8afaae5cd3f25159406bb9914 Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Fri, 25 Nov 2011 23:19:37 +0400 Subject: max8925_power: No temperature interrupts if temperature not connected Brownstone does not have temperature reading circuit hooked up. This leads to spurious interrupts. Allow the platform layer to indicate no temperature circuit and do not activate interrupts if no temperature control is set Signed-off-by: Philip Rakity Signed-off-by: Anton Vorontsov --- drivers/power/max8925_power.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/max8925_power.c b/drivers/power/max8925_power.c index b16bd71..83b827c 100644 --- a/drivers/power/max8925_power.c +++ b/drivers/power/max8925_power.c @@ -78,6 +78,7 @@ struct max8925_power_info { unsigned batt_detect:1; /* detecing MB by ID pin */ unsigned topoff_threshold:2; unsigned fast_charge:3; + unsigned no_temp_support:1; int (*set_charger) (int); }; @@ -116,7 +117,7 @@ static irqreturn_t max8925_charger_handler(int irq, void *data) case MAX8925_IRQ_VCHG_DC_F: info->ac_online = 0; __set_charger(info, 0); - dev_dbg(chip->dev, "Adapter is removal\n"); + dev_dbg(chip->dev, "Adapter removed\n"); break; case MAX8925_IRQ_VCHG_USB_R: info->usb_online = 1; @@ -126,7 +127,7 @@ static irqreturn_t max8925_charger_handler(int irq, void *data) case MAX8925_IRQ_VCHG_USB_F: info->usb_online = 0; __set_charger(info, 0); - dev_dbg(chip->dev, "USB is removal\n"); + dev_dbg(chip->dev, "USB removed\n"); break; case MAX8925_IRQ_VCHG_THM_OK_F: /* Battery is not ready yet */ @@ -369,8 +370,10 @@ static __devinit int max8925_init_charger(struct max8925_chip *chip, 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"); + if (!info->no_temp_support) { + 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"); @@ -477,6 +480,7 @@ static __devinit int max8925_power_probe(struct platform_device *pdev) info->topoff_threshold = pdata->topoff_threshold; info->fast_charge = pdata->fast_charge; info->set_charger = pdata->set_charger; + info->no_temp_support = pdata->no_temp_support; max8925_init_charger(chip, info); return 0; -- cgit v1.1 From 5ba1fa0ae288e93179d54e3c59b2241eb1709f0c Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Fri, 25 Nov 2011 23:24:03 +0400 Subject: max8925_power: Do not detect ac insert if handled by other code On brownstone rev 4 ac-insert detect is handled by vbus. allow the platform code to configure the disabling of insert by setting no_insert_detect. Signed-off-by: Philip Rakity Signed-off-by: Anton Vorontsov --- drivers/power/max8925_power.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/max8925_power.c b/drivers/power/max8925_power.c index 83b827c..be2d563 100644 --- a/drivers/power/max8925_power.c +++ b/drivers/power/max8925_power.c @@ -79,6 +79,7 @@ struct max8925_power_info { unsigned topoff_threshold:2; unsigned fast_charge:3; unsigned no_temp_support:1; + unsigned no_insert_detect:1; int (*set_charger) (int); }; @@ -365,8 +366,10 @@ static __devinit int max8925_init_charger(struct max8925_chip *chip, 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"); + if (!info->no_insert_detect) { + 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"); @@ -481,6 +484,7 @@ static __devinit int max8925_power_probe(struct platform_device *pdev) info->fast_charge = pdata->fast_charge; info->set_charger = pdata->set_charger; info->no_temp_support = pdata->no_temp_support; + info->no_insert_detect = pdata->no_insert_detect; max8925_init_charger(chip, info); return 0; -- cgit v1.1 From 72af5a4b9cc9c4527f2967e0283bee632237c26e Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Fri, 25 Nov 2011 23:11:06 +0400 Subject: max8925_power: Remove support for irq bits that do not exist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The max8925 cannot return usb status.  The bits       [MAX8925_IRQ_VCHG_USB_OVP] = {               .reg            = MAX8925_CHG_IRQ1,               .mask_reg       = MAX8925_CHG_IRQ1_MASK,               .offs           = 1 << 3,       },       [MAX8925_IRQ_VCHG_USB_F] =  {               .reg            = MAX8925_CHG_IRQ1,               .mask_reg       = MAX8925_CHG_IRQ1_MASK,               .offs           = 1 << 4,       },       [MAX8925_IRQ_VCHG_USB_R] = {               .reg            = MAX8925_CHG_IRQ1,               .mask_reg       = MAX8925_CHG_IRQ1_MASK,               .offs           = 1 << 5,       }, do not exist in the irq register. Signed-off-by: Philip Rakity Signed-off-by: Anton Vorontsov --- drivers/power/max8925_power.c | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/max8925_power.c b/drivers/power/max8925_power.c index be2d563..cbc7a0b 100644 --- a/drivers/power/max8925_power.c +++ b/drivers/power/max8925_power.c @@ -120,16 +120,6 @@ static irqreturn_t max8925_charger_handler(int irq, void *data) __set_charger(info, 0); dev_dbg(chip->dev, "Adapter removed\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 removed\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"); @@ -370,9 +360,6 @@ static __devinit int max8925_init_charger(struct max8925_chip *chip, 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"); if (!info->no_temp_support) { REQUEST_IRQ(MAX8925_IRQ_VCHG_THM_OK_R, "batt-temp-in-range"); REQUEST_IRQ(MAX8925_IRQ_VCHG_THM_OK_F, "batt-temp-out-range"); -- cgit v1.1 From e7a5f6d55991fb3b3214f435681ee2db96320395 Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Mon, 29 Aug 2011 09:32:04 -0700 Subject: max8925_power: Enable power change notifications The power core infrastructure allow external power change events to be passed to drivers what are listed in the supplied_to call back field. Enable this feature by allowing the supplied_to field to be passed to the driver. This feature will enable drivers named in the supplied_to field that have a external_power_changed callback to be notified when power was been turned on or off. Signed-off-by: Philip Rakity Signed-off-by: Anton Vorontsov --- drivers/power/max8925_power.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/max8925_power.c b/drivers/power/max8925_power.c index cbc7a0b..377d1e6 100644 --- a/drivers/power/max8925_power.c +++ b/drivers/power/max8925_power.c @@ -441,6 +441,8 @@ static __devinit int max8925_power_probe(struct platform_device *pdev) info->ac.properties = max8925_ac_props; info->ac.num_properties = ARRAY_SIZE(max8925_ac_props); info->ac.get_property = max8925_ac_get_prop; + info->ac.supplied_to = pdata->supplied_to; + info->ac.num_supplicants = pdata->num_supplicants; ret = power_supply_register(&pdev->dev, &info->ac); if (ret) goto out; @@ -451,6 +453,9 @@ static __devinit int max8925_power_probe(struct platform_device *pdev) info->usb.properties = max8925_usb_props; info->usb.num_properties = ARRAY_SIZE(max8925_usb_props); info->usb.get_property = max8925_usb_get_prop; + info->usb.supplied_to = pdata->supplied_to; + info->usb.num_supplicants = pdata->num_supplicants; + ret = power_supply_register(&pdev->dev, &info->usb); if (ret) goto out_usb; -- cgit v1.1 From b68f6216c5f53698379603209d3168766449fdab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Tue, 1 Nov 2011 01:43:03 +0100 Subject: bq27x00_battery: Do not cache current_now value for bq27000 batery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * This prevent reporting old current_now value for bq27000 * Also ask for current flags, to make sure that current_now will be reported with correct signature Signed-off-by: Pali Rohár Signed-off-by: Anton Vorontsov --- drivers/power/bq27x00_battery.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index bb16f5b..d238144d 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -80,8 +80,6 @@ struct bq27x00_reg_cache { int cycle_count; int capacity; int flags; - - int current_now; }; struct bq27x00_device_info { @@ -270,17 +268,12 @@ static void bq27x00_update(struct bq27x00_device_info *di) cache.charge_full = bq27x00_battery_read_lmd(di); cache.cycle_count = 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 - * and is likely to be different even between two consecutive reads */ - if (memcmp(&di->cache, &cache, sizeof(cache) - sizeof(int)) != 0) { + if (memcmp(&di->cache, &cache, sizeof(cache)) != 0) { di->cache = cache; power_supply_changed(&di->bat); } @@ -330,12 +323,9 @@ static int bq27x00_battery_current(struct bq27x00_device_info *di, union power_supply_propval *val) { int curr; + int flags; - if (di->chip == BQ27500) - curr = bq27x00_read(di, BQ27x00_REG_AI, false); - else - curr = di->cache.current_now; - + curr = bq27x00_read(di, BQ27x00_REG_AI, false); if (curr < 0) return curr; @@ -343,7 +333,8 @@ static int bq27x00_battery_current(struct bq27x00_device_info *di, /* bq27500 returns signed value */ val->intval = (int)((s16)curr) * 1000; } else { - if (di->cache.flags & BQ27000_FLAG_CHGS) { + flags = bq27x00_read(di, BQ27x00_REG_FLAGS, false); + if (flags & BQ27000_FLAG_CHGS) { dev_dbg(di->dev, "negative current!\n"); curr = -curr; } -- cgit v1.1 From d66bab3f3a5119084ac0ed010a7eae0000af4f16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Tue, 1 Nov 2011 01:43:04 +0100 Subject: bq27x00_battery: Add support for property POWER_SUPPLY_PROP_CAPACITY_LEVEL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pali Rohár Signed-off-by: Anton Vorontsov --- drivers/power/bq27x00_battery.c | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index d238144d..a531122 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -54,12 +54,16 @@ #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_EDVF BIT(0) /* Final End-of-Discharge-Voltage flag */ +#define BQ27000_FLAG_EDV1 BIT(1) /* First End-of-Discharge-Voltage flag */ #define BQ27000_FLAG_FC BIT(5) +#define BQ27000_FLAG_CHGS BIT(7) /* Charge state flag */ #define BQ27500_REG_SOC 0x2C #define BQ27500_REG_DCAP 0x3C /* Design capacity */ #define BQ27500_FLAG_DSC BIT(0) +#define BQ27500_FLAG_SOCF BIT(1) /* State-of-Charge threshold final */ +#define BQ27500_FLAG_SOC1 BIT(2) /* State-of-Charge threshold 1 */ #define BQ27500_FLAG_FC BIT(9) #define BQ27000_RS 20 /* Resistor sense */ @@ -106,6 +110,7 @@ static enum power_supply_property bq27x00_battery_props[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, @@ -373,6 +378,36 @@ static int bq27x00_battery_status(struct bq27x00_device_info *di, return 0; } +static int bq27x00_battery_capacity_level(struct bq27x00_device_info *di, + union power_supply_propval *val) +{ + int level; + + if (di->chip == BQ27500) { + if (di->cache.flags & BQ27500_FLAG_FC) + level = POWER_SUPPLY_CAPACITY_LEVEL_FULL; + else if (di->cache.flags & BQ27500_FLAG_SOC1) + level = POWER_SUPPLY_CAPACITY_LEVEL_LOW; + else if (di->cache.flags & BQ27500_FLAG_SOCF) + level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; + else + level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; + } else { + if (di->cache.flags & BQ27000_FLAG_FC) + level = POWER_SUPPLY_CAPACITY_LEVEL_FULL; + else if (di->cache.flags & BQ27000_FLAG_EDV1) + level = POWER_SUPPLY_CAPACITY_LEVEL_LOW; + else if (di->cache.flags & BQ27000_FLAG_EDVF) + level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; + else + level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; + } + + val->intval = level; + + return 0; +} + /* * Return the battery Voltage in milivolts * Or < 0 if something fails. @@ -464,6 +499,9 @@ static int bq27x00_battery_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CAPACITY: ret = bq27x00_simple_value(di->cache.capacity, val); break; + case POWER_SUPPLY_PROP_CAPACITY_LEVEL: + ret = bq27x00_battery_capacity_level(di, val); + break; case POWER_SUPPLY_PROP_TEMP: ret = bq27x00_battery_temperature(di, val); break; -- cgit v1.1 From 4b226c2cf04eb373369d298fa00181b0eec71233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Tue, 1 Nov 2011 01:43:05 +0100 Subject: bq27x00_battery: Report -ENODATA if bq27000 battery was not calibrated MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * CI (Capacity Inaccurate) flag is set after full reset on bq27000 battery * when is set, all capacity properties should be reported incorrectly, because there was no learning cycle and battery was not calibrated * instead reporting incorrect values, report -ENODATA Signed-off-by: Pali Rohár Signed-off-by: Anton Vorontsov --- drivers/power/bq27x00_battery.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index a531122..6c8dfdb 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -56,6 +56,7 @@ #define BQ27000_REG_ILMD 0x76 /* Initial last measured discharge */ #define BQ27000_FLAG_EDVF BIT(0) /* Final End-of-Discharge-Voltage flag */ #define BQ27000_FLAG_EDV1 BIT(1) /* First End-of-Discharge-Voltage flag */ +#define BQ27000_FLAG_CI BIT(4) /* Capacity Inaccurate flag */ #define BQ27000_FLAG_FC BIT(5) #define BQ27000_FLAG_CHGS BIT(7) /* Charge state flag */ @@ -265,12 +266,20 @@ static void bq27x00_update(struct bq27x00_device_info *di) cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, is_bq27500); if (cache.flags >= 0) { - cache.capacity = bq27x00_battery_read_rsoc(di); + if (!is_bq27500 && (cache.flags & BQ27000_FLAG_CI)) { + cache.capacity = -ENODATA; + cache.time_to_empty = -ENODATA; + cache.time_to_empty_avg = -ENODATA; + cache.time_to_full = -ENODATA; + cache.charge_full = -ENODATA; + } else { + cache.capacity = bq27x00_battery_read_rsoc(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.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); - cache.charge_full = bq27x00_battery_read_lmd(di); cache.cycle_count = bq27x00_battery_read_cyct(di); /* We only have to read charge design full once */ -- cgit v1.1 From a8f6bd23cc73a54b9182cf8be102739e01ea874a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Tue, 1 Nov 2011 01:43:06 +0100 Subject: bq27x00_battery: Cache energy property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pali Rohár Signed-off-by: Anton Vorontsov --- drivers/power/bq27x00_battery.c | 53 ++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 27 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 6c8dfdb..e9aeb53 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -84,6 +84,7 @@ struct bq27x00_reg_cache { int charge_full; int cycle_count; int capacity; + int energy; int flags; }; @@ -225,6 +226,28 @@ static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di) } /* + * Return the battery Available energy in µWh + * Or < 0 if something fails. + */ +static int bq27x00_battery_read_energy(struct bq27x00_device_info *di) +{ + 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; + + return ae; +} + +/* * Return the battery Cycle count total * Or < 0 if something fails. */ @@ -268,12 +291,14 @@ static void bq27x00_update(struct bq27x00_device_info *di) if (cache.flags >= 0) { if (!is_bq27500 && (cache.flags & BQ27000_FLAG_CI)) { cache.capacity = -ENODATA; + cache.energy = -ENODATA; cache.time_to_empty = -ENODATA; cache.time_to_empty_avg = -ENODATA; cache.time_to_full = -ENODATA; cache.charge_full = -ENODATA; } else { cache.capacity = bq27x00_battery_read_rsoc(di); + cache.energy = bq27x00_battery_read_energy(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); @@ -435,32 +460,6 @@ 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) { @@ -539,7 +538,7 @@ static int bq27x00_battery_get_property(struct power_supply *psy, ret = bq27x00_simple_value(di->cache.cycle_count, val); break; case POWER_SUPPLY_PROP_ENERGY_NOW: - ret = bq27x00_battery_energy(di, val); + ret = bq27x00_simple_value(di->cache.energy, val); break; default: return -EINVAL; -- cgit v1.1 From d149e98e02c84fe1e81aa7f6b91a667d796b2c6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Tue, 1 Nov 2011 01:43:07 +0100 Subject: bq27x00_battery: Cache temperature value in converted unit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pali Rohár Signed-off-by: Anton Vorontsov --- drivers/power/bq27x00_battery.c | 45 ++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 21 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index e9aeb53..a22124a 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -248,6 +248,28 @@ static int bq27x00_battery_read_energy(struct bq27x00_device_info *di) } /* + * Return the battery temperature in tenths of degree Celsius + * Or < 0 if something fails. + */ +static int bq27x00_battery_read_temperature(struct bq27x00_device_info *di) +{ + int temp; + + temp = bq27x00_read(di, BQ27x00_REG_TEMP, false); + if (temp < 0) { + dev_err(di->dev, "error reading temperature\n"); + return temp; + } + + if (di->chip == BQ27500) + temp -= 2731; + else + temp = ((temp * 5) - 5463) / 2; + + return temp; +} + +/* * Return the battery Cycle count total * Or < 0 if something fails. */ @@ -304,7 +326,7 @@ static void bq27x00_update(struct bq27x00_device_info *di) cache.time_to_full = bq27x00_battery_read_time(di, BQ27x00_REG_TTF); cache.charge_full = bq27x00_battery_read_lmd(di); } - cache.temperature = bq27x00_read(di, BQ27x00_REG_TEMP, false); + cache.temperature = bq27x00_battery_read_temperature(di); cache.cycle_count = bq27x00_battery_read_cyct(di); /* We only have to read charge design full once */ @@ -334,25 +356,6 @@ static void bq27x00_battery_poll(struct work_struct *work) } } - -/* - * 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; -} - /* * Return the battery average current in µA * Note that current can be negative signed as well @@ -511,7 +514,7 @@ static int bq27x00_battery_get_property(struct power_supply *psy, ret = bq27x00_battery_capacity_level(di, val); break; case POWER_SUPPLY_PROP_TEMP: - ret = bq27x00_battery_temperature(di, val); + ret = bq27x00_simple_value(di->cache.temperature, val); break; case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: ret = bq27x00_simple_value(di->cache.time_to_empty, val); -- cgit v1.1 From 270968c0984aeed096da3cfffb0e131f4c416166 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Tue, 1 Nov 2011 01:43:08 +0100 Subject: bq27x00_battery: Fix reporting status value for bq27500 battery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pali Rohár Signed-off-by: Anton Vorontsov --- drivers/power/bq27x00_battery.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index a22124a..c4c403e 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -62,10 +62,11 @@ #define BQ27500_REG_SOC 0x2C #define BQ27500_REG_DCAP 0x3C /* Design capacity */ -#define BQ27500_FLAG_DSC BIT(0) +#define BQ27500_FLAG_DSG BIT(0) /* Discharging */ #define BQ27500_FLAG_SOCF BIT(1) /* State-of-Charge threshold final */ #define BQ27500_FLAG_SOC1 BIT(2) /* State-of-Charge threshold 1 */ -#define BQ27500_FLAG_FC BIT(9) +#define BQ27500_FLAG_CHG BIT(8) /* Charging */ +#define BQ27500_FLAG_FC BIT(9) /* Fully charged */ #define BQ27000_RS 20 /* Resistor sense */ @@ -395,10 +396,14 @@ static int bq27x00_battery_status(struct bq27x00_device_info *di, if (di->chip == BQ27500) { if (di->cache.flags & BQ27500_FLAG_FC) status = POWER_SUPPLY_STATUS_FULL; - else if (di->cache.flags & BQ27500_FLAG_DSC) + else if (di->cache.flags & BQ27500_FLAG_DSG) status = POWER_SUPPLY_STATUS_DISCHARGING; - else + else if (di->cache.flags & BQ27500_FLAG_CHG) 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_UNKNOWN; } else { if (di->cache.flags & BQ27000_FLAG_FC) status = POWER_SUPPLY_STATUS_FULL; -- cgit v1.1 From c6cd4f267d442f2ce279024adb03a69e8608e0f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Tue, 1 Nov 2011 01:43:09 +0100 Subject: bq27x00_battery: Fix reporting error messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Do not be noise if battery is not calibrated (use dev_dbg) Signed-off-by: Pali Rohár Signed-off-by: Anton Vorontsov --- drivers/power/bq27x00_battery.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index c4c403e..82ab6ca 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -155,7 +155,7 @@ static int bq27x00_battery_read_rsoc(struct bq27x00_device_info *di) rsoc = bq27x00_read(di, BQ27000_REG_RSOC, true); if (rsoc < 0) - dev_err(di->dev, "error reading relative State-of-Charge\n"); + dev_dbg(di->dev, "error reading relative State-of-Charge\n"); return rsoc; } @@ -170,7 +170,8 @@ static int bq27x00_battery_read_charge(struct bq27x00_device_info *di, u8 reg) charge = bq27x00_read(di, reg, false); if (charge < 0) { - dev_err(di->dev, "error reading nominal available capacity\n"); + dev_dbg(di->dev, "error reading charge register %02x: %d\n", + reg, charge); return charge; } @@ -214,7 +215,7 @@ static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di) ilmd = bq27x00_read(di, BQ27000_REG_ILMD, true); if (ilmd < 0) { - dev_err(di->dev, "error reading initial last measured discharge\n"); + dev_dbg(di->dev, "error reading initial last measured discharge\n"); return ilmd; } @@ -236,7 +237,7 @@ static int bq27x00_battery_read_energy(struct bq27x00_device_info *di) ae = bq27x00_read(di, BQ27x00_REG_AE, false); if (ae < 0) { - dev_err(di->dev, "error reading available energy\n"); + dev_dbg(di->dev, "error reading available energy\n"); return ae; } @@ -295,7 +296,8 @@ static int bq27x00_battery_read_time(struct bq27x00_device_info *di, u8 reg) tval = bq27x00_read(di, reg, false); if (tval < 0) { - dev_err(di->dev, "error reading register %02x: %d\n", reg, tval); + dev_dbg(di->dev, "error reading time register %02x: %d\n", + reg, tval); return tval; } @@ -313,6 +315,7 @@ static void bq27x00_update(struct bq27x00_device_info *di) cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, is_bq27500); if (cache.flags >= 0) { if (!is_bq27500 && (cache.flags & BQ27000_FLAG_CI)) { + dev_info(di->dev, "battery is not calibrated! ignoring capacity values\n"); cache.capacity = -ENODATA; cache.energy = -ENODATA; cache.time_to_empty = -ENODATA; @@ -369,8 +372,10 @@ static int bq27x00_battery_current(struct bq27x00_device_info *di, int flags; curr = bq27x00_read(di, BQ27x00_REG_AI, false); - if (curr < 0) + if (curr < 0) { + dev_err(di->dev, "error reading current\n"); return curr; + } if (di->chip == BQ27500) { /* bq27500 returns signed value */ @@ -460,8 +465,10 @@ static int bq27x00_battery_voltage(struct bq27x00_device_info *di, int volt; volt = bq27x00_read(di, BQ27x00_REG_VOLT, false); - if (volt < 0) + if (volt < 0) { + dev_err(di->dev, "error reading voltage\n"); return volt; + } val->intval = volt * 1000; -- cgit v1.1 From 8cfaaa811894a3ae2d7360a15a6cfccff3ebc7db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Tue, 1 Nov 2011 01:43:11 +0100 Subject: bq27x00_battery: Fix OOPS caused by unregistring bq27x00 driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * power_supply_unregister call bq27x00_battery_get_property which call bq27x00_battery_poll * make sure that bq27x00_battery_poll will not call schedule_delayed_work again after unregister (which cause OOPS) Signed-off-by: Pali Rohár Signed-off-by: Anton Vorontsov --- drivers/power/bq27x00_battery.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 82ab6ca..98bf567 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -598,6 +598,14 @@ static int bq27x00_powersupply_init(struct bq27x00_device_info *di) static void bq27x00_powersupply_unregister(struct bq27x00_device_info *di) { + /* + * power_supply_unregister call bq27x00_battery_get_property which + * call bq27x00_battery_poll. + * Make sure that bq27x00_battery_poll will not call + * schedule_delayed_work again after unregister (which cause OOPS). + */ + poll_interval = 0; + cancel_delayed_work_sync(&di->work); power_supply_unregister(&di->bat); -- cgit v1.1 From 3b176b25a8146673ade4772d1edfe318caa6818a Mon Sep 17 00:00:00 2001 From: Yong Zhang Date: Thu, 22 Sep 2011 16:59:11 +0800 Subject: power_supply: Remove IRQF_DISABLED Since commit [e58aa3d2: genirq: Run irq handlers with interrupts disabled], We run all interrupt handlers with interrupts disabled and we even check and yell when an interrupt handler returns with interrupts enabled (see commit [b738a50a: genirq: Warn when handler enables interrupts]). So now this flag is a NOOP and can be removed. Signed-off-by: Yong Zhang Signed-off-by: Anton Vorontsov --- drivers/power/wm97xx_battery.c | 2 +- drivers/power/z2_battery.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/wm97xx_battery.c b/drivers/power/wm97xx_battery.c index 156559e..cf8681c1 100644 --- a/drivers/power/wm97xx_battery.c +++ b/drivers/power/wm97xx_battery.c @@ -196,7 +196,7 @@ static int __devinit wm97xx_bat_probe(struct platform_device *dev) if (ret) goto err2; ret = request_irq(gpio_to_irq(pdata->charge_gpio), - wm97xx_chrg_irq, IRQF_DISABLED, + wm97xx_chrg_irq, 0, "AC Detect", dev); if (ret) goto err2; diff --git a/drivers/power/z2_battery.c b/drivers/power/z2_battery.c index ae38c44..636ebb2 100644 --- a/drivers/power/z2_battery.c +++ b/drivers/power/z2_battery.c @@ -218,7 +218,7 @@ static int __devinit z2_batt_probe(struct i2c_client *client, irq_set_irq_type(gpio_to_irq(info->charge_gpio), IRQ_TYPE_EDGE_BOTH); ret = request_irq(gpio_to_irq(info->charge_gpio), - z2_charge_switch_irq, IRQF_DISABLED, + z2_charge_switch_irq, 0, "AC Detect", charger); if (ret) goto err3; -- cgit v1.1 From 86e6c6bd9b70260fc5bc9fdfa2e5b4dfd8fe545c Mon Sep 17 00:00:00 2001 From: Paul Parsons Date: Wed, 23 Nov 2011 22:37:15 +0000 Subject: ds2760_battery: Add rated capacity of the hx4700 3600mAh battery Add rated capacity of the HP iPAQ hx4700 3.7V 3600mAh (359114-001) battery. For this battery the value of the rated capacity EEPROM register at 0x32 is 14; thus rated_capacities[14] = 3600. Signed-off-by: Paul Parsons Signed-off-by: Anton Vorontsov --- drivers/power/ds2760_battery.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/ds2760_battery.c b/drivers/power/ds2760_battery.c index f2c9cc3..cda03da 100644 --- a/drivers/power/ds2760_battery.c +++ b/drivers/power/ds2760_battery.c @@ -95,7 +95,11 @@ static int rated_capacities[] = { 2880, /* Samsung */ 2880, /* BYD */ 2880, /* Lishen */ - 2880 /* NEC */ + 2880, /* NEC */ +#ifdef CONFIG_MACH_H4700 + 0, + 3600, /* HP iPAQ hx4700 3.7V 3600mAh (359114-001) */ +#endif }; /* array is level at temps 0°C, 10°C, 20°C, 30°C, 40°C -- cgit v1.1 From 60a1f6e4462bb71b39543ddbca314886a55adb9d Mon Sep 17 00:00:00 2001 From: Ramakrishna Pallala Date: Sat, 26 Nov 2011 04:11:15 +0400 Subject: max17042_battery: Fix error handling In max17042_get_property(...), the values returned by max17042_read_reg are directly assigned to the variables, even if the read results in an error. This patch checks for the return code from max17042_read_reg and exits the function if there is any error. Signed-off-by: Ramakrishna Pallala Acked-by: MyungJoo Ham Signed-off-by: Anton Vorontsov --- drivers/power/max17042_battery.c | 85 +++++++++++++++++++++++++++------------- 1 file changed, 58 insertions(+), 27 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index a6dc9c7..10ffa8c2 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c @@ -84,55 +84,79 @@ static int max17042_get_property(struct power_supply *psy, { struct max17042_chip *chip = container_of(psy, struct max17042_chip, battery); + int ret; switch (psp) { case POWER_SUPPLY_PROP_PRESENT: - val->intval = max17042_read_reg(chip->client, - MAX17042_STATUS); - if (val->intval & MAX17042_STATUS_BattAbsent) + ret = max17042_read_reg(chip->client, MAX17042_STATUS); + if (ret < 0) + return ret; + + if (ret & MAX17042_STATUS_BattAbsent) val->intval = 0; else val->intval = 1; break; case POWER_SUPPLY_PROP_CYCLE_COUNT: - val->intval = max17042_read_reg(chip->client, - MAX17042_Cycles); + ret = max17042_read_reg(chip->client, MAX17042_Cycles); + if (ret < 0) + return ret; + + val->intval = ret; break; case POWER_SUPPLY_PROP_VOLTAGE_MAX: - val->intval = max17042_read_reg(chip->client, - MAX17042_MinMaxVolt); - val->intval >>= 8; + ret = max17042_read_reg(chip->client, MAX17042_MinMaxVolt); + if (ret < 0) + return ret; + + val->intval = ret >> 8; val->intval *= 20000; /* Units of LSB = 20mV */ break; case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: - val->intval = max17042_read_reg(chip->client, - MAX17042_V_empty); - val->intval >>= 7; + ret = max17042_read_reg(chip->client, MAX17042_V_empty); + if (ret < 0) + return ret; + + val->intval = ret >> 7; val->intval *= 10000; /* Units of LSB = 10mV */ break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: - val->intval = max17042_read_reg(chip->client, MAX17042_VCELL) - * 625 / 8; + ret = max17042_read_reg(chip->client, MAX17042_VCELL); + if (ret < 0) + return ret; + + val->intval = ret * 625 / 8; break; case POWER_SUPPLY_PROP_VOLTAGE_AVG: - val->intval = max17042_read_reg(chip->client, MAX17042_AvgVCELL) - * 625 / 8; + ret = max17042_read_reg(chip->client, MAX17042_AvgVCELL); + if (ret < 0) + return ret; + + val->intval = ret * 625 / 8; break; case POWER_SUPPLY_PROP_CAPACITY: - val->intval = max17042_read_reg(chip->client, - MAX17042_SOC) / 256; + ret = max17042_read_reg(chip->client, MAX17042_SOC); + if (ret < 0) + return ret; + + val->intval = ret >> 8; break; case POWER_SUPPLY_PROP_CHARGE_FULL: - val->intval = max17042_read_reg(chip->client, - MAX17042_RepSOC); - if ((val->intval / 256) >= MAX17042_BATTERY_FULL) + ret = max17042_read_reg(chip->client, MAX17042_RepSOC); + if (ret < 0) + return ret; + + if ((ret >> 8) >= MAX17042_BATTERY_FULL) val->intval = 1; - else if (val->intval >= 0) + else if (ret >= 0) val->intval = 0; break; case POWER_SUPPLY_PROP_TEMP: - val->intval = max17042_read_reg(chip->client, - MAX17042_TEMP); + ret = max17042_read_reg(chip->client, MAX17042_TEMP); + if (ret < 0) + return ret; + + val->intval = ret; /* The value is signed. */ if (val->intval & 0x8000) { val->intval = (0x7fff & ~val->intval) + 1; @@ -144,8 +168,11 @@ static int max17042_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CURRENT_NOW: if (chip->pdata->enable_current_sense) { - val->intval = max17042_read_reg(chip->client, - MAX17042_Current); + ret = max17042_read_reg(chip->client, MAX17042_Current); + if (ret < 0) + return ret; + + val->intval = ret; if (val->intval & 0x8000) { /* Negative */ val->intval = ~val->intval & 0x7fff; @@ -159,8 +186,12 @@ static int max17042_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CURRENT_AVG: if (chip->pdata->enable_current_sense) { - val->intval = max17042_read_reg(chip->client, - MAX17042_AvgCurrent); + ret = max17042_read_reg(chip->client, + MAX17042_AvgCurrent); + if (ret < 0) + return ret; + + val->intval = ret; if (val->intval & 0x8000) { /* Negative */ val->intval = ~val->intval & 0x7fff; -- cgit v1.1 From b57f2f6108fbf9ce33191fb574fe94b1f3ccbff3 Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Fri, 2 Sep 2011 04:20:01 +0200 Subject: max8925_power: Do not read random data from chip registers Reading the voltage, charge etc requires that we tell the chip what property we want to read before reading it according to maxim. Signed-off-by: Philip Rakity Signed-off-by: Thomas Liu Tested-by: Ted Bennett Signed-off-by: Anton Vorontsov --- drivers/power/max8925_power.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/max8925_power.c b/drivers/power/max8925_power.c index 377d1e6..d87032a 100644 --- a/drivers/power/max8925_power.c +++ b/drivers/power/max8925_power.c @@ -160,25 +160,31 @@ static irqreturn_t max8925_charger_handler(int irq, void *data) static int start_measure(struct max8925_power_info *info, int type) { unsigned char buf[2] = {0, 0}; + int meas_cmd; int meas_reg = 0, ret; switch (type) { case MEASURE_VCHG: + meas_cmd = MAX8925_CMD_VCHG; meas_reg = MAX8925_ADC_VCHG; break; case MEASURE_VBBATT: + meas_cmd = MAX8925_CMD_VBBATT; meas_reg = MAX8925_ADC_VBBATT; break; case MEASURE_VMBATT: + meas_cmd = MAX8925_CMD_VMBATT; meas_reg = MAX8925_ADC_VMBATT; break; case MEASURE_ISNS: + meas_cmd = MAX8925_CMD_ISNS; meas_reg = MAX8925_ADC_ISNS; break; default: return -EINVAL; } + max8925_reg_write(info->adc, meas_cmd, 0); max8925_bulk_read(info->adc, meas_reg, 2, buf); ret = ((buf[0]<<8) | buf[1]) >> 4; -- cgit v1.1 From 00a159a5567232fbe1dd85bc611c55f53943b0fc Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Fri, 2 Sep 2011 04:20:01 +0200 Subject: max8925_power: Check at probe time if power to set online The interrupt for ac on/off can be missed during boot time. Check if online by seeing if we have power. We choose 0.5V since this is high enough to avoid random reading from a input that could be floating if no charger. Signed-off-by: Philip Rakity Signed-off-by: Ted Bennett Signed-off-by: Anton Vorontsov --- drivers/power/max8925_power.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/max8925_power.c b/drivers/power/max8925_power.c index d87032a..8155250 100644 --- a/drivers/power/max8925_power.c +++ b/drivers/power/max8925_power.c @@ -377,9 +377,15 @@ static __devinit int max8925_init_charger(struct max8925_chip *chip, 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; + + /* check for power - can miss interrupt at boot time */ + if (start_measure(info, MEASURE_VCHG) * 2000 > 500000) + info->ac_online = 1; + else + info->ac_online = 0; + ret = max8925_reg_read(info->gpm, MAX8925_CHG_STATUS); if (ret >= 0) { /* -- cgit v1.1 From 25a0bc2dfc2ea732f40af2dae52426ead66ae76e Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Wed, 7 Dec 2011 11:24:20 -0800 Subject: power_supply: add SCOPE attribute to power supplies This adds a "scope" attribute to a power_supply, which indicates how much of the system it powers. It appears in sysfs as "scope" or in the uevent file as POWER_SUPPLY_SCOPE=. There are presently three possible values: Unknown - unknown power topology System - the power supply powers the whole system Device - it powers a specific device, or tree of devices A power supply which doesn't have a "scope" attribute should be assumed to have "System" scope. In general, usermode should assume that loss of all System-scoped power supplies will power off the whole system, but any single one is sufficient to power the system. Signed-off-by: Jeremy Fitzhardinge Cc: Richard Hughes --- drivers/power/power_supply_sysfs.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index e15d4c9..21178eb 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -63,6 +63,9 @@ static ssize_t power_supply_show_property(struct device *dev, static char *capacity_level_text[] = { "Unknown", "Critical", "Low", "Normal", "High", "Full" }; + static char *scope_text[] = { + "Unknown", "System", "Device" + }; ssize_t ret = 0; struct power_supply *psy = dev_get_drvdata(dev); const ptrdiff_t off = attr - power_supply_attrs; @@ -95,6 +98,8 @@ static ssize_t power_supply_show_property(struct device *dev, return sprintf(buf, "%s\n", capacity_level_text[value.intval]); else if (off == POWER_SUPPLY_PROP_TYPE) return sprintf(buf, "%s\n", type_text[value.intval]); + else if (off == POWER_SUPPLY_PROP_SCOPE) + return sprintf(buf, "%s\n", scope_text[value.intval]); else if (off >= POWER_SUPPLY_PROP_MODEL_NAME) return sprintf(buf, "%s\n", value.strval); @@ -167,6 +172,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(time_to_full_now), POWER_SUPPLY_ATTR(time_to_full_avg), POWER_SUPPLY_ATTR(type), + POWER_SUPPLY_ATTR(scope), /* Properties of type `const char *' */ POWER_SUPPLY_ATTR(model_name), POWER_SUPPLY_ATTR(manufacturer), -- cgit v1.1 From 8351665195cec6d2b73cce8b66f02d6dde246a8e Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Wed, 7 Dec 2011 09:15:45 -0800 Subject: power_supply: allow a power supply to explicitly point to powered device If a power supply has a scope of "Device", then allow the power supply to indicate what device it actually powers. This is represented in the power supply's sysfs directory as a symlink named "powers", which points to the sysfs directory of the powered device. If the device has children, then the sub-devices are also powered by the same power supply. Signed-off-by: Jeremy Fitzhardinge Cc: Richard Hughes --- drivers/power/power_supply_core.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 329b46b..b10c121 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -147,6 +147,12 @@ struct power_supply *power_supply_get_by_name(char *name) } EXPORT_SYMBOL_GPL(power_supply_get_by_name); +int power_supply_powers(struct power_supply *psy, struct device *dev) +{ + return sysfs_create_link_nowarn(&psy->dev->kobj, &dev->kobj, "powers"); +} +EXPORT_SYMBOL_GPL(power_supply_powers); + static void power_supply_dev_release(struct device *dev) { pr_debug("device: '%s': %s\n", dev_name(dev), __func__); @@ -202,6 +208,7 @@ EXPORT_SYMBOL_GPL(power_supply_register); void power_supply_unregister(struct power_supply *psy) { cancel_work_sync(&psy->changed_work); + sysfs_remove_link(&psy->dev->kobj, "powers"); power_supply_remove_triggers(psy); device_unregister(psy->dev); } -- cgit v1.1 From 3bb3dbbd56ea39e5537db8f8041ea95d28f16a7f Mon Sep 17 00:00:00 2001 From: Donggeun Kim Date: Tue, 27 Dec 2011 18:47:48 +0900 Subject: power_supply: Add initial Charger-Manager driver Because battery health monitoring should be done even when suspended, it needs to wake up and suspend periodically. Thus, userspace battery monitoring may incur too much overhead; every device and task is woken up periodically. Charger Manager uses suspend-again to provide in-suspend monitoring. This patch allows to monitor battery health in-suspend state. Signed-off-by: Donggeun Kim Signed-off-by: MyungJoo Ham Signed-off-by: Kyungmin Park Signed-off-by: Anton Vorontsov --- drivers/power/Kconfig | 10 + drivers/power/Makefile | 1 + drivers/power/charger-manager.c | 779 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 790 insertions(+) create mode 100644 drivers/power/charger-manager.c (limited to 'drivers/power') diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 57de051..363f4d1 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -235,6 +235,16 @@ config CHARGER_GPIO This driver can be build as a module. If so, the module will be called gpio-charger. +config CHARGER_MANAGER + bool "Battery charger manager for multiple chargers" + depends on REGULATOR && RTC_CLASS + help + Say Y to enable charger-manager support, which allows multiple + chargers attached to a battery and multiple batteries attached to a + system. The charger-manager also can monitor charging status in + runtime and in suspend-to-RAM by waking up the system periodically + with help of suspend_again support. + config CHARGER_MAX8997 tristate "Maxim MAX8997/MAX8966 PMIC battery charger driver" depends on MFD_MAX8997 && REGULATOR_MAX8997 diff --git a/drivers/power/Makefile b/drivers/power/Makefile index b4af13d..d3b24e9 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -36,5 +36,6 @@ obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o +obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c new file mode 100644 index 0000000..727a259 --- /dev/null +++ b/drivers/power/charger-manager.c @@ -0,0 +1,779 @@ +/* + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * MyungJoo Ham + * + * This driver enables to monitor battery health and control charger + * during suspend-to-mem. + * Charger manager depends on other devices. register this later than + * the depending devices. + * + * 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 +#include +#include +#include + +/* + * Regard CM_JIFFIES_SMALL jiffies is small enough to ignore for + * delayed works so that we can run delayed works with CM_JIFFIES_SMALL + * without any delays. + */ +#define CM_JIFFIES_SMALL (2) + +/* If y is valid (> 0) and smaller than x, do x = y */ +#define CM_MIN_VALID(x, y) x = (((y > 0) && ((x) > (y))) ? (y) : (x)) + +/* + * Regard CM_RTC_SMALL (sec) is small enough to ignore error in invoking + * rtc alarm. It should be 2 or larger + */ +#define CM_RTC_SMALL (2) + +#define UEVENT_BUF_SIZE 32 + +static LIST_HEAD(cm_list); +static DEFINE_MUTEX(cm_list_mtx); + +/* About in-suspend (suspend-again) monitoring */ +static struct rtc_device *rtc_dev; +/* + * Backup RTC alarm + * Save the wakeup alarm before entering suspend-to-RAM + */ +static struct rtc_wkalrm rtc_wkalarm_save; +/* Backup RTC alarm time in terms of seconds since 01-01-1970 00:00:00 */ +static unsigned long rtc_wkalarm_save_time; +static bool cm_suspended; +static bool cm_rtc_set; +static unsigned long cm_suspend_duration_ms; + +/* Global charger-manager description */ +static struct charger_global_desc *g_desc; /* init with setup_charger_manager */ + +/** + * is_batt_present - See if the battery presents in place. + * @cm: the Charger Manager representing the battery. + */ +static bool is_batt_present(struct charger_manager *cm) +{ + union power_supply_propval val; + bool present = false; + int i, ret; + + switch (cm->desc->battery_present) { + case CM_FUEL_GAUGE: + ret = cm->fuel_gauge->get_property(cm->fuel_gauge, + POWER_SUPPLY_PROP_PRESENT, &val); + if (ret == 0 && val.intval) + present = true; + break; + case CM_CHARGER_STAT: + for (i = 0; cm->charger_stat[i]; i++) { + ret = cm->charger_stat[i]->get_property( + cm->charger_stat[i], + POWER_SUPPLY_PROP_PRESENT, &val); + if (ret == 0 && val.intval) { + present = true; + break; + } + } + break; + } + + return present; +} + +/** + * is_ext_pwr_online - See if an external power source is attached to charge + * @cm: the Charger Manager representing the battery. + * + * Returns true if at least one of the chargers of the battery has an external + * power source attached to charge the battery regardless of whether it is + * actually charging or not. + */ +static bool is_ext_pwr_online(struct charger_manager *cm) +{ + union power_supply_propval val; + bool online = false; + int i, ret; + + /* If at least one of them has one, it's yes. */ + for (i = 0; cm->charger_stat[i]; i++) { + ret = cm->charger_stat[i]->get_property( + cm->charger_stat[i], + POWER_SUPPLY_PROP_ONLINE, &val); + if (ret == 0 && val.intval) { + online = true; + break; + } + } + + return online; +} + +/** + * is_charging - Returns true if the battery is being charged. + * @cm: the Charger Manager representing the battery. + */ +static bool is_charging(struct charger_manager *cm) +{ + int i, ret; + bool charging = false; + union power_supply_propval val; + + /* If there is no battery, it cannot be charged */ + if (!is_batt_present(cm)) + return false; + + /* If at least one of the charger is charging, return yes */ + for (i = 0; cm->charger_stat[i]; i++) { + /* 1. The charger sholuld not be DISABLED */ + if (cm->emergency_stop) + continue; + if (!cm->charger_enabled) + continue; + + /* 2. The charger should be online (ext-power) */ + ret = cm->charger_stat[i]->get_property( + cm->charger_stat[i], + POWER_SUPPLY_PROP_ONLINE, &val); + if (ret) { + dev_warn(cm->dev, "Cannot read ONLINE value from %s.\n", + cm->desc->psy_charger_stat[i]); + continue; + } + if (val.intval == 0) + continue; + + /* + * 3. The charger should not be FULL, DISCHARGING, + * or NOT_CHARGING. + */ + ret = cm->charger_stat[i]->get_property( + cm->charger_stat[i], + POWER_SUPPLY_PROP_STATUS, &val); + if (ret) { + dev_warn(cm->dev, "Cannot read STATUS value from %s.\n", + cm->desc->psy_charger_stat[i]); + continue; + } + if (val.intval == POWER_SUPPLY_STATUS_FULL || + val.intval == POWER_SUPPLY_STATUS_DISCHARGING || + val.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) + continue; + + /* Then, this is charging. */ + charging = true; + break; + } + + return charging; +} + +/** + * is_polling_required - Return true if need to continue polling for this CM. + * @cm: the Charger Manager representing the battery. + */ +static bool is_polling_required(struct charger_manager *cm) +{ + switch (cm->desc->polling_mode) { + case CM_POLL_DISABLE: + return false; + case CM_POLL_ALWAYS: + return true; + case CM_POLL_EXTERNAL_POWER_ONLY: + return is_ext_pwr_online(cm); + case CM_POLL_CHARGING_ONLY: + return is_charging(cm); + default: + dev_warn(cm->dev, "Incorrect polling_mode (%d)\n", + cm->desc->polling_mode); + } + + return false; +} + +/** + * try_charger_enable - Enable/Disable chargers altogether + * @cm: the Charger Manager representing the battery. + * @enable: true: enable / false: disable + * + * Note that Charger Manager keeps the charger enabled regardless whether + * the charger is charging or not (because battery is full or no external + * power source exists) except when CM needs to disable chargers forcibly + * bacause of emergency causes; when the battery is overheated or too cold. + */ +static int try_charger_enable(struct charger_manager *cm, bool enable) +{ + int err = 0, i; + struct charger_desc *desc = cm->desc; + + /* Ignore if it's redundent command */ + if (enable && cm->charger_enabled) + return 0; + if (!enable && !cm->charger_enabled) + return 0; + + if (enable) { + if (cm->emergency_stop) + return -EAGAIN; + err = regulator_bulk_enable(desc->num_charger_regulators, + desc->charger_regulators); + } else { + /* + * Abnormal battery state - Stop charging forcibly, + * even if charger was enabled at the other places + */ + err = regulator_bulk_disable(desc->num_charger_regulators, + desc->charger_regulators); + + for (i = 0; i < desc->num_charger_regulators; i++) { + if (regulator_is_enabled( + desc->charger_regulators[i].consumer)) { + regulator_force_disable( + desc->charger_regulators[i].consumer); + dev_warn(cm->dev, + "Disable regulator(%s) forcibly.\n", + desc->charger_regulators[i].supply); + } + } + } + + if (!err) + cm->charger_enabled = enable; + + return err; +} + +/** + * uevent_notify - Let users know something has changed. + * @cm: the Charger Manager representing the battery. + * @event: the event string. + * + * If @event is null, it implies that uevent_notify is called + * by resume function. When called in the resume function, cm_suspended + * should be already reset to false in order to let uevent_notify + * notify the recent event during the suspend to users. While + * suspended, uevent_notify does not notify users, but tracks + * events so that uevent_notify can notify users later after resumed. + */ +static void uevent_notify(struct charger_manager *cm, const char *event) +{ + static char env_str[UEVENT_BUF_SIZE + 1] = ""; + static char env_str_save[UEVENT_BUF_SIZE + 1] = ""; + + if (cm_suspended) { + /* Nothing in suspended-event buffer */ + if (env_str_save[0] == 0) { + if (!strncmp(env_str, event, UEVENT_BUF_SIZE)) + return; /* status not changed */ + strncpy(env_str_save, event, UEVENT_BUF_SIZE); + return; + } + + if (!strncmp(env_str_save, event, UEVENT_BUF_SIZE)) + return; /* Duplicated. */ + else + strncpy(env_str_save, event, UEVENT_BUF_SIZE); + + return; + } + + if (event == NULL) { + /* No messages pending */ + if (!env_str_save[0]) + return; + + strncpy(env_str, env_str_save, UEVENT_BUF_SIZE); + kobject_uevent(&cm->dev->kobj, KOBJ_CHANGE); + env_str_save[0] = 0; + + return; + } + + /* status not changed */ + if (!strncmp(env_str, event, UEVENT_BUF_SIZE)) + return; + + /* save the status and notify the update */ + strncpy(env_str, event, UEVENT_BUF_SIZE); + kobject_uevent(&cm->dev->kobj, KOBJ_CHANGE); + + dev_info(cm->dev, event); +} + +/** + * _cm_monitor - Monitor the temperature and return true for exceptions. + * @cm: the Charger Manager representing the battery. + * + * Returns true if there is an event to notify for the battery. + * (True if the status of "emergency_stop" changes) + */ +static bool _cm_monitor(struct charger_manager *cm) +{ + struct charger_desc *desc = cm->desc; + int temp = desc->temperature_out_of_range(&cm->last_temp_mC); + + dev_dbg(cm->dev, "monitoring (%2.2d.%3.3dC)\n", + cm->last_temp_mC / 1000, cm->last_temp_mC % 1000); + + /* It has been stopped or charging already */ + if (!!temp == !!cm->emergency_stop) + return false; + + if (temp) { + cm->emergency_stop = temp; + if (!try_charger_enable(cm, false)) { + if (temp > 0) + uevent_notify(cm, "OVERHEAT"); + else + uevent_notify(cm, "COLD"); + } + } else { + cm->emergency_stop = 0; + if (!try_charger_enable(cm, true)) + uevent_notify(cm, "CHARGING"); + } + + return true; +} + +/** + * cm_monitor - Monitor every battery. + * + * Returns true if there is an event to notify from any of the batteries. + * (True if the status of "emergency_stop" changes) + */ +static bool cm_monitor(void) +{ + bool stop = false; + struct charger_manager *cm; + + mutex_lock(&cm_list_mtx); + + list_for_each_entry(cm, &cm_list, entry) + stop = stop || _cm_monitor(cm); + + mutex_unlock(&cm_list_mtx); + + return stop; +} + +/** + * cm_setup_timer - For in-suspend monitoring setup wakeup alarm + * for suspend_again. + * + * Returns true if the alarm is set for Charger Manager to use. + * Returns false if + * cm_setup_timer fails to set an alarm, + * cm_setup_timer does not need to set an alarm for Charger Manager, + * or an alarm previously configured is to be used. + */ +static bool cm_setup_timer(void) +{ + struct charger_manager *cm; + unsigned int wakeup_ms = UINT_MAX; + bool ret = false; + + mutex_lock(&cm_list_mtx); + + list_for_each_entry(cm, &cm_list, entry) { + /* Skip if polling is not required for this CM */ + if (!is_polling_required(cm) && !cm->emergency_stop) + continue; + if (cm->desc->polling_interval_ms == 0) + continue; + CM_MIN_VALID(wakeup_ms, cm->desc->polling_interval_ms); + } + + mutex_unlock(&cm_list_mtx); + + if (wakeup_ms < UINT_MAX && wakeup_ms > 0) { + pr_info("Charger Manager wakeup timer: %u ms.\n", wakeup_ms); + if (rtc_dev) { + struct rtc_wkalrm tmp; + unsigned long time, now; + unsigned long add = DIV_ROUND_UP(wakeup_ms, 1000); + + /* + * Set alarm with the polling interval (wakeup_ms) + * except when rtc_wkalarm_save comes first. + * However, the alarm time should be NOW + + * CM_RTC_SMALL or later. + */ + tmp.enabled = 1; + rtc_read_time(rtc_dev, &tmp.time); + rtc_tm_to_time(&tmp.time, &now); + if (add < CM_RTC_SMALL) + add = CM_RTC_SMALL; + time = now + add; + + ret = true; + + if (rtc_wkalarm_save.enabled && + rtc_wkalarm_save_time && + rtc_wkalarm_save_time < time) { + if (rtc_wkalarm_save_time < now + CM_RTC_SMALL) + time = now + CM_RTC_SMALL; + else + time = rtc_wkalarm_save_time; + + /* The timer is not appointed by CM */ + ret = false; + } + + pr_info("Waking up after %lu secs.\n", + time - now); + + rtc_time_to_tm(time, &tmp.time); + rtc_set_alarm(rtc_dev, &tmp); + cm_suspend_duration_ms += wakeup_ms; + return ret; + } + } + + if (rtc_dev) + rtc_set_alarm(rtc_dev, &rtc_wkalarm_save); + return false; +} + +/** + * cm_suspend_again - Determine whether suspend again or not + * + * Returns true if the system should be suspended again + * Returns false if the system should be woken up + */ +bool cm_suspend_again(void) +{ + struct charger_manager *cm; + bool ret = false; + + if (!g_desc || !g_desc->rtc_only_wakeup || !g_desc->rtc_only_wakeup() || + !cm_rtc_set) + return false; + + if (cm_monitor()) + goto out; + + ret = true; + mutex_lock(&cm_list_mtx); + list_for_each_entry(cm, &cm_list, entry) { + if (cm->status_save_ext_pwr_inserted != is_ext_pwr_online(cm) || + cm->status_save_batt != is_batt_present(cm)) + ret = false; + } + mutex_unlock(&cm_list_mtx); + + cm_rtc_set = cm_setup_timer(); +out: + /* It's about the time when the non-CM appointed timer goes off */ + if (rtc_wkalarm_save.enabled) { + unsigned long now; + struct rtc_time tmp; + + rtc_read_time(rtc_dev, &tmp); + rtc_tm_to_time(&tmp, &now); + + if (rtc_wkalarm_save_time && + now + CM_RTC_SMALL >= rtc_wkalarm_save_time) + return false; + } + return ret; +} +EXPORT_SYMBOL_GPL(cm_suspend_again); + +/** + * setup_charger_manager - initialize charger_global_desc data + * @gd: pointer to instance of charger_global_desc + */ +int setup_charger_manager(struct charger_global_desc *gd) +{ + if (!gd) + return -EINVAL; + + if (rtc_dev) + rtc_class_close(rtc_dev); + rtc_dev = NULL; + g_desc = NULL; + + if (!gd->rtc_only_wakeup) { + pr_err("The callback rtc_only_wakeup is not given.\n"); + return -EINVAL; + } + + if (gd->rtc_name) { + rtc_dev = rtc_class_open(gd->rtc_name); + if (IS_ERR_OR_NULL(rtc_dev)) { + rtc_dev = NULL; + /* Retry at probe. RTC may be not registered yet */ + } + } else { + pr_warn("No wakeup timer is given for charger manager." + "In-suspend monitoring won't work.\n"); + } + + g_desc = gd; + return 0; +} +EXPORT_SYMBOL_GPL(setup_charger_manager); + +static int charger_manager_probe(struct platform_device *pdev) +{ + struct charger_desc *desc = dev_get_platdata(&pdev->dev); + struct charger_manager *cm; + int ret = 0, i = 0; + + if (g_desc && !rtc_dev && g_desc->rtc_name) { + rtc_dev = rtc_class_open(g_desc->rtc_name); + if (IS_ERR_OR_NULL(rtc_dev)) { + rtc_dev = NULL; + dev_err(&pdev->dev, "Cannot get RTC %s.\n", + g_desc->rtc_name); + ret = -ENODEV; + goto err_alloc; + } + } + + if (!desc) { + dev_err(&pdev->dev, "No platform data (desc) found.\n"); + ret = -ENODEV; + goto err_alloc; + } + + cm = kzalloc(sizeof(struct charger_manager), GFP_KERNEL); + if (!cm) { + dev_err(&pdev->dev, "Cannot allocate memory.\n"); + ret = -ENOMEM; + goto err_alloc; + } + + /* Basic Values. Unspecified are Null or 0 */ + cm->dev = &pdev->dev; + cm->desc = kzalloc(sizeof(struct charger_desc), GFP_KERNEL); + if (!cm->desc) { + dev_err(&pdev->dev, "Cannot allocate memory.\n"); + ret = -ENOMEM; + goto err_alloc_desc; + } + memcpy(cm->desc, desc, sizeof(struct charger_desc)); + cm->last_temp_mC = INT_MIN; /* denotes "unmeasured, yet" */ + + if (!desc->charger_regulators || desc->num_charger_regulators < 1) { + ret = -EINVAL; + dev_err(&pdev->dev, "charger_regulators undefined.\n"); + goto err_no_charger; + } + + if (!desc->psy_charger_stat || !desc->psy_charger_stat[0]) { + dev_err(&pdev->dev, "No power supply defined.\n"); + ret = -EINVAL; + goto err_no_charger_stat; + } + + /* Counting index only */ + while (desc->psy_charger_stat[i]) + i++; + + cm->charger_stat = kzalloc(sizeof(struct power_supply *) * (i + 1), + GFP_KERNEL); + if (!cm->charger_stat) { + ret = -ENOMEM; + goto err_no_charger_stat; + } + + for (i = 0; desc->psy_charger_stat[i]; i++) { + cm->charger_stat[i] = power_supply_get_by_name( + desc->psy_charger_stat[i]); + if (!cm->charger_stat[i]) { + dev_err(&pdev->dev, "Cannot find power supply " + "\"%s\"\n", + desc->psy_charger_stat[i]); + ret = -ENODEV; + goto err_chg_stat; + } + } + + cm->fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge); + if (!cm->fuel_gauge) { + dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n", + desc->psy_fuel_gauge); + ret = -ENODEV; + goto err_chg_stat; + } + + if (desc->polling_interval_ms == 0 || + msecs_to_jiffies(desc->polling_interval_ms) <= CM_JIFFIES_SMALL) { + dev_err(&pdev->dev, "polling_interval_ms is too small\n"); + ret = -EINVAL; + goto err_chg_stat; + } + + if (!desc->temperature_out_of_range) { + dev_err(&pdev->dev, "there is no temperature_out_of_range\n"); + ret = -EINVAL; + goto err_chg_stat; + } + + platform_set_drvdata(pdev, cm); + + ret = regulator_bulk_get(&pdev->dev, desc->num_charger_regulators, + desc->charger_regulators); + if (ret) { + dev_err(&pdev->dev, "Cannot get charger regulators.\n"); + goto err_chg_stat; + } + + ret = try_charger_enable(cm, true); + if (ret) { + dev_err(&pdev->dev, "Cannot enable charger regulators\n"); + goto err_chg_enable; + } + + /* Add to the list */ + mutex_lock(&cm_list_mtx); + list_add(&cm->entry, &cm_list); + mutex_unlock(&cm_list_mtx); + + return 0; + +err_chg_enable: + if (desc->charger_regulators) + regulator_bulk_free(desc->num_charger_regulators, + desc->charger_regulators); +err_chg_stat: + kfree(cm->charger_stat); +err_no_charger_stat: +err_no_charger: + kfree(cm->desc); +err_alloc_desc: + kfree(cm); +err_alloc: + return ret; +} + +static int __devexit charger_manager_remove(struct platform_device *pdev) +{ + struct charger_manager *cm = platform_get_drvdata(pdev); + struct charger_desc *desc = cm->desc; + + /* Remove from the list */ + mutex_lock(&cm_list_mtx); + list_del(&cm->entry); + mutex_unlock(&cm_list_mtx); + + if (desc->charger_regulators) + regulator_bulk_free(desc->num_charger_regulators, + desc->charger_regulators); + kfree(cm->charger_stat); + kfree(cm->desc); + kfree(cm); + + return 0; +} + +const struct platform_device_id charger_manager_id[] = { + { "charger-manager", 0 }, + { }, +}; + +static int cm_suspend_prepare(struct device *dev) +{ + struct platform_device *pdev = container_of(dev, struct platform_device, + dev); + struct charger_manager *cm = platform_get_drvdata(pdev); + + if (!cm_suspended) { + if (rtc_dev) { + struct rtc_time tmp; + unsigned long now; + + rtc_read_alarm(rtc_dev, &rtc_wkalarm_save); + rtc_read_time(rtc_dev, &tmp); + + if (rtc_wkalarm_save.enabled) { + rtc_tm_to_time(&rtc_wkalarm_save.time, + &rtc_wkalarm_save_time); + rtc_tm_to_time(&tmp, &now); + if (now > rtc_wkalarm_save_time) + rtc_wkalarm_save_time = 0; + } else { + rtc_wkalarm_save_time = 0; + } + } + cm_suspended = true; + } + + cm->status_save_ext_pwr_inserted = is_ext_pwr_online(cm); + cm->status_save_batt = is_batt_present(cm); + + if (!cm_rtc_set) { + cm_suspend_duration_ms = 0; + cm_rtc_set = cm_setup_timer(); + } + + return 0; +} + +static void cm_suspend_complete(struct device *dev) +{ + struct platform_device *pdev = container_of(dev, struct platform_device, + dev); + struct charger_manager *cm = platform_get_drvdata(pdev); + + if (cm_suspended) { + if (rtc_dev) { + struct rtc_wkalrm tmp; + + rtc_read_alarm(rtc_dev, &tmp); + rtc_wkalarm_save.pending = tmp.pending; + rtc_set_alarm(rtc_dev, &rtc_wkalarm_save); + } + cm_suspended = false; + cm_rtc_set = false; + } + + uevent_notify(cm, NULL); +} + +static const struct dev_pm_ops charger_manager_pm = { + .prepare = cm_suspend_prepare, + .complete = cm_suspend_complete, +}; + +static struct platform_driver charger_manager_driver = { + .driver = { + .name = "charger-manager", + .owner = THIS_MODULE, + .pm = &charger_manager_pm, + }, + .probe = charger_manager_probe, + .remove = __devexit_p(charger_manager_remove), + .id_table = charger_manager_id, +}; + +static int __init charger_manager_init(void) +{ + return platform_driver_register(&charger_manager_driver); +} +late_initcall(charger_manager_init); + +static void __exit charger_manager_cleanup(void) +{ + platform_driver_unregister(&charger_manager_driver); +} +module_exit(charger_manager_cleanup); + +MODULE_AUTHOR("MyungJoo Ham "); +MODULE_DESCRIPTION("Charger Manager"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("charger-manager"); -- cgit v1.1 From ad3d13eee78ec44194bf919a37e2f711e53cbdf0 Mon Sep 17 00:00:00 2001 From: Donggeun Kim Date: Tue, 27 Dec 2011 18:47:49 +0900 Subject: power_supply: Charger-Manager: Add properties for power-supply-class Charger Manager provides power-supply-class aggregating information from multiple chargers and a fuel-gauge. Signed-off-by: Donggeun Kim Signed-off-by: MyungJoo Ham Signed-off-by: Kyungmin Park Signed-off-by: Anton Vorontsov --- drivers/power/charger-manager.c | 295 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 294 insertions(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index 727a259..0378d01 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -122,6 +122,32 @@ static bool is_ext_pwr_online(struct charger_manager *cm) } /** + * get_batt_uV - Get the voltage level of the battery + * @cm: the Charger Manager representing the battery. + * @uV: the voltage level returned. + * + * Returns 0 if there is no error. + * Returns a negative value on error. + */ +static int get_batt_uV(struct charger_manager *cm, int *uV) +{ + union power_supply_propval val; + int ret; + + if (cm->fuel_gauge) + ret = cm->fuel_gauge->get_property(cm->fuel_gauge, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &val); + else + return -ENODEV; + + if (ret) + return ret; + + *uV = val.intval; + return 0; +} + +/** * is_charging - Returns true if the battery is being charged. * @cm: the Charger Manager representing the battery. */ @@ -369,6 +395,208 @@ static bool cm_monitor(void) return stop; } +static int charger_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct charger_manager *cm = container_of(psy, + struct charger_manager, charger_psy); + struct charger_desc *desc = cm->desc; + int i, ret = 0, uV; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (is_charging(cm)) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else if (is_ext_pwr_online(cm)) + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + else + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + break; + case POWER_SUPPLY_PROP_HEALTH: + if (cm->emergency_stop > 0) + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + else if (cm->emergency_stop < 0) + val->intval = POWER_SUPPLY_HEALTH_COLD; + else + val->intval = POWER_SUPPLY_HEALTH_GOOD; + break; + case POWER_SUPPLY_PROP_PRESENT: + if (is_batt_present(cm)) + val->intval = 1; + else + val->intval = 0; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = get_batt_uV(cm, &i); + val->intval = i; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = cm->fuel_gauge->get_property(cm->fuel_gauge, + POWER_SUPPLY_PROP_CURRENT_NOW, val); + break; + case POWER_SUPPLY_PROP_TEMP: + /* in thenth of centigrade */ + if (cm->last_temp_mC == INT_MIN) + desc->temperature_out_of_range(&cm->last_temp_mC); + val->intval = cm->last_temp_mC / 100; + if (!desc->measure_battery_temp) + ret = -ENODEV; + break; + case POWER_SUPPLY_PROP_TEMP_AMBIENT: + /* in thenth of centigrade */ + if (cm->last_temp_mC == INT_MIN) + desc->temperature_out_of_range(&cm->last_temp_mC); + val->intval = cm->last_temp_mC / 100; + if (desc->measure_battery_temp) + ret = -ENODEV; + break; + case POWER_SUPPLY_PROP_CAPACITY: + if (!cm->fuel_gauge) { + ret = -ENODEV; + break; + } + + if (!is_batt_present(cm)) { + /* There is no battery. Assume 100% */ + val->intval = 100; + break; + } + + ret = cm->fuel_gauge->get_property(cm->fuel_gauge, + POWER_SUPPLY_PROP_CAPACITY, val); + if (ret) + break; + + if (val->intval > 100) { + val->intval = 100; + break; + } + if (val->intval < 0) + val->intval = 0; + + /* Do not adjust SOC when charging: voltage is overrated */ + if (is_charging(cm)) + break; + + /* + * If the capacity value is inconsistent, calibrate it base on + * the battery voltage values and the thresholds given as desc + */ + ret = get_batt_uV(cm, &uV); + if (ret) { + /* Voltage information not available. No calibration */ + ret = 0; + break; + } + + if (desc->fullbatt_uV > 0 && uV >= desc->fullbatt_uV && + !is_charging(cm)) { + val->intval = 100; + break; + } + + break; + case POWER_SUPPLY_PROP_ONLINE: + if (is_ext_pwr_online(cm)) + val->intval = 1; + else + val->intval = 0; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + if (cm->fuel_gauge) { + if (cm->fuel_gauge->get_property(cm->fuel_gauge, + POWER_SUPPLY_PROP_CHARGE_FULL, val) == 0) + break; + } + + if (is_ext_pwr_online(cm)) { + /* Not full if it's charging. */ + if (is_charging(cm)) { + val->intval = 0; + break; + } + /* + * Full if it's powered but not charging andi + * not forced stop by emergency + */ + if (!cm->emergency_stop) { + val->intval = 1; + break; + } + } + + /* Full if it's over the fullbatt voltage */ + ret = get_batt_uV(cm, &uV); + if (!ret && desc->fullbatt_uV > 0 && uV >= desc->fullbatt_uV && + !is_charging(cm)) { + val->intval = 1; + break; + } + + /* Full if the cap is 100 */ + if (cm->fuel_gauge) { + ret = cm->fuel_gauge->get_property(cm->fuel_gauge, + POWER_SUPPLY_PROP_CAPACITY, val); + if (!ret && val->intval >= 100 && !is_charging(cm)) { + val->intval = 1; + break; + } + } + + val->intval = 0; + ret = 0; + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + if (is_charging(cm)) { + ret = cm->fuel_gauge->get_property(cm->fuel_gauge, + POWER_SUPPLY_PROP_CHARGE_NOW, + val); + if (ret) { + val->intval = 1; + ret = 0; + } else { + /* If CHARGE_NOW is supplied, use it */ + val->intval = (val->intval > 0) ? + val->intval : 1; + } + } else { + val->intval = 0; + } + break; + default: + return -EINVAL; + } + return ret; +} + +#define NUM_CHARGER_PSY_OPTIONAL (4) +static enum power_supply_property default_charger_props[] = { + /* Guaranteed to provide */ + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_CHARGE_FULL, + /* + * Optional properties are: + * POWER_SUPPLY_PROP_CHARGE_NOW, + * POWER_SUPPLY_PROP_CURRENT_NOW, + * POWER_SUPPLY_PROP_TEMP, and + * POWER_SUPPLY_PROP_TEMP_AMBIENT, + */ +}; + +static struct power_supply psy_default = { + .name = "battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = default_charger_props, + .num_properties = ARRAY_SIZE(default_charger_props), + .get_property = charger_get_property, +}; + /** * cm_setup_timer - For in-suspend monitoring setup wakeup alarm * for suspend_again. @@ -532,6 +760,7 @@ static int charger_manager_probe(struct platform_device *pdev) struct charger_desc *desc = dev_get_platdata(&pdev->dev); struct charger_manager *cm; int ret = 0, i = 0; + union power_supply_propval val; if (g_desc && !rtc_dev && g_desc->rtc_name) { rtc_dev = rtc_class_open(g_desc->rtc_name); @@ -626,11 +855,68 @@ static int charger_manager_probe(struct platform_device *pdev) platform_set_drvdata(pdev, cm); + memcpy(&cm->charger_psy, &psy_default, + sizeof(psy_default)); + if (!desc->psy_name) { + strncpy(cm->psy_name_buf, psy_default.name, + PSY_NAME_MAX); + } else { + strncpy(cm->psy_name_buf, desc->psy_name, PSY_NAME_MAX); + } + cm->charger_psy.name = cm->psy_name_buf; + + /* Allocate for psy properties because they may vary */ + cm->charger_psy.properties = kzalloc(sizeof(enum power_supply_property) + * (ARRAY_SIZE(default_charger_props) + + NUM_CHARGER_PSY_OPTIONAL), + GFP_KERNEL); + if (!cm->charger_psy.properties) { + dev_err(&pdev->dev, "Cannot allocate for psy properties.\n"); + ret = -ENOMEM; + goto err_chg_stat; + } + memcpy(cm->charger_psy.properties, default_charger_props, + sizeof(enum power_supply_property) * + ARRAY_SIZE(default_charger_props)); + cm->charger_psy.num_properties = psy_default.num_properties; + + /* Find which optional psy-properties are available */ + if (!cm->fuel_gauge->get_property(cm->fuel_gauge, + POWER_SUPPLY_PROP_CHARGE_NOW, &val)) { + cm->charger_psy.properties[cm->charger_psy.num_properties] = + POWER_SUPPLY_PROP_CHARGE_NOW; + cm->charger_psy.num_properties++; + } + if (!cm->fuel_gauge->get_property(cm->fuel_gauge, + POWER_SUPPLY_PROP_CURRENT_NOW, + &val)) { + cm->charger_psy.properties[cm->charger_psy.num_properties] = + POWER_SUPPLY_PROP_CURRENT_NOW; + cm->charger_psy.num_properties++; + } + if (!desc->measure_battery_temp) { + cm->charger_psy.properties[cm->charger_psy.num_properties] = + POWER_SUPPLY_PROP_TEMP_AMBIENT; + cm->charger_psy.num_properties++; + } + if (desc->measure_battery_temp) { + cm->charger_psy.properties[cm->charger_psy.num_properties] = + POWER_SUPPLY_PROP_TEMP; + cm->charger_psy.num_properties++; + } + + ret = power_supply_register(NULL, &cm->charger_psy); + if (ret) { + dev_err(&pdev->dev, "Cannot register charger-manager with" + " name \"%s\".\n", cm->charger_psy.name); + goto err_register; + } + ret = regulator_bulk_get(&pdev->dev, desc->num_charger_regulators, desc->charger_regulators); if (ret) { dev_err(&pdev->dev, "Cannot get charger regulators.\n"); - goto err_chg_stat; + goto err_bulk_get; } ret = try_charger_enable(cm, true); @@ -650,6 +936,10 @@ err_chg_enable: if (desc->charger_regulators) regulator_bulk_free(desc->num_charger_regulators, desc->charger_regulators); +err_bulk_get: + power_supply_unregister(&cm->charger_psy); +err_register: + kfree(cm->charger_psy.properties); err_chg_stat: kfree(cm->charger_stat); err_no_charger_stat: @@ -674,6 +964,9 @@ static int __devexit charger_manager_remove(struct platform_device *pdev) if (desc->charger_regulators) regulator_bulk_free(desc->num_charger_regulators, desc->charger_regulators); + + power_supply_unregister(&cm->charger_psy); + kfree(cm->charger_psy.properties); kfree(cm->charger_stat); kfree(cm->desc); kfree(cm); -- cgit v1.1 From 2165c8a45bf2fba49f54fb81a50914f883bd14df Mon Sep 17 00:00:00 2001 From: Woogyom Kim Date: Wed, 4 Jan 2012 08:27:43 +0400 Subject: power_supply: Add LP8727 charger driver National Semiconductor LP8727 is the battery charger with Micro/Mini USB interface. This IC includes below functions: - I2C interface for accessing user registers - Single input Li-Ion battery charger - Charger input ID detection from Micro/Mini USB - Multiplexing switches on USB, UART Signed-off-by: Woogyom Kim Signed-off-by: Anton Vorontsov --- drivers/power/Kconfig | 6 + drivers/power/Makefile | 1 + drivers/power/lp8727_charger.c | 491 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 498 insertions(+) create mode 100644 drivers/power/lp8727_charger.c (limited to 'drivers/power') diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 363f4d1..ac807c4 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -225,6 +225,12 @@ config CHARGER_TWL4030 help Say Y here to enable support for TWL4030 Battery Charge Interface. +config CHARGER_LP8727 + tristate "National Semiconductor LP8727 charger driver" + depends on I2C + help + Say Y here to enable support for LP8727 Charger Driver. + config CHARGER_GPIO tristate "GPIO charger" depends on GPIOLIB diff --git a/drivers/power/Makefile b/drivers/power/Makefile index d3b24e9..9a78b1d 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o +obj-$(CONFIG_CHARGER_LP8727) += lp8727_charger.o obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o diff --git a/drivers/power/lp8727_charger.c b/drivers/power/lp8727_charger.c new file mode 100644 index 0000000..2a649e0 --- /dev/null +++ b/drivers/power/lp8727_charger.c @@ -0,0 +1,491 @@ +/* + * Driver for LP8727 Micro/Mini USB IC with intergrated charger + * + * Copyright (C) 2011 National Semiconductor + * + * 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 + +#define DEBOUNCE_MSEC 270 + +/* Registers */ +#define CTRL1 0x1 +#define CTRL2 0x2 +#define SWCTRL 0x3 +#define INT1 0x4 +#define INT2 0x5 +#define STATUS1 0x6 +#define STATUS2 0x7 +#define CHGCTRL2 0x9 + +/* CTRL1 register */ +#define CP_EN (1 << 0) +#define ADC_EN (1 << 1) +#define ID200_EN (1 << 4) + +/* CTRL2 register */ +#define CHGDET_EN (1 << 1) +#define INT_EN (1 << 6) + +/* SWCTRL register */ +#define SW_DM1_DM (0x0 << 0) +#define SW_DM1_U1 (0x1 << 0) +#define SW_DM1_HiZ (0x7 << 0) +#define SW_DP2_DP (0x0 << 3) +#define SW_DP2_U2 (0x1 << 3) +#define SW_DP2_HiZ (0x7 << 3) + +/* INT1 register */ +#define IDNO (0xF << 0) +#define VBUS (1 << 4) + +/* STATUS1 register */ +#define CHGSTAT (3 << 4) +#define CHPORT (1 << 6) +#define DCPORT (1 << 7) + +/* STATUS2 register */ +#define TEMP_STAT (3 << 5) + +enum lp8727_dev_id { + ID_NONE, + ID_TA, + ID_DEDICATED_CHG, + ID_USB_CHG, + ID_USB_DS, + ID_MAX, +}; + +enum lp8727_chg_stat { + PRECHG, + CC, + CV, + EOC, +}; + +struct lp8727_psy { + struct power_supply ac; + struct power_supply usb; + struct power_supply batt; +}; + +struct lp8727_chg { + struct device *dev; + struct i2c_client *client; + struct mutex xfer_lock; + struct delayed_work work; + struct workqueue_struct *irqthread; + struct lp8727_platform_data *pdata; + struct lp8727_psy *psy; + struct lp8727_chg_param *chg_parm; + enum lp8727_dev_id devid; +}; + +static int lp8727_i2c_read(struct lp8727_chg *pchg, u8 reg, u8 * data, u8 len) +{ + s32 ret; + + mutex_lock(&pchg->xfer_lock); + ret = i2c_smbus_read_i2c_block_data(pchg->client, reg, len, data); + mutex_unlock(&pchg->xfer_lock); + + return (ret != len) ? -EIO : 0; +} + +static int lp8727_i2c_write(struct lp8727_chg *pchg, u8 reg, u8 * data, u8 len) +{ + s32 ret; + + mutex_lock(&pchg->xfer_lock); + ret = i2c_smbus_write_i2c_block_data(pchg->client, reg, len, data); + mutex_unlock(&pchg->xfer_lock); + + return ret; +} + +static inline int lp8727_i2c_read_byte(struct lp8727_chg *pchg, u8 reg, + u8 * data) +{ + return lp8727_i2c_read(pchg, reg, data, 1); +} + +static inline int lp8727_i2c_write_byte(struct lp8727_chg *pchg, u8 reg, + u8 * data) +{ + return lp8727_i2c_write(pchg, reg, data, 1); +} + +static int lp8727_is_charger_attached(const char *name, int id) +{ + if (name) { + if (!strcmp(name, "ac")) + return (id == ID_TA || id == ID_DEDICATED_CHG) ? 1 : 0; + else if (!strcmp(name, "usb")) + return (id == ID_USB_CHG) ? 1 : 0; + } + + return (id >= ID_TA && id <= ID_USB_CHG) ? 1 : 0; +} + +static void lp8727_init_device(struct lp8727_chg *pchg) +{ + u8 val; + + val = ID200_EN | ADC_EN | CP_EN; + if (lp8727_i2c_write_byte(pchg, CTRL1, &val)) + dev_err(pchg->dev, "i2c write err : addr=0x%.2x\n", CTRL1); + + val = INT_EN | CHGDET_EN; + if (lp8727_i2c_write_byte(pchg, CTRL2, &val)) + dev_err(pchg->dev, "i2c write err : addr=0x%.2x\n", CTRL2); +} + +static int lp8727_is_dedicated_charger(struct lp8727_chg *pchg) +{ + u8 val; + (void)lp8727_i2c_read_byte(pchg, STATUS1, &val); + return (val & DCPORT); +} + +static int lp8727_is_usb_charger(struct lp8727_chg *pchg) +{ + u8 val; + (void)lp8727_i2c_read_byte(pchg, STATUS1, &val); + return (val & CHPORT); +} + +static void lp8727_ctrl_switch(struct lp8727_chg *pchg, u8 sw) +{ + u8 val = sw; + (void)lp8727_i2c_write_byte(pchg, SWCTRL, &val); +} + +static void lp8727_id_detection(struct lp8727_chg *pchg, u8 id, int vbusin) +{ + u8 devid = ID_NONE; + u8 swctrl = SW_DM1_HiZ | SW_DP2_HiZ; + + switch (id) { + case 0x5: + devid = ID_TA; + pchg->chg_parm = &pchg->pdata->ac; + break; + case 0xB: + if (lp8727_is_dedicated_charger(pchg)) { + pchg->chg_parm = &pchg->pdata->ac; + devid = ID_DEDICATED_CHG; + } else if (lp8727_is_usb_charger(pchg)) { + pchg->chg_parm = &pchg->pdata->usb; + devid = ID_USB_CHG; + swctrl = SW_DM1_DM | SW_DP2_DP; + } else if (vbusin) { + devid = ID_USB_DS; + swctrl = SW_DM1_DM | SW_DP2_DP; + } + break; + default: + devid = ID_NONE; + pchg->chg_parm = NULL; + break; + } + + pchg->devid = devid; + lp8727_ctrl_switch(pchg, swctrl); +} + +static void lp8727_enable_chgdet(struct lp8727_chg *pchg) +{ + u8 val; + + (void)lp8727_i2c_read_byte(pchg, CTRL2, &val); + val |= CHGDET_EN; + (void)lp8727_i2c_write_byte(pchg, CTRL2, &val); +} + +static void lp8727_delayed_func(struct work_struct *_work) +{ + u8 intstat[2], idno, vbus; + struct lp8727_chg *pchg = + container_of(_work, struct lp8727_chg, work.work); + + if (lp8727_i2c_read(pchg, INT1, intstat, 2)) { + dev_err(pchg->dev, "can not read INT registers\n"); + return; + } + + idno = intstat[0] & IDNO; + vbus = intstat[0] & VBUS; + + lp8727_id_detection(pchg, idno, vbus); + lp8727_enable_chgdet(pchg); + + power_supply_changed(&pchg->psy->ac); + power_supply_changed(&pchg->psy->usb); + power_supply_changed(&pchg->psy->batt); +} + +static irqreturn_t lp8727_isr_func(int irq, void *ptr) +{ + struct lp8727_chg *pchg = ptr; + unsigned long delay = msecs_to_jiffies(DEBOUNCE_MSEC); + + queue_delayed_work(pchg->irqthread, &pchg->work, delay); + + return IRQ_HANDLED; +} + +static void lp8727_intr_config(struct lp8727_chg *pchg) +{ + INIT_DELAYED_WORK(&pchg->work, lp8727_delayed_func); + + pchg->irqthread = create_singlethread_workqueue("lp8727-irqthd"); + if (!pchg->irqthread) + dev_err(pchg->dev, "can not create thread for lp8727\n"); + + if (request_threaded_irq(pchg->client->irq, + NULL, + lp8727_isr_func, + IRQF_TRIGGER_FALLING, "lp8727_irq", pchg)) { + dev_err(pchg->dev, "lp8727 irq can not be registered\n"); + } +} + +static enum power_supply_property lp8727_charger_prop[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static enum power_supply_property lp8727_battery_prop[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_TEMP, +}; + +static char *battery_supplied_to[] = { + "main_batt", +}; + +static int lp8727_charger_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent); + + if (psp == POWER_SUPPLY_PROP_ONLINE) { + val->intval = lp8727_is_charger_attached(psy->name, + pchg->devid); + } + + return 0; +} + +static int lp8727_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent); + u8 read; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (lp8727_is_charger_attached(psy->name, pchg->devid)) { + (void)lp8727_i2c_read_byte(pchg, STATUS1, &read); + if (((read & CHGSTAT) >> 4) == EOC) + val->intval = POWER_SUPPLY_STATUS_FULL; + else + val->intval = POWER_SUPPLY_STATUS_CHARGING; + } else { + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + } + break; + case POWER_SUPPLY_PROP_HEALTH: + (void)lp8727_i2c_read_byte(pchg, STATUS2, &read); + read = (read & TEMP_STAT) >> 5; + if (read >= 0x1 && read <= 0x3) + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + else + val->intval = POWER_SUPPLY_HEALTH_GOOD; + break; + case POWER_SUPPLY_PROP_PRESENT: + if (pchg->pdata->get_batt_present) + val->intval = pchg->pdata->get_batt_present(); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + if (pchg->pdata->get_batt_level) + val->intval = pchg->pdata->get_batt_level(); + break; + case POWER_SUPPLY_PROP_CAPACITY: + if (pchg->pdata->get_batt_capacity) + val->intval = pchg->pdata->get_batt_capacity(); + break; + case POWER_SUPPLY_PROP_TEMP: + if (pchg->pdata->get_batt_temp) + val->intval = pchg->pdata->get_batt_temp(); + break; + default: + break; + } + + return 0; +} + +static void lp8727_charger_changed(struct power_supply *psy) +{ + struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent); + u8 val; + u8 eoc_level, ichg; + + if (lp8727_is_charger_attached(psy->name, pchg->devid)) { + if (pchg->chg_parm) { + eoc_level = pchg->chg_parm->eoc_level; + ichg = pchg->chg_parm->ichg; + val = (ichg << 4) | eoc_level; + (void)lp8727_i2c_write_byte(pchg, CHGCTRL2, &val); + } + } +} + +static int lp8727_register_psy(struct lp8727_chg *pchg) +{ + struct lp8727_psy *psy; + + psy = kzalloc(sizeof(*psy), GFP_KERNEL); + if (!psy) + goto err_mem; + + pchg->psy = psy; + + psy->ac.name = "ac"; + psy->ac.type = POWER_SUPPLY_TYPE_MAINS; + psy->ac.properties = lp8727_charger_prop; + psy->ac.num_properties = ARRAY_SIZE(lp8727_charger_prop); + psy->ac.get_property = lp8727_charger_get_property; + psy->ac.supplied_to = battery_supplied_to; + psy->ac.num_supplicants = ARRAY_SIZE(battery_supplied_to); + + if (power_supply_register(pchg->dev, &psy->ac)) + goto err_psy; + + psy->usb.name = "usb"; + psy->usb.type = POWER_SUPPLY_TYPE_USB; + psy->usb.properties = lp8727_charger_prop; + psy->usb.num_properties = ARRAY_SIZE(lp8727_charger_prop); + psy->usb.get_property = lp8727_charger_get_property; + psy->usb.supplied_to = battery_supplied_to; + psy->usb.num_supplicants = ARRAY_SIZE(battery_supplied_to); + + if (power_supply_register(pchg->dev, &psy->usb)) + goto err_psy; + + psy->batt.name = "main_batt"; + psy->batt.type = POWER_SUPPLY_TYPE_BATTERY; + psy->batt.properties = lp8727_battery_prop; + psy->batt.num_properties = ARRAY_SIZE(lp8727_battery_prop); + psy->batt.get_property = lp8727_battery_get_property; + psy->batt.external_power_changed = lp8727_charger_changed; + + if (power_supply_register(pchg->dev, &psy->batt)) + goto err_psy; + + return 0; + +err_mem: + return -ENOMEM; +err_psy: + kfree(psy); + return -EPERM; +} + +static void lp8727_unregister_psy(struct lp8727_chg *pchg) +{ + struct lp8727_psy *psy = pchg->psy; + + if (psy) { + power_supply_unregister(&psy->ac); + power_supply_unregister(&psy->usb); + power_supply_unregister(&psy->batt); + kfree(psy); + } +} + +static int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id) +{ + struct lp8727_chg *pchg; + int ret; + + pchg = kzalloc(sizeof(*pchg), GFP_KERNEL); + if (!pchg) + return -ENOMEM; + + pchg->client = cl; + pchg->dev = &cl->dev; + pchg->pdata = cl->dev.platform_data; + i2c_set_clientdata(cl, pchg); + + mutex_init(&pchg->xfer_lock); + + lp8727_init_device(pchg); + lp8727_intr_config(pchg); + + ret = lp8727_register_psy(pchg); + if (ret) { + dev_err(pchg->dev, + "can not register power supplies. err=%d", ret); + } + + return 0; +} + +static int __devexit lp8727_remove(struct i2c_client *cl) +{ + struct lp8727_chg *pchg = i2c_get_clientdata(cl); + + lp8727_unregister_psy(pchg); + free_irq(pchg->client->irq, pchg); + flush_workqueue(pchg->irqthread); + destroy_workqueue(pchg->irqthread); + kfree(pchg); + return 0; +} + +static const struct i2c_device_id lp8727_ids[] = { + {"lp8727", 0}, +}; + +static struct i2c_driver lp8727_driver = { + .driver = { + .name = "lp8727", + }, + .probe = lp8727_probe, + .remove = __devexit_p(lp8727_remove), + .id_table = lp8727_ids, +}; + +static int __init lp8727_init(void) +{ + return i2c_add_driver(&lp8727_driver); +} + +static void __exit lp8727_chg_exit(void) +{ + i2c_del_driver(&lp8727_driver); +} + +module_init(lp8727_init); +module_exit(lp8727_chg_exit); + +MODULE_DESCRIPTION("National Semiconductor LP8727 charger driver"); +MODULE_AUTHOR("Woogyom Kim "); +MODULE_LICENSE("GPL"); -- cgit v1.1 From 998a8e7a72f6a81f540d3a3774d8e8aae6c7f9f2 Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Thu, 17 Nov 2011 21:43:06 -0800 Subject: lp8727_charger: Add supported i2c functionality check routine lp8727 i2c r/w functions are based on SMBUS I2C BLOCK. So the driver needs to check whether i2c bus supports this functionality or not. Signed-off-by: Woogyom Kim Signed-off-by: Anton Vorontsov --- drivers/power/lp8727_charger.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/power') diff --git a/drivers/power/lp8727_charger.c b/drivers/power/lp8727_charger.c index 2a649e0..a7e5cc2 100644 --- a/drivers/power/lp8727_charger.c +++ b/drivers/power/lp8727_charger.c @@ -425,6 +425,9 @@ static int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id) struct lp8727_chg *pchg; int ret; + if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) + return -EIO; + pchg = kzalloc(sizeof(*pchg), GFP_KERNEL); if (!pchg) return -ENOMEM; -- cgit v1.1 From ce09affc59bca2a1ac1f77b75915fd914fab1f70 Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Wed, 4 Jan 2012 09:03:18 +0400 Subject: lp8727_charger: Coding style changes 1. Useless braces were omitted 2. Useless void casts were omitted 3. module exit name changed lp8727_chg_exit -> lp8727_exit 4. Pointer coding style changes no space between pointer('*') and pointer name ex) u8 * data -> u8 *data 5. Author information change : email and additional author Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Anton Vorontsov --- drivers/power/lp8727_charger.c | 50 +++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 25 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/lp8727_charger.c b/drivers/power/lp8727_charger.c index a7e5cc2..b15b575 100644 --- a/drivers/power/lp8727_charger.c +++ b/drivers/power/lp8727_charger.c @@ -91,7 +91,7 @@ struct lp8727_chg { enum lp8727_dev_id devid; }; -static int lp8727_i2c_read(struct lp8727_chg *pchg, u8 reg, u8 * data, u8 len) +static int lp8727_i2c_read(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len) { s32 ret; @@ -102,7 +102,7 @@ static int lp8727_i2c_read(struct lp8727_chg *pchg, u8 reg, u8 * data, u8 len) return (ret != len) ? -EIO : 0; } -static int lp8727_i2c_write(struct lp8727_chg *pchg, u8 reg, u8 * data, u8 len) +static int lp8727_i2c_write(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len) { s32 ret; @@ -114,13 +114,13 @@ static int lp8727_i2c_write(struct lp8727_chg *pchg, u8 reg, u8 * data, u8 len) } static inline int lp8727_i2c_read_byte(struct lp8727_chg *pchg, u8 reg, - u8 * data) + u8 *data) { return lp8727_i2c_read(pchg, reg, data, 1); } static inline int lp8727_i2c_write_byte(struct lp8727_chg *pchg, u8 reg, - u8 * data) + u8 *data) { return lp8727_i2c_write(pchg, reg, data, 1); } @@ -153,21 +153,21 @@ static void lp8727_init_device(struct lp8727_chg *pchg) static int lp8727_is_dedicated_charger(struct lp8727_chg *pchg) { u8 val; - (void)lp8727_i2c_read_byte(pchg, STATUS1, &val); + lp8727_i2c_read_byte(pchg, STATUS1, &val); return (val & DCPORT); } static int lp8727_is_usb_charger(struct lp8727_chg *pchg) { u8 val; - (void)lp8727_i2c_read_byte(pchg, STATUS1, &val); + lp8727_i2c_read_byte(pchg, STATUS1, &val); return (val & CHPORT); } static void lp8727_ctrl_switch(struct lp8727_chg *pchg, u8 sw) { u8 val = sw; - (void)lp8727_i2c_write_byte(pchg, SWCTRL, &val); + lp8727_i2c_write_byte(pchg, SWCTRL, &val); } static void lp8727_id_detection(struct lp8727_chg *pchg, u8 id, int vbusin) @@ -207,9 +207,9 @@ static void lp8727_enable_chgdet(struct lp8727_chg *pchg) { u8 val; - (void)lp8727_i2c_read_byte(pchg, CTRL2, &val); + lp8727_i2c_read_byte(pchg, CTRL2, &val); val |= CHGDET_EN; - (void)lp8727_i2c_write_byte(pchg, CTRL2, &val); + lp8727_i2c_write_byte(pchg, CTRL2, &val); } static void lp8727_delayed_func(struct work_struct *_work) @@ -283,10 +283,9 @@ static int lp8727_charger_get_property(struct power_supply *psy, { struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent); - if (psp == POWER_SUPPLY_PROP_ONLINE) { + if (psp == POWER_SUPPLY_PROP_ONLINE) val->intval = lp8727_is_charger_attached(psy->name, pchg->devid); - } return 0; } @@ -301,7 +300,7 @@ static int lp8727_battery_get_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_STATUS: if (lp8727_is_charger_attached(psy->name, pchg->devid)) { - (void)lp8727_i2c_read_byte(pchg, STATUS1, &read); + lp8727_i2c_read_byte(pchg, STATUS1, &read); if (((read & CHGSTAT) >> 4) == EOC) val->intval = POWER_SUPPLY_STATUS_FULL; else @@ -311,7 +310,7 @@ static int lp8727_battery_get_property(struct power_supply *psy, } break; case POWER_SUPPLY_PROP_HEALTH: - (void)lp8727_i2c_read_byte(pchg, STATUS2, &read); + lp8727_i2c_read_byte(pchg, STATUS2, &read); read = (read & TEMP_STAT) >> 5; if (read >= 0x1 && read <= 0x3) val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; @@ -352,7 +351,7 @@ static void lp8727_charger_changed(struct power_supply *psy) eoc_level = pchg->chg_parm->eoc_level; ichg = pchg->chg_parm->ichg; val = (ichg << 4) | eoc_level; - (void)lp8727_i2c_write_byte(pchg, CHGCTRL2, &val); + lp8727_i2c_write_byte(pchg, CHGCTRL2, &val); } } } @@ -412,12 +411,13 @@ static void lp8727_unregister_psy(struct lp8727_chg *pchg) { struct lp8727_psy *psy = pchg->psy; - if (psy) { - power_supply_unregister(&psy->ac); - power_supply_unregister(&psy->usb); - power_supply_unregister(&psy->batt); - kfree(psy); - } + if (!psy) + return; + + power_supply_unregister(&psy->ac); + power_supply_unregister(&psy->usb); + power_supply_unregister(&psy->batt); + kfree(psy); } static int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id) @@ -443,10 +443,9 @@ static int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id) lp8727_intr_config(pchg); ret = lp8727_register_psy(pchg); - if (ret) { + if (ret) dev_err(pchg->dev, "can not register power supplies. err=%d", ret); - } return 0; } @@ -481,14 +480,15 @@ static int __init lp8727_init(void) return i2c_add_driver(&lp8727_driver); } -static void __exit lp8727_chg_exit(void) +static void __exit lp8727_exit(void) { i2c_del_driver(&lp8727_driver); } module_init(lp8727_init); -module_exit(lp8727_chg_exit); +module_exit(lp8727_exit); MODULE_DESCRIPTION("National Semiconductor LP8727 charger driver"); -MODULE_AUTHOR("Woogyom Kim "); +MODULE_AUTHOR + ("Woogyom Kim , Daniel Jeong "); MODULE_LICENSE("GPL"); -- cgit v1.1 From 9b8872273af6983b246252a6508fa7cf34c69d6e Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Wed, 30 Nov 2011 23:08:33 -0800 Subject: power_supply: Add "unknown" in power supply type For the default value of power supply type, "unknown" is added. With default prop value, supply type property can be displayed as default - "Unknown". Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Anton Vorontsov --- drivers/power/power_supply_sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 58cc490..939e2e4 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -42,7 +42,7 @@ static ssize_t power_supply_show_property(struct device *dev, struct device_attribute *attr, char *buf) { static char *type_text[] = { - "Battery", "UPS", "Mains", "USB", + "Unknown", "Battery", "UPS", "Mains", "USB", "USB_DCP", "USB_CDP", "USB_ACA" }; static char *status_text[] = { -- cgit v1.1 From 300bac7fb85a20b2704dc3645419057992f78565 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 26 Nov 2011 12:01:10 +0800 Subject: power_supply: Convert drivers/power/* to use module_platform_driver() This patch converts the drivers in drivers/power/* to use the module_platform_driver() macro which makes the code smaller and a bit simpler. Cc: Mike Rapoport Cc: Lars-Peter Clausen Cc: Nithish Mahalingam Cc: MyungJoo Ham Cc: Haojian Zhuang Cc: Balaji Rao Cc: Mark Brown Signed-off-by: Axel Lin Acked-by: Clifton Barnes Signed-off-by: Anton Vorontsov --- drivers/power/da9030_battery.c | 13 +------------ drivers/power/ds2760_battery.c | 13 +------------ drivers/power/ds2780_battery.c | 16 ++-------------- drivers/power/gpio-charger.c | 12 +----------- drivers/power/intel_mid_battery.c | 13 +------------ drivers/power/isp1704_charger.c | 12 +----------- drivers/power/jz4740-battery.c | 12 +----------- drivers/power/max8903_charger.c | 12 +----------- drivers/power/max8925_power.c | 12 +----------- drivers/power/max8998_charger.c | 12 +----------- drivers/power/olpc_battery.c | 12 +----------- drivers/power/pcf50633-charger.c | 12 +----------- drivers/power/pda_power.c | 15 ++------------- drivers/power/s3c_adc_battery.c | 12 +----------- drivers/power/tosa_battery.c | 13 +------------ drivers/power/wm831x_backup.c | 12 +----------- drivers/power/wm831x_power.c | 12 +----------- drivers/power/wm8350_power.c | 12 +----------- drivers/power/wm97xx_battery.c | 13 +------------ 19 files changed, 21 insertions(+), 219 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/da9030_battery.c b/drivers/power/da9030_battery.c index d2c793cf..3fd3e95 100644 --- a/drivers/power/da9030_battery.c +++ b/drivers/power/da9030_battery.c @@ -588,18 +588,7 @@ static struct platform_driver da903x_battery_driver = { .remove = da9030_battery_remove, }; -static int da903x_battery_init(void) -{ - return platform_driver_register(&da903x_battery_driver); -} - -static void da903x_battery_exit(void) -{ - platform_driver_unregister(&da903x_battery_driver); -} - -module_init(da903x_battery_init); -module_exit(da903x_battery_exit); +module_platform_driver(da903x_battery_driver); MODULE_DESCRIPTION("DA9030 battery charger driver"); MODULE_AUTHOR("Mike Rapoport, CompuLab"); diff --git a/drivers/power/ds2760_battery.c b/drivers/power/ds2760_battery.c index cda03da..545874b 100644 --- a/drivers/power/ds2760_battery.c +++ b/drivers/power/ds2760_battery.c @@ -641,18 +641,7 @@ static struct platform_driver ds2760_battery_driver = { .resume = ds2760_battery_resume, }; -static int __init ds2760_battery_init(void) -{ - return platform_driver_register(&ds2760_battery_driver); -} - -static void __exit ds2760_battery_exit(void) -{ - platform_driver_unregister(&ds2760_battery_driver); -} - -module_init(ds2760_battery_init); -module_exit(ds2760_battery_exit); +module_platform_driver(ds2760_battery_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Szabolcs Gyurko , " diff --git a/drivers/power/ds2780_battery.c b/drivers/power/ds2780_battery.c index 887ec98..de31cae 100644 --- a/drivers/power/ds2780_battery.c +++ b/drivers/power/ds2780_battery.c @@ -841,8 +841,6 @@ static int __devexit ds2780_battery_remove(struct platform_device *pdev) return 0; } -MODULE_ALIAS("platform:ds2780-battery"); - static struct platform_driver ds2780_battery_driver = { .driver = { .name = "ds2780-battery", @@ -851,19 +849,9 @@ static struct platform_driver ds2780_battery_driver = { .remove = __devexit_p(ds2780_battery_remove), }; -static int __init ds2780_battery_init(void) -{ - return platform_driver_register(&ds2780_battery_driver); -} - -static void __exit ds2780_battery_exit(void) -{ - platform_driver_unregister(&ds2780_battery_driver); -} - -module_init(ds2780_battery_init); -module_exit(ds2780_battery_exit); +module_platform_driver(ds2780_battery_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Clifton Barnes "); MODULE_DESCRIPTION("Maxim/Dallas DS2780 Stand-Alone Fuel Gauage IC driver"); +MODULE_ALIAS("platform:ds2780-battery"); diff --git a/drivers/power/gpio-charger.c b/drivers/power/gpio-charger.c index a64b885..8672c91 100644 --- a/drivers/power/gpio-charger.c +++ b/drivers/power/gpio-charger.c @@ -185,17 +185,7 @@ static struct platform_driver gpio_charger_driver = { }, }; -static int __init gpio_charger_init(void) -{ - return platform_driver_register(&gpio_charger_driver); -} -module_init(gpio_charger_init); - -static void __exit gpio_charger_exit(void) -{ - platform_driver_unregister(&gpio_charger_driver); -} -module_exit(gpio_charger_exit); +module_platform_driver(gpio_charger_driver); MODULE_AUTHOR("Lars-Peter Clausen "); MODULE_DESCRIPTION("Driver for chargers which report their online status through a GPIO"); diff --git a/drivers/power/intel_mid_battery.c b/drivers/power/intel_mid_battery.c index 01fa671..d096497 100644 --- a/drivers/power/intel_mid_battery.c +++ b/drivers/power/intel_mid_battery.c @@ -779,18 +779,7 @@ static struct platform_driver platform_pmic_battery_driver = { .remove = __devexit_p(platform_pmic_battery_remove), }; -static int __init platform_pmic_battery_module_init(void) -{ - return platform_driver_register(&platform_pmic_battery_driver); -} - -static void __exit platform_pmic_battery_module_exit(void) -{ - platform_driver_unregister(&platform_pmic_battery_driver); -} - -module_init(platform_pmic_battery_module_init); -module_exit(platform_pmic_battery_module_exit); +module_platform_driver(platform_pmic_battery_driver); MODULE_AUTHOR("Nithish Mahalingam "); MODULE_DESCRIPTION("Intel Moorestown PMIC Battery Driver"); diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c index f6d72b4..6a4ef38 100644 --- a/drivers/power/isp1704_charger.c +++ b/drivers/power/isp1704_charger.c @@ -494,17 +494,7 @@ static struct platform_driver isp1704_charger_driver = { .remove = __devexit_p(isp1704_charger_remove), }; -static int __init isp1704_charger_init(void) -{ - return platform_driver_register(&isp1704_charger_driver); -} -module_init(isp1704_charger_init); - -static void __exit isp1704_charger_exit(void) -{ - platform_driver_unregister(&isp1704_charger_driver); -} -module_exit(isp1704_charger_exit); +module_platform_driver(isp1704_charger_driver); MODULE_ALIAS("platform:isp1704_charger"); MODULE_AUTHOR("Nokia Corporation"); diff --git a/drivers/power/jz4740-battery.c b/drivers/power/jz4740-battery.c index 763f894..6b77029 100644 --- a/drivers/power/jz4740-battery.c +++ b/drivers/power/jz4740-battery.c @@ -441,17 +441,7 @@ static struct platform_driver jz_battery_driver = { }, }; -static int __init jz_battery_init(void) -{ - return platform_driver_register(&jz_battery_driver); -} -module_init(jz_battery_init); - -static void __exit jz_battery_exit(void) -{ - platform_driver_unregister(&jz_battery_driver); -} -module_exit(jz_battery_exit); +module_platform_driver(jz_battery_driver); MODULE_ALIAS("platform:jz4740-battery"); MODULE_LICENSE("GPL"); diff --git a/drivers/power/max8903_charger.c b/drivers/power/max8903_charger.c index f204ad1..3e23f43 100644 --- a/drivers/power/max8903_charger.c +++ b/drivers/power/max8903_charger.c @@ -374,17 +374,7 @@ static struct platform_driver max8903_driver = { }, }; -static int __init max8903_init(void) -{ - return platform_driver_register(&max8903_driver); -} -module_init(max8903_init); - -static void __exit max8903_exit(void) -{ - platform_driver_unregister(&max8903_driver); -} -module_exit(max8903_exit); +module_platform_driver(max8903_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("MAX8903 Charger Driver"); diff --git a/drivers/power/max8925_power.c b/drivers/power/max8925_power.c index 8155250..daa333b 100644 --- a/drivers/power/max8925_power.c +++ b/drivers/power/max8925_power.c @@ -523,17 +523,7 @@ static struct platform_driver max8925_power_driver = { }, }; -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_platform_driver(max8925_power_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Power supply driver for MAX8925"); diff --git a/drivers/power/max8998_charger.c b/drivers/power/max8998_charger.c index 0737302..9b3f2bf5 100644 --- a/drivers/power/max8998_charger.c +++ b/drivers/power/max8998_charger.c @@ -204,17 +204,7 @@ static struct platform_driver max8998_battery_driver = { .id_table = max8998_battery_id, }; -static int __init max8998_battery_init(void) -{ - return platform_driver_register(&max8998_battery_driver); -} -module_init(max8998_battery_init); - -static void __exit max8998_battery_cleanup(void) -{ - platform_driver_unregister(&max8998_battery_driver); -} -module_exit(max8998_battery_cleanup); +module_platform_driver(max8998_battery_driver); MODULE_DESCRIPTION("MAXIM 8998 battery control driver"); MODULE_AUTHOR("MyungJoo Ham "); diff --git a/drivers/power/olpc_battery.c b/drivers/power/olpc_battery.c index 21e7c06..7385092 100644 --- a/drivers/power/olpc_battery.c +++ b/drivers/power/olpc_battery.c @@ -630,17 +630,7 @@ static struct platform_driver olpc_battery_driver = { .suspend = olpc_battery_suspend, }; -static int __init olpc_bat_init(void) -{ - return platform_driver_register(&olpc_battery_driver); -} -module_init(olpc_bat_init); - -static void __exit olpc_bat_exit(void) -{ - platform_driver_unregister(&olpc_battery_driver); -} -module_exit(olpc_bat_exit); +module_platform_driver(olpc_battery_driver); MODULE_AUTHOR("David Woodhouse "); MODULE_LICENSE("GPL"); diff --git a/drivers/power/pcf50633-charger.c b/drivers/power/pcf50633-charger.c index 4fa52e1..3d1e9ef 100644 --- a/drivers/power/pcf50633-charger.c +++ b/drivers/power/pcf50633-charger.c @@ -474,17 +474,7 @@ static struct platform_driver pcf50633_mbc_driver = { .remove = __devexit_p(pcf50633_mbc_remove), }; -static int __init pcf50633_mbc_init(void) -{ - return platform_driver_register(&pcf50633_mbc_driver); -} -module_init(pcf50633_mbc_init); - -static void __exit pcf50633_mbc_exit(void) -{ - platform_driver_unregister(&pcf50633_mbc_driver); -} -module_exit(pcf50633_mbc_exit); +module_platform_driver(pcf50633_mbc_driver); MODULE_AUTHOR("Balaji Rao "); MODULE_DESCRIPTION("PCF50633 mbc driver"); diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c index 87be679..fd49689 100644 --- a/drivers/power/pda_power.c +++ b/drivers/power/pda_power.c @@ -498,8 +498,6 @@ static int pda_power_resume(struct platform_device *pdev) #define pda_power_resume NULL #endif /* CONFIG_PM */ -MODULE_ALIAS("platform:pda-power"); - static struct platform_driver pda_power_pdrv = { .driver = { .name = "pda-power", @@ -510,17 +508,8 @@ static struct platform_driver pda_power_pdrv = { .resume = pda_power_resume, }; -static int __init pda_power_init(void) -{ - return platform_driver_register(&pda_power_pdrv); -} - -static void __exit pda_power_exit(void) -{ - platform_driver_unregister(&pda_power_pdrv); -} +module_platform_driver(pda_power_pdrv); -module_init(pda_power_init); -module_exit(pda_power_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Anton Vorontsov "); +MODULE_ALIAS("platform:pda-power"); diff --git a/drivers/power/s3c_adc_battery.c b/drivers/power/s3c_adc_battery.c index d32d0d7..e687ee7 100644 --- a/drivers/power/s3c_adc_battery.c +++ b/drivers/power/s3c_adc_battery.c @@ -421,17 +421,7 @@ static struct platform_driver s3c_adc_bat_driver = { .resume = s3c_adc_bat_resume, }; -static int __init s3c_adc_bat_init(void) -{ - return platform_driver_register(&s3c_adc_bat_driver); -} -module_init(s3c_adc_bat_init); - -static void __exit s3c_adc_bat_exit(void) -{ - platform_driver_unregister(&s3c_adc_bat_driver); -} -module_exit(s3c_adc_bat_exit); +module_platform_driver(s3c_adc_bat_driver); MODULE_AUTHOR("Vasily Khoruzhick "); MODULE_DESCRIPTION("iPAQ H1930/H1940/RX1950 battery controller driver"); diff --git a/drivers/power/tosa_battery.c b/drivers/power/tosa_battery.c index ada86a4..28bbe7e 100644 --- a/drivers/power/tosa_battery.c +++ b/drivers/power/tosa_battery.c @@ -438,18 +438,7 @@ static struct platform_driver tosa_bat_driver = { .resume = tosa_bat_resume, }; -static int __init tosa_bat_init(void) -{ - return platform_driver_register(&tosa_bat_driver); -} - -static void __exit tosa_bat_exit(void) -{ - platform_driver_unregister(&tosa_bat_driver); -} - -module_init(tosa_bat_init); -module_exit(tosa_bat_exit); +module_platform_driver(tosa_bat_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Dmitry Baryshkov"); diff --git a/drivers/power/wm831x_backup.c b/drivers/power/wm831x_backup.c index e648cbe..6243e69 100644 --- a/drivers/power/wm831x_backup.c +++ b/drivers/power/wm831x_backup.c @@ -226,17 +226,7 @@ static struct platform_driver wm831x_backup_driver = { }, }; -static int __init wm831x_backup_init(void) -{ - return platform_driver_register(&wm831x_backup_driver); -} -module_init(wm831x_backup_init); - -static void __exit wm831x_backup_exit(void) -{ - platform_driver_unregister(&wm831x_backup_driver); -} -module_exit(wm831x_backup_exit); +module_platform_driver(wm831x_backup_driver); MODULE_DESCRIPTION("Backup battery charger driver for WM831x PMICs"); MODULE_AUTHOR("Mark Brown "); diff --git a/drivers/power/wm831x_power.c b/drivers/power/wm831x_power.c index c32e6f8..987332b 100644 --- a/drivers/power/wm831x_power.c +++ b/drivers/power/wm831x_power.c @@ -655,17 +655,7 @@ static struct platform_driver wm831x_power_driver = { }, }; -static int __init wm831x_power_init(void) -{ - return platform_driver_register(&wm831x_power_driver); -} -module_init(wm831x_power_init); - -static void __exit wm831x_power_exit(void) -{ - platform_driver_unregister(&wm831x_power_driver); -} -module_exit(wm831x_power_exit); +module_platform_driver(wm831x_power_driver); MODULE_DESCRIPTION("Power supply driver for WM831x PMICs"); MODULE_AUTHOR("Mark Brown "); diff --git a/drivers/power/wm8350_power.c b/drivers/power/wm8350_power.c index 0693902..fae04d3 100644 --- a/drivers/power/wm8350_power.c +++ b/drivers/power/wm8350_power.c @@ -522,17 +522,7 @@ static struct platform_driver wm8350_power_driver = { }, }; -static int __init wm8350_power_init(void) -{ - return platform_driver_register(&wm8350_power_driver); -} -module_init(wm8350_power_init); - -static void __exit wm8350_power_exit(void) -{ - platform_driver_unregister(&wm8350_power_driver); -} -module_exit(wm8350_power_exit); +module_platform_driver(wm8350_power_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Power supply driver for WM8350"); diff --git a/drivers/power/wm97xx_battery.c b/drivers/power/wm97xx_battery.c index cf8681c1..199818f 100644 --- a/drivers/power/wm97xx_battery.c +++ b/drivers/power/wm97xx_battery.c @@ -291,18 +291,7 @@ static struct platform_driver wm97xx_bat_driver = { .remove = __devexit_p(wm97xx_bat_remove), }; -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); -} - -module_init(wm97xx_bat_init); -module_exit(wm97xx_bat_exit); +module_platform_driver(wm97xx_bat_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Marek Vasut "); -- cgit v1.1 From 68597986f7db9abbe380af3db6a34b6c83cdf302 Mon Sep 17 00:00:00 2001 From: Jonghwan Choi Date: Wed, 23 Nov 2011 13:40:05 +0900 Subject: max8997_charger: Remove duplicate module.h module.h was included twice. Signed-off-by: Jonghwan Choi Acked-by: MyungJoo Ham Signed-off-by: Anton Vorontsov --- drivers/power/max8997_charger.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/max8997_charger.c b/drivers/power/max8997_charger.c index 23ca65d..6e88c5d 100644 --- a/drivers/power/max8997_charger.c +++ b/drivers/power/max8997_charger.c @@ -19,7 +19,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include #include #include #include -- cgit v1.1 From daf22c3c4444e10a852a2f460f5cb1a43038bd53 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 28 Nov 2011 22:37:35 +0800 Subject: wm97xx_battery: Use DEFINE_MUTEX() for work_lock work_lock can be initialized automatically with DEFINE_MUTEX() rather than explicitly calling mutex_init(). This patch also removes an unused bat_lock mutex. Signed-off-by: Axel Lin Signed-off-by: Anton Vorontsov --- drivers/power/wm97xx_battery.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/wm97xx_battery.c b/drivers/power/wm97xx_battery.c index 199818f..d2d4c08 100644 --- a/drivers/power/wm97xx_battery.c +++ b/drivers/power/wm97xx_battery.c @@ -25,9 +25,8 @@ #include #include -static DEFINE_MUTEX(bat_lock); static struct work_struct bat_work; -static struct mutex work_lock; +static DEFINE_MUTEX(work_lock); static int bat_status = POWER_SUPPLY_STATUS_UNKNOWN; static enum power_supply_property *prop; @@ -181,8 +180,6 @@ static int __devinit wm97xx_bat_probe(struct platform_device *dev) if (dev->id != -1) return -EINVAL; - mutex_init(&work_lock); - if (!pdata) { dev_err(&dev->dev, "No platform_data supplied\n"); return -EINVAL; -- cgit v1.1 From c78f2b64963654419a8cd3b7e264251860e9f9eb Mon Sep 17 00:00:00 2001 From: Rhyland Klein Date: Mon, 5 Dec 2011 17:50:45 -0800 Subject: bq20z75: Rename to sbs-battery This driver for the bq20z75 implemented the register spec defined by the SBS standard. As this is not unique to this the TI part this was originally written for, we can generalize this driver to show its support for any SBS compliant battery. Signed-off-by: Rhyland Klein Signed-off-by: Anton Vorontsov --- drivers/power/Kconfig | 8 +- drivers/power/Makefile | 2 +- drivers/power/bq20z75.c | 871 -------------------------------------------- drivers/power/sbs-battery.c | 871 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 876 insertions(+), 876 deletions(-) delete mode 100644 drivers/power/bq20z75.c create mode 100644 drivers/power/sbs-battery.c (limited to 'drivers/power') diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 3bd2ed8..e24485f 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -116,12 +116,12 @@ config BATTERY_WM97XX help Say Y to enable support for battery measured by WM97xx aux port. -config BATTERY_BQ20Z75 - tristate "TI BQ20z75 gas gauge" +config BATTERY_SBS + tristate "SBS Compliant gas gauge" depends on I2C help - Say Y to include support for TI BQ20z75 SBS-compliant - gas gauge and protection IC. + Say Y to include support for SBS battery driver for SBS-compliant + gas gauges. config BATTERY_BQ27x00 tristate "BQ27x00 battery driver" diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 9a78b1d..9c3bbf7 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -22,7 +22,7 @@ obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o obj-$(CONFIG_BATTERY_COLLIE) += collie_battery.o obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o -obj-$(CONFIG_BATTERY_BQ20Z75) += bq20z75.o +obj-$(CONFIG_BATTERY_SBS) += sbs-battery.o obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o diff --git a/drivers/power/bq20z75.c b/drivers/power/bq20z75.c deleted file mode 100644 index ce95ff7..0000000 --- a/drivers/power/bq20z75.c +++ /dev/null @@ -1,871 +0,0 @@ -/* - * Gas Gauge driver for TI's BQ20Z75 - * - * Copyright (c) 2010, NVIDIA Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -enum { - REG_MANUFACTURER_DATA, - REG_TEMPERATURE, - REG_VOLTAGE, - REG_CURRENT, - REG_CAPACITY, - REG_TIME_TO_EMPTY, - REG_TIME_TO_FULL, - REG_STATUS, - 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 - -/* battery status value bits */ -#define BATTERY_DISCHARGING 0x40 -#define BATTERY_FULL_CHARGED 0x20 -#define BATTERY_FULL_DISCHARGED 0x10 - -#define BQ20Z75_DATA(_psp, _addr, _min_value, _max_value) { \ - .psp = _psp, \ - .addr = _addr, \ - .min_value = _min_value, \ - .max_value = _max_value, \ -} - -static const struct bq20z75_device_data { - enum power_supply_property psp; - u8 addr; - int min_value; - int max_value; -} bq20z75_data[] = { - [REG_MANUFACTURER_DATA] = - BQ20Z75_DATA(POWER_SUPPLY_PROP_PRESENT, 0x00, 0, 65535), - [REG_TEMPERATURE] = - BQ20Z75_DATA(POWER_SUPPLY_PROP_TEMP, 0x08, 0, 65535), - [REG_VOLTAGE] = - BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 20000), - [REG_CURRENT] = - BQ20Z75_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768, - 32767), - [REG_CAPACITY] = - 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), - [REG_TIME_TO_FULL] = - BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, 0x13, 0, - 65535), - [REG_STATUS] = - BQ20Z75_DATA(POWER_SUPPLY_PROP_STATUS, 0x16, 0, 65535), - [REG_CYCLE_COUNT] = - BQ20Z75_DATA(POWER_SUPPLY_PROP_CYCLE_COUNT, 0x17, 0, 65535), - [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), - [REG_SERIAL_NUMBER] = - BQ20Z75_DATA(POWER_SUPPLY_PROP_SERIAL_NUMBER, 0x1C, 0, 65535), -}; - -static enum power_supply_property bq20z75_properties[] = { - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_HEALTH, - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_CYCLE_COUNT, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_CURRENT_NOW, - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, - POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, - POWER_SUPPLY_PROP_SERIAL_NUMBER, - POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, - 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 { - 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; - int last_state; - int poll_time; - struct delayed_work work; - int ignore_changes; -}; - -static int bq20z75_read_word_data(struct i2c_client *client, u8 address) -{ - 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--; - } - - if (ret < 0) { - dev_dbg(&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) -{ - 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--; - } - - if (ret < 0) { - dev_dbg(&client->dev, - "%s: i2c write to address 0x%x failed\n", - __func__, address); - return ret; - } - - return 0; -} - -static int bq20z75_get_battery_presence_and_health( - struct i2c_client *client, enum power_supply_property psp, - 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; - bq20z75_device->is_present = val->intval; - return ret; - } - - /* Write to ManufacturerAccess with - * ManufacturerAccess command and then - * read the status */ - ret = bq20z75_write_word_data(client, - bq20z75_data[REG_MANUFACTURER_DATA].addr, - MANUFACTURER_ACCESS_STATUS); - 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; - return 0; - } - - /* Mask the upper nibble of 2nd byte and - * lower byte of response then - * shift the result by 8 to get status*/ - ret &= 0x0F00; - ret >>= 8; - if (psp == POWER_SUPPLY_PROP_PRESENT) { - if (ret == 0x0F) - /* battery removed */ - val->intval = 0; - else - val->intval = 1; - } else if (psp == POWER_SUPPLY_PROP_HEALTH) { - if (ret == 0x09) - val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; - else if (ret == 0x0B) - val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; - else if (ret == 0x0C) - val->intval = POWER_SUPPLY_HEALTH_DEAD; - else - val->intval = POWER_SUPPLY_HEALTH_GOOD; - } - - return 0; -} - -static int bq20z75_get_battery_property(struct i2c_client *client, - int reg_offset, enum power_supply_property psp, - union power_supply_propval *val) -{ - struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); - s32 ret; - - ret = bq20z75_read_word_data(client, - bq20z75_data[reg_offset].addr); - if (ret < 0) - return ret; - - /* returned values are 16 bit */ - if (bq20z75_data[reg_offset].min_value < 0) - ret = (s16)ret; - - if (ret >= bq20z75_data[reg_offset].min_value && - ret <= bq20z75_data[reg_offset].max_value) { - val->intval = ret; - if (psp != POWER_SUPPLY_PROP_STATUS) - return 0; - - if (ret & BATTERY_FULL_CHARGED) - val->intval = POWER_SUPPLY_STATUS_FULL; - else if (ret & BATTERY_FULL_DISCHARGED) - val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; - else if (ret & BATTERY_DISCHARGING) - val->intval = POWER_SUPPLY_STATUS_DISCHARGING; - else - val->intval = POWER_SUPPLY_STATUS_CHARGING; - - if (bq20z75_device->poll_time == 0) - bq20z75_device->last_state = val->intval; - else if (bq20z75_device->last_state != val->intval) { - cancel_delayed_work_sync(&bq20z75_device->work); - power_supply_changed(&bq20z75_device->power_supply); - bq20z75_device->poll_time = 0; - } - } else { - if (psp == POWER_SUPPLY_PROP_STATUS) - val->intval = POWER_SUPPLY_STATUS_UNKNOWN; - else - val->intval = 0; - } - - return 0; -} - -static void bq20z75_unit_adjustment(struct i2c_client *client, - enum power_supply_property psp, union power_supply_propval *val) -{ -#define BASE_UNIT_CONVERSION 1000 -#define BATTERY_MODE_CAP_MULT_WATT (10 * BASE_UNIT_CONVERSION) -#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; - - 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; - - case POWER_SUPPLY_PROP_TEMP: - /* 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; - - default: - dev_dbg(&client->dev, - "%s: no need for unit conversion %d\n", __func__, psp); - } -} - -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) - return ret; - - if (psp == POWER_SUPPLY_PROP_CAPACITY) { - /* bq20z75 spec says that this can be >100 % - * even if max value is 100 % */ - val->intval = min(ret, 100); - } else - val->intval = ret; - - ret = bq20z75_set_battery_mode(client, mode); - if (ret < 0) - return ret; - - return 0; -} - -static char bq20z75_serial[5]; -static int bq20z75_get_battery_serial_number(struct i2c_client *client, - union power_supply_propval *val) -{ - int ret; - - ret = bq20z75_read_word_data(client, - bq20z75_data[REG_SERIAL_NUMBER].addr); - if (ret < 0) - return ret; - - ret = sprintf(bq20z75_serial, "%04x", ret); - val->strval = bq20z75_serial; - - 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 ret = 0; - struct bq20z75_info *bq20z75_device = container_of(psy, - struct bq20z75_info, power_supply); - struct i2c_client *client = bq20z75_device->client; - - switch (psp) { - 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: - val->intval = POWER_SUPPLY_TECHNOLOGY_LION; - break; - - 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: - 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); - break; - - case POWER_SUPPLY_PROP_STATUS: - case POWER_SUPPLY_PROP_CYCLE_COUNT: - case POWER_SUPPLY_PROP_VOLTAGE_NOW: - case POWER_SUPPLY_PROP_CURRENT_NOW: - case POWER_SUPPLY_PROP_TEMP: - case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: - case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: - case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: - ret = bq20z75_get_property_index(client, psp); - if (ret < 0) - break; - - ret = bq20z75_get_battery_property(client, ret, psp, val); - break; - - default: - dev_err(&client->dev, - "%s: INVALID property\n", __func__); - return -EINVAL; - } - - 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 = %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 0; -} - -static irqreturn_t bq20z75_irq(int irq, void *devid) -{ - struct power_supply *battery = devid; - - power_supply_changed(battery); - - return IRQ_HANDLED; -} - -static void bq20z75_external_power_changed(struct power_supply *psy) -{ - struct bq20z75_info *bq20z75_device; - - bq20z75_device = container_of(psy, struct bq20z75_info, power_supply); - - if (bq20z75_device->ignore_changes > 0) { - bq20z75_device->ignore_changes--; - return; - } - - /* cancel outstanding work */ - cancel_delayed_work_sync(&bq20z75_device->work); - - schedule_delayed_work(&bq20z75_device->work, HZ); - bq20z75_device->poll_time = bq20z75_device->pdata->poll_retry_count; -} - -static void bq20z75_delayed_work(struct work_struct *work) -{ - struct bq20z75_info *bq20z75_device; - s32 ret; - - bq20z75_device = container_of(work, struct bq20z75_info, work.work); - - ret = bq20z75_read_word_data(bq20z75_device->client, - bq20z75_data[REG_STATUS].addr); - /* if the read failed, give up on this work */ - if (ret < 0) { - bq20z75_device->poll_time = 0; - return; - } - - if (ret & BATTERY_FULL_CHARGED) - ret = POWER_SUPPLY_STATUS_FULL; - else if (ret & BATTERY_FULL_DISCHARGED) - ret = POWER_SUPPLY_STATUS_NOT_CHARGING; - else if (ret & BATTERY_DISCHARGING) - ret = POWER_SUPPLY_STATUS_DISCHARGING; - else - ret = POWER_SUPPLY_STATUS_CHARGING; - - if (bq20z75_device->last_state != ret) { - bq20z75_device->poll_time = 0; - power_supply_changed(&bq20z75_device->power_supply); - return; - } - if (bq20z75_device->poll_time > 0) { - schedule_delayed_work(&bq20z75_device->work, HZ); - bq20z75_device->poll_time--; - return; - } -} - -#if defined(CONFIG_OF) - -#include -#include - -static const struct of_device_id bq20z75_dt_ids[] = { - { .compatible = "ti,bq20z75" }, - { } -}; -MODULE_DEVICE_TABLE(i2c, bq20z75_dt_ids); - -static struct bq20z75_platform_data *bq20z75_of_populate_pdata( - struct i2c_client *client) -{ - struct device_node *of_node = client->dev.of_node; - struct bq20z75_platform_data *pdata = client->dev.platform_data; - enum of_gpio_flags gpio_flags; - int rc; - u32 prop; - - /* verify this driver matches this device */ - if (!of_node) - return NULL; - - /* if platform data is set, honor it */ - if (pdata) - return pdata; - - /* first make sure at least one property is set, otherwise - * it won't change behavior from running without pdata. - */ - if (!of_get_property(of_node, "ti,i2c-retry-count", NULL) && - !of_get_property(of_node, "ti,poll-retry-count", NULL) && - !of_get_property(of_node, "ti,battery-detect-gpios", NULL)) - goto of_out; - - pdata = devm_kzalloc(&client->dev, sizeof(struct bq20z75_platform_data), - GFP_KERNEL); - if (!pdata) - goto of_out; - - rc = of_property_read_u32(of_node, "ti,i2c-retry-count", &prop); - if (!rc) - pdata->i2c_retry_count = prop; - - rc = of_property_read_u32(of_node, "ti,poll-retry-count", &prop); - if (!rc) - pdata->poll_retry_count = prop; - - if (!of_get_property(of_node, "ti,battery-detect-gpios", NULL)) { - pdata->battery_detect = -1; - goto of_out; - } - - pdata->battery_detect = of_get_named_gpio_flags(of_node, - "ti,battery-detect-gpios", 0, &gpio_flags); - - if (gpio_flags & OF_GPIO_ACTIVE_LOW) - pdata->battery_detect_present = 0; - else - pdata->battery_detect_present = 1; - -of_out: - return pdata; -} -#else -#define bq20z75_dt_ids NULL -static struct bq20z75_platform_data *bq20z75_of_populate_pdata( - struct i2c_client *client) -{ - return client->dev.platform_data; -} -#endif - -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; - bq20z75_device->power_supply.num_properties = - ARRAY_SIZE(bq20z75_properties); - bq20z75_device->power_supply.get_property = bq20z75_get_property; - /* ignore first notification of external change, it is generated - * from the power_supply_register call back - */ - bq20z75_device->ignore_changes = 1; - bq20z75_device->last_state = POWER_SUPPLY_STATUS_UNKNOWN; - bq20z75_device->power_supply.external_power_changed = - bq20z75_external_power_changed; - - pdata = bq20z75_of_populate_pdata(client); - - 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__); - goto exit_psupply; - } - - dev_info(&client->dev, - "%s: battery gas gauge device registered\n", client->name); - - INIT_DELAYED_WORK(&bq20z75_device->work, bq20z75_delayed_work); - - bq20z75_device->enable_detection = true; - - 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 __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); - - cancel_delayed_work_sync(&bq20z75_device->work); - - kfree(bq20z75_device); - bq20z75_device = NULL; - - return 0; -} - -#if defined CONFIG_PM -static int bq20z75_suspend(struct i2c_client *client, - pm_message_t state) -{ - struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); - s32 ret; - - if (bq20z75_device->poll_time > 0) - cancel_delayed_work_sync(&bq20z75_device->work); - - /* write to manufacturer access with sleep command */ - ret = bq20z75_write_word_data(client, - bq20z75_data[REG_MANUFACTURER_DATA].addr, - MANUFACTURER_ACCESS_SLEEP); - if (bq20z75_device->is_present && ret < 0) - return ret; - - return 0; -} -#else -#define bq20z75_suspend NULL -#endif -/* any smbus transaction will wake up bq20z75 */ -#define bq20z75_resume NULL - -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, - .remove = __devexit_p(bq20z75_remove), - .suspend = bq20z75_suspend, - .resume = bq20z75_resume, - .id_table = bq20z75_id, - .driver = { - .name = "bq20z75-battery", - .of_match_table = bq20z75_dt_ids, - }, -}; - -static int __init bq20z75_battery_init(void) -{ - return i2c_add_driver(&bq20z75_battery_driver); -} -module_init(bq20z75_battery_init); - -static void __exit bq20z75_battery_exit(void) -{ - i2c_del_driver(&bq20z75_battery_driver); -} -module_exit(bq20z75_battery_exit); - -MODULE_DESCRIPTION("BQ20z75 battery monitor driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/power/sbs-battery.c b/drivers/power/sbs-battery.c new file mode 100644 index 0000000..ce95ff7 --- /dev/null +++ b/drivers/power/sbs-battery.c @@ -0,0 +1,871 @@ +/* + * Gas Gauge driver for TI's BQ20Z75 + * + * Copyright (c) 2010, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +enum { + REG_MANUFACTURER_DATA, + REG_TEMPERATURE, + REG_VOLTAGE, + REG_CURRENT, + REG_CAPACITY, + REG_TIME_TO_EMPTY, + REG_TIME_TO_FULL, + REG_STATUS, + 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 + +/* battery status value bits */ +#define BATTERY_DISCHARGING 0x40 +#define BATTERY_FULL_CHARGED 0x20 +#define BATTERY_FULL_DISCHARGED 0x10 + +#define BQ20Z75_DATA(_psp, _addr, _min_value, _max_value) { \ + .psp = _psp, \ + .addr = _addr, \ + .min_value = _min_value, \ + .max_value = _max_value, \ +} + +static const struct bq20z75_device_data { + enum power_supply_property psp; + u8 addr; + int min_value; + int max_value; +} bq20z75_data[] = { + [REG_MANUFACTURER_DATA] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_PRESENT, 0x00, 0, 65535), + [REG_TEMPERATURE] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_TEMP, 0x08, 0, 65535), + [REG_VOLTAGE] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 20000), + [REG_CURRENT] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768, + 32767), + [REG_CAPACITY] = + 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), + [REG_TIME_TO_FULL] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, 0x13, 0, + 65535), + [REG_STATUS] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_STATUS, 0x16, 0, 65535), + [REG_CYCLE_COUNT] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_CYCLE_COUNT, 0x17, 0, 65535), + [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), + [REG_SERIAL_NUMBER] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_SERIAL_NUMBER, 0x1C, 0, 65535), +}; + +static enum power_supply_property bq20z75_properties[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, + POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, + POWER_SUPPLY_PROP_SERIAL_NUMBER, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + 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 { + 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; + int last_state; + int poll_time; + struct delayed_work work; + int ignore_changes; +}; + +static int bq20z75_read_word_data(struct i2c_client *client, u8 address) +{ + 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--; + } + + if (ret < 0) { + dev_dbg(&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) +{ + 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--; + } + + if (ret < 0) { + dev_dbg(&client->dev, + "%s: i2c write to address 0x%x failed\n", + __func__, address); + return ret; + } + + return 0; +} + +static int bq20z75_get_battery_presence_and_health( + struct i2c_client *client, enum power_supply_property psp, + 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; + bq20z75_device->is_present = val->intval; + return ret; + } + + /* Write to ManufacturerAccess with + * ManufacturerAccess command and then + * read the status */ + ret = bq20z75_write_word_data(client, + bq20z75_data[REG_MANUFACTURER_DATA].addr, + MANUFACTURER_ACCESS_STATUS); + 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; + return 0; + } + + /* Mask the upper nibble of 2nd byte and + * lower byte of response then + * shift the result by 8 to get status*/ + ret &= 0x0F00; + ret >>= 8; + if (psp == POWER_SUPPLY_PROP_PRESENT) { + if (ret == 0x0F) + /* battery removed */ + val->intval = 0; + else + val->intval = 1; + } else if (psp == POWER_SUPPLY_PROP_HEALTH) { + if (ret == 0x09) + val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + else if (ret == 0x0B) + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + else if (ret == 0x0C) + val->intval = POWER_SUPPLY_HEALTH_DEAD; + else + val->intval = POWER_SUPPLY_HEALTH_GOOD; + } + + return 0; +} + +static int bq20z75_get_battery_property(struct i2c_client *client, + int reg_offset, enum power_supply_property psp, + union power_supply_propval *val) +{ + struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); + s32 ret; + + ret = bq20z75_read_word_data(client, + bq20z75_data[reg_offset].addr); + if (ret < 0) + return ret; + + /* returned values are 16 bit */ + if (bq20z75_data[reg_offset].min_value < 0) + ret = (s16)ret; + + if (ret >= bq20z75_data[reg_offset].min_value && + ret <= bq20z75_data[reg_offset].max_value) { + val->intval = ret; + if (psp != POWER_SUPPLY_PROP_STATUS) + return 0; + + if (ret & BATTERY_FULL_CHARGED) + val->intval = POWER_SUPPLY_STATUS_FULL; + else if (ret & BATTERY_FULL_DISCHARGED) + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + else if (ret & BATTERY_DISCHARGING) + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + else + val->intval = POWER_SUPPLY_STATUS_CHARGING; + + if (bq20z75_device->poll_time == 0) + bq20z75_device->last_state = val->intval; + else if (bq20z75_device->last_state != val->intval) { + cancel_delayed_work_sync(&bq20z75_device->work); + power_supply_changed(&bq20z75_device->power_supply); + bq20z75_device->poll_time = 0; + } + } else { + if (psp == POWER_SUPPLY_PROP_STATUS) + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + else + val->intval = 0; + } + + return 0; +} + +static void bq20z75_unit_adjustment(struct i2c_client *client, + enum power_supply_property psp, union power_supply_propval *val) +{ +#define BASE_UNIT_CONVERSION 1000 +#define BATTERY_MODE_CAP_MULT_WATT (10 * BASE_UNIT_CONVERSION) +#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; + + 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; + + case POWER_SUPPLY_PROP_TEMP: + /* 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; + + default: + dev_dbg(&client->dev, + "%s: no need for unit conversion %d\n", __func__, psp); + } +} + +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) + return ret; + + if (psp == POWER_SUPPLY_PROP_CAPACITY) { + /* bq20z75 spec says that this can be >100 % + * even if max value is 100 % */ + val->intval = min(ret, 100); + } else + val->intval = ret; + + ret = bq20z75_set_battery_mode(client, mode); + if (ret < 0) + return ret; + + return 0; +} + +static char bq20z75_serial[5]; +static int bq20z75_get_battery_serial_number(struct i2c_client *client, + union power_supply_propval *val) +{ + int ret; + + ret = bq20z75_read_word_data(client, + bq20z75_data[REG_SERIAL_NUMBER].addr); + if (ret < 0) + return ret; + + ret = sprintf(bq20z75_serial, "%04x", ret); + val->strval = bq20z75_serial; + + 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 ret = 0; + struct bq20z75_info *bq20z75_device = container_of(psy, + struct bq20z75_info, power_supply); + struct i2c_client *client = bq20z75_device->client; + + switch (psp) { + 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: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + + 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: + 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); + break; + + case POWER_SUPPLY_PROP_STATUS: + case POWER_SUPPLY_PROP_CYCLE_COUNT: + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + case POWER_SUPPLY_PROP_CURRENT_NOW: + case POWER_SUPPLY_PROP_TEMP: + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: + case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + ret = bq20z75_get_property_index(client, psp); + if (ret < 0) + break; + + ret = bq20z75_get_battery_property(client, ret, psp, val); + break; + + default: + dev_err(&client->dev, + "%s: INVALID property\n", __func__); + return -EINVAL; + } + + 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 = %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 0; +} + +static irqreturn_t bq20z75_irq(int irq, void *devid) +{ + struct power_supply *battery = devid; + + power_supply_changed(battery); + + return IRQ_HANDLED; +} + +static void bq20z75_external_power_changed(struct power_supply *psy) +{ + struct bq20z75_info *bq20z75_device; + + bq20z75_device = container_of(psy, struct bq20z75_info, power_supply); + + if (bq20z75_device->ignore_changes > 0) { + bq20z75_device->ignore_changes--; + return; + } + + /* cancel outstanding work */ + cancel_delayed_work_sync(&bq20z75_device->work); + + schedule_delayed_work(&bq20z75_device->work, HZ); + bq20z75_device->poll_time = bq20z75_device->pdata->poll_retry_count; +} + +static void bq20z75_delayed_work(struct work_struct *work) +{ + struct bq20z75_info *bq20z75_device; + s32 ret; + + bq20z75_device = container_of(work, struct bq20z75_info, work.work); + + ret = bq20z75_read_word_data(bq20z75_device->client, + bq20z75_data[REG_STATUS].addr); + /* if the read failed, give up on this work */ + if (ret < 0) { + bq20z75_device->poll_time = 0; + return; + } + + if (ret & BATTERY_FULL_CHARGED) + ret = POWER_SUPPLY_STATUS_FULL; + else if (ret & BATTERY_FULL_DISCHARGED) + ret = POWER_SUPPLY_STATUS_NOT_CHARGING; + else if (ret & BATTERY_DISCHARGING) + ret = POWER_SUPPLY_STATUS_DISCHARGING; + else + ret = POWER_SUPPLY_STATUS_CHARGING; + + if (bq20z75_device->last_state != ret) { + bq20z75_device->poll_time = 0; + power_supply_changed(&bq20z75_device->power_supply); + return; + } + if (bq20z75_device->poll_time > 0) { + schedule_delayed_work(&bq20z75_device->work, HZ); + bq20z75_device->poll_time--; + return; + } +} + +#if defined(CONFIG_OF) + +#include +#include + +static const struct of_device_id bq20z75_dt_ids[] = { + { .compatible = "ti,bq20z75" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, bq20z75_dt_ids); + +static struct bq20z75_platform_data *bq20z75_of_populate_pdata( + struct i2c_client *client) +{ + struct device_node *of_node = client->dev.of_node; + struct bq20z75_platform_data *pdata = client->dev.platform_data; + enum of_gpio_flags gpio_flags; + int rc; + u32 prop; + + /* verify this driver matches this device */ + if (!of_node) + return NULL; + + /* if platform data is set, honor it */ + if (pdata) + return pdata; + + /* first make sure at least one property is set, otherwise + * it won't change behavior from running without pdata. + */ + if (!of_get_property(of_node, "ti,i2c-retry-count", NULL) && + !of_get_property(of_node, "ti,poll-retry-count", NULL) && + !of_get_property(of_node, "ti,battery-detect-gpios", NULL)) + goto of_out; + + pdata = devm_kzalloc(&client->dev, sizeof(struct bq20z75_platform_data), + GFP_KERNEL); + if (!pdata) + goto of_out; + + rc = of_property_read_u32(of_node, "ti,i2c-retry-count", &prop); + if (!rc) + pdata->i2c_retry_count = prop; + + rc = of_property_read_u32(of_node, "ti,poll-retry-count", &prop); + if (!rc) + pdata->poll_retry_count = prop; + + if (!of_get_property(of_node, "ti,battery-detect-gpios", NULL)) { + pdata->battery_detect = -1; + goto of_out; + } + + pdata->battery_detect = of_get_named_gpio_flags(of_node, + "ti,battery-detect-gpios", 0, &gpio_flags); + + if (gpio_flags & OF_GPIO_ACTIVE_LOW) + pdata->battery_detect_present = 0; + else + pdata->battery_detect_present = 1; + +of_out: + return pdata; +} +#else +#define bq20z75_dt_ids NULL +static struct bq20z75_platform_data *bq20z75_of_populate_pdata( + struct i2c_client *client) +{ + return client->dev.platform_data; +} +#endif + +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; + bq20z75_device->power_supply.num_properties = + ARRAY_SIZE(bq20z75_properties); + bq20z75_device->power_supply.get_property = bq20z75_get_property; + /* ignore first notification of external change, it is generated + * from the power_supply_register call back + */ + bq20z75_device->ignore_changes = 1; + bq20z75_device->last_state = POWER_SUPPLY_STATUS_UNKNOWN; + bq20z75_device->power_supply.external_power_changed = + bq20z75_external_power_changed; + + pdata = bq20z75_of_populate_pdata(client); + + 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__); + goto exit_psupply; + } + + dev_info(&client->dev, + "%s: battery gas gauge device registered\n", client->name); + + INIT_DELAYED_WORK(&bq20z75_device->work, bq20z75_delayed_work); + + bq20z75_device->enable_detection = true; + + 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 __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); + + cancel_delayed_work_sync(&bq20z75_device->work); + + kfree(bq20z75_device); + bq20z75_device = NULL; + + return 0; +} + +#if defined CONFIG_PM +static int bq20z75_suspend(struct i2c_client *client, + pm_message_t state) +{ + struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); + s32 ret; + + if (bq20z75_device->poll_time > 0) + cancel_delayed_work_sync(&bq20z75_device->work); + + /* write to manufacturer access with sleep command */ + ret = bq20z75_write_word_data(client, + bq20z75_data[REG_MANUFACTURER_DATA].addr, + MANUFACTURER_ACCESS_SLEEP); + if (bq20z75_device->is_present && ret < 0) + return ret; + + return 0; +} +#else +#define bq20z75_suspend NULL +#endif +/* any smbus transaction will wake up bq20z75 */ +#define bq20z75_resume NULL + +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, + .remove = __devexit_p(bq20z75_remove), + .suspend = bq20z75_suspend, + .resume = bq20z75_resume, + .id_table = bq20z75_id, + .driver = { + .name = "bq20z75-battery", + .of_match_table = bq20z75_dt_ids, + }, +}; + +static int __init bq20z75_battery_init(void) +{ + return i2c_add_driver(&bq20z75_battery_driver); +} +module_init(bq20z75_battery_init); + +static void __exit bq20z75_battery_exit(void) +{ + i2c_del_driver(&bq20z75_battery_driver); +} +module_exit(bq20z75_battery_exit); + +MODULE_DESCRIPTION("BQ20z75 battery monitor driver"); +MODULE_LICENSE("GPL"); -- cgit v1.1 From 3ddca062f8d71724529b0d52609994c9886f1a18 Mon Sep 17 00:00:00 2001 From: Rhyland Klein Date: Mon, 5 Dec 2011 17:50:46 -0800 Subject: sbs-battery: Rename internals to new name Now that this driver is named more generally, this change updates the internal variables, defines and functions to use this new name. Signed-off-by: Rhyland Klein Signed-off-by: Anton Vorontsov --- drivers/power/sbs-battery.c | 423 +++++++++++++++++++++----------------------- 1 file changed, 204 insertions(+), 219 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/sbs-battery.c b/drivers/power/sbs-battery.c index ce95ff7..00bd9e0 100644 --- a/drivers/power/sbs-battery.c +++ b/drivers/power/sbs-battery.c @@ -1,5 +1,5 @@ /* - * Gas Gauge driver for TI's BQ20Z75 + * Gas Gauge driver for SBS Compliant Batteries * * Copyright (c) 2010, NVIDIA Corporation. * @@ -28,7 +28,7 @@ #include #include -#include +#include enum { REG_MANUFACTURER_DATA, @@ -53,7 +53,7 @@ enum { /* Battery Mode defines */ #define BATTERY_MODE_OFFSET 0x03 #define BATTERY_MODE_MASK 0x8000 -enum bq20z75_battery_mode { +enum sbs_battery_mode { BATTERY_MODE_AMPS, BATTERY_MODE_WATTS }; @@ -67,62 +67,56 @@ enum bq20z75_battery_mode { #define BATTERY_FULL_CHARGED 0x20 #define BATTERY_FULL_DISCHARGED 0x10 -#define BQ20Z75_DATA(_psp, _addr, _min_value, _max_value) { \ +#define SBS_DATA(_psp, _addr, _min_value, _max_value) { \ .psp = _psp, \ .addr = _addr, \ .min_value = _min_value, \ .max_value = _max_value, \ } -static const struct bq20z75_device_data { +static const struct chip_data { enum power_supply_property psp; u8 addr; int min_value; int max_value; -} bq20z75_data[] = { +} sbs_data[] = { [REG_MANUFACTURER_DATA] = - BQ20Z75_DATA(POWER_SUPPLY_PROP_PRESENT, 0x00, 0, 65535), + SBS_DATA(POWER_SUPPLY_PROP_PRESENT, 0x00, 0, 65535), [REG_TEMPERATURE] = - BQ20Z75_DATA(POWER_SUPPLY_PROP_TEMP, 0x08, 0, 65535), + SBS_DATA(POWER_SUPPLY_PROP_TEMP, 0x08, 0, 65535), [REG_VOLTAGE] = - BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 20000), + SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 20000), [REG_CURRENT] = - BQ20Z75_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768, - 32767), + SBS_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768, 32767), [REG_CAPACITY] = - BQ20Z75_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0E, 0, 100), + SBS_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0E, 0, 100), [REG_REMAINING_CAPACITY] = - BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_NOW, 0x0F, 0, 65535), + SBS_DATA(POWER_SUPPLY_PROP_ENERGY_NOW, 0x0F, 0, 65535), [REG_REMAINING_CAPACITY_CHARGE] = - BQ20Z75_DATA(POWER_SUPPLY_PROP_CHARGE_NOW, 0x0F, 0, 65535), + SBS_DATA(POWER_SUPPLY_PROP_CHARGE_NOW, 0x0F, 0, 65535), [REG_FULL_CHARGE_CAPACITY] = - BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL, 0x10, 0, 65535), + SBS_DATA(POWER_SUPPLY_PROP_ENERGY_FULL, 0x10, 0, 65535), [REG_FULL_CHARGE_CAPACITY_CHARGE] = - BQ20Z75_DATA(POWER_SUPPLY_PROP_CHARGE_FULL, 0x10, 0, 65535), + SBS_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), + SBS_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0, 65535), [REG_TIME_TO_FULL] = - BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, 0x13, 0, - 65535), + SBS_DATA(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, 0x13, 0, 65535), [REG_STATUS] = - BQ20Z75_DATA(POWER_SUPPLY_PROP_STATUS, 0x16, 0, 65535), + SBS_DATA(POWER_SUPPLY_PROP_STATUS, 0x16, 0, 65535), [REG_CYCLE_COUNT] = - BQ20Z75_DATA(POWER_SUPPLY_PROP_CYCLE_COUNT, 0x17, 0, 65535), + SBS_DATA(POWER_SUPPLY_PROP_CYCLE_COUNT, 0x17, 0, 65535), [REG_DESIGN_CAPACITY] = - BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 0x18, 0, - 65535), + SBS_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), + SBS_DATA(POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 0x18, 0, 65535), [REG_DESIGN_VOLTAGE] = - BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 0x19, 0, - 65535), + SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 0x19, 0, 65535), [REG_SERIAL_NUMBER] = - BQ20Z75_DATA(POWER_SUPPLY_PROP_SERIAL_NUMBER, 0x1C, 0, 65535), + SBS_DATA(POWER_SUPPLY_PROP_SERIAL_NUMBER, 0x1C, 0, 65535), }; -static enum power_supply_property bq20z75_properties[] = { +static enum power_supply_property sbs_properties[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_PRESENT, @@ -144,10 +138,10 @@ static enum power_supply_property bq20z75_properties[] = { POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, }; -struct bq20z75_info { +struct sbs_info { struct i2c_client *client; struct power_supply power_supply; - struct bq20z75_platform_data *pdata; + struct sbs_platform_data *pdata; bool is_present; bool gpio_detect; bool enable_detection; @@ -158,14 +152,14 @@ struct bq20z75_info { int ignore_changes; }; -static int bq20z75_read_word_data(struct i2c_client *client, u8 address) +static int sbs_read_word_data(struct i2c_client *client, u8 address) { - struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); + struct sbs_info *chip = i2c_get_clientdata(client); s32 ret = 0; int retries = 1; - if (bq20z75_device->pdata) - retries = max(bq20z75_device->pdata->i2c_retry_count + 1, 1); + if (chip->pdata) + retries = max(chip->pdata->i2c_retry_count + 1, 1); while (retries > 0) { ret = i2c_smbus_read_word_data(client, address); @@ -184,15 +178,15 @@ static int bq20z75_read_word_data(struct i2c_client *client, u8 address) return le16_to_cpu(ret); } -static int bq20z75_write_word_data(struct i2c_client *client, u8 address, +static int sbs_write_word_data(struct i2c_client *client, u8 address, u16 value) { - struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); + struct sbs_info *chip = i2c_get_clientdata(client); s32 ret = 0; int retries = 1; - if (bq20z75_device->pdata) - retries = max(bq20z75_device->pdata->i2c_retry_count + 1, 1); + if (chip->pdata) + retries = max(chip->pdata->i2c_retry_count + 1, 1); while (retries > 0) { ret = i2c_smbus_write_word_data(client, address, @@ -212,44 +206,41 @@ static int bq20z75_write_word_data(struct i2c_client *client, u8 address, return 0; } -static int bq20z75_get_battery_presence_and_health( +static int sbs_get_battery_presence_and_health( struct i2c_client *client, enum power_supply_property psp, union power_supply_propval *val) { s32 ret; - struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); + struct sbs_info *chip = 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) + chip->gpio_detect) { + ret = gpio_get_value(chip->pdata->battery_detect); + if (ret == chip->pdata->battery_detect_present) val->intval = 1; else val->intval = 0; - bq20z75_device->is_present = val->intval; + chip->is_present = val->intval; return ret; } /* Write to ManufacturerAccess with * ManufacturerAccess command and then * read the status */ - ret = bq20z75_write_word_data(client, - bq20z75_data[REG_MANUFACTURER_DATA].addr, - MANUFACTURER_ACCESS_STATUS); + ret = sbs_write_word_data(client, sbs_data[REG_MANUFACTURER_DATA].addr, + MANUFACTURER_ACCESS_STATUS); 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); + ret = sbs_read_word_data(client, sbs_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) { + if (ret < sbs_data[REG_MANUFACTURER_DATA].min_value || + ret > sbs_data[REG_MANUFACTURER_DATA].max_value) { val->intval = 0; return 0; } @@ -279,24 +270,23 @@ static int bq20z75_get_battery_presence_and_health( return 0; } -static int bq20z75_get_battery_property(struct i2c_client *client, +static int sbs_get_battery_property(struct i2c_client *client, int reg_offset, enum power_supply_property psp, union power_supply_propval *val) { - struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); + struct sbs_info *chip = i2c_get_clientdata(client); s32 ret; - ret = bq20z75_read_word_data(client, - bq20z75_data[reg_offset].addr); + ret = sbs_read_word_data(client, sbs_data[reg_offset].addr); if (ret < 0) return ret; /* returned values are 16 bit */ - if (bq20z75_data[reg_offset].min_value < 0) + if (sbs_data[reg_offset].min_value < 0) ret = (s16)ret; - if (ret >= bq20z75_data[reg_offset].min_value && - ret <= bq20z75_data[reg_offset].max_value) { + if (ret >= sbs_data[reg_offset].min_value && + ret <= sbs_data[reg_offset].max_value) { val->intval = ret; if (psp != POWER_SUPPLY_PROP_STATUS) return 0; @@ -310,12 +300,12 @@ static int bq20z75_get_battery_property(struct i2c_client *client, else val->intval = POWER_SUPPLY_STATUS_CHARGING; - if (bq20z75_device->poll_time == 0) - bq20z75_device->last_state = val->intval; - else if (bq20z75_device->last_state != val->intval) { - cancel_delayed_work_sync(&bq20z75_device->work); - power_supply_changed(&bq20z75_device->power_supply); - bq20z75_device->poll_time = 0; + if (chip->poll_time == 0) + chip->last_state = val->intval; + else if (chip->last_state != val->intval) { + cancel_delayed_work_sync(&chip->work); + power_supply_changed(&chip->power_supply); + chip->poll_time = 0; } } else { if (psp == POWER_SUPPLY_PROP_STATUS) @@ -327,7 +317,7 @@ static int bq20z75_get_battery_property(struct i2c_client *client, return 0; } -static void bq20z75_unit_adjustment(struct i2c_client *client, +static void sbs_unit_adjustment(struct i2c_client *client, enum power_supply_property psp, union power_supply_propval *val) { #define BASE_UNIT_CONVERSION 1000 @@ -338,7 +328,7 @@ static void bq20z75_unit_adjustment(struct i2c_client *client, 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. + /* sbs provides energy in units of 10mWh. * Convert to µWh */ val->intval *= BATTERY_MODE_CAP_MULT_WATT; @@ -354,7 +344,7 @@ static void bq20z75_unit_adjustment(struct i2c_client *client, break; case POWER_SUPPLY_PROP_TEMP: - /* bq20z75 provides battery temperature in 0.1K + /* sbs provides battery temperature in 0.1K * so convert it to 0.1°C */ val->intval -= TEMP_KELVIN_TO_CELSIUS; @@ -362,7 +352,7 @@ static void bq20z75_unit_adjustment(struct i2c_client *client, 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. + /* sbs provides time to empty and time to full in minutes. * Convert to seconds */ val->intval *= TIME_UNIT_CONVERSION; @@ -374,13 +364,12 @@ 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) +static enum sbs_battery_mode sbs_set_battery_mode(struct i2c_client *client, + enum sbs_battery_mode mode) { int ret, original_val; - original_val = bq20z75_read_word_data(client, BATTERY_MODE_OFFSET); + original_val = sbs_read_word_data(client, BATTERY_MODE_OFFSET); if (original_val < 0) return original_val; @@ -392,68 +381,67 @@ bq20z75_set_battery_mode(struct i2c_client *client, else ret = original_val | BATTERY_MODE_MASK; - ret = bq20z75_write_word_data(client, BATTERY_MODE_OFFSET, ret); + ret = sbs_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, +static int sbs_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; + enum sbs_battery_mode mode = BATTERY_MODE_WATTS; if (power_supply_is_amp_property(psp)) mode = BATTERY_MODE_AMPS; - mode = bq20z75_set_battery_mode(client, mode); + mode = sbs_set_battery_mode(client, mode); if (mode < 0) return mode; - ret = bq20z75_read_word_data(client, bq20z75_data[reg_offset].addr); + ret = sbs_read_word_data(client, sbs_data[reg_offset].addr); if (ret < 0) return ret; if (psp == POWER_SUPPLY_PROP_CAPACITY) { - /* bq20z75 spec says that this can be >100 % + /* sbs spec says that this can be >100 % * even if max value is 100 % */ val->intval = min(ret, 100); } else val->intval = ret; - ret = bq20z75_set_battery_mode(client, mode); + ret = sbs_set_battery_mode(client, mode); if (ret < 0) return ret; return 0; } -static char bq20z75_serial[5]; -static int bq20z75_get_battery_serial_number(struct i2c_client *client, +static char sbs_serial[5]; +static int sbs_get_battery_serial_number(struct i2c_client *client, union power_supply_propval *val) { int ret; - ret = bq20z75_read_word_data(client, - bq20z75_data[REG_SERIAL_NUMBER].addr); + ret = sbs_read_word_data(client, sbs_data[REG_SERIAL_NUMBER].addr); if (ret < 0) return ret; - ret = sprintf(bq20z75_serial, "%04x", ret); - val->strval = bq20z75_serial; + ret = sprintf(sbs_serial, "%04x", ret); + val->strval = sbs_serial; return 0; } -static int bq20z75_get_property_index(struct i2c_client *client, +static int sbs_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) + for (count = 0; count < ARRAY_SIZE(sbs_data); count++) + if (psp == sbs_data[count].psp) return count; dev_warn(&client->dev, @@ -462,19 +450,19 @@ static int bq20z75_get_property_index(struct i2c_client *client, return -EINVAL; } -static int bq20z75_get_property(struct power_supply *psy, +static int sbs_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { int ret = 0; - struct bq20z75_info *bq20z75_device = container_of(psy, - struct bq20z75_info, power_supply); - struct i2c_client *client = bq20z75_device->client; + struct sbs_info *chip = container_of(psy, + struct sbs_info, power_supply); + struct i2c_client *client = chip->client; switch (psp) { case POWER_SUPPLY_PROP_PRESENT: case POWER_SUPPLY_PROP_HEALTH: - ret = bq20z75_get_battery_presence_and_health(client, psp, val); + ret = sbs_get_battery_presence_and_health(client, psp, val); if (psp == POWER_SUPPLY_PROP_PRESENT) return 0; break; @@ -490,15 +478,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: - ret = bq20z75_get_property_index(client, psp); + ret = sbs_get_property_index(client, psp); if (ret < 0) break; - ret = bq20z75_get_battery_capacity(client, ret, psp, val); + ret = sbs_get_battery_capacity(client, ret, psp, val); break; case POWER_SUPPLY_PROP_SERIAL_NUMBER: - ret = bq20z75_get_battery_serial_number(client, val); + ret = sbs_get_battery_serial_number(client, val); break; case POWER_SUPPLY_PROP_STATUS: @@ -509,11 +497,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: - ret = bq20z75_get_property_index(client, psp); + ret = sbs_get_property_index(client, psp); if (ret < 0) break; - ret = bq20z75_get_battery_property(client, ret, psp, val); + ret = sbs_get_battery_property(client, ret, psp, val); break; default: @@ -522,25 +510,25 @@ static int bq20z75_get_property(struct power_supply *psy, return -EINVAL; } - if (!bq20z75_device->enable_detection) + if (!chip->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); + if (!chip->gpio_detect && + chip->is_present != (ret >= 0)) { + chip->is_present = (ret >= 0); + power_supply_changed(&chip->power_supply); } done: if (!ret) { /* Convert units to match requirements for power supply class */ - bq20z75_unit_adjustment(client, psp, val); + sbs_unit_adjustment(client, psp, val); } dev_dbg(&client->dev, "%s: property = %d, value = %x\n", __func__, psp, val->intval); - if (ret && bq20z75_device->is_present) + if (ret && chip->is_present) return ret; /* battery not present, so return NODATA for properties */ @@ -550,7 +538,7 @@ done: return 0; } -static irqreturn_t bq20z75_irq(int irq, void *devid) +static irqreturn_t sbs_irq(int irq, void *devid) { struct power_supply *battery = devid; @@ -559,36 +547,35 @@ static irqreturn_t bq20z75_irq(int irq, void *devid) return IRQ_HANDLED; } -static void bq20z75_external_power_changed(struct power_supply *psy) +static void sbs_external_power_changed(struct power_supply *psy) { - struct bq20z75_info *bq20z75_device; + struct sbs_info *chip; - bq20z75_device = container_of(psy, struct bq20z75_info, power_supply); + chip = container_of(psy, struct sbs_info, power_supply); - if (bq20z75_device->ignore_changes > 0) { - bq20z75_device->ignore_changes--; + if (chip->ignore_changes > 0) { + chip->ignore_changes--; return; } /* cancel outstanding work */ - cancel_delayed_work_sync(&bq20z75_device->work); + cancel_delayed_work_sync(&chip->work); - schedule_delayed_work(&bq20z75_device->work, HZ); - bq20z75_device->poll_time = bq20z75_device->pdata->poll_retry_count; + schedule_delayed_work(&chip->work, HZ); + chip->poll_time = chip->pdata->poll_retry_count; } -static void bq20z75_delayed_work(struct work_struct *work) +static void sbs_delayed_work(struct work_struct *work) { - struct bq20z75_info *bq20z75_device; + struct sbs_info *chip; s32 ret; - bq20z75_device = container_of(work, struct bq20z75_info, work.work); + chip = container_of(work, struct sbs_info, work.work); - ret = bq20z75_read_word_data(bq20z75_device->client, - bq20z75_data[REG_STATUS].addr); + ret = sbs_read_word_data(chip->client, sbs_data[REG_STATUS].addr); /* if the read failed, give up on this work */ if (ret < 0) { - bq20z75_device->poll_time = 0; + chip->poll_time = 0; return; } @@ -601,14 +588,14 @@ static void bq20z75_delayed_work(struct work_struct *work) else ret = POWER_SUPPLY_STATUS_CHARGING; - if (bq20z75_device->last_state != ret) { - bq20z75_device->poll_time = 0; - power_supply_changed(&bq20z75_device->power_supply); + if (chip->last_state != ret) { + chip->poll_time = 0; + power_supply_changed(&chip->power_supply); return; } - if (bq20z75_device->poll_time > 0) { - schedule_delayed_work(&bq20z75_device->work, HZ); - bq20z75_device->poll_time--; + if (chip->poll_time > 0) { + schedule_delayed_work(&chip->work, HZ); + chip->poll_time--; return; } } @@ -618,17 +605,18 @@ static void bq20z75_delayed_work(struct work_struct *work) #include #include -static const struct of_device_id bq20z75_dt_ids[] = { +static const struct of_device_id sbs_dt_ids[] = { + { .compatible = "sbs,sbs-battery" }, { .compatible = "ti,bq20z75" }, { } }; -MODULE_DEVICE_TABLE(i2c, bq20z75_dt_ids); +MODULE_DEVICE_TABLE(i2c, sbs_dt_ids); -static struct bq20z75_platform_data *bq20z75_of_populate_pdata( +static struct sbs_platform_data *sbs_of_populate_pdata( struct i2c_client *client) { struct device_node *of_node = client->dev.of_node; - struct bq20z75_platform_data *pdata = client->dev.platform_data; + struct sbs_platform_data *pdata = client->dev.platform_data; enum of_gpio_flags gpio_flags; int rc; u32 prop; @@ -644,31 +632,31 @@ static struct bq20z75_platform_data *bq20z75_of_populate_pdata( /* first make sure at least one property is set, otherwise * it won't change behavior from running without pdata. */ - if (!of_get_property(of_node, "ti,i2c-retry-count", NULL) && - !of_get_property(of_node, "ti,poll-retry-count", NULL) && - !of_get_property(of_node, "ti,battery-detect-gpios", NULL)) + if (!of_get_property(of_node, "sbs,i2c-retry-count", NULL) && + !of_get_property(of_node, "sbs,poll-retry-count", NULL) && + !of_get_property(of_node, "sbs,battery-detect-gpios", NULL)) goto of_out; - pdata = devm_kzalloc(&client->dev, sizeof(struct bq20z75_platform_data), + pdata = devm_kzalloc(&client->dev, sizeof(struct sbs_platform_data), GFP_KERNEL); if (!pdata) goto of_out; - rc = of_property_read_u32(of_node, "ti,i2c-retry-count", &prop); + rc = of_property_read_u32(of_node, "sbs,i2c-retry-count", &prop); if (!rc) pdata->i2c_retry_count = prop; - rc = of_property_read_u32(of_node, "ti,poll-retry-count", &prop); + rc = of_property_read_u32(of_node, "sbs,poll-retry-count", &prop); if (!rc) pdata->poll_retry_count = prop; - if (!of_get_property(of_node, "ti,battery-detect-gpios", NULL)) { + if (!of_get_property(of_node, "sbs,battery-detect-gpios", NULL)) { pdata->battery_detect = -1; goto of_out; } pdata->battery_detect = of_get_named_gpio_flags(of_node, - "ti,battery-detect-gpios", 0, &gpio_flags); + "sbs,battery-detect-gpios", 0, &gpio_flags); if (gpio_flags & OF_GPIO_ACTIVE_LOW) pdata->battery_detect_present = 0; @@ -679,60 +667,57 @@ of_out: return pdata; } #else -#define bq20z75_dt_ids NULL -static struct bq20z75_platform_data *bq20z75_of_populate_pdata( +#define sbs_dt_ids NULL +static struct sbs_platform_data *sbs_of_populate_pdata( struct i2c_client *client) { return client->dev.platform_data; } #endif -static int __devinit bq20z75_probe(struct i2c_client *client, +static int __devinit sbs_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; + struct sbs_info *chip; + struct sbs_platform_data *pdata = client->dev.platform_data; int rc; int irq; - bq20z75_device = kzalloc(sizeof(struct bq20z75_info), GFP_KERNEL); - if (!bq20z75_device) + chip = kzalloc(sizeof(struct sbs_info), GFP_KERNEL); + if (!chip) 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; - bq20z75_device->power_supply.num_properties = - ARRAY_SIZE(bq20z75_properties); - bq20z75_device->power_supply.get_property = bq20z75_get_property; + chip->client = client; + chip->enable_detection = false; + chip->gpio_detect = false; + chip->power_supply.name = "battery"; + chip->power_supply.type = POWER_SUPPLY_TYPE_BATTERY; + chip->power_supply.properties = sbs_properties; + chip->power_supply.num_properties = ARRAY_SIZE(sbs_properties); + chip->power_supply.get_property = sbs_get_property; /* ignore first notification of external change, it is generated * from the power_supply_register call back */ - bq20z75_device->ignore_changes = 1; - bq20z75_device->last_state = POWER_SUPPLY_STATUS_UNKNOWN; - bq20z75_device->power_supply.external_power_changed = - bq20z75_external_power_changed; + chip->ignore_changes = 1; + chip->last_state = POWER_SUPPLY_STATUS_UNKNOWN; + chip->power_supply.external_power_changed = sbs_external_power_changed; - pdata = bq20z75_of_populate_pdata(client); + pdata = sbs_of_populate_pdata(client); if (pdata) { - bq20z75_device->gpio_detect = - gpio_is_valid(pdata->battery_detect); - bq20z75_device->pdata = pdata; + chip->gpio_detect = gpio_is_valid(pdata->battery_detect); + chip->pdata = pdata; } - i2c_set_clientdata(client, bq20z75_device); + i2c_set_clientdata(client, chip); - if (!bq20z75_device->gpio_detect) + if (!chip->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; + chip->gpio_detect = false; goto skip_gpio; } @@ -740,7 +725,7 @@ static int __devinit bq20z75_probe(struct i2c_client *client, 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; + chip->gpio_detect = false; goto skip_gpio; } @@ -748,25 +733,25 @@ static int __devinit bq20z75_probe(struct i2c_client *client, 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; + chip->gpio_detect = false; goto skip_gpio; } - rc = request_irq(irq, bq20z75_irq, + rc = request_irq(irq, sbs_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - dev_name(&client->dev), &bq20z75_device->power_supply); + dev_name(&client->dev), &chip->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; + chip->gpio_detect = false; goto skip_gpio; } - bq20z75_device->irq = irq; + chip->irq = irq; skip_gpio: - rc = power_supply_register(&client->dev, &bq20z75_device->power_supply); + rc = power_supply_register(&client->dev, &chip->power_supply); if (rc) { dev_err(&client->dev, "%s: Failed to register power supply\n", __func__); @@ -776,96 +761,96 @@ skip_gpio: dev_info(&client->dev, "%s: battery gas gauge device registered\n", client->name); - INIT_DELAYED_WORK(&bq20z75_device->work, bq20z75_delayed_work); + INIT_DELAYED_WORK(&chip->work, sbs_delayed_work); - bq20z75_device->enable_detection = true; + chip->enable_detection = true; return 0; exit_psupply: - if (bq20z75_device->irq) - free_irq(bq20z75_device->irq, &bq20z75_device->power_supply); - if (bq20z75_device->gpio_detect) + if (chip->irq) + free_irq(chip->irq, &chip->power_supply); + if (chip->gpio_detect) gpio_free(pdata->battery_detect); - kfree(bq20z75_device); + kfree(chip); return rc; } -static int __devexit bq20z75_remove(struct i2c_client *client) +static int __devexit sbs_remove(struct i2c_client *client) { - struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); + struct sbs_info *chip = 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); + if (chip->irq) + free_irq(chip->irq, &chip->power_supply); + if (chip->gpio_detect) + gpio_free(chip->pdata->battery_detect); - power_supply_unregister(&bq20z75_device->power_supply); + power_supply_unregister(&chip->power_supply); - cancel_delayed_work_sync(&bq20z75_device->work); + cancel_delayed_work_sync(&chip->work); - kfree(bq20z75_device); - bq20z75_device = NULL; + kfree(chip); + chip = NULL; return 0; } #if defined CONFIG_PM -static int bq20z75_suspend(struct i2c_client *client, +static int sbs_suspend(struct i2c_client *client, pm_message_t state) { - struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); + struct sbs_info *chip = i2c_get_clientdata(client); s32 ret; - if (bq20z75_device->poll_time > 0) - cancel_delayed_work_sync(&bq20z75_device->work); + if (chip->poll_time > 0) + cancel_delayed_work_sync(&chip->work); /* write to manufacturer access with sleep command */ - ret = bq20z75_write_word_data(client, - bq20z75_data[REG_MANUFACTURER_DATA].addr, + ret = sbs_write_word_data(client, sbs_data[REG_MANUFACTURER_DATA].addr, MANUFACTURER_ACCESS_SLEEP); - if (bq20z75_device->is_present && ret < 0) + if (chip->is_present && ret < 0) return ret; return 0; } #else -#define bq20z75_suspend NULL +#define sbs_suspend NULL #endif -/* any smbus transaction will wake up bq20z75 */ -#define bq20z75_resume NULL +/* any smbus transaction will wake up sbs */ +#define sbs_resume NULL -static const struct i2c_device_id bq20z75_id[] = { +static const struct i2c_device_id sbs_id[] = { { "bq20z75", 0 }, + { "sbs-battery", 1 }, {} }; -MODULE_DEVICE_TABLE(i2c, bq20z75_id); - -static struct i2c_driver bq20z75_battery_driver = { - .probe = bq20z75_probe, - .remove = __devexit_p(bq20z75_remove), - .suspend = bq20z75_suspend, - .resume = bq20z75_resume, - .id_table = bq20z75_id, +MODULE_DEVICE_TABLE(i2c, sbs_id); + +static struct i2c_driver sbs_battery_driver = { + .probe = sbs_probe, + .remove = __devexit_p(sbs_remove), + .suspend = sbs_suspend, + .resume = sbs_resume, + .id_table = sbs_id, .driver = { - .name = "bq20z75-battery", - .of_match_table = bq20z75_dt_ids, + .name = "sbs-battery", + .of_match_table = sbs_dt_ids, }, }; -static int __init bq20z75_battery_init(void) +static int __init sbs_battery_init(void) { - return i2c_add_driver(&bq20z75_battery_driver); + return i2c_add_driver(&sbs_battery_driver); } -module_init(bq20z75_battery_init); +module_init(sbs_battery_init); -static void __exit bq20z75_battery_exit(void) +static void __exit sbs_battery_exit(void) { - i2c_del_driver(&bq20z75_battery_driver); + i2c_del_driver(&sbs_battery_driver); } -module_exit(bq20z75_battery_exit); +module_exit(sbs_battery_exit); -MODULE_DESCRIPTION("BQ20z75 battery monitor driver"); +MODULE_DESCRIPTION("SBS battery monitor driver"); MODULE_LICENSE("GPL"); -- cgit v1.1 From 52f56c69beb049928f6e1a3d484afe27843d6c04 Mon Sep 17 00:00:00 2001 From: Rhyland Klein Date: Mon, 5 Dec 2011 17:50:49 -0800 Subject: sbs-battery: Change power supply name The power supply name used to be fixed as "battery". This change allows for multiple batteries by generating the name rather than using a fixed value. Signed-off-by: Rhyland Klein Signed-off-by: Anton Vorontsov --- drivers/power/sbs-battery.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/sbs-battery.c b/drivers/power/sbs-battery.c index 00bd9e0..b677b88d 100644 --- a/drivers/power/sbs-battery.c +++ b/drivers/power/sbs-battery.c @@ -682,15 +682,24 @@ static int __devinit sbs_probe(struct i2c_client *client, struct sbs_platform_data *pdata = client->dev.platform_data; int rc; int irq; + char *name; - chip = kzalloc(sizeof(struct sbs_info), GFP_KERNEL); - if (!chip) + name = kasprintf(GFP_KERNEL, "sbs-%s", dev_name(&client->dev)); + if (!name) { + dev_err(&client->dev, "Failed to allocate device name\n"); return -ENOMEM; + } + + chip = kzalloc(sizeof(struct sbs_info), GFP_KERNEL); + if (!chip) { + rc = -ENOMEM; + goto exit_free_name; + } chip->client = client; chip->enable_detection = false; chip->gpio_detect = false; - chip->power_supply.name = "battery"; + chip->power_supply.name = name; chip->power_supply.type = POWER_SUPPLY_TYPE_BATTERY; chip->power_supply.properties = sbs_properties; chip->power_supply.num_properties = ARRAY_SIZE(sbs_properties); @@ -775,6 +784,9 @@ exit_psupply: kfree(chip); +exit_free_name: + kfree(name); + return rc; } @@ -791,6 +803,7 @@ static int __devexit sbs_remove(struct i2c_client *client) cancel_delayed_work_sync(&chip->work); + kfree(chip->power_supply.name); kfree(chip); chip = NULL; -- cgit v1.1 From 62df3935a7ef842ad0af6025d2fc59d353de2e1d Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Fri, 6 Jan 2012 05:45:34 +0400 Subject: sbs-battery: Fix devicetree match table It should be an of module table, not i2c. Signed-off-by: Olof Johansson Acked-by: Rhyland Klein Signed-off-by: Anton Vorontsov --- drivers/power/sbs-battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/sbs-battery.c b/drivers/power/sbs-battery.c index b677b88d..9ff8af0 100644 --- a/drivers/power/sbs-battery.c +++ b/drivers/power/sbs-battery.c @@ -610,7 +610,7 @@ static const struct of_device_id sbs_dt_ids[] = { { .compatible = "ti,bq20z75" }, { } }; -MODULE_DEVICE_TABLE(i2c, sbs_dt_ids); +MODULE_DEVICE_TABLE(of, sbs_dt_ids); static struct sbs_platform_data *sbs_of_populate_pdata( struct i2c_client *client) -- cgit v1.1 From 2530daa187be3adef2d7cb41bd51f1384e478f2b Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sat, 10 Dec 2011 22:53:36 +0100 Subject: power_supply: Assume mains power by default If no power class device is found in power_supply_is_system_supplied(), the function currently returns 0, which basically means that the system is supposed to be running on battery. In practice, mobile devices tend to always implement at least one power class device and more often two (battery and AC adapter). Systems with no registered power class devices are more likely to be desktop systems, where the system is always powered by mains. So, change the default return value of power_supply_is_system_supplied() from 0 (running on battery) to 1 (running on mains.) Signed-off-by: Jean Delvare Cc: David Woodhouse Cc: Matthew Garrett Cc: Alex Deucher Signed-off-by: Anton Vorontsov --- drivers/power/power_supply_core.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index b10c121..bc82f95 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -98,7 +98,9 @@ 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); + unsigned int *count = data; + (*count)++; if (psy->type != POWER_SUPPLY_TYPE_BATTERY) { if (psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &ret)) return 0; @@ -111,10 +113,18 @@ static int __power_supply_is_system_supplied(struct device *dev, void *data) int power_supply_is_system_supplied(void) { int error; + unsigned int count = 0; - error = class_for_each_device(power_supply_class, NULL, NULL, + error = class_for_each_device(power_supply_class, NULL, &count, __power_supply_is_system_supplied); + /* + * If no power class device was found at all, most probably we are + * running on a desktop system, so assume we are on mains power. + */ + if (count == 0) + return 1; + return error; } EXPORT_SYMBOL_GPL(power_supply_is_system_supplied); -- cgit v1.1 From ef7906f3204280ccaf8cd7ace974b7c0a1393812 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 8 Dec 2011 21:05:26 +0800 Subject: jz4740-battery: Fix signedness bug wait_for_completion_interruptible_timeout() may return negative value. In this case, checking if (t > 0) will return true if t is unsigned. Signed-off-by: Axel Lin Acked-by: Lars-Peter Clausen Signed-off-by: Anton Vorontsov --- drivers/power/jz4740-battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/jz4740-battery.c b/drivers/power/jz4740-battery.c index 6b77029..8dbc7bf 100644 --- a/drivers/power/jz4740-battery.c +++ b/drivers/power/jz4740-battery.c @@ -67,7 +67,7 @@ static irqreturn_t jz_battery_irq_handler(int irq, void *devid) static long jz_battery_read_voltage(struct jz_battery *battery) { - unsigned long t; + long t; unsigned long val; long voltage; -- cgit v1.1 From c934502db7fda4fac1bb3b5c74f35e9bde8e7547 Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Fri, 6 Jan 2012 06:00:48 +0400 Subject: isp1704_charger: Fix missing check A segfault happens if there's no board information. Signed-off-by: Felipe Contreras Acked-by: Heikki Krogerus Signed-off-by: Anton Vorontsov --- drivers/power/isp1704_charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c index 6a4ef38..b806667 100644 --- a/drivers/power/isp1704_charger.c +++ b/drivers/power/isp1704_charger.c @@ -79,7 +79,7 @@ static void isp1704_charger_set_power(struct isp1704_charger *isp, bool on) { struct isp1704_charger_data *board = isp->dev->platform_data; - if (board->set_power) + if (board && board->set_power) board->set_power(on); } -- cgit v1.1 From ded7fc7b055055427d0d04958482dbe3d23e087d Mon Sep 17 00:00:00 2001 From: Ashish Jangam Date: Wed, 14 Dec 2011 17:57:56 +0530 Subject: power_supply: Add DA9052 battery driver Driver for DA9052 battery charger. This driver depends on DA9052 MFD core dirver for definitions and methods. This patch is functionally tested on Samsung SMDKV6410. Signed-off-by: David Dajun Chen Signed-off-by: Ashish Jangam Signed-off-by: Anton Vorontsov --- drivers/power/Kconfig | 7 + drivers/power/Makefile | 1 + drivers/power/da9052-battery.c | 664 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 672 insertions(+) create mode 100644 drivers/power/da9052-battery.c (limited to 'drivers/power') diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index e24485f..ecc1b49 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -150,6 +150,13 @@ config BATTERY_DA9030 Say Y here to enable support for batteries charger integrated into DA9030 PMIC. +config BATTERY_DA9052 + tristate "Dialog DA9052 Battery" + depends on PMIC_DA9052 + help + Say Y here to enable support for batteries charger integrated into + DA9052 PMIC. + config BATTERY_MAX17040 tristate "Maxim MAX17040 Fuel Gauge" depends on I2C diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 9c3bbf7..e429008 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o obj-$(CONFIG_BATTERY_SBS) += sbs-battery.o obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o +obj-$(CONFIG_BATTERY_DA9052) += da9052-battery.o obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o obj-$(CONFIG_BATTERY_Z2) += z2_battery.o diff --git a/drivers/power/da9052-battery.c b/drivers/power/da9052-battery.c new file mode 100644 index 0000000..e8ea47a --- /dev/null +++ b/drivers/power/da9052-battery.c @@ -0,0 +1,664 @@ +/* + * Batttery Driver for Dialog DA9052 PMICs + * + * Copyright(c) 2011 Dialog Semiconductor Ltd. + * + * Author: David Dajun Chen + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* STATIC CONFIGURATION */ +#define DA9052_BAT_CUTOFF_VOLT 2800 +#define DA9052_BAT_TSH 62000 +#define DA9052_BAT_LOW_CAP 4 +#define DA9052_AVG_SZ 4 +#define DA9052_VC_TBL_SZ 68 +#define DA9052_VC_TBL_REF_SZ 3 + +#define DA9052_ISET_USB_MASK 0x0F +#define DA9052_CHG_USB_ILIM_MASK 0x40 +#define DA9052_CHG_LIM_COLS 16 + +#define DA9052_MEAN(x, y) ((x + y) / 2) + +enum charger_type_enum { + DA9052_NOCHARGER = 1, + DA9052_CHARGER, +}; + +static const u16 da9052_chg_current_lim[2][DA9052_CHG_LIM_COLS] = { + {70, 80, 90, 100, 110, 120, 400, 450, + 500, 550, 600, 650, 700, 900, 1100, 1300}, + {80, 90, 100, 110, 120, 400, 450, 500, + 550, 600, 800, 1000, 1200, 1400, 1600, 1800}, +}; + +static const u16 vc_tbl_ref[3] = {10, 25, 40}; +/* Lookup table for voltage vs capacity */ +static u32 const vc_tbl[3][68][2] = { + /* For temperature 10 degree Celsius */ + { + {4082, 100}, {4036, 98}, + {4020, 96}, {4008, 95}, + {3997, 93}, {3983, 91}, + {3964, 90}, {3943, 88}, + {3926, 87}, {3912, 85}, + {3900, 84}, {3890, 82}, + {3881, 80}, {3873, 79}, + {3865, 77}, {3857, 76}, + {3848, 74}, {3839, 73}, + {3829, 71}, {3820, 70}, + {3811, 68}, {3802, 67}, + {3794, 65}, {3785, 64}, + {3778, 62}, {3770, 61}, + {3763, 59}, {3756, 58}, + {3750, 56}, {3744, 55}, + {3738, 53}, {3732, 52}, + {3727, 50}, {3722, 49}, + {3717, 47}, {3712, 46}, + {3708, 44}, {3703, 43}, + {3700, 41}, {3696, 40}, + {3693, 38}, {3691, 37}, + {3688, 35}, {3686, 34}, + {3683, 32}, {3681, 31}, + {3678, 29}, {3675, 28}, + {3672, 26}, {3669, 25}, + {3665, 23}, {3661, 22}, + {3656, 21}, {3651, 19}, + {3645, 18}, {3639, 16}, + {3631, 15}, {3622, 13}, + {3611, 12}, {3600, 10}, + {3587, 9}, {3572, 7}, + {3548, 6}, {3503, 5}, + {3420, 3}, {3268, 2}, + {2992, 1}, {2746, 0} + }, + /* For temperature 25 degree Celsius */ + { + {4102, 100}, {4065, 98}, + {4048, 96}, {4034, 95}, + {4021, 93}, {4011, 92}, + {4001, 90}, {3986, 88}, + {3968, 87}, {3952, 85}, + {3938, 84}, {3926, 82}, + {3916, 81}, {3908, 79}, + {3900, 77}, {3892, 76}, + {3883, 74}, {3874, 73}, + {3864, 71}, {3855, 70}, + {3846, 68}, {3836, 67}, + {3827, 65}, {3819, 64}, + {3810, 62}, {3801, 61}, + {3793, 59}, {3786, 58}, + {3778, 56}, {3772, 55}, + {3765, 53}, {3759, 52}, + {3754, 50}, {3748, 49}, + {3743, 47}, {3738, 46}, + {3733, 44}, {3728, 43}, + {3724, 41}, {3720, 40}, + {3716, 38}, {3712, 37}, + {3709, 35}, {3706, 34}, + {3703, 33}, {3701, 31}, + {3698, 30}, {3696, 28}, + {3693, 27}, {3690, 25}, + {3687, 24}, {3683, 22}, + {3680, 21}, {3675, 19}, + {3671, 18}, {3666, 17}, + {3660, 15}, {3654, 14}, + {3647, 12}, {3639, 11}, + {3630, 9}, {3621, 8}, + {3613, 6}, {3606, 5}, + {3597, 4}, {3582, 2}, + {3546, 1}, {2747, 0} + }, + /* For temperature 40 degree Celsius */ + { + {4114, 100}, {4081, 98}, + {4065, 96}, {4050, 95}, + {4036, 93}, {4024, 92}, + {4013, 90}, {4002, 88}, + {3990, 87}, {3976, 85}, + {3962, 84}, {3950, 82}, + {3939, 81}, {3930, 79}, + {3921, 77}, {3912, 76}, + {3902, 74}, {3893, 73}, + {3883, 71}, {3874, 70}, + {3865, 68}, {3856, 67}, + {3847, 65}, {3838, 64}, + {3829, 62}, {3820, 61}, + {3812, 59}, {3803, 58}, + {3795, 56}, {3787, 55}, + {3780, 53}, {3773, 52}, + {3767, 50}, {3761, 49}, + {3756, 47}, {3751, 46}, + {3746, 44}, {3741, 43}, + {3736, 41}, {3732, 40}, + {3728, 38}, {3724, 37}, + {3720, 35}, {3716, 34}, + {3713, 33}, {3710, 31}, + {3707, 30}, {3704, 28}, + {3701, 27}, {3698, 25}, + {3695, 24}, {3691, 22}, + {3686, 21}, {3681, 19}, + {3676, 18}, {3671, 17}, + {3666, 15}, {3661, 14}, + {3655, 12}, {3648, 11}, + {3640, 9}, {3632, 8}, + {3622, 6}, {3616, 5}, + {3611, 4}, {3604, 2}, + {3594, 1}, {2747, 0} + } +}; + +struct da9052_battery { + struct da9052 *da9052; + struct power_supply psy; + struct notifier_block nb; + int charger_type; + int status; + int health; +}; + +static inline int volt_reg_to_mV(int value) +{ + return ((value * 1000) / 512) + 2500; +} + +static inline int ichg_reg_to_mA(int value) +{ + return (value * 3900) / 1000; +} + +static int da9052_read_chgend_current(struct da9052_battery *bat, + int *current_mA) +{ + int ret; + + if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING) + return -EINVAL; + + ret = da9052_reg_read(bat->da9052, DA9052_ICHG_END_REG); + if (ret < 0) + return ret; + + *current_mA = ichg_reg_to_mA(ret & DA9052_ICHGEND_ICHGEND); + + return 0; +} + +static int da9052_read_chg_current(struct da9052_battery *bat, int *current_mA) +{ + int ret; + + if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING) + return -EINVAL; + + ret = da9052_reg_read(bat->da9052, DA9052_ICHG_AV_REG); + if (ret < 0) + return ret; + + *current_mA = ichg_reg_to_mA(ret & DA9052_ICHGAV_ICHGAV); + + return 0; +} + +static int da9052_bat_check_status(struct da9052_battery *bat, int *status) +{ + u8 v[2] = {0, 0}; + u8 bat_status; + u8 chg_end; + int ret; + int chg_current; + int chg_end_current; + bool dcinsel; + bool dcindet; + bool vbussel; + bool vbusdet; + bool dc; + bool vbus; + + ret = da9052_group_read(bat->da9052, DA9052_STATUS_A_REG, 2, v); + if (ret < 0) + return ret; + + bat_status = v[0]; + chg_end = v[1]; + + dcinsel = bat_status & DA9052_STATUSA_DCINSEL; + dcindet = bat_status & DA9052_STATUSA_DCINDET; + vbussel = bat_status & DA9052_STATUSA_VBUSSEL; + vbusdet = bat_status & DA9052_STATUSA_VBUSDET; + dc = dcinsel && dcindet; + vbus = vbussel && vbusdet; + + /* Preference to WALL(DCIN) charger unit */ + if (dc || vbus) { + bat->charger_type = DA9052_CHARGER; + + /* If charging end flag is set and Charging current is greater + * than charging end limit then battery is charging + */ + if ((chg_end & DA9052_STATUSB_CHGEND) != 0) { + ret = da9052_read_chg_current(bat, &chg_current); + if (ret < 0) + return ret; + ret = da9052_read_chgend_current(bat, &chg_end_current); + if (ret < 0) + return ret; + + if (chg_current >= chg_end_current) + bat->status = POWER_SUPPLY_STATUS_CHARGING; + else + bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING; + } else { + /* If Charging end flag is cleared then battery is + * charging + */ + bat->status = POWER_SUPPLY_STATUS_CHARGING; + } + } else if (dcindet || vbusdet) { + bat->charger_type = DA9052_CHARGER; + bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING; + } else { + bat->charger_type = DA9052_NOCHARGER; + bat->status = POWER_SUPPLY_STATUS_DISCHARGING; + } + + if (status != NULL) + *status = bat->status; + return 0; +} + +static int da9052_bat_read_volt(struct da9052_battery *bat, int *volt_mV) +{ + int volt; + + volt = da9052_adc_manual_read(bat->da9052, DA9052_ADC_MAN_MUXSEL_VBAT); + if (volt < 0) + return volt; + + *volt_mV = volt_reg_to_mV(volt); + + return 0; +} + +static int da9052_bat_check_presence(struct da9052_battery *bat, int *illegal) +{ + int bat_temp; + + bat_temp = da9052_adc_read_temp(bat->da9052); + if (bat_temp < 0) + return bat_temp; + + if (bat_temp > DA9052_BAT_TSH) + *illegal = 1; + else + *illegal = 0; + + return 0; +} + +static int da9052_bat_interpolate(int vbat_lower, int vbat_upper, + int level_lower, int level_upper, + int bat_voltage) +{ + int tmp; + + tmp = ((level_upper - level_lower) * 1000) / (vbat_upper - vbat_lower); + tmp = level_lower + (((bat_voltage - vbat_lower) * tmp) / 1000); + + return tmp; +} + +unsigned char da9052_determine_vc_tbl_index(unsigned char adc_temp) +{ + int i; + + if (adc_temp <= vc_tbl_ref[0]) + return 0; + + if (adc_temp > vc_tbl_ref[DA9052_VC_TBL_REF_SZ - 1]) + return DA9052_VC_TBL_REF_SZ - 1; + + for (i = 0; i < DA9052_VC_TBL_REF_SZ; i++) { + if ((adc_temp > vc_tbl_ref[i]) && + (adc_temp <= DA9052_MEAN(vc_tbl_ref[i], vc_tbl_ref[i + 1]))) + return i; + if ((adc_temp > DA9052_MEAN(vc_tbl_ref[i], vc_tbl_ref[i + 1])) + && (adc_temp <= vc_tbl_ref[i])) + return i + 1; + } +} + +static int da9052_bat_read_capacity(struct da9052_battery *bat, int *capacity) +{ + int adc_temp; + int bat_voltage; + int vbat_lower; + int vbat_upper; + int level_upper; + int level_lower; + int ret; + int flag; + int i = 0; + int j; + + ret = da9052_bat_read_volt(bat, &bat_voltage); + if (ret < 0) + return ret; + + adc_temp = da9052_adc_read_temp(bat->da9052); + if (adc_temp < 0) + return adc_temp; + + i = da9052_determine_vc_tbl_index(adc_temp); + + if (bat_voltage >= vc_tbl[i][0][0]) { + *capacity = 100; + return 0; + } + if (bat_voltage <= vc_tbl[i][DA9052_VC_TBL_SZ - 1][0]) { + *capacity = 0; + return 0; + } + flag = 0; + + for (j = 0; j < (DA9052_VC_TBL_SZ-1); j++) { + if ((bat_voltage <= vc_tbl[i][j][0]) && + (bat_voltage >= vc_tbl[i][j + 1][0])) { + vbat_upper = vc_tbl[i][j][0]; + vbat_lower = vc_tbl[i][j + 1][0]; + level_upper = vc_tbl[i][j][1]; + level_lower = vc_tbl[i][j + 1][1]; + flag = 1; + break; + } + } + if (!flag) + return -EIO; + + *capacity = da9052_bat_interpolate(vbat_lower, vbat_upper, level_lower, + level_upper, bat_voltage); + + return 0; +} + +static int da9052_bat_check_health(struct da9052_battery *bat, int *health) +{ + int ret; + int bat_illegal; + int capacity; + + ret = da9052_bat_check_presence(bat, &bat_illegal); + if (ret < 0) + return ret; + + if (bat_illegal) { + bat->health = POWER_SUPPLY_HEALTH_UNKNOWN; + return 0; + } + + if (bat->health != POWER_SUPPLY_HEALTH_OVERHEAT) { + ret = da9052_bat_read_capacity(bat, &capacity); + if (ret < 0) + return ret; + if (capacity < DA9052_BAT_LOW_CAP) + bat->health = POWER_SUPPLY_HEALTH_DEAD; + else + bat->health = POWER_SUPPLY_HEALTH_GOOD; + } + + *health = bat->health; + + return 0; +} + +static irqreturn_t da9052_bat_irq(int irq, void *data) +{ + struct da9052_battery *bat = data; + + irq -= bat->da9052->irq_base; + + if (irq == DA9052_IRQ_CHGEND) + bat->status = POWER_SUPPLY_STATUS_FULL; + else + da9052_bat_check_status(bat, NULL); + + if (irq == DA9052_IRQ_CHGEND || irq == DA9052_IRQ_DCIN || + irq == DA9052_IRQ_VBUS || irq == DA9052_IRQ_TBAT) { + power_supply_changed(&bat->psy); + } + + return IRQ_HANDLED; +} + +static int da9052_USB_current_notifier(struct notifier_block *nb, + unsigned long events, void *data) +{ + u8 row; + u8 col; + int *current_mA = data; + int ret; + struct da9052_battery *bat = container_of(nb, struct da9052_battery, + nb); + + if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING) + return -EPERM; + + ret = da9052_reg_read(bat->da9052, DA9052_CHGBUCK_REG); + if (ret & DA9052_CHG_USB_ILIM_MASK) + return -EPERM; + + if (bat->da9052->chip_id == DA9052) + row = 0; + else + row = 1; + + if (*current_mA < da9052_chg_current_lim[row][0] || + *current_mA > da9052_chg_current_lim[row][DA9052_CHG_LIM_COLS - 1]) + return -EINVAL; + + for (col = 0; col <= DA9052_CHG_LIM_COLS - 1 ; col++) { + if (*current_mA <= da9052_chg_current_lim[row][col]) + break; + } + + return da9052_reg_update(bat->da9052, DA9052_ISET_REG, + DA9052_ISET_USB_MASK, col); +} + +static int da9052_bat_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret; + int illegal; + struct da9052_battery *bat = container_of(psy, struct da9052_battery, + psy); + + ret = da9052_bat_check_presence(bat, &illegal); + if (ret < 0) + return ret; + + if (illegal && psp != POWER_SUPPLY_PROP_PRESENT) + return -ENODEV; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + ret = da9052_bat_check_status(bat, &val->intval); + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = + (bat->charger_type == DA9052_NOCHARGER) ? 0 : 1; + break; + case POWER_SUPPLY_PROP_PRESENT: + ret = da9052_bat_check_presence(bat, &val->intval); + break; + case POWER_SUPPLY_PROP_HEALTH: + ret = da9052_bat_check_health(bat, &val->intval); + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + val->intval = DA9052_BAT_CUTOFF_VOLT * 1000; + break; + case POWER_SUPPLY_PROP_VOLTAGE_AVG: + ret = da9052_bat_read_volt(bat, &val->intval); + break; + case POWER_SUPPLY_PROP_CURRENT_AVG: + ret = da9052_read_chg_current(bat, &val->intval); + break; + case POWER_SUPPLY_PROP_CAPACITY: + ret = da9052_bat_read_capacity(bat, &val->intval); + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = da9052_adc_read_temp(bat->da9052); + ret = val->intval; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + default: + return -EINVAL; + } + return ret; +} + +static enum power_supply_property da9052_bat_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_AVG, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TECHNOLOGY, +}; + +static struct power_supply template_battery = { + .name = "da9052-bat", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = da9052_bat_props, + .num_properties = ARRAY_SIZE(da9052_bat_props), + .get_property = da9052_bat_get_property, +}; + +static const char *const da9052_bat_irqs[] = { + "BATT TEMP", + "DCIN DET", + "DCIN REM", + "VBUS DET", + "VBUS REM", + "CHG END", +}; + +static s32 __devinit da9052_bat_probe(struct platform_device *pdev) +{ + struct da9052_pdata *pdata; + struct da9052_battery *bat; + int ret; + int irq; + int i; + + bat = kzalloc(sizeof(struct da9052_battery), GFP_KERNEL); + if (!bat) + return -ENOMEM; + + bat->da9052 = dev_get_drvdata(pdev->dev.parent); + bat->psy = template_battery; + bat->charger_type = DA9052_NOCHARGER; + bat->status = POWER_SUPPLY_STATUS_UNKNOWN; + bat->health = POWER_SUPPLY_HEALTH_UNKNOWN; + bat->nb.notifier_call = da9052_USB_current_notifier; + + pdata = bat->da9052->dev->platform_data; + if (pdata != NULL && pdata->use_for_apm) + bat->psy.use_for_apm = pdata->use_for_apm; + else + bat->psy.use_for_apm = 1; + + for (i = 0; i < ARRAY_SIZE(da9052_bat_irqs); i++) { + irq = platform_get_irq_byname(pdev, da9052_bat_irqs[i]); + ret = request_threaded_irq(bat->da9052->irq_base + irq, + NULL, da9052_bat_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + da9052_bat_irqs[i], bat); + if (ret != 0) { + dev_err(bat->da9052->dev, + "DA9052 failed to request %s IRQ %d: %d\n", + da9052_bat_irqs[i], irq, ret); + goto err; + } + } + + ret = power_supply_register(&pdev->dev, &bat->psy); + if (ret) + goto err; + + return 0; + +err: + for (; i >= 0; i--) { + irq = platform_get_irq_byname(pdev, da9052_bat_irqs[i]); + free_irq(bat->da9052->irq_base + irq, bat); + } + kfree(bat); + return ret; +} +static int __devexit da9052_bat_remove(struct platform_device *pdev) +{ + int i; + int irq; + struct da9052_battery *bat = platform_get_drvdata(pdev); + + for (i = 0; i < ARRAY_SIZE(da9052_bat_irqs); i++) { + irq = platform_get_irq_byname(pdev, da9052_bat_irqs[i]); + free_irq(bat->da9052->irq_base + irq, bat); + } + power_supply_unregister(&bat->psy); + + return 0; +} + +static struct platform_driver da9052_bat_driver = { + .probe = da9052_bat_probe, + .remove = __devexit_p(da9052_bat_remove), + .driver = { + .name = "da9052-bat", + .owner = THIS_MODULE, + }, +}; + +static int __init da9052_bat_init(void) +{ + return platform_driver_register(&da9052_bat_driver); +} +module_init(da9052_bat_init); + +static void __exit da9052_bat_exit(void) +{ + platform_driver_unregister(&da9052_bat_driver); +} +module_exit(da9052_bat_exit); + +MODULE_DESCRIPTION("DA9052 BAT Device Driver"); +MODULE_AUTHOR("David Dajun Chen "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:da9052-bat"); -- cgit v1.1 From 34aed73df3a9e75e313a7510b201f6755ae3e6bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heiko=20St=C3=BCbner?= Date: Thu, 29 Dec 2011 12:52:07 +0100 Subject: s3c_adc_battery: Average over more than one adc sample Some sources for adc battery information provide only inaccurate results where the read value differs from the real value with positive and negative offsets. For such sources it can be more accurate to collect two or more value sample and use the average of all collected values. This patch adds pdata options volt_samples, current_samples and backup_volt_samples to specifiy the number of samples to collect, reads the specified number of samples and calculates the average of those. For unset sample-number-values a default of 1 is assumed. Signed-off-by: Heiko Stuebner Signed-off-by: Anton Vorontsov --- drivers/power/s3c_adc_battery.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) (limited to 'drivers/power') diff --git a/drivers/power/s3c_adc_battery.c b/drivers/power/s3c_adc_battery.c index e687ee7..8b804a5 100644 --- a/drivers/power/s3c_adc_battery.c +++ b/drivers/power/s3c_adc_battery.c @@ -47,6 +47,22 @@ static void s3c_adc_bat_ext_power_changed(struct power_supply *psy) msecs_to_jiffies(JITTER_DELAY)); } +static int gather_samples(struct s3c_adc_client *client, int num, int channel) +{ + int value, i; + + /* default to 1 if nothing is set */ + if (num < 1) + num = 1; + + value = 0; + for (i = 0; i < num; i++) + value += s3c_adc_read(client, channel); + value /= num; + + return value; +} + static enum power_supply_property s3c_adc_backup_bat_props[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_VOLTAGE_MIN, @@ -67,7 +83,8 @@ static int s3c_adc_backup_bat_get_property(struct power_supply *psy, if (bat->volt_value < 0 || jiffies_to_msecs(jiffies - bat->timestamp) > BAT_POLL_INTERVAL) { - bat->volt_value = s3c_adc_read(bat->client, + bat->volt_value = gather_samples(bat->client, + bat->pdata->backup_volt_samples, bat->pdata->backup_volt_channel); bat->volt_value *= bat->pdata->backup_volt_mult; bat->timestamp = jiffies; @@ -139,9 +156,11 @@ static int s3c_adc_bat_get_property(struct power_supply *psy, if (bat->volt_value < 0 || bat->cur_value < 0 || jiffies_to_msecs(jiffies - bat->timestamp) > BAT_POLL_INTERVAL) { - bat->volt_value = s3c_adc_read(bat->client, + bat->volt_value = gather_samples(bat->client, + bat->pdata->volt_samples, bat->pdata->volt_channel) * bat->pdata->volt_mult; - bat->cur_value = s3c_adc_read(bat->client, + bat->cur_value = gather_samples(bat->client, + bat->pdata->current_samples, bat->pdata->current_channel) * bat->pdata->current_mult; bat->timestamp = jiffies; } -- cgit v1.1 From 93278d151e7bd35ccd0e083d7f2d8123cbaf36f8 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 5 Jan 2012 19:17:25 +0400 Subject: power_supply: Drop usage of nowarn variant of sysfs_create_link() The function is not exported to modules, plus we do want to catch anyone who tries to create complex hierarchy (in that case we'd need to change 'powers' symlink to a directory, probably under a different name to not break ABI). This patch fixes the following build error: ERROR: "sysfs_create_link_nowarn" [drivers/power/power_supply.ko] undefined! Reported-by: Stephen Rothwell Signed-off-by: Anton Vorontsov --- drivers/power/power_supply_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/power') diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index bc82f95..6ad6127 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -159,7 +159,7 @@ EXPORT_SYMBOL_GPL(power_supply_get_by_name); int power_supply_powers(struct power_supply *psy, struct device *dev) { - return sysfs_create_link_nowarn(&psy->dev->kobj, &dev->kobj, "powers"); + return sysfs_create_link(&psy->dev->kobj, &dev->kobj, "powers"); } EXPORT_SYMBOL_GPL(power_supply_powers); -- cgit v1.1 From 6cfc2a23540667cff6da6e41d1f1167a9a45aa9a Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Tue, 10 Jan 2012 04:53:37 +0400 Subject: power_supply: Mark da9052 driver as broken There are some problems with MFD part of this driver, so the driver fails to build: drivers/power/da9052-battery.c: In function 'da9052_bat_read_volt': drivers/power/da9052-battery.c:293:2: error: implicit declaration of function 'da9052_adc_manual_read' [-Werror=implicit-function-declaration] drivers/power/da9052-battery.c: In function 'da9052_bat_check_presence': drivers/power/da9052-battery.c:306:2: error: implicit declaration of function 'da9052_adc_read_temp' [-Werror=implicit-function-declaration] drivers/power/da9052-battery.c: In function 'da9052_determine_vc_tbl_index': drivers/power/da9052-battery.c:348:1: warning: control reaches end of non-void function [-Wreturn-type] cc1: some warnings being treated as errors The fix for MFD part will probably go post -rc1 (or in the next merge window), so let's disable the driver for now. Reported-by: Stephen Rothwell Signed-off-by: Anton Vorontsov --- drivers/power/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/power') diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index ecc1b49..3a8daf8 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -153,6 +153,7 @@ config BATTERY_DA9030 config BATTERY_DA9052 tristate "Dialog DA9052 Battery" depends on PMIC_DA9052 + depends on BROKEN help Say Y here to enable support for batteries charger integrated into DA9052 PMIC. -- cgit v1.1