diff options
| -rw-r--r-- | drivers/power/pcf50633-charger.c | 73 | ||||
| -rw-r--r-- | include/linux/mfd/pcf50633/core.h | 2 |
2 files changed, 73 insertions, 2 deletions
diff --git a/drivers/power/pcf50633-charger.c b/drivers/power/pcf50633-charger.c index 41aec2acbb91..1fe1e851a8dd 100644 --- a/drivers/power/pcf50633-charger.c +++ b/drivers/power/pcf50633-charger.c | |||
| @@ -36,6 +36,8 @@ struct pcf50633_mbc { | |||
| 36 | 36 | ||
| 37 | struct power_supply usb; | 37 | struct power_supply usb; |
| 38 | struct power_supply adapter; | 38 | struct power_supply adapter; |
| 39 | |||
| 40 | struct delayed_work charging_restart_work; | ||
| 39 | }; | 41 | }; |
| 40 | 42 | ||
| 41 | int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma) | 43 | int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma) |
| @@ -43,6 +45,8 @@ int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma) | |||
| 43 | struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev); | 45 | struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev); |
| 44 | int ret = 0; | 46 | int ret = 0; |
| 45 | u8 bits; | 47 | u8 bits; |
| 48 | int charging_start = 1; | ||
| 49 | u8 mbcs2, chgmod; | ||
| 46 | 50 | ||
| 47 | if (ma >= 1000) | 51 | if (ma >= 1000) |
| 48 | bits = PCF50633_MBCC7_USB_1000mA; | 52 | bits = PCF50633_MBCC7_USB_1000mA; |
| @@ -50,8 +54,10 @@ int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma) | |||
| 50 | bits = PCF50633_MBCC7_USB_500mA; | 54 | bits = PCF50633_MBCC7_USB_500mA; |
| 51 | else if (ma >= 100) | 55 | else if (ma >= 100) |
| 52 | bits = PCF50633_MBCC7_USB_100mA; | 56 | bits = PCF50633_MBCC7_USB_100mA; |
| 53 | else | 57 | else { |
| 54 | bits = PCF50633_MBCC7_USB_SUSPEND; | 58 | bits = PCF50633_MBCC7_USB_SUSPEND; |
| 59 | charging_start = 0; | ||
| 60 | } | ||
| 55 | 61 | ||
| 56 | ret = pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC7, | 62 | ret = pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC7, |
| 57 | PCF50633_MBCC7_USB_MASK, bits); | 63 | PCF50633_MBCC7_USB_MASK, bits); |
| @@ -60,6 +66,22 @@ int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma) | |||
| 60 | else | 66 | else |
| 61 | dev_info(pcf->dev, "usb curlim to %d mA\n", ma); | 67 | dev_info(pcf->dev, "usb curlim to %d mA\n", ma); |
| 62 | 68 | ||
| 69 | /* Manual charging start */ | ||
| 70 | mbcs2 = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2); | ||
| 71 | chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK); | ||
| 72 | |||
| 73 | /* If chgmod == BATFULL, setting chgena has no effect. | ||
| 74 | * We need to set resume instead. | ||
| 75 | */ | ||
| 76 | if (chgmod != PCF50633_MBCS2_MBC_BAT_FULL) | ||
| 77 | pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1, | ||
| 78 | PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA); | ||
| 79 | else | ||
| 80 | pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1, | ||
| 81 | PCF50633_MBCC1_RESUME, PCF50633_MBCC1_RESUME); | ||
| 82 | |||
| 83 | mbc->usb_active = charging_start; | ||
| 84 | |||
| 63 | power_supply_changed(&mbc->usb); | 85 | power_supply_changed(&mbc->usb); |
| 64 | 86 | ||
| 65 | return ret; | 87 | return ret; |
| @@ -160,10 +182,44 @@ static struct attribute_group mbc_attr_group = { | |||
| 160 | .attrs = pcf50633_mbc_sysfs_entries, | 182 | .attrs = pcf50633_mbc_sysfs_entries, |
| 161 | }; | 183 | }; |
| 162 | 184 | ||
| 185 | /* MBC state machine switches into charging mode when the battery voltage | ||
| 186 | * falls below 96% of a battery float voltage. But the voltage drop in Li-ion | ||
| 187 | * batteries is marginal(1~2 %) till about 80% of its capacity - which means, | ||
| 188 | * after a BATFULL, charging won't be restarted until 80%. | ||
| 189 | * | ||
| 190 | * This work_struct function restarts charging at regular intervals to make | ||
| 191 | * sure we don't discharge too much | ||
| 192 | */ | ||
| 193 | |||
| 194 | static void pcf50633_mbc_charging_restart(struct work_struct *work) | ||
| 195 | { | ||
| 196 | struct pcf50633_mbc *mbc; | ||
| 197 | u8 mbcs2, chgmod; | ||
| 198 | |||
| 199 | mbc = container_of(work, struct pcf50633_mbc, | ||
| 200 | charging_restart_work.work); | ||
| 201 | |||
| 202 | mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2); | ||
| 203 | chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK); | ||
| 204 | |||
| 205 | if (chgmod != PCF50633_MBCS2_MBC_BAT_FULL) | ||
| 206 | return; | ||
| 207 | |||
| 208 | /* Restart charging */ | ||
| 209 | pcf50633_reg_set_bit_mask(mbc->pcf, PCF50633_REG_MBCC1, | ||
| 210 | PCF50633_MBCC1_RESUME, PCF50633_MBCC1_RESUME); | ||
| 211 | mbc->usb_active = 1; | ||
| 212 | power_supply_changed(&mbc->usb); | ||
| 213 | |||
| 214 | dev_info(mbc->pcf->dev, "Charging restarted\n"); | ||
| 215 | } | ||
| 216 | |||
| 163 | static void | 217 | static void |
| 164 | pcf50633_mbc_irq_handler(int irq, void *data) | 218 | pcf50633_mbc_irq_handler(int irq, void *data) |
| 165 | { | 219 | { |
| 166 | struct pcf50633_mbc *mbc = data; | 220 | struct pcf50633_mbc *mbc = data; |
| 221 | int chg_restart_interval = | ||
| 222 | mbc->pcf->pdata->charging_restart_interval; | ||
| 167 | 223 | ||
| 168 | /* USB */ | 224 | /* USB */ |
| 169 | if (irq == PCF50633_IRQ_USBINS) { | 225 | if (irq == PCF50633_IRQ_USBINS) { |
| @@ -172,6 +228,7 @@ pcf50633_mbc_irq_handler(int irq, void *data) | |||
| 172 | mbc->usb_online = 0; | 228 | mbc->usb_online = 0; |
| 173 | mbc->usb_active = 0; | 229 | mbc->usb_active = 0; |
| 174 | pcf50633_mbc_usb_curlim_set(mbc->pcf, 0); | 230 | pcf50633_mbc_usb_curlim_set(mbc->pcf, 0); |
| 231 | cancel_delayed_work_sync(&mbc->charging_restart_work); | ||
| 175 | } | 232 | } |
| 176 | 233 | ||
| 177 | /* Adapter */ | 234 | /* Adapter */ |
| @@ -186,7 +243,14 @@ pcf50633_mbc_irq_handler(int irq, void *data) | |||
| 186 | if (irq == PCF50633_IRQ_BATFULL) { | 243 | if (irq == PCF50633_IRQ_BATFULL) { |
| 187 | mbc->usb_active = 0; | 244 | mbc->usb_active = 0; |
| 188 | mbc->adapter_active = 0; | 245 | mbc->adapter_active = 0; |
| 189 | } | 246 | |
| 247 | if (chg_restart_interval > 0) | ||
| 248 | schedule_delayed_work(&mbc->charging_restart_work, | ||
| 249 | chg_restart_interval); | ||
| 250 | } else if (irq == PCF50633_IRQ_USBLIMON) | ||
| 251 | mbc->usb_active = 0; | ||
| 252 | else if (irq == PCF50633_IRQ_USBLIMOFF) | ||
| 253 | mbc->usb_active = 1; | ||
| 190 | 254 | ||
| 191 | power_supply_changed(&mbc->usb); | 255 | power_supply_changed(&mbc->usb); |
| 192 | power_supply_changed(&mbc->adapter); | 256 | power_supply_changed(&mbc->adapter); |
| @@ -303,6 +367,9 @@ static int __devinit pcf50633_mbc_probe(struct platform_device *pdev) | |||
| 303 | return ret; | 367 | return ret; |
| 304 | } | 368 | } |
| 305 | 369 | ||
| 370 | INIT_DELAYED_WORK(&mbc->charging_restart_work, | ||
| 371 | pcf50633_mbc_charging_restart); | ||
| 372 | |||
| 306 | ret = sysfs_create_group(&pdev->dev.kobj, &mbc_attr_group); | 373 | ret = sysfs_create_group(&pdev->dev.kobj, &mbc_attr_group); |
| 307 | if (ret) | 374 | if (ret) |
| 308 | dev_err(mbc->pcf->dev, "failed to create sysfs entries\n"); | 375 | dev_err(mbc->pcf->dev, "failed to create sysfs entries\n"); |
| @@ -328,6 +395,8 @@ static int __devexit pcf50633_mbc_remove(struct platform_device *pdev) | |||
| 328 | power_supply_unregister(&mbc->usb); | 395 | power_supply_unregister(&mbc->usb); |
| 329 | power_supply_unregister(&mbc->adapter); | 396 | power_supply_unregister(&mbc->adapter); |
| 330 | 397 | ||
| 398 | cancel_delayed_work_sync(&mbc->charging_restart_work); | ||
| 399 | |||
| 331 | kfree(mbc); | 400 | kfree(mbc); |
| 332 | 401 | ||
| 333 | return 0; | 402 | return 0; |
diff --git a/include/linux/mfd/pcf50633/core.h b/include/linux/mfd/pcf50633/core.h index 4455b212d75a..c8f51c3c0a72 100644 --- a/include/linux/mfd/pcf50633/core.h +++ b/include/linux/mfd/pcf50633/core.h | |||
| @@ -29,6 +29,8 @@ struct pcf50633_platform_data { | |||
| 29 | char **batteries; | 29 | char **batteries; |
| 30 | int num_batteries; | 30 | int num_batteries; |
| 31 | 31 | ||
| 32 | int charging_restart_interval; | ||
| 33 | |||
| 32 | /* Callbacks */ | 34 | /* Callbacks */ |
| 33 | void (*probe_done)(struct pcf50633 *); | 35 | void (*probe_done)(struct pcf50633 *); |
| 34 | void (*mbc_event_callback)(struct pcf50633 *, int); | 36 | void (*mbc_event_callback)(struct pcf50633 *, int); |
