diff options
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/pcf50633-charger.c | 73 |
1 files changed, 71 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; |