From 31b4ff06e01a9a98a8e6ae6e8c42213648eec1d1 Mon Sep 17 00:00:00 2001 From: Balaji Rao Date: Thu, 5 Nov 2009 00:24:55 +0300 Subject: pcf50633: introduces battery charging current control Implement a new sysfs attribute to allow changing MBC charging limit on the fly independently of usb current limit. It also gets set automatically every time usb current limit is changed. Limiting charging current also prevents violating USB specification in the case when the whole device is shut down and usb current limit is reset to the factory default by the pcf50633 state transition. Signed-off-by: Balaji Rao Signed-off-by: Paul Fertser Signed-off-by: Anton Vorontsov --- include/linux/mfd/pcf50633/core.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include') diff --git a/include/linux/mfd/pcf50633/core.h b/include/linux/mfd/pcf50633/core.h index 9aba7b779fbc..09af8fdfbb5d 100644 --- a/include/linux/mfd/pcf50633/core.h +++ b/include/linux/mfd/pcf50633/core.h @@ -31,6 +31,13 @@ struct pcf50633_platform_data { int charging_restart_interval; + /* + * Should be set accordingly to the reference resistor used, see + * I_{ch(ref)} charger reference current in the pcf50633 User + * Manual. + */ + int charger_reference_current_ma; + /* Callbacks */ void (*probe_done)(struct pcf50633 *); void (*mbc_event_callback)(struct pcf50633 *, int); -- cgit v1.2.3-55-g7522 From e98c73a24f33d6f54402f5cef2e7bf282d1d1fcc Mon Sep 17 00:00:00 2001 From: Paul Fertser Date: Thu, 5 Nov 2009 00:24:57 +0300 Subject: pcf50633: Get rid of charging restart software auto-triggering After reaching Battery Full condition MBC state machine switches back into charging mode when the battery voltage falls below 96% of a battery float voltage. The voltage drop in Li-Ion batteries is marginal (1-2%) till about 80% of its capacity - which means, after a BATFULL, charging won't be restarted until 75-80%. That is a desired behaviour recommended by battery manufacturers, don't mess with it. Signed-off-by: Paul Fertser Signed-off-by: Anton Vorontsov --- drivers/power/pcf50633-charger.c | 46 --------------------------------------- include/linux/mfd/pcf50633/core.h | 2 -- 2 files changed, 48 deletions(-) (limited to 'include') diff --git a/drivers/power/pcf50633-charger.c b/drivers/power/pcf50633-charger.c index 21b6e64e7805..338311996eea 100644 --- a/drivers/power/pcf50633-charger.c +++ b/drivers/power/pcf50633-charger.c @@ -37,8 +37,6 @@ struct pcf50633_mbc { struct power_supply usb; struct power_supply adapter; struct power_supply ac; - - struct delayed_work charging_restart_work; }; int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma) @@ -236,44 +234,10 @@ static struct attribute_group mbc_attr_group = { .attrs = pcf50633_mbc_sysfs_entries, }; -/* MBC state machine switches into charging mode when the battery voltage - * falls below 96% of a battery float voltage. But the voltage drop in Li-ion - * batteries is marginal(1~2 %) till about 80% of its capacity - which means, - * after a BATFULL, charging won't be restarted until 80%. - * - * This work_struct function restarts charging at regular intervals to make - * sure we don't discharge too much - */ - -static void pcf50633_mbc_charging_restart(struct work_struct *work) -{ - struct pcf50633_mbc *mbc; - u8 mbcs2, chgmod; - - mbc = container_of(work, struct pcf50633_mbc, - charging_restart_work.work); - - mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2); - chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK); - - if (chgmod != PCF50633_MBCS2_MBC_BAT_FULL) - return; - - /* Restart charging */ - pcf50633_reg_set_bit_mask(mbc->pcf, PCF50633_REG_MBCC1, - PCF50633_MBCC1_RESUME, PCF50633_MBCC1_RESUME); - mbc->usb_active = 1; - power_supply_changed(&mbc->usb); - - dev_info(mbc->pcf->dev, "Charging restarted\n"); -} - static void pcf50633_mbc_irq_handler(int irq, void *data) { struct pcf50633_mbc *mbc = data; - int chg_restart_interval = - mbc->pcf->pdata->charging_restart_interval; /* USB */ if (irq == PCF50633_IRQ_USBINS) { @@ -282,7 +246,6 @@ pcf50633_mbc_irq_handler(int irq, void *data) mbc->usb_online = 0; mbc->usb_active = 0; pcf50633_mbc_usb_curlim_set(mbc->pcf, 0); - cancel_delayed_work_sync(&mbc->charging_restart_work); } /* Adapter */ @@ -297,10 +260,6 @@ pcf50633_mbc_irq_handler(int irq, void *data) if (irq == PCF50633_IRQ_BATFULL) { mbc->usb_active = 0; mbc->adapter_active = 0; - - if (chg_restart_interval > 0) - schedule_delayed_work(&mbc->charging_restart_work, - chg_restart_interval); } else if (irq == PCF50633_IRQ_USBLIMON) mbc->usb_active = 0; else if (irq == PCF50633_IRQ_USBLIMOFF) @@ -463,9 +422,6 @@ static int __devinit pcf50633_mbc_probe(struct platform_device *pdev) return ret; } - INIT_DELAYED_WORK(&mbc->charging_restart_work, - pcf50633_mbc_charging_restart); - ret = sysfs_create_group(&pdev->dev.kobj, &mbc_attr_group); if (ret) dev_err(mbc->pcf->dev, "failed to create sysfs entries\n"); @@ -492,8 +448,6 @@ static int __devexit pcf50633_mbc_remove(struct platform_device *pdev) power_supply_unregister(&mbc->adapter); power_supply_unregister(&mbc->ac); - cancel_delayed_work_sync(&mbc->charging_restart_work); - kfree(mbc); return 0; diff --git a/include/linux/mfd/pcf50633/core.h b/include/linux/mfd/pcf50633/core.h index 09af8fdfbb5d..46df7f053c29 100644 --- a/include/linux/mfd/pcf50633/core.h +++ b/include/linux/mfd/pcf50633/core.h @@ -29,8 +29,6 @@ struct pcf50633_platform_data { char **batteries; int num_batteries; - int charging_restart_interval; - /* * Should be set accordingly to the reference resistor used, see * I_{ch(ref)} charger reference current in the pcf50633 User -- cgit v1.2.3-55-g7522 From c329795052aa339850a45fab649ab97a36905136 Mon Sep 17 00:00:00 2001 From: Paul Fertser Date: Thu, 5 Nov 2009 00:24:59 +0300 Subject: pcf50633: Query charger status directly Current scheme is fragile and is likely to go off sync, especially on batfull->adapter charging automatic MBC transition. Query the status bit every time we need it instead. We need to export another function to query for USB presence because we can't read anything from PCF50633 (via I2C) inside irq context and that is needed by usb gadgets. Signed-off-by: Paul Fertser Signed-off-by: Anton Vorontsov --- drivers/power/pcf50633-charger.c | 50 +++++++++++++++++++++++----------------- include/linux/mfd/pcf50633/mbc.h | 1 + 2 files changed, 30 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/power/pcf50633-charger.c b/drivers/power/pcf50633-charger.c index 47187337b96c..b915a008f587 100644 --- a/drivers/power/pcf50633-charger.c +++ b/drivers/power/pcf50633-charger.c @@ -29,9 +29,7 @@ struct pcf50633_mbc { struct pcf50633 *pcf; - int adapter_active; int adapter_online; - int usb_active; int usb_online; struct power_supply usb; @@ -88,7 +86,7 @@ int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma) pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5); } - mbcs2 = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2); + mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2); chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK); /* If chgmod == BATFULL, setting chgena has no effect. @@ -105,8 +103,6 @@ int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma) PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA); } - mbc->usb_active = charging_start; - power_supply_changed(&mbc->usb); return ret; @@ -117,20 +113,44 @@ int pcf50633_mbc_get_status(struct pcf50633 *pcf) { struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev); int status = 0; + u8 chgmod; + + if (!mbc) + return 0; + + chgmod = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2) + & PCF50633_MBCS2_MBC_MASK; if (mbc->usb_online) status |= PCF50633_MBC_USB_ONLINE; - if (mbc->usb_active) + if (chgmod == PCF50633_MBCS2_MBC_USB_PRE || + chgmod == PCF50633_MBCS2_MBC_USB_PRE_WAIT || + chgmod == PCF50633_MBCS2_MBC_USB_FAST || + chgmod == PCF50633_MBCS2_MBC_USB_FAST_WAIT) status |= PCF50633_MBC_USB_ACTIVE; if (mbc->adapter_online) status |= PCF50633_MBC_ADAPTER_ONLINE; - if (mbc->adapter_active) + if (chgmod == PCF50633_MBCS2_MBC_ADP_PRE || + chgmod == PCF50633_MBCS2_MBC_ADP_PRE_WAIT || + chgmod == PCF50633_MBCS2_MBC_ADP_FAST || + chgmod == PCF50633_MBCS2_MBC_ADP_FAST_WAIT) status |= PCF50633_MBC_ADAPTER_ACTIVE; return status; } EXPORT_SYMBOL_GPL(pcf50633_mbc_get_status); +int pcf50633_mbc_get_usb_online_status(struct pcf50633 *pcf) +{ + struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev); + + if (!mbc) + return 0; + + return mbc->usb_online; +} +EXPORT_SYMBOL_GPL(pcf50633_mbc_get_usb_online_status); + static ssize_t show_chgmode(struct device *dev, struct device_attribute *attr, char *buf) { @@ -248,26 +268,14 @@ pcf50633_mbc_irq_handler(int irq, void *data) mbc->usb_online = 1; } else if (irq == PCF50633_IRQ_USBREM) { mbc->usb_online = 0; - mbc->usb_active = 0; pcf50633_mbc_usb_curlim_set(mbc->pcf, 0); } /* Adapter */ - if (irq == PCF50633_IRQ_ADPINS) { + if (irq == PCF50633_IRQ_ADPINS) mbc->adapter_online = 1; - mbc->adapter_active = 1; - } else if (irq == PCF50633_IRQ_ADPREM) { + else if (irq == PCF50633_IRQ_ADPREM) mbc->adapter_online = 0; - mbc->adapter_active = 0; - } - - if (irq == PCF50633_IRQ_BATFULL) { - mbc->usb_active = 0; - mbc->adapter_active = 0; - } else if (irq == PCF50633_IRQ_USBLIMON) - mbc->usb_active = 0; - else if (irq == PCF50633_IRQ_USBLIMOFF) - mbc->usb_active = 1; power_supply_changed(&mbc->ac); power_supply_changed(&mbc->usb); diff --git a/include/linux/mfd/pcf50633/mbc.h b/include/linux/mfd/pcf50633/mbc.h index 4119579acf2c..df4f5fa88de3 100644 --- a/include/linux/mfd/pcf50633/mbc.h +++ b/include/linux/mfd/pcf50633/mbc.h @@ -128,6 +128,7 @@ enum pcf50633_reg_mbcs3 { int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma); int pcf50633_mbc_get_status(struct pcf50633 *); +int pcf50633_mbc_get_usb_online_status(struct pcf50633 *); #endif -- cgit v1.2.3-55-g7522