xref: /linux/drivers/power/supply/sc27xx_fuel_gauge.c (revision 195ca1703784d1fbc34b38019aedcb74f08154f1)
1*195ca170SBaolin Wang // SPDX-License-Identifier: GPL-2.0
2*195ca170SBaolin Wang // Copyright (C) 2018 Spreadtrum Communications Inc.
3*195ca170SBaolin Wang 
4*195ca170SBaolin Wang #include <linux/gpio/consumer.h>
5*195ca170SBaolin Wang #include <linux/iio/consumer.h>
6*195ca170SBaolin Wang #include <linux/interrupt.h>
7*195ca170SBaolin Wang #include <linux/kernel.h>
8*195ca170SBaolin Wang #include <linux/module.h>
9*195ca170SBaolin Wang #include <linux/of.h>
10*195ca170SBaolin Wang #include <linux/platform_device.h>
11*195ca170SBaolin Wang #include <linux/power_supply.h>
12*195ca170SBaolin Wang #include <linux/regmap.h>
13*195ca170SBaolin Wang 
14*195ca170SBaolin Wang /* PMIC global control registers definition */
15*195ca170SBaolin Wang #define SC27XX_MODULE_EN0		0xc08
16*195ca170SBaolin Wang #define SC27XX_CLK_EN0			0xc18
17*195ca170SBaolin Wang #define SC27XX_FGU_EN			BIT(7)
18*195ca170SBaolin Wang #define SC27XX_FGU_RTC_EN		BIT(6)
19*195ca170SBaolin Wang 
20*195ca170SBaolin Wang /* FGU registers definition */
21*195ca170SBaolin Wang #define SC27XX_FGU_START		0x0
22*195ca170SBaolin Wang #define SC27XX_FGU_CONFIG		0x4
23*195ca170SBaolin Wang #define SC27XX_FGU_ADC_CONFIG		0x8
24*195ca170SBaolin Wang #define SC27XX_FGU_STATUS		0xc
25*195ca170SBaolin Wang #define SC27XX_FGU_INT_EN		0x10
26*195ca170SBaolin Wang #define SC27XX_FGU_INT_CLR		0x14
27*195ca170SBaolin Wang #define SC27XX_FGU_INT_STS		0x1c
28*195ca170SBaolin Wang #define SC27XX_FGU_VOLTAGE		0x20
29*195ca170SBaolin Wang #define SC27XX_FGU_OCV			0x24
30*195ca170SBaolin Wang #define SC27XX_FGU_POCV			0x28
31*195ca170SBaolin Wang #define SC27XX_FGU_CURRENT		0x2c
32*195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_SETH		0x50
33*195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_SETL		0x54
34*195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_VALH		0x68
35*195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_VALL		0x6c
36*195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_QMAXL		0x74
37*195ca170SBaolin Wang 
38*195ca170SBaolin Wang #define SC27XX_WRITE_SELCLB_EN		BIT(0)
39*195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_MASK		GENMASK(15, 0)
40*195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_SHIFT		16
41*195ca170SBaolin Wang 
42*195ca170SBaolin Wang #define SC27XX_FGU_1000MV_ADC		686
43*195ca170SBaolin Wang #define SC27XX_FGU_1000MA_ADC		1372
44*195ca170SBaolin Wang #define SC27XX_FGU_CUR_BASIC_ADC	8192
45*195ca170SBaolin Wang #define SC27XX_FGU_SAMPLE_HZ		2
46*195ca170SBaolin Wang 
47*195ca170SBaolin Wang /*
48*195ca170SBaolin Wang  * struct sc27xx_fgu_data: describe the FGU device
49*195ca170SBaolin Wang  * @regmap: regmap for register access
50*195ca170SBaolin Wang  * @dev: platform device
51*195ca170SBaolin Wang  * @battery: battery power supply
52*195ca170SBaolin Wang  * @base: the base offset for the controller
53*195ca170SBaolin Wang  * @lock: protect the structure
54*195ca170SBaolin Wang  * @gpiod: GPIO for battery detection
55*195ca170SBaolin Wang  * @channel: IIO channel to get battery temperature
56*195ca170SBaolin Wang  * @internal_resist: the battery internal resistance in mOhm
57*195ca170SBaolin Wang  * @total_cap: the total capacity of the battery in mAh
58*195ca170SBaolin Wang  * @init_cap: the initial capacity of the battery in mAh
59*195ca170SBaolin Wang  * @init_clbcnt: the initial coulomb counter
60*195ca170SBaolin Wang  * @max_volt: the maximum constant input voltage in millivolt
61*195ca170SBaolin Wang  * @table_len: the capacity table length
62*195ca170SBaolin Wang  * @cap_table: capacity table with corresponding ocv
63*195ca170SBaolin Wang  */
64*195ca170SBaolin Wang struct sc27xx_fgu_data {
65*195ca170SBaolin Wang 	struct regmap *regmap;
66*195ca170SBaolin Wang 	struct device *dev;
67*195ca170SBaolin Wang 	struct power_supply *battery;
68*195ca170SBaolin Wang 	u32 base;
69*195ca170SBaolin Wang 	struct mutex lock;
70*195ca170SBaolin Wang 	struct gpio_desc *gpiod;
71*195ca170SBaolin Wang 	struct iio_channel *channel;
72*195ca170SBaolin Wang 	bool bat_present;
73*195ca170SBaolin Wang 	int internal_resist;
74*195ca170SBaolin Wang 	int total_cap;
75*195ca170SBaolin Wang 	int init_cap;
76*195ca170SBaolin Wang 	int init_clbcnt;
77*195ca170SBaolin Wang 	int max_volt;
78*195ca170SBaolin Wang 	int table_len;
79*195ca170SBaolin Wang 	struct power_supply_battery_ocv_table *cap_table;
80*195ca170SBaolin Wang };
81*195ca170SBaolin Wang 
82*195ca170SBaolin Wang static const char * const sc27xx_charger_supply_name[] = {
83*195ca170SBaolin Wang 	"sc2731_charger",
84*195ca170SBaolin Wang 	"sc2720_charger",
85*195ca170SBaolin Wang 	"sc2721_charger",
86*195ca170SBaolin Wang 	"sc2723_charger",
87*195ca170SBaolin Wang };
88*195ca170SBaolin Wang 
89*195ca170SBaolin Wang static int sc27xx_fgu_adc_to_current(int adc)
90*195ca170SBaolin Wang {
91*195ca170SBaolin Wang 	return DIV_ROUND_CLOSEST(adc * 1000, SC27XX_FGU_1000MA_ADC);
92*195ca170SBaolin Wang }
93*195ca170SBaolin Wang 
94*195ca170SBaolin Wang static int sc27xx_fgu_adc_to_voltage(int adc)
95*195ca170SBaolin Wang {
96*195ca170SBaolin Wang 	return DIV_ROUND_CLOSEST(adc * 1000, SC27XX_FGU_1000MV_ADC);
97*195ca170SBaolin Wang }
98*195ca170SBaolin Wang 
99*195ca170SBaolin Wang /*
100*195ca170SBaolin Wang  * When system boots on, we can not read battery capacity from coulomb
101*195ca170SBaolin Wang  * registers, since now the coulomb registers are invalid. So we should
102*195ca170SBaolin Wang  * calculate the battery open circuit voltage, and get current battery
103*195ca170SBaolin Wang  * capacity according to the capacity table.
104*195ca170SBaolin Wang  */
105*195ca170SBaolin Wang static int sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data *data, int *cap)
106*195ca170SBaolin Wang {
107*195ca170SBaolin Wang 	int volt, cur, oci, ocv, ret;
108*195ca170SBaolin Wang 
109*195ca170SBaolin Wang 	/*
110*195ca170SBaolin Wang 	 * After system booting on, the SC27XX_FGU_CLBCNT_QMAXL register saved
111*195ca170SBaolin Wang 	 * the first sampled open circuit current.
112*195ca170SBaolin Wang 	 */
113*195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_QMAXL,
114*195ca170SBaolin Wang 			  &cur);
115*195ca170SBaolin Wang 	if (ret)
116*195ca170SBaolin Wang 		return ret;
117*195ca170SBaolin Wang 
118*195ca170SBaolin Wang 	cur <<= 1;
119*195ca170SBaolin Wang 	oci = sc27xx_fgu_adc_to_current(cur - SC27XX_FGU_CUR_BASIC_ADC);
120*195ca170SBaolin Wang 
121*195ca170SBaolin Wang 	/*
122*195ca170SBaolin Wang 	 * Should get the OCV from SC27XX_FGU_POCV register at the system
123*195ca170SBaolin Wang 	 * beginning. It is ADC values reading from registers which need to
124*195ca170SBaolin Wang 	 * convert the corresponding voltage.
125*195ca170SBaolin Wang 	 */
126*195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_POCV, &volt);
127*195ca170SBaolin Wang 	if (ret)
128*195ca170SBaolin Wang 		return ret;
129*195ca170SBaolin Wang 
130*195ca170SBaolin Wang 	volt = sc27xx_fgu_adc_to_voltage(volt);
131*195ca170SBaolin Wang 	ocv = volt * 1000 - oci * data->internal_resist;
132*195ca170SBaolin Wang 
133*195ca170SBaolin Wang 	/*
134*195ca170SBaolin Wang 	 * Parse the capacity table to look up the correct capacity percent
135*195ca170SBaolin Wang 	 * according to current battery's corresponding OCV values.
136*195ca170SBaolin Wang 	 */
137*195ca170SBaolin Wang 	*cap = power_supply_ocv2cap_simple(data->cap_table, data->table_len,
138*195ca170SBaolin Wang 					   ocv);
139*195ca170SBaolin Wang 
140*195ca170SBaolin Wang 	return 0;
141*195ca170SBaolin Wang }
142*195ca170SBaolin Wang 
143*195ca170SBaolin Wang static int sc27xx_fgu_set_clbcnt(struct sc27xx_fgu_data *data, int clbcnt)
144*195ca170SBaolin Wang {
145*195ca170SBaolin Wang 	int ret;
146*195ca170SBaolin Wang 
147*195ca170SBaolin Wang 	clbcnt *= SC27XX_FGU_SAMPLE_HZ;
148*195ca170SBaolin Wang 
149*195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap,
150*195ca170SBaolin Wang 				 data->base + SC27XX_FGU_CLBCNT_SETL,
151*195ca170SBaolin Wang 				 SC27XX_FGU_CLBCNT_MASK, clbcnt);
152*195ca170SBaolin Wang 	if (ret)
153*195ca170SBaolin Wang 		return ret;
154*195ca170SBaolin Wang 
155*195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap,
156*195ca170SBaolin Wang 				 data->base + SC27XX_FGU_CLBCNT_SETH,
157*195ca170SBaolin Wang 				 SC27XX_FGU_CLBCNT_MASK,
158*195ca170SBaolin Wang 				 clbcnt >> SC27XX_FGU_CLBCNT_SHIFT);
159*195ca170SBaolin Wang 	if (ret)
160*195ca170SBaolin Wang 		return ret;
161*195ca170SBaolin Wang 
162*195ca170SBaolin Wang 	return regmap_update_bits(data->regmap, data->base + SC27XX_FGU_START,
163*195ca170SBaolin Wang 				 SC27XX_WRITE_SELCLB_EN,
164*195ca170SBaolin Wang 				 SC27XX_WRITE_SELCLB_EN);
165*195ca170SBaolin Wang }
166*195ca170SBaolin Wang 
167*195ca170SBaolin Wang static int sc27xx_fgu_get_clbcnt(struct sc27xx_fgu_data *data, int *clb_cnt)
168*195ca170SBaolin Wang {
169*195ca170SBaolin Wang 	int ccl, cch, ret;
170*195ca170SBaolin Wang 
171*195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALL,
172*195ca170SBaolin Wang 			  &ccl);
173*195ca170SBaolin Wang 	if (ret)
174*195ca170SBaolin Wang 		return ret;
175*195ca170SBaolin Wang 
176*195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALH,
177*195ca170SBaolin Wang 			  &cch);
178*195ca170SBaolin Wang 	if (ret)
179*195ca170SBaolin Wang 		return ret;
180*195ca170SBaolin Wang 
181*195ca170SBaolin Wang 	*clb_cnt = ccl & SC27XX_FGU_CLBCNT_MASK;
182*195ca170SBaolin Wang 	*clb_cnt |= (cch & SC27XX_FGU_CLBCNT_MASK) << SC27XX_FGU_CLBCNT_SHIFT;
183*195ca170SBaolin Wang 	*clb_cnt /= SC27XX_FGU_SAMPLE_HZ;
184*195ca170SBaolin Wang 
185*195ca170SBaolin Wang 	return 0;
186*195ca170SBaolin Wang }
187*195ca170SBaolin Wang 
188*195ca170SBaolin Wang static int sc27xx_fgu_get_capacity(struct sc27xx_fgu_data *data, int *cap)
189*195ca170SBaolin Wang {
190*195ca170SBaolin Wang 	int ret, cur_clbcnt, delta_clbcnt, delta_cap, temp;
191*195ca170SBaolin Wang 
192*195ca170SBaolin Wang 	/* Get current coulomb counters firstly */
193*195ca170SBaolin Wang 	ret = sc27xx_fgu_get_clbcnt(data, &cur_clbcnt);
194*195ca170SBaolin Wang 	if (ret)
195*195ca170SBaolin Wang 		return ret;
196*195ca170SBaolin Wang 
197*195ca170SBaolin Wang 	delta_clbcnt = cur_clbcnt - data->init_clbcnt;
198*195ca170SBaolin Wang 
199*195ca170SBaolin Wang 	/*
200*195ca170SBaolin Wang 	 * Convert coulomb counter to delta capacity (mAh), and set multiplier
201*195ca170SBaolin Wang 	 * as 100 to improve the precision.
202*195ca170SBaolin Wang 	 */
203*195ca170SBaolin Wang 	temp = DIV_ROUND_CLOSEST(delta_clbcnt, 360);
204*195ca170SBaolin Wang 	temp = sc27xx_fgu_adc_to_current(temp);
205*195ca170SBaolin Wang 
206*195ca170SBaolin Wang 	/*
207*195ca170SBaolin Wang 	 * Convert to capacity percent of the battery total capacity,
208*195ca170SBaolin Wang 	 * and multiplier is 100 too.
209*195ca170SBaolin Wang 	 */
210*195ca170SBaolin Wang 	delta_cap = DIV_ROUND_CLOSEST(temp * 100, data->total_cap);
211*195ca170SBaolin Wang 	*cap = delta_cap + data->init_cap;
212*195ca170SBaolin Wang 
213*195ca170SBaolin Wang 	return 0;
214*195ca170SBaolin Wang }
215*195ca170SBaolin Wang 
216*195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_vol(struct sc27xx_fgu_data *data, int *val)
217*195ca170SBaolin Wang {
218*195ca170SBaolin Wang 	int ret, vol;
219*195ca170SBaolin Wang 
220*195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_VOLTAGE, &vol);
221*195ca170SBaolin Wang 	if (ret)
222*195ca170SBaolin Wang 		return ret;
223*195ca170SBaolin Wang 
224*195ca170SBaolin Wang 	/*
225*195ca170SBaolin Wang 	 * It is ADC values reading from registers which need to convert to
226*195ca170SBaolin Wang 	 * corresponding voltage values.
227*195ca170SBaolin Wang 	 */
228*195ca170SBaolin Wang 	*val = sc27xx_fgu_adc_to_voltage(vol);
229*195ca170SBaolin Wang 
230*195ca170SBaolin Wang 	return 0;
231*195ca170SBaolin Wang }
232*195ca170SBaolin Wang 
233*195ca170SBaolin Wang static int sc27xx_fgu_get_current(struct sc27xx_fgu_data *data, int *val)
234*195ca170SBaolin Wang {
235*195ca170SBaolin Wang 	int ret, cur;
236*195ca170SBaolin Wang 
237*195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CURRENT, &cur);
238*195ca170SBaolin Wang 	if (ret)
239*195ca170SBaolin Wang 		return ret;
240*195ca170SBaolin Wang 
241*195ca170SBaolin Wang 	/*
242*195ca170SBaolin Wang 	 * It is ADC values reading from registers which need to convert to
243*195ca170SBaolin Wang 	 * corresponding current values.
244*195ca170SBaolin Wang 	 */
245*195ca170SBaolin Wang 	*val = sc27xx_fgu_adc_to_current(cur - SC27XX_FGU_CUR_BASIC_ADC);
246*195ca170SBaolin Wang 
247*195ca170SBaolin Wang 	return 0;
248*195ca170SBaolin Wang }
249*195ca170SBaolin Wang 
250*195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val)
251*195ca170SBaolin Wang {
252*195ca170SBaolin Wang 	int vol, cur, ret;
253*195ca170SBaolin Wang 
254*195ca170SBaolin Wang 	ret = sc27xx_fgu_get_vbat_vol(data, &vol);
255*195ca170SBaolin Wang 	if (ret)
256*195ca170SBaolin Wang 		return ret;
257*195ca170SBaolin Wang 
258*195ca170SBaolin Wang 	ret = sc27xx_fgu_get_current(data, &cur);
259*195ca170SBaolin Wang 	if (ret)
260*195ca170SBaolin Wang 		return ret;
261*195ca170SBaolin Wang 
262*195ca170SBaolin Wang 	/* Return the battery OCV in micro volts. */
263*195ca170SBaolin Wang 	*val = vol * 1000 - cur * data->internal_resist;
264*195ca170SBaolin Wang 
265*195ca170SBaolin Wang 	return 0;
266*195ca170SBaolin Wang }
267*195ca170SBaolin Wang 
268*195ca170SBaolin Wang static int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp)
269*195ca170SBaolin Wang {
270*195ca170SBaolin Wang 	return iio_read_channel_processed(data->channel, temp);
271*195ca170SBaolin Wang }
272*195ca170SBaolin Wang 
273*195ca170SBaolin Wang static int sc27xx_fgu_get_health(struct sc27xx_fgu_data *data, int *health)
274*195ca170SBaolin Wang {
275*195ca170SBaolin Wang 	int ret, vol;
276*195ca170SBaolin Wang 
277*195ca170SBaolin Wang 	ret = sc27xx_fgu_get_vbat_vol(data, &vol);
278*195ca170SBaolin Wang 	if (ret)
279*195ca170SBaolin Wang 		return ret;
280*195ca170SBaolin Wang 
281*195ca170SBaolin Wang 	if (vol > data->max_volt)
282*195ca170SBaolin Wang 		*health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
283*195ca170SBaolin Wang 	else
284*195ca170SBaolin Wang 		*health = POWER_SUPPLY_HEALTH_GOOD;
285*195ca170SBaolin Wang 
286*195ca170SBaolin Wang 	return 0;
287*195ca170SBaolin Wang }
288*195ca170SBaolin Wang 
289*195ca170SBaolin Wang static int sc27xx_fgu_get_status(struct sc27xx_fgu_data *data, int *status)
290*195ca170SBaolin Wang {
291*195ca170SBaolin Wang 	union power_supply_propval val;
292*195ca170SBaolin Wang 	struct power_supply *psy;
293*195ca170SBaolin Wang 	int i, ret = -EINVAL;
294*195ca170SBaolin Wang 
295*195ca170SBaolin Wang 	for (i = 0; i < ARRAY_SIZE(sc27xx_charger_supply_name); i++) {
296*195ca170SBaolin Wang 		psy = power_supply_get_by_name(sc27xx_charger_supply_name[i]);
297*195ca170SBaolin Wang 		if (!psy)
298*195ca170SBaolin Wang 			continue;
299*195ca170SBaolin Wang 
300*195ca170SBaolin Wang 		ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS,
301*195ca170SBaolin Wang 						&val);
302*195ca170SBaolin Wang 		power_supply_put(psy);
303*195ca170SBaolin Wang 		if (ret)
304*195ca170SBaolin Wang 			return ret;
305*195ca170SBaolin Wang 
306*195ca170SBaolin Wang 		*status = val.intval;
307*195ca170SBaolin Wang 	}
308*195ca170SBaolin Wang 
309*195ca170SBaolin Wang 	return ret;
310*195ca170SBaolin Wang }
311*195ca170SBaolin Wang 
312*195ca170SBaolin Wang static int sc27xx_fgu_get_property(struct power_supply *psy,
313*195ca170SBaolin Wang 				   enum power_supply_property psp,
314*195ca170SBaolin Wang 				   union power_supply_propval *val)
315*195ca170SBaolin Wang {
316*195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy);
317*195ca170SBaolin Wang 	int ret = 0;
318*195ca170SBaolin Wang 	int value;
319*195ca170SBaolin Wang 
320*195ca170SBaolin Wang 	mutex_lock(&data->lock);
321*195ca170SBaolin Wang 
322*195ca170SBaolin Wang 	switch (psp) {
323*195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_STATUS:
324*195ca170SBaolin Wang 		ret = sc27xx_fgu_get_status(data, &value);
325*195ca170SBaolin Wang 		if (ret)
326*195ca170SBaolin Wang 			goto error;
327*195ca170SBaolin Wang 
328*195ca170SBaolin Wang 		val->intval = value;
329*195ca170SBaolin Wang 		break;
330*195ca170SBaolin Wang 
331*195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_HEALTH:
332*195ca170SBaolin Wang 		ret = sc27xx_fgu_get_health(data, &value);
333*195ca170SBaolin Wang 		if (ret)
334*195ca170SBaolin Wang 			goto error;
335*195ca170SBaolin Wang 
336*195ca170SBaolin Wang 		val->intval = value;
337*195ca170SBaolin Wang 		break;
338*195ca170SBaolin Wang 
339*195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_PRESENT:
340*195ca170SBaolin Wang 		val->intval = data->bat_present;
341*195ca170SBaolin Wang 		break;
342*195ca170SBaolin Wang 
343*195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_TEMP:
344*195ca170SBaolin Wang 		ret = sc27xx_fgu_get_temp(data, &value);
345*195ca170SBaolin Wang 		if (ret)
346*195ca170SBaolin Wang 			goto error;
347*195ca170SBaolin Wang 
348*195ca170SBaolin Wang 		val->intval = value;
349*195ca170SBaolin Wang 		break;
350*195ca170SBaolin Wang 
351*195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_TECHNOLOGY:
352*195ca170SBaolin Wang 		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
353*195ca170SBaolin Wang 		break;
354*195ca170SBaolin Wang 
355*195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_CAPACITY:
356*195ca170SBaolin Wang 		ret = sc27xx_fgu_get_capacity(data, &value);
357*195ca170SBaolin Wang 		if (ret)
358*195ca170SBaolin Wang 			goto error;
359*195ca170SBaolin Wang 
360*195ca170SBaolin Wang 		val->intval = value;
361*195ca170SBaolin Wang 		break;
362*195ca170SBaolin Wang 
363*195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
364*195ca170SBaolin Wang 		ret = sc27xx_fgu_get_vbat_vol(data, &value);
365*195ca170SBaolin Wang 		if (ret)
366*195ca170SBaolin Wang 			goto error;
367*195ca170SBaolin Wang 
368*195ca170SBaolin Wang 		val->intval = value * 1000;
369*195ca170SBaolin Wang 		break;
370*195ca170SBaolin Wang 
371*195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_VOLTAGE_OCV:
372*195ca170SBaolin Wang 		ret = sc27xx_fgu_get_vbat_ocv(data, &value);
373*195ca170SBaolin Wang 		if (ret)
374*195ca170SBaolin Wang 			goto error;
375*195ca170SBaolin Wang 
376*195ca170SBaolin Wang 		val->intval = value;
377*195ca170SBaolin Wang 		break;
378*195ca170SBaolin Wang 
379*195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_CURRENT_NOW:
380*195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_CURRENT_AVG:
381*195ca170SBaolin Wang 		ret = sc27xx_fgu_get_current(data, &value);
382*195ca170SBaolin Wang 		if (ret)
383*195ca170SBaolin Wang 			goto error;
384*195ca170SBaolin Wang 
385*195ca170SBaolin Wang 		val->intval = value * 1000;
386*195ca170SBaolin Wang 		break;
387*195ca170SBaolin Wang 
388*195ca170SBaolin Wang 	default:
389*195ca170SBaolin Wang 		ret = -EINVAL;
390*195ca170SBaolin Wang 		break;
391*195ca170SBaolin Wang 	}
392*195ca170SBaolin Wang 
393*195ca170SBaolin Wang error:
394*195ca170SBaolin Wang 	mutex_unlock(&data->lock);
395*195ca170SBaolin Wang 	return ret;
396*195ca170SBaolin Wang }
397*195ca170SBaolin Wang 
398*195ca170SBaolin Wang static void sc27xx_fgu_external_power_changed(struct power_supply *psy)
399*195ca170SBaolin Wang {
400*195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy);
401*195ca170SBaolin Wang 
402*195ca170SBaolin Wang 	power_supply_changed(data->battery);
403*195ca170SBaolin Wang }
404*195ca170SBaolin Wang 
405*195ca170SBaolin Wang static enum power_supply_property sc27xx_fgu_props[] = {
406*195ca170SBaolin Wang 	POWER_SUPPLY_PROP_STATUS,
407*195ca170SBaolin Wang 	POWER_SUPPLY_PROP_HEALTH,
408*195ca170SBaolin Wang 	POWER_SUPPLY_PROP_PRESENT,
409*195ca170SBaolin Wang 	POWER_SUPPLY_PROP_TEMP,
410*195ca170SBaolin Wang 	POWER_SUPPLY_PROP_TECHNOLOGY,
411*195ca170SBaolin Wang 	POWER_SUPPLY_PROP_CAPACITY,
412*195ca170SBaolin Wang 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
413*195ca170SBaolin Wang 	POWER_SUPPLY_PROP_VOLTAGE_OCV,
414*195ca170SBaolin Wang 	POWER_SUPPLY_PROP_CURRENT_NOW,
415*195ca170SBaolin Wang 	POWER_SUPPLY_PROP_CURRENT_AVG,
416*195ca170SBaolin Wang };
417*195ca170SBaolin Wang 
418*195ca170SBaolin Wang static const struct power_supply_desc sc27xx_fgu_desc = {
419*195ca170SBaolin Wang 	.name			= "sc27xx-fgu",
420*195ca170SBaolin Wang 	.type			= POWER_SUPPLY_TYPE_BATTERY,
421*195ca170SBaolin Wang 	.properties		= sc27xx_fgu_props,
422*195ca170SBaolin Wang 	.num_properties		= ARRAY_SIZE(sc27xx_fgu_props),
423*195ca170SBaolin Wang 	.get_property		= sc27xx_fgu_get_property,
424*195ca170SBaolin Wang 	.external_power_changed	= sc27xx_fgu_external_power_changed,
425*195ca170SBaolin Wang };
426*195ca170SBaolin Wang 
427*195ca170SBaolin Wang static irqreturn_t sc27xx_fgu_bat_detection(int irq, void *dev_id)
428*195ca170SBaolin Wang {
429*195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = dev_id;
430*195ca170SBaolin Wang 	int state;
431*195ca170SBaolin Wang 
432*195ca170SBaolin Wang 	mutex_lock(&data->lock);
433*195ca170SBaolin Wang 
434*195ca170SBaolin Wang 	state = gpiod_get_value_cansleep(data->gpiod);
435*195ca170SBaolin Wang 	if (state < 0) {
436*195ca170SBaolin Wang 		dev_err(data->dev, "failed to get gpio state\n");
437*195ca170SBaolin Wang 		mutex_unlock(&data->lock);
438*195ca170SBaolin Wang 		return IRQ_RETVAL(state);
439*195ca170SBaolin Wang 	}
440*195ca170SBaolin Wang 
441*195ca170SBaolin Wang 	data->bat_present = !!state;
442*195ca170SBaolin Wang 
443*195ca170SBaolin Wang 	mutex_unlock(&data->lock);
444*195ca170SBaolin Wang 
445*195ca170SBaolin Wang 	power_supply_changed(data->battery);
446*195ca170SBaolin Wang 	return IRQ_HANDLED;
447*195ca170SBaolin Wang }
448*195ca170SBaolin Wang 
449*195ca170SBaolin Wang static void sc27xx_fgu_disable(void *_data)
450*195ca170SBaolin Wang {
451*195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = _data;
452*195ca170SBaolin Wang 
453*195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0);
454*195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0);
455*195ca170SBaolin Wang }
456*195ca170SBaolin Wang 
457*195ca170SBaolin Wang static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity)
458*195ca170SBaolin Wang {
459*195ca170SBaolin Wang 	/*
460*195ca170SBaolin Wang 	 * Get current capacity (mAh) = battery total capacity (mAh) *
461*195ca170SBaolin Wang 	 * current capacity percent (capacity / 100).
462*195ca170SBaolin Wang 	 */
463*195ca170SBaolin Wang 	int cur_cap = DIV_ROUND_CLOSEST(data->total_cap * capacity, 100);
464*195ca170SBaolin Wang 
465*195ca170SBaolin Wang 	/*
466*195ca170SBaolin Wang 	 * Convert current capacity (mAh) to coulomb counter according to the
467*195ca170SBaolin Wang 	 * formula: 1 mAh =3.6 coulomb.
468*195ca170SBaolin Wang 	 */
469*195ca170SBaolin Wang 	return DIV_ROUND_CLOSEST(cur_cap * 36, 10);
470*195ca170SBaolin Wang }
471*195ca170SBaolin Wang 
472*195ca170SBaolin Wang static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data)
473*195ca170SBaolin Wang {
474*195ca170SBaolin Wang 	struct power_supply_battery_info info = { };
475*195ca170SBaolin Wang 	struct power_supply_battery_ocv_table *table;
476*195ca170SBaolin Wang 	int ret;
477*195ca170SBaolin Wang 
478*195ca170SBaolin Wang 	ret = power_supply_get_battery_info(data->battery, &info);
479*195ca170SBaolin Wang 	if (ret) {
480*195ca170SBaolin Wang 		dev_err(data->dev, "failed to get battery information\n");
481*195ca170SBaolin Wang 		return ret;
482*195ca170SBaolin Wang 	}
483*195ca170SBaolin Wang 
484*195ca170SBaolin Wang 	data->total_cap = info.charge_full_design_uah / 1000;
485*195ca170SBaolin Wang 	data->max_volt = info.constant_charge_voltage_max_uv / 1000;
486*195ca170SBaolin Wang 	data->internal_resist = info.factory_internal_resistance_uohm / 1000;
487*195ca170SBaolin Wang 
488*195ca170SBaolin Wang 	/*
489*195ca170SBaolin Wang 	 * For SC27XX fuel gauge device, we only use one ocv-capacity
490*195ca170SBaolin Wang 	 * table in normal temperature 20 Celsius.
491*195ca170SBaolin Wang 	 */
492*195ca170SBaolin Wang 	table = power_supply_find_ocv2cap_table(&info, 20, &data->table_len);
493*195ca170SBaolin Wang 	if (!table)
494*195ca170SBaolin Wang 		return -EINVAL;
495*195ca170SBaolin Wang 
496*195ca170SBaolin Wang 	data->cap_table = devm_kmemdup(data->dev, table,
497*195ca170SBaolin Wang 				       data->table_len * sizeof(*table),
498*195ca170SBaolin Wang 				       GFP_KERNEL);
499*195ca170SBaolin Wang 	if (!data->cap_table) {
500*195ca170SBaolin Wang 		power_supply_put_battery_info(data->battery, &info);
501*195ca170SBaolin Wang 		return -ENOMEM;
502*195ca170SBaolin Wang 	}
503*195ca170SBaolin Wang 
504*195ca170SBaolin Wang 	power_supply_put_battery_info(data->battery, &info);
505*195ca170SBaolin Wang 
506*195ca170SBaolin Wang 	/* Enable the FGU module */
507*195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap, SC27XX_MODULE_EN0,
508*195ca170SBaolin Wang 				 SC27XX_FGU_EN, SC27XX_FGU_EN);
509*195ca170SBaolin Wang 	if (ret) {
510*195ca170SBaolin Wang 		dev_err(data->dev, "failed to enable fgu\n");
511*195ca170SBaolin Wang 		return ret;
512*195ca170SBaolin Wang 	}
513*195ca170SBaolin Wang 
514*195ca170SBaolin Wang 	/* Enable the FGU RTC clock to make it work */
515*195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap, SC27XX_CLK_EN0,
516*195ca170SBaolin Wang 				 SC27XX_FGU_RTC_EN, SC27XX_FGU_RTC_EN);
517*195ca170SBaolin Wang 	if (ret) {
518*195ca170SBaolin Wang 		dev_err(data->dev, "failed to enable fgu RTC clock\n");
519*195ca170SBaolin Wang 		goto disable_fgu;
520*195ca170SBaolin Wang 	}
521*195ca170SBaolin Wang 
522*195ca170SBaolin Wang 	/*
523*195ca170SBaolin Wang 	 * Get the boot battery capacity when system powers on, which is used to
524*195ca170SBaolin Wang 	 * initialize the coulomb counter. After that, we can read the coulomb
525*195ca170SBaolin Wang 	 * counter to measure the battery capacity.
526*195ca170SBaolin Wang 	 */
527*195ca170SBaolin Wang 	ret = sc27xx_fgu_get_boot_capacity(data, &data->init_cap);
528*195ca170SBaolin Wang 	if (ret) {
529*195ca170SBaolin Wang 		dev_err(data->dev, "failed to get boot capacity\n");
530*195ca170SBaolin Wang 		goto disable_clk;
531*195ca170SBaolin Wang 	}
532*195ca170SBaolin Wang 
533*195ca170SBaolin Wang 	/*
534*195ca170SBaolin Wang 	 * Convert battery capacity to the corresponding initial coulomb counter
535*195ca170SBaolin Wang 	 * and set into coulomb counter registers.
536*195ca170SBaolin Wang 	 */
537*195ca170SBaolin Wang 	data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap);
538*195ca170SBaolin Wang 	ret = sc27xx_fgu_set_clbcnt(data, data->init_clbcnt);
539*195ca170SBaolin Wang 	if (ret) {
540*195ca170SBaolin Wang 		dev_err(data->dev, "failed to initialize coulomb counter\n");
541*195ca170SBaolin Wang 		goto disable_clk;
542*195ca170SBaolin Wang 	}
543*195ca170SBaolin Wang 
544*195ca170SBaolin Wang 	return 0;
545*195ca170SBaolin Wang 
546*195ca170SBaolin Wang disable_clk:
547*195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0);
548*195ca170SBaolin Wang disable_fgu:
549*195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0);
550*195ca170SBaolin Wang 
551*195ca170SBaolin Wang 	return ret;
552*195ca170SBaolin Wang }
553*195ca170SBaolin Wang 
554*195ca170SBaolin Wang static int sc27xx_fgu_probe(struct platform_device *pdev)
555*195ca170SBaolin Wang {
556*195ca170SBaolin Wang 	struct device_node *np = pdev->dev.of_node;
557*195ca170SBaolin Wang 	struct power_supply_config fgu_cfg = { };
558*195ca170SBaolin Wang 	struct sc27xx_fgu_data *data;
559*195ca170SBaolin Wang 	int ret, irq;
560*195ca170SBaolin Wang 
561*195ca170SBaolin Wang 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
562*195ca170SBaolin Wang 	if (!data)
563*195ca170SBaolin Wang 		return -ENOMEM;
564*195ca170SBaolin Wang 
565*195ca170SBaolin Wang 	data->regmap = dev_get_regmap(pdev->dev.parent, NULL);
566*195ca170SBaolin Wang 	if (!data->regmap) {
567*195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to get regmap\n");
568*195ca170SBaolin Wang 		return -ENODEV;
569*195ca170SBaolin Wang 	}
570*195ca170SBaolin Wang 
571*195ca170SBaolin Wang 	ret = device_property_read_u32(&pdev->dev, "reg", &data->base);
572*195ca170SBaolin Wang 	if (ret) {
573*195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to get fgu address\n");
574*195ca170SBaolin Wang 		return ret;
575*195ca170SBaolin Wang 	}
576*195ca170SBaolin Wang 
577*195ca170SBaolin Wang 	data->channel = devm_iio_channel_get(&pdev->dev, "bat-temp");
578*195ca170SBaolin Wang 	if (IS_ERR(data->channel)) {
579*195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to get IIO channel\n");
580*195ca170SBaolin Wang 		return PTR_ERR(data->channel);
581*195ca170SBaolin Wang 	}
582*195ca170SBaolin Wang 
583*195ca170SBaolin Wang 	data->gpiod = devm_gpiod_get(&pdev->dev, "bat-detect", GPIOD_IN);
584*195ca170SBaolin Wang 	if (IS_ERR(data->gpiod)) {
585*195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to get battery detection GPIO\n");
586*195ca170SBaolin Wang 		return PTR_ERR(data->gpiod);
587*195ca170SBaolin Wang 	}
588*195ca170SBaolin Wang 
589*195ca170SBaolin Wang 	ret = gpiod_get_value_cansleep(data->gpiod);
590*195ca170SBaolin Wang 	if (ret < 0) {
591*195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to get gpio state\n");
592*195ca170SBaolin Wang 		return ret;
593*195ca170SBaolin Wang 	}
594*195ca170SBaolin Wang 
595*195ca170SBaolin Wang 	data->bat_present = !!ret;
596*195ca170SBaolin Wang 	mutex_init(&data->lock);
597*195ca170SBaolin Wang 	data->dev = &pdev->dev;
598*195ca170SBaolin Wang 
599*195ca170SBaolin Wang 	fgu_cfg.drv_data = data;
600*195ca170SBaolin Wang 	fgu_cfg.of_node = np;
601*195ca170SBaolin Wang 	data->battery = devm_power_supply_register(&pdev->dev, &sc27xx_fgu_desc,
602*195ca170SBaolin Wang 						   &fgu_cfg);
603*195ca170SBaolin Wang 	if (IS_ERR(data->battery)) {
604*195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to register power supply\n");
605*195ca170SBaolin Wang 		return PTR_ERR(data->battery);
606*195ca170SBaolin Wang 	}
607*195ca170SBaolin Wang 
608*195ca170SBaolin Wang 	ret = sc27xx_fgu_hw_init(data);
609*195ca170SBaolin Wang 	if (ret) {
610*195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to initialize fgu hardware\n");
611*195ca170SBaolin Wang 		return ret;
612*195ca170SBaolin Wang 	}
613*195ca170SBaolin Wang 
614*195ca170SBaolin Wang 	ret = devm_add_action(&pdev->dev, sc27xx_fgu_disable, data);
615*195ca170SBaolin Wang 	if (ret) {
616*195ca170SBaolin Wang 		sc27xx_fgu_disable(data);
617*195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to add fgu disable action\n");
618*195ca170SBaolin Wang 		return ret;
619*195ca170SBaolin Wang 	}
620*195ca170SBaolin Wang 
621*195ca170SBaolin Wang 	irq = gpiod_to_irq(data->gpiod);
622*195ca170SBaolin Wang 	if (irq < 0) {
623*195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to translate GPIO to IRQ\n");
624*195ca170SBaolin Wang 		return irq;
625*195ca170SBaolin Wang 	}
626*195ca170SBaolin Wang 
627*195ca170SBaolin Wang 	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
628*195ca170SBaolin Wang 					sc27xx_fgu_bat_detection,
629*195ca170SBaolin Wang 					IRQF_ONESHOT | IRQF_TRIGGER_RISING |
630*195ca170SBaolin Wang 					IRQF_TRIGGER_FALLING,
631*195ca170SBaolin Wang 					pdev->name, data);
632*195ca170SBaolin Wang 	if (ret) {
633*195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to request IRQ\n");
634*195ca170SBaolin Wang 		return ret;
635*195ca170SBaolin Wang 	}
636*195ca170SBaolin Wang 
637*195ca170SBaolin Wang 	return 0;
638*195ca170SBaolin Wang }
639*195ca170SBaolin Wang 
640*195ca170SBaolin Wang static const struct of_device_id sc27xx_fgu_of_match[] = {
641*195ca170SBaolin Wang 	{ .compatible = "sprd,sc2731-fgu", },
642*195ca170SBaolin Wang 	{ }
643*195ca170SBaolin Wang };
644*195ca170SBaolin Wang 
645*195ca170SBaolin Wang static struct platform_driver sc27xx_fgu_driver = {
646*195ca170SBaolin Wang 	.probe = sc27xx_fgu_probe,
647*195ca170SBaolin Wang 	.driver = {
648*195ca170SBaolin Wang 		.name = "sc27xx-fgu",
649*195ca170SBaolin Wang 		.of_match_table = sc27xx_fgu_of_match,
650*195ca170SBaolin Wang 	}
651*195ca170SBaolin Wang };
652*195ca170SBaolin Wang 
653*195ca170SBaolin Wang module_platform_driver(sc27xx_fgu_driver);
654*195ca170SBaolin Wang 
655*195ca170SBaolin Wang MODULE_DESCRIPTION("Spreadtrum SC27XX PMICs Fual Gauge Unit Driver");
656*195ca170SBaolin Wang MODULE_LICENSE("GPL v2");
657