xref: /linux/drivers/power/supply/sc27xx_fuel_gauge.c (revision 65c9fab7bc4b3a103bcac248e148af4a2efc17fe)
1195ca170SBaolin Wang // SPDX-License-Identifier: GPL-2.0
2195ca170SBaolin Wang // Copyright (C) 2018 Spreadtrum Communications Inc.
3195ca170SBaolin Wang 
4195ca170SBaolin Wang #include <linux/gpio/consumer.h>
5195ca170SBaolin Wang #include <linux/iio/consumer.h>
6195ca170SBaolin Wang #include <linux/interrupt.h>
7195ca170SBaolin Wang #include <linux/kernel.h>
8195ca170SBaolin Wang #include <linux/module.h>
9*65c9fab7SBaolin Wang #include <linux/nvmem-consumer.h>
10195ca170SBaolin Wang #include <linux/of.h>
11195ca170SBaolin Wang #include <linux/platform_device.h>
12195ca170SBaolin Wang #include <linux/power_supply.h>
13195ca170SBaolin Wang #include <linux/regmap.h>
14*65c9fab7SBaolin Wang #include <linux/slab.h>
15195ca170SBaolin Wang 
16195ca170SBaolin Wang /* PMIC global control registers definition */
17195ca170SBaolin Wang #define SC27XX_MODULE_EN0		0xc08
18195ca170SBaolin Wang #define SC27XX_CLK_EN0			0xc18
19195ca170SBaolin Wang #define SC27XX_FGU_EN			BIT(7)
20195ca170SBaolin Wang #define SC27XX_FGU_RTC_EN		BIT(6)
21195ca170SBaolin Wang 
22195ca170SBaolin Wang /* FGU registers definition */
23195ca170SBaolin Wang #define SC27XX_FGU_START		0x0
24195ca170SBaolin Wang #define SC27XX_FGU_CONFIG		0x4
25195ca170SBaolin Wang #define SC27XX_FGU_ADC_CONFIG		0x8
26195ca170SBaolin Wang #define SC27XX_FGU_STATUS		0xc
27195ca170SBaolin Wang #define SC27XX_FGU_INT_EN		0x10
28195ca170SBaolin Wang #define SC27XX_FGU_INT_CLR		0x14
29195ca170SBaolin Wang #define SC27XX_FGU_INT_STS		0x1c
30195ca170SBaolin Wang #define SC27XX_FGU_VOLTAGE		0x20
31195ca170SBaolin Wang #define SC27XX_FGU_OCV			0x24
32195ca170SBaolin Wang #define SC27XX_FGU_POCV			0x28
33195ca170SBaolin Wang #define SC27XX_FGU_CURRENT		0x2c
34195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_SETH		0x50
35195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_SETL		0x54
36195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_VALH		0x68
37195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_VALL		0x6c
38195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_QMAXL		0x74
39195ca170SBaolin Wang 
40195ca170SBaolin Wang #define SC27XX_WRITE_SELCLB_EN		BIT(0)
41195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_MASK		GENMASK(15, 0)
42195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_SHIFT		16
43195ca170SBaolin Wang 
44195ca170SBaolin Wang #define SC27XX_FGU_CUR_BASIC_ADC	8192
45195ca170SBaolin Wang #define SC27XX_FGU_SAMPLE_HZ		2
46195ca170SBaolin Wang 
47195ca170SBaolin Wang /*
48195ca170SBaolin Wang  * struct sc27xx_fgu_data: describe the FGU device
49195ca170SBaolin Wang  * @regmap: regmap for register access
50195ca170SBaolin Wang  * @dev: platform device
51195ca170SBaolin Wang  * @battery: battery power supply
52195ca170SBaolin Wang  * @base: the base offset for the controller
53195ca170SBaolin Wang  * @lock: protect the structure
54195ca170SBaolin Wang  * @gpiod: GPIO for battery detection
55195ca170SBaolin Wang  * @channel: IIO channel to get battery temperature
56195ca170SBaolin Wang  * @internal_resist: the battery internal resistance in mOhm
57195ca170SBaolin Wang  * @total_cap: the total capacity of the battery in mAh
58195ca170SBaolin Wang  * @init_cap: the initial capacity of the battery in mAh
59195ca170SBaolin Wang  * @init_clbcnt: the initial coulomb counter
60195ca170SBaolin Wang  * @max_volt: the maximum constant input voltage in millivolt
61195ca170SBaolin Wang  * @table_len: the capacity table length
62*65c9fab7SBaolin Wang  * @cur_1000ma_adc: ADC value corresponding to 1000 mA
63*65c9fab7SBaolin Wang  * @vol_1000mv_adc: ADC value corresponding to 1000 mV
64195ca170SBaolin Wang  * @cap_table: capacity table with corresponding ocv
65195ca170SBaolin Wang  */
66195ca170SBaolin Wang struct sc27xx_fgu_data {
67195ca170SBaolin Wang 	struct regmap *regmap;
68195ca170SBaolin Wang 	struct device *dev;
69195ca170SBaolin Wang 	struct power_supply *battery;
70195ca170SBaolin Wang 	u32 base;
71195ca170SBaolin Wang 	struct mutex lock;
72195ca170SBaolin Wang 	struct gpio_desc *gpiod;
73195ca170SBaolin Wang 	struct iio_channel *channel;
74195ca170SBaolin Wang 	bool bat_present;
75195ca170SBaolin Wang 	int internal_resist;
76195ca170SBaolin Wang 	int total_cap;
77195ca170SBaolin Wang 	int init_cap;
78195ca170SBaolin Wang 	int init_clbcnt;
79195ca170SBaolin Wang 	int max_volt;
80195ca170SBaolin Wang 	int table_len;
81*65c9fab7SBaolin Wang 	int cur_1000ma_adc;
82*65c9fab7SBaolin Wang 	int vol_1000mv_adc;
83195ca170SBaolin Wang 	struct power_supply_battery_ocv_table *cap_table;
84195ca170SBaolin Wang };
85195ca170SBaolin Wang 
86195ca170SBaolin Wang static const char * const sc27xx_charger_supply_name[] = {
87195ca170SBaolin Wang 	"sc2731_charger",
88195ca170SBaolin Wang 	"sc2720_charger",
89195ca170SBaolin Wang 	"sc2721_charger",
90195ca170SBaolin Wang 	"sc2723_charger",
91195ca170SBaolin Wang };
92195ca170SBaolin Wang 
93*65c9fab7SBaolin Wang static int sc27xx_fgu_adc_to_current(struct sc27xx_fgu_data *data, int adc)
94195ca170SBaolin Wang {
95*65c9fab7SBaolin Wang 	return DIV_ROUND_CLOSEST(adc * 1000, data->cur_1000ma_adc);
96195ca170SBaolin Wang }
97195ca170SBaolin Wang 
98*65c9fab7SBaolin Wang static int sc27xx_fgu_adc_to_voltage(struct sc27xx_fgu_data *data, int adc)
99195ca170SBaolin Wang {
100*65c9fab7SBaolin Wang 	return DIV_ROUND_CLOSEST(adc * 1000, data->vol_1000mv_adc);
101195ca170SBaolin Wang }
102195ca170SBaolin Wang 
103195ca170SBaolin Wang /*
104195ca170SBaolin Wang  * When system boots on, we can not read battery capacity from coulomb
105195ca170SBaolin Wang  * registers, since now the coulomb registers are invalid. So we should
106195ca170SBaolin Wang  * calculate the battery open circuit voltage, and get current battery
107195ca170SBaolin Wang  * capacity according to the capacity table.
108195ca170SBaolin Wang  */
109195ca170SBaolin Wang static int sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data *data, int *cap)
110195ca170SBaolin Wang {
111195ca170SBaolin Wang 	int volt, cur, oci, ocv, ret;
112195ca170SBaolin Wang 
113195ca170SBaolin Wang 	/*
114195ca170SBaolin Wang 	 * After system booting on, the SC27XX_FGU_CLBCNT_QMAXL register saved
115195ca170SBaolin Wang 	 * the first sampled open circuit current.
116195ca170SBaolin Wang 	 */
117195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_QMAXL,
118195ca170SBaolin Wang 			  &cur);
119195ca170SBaolin Wang 	if (ret)
120195ca170SBaolin Wang 		return ret;
121195ca170SBaolin Wang 
122195ca170SBaolin Wang 	cur <<= 1;
123*65c9fab7SBaolin Wang 	oci = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC);
124195ca170SBaolin Wang 
125195ca170SBaolin Wang 	/*
126195ca170SBaolin Wang 	 * Should get the OCV from SC27XX_FGU_POCV register at the system
127195ca170SBaolin Wang 	 * beginning. It is ADC values reading from registers which need to
128195ca170SBaolin Wang 	 * convert the corresponding voltage.
129195ca170SBaolin Wang 	 */
130195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_POCV, &volt);
131195ca170SBaolin Wang 	if (ret)
132195ca170SBaolin Wang 		return ret;
133195ca170SBaolin Wang 
134*65c9fab7SBaolin Wang 	volt = sc27xx_fgu_adc_to_voltage(data, volt);
135195ca170SBaolin Wang 	ocv = volt * 1000 - oci * data->internal_resist;
136195ca170SBaolin Wang 
137195ca170SBaolin Wang 	/*
138195ca170SBaolin Wang 	 * Parse the capacity table to look up the correct capacity percent
139195ca170SBaolin Wang 	 * according to current battery's corresponding OCV values.
140195ca170SBaolin Wang 	 */
141195ca170SBaolin Wang 	*cap = power_supply_ocv2cap_simple(data->cap_table, data->table_len,
142195ca170SBaolin Wang 					   ocv);
143195ca170SBaolin Wang 
144195ca170SBaolin Wang 	return 0;
145195ca170SBaolin Wang }
146195ca170SBaolin Wang 
147195ca170SBaolin Wang static int sc27xx_fgu_set_clbcnt(struct sc27xx_fgu_data *data, int clbcnt)
148195ca170SBaolin Wang {
149195ca170SBaolin Wang 	int ret;
150195ca170SBaolin Wang 
151195ca170SBaolin Wang 	clbcnt *= SC27XX_FGU_SAMPLE_HZ;
152195ca170SBaolin Wang 
153195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap,
154195ca170SBaolin Wang 				 data->base + SC27XX_FGU_CLBCNT_SETL,
155195ca170SBaolin Wang 				 SC27XX_FGU_CLBCNT_MASK, clbcnt);
156195ca170SBaolin Wang 	if (ret)
157195ca170SBaolin Wang 		return ret;
158195ca170SBaolin Wang 
159195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap,
160195ca170SBaolin Wang 				 data->base + SC27XX_FGU_CLBCNT_SETH,
161195ca170SBaolin Wang 				 SC27XX_FGU_CLBCNT_MASK,
162195ca170SBaolin Wang 				 clbcnt >> SC27XX_FGU_CLBCNT_SHIFT);
163195ca170SBaolin Wang 	if (ret)
164195ca170SBaolin Wang 		return ret;
165195ca170SBaolin Wang 
166195ca170SBaolin Wang 	return regmap_update_bits(data->regmap, data->base + SC27XX_FGU_START,
167195ca170SBaolin Wang 				 SC27XX_WRITE_SELCLB_EN,
168195ca170SBaolin Wang 				 SC27XX_WRITE_SELCLB_EN);
169195ca170SBaolin Wang }
170195ca170SBaolin Wang 
171195ca170SBaolin Wang static int sc27xx_fgu_get_clbcnt(struct sc27xx_fgu_data *data, int *clb_cnt)
172195ca170SBaolin Wang {
173195ca170SBaolin Wang 	int ccl, cch, ret;
174195ca170SBaolin Wang 
175195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALL,
176195ca170SBaolin Wang 			  &ccl);
177195ca170SBaolin Wang 	if (ret)
178195ca170SBaolin Wang 		return ret;
179195ca170SBaolin Wang 
180195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALH,
181195ca170SBaolin Wang 			  &cch);
182195ca170SBaolin Wang 	if (ret)
183195ca170SBaolin Wang 		return ret;
184195ca170SBaolin Wang 
185195ca170SBaolin Wang 	*clb_cnt = ccl & SC27XX_FGU_CLBCNT_MASK;
186195ca170SBaolin Wang 	*clb_cnt |= (cch & SC27XX_FGU_CLBCNT_MASK) << SC27XX_FGU_CLBCNT_SHIFT;
187195ca170SBaolin Wang 	*clb_cnt /= SC27XX_FGU_SAMPLE_HZ;
188195ca170SBaolin Wang 
189195ca170SBaolin Wang 	return 0;
190195ca170SBaolin Wang }
191195ca170SBaolin Wang 
192195ca170SBaolin Wang static int sc27xx_fgu_get_capacity(struct sc27xx_fgu_data *data, int *cap)
193195ca170SBaolin Wang {
194195ca170SBaolin Wang 	int ret, cur_clbcnt, delta_clbcnt, delta_cap, temp;
195195ca170SBaolin Wang 
196195ca170SBaolin Wang 	/* Get current coulomb counters firstly */
197195ca170SBaolin Wang 	ret = sc27xx_fgu_get_clbcnt(data, &cur_clbcnt);
198195ca170SBaolin Wang 	if (ret)
199195ca170SBaolin Wang 		return ret;
200195ca170SBaolin Wang 
201195ca170SBaolin Wang 	delta_clbcnt = cur_clbcnt - data->init_clbcnt;
202195ca170SBaolin Wang 
203195ca170SBaolin Wang 	/*
204195ca170SBaolin Wang 	 * Convert coulomb counter to delta capacity (mAh), and set multiplier
205195ca170SBaolin Wang 	 * as 100 to improve the precision.
206195ca170SBaolin Wang 	 */
207195ca170SBaolin Wang 	temp = DIV_ROUND_CLOSEST(delta_clbcnt, 360);
208*65c9fab7SBaolin Wang 	temp = sc27xx_fgu_adc_to_current(data, temp);
209195ca170SBaolin Wang 
210195ca170SBaolin Wang 	/*
211195ca170SBaolin Wang 	 * Convert to capacity percent of the battery total capacity,
212195ca170SBaolin Wang 	 * and multiplier is 100 too.
213195ca170SBaolin Wang 	 */
214195ca170SBaolin Wang 	delta_cap = DIV_ROUND_CLOSEST(temp * 100, data->total_cap);
215195ca170SBaolin Wang 	*cap = delta_cap + data->init_cap;
216195ca170SBaolin Wang 
217195ca170SBaolin Wang 	return 0;
218195ca170SBaolin Wang }
219195ca170SBaolin Wang 
220195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_vol(struct sc27xx_fgu_data *data, int *val)
221195ca170SBaolin Wang {
222195ca170SBaolin Wang 	int ret, vol;
223195ca170SBaolin Wang 
224195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_VOLTAGE, &vol);
225195ca170SBaolin Wang 	if (ret)
226195ca170SBaolin Wang 		return ret;
227195ca170SBaolin Wang 
228195ca170SBaolin Wang 	/*
229195ca170SBaolin Wang 	 * It is ADC values reading from registers which need to convert to
230195ca170SBaolin Wang 	 * corresponding voltage values.
231195ca170SBaolin Wang 	 */
232*65c9fab7SBaolin Wang 	*val = sc27xx_fgu_adc_to_voltage(data, vol);
233195ca170SBaolin Wang 
234195ca170SBaolin Wang 	return 0;
235195ca170SBaolin Wang }
236195ca170SBaolin Wang 
237195ca170SBaolin Wang static int sc27xx_fgu_get_current(struct sc27xx_fgu_data *data, int *val)
238195ca170SBaolin Wang {
239195ca170SBaolin Wang 	int ret, cur;
240195ca170SBaolin Wang 
241195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CURRENT, &cur);
242195ca170SBaolin Wang 	if (ret)
243195ca170SBaolin Wang 		return ret;
244195ca170SBaolin Wang 
245195ca170SBaolin Wang 	/*
246195ca170SBaolin Wang 	 * It is ADC values reading from registers which need to convert to
247195ca170SBaolin Wang 	 * corresponding current values.
248195ca170SBaolin Wang 	 */
249*65c9fab7SBaolin Wang 	*val = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC);
250195ca170SBaolin Wang 
251195ca170SBaolin Wang 	return 0;
252195ca170SBaolin Wang }
253195ca170SBaolin Wang 
254195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val)
255195ca170SBaolin Wang {
256195ca170SBaolin Wang 	int vol, cur, ret;
257195ca170SBaolin Wang 
258195ca170SBaolin Wang 	ret = sc27xx_fgu_get_vbat_vol(data, &vol);
259195ca170SBaolin Wang 	if (ret)
260195ca170SBaolin Wang 		return ret;
261195ca170SBaolin Wang 
262195ca170SBaolin Wang 	ret = sc27xx_fgu_get_current(data, &cur);
263195ca170SBaolin Wang 	if (ret)
264195ca170SBaolin Wang 		return ret;
265195ca170SBaolin Wang 
266195ca170SBaolin Wang 	/* Return the battery OCV in micro volts. */
267195ca170SBaolin Wang 	*val = vol * 1000 - cur * data->internal_resist;
268195ca170SBaolin Wang 
269195ca170SBaolin Wang 	return 0;
270195ca170SBaolin Wang }
271195ca170SBaolin Wang 
272195ca170SBaolin Wang static int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp)
273195ca170SBaolin Wang {
274195ca170SBaolin Wang 	return iio_read_channel_processed(data->channel, temp);
275195ca170SBaolin Wang }
276195ca170SBaolin Wang 
277195ca170SBaolin Wang static int sc27xx_fgu_get_health(struct sc27xx_fgu_data *data, int *health)
278195ca170SBaolin Wang {
279195ca170SBaolin Wang 	int ret, vol;
280195ca170SBaolin Wang 
281195ca170SBaolin Wang 	ret = sc27xx_fgu_get_vbat_vol(data, &vol);
282195ca170SBaolin Wang 	if (ret)
283195ca170SBaolin Wang 		return ret;
284195ca170SBaolin Wang 
285195ca170SBaolin Wang 	if (vol > data->max_volt)
286195ca170SBaolin Wang 		*health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
287195ca170SBaolin Wang 	else
288195ca170SBaolin Wang 		*health = POWER_SUPPLY_HEALTH_GOOD;
289195ca170SBaolin Wang 
290195ca170SBaolin Wang 	return 0;
291195ca170SBaolin Wang }
292195ca170SBaolin Wang 
293195ca170SBaolin Wang static int sc27xx_fgu_get_status(struct sc27xx_fgu_data *data, int *status)
294195ca170SBaolin Wang {
295195ca170SBaolin Wang 	union power_supply_propval val;
296195ca170SBaolin Wang 	struct power_supply *psy;
297195ca170SBaolin Wang 	int i, ret = -EINVAL;
298195ca170SBaolin Wang 
299195ca170SBaolin Wang 	for (i = 0; i < ARRAY_SIZE(sc27xx_charger_supply_name); i++) {
300195ca170SBaolin Wang 		psy = power_supply_get_by_name(sc27xx_charger_supply_name[i]);
301195ca170SBaolin Wang 		if (!psy)
302195ca170SBaolin Wang 			continue;
303195ca170SBaolin Wang 
304195ca170SBaolin Wang 		ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS,
305195ca170SBaolin Wang 						&val);
306195ca170SBaolin Wang 		power_supply_put(psy);
307195ca170SBaolin Wang 		if (ret)
308195ca170SBaolin Wang 			return ret;
309195ca170SBaolin Wang 
310195ca170SBaolin Wang 		*status = val.intval;
311195ca170SBaolin Wang 	}
312195ca170SBaolin Wang 
313195ca170SBaolin Wang 	return ret;
314195ca170SBaolin Wang }
315195ca170SBaolin Wang 
316195ca170SBaolin Wang static int sc27xx_fgu_get_property(struct power_supply *psy,
317195ca170SBaolin Wang 				   enum power_supply_property psp,
318195ca170SBaolin Wang 				   union power_supply_propval *val)
319195ca170SBaolin Wang {
320195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy);
321195ca170SBaolin Wang 	int ret = 0;
322195ca170SBaolin Wang 	int value;
323195ca170SBaolin Wang 
324195ca170SBaolin Wang 	mutex_lock(&data->lock);
325195ca170SBaolin Wang 
326195ca170SBaolin Wang 	switch (psp) {
327195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_STATUS:
328195ca170SBaolin Wang 		ret = sc27xx_fgu_get_status(data, &value);
329195ca170SBaolin Wang 		if (ret)
330195ca170SBaolin Wang 			goto error;
331195ca170SBaolin Wang 
332195ca170SBaolin Wang 		val->intval = value;
333195ca170SBaolin Wang 		break;
334195ca170SBaolin Wang 
335195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_HEALTH:
336195ca170SBaolin Wang 		ret = sc27xx_fgu_get_health(data, &value);
337195ca170SBaolin Wang 		if (ret)
338195ca170SBaolin Wang 			goto error;
339195ca170SBaolin Wang 
340195ca170SBaolin Wang 		val->intval = value;
341195ca170SBaolin Wang 		break;
342195ca170SBaolin Wang 
343195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_PRESENT:
344195ca170SBaolin Wang 		val->intval = data->bat_present;
345195ca170SBaolin Wang 		break;
346195ca170SBaolin Wang 
347195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_TEMP:
348195ca170SBaolin Wang 		ret = sc27xx_fgu_get_temp(data, &value);
349195ca170SBaolin Wang 		if (ret)
350195ca170SBaolin Wang 			goto error;
351195ca170SBaolin Wang 
352195ca170SBaolin Wang 		val->intval = value;
353195ca170SBaolin Wang 		break;
354195ca170SBaolin Wang 
355195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_TECHNOLOGY:
356195ca170SBaolin Wang 		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
357195ca170SBaolin Wang 		break;
358195ca170SBaolin Wang 
359195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_CAPACITY:
360195ca170SBaolin Wang 		ret = sc27xx_fgu_get_capacity(data, &value);
361195ca170SBaolin Wang 		if (ret)
362195ca170SBaolin Wang 			goto error;
363195ca170SBaolin Wang 
364195ca170SBaolin Wang 		val->intval = value;
365195ca170SBaolin Wang 		break;
366195ca170SBaolin Wang 
367195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
368195ca170SBaolin Wang 		ret = sc27xx_fgu_get_vbat_vol(data, &value);
369195ca170SBaolin Wang 		if (ret)
370195ca170SBaolin Wang 			goto error;
371195ca170SBaolin Wang 
372195ca170SBaolin Wang 		val->intval = value * 1000;
373195ca170SBaolin Wang 		break;
374195ca170SBaolin Wang 
375195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_VOLTAGE_OCV:
376195ca170SBaolin Wang 		ret = sc27xx_fgu_get_vbat_ocv(data, &value);
377195ca170SBaolin Wang 		if (ret)
378195ca170SBaolin Wang 			goto error;
379195ca170SBaolin Wang 
380195ca170SBaolin Wang 		val->intval = value;
381195ca170SBaolin Wang 		break;
382195ca170SBaolin Wang 
383195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_CURRENT_NOW:
384195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_CURRENT_AVG:
385195ca170SBaolin Wang 		ret = sc27xx_fgu_get_current(data, &value);
386195ca170SBaolin Wang 		if (ret)
387195ca170SBaolin Wang 			goto error;
388195ca170SBaolin Wang 
389195ca170SBaolin Wang 		val->intval = value * 1000;
390195ca170SBaolin Wang 		break;
391195ca170SBaolin Wang 
392195ca170SBaolin Wang 	default:
393195ca170SBaolin Wang 		ret = -EINVAL;
394195ca170SBaolin Wang 		break;
395195ca170SBaolin Wang 	}
396195ca170SBaolin Wang 
397195ca170SBaolin Wang error:
398195ca170SBaolin Wang 	mutex_unlock(&data->lock);
399195ca170SBaolin Wang 	return ret;
400195ca170SBaolin Wang }
401195ca170SBaolin Wang 
402195ca170SBaolin Wang static void sc27xx_fgu_external_power_changed(struct power_supply *psy)
403195ca170SBaolin Wang {
404195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy);
405195ca170SBaolin Wang 
406195ca170SBaolin Wang 	power_supply_changed(data->battery);
407195ca170SBaolin Wang }
408195ca170SBaolin Wang 
409195ca170SBaolin Wang static enum power_supply_property sc27xx_fgu_props[] = {
410195ca170SBaolin Wang 	POWER_SUPPLY_PROP_STATUS,
411195ca170SBaolin Wang 	POWER_SUPPLY_PROP_HEALTH,
412195ca170SBaolin Wang 	POWER_SUPPLY_PROP_PRESENT,
413195ca170SBaolin Wang 	POWER_SUPPLY_PROP_TEMP,
414195ca170SBaolin Wang 	POWER_SUPPLY_PROP_TECHNOLOGY,
415195ca170SBaolin Wang 	POWER_SUPPLY_PROP_CAPACITY,
416195ca170SBaolin Wang 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
417195ca170SBaolin Wang 	POWER_SUPPLY_PROP_VOLTAGE_OCV,
418195ca170SBaolin Wang 	POWER_SUPPLY_PROP_CURRENT_NOW,
419195ca170SBaolin Wang 	POWER_SUPPLY_PROP_CURRENT_AVG,
420195ca170SBaolin Wang };
421195ca170SBaolin Wang 
422195ca170SBaolin Wang static const struct power_supply_desc sc27xx_fgu_desc = {
423195ca170SBaolin Wang 	.name			= "sc27xx-fgu",
424195ca170SBaolin Wang 	.type			= POWER_SUPPLY_TYPE_BATTERY,
425195ca170SBaolin Wang 	.properties		= sc27xx_fgu_props,
426195ca170SBaolin Wang 	.num_properties		= ARRAY_SIZE(sc27xx_fgu_props),
427195ca170SBaolin Wang 	.get_property		= sc27xx_fgu_get_property,
428195ca170SBaolin Wang 	.external_power_changed	= sc27xx_fgu_external_power_changed,
429195ca170SBaolin Wang };
430195ca170SBaolin Wang 
431195ca170SBaolin Wang static irqreturn_t sc27xx_fgu_bat_detection(int irq, void *dev_id)
432195ca170SBaolin Wang {
433195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = dev_id;
434195ca170SBaolin Wang 	int state;
435195ca170SBaolin Wang 
436195ca170SBaolin Wang 	mutex_lock(&data->lock);
437195ca170SBaolin Wang 
438195ca170SBaolin Wang 	state = gpiod_get_value_cansleep(data->gpiod);
439195ca170SBaolin Wang 	if (state < 0) {
440195ca170SBaolin Wang 		dev_err(data->dev, "failed to get gpio state\n");
441195ca170SBaolin Wang 		mutex_unlock(&data->lock);
442195ca170SBaolin Wang 		return IRQ_RETVAL(state);
443195ca170SBaolin Wang 	}
444195ca170SBaolin Wang 
445195ca170SBaolin Wang 	data->bat_present = !!state;
446195ca170SBaolin Wang 
447195ca170SBaolin Wang 	mutex_unlock(&data->lock);
448195ca170SBaolin Wang 
449195ca170SBaolin Wang 	power_supply_changed(data->battery);
450195ca170SBaolin Wang 	return IRQ_HANDLED;
451195ca170SBaolin Wang }
452195ca170SBaolin Wang 
453195ca170SBaolin Wang static void sc27xx_fgu_disable(void *_data)
454195ca170SBaolin Wang {
455195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = _data;
456195ca170SBaolin Wang 
457195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0);
458195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0);
459195ca170SBaolin Wang }
460195ca170SBaolin Wang 
461195ca170SBaolin Wang static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity)
462195ca170SBaolin Wang {
463195ca170SBaolin Wang 	/*
464195ca170SBaolin Wang 	 * Get current capacity (mAh) = battery total capacity (mAh) *
465195ca170SBaolin Wang 	 * current capacity percent (capacity / 100).
466195ca170SBaolin Wang 	 */
467195ca170SBaolin Wang 	int cur_cap = DIV_ROUND_CLOSEST(data->total_cap * capacity, 100);
468195ca170SBaolin Wang 
469195ca170SBaolin Wang 	/*
470195ca170SBaolin Wang 	 * Convert current capacity (mAh) to coulomb counter according to the
471195ca170SBaolin Wang 	 * formula: 1 mAh =3.6 coulomb.
472195ca170SBaolin Wang 	 */
473195ca170SBaolin Wang 	return DIV_ROUND_CLOSEST(cur_cap * 36, 10);
474195ca170SBaolin Wang }
475195ca170SBaolin Wang 
476*65c9fab7SBaolin Wang static int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data)
477*65c9fab7SBaolin Wang {
478*65c9fab7SBaolin Wang 	struct nvmem_cell *cell;
479*65c9fab7SBaolin Wang 	int calib_data, cal_4200mv;
480*65c9fab7SBaolin Wang 	void *buf;
481*65c9fab7SBaolin Wang 	size_t len;
482*65c9fab7SBaolin Wang 
483*65c9fab7SBaolin Wang 	cell = nvmem_cell_get(data->dev, "fgu_calib");
484*65c9fab7SBaolin Wang 	if (IS_ERR(cell))
485*65c9fab7SBaolin Wang 		return PTR_ERR(cell);
486*65c9fab7SBaolin Wang 
487*65c9fab7SBaolin Wang 	buf = nvmem_cell_read(cell, &len);
488*65c9fab7SBaolin Wang 	nvmem_cell_put(cell);
489*65c9fab7SBaolin Wang 
490*65c9fab7SBaolin Wang 	if (IS_ERR(buf))
491*65c9fab7SBaolin Wang 		return PTR_ERR(buf);
492*65c9fab7SBaolin Wang 
493*65c9fab7SBaolin Wang 	memcpy(&calib_data, buf, min(len, sizeof(u32)));
494*65c9fab7SBaolin Wang 
495*65c9fab7SBaolin Wang 	/*
496*65c9fab7SBaolin Wang 	 * Get the ADC value corresponding to 4200 mV from eFuse controller
497*65c9fab7SBaolin Wang 	 * according to below formula. Then convert to ADC values corresponding
498*65c9fab7SBaolin Wang 	 * to 1000 mV and 1000 mA.
499*65c9fab7SBaolin Wang 	 */
500*65c9fab7SBaolin Wang 	cal_4200mv = (calib_data & 0x1ff) + 6963 - 4096 - 256;
501*65c9fab7SBaolin Wang 	data->vol_1000mv_adc = DIV_ROUND_CLOSEST(cal_4200mv * 10, 42);
502*65c9fab7SBaolin Wang 	data->cur_1000ma_adc = data->vol_1000mv_adc * 4;
503*65c9fab7SBaolin Wang 
504*65c9fab7SBaolin Wang 	kfree(buf);
505*65c9fab7SBaolin Wang 	return 0;
506*65c9fab7SBaolin Wang }
507*65c9fab7SBaolin Wang 
508195ca170SBaolin Wang static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data)
509195ca170SBaolin Wang {
510195ca170SBaolin Wang 	struct power_supply_battery_info info = { };
511195ca170SBaolin Wang 	struct power_supply_battery_ocv_table *table;
512195ca170SBaolin Wang 	int ret;
513195ca170SBaolin Wang 
514195ca170SBaolin Wang 	ret = power_supply_get_battery_info(data->battery, &info);
515195ca170SBaolin Wang 	if (ret) {
516195ca170SBaolin Wang 		dev_err(data->dev, "failed to get battery information\n");
517195ca170SBaolin Wang 		return ret;
518195ca170SBaolin Wang 	}
519195ca170SBaolin Wang 
520195ca170SBaolin Wang 	data->total_cap = info.charge_full_design_uah / 1000;
521195ca170SBaolin Wang 	data->max_volt = info.constant_charge_voltage_max_uv / 1000;
522195ca170SBaolin Wang 	data->internal_resist = info.factory_internal_resistance_uohm / 1000;
523195ca170SBaolin Wang 
524195ca170SBaolin Wang 	/*
525195ca170SBaolin Wang 	 * For SC27XX fuel gauge device, we only use one ocv-capacity
526195ca170SBaolin Wang 	 * table in normal temperature 20 Celsius.
527195ca170SBaolin Wang 	 */
528195ca170SBaolin Wang 	table = power_supply_find_ocv2cap_table(&info, 20, &data->table_len);
529195ca170SBaolin Wang 	if (!table)
530195ca170SBaolin Wang 		return -EINVAL;
531195ca170SBaolin Wang 
532195ca170SBaolin Wang 	data->cap_table = devm_kmemdup(data->dev, table,
533195ca170SBaolin Wang 				       data->table_len * sizeof(*table),
534195ca170SBaolin Wang 				       GFP_KERNEL);
535195ca170SBaolin Wang 	if (!data->cap_table) {
536195ca170SBaolin Wang 		power_supply_put_battery_info(data->battery, &info);
537195ca170SBaolin Wang 		return -ENOMEM;
538195ca170SBaolin Wang 	}
539195ca170SBaolin Wang 
540195ca170SBaolin Wang 	power_supply_put_battery_info(data->battery, &info);
541195ca170SBaolin Wang 
542*65c9fab7SBaolin Wang 	ret = sc27xx_fgu_calibration(data);
543*65c9fab7SBaolin Wang 	if (ret)
544*65c9fab7SBaolin Wang 		return ret;
545*65c9fab7SBaolin Wang 
546195ca170SBaolin Wang 	/* Enable the FGU module */
547195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap, SC27XX_MODULE_EN0,
548195ca170SBaolin Wang 				 SC27XX_FGU_EN, SC27XX_FGU_EN);
549195ca170SBaolin Wang 	if (ret) {
550195ca170SBaolin Wang 		dev_err(data->dev, "failed to enable fgu\n");
551195ca170SBaolin Wang 		return ret;
552195ca170SBaolin Wang 	}
553195ca170SBaolin Wang 
554195ca170SBaolin Wang 	/* Enable the FGU RTC clock to make it work */
555195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap, SC27XX_CLK_EN0,
556195ca170SBaolin Wang 				 SC27XX_FGU_RTC_EN, SC27XX_FGU_RTC_EN);
557195ca170SBaolin Wang 	if (ret) {
558195ca170SBaolin Wang 		dev_err(data->dev, "failed to enable fgu RTC clock\n");
559195ca170SBaolin Wang 		goto disable_fgu;
560195ca170SBaolin Wang 	}
561195ca170SBaolin Wang 
562195ca170SBaolin Wang 	/*
563195ca170SBaolin Wang 	 * Get the boot battery capacity when system powers on, which is used to
564195ca170SBaolin Wang 	 * initialize the coulomb counter. After that, we can read the coulomb
565195ca170SBaolin Wang 	 * counter to measure the battery capacity.
566195ca170SBaolin Wang 	 */
567195ca170SBaolin Wang 	ret = sc27xx_fgu_get_boot_capacity(data, &data->init_cap);
568195ca170SBaolin Wang 	if (ret) {
569195ca170SBaolin Wang 		dev_err(data->dev, "failed to get boot capacity\n");
570195ca170SBaolin Wang 		goto disable_clk;
571195ca170SBaolin Wang 	}
572195ca170SBaolin Wang 
573195ca170SBaolin Wang 	/*
574195ca170SBaolin Wang 	 * Convert battery capacity to the corresponding initial coulomb counter
575195ca170SBaolin Wang 	 * and set into coulomb counter registers.
576195ca170SBaolin Wang 	 */
577195ca170SBaolin Wang 	data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap);
578195ca170SBaolin Wang 	ret = sc27xx_fgu_set_clbcnt(data, data->init_clbcnt);
579195ca170SBaolin Wang 	if (ret) {
580195ca170SBaolin Wang 		dev_err(data->dev, "failed to initialize coulomb counter\n");
581195ca170SBaolin Wang 		goto disable_clk;
582195ca170SBaolin Wang 	}
583195ca170SBaolin Wang 
584195ca170SBaolin Wang 	return 0;
585195ca170SBaolin Wang 
586195ca170SBaolin Wang disable_clk:
587195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0);
588195ca170SBaolin Wang disable_fgu:
589195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0);
590195ca170SBaolin Wang 
591195ca170SBaolin Wang 	return ret;
592195ca170SBaolin Wang }
593195ca170SBaolin Wang 
594195ca170SBaolin Wang static int sc27xx_fgu_probe(struct platform_device *pdev)
595195ca170SBaolin Wang {
596195ca170SBaolin Wang 	struct device_node *np = pdev->dev.of_node;
597195ca170SBaolin Wang 	struct power_supply_config fgu_cfg = { };
598195ca170SBaolin Wang 	struct sc27xx_fgu_data *data;
599195ca170SBaolin Wang 	int ret, irq;
600195ca170SBaolin Wang 
601195ca170SBaolin Wang 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
602195ca170SBaolin Wang 	if (!data)
603195ca170SBaolin Wang 		return -ENOMEM;
604195ca170SBaolin Wang 
605195ca170SBaolin Wang 	data->regmap = dev_get_regmap(pdev->dev.parent, NULL);
606195ca170SBaolin Wang 	if (!data->regmap) {
607195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to get regmap\n");
608195ca170SBaolin Wang 		return -ENODEV;
609195ca170SBaolin Wang 	}
610195ca170SBaolin Wang 
611195ca170SBaolin Wang 	ret = device_property_read_u32(&pdev->dev, "reg", &data->base);
612195ca170SBaolin Wang 	if (ret) {
613195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to get fgu address\n");
614195ca170SBaolin Wang 		return ret;
615195ca170SBaolin Wang 	}
616195ca170SBaolin Wang 
617195ca170SBaolin Wang 	data->channel = devm_iio_channel_get(&pdev->dev, "bat-temp");
618195ca170SBaolin Wang 	if (IS_ERR(data->channel)) {
619195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to get IIO channel\n");
620195ca170SBaolin Wang 		return PTR_ERR(data->channel);
621195ca170SBaolin Wang 	}
622195ca170SBaolin Wang 
623195ca170SBaolin Wang 	data->gpiod = devm_gpiod_get(&pdev->dev, "bat-detect", GPIOD_IN);
624195ca170SBaolin Wang 	if (IS_ERR(data->gpiod)) {
625195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to get battery detection GPIO\n");
626195ca170SBaolin Wang 		return PTR_ERR(data->gpiod);
627195ca170SBaolin Wang 	}
628195ca170SBaolin Wang 
629195ca170SBaolin Wang 	ret = gpiod_get_value_cansleep(data->gpiod);
630195ca170SBaolin Wang 	if (ret < 0) {
631195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to get gpio state\n");
632195ca170SBaolin Wang 		return ret;
633195ca170SBaolin Wang 	}
634195ca170SBaolin Wang 
635195ca170SBaolin Wang 	data->bat_present = !!ret;
636195ca170SBaolin Wang 	mutex_init(&data->lock);
637195ca170SBaolin Wang 	data->dev = &pdev->dev;
638195ca170SBaolin Wang 
639195ca170SBaolin Wang 	fgu_cfg.drv_data = data;
640195ca170SBaolin Wang 	fgu_cfg.of_node = np;
641195ca170SBaolin Wang 	data->battery = devm_power_supply_register(&pdev->dev, &sc27xx_fgu_desc,
642195ca170SBaolin Wang 						   &fgu_cfg);
643195ca170SBaolin Wang 	if (IS_ERR(data->battery)) {
644195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to register power supply\n");
645195ca170SBaolin Wang 		return PTR_ERR(data->battery);
646195ca170SBaolin Wang 	}
647195ca170SBaolin Wang 
648195ca170SBaolin Wang 	ret = sc27xx_fgu_hw_init(data);
649195ca170SBaolin Wang 	if (ret) {
650195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to initialize fgu hardware\n");
651195ca170SBaolin Wang 		return ret;
652195ca170SBaolin Wang 	}
653195ca170SBaolin Wang 
654195ca170SBaolin Wang 	ret = devm_add_action(&pdev->dev, sc27xx_fgu_disable, data);
655195ca170SBaolin Wang 	if (ret) {
656195ca170SBaolin Wang 		sc27xx_fgu_disable(data);
657195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to add fgu disable action\n");
658195ca170SBaolin Wang 		return ret;
659195ca170SBaolin Wang 	}
660195ca170SBaolin Wang 
661195ca170SBaolin Wang 	irq = gpiod_to_irq(data->gpiod);
662195ca170SBaolin Wang 	if (irq < 0) {
663195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to translate GPIO to IRQ\n");
664195ca170SBaolin Wang 		return irq;
665195ca170SBaolin Wang 	}
666195ca170SBaolin Wang 
667195ca170SBaolin Wang 	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
668195ca170SBaolin Wang 					sc27xx_fgu_bat_detection,
669195ca170SBaolin Wang 					IRQF_ONESHOT | IRQF_TRIGGER_RISING |
670195ca170SBaolin Wang 					IRQF_TRIGGER_FALLING,
671195ca170SBaolin Wang 					pdev->name, data);
672195ca170SBaolin Wang 	if (ret) {
673195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to request IRQ\n");
674195ca170SBaolin Wang 		return ret;
675195ca170SBaolin Wang 	}
676195ca170SBaolin Wang 
677195ca170SBaolin Wang 	return 0;
678195ca170SBaolin Wang }
679195ca170SBaolin Wang 
680195ca170SBaolin Wang static const struct of_device_id sc27xx_fgu_of_match[] = {
681195ca170SBaolin Wang 	{ .compatible = "sprd,sc2731-fgu", },
682195ca170SBaolin Wang 	{ }
683195ca170SBaolin Wang };
684195ca170SBaolin Wang 
685195ca170SBaolin Wang static struct platform_driver sc27xx_fgu_driver = {
686195ca170SBaolin Wang 	.probe = sc27xx_fgu_probe,
687195ca170SBaolin Wang 	.driver = {
688195ca170SBaolin Wang 		.name = "sc27xx-fgu",
689195ca170SBaolin Wang 		.of_match_table = sc27xx_fgu_of_match,
690195ca170SBaolin Wang 	}
691195ca170SBaolin Wang };
692195ca170SBaolin Wang 
693195ca170SBaolin Wang module_platform_driver(sc27xx_fgu_driver);
694195ca170SBaolin Wang 
695195ca170SBaolin Wang MODULE_DESCRIPTION("Spreadtrum SC27XX PMICs Fual Gauge Unit Driver");
696195ca170SBaolin Wang MODULE_LICENSE("GPL v2");
697