• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/drivers/power/
1/*
2 * Battery charger driver for Dialog Semiconductor DA9030
3 *
4 * Copyright (C) 2008 Compulab, Ltd.
5 * 	Mike Rapoport <mike@compulab.co.il>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/kernel.h>
13#include <linux/slab.h>
14#include <linux/init.h>
15#include <linux/types.h>
16#include <linux/device.h>
17#include <linux/workqueue.h>
18#include <linux/module.h>
19#include <linux/platform_device.h>
20#include <linux/power_supply.h>
21#include <linux/mfd/da903x.h>
22
23#include <linux/debugfs.h>
24#include <linux/seq_file.h>
25
26#define DA9030_FAULT_LOG		0x0a
27#define DA9030_FAULT_LOG_OVER_TEMP	(1 << 7)
28#define DA9030_FAULT_LOG_VBAT_OVER	(1 << 4)
29
30#define DA9030_CHARGE_CONTROL		0x28
31#define DA9030_CHRG_CHARGER_ENABLE	(1 << 7)
32
33#define DA9030_ADC_MAN_CONTROL		0x30
34#define DA9030_ADC_TBATREF_ENABLE	(1 << 5)
35#define DA9030_ADC_LDO_INT_ENABLE	(1 << 4)
36
37#define DA9030_ADC_AUTO_CONTROL		0x31
38#define DA9030_ADC_TBAT_ENABLE		(1 << 5)
39#define DA9030_ADC_VBAT_IN_TXON		(1 << 4)
40#define DA9030_ADC_VCH_ENABLE		(1 << 3)
41#define DA9030_ADC_ICH_ENABLE		(1 << 2)
42#define DA9030_ADC_VBAT_ENABLE		(1 << 1)
43#define DA9030_ADC_AUTO_SLEEP_ENABLE	(1 << 0)
44
45#define DA9030_VBATMON		0x32
46#define DA9030_VBATMONTXON	0x33
47#define DA9030_TBATHIGHP	0x34
48#define DA9030_TBATHIGHN	0x35
49#define DA9030_TBATLOW		0x36
50
51#define DA9030_VBAT_RES		0x41
52#define DA9030_VBATMIN_RES	0x42
53#define DA9030_VBATMINTXON_RES	0x43
54#define DA9030_ICHMAX_RES	0x44
55#define DA9030_ICHMIN_RES	0x45
56#define DA9030_ICHAVERAGE_RES	0x46
57#define DA9030_VCHMAX_RES	0x47
58#define DA9030_VCHMIN_RES	0x48
59#define DA9030_TBAT_RES		0x49
60
61struct da9030_adc_res {
62	uint8_t vbat_res;
63	uint8_t vbatmin_res;
64	uint8_t vbatmintxon;
65	uint8_t ichmax_res;
66	uint8_t ichmin_res;
67	uint8_t ichaverage_res;
68	uint8_t vchmax_res;
69	uint8_t vchmin_res;
70	uint8_t tbat_res;
71	uint8_t adc_in4_res;
72	uint8_t adc_in5_res;
73};
74
75struct da9030_battery_thresholds {
76	int tbat_low;
77	int tbat_high;
78	int tbat_restart;
79
80	int vbat_low;
81	int vbat_crit;
82	int vbat_charge_start;
83	int vbat_charge_stop;
84	int vbat_charge_restart;
85
86	int vcharge_min;
87	int vcharge_max;
88};
89
90struct da9030_charger {
91	struct power_supply psy;
92
93	struct device *master;
94
95	struct da9030_adc_res adc;
96	struct delayed_work work;
97	unsigned int interval;
98
99	struct power_supply_info *battery_info;
100
101	struct da9030_battery_thresholds thresholds;
102
103	unsigned int charge_milliamp;
104	unsigned int charge_millivolt;
105
106	/* charger status */
107	bool chdet;
108	uint8_t fault;
109	int mA;
110	int mV;
111	bool is_on;
112
113	struct notifier_block nb;
114
115	/* platform callbacks for battery low and critical events */
116	void (*battery_low)(void);
117	void (*battery_critical)(void);
118
119	struct dentry *debug_file;
120};
121
122static inline int da9030_reg_to_mV(int reg)
123{
124	return ((reg * 2650) >> 8) + 2650;
125}
126
127static inline int da9030_millivolt_to_reg(int mV)
128{
129	return ((mV - 2650) << 8) / 2650;
130}
131
132static inline int da9030_reg_to_mA(int reg)
133{
134	return ((reg * 24000) >> 8) / 15;
135}
136
137#ifdef CONFIG_DEBUG_FS
138static int bat_debug_show(struct seq_file *s, void *data)
139{
140	struct da9030_charger *charger = s->private;
141
142	seq_printf(s, "charger is %s\n", charger->is_on ? "on" : "off");
143	if (charger->chdet) {
144		seq_printf(s, "iset = %dmA, vset = %dmV\n",
145			   charger->mA, charger->mV);
146	}
147
148	seq_printf(s, "vbat_res = %d (%dmV)\n",
149		   charger->adc.vbat_res,
150		   da9030_reg_to_mV(charger->adc.vbat_res));
151	seq_printf(s, "vbatmin_res = %d (%dmV)\n",
152		   charger->adc.vbatmin_res,
153		   da9030_reg_to_mV(charger->adc.vbatmin_res));
154	seq_printf(s, "vbatmintxon = %d (%dmV)\n",
155		   charger->adc.vbatmintxon,
156		   da9030_reg_to_mV(charger->adc.vbatmintxon));
157	seq_printf(s, "ichmax_res = %d (%dmA)\n",
158		   charger->adc.ichmax_res,
159		   da9030_reg_to_mV(charger->adc.ichmax_res));
160	seq_printf(s, "ichmin_res = %d (%dmA)\n",
161		   charger->adc.ichmin_res,
162		   da9030_reg_to_mA(charger->adc.ichmin_res));
163	seq_printf(s, "ichaverage_res = %d (%dmA)\n",
164		   charger->adc.ichaverage_res,
165		   da9030_reg_to_mA(charger->adc.ichaverage_res));
166	seq_printf(s, "vchmax_res = %d (%dmV)\n",
167		   charger->adc.vchmax_res,
168		   da9030_reg_to_mA(charger->adc.vchmax_res));
169	seq_printf(s, "vchmin_res = %d (%dmV)\n",
170		   charger->adc.vchmin_res,
171		   da9030_reg_to_mV(charger->adc.vchmin_res));
172
173	return 0;
174}
175
176static int debug_open(struct inode *inode, struct file *file)
177{
178	return single_open(file, bat_debug_show, inode->i_private);
179}
180
181static const struct file_operations bat_debug_fops = {
182	.open		= debug_open,
183	.read		= seq_read,
184	.llseek		= seq_lseek,
185	.release	= single_release,
186};
187
188static struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger)
189{
190	charger->debug_file = debugfs_create_file("charger", 0666, 0, charger,
191						 &bat_debug_fops);
192	return charger->debug_file;
193}
194
195static void da9030_bat_remove_debugfs(struct da9030_charger *charger)
196{
197	debugfs_remove(charger->debug_file);
198}
199#else
200static inline struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger)
201{
202	return NULL;
203}
204static inline void da9030_bat_remove_debugfs(struct da9030_charger *charger)
205{
206}
207#endif
208
209static inline void da9030_read_adc(struct da9030_charger *charger,
210				   struct da9030_adc_res *adc)
211{
212	da903x_reads(charger->master, DA9030_VBAT_RES,
213		     sizeof(*adc), (uint8_t *)adc);
214}
215
216static void da9030_charger_update_state(struct da9030_charger *charger)
217{
218	uint8_t val;
219
220	da903x_read(charger->master, DA9030_CHARGE_CONTROL, &val);
221	charger->is_on = (val & DA9030_CHRG_CHARGER_ENABLE) ? 1 : 0;
222	charger->mA = ((val >> 3) & 0xf) * 100;
223	charger->mV = (val & 0x7) * 50 + 4000;
224
225	da9030_read_adc(charger, &charger->adc);
226	da903x_read(charger->master, DA9030_FAULT_LOG, &charger->fault);
227	charger->chdet = da903x_query_status(charger->master,
228						     DA9030_STATUS_CHDET);
229}
230
231static void da9030_set_charge(struct da9030_charger *charger, int on)
232{
233	uint8_t val;
234
235	if (on) {
236		val = DA9030_CHRG_CHARGER_ENABLE;
237		val |= (charger->charge_milliamp / 100) << 3;
238		val |= (charger->charge_millivolt - 4000) / 50;
239		charger->is_on = 1;
240	} else {
241		val = 0;
242		charger->is_on = 0;
243	}
244
245	da903x_write(charger->master, DA9030_CHARGE_CONTROL, val);
246
247	power_supply_changed(&charger->psy);
248}
249
250static void da9030_charger_check_state(struct da9030_charger *charger)
251{
252	da9030_charger_update_state(charger);
253
254	/* we wake or boot with external power on */
255	if (!charger->is_on) {
256		if ((charger->chdet) &&
257		    (charger->adc.vbat_res <
258		     charger->thresholds.vbat_charge_start)) {
259			da9030_set_charge(charger, 1);
260		}
261	} else {
262		/* Charger has been pulled out */
263		if (!charger->chdet) {
264			da9030_set_charge(charger, 0);
265			return;
266		}
267
268		if (charger->adc.vbat_res >=
269		    charger->thresholds.vbat_charge_stop) {
270			da9030_set_charge(charger, 0);
271			da903x_write(charger->master, DA9030_VBATMON,
272				       charger->thresholds.vbat_charge_restart);
273		} else if (charger->adc.vbat_res >
274			   charger->thresholds.vbat_low) {
275			/* we are charging and passed LOW_THRESH,
276			   so upate DA9030 VBAT threshold
277			 */
278			da903x_write(charger->master, DA9030_VBATMON,
279				     charger->thresholds.vbat_low);
280		}
281		if (charger->adc.vchmax_res > charger->thresholds.vcharge_max ||
282		    charger->adc.vchmin_res < charger->thresholds.vcharge_min ||
283		    /* Tempreture readings are negative */
284		    charger->adc.tbat_res < charger->thresholds.tbat_high ||
285		    charger->adc.tbat_res > charger->thresholds.tbat_low) {
286			/* disable charger */
287			da9030_set_charge(charger, 0);
288		}
289	}
290}
291
292static void da9030_charging_monitor(struct work_struct *work)
293{
294	struct da9030_charger *charger;
295
296	charger = container_of(work, struct da9030_charger, work.work);
297
298	da9030_charger_check_state(charger);
299
300	/* reschedule for the next time */
301	schedule_delayed_work(&charger->work, charger->interval);
302}
303
304static enum power_supply_property da9030_battery_props[] = {
305	POWER_SUPPLY_PROP_MODEL_NAME,
306	POWER_SUPPLY_PROP_STATUS,
307	POWER_SUPPLY_PROP_HEALTH,
308	POWER_SUPPLY_PROP_TECHNOLOGY,
309	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
310	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
311	POWER_SUPPLY_PROP_VOLTAGE_NOW,
312	POWER_SUPPLY_PROP_CURRENT_AVG,
313};
314
315static void da9030_battery_check_status(struct da9030_charger *charger,
316				    union power_supply_propval *val)
317{
318	if (charger->chdet) {
319		if (charger->is_on)
320			val->intval = POWER_SUPPLY_STATUS_CHARGING;
321		else
322			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
323	} else {
324		val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
325	}
326}
327
328static void da9030_battery_check_health(struct da9030_charger *charger,
329				    union power_supply_propval *val)
330{
331	if (charger->fault & DA9030_FAULT_LOG_OVER_TEMP)
332		val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
333	else if (charger->fault & DA9030_FAULT_LOG_VBAT_OVER)
334		val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
335	else
336		val->intval = POWER_SUPPLY_HEALTH_GOOD;
337}
338
339static int da9030_battery_get_property(struct power_supply *psy,
340				   enum power_supply_property psp,
341				   union power_supply_propval *val)
342{
343	struct da9030_charger *charger;
344	charger = container_of(psy, struct da9030_charger, psy);
345
346	switch (psp) {
347	case POWER_SUPPLY_PROP_STATUS:
348		da9030_battery_check_status(charger, val);
349		break;
350	case POWER_SUPPLY_PROP_HEALTH:
351		da9030_battery_check_health(charger, val);
352		break;
353	case POWER_SUPPLY_PROP_TECHNOLOGY:
354		val->intval = charger->battery_info->technology;
355		break;
356	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
357		val->intval = charger->battery_info->voltage_max_design;
358		break;
359	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
360		val->intval = charger->battery_info->voltage_min_design;
361		break;
362	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
363		val->intval = da9030_reg_to_mV(charger->adc.vbat_res) * 1000;
364		break;
365	case POWER_SUPPLY_PROP_CURRENT_AVG:
366		val->intval =
367			da9030_reg_to_mA(charger->adc.ichaverage_res) * 1000;
368		break;
369	case POWER_SUPPLY_PROP_MODEL_NAME:
370		val->strval = charger->battery_info->name;
371		break;
372	default:
373		break;
374	}
375
376	return 0;
377}
378
379static void da9030_battery_vbat_event(struct da9030_charger *charger)
380{
381	da9030_read_adc(charger, &charger->adc);
382
383	if (charger->is_on)
384		return;
385
386	if (charger->adc.vbat_res < charger->thresholds.vbat_low) {
387		/* set VBAT threshold for critical */
388		da903x_write(charger->master, DA9030_VBATMON,
389			     charger->thresholds.vbat_crit);
390		if (charger->battery_low)
391			charger->battery_low();
392	} else if (charger->adc.vbat_res <
393		   charger->thresholds.vbat_crit) {
394		/* notify the system of battery critical */
395		if (charger->battery_critical)
396			charger->battery_critical();
397	}
398}
399
400static int da9030_battery_event(struct notifier_block *nb, unsigned long event,
401				void *data)
402{
403	struct da9030_charger *charger =
404		container_of(nb, struct da9030_charger, nb);
405
406	switch (event) {
407	case DA9030_EVENT_CHDET:
408		cancel_delayed_work_sync(&charger->work);
409		schedule_work(&charger->work.work);
410		break;
411	case DA9030_EVENT_VBATMON:
412		da9030_battery_vbat_event(charger);
413		break;
414	case DA9030_EVENT_CHIOVER:
415	case DA9030_EVENT_TBAT:
416		da9030_set_charge(charger, 0);
417		break;
418	}
419
420	return 0;
421}
422
423static void da9030_battery_convert_thresholds(struct da9030_charger *charger,
424					      struct da9030_battery_info *pdata)
425{
426	charger->thresholds.tbat_low = pdata->tbat_low;
427	charger->thresholds.tbat_high = pdata->tbat_high;
428	charger->thresholds.tbat_restart  = pdata->tbat_restart;
429
430	charger->thresholds.vbat_low =
431		da9030_millivolt_to_reg(pdata->vbat_low);
432	charger->thresholds.vbat_crit =
433		da9030_millivolt_to_reg(pdata->vbat_crit);
434	charger->thresholds.vbat_charge_start =
435		da9030_millivolt_to_reg(pdata->vbat_charge_start);
436	charger->thresholds.vbat_charge_stop =
437		da9030_millivolt_to_reg(pdata->vbat_charge_stop);
438	charger->thresholds.vbat_charge_restart =
439		da9030_millivolt_to_reg(pdata->vbat_charge_restart);
440
441	charger->thresholds.vcharge_min =
442		da9030_millivolt_to_reg(pdata->vcharge_min);
443	charger->thresholds.vcharge_max =
444		da9030_millivolt_to_reg(pdata->vcharge_max);
445}
446
447static void da9030_battery_setup_psy(struct da9030_charger *charger)
448{
449	struct power_supply *psy = &charger->psy;
450	struct power_supply_info *info = charger->battery_info;
451
452	psy->name = info->name;
453	psy->use_for_apm = info->use_for_apm;
454	psy->type = POWER_SUPPLY_TYPE_BATTERY;
455	psy->get_property = da9030_battery_get_property;
456
457	psy->properties = da9030_battery_props;
458	psy->num_properties = ARRAY_SIZE(da9030_battery_props);
459};
460
461static int da9030_battery_charger_init(struct da9030_charger *charger)
462{
463	char v[5];
464	int ret;
465
466	v[0] = v[1] = charger->thresholds.vbat_low;
467	v[2] = charger->thresholds.tbat_high;
468	v[3] = charger->thresholds.tbat_restart;
469	v[4] = charger->thresholds.tbat_low;
470
471	ret = da903x_writes(charger->master, DA9030_VBATMON, 5, v);
472	if (ret)
473		return ret;
474
475	/*
476	 * Enable reference voltage supply for ADC from the LDO_INTERNAL
477	 * regulator. Must be set before ADC measurements can be made.
478	 */
479	ret = da903x_write(charger->master, DA9030_ADC_MAN_CONTROL,
480			   DA9030_ADC_LDO_INT_ENABLE |
481			   DA9030_ADC_TBATREF_ENABLE);
482	if (ret)
483		return ret;
484
485	/* enable auto ADC measuremnts */
486	return da903x_write(charger->master, DA9030_ADC_AUTO_CONTROL,
487			    DA9030_ADC_TBAT_ENABLE | DA9030_ADC_VBAT_IN_TXON |
488			    DA9030_ADC_VCH_ENABLE | DA9030_ADC_ICH_ENABLE |
489			    DA9030_ADC_VBAT_ENABLE |
490			    DA9030_ADC_AUTO_SLEEP_ENABLE);
491}
492
493static int da9030_battery_probe(struct platform_device *pdev)
494{
495	struct da9030_charger *charger;
496	struct da9030_battery_info *pdata = pdev->dev.platform_data;
497	int ret;
498
499	if (pdata == NULL)
500		return -EINVAL;
501
502	if (pdata->charge_milliamp >= 1500 ||
503	    pdata->charge_millivolt < 4000 ||
504	    pdata->charge_millivolt > 4350)
505		return -EINVAL;
506
507	charger = kzalloc(sizeof(*charger), GFP_KERNEL);
508	if (charger == NULL)
509		return -ENOMEM;
510
511	charger->master = pdev->dev.parent;
512
513	/* 10 seconds between monitor runs unless platform defines other
514	   interval */
515	charger->interval = msecs_to_jiffies(
516		(pdata->batmon_interval ? : 10) * 1000);
517
518	charger->charge_milliamp = pdata->charge_milliamp;
519	charger->charge_millivolt = pdata->charge_millivolt;
520	charger->battery_info = pdata->battery_info;
521	charger->battery_low = pdata->battery_low;
522	charger->battery_critical = pdata->battery_critical;
523
524	da9030_battery_convert_thresholds(charger, pdata);
525
526	ret = da9030_battery_charger_init(charger);
527	if (ret)
528		goto err_charger_init;
529
530	INIT_DELAYED_WORK(&charger->work, da9030_charging_monitor);
531	schedule_delayed_work(&charger->work, charger->interval);
532
533	charger->nb.notifier_call = da9030_battery_event;
534	ret = da903x_register_notifier(charger->master, &charger->nb,
535				       DA9030_EVENT_CHDET |
536				       DA9030_EVENT_VBATMON |
537				       DA9030_EVENT_CHIOVER |
538				       DA9030_EVENT_TBAT);
539	if (ret)
540		goto err_notifier;
541
542	da9030_battery_setup_psy(charger);
543	ret = power_supply_register(&pdev->dev, &charger->psy);
544	if (ret)
545		goto err_ps_register;
546
547	charger->debug_file = da9030_bat_create_debugfs(charger);
548	platform_set_drvdata(pdev, charger);
549	return 0;
550
551err_ps_register:
552	da903x_unregister_notifier(charger->master, &charger->nb,
553				   DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON |
554				   DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT);
555err_notifier:
556	cancel_delayed_work(&charger->work);
557
558err_charger_init:
559	kfree(charger);
560
561	return ret;
562}
563
564static int da9030_battery_remove(struct platform_device *dev)
565{
566	struct da9030_charger *charger = platform_get_drvdata(dev);
567
568	da9030_bat_remove_debugfs(charger);
569
570	da903x_unregister_notifier(charger->master, &charger->nb,
571				   DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON |
572				   DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT);
573	cancel_delayed_work_sync(&charger->work);
574	da9030_set_charge(charger, 0);
575	power_supply_unregister(&charger->psy);
576
577	kfree(charger);
578
579	return 0;
580}
581
582static struct platform_driver da903x_battery_driver = {
583	.driver	= {
584		.name	= "da903x-battery",
585		.owner	= THIS_MODULE,
586	},
587	.probe = da9030_battery_probe,
588	.remove = da9030_battery_remove,
589};
590
591static int da903x_battery_init(void)
592{
593	return platform_driver_register(&da903x_battery_driver);
594}
595
596static void da903x_battery_exit(void)
597{
598	platform_driver_unregister(&da903x_battery_driver);
599}
600
601module_init(da903x_battery_init);
602module_exit(da903x_battery_exit);
603
604MODULE_DESCRIPTION("DA9030 battery charger driver");
605MODULE_AUTHOR("Mike Rapoport, CompuLab");
606MODULE_LICENSE("GPL");
607