1342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport/*
2342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport * Battery charger driver for Dialog Semiconductor DA9030
3342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport *
4342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport * Copyright (C) 2008 Compulab, Ltd.
5342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport * 	Mike Rapoport <mike@compulab.co.il>
6342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport *
7342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport * This program is free software; you can redistribute it and/or modify
8342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport * it under the terms of the GNU General Public License version 2 as
9342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport * published by the Free Software Foundation.
10342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport */
11342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
12342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#include <linux/kernel.h>
135a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
14342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#include <linux/init.h>
15342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#include <linux/types.h>
16342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#include <linux/device.h>
17342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#include <linux/workqueue.h>
18342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#include <linux/module.h>
19342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#include <linux/platform_device.h>
20342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#include <linux/power_supply.h>
21342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#include <linux/mfd/da903x.h>
22342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
23342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#include <linux/debugfs.h>
24342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#include <linux/seq_file.h>
25342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
26342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_FAULT_LOG		0x0a
27342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_FAULT_LOG_OVER_TEMP	(1 << 7)
28342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_FAULT_LOG_VBAT_OVER	(1 << 4)
29342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
30342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_CHARGE_CONTROL		0x28
31342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_CHRG_CHARGER_ENABLE	(1 << 7)
32342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
33342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_ADC_MAN_CONTROL		0x30
34342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_ADC_TBATREF_ENABLE	(1 << 5)
35342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_ADC_LDO_INT_ENABLE	(1 << 4)
36342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
37342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_ADC_AUTO_CONTROL		0x31
38342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_ADC_TBAT_ENABLE		(1 << 5)
39342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_ADC_VBAT_IN_TXON		(1 << 4)
40342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_ADC_VCH_ENABLE		(1 << 3)
41342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_ADC_ICH_ENABLE		(1 << 2)
42342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_ADC_VBAT_ENABLE		(1 << 1)
43342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_ADC_AUTO_SLEEP_ENABLE	(1 << 0)
44342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
45342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_VBATMON		0x32
46342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_VBATMONTXON	0x33
47342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_TBATHIGHP	0x34
48342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_TBATHIGHN	0x35
49342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_TBATLOW		0x36
50342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
51342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_VBAT_RES		0x41
52342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_VBATMIN_RES	0x42
53342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_VBATMINTXON_RES	0x43
54342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_ICHMAX_RES	0x44
55342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_ICHMIN_RES	0x45
56342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_ICHAVERAGE_RES	0x46
57342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_VCHMAX_RES	0x47
58342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_VCHMIN_RES	0x48
59342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#define DA9030_TBAT_RES		0x49
60342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
61342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstruct da9030_adc_res {
62342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	uint8_t vbat_res;
63342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	uint8_t vbatmin_res;
64342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	uint8_t vbatmintxon;
65342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	uint8_t ichmax_res;
66342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	uint8_t ichmin_res;
67342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	uint8_t ichaverage_res;
68342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	uint8_t vchmax_res;
69342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	uint8_t vchmin_res;
70342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	uint8_t tbat_res;
71342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	uint8_t adc_in4_res;
72342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	uint8_t adc_in5_res;
73342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport};
74342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
75342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstruct da9030_battery_thresholds {
76342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	int tbat_low;
77342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	int tbat_high;
78342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	int tbat_restart;
79342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
80342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	int vbat_low;
81342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	int vbat_crit;
82342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	int vbat_charge_start;
83342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	int vbat_charge_stop;
84342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	int vbat_charge_restart;
85342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
86342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	int vcharge_min;
87342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	int vcharge_max;
88342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport};
89342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
90342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstruct da9030_charger {
91342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	struct power_supply psy;
92342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
93342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	struct device *master;
94342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
95342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	struct da9030_adc_res adc;
96342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	struct delayed_work work;
97342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	unsigned int interval;
98342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
99342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	struct power_supply_info *battery_info;
100342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
101342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	struct da9030_battery_thresholds thresholds;
102342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
103342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	unsigned int charge_milliamp;
104342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	unsigned int charge_millivolt;
105342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
106342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	/* charger status */
107342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	bool chdet;
108342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	uint8_t fault;
109342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	int mA;
110342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	int mV;
111342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	bool is_on;
112342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
113342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	struct notifier_block nb;
114342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
115342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	/* platform callbacks for battery low and critical events */
116342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	void (*battery_low)(void);
117342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	void (*battery_critical)(void);
118342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
119342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	struct dentry *debug_file;
120342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport};
121342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
122342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic inline int da9030_reg_to_mV(int reg)
123342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{
124342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	return ((reg * 2650) >> 8) + 2650;
125342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}
126342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
127342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic inline int da9030_millivolt_to_reg(int mV)
128342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{
129342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	return ((mV - 2650) << 8) / 2650;
130342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}
131342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
132342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic inline int da9030_reg_to_mA(int reg)
133342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{
134342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	return ((reg * 24000) >> 8) / 15;
135342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}
136342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
137342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#ifdef CONFIG_DEBUG_FS
138342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic int bat_debug_show(struct seq_file *s, void *data)
139342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{
140342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	struct da9030_charger *charger = s->private;
141342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
142342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	seq_printf(s, "charger is %s\n", charger->is_on ? "on" : "off");
143342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	if (charger->chdet) {
144342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		seq_printf(s, "iset = %dmA, vset = %dmV\n",
145342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport			   charger->mA, charger->mV);
146342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	}
147342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
148342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	seq_printf(s, "vbat_res = %d (%dmV)\n",
149342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		   charger->adc.vbat_res,
150342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		   da9030_reg_to_mV(charger->adc.vbat_res));
151342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	seq_printf(s, "vbatmin_res = %d (%dmV)\n",
152342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		   charger->adc.vbatmin_res,
153342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		   da9030_reg_to_mV(charger->adc.vbatmin_res));
154342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	seq_printf(s, "vbatmintxon = %d (%dmV)\n",
155342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		   charger->adc.vbatmintxon,
156342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		   da9030_reg_to_mV(charger->adc.vbatmintxon));
157342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	seq_printf(s, "ichmax_res = %d (%dmA)\n",
158342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		   charger->adc.ichmax_res,
159342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		   da9030_reg_to_mV(charger->adc.ichmax_res));
160342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	seq_printf(s, "ichmin_res = %d (%dmA)\n",
161342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		   charger->adc.ichmin_res,
162342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		   da9030_reg_to_mA(charger->adc.ichmin_res));
163342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	seq_printf(s, "ichaverage_res = %d (%dmA)\n",
164342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		   charger->adc.ichaverage_res,
165342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		   da9030_reg_to_mA(charger->adc.ichaverage_res));
166342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	seq_printf(s, "vchmax_res = %d (%dmV)\n",
167342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		   charger->adc.vchmax_res,
168342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		   da9030_reg_to_mA(charger->adc.vchmax_res));
169342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	seq_printf(s, "vchmin_res = %d (%dmV)\n",
170342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		   charger->adc.vchmin_res,
171342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		   da9030_reg_to_mV(charger->adc.vchmin_res));
172342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
173342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	return 0;
174342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}
175342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
176342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic int debug_open(struct inode *inode, struct file *file)
177342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{
178342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	return single_open(file, bat_debug_show, inode->i_private);
179342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}
180342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
181342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic const struct file_operations bat_debug_fops = {
182342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	.open		= debug_open,
183342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	.read		= seq_read,
184342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	.llseek		= seq_lseek,
185342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	.release	= single_release,
186342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport};
187342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
188342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger)
189342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{
190342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger->debug_file = debugfs_create_file("charger", 0666, 0, charger,
191342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport						 &bat_debug_fops);
192342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	return charger->debug_file;
193342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}
194342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
195342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic void da9030_bat_remove_debugfs(struct da9030_charger *charger)
196342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{
197342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	debugfs_remove(charger->debug_file);
198342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}
199342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#else
200342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic inline struct dentry *da9030_bat_create_debugfs(struct da9030_charger *charger)
201342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{
202342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	return NULL;
203342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}
204342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic inline void da9030_bat_remove_debugfs(struct da9030_charger *charger)
205342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{
206342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}
207342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport#endif
208342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
209342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic inline void da9030_read_adc(struct da9030_charger *charger,
210342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport				   struct da9030_adc_res *adc)
211342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{
212342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	da903x_reads(charger->master, DA9030_VBAT_RES,
213342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		     sizeof(*adc), (uint8_t *)adc);
214342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}
215342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
216342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic void da9030_charger_update_state(struct da9030_charger *charger)
217342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{
218342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	uint8_t val;
219342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
220342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	da903x_read(charger->master, DA9030_CHARGE_CONTROL, &val);
221342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger->is_on = (val & DA9030_CHRG_CHARGER_ENABLE) ? 1 : 0;
222342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger->mA = ((val >> 3) & 0xf) * 100;
223342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger->mV = (val & 0x7) * 50 + 4000;
224342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
225342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	da9030_read_adc(charger, &charger->adc);
226342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	da903x_read(charger->master, DA9030_FAULT_LOG, &charger->fault);
227342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger->chdet = da903x_query_status(charger->master,
228342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport						     DA9030_STATUS_CHDET);
229342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}
230342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
231342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic void da9030_set_charge(struct da9030_charger *charger, int on)
232342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{
233342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	uint8_t val;
234342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
235342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	if (on) {
236342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		val = DA9030_CHRG_CHARGER_ENABLE;
237342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		val |= (charger->charge_milliamp / 100) << 3;
238342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		val |= (charger->charge_millivolt - 4000) / 50;
239342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		charger->is_on = 1;
240342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	} else {
241342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		val = 0;
242342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		charger->is_on = 0;
243342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	}
244342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
245342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	da903x_write(charger->master, DA9030_CHARGE_CONTROL, val);
246a35d01a5d2ac533edab94a8e3b6749ab213c91c5Mike Rapoport
247a35d01a5d2ac533edab94a8e3b6749ab213c91c5Mike Rapoport	power_supply_changed(&charger->psy);
248342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}
249342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
250342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic void da9030_charger_check_state(struct da9030_charger *charger)
251342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{
252342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	da9030_charger_update_state(charger);
253342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
254342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	/* we wake or boot with external power on */
255342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	if (!charger->is_on) {
256342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		if ((charger->chdet) &&
257342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		    (charger->adc.vbat_res <
258342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		     charger->thresholds.vbat_charge_start)) {
259342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport			da9030_set_charge(charger, 1);
260342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		}
261342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	} else {
262a35d01a5d2ac533edab94a8e3b6749ab213c91c5Mike Rapoport		/* Charger has been pulled out */
263a35d01a5d2ac533edab94a8e3b6749ab213c91c5Mike Rapoport		if (!charger->chdet) {
264a35d01a5d2ac533edab94a8e3b6749ab213c91c5Mike Rapoport			da9030_set_charge(charger, 0);
265a35d01a5d2ac533edab94a8e3b6749ab213c91c5Mike Rapoport			return;
266a35d01a5d2ac533edab94a8e3b6749ab213c91c5Mike Rapoport		}
267a35d01a5d2ac533edab94a8e3b6749ab213c91c5Mike Rapoport
268342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		if (charger->adc.vbat_res >=
269342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		    charger->thresholds.vbat_charge_stop) {
270342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport			da9030_set_charge(charger, 0);
271342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport			da903x_write(charger->master, DA9030_VBATMON,
272342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport				       charger->thresholds.vbat_charge_restart);
273342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		} else if (charger->adc.vbat_res >
274342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport			   charger->thresholds.vbat_low) {
275342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport			/* we are charging and passed LOW_THRESH,
276342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport			   so upate DA9030 VBAT threshold
277342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport			 */
278342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport			da903x_write(charger->master, DA9030_VBATMON,
279342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport				     charger->thresholds.vbat_low);
280342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		}
281342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		if (charger->adc.vchmax_res > charger->thresholds.vcharge_max ||
282342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		    charger->adc.vchmin_res < charger->thresholds.vcharge_min ||
283342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		    /* Tempreture readings are negative */
284342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		    charger->adc.tbat_res < charger->thresholds.tbat_high ||
285342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		    charger->adc.tbat_res > charger->thresholds.tbat_low) {
286342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport			/* disable charger */
287342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport			da9030_set_charge(charger, 0);
288342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		}
289342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	}
290342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}
291342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
292342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic void da9030_charging_monitor(struct work_struct *work)
293342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{
294342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	struct da9030_charger *charger;
295342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
296342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger = container_of(work, struct da9030_charger, work.work);
297342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
298342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	da9030_charger_check_state(charger);
299342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
300342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	/* reschedule for the next time */
301342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	schedule_delayed_work(&charger->work, charger->interval);
302342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}
303342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
304342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic enum power_supply_property da9030_battery_props[] = {
305342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	POWER_SUPPLY_PROP_MODEL_NAME,
306342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	POWER_SUPPLY_PROP_STATUS,
307342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	POWER_SUPPLY_PROP_HEALTH,
308342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	POWER_SUPPLY_PROP_TECHNOLOGY,
309342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
310342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
311342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	POWER_SUPPLY_PROP_VOLTAGE_NOW,
312342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	POWER_SUPPLY_PROP_CURRENT_AVG,
313342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport};
314342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
315342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic void da9030_battery_check_status(struct da9030_charger *charger,
316342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport				    union power_supply_propval *val)
317342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{
318342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	if (charger->chdet) {
319342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		if (charger->is_on)
320342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport			val->intval = POWER_SUPPLY_STATUS_CHARGING;
321342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		else
322342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
323342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	} else {
324342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
325342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	}
326342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}
327342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
328342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic void da9030_battery_check_health(struct da9030_charger *charger,
329342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport				    union power_supply_propval *val)
330342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{
331342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	if (charger->fault & DA9030_FAULT_LOG_OVER_TEMP)
332342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
333342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	else if (charger->fault & DA9030_FAULT_LOG_VBAT_OVER)
334342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
335342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	else
336342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		val->intval = POWER_SUPPLY_HEALTH_GOOD;
337342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}
338342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
339342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic int da9030_battery_get_property(struct power_supply *psy,
340342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport				   enum power_supply_property psp,
341342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport				   union power_supply_propval *val)
342342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{
343342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	struct da9030_charger *charger;
344342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger = container_of(psy, struct da9030_charger, psy);
345342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
346342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	switch (psp) {
347342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	case POWER_SUPPLY_PROP_STATUS:
348342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		da9030_battery_check_status(charger, val);
349342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		break;
350342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	case POWER_SUPPLY_PROP_HEALTH:
351342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		da9030_battery_check_health(charger, val);
352342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		break;
353342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	case POWER_SUPPLY_PROP_TECHNOLOGY:
354342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		val->intval = charger->battery_info->technology;
355342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		break;
356342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
357342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		val->intval = charger->battery_info->voltage_max_design;
358342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		break;
359342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
360342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		val->intval = charger->battery_info->voltage_min_design;
361342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		break;
362342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
363342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		val->intval = da9030_reg_to_mV(charger->adc.vbat_res) * 1000;
364342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		break;
365342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	case POWER_SUPPLY_PROP_CURRENT_AVG:
366342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		val->intval =
367342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport			da9030_reg_to_mA(charger->adc.ichaverage_res) * 1000;
368342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		break;
369342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	case POWER_SUPPLY_PROP_MODEL_NAME:
370342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		val->strval = charger->battery_info->name;
371342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		break;
372342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	default:
373342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		break;
374342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	}
375342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
376342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	return 0;
377342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}
378342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
379342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic void da9030_battery_vbat_event(struct da9030_charger *charger)
380342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{
381342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	da9030_read_adc(charger, &charger->adc);
382342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
383342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	if (charger->is_on)
384342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		return;
385342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
386342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	if (charger->adc.vbat_res < charger->thresholds.vbat_low) {
387342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		/* set VBAT threshold for critical */
388342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		da903x_write(charger->master, DA9030_VBATMON,
389342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport			     charger->thresholds.vbat_crit);
390342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		if (charger->battery_low)
391342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport			charger->battery_low();
392342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	} else if (charger->adc.vbat_res <
393342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		   charger->thresholds.vbat_crit) {
394342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		/* notify the system of battery critical */
395342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		if (charger->battery_critical)
396342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport			charger->battery_critical();
397342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	}
398342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}
399342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
400342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic int da9030_battery_event(struct notifier_block *nb, unsigned long event,
401342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport				void *data)
402342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{
403342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	struct da9030_charger *charger =
404342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		container_of(nb, struct da9030_charger, nb);
405342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
406342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	switch (event) {
407342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	case DA9030_EVENT_CHDET:
408a35d01a5d2ac533edab94a8e3b6749ab213c91c5Mike Rapoport		cancel_delayed_work_sync(&charger->work);
409a35d01a5d2ac533edab94a8e3b6749ab213c91c5Mike Rapoport		schedule_work(&charger->work.work);
410342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		break;
411342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	case DA9030_EVENT_VBATMON:
412342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		da9030_battery_vbat_event(charger);
413342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		break;
414342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	case DA9030_EVENT_CHIOVER:
415342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	case DA9030_EVENT_TBAT:
416342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		da9030_set_charge(charger, 0);
417342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		break;
418342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	}
419342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
420342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	return 0;
421342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}
422342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
423342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic void da9030_battery_convert_thresholds(struct da9030_charger *charger,
424342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport					      struct da9030_battery_info *pdata)
425342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{
426342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger->thresholds.tbat_low = pdata->tbat_low;
427342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger->thresholds.tbat_high = pdata->tbat_high;
428342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger->thresholds.tbat_restart  = pdata->tbat_restart;
429342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
430342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger->thresholds.vbat_low =
431342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		da9030_millivolt_to_reg(pdata->vbat_low);
432342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger->thresholds.vbat_crit =
433342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		da9030_millivolt_to_reg(pdata->vbat_crit);
434342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger->thresholds.vbat_charge_start =
435342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		da9030_millivolt_to_reg(pdata->vbat_charge_start);
436342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger->thresholds.vbat_charge_stop =
437342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		da9030_millivolt_to_reg(pdata->vbat_charge_stop);
438342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger->thresholds.vbat_charge_restart =
439342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		da9030_millivolt_to_reg(pdata->vbat_charge_restart);
440342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
441342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger->thresholds.vcharge_min =
442342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		da9030_millivolt_to_reg(pdata->vcharge_min);
443342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger->thresholds.vcharge_max =
444342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		da9030_millivolt_to_reg(pdata->vcharge_max);
445342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}
446342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
447342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic void da9030_battery_setup_psy(struct da9030_charger *charger)
448342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{
449342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	struct power_supply *psy = &charger->psy;
450342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	struct power_supply_info *info = charger->battery_info;
451342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
452342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	psy->name = info->name;
453342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	psy->use_for_apm = info->use_for_apm;
454342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	psy->type = POWER_SUPPLY_TYPE_BATTERY;
455342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	psy->get_property = da9030_battery_get_property;
456342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
457342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	psy->properties = da9030_battery_props;
458342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	psy->num_properties = ARRAY_SIZE(da9030_battery_props);
459342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport};
460342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
461342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic int da9030_battery_charger_init(struct da9030_charger *charger)
462342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{
463342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	char v[5];
464342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	int ret;
465342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
466342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	v[0] = v[1] = charger->thresholds.vbat_low;
467342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	v[2] = charger->thresholds.tbat_high;
468342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	v[3] = charger->thresholds.tbat_restart;
469342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	v[4] = charger->thresholds.tbat_low;
470342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
471342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	ret = da903x_writes(charger->master, DA9030_VBATMON, 5, v);
472342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	if (ret)
473342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		return ret;
474342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
475342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	/*
476342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	 * Enable reference voltage supply for ADC from the LDO_INTERNAL
477342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	 * regulator. Must be set before ADC measurements can be made.
478342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	 */
479342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	ret = da903x_write(charger->master, DA9030_ADC_MAN_CONTROL,
480342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport			   DA9030_ADC_LDO_INT_ENABLE |
481342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport			   DA9030_ADC_TBATREF_ENABLE);
482342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	if (ret)
483342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		return ret;
484342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
485342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	/* enable auto ADC measuremnts */
486342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	return da903x_write(charger->master, DA9030_ADC_AUTO_CONTROL,
487342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport			    DA9030_ADC_TBAT_ENABLE | DA9030_ADC_VBAT_IN_TXON |
488342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport			    DA9030_ADC_VCH_ENABLE | DA9030_ADC_ICH_ENABLE |
489342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport			    DA9030_ADC_VBAT_ENABLE |
490342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport			    DA9030_ADC_AUTO_SLEEP_ENABLE);
491342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}
492342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
493342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic int da9030_battery_probe(struct platform_device *pdev)
494342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{
495342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	struct da9030_charger *charger;
496342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	struct da9030_battery_info *pdata = pdev->dev.platform_data;
497342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	int ret;
498342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
499342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	if (pdata == NULL)
500342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		return -EINVAL;
501342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
502342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	if (pdata->charge_milliamp >= 1500 ||
503342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	    pdata->charge_millivolt < 4000 ||
504342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	    pdata->charge_millivolt > 4350)
505342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		return -EINVAL;
506342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
507342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger = kzalloc(sizeof(*charger), GFP_KERNEL);
508342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	if (charger == NULL)
509342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		return -ENOMEM;
510342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
511342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger->master = pdev->dev.parent;
512342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
5135324dc0e3872324ed0bf372bce7bc437436910b6Stefan Weil	/* 10 seconds between monitor runs unless platform defines other
514342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	   interval */
515342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger->interval = msecs_to_jiffies(
516342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		(pdata->batmon_interval ? : 10) * 1000);
517342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
518342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger->charge_milliamp = pdata->charge_milliamp;
519342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger->charge_millivolt = pdata->charge_millivolt;
520342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger->battery_info = pdata->battery_info;
521342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger->battery_low = pdata->battery_low;
522342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger->battery_critical = pdata->battery_critical;
523342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
524342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	da9030_battery_convert_thresholds(charger, pdata);
525342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
526342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	ret = da9030_battery_charger_init(charger);
527342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	if (ret)
528342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		goto err_charger_init;
529342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
530342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	INIT_DELAYED_WORK(&charger->work, da9030_charging_monitor);
531342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	schedule_delayed_work(&charger->work, charger->interval);
532342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
533342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger->nb.notifier_call = da9030_battery_event;
534342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	ret = da903x_register_notifier(charger->master, &charger->nb,
535342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport				       DA9030_EVENT_CHDET |
536342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport				       DA9030_EVENT_VBATMON |
537342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport				       DA9030_EVENT_CHIOVER |
538342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport				       DA9030_EVENT_TBAT);
539342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	if (ret)
540342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		goto err_notifier;
541342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
542342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	da9030_battery_setup_psy(charger);
543342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	ret = power_supply_register(&pdev->dev, &charger->psy);
544342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	if (ret)
545342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		goto err_ps_register;
546342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
547342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	charger->debug_file = da9030_bat_create_debugfs(charger);
548342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	platform_set_drvdata(pdev, charger);
549342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	return 0;
550342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
551342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoporterr_ps_register:
552342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	da903x_unregister_notifier(charger->master, &charger->nb,
553342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport				   DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON |
554342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport				   DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT);
555342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoporterr_notifier:
556342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	cancel_delayed_work(&charger->work);
557342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
558342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoporterr_charger_init:
559342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	kfree(charger);
560342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
561342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	return ret;
562342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}
563342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
564342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic int da9030_battery_remove(struct platform_device *dev)
565342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport{
566342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	struct da9030_charger *charger = platform_get_drvdata(dev);
567342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
568342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	da9030_bat_remove_debugfs(charger);
569342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
570342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	da903x_unregister_notifier(charger->master, &charger->nb,
571342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport				   DA9030_EVENT_CHDET | DA9030_EVENT_VBATMON |
572342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport				   DA9030_EVENT_CHIOVER | DA9030_EVENT_TBAT);
573a35d01a5d2ac533edab94a8e3b6749ab213c91c5Mike Rapoport	cancel_delayed_work_sync(&charger->work);
574a35d01a5d2ac533edab94a8e3b6749ab213c91c5Mike Rapoport	da9030_set_charge(charger, 0);
575342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	power_supply_unregister(&charger->psy);
576342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
577342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	kfree(charger);
578342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
579342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	return 0;
580342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport}
581342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
582342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoportstatic struct platform_driver da903x_battery_driver = {
583342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	.driver	= {
584342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		.name	= "da903x-battery",
585342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport		.owner	= THIS_MODULE,
586342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	},
587342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	.probe = da9030_battery_probe,
588342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport	.remove = da9030_battery_remove,
589342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport};
590342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
591300bac7fb85a20b2704dc3645419057992f78565Axel Linmodule_platform_driver(da903x_battery_driver);
592342d765e011f9cbe4292119a9164f76ccf0b922aMike Rapoport
593342d765e011f9cbe4292119a9164f76ccf0b922aMike RapoportMODULE_DESCRIPTION("DA9030 battery charger driver");
594342d765e011f9cbe4292119a9164f76ccf0b922aMike RapoportMODULE_AUTHOR("Mike Rapoport, CompuLab");
595342d765e011f9cbe4292119a9164f76ccf0b922aMike RapoportMODULE_LICENSE("GPL");
596