xref: /linux/drivers/power/supply/sc27xx_fuel_gauge.c (revision 4a040e7c72e6bd2ffc023ecca31e336aec9dbb87)
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
42*4a040e7cSYuanjiang Yu #define SC27XX_FGU_USER_AREA_SET	0xa0
43*4a040e7cSYuanjiang Yu #define SC27XX_FGU_USER_AREA_CLEAR	0xa4
44*4a040e7cSYuanjiang Yu #define SC27XX_FGU_USER_AREA_STATUS	0xa8
45195ca170SBaolin Wang 
46195ca170SBaolin Wang #define SC27XX_WRITE_SELCLB_EN		BIT(0)
47195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_MASK		GENMASK(15, 0)
48195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_SHIFT		16
49edcb1c0aSYuanjiang Yu #define SC27XX_FGU_LOW_OVERLOAD_MASK	GENMASK(12, 0)
50edcb1c0aSYuanjiang Yu 
51edcb1c0aSYuanjiang Yu #define SC27XX_FGU_INT_MASK		GENMASK(9, 0)
52edcb1c0aSYuanjiang Yu #define SC27XX_FGU_LOW_OVERLOAD_INT	BIT(0)
53edcb1c0aSYuanjiang Yu #define SC27XX_FGU_CLBCNT_DELTA_INT	BIT(2)
54195ca170SBaolin Wang 
55*4a040e7cSYuanjiang Yu #define SC27XX_FGU_MODE_AREA_MASK	GENMASK(15, 12)
56*4a040e7cSYuanjiang Yu #define SC27XX_FGU_CAP_AREA_MASK	GENMASK(11, 0)
57*4a040e7cSYuanjiang Yu #define SC27XX_FGU_MODE_AREA_SHIFT	12
58*4a040e7cSYuanjiang Yu 
59*4a040e7cSYuanjiang Yu #define SC27XX_FGU_FIRST_POWERTON	GENMASK(3, 0)
60*4a040e7cSYuanjiang Yu #define SC27XX_FGU_DEFAULT_CAP		GENMASK(11, 0)
61*4a040e7cSYuanjiang Yu #define SC27XX_FGU_NORMAIL_POWERTON	0x5
62*4a040e7cSYuanjiang Yu 
63195ca170SBaolin Wang #define SC27XX_FGU_CUR_BASIC_ADC	8192
64195ca170SBaolin Wang #define SC27XX_FGU_SAMPLE_HZ		2
65195ca170SBaolin Wang 
66195ca170SBaolin Wang /*
67195ca170SBaolin Wang  * struct sc27xx_fgu_data: describe the FGU device
68195ca170SBaolin Wang  * @regmap: regmap for register access
69195ca170SBaolin Wang  * @dev: platform device
70195ca170SBaolin Wang  * @battery: battery power supply
71195ca170SBaolin Wang  * @base: the base offset for the controller
72195ca170SBaolin Wang  * @lock: protect the structure
73195ca170SBaolin Wang  * @gpiod: GPIO for battery detection
74195ca170SBaolin Wang  * @channel: IIO channel to get battery temperature
75195ca170SBaolin Wang  * @internal_resist: the battery internal resistance in mOhm
76195ca170SBaolin Wang  * @total_cap: the total capacity of the battery in mAh
77195ca170SBaolin Wang  * @init_cap: the initial capacity of the battery in mAh
78edcb1c0aSYuanjiang Yu  * @alarm_cap: the alarm capacity
79195ca170SBaolin Wang  * @init_clbcnt: the initial coulomb counter
80195ca170SBaolin Wang  * @max_volt: the maximum constant input voltage in millivolt
81edcb1c0aSYuanjiang Yu  * @min_volt: the minimum drained battery voltage in microvolt
82195ca170SBaolin Wang  * @table_len: the capacity table length
8365c9fab7SBaolin Wang  * @cur_1000ma_adc: ADC value corresponding to 1000 mA
8465c9fab7SBaolin Wang  * @vol_1000mv_adc: ADC value corresponding to 1000 mV
85195ca170SBaolin Wang  * @cap_table: capacity table with corresponding ocv
86195ca170SBaolin Wang  */
87195ca170SBaolin Wang struct sc27xx_fgu_data {
88195ca170SBaolin Wang 	struct regmap *regmap;
89195ca170SBaolin Wang 	struct device *dev;
90195ca170SBaolin Wang 	struct power_supply *battery;
91195ca170SBaolin Wang 	u32 base;
92195ca170SBaolin Wang 	struct mutex lock;
93195ca170SBaolin Wang 	struct gpio_desc *gpiod;
94195ca170SBaolin Wang 	struct iio_channel *channel;
95195ca170SBaolin Wang 	bool bat_present;
96195ca170SBaolin Wang 	int internal_resist;
97195ca170SBaolin Wang 	int total_cap;
98195ca170SBaolin Wang 	int init_cap;
99edcb1c0aSYuanjiang Yu 	int alarm_cap;
100195ca170SBaolin Wang 	int init_clbcnt;
101195ca170SBaolin Wang 	int max_volt;
102edcb1c0aSYuanjiang Yu 	int min_volt;
103195ca170SBaolin Wang 	int table_len;
10465c9fab7SBaolin Wang 	int cur_1000ma_adc;
10565c9fab7SBaolin Wang 	int vol_1000mv_adc;
106195ca170SBaolin Wang 	struct power_supply_battery_ocv_table *cap_table;
107195ca170SBaolin Wang };
108195ca170SBaolin Wang 
109edcb1c0aSYuanjiang Yu static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity);
110edcb1c0aSYuanjiang Yu 
111195ca170SBaolin Wang static const char * const sc27xx_charger_supply_name[] = {
112195ca170SBaolin Wang 	"sc2731_charger",
113195ca170SBaolin Wang 	"sc2720_charger",
114195ca170SBaolin Wang 	"sc2721_charger",
115195ca170SBaolin Wang 	"sc2723_charger",
116195ca170SBaolin Wang };
117195ca170SBaolin Wang 
11865c9fab7SBaolin Wang static int sc27xx_fgu_adc_to_current(struct sc27xx_fgu_data *data, int adc)
119195ca170SBaolin Wang {
12065c9fab7SBaolin Wang 	return DIV_ROUND_CLOSEST(adc * 1000, data->cur_1000ma_adc);
121195ca170SBaolin Wang }
122195ca170SBaolin Wang 
12365c9fab7SBaolin Wang static int sc27xx_fgu_adc_to_voltage(struct sc27xx_fgu_data *data, int adc)
124195ca170SBaolin Wang {
12565c9fab7SBaolin Wang 	return DIV_ROUND_CLOSEST(adc * 1000, data->vol_1000mv_adc);
126195ca170SBaolin Wang }
127195ca170SBaolin Wang 
128edcb1c0aSYuanjiang Yu static int sc27xx_fgu_voltage_to_adc(struct sc27xx_fgu_data *data, int vol)
129edcb1c0aSYuanjiang Yu {
130edcb1c0aSYuanjiang Yu 	return DIV_ROUND_CLOSEST(vol * data->vol_1000mv_adc, 1000);
131edcb1c0aSYuanjiang Yu }
132edcb1c0aSYuanjiang Yu 
133*4a040e7cSYuanjiang Yu static bool sc27xx_fgu_is_first_poweron(struct sc27xx_fgu_data *data)
134*4a040e7cSYuanjiang Yu {
135*4a040e7cSYuanjiang Yu 	int ret, status, cap, mode;
136*4a040e7cSYuanjiang Yu 
137*4a040e7cSYuanjiang Yu 	ret = regmap_read(data->regmap,
138*4a040e7cSYuanjiang Yu 			  data->base + SC27XX_FGU_USER_AREA_STATUS, &status);
139*4a040e7cSYuanjiang Yu 	if (ret)
140*4a040e7cSYuanjiang Yu 		return false;
141*4a040e7cSYuanjiang Yu 
142*4a040e7cSYuanjiang Yu 	/*
143*4a040e7cSYuanjiang Yu 	 * We use low 4 bits to save the last battery capacity and high 12 bits
144*4a040e7cSYuanjiang Yu 	 * to save the system boot mode.
145*4a040e7cSYuanjiang Yu 	 */
146*4a040e7cSYuanjiang Yu 	mode = (status & SC27XX_FGU_MODE_AREA_MASK) >> SC27XX_FGU_MODE_AREA_SHIFT;
147*4a040e7cSYuanjiang Yu 	cap = status & SC27XX_FGU_CAP_AREA_MASK;
148*4a040e7cSYuanjiang Yu 
149*4a040e7cSYuanjiang Yu 	/*
150*4a040e7cSYuanjiang Yu 	 * When FGU has been powered down, the user area registers became
151*4a040e7cSYuanjiang Yu 	 * default value (0xffff), which can be used to valid if the system is
152*4a040e7cSYuanjiang Yu 	 * first power on or not.
153*4a040e7cSYuanjiang Yu 	 */
154*4a040e7cSYuanjiang Yu 	if (mode == SC27XX_FGU_FIRST_POWERTON || cap == SC27XX_FGU_DEFAULT_CAP)
155*4a040e7cSYuanjiang Yu 		return true;
156*4a040e7cSYuanjiang Yu 
157*4a040e7cSYuanjiang Yu 	return false;
158*4a040e7cSYuanjiang Yu }
159*4a040e7cSYuanjiang Yu 
160*4a040e7cSYuanjiang Yu static int sc27xx_fgu_save_boot_mode(struct sc27xx_fgu_data *data,
161*4a040e7cSYuanjiang Yu 				     int boot_mode)
162*4a040e7cSYuanjiang Yu {
163*4a040e7cSYuanjiang Yu 	int ret;
164*4a040e7cSYuanjiang Yu 
165*4a040e7cSYuanjiang Yu 	ret = regmap_update_bits(data->regmap,
166*4a040e7cSYuanjiang Yu 				 data->base + SC27XX_FGU_USER_AREA_CLEAR,
167*4a040e7cSYuanjiang Yu 				 SC27XX_FGU_MODE_AREA_MASK,
168*4a040e7cSYuanjiang Yu 				 SC27XX_FGU_MODE_AREA_MASK);
169*4a040e7cSYuanjiang Yu 	if (ret)
170*4a040e7cSYuanjiang Yu 		return ret;
171*4a040e7cSYuanjiang Yu 
172*4a040e7cSYuanjiang Yu 	return regmap_update_bits(data->regmap,
173*4a040e7cSYuanjiang Yu 				  data->base + SC27XX_FGU_USER_AREA_SET,
174*4a040e7cSYuanjiang Yu 				  SC27XX_FGU_MODE_AREA_MASK,
175*4a040e7cSYuanjiang Yu 				  boot_mode << SC27XX_FGU_MODE_AREA_SHIFT);
176*4a040e7cSYuanjiang Yu }
177*4a040e7cSYuanjiang Yu 
178*4a040e7cSYuanjiang Yu static int sc27xx_fgu_save_last_cap(struct sc27xx_fgu_data *data, int cap)
179*4a040e7cSYuanjiang Yu {
180*4a040e7cSYuanjiang Yu 	int ret;
181*4a040e7cSYuanjiang Yu 
182*4a040e7cSYuanjiang Yu 	ret = regmap_update_bits(data->regmap,
183*4a040e7cSYuanjiang Yu 				 data->base + SC27XX_FGU_USER_AREA_CLEAR,
184*4a040e7cSYuanjiang Yu 				 SC27XX_FGU_CAP_AREA_MASK,
185*4a040e7cSYuanjiang Yu 				 SC27XX_FGU_CAP_AREA_MASK);
186*4a040e7cSYuanjiang Yu 	if (ret)
187*4a040e7cSYuanjiang Yu 		return ret;
188*4a040e7cSYuanjiang Yu 
189*4a040e7cSYuanjiang Yu 	return regmap_update_bits(data->regmap,
190*4a040e7cSYuanjiang Yu 				  data->base + SC27XX_FGU_USER_AREA_SET,
191*4a040e7cSYuanjiang Yu 				  SC27XX_FGU_CAP_AREA_MASK, cap);
192*4a040e7cSYuanjiang Yu }
193*4a040e7cSYuanjiang Yu 
194*4a040e7cSYuanjiang Yu static int sc27xx_fgu_read_last_cap(struct sc27xx_fgu_data *data, int *cap)
195*4a040e7cSYuanjiang Yu {
196*4a040e7cSYuanjiang Yu 	int ret, value;
197*4a040e7cSYuanjiang Yu 
198*4a040e7cSYuanjiang Yu 	ret = regmap_read(data->regmap,
199*4a040e7cSYuanjiang Yu 			  data->base + SC27XX_FGU_USER_AREA_STATUS, &value);
200*4a040e7cSYuanjiang Yu 	if (ret)
201*4a040e7cSYuanjiang Yu 		return ret;
202*4a040e7cSYuanjiang Yu 
203*4a040e7cSYuanjiang Yu 	*cap = value & SC27XX_FGU_CAP_AREA_MASK;
204*4a040e7cSYuanjiang Yu 	return 0;
205*4a040e7cSYuanjiang Yu }
206*4a040e7cSYuanjiang Yu 
207195ca170SBaolin Wang /*
208195ca170SBaolin Wang  * When system boots on, we can not read battery capacity from coulomb
209195ca170SBaolin Wang  * registers, since now the coulomb registers are invalid. So we should
210195ca170SBaolin Wang  * calculate the battery open circuit voltage, and get current battery
211195ca170SBaolin Wang  * capacity according to the capacity table.
212195ca170SBaolin Wang  */
213195ca170SBaolin Wang static int sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data *data, int *cap)
214195ca170SBaolin Wang {
215195ca170SBaolin Wang 	int volt, cur, oci, ocv, ret;
216*4a040e7cSYuanjiang Yu 	bool is_first_poweron = sc27xx_fgu_is_first_poweron(data);
217*4a040e7cSYuanjiang Yu 
218*4a040e7cSYuanjiang Yu 	/*
219*4a040e7cSYuanjiang Yu 	 * If system is not the first power on, we should use the last saved
220*4a040e7cSYuanjiang Yu 	 * battery capacity as the initial battery capacity. Otherwise we should
221*4a040e7cSYuanjiang Yu 	 * re-calculate the initial battery capacity.
222*4a040e7cSYuanjiang Yu 	 */
223*4a040e7cSYuanjiang Yu 	if (!is_first_poweron) {
224*4a040e7cSYuanjiang Yu 		ret = sc27xx_fgu_read_last_cap(data, cap);
225*4a040e7cSYuanjiang Yu 		if (ret)
226*4a040e7cSYuanjiang Yu 			return ret;
227*4a040e7cSYuanjiang Yu 
228*4a040e7cSYuanjiang Yu 		return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON);
229*4a040e7cSYuanjiang Yu 	}
230195ca170SBaolin Wang 
231195ca170SBaolin Wang 	/*
232195ca170SBaolin Wang 	 * After system booting on, the SC27XX_FGU_CLBCNT_QMAXL register saved
233195ca170SBaolin Wang 	 * the first sampled open circuit current.
234195ca170SBaolin Wang 	 */
235195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_QMAXL,
236195ca170SBaolin Wang 			  &cur);
237195ca170SBaolin Wang 	if (ret)
238195ca170SBaolin Wang 		return ret;
239195ca170SBaolin Wang 
240195ca170SBaolin Wang 	cur <<= 1;
24165c9fab7SBaolin Wang 	oci = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC);
242195ca170SBaolin Wang 
243195ca170SBaolin Wang 	/*
244195ca170SBaolin Wang 	 * Should get the OCV from SC27XX_FGU_POCV register at the system
245195ca170SBaolin Wang 	 * beginning. It is ADC values reading from registers which need to
246195ca170SBaolin Wang 	 * convert the corresponding voltage.
247195ca170SBaolin Wang 	 */
248195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_POCV, &volt);
249195ca170SBaolin Wang 	if (ret)
250195ca170SBaolin Wang 		return ret;
251195ca170SBaolin Wang 
25265c9fab7SBaolin Wang 	volt = sc27xx_fgu_adc_to_voltage(data, volt);
253195ca170SBaolin Wang 	ocv = volt * 1000 - oci * data->internal_resist;
254195ca170SBaolin Wang 
255195ca170SBaolin Wang 	/*
256195ca170SBaolin Wang 	 * Parse the capacity table to look up the correct capacity percent
257195ca170SBaolin Wang 	 * according to current battery's corresponding OCV values.
258195ca170SBaolin Wang 	 */
259195ca170SBaolin Wang 	*cap = power_supply_ocv2cap_simple(data->cap_table, data->table_len,
260195ca170SBaolin Wang 					   ocv);
261195ca170SBaolin Wang 
262*4a040e7cSYuanjiang Yu 	ret = sc27xx_fgu_save_last_cap(data, *cap);
263*4a040e7cSYuanjiang Yu 	if (ret)
264*4a040e7cSYuanjiang Yu 		return ret;
265*4a040e7cSYuanjiang Yu 
266*4a040e7cSYuanjiang Yu 	return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON);
267195ca170SBaolin Wang }
268195ca170SBaolin Wang 
269195ca170SBaolin Wang static int sc27xx_fgu_set_clbcnt(struct sc27xx_fgu_data *data, int clbcnt)
270195ca170SBaolin Wang {
271195ca170SBaolin Wang 	int ret;
272195ca170SBaolin Wang 
273195ca170SBaolin Wang 	clbcnt *= SC27XX_FGU_SAMPLE_HZ;
274195ca170SBaolin Wang 
275195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap,
276195ca170SBaolin Wang 				 data->base + SC27XX_FGU_CLBCNT_SETL,
277195ca170SBaolin Wang 				 SC27XX_FGU_CLBCNT_MASK, clbcnt);
278195ca170SBaolin Wang 	if (ret)
279195ca170SBaolin Wang 		return ret;
280195ca170SBaolin Wang 
281195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap,
282195ca170SBaolin Wang 				 data->base + SC27XX_FGU_CLBCNT_SETH,
283195ca170SBaolin Wang 				 SC27XX_FGU_CLBCNT_MASK,
284195ca170SBaolin Wang 				 clbcnt >> SC27XX_FGU_CLBCNT_SHIFT);
285195ca170SBaolin Wang 	if (ret)
286195ca170SBaolin Wang 		return ret;
287195ca170SBaolin Wang 
288195ca170SBaolin Wang 	return regmap_update_bits(data->regmap, data->base + SC27XX_FGU_START,
289195ca170SBaolin Wang 				 SC27XX_WRITE_SELCLB_EN,
290195ca170SBaolin Wang 				 SC27XX_WRITE_SELCLB_EN);
291195ca170SBaolin Wang }
292195ca170SBaolin Wang 
293195ca170SBaolin Wang static int sc27xx_fgu_get_clbcnt(struct sc27xx_fgu_data *data, int *clb_cnt)
294195ca170SBaolin Wang {
295195ca170SBaolin Wang 	int ccl, cch, ret;
296195ca170SBaolin Wang 
297195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALL,
298195ca170SBaolin Wang 			  &ccl);
299195ca170SBaolin Wang 	if (ret)
300195ca170SBaolin Wang 		return ret;
301195ca170SBaolin Wang 
302195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALH,
303195ca170SBaolin Wang 			  &cch);
304195ca170SBaolin Wang 	if (ret)
305195ca170SBaolin Wang 		return ret;
306195ca170SBaolin Wang 
307195ca170SBaolin Wang 	*clb_cnt = ccl & SC27XX_FGU_CLBCNT_MASK;
308195ca170SBaolin Wang 	*clb_cnt |= (cch & SC27XX_FGU_CLBCNT_MASK) << SC27XX_FGU_CLBCNT_SHIFT;
309195ca170SBaolin Wang 	*clb_cnt /= SC27XX_FGU_SAMPLE_HZ;
310195ca170SBaolin Wang 
311195ca170SBaolin Wang 	return 0;
312195ca170SBaolin Wang }
313195ca170SBaolin Wang 
314195ca170SBaolin Wang static int sc27xx_fgu_get_capacity(struct sc27xx_fgu_data *data, int *cap)
315195ca170SBaolin Wang {
316195ca170SBaolin Wang 	int ret, cur_clbcnt, delta_clbcnt, delta_cap, temp;
317195ca170SBaolin Wang 
318195ca170SBaolin Wang 	/* Get current coulomb counters firstly */
319195ca170SBaolin Wang 	ret = sc27xx_fgu_get_clbcnt(data, &cur_clbcnt);
320195ca170SBaolin Wang 	if (ret)
321195ca170SBaolin Wang 		return ret;
322195ca170SBaolin Wang 
323195ca170SBaolin Wang 	delta_clbcnt = cur_clbcnt - data->init_clbcnt;
324195ca170SBaolin Wang 
325195ca170SBaolin Wang 	/*
326195ca170SBaolin Wang 	 * Convert coulomb counter to delta capacity (mAh), and set multiplier
327195ca170SBaolin Wang 	 * as 100 to improve the precision.
328195ca170SBaolin Wang 	 */
329195ca170SBaolin Wang 	temp = DIV_ROUND_CLOSEST(delta_clbcnt, 360);
33065c9fab7SBaolin Wang 	temp = sc27xx_fgu_adc_to_current(data, temp);
331195ca170SBaolin Wang 
332195ca170SBaolin Wang 	/*
333195ca170SBaolin Wang 	 * Convert to capacity percent of the battery total capacity,
334195ca170SBaolin Wang 	 * and multiplier is 100 too.
335195ca170SBaolin Wang 	 */
336195ca170SBaolin Wang 	delta_cap = DIV_ROUND_CLOSEST(temp * 100, data->total_cap);
337195ca170SBaolin Wang 	*cap = delta_cap + data->init_cap;
338195ca170SBaolin Wang 
339195ca170SBaolin Wang 	return 0;
340195ca170SBaolin Wang }
341195ca170SBaolin Wang 
342195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_vol(struct sc27xx_fgu_data *data, int *val)
343195ca170SBaolin Wang {
344195ca170SBaolin Wang 	int ret, vol;
345195ca170SBaolin Wang 
346195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_VOLTAGE, &vol);
347195ca170SBaolin Wang 	if (ret)
348195ca170SBaolin Wang 		return ret;
349195ca170SBaolin Wang 
350195ca170SBaolin Wang 	/*
351195ca170SBaolin Wang 	 * It is ADC values reading from registers which need to convert to
352195ca170SBaolin Wang 	 * corresponding voltage values.
353195ca170SBaolin Wang 	 */
35465c9fab7SBaolin Wang 	*val = sc27xx_fgu_adc_to_voltage(data, vol);
355195ca170SBaolin Wang 
356195ca170SBaolin Wang 	return 0;
357195ca170SBaolin Wang }
358195ca170SBaolin Wang 
359195ca170SBaolin Wang static int sc27xx_fgu_get_current(struct sc27xx_fgu_data *data, int *val)
360195ca170SBaolin Wang {
361195ca170SBaolin Wang 	int ret, cur;
362195ca170SBaolin Wang 
363195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CURRENT, &cur);
364195ca170SBaolin Wang 	if (ret)
365195ca170SBaolin Wang 		return ret;
366195ca170SBaolin Wang 
367195ca170SBaolin Wang 	/*
368195ca170SBaolin Wang 	 * It is ADC values reading from registers which need to convert to
369195ca170SBaolin Wang 	 * corresponding current values.
370195ca170SBaolin Wang 	 */
37165c9fab7SBaolin Wang 	*val = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC);
372195ca170SBaolin Wang 
373195ca170SBaolin Wang 	return 0;
374195ca170SBaolin Wang }
375195ca170SBaolin Wang 
376195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val)
377195ca170SBaolin Wang {
378195ca170SBaolin Wang 	int vol, cur, ret;
379195ca170SBaolin Wang 
380195ca170SBaolin Wang 	ret = sc27xx_fgu_get_vbat_vol(data, &vol);
381195ca170SBaolin Wang 	if (ret)
382195ca170SBaolin Wang 		return ret;
383195ca170SBaolin Wang 
384195ca170SBaolin Wang 	ret = sc27xx_fgu_get_current(data, &cur);
385195ca170SBaolin Wang 	if (ret)
386195ca170SBaolin Wang 		return ret;
387195ca170SBaolin Wang 
388195ca170SBaolin Wang 	/* Return the battery OCV in micro volts. */
389195ca170SBaolin Wang 	*val = vol * 1000 - cur * data->internal_resist;
390195ca170SBaolin Wang 
391195ca170SBaolin Wang 	return 0;
392195ca170SBaolin Wang }
393195ca170SBaolin Wang 
394195ca170SBaolin Wang static int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp)
395195ca170SBaolin Wang {
396195ca170SBaolin Wang 	return iio_read_channel_processed(data->channel, temp);
397195ca170SBaolin Wang }
398195ca170SBaolin Wang 
399195ca170SBaolin Wang static int sc27xx_fgu_get_health(struct sc27xx_fgu_data *data, int *health)
400195ca170SBaolin Wang {
401195ca170SBaolin Wang 	int ret, vol;
402195ca170SBaolin Wang 
403195ca170SBaolin Wang 	ret = sc27xx_fgu_get_vbat_vol(data, &vol);
404195ca170SBaolin Wang 	if (ret)
405195ca170SBaolin Wang 		return ret;
406195ca170SBaolin Wang 
407195ca170SBaolin Wang 	if (vol > data->max_volt)
408195ca170SBaolin Wang 		*health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
409195ca170SBaolin Wang 	else
410195ca170SBaolin Wang 		*health = POWER_SUPPLY_HEALTH_GOOD;
411195ca170SBaolin Wang 
412195ca170SBaolin Wang 	return 0;
413195ca170SBaolin Wang }
414195ca170SBaolin Wang 
415195ca170SBaolin Wang static int sc27xx_fgu_get_status(struct sc27xx_fgu_data *data, int *status)
416195ca170SBaolin Wang {
417195ca170SBaolin Wang 	union power_supply_propval val;
418195ca170SBaolin Wang 	struct power_supply *psy;
419195ca170SBaolin Wang 	int i, ret = -EINVAL;
420195ca170SBaolin Wang 
421195ca170SBaolin Wang 	for (i = 0; i < ARRAY_SIZE(sc27xx_charger_supply_name); i++) {
422195ca170SBaolin Wang 		psy = power_supply_get_by_name(sc27xx_charger_supply_name[i]);
423195ca170SBaolin Wang 		if (!psy)
424195ca170SBaolin Wang 			continue;
425195ca170SBaolin Wang 
426195ca170SBaolin Wang 		ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS,
427195ca170SBaolin Wang 						&val);
428195ca170SBaolin Wang 		power_supply_put(psy);
429195ca170SBaolin Wang 		if (ret)
430195ca170SBaolin Wang 			return ret;
431195ca170SBaolin Wang 
432195ca170SBaolin Wang 		*status = val.intval;
433195ca170SBaolin Wang 	}
434195ca170SBaolin Wang 
435195ca170SBaolin Wang 	return ret;
436195ca170SBaolin Wang }
437195ca170SBaolin Wang 
438195ca170SBaolin Wang static int sc27xx_fgu_get_property(struct power_supply *psy,
439195ca170SBaolin Wang 				   enum power_supply_property psp,
440195ca170SBaolin Wang 				   union power_supply_propval *val)
441195ca170SBaolin Wang {
442195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy);
443195ca170SBaolin Wang 	int ret = 0;
444195ca170SBaolin Wang 	int value;
445195ca170SBaolin Wang 
446195ca170SBaolin Wang 	mutex_lock(&data->lock);
447195ca170SBaolin Wang 
448195ca170SBaolin Wang 	switch (psp) {
449195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_STATUS:
450195ca170SBaolin Wang 		ret = sc27xx_fgu_get_status(data, &value);
451195ca170SBaolin Wang 		if (ret)
452195ca170SBaolin Wang 			goto error;
453195ca170SBaolin Wang 
454195ca170SBaolin Wang 		val->intval = value;
455195ca170SBaolin Wang 		break;
456195ca170SBaolin Wang 
457195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_HEALTH:
458195ca170SBaolin Wang 		ret = sc27xx_fgu_get_health(data, &value);
459195ca170SBaolin Wang 		if (ret)
460195ca170SBaolin Wang 			goto error;
461195ca170SBaolin Wang 
462195ca170SBaolin Wang 		val->intval = value;
463195ca170SBaolin Wang 		break;
464195ca170SBaolin Wang 
465195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_PRESENT:
466195ca170SBaolin Wang 		val->intval = data->bat_present;
467195ca170SBaolin Wang 		break;
468195ca170SBaolin Wang 
469195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_TEMP:
470195ca170SBaolin Wang 		ret = sc27xx_fgu_get_temp(data, &value);
471195ca170SBaolin Wang 		if (ret)
472195ca170SBaolin Wang 			goto error;
473195ca170SBaolin Wang 
474195ca170SBaolin Wang 		val->intval = value;
475195ca170SBaolin Wang 		break;
476195ca170SBaolin Wang 
477195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_TECHNOLOGY:
478195ca170SBaolin Wang 		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
479195ca170SBaolin Wang 		break;
480195ca170SBaolin Wang 
481195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_CAPACITY:
482195ca170SBaolin Wang 		ret = sc27xx_fgu_get_capacity(data, &value);
483195ca170SBaolin Wang 		if (ret)
484195ca170SBaolin Wang 			goto error;
485195ca170SBaolin Wang 
486195ca170SBaolin Wang 		val->intval = value;
487195ca170SBaolin Wang 		break;
488195ca170SBaolin Wang 
489195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
490195ca170SBaolin Wang 		ret = sc27xx_fgu_get_vbat_vol(data, &value);
491195ca170SBaolin Wang 		if (ret)
492195ca170SBaolin Wang 			goto error;
493195ca170SBaolin Wang 
494195ca170SBaolin Wang 		val->intval = value * 1000;
495195ca170SBaolin Wang 		break;
496195ca170SBaolin Wang 
497195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_VOLTAGE_OCV:
498195ca170SBaolin Wang 		ret = sc27xx_fgu_get_vbat_ocv(data, &value);
499195ca170SBaolin Wang 		if (ret)
500195ca170SBaolin Wang 			goto error;
501195ca170SBaolin Wang 
502195ca170SBaolin Wang 		val->intval = value;
503195ca170SBaolin Wang 		break;
504195ca170SBaolin Wang 
505195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_CURRENT_NOW:
506195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_CURRENT_AVG:
507195ca170SBaolin Wang 		ret = sc27xx_fgu_get_current(data, &value);
508195ca170SBaolin Wang 		if (ret)
509195ca170SBaolin Wang 			goto error;
510195ca170SBaolin Wang 
511195ca170SBaolin Wang 		val->intval = value * 1000;
512195ca170SBaolin Wang 		break;
513195ca170SBaolin Wang 
514195ca170SBaolin Wang 	default:
515195ca170SBaolin Wang 		ret = -EINVAL;
516195ca170SBaolin Wang 		break;
517195ca170SBaolin Wang 	}
518195ca170SBaolin Wang 
519195ca170SBaolin Wang error:
520195ca170SBaolin Wang 	mutex_unlock(&data->lock);
521195ca170SBaolin Wang 	return ret;
522195ca170SBaolin Wang }
523195ca170SBaolin Wang 
524*4a040e7cSYuanjiang Yu static int sc27xx_fgu_set_property(struct power_supply *psy,
525*4a040e7cSYuanjiang Yu 				   enum power_supply_property psp,
526*4a040e7cSYuanjiang Yu 				   const union power_supply_propval *val)
527*4a040e7cSYuanjiang Yu {
528*4a040e7cSYuanjiang Yu 	struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy);
529*4a040e7cSYuanjiang Yu 	int ret;
530*4a040e7cSYuanjiang Yu 
531*4a040e7cSYuanjiang Yu 	if (psp != POWER_SUPPLY_PROP_CAPACITY)
532*4a040e7cSYuanjiang Yu 		return -EINVAL;
533*4a040e7cSYuanjiang Yu 
534*4a040e7cSYuanjiang Yu 	mutex_lock(&data->lock);
535*4a040e7cSYuanjiang Yu 
536*4a040e7cSYuanjiang Yu 	ret = sc27xx_fgu_save_last_cap(data, val->intval);
537*4a040e7cSYuanjiang Yu 
538*4a040e7cSYuanjiang Yu 	mutex_unlock(&data->lock);
539*4a040e7cSYuanjiang Yu 
540*4a040e7cSYuanjiang Yu 	if (ret < 0)
541*4a040e7cSYuanjiang Yu 		dev_err(data->dev, "failed to save battery capacity\n");
542*4a040e7cSYuanjiang Yu 
543*4a040e7cSYuanjiang Yu 	return ret;
544*4a040e7cSYuanjiang Yu }
545*4a040e7cSYuanjiang Yu 
546195ca170SBaolin Wang static void sc27xx_fgu_external_power_changed(struct power_supply *psy)
547195ca170SBaolin Wang {
548195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy);
549195ca170SBaolin Wang 
550195ca170SBaolin Wang 	power_supply_changed(data->battery);
551195ca170SBaolin Wang }
552195ca170SBaolin Wang 
553*4a040e7cSYuanjiang Yu static int sc27xx_fgu_property_is_writeable(struct power_supply *psy,
554*4a040e7cSYuanjiang Yu 					    enum power_supply_property psp)
555*4a040e7cSYuanjiang Yu {
556*4a040e7cSYuanjiang Yu 	return psp == POWER_SUPPLY_PROP_CAPACITY;
557*4a040e7cSYuanjiang Yu }
558*4a040e7cSYuanjiang Yu 
559195ca170SBaolin Wang static enum power_supply_property sc27xx_fgu_props[] = {
560195ca170SBaolin Wang 	POWER_SUPPLY_PROP_STATUS,
561195ca170SBaolin Wang 	POWER_SUPPLY_PROP_HEALTH,
562195ca170SBaolin Wang 	POWER_SUPPLY_PROP_PRESENT,
563195ca170SBaolin Wang 	POWER_SUPPLY_PROP_TEMP,
564195ca170SBaolin Wang 	POWER_SUPPLY_PROP_TECHNOLOGY,
565195ca170SBaolin Wang 	POWER_SUPPLY_PROP_CAPACITY,
566195ca170SBaolin Wang 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
567195ca170SBaolin Wang 	POWER_SUPPLY_PROP_VOLTAGE_OCV,
568195ca170SBaolin Wang 	POWER_SUPPLY_PROP_CURRENT_NOW,
569195ca170SBaolin Wang 	POWER_SUPPLY_PROP_CURRENT_AVG,
570195ca170SBaolin Wang };
571195ca170SBaolin Wang 
572195ca170SBaolin Wang static const struct power_supply_desc sc27xx_fgu_desc = {
573195ca170SBaolin Wang 	.name			= "sc27xx-fgu",
574195ca170SBaolin Wang 	.type			= POWER_SUPPLY_TYPE_BATTERY,
575195ca170SBaolin Wang 	.properties		= sc27xx_fgu_props,
576195ca170SBaolin Wang 	.num_properties		= ARRAY_SIZE(sc27xx_fgu_props),
577195ca170SBaolin Wang 	.get_property		= sc27xx_fgu_get_property,
578*4a040e7cSYuanjiang Yu 	.set_property		= sc27xx_fgu_set_property,
579195ca170SBaolin Wang 	.external_power_changed	= sc27xx_fgu_external_power_changed,
580*4a040e7cSYuanjiang Yu 	.property_is_writeable	= sc27xx_fgu_property_is_writeable,
581195ca170SBaolin Wang };
582195ca170SBaolin Wang 
583edcb1c0aSYuanjiang Yu static void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap)
584edcb1c0aSYuanjiang Yu {
585edcb1c0aSYuanjiang Yu 	data->init_cap = cap;
586edcb1c0aSYuanjiang Yu 	data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap);
587edcb1c0aSYuanjiang Yu }
588edcb1c0aSYuanjiang Yu 
589edcb1c0aSYuanjiang Yu static irqreturn_t sc27xx_fgu_interrupt(int irq, void *dev_id)
590edcb1c0aSYuanjiang Yu {
591edcb1c0aSYuanjiang Yu 	struct sc27xx_fgu_data *data = dev_id;
592edcb1c0aSYuanjiang Yu 	int ret, cap, ocv, adc;
593edcb1c0aSYuanjiang Yu 	u32 status;
594edcb1c0aSYuanjiang Yu 
595edcb1c0aSYuanjiang Yu 	mutex_lock(&data->lock);
596edcb1c0aSYuanjiang Yu 
597edcb1c0aSYuanjiang Yu 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_INT_STS,
598edcb1c0aSYuanjiang Yu 			  &status);
599edcb1c0aSYuanjiang Yu 	if (ret)
600edcb1c0aSYuanjiang Yu 		goto out;
601edcb1c0aSYuanjiang Yu 
602edcb1c0aSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR,
603edcb1c0aSYuanjiang Yu 				 status, status);
604edcb1c0aSYuanjiang Yu 	if (ret)
605edcb1c0aSYuanjiang Yu 		goto out;
606edcb1c0aSYuanjiang Yu 
607edcb1c0aSYuanjiang Yu 	/*
608edcb1c0aSYuanjiang Yu 	 * When low overload voltage interrupt happens, we should calibrate the
609edcb1c0aSYuanjiang Yu 	 * battery capacity in lower voltage stage.
610edcb1c0aSYuanjiang Yu 	 */
611edcb1c0aSYuanjiang Yu 	if (!(status & SC27XX_FGU_LOW_OVERLOAD_INT))
612edcb1c0aSYuanjiang Yu 		goto out;
613edcb1c0aSYuanjiang Yu 
614edcb1c0aSYuanjiang Yu 	ret = sc27xx_fgu_get_capacity(data, &cap);
615edcb1c0aSYuanjiang Yu 	if (ret)
616edcb1c0aSYuanjiang Yu 		goto out;
617edcb1c0aSYuanjiang Yu 
618edcb1c0aSYuanjiang Yu 	ret = sc27xx_fgu_get_vbat_ocv(data, &ocv);
619edcb1c0aSYuanjiang Yu 	if (ret)
620edcb1c0aSYuanjiang Yu 		goto out;
621edcb1c0aSYuanjiang Yu 
622edcb1c0aSYuanjiang Yu 	/*
623edcb1c0aSYuanjiang Yu 	 * If current OCV value is less than the minimum OCV value in OCV table,
624edcb1c0aSYuanjiang Yu 	 * which means now battery capacity is 0%, and we should adjust the
625edcb1c0aSYuanjiang Yu 	 * inititial capacity to 0.
626edcb1c0aSYuanjiang Yu 	 */
627edcb1c0aSYuanjiang Yu 	if (ocv <= data->cap_table[data->table_len - 1].ocv) {
628edcb1c0aSYuanjiang Yu 		sc27xx_fgu_adjust_cap(data, 0);
629edcb1c0aSYuanjiang Yu 	} else if (ocv <= data->min_volt) {
630edcb1c0aSYuanjiang Yu 		/*
631edcb1c0aSYuanjiang Yu 		 * If current OCV value is less than the low alarm voltage, but
632edcb1c0aSYuanjiang Yu 		 * current capacity is larger than the alarm capacity, we should
633edcb1c0aSYuanjiang Yu 		 * adjust the inititial capacity to alarm capacity.
634edcb1c0aSYuanjiang Yu 		 */
635edcb1c0aSYuanjiang Yu 		if (cap > data->alarm_cap) {
636edcb1c0aSYuanjiang Yu 			sc27xx_fgu_adjust_cap(data, data->alarm_cap);
637edcb1c0aSYuanjiang Yu 		} else if (cap <= 0) {
638edcb1c0aSYuanjiang Yu 			int cur_cap;
639edcb1c0aSYuanjiang Yu 
640edcb1c0aSYuanjiang Yu 			/*
641edcb1c0aSYuanjiang Yu 			 * If current capacity is equal with 0 or less than 0
642edcb1c0aSYuanjiang Yu 			 * (some error occurs), we should adjust inititial
643edcb1c0aSYuanjiang Yu 			 * capacity to the capacity corresponding to current OCV
644edcb1c0aSYuanjiang Yu 			 * value.
645edcb1c0aSYuanjiang Yu 			 */
646edcb1c0aSYuanjiang Yu 			cur_cap = power_supply_ocv2cap_simple(data->cap_table,
647edcb1c0aSYuanjiang Yu 							      data->table_len,
648edcb1c0aSYuanjiang Yu 							      ocv);
649edcb1c0aSYuanjiang Yu 			sc27xx_fgu_adjust_cap(data, cur_cap);
650edcb1c0aSYuanjiang Yu 		}
651edcb1c0aSYuanjiang Yu 
652edcb1c0aSYuanjiang Yu 		/*
653edcb1c0aSYuanjiang Yu 		 * After adjusting the battery capacity, we should set the
654edcb1c0aSYuanjiang Yu 		 * lowest alarm voltage instead.
655edcb1c0aSYuanjiang Yu 		 */
656edcb1c0aSYuanjiang Yu 		data->min_volt = data->cap_table[data->table_len - 1].ocv;
657edcb1c0aSYuanjiang Yu 		adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000);
658edcb1c0aSYuanjiang Yu 		regmap_update_bits(data->regmap, data->base + SC27XX_FGU_LOW_OVERLOAD,
659edcb1c0aSYuanjiang Yu 				   SC27XX_FGU_LOW_OVERLOAD_MASK, adc);
660edcb1c0aSYuanjiang Yu 	}
661edcb1c0aSYuanjiang Yu 
662edcb1c0aSYuanjiang Yu out:
663edcb1c0aSYuanjiang Yu 	mutex_unlock(&data->lock);
664edcb1c0aSYuanjiang Yu 
665edcb1c0aSYuanjiang Yu 	power_supply_changed(data->battery);
666edcb1c0aSYuanjiang Yu 	return IRQ_HANDLED;
667edcb1c0aSYuanjiang Yu }
668edcb1c0aSYuanjiang Yu 
669195ca170SBaolin Wang static irqreturn_t sc27xx_fgu_bat_detection(int irq, void *dev_id)
670195ca170SBaolin Wang {
671195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = dev_id;
672195ca170SBaolin Wang 	int state;
673195ca170SBaolin Wang 
674195ca170SBaolin Wang 	mutex_lock(&data->lock);
675195ca170SBaolin Wang 
676195ca170SBaolin Wang 	state = gpiod_get_value_cansleep(data->gpiod);
677195ca170SBaolin Wang 	if (state < 0) {
678195ca170SBaolin Wang 		dev_err(data->dev, "failed to get gpio state\n");
679195ca170SBaolin Wang 		mutex_unlock(&data->lock);
680195ca170SBaolin Wang 		return IRQ_RETVAL(state);
681195ca170SBaolin Wang 	}
682195ca170SBaolin Wang 
683195ca170SBaolin Wang 	data->bat_present = !!state;
684195ca170SBaolin Wang 
685195ca170SBaolin Wang 	mutex_unlock(&data->lock);
686195ca170SBaolin Wang 
687195ca170SBaolin Wang 	power_supply_changed(data->battery);
688195ca170SBaolin Wang 	return IRQ_HANDLED;
689195ca170SBaolin Wang }
690195ca170SBaolin Wang 
691195ca170SBaolin Wang static void sc27xx_fgu_disable(void *_data)
692195ca170SBaolin Wang {
693195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = _data;
694195ca170SBaolin Wang 
695195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0);
696195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0);
697195ca170SBaolin Wang }
698195ca170SBaolin Wang 
699195ca170SBaolin Wang static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity)
700195ca170SBaolin Wang {
701195ca170SBaolin Wang 	/*
702195ca170SBaolin Wang 	 * Get current capacity (mAh) = battery total capacity (mAh) *
703195ca170SBaolin Wang 	 * current capacity percent (capacity / 100).
704195ca170SBaolin Wang 	 */
705195ca170SBaolin Wang 	int cur_cap = DIV_ROUND_CLOSEST(data->total_cap * capacity, 100);
706195ca170SBaolin Wang 
707195ca170SBaolin Wang 	/*
708195ca170SBaolin Wang 	 * Convert current capacity (mAh) to coulomb counter according to the
709195ca170SBaolin Wang 	 * formula: 1 mAh =3.6 coulomb.
710195ca170SBaolin Wang 	 */
711195ca170SBaolin Wang 	return DIV_ROUND_CLOSEST(cur_cap * 36, 10);
712195ca170SBaolin Wang }
713195ca170SBaolin Wang 
71465c9fab7SBaolin Wang static int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data)
71565c9fab7SBaolin Wang {
71665c9fab7SBaolin Wang 	struct nvmem_cell *cell;
71765c9fab7SBaolin Wang 	int calib_data, cal_4200mv;
71865c9fab7SBaolin Wang 	void *buf;
71965c9fab7SBaolin Wang 	size_t len;
72065c9fab7SBaolin Wang 
72165c9fab7SBaolin Wang 	cell = nvmem_cell_get(data->dev, "fgu_calib");
72265c9fab7SBaolin Wang 	if (IS_ERR(cell))
72365c9fab7SBaolin Wang 		return PTR_ERR(cell);
72465c9fab7SBaolin Wang 
72565c9fab7SBaolin Wang 	buf = nvmem_cell_read(cell, &len);
72665c9fab7SBaolin Wang 	nvmem_cell_put(cell);
72765c9fab7SBaolin Wang 
72865c9fab7SBaolin Wang 	if (IS_ERR(buf))
72965c9fab7SBaolin Wang 		return PTR_ERR(buf);
73065c9fab7SBaolin Wang 
73165c9fab7SBaolin Wang 	memcpy(&calib_data, buf, min(len, sizeof(u32)));
73265c9fab7SBaolin Wang 
73365c9fab7SBaolin Wang 	/*
73465c9fab7SBaolin Wang 	 * Get the ADC value corresponding to 4200 mV from eFuse controller
73565c9fab7SBaolin Wang 	 * according to below formula. Then convert to ADC values corresponding
73665c9fab7SBaolin Wang 	 * to 1000 mV and 1000 mA.
73765c9fab7SBaolin Wang 	 */
73865c9fab7SBaolin Wang 	cal_4200mv = (calib_data & 0x1ff) + 6963 - 4096 - 256;
73965c9fab7SBaolin Wang 	data->vol_1000mv_adc = DIV_ROUND_CLOSEST(cal_4200mv * 10, 42);
74065c9fab7SBaolin Wang 	data->cur_1000ma_adc = data->vol_1000mv_adc * 4;
74165c9fab7SBaolin Wang 
74265c9fab7SBaolin Wang 	kfree(buf);
74365c9fab7SBaolin Wang 	return 0;
74465c9fab7SBaolin Wang }
74565c9fab7SBaolin Wang 
746195ca170SBaolin Wang static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data)
747195ca170SBaolin Wang {
748195ca170SBaolin Wang 	struct power_supply_battery_info info = { };
749195ca170SBaolin Wang 	struct power_supply_battery_ocv_table *table;
750edcb1c0aSYuanjiang Yu 	int ret, delta_clbcnt, alarm_adc;
751195ca170SBaolin Wang 
752195ca170SBaolin Wang 	ret = power_supply_get_battery_info(data->battery, &info);
753195ca170SBaolin Wang 	if (ret) {
754195ca170SBaolin Wang 		dev_err(data->dev, "failed to get battery information\n");
755195ca170SBaolin Wang 		return ret;
756195ca170SBaolin Wang 	}
757195ca170SBaolin Wang 
758195ca170SBaolin Wang 	data->total_cap = info.charge_full_design_uah / 1000;
759195ca170SBaolin Wang 	data->max_volt = info.constant_charge_voltage_max_uv / 1000;
760195ca170SBaolin Wang 	data->internal_resist = info.factory_internal_resistance_uohm / 1000;
761edcb1c0aSYuanjiang Yu 	data->min_volt = info.voltage_min_design_uv;
762195ca170SBaolin Wang 
763195ca170SBaolin Wang 	/*
764195ca170SBaolin Wang 	 * For SC27XX fuel gauge device, we only use one ocv-capacity
765195ca170SBaolin Wang 	 * table in normal temperature 20 Celsius.
766195ca170SBaolin Wang 	 */
767195ca170SBaolin Wang 	table = power_supply_find_ocv2cap_table(&info, 20, &data->table_len);
768195ca170SBaolin Wang 	if (!table)
769195ca170SBaolin Wang 		return -EINVAL;
770195ca170SBaolin Wang 
771195ca170SBaolin Wang 	data->cap_table = devm_kmemdup(data->dev, table,
772195ca170SBaolin Wang 				       data->table_len * sizeof(*table),
773195ca170SBaolin Wang 				       GFP_KERNEL);
774195ca170SBaolin Wang 	if (!data->cap_table) {
775195ca170SBaolin Wang 		power_supply_put_battery_info(data->battery, &info);
776195ca170SBaolin Wang 		return -ENOMEM;
777195ca170SBaolin Wang 	}
778195ca170SBaolin Wang 
779edcb1c0aSYuanjiang Yu 	data->alarm_cap = power_supply_ocv2cap_simple(data->cap_table,
780edcb1c0aSYuanjiang Yu 						      data->table_len,
781edcb1c0aSYuanjiang Yu 						      data->min_volt);
782edcb1c0aSYuanjiang Yu 
783195ca170SBaolin Wang 	power_supply_put_battery_info(data->battery, &info);
784195ca170SBaolin Wang 
78565c9fab7SBaolin Wang 	ret = sc27xx_fgu_calibration(data);
78665c9fab7SBaolin Wang 	if (ret)
78765c9fab7SBaolin Wang 		return ret;
78865c9fab7SBaolin Wang 
789195ca170SBaolin Wang 	/* Enable the FGU module */
790195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap, SC27XX_MODULE_EN0,
791195ca170SBaolin Wang 				 SC27XX_FGU_EN, SC27XX_FGU_EN);
792195ca170SBaolin Wang 	if (ret) {
793195ca170SBaolin Wang 		dev_err(data->dev, "failed to enable fgu\n");
794195ca170SBaolin Wang 		return ret;
795195ca170SBaolin Wang 	}
796195ca170SBaolin Wang 
797195ca170SBaolin Wang 	/* Enable the FGU RTC clock to make it work */
798195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap, SC27XX_CLK_EN0,
799195ca170SBaolin Wang 				 SC27XX_FGU_RTC_EN, SC27XX_FGU_RTC_EN);
800195ca170SBaolin Wang 	if (ret) {
801195ca170SBaolin Wang 		dev_err(data->dev, "failed to enable fgu RTC clock\n");
802195ca170SBaolin Wang 		goto disable_fgu;
803195ca170SBaolin Wang 	}
804195ca170SBaolin Wang 
805edcb1c0aSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR,
806edcb1c0aSYuanjiang Yu 				 SC27XX_FGU_INT_MASK, SC27XX_FGU_INT_MASK);
807edcb1c0aSYuanjiang Yu 	if (ret) {
808edcb1c0aSYuanjiang Yu 		dev_err(data->dev, "failed to clear interrupt status\n");
809edcb1c0aSYuanjiang Yu 		goto disable_clk;
810edcb1c0aSYuanjiang Yu 	}
811edcb1c0aSYuanjiang Yu 
812edcb1c0aSYuanjiang Yu 	/*
813edcb1c0aSYuanjiang Yu 	 * Set the voltage low overload threshold, which means when the battery
814edcb1c0aSYuanjiang Yu 	 * voltage is lower than this threshold, the controller will generate
815edcb1c0aSYuanjiang Yu 	 * one interrupt to notify.
816edcb1c0aSYuanjiang Yu 	 */
817edcb1c0aSYuanjiang Yu 	alarm_adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000);
818edcb1c0aSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_LOW_OVERLOAD,
819edcb1c0aSYuanjiang Yu 				 SC27XX_FGU_LOW_OVERLOAD_MASK, alarm_adc);
820edcb1c0aSYuanjiang Yu 	if (ret) {
821edcb1c0aSYuanjiang Yu 		dev_err(data->dev, "failed to set fgu low overload\n");
822edcb1c0aSYuanjiang Yu 		goto disable_clk;
823edcb1c0aSYuanjiang Yu 	}
824edcb1c0aSYuanjiang Yu 
825edcb1c0aSYuanjiang Yu 	/*
826edcb1c0aSYuanjiang Yu 	 * Set the coulomb counter delta threshold, that means when the coulomb
827edcb1c0aSYuanjiang Yu 	 * counter change is multiples of the delta threshold, the controller
828edcb1c0aSYuanjiang Yu 	 * will generate one interrupt to notify the users to update the battery
829edcb1c0aSYuanjiang Yu 	 * capacity. Now we set the delta threshold as a counter value of 1%
830edcb1c0aSYuanjiang Yu 	 * capacity.
831edcb1c0aSYuanjiang Yu 	 */
832edcb1c0aSYuanjiang Yu 	delta_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, 1);
833edcb1c0aSYuanjiang Yu 
834edcb1c0aSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTL,
835edcb1c0aSYuanjiang Yu 				 SC27XX_FGU_CLBCNT_MASK, delta_clbcnt);
836edcb1c0aSYuanjiang Yu 	if (ret) {
837edcb1c0aSYuanjiang Yu 		dev_err(data->dev, "failed to set low delta coulomb counter\n");
838edcb1c0aSYuanjiang Yu 		goto disable_clk;
839edcb1c0aSYuanjiang Yu 	}
840edcb1c0aSYuanjiang Yu 
841edcb1c0aSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTH,
842edcb1c0aSYuanjiang Yu 				 SC27XX_FGU_CLBCNT_MASK,
843edcb1c0aSYuanjiang Yu 				 delta_clbcnt >> SC27XX_FGU_CLBCNT_SHIFT);
844edcb1c0aSYuanjiang Yu 	if (ret) {
845edcb1c0aSYuanjiang Yu 		dev_err(data->dev, "failed to set high delta coulomb counter\n");
846edcb1c0aSYuanjiang Yu 		goto disable_clk;
847edcb1c0aSYuanjiang Yu 	}
848edcb1c0aSYuanjiang Yu 
849195ca170SBaolin Wang 	/*
850195ca170SBaolin Wang 	 * Get the boot battery capacity when system powers on, which is used to
851195ca170SBaolin Wang 	 * initialize the coulomb counter. After that, we can read the coulomb
852195ca170SBaolin Wang 	 * counter to measure the battery capacity.
853195ca170SBaolin Wang 	 */
854195ca170SBaolin Wang 	ret = sc27xx_fgu_get_boot_capacity(data, &data->init_cap);
855195ca170SBaolin Wang 	if (ret) {
856195ca170SBaolin Wang 		dev_err(data->dev, "failed to get boot capacity\n");
857195ca170SBaolin Wang 		goto disable_clk;
858195ca170SBaolin Wang 	}
859195ca170SBaolin Wang 
860195ca170SBaolin Wang 	/*
861195ca170SBaolin Wang 	 * Convert battery capacity to the corresponding initial coulomb counter
862195ca170SBaolin Wang 	 * and set into coulomb counter registers.
863195ca170SBaolin Wang 	 */
864195ca170SBaolin Wang 	data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap);
865195ca170SBaolin Wang 	ret = sc27xx_fgu_set_clbcnt(data, data->init_clbcnt);
866195ca170SBaolin Wang 	if (ret) {
867195ca170SBaolin Wang 		dev_err(data->dev, "failed to initialize coulomb counter\n");
868195ca170SBaolin Wang 		goto disable_clk;
869195ca170SBaolin Wang 	}
870195ca170SBaolin Wang 
871195ca170SBaolin Wang 	return 0;
872195ca170SBaolin Wang 
873195ca170SBaolin Wang disable_clk:
874195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0);
875195ca170SBaolin Wang disable_fgu:
876195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0);
877195ca170SBaolin Wang 
878195ca170SBaolin Wang 	return ret;
879195ca170SBaolin Wang }
880195ca170SBaolin Wang 
881195ca170SBaolin Wang static int sc27xx_fgu_probe(struct platform_device *pdev)
882195ca170SBaolin Wang {
883195ca170SBaolin Wang 	struct device_node *np = pdev->dev.of_node;
884195ca170SBaolin Wang 	struct power_supply_config fgu_cfg = { };
885195ca170SBaolin Wang 	struct sc27xx_fgu_data *data;
886195ca170SBaolin Wang 	int ret, irq;
887195ca170SBaolin Wang 
888195ca170SBaolin Wang 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
889195ca170SBaolin Wang 	if (!data)
890195ca170SBaolin Wang 		return -ENOMEM;
891195ca170SBaolin Wang 
892195ca170SBaolin Wang 	data->regmap = dev_get_regmap(pdev->dev.parent, NULL);
893195ca170SBaolin Wang 	if (!data->regmap) {
894195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to get regmap\n");
895195ca170SBaolin Wang 		return -ENODEV;
896195ca170SBaolin Wang 	}
897195ca170SBaolin Wang 
898195ca170SBaolin Wang 	ret = device_property_read_u32(&pdev->dev, "reg", &data->base);
899195ca170SBaolin Wang 	if (ret) {
900195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to get fgu address\n");
901195ca170SBaolin Wang 		return ret;
902195ca170SBaolin Wang 	}
903195ca170SBaolin Wang 
904195ca170SBaolin Wang 	data->channel = devm_iio_channel_get(&pdev->dev, "bat-temp");
905195ca170SBaolin Wang 	if (IS_ERR(data->channel)) {
906195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to get IIO channel\n");
907195ca170SBaolin Wang 		return PTR_ERR(data->channel);
908195ca170SBaolin Wang 	}
909195ca170SBaolin Wang 
910195ca170SBaolin Wang 	data->gpiod = devm_gpiod_get(&pdev->dev, "bat-detect", GPIOD_IN);
911195ca170SBaolin Wang 	if (IS_ERR(data->gpiod)) {
912195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to get battery detection GPIO\n");
913195ca170SBaolin Wang 		return PTR_ERR(data->gpiod);
914195ca170SBaolin Wang 	}
915195ca170SBaolin Wang 
916195ca170SBaolin Wang 	ret = gpiod_get_value_cansleep(data->gpiod);
917195ca170SBaolin Wang 	if (ret < 0) {
918195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to get gpio state\n");
919195ca170SBaolin Wang 		return ret;
920195ca170SBaolin Wang 	}
921195ca170SBaolin Wang 
922195ca170SBaolin Wang 	data->bat_present = !!ret;
923195ca170SBaolin Wang 	mutex_init(&data->lock);
924195ca170SBaolin Wang 	data->dev = &pdev->dev;
925e2fb615bSYuanjiang Yu 	platform_set_drvdata(pdev, data);
926195ca170SBaolin Wang 
927195ca170SBaolin Wang 	fgu_cfg.drv_data = data;
928195ca170SBaolin Wang 	fgu_cfg.of_node = np;
929195ca170SBaolin Wang 	data->battery = devm_power_supply_register(&pdev->dev, &sc27xx_fgu_desc,
930195ca170SBaolin Wang 						   &fgu_cfg);
931195ca170SBaolin Wang 	if (IS_ERR(data->battery)) {
932195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to register power supply\n");
933195ca170SBaolin Wang 		return PTR_ERR(data->battery);
934195ca170SBaolin Wang 	}
935195ca170SBaolin Wang 
936195ca170SBaolin Wang 	ret = sc27xx_fgu_hw_init(data);
937195ca170SBaolin Wang 	if (ret) {
938195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to initialize fgu hardware\n");
939195ca170SBaolin Wang 		return ret;
940195ca170SBaolin Wang 	}
941195ca170SBaolin Wang 
942195ca170SBaolin Wang 	ret = devm_add_action(&pdev->dev, sc27xx_fgu_disable, data);
943195ca170SBaolin Wang 	if (ret) {
944195ca170SBaolin Wang 		sc27xx_fgu_disable(data);
945195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to add fgu disable action\n");
946195ca170SBaolin Wang 		return ret;
947195ca170SBaolin Wang 	}
948195ca170SBaolin Wang 
949edcb1c0aSYuanjiang Yu 	irq = platform_get_irq(pdev, 0);
950edcb1c0aSYuanjiang Yu 	if (irq < 0) {
951edcb1c0aSYuanjiang Yu 		dev_err(&pdev->dev, "no irq resource specified\n");
952edcb1c0aSYuanjiang Yu 		return irq;
953edcb1c0aSYuanjiang Yu 	}
954edcb1c0aSYuanjiang Yu 
955edcb1c0aSYuanjiang Yu 	ret = devm_request_threaded_irq(data->dev, irq, NULL,
956edcb1c0aSYuanjiang Yu 					sc27xx_fgu_interrupt,
957edcb1c0aSYuanjiang Yu 					IRQF_NO_SUSPEND | IRQF_ONESHOT,
958edcb1c0aSYuanjiang Yu 					pdev->name, data);
959edcb1c0aSYuanjiang Yu 	if (ret) {
960edcb1c0aSYuanjiang Yu 		dev_err(data->dev, "failed to request fgu IRQ\n");
961edcb1c0aSYuanjiang Yu 		return ret;
962edcb1c0aSYuanjiang Yu 	}
963edcb1c0aSYuanjiang Yu 
964195ca170SBaolin Wang 	irq = gpiod_to_irq(data->gpiod);
965195ca170SBaolin Wang 	if (irq < 0) {
966195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to translate GPIO to IRQ\n");
967195ca170SBaolin Wang 		return irq;
968195ca170SBaolin Wang 	}
969195ca170SBaolin Wang 
970195ca170SBaolin Wang 	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
971195ca170SBaolin Wang 					sc27xx_fgu_bat_detection,
972195ca170SBaolin Wang 					IRQF_ONESHOT | IRQF_TRIGGER_RISING |
973195ca170SBaolin Wang 					IRQF_TRIGGER_FALLING,
974195ca170SBaolin Wang 					pdev->name, data);
975195ca170SBaolin Wang 	if (ret) {
976195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to request IRQ\n");
977195ca170SBaolin Wang 		return ret;
978195ca170SBaolin Wang 	}
979195ca170SBaolin Wang 
980195ca170SBaolin Wang 	return 0;
981195ca170SBaolin Wang }
982195ca170SBaolin Wang 
983e2fb615bSYuanjiang Yu #ifdef CONFIG_PM_SLEEP
984e2fb615bSYuanjiang Yu static int sc27xx_fgu_resume(struct device *dev)
985e2fb615bSYuanjiang Yu {
986e2fb615bSYuanjiang Yu 	struct sc27xx_fgu_data *data = dev_get_drvdata(dev);
987e2fb615bSYuanjiang Yu 	int ret;
988e2fb615bSYuanjiang Yu 
989e2fb615bSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN,
990e2fb615bSYuanjiang Yu 				 SC27XX_FGU_LOW_OVERLOAD_INT |
991e2fb615bSYuanjiang Yu 				 SC27XX_FGU_CLBCNT_DELTA_INT, 0);
992e2fb615bSYuanjiang Yu 	if (ret) {
993e2fb615bSYuanjiang Yu 		dev_err(data->dev, "failed to disable fgu interrupts\n");
994e2fb615bSYuanjiang Yu 		return ret;
995e2fb615bSYuanjiang Yu 	}
996e2fb615bSYuanjiang Yu 
997e2fb615bSYuanjiang Yu 	return 0;
998e2fb615bSYuanjiang Yu }
999e2fb615bSYuanjiang Yu 
1000e2fb615bSYuanjiang Yu static int sc27xx_fgu_suspend(struct device *dev)
1001e2fb615bSYuanjiang Yu {
1002e2fb615bSYuanjiang Yu 	struct sc27xx_fgu_data *data = dev_get_drvdata(dev);
1003e2fb615bSYuanjiang Yu 	int ret, status, ocv;
1004e2fb615bSYuanjiang Yu 
1005e2fb615bSYuanjiang Yu 	ret = sc27xx_fgu_get_status(data, &status);
1006e2fb615bSYuanjiang Yu 	if (ret)
1007e2fb615bSYuanjiang Yu 		return ret;
1008e2fb615bSYuanjiang Yu 
1009e2fb615bSYuanjiang Yu 	/*
1010e2fb615bSYuanjiang Yu 	 * If we are charging, then no need to enable the FGU interrupts to
1011e2fb615bSYuanjiang Yu 	 * adjust the battery capacity.
1012e2fb615bSYuanjiang Yu 	 */
1013e2fb615bSYuanjiang Yu 	if (status != POWER_SUPPLY_STATUS_NOT_CHARGING)
1014e2fb615bSYuanjiang Yu 		return 0;
1015e2fb615bSYuanjiang Yu 
1016e2fb615bSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN,
1017e2fb615bSYuanjiang Yu 				 SC27XX_FGU_LOW_OVERLOAD_INT,
1018e2fb615bSYuanjiang Yu 				 SC27XX_FGU_LOW_OVERLOAD_INT);
1019e2fb615bSYuanjiang Yu 	if (ret) {
1020e2fb615bSYuanjiang Yu 		dev_err(data->dev, "failed to enable low voltage interrupt\n");
1021e2fb615bSYuanjiang Yu 		return ret;
1022e2fb615bSYuanjiang Yu 	}
1023e2fb615bSYuanjiang Yu 
1024e2fb615bSYuanjiang Yu 	ret = sc27xx_fgu_get_vbat_ocv(data, &ocv);
1025e2fb615bSYuanjiang Yu 	if (ret)
1026e2fb615bSYuanjiang Yu 		goto disable_int;
1027e2fb615bSYuanjiang Yu 
1028e2fb615bSYuanjiang Yu 	/*
1029e2fb615bSYuanjiang Yu 	 * If current OCV is less than the minimum voltage, we should enable the
1030e2fb615bSYuanjiang Yu 	 * coulomb counter threshold interrupt to notify events to adjust the
1031e2fb615bSYuanjiang Yu 	 * battery capacity.
1032e2fb615bSYuanjiang Yu 	 */
1033e2fb615bSYuanjiang Yu 	if (ocv < data->min_volt) {
1034e2fb615bSYuanjiang Yu 		ret = regmap_update_bits(data->regmap,
1035e2fb615bSYuanjiang Yu 					 data->base + SC27XX_FGU_INT_EN,
1036e2fb615bSYuanjiang Yu 					 SC27XX_FGU_CLBCNT_DELTA_INT,
1037e2fb615bSYuanjiang Yu 					 SC27XX_FGU_CLBCNT_DELTA_INT);
1038e2fb615bSYuanjiang Yu 		if (ret) {
1039e2fb615bSYuanjiang Yu 			dev_err(data->dev,
1040e2fb615bSYuanjiang Yu 				"failed to enable coulomb threshold int\n");
1041e2fb615bSYuanjiang Yu 			goto disable_int;
1042e2fb615bSYuanjiang Yu 		}
1043e2fb615bSYuanjiang Yu 	}
1044e2fb615bSYuanjiang Yu 
1045e2fb615bSYuanjiang Yu 	return 0;
1046e2fb615bSYuanjiang Yu 
1047e2fb615bSYuanjiang Yu disable_int:
1048e2fb615bSYuanjiang Yu 	regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN,
1049e2fb615bSYuanjiang Yu 			   SC27XX_FGU_LOW_OVERLOAD_INT, 0);
1050e2fb615bSYuanjiang Yu 	return ret;
1051e2fb615bSYuanjiang Yu }
1052e2fb615bSYuanjiang Yu #endif
1053e2fb615bSYuanjiang Yu 
1054e2fb615bSYuanjiang Yu static const struct dev_pm_ops sc27xx_fgu_pm_ops = {
1055e2fb615bSYuanjiang Yu 	SET_SYSTEM_SLEEP_PM_OPS(sc27xx_fgu_suspend, sc27xx_fgu_resume)
1056e2fb615bSYuanjiang Yu };
1057e2fb615bSYuanjiang Yu 
1058195ca170SBaolin Wang static const struct of_device_id sc27xx_fgu_of_match[] = {
1059195ca170SBaolin Wang 	{ .compatible = "sprd,sc2731-fgu", },
1060195ca170SBaolin Wang 	{ }
1061195ca170SBaolin Wang };
1062195ca170SBaolin Wang 
1063195ca170SBaolin Wang static struct platform_driver sc27xx_fgu_driver = {
1064195ca170SBaolin Wang 	.probe = sc27xx_fgu_probe,
1065195ca170SBaolin Wang 	.driver = {
1066195ca170SBaolin Wang 		.name = "sc27xx-fgu",
1067195ca170SBaolin Wang 		.of_match_table = sc27xx_fgu_of_match,
1068e2fb615bSYuanjiang Yu 		.pm = &sc27xx_fgu_pm_ops,
1069195ca170SBaolin Wang 	}
1070195ca170SBaolin Wang };
1071195ca170SBaolin Wang 
1072195ca170SBaolin Wang module_platform_driver(sc27xx_fgu_driver);
1073195ca170SBaolin Wang 
1074195ca170SBaolin Wang MODULE_DESCRIPTION("Spreadtrum SC27XX PMICs Fual Gauge Unit Driver");
1075195ca170SBaolin Wang MODULE_LICENSE("GPL v2");
1076