diff options
Diffstat (limited to 'drivers/power/pm2301_charger.c')
-rw-r--r-- | drivers/power/pm2301_charger.c | 1088 |
1 files changed, 1088 insertions, 0 deletions
diff --git a/drivers/power/pm2301_charger.c b/drivers/power/pm2301_charger.c new file mode 100644 index 000000000000..ed48d75bb786 --- /dev/null +++ b/drivers/power/pm2301_charger.c | |||
@@ -0,0 +1,1088 @@ | |||
1 | /* | ||
2 | * Copyright 2012 ST Ericsson. | ||
3 | * | ||
4 | * Power supply driver for ST Ericsson pm2xxx_charger charger | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #include <linux/init.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/device.h> | ||
14 | #include <linux/interrupt.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/power_supply.h> | ||
19 | #include <linux/completion.h> | ||
20 | #include <linux/regulator/consumer.h> | ||
21 | #include <linux/err.h> | ||
22 | #include <linux/i2c.h> | ||
23 | #include <linux/workqueue.h> | ||
24 | #include <linux/kobject.h> | ||
25 | #include <linux/mfd/abx500.h> | ||
26 | #include <linux/mfd/abx500/ab8500.h> | ||
27 | #include <linux/mfd/abx500/ab8500-bm.h> | ||
28 | #include <linux/mfd/abx500/ab8500-gpadc.h> | ||
29 | #include <linux/mfd/abx500/ux500_chargalg.h> | ||
30 | #include <linux/pm2301_charger.h> | ||
31 | #include <linux/gpio.h> | ||
32 | |||
33 | #include "pm2301_charger.h" | ||
34 | |||
35 | #define to_pm2xxx_charger_ac_device_info(x) container_of((x), \ | ||
36 | struct pm2xxx_charger, ac_chg) | ||
37 | |||
38 | static int pm2xxx_interrupt_registers[] = { | ||
39 | PM2XXX_REG_INT1, | ||
40 | PM2XXX_REG_INT2, | ||
41 | PM2XXX_REG_INT3, | ||
42 | PM2XXX_REG_INT4, | ||
43 | PM2XXX_REG_INT5, | ||
44 | PM2XXX_REG_INT6, | ||
45 | }; | ||
46 | |||
47 | static enum power_supply_property pm2xxx_charger_ac_props[] = { | ||
48 | POWER_SUPPLY_PROP_HEALTH, | ||
49 | POWER_SUPPLY_PROP_PRESENT, | ||
50 | POWER_SUPPLY_PROP_ONLINE, | ||
51 | POWER_SUPPLY_PROP_VOLTAGE_AVG, | ||
52 | }; | ||
53 | |||
54 | static int pm2xxx_charger_voltage_map[] = { | ||
55 | 3500, | ||
56 | 3525, | ||
57 | 3550, | ||
58 | 3575, | ||
59 | 3600, | ||
60 | 3625, | ||
61 | 3650, | ||
62 | 3675, | ||
63 | 3700, | ||
64 | 3725, | ||
65 | 3750, | ||
66 | 3775, | ||
67 | 3800, | ||
68 | 3825, | ||
69 | 3850, | ||
70 | 3875, | ||
71 | 3900, | ||
72 | 3925, | ||
73 | 3950, | ||
74 | 3975, | ||
75 | 4000, | ||
76 | 4025, | ||
77 | 4050, | ||
78 | 4075, | ||
79 | 4100, | ||
80 | 4125, | ||
81 | 4150, | ||
82 | 4175, | ||
83 | 4200, | ||
84 | 4225, | ||
85 | 4250, | ||
86 | 4275, | ||
87 | 4300, | ||
88 | }; | ||
89 | |||
90 | static int pm2xxx_charger_current_map[] = { | ||
91 | 200, | ||
92 | 200, | ||
93 | 400, | ||
94 | 600, | ||
95 | 800, | ||
96 | 1000, | ||
97 | 1200, | ||
98 | 1400, | ||
99 | 1600, | ||
100 | 1800, | ||
101 | 2000, | ||
102 | 2200, | ||
103 | 2400, | ||
104 | 2600, | ||
105 | 2800, | ||
106 | 3000, | ||
107 | }; | ||
108 | |||
109 | static const struct i2c_device_id pm2xxx_ident[] = { | ||
110 | { "pm2301", 0 }, | ||
111 | { } | ||
112 | }; | ||
113 | |||
114 | static void set_lpn_pin(struct pm2xxx_charger *pm2) | ||
115 | { | ||
116 | if (pm2->ac.charger_connected) | ||
117 | return; | ||
118 | gpio_set_value(pm2->lpn_pin, 1); | ||
119 | |||
120 | return; | ||
121 | } | ||
122 | |||
123 | static void clear_lpn_pin(struct pm2xxx_charger *pm2) | ||
124 | { | ||
125 | if (pm2->ac.charger_connected) | ||
126 | return; | ||
127 | gpio_set_value(pm2->lpn_pin, 0); | ||
128 | |||
129 | return; | ||
130 | } | ||
131 | |||
132 | static int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val) | ||
133 | { | ||
134 | int ret; | ||
135 | /* | ||
136 | * When AC adaptor is unplugged, the host | ||
137 | * must put LPN high to be able to | ||
138 | * communicate by I2C with PM2301 | ||
139 | * and receive I2C "acknowledge" from PM2301. | ||
140 | */ | ||
141 | mutex_lock(&pm2->lock); | ||
142 | set_lpn_pin(pm2); | ||
143 | |||
144 | ret = i2c_smbus_read_i2c_block_data(pm2->config.pm2xxx_i2c, reg, | ||
145 | 1, val); | ||
146 | if (ret < 0) | ||
147 | dev_err(pm2->dev, "Error reading register at 0x%x\n", reg); | ||
148 | else | ||
149 | ret = 0; | ||
150 | clear_lpn_pin(pm2); | ||
151 | mutex_unlock(&pm2->lock); | ||
152 | |||
153 | return ret; | ||
154 | } | ||
155 | |||
156 | static int pm2xxx_reg_write(struct pm2xxx_charger *pm2, int reg, u8 val) | ||
157 | { | ||
158 | int ret; | ||
159 | /* | ||
160 | * When AC adaptor is unplugged, the host | ||
161 | * must put LPN high to be able to | ||
162 | * communicate by I2C with PM2301 | ||
163 | * and receive I2C "acknowledge" from PM2301. | ||
164 | */ | ||
165 | mutex_lock(&pm2->lock); | ||
166 | set_lpn_pin(pm2); | ||
167 | |||
168 | ret = i2c_smbus_write_i2c_block_data(pm2->config.pm2xxx_i2c, reg, | ||
169 | 1, &val); | ||
170 | if (ret < 0) | ||
171 | dev_err(pm2->dev, "Error writing register at 0x%x\n", reg); | ||
172 | else | ||
173 | ret = 0; | ||
174 | clear_lpn_pin(pm2); | ||
175 | mutex_unlock(&pm2->lock); | ||
176 | |||
177 | return ret; | ||
178 | } | ||
179 | |||
180 | static int pm2xxx_charging_enable_mngt(struct pm2xxx_charger *pm2) | ||
181 | { | ||
182 | int ret; | ||
183 | |||
184 | /* Enable charging */ | ||
185 | ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG2, | ||
186 | (PM2XXX_CH_AUTO_RESUME_EN | PM2XXX_CHARGER_ENA)); | ||
187 | |||
188 | return ret; | ||
189 | } | ||
190 | |||
191 | static int pm2xxx_charging_disable_mngt(struct pm2xxx_charger *pm2) | ||
192 | { | ||
193 | int ret; | ||
194 | |||
195 | /* Disable charging */ | ||
196 | ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG2, | ||
197 | (PM2XXX_CH_AUTO_RESUME_DIS | PM2XXX_CHARGER_DIS)); | ||
198 | |||
199 | return ret; | ||
200 | } | ||
201 | |||
202 | static int pm2xxx_charger_batt_therm_mngt(struct pm2xxx_charger *pm2, int val) | ||
203 | { | ||
204 | queue_work(pm2->charger_wq, &pm2->check_main_thermal_prot_work); | ||
205 | |||
206 | return 0; | ||
207 | } | ||
208 | |||
209 | |||
210 | int pm2xxx_charger_die_therm_mngt(struct pm2xxx_charger *pm2, int val) | ||
211 | { | ||
212 | queue_work(pm2->charger_wq, &pm2->check_main_thermal_prot_work); | ||
213 | |||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | static int pm2xxx_charger_ovv_mngt(struct pm2xxx_charger *pm2, int val) | ||
218 | { | ||
219 | int ret = 0; | ||
220 | |||
221 | pm2->failure_input_ovv++; | ||
222 | if (pm2->failure_input_ovv < 4) { | ||
223 | ret = pm2xxx_charging_enable_mngt(pm2); | ||
224 | goto out; | ||
225 | } else { | ||
226 | pm2->failure_input_ovv = 0; | ||
227 | dev_err(pm2->dev, "Overvoltage detected\n"); | ||
228 | pm2->flags.ovv = true; | ||
229 | power_supply_changed(&pm2->ac_chg.psy); | ||
230 | } | ||
231 | |||
232 | out: | ||
233 | return ret; | ||
234 | } | ||
235 | |||
236 | static int pm2xxx_charger_wd_exp_mngt(struct pm2xxx_charger *pm2, int val) | ||
237 | { | ||
238 | dev_dbg(pm2->dev , "20 minutes watchdog occured\n"); | ||
239 | |||
240 | pm2->ac.wd_expired = true; | ||
241 | power_supply_changed(&pm2->ac_chg.psy); | ||
242 | |||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | static int pm2xxx_charger_vbat_lsig_mngt(struct pm2xxx_charger *pm2, int val) | ||
247 | { | ||
248 | switch (val) { | ||
249 | case PM2XXX_INT1_ITVBATLOWR: | ||
250 | dev_dbg(pm2->dev, "VBAT grows above VBAT_LOW level\n"); | ||
251 | break; | ||
252 | |||
253 | case PM2XXX_INT1_ITVBATLOWF: | ||
254 | dev_dbg(pm2->dev, "VBAT drops below VBAT_LOW level\n"); | ||
255 | break; | ||
256 | |||
257 | default: | ||
258 | dev_err(pm2->dev, "Unknown VBAT level\n"); | ||
259 | } | ||
260 | |||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | static int pm2xxx_charger_bat_disc_mngt(struct pm2xxx_charger *pm2, int val) | ||
265 | { | ||
266 | dev_dbg(pm2->dev, "battery disconnected\n"); | ||
267 | |||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | static int pm2xxx_charger_detection(struct pm2xxx_charger *pm2, u8 *val) | ||
272 | { | ||
273 | int ret; | ||
274 | |||
275 | ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT2, val); | ||
276 | |||
277 | if (ret < 0) { | ||
278 | dev_err(pm2->dev, "Charger detection failed\n"); | ||
279 | goto out; | ||
280 | } | ||
281 | |||
282 | *val &= (PM2XXX_INT2_S_ITVPWR1PLUG | PM2XXX_INT2_S_ITVPWR2PLUG); | ||
283 | |||
284 | out: | ||
285 | return ret; | ||
286 | } | ||
287 | |||
288 | static int pm2xxx_charger_itv_pwr_plug_mngt(struct pm2xxx_charger *pm2, int val) | ||
289 | { | ||
290 | |||
291 | int ret; | ||
292 | u8 read_val; | ||
293 | |||
294 | /* | ||
295 | * Since we can't be sure that the events are received | ||
296 | * synchronously, we have the check if the main charger is | ||
297 | * connected by reading the interrupt source register. | ||
298 | */ | ||
299 | ret = pm2xxx_charger_detection(pm2, &read_val); | ||
300 | |||
301 | if ((ret == 0) && read_val) { | ||
302 | pm2->ac.charger_connected = 1; | ||
303 | pm2->ac_conn = true; | ||
304 | queue_work(pm2->charger_wq, &pm2->ac_work); | ||
305 | } | ||
306 | |||
307 | |||
308 | return ret; | ||
309 | } | ||
310 | |||
311 | static int pm2xxx_charger_itv_pwr_unplug_mngt(struct pm2xxx_charger *pm2, | ||
312 | int val) | ||
313 | { | ||
314 | pm2->ac.charger_connected = 0; | ||
315 | queue_work(pm2->charger_wq, &pm2->ac_work); | ||
316 | |||
317 | return 0; | ||
318 | } | ||
319 | |||
320 | static int pm2_int_reg0(void *pm2_data, int val) | ||
321 | { | ||
322 | struct pm2xxx_charger *pm2 = pm2_data; | ||
323 | int ret = 0; | ||
324 | |||
325 | if (val & (PM2XXX_INT1_ITVBATLOWR | PM2XXX_INT1_ITVBATLOWF)) { | ||
326 | ret = pm2xxx_charger_vbat_lsig_mngt(pm2, val & | ||
327 | (PM2XXX_INT1_ITVBATLOWR | PM2XXX_INT1_ITVBATLOWF)); | ||
328 | } | ||
329 | |||
330 | if (val & PM2XXX_INT1_ITVBATDISCONNECT) { | ||
331 | ret = pm2xxx_charger_bat_disc_mngt(pm2, | ||
332 | PM2XXX_INT1_ITVBATDISCONNECT); | ||
333 | } | ||
334 | |||
335 | return ret; | ||
336 | } | ||
337 | |||
338 | static int pm2_int_reg1(void *pm2_data, int val) | ||
339 | { | ||
340 | struct pm2xxx_charger *pm2 = pm2_data; | ||
341 | int ret = 0; | ||
342 | |||
343 | if (val & (PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG)) { | ||
344 | dev_dbg(pm2->dev , "Main charger plugged\n"); | ||
345 | ret = pm2xxx_charger_itv_pwr_plug_mngt(pm2, val & | ||
346 | (PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG)); | ||
347 | } | ||
348 | |||
349 | if (val & | ||
350 | (PM2XXX_INT2_ITVPWR1UNPLUG | PM2XXX_INT2_ITVPWR2UNPLUG)) { | ||
351 | dev_dbg(pm2->dev , "Main charger unplugged\n"); | ||
352 | ret = pm2xxx_charger_itv_pwr_unplug_mngt(pm2, val & | ||
353 | (PM2XXX_INT2_ITVPWR1UNPLUG | | ||
354 | PM2XXX_INT2_ITVPWR2UNPLUG)); | ||
355 | } | ||
356 | |||
357 | return ret; | ||
358 | } | ||
359 | |||
360 | static int pm2_int_reg2(void *pm2_data, int val) | ||
361 | { | ||
362 | struct pm2xxx_charger *pm2 = pm2_data; | ||
363 | int ret = 0; | ||
364 | |||
365 | if (val & PM2XXX_INT3_ITAUTOTIMEOUTWD) | ||
366 | ret = pm2xxx_charger_wd_exp_mngt(pm2, val); | ||
367 | |||
368 | if (val & (PM2XXX_INT3_ITCHPRECHARGEWD | | ||
369 | PM2XXX_INT3_ITCHCCWD | PM2XXX_INT3_ITCHCVWD)) { | ||
370 | dev_dbg(pm2->dev, | ||
371 | "Watchdog occured for precharge, CC and CV charge\n"); | ||
372 | } | ||
373 | |||
374 | return ret; | ||
375 | } | ||
376 | |||
377 | static int pm2_int_reg3(void *pm2_data, int val) | ||
378 | { | ||
379 | struct pm2xxx_charger *pm2 = pm2_data; | ||
380 | int ret = 0; | ||
381 | |||
382 | if (val & (PM2XXX_INT4_ITCHARGINGON)) { | ||
383 | dev_dbg(pm2->dev , | ||
384 | "chargind operation has started\n"); | ||
385 | } | ||
386 | |||
387 | if (val & (PM2XXX_INT4_ITVRESUME)) { | ||
388 | dev_dbg(pm2->dev, | ||
389 | "battery discharged down to VResume threshold\n"); | ||
390 | } | ||
391 | |||
392 | if (val & (PM2XXX_INT4_ITBATTFULL)) { | ||
393 | dev_dbg(pm2->dev , "battery fully detected\n"); | ||
394 | } | ||
395 | |||
396 | if (val & (PM2XXX_INT4_ITCVPHASE)) { | ||
397 | dev_dbg(pm2->dev, "CV phase enter with 0.5C charging\n"); | ||
398 | } | ||
399 | |||
400 | if (val & (PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV)) { | ||
401 | pm2->failure_case = VPWR_OVV; | ||
402 | ret = pm2xxx_charger_ovv_mngt(pm2, val & | ||
403 | (PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV)); | ||
404 | dev_dbg(pm2->dev, "VPWR/VSYSTEM overvoltage detected\n"); | ||
405 | } | ||
406 | |||
407 | if (val & (PM2XXX_INT4_S_ITBATTEMPCOLD | | ||
408 | PM2XXX_INT4_S_ITBATTEMPHOT)) { | ||
409 | ret = pm2xxx_charger_batt_therm_mngt(pm2, val & | ||
410 | (PM2XXX_INT4_S_ITBATTEMPCOLD | | ||
411 | PM2XXX_INT4_S_ITBATTEMPHOT)); | ||
412 | dev_dbg(pm2->dev, "BTEMP is too Low/High\n"); | ||
413 | } | ||
414 | |||
415 | return ret; | ||
416 | } | ||
417 | |||
418 | static int pm2_int_reg4(void *pm2_data, int val) | ||
419 | { | ||
420 | struct pm2xxx_charger *pm2 = pm2_data; | ||
421 | int ret = 0; | ||
422 | |||
423 | if (val & PM2XXX_INT5_ITVSYSTEMOVV) { | ||
424 | pm2->failure_case = VSYSTEM_OVV; | ||
425 | ret = pm2xxx_charger_ovv_mngt(pm2, val & | ||
426 | PM2XXX_INT5_ITVSYSTEMOVV); | ||
427 | dev_dbg(pm2->dev, "VSYSTEM overvoltage detected\n"); | ||
428 | } | ||
429 | |||
430 | if (val & (PM2XXX_INT5_ITTHERMALWARNINGFALL | | ||
431 | PM2XXX_INT5_ITTHERMALWARNINGRISE | | ||
432 | PM2XXX_INT5_ITTHERMALSHUTDOWNFALL | | ||
433 | PM2XXX_INT5_ITTHERMALSHUTDOWNRISE)) { | ||
434 | dev_dbg(pm2->dev, "BTEMP die temperature is too Low/High\n"); | ||
435 | ret = pm2xxx_charger_die_therm_mngt(pm2, val & | ||
436 | (PM2XXX_INT5_ITTHERMALWARNINGFALL | | ||
437 | PM2XXX_INT5_ITTHERMALWARNINGRISE | | ||
438 | PM2XXX_INT5_ITTHERMALSHUTDOWNFALL | | ||
439 | PM2XXX_INT5_ITTHERMALSHUTDOWNRISE)); | ||
440 | } | ||
441 | |||
442 | return ret; | ||
443 | } | ||
444 | |||
445 | static int pm2_int_reg5(void *pm2_data, int val) | ||
446 | { | ||
447 | struct pm2xxx_charger *pm2 = pm2_data; | ||
448 | int ret = 0; | ||
449 | |||
450 | |||
451 | if (val & (PM2XXX_INT6_ITVPWR2DROP | PM2XXX_INT6_ITVPWR1DROP)) { | ||
452 | dev_dbg(pm2->dev, "VMPWR drop to VBAT level\n"); | ||
453 | } | ||
454 | |||
455 | if (val & (PM2XXX_INT6_ITVPWR2VALIDRISE | | ||
456 | PM2XXX_INT6_ITVPWR1VALIDRISE | | ||
457 | PM2XXX_INT6_ITVPWR2VALIDFALL | | ||
458 | PM2XXX_INT6_ITVPWR1VALIDFALL)) { | ||
459 | dev_dbg(pm2->dev, "Falling/Rising edge on WPWR1/2\n"); | ||
460 | } | ||
461 | |||
462 | return ret; | ||
463 | } | ||
464 | |||
465 | static irqreturn_t pm2xxx_irq_int(int irq, void *data) | ||
466 | { | ||
467 | struct pm2xxx_charger *pm2 = data; | ||
468 | struct pm2xxx_interrupts *interrupt = pm2->pm2_int; | ||
469 | int i; | ||
470 | |||
471 | for (i = 0; i < PM2XXX_NUM_INT_REG; i++) { | ||
472 | pm2xxx_reg_read(pm2, | ||
473 | pm2xxx_interrupt_registers[i], | ||
474 | &(interrupt->reg[i])); | ||
475 | |||
476 | if (interrupt->reg[i] > 0) | ||
477 | interrupt->handler[i](pm2, interrupt->reg[i]); | ||
478 | } | ||
479 | |||
480 | return IRQ_HANDLED; | ||
481 | } | ||
482 | |||
483 | static int pm2xxx_charger_get_ac_cv(struct pm2xxx_charger *pm2) | ||
484 | { | ||
485 | int ret = 0; | ||
486 | u8 val; | ||
487 | |||
488 | if (pm2->ac.charger_connected && pm2->ac.charger_online) { | ||
489 | |||
490 | ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT4, &val); | ||
491 | if (ret < 0) { | ||
492 | dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__); | ||
493 | goto out; | ||
494 | } | ||
495 | |||
496 | if (val & PM2XXX_INT4_S_ITCVPHASE) | ||
497 | ret = PM2XXX_CONST_VOLT; | ||
498 | else | ||
499 | ret = PM2XXX_CONST_CURR; | ||
500 | } | ||
501 | out: | ||
502 | return ret; | ||
503 | } | ||
504 | |||
505 | static int pm2xxx_current_to_regval(int curr) | ||
506 | { | ||
507 | int i; | ||
508 | |||
509 | if (curr < pm2xxx_charger_current_map[0]) | ||
510 | return 0; | ||
511 | |||
512 | for (i = 1; i < ARRAY_SIZE(pm2xxx_charger_current_map); i++) { | ||
513 | if (curr < pm2xxx_charger_current_map[i]) | ||
514 | return (i - 1); | ||
515 | } | ||
516 | |||
517 | i = ARRAY_SIZE(pm2xxx_charger_current_map) - 1; | ||
518 | if (curr == pm2xxx_charger_current_map[i]) | ||
519 | return i; | ||
520 | else | ||
521 | return -EINVAL; | ||
522 | } | ||
523 | |||
524 | static int pm2xxx_voltage_to_regval(int curr) | ||
525 | { | ||
526 | int i; | ||
527 | |||
528 | if (curr < pm2xxx_charger_voltage_map[0]) | ||
529 | return 0; | ||
530 | |||
531 | for (i = 1; i < ARRAY_SIZE(pm2xxx_charger_voltage_map); i++) { | ||
532 | if (curr < pm2xxx_charger_voltage_map[i]) | ||
533 | return i - 1; | ||
534 | } | ||
535 | |||
536 | i = ARRAY_SIZE(pm2xxx_charger_voltage_map) - 1; | ||
537 | if (curr == pm2xxx_charger_voltage_map[i]) | ||
538 | return i; | ||
539 | else | ||
540 | return -EINVAL; | ||
541 | } | ||
542 | |||
543 | static int pm2xxx_charger_update_charger_current(struct ux500_charger *charger, | ||
544 | int ich_out) | ||
545 | { | ||
546 | int ret; | ||
547 | int curr_index; | ||
548 | struct pm2xxx_charger *pm2; | ||
549 | u8 val; | ||
550 | |||
551 | if (charger->psy.type == POWER_SUPPLY_TYPE_MAINS) | ||
552 | pm2 = to_pm2xxx_charger_ac_device_info(charger); | ||
553 | else | ||
554 | return -ENXIO; | ||
555 | |||
556 | curr_index = pm2xxx_current_to_regval(ich_out); | ||
557 | if (curr_index < 0) { | ||
558 | dev_err(pm2->dev, | ||
559 | "Charger current too high, charging not started\n"); | ||
560 | return -ENXIO; | ||
561 | } | ||
562 | |||
563 | ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG6, &val); | ||
564 | if (ret >= 0) { | ||
565 | val &= ~PM2XXX_DIR_CH_CC_CURRENT_MASK; | ||
566 | val |= curr_index; | ||
567 | ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6, val); | ||
568 | if (ret < 0) { | ||
569 | dev_err(pm2->dev, | ||
570 | "%s write failed\n", __func__); | ||
571 | } | ||
572 | } | ||
573 | else | ||
574 | dev_err(pm2->dev, "%s read failed\n", __func__); | ||
575 | |||
576 | return ret; | ||
577 | } | ||
578 | |||
579 | static int pm2xxx_charger_ac_get_property(struct power_supply *psy, | ||
580 | enum power_supply_property psp, | ||
581 | union power_supply_propval *val) | ||
582 | { | ||
583 | struct pm2xxx_charger *pm2; | ||
584 | |||
585 | pm2 = to_pm2xxx_charger_ac_device_info(psy_to_ux500_charger(psy)); | ||
586 | |||
587 | switch (psp) { | ||
588 | case POWER_SUPPLY_PROP_HEALTH: | ||
589 | if (pm2->flags.mainextchnotok) | ||
590 | val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; | ||
591 | else if (pm2->ac.wd_expired) | ||
592 | val->intval = POWER_SUPPLY_HEALTH_DEAD; | ||
593 | else if (pm2->flags.main_thermal_prot) | ||
594 | val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; | ||
595 | else | ||
596 | val->intval = POWER_SUPPLY_HEALTH_GOOD; | ||
597 | break; | ||
598 | case POWER_SUPPLY_PROP_ONLINE: | ||
599 | val->intval = pm2->ac.charger_online; | ||
600 | break; | ||
601 | case POWER_SUPPLY_PROP_PRESENT: | ||
602 | val->intval = pm2->ac.charger_connected; | ||
603 | break; | ||
604 | case POWER_SUPPLY_PROP_VOLTAGE_AVG: | ||
605 | pm2->ac.cv_active = pm2xxx_charger_get_ac_cv(pm2); | ||
606 | val->intval = pm2->ac.cv_active; | ||
607 | break; | ||
608 | default: | ||
609 | return -EINVAL; | ||
610 | } | ||
611 | return 0; | ||
612 | } | ||
613 | |||
614 | static int pm2xxx_charging_init(struct pm2xxx_charger *pm2) | ||
615 | { | ||
616 | int ret = 0; | ||
617 | |||
618 | /* enable CC and CV watchdog */ | ||
619 | ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG3, | ||
620 | (PM2XXX_CH_WD_CV_PHASE_60MIN | PM2XXX_CH_WD_CC_PHASE_60MIN)); | ||
621 | if( ret < 0) | ||
622 | return ret; | ||
623 | |||
624 | /* enable precharge watchdog */ | ||
625 | ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG4, | ||
626 | PM2XXX_CH_WD_PRECH_PHASE_60MIN); | ||
627 | |||
628 | /* Disable auto timeout */ | ||
629 | ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG5, | ||
630 | PM2XXX_CH_WD_AUTO_TIMEOUT_20MIN); | ||
631 | |||
632 | /* | ||
633 | * EOC current level = 100mA | ||
634 | * Precharge current level = 100mA | ||
635 | * CC current level = 1000mA | ||
636 | */ | ||
637 | ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6, | ||
638 | (PM2XXX_DIR_CH_CC_CURRENT_1000MA | | ||
639 | PM2XXX_CH_PRECH_CURRENT_100MA | | ||
640 | PM2XXX_CH_EOC_CURRENT_100MA)); | ||
641 | |||
642 | /* | ||
643 | * recharge threshold = 3.8V | ||
644 | * Precharge to CC threshold = 2.9V | ||
645 | */ | ||
646 | ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG7, | ||
647 | (PM2XXX_CH_PRECH_VOL_2_9 | PM2XXX_CH_VRESUME_VOL_3_8)); | ||
648 | |||
649 | /* float voltage charger level = 4.2V */ | ||
650 | ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG8, | ||
651 | PM2XXX_CH_VOLT_4_2); | ||
652 | |||
653 | /* Voltage drop between VBAT and VSYS in HW charging = 300mV */ | ||
654 | ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG9, | ||
655 | (PM2XXX_CH_150MV_DROP_300MV | PM2XXX_CHARCHING_INFO_DIS | | ||
656 | PM2XXX_CH_CC_REDUCED_CURRENT_IDENT | | ||
657 | PM2XXX_CH_CC_MODEDROP_DIS)); | ||
658 | |||
659 | /* Input charger level of over voltage = 10V */ | ||
660 | ret = pm2xxx_reg_write(pm2, PM2XXX_INP_VOLT_VPWR2, | ||
661 | PM2XXX_VPWR2_OVV_10); | ||
662 | ret = pm2xxx_reg_write(pm2, PM2XXX_INP_VOLT_VPWR1, | ||
663 | PM2XXX_VPWR1_OVV_10); | ||
664 | |||
665 | /* Input charger drop */ | ||
666 | ret = pm2xxx_reg_write(pm2, PM2XXX_INP_DROP_VPWR2, | ||
667 | (PM2XXX_VPWR2_HW_OPT_DIS | PM2XXX_VPWR2_VALID_DIS | | ||
668 | PM2XXX_VPWR2_DROP_DIS)); | ||
669 | ret = pm2xxx_reg_write(pm2, PM2XXX_INP_DROP_VPWR1, | ||
670 | (PM2XXX_VPWR1_HW_OPT_DIS | PM2XXX_VPWR1_VALID_DIS | | ||
671 | PM2XXX_VPWR1_DROP_DIS)); | ||
672 | |||
673 | /* Disable battery low monitoring */ | ||
674 | ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_LOW_LEV_COMP_REG, | ||
675 | PM2XXX_VBAT_LOW_MONITORING_ENA); | ||
676 | |||
677 | /* Disable LED */ | ||
678 | ret = pm2xxx_reg_write(pm2, PM2XXX_LED_CTRL_REG, | ||
679 | PM2XXX_LED_SELECT_DIS); | ||
680 | |||
681 | return ret; | ||
682 | } | ||
683 | |||
684 | static int pm2xxx_charger_ac_en(struct ux500_charger *charger, | ||
685 | int enable, int vset, int iset) | ||
686 | { | ||
687 | int ret; | ||
688 | int volt_index; | ||
689 | int curr_index; | ||
690 | u8 val; | ||
691 | |||
692 | struct pm2xxx_charger *pm2 = to_pm2xxx_charger_ac_device_info(charger); | ||
693 | |||
694 | if (enable) { | ||
695 | if (!pm2->ac.charger_connected) { | ||
696 | dev_dbg(pm2->dev, "AC charger not connected\n"); | ||
697 | return -ENXIO; | ||
698 | } | ||
699 | |||
700 | dev_dbg(pm2->dev, "Enable AC: %dmV %dmA\n", vset, iset); | ||
701 | if (!pm2->vddadc_en_ac) { | ||
702 | regulator_enable(pm2->regu); | ||
703 | pm2->vddadc_en_ac = true; | ||
704 | } | ||
705 | |||
706 | ret = pm2xxx_charging_init(pm2); | ||
707 | if (ret < 0) { | ||
708 | dev_err(pm2->dev, "%s charging init failed\n", | ||
709 | __func__); | ||
710 | goto error_occured; | ||
711 | } | ||
712 | |||
713 | volt_index = pm2xxx_voltage_to_regval(vset); | ||
714 | curr_index = pm2xxx_current_to_regval(iset); | ||
715 | |||
716 | if (volt_index < 0 || curr_index < 0) { | ||
717 | dev_err(pm2->dev, | ||
718 | "Charger voltage or current too high, " | ||
719 | "charging not started\n"); | ||
720 | return -ENXIO; | ||
721 | } | ||
722 | |||
723 | ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG8, &val); | ||
724 | if (ret < 0) { | ||
725 | dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__); | ||
726 | goto error_occured; | ||
727 | } | ||
728 | val &= ~PM2XXX_CH_VOLT_MASK; | ||
729 | val |= volt_index; | ||
730 | ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG8, val); | ||
731 | if (ret < 0) { | ||
732 | dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__); | ||
733 | goto error_occured; | ||
734 | } | ||
735 | |||
736 | ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG6, &val); | ||
737 | if (ret < 0) { | ||
738 | dev_err(pm2->dev, "%s pm2xxx read failed\n", __func__); | ||
739 | goto error_occured; | ||
740 | } | ||
741 | val &= ~PM2XXX_DIR_CH_CC_CURRENT_MASK; | ||
742 | val |= curr_index; | ||
743 | ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6, val); | ||
744 | if (ret < 0) { | ||
745 | dev_err(pm2->dev, "%s pm2xxx write failed\n", __func__); | ||
746 | goto error_occured; | ||
747 | } | ||
748 | |||
749 | if (!pm2->bat->enable_overshoot) { | ||
750 | ret = pm2xxx_reg_read(pm2, PM2XXX_LED_CTRL_REG, &val); | ||
751 | if (ret < 0) { | ||
752 | dev_err(pm2->dev, "%s pm2xxx read failed\n", | ||
753 | __func__); | ||
754 | goto error_occured; | ||
755 | } | ||
756 | val |= PM2XXX_ANTI_OVERSHOOT_EN; | ||
757 | ret = pm2xxx_reg_write(pm2, PM2XXX_LED_CTRL_REG, val); | ||
758 | if (ret < 0) { | ||
759 | dev_err(pm2->dev, "%s pm2xxx write failed\n", | ||
760 | __func__); | ||
761 | goto error_occured; | ||
762 | } | ||
763 | } | ||
764 | |||
765 | ret = pm2xxx_charging_enable_mngt(pm2); | ||
766 | if (ret < 0) { | ||
767 | dev_err(pm2->dev, "Failed to enable" | ||
768 | "pm2xxx ac charger\n"); | ||
769 | goto error_occured; | ||
770 | } | ||
771 | |||
772 | pm2->ac.charger_online = 1; | ||
773 | } else { | ||
774 | pm2->ac.charger_online = 0; | ||
775 | pm2->ac.wd_expired = false; | ||
776 | |||
777 | /* Disable regulator if enabled */ | ||
778 | if (pm2->vddadc_en_ac) { | ||
779 | regulator_disable(pm2->regu); | ||
780 | pm2->vddadc_en_ac = false; | ||
781 | } | ||
782 | |||
783 | ret = pm2xxx_charging_disable_mngt(pm2); | ||
784 | if (ret < 0) { | ||
785 | dev_err(pm2->dev, "failed to disable" | ||
786 | "pm2xxx ac charger\n"); | ||
787 | goto error_occured; | ||
788 | } | ||
789 | |||
790 | dev_dbg(pm2->dev, "PM2301: " "Disabled AC charging\n"); | ||
791 | } | ||
792 | power_supply_changed(&pm2->ac_chg.psy); | ||
793 | |||
794 | error_occured: | ||
795 | return ret; | ||
796 | } | ||
797 | |||
798 | static int pm2xxx_charger_watchdog_kick(struct ux500_charger *charger) | ||
799 | { | ||
800 | int ret; | ||
801 | struct pm2xxx_charger *pm2; | ||
802 | |||
803 | if (charger->psy.type == POWER_SUPPLY_TYPE_MAINS) | ||
804 | pm2 = to_pm2xxx_charger_ac_device_info(charger); | ||
805 | else | ||
806 | return -ENXIO; | ||
807 | |||
808 | ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_WD_KICK, WD_TIMER); | ||
809 | if (ret) | ||
810 | dev_err(pm2->dev, "Failed to kick WD!\n"); | ||
811 | |||
812 | return ret; | ||
813 | } | ||
814 | |||
815 | static void pm2xxx_charger_ac_work(struct work_struct *work) | ||
816 | { | ||
817 | struct pm2xxx_charger *pm2 = container_of(work, | ||
818 | struct pm2xxx_charger, ac_work); | ||
819 | |||
820 | |||
821 | power_supply_changed(&pm2->ac_chg.psy); | ||
822 | sysfs_notify(&pm2->ac_chg.psy.dev->kobj, NULL, "present"); | ||
823 | }; | ||
824 | |||
825 | static void pm2xxx_charger_check_main_thermal_prot_work( | ||
826 | struct work_struct *work) | ||
827 | { | ||
828 | }; | ||
829 | |||
830 | static struct pm2xxx_interrupts pm2xxx_int = { | ||
831 | .handler[0] = pm2_int_reg0, | ||
832 | .handler[1] = pm2_int_reg1, | ||
833 | .handler[2] = pm2_int_reg2, | ||
834 | .handler[3] = pm2_int_reg3, | ||
835 | .handler[4] = pm2_int_reg4, | ||
836 | .handler[5] = pm2_int_reg5, | ||
837 | }; | ||
838 | |||
839 | static struct pm2xxx_irq pm2xxx_charger_irq[] = { | ||
840 | {"PM2XXX_IRQ_INT", pm2xxx_irq_int}, | ||
841 | }; | ||
842 | |||
843 | static int pm2xxx_wall_charger_resume(struct i2c_client *i2c_client) | ||
844 | { | ||
845 | return 0; | ||
846 | } | ||
847 | |||
848 | static int pm2xxx_wall_charger_suspend(struct i2c_client *i2c_client, | ||
849 | pm_message_t state) | ||
850 | { | ||
851 | return 0; | ||
852 | } | ||
853 | |||
854 | static int __devinit pm2xxx_wall_charger_probe(struct i2c_client *i2c_client, | ||
855 | const struct i2c_device_id *id) | ||
856 | { | ||
857 | struct pm2xxx_platform_data *pl_data = i2c_client->dev.platform_data; | ||
858 | struct pm2xxx_charger *pm2; | ||
859 | int ret = 0; | ||
860 | u8 val; | ||
861 | |||
862 | pm2 = kzalloc(sizeof(struct pm2xxx_charger), GFP_KERNEL); | ||
863 | if (!pm2) { | ||
864 | dev_err(pm2->dev, "pm2xxx_charger allocation failed\n"); | ||
865 | return -ENOMEM; | ||
866 | } | ||
867 | |||
868 | /* get parent data */ | ||
869 | pm2->dev = &i2c_client->dev; | ||
870 | pm2->gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); | ||
871 | |||
872 | pm2->pm2_int = &pm2xxx_int; | ||
873 | |||
874 | /* get charger spcific platform data */ | ||
875 | if (!pl_data->wall_charger) { | ||
876 | dev_err(pm2->dev, "no charger platform data supplied\n"); | ||
877 | ret = -EINVAL; | ||
878 | goto free_device_info; | ||
879 | } | ||
880 | |||
881 | pm2->pdata = pl_data->wall_charger; | ||
882 | |||
883 | /* get battery specific platform data */ | ||
884 | if (!pl_data->battery) { | ||
885 | dev_err(pm2->dev, "no battery platform data supplied\n"); | ||
886 | ret = -EINVAL; | ||
887 | goto free_device_info; | ||
888 | } | ||
889 | |||
890 | pm2->bat = pl_data->battery; | ||
891 | |||
892 | /*get lpn GPIO from platform data*/ | ||
893 | if (!pm2->pdata->lpn_gpio) { | ||
894 | dev_err(pm2->dev, "no lpn gpio data supplied\n"); | ||
895 | ret = -EINVAL; | ||
896 | goto free_device_info; | ||
897 | } | ||
898 | pm2->lpn_pin = pm2->pdata->lpn_gpio; | ||
899 | |||
900 | if (!i2c_check_functionality(i2c_client->adapter, | ||
901 | I2C_FUNC_SMBUS_BYTE_DATA | | ||
902 | I2C_FUNC_SMBUS_READ_WORD_DATA)) { | ||
903 | ret = -ENODEV; | ||
904 | dev_info(pm2->dev, "pm2301 i2c_check_functionality failed\n"); | ||
905 | goto free_device_info; | ||
906 | } | ||
907 | |||
908 | pm2->config.pm2xxx_i2c = i2c_client; | ||
909 | pm2->config.pm2xxx_id = (struct i2c_device_id *) id; | ||
910 | i2c_set_clientdata(i2c_client, pm2); | ||
911 | |||
912 | /* AC supply */ | ||
913 | /* power_supply base class */ | ||
914 | pm2->ac_chg.psy.name = pm2->pdata->label; | ||
915 | pm2->ac_chg.psy.type = POWER_SUPPLY_TYPE_MAINS; | ||
916 | pm2->ac_chg.psy.properties = pm2xxx_charger_ac_props; | ||
917 | pm2->ac_chg.psy.num_properties = ARRAY_SIZE(pm2xxx_charger_ac_props); | ||
918 | pm2->ac_chg.psy.get_property = pm2xxx_charger_ac_get_property; | ||
919 | pm2->ac_chg.psy.supplied_to = pm2->pdata->supplied_to; | ||
920 | pm2->ac_chg.psy.num_supplicants = pm2->pdata->num_supplicants; | ||
921 | /* pm2xxx_charger sub-class */ | ||
922 | pm2->ac_chg.ops.enable = &pm2xxx_charger_ac_en; | ||
923 | pm2->ac_chg.ops.kick_wd = &pm2xxx_charger_watchdog_kick; | ||
924 | pm2->ac_chg.ops.update_curr = &pm2xxx_charger_update_charger_current; | ||
925 | pm2->ac_chg.max_out_volt = pm2xxx_charger_voltage_map[ | ||
926 | ARRAY_SIZE(pm2xxx_charger_voltage_map) - 1]; | ||
927 | pm2->ac_chg.max_out_curr = pm2xxx_charger_current_map[ | ||
928 | ARRAY_SIZE(pm2xxx_charger_current_map) - 1]; | ||
929 | pm2->ac_chg.wdt_refresh = WD_KICK_INTERVAL; | ||
930 | pm2->ac_chg.enabled = true; | ||
931 | pm2->ac_chg.external = true; | ||
932 | |||
933 | /* Create a work queue for the charger */ | ||
934 | pm2->charger_wq = | ||
935 | create_singlethread_workqueue("pm2xxx_charger_wq"); | ||
936 | if (pm2->charger_wq == NULL) { | ||
937 | dev_err(pm2->dev, "failed to create work queue\n"); | ||
938 | goto free_device_info; | ||
939 | } | ||
940 | |||
941 | /* Init work for charger detection */ | ||
942 | INIT_WORK(&pm2->ac_work, pm2xxx_charger_ac_work); | ||
943 | |||
944 | /* Init work for checking HW status */ | ||
945 | INIT_WORK(&pm2->check_main_thermal_prot_work, | ||
946 | pm2xxx_charger_check_main_thermal_prot_work); | ||
947 | |||
948 | /* | ||
949 | * VDD ADC supply needs to be enabled from this driver when there | ||
950 | * is a charger connected to avoid erroneous BTEMP_HIGH/LOW | ||
951 | * interrupts during charging | ||
952 | */ | ||
953 | pm2->regu = regulator_get(pm2->dev, "vddadc"); | ||
954 | if (IS_ERR(pm2->regu)) { | ||
955 | ret = PTR_ERR(pm2->regu); | ||
956 | dev_err(pm2->dev, "failed to get vddadc regulator\n"); | ||
957 | goto free_charger_wq; | ||
958 | } | ||
959 | |||
960 | /* Register AC charger class */ | ||
961 | ret = power_supply_register(pm2->dev, &pm2->ac_chg.psy); | ||
962 | if (ret) { | ||
963 | dev_err(pm2->dev, "failed to register AC charger\n"); | ||
964 | goto free_regulator; | ||
965 | } | ||
966 | |||
967 | /* Register interrupts */ | ||
968 | ret = request_threaded_irq(pm2->pdata->irq_number, NULL, | ||
969 | pm2xxx_charger_irq[0].isr, | ||
970 | pm2->pdata->irq_type, | ||
971 | pm2xxx_charger_irq[0].name, pm2); | ||
972 | |||
973 | if (ret != 0) { | ||
974 | dev_err(pm2->dev, "failed to request %s IRQ %d: %d\n", | ||
975 | pm2xxx_charger_irq[0].name, pm2->pdata->irq_number, ret); | ||
976 | goto unregister_pm2xxx_charger; | ||
977 | } | ||
978 | |||
979 | /*Initialize lock*/ | ||
980 | mutex_init(&pm2->lock); | ||
981 | |||
982 | /* | ||
983 | * Charger detection mechanism requires pulling up the LPN pin | ||
984 | * while i2c communication if Charger is not connected | ||
985 | * LPN pin of PM2301 is GPIO60 of AB9540 | ||
986 | */ | ||
987 | ret = gpio_request(pm2->lpn_pin, "pm2301_lpm_gpio"); | ||
988 | if (ret < 0) { | ||
989 | dev_err(pm2->dev, "pm2301_lpm_gpio request failed\n"); | ||
990 | goto unregister_pm2xxx_charger; | ||
991 | } | ||
992 | ret = gpio_direction_output(pm2->lpn_pin, 0); | ||
993 | if (ret < 0) { | ||
994 | dev_err(pm2->dev, "pm2301_lpm_gpio direction failed\n"); | ||
995 | goto free_gpio; | ||
996 | } | ||
997 | |||
998 | ret = pm2xxx_charger_detection(pm2, &val); | ||
999 | |||
1000 | if ((ret == 0) && val) { | ||
1001 | pm2->ac.charger_connected = 1; | ||
1002 | pm2->ac_conn = true; | ||
1003 | power_supply_changed(&pm2->ac_chg.psy); | ||
1004 | sysfs_notify(&pm2->ac_chg.psy.dev->kobj, NULL, "present"); | ||
1005 | } | ||
1006 | |||
1007 | return 0; | ||
1008 | |||
1009 | free_gpio: | ||
1010 | gpio_free(pm2->lpn_pin); | ||
1011 | unregister_pm2xxx_charger: | ||
1012 | /* unregister power supply */ | ||
1013 | power_supply_unregister(&pm2->ac_chg.psy); | ||
1014 | free_regulator: | ||
1015 | /* disable the regulator */ | ||
1016 | regulator_put(pm2->regu); | ||
1017 | free_charger_wq: | ||
1018 | destroy_workqueue(pm2->charger_wq); | ||
1019 | free_device_info: | ||
1020 | kfree(pm2); | ||
1021 | return ret; | ||
1022 | } | ||
1023 | |||
1024 | static int __devexit pm2xxx_wall_charger_remove(struct i2c_client *i2c_client) | ||
1025 | { | ||
1026 | struct pm2xxx_charger *pm2 = i2c_get_clientdata(i2c_client); | ||
1027 | |||
1028 | /* Disable AC charging */ | ||
1029 | pm2xxx_charger_ac_en(&pm2->ac_chg, false, 0, 0); | ||
1030 | |||
1031 | /* Disable interrupts */ | ||
1032 | free_irq(pm2->pdata->irq_number, pm2); | ||
1033 | |||
1034 | /* Delete the work queue */ | ||
1035 | destroy_workqueue(pm2->charger_wq); | ||
1036 | |||
1037 | flush_scheduled_work(); | ||
1038 | |||
1039 | /* disable the regulator */ | ||
1040 | regulator_put(pm2->regu); | ||
1041 | |||
1042 | power_supply_unregister(&pm2->ac_chg.psy); | ||
1043 | |||
1044 | /*Free GPIO60*/ | ||
1045 | gpio_free(pm2->lpn_pin); | ||
1046 | |||
1047 | kfree(pm2); | ||
1048 | |||
1049 | return 0; | ||
1050 | } | ||
1051 | |||
1052 | static const struct i2c_device_id pm2xxx_id[] = { | ||
1053 | { "pm2301", 0 }, | ||
1054 | { } | ||
1055 | }; | ||
1056 | |||
1057 | MODULE_DEVICE_TABLE(i2c, pm2xxx_id); | ||
1058 | |||
1059 | static struct i2c_driver pm2xxx_charger_driver = { | ||
1060 | .probe = pm2xxx_wall_charger_probe, | ||
1061 | .remove = __devexit_p(pm2xxx_wall_charger_remove), | ||
1062 | .suspend = pm2xxx_wall_charger_suspend, | ||
1063 | .resume = pm2xxx_wall_charger_resume, | ||
1064 | .driver = { | ||
1065 | .name = "pm2xxx-wall_charger", | ||
1066 | .owner = THIS_MODULE, | ||
1067 | }, | ||
1068 | .id_table = pm2xxx_id, | ||
1069 | }; | ||
1070 | |||
1071 | static int __init pm2xxx_charger_init(void) | ||
1072 | { | ||
1073 | return i2c_add_driver(&pm2xxx_charger_driver); | ||
1074 | } | ||
1075 | |||
1076 | static void __exit pm2xxx_charger_exit(void) | ||
1077 | { | ||
1078 | i2c_del_driver(&pm2xxx_charger_driver); | ||
1079 | } | ||
1080 | |||
1081 | subsys_initcall_sync(pm2xxx_charger_init); | ||
1082 | module_exit(pm2xxx_charger_exit); | ||
1083 | |||
1084 | MODULE_LICENSE("GPL v2"); | ||
1085 | MODULE_AUTHOR("Rajkumar kasirajan, Olivier Launay"); | ||
1086 | MODULE_ALIAS("platform:pm2xxx-charger"); | ||
1087 | MODULE_DESCRIPTION("PM2xxx charger management driver"); | ||
1088 | |||