blob: ed48d75bb7866fe306dea509d450fb64fde8c7e5 [file] [log] [blame]
Michel JAOUEN01ec8c52012-04-26 10:00:04 +02001/*
Michel JAOUEN01ec8c52012-04-26 10:00:04 +02002 * Copyright 2012 ST Ericsson.
3 *
Lee Jones2fa5b0f2013-01-23 14:33:47 +00004 * Power supply driver for ST Ericsson pm2xxx_charger charger
5 *
Michel JAOUEN01ec8c52012-04-26 10:00:04 +02006 * 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>
Lee Jones39880432013-01-23 14:38:15 +000031#include <linux/gpio.h>
Michel JAOUEN01ec8c52012-04-26 10:00:04 +020032
Lee Jones2fa5b0f2013-01-23 14:33:47 +000033#include "pm2301_charger.h"
Michel JAOUEN01ec8c52012-04-26 10:00:04 +020034
35#define to_pm2xxx_charger_ac_device_info(x) container_of((x), \
36 struct pm2xxx_charger, ac_chg)
37
38static 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
Michel JAOUEN01ec8c52012-04-26 10:00:04 +020047static enum power_supply_property pm2xxx_charger_ac_props[] = {
48 POWER_SUPPLY_PROP_HEALTH,
49 POWER_SUPPLY_PROP_PRESENT,
50 POWER_SUPPLY_PROP_ONLINE,
Michel JAOUEN01ec8c52012-04-26 10:00:04 +020051 POWER_SUPPLY_PROP_VOLTAGE_AVG,
Michel JAOUEN01ec8c52012-04-26 10:00:04 +020052};
53
54static 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
90static 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
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200109static const struct i2c_device_id pm2xxx_ident[] = {
110 { "pm2301", 0 },
111 { }
112};
113
Lee Jones39880432013-01-23 14:38:15 +0000114static 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
123static 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
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200132static int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val)
133{
134 int ret;
Lee Jones39880432013-01-23 14:38:15 +0000135 /*
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);
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200143
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);
Rajkumar Kasirajane41f39e2012-05-28 15:57:33 +0530148 else
149 ret = 0;
Lee Jones39880432013-01-23 14:38:15 +0000150 clear_lpn_pin(pm2);
151 mutex_unlock(&pm2->lock);
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200152
153 return ret;
154}
155
156static int pm2xxx_reg_write(struct pm2xxx_charger *pm2, int reg, u8 val)
157{
158 int ret;
Lee Jones39880432013-01-23 14:38:15 +0000159 /*
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);
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200167
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);
Rajkumar Kasirajane41f39e2012-05-28 15:57:33 +0530172 else
173 ret = 0;
Lee Jones39880432013-01-23 14:38:15 +0000174 clear_lpn_pin(pm2);
175 mutex_unlock(&pm2->lock);
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200176
177 return ret;
178}
179
180static 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
191static 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
202static 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
210int 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
217static 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
232out:
233 return ret;
234}
235
236static 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
246static 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
264static int pm2xxx_charger_bat_disc_mngt(struct pm2xxx_charger *pm2, int val)
265{
266 dev_dbg(pm2->dev, "battery disconnected\n");
267
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200268 return 0;
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200269}
270
271static int pm2xxx_charger_detection(struct pm2xxx_charger *pm2, u8 *val)
272{
Rajkumar Kasirajane41f39e2012-05-28 15:57:33 +0530273 int ret;
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200274
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);
Rajkumar Kasirajane41f39e2012-05-28 15:57:33 +0530283
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200284out:
285 return ret;
286}
287
288static 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
311static 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
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200320static int pm2_int_reg0(void *pm2_data, int val)
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200321{
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200322 struct pm2xxx_charger *pm2 = pm2_data;
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200323 int ret = 0;
324
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200325 if (val & (PM2XXX_INT1_ITVBATLOWR | PM2XXX_INT1_ITVBATLOWF)) {
326 ret = pm2xxx_charger_vbat_lsig_mngt(pm2, val &
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200327 (PM2XXX_INT1_ITVBATLOWR | PM2XXX_INT1_ITVBATLOWF));
328 }
329
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200330 if (val & PM2XXX_INT1_ITVBATDISCONNECT) {
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200331 ret = pm2xxx_charger_bat_disc_mngt(pm2,
332 PM2XXX_INT1_ITVBATDISCONNECT);
333 }
334
335 return ret;
336}
337
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200338static int pm2_int_reg1(void *pm2_data, int val)
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200339{
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200340 struct pm2xxx_charger *pm2 = pm2_data;
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200341 int ret = 0;
342
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200343 if (val & (PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG)) {
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200344 dev_dbg(pm2->dev , "Main charger plugged\n");
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200345 ret = pm2xxx_charger_itv_pwr_plug_mngt(pm2, val &
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200346 (PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG));
347 }
348
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200349 if (val &
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200350 (PM2XXX_INT2_ITVPWR1UNPLUG | PM2XXX_INT2_ITVPWR2UNPLUG)) {
351 dev_dbg(pm2->dev , "Main charger unplugged\n");
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200352 ret = pm2xxx_charger_itv_pwr_unplug_mngt(pm2, val &
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200353 (PM2XXX_INT2_ITVPWR1UNPLUG |
354 PM2XXX_INT2_ITVPWR2UNPLUG));
355 }
356
357 return ret;
358}
359
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200360static int pm2_int_reg2(void *pm2_data, int val)
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200361{
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200362 struct pm2xxx_charger *pm2 = pm2_data;
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200363 int ret = 0;
364
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200365 if (val & PM2XXX_INT3_ITAUTOTIMEOUTWD)
366 ret = pm2xxx_charger_wd_exp_mngt(pm2, val);
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200367
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200368 if (val & (PM2XXX_INT3_ITCHPRECHARGEWD |
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200369 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
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200377static int pm2_int_reg3(void *pm2_data, int val)
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200378{
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200379 struct pm2xxx_charger *pm2 = pm2_data;
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200380 int ret = 0;
381
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200382 if (val & (PM2XXX_INT4_ITCHARGINGON)) {
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200383 dev_dbg(pm2->dev ,
384 "chargind operation has started\n");
385 }
386
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200387 if (val & (PM2XXX_INT4_ITVRESUME)) {
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200388 dev_dbg(pm2->dev,
389 "battery discharged down to VResume threshold\n");
390 }
391
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200392 if (val & (PM2XXX_INT4_ITBATTFULL)) {
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200393 dev_dbg(pm2->dev , "battery fully detected\n");
394 }
395
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200396 if (val & (PM2XXX_INT4_ITCVPHASE)) {
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200397 dev_dbg(pm2->dev, "CV phase enter with 0.5C charging\n");
398 }
399
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200400 if (val & (PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV)) {
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200401 pm2->failure_case = VPWR_OVV;
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200402 ret = pm2xxx_charger_ovv_mngt(pm2, val &
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200403 (PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV));
404 dev_dbg(pm2->dev, "VPWR/VSYSTEM overvoltage detected\n");
405 }
406
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200407 if (val & (PM2XXX_INT4_S_ITBATTEMPCOLD |
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200408 PM2XXX_INT4_S_ITBATTEMPHOT)) {
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200409 ret = pm2xxx_charger_batt_therm_mngt(pm2, val &
410 (PM2XXX_INT4_S_ITBATTEMPCOLD |
411 PM2XXX_INT4_S_ITBATTEMPHOT));
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200412 dev_dbg(pm2->dev, "BTEMP is too Low/High\n");
413 }
414
415 return ret;
416}
417
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200418static int pm2_int_reg4(void *pm2_data, int val)
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200419{
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200420 struct pm2xxx_charger *pm2 = pm2_data;
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200421 int ret = 0;
422
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200423 if (val & PM2XXX_INT5_ITVSYSTEMOVV) {
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200424 pm2->failure_case = VSYSTEM_OVV;
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200425 ret = pm2xxx_charger_ovv_mngt(pm2, val &
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200426 PM2XXX_INT5_ITVSYSTEMOVV);
427 dev_dbg(pm2->dev, "VSYSTEM overvoltage detected\n");
428 }
429
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200430 if (val & (PM2XXX_INT5_ITTHERMALWARNINGFALL |
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200431 PM2XXX_INT5_ITTHERMALWARNINGRISE |
432 PM2XXX_INT5_ITTHERMALSHUTDOWNFALL |
433 PM2XXX_INT5_ITTHERMALSHUTDOWNRISE)) {
434 dev_dbg(pm2->dev, "BTEMP die temperature is too Low/High\n");
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200435 ret = pm2xxx_charger_die_therm_mngt(pm2, val &
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200436 (PM2XXX_INT5_ITTHERMALWARNINGFALL |
437 PM2XXX_INT5_ITTHERMALWARNINGRISE |
438 PM2XXX_INT5_ITTHERMALSHUTDOWNFALL |
439 PM2XXX_INT5_ITTHERMALSHUTDOWNRISE));
440 }
441
442 return ret;
443}
444
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200445static int pm2_int_reg5(void *pm2_data, int val)
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200446{
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200447 struct pm2xxx_charger *pm2 = pm2_data;
448 int ret = 0;
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200449
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200450
451 if (val & (PM2XXX_INT6_ITVPWR2DROP | PM2XXX_INT6_ITVPWR1DROP)) {
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200452 dev_dbg(pm2->dev, "VMPWR drop to VBAT level\n");
453 }
454
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200455 if (val & (PM2XXX_INT6_ITVPWR2VALIDRISE |
456 PM2XXX_INT6_ITVPWR1VALIDRISE |
457 PM2XXX_INT6_ITVPWR2VALIDFALL |
458 PM2XXX_INT6_ITVPWR1VALIDFALL)) {
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200459 dev_dbg(pm2->dev, "Falling/Rising edge on WPWR1/2\n");
460 }
461
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200462 return ret;
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200463}
464
465static irqreturn_t pm2xxx_irq_int(int irq, void *data)
466{
467 struct pm2xxx_charger *pm2 = data;
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200468 struct pm2xxx_interrupts *interrupt = pm2->pm2_int;
469 int i;
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200470
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200471 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]);
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200478 }
479
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200480 return IRQ_HANDLED;
481}
482
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200483static 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 }
501out:
502 return ret;
503}
504
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200505static 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
524static 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
543static 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,
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200559 "Charger current too high, charging not started\n");
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200560 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
579static 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;
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200604 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;
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200608 default:
609 return -EINVAL;
610 }
611 return 0;
612}
613
614static 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
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200628 /* 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,
Rajkumar Kasirajane41f39e2012-05-28 15:57:33 +0530675 PM2XXX_VBAT_LOW_MONITORING_ENA);
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200676
677 /* Disable LED */
678 ret = pm2xxx_reg_write(pm2, PM2XXX_LED_CTRL_REG,
679 PM2XXX_LED_SELECT_DIS);
680
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200681 return ret;
682}
683
684static 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);
Rajkumar Kasirajane41f39e2012-05-28 15:57:33 +0530724 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;
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200734 }
735
736 ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG6, &val);
Rajkumar Kasirajane41f39e2012-05-28 15:57:33 +0530737 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;
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200747 }
748
749 if (!pm2->bat->enable_overshoot) {
750 ret = pm2xxx_reg_read(pm2, PM2XXX_LED_CTRL_REG, &val);
Rajkumar Kasirajane41f39e2012-05-28 15:57:33 +0530751 if (ret < 0) {
752 dev_err(pm2->dev, "%s pm2xxx read failed\n",
753 __func__);
754 goto error_occured;
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200755 }
Rajkumar Kasirajane41f39e2012-05-28 15:57:33 +0530756 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 }
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200763 }
764
765 ret = pm2xxx_charging_enable_mngt(pm2);
Rajkumar Kasirajane41f39e2012-05-28 15:57:33 +0530766 if (ret < 0) {
767 dev_err(pm2->dev, "Failed to enable"
768 "pm2xxx ac charger\n");
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200769 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);
Rajkumar Kasirajane41f39e2012-05-28 15:57:33 +0530784 if (ret < 0) {
785 dev_err(pm2->dev, "failed to disable"
786 "pm2xxx ac charger\n");
787 goto error_occured;
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200788 }
789
790 dev_dbg(pm2->dev, "PM2301: " "Disabled AC charging\n");
791 }
792 power_supply_changed(&pm2->ac_chg.psy);
793
794error_occured:
795 return ret;
796}
797
798static 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
815static 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
825static void pm2xxx_charger_check_main_thermal_prot_work(
826 struct work_struct *work)
827{
828};
829
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200830static 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
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200839static struct pm2xxx_irq pm2xxx_charger_irq[] = {
840 {"PM2XXX_IRQ_INT", pm2xxx_irq_int},
841};
842
843static int pm2xxx_wall_charger_resume(struct i2c_client *i2c_client)
844{
845 return 0;
846}
847
848static int pm2xxx_wall_charger_suspend(struct i2c_client *i2c_client,
849 pm_message_t state)
850{
851 return 0;
852}
853
854static 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
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200872 pm2->pm2_int = &pm2xxx_int;
873
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200874 /* 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
Lee Jones39880432013-01-23 14:38:15 +0000892 /*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
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200900 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];
Loic Pallardye07a5642012-05-10 15:33:56 +0200929 pm2->ac_chg.wdt_refresh = WD_KICK_INTERVAL;
Olivier Clergeaud006f82d2012-05-14 16:50:29 +0200930 pm2->ac_chg.enabled = true;
Loic Pallardye07a5642012-05-10 15:33:56 +0200931 pm2->ac_chg.external = true;
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200932
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
Lee Jones39880432013-01-23 14:38:15 +0000979 /*Initialize lock*/
980 mutex_init(&pm2->lock);
981
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200982 /*
Lee Jones39880432013-01-23 14:38:15 +0000983 * 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
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200986 */
Lee Jones39880432013-01-23 14:38:15 +0000987 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
Michel JAOUEN01ec8c52012-04-26 10:00:04 +0200998 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
Lee Jones39880432013-01-23 14:38:15 +00001009free_gpio:
1010 gpio_free(pm2->lpn_pin);
Michel JAOUEN01ec8c52012-04-26 10:00:04 +02001011unregister_pm2xxx_charger:
1012 /* unregister power supply */
1013 power_supply_unregister(&pm2->ac_chg.psy);
1014free_regulator:
1015 /* disable the regulator */
1016 regulator_put(pm2->regu);
1017free_charger_wq:
1018 destroy_workqueue(pm2->charger_wq);
1019free_device_info:
1020 kfree(pm2);
1021 return ret;
1022}
1023
1024static 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
Lee Jones39880432013-01-23 14:38:15 +00001044 /*Free GPIO60*/
1045 gpio_free(pm2->lpn_pin);
1046
Michel JAOUEN01ec8c52012-04-26 10:00:04 +02001047 kfree(pm2);
1048
1049 return 0;
1050}
1051
1052static const struct i2c_device_id pm2xxx_id[] = {
1053 { "pm2301", 0 },
1054 { }
1055};
1056
1057MODULE_DEVICE_TABLE(i2c, pm2xxx_id);
1058
1059static 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
1071static int __init pm2xxx_charger_init(void)
1072{
1073 return i2c_add_driver(&pm2xxx_charger_driver);
1074}
1075
1076static void __exit pm2xxx_charger_exit(void)
1077{
1078 i2c_del_driver(&pm2xxx_charger_driver);
1079}
1080
1081subsys_initcall_sync(pm2xxx_charger_init);
1082module_exit(pm2xxx_charger_exit);
1083
1084MODULE_LICENSE("GPL v2");
1085MODULE_AUTHOR("Rajkumar kasirajan, Olivier Launay");
1086MODULE_ALIAS("platform:pm2xxx-charger");
1087MODULE_DESCRIPTION("PM2xxx charger management driver");
1088