Blame view

drivers/power/lp8727_charger.c 11.2 KB
2165c8a45   Woogyom Kim   power_supply: Add...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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
  /*
   * Driver for LP8727 Micro/Mini USB IC with intergrated charger
   *
   *			Copyright (C) 2011 National Semiconductor
   *
   * 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/module.h>
  #include <linux/slab.h>
  #include <linux/interrupt.h>
  #include <linux/i2c.h>
  #include <linux/power_supply.h>
  #include <linux/lp8727.h>
  
  #define DEBOUNCE_MSEC	270
  
  /* Registers */
  #define CTRL1		0x1
  #define CTRL2		0x2
  #define	SWCTRL		0x3
  #define INT1		0x4
  #define INT2		0x5
  #define STATUS1		0x6
  #define STATUS2 	0x7
  #define CHGCTRL2	0x9
  
  /* CTRL1 register */
  #define CP_EN		(1 << 0)
  #define ADC_EN		(1 << 1)
  #define ID200_EN	(1 << 4)
  
  /* CTRL2 register */
  #define CHGDET_EN	(1 << 1)
  #define INT_EN		(1 << 6)
  
  /* SWCTRL register */
  #define SW_DM1_DM	(0x0 << 0)
  #define SW_DM1_U1	(0x1 << 0)
  #define SW_DM1_HiZ	(0x7 << 0)
  #define SW_DP2_DP	(0x0 << 3)
  #define SW_DP2_U2	(0x1 << 3)
  #define SW_DP2_HiZ	(0x7 << 3)
  
  /* INT1 register */
  #define IDNO		(0xF << 0)
  #define VBUS		(1 << 4)
  
  /* STATUS1 register */
  #define CHGSTAT		(3 << 4)
  #define CHPORT		(1 << 6)
  #define DCPORT		(1 << 7)
  
  /* STATUS2 register */
  #define TEMP_STAT	(3 << 5)
  
  enum lp8727_dev_id {
  	ID_NONE,
  	ID_TA,
  	ID_DEDICATED_CHG,
  	ID_USB_CHG,
  	ID_USB_DS,
  	ID_MAX,
  };
  
  enum lp8727_chg_stat {
  	PRECHG,
  	CC,
  	CV,
  	EOC,
  };
  
  struct lp8727_psy {
  	struct power_supply ac;
  	struct power_supply usb;
  	struct power_supply batt;
  };
  
  struct lp8727_chg {
  	struct device *dev;
  	struct i2c_client *client;
  	struct mutex xfer_lock;
  	struct delayed_work work;
  	struct workqueue_struct *irqthread;
  	struct lp8727_platform_data *pdata;
  	struct lp8727_psy *psy;
  	struct lp8727_chg_param *chg_parm;
  	enum lp8727_dev_id devid;
  };
ce09affc5   Kim, Milo   lp8727_charger: C...
93
  static int lp8727_i2c_read(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len)
2165c8a45   Woogyom Kim   power_supply: Add...
94
95
96
97
98
99
100
101
102
  {
  	s32 ret;
  
  	mutex_lock(&pchg->xfer_lock);
  	ret = i2c_smbus_read_i2c_block_data(pchg->client, reg, len, data);
  	mutex_unlock(&pchg->xfer_lock);
  
  	return (ret != len) ? -EIO : 0;
  }
ce09affc5   Kim, Milo   lp8727_charger: C...
103
  static int lp8727_i2c_write(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len)
2165c8a45   Woogyom Kim   power_supply: Add...
104
105
106
107
108
109
110
111
112
113
114
  {
  	s32 ret;
  
  	mutex_lock(&pchg->xfer_lock);
  	ret = i2c_smbus_write_i2c_block_data(pchg->client, reg, len, data);
  	mutex_unlock(&pchg->xfer_lock);
  
  	return ret;
  }
  
  static inline int lp8727_i2c_read_byte(struct lp8727_chg *pchg, u8 reg,
ce09affc5   Kim, Milo   lp8727_charger: C...
115
  				       u8 *data)
2165c8a45   Woogyom Kim   power_supply: Add...
116
117
118
119
120
  {
  	return lp8727_i2c_read(pchg, reg, data, 1);
  }
  
  static inline int lp8727_i2c_write_byte(struct lp8727_chg *pchg, u8 reg,
ce09affc5   Kim, Milo   lp8727_charger: C...
121
  					u8 *data)
2165c8a45   Woogyom Kim   power_supply: Add...
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
  {
  	return lp8727_i2c_write(pchg, reg, data, 1);
  }
  
  static int lp8727_is_charger_attached(const char *name, int id)
  {
  	if (name) {
  		if (!strcmp(name, "ac"))
  			return (id == ID_TA || id == ID_DEDICATED_CHG) ? 1 : 0;
  		else if (!strcmp(name, "usb"))
  			return (id == ID_USB_CHG) ? 1 : 0;
  	}
  
  	return (id >= ID_TA && id <= ID_USB_CHG) ? 1 : 0;
  }
  
  static void lp8727_init_device(struct lp8727_chg *pchg)
  {
  	u8 val;
  
  	val = ID200_EN | ADC_EN | CP_EN;
  	if (lp8727_i2c_write_byte(pchg, CTRL1, &val))
  		dev_err(pchg->dev, "i2c write err : addr=0x%.2x
  ", CTRL1);
  
  	val = INT_EN | CHGDET_EN;
  	if (lp8727_i2c_write_byte(pchg, CTRL2, &val))
  		dev_err(pchg->dev, "i2c write err : addr=0x%.2x
  ", CTRL2);
  }
  
  static int lp8727_is_dedicated_charger(struct lp8727_chg *pchg)
  {
  	u8 val;
ce09affc5   Kim, Milo   lp8727_charger: C...
156
  	lp8727_i2c_read_byte(pchg, STATUS1, &val);
2165c8a45   Woogyom Kim   power_supply: Add...
157
158
159
160
161
162
  	return (val & DCPORT);
  }
  
  static int lp8727_is_usb_charger(struct lp8727_chg *pchg)
  {
  	u8 val;
ce09affc5   Kim, Milo   lp8727_charger: C...
163
  	lp8727_i2c_read_byte(pchg, STATUS1, &val);
2165c8a45   Woogyom Kim   power_supply: Add...
164
165
166
167
168
169
  	return (val & CHPORT);
  }
  
  static void lp8727_ctrl_switch(struct lp8727_chg *pchg, u8 sw)
  {
  	u8 val = sw;
ce09affc5   Kim, Milo   lp8727_charger: C...
170
  	lp8727_i2c_write_byte(pchg, SWCTRL, &val);
2165c8a45   Woogyom Kim   power_supply: Add...
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
  }
  
  static void lp8727_id_detection(struct lp8727_chg *pchg, u8 id, int vbusin)
  {
  	u8 devid = ID_NONE;
  	u8 swctrl = SW_DM1_HiZ | SW_DP2_HiZ;
  
  	switch (id) {
  	case 0x5:
  		devid = ID_TA;
  		pchg->chg_parm = &pchg->pdata->ac;
  		break;
  	case 0xB:
  		if (lp8727_is_dedicated_charger(pchg)) {
  			pchg->chg_parm = &pchg->pdata->ac;
  			devid = ID_DEDICATED_CHG;
  		} else if (lp8727_is_usb_charger(pchg)) {
  			pchg->chg_parm = &pchg->pdata->usb;
  			devid = ID_USB_CHG;
  			swctrl = SW_DM1_DM | SW_DP2_DP;
  		} else if (vbusin) {
  			devid = ID_USB_DS;
  			swctrl = SW_DM1_DM | SW_DP2_DP;
  		}
  		break;
  	default:
  		devid = ID_NONE;
  		pchg->chg_parm = NULL;
  		break;
  	}
  
  	pchg->devid = devid;
  	lp8727_ctrl_switch(pchg, swctrl);
  }
  
  static void lp8727_enable_chgdet(struct lp8727_chg *pchg)
  {
  	u8 val;
ce09affc5   Kim, Milo   lp8727_charger: C...
209
  	lp8727_i2c_read_byte(pchg, CTRL2, &val);
2165c8a45   Woogyom Kim   power_supply: Add...
210
  	val |= CHGDET_EN;
ce09affc5   Kim, Milo   lp8727_charger: C...
211
  	lp8727_i2c_write_byte(pchg, CTRL2, &val);
2165c8a45   Woogyom Kim   power_supply: Add...
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
  }
  
  static void lp8727_delayed_func(struct work_struct *_work)
  {
  	u8 intstat[2], idno, vbus;
  	struct lp8727_chg *pchg =
  	    container_of(_work, struct lp8727_chg, work.work);
  
  	if (lp8727_i2c_read(pchg, INT1, intstat, 2)) {
  		dev_err(pchg->dev, "can not read INT registers
  ");
  		return;
  	}
  
  	idno = intstat[0] & IDNO;
  	vbus = intstat[0] & VBUS;
  
  	lp8727_id_detection(pchg, idno, vbus);
  	lp8727_enable_chgdet(pchg);
  
  	power_supply_changed(&pchg->psy->ac);
  	power_supply_changed(&pchg->psy->usb);
  	power_supply_changed(&pchg->psy->batt);
  }
  
  static irqreturn_t lp8727_isr_func(int irq, void *ptr)
  {
  	struct lp8727_chg *pchg = ptr;
  	unsigned long delay = msecs_to_jiffies(DEBOUNCE_MSEC);
  
  	queue_delayed_work(pchg->irqthread, &pchg->work, delay);
  
  	return IRQ_HANDLED;
  }
  
  static void lp8727_intr_config(struct lp8727_chg *pchg)
  {
  	INIT_DELAYED_WORK(&pchg->work, lp8727_delayed_func);
  
  	pchg->irqthread = create_singlethread_workqueue("lp8727-irqthd");
  	if (!pchg->irqthread)
  		dev_err(pchg->dev, "can not create thread for lp8727
  ");
  
  	if (request_threaded_irq(pchg->client->irq,
  				 NULL,
  				 lp8727_isr_func,
  				 IRQF_TRIGGER_FALLING, "lp8727_irq", pchg)) {
  		dev_err(pchg->dev, "lp8727 irq can not be registered
  ");
  	}
  }
  
  static enum power_supply_property lp8727_charger_prop[] = {
  	POWER_SUPPLY_PROP_ONLINE,
  };
  
  static enum power_supply_property lp8727_battery_prop[] = {
  	POWER_SUPPLY_PROP_STATUS,
  	POWER_SUPPLY_PROP_HEALTH,
  	POWER_SUPPLY_PROP_PRESENT,
  	POWER_SUPPLY_PROP_VOLTAGE_NOW,
  	POWER_SUPPLY_PROP_CAPACITY,
  	POWER_SUPPLY_PROP_TEMP,
  };
  
  static char *battery_supplied_to[] = {
  	"main_batt",
  };
  
  static int lp8727_charger_get_property(struct power_supply *psy,
  				       enum power_supply_property psp,
  				       union power_supply_propval *val)
  {
  	struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent);
ce09affc5   Kim, Milo   lp8727_charger: C...
287
  	if (psp == POWER_SUPPLY_PROP_ONLINE)
2165c8a45   Woogyom Kim   power_supply: Add...
288
289
  		val->intval = lp8727_is_charger_attached(psy->name,
  							 pchg->devid);
2165c8a45   Woogyom Kim   power_supply: Add...
290
291
292
293
294
295
296
297
298
299
300
301
302
303
  
  	return 0;
  }
  
  static int lp8727_battery_get_property(struct power_supply *psy,
  				       enum power_supply_property psp,
  				       union power_supply_propval *val)
  {
  	struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent);
  	u8 read;
  
  	switch (psp) {
  	case POWER_SUPPLY_PROP_STATUS:
  		if (lp8727_is_charger_attached(psy->name, pchg->devid)) {
ce09affc5   Kim, Milo   lp8727_charger: C...
304
  			lp8727_i2c_read_byte(pchg, STATUS1, &read);
2165c8a45   Woogyom Kim   power_supply: Add...
305
306
307
308
309
310
311
312
313
  			if (((read & CHGSTAT) >> 4) == EOC)
  				val->intval = POWER_SUPPLY_STATUS_FULL;
  			else
  				val->intval = POWER_SUPPLY_STATUS_CHARGING;
  		} else {
  			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
  		}
  		break;
  	case POWER_SUPPLY_PROP_HEALTH:
ce09affc5   Kim, Milo   lp8727_charger: C...
314
  		lp8727_i2c_read_byte(pchg, STATUS2, &read);
2165c8a45   Woogyom Kim   power_supply: Add...
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
  		read = (read & TEMP_STAT) >> 5;
  		if (read >= 0x1 && read <= 0x3)
  			val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
  		else
  			val->intval = POWER_SUPPLY_HEALTH_GOOD;
  		break;
  	case POWER_SUPPLY_PROP_PRESENT:
  		if (pchg->pdata->get_batt_present)
  			val->intval = pchg->pdata->get_batt_present();
  		break;
  	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
  		if (pchg->pdata->get_batt_level)
  			val->intval = pchg->pdata->get_batt_level();
  		break;
  	case POWER_SUPPLY_PROP_CAPACITY:
  		if (pchg->pdata->get_batt_capacity)
  			val->intval = pchg->pdata->get_batt_capacity();
  		break;
  	case POWER_SUPPLY_PROP_TEMP:
  		if (pchg->pdata->get_batt_temp)
  			val->intval = pchg->pdata->get_batt_temp();
  		break;
  	default:
  		break;
  	}
  
  	return 0;
  }
  
  static void lp8727_charger_changed(struct power_supply *psy)
  {
  	struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent);
  	u8 val;
  	u8 eoc_level, ichg;
  
  	if (lp8727_is_charger_attached(psy->name, pchg->devid)) {
  		if (pchg->chg_parm) {
  			eoc_level = pchg->chg_parm->eoc_level;
  			ichg = pchg->chg_parm->ichg;
  			val = (ichg << 4) | eoc_level;
ce09affc5   Kim, Milo   lp8727_charger: C...
355
  			lp8727_i2c_write_byte(pchg, CHGCTRL2, &val);
2165c8a45   Woogyom Kim   power_supply: Add...
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
  		}
  	}
  }
  
  static int lp8727_register_psy(struct lp8727_chg *pchg)
  {
  	struct lp8727_psy *psy;
  
  	psy = kzalloc(sizeof(*psy), GFP_KERNEL);
  	if (!psy)
  		goto err_mem;
  
  	pchg->psy = psy;
  
  	psy->ac.name = "ac";
  	psy->ac.type = POWER_SUPPLY_TYPE_MAINS;
  	psy->ac.properties = lp8727_charger_prop;
  	psy->ac.num_properties = ARRAY_SIZE(lp8727_charger_prop);
  	psy->ac.get_property = lp8727_charger_get_property;
  	psy->ac.supplied_to = battery_supplied_to;
  	psy->ac.num_supplicants = ARRAY_SIZE(battery_supplied_to);
  
  	if (power_supply_register(pchg->dev, &psy->ac))
  		goto err_psy;
  
  	psy->usb.name = "usb";
  	psy->usb.type = POWER_SUPPLY_TYPE_USB;
  	psy->usb.properties = lp8727_charger_prop;
  	psy->usb.num_properties = ARRAY_SIZE(lp8727_charger_prop);
  	psy->usb.get_property = lp8727_charger_get_property;
  	psy->usb.supplied_to = battery_supplied_to;
  	psy->usb.num_supplicants = ARRAY_SIZE(battery_supplied_to);
  
  	if (power_supply_register(pchg->dev, &psy->usb))
  		goto err_psy;
  
  	psy->batt.name = "main_batt";
  	psy->batt.type = POWER_SUPPLY_TYPE_BATTERY;
  	psy->batt.properties = lp8727_battery_prop;
  	psy->batt.num_properties = ARRAY_SIZE(lp8727_battery_prop);
  	psy->batt.get_property = lp8727_battery_get_property;
  	psy->batt.external_power_changed = lp8727_charger_changed;
  
  	if (power_supply_register(pchg->dev, &psy->batt))
  		goto err_psy;
  
  	return 0;
  
  err_mem:
  	return -ENOMEM;
  err_psy:
  	kfree(psy);
  	return -EPERM;
  }
  
  static void lp8727_unregister_psy(struct lp8727_chg *pchg)
  {
  	struct lp8727_psy *psy = pchg->psy;
ce09affc5   Kim, Milo   lp8727_charger: C...
414
415
416
417
418
419
420
  	if (!psy)
  		return;
  
  	power_supply_unregister(&psy->ac);
  	power_supply_unregister(&psy->usb);
  	power_supply_unregister(&psy->batt);
  	kfree(psy);
2165c8a45   Woogyom Kim   power_supply: Add...
421
422
423
424
425
426
  }
  
  static int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id)
  {
  	struct lp8727_chg *pchg;
  	int ret;
998a8e7a7   Kim, Milo   lp8727_charger: A...
427
428
  	if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
  		return -EIO;
2165c8a45   Woogyom Kim   power_supply: Add...
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
  	pchg = kzalloc(sizeof(*pchg), GFP_KERNEL);
  	if (!pchg)
  		return -ENOMEM;
  
  	pchg->client = cl;
  	pchg->dev = &cl->dev;
  	pchg->pdata = cl->dev.platform_data;
  	i2c_set_clientdata(cl, pchg);
  
  	mutex_init(&pchg->xfer_lock);
  
  	lp8727_init_device(pchg);
  	lp8727_intr_config(pchg);
  
  	ret = lp8727_register_psy(pchg);
ce09affc5   Kim, Milo   lp8727_charger: C...
444
  	if (ret)
2165c8a45   Woogyom Kim   power_supply: Add...
445
446
  		dev_err(pchg->dev,
  			"can not register power supplies. err=%d", ret);
2165c8a45   Woogyom Kim   power_supply: Add...
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
  
  	return 0;
  }
  
  static int __devexit lp8727_remove(struct i2c_client *cl)
  {
  	struct lp8727_chg *pchg = i2c_get_clientdata(cl);
  
  	lp8727_unregister_psy(pchg);
  	free_irq(pchg->client->irq, pchg);
  	flush_workqueue(pchg->irqthread);
  	destroy_workqueue(pchg->irqthread);
  	kfree(pchg);
  	return 0;
  }
  
  static const struct i2c_device_id lp8727_ids[] = {
  	{"lp8727", 0},
  };
  
  static struct i2c_driver lp8727_driver = {
  	.driver = {
  		   .name = "lp8727",
  		   },
  	.probe = lp8727_probe,
  	.remove = __devexit_p(lp8727_remove),
  	.id_table = lp8727_ids,
  };
  
  static int __init lp8727_init(void)
  {
  	return i2c_add_driver(&lp8727_driver);
  }
ce09affc5   Kim, Milo   lp8727_charger: C...
480
  static void __exit lp8727_exit(void)
2165c8a45   Woogyom Kim   power_supply: Add...
481
482
483
484
485
  {
  	i2c_del_driver(&lp8727_driver);
  }
  
  module_init(lp8727_init);
ce09affc5   Kim, Milo   lp8727_charger: C...
486
  module_exit(lp8727_exit);
2165c8a45   Woogyom Kim   power_supply: Add...
487
488
  
  MODULE_DESCRIPTION("National Semiconductor LP8727 charger driver");
ce09affc5   Kim, Milo   lp8727_charger: C...
489
490
  MODULE_AUTHOR
      ("Woogyom Kim <milo.kim@ti.com>, Daniel Jeong <daniel.jeong@ti.com>");
2165c8a45   Woogyom Kim   power_supply: Add...
491
  MODULE_LICENSE("GPL");