xref: /linux/drivers/power/supply/sc27xx_fuel_gauge.c (revision e2fb615b69e0e612078363fd0ecc63738a54e1cd)
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>
965c9fab7SBaolin 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>
1465c9fab7SBaolin 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
34edcb1c0aSYuanjiang Yu #define SC27XX_FGU_LOW_OVERLOAD		0x34
35195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_SETH		0x50
36195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_SETL		0x54
37edcb1c0aSYuanjiang Yu #define SC27XX_FGU_CLBCNT_DELTH		0x58
38edcb1c0aSYuanjiang Yu #define SC27XX_FGU_CLBCNT_DELTL		0x5c
39195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_VALH		0x68
40195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_VALL		0x6c
41195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_QMAXL		0x74
42195ca170SBaolin Wang 
43195ca170SBaolin Wang #define SC27XX_WRITE_SELCLB_EN		BIT(0)
44195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_MASK		GENMASK(15, 0)
45195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_SHIFT		16
46edcb1c0aSYuanjiang Yu #define SC27XX_FGU_LOW_OVERLOAD_MASK	GENMASK(12, 0)
47edcb1c0aSYuanjiang Yu 
48edcb1c0aSYuanjiang Yu #define SC27XX_FGU_INT_MASK		GENMASK(9, 0)
49edcb1c0aSYuanjiang Yu #define SC27XX_FGU_LOW_OVERLOAD_INT	BIT(0)
50edcb1c0aSYuanjiang Yu #define SC27XX_FGU_CLBCNT_DELTA_INT	BIT(2)
51195ca170SBaolin Wang 
52195ca170SBaolin Wang #define SC27XX_FGU_CUR_BASIC_ADC	8192
53195ca170SBaolin Wang #define SC27XX_FGU_SAMPLE_HZ		2
54195ca170SBaolin Wang 
55195ca170SBaolin Wang /*
56195ca170SBaolin Wang  * struct sc27xx_fgu_data: describe the FGU device
57195ca170SBaolin Wang  * @regmap: regmap for register access
58195ca170SBaolin Wang  * @dev: platform device
59195ca170SBaolin Wang  * @battery: battery power supply
60195ca170SBaolin Wang  * @base: the base offset for the controller
61195ca170SBaolin Wang  * @lock: protect the structure
62195ca170SBaolin Wang  * @gpiod: GPIO for battery detection
63195ca170SBaolin Wang  * @channel: IIO channel to get battery temperature
64195ca170SBaolin Wang  * @internal_resist: the battery internal resistance in mOhm
65195ca170SBaolin Wang  * @total_cap: the total capacity of the battery in mAh
66195ca170SBaolin Wang  * @init_cap: the initial capacity of the battery in mAh
67edcb1c0aSYuanjiang Yu  * @alarm_cap: the alarm capacity
68195ca170SBaolin Wang  * @init_clbcnt: the initial coulomb counter
69195ca170SBaolin Wang  * @max_volt: the maximum constant input voltage in millivolt
70edcb1c0aSYuanjiang Yu  * @min_volt: the minimum drained battery voltage in microvolt
71195ca170SBaolin Wang  * @table_len: the capacity table length
7265c9fab7SBaolin Wang  * @cur_1000ma_adc: ADC value corresponding to 1000 mA
7365c9fab7SBaolin Wang  * @vol_1000mv_adc: ADC value corresponding to 1000 mV
74195ca170SBaolin Wang  * @cap_table: capacity table with corresponding ocv
75195ca170SBaolin Wang  */
76195ca170SBaolin Wang struct sc27xx_fgu_data {
77195ca170SBaolin Wang 	struct regmap *regmap;
78195ca170SBaolin Wang 	struct device *dev;
79195ca170SBaolin Wang 	struct power_supply *battery;
80195ca170SBaolin Wang 	u32 base;
81195ca170SBaolin Wang 	struct mutex lock;
82195ca170SBaolin Wang 	struct gpio_desc *gpiod;
83195ca170SBaolin Wang 	struct iio_channel *channel;
84195ca170SBaolin Wang 	bool bat_present;
85195ca170SBaolin Wang 	int internal_resist;
86195ca170SBaolin Wang 	int total_cap;
87195ca170SBaolin Wang 	int init_cap;
88edcb1c0aSYuanjiang Yu 	int alarm_cap;
89195ca170SBaolin Wang 	int init_clbcnt;
90195ca170SBaolin Wang 	int max_volt;
91edcb1c0aSYuanjiang Yu 	int min_volt;
92195ca170SBaolin Wang 	int table_len;
9365c9fab7SBaolin Wang 	int cur_1000ma_adc;
9465c9fab7SBaolin Wang 	int vol_1000mv_adc;
95195ca170SBaolin Wang 	struct power_supply_battery_ocv_table *cap_table;
96195ca170SBaolin Wang };
97195ca170SBaolin Wang 
98edcb1c0aSYuanjiang Yu static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity);
99edcb1c0aSYuanjiang Yu 
100195ca170SBaolin Wang static const char * const sc27xx_charger_supply_name[] = {
101195ca170SBaolin Wang 	"sc2731_charger",
102195ca170SBaolin Wang 	"sc2720_charger",
103195ca170SBaolin Wang 	"sc2721_charger",
104195ca170SBaolin Wang 	"sc2723_charger",
105195ca170SBaolin Wang };
106195ca170SBaolin Wang 
10765c9fab7SBaolin Wang static int sc27xx_fgu_adc_to_current(struct sc27xx_fgu_data *data, int adc)
108195ca170SBaolin Wang {
10965c9fab7SBaolin Wang 	return DIV_ROUND_CLOSEST(adc * 1000, data->cur_1000ma_adc);
110195ca170SBaolin Wang }
111195ca170SBaolin Wang 
11265c9fab7SBaolin Wang static int sc27xx_fgu_adc_to_voltage(struct sc27xx_fgu_data *data, int adc)
113195ca170SBaolin Wang {
11465c9fab7SBaolin Wang 	return DIV_ROUND_CLOSEST(adc * 1000, data->vol_1000mv_adc);
115195ca170SBaolin Wang }
116195ca170SBaolin Wang 
117edcb1c0aSYuanjiang Yu static int sc27xx_fgu_voltage_to_adc(struct sc27xx_fgu_data *data, int vol)
118edcb1c0aSYuanjiang Yu {
119edcb1c0aSYuanjiang Yu 	return DIV_ROUND_CLOSEST(vol * data->vol_1000mv_adc, 1000);
120edcb1c0aSYuanjiang Yu }
121edcb1c0aSYuanjiang Yu 
122195ca170SBaolin Wang /*
123195ca170SBaolin Wang  * When system boots on, we can not read battery capacity from coulomb
124195ca170SBaolin Wang  * registers, since now the coulomb registers are invalid. So we should
125195ca170SBaolin Wang  * calculate the battery open circuit voltage, and get current battery
126195ca170SBaolin Wang  * capacity according to the capacity table.
127195ca170SBaolin Wang  */
128195ca170SBaolin Wang static int sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data *data, int *cap)
129195ca170SBaolin Wang {
130195ca170SBaolin Wang 	int volt, cur, oci, ocv, ret;
131195ca170SBaolin Wang 
132195ca170SBaolin Wang 	/*
133195ca170SBaolin Wang 	 * After system booting on, the SC27XX_FGU_CLBCNT_QMAXL register saved
134195ca170SBaolin Wang 	 * the first sampled open circuit current.
135195ca170SBaolin Wang 	 */
136195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_QMAXL,
137195ca170SBaolin Wang 			  &cur);
138195ca170SBaolin Wang 	if (ret)
139195ca170SBaolin Wang 		return ret;
140195ca170SBaolin Wang 
141195ca170SBaolin Wang 	cur <<= 1;
14265c9fab7SBaolin Wang 	oci = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC);
143195ca170SBaolin Wang 
144195ca170SBaolin Wang 	/*
145195ca170SBaolin Wang 	 * Should get the OCV from SC27XX_FGU_POCV register at the system
146195ca170SBaolin Wang 	 * beginning. It is ADC values reading from registers which need to
147195ca170SBaolin Wang 	 * convert the corresponding voltage.
148195ca170SBaolin Wang 	 */
149195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_POCV, &volt);
150195ca170SBaolin Wang 	if (ret)
151195ca170SBaolin Wang 		return ret;
152195ca170SBaolin Wang 
15365c9fab7SBaolin Wang 	volt = sc27xx_fgu_adc_to_voltage(data, volt);
154195ca170SBaolin Wang 	ocv = volt * 1000 - oci * data->internal_resist;
155195ca170SBaolin Wang 
156195ca170SBaolin Wang 	/*
157195ca170SBaolin Wang 	 * Parse the capacity table to look up the correct capacity percent
158195ca170SBaolin Wang 	 * according to current battery's corresponding OCV values.
159195ca170SBaolin Wang 	 */
160195ca170SBaolin Wang 	*cap = power_supply_ocv2cap_simple(data->cap_table, data->table_len,
161195ca170SBaolin Wang 					   ocv);
162195ca170SBaolin Wang 
163195ca170SBaolin Wang 	return 0;
164195ca170SBaolin Wang }
165195ca170SBaolin Wang 
166195ca170SBaolin Wang static int sc27xx_fgu_set_clbcnt(struct sc27xx_fgu_data *data, int clbcnt)
167195ca170SBaolin Wang {
168195ca170SBaolin Wang 	int ret;
169195ca170SBaolin Wang 
170195ca170SBaolin Wang 	clbcnt *= SC27XX_FGU_SAMPLE_HZ;
171195ca170SBaolin Wang 
172195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap,
173195ca170SBaolin Wang 				 data->base + SC27XX_FGU_CLBCNT_SETL,
174195ca170SBaolin Wang 				 SC27XX_FGU_CLBCNT_MASK, clbcnt);
175195ca170SBaolin Wang 	if (ret)
176195ca170SBaolin Wang 		return ret;
177195ca170SBaolin Wang 
178195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap,
179195ca170SBaolin Wang 				 data->base + SC27XX_FGU_CLBCNT_SETH,
180195ca170SBaolin Wang 				 SC27XX_FGU_CLBCNT_MASK,
181195ca170SBaolin Wang 				 clbcnt >> SC27XX_FGU_CLBCNT_SHIFT);
182195ca170SBaolin Wang 	if (ret)
183195ca170SBaolin Wang 		return ret;
184195ca170SBaolin Wang 
185195ca170SBaolin Wang 	return regmap_update_bits(data->regmap, data->base + SC27XX_FGU_START,
186195ca170SBaolin Wang 				 SC27XX_WRITE_SELCLB_EN,
187195ca170SBaolin Wang 				 SC27XX_WRITE_SELCLB_EN);
188195ca170SBaolin Wang }
189195ca170SBaolin Wang 
190195ca170SBaolin Wang static int sc27xx_fgu_get_clbcnt(struct sc27xx_fgu_data *data, int *clb_cnt)
191195ca170SBaolin Wang {
192195ca170SBaolin Wang 	int ccl, cch, ret;
193195ca170SBaolin Wang 
194195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALL,
195195ca170SBaolin Wang 			  &ccl);
196195ca170SBaolin Wang 	if (ret)
197195ca170SBaolin Wang 		return ret;
198195ca170SBaolin Wang 
199195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALH,
200195ca170SBaolin Wang 			  &cch);
201195ca170SBaolin Wang 	if (ret)
202195ca170SBaolin Wang 		return ret;
203195ca170SBaolin Wang 
204195ca170SBaolin Wang 	*clb_cnt = ccl & SC27XX_FGU_CLBCNT_MASK;
205195ca170SBaolin Wang 	*clb_cnt |= (cch & SC27XX_FGU_CLBCNT_MASK) << SC27XX_FGU_CLBCNT_SHIFT;
206195ca170SBaolin Wang 	*clb_cnt /= SC27XX_FGU_SAMPLE_HZ;
207195ca170SBaolin Wang 
208195ca170SBaolin Wang 	return 0;
209195ca170SBaolin Wang }
210195ca170SBaolin Wang 
211195ca170SBaolin Wang static int sc27xx_fgu_get_capacity(struct sc27xx_fgu_data *data, int *cap)
212195ca170SBaolin Wang {
213195ca170SBaolin Wang 	int ret, cur_clbcnt, delta_clbcnt, delta_cap, temp;
214195ca170SBaolin Wang 
215195ca170SBaolin Wang 	/* Get current coulomb counters firstly */
216195ca170SBaolin Wang 	ret = sc27xx_fgu_get_clbcnt(data, &cur_clbcnt);
217195ca170SBaolin Wang 	if (ret)
218195ca170SBaolin Wang 		return ret;
219195ca170SBaolin Wang 
220195ca170SBaolin Wang 	delta_clbcnt = cur_clbcnt - data->init_clbcnt;
221195ca170SBaolin Wang 
222195ca170SBaolin Wang 	/*
223195ca170SBaolin Wang 	 * Convert coulomb counter to delta capacity (mAh), and set multiplier
224195ca170SBaolin Wang 	 * as 100 to improve the precision.
225195ca170SBaolin Wang 	 */
226195ca170SBaolin Wang 	temp = DIV_ROUND_CLOSEST(delta_clbcnt, 360);
22765c9fab7SBaolin Wang 	temp = sc27xx_fgu_adc_to_current(data, temp);
228195ca170SBaolin Wang 
229195ca170SBaolin Wang 	/*
230195ca170SBaolin Wang 	 * Convert to capacity percent of the battery total capacity,
231195ca170SBaolin Wang 	 * and multiplier is 100 too.
232195ca170SBaolin Wang 	 */
233195ca170SBaolin Wang 	delta_cap = DIV_ROUND_CLOSEST(temp * 100, data->total_cap);
234195ca170SBaolin Wang 	*cap = delta_cap + data->init_cap;
235195ca170SBaolin Wang 
236195ca170SBaolin Wang 	return 0;
237195ca170SBaolin Wang }
238195ca170SBaolin Wang 
239195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_vol(struct sc27xx_fgu_data *data, int *val)
240195ca170SBaolin Wang {
241195ca170SBaolin Wang 	int ret, vol;
242195ca170SBaolin Wang 
243195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_VOLTAGE, &vol);
244195ca170SBaolin Wang 	if (ret)
245195ca170SBaolin Wang 		return ret;
246195ca170SBaolin Wang 
247195ca170SBaolin Wang 	/*
248195ca170SBaolin Wang 	 * It is ADC values reading from registers which need to convert to
249195ca170SBaolin Wang 	 * corresponding voltage values.
250195ca170SBaolin Wang 	 */
25165c9fab7SBaolin Wang 	*val = sc27xx_fgu_adc_to_voltage(data, vol);
252195ca170SBaolin Wang 
253195ca170SBaolin Wang 	return 0;
254195ca170SBaolin Wang }
255195ca170SBaolin Wang 
256195ca170SBaolin Wang static int sc27xx_fgu_get_current(struct sc27xx_fgu_data *data, int *val)
257195ca170SBaolin Wang {
258195ca170SBaolin Wang 	int ret, cur;
259195ca170SBaolin Wang 
260195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CURRENT, &cur);
261195ca170SBaolin Wang 	if (ret)
262195ca170SBaolin Wang 		return ret;
263195ca170SBaolin Wang 
264195ca170SBaolin Wang 	/*
265195ca170SBaolin Wang 	 * It is ADC values reading from registers which need to convert to
266195ca170SBaolin Wang 	 * corresponding current values.
267195ca170SBaolin Wang 	 */
26865c9fab7SBaolin Wang 	*val = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC);
269195ca170SBaolin Wang 
270195ca170SBaolin Wang 	return 0;
271195ca170SBaolin Wang }
272195ca170SBaolin Wang 
273195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val)
274195ca170SBaolin Wang {
275195ca170SBaolin Wang 	int vol, cur, ret;
276195ca170SBaolin Wang 
277195ca170SBaolin Wang 	ret = sc27xx_fgu_get_vbat_vol(data, &vol);
278195ca170SBaolin Wang 	if (ret)
279195ca170SBaolin Wang 		return ret;
280195ca170SBaolin Wang 
281195ca170SBaolin Wang 	ret = sc27xx_fgu_get_current(data, &cur);
282195ca170SBaolin Wang 	if (ret)
283195ca170SBaolin Wang 		return ret;
284195ca170SBaolin Wang 
285195ca170SBaolin Wang 	/* Return the battery OCV in micro volts. */
286195ca170SBaolin Wang 	*val = vol * 1000 - cur * data->internal_resist;
287195ca170SBaolin Wang 
288195ca170SBaolin Wang 	return 0;
289195ca170SBaolin Wang }
290195ca170SBaolin Wang 
291195ca170SBaolin Wang static int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp)
292195ca170SBaolin Wang {
293195ca170SBaolin Wang 	return iio_read_channel_processed(data->channel, temp);
294195ca170SBaolin Wang }
295195ca170SBaolin Wang 
296195ca170SBaolin Wang static int sc27xx_fgu_get_health(struct sc27xx_fgu_data *data, int *health)
297195ca170SBaolin Wang {
298195ca170SBaolin Wang 	int ret, vol;
299195ca170SBaolin Wang 
300195ca170SBaolin Wang 	ret = sc27xx_fgu_get_vbat_vol(data, &vol);
301195ca170SBaolin Wang 	if (ret)
302195ca170SBaolin Wang 		return ret;
303195ca170SBaolin Wang 
304195ca170SBaolin Wang 	if (vol > data->max_volt)
305195ca170SBaolin Wang 		*health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
306195ca170SBaolin Wang 	else
307195ca170SBaolin Wang 		*health = POWER_SUPPLY_HEALTH_GOOD;
308195ca170SBaolin Wang 
309195ca170SBaolin Wang 	return 0;
310195ca170SBaolin Wang }
311195ca170SBaolin Wang 
312195ca170SBaolin Wang static int sc27xx_fgu_get_status(struct sc27xx_fgu_data *data, int *status)
313195ca170SBaolin Wang {
314195ca170SBaolin Wang 	union power_supply_propval val;
315195ca170SBaolin Wang 	struct power_supply *psy;
316195ca170SBaolin Wang 	int i, ret = -EINVAL;
317195ca170SBaolin Wang 
318195ca170SBaolin Wang 	for (i = 0; i < ARRAY_SIZE(sc27xx_charger_supply_name); i++) {
319195ca170SBaolin Wang 		psy = power_supply_get_by_name(sc27xx_charger_supply_name[i]);
320195ca170SBaolin Wang 		if (!psy)
321195ca170SBaolin Wang 			continue;
322195ca170SBaolin Wang 
323195ca170SBaolin Wang 		ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS,
324195ca170SBaolin Wang 						&val);
325195ca170SBaolin Wang 		power_supply_put(psy);
326195ca170SBaolin Wang 		if (ret)
327195ca170SBaolin Wang 			return ret;
328195ca170SBaolin Wang 
329195ca170SBaolin Wang 		*status = val.intval;
330195ca170SBaolin Wang 	}
331195ca170SBaolin Wang 
332195ca170SBaolin Wang 	return ret;
333195ca170SBaolin Wang }
334195ca170SBaolin Wang 
335195ca170SBaolin Wang static int sc27xx_fgu_get_property(struct power_supply *psy,
336195ca170SBaolin Wang 				   enum power_supply_property psp,
337195ca170SBaolin Wang 				   union power_supply_propval *val)
338195ca170SBaolin Wang {
339195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy);
340195ca170SBaolin Wang 	int ret = 0;
341195ca170SBaolin Wang 	int value;
342195ca170SBaolin Wang 
343195ca170SBaolin Wang 	mutex_lock(&data->lock);
344195ca170SBaolin Wang 
345195ca170SBaolin Wang 	switch (psp) {
346195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_STATUS:
347195ca170SBaolin Wang 		ret = sc27xx_fgu_get_status(data, &value);
348195ca170SBaolin Wang 		if (ret)
349195ca170SBaolin Wang 			goto error;
350195ca170SBaolin Wang 
351195ca170SBaolin Wang 		val->intval = value;
352195ca170SBaolin Wang 		break;
353195ca170SBaolin Wang 
354195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_HEALTH:
355195ca170SBaolin Wang 		ret = sc27xx_fgu_get_health(data, &value);
356195ca170SBaolin Wang 		if (ret)
357195ca170SBaolin Wang 			goto error;
358195ca170SBaolin Wang 
359195ca170SBaolin Wang 		val->intval = value;
360195ca170SBaolin Wang 		break;
361195ca170SBaolin Wang 
362195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_PRESENT:
363195ca170SBaolin Wang 		val->intval = data->bat_present;
364195ca170SBaolin Wang 		break;
365195ca170SBaolin Wang 
366195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_TEMP:
367195ca170SBaolin Wang 		ret = sc27xx_fgu_get_temp(data, &value);
368195ca170SBaolin Wang 		if (ret)
369195ca170SBaolin Wang 			goto error;
370195ca170SBaolin Wang 
371195ca170SBaolin Wang 		val->intval = value;
372195ca170SBaolin Wang 		break;
373195ca170SBaolin Wang 
374195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_TECHNOLOGY:
375195ca170SBaolin Wang 		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
376195ca170SBaolin Wang 		break;
377195ca170SBaolin Wang 
378195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_CAPACITY:
379195ca170SBaolin Wang 		ret = sc27xx_fgu_get_capacity(data, &value);
380195ca170SBaolin Wang 		if (ret)
381195ca170SBaolin Wang 			goto error;
382195ca170SBaolin Wang 
383195ca170SBaolin Wang 		val->intval = value;
384195ca170SBaolin Wang 		break;
385195ca170SBaolin Wang 
386195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
387195ca170SBaolin Wang 		ret = sc27xx_fgu_get_vbat_vol(data, &value);
388195ca170SBaolin Wang 		if (ret)
389195ca170SBaolin Wang 			goto error;
390195ca170SBaolin Wang 
391195ca170SBaolin Wang 		val->intval = value * 1000;
392195ca170SBaolin Wang 		break;
393195ca170SBaolin Wang 
394195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_VOLTAGE_OCV:
395195ca170SBaolin Wang 		ret = sc27xx_fgu_get_vbat_ocv(data, &value);
396195ca170SBaolin Wang 		if (ret)
397195ca170SBaolin Wang 			goto error;
398195ca170SBaolin Wang 
399195ca170SBaolin Wang 		val->intval = value;
400195ca170SBaolin Wang 		break;
401195ca170SBaolin Wang 
402195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_CURRENT_NOW:
403195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_CURRENT_AVG:
404195ca170SBaolin Wang 		ret = sc27xx_fgu_get_current(data, &value);
405195ca170SBaolin Wang 		if (ret)
406195ca170SBaolin Wang 			goto error;
407195ca170SBaolin Wang 
408195ca170SBaolin Wang 		val->intval = value * 1000;
409195ca170SBaolin Wang 		break;
410195ca170SBaolin Wang 
411195ca170SBaolin Wang 	default:
412195ca170SBaolin Wang 		ret = -EINVAL;
413195ca170SBaolin Wang 		break;
414195ca170SBaolin Wang 	}
415195ca170SBaolin Wang 
416195ca170SBaolin Wang error:
417195ca170SBaolin Wang 	mutex_unlock(&data->lock);
418195ca170SBaolin Wang 	return ret;
419195ca170SBaolin Wang }
420195ca170SBaolin Wang 
421195ca170SBaolin Wang static void sc27xx_fgu_external_power_changed(struct power_supply *psy)
422195ca170SBaolin Wang {
423195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy);
424195ca170SBaolin Wang 
425195ca170SBaolin Wang 	power_supply_changed(data->battery);
426195ca170SBaolin Wang }
427195ca170SBaolin Wang 
428195ca170SBaolin Wang static enum power_supply_property sc27xx_fgu_props[] = {
429195ca170SBaolin Wang 	POWER_SUPPLY_PROP_STATUS,
430195ca170SBaolin Wang 	POWER_SUPPLY_PROP_HEALTH,
431195ca170SBaolin Wang 	POWER_SUPPLY_PROP_PRESENT,
432195ca170SBaolin Wang 	POWER_SUPPLY_PROP_TEMP,
433195ca170SBaolin Wang 	POWER_SUPPLY_PROP_TECHNOLOGY,
434195ca170SBaolin Wang 	POWER_SUPPLY_PROP_CAPACITY,
435195ca170SBaolin Wang 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
436195ca170SBaolin Wang 	POWER_SUPPLY_PROP_VOLTAGE_OCV,
437195ca170SBaolin Wang 	POWER_SUPPLY_PROP_CURRENT_NOW,
438195ca170SBaolin Wang 	POWER_SUPPLY_PROP_CURRENT_AVG,
439195ca170SBaolin Wang };
440195ca170SBaolin Wang 
441195ca170SBaolin Wang static const struct power_supply_desc sc27xx_fgu_desc = {
442195ca170SBaolin Wang 	.name			= "sc27xx-fgu",
443195ca170SBaolin Wang 	.type			= POWER_SUPPLY_TYPE_BATTERY,
444195ca170SBaolin Wang 	.properties		= sc27xx_fgu_props,
445195ca170SBaolin Wang 	.num_properties		= ARRAY_SIZE(sc27xx_fgu_props),
446195ca170SBaolin Wang 	.get_property		= sc27xx_fgu_get_property,
447195ca170SBaolin Wang 	.external_power_changed	= sc27xx_fgu_external_power_changed,
448195ca170SBaolin Wang };
449195ca170SBaolin Wang 
450edcb1c0aSYuanjiang Yu static void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap)
451edcb1c0aSYuanjiang Yu {
452edcb1c0aSYuanjiang Yu 	data->init_cap = cap;
453edcb1c0aSYuanjiang Yu 	data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap);
454edcb1c0aSYuanjiang Yu }
455edcb1c0aSYuanjiang Yu 
456edcb1c0aSYuanjiang Yu static irqreturn_t sc27xx_fgu_interrupt(int irq, void *dev_id)
457edcb1c0aSYuanjiang Yu {
458edcb1c0aSYuanjiang Yu 	struct sc27xx_fgu_data *data = dev_id;
459edcb1c0aSYuanjiang Yu 	int ret, cap, ocv, adc;
460edcb1c0aSYuanjiang Yu 	u32 status;
461edcb1c0aSYuanjiang Yu 
462edcb1c0aSYuanjiang Yu 	mutex_lock(&data->lock);
463edcb1c0aSYuanjiang Yu 
464edcb1c0aSYuanjiang Yu 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_INT_STS,
465edcb1c0aSYuanjiang Yu 			  &status);
466edcb1c0aSYuanjiang Yu 	if (ret)
467edcb1c0aSYuanjiang Yu 		goto out;
468edcb1c0aSYuanjiang Yu 
469edcb1c0aSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR,
470edcb1c0aSYuanjiang Yu 				 status, status);
471edcb1c0aSYuanjiang Yu 	if (ret)
472edcb1c0aSYuanjiang Yu 		goto out;
473edcb1c0aSYuanjiang Yu 
474edcb1c0aSYuanjiang Yu 	/*
475edcb1c0aSYuanjiang Yu 	 * When low overload voltage interrupt happens, we should calibrate the
476edcb1c0aSYuanjiang Yu 	 * battery capacity in lower voltage stage.
477edcb1c0aSYuanjiang Yu 	 */
478edcb1c0aSYuanjiang Yu 	if (!(status & SC27XX_FGU_LOW_OVERLOAD_INT))
479edcb1c0aSYuanjiang Yu 		goto out;
480edcb1c0aSYuanjiang Yu 
481edcb1c0aSYuanjiang Yu 	ret = sc27xx_fgu_get_capacity(data, &cap);
482edcb1c0aSYuanjiang Yu 	if (ret)
483edcb1c0aSYuanjiang Yu 		goto out;
484edcb1c0aSYuanjiang Yu 
485edcb1c0aSYuanjiang Yu 	ret = sc27xx_fgu_get_vbat_ocv(data, &ocv);
486edcb1c0aSYuanjiang Yu 	if (ret)
487edcb1c0aSYuanjiang Yu 		goto out;
488edcb1c0aSYuanjiang Yu 
489edcb1c0aSYuanjiang Yu 	/*
490edcb1c0aSYuanjiang Yu 	 * If current OCV value is less than the minimum OCV value in OCV table,
491edcb1c0aSYuanjiang Yu 	 * which means now battery capacity is 0%, and we should adjust the
492edcb1c0aSYuanjiang Yu 	 * inititial capacity to 0.
493edcb1c0aSYuanjiang Yu 	 */
494edcb1c0aSYuanjiang Yu 	if (ocv <= data->cap_table[data->table_len - 1].ocv) {
495edcb1c0aSYuanjiang Yu 		sc27xx_fgu_adjust_cap(data, 0);
496edcb1c0aSYuanjiang Yu 	} else if (ocv <= data->min_volt) {
497edcb1c0aSYuanjiang Yu 		/*
498edcb1c0aSYuanjiang Yu 		 * If current OCV value is less than the low alarm voltage, but
499edcb1c0aSYuanjiang Yu 		 * current capacity is larger than the alarm capacity, we should
500edcb1c0aSYuanjiang Yu 		 * adjust the inititial capacity to alarm capacity.
501edcb1c0aSYuanjiang Yu 		 */
502edcb1c0aSYuanjiang Yu 		if (cap > data->alarm_cap) {
503edcb1c0aSYuanjiang Yu 			sc27xx_fgu_adjust_cap(data, data->alarm_cap);
504edcb1c0aSYuanjiang Yu 		} else if (cap <= 0) {
505edcb1c0aSYuanjiang Yu 			int cur_cap;
506edcb1c0aSYuanjiang Yu 
507edcb1c0aSYuanjiang Yu 			/*
508edcb1c0aSYuanjiang Yu 			 * If current capacity is equal with 0 or less than 0
509edcb1c0aSYuanjiang Yu 			 * (some error occurs), we should adjust inititial
510edcb1c0aSYuanjiang Yu 			 * capacity to the capacity corresponding to current OCV
511edcb1c0aSYuanjiang Yu 			 * value.
512edcb1c0aSYuanjiang Yu 			 */
513edcb1c0aSYuanjiang Yu 			cur_cap = power_supply_ocv2cap_simple(data->cap_table,
514edcb1c0aSYuanjiang Yu 							      data->table_len,
515edcb1c0aSYuanjiang Yu 							      ocv);
516edcb1c0aSYuanjiang Yu 			sc27xx_fgu_adjust_cap(data, cur_cap);
517edcb1c0aSYuanjiang Yu 		}
518edcb1c0aSYuanjiang Yu 
519edcb1c0aSYuanjiang Yu 		/*
520edcb1c0aSYuanjiang Yu 		 * After adjusting the battery capacity, we should set the
521edcb1c0aSYuanjiang Yu 		 * lowest alarm voltage instead.
522edcb1c0aSYuanjiang Yu 		 */
523edcb1c0aSYuanjiang Yu 		data->min_volt = data->cap_table[data->table_len - 1].ocv;
524edcb1c0aSYuanjiang Yu 		adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000);
525edcb1c0aSYuanjiang Yu 		regmap_update_bits(data->regmap, data->base + SC27XX_FGU_LOW_OVERLOAD,
526edcb1c0aSYuanjiang Yu 				   SC27XX_FGU_LOW_OVERLOAD_MASK, adc);
527edcb1c0aSYuanjiang Yu 	}
528edcb1c0aSYuanjiang Yu 
529edcb1c0aSYuanjiang Yu out:
530edcb1c0aSYuanjiang Yu 	mutex_unlock(&data->lock);
531edcb1c0aSYuanjiang Yu 
532edcb1c0aSYuanjiang Yu 	power_supply_changed(data->battery);
533edcb1c0aSYuanjiang Yu 	return IRQ_HANDLED;
534edcb1c0aSYuanjiang Yu }
535edcb1c0aSYuanjiang Yu 
536195ca170SBaolin Wang static irqreturn_t sc27xx_fgu_bat_detection(int irq, void *dev_id)
537195ca170SBaolin Wang {
538195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = dev_id;
539195ca170SBaolin Wang 	int state;
540195ca170SBaolin Wang 
541195ca170SBaolin Wang 	mutex_lock(&data->lock);
542195ca170SBaolin Wang 
543195ca170SBaolin Wang 	state = gpiod_get_value_cansleep(data->gpiod);
544195ca170SBaolin Wang 	if (state < 0) {
545195ca170SBaolin Wang 		dev_err(data->dev, "failed to get gpio state\n");
546195ca170SBaolin Wang 		mutex_unlock(&data->lock);
547195ca170SBaolin Wang 		return IRQ_RETVAL(state);
548195ca170SBaolin Wang 	}
549195ca170SBaolin Wang 
550195ca170SBaolin Wang 	data->bat_present = !!state;
551195ca170SBaolin Wang 
552195ca170SBaolin Wang 	mutex_unlock(&data->lock);
553195ca170SBaolin Wang 
554195ca170SBaolin Wang 	power_supply_changed(data->battery);
555195ca170SBaolin Wang 	return IRQ_HANDLED;
556195ca170SBaolin Wang }
557195ca170SBaolin Wang 
558195ca170SBaolin Wang static void sc27xx_fgu_disable(void *_data)
559195ca170SBaolin Wang {
560195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = _data;
561195ca170SBaolin Wang 
562195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0);
563195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0);
564195ca170SBaolin Wang }
565195ca170SBaolin Wang 
566195ca170SBaolin Wang static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity)
567195ca170SBaolin Wang {
568195ca170SBaolin Wang 	/*
569195ca170SBaolin Wang 	 * Get current capacity (mAh) = battery total capacity (mAh) *
570195ca170SBaolin Wang 	 * current capacity percent (capacity / 100).
571195ca170SBaolin Wang 	 */
572195ca170SBaolin Wang 	int cur_cap = DIV_ROUND_CLOSEST(data->total_cap * capacity, 100);
573195ca170SBaolin Wang 
574195ca170SBaolin Wang 	/*
575195ca170SBaolin Wang 	 * Convert current capacity (mAh) to coulomb counter according to the
576195ca170SBaolin Wang 	 * formula: 1 mAh =3.6 coulomb.
577195ca170SBaolin Wang 	 */
578195ca170SBaolin Wang 	return DIV_ROUND_CLOSEST(cur_cap * 36, 10);
579195ca170SBaolin Wang }
580195ca170SBaolin Wang 
58165c9fab7SBaolin Wang static int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data)
58265c9fab7SBaolin Wang {
58365c9fab7SBaolin Wang 	struct nvmem_cell *cell;
58465c9fab7SBaolin Wang 	int calib_data, cal_4200mv;
58565c9fab7SBaolin Wang 	void *buf;
58665c9fab7SBaolin Wang 	size_t len;
58765c9fab7SBaolin Wang 
58865c9fab7SBaolin Wang 	cell = nvmem_cell_get(data->dev, "fgu_calib");
58965c9fab7SBaolin Wang 	if (IS_ERR(cell))
59065c9fab7SBaolin Wang 		return PTR_ERR(cell);
59165c9fab7SBaolin Wang 
59265c9fab7SBaolin Wang 	buf = nvmem_cell_read(cell, &len);
59365c9fab7SBaolin Wang 	nvmem_cell_put(cell);
59465c9fab7SBaolin Wang 
59565c9fab7SBaolin Wang 	if (IS_ERR(buf))
59665c9fab7SBaolin Wang 		return PTR_ERR(buf);
59765c9fab7SBaolin Wang 
59865c9fab7SBaolin Wang 	memcpy(&calib_data, buf, min(len, sizeof(u32)));
59965c9fab7SBaolin Wang 
60065c9fab7SBaolin Wang 	/*
60165c9fab7SBaolin Wang 	 * Get the ADC value corresponding to 4200 mV from eFuse controller
60265c9fab7SBaolin Wang 	 * according to below formula. Then convert to ADC values corresponding
60365c9fab7SBaolin Wang 	 * to 1000 mV and 1000 mA.
60465c9fab7SBaolin Wang 	 */
60565c9fab7SBaolin Wang 	cal_4200mv = (calib_data & 0x1ff) + 6963 - 4096 - 256;
60665c9fab7SBaolin Wang 	data->vol_1000mv_adc = DIV_ROUND_CLOSEST(cal_4200mv * 10, 42);
60765c9fab7SBaolin Wang 	data->cur_1000ma_adc = data->vol_1000mv_adc * 4;
60865c9fab7SBaolin Wang 
60965c9fab7SBaolin Wang 	kfree(buf);
61065c9fab7SBaolin Wang 	return 0;
61165c9fab7SBaolin Wang }
61265c9fab7SBaolin Wang 
613195ca170SBaolin Wang static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data)
614195ca170SBaolin Wang {
615195ca170SBaolin Wang 	struct power_supply_battery_info info = { };
616195ca170SBaolin Wang 	struct power_supply_battery_ocv_table *table;
617edcb1c0aSYuanjiang Yu 	int ret, delta_clbcnt, alarm_adc;
618195ca170SBaolin Wang 
619195ca170SBaolin Wang 	ret = power_supply_get_battery_info(data->battery, &info);
620195ca170SBaolin Wang 	if (ret) {
621195ca170SBaolin Wang 		dev_err(data->dev, "failed to get battery information\n");
622195ca170SBaolin Wang 		return ret;
623195ca170SBaolin Wang 	}
624195ca170SBaolin Wang 
625195ca170SBaolin Wang 	data->total_cap = info.charge_full_design_uah / 1000;
626195ca170SBaolin Wang 	data->max_volt = info.constant_charge_voltage_max_uv / 1000;
627195ca170SBaolin Wang 	data->internal_resist = info.factory_internal_resistance_uohm / 1000;
628edcb1c0aSYuanjiang Yu 	data->min_volt = info.voltage_min_design_uv;
629195ca170SBaolin Wang 
630195ca170SBaolin Wang 	/*
631195ca170SBaolin Wang 	 * For SC27XX fuel gauge device, we only use one ocv-capacity
632195ca170SBaolin Wang 	 * table in normal temperature 20 Celsius.
633195ca170SBaolin Wang 	 */
634195ca170SBaolin Wang 	table = power_supply_find_ocv2cap_table(&info, 20, &data->table_len);
635195ca170SBaolin Wang 	if (!table)
636195ca170SBaolin Wang 		return -EINVAL;
637195ca170SBaolin Wang 
638195ca170SBaolin Wang 	data->cap_table = devm_kmemdup(data->dev, table,
639195ca170SBaolin Wang 				       data->table_len * sizeof(*table),
640195ca170SBaolin Wang 				       GFP_KERNEL);
641195ca170SBaolin Wang 	if (!data->cap_table) {
642195ca170SBaolin Wang 		power_supply_put_battery_info(data->battery, &info);
643195ca170SBaolin Wang 		return -ENOMEM;
644195ca170SBaolin Wang 	}
645195ca170SBaolin Wang 
646edcb1c0aSYuanjiang Yu 	data->alarm_cap = power_supply_ocv2cap_simple(data->cap_table,
647edcb1c0aSYuanjiang Yu 						      data->table_len,
648edcb1c0aSYuanjiang Yu 						      data->min_volt);
649edcb1c0aSYuanjiang Yu 
650195ca170SBaolin Wang 	power_supply_put_battery_info(data->battery, &info);
651195ca170SBaolin Wang 
65265c9fab7SBaolin Wang 	ret = sc27xx_fgu_calibration(data);
65365c9fab7SBaolin Wang 	if (ret)
65465c9fab7SBaolin Wang 		return ret;
65565c9fab7SBaolin Wang 
656195ca170SBaolin Wang 	/* Enable the FGU module */
657195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap, SC27XX_MODULE_EN0,
658195ca170SBaolin Wang 				 SC27XX_FGU_EN, SC27XX_FGU_EN);
659195ca170SBaolin Wang 	if (ret) {
660195ca170SBaolin Wang 		dev_err(data->dev, "failed to enable fgu\n");
661195ca170SBaolin Wang 		return ret;
662195ca170SBaolin Wang 	}
663195ca170SBaolin Wang 
664195ca170SBaolin Wang 	/* Enable the FGU RTC clock to make it work */
665195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap, SC27XX_CLK_EN0,
666195ca170SBaolin Wang 				 SC27XX_FGU_RTC_EN, SC27XX_FGU_RTC_EN);
667195ca170SBaolin Wang 	if (ret) {
668195ca170SBaolin Wang 		dev_err(data->dev, "failed to enable fgu RTC clock\n");
669195ca170SBaolin Wang 		goto disable_fgu;
670195ca170SBaolin Wang 	}
671195ca170SBaolin Wang 
672edcb1c0aSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR,
673edcb1c0aSYuanjiang Yu 				 SC27XX_FGU_INT_MASK, SC27XX_FGU_INT_MASK);
674edcb1c0aSYuanjiang Yu 	if (ret) {
675edcb1c0aSYuanjiang Yu 		dev_err(data->dev, "failed to clear interrupt status\n");
676edcb1c0aSYuanjiang Yu 		goto disable_clk;
677edcb1c0aSYuanjiang Yu 	}
678edcb1c0aSYuanjiang Yu 
679edcb1c0aSYuanjiang Yu 	/*
680edcb1c0aSYuanjiang Yu 	 * Set the voltage low overload threshold, which means when the battery
681edcb1c0aSYuanjiang Yu 	 * voltage is lower than this threshold, the controller will generate
682edcb1c0aSYuanjiang Yu 	 * one interrupt to notify.
683edcb1c0aSYuanjiang Yu 	 */
684edcb1c0aSYuanjiang Yu 	alarm_adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000);
685edcb1c0aSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_LOW_OVERLOAD,
686edcb1c0aSYuanjiang Yu 				 SC27XX_FGU_LOW_OVERLOAD_MASK, alarm_adc);
687edcb1c0aSYuanjiang Yu 	if (ret) {
688edcb1c0aSYuanjiang Yu 		dev_err(data->dev, "failed to set fgu low overload\n");
689edcb1c0aSYuanjiang Yu 		goto disable_clk;
690edcb1c0aSYuanjiang Yu 	}
691edcb1c0aSYuanjiang Yu 
692edcb1c0aSYuanjiang Yu 	/*
693edcb1c0aSYuanjiang Yu 	 * Set the coulomb counter delta threshold, that means when the coulomb
694edcb1c0aSYuanjiang Yu 	 * counter change is multiples of the delta threshold, the controller
695edcb1c0aSYuanjiang Yu 	 * will generate one interrupt to notify the users to update the battery
696edcb1c0aSYuanjiang Yu 	 * capacity. Now we set the delta threshold as a counter value of 1%
697edcb1c0aSYuanjiang Yu 	 * capacity.
698edcb1c0aSYuanjiang Yu 	 */
699edcb1c0aSYuanjiang Yu 	delta_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, 1);
700edcb1c0aSYuanjiang Yu 
701edcb1c0aSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTL,
702edcb1c0aSYuanjiang Yu 				 SC27XX_FGU_CLBCNT_MASK, delta_clbcnt);
703edcb1c0aSYuanjiang Yu 	if (ret) {
704edcb1c0aSYuanjiang Yu 		dev_err(data->dev, "failed to set low delta coulomb counter\n");
705edcb1c0aSYuanjiang Yu 		goto disable_clk;
706edcb1c0aSYuanjiang Yu 	}
707edcb1c0aSYuanjiang Yu 
708edcb1c0aSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTH,
709edcb1c0aSYuanjiang Yu 				 SC27XX_FGU_CLBCNT_MASK,
710edcb1c0aSYuanjiang Yu 				 delta_clbcnt >> SC27XX_FGU_CLBCNT_SHIFT);
711edcb1c0aSYuanjiang Yu 	if (ret) {
712edcb1c0aSYuanjiang Yu 		dev_err(data->dev, "failed to set high delta coulomb counter\n");
713edcb1c0aSYuanjiang Yu 		goto disable_clk;
714edcb1c0aSYuanjiang Yu 	}
715edcb1c0aSYuanjiang Yu 
716195ca170SBaolin Wang 	/*
717195ca170SBaolin Wang 	 * Get the boot battery capacity when system powers on, which is used to
718195ca170SBaolin Wang 	 * initialize the coulomb counter. After that, we can read the coulomb
719195ca170SBaolin Wang 	 * counter to measure the battery capacity.
720195ca170SBaolin Wang 	 */
721195ca170SBaolin Wang 	ret = sc27xx_fgu_get_boot_capacity(data, &data->init_cap);
722195ca170SBaolin Wang 	if (ret) {
723195ca170SBaolin Wang 		dev_err(data->dev, "failed to get boot capacity\n");
724195ca170SBaolin Wang 		goto disable_clk;
725195ca170SBaolin Wang 	}
726195ca170SBaolin Wang 
727195ca170SBaolin Wang 	/*
728195ca170SBaolin Wang 	 * Convert battery capacity to the corresponding initial coulomb counter
729195ca170SBaolin Wang 	 * and set into coulomb counter registers.
730195ca170SBaolin Wang 	 */
731195ca170SBaolin Wang 	data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap);
732195ca170SBaolin Wang 	ret = sc27xx_fgu_set_clbcnt(data, data->init_clbcnt);
733195ca170SBaolin Wang 	if (ret) {
734195ca170SBaolin Wang 		dev_err(data->dev, "failed to initialize coulomb counter\n");
735195ca170SBaolin Wang 		goto disable_clk;
736195ca170SBaolin Wang 	}
737195ca170SBaolin Wang 
738195ca170SBaolin Wang 	return 0;
739195ca170SBaolin Wang 
740195ca170SBaolin Wang disable_clk:
741195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0);
742195ca170SBaolin Wang disable_fgu:
743195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0);
744195ca170SBaolin Wang 
745195ca170SBaolin Wang 	return ret;
746195ca170SBaolin Wang }
747195ca170SBaolin Wang 
748195ca170SBaolin Wang static int sc27xx_fgu_probe(struct platform_device *pdev)
749195ca170SBaolin Wang {
750195ca170SBaolin Wang 	struct device_node *np = pdev->dev.of_node;
751195ca170SBaolin Wang 	struct power_supply_config fgu_cfg = { };
752195ca170SBaolin Wang 	struct sc27xx_fgu_data *data;
753195ca170SBaolin Wang 	int ret, irq;
754195ca170SBaolin Wang 
755195ca170SBaolin Wang 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
756195ca170SBaolin Wang 	if (!data)
757195ca170SBaolin Wang 		return -ENOMEM;
758195ca170SBaolin Wang 
759195ca170SBaolin Wang 	data->regmap = dev_get_regmap(pdev->dev.parent, NULL);
760195ca170SBaolin Wang 	if (!data->regmap) {
761195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to get regmap\n");
762195ca170SBaolin Wang 		return -ENODEV;
763195ca170SBaolin Wang 	}
764195ca170SBaolin Wang 
765195ca170SBaolin Wang 	ret = device_property_read_u32(&pdev->dev, "reg", &data->base);
766195ca170SBaolin Wang 	if (ret) {
767195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to get fgu address\n");
768195ca170SBaolin Wang 		return ret;
769195ca170SBaolin Wang 	}
770195ca170SBaolin Wang 
771195ca170SBaolin Wang 	data->channel = devm_iio_channel_get(&pdev->dev, "bat-temp");
772195ca170SBaolin Wang 	if (IS_ERR(data->channel)) {
773195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to get IIO channel\n");
774195ca170SBaolin Wang 		return PTR_ERR(data->channel);
775195ca170SBaolin Wang 	}
776195ca170SBaolin Wang 
777195ca170SBaolin Wang 	data->gpiod = devm_gpiod_get(&pdev->dev, "bat-detect", GPIOD_IN);
778195ca170SBaolin Wang 	if (IS_ERR(data->gpiod)) {
779195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to get battery detection GPIO\n");
780195ca170SBaolin Wang 		return PTR_ERR(data->gpiod);
781195ca170SBaolin Wang 	}
782195ca170SBaolin Wang 
783195ca170SBaolin Wang 	ret = gpiod_get_value_cansleep(data->gpiod);
784195ca170SBaolin Wang 	if (ret < 0) {
785195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to get gpio state\n");
786195ca170SBaolin Wang 		return ret;
787195ca170SBaolin Wang 	}
788195ca170SBaolin Wang 
789195ca170SBaolin Wang 	data->bat_present = !!ret;
790195ca170SBaolin Wang 	mutex_init(&data->lock);
791195ca170SBaolin Wang 	data->dev = &pdev->dev;
792*e2fb615bSYuanjiang Yu 	platform_set_drvdata(pdev, data);
793195ca170SBaolin Wang 
794195ca170SBaolin Wang 	fgu_cfg.drv_data = data;
795195ca170SBaolin Wang 	fgu_cfg.of_node = np;
796195ca170SBaolin Wang 	data->battery = devm_power_supply_register(&pdev->dev, &sc27xx_fgu_desc,
797195ca170SBaolin Wang 						   &fgu_cfg);
798195ca170SBaolin Wang 	if (IS_ERR(data->battery)) {
799195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to register power supply\n");
800195ca170SBaolin Wang 		return PTR_ERR(data->battery);
801195ca170SBaolin Wang 	}
802195ca170SBaolin Wang 
803195ca170SBaolin Wang 	ret = sc27xx_fgu_hw_init(data);
804195ca170SBaolin Wang 	if (ret) {
805195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to initialize fgu hardware\n");
806195ca170SBaolin Wang 		return ret;
807195ca170SBaolin Wang 	}
808195ca170SBaolin Wang 
809195ca170SBaolin Wang 	ret = devm_add_action(&pdev->dev, sc27xx_fgu_disable, data);
810195ca170SBaolin Wang 	if (ret) {
811195ca170SBaolin Wang 		sc27xx_fgu_disable(data);
812195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to add fgu disable action\n");
813195ca170SBaolin Wang 		return ret;
814195ca170SBaolin Wang 	}
815195ca170SBaolin Wang 
816edcb1c0aSYuanjiang Yu 	irq = platform_get_irq(pdev, 0);
817edcb1c0aSYuanjiang Yu 	if (irq < 0) {
818edcb1c0aSYuanjiang Yu 		dev_err(&pdev->dev, "no irq resource specified\n");
819edcb1c0aSYuanjiang Yu 		return irq;
820edcb1c0aSYuanjiang Yu 	}
821edcb1c0aSYuanjiang Yu 
822edcb1c0aSYuanjiang Yu 	ret = devm_request_threaded_irq(data->dev, irq, NULL,
823edcb1c0aSYuanjiang Yu 					sc27xx_fgu_interrupt,
824edcb1c0aSYuanjiang Yu 					IRQF_NO_SUSPEND | IRQF_ONESHOT,
825edcb1c0aSYuanjiang Yu 					pdev->name, data);
826edcb1c0aSYuanjiang Yu 	if (ret) {
827edcb1c0aSYuanjiang Yu 		dev_err(data->dev, "failed to request fgu IRQ\n");
828edcb1c0aSYuanjiang Yu 		return ret;
829edcb1c0aSYuanjiang Yu 	}
830edcb1c0aSYuanjiang Yu 
831195ca170SBaolin Wang 	irq = gpiod_to_irq(data->gpiod);
832195ca170SBaolin Wang 	if (irq < 0) {
833195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to translate GPIO to IRQ\n");
834195ca170SBaolin Wang 		return irq;
835195ca170SBaolin Wang 	}
836195ca170SBaolin Wang 
837195ca170SBaolin Wang 	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
838195ca170SBaolin Wang 					sc27xx_fgu_bat_detection,
839195ca170SBaolin Wang 					IRQF_ONESHOT | IRQF_TRIGGER_RISING |
840195ca170SBaolin Wang 					IRQF_TRIGGER_FALLING,
841195ca170SBaolin Wang 					pdev->name, data);
842195ca170SBaolin Wang 	if (ret) {
843195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to request IRQ\n");
844195ca170SBaolin Wang 		return ret;
845195ca170SBaolin Wang 	}
846195ca170SBaolin Wang 
847195ca170SBaolin Wang 	return 0;
848195ca170SBaolin Wang }
849195ca170SBaolin Wang 
850*e2fb615bSYuanjiang Yu #ifdef CONFIG_PM_SLEEP
851*e2fb615bSYuanjiang Yu static int sc27xx_fgu_resume(struct device *dev)
852*e2fb615bSYuanjiang Yu {
853*e2fb615bSYuanjiang Yu 	struct sc27xx_fgu_data *data = dev_get_drvdata(dev);
854*e2fb615bSYuanjiang Yu 	int ret;
855*e2fb615bSYuanjiang Yu 
856*e2fb615bSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN,
857*e2fb615bSYuanjiang Yu 				 SC27XX_FGU_LOW_OVERLOAD_INT |
858*e2fb615bSYuanjiang Yu 				 SC27XX_FGU_CLBCNT_DELTA_INT, 0);
859*e2fb615bSYuanjiang Yu 	if (ret) {
860*e2fb615bSYuanjiang Yu 		dev_err(data->dev, "failed to disable fgu interrupts\n");
861*e2fb615bSYuanjiang Yu 		return ret;
862*e2fb615bSYuanjiang Yu 	}
863*e2fb615bSYuanjiang Yu 
864*e2fb615bSYuanjiang Yu 	return 0;
865*e2fb615bSYuanjiang Yu }
866*e2fb615bSYuanjiang Yu 
867*e2fb615bSYuanjiang Yu static int sc27xx_fgu_suspend(struct device *dev)
868*e2fb615bSYuanjiang Yu {
869*e2fb615bSYuanjiang Yu 	struct sc27xx_fgu_data *data = dev_get_drvdata(dev);
870*e2fb615bSYuanjiang Yu 	int ret, status, ocv;
871*e2fb615bSYuanjiang Yu 
872*e2fb615bSYuanjiang Yu 	ret = sc27xx_fgu_get_status(data, &status);
873*e2fb615bSYuanjiang Yu 	if (ret)
874*e2fb615bSYuanjiang Yu 		return ret;
875*e2fb615bSYuanjiang Yu 
876*e2fb615bSYuanjiang Yu 	/*
877*e2fb615bSYuanjiang Yu 	 * If we are charging, then no need to enable the FGU interrupts to
878*e2fb615bSYuanjiang Yu 	 * adjust the battery capacity.
879*e2fb615bSYuanjiang Yu 	 */
880*e2fb615bSYuanjiang Yu 	if (status != POWER_SUPPLY_STATUS_NOT_CHARGING)
881*e2fb615bSYuanjiang Yu 		return 0;
882*e2fb615bSYuanjiang Yu 
883*e2fb615bSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN,
884*e2fb615bSYuanjiang Yu 				 SC27XX_FGU_LOW_OVERLOAD_INT,
885*e2fb615bSYuanjiang Yu 				 SC27XX_FGU_LOW_OVERLOAD_INT);
886*e2fb615bSYuanjiang Yu 	if (ret) {
887*e2fb615bSYuanjiang Yu 		dev_err(data->dev, "failed to enable low voltage interrupt\n");
888*e2fb615bSYuanjiang Yu 		return ret;
889*e2fb615bSYuanjiang Yu 	}
890*e2fb615bSYuanjiang Yu 
891*e2fb615bSYuanjiang Yu 	ret = sc27xx_fgu_get_vbat_ocv(data, &ocv);
892*e2fb615bSYuanjiang Yu 	if (ret)
893*e2fb615bSYuanjiang Yu 		goto disable_int;
894*e2fb615bSYuanjiang Yu 
895*e2fb615bSYuanjiang Yu 	/*
896*e2fb615bSYuanjiang Yu 	 * If current OCV is less than the minimum voltage, we should enable the
897*e2fb615bSYuanjiang Yu 	 * coulomb counter threshold interrupt to notify events to adjust the
898*e2fb615bSYuanjiang Yu 	 * battery capacity.
899*e2fb615bSYuanjiang Yu 	 */
900*e2fb615bSYuanjiang Yu 	if (ocv < data->min_volt) {
901*e2fb615bSYuanjiang Yu 		ret = regmap_update_bits(data->regmap,
902*e2fb615bSYuanjiang Yu 					 data->base + SC27XX_FGU_INT_EN,
903*e2fb615bSYuanjiang Yu 					 SC27XX_FGU_CLBCNT_DELTA_INT,
904*e2fb615bSYuanjiang Yu 					 SC27XX_FGU_CLBCNT_DELTA_INT);
905*e2fb615bSYuanjiang Yu 		if (ret) {
906*e2fb615bSYuanjiang Yu 			dev_err(data->dev,
907*e2fb615bSYuanjiang Yu 				"failed to enable coulomb threshold int\n");
908*e2fb615bSYuanjiang Yu 			goto disable_int;
909*e2fb615bSYuanjiang Yu 		}
910*e2fb615bSYuanjiang Yu 	}
911*e2fb615bSYuanjiang Yu 
912*e2fb615bSYuanjiang Yu 	return 0;
913*e2fb615bSYuanjiang Yu 
914*e2fb615bSYuanjiang Yu disable_int:
915*e2fb615bSYuanjiang Yu 	regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN,
916*e2fb615bSYuanjiang Yu 			   SC27XX_FGU_LOW_OVERLOAD_INT, 0);
917*e2fb615bSYuanjiang Yu 	return ret;
918*e2fb615bSYuanjiang Yu }
919*e2fb615bSYuanjiang Yu #endif
920*e2fb615bSYuanjiang Yu 
921*e2fb615bSYuanjiang Yu static const struct dev_pm_ops sc27xx_fgu_pm_ops = {
922*e2fb615bSYuanjiang Yu 	SET_SYSTEM_SLEEP_PM_OPS(sc27xx_fgu_suspend, sc27xx_fgu_resume)
923*e2fb615bSYuanjiang Yu };
924*e2fb615bSYuanjiang Yu 
925195ca170SBaolin Wang static const struct of_device_id sc27xx_fgu_of_match[] = {
926195ca170SBaolin Wang 	{ .compatible = "sprd,sc2731-fgu", },
927195ca170SBaolin Wang 	{ }
928195ca170SBaolin Wang };
929195ca170SBaolin Wang 
930195ca170SBaolin Wang static struct platform_driver sc27xx_fgu_driver = {
931195ca170SBaolin Wang 	.probe = sc27xx_fgu_probe,
932195ca170SBaolin Wang 	.driver = {
933195ca170SBaolin Wang 		.name = "sc27xx-fgu",
934195ca170SBaolin Wang 		.of_match_table = sc27xx_fgu_of_match,
935*e2fb615bSYuanjiang Yu 		.pm = &sc27xx_fgu_pm_ops,
936195ca170SBaolin Wang 	}
937195ca170SBaolin Wang };
938195ca170SBaolin Wang 
939195ca170SBaolin Wang module_platform_driver(sc27xx_fgu_driver);
940195ca170SBaolin Wang 
941195ca170SBaolin Wang MODULE_DESCRIPTION("Spreadtrum SC27XX PMICs Fual Gauge Unit Driver");
942195ca170SBaolin Wang MODULE_LICENSE("GPL v2");
943