diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-12-15 11:59:33 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-12-15 11:59:33 -0500 |
commit | 48e902f0a3aea4b6b3a73e9d277b92024a493e6d (patch) | |
tree | 6dbb3a4c5ab4e7fc72172d8c11e7c0418b6559d1 /drivers/power/pcf50633-charger.c | |
parent | 7f6cd5408a8ace522ca7f15893243e94ccc913e0 (diff) | |
parent | 9d233e8bb92e355fd20b14745c1d9ff402e0e685 (diff) |
Merge git://git.infradead.org/battery-2.6
* git://git.infradead.org/battery-2.6:
power_supply_sysfs: Handle -ENODATA in a special way
wm831x_backup: Remove unused variables
gta02: Set pcf50633 charger_reference_current_ma
pcf50633: Query charger status directly
pcf50633: Properly reenable charging when the supply conditions change
pcf50633: Get rid of charging restart software auto-triggering
pcf50633: introduces battery charging current control
pcf50633: Add ac power supply class to the charger
wm831x: Factor out WM831x backup battery charger
Diffstat (limited to 'drivers/power/pcf50633-charger.c')
-rw-r--r-- | drivers/power/pcf50633-charger.c | 228 |
1 files changed, 153 insertions, 75 deletions
diff --git a/drivers/power/pcf50633-charger.c b/drivers/power/pcf50633-charger.c index 6a84a8eb8d7a..ea3fdfaca90d 100644 --- a/drivers/power/pcf50633-charger.c +++ b/drivers/power/pcf50633-charger.c | |||
@@ -29,15 +29,12 @@ | |||
29 | struct pcf50633_mbc { | 29 | struct pcf50633_mbc { |
30 | struct pcf50633 *pcf; | 30 | struct pcf50633 *pcf; |
31 | 31 | ||
32 | int adapter_active; | ||
33 | int adapter_online; | 32 | int adapter_online; |
34 | int usb_active; | ||
35 | int usb_online; | 33 | int usb_online; |
36 | 34 | ||
37 | struct power_supply usb; | 35 | struct power_supply usb; |
38 | struct power_supply adapter; | 36 | struct power_supply adapter; |
39 | 37 | struct power_supply ac; | |
40 | struct delayed_work charging_restart_work; | ||
41 | }; | 38 | }; |
42 | 39 | ||
43 | int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma) | 40 | int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma) |
@@ -47,16 +44,21 @@ int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma) | |||
47 | u8 bits; | 44 | u8 bits; |
48 | int charging_start = 1; | 45 | int charging_start = 1; |
49 | u8 mbcs2, chgmod; | 46 | u8 mbcs2, chgmod; |
47 | unsigned int mbcc5; | ||
50 | 48 | ||
51 | if (ma >= 1000) | 49 | if (ma >= 1000) { |
52 | bits = PCF50633_MBCC7_USB_1000mA; | 50 | bits = PCF50633_MBCC7_USB_1000mA; |
53 | else if (ma >= 500) | 51 | ma = 1000; |
52 | } else if (ma >= 500) { | ||
54 | bits = PCF50633_MBCC7_USB_500mA; | 53 | bits = PCF50633_MBCC7_USB_500mA; |
55 | else if (ma >= 100) | 54 | ma = 500; |
55 | } else if (ma >= 100) { | ||
56 | bits = PCF50633_MBCC7_USB_100mA; | 56 | bits = PCF50633_MBCC7_USB_100mA; |
57 | else { | 57 | ma = 100; |
58 | } else { | ||
58 | bits = PCF50633_MBCC7_USB_SUSPEND; | 59 | bits = PCF50633_MBCC7_USB_SUSPEND; |
59 | charging_start = 0; | 60 | charging_start = 0; |
61 | ma = 0; | ||
60 | } | 62 | } |
61 | 63 | ||
62 | ret = pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC7, | 64 | ret = pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC7, |
@@ -66,21 +68,40 @@ int pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma) | |||
66 | else | 68 | else |
67 | dev_info(pcf->dev, "usb curlim to %d mA\n", ma); | 69 | dev_info(pcf->dev, "usb curlim to %d mA\n", ma); |
68 | 70 | ||
69 | /* Manual charging start */ | 71 | /* |
70 | mbcs2 = pcf50633_reg_read(pcf, PCF50633_REG_MBCS2); | 72 | * We limit the charging current to be the USB current limit. |
73 | * The reason is that on pcf50633, when it enters PMU Standby mode, | ||
74 | * which it does when the device goes "off", the USB current limit | ||
75 | * reverts to the variant default. In at least one common case, that | ||
76 | * default is 500mA. By setting the charging current to be the same | ||
77 | * as the USB limit we set here before PMU standby, we enforce it only | ||
78 | * using the correct amount of current even when the USB current limit | ||
79 | * gets reset to the wrong thing | ||
80 | */ | ||
81 | |||
82 | if (mbc->pcf->pdata->charger_reference_current_ma) { | ||
83 | mbcc5 = (ma << 8) / mbc->pcf->pdata->charger_reference_current_ma; | ||
84 | if (mbcc5 > 255) | ||
85 | mbcc5 = 255; | ||
86 | pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5); | ||
87 | } | ||
88 | |||
89 | mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2); | ||
71 | chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK); | 90 | chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK); |
72 | 91 | ||
73 | /* If chgmod == BATFULL, setting chgena has no effect. | 92 | /* If chgmod == BATFULL, setting chgena has no effect. |
74 | * We need to set resume instead. | 93 | * Datasheet says we need to set resume instead but when autoresume is |
94 | * used resume doesn't work. Clear and set chgena instead. | ||
75 | */ | 95 | */ |
76 | if (chgmod != PCF50633_MBCS2_MBC_BAT_FULL) | 96 | if (chgmod != PCF50633_MBCS2_MBC_BAT_FULL) |
77 | pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1, | 97 | pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1, |
78 | PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA); | 98 | PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA); |
79 | else | 99 | else { |
100 | pcf50633_reg_clear_bits(pcf, PCF50633_REG_MBCC1, | ||
101 | PCF50633_MBCC1_CHGENA); | ||
80 | pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1, | 102 | pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1, |
81 | PCF50633_MBCC1_RESUME, PCF50633_MBCC1_RESUME); | 103 | PCF50633_MBCC1_CHGENA, PCF50633_MBCC1_CHGENA); |
82 | 104 | } | |
83 | mbc->usb_active = charging_start; | ||
84 | 105 | ||
85 | power_supply_changed(&mbc->usb); | 106 | power_supply_changed(&mbc->usb); |
86 | 107 | ||
@@ -92,20 +113,44 @@ int pcf50633_mbc_get_status(struct pcf50633 *pcf) | |||
92 | { | 113 | { |
93 | struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev); | 114 | struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev); |
94 | int status = 0; | 115 | int status = 0; |
116 | u8 chgmod; | ||
117 | |||
118 | if (!mbc) | ||
119 | return 0; | ||
120 | |||
121 | chgmod = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2) | ||
122 | & PCF50633_MBCS2_MBC_MASK; | ||
95 | 123 | ||
96 | if (mbc->usb_online) | 124 | if (mbc->usb_online) |
97 | status |= PCF50633_MBC_USB_ONLINE; | 125 | status |= PCF50633_MBC_USB_ONLINE; |
98 | if (mbc->usb_active) | 126 | if (chgmod == PCF50633_MBCS2_MBC_USB_PRE || |
127 | chgmod == PCF50633_MBCS2_MBC_USB_PRE_WAIT || | ||
128 | chgmod == PCF50633_MBCS2_MBC_USB_FAST || | ||
129 | chgmod == PCF50633_MBCS2_MBC_USB_FAST_WAIT) | ||
99 | status |= PCF50633_MBC_USB_ACTIVE; | 130 | status |= PCF50633_MBC_USB_ACTIVE; |
100 | if (mbc->adapter_online) | 131 | if (mbc->adapter_online) |
101 | status |= PCF50633_MBC_ADAPTER_ONLINE; | 132 | status |= PCF50633_MBC_ADAPTER_ONLINE; |
102 | if (mbc->adapter_active) | 133 | if (chgmod == PCF50633_MBCS2_MBC_ADP_PRE || |
134 | chgmod == PCF50633_MBCS2_MBC_ADP_PRE_WAIT || | ||
135 | chgmod == PCF50633_MBCS2_MBC_ADP_FAST || | ||
136 | chgmod == PCF50633_MBCS2_MBC_ADP_FAST_WAIT) | ||
103 | status |= PCF50633_MBC_ADAPTER_ACTIVE; | 137 | status |= PCF50633_MBC_ADAPTER_ACTIVE; |
104 | 138 | ||
105 | return status; | 139 | return status; |
106 | } | 140 | } |
107 | EXPORT_SYMBOL_GPL(pcf50633_mbc_get_status); | 141 | EXPORT_SYMBOL_GPL(pcf50633_mbc_get_status); |
108 | 142 | ||
143 | int pcf50633_mbc_get_usb_online_status(struct pcf50633 *pcf) | ||
144 | { | ||
145 | struct pcf50633_mbc *mbc = platform_get_drvdata(pcf->mbc_pdev); | ||
146 | |||
147 | if (!mbc) | ||
148 | return 0; | ||
149 | |||
150 | return mbc->usb_online; | ||
151 | } | ||
152 | EXPORT_SYMBOL_GPL(pcf50633_mbc_get_usb_online_status); | ||
153 | |||
109 | static ssize_t | 154 | static ssize_t |
110 | show_chgmode(struct device *dev, struct device_attribute *attr, char *buf) | 155 | show_chgmode(struct device *dev, struct device_attribute *attr, char *buf) |
111 | { | 156 | { |
@@ -156,9 +201,55 @@ static ssize_t set_usblim(struct device *dev, | |||
156 | 201 | ||
157 | static DEVICE_ATTR(usb_curlim, S_IRUGO | S_IWUSR, show_usblim, set_usblim); | 202 | static DEVICE_ATTR(usb_curlim, S_IRUGO | S_IWUSR, show_usblim, set_usblim); |
158 | 203 | ||
204 | static ssize_t | ||
205 | show_chglim(struct device *dev, struct device_attribute *attr, char *buf) | ||
206 | { | ||
207 | struct pcf50633_mbc *mbc = dev_get_drvdata(dev); | ||
208 | u8 mbcc5 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC5); | ||
209 | unsigned int ma; | ||
210 | |||
211 | if (!mbc->pcf->pdata->charger_reference_current_ma) | ||
212 | return -ENODEV; | ||
213 | |||
214 | ma = (mbc->pcf->pdata->charger_reference_current_ma * mbcc5) >> 8; | ||
215 | |||
216 | return sprintf(buf, "%u\n", ma); | ||
217 | } | ||
218 | |||
219 | static ssize_t set_chglim(struct device *dev, | ||
220 | struct device_attribute *attr, const char *buf, size_t count) | ||
221 | { | ||
222 | struct pcf50633_mbc *mbc = dev_get_drvdata(dev); | ||
223 | unsigned long ma; | ||
224 | unsigned int mbcc5; | ||
225 | int ret; | ||
226 | |||
227 | if (!mbc->pcf->pdata->charger_reference_current_ma) | ||
228 | return -ENODEV; | ||
229 | |||
230 | ret = strict_strtoul(buf, 10, &ma); | ||
231 | if (ret) | ||
232 | return -EINVAL; | ||
233 | |||
234 | mbcc5 = (ma << 8) / mbc->pcf->pdata->charger_reference_current_ma; | ||
235 | if (mbcc5 > 255) | ||
236 | mbcc5 = 255; | ||
237 | pcf50633_reg_write(mbc->pcf, PCF50633_REG_MBCC5, mbcc5); | ||
238 | |||
239 | return count; | ||
240 | } | ||
241 | |||
242 | /* | ||
243 | * This attribute allows to change MBC charging limit on the fly | ||
244 | * independently of usb current limit. It also gets set automatically every | ||
245 | * time usb current limit is changed. | ||
246 | */ | ||
247 | static DEVICE_ATTR(chg_curlim, S_IRUGO | S_IWUSR, show_chglim, set_chglim); | ||
248 | |||
159 | static struct attribute *pcf50633_mbc_sysfs_entries[] = { | 249 | static struct attribute *pcf50633_mbc_sysfs_entries[] = { |
160 | &dev_attr_chgmode.attr, | 250 | &dev_attr_chgmode.attr, |
161 | &dev_attr_usb_curlim.attr, | 251 | &dev_attr_usb_curlim.attr, |
252 | &dev_attr_chg_curlim.attr, | ||
162 | NULL, | 253 | NULL, |
163 | }; | 254 | }; |
164 | 255 | ||
@@ -167,76 +258,26 @@ static struct attribute_group mbc_attr_group = { | |||
167 | .attrs = pcf50633_mbc_sysfs_entries, | 258 | .attrs = pcf50633_mbc_sysfs_entries, |
168 | }; | 259 | }; |
169 | 260 | ||
170 | /* MBC state machine switches into charging mode when the battery voltage | ||
171 | * falls below 96% of a battery float voltage. But the voltage drop in Li-ion | ||
172 | * batteries is marginal(1~2 %) till about 80% of its capacity - which means, | ||
173 | * after a BATFULL, charging won't be restarted until 80%. | ||
174 | * | ||
175 | * This work_struct function restarts charging at regular intervals to make | ||
176 | * sure we don't discharge too much | ||
177 | */ | ||
178 | |||
179 | static void pcf50633_mbc_charging_restart(struct work_struct *work) | ||
180 | { | ||
181 | struct pcf50633_mbc *mbc; | ||
182 | u8 mbcs2, chgmod; | ||
183 | |||
184 | mbc = container_of(work, struct pcf50633_mbc, | ||
185 | charging_restart_work.work); | ||
186 | |||
187 | mbcs2 = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCS2); | ||
188 | chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK); | ||
189 | |||
190 | if (chgmod != PCF50633_MBCS2_MBC_BAT_FULL) | ||
191 | return; | ||
192 | |||
193 | /* Restart charging */ | ||
194 | pcf50633_reg_set_bit_mask(mbc->pcf, PCF50633_REG_MBCC1, | ||
195 | PCF50633_MBCC1_RESUME, PCF50633_MBCC1_RESUME); | ||
196 | mbc->usb_active = 1; | ||
197 | power_supply_changed(&mbc->usb); | ||
198 | |||
199 | dev_info(mbc->pcf->dev, "Charging restarted\n"); | ||
200 | } | ||
201 | |||
202 | static void | 261 | static void |
203 | pcf50633_mbc_irq_handler(int irq, void *data) | 262 | pcf50633_mbc_irq_handler(int irq, void *data) |
204 | { | 263 | { |
205 | struct pcf50633_mbc *mbc = data; | 264 | struct pcf50633_mbc *mbc = data; |
206 | int chg_restart_interval = | ||
207 | mbc->pcf->pdata->charging_restart_interval; | ||
208 | 265 | ||
209 | /* USB */ | 266 | /* USB */ |
210 | if (irq == PCF50633_IRQ_USBINS) { | 267 | if (irq == PCF50633_IRQ_USBINS) { |
211 | mbc->usb_online = 1; | 268 | mbc->usb_online = 1; |
212 | } else if (irq == PCF50633_IRQ_USBREM) { | 269 | } else if (irq == PCF50633_IRQ_USBREM) { |
213 | mbc->usb_online = 0; | 270 | mbc->usb_online = 0; |
214 | mbc->usb_active = 0; | ||
215 | pcf50633_mbc_usb_curlim_set(mbc->pcf, 0); | 271 | pcf50633_mbc_usb_curlim_set(mbc->pcf, 0); |
216 | cancel_delayed_work_sync(&mbc->charging_restart_work); | ||
217 | } | 272 | } |
218 | 273 | ||
219 | /* Adapter */ | 274 | /* Adapter */ |
220 | if (irq == PCF50633_IRQ_ADPINS) { | 275 | if (irq == PCF50633_IRQ_ADPINS) |
221 | mbc->adapter_online = 1; | 276 | mbc->adapter_online = 1; |
222 | mbc->adapter_active = 1; | 277 | else if (irq == PCF50633_IRQ_ADPREM) |
223 | } else if (irq == PCF50633_IRQ_ADPREM) { | ||
224 | mbc->adapter_online = 0; | 278 | mbc->adapter_online = 0; |
225 | mbc->adapter_active = 0; | ||
226 | } | ||
227 | |||
228 | if (irq == PCF50633_IRQ_BATFULL) { | ||
229 | mbc->usb_active = 0; | ||
230 | mbc->adapter_active = 0; | ||
231 | |||
232 | if (chg_restart_interval > 0) | ||
233 | schedule_delayed_work(&mbc->charging_restart_work, | ||
234 | chg_restart_interval); | ||
235 | } else if (irq == PCF50633_IRQ_USBLIMON) | ||
236 | mbc->usb_active = 0; | ||
237 | else if (irq == PCF50633_IRQ_USBLIMOFF) | ||
238 | mbc->usb_active = 1; | ||
239 | 279 | ||
280 | power_supply_changed(&mbc->ac); | ||
240 | power_supply_changed(&mbc->usb); | 281 | power_supply_changed(&mbc->usb); |
241 | power_supply_changed(&mbc->adapter); | 282 | power_supply_changed(&mbc->adapter); |
242 | 283 | ||
@@ -269,10 +310,34 @@ static int usb_get_property(struct power_supply *psy, | |||
269 | { | 310 | { |
270 | struct pcf50633_mbc *mbc = container_of(psy, struct pcf50633_mbc, usb); | 311 | struct pcf50633_mbc *mbc = container_of(psy, struct pcf50633_mbc, usb); |
271 | int ret = 0; | 312 | int ret = 0; |
313 | u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) & | ||
314 | PCF50633_MBCC7_USB_MASK; | ||
272 | 315 | ||
273 | switch (psp) { | 316 | switch (psp) { |
274 | case POWER_SUPPLY_PROP_ONLINE: | 317 | case POWER_SUPPLY_PROP_ONLINE: |
275 | val->intval = mbc->usb_online; | 318 | val->intval = mbc->usb_online && |
319 | (usblim <= PCF50633_MBCC7_USB_500mA); | ||
320 | break; | ||
321 | default: | ||
322 | ret = -EINVAL; | ||
323 | break; | ||
324 | } | ||
325 | return ret; | ||
326 | } | ||
327 | |||
328 | static int ac_get_property(struct power_supply *psy, | ||
329 | enum power_supply_property psp, | ||
330 | union power_supply_propval *val) | ||
331 | { | ||
332 | struct pcf50633_mbc *mbc = container_of(psy, struct pcf50633_mbc, ac); | ||
333 | int ret = 0; | ||
334 | u8 usblim = pcf50633_reg_read(mbc->pcf, PCF50633_REG_MBCC7) & | ||
335 | PCF50633_MBCC7_USB_MASK; | ||
336 | |||
337 | switch (psp) { | ||
338 | case POWER_SUPPLY_PROP_ONLINE: | ||
339 | val->intval = mbc->usb_online && | ||
340 | (usblim == PCF50633_MBCC7_USB_1000mA); | ||
276 | break; | 341 | break; |
277 | default: | 342 | default: |
278 | ret = -EINVAL; | 343 | ret = -EINVAL; |
@@ -336,6 +401,14 @@ static int __devinit pcf50633_mbc_probe(struct platform_device *pdev) | |||
336 | mbc->usb.supplied_to = mbc->pcf->pdata->batteries; | 401 | mbc->usb.supplied_to = mbc->pcf->pdata->batteries; |
337 | mbc->usb.num_supplicants = mbc->pcf->pdata->num_batteries; | 402 | mbc->usb.num_supplicants = mbc->pcf->pdata->num_batteries; |
338 | 403 | ||
404 | mbc->ac.name = "ac"; | ||
405 | mbc->ac.type = POWER_SUPPLY_TYPE_MAINS; | ||
406 | mbc->ac.properties = power_props; | ||
407 | mbc->ac.num_properties = ARRAY_SIZE(power_props); | ||
408 | mbc->ac.get_property = ac_get_property; | ||
409 | mbc->ac.supplied_to = mbc->pcf->pdata->batteries; | ||
410 | mbc->ac.num_supplicants = mbc->pcf->pdata->num_batteries; | ||
411 | |||
339 | ret = power_supply_register(&pdev->dev, &mbc->adapter); | 412 | ret = power_supply_register(&pdev->dev, &mbc->adapter); |
340 | if (ret) { | 413 | if (ret) { |
341 | dev_err(mbc->pcf->dev, "failed to register adapter\n"); | 414 | dev_err(mbc->pcf->dev, "failed to register adapter\n"); |
@@ -351,8 +424,14 @@ static int __devinit pcf50633_mbc_probe(struct platform_device *pdev) | |||
351 | return ret; | 424 | return ret; |
352 | } | 425 | } |
353 | 426 | ||
354 | INIT_DELAYED_WORK(&mbc->charging_restart_work, | 427 | ret = power_supply_register(&pdev->dev, &mbc->ac); |
355 | pcf50633_mbc_charging_restart); | 428 | if (ret) { |
429 | dev_err(mbc->pcf->dev, "failed to register ac\n"); | ||
430 | power_supply_unregister(&mbc->adapter); | ||
431 | power_supply_unregister(&mbc->usb); | ||
432 | kfree(mbc); | ||
433 | return ret; | ||
434 | } | ||
356 | 435 | ||
357 | ret = sysfs_create_group(&pdev->dev.kobj, &mbc_attr_group); | 436 | ret = sysfs_create_group(&pdev->dev.kobj, &mbc_attr_group); |
358 | if (ret) | 437 | if (ret) |
@@ -378,8 +457,7 @@ static int __devexit pcf50633_mbc_remove(struct platform_device *pdev) | |||
378 | 457 | ||
379 | power_supply_unregister(&mbc->usb); | 458 | power_supply_unregister(&mbc->usb); |
380 | power_supply_unregister(&mbc->adapter); | 459 | power_supply_unregister(&mbc->adapter); |
381 | 460 | power_supply_unregister(&mbc->ac); | |
382 | cancel_delayed_work_sync(&mbc->charging_restart_work); | ||
383 | 461 | ||
384 | kfree(mbc); | 462 | kfree(mbc); |
385 | 463 | ||