Blame view

drivers/power/pm2301_charger.c 29.7 KB
01ec8c542   Michel JAOUEN   pm2301: Provide u...
1
  /*
01ec8c542   Michel JAOUEN   pm2301: Provide u...
2
3
   * Copyright 2012 ST Ericsson.
   *
2fa5b0f4a   Lee Jones   pm2301: Move all ...
4
5
   * Power supply driver for ST Ericsson pm2xxx_charger charger
   *
01ec8c542   Michel JAOUEN   pm2301: Provide u...
6
7
8
9
10
11
12
13
14
15
16
17
18
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   */
  
  #include <linux/init.h>
  #include <linux/module.h>
  #include <linux/device.h>
  #include <linux/interrupt.h>
  #include <linux/delay.h>
  #include <linux/slab.h>
  #include <linux/platform_device.h>
  #include <linux/power_supply.h>
01ec8c542   Michel JAOUEN   pm2301: Provide u...
19
20
21
22
  #include <linux/regulator/consumer.h>
  #include <linux/err.h>
  #include <linux/i2c.h>
  #include <linux/workqueue.h>
01ec8c542   Michel JAOUEN   pm2301: Provide u...
23
24
  #include <linux/mfd/abx500/ab8500.h>
  #include <linux/mfd/abx500/ab8500-bm.h>
01ec8c542   Michel JAOUEN   pm2301: Provide u...
25
26
  #include <linux/mfd/abx500/ux500_chargalg.h>
  #include <linux/pm2301_charger.h>
3988043b0   Lee Jones   pm2301: LPN mode ...
27
  #include <linux/gpio.h>
aee2b8468   Lee Jones   pm2301-charger: A...
28
  #include <linux/pm_runtime.h>
6b170807c   Lars-Peter Clausen   pm2301-charger: F...
29
  #include <linux/pm.h>
01ec8c542   Michel JAOUEN   pm2301: Provide u...
30

2fa5b0f4a   Lee Jones   pm2301: Move all ...
31
  #include "pm2301_charger.h"
01ec8c542   Michel JAOUEN   pm2301: Provide u...
32
33
34
  
  #define to_pm2xxx_charger_ac_device_info(x) container_of((x), \
  		struct pm2xxx_charger, ac_chg)
49fddeec9   Mustapha Ben Zoubeir   pm2301-charger: R...
35
36
  #define SLEEP_MIN		50
  #define SLEEP_MAX		100
aee2b8468   Lee Jones   pm2301-charger: A...
37
  #define PM2XXX_AUTOSUSPEND_DELAY 500
01ec8c542   Michel JAOUEN   pm2301: Provide u...
38
39
40
41
42
43
44
45
46
  
  static int pm2xxx_interrupt_registers[] = {
  	PM2XXX_REG_INT1,
  	PM2XXX_REG_INT2,
  	PM2XXX_REG_INT3,
  	PM2XXX_REG_INT4,
  	PM2XXX_REG_INT5,
  	PM2XXX_REG_INT6,
  };
01ec8c542   Michel JAOUEN   pm2301: Provide u...
47
48
49
50
  static enum power_supply_property pm2xxx_charger_ac_props[] = {
  	POWER_SUPPLY_PROP_HEALTH,
  	POWER_SUPPLY_PROP_PRESENT,
  	POWER_SUPPLY_PROP_ONLINE,
01ec8c542   Michel JAOUEN   pm2301: Provide u...
51
  	POWER_SUPPLY_PROP_VOLTAGE_AVG,
01ec8c542   Michel JAOUEN   pm2301: Provide u...
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
  };
  
  static int pm2xxx_charger_voltage_map[] = {
  	3500,
  	3525,
  	3550,
  	3575,
  	3600,
  	3625,
  	3650,
  	3675,
  	3700,
  	3725,
  	3750,
  	3775,
  	3800,
  	3825,
  	3850,
  	3875,
  	3900,
  	3925,
  	3950,
  	3975,
  	4000,
  	4025,
  	4050,
  	4075,
  	4100,
  	4125,
  	4150,
  	4175,
  	4200,
  	4225,
  	4250,
  	4275,
  	4300,
  };
  
  static int pm2xxx_charger_current_map[] = {
  	200,
  	200,
  	400,
  	600,
  	800,
  	1000,
  	1200,
  	1400,
  	1600,
  	1800,
  	2000,
  	2200,
  	2400,
  	2600,
  	2800,
  	3000,
  };
01ec8c542   Michel JAOUEN   pm2301: Provide u...
108
109
110
111
  static const struct i2c_device_id pm2xxx_ident[] = {
  	{ "pm2301", 0 },
  	{ }
  };
3988043b0   Lee Jones   pm2301: LPN mode ...
112
113
  static void set_lpn_pin(struct pm2xxx_charger *pm2)
  {
a21e22f2f   lme00437   pm2301-charger: l...
114
115
116
117
  	if (!pm2->ac.charger_connected && gpio_is_valid(pm2->lpn_pin)) {
  		gpio_set_value(pm2->lpn_pin, 1);
  		usleep_range(SLEEP_MIN, SLEEP_MAX);
  	}
3988043b0   Lee Jones   pm2301: LPN mode ...
118
119
120
121
  }
  
  static void clear_lpn_pin(struct pm2xxx_charger *pm2)
  {
a21e22f2f   lme00437   pm2301-charger: l...
122
123
  	if (!pm2->ac.charger_connected && gpio_is_valid(pm2->lpn_pin))
  		gpio_set_value(pm2->lpn_pin, 0);
3988043b0   Lee Jones   pm2301: LPN mode ...
124
  }
01ec8c542   Michel JAOUEN   pm2301: Provide u...
125
126
127
  static int pm2xxx_reg_read(struct pm2xxx_charger *pm2, int reg, u8 *val)
  {
  	int ret;
b64f51c4a   Rupesh Kumar   pm2301-charger: W...
128
129
130
  
  	/* wake up the device */
  	pm_runtime_get_sync(pm2->dev);
01ec8c542   Michel JAOUEN   pm2301: Provide u...
131
132
133
134
135
136
  
  	ret = i2c_smbus_read_i2c_block_data(pm2->config.pm2xxx_i2c, reg,
  				1, val);
  	if (ret < 0)
  		dev_err(pm2->dev, "Error reading register at 0x%x
  ", reg);
e41f39ea2   Rajkumar Kasirajan   pm2301: Enable vb...
137
138
  	else
  		ret = 0;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
139

9b7f50e3e   Rupesh Kumar   pm2301-charger: R...
140
  	pm_runtime_put_sync(pm2->dev);
01ec8c542   Michel JAOUEN   pm2301: Provide u...
141
142
143
144
145
146
147
  
  	return ret;
  }
  
  static int pm2xxx_reg_write(struct pm2xxx_charger *pm2, int reg, u8 val)
  {
  	int ret;
b64f51c4a   Rupesh Kumar   pm2301-charger: W...
148
149
150
  
  	/* wake up the device */
  	pm_runtime_get_sync(pm2->dev);
01ec8c542   Michel JAOUEN   pm2301: Provide u...
151
152
153
154
155
156
  
  	ret = i2c_smbus_write_i2c_block_data(pm2->config.pm2xxx_i2c, reg,
  				1, &val);
  	if (ret < 0)
  		dev_err(pm2->dev, "Error writing register at 0x%x
  ", reg);
e41f39ea2   Rajkumar Kasirajan   pm2301: Enable vb...
157
158
  	else
  		ret = 0;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
159

9b7f50e3e   Rupesh Kumar   pm2301-charger: R...
160
  	pm_runtime_put_sync(pm2->dev);
01ec8c542   Michel JAOUEN   pm2301: Provide u...
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
  
  	return ret;
  }
  
  static int pm2xxx_charging_enable_mngt(struct pm2xxx_charger *pm2)
  {
  	int ret;
  
  	/* Enable charging */
  	ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG2,
  			(PM2XXX_CH_AUTO_RESUME_EN | PM2XXX_CHARGER_ENA));
  
  	return ret;
  }
  
  static int pm2xxx_charging_disable_mngt(struct pm2xxx_charger *pm2)
  {
  	int ret;
584f97033   Rajkumar Kasirajan   pm2301-charger: E...
179
180
181
182
183
184
185
  	/* Disable SW EOC ctrl */
  	ret = pm2xxx_reg_write(pm2, PM2XXX_SW_CTRL_REG, PM2XXX_SWCTRL_HW);
  	if (ret < 0) {
  		dev_err(pm2->dev, "%s pm2xxx write failed
  ", __func__);
  		return ret;
  	}
01ec8c542   Michel JAOUEN   pm2301: Provide u...
186
187
188
  	/* Disable charging */
  	ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG2,
  			(PM2XXX_CH_AUTO_RESUME_DIS | PM2XXX_CHARGER_DIS));
584f97033   Rajkumar Kasirajan   pm2301-charger: E...
189
190
191
192
193
  	if (ret < 0) {
  		dev_err(pm2->dev, "%s pm2xxx write failed
  ", __func__);
  		return ret;
  	}
01ec8c542   Michel JAOUEN   pm2301: Provide u...
194

584f97033   Rajkumar Kasirajan   pm2301-charger: E...
195
  	return 0;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
196
197
198
199
200
201
202
203
  }
  
  static int pm2xxx_charger_batt_therm_mngt(struct pm2xxx_charger *pm2, int val)
  {
  	queue_work(pm2->charger_wq, &pm2->check_main_thermal_prot_work);
  
  	return 0;
  }
83bd57aeb   Sachin Kamat   pm2301-charger: S...
204
  static int pm2xxx_charger_die_therm_mngt(struct pm2xxx_charger *pm2, int val)
01ec8c542   Michel JAOUEN   pm2301: Provide u...
205
206
207
208
209
210
211
212
  {
  	queue_work(pm2->charger_wq, &pm2->check_main_thermal_prot_work);
  
  	return 0;
  }
  
  static int pm2xxx_charger_ovv_mngt(struct pm2xxx_charger *pm2, int val)
  {
789ca7b46   Rupesh Kumar   pm2301-charger: S...
213
214
215
216
  	dev_err(pm2->dev, "Overvoltage detected
  ");
  	pm2->flags.ovv = true;
  	power_supply_changed(&pm2->ac_chg.psy);
01ec8c542   Michel JAOUEN   pm2301: Provide u...
217

789ca7b46   Rupesh Kumar   pm2301-charger: S...
218
219
  	/* Schedule a new HW failure check */
  	queue_delayed_work(pm2->charger_wq, &pm2->check_hw_failure_work, 0);
01ec8c542   Michel JAOUEN   pm2301: Provide u...
220

789ca7b46   Rupesh Kumar   pm2301-charger: S...
221
  	return 0;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
222
223
224
225
  }
  
  static int pm2xxx_charger_wd_exp_mngt(struct pm2xxx_charger *pm2, int val)
  {
07f422588   Masanari Iida   treewide: Fix typ...
226
227
  	dev_dbg(pm2->dev , "20 minutes watchdog expired
  ");
01ec8c542   Michel JAOUEN   pm2301: Provide u...
228
229
230
231
232
233
234
235
236
  
  	pm2->ac.wd_expired = true;
  	power_supply_changed(&pm2->ac_chg.psy);
  
  	return 0;
  }
  
  static int pm2xxx_charger_vbat_lsig_mngt(struct pm2xxx_charger *pm2, int val)
  {
584f97033   Rajkumar Kasirajan   pm2301-charger: E...
237
  	int ret;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
238
239
240
241
  	switch (val) {
  	case PM2XXX_INT1_ITVBATLOWR:
  		dev_dbg(pm2->dev, "VBAT grows above VBAT_LOW level
  ");
584f97033   Rajkumar Kasirajan   pm2301-charger: E...
242
243
244
245
246
247
248
249
  		/* Enable SW EOC ctrl */
  		ret = pm2xxx_reg_write(pm2, PM2XXX_SW_CTRL_REG,
  							PM2XXX_SWCTRL_SW);
  		if (ret < 0) {
  			dev_err(pm2->dev, "%s pm2xxx write failed
  ", __func__);
  			return ret;
  		}
01ec8c542   Michel JAOUEN   pm2301: Provide u...
250
251
252
253
254
  		break;
  
  	case PM2XXX_INT1_ITVBATLOWF:
  		dev_dbg(pm2->dev, "VBAT drops below VBAT_LOW level
  ");
584f97033   Rajkumar Kasirajan   pm2301-charger: E...
255
256
257
258
259
260
261
262
  		/* Disable SW EOC ctrl */
  		ret = pm2xxx_reg_write(pm2, PM2XXX_SW_CTRL_REG,
  							PM2XXX_SWCTRL_HW);
  		if (ret < 0) {
  			dev_err(pm2->dev, "%s pm2xxx write failed
  ", __func__);
  			return ret;
  		}
01ec8c542   Michel JAOUEN   pm2301: Provide u...
263
264
265
266
267
268
269
270
271
272
273
274
275
276
  		break;
  
  	default:
  		dev_err(pm2->dev, "Unknown VBAT level
  ");
  	}
  
  	return 0;
  }
  
  static int pm2xxx_charger_bat_disc_mngt(struct pm2xxx_charger *pm2, int val)
  {
  	dev_dbg(pm2->dev, "battery disconnected
  ");
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
277
  	return 0;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
278
279
280
281
  }
  
  static int pm2xxx_charger_detection(struct pm2xxx_charger *pm2, u8 *val)
  {
e41f39ea2   Rajkumar Kasirajan   pm2301: Enable vb...
282
  	int ret;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
283
284
285
286
287
288
289
290
291
292
  
  	ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT2, val);
  
  	if (ret < 0) {
  		dev_err(pm2->dev, "Charger detection failed
  ");
  		goto out;
  	}
  
  	*val &= (PM2XXX_INT2_S_ITVPWR1PLUG | PM2XXX_INT2_S_ITVPWR2PLUG);
e41f39ea2   Rajkumar Kasirajan   pm2301: Enable vb...
293

01ec8c542   Michel JAOUEN   pm2301: Provide u...
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
  out:
  	return ret;
  }
  
  static int pm2xxx_charger_itv_pwr_plug_mngt(struct pm2xxx_charger *pm2, int val)
  {
  
  	int ret;
  	u8 read_val;
  
  	/*
  	 * Since we can't be sure that the events are received
  	 * synchronously, we have the check if the main charger is
  	 * connected by reading the interrupt source register.
  	 */
  	ret = pm2xxx_charger_detection(pm2, &read_val);
  
  	if ((ret == 0) && read_val) {
  		pm2->ac.charger_connected = 1;
  		pm2->ac_conn = true;
  		queue_work(pm2->charger_wq, &pm2->ac_work);
  	}
  
  
  	return ret;
  }
  
  static int pm2xxx_charger_itv_pwr_unplug_mngt(struct pm2xxx_charger *pm2,
  								int val)
  {
  	pm2->ac.charger_connected = 0;
  	queue_work(pm2->charger_wq, &pm2->ac_work);
  
  	return 0;
  }
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
329
  static int pm2_int_reg0(void *pm2_data, int val)
01ec8c542   Michel JAOUEN   pm2301: Provide u...
330
  {
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
331
  	struct pm2xxx_charger *pm2 = pm2_data;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
332
  	int ret = 0;
584f97033   Rajkumar Kasirajan   pm2301-charger: E...
333
334
335
336
337
338
339
340
341
342
343
344
  	if (val & PM2XXX_INT1_ITVBATLOWR) {
  		ret = pm2xxx_charger_vbat_lsig_mngt(pm2,
  						PM2XXX_INT1_ITVBATLOWR);
  		if (ret < 0)
  			goto out;
  	}
  
  	if (val & PM2XXX_INT1_ITVBATLOWF) {
  		ret = pm2xxx_charger_vbat_lsig_mngt(pm2,
  						PM2XXX_INT1_ITVBATLOWF);
  		if (ret < 0)
  			goto out;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
345
  	}
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
346
  	if (val & PM2XXX_INT1_ITVBATDISCONNECT) {
01ec8c542   Michel JAOUEN   pm2301: Provide u...
347
348
  		ret = pm2xxx_charger_bat_disc_mngt(pm2,
  				PM2XXX_INT1_ITVBATDISCONNECT);
584f97033   Rajkumar Kasirajan   pm2301-charger: E...
349
350
  		if (ret < 0)
  			goto out;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
351
  	}
584f97033   Rajkumar Kasirajan   pm2301-charger: E...
352
  out:
01ec8c542   Michel JAOUEN   pm2301: Provide u...
353
354
  	return ret;
  }
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
355
  static int pm2_int_reg1(void *pm2_data, int val)
01ec8c542   Michel JAOUEN   pm2301: Provide u...
356
  {
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
357
  	struct pm2xxx_charger *pm2 = pm2_data;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
358
  	int ret = 0;
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
359
  	if (val & (PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG)) {
01ec8c542   Michel JAOUEN   pm2301: Provide u...
360
361
  		dev_dbg(pm2->dev , "Main charger plugged
  ");
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
362
  		ret = pm2xxx_charger_itv_pwr_plug_mngt(pm2, val &
01ec8c542   Michel JAOUEN   pm2301: Provide u...
363
364
  			(PM2XXX_INT2_ITVPWR1PLUG | PM2XXX_INT2_ITVPWR2PLUG));
  	}
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
365
  	if (val &
01ec8c542   Michel JAOUEN   pm2301: Provide u...
366
367
368
  		(PM2XXX_INT2_ITVPWR1UNPLUG | PM2XXX_INT2_ITVPWR2UNPLUG)) {
  		dev_dbg(pm2->dev , "Main charger unplugged
  ");
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
369
  		ret = pm2xxx_charger_itv_pwr_unplug_mngt(pm2, val &
01ec8c542   Michel JAOUEN   pm2301: Provide u...
370
371
372
373
374
375
  						(PM2XXX_INT2_ITVPWR1UNPLUG |
  						PM2XXX_INT2_ITVPWR2UNPLUG));
  	}
  
  	return ret;
  }
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
376
  static int pm2_int_reg2(void *pm2_data, int val)
01ec8c542   Michel JAOUEN   pm2301: Provide u...
377
  {
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
378
  	struct pm2xxx_charger *pm2 = pm2_data;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
379
  	int ret = 0;
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
380
381
  	if (val & PM2XXX_INT3_ITAUTOTIMEOUTWD)
  		ret = pm2xxx_charger_wd_exp_mngt(pm2, val);
01ec8c542   Michel JAOUEN   pm2301: Provide u...
382

006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
383
  	if (val & (PM2XXX_INT3_ITCHPRECHARGEWD |
01ec8c542   Michel JAOUEN   pm2301: Provide u...
384
385
  				PM2XXX_INT3_ITCHCCWD | PM2XXX_INT3_ITCHCVWD)) {
  		dev_dbg(pm2->dev,
0b1587b18   Masanari Iida   treewide: Fix typ...
386
387
  			"Watchdog occurred for precharge, CC and CV charge
  ");
01ec8c542   Michel JAOUEN   pm2301: Provide u...
388
389
390
391
  	}
  
  	return ret;
  }
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
392
  static int pm2_int_reg3(void *pm2_data, int val)
01ec8c542   Michel JAOUEN   pm2301: Provide u...
393
  {
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
394
  	struct pm2xxx_charger *pm2 = pm2_data;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
395
  	int ret = 0;
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
396
  	if (val & (PM2XXX_INT4_ITCHARGINGON)) {
01ec8c542   Michel JAOUEN   pm2301: Provide u...
397
398
399
400
  		dev_dbg(pm2->dev ,
  			"chargind operation has started
  ");
  	}
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
401
  	if (val & (PM2XXX_INT4_ITVRESUME)) {
01ec8c542   Michel JAOUEN   pm2301: Provide u...
402
403
404
405
  		dev_dbg(pm2->dev,
  			"battery discharged down to VResume threshold
  ");
  	}
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
406
  	if (val & (PM2XXX_INT4_ITBATTFULL)) {
01ec8c542   Michel JAOUEN   pm2301: Provide u...
407
408
409
  		dev_dbg(pm2->dev , "battery fully detected
  ");
  	}
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
410
  	if (val & (PM2XXX_INT4_ITCVPHASE)) {
01ec8c542   Michel JAOUEN   pm2301: Provide u...
411
412
413
  		dev_dbg(pm2->dev, "CV phase enter with 0.5C charging
  ");
  	}
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
414
  	if (val & (PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV)) {
01ec8c542   Michel JAOUEN   pm2301: Provide u...
415
  		pm2->failure_case = VPWR_OVV;
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
416
  		ret = pm2xxx_charger_ovv_mngt(pm2, val &
01ec8c542   Michel JAOUEN   pm2301: Provide u...
417
418
419
420
  			(PM2XXX_INT4_ITVPWR2OVV | PM2XXX_INT4_ITVPWR1OVV));
  		dev_dbg(pm2->dev, "VPWR/VSYSTEM overvoltage detected
  ");
  	}
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
421
  	if (val & (PM2XXX_INT4_S_ITBATTEMPCOLD |
01ec8c542   Michel JAOUEN   pm2301: Provide u...
422
  				PM2XXX_INT4_S_ITBATTEMPHOT)) {
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
423
424
425
  		ret = pm2xxx_charger_batt_therm_mngt(pm2, val &
  			(PM2XXX_INT4_S_ITBATTEMPCOLD |
  			PM2XXX_INT4_S_ITBATTEMPHOT));
01ec8c542   Michel JAOUEN   pm2301: Provide u...
426
427
428
429
430
431
  		dev_dbg(pm2->dev, "BTEMP is too Low/High
  ");
  	}
  
  	return ret;
  }
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
432
  static int pm2_int_reg4(void *pm2_data, int val)
01ec8c542   Michel JAOUEN   pm2301: Provide u...
433
  {
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
434
  	struct pm2xxx_charger *pm2 = pm2_data;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
435
  	int ret = 0;
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
436
  	if (val & PM2XXX_INT5_ITVSYSTEMOVV) {
01ec8c542   Michel JAOUEN   pm2301: Provide u...
437
  		pm2->failure_case = VSYSTEM_OVV;
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
438
  		ret = pm2xxx_charger_ovv_mngt(pm2, val &
01ec8c542   Michel JAOUEN   pm2301: Provide u...
439
440
441
442
  						PM2XXX_INT5_ITVSYSTEMOVV);
  		dev_dbg(pm2->dev, "VSYSTEM overvoltage detected
  ");
  	}
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
443
  	if (val & (PM2XXX_INT5_ITTHERMALWARNINGFALL |
01ec8c542   Michel JAOUEN   pm2301: Provide u...
444
445
446
447
448
  				PM2XXX_INT5_ITTHERMALWARNINGRISE |
  				PM2XXX_INT5_ITTHERMALSHUTDOWNFALL |
  				PM2XXX_INT5_ITTHERMALSHUTDOWNRISE)) {
  		dev_dbg(pm2->dev, "BTEMP die temperature is too Low/High
  ");
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
449
  		ret = pm2xxx_charger_die_therm_mngt(pm2, val &
01ec8c542   Michel JAOUEN   pm2301: Provide u...
450
451
452
453
454
455
456
457
  			(PM2XXX_INT5_ITTHERMALWARNINGFALL |
  			PM2XXX_INT5_ITTHERMALWARNINGRISE |
  			PM2XXX_INT5_ITTHERMALSHUTDOWNFALL |
  			PM2XXX_INT5_ITTHERMALSHUTDOWNRISE));
  	}
  
  	return ret;
  }
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
458
  static int pm2_int_reg5(void *pm2_data, int val)
01ec8c542   Michel JAOUEN   pm2301: Provide u...
459
  {
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
460
461
  	struct pm2xxx_charger *pm2 = pm2_data;
  	int ret = 0;
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
462
  	if (val & (PM2XXX_INT6_ITVPWR2DROP | PM2XXX_INT6_ITVPWR1DROP)) {
01ec8c542   Michel JAOUEN   pm2301: Provide u...
463
464
465
  		dev_dbg(pm2->dev, "VMPWR drop to VBAT level
  ");
  	}
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
466
467
468
469
  	if (val & (PM2XXX_INT6_ITVPWR2VALIDRISE |
  			PM2XXX_INT6_ITVPWR1VALIDRISE |
  			PM2XXX_INT6_ITVPWR2VALIDFALL |
  			PM2XXX_INT6_ITVPWR1VALIDFALL)) {
01ec8c542   Michel JAOUEN   pm2301: Provide u...
470
471
472
  		dev_dbg(pm2->dev, "Falling/Rising edge on WPWR1/2
  ");
  	}
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
473
  	return ret;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
474
475
476
477
478
  }
  
  static irqreturn_t  pm2xxx_irq_int(int irq, void *data)
  {
  	struct pm2xxx_charger *pm2 = data;
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
479
480
  	struct pm2xxx_interrupts *interrupt = pm2->pm2_int;
  	int i;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
481

aee2b8468   Lee Jones   pm2301-charger: A...
482
483
  	/* wake up the device */
  	pm_runtime_get_sync(pm2->dev);
f4095a0f0   M BenZoubeir   pm2301-charger: A...
484
485
486
  	do {
  		for (i = 0; i < PM2XXX_NUM_INT_REG; i++) {
  			pm2xxx_reg_read(pm2,
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
487
488
  				pm2xxx_interrupt_registers[i],
  				&(interrupt->reg[i]));
01ec8c542   Michel JAOUEN   pm2301: Provide u...
489

f4095a0f0   M BenZoubeir   pm2301-charger: A...
490
491
492
493
  			if (interrupt->reg[i] > 0)
  				interrupt->handler[i](pm2, interrupt->reg[i]);
  		}
  	} while (gpio_get_value(pm2->pdata->gpio_irq_number) == 0);
01ec8c542   Michel JAOUEN   pm2301: Provide u...
494

aee2b8468   Lee Jones   pm2301-charger: A...
495
496
  	pm_runtime_mark_last_busy(pm2->dev);
  	pm_runtime_put_autosuspend(pm2->dev);
01ec8c542   Michel JAOUEN   pm2301: Provide u...
497
498
499
  
  	return IRQ_HANDLED;
  }
01ec8c542   Michel JAOUEN   pm2301: Provide u...
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
  static int pm2xxx_charger_get_ac_cv(struct pm2xxx_charger *pm2)
  {
  	int ret = 0;
  	u8 val;
  
  	if (pm2->ac.charger_connected && pm2->ac.charger_online) {
  
  		ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT4, &val);
  		if (ret < 0) {
  			dev_err(pm2->dev, "%s pm2xxx read failed
  ", __func__);
  			goto out;
  		}
  
  		if (val & PM2XXX_INT4_S_ITCVPHASE)
  			ret = PM2XXX_CONST_VOLT;
  		else
  			ret = PM2XXX_CONST_CURR;
  	}
  out:
  	return ret;
  }
01ec8c542   Michel JAOUEN   pm2301: Provide u...
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
  static int pm2xxx_current_to_regval(int curr)
  {
  	int i;
  
  	if (curr < pm2xxx_charger_current_map[0])
  		return 0;
  
  	for (i = 1; i < ARRAY_SIZE(pm2xxx_charger_current_map); i++) {
  		if (curr < pm2xxx_charger_current_map[i])
  			return (i - 1);
  	}
  
  	i = ARRAY_SIZE(pm2xxx_charger_current_map) - 1;
  	if (curr == pm2xxx_charger_current_map[i])
  		return i;
  	else
  		return -EINVAL;
  }
  
  static int pm2xxx_voltage_to_regval(int curr)
  {
  	int i;
  
  	if (curr < pm2xxx_charger_voltage_map[0])
  		return 0;
  
  	for (i = 1; i < ARRAY_SIZE(pm2xxx_charger_voltage_map); i++) {
  		if (curr < pm2xxx_charger_voltage_map[i])
  			return i - 1;
  	}
  
  	i = ARRAY_SIZE(pm2xxx_charger_voltage_map) - 1;
  	if (curr == pm2xxx_charger_voltage_map[i])
  		return i;
  	else
  		return -EINVAL;
  }
  
  static int pm2xxx_charger_update_charger_current(struct ux500_charger *charger,
  		int ich_out)
  {
  	int ret;
  	int curr_index;
  	struct pm2xxx_charger *pm2;
  	u8 val;
  
  	if (charger->psy.type == POWER_SUPPLY_TYPE_MAINS)
  		pm2 = to_pm2xxx_charger_ac_device_info(charger);
  	else
  		return -ENXIO;
  
  	curr_index = pm2xxx_current_to_regval(ich_out);
  	if (curr_index < 0) {
  		dev_err(pm2->dev,
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
576
577
  			"Charger current too high, charging not started
  ");
01ec8c542   Michel JAOUEN   pm2301: Provide u...
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
  		return -ENXIO;
  	}
  
  	ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG6, &val);
  	if (ret >= 0) {
  		val &= ~PM2XXX_DIR_CH_CC_CURRENT_MASK;
  		val |= curr_index;
  		ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6, val);
  		if (ret < 0) {
  			dev_err(pm2->dev,
  				"%s write failed
  ", __func__);
  		}
  	}
  	else
  		dev_err(pm2->dev, "%s read failed
  ", __func__);
  
  	return ret;
  }
  
  static int pm2xxx_charger_ac_get_property(struct power_supply *psy,
  	enum power_supply_property psp,
  	union power_supply_propval *val)
  {
  	struct pm2xxx_charger *pm2;
  
  	pm2 = to_pm2xxx_charger_ac_device_info(psy_to_ux500_charger(psy));
  
  	switch (psp) {
  	case POWER_SUPPLY_PROP_HEALTH:
  		if (pm2->flags.mainextchnotok)
  			val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
  		else if (pm2->ac.wd_expired)
  			val->intval = POWER_SUPPLY_HEALTH_DEAD;
  		else if (pm2->flags.main_thermal_prot)
  			val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
789ca7b46   Rupesh Kumar   pm2301-charger: S...
615
616
  		else if (pm2->flags.ovv)
  			val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
617
618
619
620
621
622
623
624
625
  		else
  			val->intval = POWER_SUPPLY_HEALTH_GOOD;
  		break;
  	case POWER_SUPPLY_PROP_ONLINE:
  		val->intval = pm2->ac.charger_online;
  		break;
  	case POWER_SUPPLY_PROP_PRESENT:
  		val->intval = pm2->ac.charger_connected;
  		break;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
626
627
628
629
  	case POWER_SUPPLY_PROP_VOLTAGE_AVG:
  		pm2->ac.cv_active = pm2xxx_charger_get_ac_cv(pm2);
  		val->intval = pm2->ac.cv_active;
  		break;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
  	default:
  		return -EINVAL;
  	}
  	return 0;
  }
  
  static int pm2xxx_charging_init(struct pm2xxx_charger *pm2)
  {
  	int ret = 0;
  
  	/* enable CC and CV watchdog */
  	ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG3,
  		(PM2XXX_CH_WD_CV_PHASE_60MIN | PM2XXX_CH_WD_CC_PHASE_60MIN));
  	if( ret < 0)
  		return ret;
  
  	/* enable precharge watchdog */
  	ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG4,
  					PM2XXX_CH_WD_PRECH_PHASE_60MIN);
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
  	/* Disable auto timeout */
  	ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG5,
  					PM2XXX_CH_WD_AUTO_TIMEOUT_20MIN);
  
  	/*
       * EOC current level = 100mA
  	 * Precharge current level = 100mA
  	 * CC current level = 1000mA
  	 */
  	ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6,
  		(PM2XXX_DIR_CH_CC_CURRENT_1000MA |
  		PM2XXX_CH_PRECH_CURRENT_100MA |
  		PM2XXX_CH_EOC_CURRENT_100MA));
  
  	/*
       * recharge threshold = 3.8V
  	 * Precharge to CC threshold = 2.9V
  	 */
  	ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG7,
  		(PM2XXX_CH_PRECH_VOL_2_9 | PM2XXX_CH_VRESUME_VOL_3_8));
  
  	/* float voltage charger level = 4.2V */
  	ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG8,
  		PM2XXX_CH_VOLT_4_2);
  
  	/* Voltage drop between VBAT and VSYS in HW charging = 300mV */
  	ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG9,
  		(PM2XXX_CH_150MV_DROP_300MV | PM2XXX_CHARCHING_INFO_DIS |
  		PM2XXX_CH_CC_REDUCED_CURRENT_IDENT |
  		PM2XXX_CH_CC_MODEDROP_DIS));
  
  	/* Input charger level of over voltage = 10V */
  	ret = pm2xxx_reg_write(pm2, PM2XXX_INP_VOLT_VPWR2,
  					PM2XXX_VPWR2_OVV_10);
  	ret = pm2xxx_reg_write(pm2, PM2XXX_INP_VOLT_VPWR1,
  					PM2XXX_VPWR1_OVV_10);
  
  	/* Input charger drop */
  	ret = pm2xxx_reg_write(pm2, PM2XXX_INP_DROP_VPWR2,
  		(PM2XXX_VPWR2_HW_OPT_DIS | PM2XXX_VPWR2_VALID_DIS |
  		PM2XXX_VPWR2_DROP_DIS));
  	ret = pm2xxx_reg_write(pm2, PM2XXX_INP_DROP_VPWR1,
  		(PM2XXX_VPWR1_HW_OPT_DIS | PM2XXX_VPWR1_VALID_DIS |
  		PM2XXX_VPWR1_DROP_DIS));
  
  	/* Disable battery low monitoring */
  	ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_LOW_LEV_COMP_REG,
e41f39ea2   Rajkumar Kasirajan   pm2301: Enable vb...
696
  		PM2XXX_VBAT_LOW_MONITORING_ENA);
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
697

01ec8c542   Michel JAOUEN   pm2301: Provide u...
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
  	return ret;
  }
  
  static int pm2xxx_charger_ac_en(struct ux500_charger *charger,
  	int enable, int vset, int iset)
  {
  	int ret;
  	int volt_index;
  	int curr_index;
  	u8 val;
  
  	struct pm2xxx_charger *pm2 = to_pm2xxx_charger_ac_device_info(charger);
  
  	if (enable) {
  		if (!pm2->ac.charger_connected) {
  			dev_dbg(pm2->dev, "AC charger not connected
  ");
  			return -ENXIO;
  		}
  
  		dev_dbg(pm2->dev, "Enable AC: %dmV %dmA
  ", vset, iset);
  		if (!pm2->vddadc_en_ac) {
508b2c134   Sachin Kamat   pm2301-charger: C...
721
722
723
724
725
726
727
  			ret = regulator_enable(pm2->regu);
  			if (ret)
  				dev_warn(pm2->dev,
  					"Failed to enable vddadc regulator
  ");
  			else
  				pm2->vddadc_en_ac = true;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
  		}
  
  		ret = pm2xxx_charging_init(pm2);
  		if (ret < 0) {
  			dev_err(pm2->dev, "%s charging init failed
  ",
  					__func__);
  			goto error_occured;
  		}
  
  		volt_index = pm2xxx_voltage_to_regval(vset);
  		curr_index = pm2xxx_current_to_regval(iset);
  
  		if (volt_index < 0 || curr_index < 0) {
  			dev_err(pm2->dev,
  				"Charger voltage or current too high, "
  				"charging not started
  ");
  			return -ENXIO;
  		}
  
  		ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG8, &val);
e41f39ea2   Rajkumar Kasirajan   pm2301: Enable vb...
750
751
752
753
754
755
756
757
758
759
760
761
  		if (ret < 0) {
  			dev_err(pm2->dev, "%s pm2xxx read failed
  ", __func__);
  			goto error_occured;
  		}
  		val &= ~PM2XXX_CH_VOLT_MASK;
  		val |= volt_index;
  		ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG8, val);
  		if (ret < 0) {
  			dev_err(pm2->dev, "%s pm2xxx write failed
  ", __func__);
  			goto error_occured;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
762
763
764
  		}
  
  		ret = pm2xxx_reg_read(pm2, PM2XXX_BATT_CTRL_REG6, &val);
e41f39ea2   Rajkumar Kasirajan   pm2301: Enable vb...
765
766
767
768
769
770
771
772
773
774
775
776
  		if (ret < 0) {
  			dev_err(pm2->dev, "%s pm2xxx read failed
  ", __func__);
  			goto error_occured;
  		}
  		val &= ~PM2XXX_DIR_CH_CC_CURRENT_MASK;
  		val |= curr_index;
  		ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_CTRL_REG6, val);
  		if (ret < 0) {
  			dev_err(pm2->dev, "%s pm2xxx write failed
  ", __func__);
  			goto error_occured;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
777
778
779
780
  		}
  
  		if (!pm2->bat->enable_overshoot) {
  			ret = pm2xxx_reg_read(pm2, PM2XXX_LED_CTRL_REG, &val);
e41f39ea2   Rajkumar Kasirajan   pm2301: Enable vb...
781
782
783
784
785
786
787
788
789
790
791
792
793
  			if (ret < 0) {
  				dev_err(pm2->dev, "%s pm2xxx read failed
  ",
  								__func__);
  				goto error_occured;
  			}
  			val |= PM2XXX_ANTI_OVERSHOOT_EN;
  			ret = pm2xxx_reg_write(pm2, PM2XXX_LED_CTRL_REG, val);
  			if (ret < 0) {
  				dev_err(pm2->dev, "%s pm2xxx write failed
  ",
  								__func__);
  				goto error_occured;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
794
  			}
01ec8c542   Michel JAOUEN   pm2301: Provide u...
795
796
797
  		}
  
  		ret = pm2xxx_charging_enable_mngt(pm2);
e41f39ea2   Rajkumar Kasirajan   pm2301: Enable vb...
798
799
800
801
  		if (ret < 0) {
  			dev_err(pm2->dev, "Failed to enable"
  						"pm2xxx ac charger
  ");
01ec8c542   Michel JAOUEN   pm2301: Provide u...
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
  			goto error_occured;
  		}
  
  		pm2->ac.charger_online = 1;
  	} else {
  		pm2->ac.charger_online = 0;
  		pm2->ac.wd_expired = false;
  
  		/* Disable regulator if enabled */
  		if (pm2->vddadc_en_ac) {
  			regulator_disable(pm2->regu);
  			pm2->vddadc_en_ac = false;
  		}
  
  		ret = pm2xxx_charging_disable_mngt(pm2);
e41f39ea2   Rajkumar Kasirajan   pm2301: Enable vb...
817
818
819
820
821
  		if (ret < 0) {
  			dev_err(pm2->dev, "failed to disable"
  						"pm2xxx ac charger
  ");
  			goto error_occured;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
  		}
  
  		dev_dbg(pm2->dev, "PM2301: " "Disabled AC charging
  ");
  	}
  	power_supply_changed(&pm2->ac_chg.psy);
  
  error_occured:
  	return ret;
  }
  
  static int pm2xxx_charger_watchdog_kick(struct ux500_charger *charger)
  {
  	int ret;
  	struct pm2xxx_charger *pm2;
  
  	if (charger->psy.type == POWER_SUPPLY_TYPE_MAINS)
  		pm2 = to_pm2xxx_charger_ac_device_info(charger);
  	else
  		return -ENXIO;
  
  	ret = pm2xxx_reg_write(pm2, PM2XXX_BATT_WD_KICK, WD_TIMER);
  	if (ret)
  		dev_err(pm2->dev, "Failed to kick WD!
  ");
  
  	return ret;
  }
  
  static void pm2xxx_charger_ac_work(struct work_struct *work)
  {
  	struct pm2xxx_charger *pm2 = container_of(work,
  		struct pm2xxx_charger, ac_work);
  
  
  	power_supply_changed(&pm2->ac_chg.psy);
  	sysfs_notify(&pm2->ac_chg.psy.dev->kobj, NULL, "present");
  };
789ca7b46   Rupesh Kumar   pm2301-charger: S...
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
  static void pm2xxx_charger_check_hw_failure_work(struct work_struct *work)
  {
  	u8 reg_value;
  
  	struct pm2xxx_charger *pm2 = container_of(work,
  		struct pm2xxx_charger, check_hw_failure_work.work);
  
  	if (pm2->flags.ovv) {
  		pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT4, &reg_value);
  
  		if (!(reg_value & (PM2XXX_INT4_S_ITVPWR1OVV |
  					PM2XXX_INT4_S_ITVPWR2OVV))) {
  			pm2->flags.ovv = false;
  			power_supply_changed(&pm2->ac_chg.psy);
  		}
  	}
  
  	/* If we still have a failure, schedule a new check */
  	if (pm2->flags.ovv) {
  		queue_delayed_work(pm2->charger_wq,
  			&pm2->check_hw_failure_work, round_jiffies(HZ));
  	}
  }
01ec8c542   Michel JAOUEN   pm2301: Provide u...
883
884
885
  static void pm2xxx_charger_check_main_thermal_prot_work(
  	struct work_struct *work)
  {
da9e83d49   Rupesh Kumar   pm2301-charger: D...
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
  	int ret;
  	u8 val;
  
  	struct pm2xxx_charger *pm2 = container_of(work, struct pm2xxx_charger,
  					check_main_thermal_prot_work);
  
  	/* Check if die temp warning is still active */
  	ret = pm2xxx_reg_read(pm2, PM2XXX_SRCE_REG_INT5, &val);
  	if (ret < 0) {
  		dev_err(pm2->dev, "%s pm2xxx read failed
  ", __func__);
  		return;
  	}
  	if (val & (PM2XXX_INT5_S_ITTHERMALWARNINGRISE
  			| PM2XXX_INT5_S_ITTHERMALSHUTDOWNRISE))
  		pm2->flags.main_thermal_prot = true;
  	else if (val & (PM2XXX_INT5_S_ITTHERMALWARNINGFALL
  				| PM2XXX_INT5_S_ITTHERMALSHUTDOWNFALL))
  		pm2->flags.main_thermal_prot = false;
  
  	power_supply_changed(&pm2->ac_chg.psy);
  }
01ec8c542   Michel JAOUEN   pm2301: Provide u...
908

006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
909
910
911
912
913
914
915
916
  static struct pm2xxx_interrupts pm2xxx_int = {
  	.handler[0] = pm2_int_reg0,
  	.handler[1] = pm2_int_reg1,
  	.handler[2] = pm2_int_reg2,
  	.handler[3] = pm2_int_reg3,
  	.handler[4] = pm2_int_reg4,
  	.handler[5] = pm2_int_reg5,
  };
01ec8c542   Michel JAOUEN   pm2301: Provide u...
917
918
919
  static struct pm2xxx_irq pm2xxx_charger_irq[] = {
  	{"PM2XXX_IRQ_INT", pm2xxx_irq_int},
  };
6b170807c   Lars-Peter Clausen   pm2301-charger: F...
920
921
922
923
924
  #ifdef CONFIG_PM
  
  #ifdef CONFIG_PM_SLEEP
  
  static int pm2xxx_wall_charger_resume(struct device *dev)
01ec8c542   Michel JAOUEN   pm2301: Provide u...
925
  {
6b170807c   Lars-Peter Clausen   pm2301-charger: F...
926
  	struct i2c_client *i2c_client = to_i2c_client(dev);
49fddeec9   Mustapha Ben Zoubeir   pm2301-charger: R...
927
928
929
930
931
932
933
934
935
  	struct pm2xxx_charger *pm2;
  
  	pm2 =  (struct pm2xxx_charger *)i2c_get_clientdata(i2c_client);
  	set_lpn_pin(pm2);
  
  	/* If we still have a HW failure, schedule a new check */
  	if (pm2->flags.ovv)
  		queue_delayed_work(pm2->charger_wq,
  				&pm2->check_hw_failure_work, 0);
01ec8c542   Michel JAOUEN   pm2301: Provide u...
936
937
  	return 0;
  }
6b170807c   Lars-Peter Clausen   pm2301-charger: F...
938
  static int pm2xxx_wall_charger_suspend(struct device *dev)
01ec8c542   Michel JAOUEN   pm2301: Provide u...
939
  {
6b170807c   Lars-Peter Clausen   pm2301-charger: F...
940
  	struct i2c_client *i2c_client = to_i2c_client(dev);
49fddeec9   Mustapha Ben Zoubeir   pm2301-charger: R...
941
942
943
944
945
946
947
948
949
950
951
  	struct pm2xxx_charger *pm2;
  
  	pm2 =  (struct pm2xxx_charger *)i2c_get_clientdata(i2c_client);
  	clear_lpn_pin(pm2);
  
  	/* Cancel any pending HW failure check */
  	if (delayed_work_pending(&pm2->check_hw_failure_work))
  		cancel_delayed_work(&pm2->check_hw_failure_work);
  
  	flush_work(&pm2->ac_work);
  	flush_work(&pm2->check_main_thermal_prot_work);
01ec8c542   Michel JAOUEN   pm2301: Provide u...
952
953
  	return 0;
  }
6b170807c   Lars-Peter Clausen   pm2301-charger: F...
954
  #endif
aee2b8468   Lee Jones   pm2301-charger: A...
955
956
957
958
  static int  pm2xxx_runtime_suspend(struct device *dev)
  {
  	struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev);
  	struct pm2xxx_charger *pm2;
aee2b8468   Lee Jones   pm2301-charger: A...
959
960
  
  	pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(pm2xxx_i2c_client);
aee2b8468   Lee Jones   pm2301-charger: A...
961
  	clear_lpn_pin(pm2);
c8024234c   Dan Carpenter   pm2301-charger: R...
962
  	return 0;
aee2b8468   Lee Jones   pm2301-charger: A...
963
964
965
966
967
968
  }
  
  static int  pm2xxx_runtime_resume(struct device *dev)
  {
  	struct i2c_client *pm2xxx_i2c_client = to_i2c_client(dev);
  	struct pm2xxx_charger *pm2;
aee2b8468   Lee Jones   pm2301-charger: A...
969
970
  
  	pm2 = (struct pm2xxx_charger *)i2c_get_clientdata(pm2xxx_i2c_client);
aee2b8468   Lee Jones   pm2301-charger: A...
971
972
973
  
  	if (gpio_is_valid(pm2->lpn_pin) && gpio_get_value(pm2->lpn_pin) == 0)
  		set_lpn_pin(pm2);
c8024234c   Dan Carpenter   pm2301-charger: R...
974
  	return 0;
aee2b8468   Lee Jones   pm2301-charger: A...
975
976
977
  }
  
  static const struct dev_pm_ops pm2xxx_pm_ops = {
6b170807c   Lars-Peter Clausen   pm2301-charger: F...
978
979
980
  	SET_SYSTEM_SLEEP_PM_OPS(pm2xxx_wall_charger_suspend,
  		pm2xxx_wall_charger_resume)
  	SET_RUNTIME_PM_OPS(pm2xxx_runtime_suspend, pm2xxx_runtime_resume, NULL)
aee2b8468   Lee Jones   pm2301-charger: A...
981
982
983
984
985
  };
  #define  PM2XXX_PM_OPS (&pm2xxx_pm_ops)
  #else
  #define  PM2XXX_PM_OPS  NULL
  #endif
116c326e7   Lee Jones   pm2301_charger: R...
986
  static int pm2xxx_wall_charger_probe(struct i2c_client *i2c_client,
01ec8c542   Michel JAOUEN   pm2301: Provide u...
987
988
989
990
991
992
  		const struct i2c_device_id *id)
  {
  	struct pm2xxx_platform_data *pl_data = i2c_client->dev.platform_data;
  	struct pm2xxx_charger *pm2;
  	int ret = 0;
  	u8 val;
f4095a0f0   M BenZoubeir   pm2301-charger: A...
993
  	int i;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
994

df311333a   Axel Lin   pm2301_charger: F...
995
996
997
998
999
  	if (!pl_data) {
  		dev_err(&i2c_client->dev, "No platform data supplied
  ");
  		return -EINVAL;
  	}
01ec8c542   Michel JAOUEN   pm2301: Provide u...
1000
1001
  	pm2 = kzalloc(sizeof(struct pm2xxx_charger), GFP_KERNEL);
  	if (!pm2) {
df311333a   Axel Lin   pm2301_charger: F...
1002
1003
  		dev_err(&i2c_client->dev, "pm2xxx_charger allocation failed
  ");
01ec8c542   Michel JAOUEN   pm2301: Provide u...
1004
1005
1006
1007
1008
  		return -ENOMEM;
  	}
  
  	/* get parent data */
  	pm2->dev = &i2c_client->dev;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
1009

006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
1010
  	pm2->pm2_int = &pm2xxx_int;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
  	/* get charger spcific platform data */
  	if (!pl_data->wall_charger) {
  		dev_err(pm2->dev, "no charger platform data supplied
  ");
  		ret = -EINVAL;
  		goto free_device_info;
  	}
  
  	pm2->pdata = pl_data->wall_charger;
  
  	/* get battery specific platform data */
  	if (!pl_data->battery) {
  		dev_err(pm2->dev, "no battery platform data supplied
  ");
  		ret = -EINVAL;
  		goto free_device_info;
  	}
  
  	pm2->bat = pl_data->battery;
  
  	if (!i2c_check_functionality(i2c_client->adapter,
  			I2C_FUNC_SMBUS_BYTE_DATA |
  			I2C_FUNC_SMBUS_READ_WORD_DATA)) {
  		ret = -ENODEV;
  		dev_info(pm2->dev, "pm2301 i2c_check_functionality failed
  ");
  		goto free_device_info;
  	}
  
  	pm2->config.pm2xxx_i2c = i2c_client;
  	pm2->config.pm2xxx_id = (struct i2c_device_id *) id;
  	i2c_set_clientdata(i2c_client, pm2);
  
  	/* AC supply */
  	/* power_supply base class */
  	pm2->ac_chg.psy.name = pm2->pdata->label;
  	pm2->ac_chg.psy.type = POWER_SUPPLY_TYPE_MAINS;
  	pm2->ac_chg.psy.properties = pm2xxx_charger_ac_props;
  	pm2->ac_chg.psy.num_properties = ARRAY_SIZE(pm2xxx_charger_ac_props);
  	pm2->ac_chg.psy.get_property = pm2xxx_charger_ac_get_property;
  	pm2->ac_chg.psy.supplied_to = pm2->pdata->supplied_to;
  	pm2->ac_chg.psy.num_supplicants = pm2->pdata->num_supplicants;
  	/* pm2xxx_charger sub-class */
  	pm2->ac_chg.ops.enable = &pm2xxx_charger_ac_en;
  	pm2->ac_chg.ops.kick_wd = &pm2xxx_charger_watchdog_kick;
  	pm2->ac_chg.ops.update_curr = &pm2xxx_charger_update_charger_current;
  	pm2->ac_chg.max_out_volt = pm2xxx_charger_voltage_map[
  		ARRAY_SIZE(pm2xxx_charger_voltage_map) - 1];
  	pm2->ac_chg.max_out_curr = pm2xxx_charger_current_map[
  		ARRAY_SIZE(pm2xxx_charger_current_map) - 1];
e07a56453   Loic Pallardy   pm2301: Update wa...
1061
  	pm2->ac_chg.wdt_refresh = WD_KICK_INTERVAL;
006f82d67   Olivier Clergeaud   pm2301: Clean-up ...
1062
  	pm2->ac_chg.enabled = true;
e07a56453   Loic Pallardy   pm2301: Update wa...
1063
  	pm2->ac_chg.external = true;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
1064
1065
  
  	/* Create a work queue for the charger */
c509a62c9   Axel Lin   pm2301_charger: R...
1066
  	pm2->charger_wq = create_singlethread_workqueue("pm2xxx_charger_wq");
01ec8c542   Michel JAOUEN   pm2301: Provide u...
1067
  	if (pm2->charger_wq == NULL) {
c509a62c9   Axel Lin   pm2301_charger: R...
1068
  		ret = -ENOMEM;
01ec8c542   Michel JAOUEN   pm2301: Provide u...
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
  		dev_err(pm2->dev, "failed to create work queue
  ");
  		goto free_device_info;
  	}
  
  	/* Init work for charger detection */
  	INIT_WORK(&pm2->ac_work, pm2xxx_charger_ac_work);
  
  	/* Init work for checking HW status */
  	INIT_WORK(&pm2->check_main_thermal_prot_work,
  		pm2xxx_charger_check_main_thermal_prot_work);
789ca7b46   Rupesh Kumar   pm2301-charger: S...
1080
1081
1082
  	/* Init work for HW failure check */
  	INIT_DEFERRABLE_WORK(&pm2->check_hw_failure_work,
  		pm2xxx_charger_check_hw_failure_work);
01ec8c542   Michel JAOUEN   pm2301: Provide u...
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
  	/*
  	 * VDD ADC supply needs to be enabled from this driver when there
  	 * is a charger connected to avoid erroneous BTEMP_HIGH/LOW
  	 * interrupts during charging
  	 */
  	pm2->regu = regulator_get(pm2->dev, "vddadc");
  	if (IS_ERR(pm2->regu)) {
  		ret = PTR_ERR(pm2->regu);
  		dev_err(pm2->dev, "failed to get vddadc regulator
  ");
  		goto free_charger_wq;
  	}
  
  	/* Register AC charger class */
  	ret = power_supply_register(pm2->dev, &pm2->ac_chg.psy);
  	if (ret) {
  		dev_err(pm2->dev, "failed to register AC charger
  ");
  		goto free_regulator;
  	}
  
  	/* Register interrupts */
f4095a0f0   M BenZoubeir   pm2301-charger: A...
1105
1106
  	ret = request_threaded_irq(gpio_to_irq(pm2->pdata->gpio_irq_number),
  				NULL,
01ec8c542   Michel JAOUEN   pm2301: Provide u...
1107
1108
1109
1110
1111
1112
1113
  				pm2xxx_charger_irq[0].isr,
  				pm2->pdata->irq_type,
  				pm2xxx_charger_irq[0].name, pm2);
  
  	if (ret != 0) {
  		dev_err(pm2->dev, "failed to request %s IRQ %d: %d
  ",
f4095a0f0   M BenZoubeir   pm2301-charger: A...
1114
1115
  		pm2xxx_charger_irq[0].name,
  			gpio_to_irq(pm2->pdata->gpio_irq_number), ret);
01ec8c542   Michel JAOUEN   pm2301: Provide u...
1116
1117
  		goto unregister_pm2xxx_charger;
  	}
aee2b8468   Lee Jones   pm2301-charger: A...
1118
1119
1120
1121
  	ret = pm_runtime_set_active(pm2->dev);
  	if (ret)
  		dev_err(pm2->dev, "set active Error
  ");
3988043b0   Lee Jones   pm2301: LPN mode ...
1122

aee2b8468   Lee Jones   pm2301-charger: A...
1123
1124
1125
1126
  	pm_runtime_enable(pm2->dev);
  	pm_runtime_set_autosuspend_delay(pm2->dev, PM2XXX_AUTOSUSPEND_DELAY);
  	pm_runtime_use_autosuspend(pm2->dev);
  	pm_runtime_resume(pm2->dev);
d4f510f6c   Rupesh Kumar   pm2301-charger: W...
1127
  	/* pm interrupt can wake up system */
f4095a0f0   M BenZoubeir   pm2301-charger: A...
1128
  	ret = enable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number));
d4f510f6c   Rupesh Kumar   pm2301-charger: W...
1129
1130
1131
1132
  	if (ret) {
  		dev_err(pm2->dev, "failed to set irq wake
  ");
  		goto unregister_pm2xxx_interrupt;
3988043b0   Lee Jones   pm2301: LPN mode ...
1133
  	}
01ec8c542   Michel JAOUEN   pm2301: Provide u...
1134

3988043b0   Lee Jones   pm2301: LPN mode ...
1135
  	mutex_init(&pm2->lock);
a21e22f2f   lme00437   pm2301-charger: l...
1136
1137
1138
  	if (gpio_is_valid(pm2->pdata->lpn_gpio)) {
  		/* get lpn GPIO from platform data */
  		pm2->lpn_pin = pm2->pdata->lpn_gpio;
3988043b0   Lee Jones   pm2301: LPN mode ...
1139

a21e22f2f   lme00437   pm2301-charger: l...
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
  		/*
  		 * Charger detection mechanism requires pulling up the LPN pin
  		 * while i2c communication if Charger is not connected
  		 * LPN pin of PM2301 is GPIO60 of AB9540
  		 */
  		ret = gpio_request(pm2->lpn_pin, "pm2301_lpm_gpio");
  
  		if (ret < 0) {
  			dev_err(pm2->dev, "pm2301_lpm_gpio request failed
  ");
  			goto disable_pm2_irq_wake;
  		}
  		ret = gpio_direction_output(pm2->lpn_pin, 0);
  		if (ret < 0) {
  			dev_err(pm2->dev, "pm2301_lpm_gpio direction failed
  ");
  			goto free_gpio;
  		}
  		set_lpn_pin(pm2);
3988043b0   Lee Jones   pm2301: LPN mode ...
1159
  	}
f4095a0f0   M BenZoubeir   pm2301-charger: A...
1160
1161
1162
1163
1164
  	/* read  interrupt registers */
  	for (i = 0; i < PM2XXX_NUM_INT_REG; i++)
  		pm2xxx_reg_read(pm2,
  			pm2xxx_interrupt_registers[i],
  			&val);
01ec8c542   Michel JAOUEN   pm2301: Provide u...
1165
1166
1167
1168
  	ret = pm2xxx_charger_detection(pm2, &val);
  
  	if ((ret == 0) && val) {
  		pm2->ac.charger_connected = 1;
54fbbb624   Per Forlin   pm2301-charger: F...
1169
1170
  		ab8500_override_turn_on_stat(~AB8500_POW_KEY_1_ON,
  					     AB8500_MAIN_CH_DET);
01ec8c542   Michel JAOUEN   pm2301: Provide u...
1171
1172
1173
1174
1175
1176
  		pm2->ac_conn = true;
  		power_supply_changed(&pm2->ac_chg.psy);
  		sysfs_notify(&pm2->ac_chg.psy.dev->kobj, NULL, "present");
  	}
  
  	return 0;
3988043b0   Lee Jones   pm2301: LPN mode ...
1177
  free_gpio:
a21e22f2f   lme00437   pm2301-charger: l...
1178
1179
  	if (gpio_is_valid(pm2->lpn_pin))
  		gpio_free(pm2->lpn_pin);
d4f510f6c   Rupesh Kumar   pm2301-charger: W...
1180
  disable_pm2_irq_wake:
f4095a0f0   M BenZoubeir   pm2301-charger: A...
1181
  	disable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number));
d4f510f6c   Rupesh Kumar   pm2301-charger: W...
1182
1183
  unregister_pm2xxx_interrupt:
  	/* disable interrupt */
f4095a0f0   M BenZoubeir   pm2301-charger: A...
1184
  	free_irq(gpio_to_irq(pm2->pdata->gpio_irq_number), pm2);
01ec8c542   Michel JAOUEN   pm2301: Provide u...
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
  unregister_pm2xxx_charger:
  	/* unregister power supply */
  	power_supply_unregister(&pm2->ac_chg.psy);
  free_regulator:
  	/* disable the regulator */
  	regulator_put(pm2->regu);
  free_charger_wq:
  	destroy_workqueue(pm2->charger_wq);
  free_device_info:
  	kfree(pm2);
d4f510f6c   Rupesh Kumar   pm2301-charger: W...
1195

01ec8c542   Michel JAOUEN   pm2301: Provide u...
1196
1197
  	return ret;
  }
116c326e7   Lee Jones   pm2301_charger: R...
1198
  static int pm2xxx_wall_charger_remove(struct i2c_client *i2c_client)
01ec8c542   Michel JAOUEN   pm2301: Provide u...
1199
1200
  {
  	struct pm2xxx_charger *pm2 = i2c_get_clientdata(i2c_client);
aee2b8468   Lee Jones   pm2301-charger: A...
1201
1202
  	/* Disable pm_runtime */
  	pm_runtime_disable(pm2->dev);
01ec8c542   Michel JAOUEN   pm2301: Provide u...
1203
1204
  	/* Disable AC charging */
  	pm2xxx_charger_ac_en(&pm2->ac_chg, false, 0, 0);
d4f510f6c   Rupesh Kumar   pm2301-charger: W...
1205
  	/* Disable wake by pm interrupt */
f4095a0f0   M BenZoubeir   pm2301-charger: A...
1206
  	disable_irq_wake(gpio_to_irq(pm2->pdata->gpio_irq_number));
d4f510f6c   Rupesh Kumar   pm2301-charger: W...
1207

01ec8c542   Michel JAOUEN   pm2301: Provide u...
1208
  	/* Disable interrupts */
f4095a0f0   M BenZoubeir   pm2301-charger: A...
1209
  	free_irq(gpio_to_irq(pm2->pdata->gpio_irq_number), pm2);
01ec8c542   Michel JAOUEN   pm2301: Provide u...
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
  
  	/* Delete the work queue */
  	destroy_workqueue(pm2->charger_wq);
  
  	flush_scheduled_work();
  
  	/* disable the regulator */
  	regulator_put(pm2->regu);
  
  	power_supply_unregister(&pm2->ac_chg.psy);
a21e22f2f   lme00437   pm2301-charger: l...
1220
1221
  	if (gpio_is_valid(pm2->lpn_pin))
  		gpio_free(pm2->lpn_pin);
3988043b0   Lee Jones   pm2301: LPN mode ...
1222

01ec8c542   Michel JAOUEN   pm2301: Provide u...
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
  	kfree(pm2);
  
  	return 0;
  }
  
  static const struct i2c_device_id pm2xxx_id[] = {
  	{ "pm2301", 0 },
  	{ }
  };
  
  MODULE_DEVICE_TABLE(i2c, pm2xxx_id);
  
  static struct i2c_driver pm2xxx_charger_driver = {
  	.probe = pm2xxx_wall_charger_probe,
116c326e7   Lee Jones   pm2301_charger: R...
1237
  	.remove = pm2xxx_wall_charger_remove,
01ec8c542   Michel JAOUEN   pm2301: Provide u...
1238
1239
1240
  	.driver = {
  		.name = "pm2xxx-wall_charger",
  		.owner = THIS_MODULE,
aee2b8468   Lee Jones   pm2301-charger: A...
1241
  		.pm = PM2XXX_PM_OPS,
01ec8c542   Michel JAOUEN   pm2301: Provide u...
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
  	},
  	.id_table = pm2xxx_id,
  };
  
  static int __init pm2xxx_charger_init(void)
  {
  	return i2c_add_driver(&pm2xxx_charger_driver);
  }
  
  static void __exit pm2xxx_charger_exit(void)
  {
  	i2c_del_driver(&pm2xxx_charger_driver);
  }
a21e22f2f   lme00437   pm2301-charger: l...
1255
  device_initcall_sync(pm2xxx_charger_init);
01ec8c542   Michel JAOUEN   pm2301: Provide u...
1256
1257
1258
1259
  module_exit(pm2xxx_charger_exit);
  
  MODULE_LICENSE("GPL v2");
  MODULE_AUTHOR("Rajkumar kasirajan, Olivier Launay");
dccab6092   Axel Lin   pm2301_charger: F...
1260
  MODULE_ALIAS("i2c:pm2xxx-charger");
01ec8c542   Michel JAOUEN   pm2301: Provide u...
1261
  MODULE_DESCRIPTION("PM2xxx charger management driver");