Root/
1 | /* |
2 | * intel_mid_battery.c - Intel MID PMIC Battery Driver |
3 | * |
4 | * Copyright (C) 2009 Intel Corporation |
5 | * |
6 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
7 | * |
8 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by |
10 | * the Free Software Foundation; version 2 of the License. |
11 | * |
12 | * This program is distributed in the hope that it will be useful, but |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU General Public License along |
18 | * with this program; if not, write to the Free Software Foundation, Inc., |
19 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. |
20 | * |
21 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
22 | * Author: Nithish Mahalingam <nithish.mahalingam@intel.com> |
23 | */ |
24 | |
25 | #include <linux/module.h> |
26 | #include <linux/init.h> |
27 | #include <linux/err.h> |
28 | #include <linux/interrupt.h> |
29 | #include <linux/workqueue.h> |
30 | #include <linux/jiffies.h> |
31 | #include <linux/param.h> |
32 | #include <linux/device.h> |
33 | #include <linux/spi/spi.h> |
34 | #include <linux/platform_device.h> |
35 | #include <linux/power_supply.h> |
36 | |
37 | #include <asm/intel_scu_ipc.h> |
38 | |
39 | #define DRIVER_NAME "pmic_battery" |
40 | |
41 | /********************************************************************* |
42 | * Generic defines |
43 | *********************************************************************/ |
44 | |
45 | static int debug; |
46 | module_param(debug, int, 0444); |
47 | MODULE_PARM_DESC(debug, "Flag to enable PMIC Battery debug messages."); |
48 | |
49 | #define PMIC_BATT_DRV_INFO_UPDATED 1 |
50 | #define PMIC_BATT_PRESENT 1 |
51 | #define PMIC_BATT_NOT_PRESENT 0 |
52 | #define PMIC_USB_PRESENT PMIC_BATT_PRESENT |
53 | #define PMIC_USB_NOT_PRESENT PMIC_BATT_NOT_PRESENT |
54 | |
55 | /* pmic battery register related */ |
56 | #define PMIC_BATT_CHR_SCHRGINT_ADDR 0xD2 |
57 | #define PMIC_BATT_CHR_SBATOVP_MASK (1 << 1) |
58 | #define PMIC_BATT_CHR_STEMP_MASK (1 << 2) |
59 | #define PMIC_BATT_CHR_SCOMP_MASK (1 << 3) |
60 | #define PMIC_BATT_CHR_SUSBDET_MASK (1 << 4) |
61 | #define PMIC_BATT_CHR_SBATDET_MASK (1 << 5) |
62 | #define PMIC_BATT_CHR_SDCLMT_MASK (1 << 6) |
63 | #define PMIC_BATT_CHR_SUSBOVP_MASK (1 << 7) |
64 | #define PMIC_BATT_CHR_EXCPT_MASK 0x86 |
65 | |
66 | #define PMIC_BATT_ADC_ACCCHRG_MASK (1 << 31) |
67 | #define PMIC_BATT_ADC_ACCCHRGVAL_MASK 0x7FFFFFFF |
68 | |
69 | /* pmic ipc related */ |
70 | #define PMIC_BATT_CHR_IPC_FCHRG_SUBID 0x4 |
71 | #define PMIC_BATT_CHR_IPC_TCHRG_SUBID 0x6 |
72 | |
73 | /* types of battery charging */ |
74 | enum batt_charge_type { |
75 | BATT_USBOTG_500MA_CHARGE, |
76 | BATT_USBOTG_TRICKLE_CHARGE, |
77 | }; |
78 | |
79 | /* valid battery events */ |
80 | enum batt_event { |
81 | BATT_EVENT_BATOVP_EXCPT, |
82 | BATT_EVENT_USBOVP_EXCPT, |
83 | BATT_EVENT_TEMP_EXCPT, |
84 | BATT_EVENT_DCLMT_EXCPT, |
85 | BATT_EVENT_EXCPT |
86 | }; |
87 | |
88 | |
89 | /********************************************************************* |
90 | * Battery properties |
91 | *********************************************************************/ |
92 | |
93 | /* |
94 | * pmic battery info |
95 | */ |
96 | struct pmic_power_module_info { |
97 | bool is_dev_info_updated; |
98 | struct device *dev; |
99 | /* pmic battery data */ |
100 | unsigned long update_time; /* jiffies when data read */ |
101 | unsigned int usb_is_present; |
102 | unsigned int batt_is_present; |
103 | unsigned int batt_health; |
104 | unsigned int usb_health; |
105 | unsigned int batt_status; |
106 | unsigned int batt_charge_now; /* in mAS */ |
107 | unsigned int batt_prev_charge_full; /* in mAS */ |
108 | unsigned int batt_charge_rate; /* in units per second */ |
109 | |
110 | struct power_supply usb; |
111 | struct power_supply batt; |
112 | int irq; /* GPE_ID or IRQ# */ |
113 | struct workqueue_struct *monitor_wqueue; |
114 | struct delayed_work monitor_battery; |
115 | struct work_struct handler; |
116 | }; |
117 | |
118 | static unsigned int delay_time = 2000; /* in ms */ |
119 | |
120 | /* |
121 | * pmic ac properties |
122 | */ |
123 | static enum power_supply_property pmic_usb_props[] = { |
124 | POWER_SUPPLY_PROP_PRESENT, |
125 | POWER_SUPPLY_PROP_HEALTH, |
126 | }; |
127 | |
128 | /* |
129 | * pmic battery properties |
130 | */ |
131 | static enum power_supply_property pmic_battery_props[] = { |
132 | POWER_SUPPLY_PROP_STATUS, |
133 | POWER_SUPPLY_PROP_HEALTH, |
134 | POWER_SUPPLY_PROP_PRESENT, |
135 | POWER_SUPPLY_PROP_CHARGE_NOW, |
136 | POWER_SUPPLY_PROP_CHARGE_FULL, |
137 | }; |
138 | |
139 | |
140 | /* |
141 | * Glue functions for talking to the IPC |
142 | */ |
143 | |
144 | struct battery_property { |
145 | u32 capacity; /* Charger capacity */ |
146 | u8 crnt; /* Quick charge current value*/ |
147 | u8 volt; /* Fine adjustment of constant charge voltage */ |
148 | u8 prot; /* CHRGPROT register value */ |
149 | u8 prot2; /* CHRGPROT1 register value */ |
150 | u8 timer; /* Charging timer */ |
151 | }; |
152 | |
153 | #define IPCMSG_BATTERY 0xEF |
154 | |
155 | /* Battery coulomb counter accumulator commands */ |
156 | #define IPC_CMD_CC_WR 0 /* Update coulomb counter value */ |
157 | #define IPC_CMD_CC_RD 1 /* Read coulomb counter value */ |
158 | #define IPC_CMD_BATTERY_PROPERTY 2 /* Read Battery property */ |
159 | |
160 | /** |
161 | * pmic_scu_ipc_battery_cc_read - read battery cc |
162 | * @value: battery coulomb counter read |
163 | * |
164 | * Reads the battery couloumb counter value, returns 0 on success, or |
165 | * an error code |
166 | * |
167 | * This function may sleep. Locking for SCU accesses is handled for |
168 | * the caller. |
169 | */ |
170 | static int pmic_scu_ipc_battery_cc_read(u32 *value) |
171 | { |
172 | return intel_scu_ipc_command(IPCMSG_BATTERY, IPC_CMD_CC_RD, |
173 | NULL, 0, value, 1); |
174 | } |
175 | |
176 | /** |
177 | * pmic_scu_ipc_battery_property_get - fetch properties |
178 | * @prop: battery properties |
179 | * |
180 | * Retrieve the battery properties from the power management |
181 | * |
182 | * This function may sleep. Locking for SCU accesses is handled for |
183 | * the caller. |
184 | */ |
185 | static int pmic_scu_ipc_battery_property_get(struct battery_property *prop) |
186 | { |
187 | u32 data[3]; |
188 | u8 *p = (u8 *)&data[1]; |
189 | int err = intel_scu_ipc_command(IPCMSG_BATTERY, |
190 | IPC_CMD_BATTERY_PROPERTY, NULL, 0, data, 3); |
191 | |
192 | prop->capacity = data[0]; |
193 | prop->crnt = *p++; |
194 | prop->volt = *p++; |
195 | prop->prot = *p++; |
196 | prop->prot2 = *p++; |
197 | prop->timer = *p++; |
198 | |
199 | return err; |
200 | } |
201 | |
202 | /** |
203 | * pmic_scu_ipc_set_charger - set charger |
204 | * @charger: charger to select |
205 | * |
206 | * Switch the charging mode for the SCU |
207 | */ |
208 | |
209 | static int pmic_scu_ipc_set_charger(int charger) |
210 | { |
211 | return intel_scu_ipc_simple_command(IPCMSG_BATTERY, charger); |
212 | } |
213 | |
214 | /** |
215 | * pmic_battery_log_event - log battery events |
216 | * @event: battery event to be logged |
217 | * Context: can sleep |
218 | * |
219 | * There are multiple battery events which may be of interest to users; |
220 | * this battery function logs the different battery events onto the |
221 | * kernel log messages. |
222 | */ |
223 | static void pmic_battery_log_event(enum batt_event event) |
224 | { |
225 | printk(KERN_WARNING "pmic-battery: "); |
226 | switch (event) { |
227 | case BATT_EVENT_BATOVP_EXCPT: |
228 | printk(KERN_CONT "battery overvoltage condition\n"); |
229 | break; |
230 | case BATT_EVENT_USBOVP_EXCPT: |
231 | printk(KERN_CONT "usb charger overvoltage condition\n"); |
232 | break; |
233 | case BATT_EVENT_TEMP_EXCPT: |
234 | printk(KERN_CONT "high battery temperature condition\n"); |
235 | break; |
236 | case BATT_EVENT_DCLMT_EXCPT: |
237 | printk(KERN_CONT "over battery charge current condition\n"); |
238 | break; |
239 | default: |
240 | printk(KERN_CONT "charger/battery exception %d\n", event); |
241 | break; |
242 | } |
243 | } |
244 | |
245 | /** |
246 | * pmic_battery_read_status - read battery status information |
247 | * @pbi: device info structure to update the read information |
248 | * Context: can sleep |
249 | * |
250 | * PMIC power source information need to be updated based on the data read |
251 | * from the PMIC battery registers. |
252 | * |
253 | */ |
254 | static void pmic_battery_read_status(struct pmic_power_module_info *pbi) |
255 | { |
256 | unsigned int update_time_intrvl; |
257 | unsigned int chrg_val; |
258 | u32 ccval; |
259 | u8 r8; |
260 | struct battery_property batt_prop; |
261 | int batt_present = 0; |
262 | int usb_present = 0; |
263 | int batt_exception = 0; |
264 | |
265 | /* make sure the last batt_status read happened delay_time before */ |
266 | if (pbi->update_time && time_before(jiffies, pbi->update_time + |
267 | msecs_to_jiffies(delay_time))) |
268 | return; |
269 | |
270 | update_time_intrvl = jiffies_to_msecs(jiffies - pbi->update_time); |
271 | pbi->update_time = jiffies; |
272 | |
273 | /* read coulomb counter registers and schrgint register */ |
274 | if (pmic_scu_ipc_battery_cc_read(&ccval)) { |
275 | dev_warn(pbi->dev, "%s(): ipc config cmd failed\n", |
276 | __func__); |
277 | return; |
278 | } |
279 | |
280 | if (intel_scu_ipc_ioread8(PMIC_BATT_CHR_SCHRGINT_ADDR, &r8)) { |
281 | dev_warn(pbi->dev, "%s(): ipc pmic read failed\n", |
282 | __func__); |
283 | return; |
284 | } |
285 | |
286 | /* |
287 | * set pmic_power_module_info members based on pmic register values |
288 | * read. |
289 | */ |
290 | |
291 | /* set batt_is_present */ |
292 | if (r8 & PMIC_BATT_CHR_SBATDET_MASK) { |
293 | pbi->batt_is_present = PMIC_BATT_PRESENT; |
294 | batt_present = 1; |
295 | } else { |
296 | pbi->batt_is_present = PMIC_BATT_NOT_PRESENT; |
297 | pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN; |
298 | pbi->batt_status = POWER_SUPPLY_STATUS_UNKNOWN; |
299 | } |
300 | |
301 | /* set batt_health */ |
302 | if (batt_present) { |
303 | if (r8 & PMIC_BATT_CHR_SBATOVP_MASK) { |
304 | pbi->batt_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; |
305 | pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING; |
306 | pmic_battery_log_event(BATT_EVENT_BATOVP_EXCPT); |
307 | batt_exception = 1; |
308 | } else if (r8 & PMIC_BATT_CHR_STEMP_MASK) { |
309 | pbi->batt_health = POWER_SUPPLY_HEALTH_OVERHEAT; |
310 | pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING; |
311 | pmic_battery_log_event(BATT_EVENT_TEMP_EXCPT); |
312 | batt_exception = 1; |
313 | } else { |
314 | pbi->batt_health = POWER_SUPPLY_HEALTH_GOOD; |
315 | if (r8 & PMIC_BATT_CHR_SDCLMT_MASK) { |
316 | /* PMIC will change charging current automatically */ |
317 | pmic_battery_log_event(BATT_EVENT_DCLMT_EXCPT); |
318 | } |
319 | } |
320 | } |
321 | |
322 | /* set usb_is_present */ |
323 | if (r8 & PMIC_BATT_CHR_SUSBDET_MASK) { |
324 | pbi->usb_is_present = PMIC_USB_PRESENT; |
325 | usb_present = 1; |
326 | } else { |
327 | pbi->usb_is_present = PMIC_USB_NOT_PRESENT; |
328 | pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN; |
329 | } |
330 | |
331 | if (usb_present) { |
332 | if (r8 & PMIC_BATT_CHR_SUSBOVP_MASK) { |
333 | pbi->usb_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE; |
334 | pmic_battery_log_event(BATT_EVENT_USBOVP_EXCPT); |
335 | } else { |
336 | pbi->usb_health = POWER_SUPPLY_HEALTH_GOOD; |
337 | } |
338 | } |
339 | |
340 | chrg_val = ccval & PMIC_BATT_ADC_ACCCHRGVAL_MASK; |
341 | |
342 | /* set batt_prev_charge_full to battery capacity the first time */ |
343 | if (!pbi->is_dev_info_updated) { |
344 | if (pmic_scu_ipc_battery_property_get(&batt_prop)) { |
345 | dev_warn(pbi->dev, "%s(): ipc config cmd failed\n", |
346 | __func__); |
347 | return; |
348 | } |
349 | pbi->batt_prev_charge_full = batt_prop.capacity; |
350 | } |
351 | |
352 | /* set batt_status */ |
353 | if (batt_present && !batt_exception) { |
354 | if (r8 & PMIC_BATT_CHR_SCOMP_MASK) { |
355 | pbi->batt_status = POWER_SUPPLY_STATUS_FULL; |
356 | pbi->batt_prev_charge_full = chrg_val; |
357 | } else if (ccval & PMIC_BATT_ADC_ACCCHRG_MASK) { |
358 | pbi->batt_status = POWER_SUPPLY_STATUS_DISCHARGING; |
359 | } else { |
360 | pbi->batt_status = POWER_SUPPLY_STATUS_CHARGING; |
361 | } |
362 | } |
363 | |
364 | /* set batt_charge_rate */ |
365 | if (pbi->is_dev_info_updated && batt_present && !batt_exception) { |
366 | if (pbi->batt_status == POWER_SUPPLY_STATUS_DISCHARGING) { |
367 | if (pbi->batt_charge_now - chrg_val) { |
368 | pbi->batt_charge_rate = ((pbi->batt_charge_now - |
369 | chrg_val) * 1000 * 60) / |
370 | update_time_intrvl; |
371 | } |
372 | } else if (pbi->batt_status == POWER_SUPPLY_STATUS_CHARGING) { |
373 | if (chrg_val - pbi->batt_charge_now) { |
374 | pbi->batt_charge_rate = ((chrg_val - |
375 | pbi->batt_charge_now) * 1000 * 60) / |
376 | update_time_intrvl; |
377 | } |
378 | } else |
379 | pbi->batt_charge_rate = 0; |
380 | } else { |
381 | pbi->batt_charge_rate = -1; |
382 | } |
383 | |
384 | /* batt_charge_now */ |
385 | if (batt_present && !batt_exception) |
386 | pbi->batt_charge_now = chrg_val; |
387 | else |
388 | pbi->batt_charge_now = -1; |
389 | |
390 | pbi->is_dev_info_updated = PMIC_BATT_DRV_INFO_UPDATED; |
391 | } |
392 | |
393 | /** |
394 | * pmic_usb_get_property - usb power source get property |
395 | * @psy: usb power supply context |
396 | * @psp: usb power source property |
397 | * @val: usb power source property value |
398 | * Context: can sleep |
399 | * |
400 | * PMIC usb power source property needs to be provided to power_supply |
401 | * subsytem for it to provide the information to users. |
402 | */ |
403 | static int pmic_usb_get_property(struct power_supply *psy, |
404 | enum power_supply_property psp, |
405 | union power_supply_propval *val) |
406 | { |
407 | struct pmic_power_module_info *pbi = container_of(psy, |
408 | struct pmic_power_module_info, usb); |
409 | |
410 | /* update pmic_power_module_info members */ |
411 | pmic_battery_read_status(pbi); |
412 | |
413 | switch (psp) { |
414 | case POWER_SUPPLY_PROP_PRESENT: |
415 | val->intval = pbi->usb_is_present; |
416 | break; |
417 | case POWER_SUPPLY_PROP_HEALTH: |
418 | val->intval = pbi->usb_health; |
419 | break; |
420 | default: |
421 | return -EINVAL; |
422 | } |
423 | |
424 | return 0; |
425 | } |
426 | |
427 | static inline unsigned long mAStouAh(unsigned long v) |
428 | { |
429 | /* seconds to hours, mA to µA */ |
430 | return (v * 1000) / 3600; |
431 | } |
432 | |
433 | /** |
434 | * pmic_battery_get_property - battery power source get property |
435 | * @psy: battery power supply context |
436 | * @psp: battery power source property |
437 | * @val: battery power source property value |
438 | * Context: can sleep |
439 | * |
440 | * PMIC battery power source property needs to be provided to power_supply |
441 | * subsytem for it to provide the information to users. |
442 | */ |
443 | static int pmic_battery_get_property(struct power_supply *psy, |
444 | enum power_supply_property psp, |
445 | union power_supply_propval *val) |
446 | { |
447 | struct pmic_power_module_info *pbi = container_of(psy, |
448 | struct pmic_power_module_info, batt); |
449 | |
450 | /* update pmic_power_module_info members */ |
451 | pmic_battery_read_status(pbi); |
452 | |
453 | switch (psp) { |
454 | case POWER_SUPPLY_PROP_STATUS: |
455 | val->intval = pbi->batt_status; |
456 | break; |
457 | case POWER_SUPPLY_PROP_HEALTH: |
458 | val->intval = pbi->batt_health; |
459 | break; |
460 | case POWER_SUPPLY_PROP_PRESENT: |
461 | val->intval = pbi->batt_is_present; |
462 | break; |
463 | case POWER_SUPPLY_PROP_CHARGE_NOW: |
464 | val->intval = mAStouAh(pbi->batt_charge_now); |
465 | break; |
466 | case POWER_SUPPLY_PROP_CHARGE_FULL: |
467 | val->intval = mAStouAh(pbi->batt_prev_charge_full); |
468 | break; |
469 | default: |
470 | return -EINVAL; |
471 | } |
472 | |
473 | return 0; |
474 | } |
475 | |
476 | /** |
477 | * pmic_battery_monitor - monitor battery status |
478 | * @work: work structure |
479 | * Context: can sleep |
480 | * |
481 | * PMIC battery status needs to be monitored for any change |
482 | * and information needs to be frequently updated. |
483 | */ |
484 | static void pmic_battery_monitor(struct work_struct *work) |
485 | { |
486 | struct pmic_power_module_info *pbi = container_of(work, |
487 | struct pmic_power_module_info, monitor_battery.work); |
488 | |
489 | /* update pmic_power_module_info members */ |
490 | pmic_battery_read_status(pbi); |
491 | queue_delayed_work(pbi->monitor_wqueue, &pbi->monitor_battery, HZ * 10); |
492 | } |
493 | |
494 | /** |
495 | * pmic_battery_set_charger - set battery charger |
496 | * @pbi: device info structure |
497 | * @chrg: charge mode to set battery charger in |
498 | * Context: can sleep |
499 | * |
500 | * PMIC battery charger needs to be enabled based on the usb charge |
501 | * capabilities connected to the platform. |
502 | */ |
503 | static int pmic_battery_set_charger(struct pmic_power_module_info *pbi, |
504 | enum batt_charge_type chrg) |
505 | { |
506 | int retval; |
507 | |
508 | /* set usblmt bits and chrgcntl register bits appropriately */ |
509 | switch (chrg) { |
510 | case BATT_USBOTG_500MA_CHARGE: |
511 | retval = pmic_scu_ipc_set_charger(PMIC_BATT_CHR_IPC_FCHRG_SUBID); |
512 | break; |
513 | case BATT_USBOTG_TRICKLE_CHARGE: |
514 | retval = pmic_scu_ipc_set_charger(PMIC_BATT_CHR_IPC_TCHRG_SUBID); |
515 | break; |
516 | default: |
517 | dev_warn(pbi->dev, "%s(): out of range usb charger " |
518 | "charge detected\n", __func__); |
519 | return -EINVAL; |
520 | } |
521 | |
522 | if (retval) { |
523 | dev_warn(pbi->dev, "%s(): ipc pmic read failed\n", |
524 | __func__); |
525 | return retval; |
526 | } |
527 | |
528 | return 0; |
529 | } |
530 | |
531 | /** |
532 | * pmic_battery_interrupt_handler - pmic battery interrupt handler |
533 | * Context: interrupt context |
534 | * |
535 | * PMIC battery interrupt handler which will be called with either |
536 | * battery full condition occurs or usb otg & battery connect |
537 | * condition occurs. |
538 | */ |
539 | static irqreturn_t pmic_battery_interrupt_handler(int id, void *dev) |
540 | { |
541 | struct pmic_power_module_info *pbi = dev; |
542 | |
543 | schedule_work(&pbi->handler); |
544 | |
545 | return IRQ_HANDLED; |
546 | } |
547 | |
548 | /** |
549 | * pmic_battery_handle_intrpt - pmic battery service interrupt |
550 | * @work: work structure |
551 | * Context: can sleep |
552 | * |
553 | * PMIC battery needs to either update the battery status as full |
554 | * if it detects battery full condition caused the interrupt or needs |
555 | * to enable battery charger if it detects usb and battery detect |
556 | * caused the source of interrupt. |
557 | */ |
558 | static void pmic_battery_handle_intrpt(struct work_struct *work) |
559 | { |
560 | struct pmic_power_module_info *pbi = container_of(work, |
561 | struct pmic_power_module_info, handler); |
562 | enum batt_charge_type chrg; |
563 | u8 r8; |
564 | |
565 | if (intel_scu_ipc_ioread8(PMIC_BATT_CHR_SCHRGINT_ADDR, &r8)) { |
566 | dev_warn(pbi->dev, "%s(): ipc pmic read failed\n", |
567 | __func__); |
568 | return; |
569 | } |
570 | /* find the cause of the interrupt */ |
571 | if (r8 & PMIC_BATT_CHR_SBATDET_MASK) { |
572 | pbi->batt_is_present = PMIC_BATT_PRESENT; |
573 | } else { |
574 | pbi->batt_is_present = PMIC_BATT_NOT_PRESENT; |
575 | pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN; |
576 | pbi->batt_status = POWER_SUPPLY_STATUS_UNKNOWN; |
577 | return; |
578 | } |
579 | |
580 | if (r8 & PMIC_BATT_CHR_EXCPT_MASK) { |
581 | pbi->batt_health = POWER_SUPPLY_HEALTH_UNKNOWN; |
582 | pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING; |
583 | pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN; |
584 | pmic_battery_log_event(BATT_EVENT_EXCPT); |
585 | return; |
586 | } else { |
587 | pbi->batt_health = POWER_SUPPLY_HEALTH_GOOD; |
588 | pbi->usb_health = POWER_SUPPLY_HEALTH_GOOD; |
589 | } |
590 | |
591 | if (r8 & PMIC_BATT_CHR_SCOMP_MASK) { |
592 | u32 ccval; |
593 | pbi->batt_status = POWER_SUPPLY_STATUS_FULL; |
594 | |
595 | if (pmic_scu_ipc_battery_cc_read(&ccval)) { |
596 | dev_warn(pbi->dev, "%s(): ipc config cmd " |
597 | "failed\n", __func__); |
598 | return; |
599 | } |
600 | pbi->batt_prev_charge_full = ccval & |
601 | PMIC_BATT_ADC_ACCCHRGVAL_MASK; |
602 | return; |
603 | } |
604 | |
605 | if (r8 & PMIC_BATT_CHR_SUSBDET_MASK) { |
606 | pbi->usb_is_present = PMIC_USB_PRESENT; |
607 | } else { |
608 | pbi->usb_is_present = PMIC_USB_NOT_PRESENT; |
609 | pbi->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN; |
610 | return; |
611 | } |
612 | |
613 | /* setup battery charging */ |
614 | |
615 | #if 0 |
616 | /* check usb otg power capability and set charger accordingly */ |
617 | retval = langwell_udc_maxpower(&power); |
618 | if (retval) { |
619 | dev_warn(pbi->dev, |
620 | "%s(): usb otg power query failed with error code %d\n", |
621 | __func__, retval); |
622 | return; |
623 | } |
624 | |
625 | if (power >= 500) |
626 | chrg = BATT_USBOTG_500MA_CHARGE; |
627 | else |
628 | #endif |
629 | chrg = BATT_USBOTG_TRICKLE_CHARGE; |
630 | |
631 | /* enable battery charging */ |
632 | if (pmic_battery_set_charger(pbi, chrg)) { |
633 | dev_warn(pbi->dev, |
634 | "%s(): failed to set up battery charging\n", __func__); |
635 | return; |
636 | } |
637 | |
638 | dev_dbg(pbi->dev, |
639 | "pmic-battery: %s() - setting up battery charger successful\n", |
640 | __func__); |
641 | } |
642 | |
643 | /** |
644 | * pmic_battery_probe - pmic battery initialize |
645 | * @irq: pmic battery device irq |
646 | * @dev: pmic battery device structure |
647 | * Context: can sleep |
648 | * |
649 | * PMIC battery initializes its internal data structue and other |
650 | * infrastructure components for it to work as expected. |
651 | */ |
652 | static __devinit int probe(int irq, struct device *dev) |
653 | { |
654 | int retval = 0; |
655 | struct pmic_power_module_info *pbi; |
656 | |
657 | dev_dbg(dev, "pmic-battery: found pmic battery device\n"); |
658 | |
659 | pbi = kzalloc(sizeof(*pbi), GFP_KERNEL); |
660 | if (!pbi) { |
661 | dev_err(dev, "%s(): memory allocation failed\n", |
662 | __func__); |
663 | return -ENOMEM; |
664 | } |
665 | |
666 | pbi->dev = dev; |
667 | pbi->irq = irq; |
668 | dev_set_drvdata(dev, pbi); |
669 | |
670 | /* initialize all required framework before enabling interrupts */ |
671 | INIT_WORK(&pbi->handler, pmic_battery_handle_intrpt); |
672 | INIT_DELAYED_WORK(&pbi->monitor_battery, pmic_battery_monitor); |
673 | pbi->monitor_wqueue = |
674 | create_singlethread_workqueue(dev_name(dev)); |
675 | if (!pbi->monitor_wqueue) { |
676 | dev_err(dev, "%s(): wqueue init failed\n", __func__); |
677 | retval = -ESRCH; |
678 | goto wqueue_failed; |
679 | } |
680 | |
681 | /* register interrupt */ |
682 | retval = request_irq(pbi->irq, pmic_battery_interrupt_handler, |
683 | 0, DRIVER_NAME, pbi); |
684 | if (retval) { |
685 | dev_err(dev, "%s(): cannot get IRQ\n", __func__); |
686 | goto requestirq_failed; |
687 | } |
688 | |
689 | /* register pmic-batt with power supply subsystem */ |
690 | pbi->batt.name = "pmic-batt"; |
691 | pbi->batt.type = POWER_SUPPLY_TYPE_BATTERY; |
692 | pbi->batt.properties = pmic_battery_props; |
693 | pbi->batt.num_properties = ARRAY_SIZE(pmic_battery_props); |
694 | pbi->batt.get_property = pmic_battery_get_property; |
695 | retval = power_supply_register(dev, &pbi->batt); |
696 | if (retval) { |
697 | dev_err(dev, |
698 | "%s(): failed to register pmic battery device with power supply subsystem\n", |
699 | __func__); |
700 | goto power_reg_failed; |
701 | } |
702 | |
703 | dev_dbg(dev, "pmic-battery: %s() - pmic battery device " |
704 | "registration with power supply subsystem successful\n", |
705 | __func__); |
706 | |
707 | queue_delayed_work(pbi->monitor_wqueue, &pbi->monitor_battery, HZ * 1); |
708 | |
709 | /* register pmic-usb with power supply subsystem */ |
710 | pbi->usb.name = "pmic-usb"; |
711 | pbi->usb.type = POWER_SUPPLY_TYPE_USB; |
712 | pbi->usb.properties = pmic_usb_props; |
713 | pbi->usb.num_properties = ARRAY_SIZE(pmic_usb_props); |
714 | pbi->usb.get_property = pmic_usb_get_property; |
715 | retval = power_supply_register(dev, &pbi->usb); |
716 | if (retval) { |
717 | dev_err(dev, |
718 | "%s(): failed to register pmic usb device with power supply subsystem\n", |
719 | __func__); |
720 | goto power_reg_failed_1; |
721 | } |
722 | |
723 | if (debug) |
724 | printk(KERN_INFO "pmic-battery: %s() - pmic usb device " |
725 | "registration with power supply subsystem successful\n", |
726 | __func__); |
727 | |
728 | return retval; |
729 | |
730 | power_reg_failed_1: |
731 | power_supply_unregister(&pbi->batt); |
732 | power_reg_failed: |
733 | cancel_delayed_work_sync(&pbi->monitor_battery); |
734 | requestirq_failed: |
735 | destroy_workqueue(pbi->monitor_wqueue); |
736 | wqueue_failed: |
737 | kfree(pbi); |
738 | |
739 | return retval; |
740 | } |
741 | |
742 | static int __devinit platform_pmic_battery_probe(struct platform_device *pdev) |
743 | { |
744 | return probe(pdev->id, &pdev->dev); |
745 | } |
746 | |
747 | /** |
748 | * pmic_battery_remove - pmic battery finalize |
749 | * @dev: pmic battery device structure |
750 | * Context: can sleep |
751 | * |
752 | * PMIC battery finalizes its internal data structue and other |
753 | * infrastructure components that it initialized in |
754 | * pmic_battery_probe. |
755 | */ |
756 | |
757 | static int __devexit platform_pmic_battery_remove(struct platform_device *pdev) |
758 | { |
759 | struct pmic_power_module_info *pbi = dev_get_drvdata(&pdev->dev); |
760 | |
761 | free_irq(pbi->irq, pbi); |
762 | cancel_delayed_work_sync(&pbi->monitor_battery); |
763 | destroy_workqueue(pbi->monitor_wqueue); |
764 | |
765 | power_supply_unregister(&pbi->usb); |
766 | power_supply_unregister(&pbi->batt); |
767 | |
768 | cancel_work_sync(&pbi->handler); |
769 | kfree(pbi); |
770 | return 0; |
771 | } |
772 | |
773 | static struct platform_driver platform_pmic_battery_driver = { |
774 | .driver = { |
775 | .name = DRIVER_NAME, |
776 | .owner = THIS_MODULE, |
777 | }, |
778 | .probe = platform_pmic_battery_probe, |
779 | .remove = __devexit_p(platform_pmic_battery_remove), |
780 | }; |
781 | |
782 | module_platform_driver(platform_pmic_battery_driver); |
783 | |
784 | MODULE_AUTHOR("Nithish Mahalingam <nithish.mahalingam@intel.com>"); |
785 | MODULE_DESCRIPTION("Intel Moorestown PMIC Battery Driver"); |
786 | MODULE_LICENSE("GPL"); |
787 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9