xref: /linux/drivers/power/supply/sc27xx_fuel_gauge.c (revision d3e67c94e8f607ee90097d296deecf030af9b8ab)
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
424a040e7cSYuanjiang Yu #define SC27XX_FGU_USER_AREA_SET	0xa0
434a040e7cSYuanjiang Yu #define SC27XX_FGU_USER_AREA_CLEAR	0xa4
444a040e7cSYuanjiang 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 
554a040e7cSYuanjiang Yu #define SC27XX_FGU_MODE_AREA_MASK	GENMASK(15, 12)
564a040e7cSYuanjiang Yu #define SC27XX_FGU_CAP_AREA_MASK	GENMASK(11, 0)
574a040e7cSYuanjiang Yu #define SC27XX_FGU_MODE_AREA_SHIFT	12
584a040e7cSYuanjiang Yu 
594a040e7cSYuanjiang Yu #define SC27XX_FGU_FIRST_POWERTON	GENMASK(3, 0)
604a040e7cSYuanjiang Yu #define SC27XX_FGU_DEFAULT_CAP		GENMASK(11, 0)
614a040e7cSYuanjiang Yu #define SC27XX_FGU_NORMAIL_POWERTON	0x5
624a040e7cSYuanjiang 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
750a4f97a1SBaolin Wang  * @charge_chan: IIO channel to get charge voltage
76195ca170SBaolin Wang  * @internal_resist: the battery internal resistance in mOhm
77195ca170SBaolin Wang  * @total_cap: the total capacity of the battery in mAh
78195ca170SBaolin Wang  * @init_cap: the initial capacity of the battery in mAh
79edcb1c0aSYuanjiang Yu  * @alarm_cap: the alarm capacity
80195ca170SBaolin Wang  * @init_clbcnt: the initial coulomb counter
81195ca170SBaolin Wang  * @max_volt: the maximum constant input voltage in millivolt
82edcb1c0aSYuanjiang Yu  * @min_volt: the minimum drained battery voltage in microvolt
83195ca170SBaolin Wang  * @table_len: the capacity table length
8465c9fab7SBaolin Wang  * @cur_1000ma_adc: ADC value corresponding to 1000 mA
8565c9fab7SBaolin Wang  * @vol_1000mv_adc: ADC value corresponding to 1000 mV
86195ca170SBaolin Wang  * @cap_table: capacity table with corresponding ocv
87195ca170SBaolin Wang  */
88195ca170SBaolin Wang struct sc27xx_fgu_data {
89195ca170SBaolin Wang 	struct regmap *regmap;
90195ca170SBaolin Wang 	struct device *dev;
91195ca170SBaolin Wang 	struct power_supply *battery;
92195ca170SBaolin Wang 	u32 base;
93195ca170SBaolin Wang 	struct mutex lock;
94195ca170SBaolin Wang 	struct gpio_desc *gpiod;
95195ca170SBaolin Wang 	struct iio_channel *channel;
960a4f97a1SBaolin Wang 	struct iio_channel *charge_chan;
97195ca170SBaolin Wang 	bool bat_present;
98195ca170SBaolin Wang 	int internal_resist;
99195ca170SBaolin Wang 	int total_cap;
100195ca170SBaolin Wang 	int init_cap;
101edcb1c0aSYuanjiang Yu 	int alarm_cap;
102195ca170SBaolin Wang 	int init_clbcnt;
103195ca170SBaolin Wang 	int max_volt;
104edcb1c0aSYuanjiang Yu 	int min_volt;
105195ca170SBaolin Wang 	int table_len;
10665c9fab7SBaolin Wang 	int cur_1000ma_adc;
10765c9fab7SBaolin Wang 	int vol_1000mv_adc;
108195ca170SBaolin Wang 	struct power_supply_battery_ocv_table *cap_table;
109195ca170SBaolin Wang };
110195ca170SBaolin Wang 
111edcb1c0aSYuanjiang Yu static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity);
112edcb1c0aSYuanjiang Yu 
113195ca170SBaolin Wang static const char * const sc27xx_charger_supply_name[] = {
114195ca170SBaolin Wang 	"sc2731_charger",
115195ca170SBaolin Wang 	"sc2720_charger",
116195ca170SBaolin Wang 	"sc2721_charger",
117195ca170SBaolin Wang 	"sc2723_charger",
118195ca170SBaolin Wang };
119195ca170SBaolin Wang 
12065c9fab7SBaolin Wang static int sc27xx_fgu_adc_to_current(struct sc27xx_fgu_data *data, int adc)
121195ca170SBaolin Wang {
12265c9fab7SBaolin Wang 	return DIV_ROUND_CLOSEST(adc * 1000, data->cur_1000ma_adc);
123195ca170SBaolin Wang }
124195ca170SBaolin Wang 
12565c9fab7SBaolin Wang static int sc27xx_fgu_adc_to_voltage(struct sc27xx_fgu_data *data, int adc)
126195ca170SBaolin Wang {
12765c9fab7SBaolin Wang 	return DIV_ROUND_CLOSEST(adc * 1000, data->vol_1000mv_adc);
128195ca170SBaolin Wang }
129195ca170SBaolin Wang 
130edcb1c0aSYuanjiang Yu static int sc27xx_fgu_voltage_to_adc(struct sc27xx_fgu_data *data, int vol)
131edcb1c0aSYuanjiang Yu {
132edcb1c0aSYuanjiang Yu 	return DIV_ROUND_CLOSEST(vol * data->vol_1000mv_adc, 1000);
133edcb1c0aSYuanjiang Yu }
134edcb1c0aSYuanjiang Yu 
1354a040e7cSYuanjiang Yu static bool sc27xx_fgu_is_first_poweron(struct sc27xx_fgu_data *data)
1364a040e7cSYuanjiang Yu {
1374a040e7cSYuanjiang Yu 	int ret, status, cap, mode;
1384a040e7cSYuanjiang Yu 
1394a040e7cSYuanjiang Yu 	ret = regmap_read(data->regmap,
1404a040e7cSYuanjiang Yu 			  data->base + SC27XX_FGU_USER_AREA_STATUS, &status);
1414a040e7cSYuanjiang Yu 	if (ret)
1424a040e7cSYuanjiang Yu 		return false;
1434a040e7cSYuanjiang Yu 
1444a040e7cSYuanjiang Yu 	/*
1454a040e7cSYuanjiang Yu 	 * We use low 4 bits to save the last battery capacity and high 12 bits
1464a040e7cSYuanjiang Yu 	 * to save the system boot mode.
1474a040e7cSYuanjiang Yu 	 */
1484a040e7cSYuanjiang Yu 	mode = (status & SC27XX_FGU_MODE_AREA_MASK) >> SC27XX_FGU_MODE_AREA_SHIFT;
1494a040e7cSYuanjiang Yu 	cap = status & SC27XX_FGU_CAP_AREA_MASK;
1504a040e7cSYuanjiang Yu 
1514a040e7cSYuanjiang Yu 	/*
1524a040e7cSYuanjiang Yu 	 * When FGU has been powered down, the user area registers became
1534a040e7cSYuanjiang Yu 	 * default value (0xffff), which can be used to valid if the system is
1544a040e7cSYuanjiang Yu 	 * first power on or not.
1554a040e7cSYuanjiang Yu 	 */
1564a040e7cSYuanjiang Yu 	if (mode == SC27XX_FGU_FIRST_POWERTON || cap == SC27XX_FGU_DEFAULT_CAP)
1574a040e7cSYuanjiang Yu 		return true;
1584a040e7cSYuanjiang Yu 
1594a040e7cSYuanjiang Yu 	return false;
1604a040e7cSYuanjiang Yu }
1614a040e7cSYuanjiang Yu 
1624a040e7cSYuanjiang Yu static int sc27xx_fgu_save_boot_mode(struct sc27xx_fgu_data *data,
1634a040e7cSYuanjiang Yu 				     int boot_mode)
1644a040e7cSYuanjiang Yu {
1654a040e7cSYuanjiang Yu 	int ret;
1664a040e7cSYuanjiang Yu 
1674a040e7cSYuanjiang Yu 	ret = regmap_update_bits(data->regmap,
1684a040e7cSYuanjiang Yu 				 data->base + SC27XX_FGU_USER_AREA_CLEAR,
1694a040e7cSYuanjiang Yu 				 SC27XX_FGU_MODE_AREA_MASK,
1704a040e7cSYuanjiang Yu 				 SC27XX_FGU_MODE_AREA_MASK);
1714a040e7cSYuanjiang Yu 	if (ret)
1724a040e7cSYuanjiang Yu 		return ret;
1734a040e7cSYuanjiang Yu 
174*d3e67c94SYuanjiang Yu 	/*
175*d3e67c94SYuanjiang Yu 	 * Since the user area registers are put on power always-on region,
176*d3e67c94SYuanjiang Yu 	 * then these registers changing time will be a little long. Thus
177*d3e67c94SYuanjiang Yu 	 * here we should delay 200us to wait until values are updated
178*d3e67c94SYuanjiang Yu 	 * successfully according to the datasheet.
179*d3e67c94SYuanjiang Yu 	 */
180*d3e67c94SYuanjiang Yu 	udelay(200);
181*d3e67c94SYuanjiang Yu 
182*d3e67c94SYuanjiang Yu 	ret = regmap_update_bits(data->regmap,
1834a040e7cSYuanjiang Yu 				 data->base + SC27XX_FGU_USER_AREA_SET,
1844a040e7cSYuanjiang Yu 				 SC27XX_FGU_MODE_AREA_MASK,
1854a040e7cSYuanjiang Yu 				 boot_mode << SC27XX_FGU_MODE_AREA_SHIFT);
186*d3e67c94SYuanjiang Yu 	if (ret)
187*d3e67c94SYuanjiang Yu 		return ret;
188*d3e67c94SYuanjiang Yu 
189*d3e67c94SYuanjiang Yu 	/*
190*d3e67c94SYuanjiang Yu 	 * Since the user area registers are put on power always-on region,
191*d3e67c94SYuanjiang Yu 	 * then these registers changing time will be a little long. Thus
192*d3e67c94SYuanjiang Yu 	 * here we should delay 200us to wait until values are updated
193*d3e67c94SYuanjiang Yu 	 * successfully according to the datasheet.
194*d3e67c94SYuanjiang Yu 	 */
195*d3e67c94SYuanjiang Yu 	udelay(200);
196*d3e67c94SYuanjiang Yu 
197*d3e67c94SYuanjiang Yu 	/*
198*d3e67c94SYuanjiang Yu 	 * According to the datasheet, we should set the USER_AREA_CLEAR to 0 to
199*d3e67c94SYuanjiang Yu 	 * make the user area data available, otherwise we can not save the user
200*d3e67c94SYuanjiang Yu 	 * area data.
201*d3e67c94SYuanjiang Yu 	 */
202*d3e67c94SYuanjiang Yu 	return regmap_update_bits(data->regmap,
203*d3e67c94SYuanjiang Yu 				  data->base + SC27XX_FGU_USER_AREA_CLEAR,
204*d3e67c94SYuanjiang Yu 				  SC27XX_FGU_MODE_AREA_MASK, 0);
2054a040e7cSYuanjiang Yu }
2064a040e7cSYuanjiang Yu 
2074a040e7cSYuanjiang Yu static int sc27xx_fgu_save_last_cap(struct sc27xx_fgu_data *data, int cap)
2084a040e7cSYuanjiang Yu {
2094a040e7cSYuanjiang Yu 	int ret;
2104a040e7cSYuanjiang Yu 
2114a040e7cSYuanjiang Yu 	ret = regmap_update_bits(data->regmap,
2124a040e7cSYuanjiang Yu 				 data->base + SC27XX_FGU_USER_AREA_CLEAR,
2134a040e7cSYuanjiang Yu 				 SC27XX_FGU_CAP_AREA_MASK,
2144a040e7cSYuanjiang Yu 				 SC27XX_FGU_CAP_AREA_MASK);
2154a040e7cSYuanjiang Yu 	if (ret)
2164a040e7cSYuanjiang Yu 		return ret;
2174a040e7cSYuanjiang Yu 
218*d3e67c94SYuanjiang Yu 	/*
219*d3e67c94SYuanjiang Yu 	 * Since the user area registers are put on power always-on region,
220*d3e67c94SYuanjiang Yu 	 * then these registers changing time will be a little long. Thus
221*d3e67c94SYuanjiang Yu 	 * here we should delay 200us to wait until values are updated
222*d3e67c94SYuanjiang Yu 	 * successfully according to the datasheet.
223*d3e67c94SYuanjiang Yu 	 */
224*d3e67c94SYuanjiang Yu 	udelay(200);
225*d3e67c94SYuanjiang Yu 
226*d3e67c94SYuanjiang Yu 	ret = regmap_update_bits(data->regmap,
2274a040e7cSYuanjiang Yu 				 data->base + SC27XX_FGU_USER_AREA_SET,
2284a040e7cSYuanjiang Yu 				 SC27XX_FGU_CAP_AREA_MASK, cap);
229*d3e67c94SYuanjiang Yu 	if (ret)
230*d3e67c94SYuanjiang Yu 		return ret;
231*d3e67c94SYuanjiang Yu 
232*d3e67c94SYuanjiang Yu 	/*
233*d3e67c94SYuanjiang Yu 	 * Since the user area registers are put on power always-on region,
234*d3e67c94SYuanjiang Yu 	 * then these registers changing time will be a little long. Thus
235*d3e67c94SYuanjiang Yu 	 * here we should delay 200us to wait until values are updated
236*d3e67c94SYuanjiang Yu 	 * successfully according to the datasheet.
237*d3e67c94SYuanjiang Yu 	 */
238*d3e67c94SYuanjiang Yu 	udelay(200);
239*d3e67c94SYuanjiang Yu 
240*d3e67c94SYuanjiang Yu 	/*
241*d3e67c94SYuanjiang Yu 	 * According to the datasheet, we should set the USER_AREA_CLEAR to 0 to
242*d3e67c94SYuanjiang Yu 	 * make the user area data available, otherwise we can not save the user
243*d3e67c94SYuanjiang Yu 	 * area data.
244*d3e67c94SYuanjiang Yu 	 */
245*d3e67c94SYuanjiang Yu 	return regmap_update_bits(data->regmap,
246*d3e67c94SYuanjiang Yu 				  data->base + SC27XX_FGU_USER_AREA_CLEAR,
247*d3e67c94SYuanjiang Yu 				  SC27XX_FGU_CAP_AREA_MASK, 0);
2484a040e7cSYuanjiang Yu }
2494a040e7cSYuanjiang Yu 
2504a040e7cSYuanjiang Yu static int sc27xx_fgu_read_last_cap(struct sc27xx_fgu_data *data, int *cap)
2514a040e7cSYuanjiang Yu {
2524a040e7cSYuanjiang Yu 	int ret, value;
2534a040e7cSYuanjiang Yu 
2544a040e7cSYuanjiang Yu 	ret = regmap_read(data->regmap,
2554a040e7cSYuanjiang Yu 			  data->base + SC27XX_FGU_USER_AREA_STATUS, &value);
2564a040e7cSYuanjiang Yu 	if (ret)
2574a040e7cSYuanjiang Yu 		return ret;
2584a040e7cSYuanjiang Yu 
2594a040e7cSYuanjiang Yu 	*cap = value & SC27XX_FGU_CAP_AREA_MASK;
2604a040e7cSYuanjiang Yu 	return 0;
2614a040e7cSYuanjiang Yu }
2624a040e7cSYuanjiang Yu 
263195ca170SBaolin Wang /*
264195ca170SBaolin Wang  * When system boots on, we can not read battery capacity from coulomb
265195ca170SBaolin Wang  * registers, since now the coulomb registers are invalid. So we should
266195ca170SBaolin Wang  * calculate the battery open circuit voltage, and get current battery
267195ca170SBaolin Wang  * capacity according to the capacity table.
268195ca170SBaolin Wang  */
269195ca170SBaolin Wang static int sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data *data, int *cap)
270195ca170SBaolin Wang {
271195ca170SBaolin Wang 	int volt, cur, oci, ocv, ret;
2724a040e7cSYuanjiang Yu 	bool is_first_poweron = sc27xx_fgu_is_first_poweron(data);
2734a040e7cSYuanjiang Yu 
2744a040e7cSYuanjiang Yu 	/*
2754a040e7cSYuanjiang Yu 	 * If system is not the first power on, we should use the last saved
2764a040e7cSYuanjiang Yu 	 * battery capacity as the initial battery capacity. Otherwise we should
2774a040e7cSYuanjiang Yu 	 * re-calculate the initial battery capacity.
2784a040e7cSYuanjiang Yu 	 */
2794a040e7cSYuanjiang Yu 	if (!is_first_poweron) {
2804a040e7cSYuanjiang Yu 		ret = sc27xx_fgu_read_last_cap(data, cap);
2814a040e7cSYuanjiang Yu 		if (ret)
2824a040e7cSYuanjiang Yu 			return ret;
2834a040e7cSYuanjiang Yu 
2844a040e7cSYuanjiang Yu 		return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON);
2854a040e7cSYuanjiang Yu 	}
286195ca170SBaolin Wang 
287195ca170SBaolin Wang 	/*
288195ca170SBaolin Wang 	 * After system booting on, the SC27XX_FGU_CLBCNT_QMAXL register saved
289195ca170SBaolin Wang 	 * the first sampled open circuit current.
290195ca170SBaolin Wang 	 */
291195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_QMAXL,
292195ca170SBaolin Wang 			  &cur);
293195ca170SBaolin Wang 	if (ret)
294195ca170SBaolin Wang 		return ret;
295195ca170SBaolin Wang 
296195ca170SBaolin Wang 	cur <<= 1;
29765c9fab7SBaolin Wang 	oci = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC);
298195ca170SBaolin Wang 
299195ca170SBaolin Wang 	/*
300195ca170SBaolin Wang 	 * Should get the OCV from SC27XX_FGU_POCV register at the system
301195ca170SBaolin Wang 	 * beginning. It is ADC values reading from registers which need to
302195ca170SBaolin Wang 	 * convert the corresponding voltage.
303195ca170SBaolin Wang 	 */
304195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_POCV, &volt);
305195ca170SBaolin Wang 	if (ret)
306195ca170SBaolin Wang 		return ret;
307195ca170SBaolin Wang 
30865c9fab7SBaolin Wang 	volt = sc27xx_fgu_adc_to_voltage(data, volt);
309195ca170SBaolin Wang 	ocv = volt * 1000 - oci * data->internal_resist;
310195ca170SBaolin Wang 
311195ca170SBaolin Wang 	/*
312195ca170SBaolin Wang 	 * Parse the capacity table to look up the correct capacity percent
313195ca170SBaolin Wang 	 * according to current battery's corresponding OCV values.
314195ca170SBaolin Wang 	 */
315195ca170SBaolin Wang 	*cap = power_supply_ocv2cap_simple(data->cap_table, data->table_len,
316195ca170SBaolin Wang 					   ocv);
317195ca170SBaolin Wang 
3184a040e7cSYuanjiang Yu 	ret = sc27xx_fgu_save_last_cap(data, *cap);
3194a040e7cSYuanjiang Yu 	if (ret)
3204a040e7cSYuanjiang Yu 		return ret;
3214a040e7cSYuanjiang Yu 
3224a040e7cSYuanjiang Yu 	return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON);
323195ca170SBaolin Wang }
324195ca170SBaolin Wang 
325195ca170SBaolin Wang static int sc27xx_fgu_set_clbcnt(struct sc27xx_fgu_data *data, int clbcnt)
326195ca170SBaolin Wang {
327195ca170SBaolin Wang 	int ret;
328195ca170SBaolin Wang 
329195ca170SBaolin Wang 	clbcnt *= SC27XX_FGU_SAMPLE_HZ;
330195ca170SBaolin Wang 
331195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap,
332195ca170SBaolin Wang 				 data->base + SC27XX_FGU_CLBCNT_SETL,
333195ca170SBaolin Wang 				 SC27XX_FGU_CLBCNT_MASK, clbcnt);
334195ca170SBaolin Wang 	if (ret)
335195ca170SBaolin Wang 		return ret;
336195ca170SBaolin Wang 
337195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap,
338195ca170SBaolin Wang 				 data->base + SC27XX_FGU_CLBCNT_SETH,
339195ca170SBaolin Wang 				 SC27XX_FGU_CLBCNT_MASK,
340195ca170SBaolin Wang 				 clbcnt >> SC27XX_FGU_CLBCNT_SHIFT);
341195ca170SBaolin Wang 	if (ret)
342195ca170SBaolin Wang 		return ret;
343195ca170SBaolin Wang 
344195ca170SBaolin Wang 	return regmap_update_bits(data->regmap, data->base + SC27XX_FGU_START,
345195ca170SBaolin Wang 				 SC27XX_WRITE_SELCLB_EN,
346195ca170SBaolin Wang 				 SC27XX_WRITE_SELCLB_EN);
347195ca170SBaolin Wang }
348195ca170SBaolin Wang 
349195ca170SBaolin Wang static int sc27xx_fgu_get_clbcnt(struct sc27xx_fgu_data *data, int *clb_cnt)
350195ca170SBaolin Wang {
351195ca170SBaolin Wang 	int ccl, cch, ret;
352195ca170SBaolin Wang 
353195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALL,
354195ca170SBaolin Wang 			  &ccl);
355195ca170SBaolin Wang 	if (ret)
356195ca170SBaolin Wang 		return ret;
357195ca170SBaolin Wang 
358195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALH,
359195ca170SBaolin Wang 			  &cch);
360195ca170SBaolin Wang 	if (ret)
361195ca170SBaolin Wang 		return ret;
362195ca170SBaolin Wang 
363195ca170SBaolin Wang 	*clb_cnt = ccl & SC27XX_FGU_CLBCNT_MASK;
364195ca170SBaolin Wang 	*clb_cnt |= (cch & SC27XX_FGU_CLBCNT_MASK) << SC27XX_FGU_CLBCNT_SHIFT;
365195ca170SBaolin Wang 	*clb_cnt /= SC27XX_FGU_SAMPLE_HZ;
366195ca170SBaolin Wang 
367195ca170SBaolin Wang 	return 0;
368195ca170SBaolin Wang }
369195ca170SBaolin Wang 
370195ca170SBaolin Wang static int sc27xx_fgu_get_capacity(struct sc27xx_fgu_data *data, int *cap)
371195ca170SBaolin Wang {
372195ca170SBaolin Wang 	int ret, cur_clbcnt, delta_clbcnt, delta_cap, temp;
373195ca170SBaolin Wang 
374195ca170SBaolin Wang 	/* Get current coulomb counters firstly */
375195ca170SBaolin Wang 	ret = sc27xx_fgu_get_clbcnt(data, &cur_clbcnt);
376195ca170SBaolin Wang 	if (ret)
377195ca170SBaolin Wang 		return ret;
378195ca170SBaolin Wang 
379195ca170SBaolin Wang 	delta_clbcnt = cur_clbcnt - data->init_clbcnt;
380195ca170SBaolin Wang 
381195ca170SBaolin Wang 	/*
382195ca170SBaolin Wang 	 * Convert coulomb counter to delta capacity (mAh), and set multiplier
383195ca170SBaolin Wang 	 * as 100 to improve the precision.
384195ca170SBaolin Wang 	 */
385195ca170SBaolin Wang 	temp = DIV_ROUND_CLOSEST(delta_clbcnt, 360);
38665c9fab7SBaolin Wang 	temp = sc27xx_fgu_adc_to_current(data, temp);
387195ca170SBaolin Wang 
388195ca170SBaolin Wang 	/*
389195ca170SBaolin Wang 	 * Convert to capacity percent of the battery total capacity,
390195ca170SBaolin Wang 	 * and multiplier is 100 too.
391195ca170SBaolin Wang 	 */
392195ca170SBaolin Wang 	delta_cap = DIV_ROUND_CLOSEST(temp * 100, data->total_cap);
393195ca170SBaolin Wang 	*cap = delta_cap + data->init_cap;
394195ca170SBaolin Wang 
395195ca170SBaolin Wang 	return 0;
396195ca170SBaolin Wang }
397195ca170SBaolin Wang 
398195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_vol(struct sc27xx_fgu_data *data, int *val)
399195ca170SBaolin Wang {
400195ca170SBaolin Wang 	int ret, vol;
401195ca170SBaolin Wang 
402195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_VOLTAGE, &vol);
403195ca170SBaolin Wang 	if (ret)
404195ca170SBaolin Wang 		return ret;
405195ca170SBaolin Wang 
406195ca170SBaolin Wang 	/*
407195ca170SBaolin Wang 	 * It is ADC values reading from registers which need to convert to
408195ca170SBaolin Wang 	 * corresponding voltage values.
409195ca170SBaolin Wang 	 */
41065c9fab7SBaolin Wang 	*val = sc27xx_fgu_adc_to_voltage(data, vol);
411195ca170SBaolin Wang 
412195ca170SBaolin Wang 	return 0;
413195ca170SBaolin Wang }
414195ca170SBaolin Wang 
415195ca170SBaolin Wang static int sc27xx_fgu_get_current(struct sc27xx_fgu_data *data, int *val)
416195ca170SBaolin Wang {
417195ca170SBaolin Wang 	int ret, cur;
418195ca170SBaolin Wang 
419195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CURRENT, &cur);
420195ca170SBaolin Wang 	if (ret)
421195ca170SBaolin Wang 		return ret;
422195ca170SBaolin Wang 
423195ca170SBaolin Wang 	/*
424195ca170SBaolin Wang 	 * It is ADC values reading from registers which need to convert to
425195ca170SBaolin Wang 	 * corresponding current values.
426195ca170SBaolin Wang 	 */
42765c9fab7SBaolin Wang 	*val = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC);
428195ca170SBaolin Wang 
429195ca170SBaolin Wang 	return 0;
430195ca170SBaolin Wang }
431195ca170SBaolin Wang 
432195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val)
433195ca170SBaolin Wang {
434195ca170SBaolin Wang 	int vol, cur, ret;
435195ca170SBaolin Wang 
436195ca170SBaolin Wang 	ret = sc27xx_fgu_get_vbat_vol(data, &vol);
437195ca170SBaolin Wang 	if (ret)
438195ca170SBaolin Wang 		return ret;
439195ca170SBaolin Wang 
440195ca170SBaolin Wang 	ret = sc27xx_fgu_get_current(data, &cur);
441195ca170SBaolin Wang 	if (ret)
442195ca170SBaolin Wang 		return ret;
443195ca170SBaolin Wang 
444195ca170SBaolin Wang 	/* Return the battery OCV in micro volts. */
445195ca170SBaolin Wang 	*val = vol * 1000 - cur * data->internal_resist;
446195ca170SBaolin Wang 
447195ca170SBaolin Wang 	return 0;
448195ca170SBaolin Wang }
449195ca170SBaolin Wang 
4500a4f97a1SBaolin Wang static int sc27xx_fgu_get_charge_vol(struct sc27xx_fgu_data *data, int *val)
4510a4f97a1SBaolin Wang {
4520a4f97a1SBaolin Wang 	int ret, vol;
4530a4f97a1SBaolin Wang 
4540a4f97a1SBaolin Wang 	ret = iio_read_channel_processed(data->charge_chan, &vol);
4550a4f97a1SBaolin Wang 	if (ret < 0)
4560a4f97a1SBaolin Wang 		return ret;
4570a4f97a1SBaolin Wang 
4580a4f97a1SBaolin Wang 	*val = vol * 1000;
4590a4f97a1SBaolin Wang 	return 0;
4600a4f97a1SBaolin Wang }
4610a4f97a1SBaolin Wang 
462195ca170SBaolin Wang static int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp)
463195ca170SBaolin Wang {
464195ca170SBaolin Wang 	return iio_read_channel_processed(data->channel, temp);
465195ca170SBaolin Wang }
466195ca170SBaolin Wang 
467195ca170SBaolin Wang static int sc27xx_fgu_get_health(struct sc27xx_fgu_data *data, int *health)
468195ca170SBaolin Wang {
469195ca170SBaolin Wang 	int ret, vol;
470195ca170SBaolin Wang 
471195ca170SBaolin Wang 	ret = sc27xx_fgu_get_vbat_vol(data, &vol);
472195ca170SBaolin Wang 	if (ret)
473195ca170SBaolin Wang 		return ret;
474195ca170SBaolin Wang 
475195ca170SBaolin Wang 	if (vol > data->max_volt)
476195ca170SBaolin Wang 		*health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
477195ca170SBaolin Wang 	else
478195ca170SBaolin Wang 		*health = POWER_SUPPLY_HEALTH_GOOD;
479195ca170SBaolin Wang 
480195ca170SBaolin Wang 	return 0;
481195ca170SBaolin Wang }
482195ca170SBaolin Wang 
483195ca170SBaolin Wang static int sc27xx_fgu_get_status(struct sc27xx_fgu_data *data, int *status)
484195ca170SBaolin Wang {
485195ca170SBaolin Wang 	union power_supply_propval val;
486195ca170SBaolin Wang 	struct power_supply *psy;
487195ca170SBaolin Wang 	int i, ret = -EINVAL;
488195ca170SBaolin Wang 
489195ca170SBaolin Wang 	for (i = 0; i < ARRAY_SIZE(sc27xx_charger_supply_name); i++) {
490195ca170SBaolin Wang 		psy = power_supply_get_by_name(sc27xx_charger_supply_name[i]);
491195ca170SBaolin Wang 		if (!psy)
492195ca170SBaolin Wang 			continue;
493195ca170SBaolin Wang 
494195ca170SBaolin Wang 		ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS,
495195ca170SBaolin Wang 						&val);
496195ca170SBaolin Wang 		power_supply_put(psy);
497195ca170SBaolin Wang 		if (ret)
498195ca170SBaolin Wang 			return ret;
499195ca170SBaolin Wang 
500195ca170SBaolin Wang 		*status = val.intval;
501195ca170SBaolin Wang 	}
502195ca170SBaolin Wang 
503195ca170SBaolin Wang 	return ret;
504195ca170SBaolin Wang }
505195ca170SBaolin Wang 
506195ca170SBaolin Wang static int sc27xx_fgu_get_property(struct power_supply *psy,
507195ca170SBaolin Wang 				   enum power_supply_property psp,
508195ca170SBaolin Wang 				   union power_supply_propval *val)
509195ca170SBaolin Wang {
510195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy);
511195ca170SBaolin Wang 	int ret = 0;
512195ca170SBaolin Wang 	int value;
513195ca170SBaolin Wang 
514195ca170SBaolin Wang 	mutex_lock(&data->lock);
515195ca170SBaolin Wang 
516195ca170SBaolin Wang 	switch (psp) {
517195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_STATUS:
518195ca170SBaolin Wang 		ret = sc27xx_fgu_get_status(data, &value);
519195ca170SBaolin Wang 		if (ret)
520195ca170SBaolin Wang 			goto error;
521195ca170SBaolin Wang 
522195ca170SBaolin Wang 		val->intval = value;
523195ca170SBaolin Wang 		break;
524195ca170SBaolin Wang 
525195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_HEALTH:
526195ca170SBaolin Wang 		ret = sc27xx_fgu_get_health(data, &value);
527195ca170SBaolin Wang 		if (ret)
528195ca170SBaolin Wang 			goto error;
529195ca170SBaolin Wang 
530195ca170SBaolin Wang 		val->intval = value;
531195ca170SBaolin Wang 		break;
532195ca170SBaolin Wang 
533195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_PRESENT:
534195ca170SBaolin Wang 		val->intval = data->bat_present;
535195ca170SBaolin Wang 		break;
536195ca170SBaolin Wang 
537195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_TEMP:
538195ca170SBaolin Wang 		ret = sc27xx_fgu_get_temp(data, &value);
539195ca170SBaolin Wang 		if (ret)
540195ca170SBaolin Wang 			goto error;
541195ca170SBaolin Wang 
542195ca170SBaolin Wang 		val->intval = value;
543195ca170SBaolin Wang 		break;
544195ca170SBaolin Wang 
545195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_TECHNOLOGY:
546195ca170SBaolin Wang 		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
547195ca170SBaolin Wang 		break;
548195ca170SBaolin Wang 
549195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_CAPACITY:
550195ca170SBaolin Wang 		ret = sc27xx_fgu_get_capacity(data, &value);
551195ca170SBaolin Wang 		if (ret)
552195ca170SBaolin Wang 			goto error;
553195ca170SBaolin Wang 
554195ca170SBaolin Wang 		val->intval = value;
555195ca170SBaolin Wang 		break;
556195ca170SBaolin Wang 
557195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
558195ca170SBaolin Wang 		ret = sc27xx_fgu_get_vbat_vol(data, &value);
559195ca170SBaolin Wang 		if (ret)
560195ca170SBaolin Wang 			goto error;
561195ca170SBaolin Wang 
562195ca170SBaolin Wang 		val->intval = value * 1000;
563195ca170SBaolin Wang 		break;
564195ca170SBaolin Wang 
565195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_VOLTAGE_OCV:
566195ca170SBaolin Wang 		ret = sc27xx_fgu_get_vbat_ocv(data, &value);
567195ca170SBaolin Wang 		if (ret)
568195ca170SBaolin Wang 			goto error;
569195ca170SBaolin Wang 
570195ca170SBaolin Wang 		val->intval = value;
571195ca170SBaolin Wang 		break;
572195ca170SBaolin Wang 
5730a4f97a1SBaolin Wang 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
5740a4f97a1SBaolin Wang 		ret = sc27xx_fgu_get_charge_vol(data, &value);
5750a4f97a1SBaolin Wang 		if (ret)
5760a4f97a1SBaolin Wang 			goto error;
5770a4f97a1SBaolin Wang 
5780a4f97a1SBaolin Wang 		val->intval = value;
5790a4f97a1SBaolin Wang 		break;
5800a4f97a1SBaolin Wang 
581195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_CURRENT_NOW:
582195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_CURRENT_AVG:
583195ca170SBaolin Wang 		ret = sc27xx_fgu_get_current(data, &value);
584195ca170SBaolin Wang 		if (ret)
585195ca170SBaolin Wang 			goto error;
586195ca170SBaolin Wang 
587195ca170SBaolin Wang 		val->intval = value * 1000;
588195ca170SBaolin Wang 		break;
589195ca170SBaolin Wang 
590195ca170SBaolin Wang 	default:
591195ca170SBaolin Wang 		ret = -EINVAL;
592195ca170SBaolin Wang 		break;
593195ca170SBaolin Wang 	}
594195ca170SBaolin Wang 
595195ca170SBaolin Wang error:
596195ca170SBaolin Wang 	mutex_unlock(&data->lock);
597195ca170SBaolin Wang 	return ret;
598195ca170SBaolin Wang }
599195ca170SBaolin Wang 
6004a040e7cSYuanjiang Yu static int sc27xx_fgu_set_property(struct power_supply *psy,
6014a040e7cSYuanjiang Yu 				   enum power_supply_property psp,
6024a040e7cSYuanjiang Yu 				   const union power_supply_propval *val)
6034a040e7cSYuanjiang Yu {
6044a040e7cSYuanjiang Yu 	struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy);
6054a040e7cSYuanjiang Yu 	int ret;
6064a040e7cSYuanjiang Yu 
6074a040e7cSYuanjiang Yu 	if (psp != POWER_SUPPLY_PROP_CAPACITY)
6084a040e7cSYuanjiang Yu 		return -EINVAL;
6094a040e7cSYuanjiang Yu 
6104a040e7cSYuanjiang Yu 	mutex_lock(&data->lock);
6114a040e7cSYuanjiang Yu 
6124a040e7cSYuanjiang Yu 	ret = sc27xx_fgu_save_last_cap(data, val->intval);
6134a040e7cSYuanjiang Yu 
6144a040e7cSYuanjiang Yu 	mutex_unlock(&data->lock);
6154a040e7cSYuanjiang Yu 
6164a040e7cSYuanjiang Yu 	if (ret < 0)
6174a040e7cSYuanjiang Yu 		dev_err(data->dev, "failed to save battery capacity\n");
6184a040e7cSYuanjiang Yu 
6194a040e7cSYuanjiang Yu 	return ret;
6204a040e7cSYuanjiang Yu }
6214a040e7cSYuanjiang Yu 
622195ca170SBaolin Wang static void sc27xx_fgu_external_power_changed(struct power_supply *psy)
623195ca170SBaolin Wang {
624195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy);
625195ca170SBaolin Wang 
626195ca170SBaolin Wang 	power_supply_changed(data->battery);
627195ca170SBaolin Wang }
628195ca170SBaolin Wang 
6294a040e7cSYuanjiang Yu static int sc27xx_fgu_property_is_writeable(struct power_supply *psy,
6304a040e7cSYuanjiang Yu 					    enum power_supply_property psp)
6314a040e7cSYuanjiang Yu {
6324a040e7cSYuanjiang Yu 	return psp == POWER_SUPPLY_PROP_CAPACITY;
6334a040e7cSYuanjiang Yu }
6344a040e7cSYuanjiang Yu 
635195ca170SBaolin Wang static enum power_supply_property sc27xx_fgu_props[] = {
636195ca170SBaolin Wang 	POWER_SUPPLY_PROP_STATUS,
637195ca170SBaolin Wang 	POWER_SUPPLY_PROP_HEALTH,
638195ca170SBaolin Wang 	POWER_SUPPLY_PROP_PRESENT,
639195ca170SBaolin Wang 	POWER_SUPPLY_PROP_TEMP,
640195ca170SBaolin Wang 	POWER_SUPPLY_PROP_TECHNOLOGY,
641195ca170SBaolin Wang 	POWER_SUPPLY_PROP_CAPACITY,
642195ca170SBaolin Wang 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
643195ca170SBaolin Wang 	POWER_SUPPLY_PROP_VOLTAGE_OCV,
644195ca170SBaolin Wang 	POWER_SUPPLY_PROP_CURRENT_NOW,
645195ca170SBaolin Wang 	POWER_SUPPLY_PROP_CURRENT_AVG,
6460a4f97a1SBaolin Wang 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
647195ca170SBaolin Wang };
648195ca170SBaolin Wang 
649195ca170SBaolin Wang static const struct power_supply_desc sc27xx_fgu_desc = {
650195ca170SBaolin Wang 	.name			= "sc27xx-fgu",
651195ca170SBaolin Wang 	.type			= POWER_SUPPLY_TYPE_BATTERY,
652195ca170SBaolin Wang 	.properties		= sc27xx_fgu_props,
653195ca170SBaolin Wang 	.num_properties		= ARRAY_SIZE(sc27xx_fgu_props),
654195ca170SBaolin Wang 	.get_property		= sc27xx_fgu_get_property,
6554a040e7cSYuanjiang Yu 	.set_property		= sc27xx_fgu_set_property,
656195ca170SBaolin Wang 	.external_power_changed	= sc27xx_fgu_external_power_changed,
6574a040e7cSYuanjiang Yu 	.property_is_writeable	= sc27xx_fgu_property_is_writeable,
658195ca170SBaolin Wang };
659195ca170SBaolin Wang 
660edcb1c0aSYuanjiang Yu static void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap)
661edcb1c0aSYuanjiang Yu {
662edcb1c0aSYuanjiang Yu 	data->init_cap = cap;
663edcb1c0aSYuanjiang Yu 	data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap);
664edcb1c0aSYuanjiang Yu }
665edcb1c0aSYuanjiang Yu 
666edcb1c0aSYuanjiang Yu static irqreturn_t sc27xx_fgu_interrupt(int irq, void *dev_id)
667edcb1c0aSYuanjiang Yu {
668edcb1c0aSYuanjiang Yu 	struct sc27xx_fgu_data *data = dev_id;
669edcb1c0aSYuanjiang Yu 	int ret, cap, ocv, adc;
670edcb1c0aSYuanjiang Yu 	u32 status;
671edcb1c0aSYuanjiang Yu 
672edcb1c0aSYuanjiang Yu 	mutex_lock(&data->lock);
673edcb1c0aSYuanjiang Yu 
674edcb1c0aSYuanjiang Yu 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_INT_STS,
675edcb1c0aSYuanjiang Yu 			  &status);
676edcb1c0aSYuanjiang Yu 	if (ret)
677edcb1c0aSYuanjiang Yu 		goto out;
678edcb1c0aSYuanjiang Yu 
679edcb1c0aSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR,
680edcb1c0aSYuanjiang Yu 				 status, status);
681edcb1c0aSYuanjiang Yu 	if (ret)
682edcb1c0aSYuanjiang Yu 		goto out;
683edcb1c0aSYuanjiang Yu 
684edcb1c0aSYuanjiang Yu 	/*
685edcb1c0aSYuanjiang Yu 	 * When low overload voltage interrupt happens, we should calibrate the
686edcb1c0aSYuanjiang Yu 	 * battery capacity in lower voltage stage.
687edcb1c0aSYuanjiang Yu 	 */
688edcb1c0aSYuanjiang Yu 	if (!(status & SC27XX_FGU_LOW_OVERLOAD_INT))
689edcb1c0aSYuanjiang Yu 		goto out;
690edcb1c0aSYuanjiang Yu 
691edcb1c0aSYuanjiang Yu 	ret = sc27xx_fgu_get_capacity(data, &cap);
692edcb1c0aSYuanjiang Yu 	if (ret)
693edcb1c0aSYuanjiang Yu 		goto out;
694edcb1c0aSYuanjiang Yu 
695edcb1c0aSYuanjiang Yu 	ret = sc27xx_fgu_get_vbat_ocv(data, &ocv);
696edcb1c0aSYuanjiang Yu 	if (ret)
697edcb1c0aSYuanjiang Yu 		goto out;
698edcb1c0aSYuanjiang Yu 
699edcb1c0aSYuanjiang Yu 	/*
700edcb1c0aSYuanjiang Yu 	 * If current OCV value is less than the minimum OCV value in OCV table,
701edcb1c0aSYuanjiang Yu 	 * which means now battery capacity is 0%, and we should adjust the
702edcb1c0aSYuanjiang Yu 	 * inititial capacity to 0.
703edcb1c0aSYuanjiang Yu 	 */
704edcb1c0aSYuanjiang Yu 	if (ocv <= data->cap_table[data->table_len - 1].ocv) {
705edcb1c0aSYuanjiang Yu 		sc27xx_fgu_adjust_cap(data, 0);
706edcb1c0aSYuanjiang Yu 	} else if (ocv <= data->min_volt) {
707edcb1c0aSYuanjiang Yu 		/*
708edcb1c0aSYuanjiang Yu 		 * If current OCV value is less than the low alarm voltage, but
709edcb1c0aSYuanjiang Yu 		 * current capacity is larger than the alarm capacity, we should
710edcb1c0aSYuanjiang Yu 		 * adjust the inititial capacity to alarm capacity.
711edcb1c0aSYuanjiang Yu 		 */
712edcb1c0aSYuanjiang Yu 		if (cap > data->alarm_cap) {
713edcb1c0aSYuanjiang Yu 			sc27xx_fgu_adjust_cap(data, data->alarm_cap);
714edcb1c0aSYuanjiang Yu 		} else if (cap <= 0) {
715edcb1c0aSYuanjiang Yu 			int cur_cap;
716edcb1c0aSYuanjiang Yu 
717edcb1c0aSYuanjiang Yu 			/*
718edcb1c0aSYuanjiang Yu 			 * If current capacity is equal with 0 or less than 0
719edcb1c0aSYuanjiang Yu 			 * (some error occurs), we should adjust inititial
720edcb1c0aSYuanjiang Yu 			 * capacity to the capacity corresponding to current OCV
721edcb1c0aSYuanjiang Yu 			 * value.
722edcb1c0aSYuanjiang Yu 			 */
723edcb1c0aSYuanjiang Yu 			cur_cap = power_supply_ocv2cap_simple(data->cap_table,
724edcb1c0aSYuanjiang Yu 							      data->table_len,
725edcb1c0aSYuanjiang Yu 							      ocv);
726edcb1c0aSYuanjiang Yu 			sc27xx_fgu_adjust_cap(data, cur_cap);
727edcb1c0aSYuanjiang Yu 		}
728edcb1c0aSYuanjiang Yu 
729edcb1c0aSYuanjiang Yu 		/*
730edcb1c0aSYuanjiang Yu 		 * After adjusting the battery capacity, we should set the
731edcb1c0aSYuanjiang Yu 		 * lowest alarm voltage instead.
732edcb1c0aSYuanjiang Yu 		 */
733edcb1c0aSYuanjiang Yu 		data->min_volt = data->cap_table[data->table_len - 1].ocv;
734edcb1c0aSYuanjiang Yu 		adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000);
735edcb1c0aSYuanjiang Yu 		regmap_update_bits(data->regmap, data->base + SC27XX_FGU_LOW_OVERLOAD,
736edcb1c0aSYuanjiang Yu 				   SC27XX_FGU_LOW_OVERLOAD_MASK, adc);
737edcb1c0aSYuanjiang Yu 	}
738edcb1c0aSYuanjiang Yu 
739edcb1c0aSYuanjiang Yu out:
740edcb1c0aSYuanjiang Yu 	mutex_unlock(&data->lock);
741edcb1c0aSYuanjiang Yu 
742edcb1c0aSYuanjiang Yu 	power_supply_changed(data->battery);
743edcb1c0aSYuanjiang Yu 	return IRQ_HANDLED;
744edcb1c0aSYuanjiang Yu }
745edcb1c0aSYuanjiang Yu 
746195ca170SBaolin Wang static irqreturn_t sc27xx_fgu_bat_detection(int irq, void *dev_id)
747195ca170SBaolin Wang {
748195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = dev_id;
749195ca170SBaolin Wang 	int state;
750195ca170SBaolin Wang 
751195ca170SBaolin Wang 	mutex_lock(&data->lock);
752195ca170SBaolin Wang 
753195ca170SBaolin Wang 	state = gpiod_get_value_cansleep(data->gpiod);
754195ca170SBaolin Wang 	if (state < 0) {
755195ca170SBaolin Wang 		dev_err(data->dev, "failed to get gpio state\n");
756195ca170SBaolin Wang 		mutex_unlock(&data->lock);
757195ca170SBaolin Wang 		return IRQ_RETVAL(state);
758195ca170SBaolin Wang 	}
759195ca170SBaolin Wang 
760195ca170SBaolin Wang 	data->bat_present = !!state;
761195ca170SBaolin Wang 
762195ca170SBaolin Wang 	mutex_unlock(&data->lock);
763195ca170SBaolin Wang 
764195ca170SBaolin Wang 	power_supply_changed(data->battery);
765195ca170SBaolin Wang 	return IRQ_HANDLED;
766195ca170SBaolin Wang }
767195ca170SBaolin Wang 
768195ca170SBaolin Wang static void sc27xx_fgu_disable(void *_data)
769195ca170SBaolin Wang {
770195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = _data;
771195ca170SBaolin Wang 
772195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0);
773195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0);
774195ca170SBaolin Wang }
775195ca170SBaolin Wang 
776195ca170SBaolin Wang static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity)
777195ca170SBaolin Wang {
778195ca170SBaolin Wang 	/*
779195ca170SBaolin Wang 	 * Get current capacity (mAh) = battery total capacity (mAh) *
780195ca170SBaolin Wang 	 * current capacity percent (capacity / 100).
781195ca170SBaolin Wang 	 */
782195ca170SBaolin Wang 	int cur_cap = DIV_ROUND_CLOSEST(data->total_cap * capacity, 100);
783195ca170SBaolin Wang 
784195ca170SBaolin Wang 	/*
785195ca170SBaolin Wang 	 * Convert current capacity (mAh) to coulomb counter according to the
786195ca170SBaolin Wang 	 * formula: 1 mAh =3.6 coulomb.
787195ca170SBaolin Wang 	 */
7883a28f203SBaolin Wang 	return DIV_ROUND_CLOSEST(cur_cap * 36 * data->cur_1000ma_adc, 10);
789195ca170SBaolin Wang }
790195ca170SBaolin Wang 
79165c9fab7SBaolin Wang static int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data)
79265c9fab7SBaolin Wang {
79365c9fab7SBaolin Wang 	struct nvmem_cell *cell;
79465c9fab7SBaolin Wang 	int calib_data, cal_4200mv;
79565c9fab7SBaolin Wang 	void *buf;
79665c9fab7SBaolin Wang 	size_t len;
79765c9fab7SBaolin Wang 
79865c9fab7SBaolin Wang 	cell = nvmem_cell_get(data->dev, "fgu_calib");
79965c9fab7SBaolin Wang 	if (IS_ERR(cell))
80065c9fab7SBaolin Wang 		return PTR_ERR(cell);
80165c9fab7SBaolin Wang 
80265c9fab7SBaolin Wang 	buf = nvmem_cell_read(cell, &len);
80365c9fab7SBaolin Wang 	nvmem_cell_put(cell);
80465c9fab7SBaolin Wang 
80565c9fab7SBaolin Wang 	if (IS_ERR(buf))
80665c9fab7SBaolin Wang 		return PTR_ERR(buf);
80765c9fab7SBaolin Wang 
80865c9fab7SBaolin Wang 	memcpy(&calib_data, buf, min(len, sizeof(u32)));
80965c9fab7SBaolin Wang 
81065c9fab7SBaolin Wang 	/*
81165c9fab7SBaolin Wang 	 * Get the ADC value corresponding to 4200 mV from eFuse controller
81265c9fab7SBaolin Wang 	 * according to below formula. Then convert to ADC values corresponding
81365c9fab7SBaolin Wang 	 * to 1000 mV and 1000 mA.
81465c9fab7SBaolin Wang 	 */
81565c9fab7SBaolin Wang 	cal_4200mv = (calib_data & 0x1ff) + 6963 - 4096 - 256;
81665c9fab7SBaolin Wang 	data->vol_1000mv_adc = DIV_ROUND_CLOSEST(cal_4200mv * 10, 42);
81765c9fab7SBaolin Wang 	data->cur_1000ma_adc = data->vol_1000mv_adc * 4;
81865c9fab7SBaolin Wang 
81965c9fab7SBaolin Wang 	kfree(buf);
82065c9fab7SBaolin Wang 	return 0;
82165c9fab7SBaolin Wang }
82265c9fab7SBaolin Wang 
823195ca170SBaolin Wang static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data)
824195ca170SBaolin Wang {
825195ca170SBaolin Wang 	struct power_supply_battery_info info = { };
826195ca170SBaolin Wang 	struct power_supply_battery_ocv_table *table;
827edcb1c0aSYuanjiang Yu 	int ret, delta_clbcnt, alarm_adc;
828195ca170SBaolin Wang 
829195ca170SBaolin Wang 	ret = power_supply_get_battery_info(data->battery, &info);
830195ca170SBaolin Wang 	if (ret) {
831195ca170SBaolin Wang 		dev_err(data->dev, "failed to get battery information\n");
832195ca170SBaolin Wang 		return ret;
833195ca170SBaolin Wang 	}
834195ca170SBaolin Wang 
835195ca170SBaolin Wang 	data->total_cap = info.charge_full_design_uah / 1000;
836195ca170SBaolin Wang 	data->max_volt = info.constant_charge_voltage_max_uv / 1000;
837195ca170SBaolin Wang 	data->internal_resist = info.factory_internal_resistance_uohm / 1000;
838edcb1c0aSYuanjiang Yu 	data->min_volt = info.voltage_min_design_uv;
839195ca170SBaolin Wang 
840195ca170SBaolin Wang 	/*
841195ca170SBaolin Wang 	 * For SC27XX fuel gauge device, we only use one ocv-capacity
842195ca170SBaolin Wang 	 * table in normal temperature 20 Celsius.
843195ca170SBaolin Wang 	 */
844195ca170SBaolin Wang 	table = power_supply_find_ocv2cap_table(&info, 20, &data->table_len);
845195ca170SBaolin Wang 	if (!table)
846195ca170SBaolin Wang 		return -EINVAL;
847195ca170SBaolin Wang 
848195ca170SBaolin Wang 	data->cap_table = devm_kmemdup(data->dev, table,
849195ca170SBaolin Wang 				       data->table_len * sizeof(*table),
850195ca170SBaolin Wang 				       GFP_KERNEL);
851195ca170SBaolin Wang 	if (!data->cap_table) {
852195ca170SBaolin Wang 		power_supply_put_battery_info(data->battery, &info);
853195ca170SBaolin Wang 		return -ENOMEM;
854195ca170SBaolin Wang 	}
855195ca170SBaolin Wang 
856edcb1c0aSYuanjiang Yu 	data->alarm_cap = power_supply_ocv2cap_simple(data->cap_table,
857edcb1c0aSYuanjiang Yu 						      data->table_len,
858edcb1c0aSYuanjiang Yu 						      data->min_volt);
859edcb1c0aSYuanjiang Yu 
860195ca170SBaolin Wang 	power_supply_put_battery_info(data->battery, &info);
861195ca170SBaolin Wang 
86265c9fab7SBaolin Wang 	ret = sc27xx_fgu_calibration(data);
86365c9fab7SBaolin Wang 	if (ret)
86465c9fab7SBaolin Wang 		return ret;
86565c9fab7SBaolin Wang 
866195ca170SBaolin Wang 	/* Enable the FGU module */
867195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap, SC27XX_MODULE_EN0,
868195ca170SBaolin Wang 				 SC27XX_FGU_EN, SC27XX_FGU_EN);
869195ca170SBaolin Wang 	if (ret) {
870195ca170SBaolin Wang 		dev_err(data->dev, "failed to enable fgu\n");
871195ca170SBaolin Wang 		return ret;
872195ca170SBaolin Wang 	}
873195ca170SBaolin Wang 
874195ca170SBaolin Wang 	/* Enable the FGU RTC clock to make it work */
875195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap, SC27XX_CLK_EN0,
876195ca170SBaolin Wang 				 SC27XX_FGU_RTC_EN, SC27XX_FGU_RTC_EN);
877195ca170SBaolin Wang 	if (ret) {
878195ca170SBaolin Wang 		dev_err(data->dev, "failed to enable fgu RTC clock\n");
879195ca170SBaolin Wang 		goto disable_fgu;
880195ca170SBaolin Wang 	}
881195ca170SBaolin Wang 
882edcb1c0aSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR,
883edcb1c0aSYuanjiang Yu 				 SC27XX_FGU_INT_MASK, SC27XX_FGU_INT_MASK);
884edcb1c0aSYuanjiang Yu 	if (ret) {
885edcb1c0aSYuanjiang Yu 		dev_err(data->dev, "failed to clear interrupt status\n");
886edcb1c0aSYuanjiang Yu 		goto disable_clk;
887edcb1c0aSYuanjiang Yu 	}
888edcb1c0aSYuanjiang Yu 
889edcb1c0aSYuanjiang Yu 	/*
890edcb1c0aSYuanjiang Yu 	 * Set the voltage low overload threshold, which means when the battery
891edcb1c0aSYuanjiang Yu 	 * voltage is lower than this threshold, the controller will generate
892edcb1c0aSYuanjiang Yu 	 * one interrupt to notify.
893edcb1c0aSYuanjiang Yu 	 */
894edcb1c0aSYuanjiang Yu 	alarm_adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000);
895edcb1c0aSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_LOW_OVERLOAD,
896edcb1c0aSYuanjiang Yu 				 SC27XX_FGU_LOW_OVERLOAD_MASK, alarm_adc);
897edcb1c0aSYuanjiang Yu 	if (ret) {
898edcb1c0aSYuanjiang Yu 		dev_err(data->dev, "failed to set fgu low overload\n");
899edcb1c0aSYuanjiang Yu 		goto disable_clk;
900edcb1c0aSYuanjiang Yu 	}
901edcb1c0aSYuanjiang Yu 
902edcb1c0aSYuanjiang Yu 	/*
903edcb1c0aSYuanjiang Yu 	 * Set the coulomb counter delta threshold, that means when the coulomb
904edcb1c0aSYuanjiang Yu 	 * counter change is multiples of the delta threshold, the controller
905edcb1c0aSYuanjiang Yu 	 * will generate one interrupt to notify the users to update the battery
906edcb1c0aSYuanjiang Yu 	 * capacity. Now we set the delta threshold as a counter value of 1%
907edcb1c0aSYuanjiang Yu 	 * capacity.
908edcb1c0aSYuanjiang Yu 	 */
909edcb1c0aSYuanjiang Yu 	delta_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, 1);
910edcb1c0aSYuanjiang Yu 
911edcb1c0aSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTL,
912edcb1c0aSYuanjiang Yu 				 SC27XX_FGU_CLBCNT_MASK, delta_clbcnt);
913edcb1c0aSYuanjiang Yu 	if (ret) {
914edcb1c0aSYuanjiang Yu 		dev_err(data->dev, "failed to set low delta coulomb counter\n");
915edcb1c0aSYuanjiang Yu 		goto disable_clk;
916edcb1c0aSYuanjiang Yu 	}
917edcb1c0aSYuanjiang Yu 
918edcb1c0aSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTH,
919edcb1c0aSYuanjiang Yu 				 SC27XX_FGU_CLBCNT_MASK,
920edcb1c0aSYuanjiang Yu 				 delta_clbcnt >> SC27XX_FGU_CLBCNT_SHIFT);
921edcb1c0aSYuanjiang Yu 	if (ret) {
922edcb1c0aSYuanjiang Yu 		dev_err(data->dev, "failed to set high delta coulomb counter\n");
923edcb1c0aSYuanjiang Yu 		goto disable_clk;
924edcb1c0aSYuanjiang Yu 	}
925edcb1c0aSYuanjiang Yu 
926195ca170SBaolin Wang 	/*
927195ca170SBaolin Wang 	 * Get the boot battery capacity when system powers on, which is used to
928195ca170SBaolin Wang 	 * initialize the coulomb counter. After that, we can read the coulomb
929195ca170SBaolin Wang 	 * counter to measure the battery capacity.
930195ca170SBaolin Wang 	 */
931195ca170SBaolin Wang 	ret = sc27xx_fgu_get_boot_capacity(data, &data->init_cap);
932195ca170SBaolin Wang 	if (ret) {
933195ca170SBaolin Wang 		dev_err(data->dev, "failed to get boot capacity\n");
934195ca170SBaolin Wang 		goto disable_clk;
935195ca170SBaolin Wang 	}
936195ca170SBaolin Wang 
937195ca170SBaolin Wang 	/*
938195ca170SBaolin Wang 	 * Convert battery capacity to the corresponding initial coulomb counter
939195ca170SBaolin Wang 	 * and set into coulomb counter registers.
940195ca170SBaolin Wang 	 */
941195ca170SBaolin Wang 	data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap);
942195ca170SBaolin Wang 	ret = sc27xx_fgu_set_clbcnt(data, data->init_clbcnt);
943195ca170SBaolin Wang 	if (ret) {
944195ca170SBaolin Wang 		dev_err(data->dev, "failed to initialize coulomb counter\n");
945195ca170SBaolin Wang 		goto disable_clk;
946195ca170SBaolin Wang 	}
947195ca170SBaolin Wang 
948195ca170SBaolin Wang 	return 0;
949195ca170SBaolin Wang 
950195ca170SBaolin Wang disable_clk:
951195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0);
952195ca170SBaolin Wang disable_fgu:
953195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0);
954195ca170SBaolin Wang 
955195ca170SBaolin Wang 	return ret;
956195ca170SBaolin Wang }
957195ca170SBaolin Wang 
958195ca170SBaolin Wang static int sc27xx_fgu_probe(struct platform_device *pdev)
959195ca170SBaolin Wang {
960195ca170SBaolin Wang 	struct device_node *np = pdev->dev.of_node;
961195ca170SBaolin Wang 	struct power_supply_config fgu_cfg = { };
962195ca170SBaolin Wang 	struct sc27xx_fgu_data *data;
963195ca170SBaolin Wang 	int ret, irq;
964195ca170SBaolin Wang 
965195ca170SBaolin Wang 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
966195ca170SBaolin Wang 	if (!data)
967195ca170SBaolin Wang 		return -ENOMEM;
968195ca170SBaolin Wang 
969195ca170SBaolin Wang 	data->regmap = dev_get_regmap(pdev->dev.parent, NULL);
970195ca170SBaolin Wang 	if (!data->regmap) {
971195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to get regmap\n");
972195ca170SBaolin Wang 		return -ENODEV;
973195ca170SBaolin Wang 	}
974195ca170SBaolin Wang 
975195ca170SBaolin Wang 	ret = device_property_read_u32(&pdev->dev, "reg", &data->base);
976195ca170SBaolin Wang 	if (ret) {
977195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to get fgu address\n");
978195ca170SBaolin Wang 		return ret;
979195ca170SBaolin Wang 	}
980195ca170SBaolin Wang 
981195ca170SBaolin Wang 	data->channel = devm_iio_channel_get(&pdev->dev, "bat-temp");
982195ca170SBaolin Wang 	if (IS_ERR(data->channel)) {
983195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to get IIO channel\n");
984195ca170SBaolin Wang 		return PTR_ERR(data->channel);
985195ca170SBaolin Wang 	}
986195ca170SBaolin Wang 
9870a4f97a1SBaolin Wang 	data->charge_chan = devm_iio_channel_get(&pdev->dev, "charge-vol");
9880a4f97a1SBaolin Wang 	if (IS_ERR(data->charge_chan)) {
9890a4f97a1SBaolin Wang 		dev_err(&pdev->dev, "failed to get charge IIO channel\n");
9900a4f97a1SBaolin Wang 		return PTR_ERR(data->charge_chan);
9910a4f97a1SBaolin Wang 	}
9920a4f97a1SBaolin Wang 
993195ca170SBaolin Wang 	data->gpiod = devm_gpiod_get(&pdev->dev, "bat-detect", GPIOD_IN);
994195ca170SBaolin Wang 	if (IS_ERR(data->gpiod)) {
995195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to get battery detection GPIO\n");
996195ca170SBaolin Wang 		return PTR_ERR(data->gpiod);
997195ca170SBaolin Wang 	}
998195ca170SBaolin Wang 
999195ca170SBaolin Wang 	ret = gpiod_get_value_cansleep(data->gpiod);
1000195ca170SBaolin Wang 	if (ret < 0) {
1001195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to get gpio state\n");
1002195ca170SBaolin Wang 		return ret;
1003195ca170SBaolin Wang 	}
1004195ca170SBaolin Wang 
1005195ca170SBaolin Wang 	data->bat_present = !!ret;
1006195ca170SBaolin Wang 	mutex_init(&data->lock);
1007195ca170SBaolin Wang 	data->dev = &pdev->dev;
1008e2fb615bSYuanjiang Yu 	platform_set_drvdata(pdev, data);
1009195ca170SBaolin Wang 
1010195ca170SBaolin Wang 	fgu_cfg.drv_data = data;
1011195ca170SBaolin Wang 	fgu_cfg.of_node = np;
1012195ca170SBaolin Wang 	data->battery = devm_power_supply_register(&pdev->dev, &sc27xx_fgu_desc,
1013195ca170SBaolin Wang 						   &fgu_cfg);
1014195ca170SBaolin Wang 	if (IS_ERR(data->battery)) {
1015195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to register power supply\n");
1016195ca170SBaolin Wang 		return PTR_ERR(data->battery);
1017195ca170SBaolin Wang 	}
1018195ca170SBaolin Wang 
1019195ca170SBaolin Wang 	ret = sc27xx_fgu_hw_init(data);
1020195ca170SBaolin Wang 	if (ret) {
1021195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to initialize fgu hardware\n");
1022195ca170SBaolin Wang 		return ret;
1023195ca170SBaolin Wang 	}
1024195ca170SBaolin Wang 
1025195ca170SBaolin Wang 	ret = devm_add_action(&pdev->dev, sc27xx_fgu_disable, data);
1026195ca170SBaolin Wang 	if (ret) {
1027195ca170SBaolin Wang 		sc27xx_fgu_disable(data);
1028195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to add fgu disable action\n");
1029195ca170SBaolin Wang 		return ret;
1030195ca170SBaolin Wang 	}
1031195ca170SBaolin Wang 
1032edcb1c0aSYuanjiang Yu 	irq = platform_get_irq(pdev, 0);
1033edcb1c0aSYuanjiang Yu 	if (irq < 0) {
1034edcb1c0aSYuanjiang Yu 		dev_err(&pdev->dev, "no irq resource specified\n");
1035edcb1c0aSYuanjiang Yu 		return irq;
1036edcb1c0aSYuanjiang Yu 	}
1037edcb1c0aSYuanjiang Yu 
1038edcb1c0aSYuanjiang Yu 	ret = devm_request_threaded_irq(data->dev, irq, NULL,
1039edcb1c0aSYuanjiang Yu 					sc27xx_fgu_interrupt,
1040edcb1c0aSYuanjiang Yu 					IRQF_NO_SUSPEND | IRQF_ONESHOT,
1041edcb1c0aSYuanjiang Yu 					pdev->name, data);
1042edcb1c0aSYuanjiang Yu 	if (ret) {
1043edcb1c0aSYuanjiang Yu 		dev_err(data->dev, "failed to request fgu IRQ\n");
1044edcb1c0aSYuanjiang Yu 		return ret;
1045edcb1c0aSYuanjiang Yu 	}
1046edcb1c0aSYuanjiang Yu 
1047195ca170SBaolin Wang 	irq = gpiod_to_irq(data->gpiod);
1048195ca170SBaolin Wang 	if (irq < 0) {
1049195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to translate GPIO to IRQ\n");
1050195ca170SBaolin Wang 		return irq;
1051195ca170SBaolin Wang 	}
1052195ca170SBaolin Wang 
1053195ca170SBaolin Wang 	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
1054195ca170SBaolin Wang 					sc27xx_fgu_bat_detection,
1055195ca170SBaolin Wang 					IRQF_ONESHOT | IRQF_TRIGGER_RISING |
1056195ca170SBaolin Wang 					IRQF_TRIGGER_FALLING,
1057195ca170SBaolin Wang 					pdev->name, data);
1058195ca170SBaolin Wang 	if (ret) {
1059195ca170SBaolin Wang 		dev_err(&pdev->dev, "failed to request IRQ\n");
1060195ca170SBaolin Wang 		return ret;
1061195ca170SBaolin Wang 	}
1062195ca170SBaolin Wang 
1063195ca170SBaolin Wang 	return 0;
1064195ca170SBaolin Wang }
1065195ca170SBaolin Wang 
1066e2fb615bSYuanjiang Yu #ifdef CONFIG_PM_SLEEP
1067e2fb615bSYuanjiang Yu static int sc27xx_fgu_resume(struct device *dev)
1068e2fb615bSYuanjiang Yu {
1069e2fb615bSYuanjiang Yu 	struct sc27xx_fgu_data *data = dev_get_drvdata(dev);
1070e2fb615bSYuanjiang Yu 	int ret;
1071e2fb615bSYuanjiang Yu 
1072e2fb615bSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN,
1073e2fb615bSYuanjiang Yu 				 SC27XX_FGU_LOW_OVERLOAD_INT |
1074e2fb615bSYuanjiang Yu 				 SC27XX_FGU_CLBCNT_DELTA_INT, 0);
1075e2fb615bSYuanjiang Yu 	if (ret) {
1076e2fb615bSYuanjiang Yu 		dev_err(data->dev, "failed to disable fgu interrupts\n");
1077e2fb615bSYuanjiang Yu 		return ret;
1078e2fb615bSYuanjiang Yu 	}
1079e2fb615bSYuanjiang Yu 
1080e2fb615bSYuanjiang Yu 	return 0;
1081e2fb615bSYuanjiang Yu }
1082e2fb615bSYuanjiang Yu 
1083e2fb615bSYuanjiang Yu static int sc27xx_fgu_suspend(struct device *dev)
1084e2fb615bSYuanjiang Yu {
1085e2fb615bSYuanjiang Yu 	struct sc27xx_fgu_data *data = dev_get_drvdata(dev);
1086e2fb615bSYuanjiang Yu 	int ret, status, ocv;
1087e2fb615bSYuanjiang Yu 
1088e2fb615bSYuanjiang Yu 	ret = sc27xx_fgu_get_status(data, &status);
1089e2fb615bSYuanjiang Yu 	if (ret)
1090e2fb615bSYuanjiang Yu 		return ret;
1091e2fb615bSYuanjiang Yu 
1092e2fb615bSYuanjiang Yu 	/*
1093e2fb615bSYuanjiang Yu 	 * If we are charging, then no need to enable the FGU interrupts to
1094e2fb615bSYuanjiang Yu 	 * adjust the battery capacity.
1095e2fb615bSYuanjiang Yu 	 */
1096e2fb615bSYuanjiang Yu 	if (status != POWER_SUPPLY_STATUS_NOT_CHARGING)
1097e2fb615bSYuanjiang Yu 		return 0;
1098e2fb615bSYuanjiang Yu 
1099e2fb615bSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN,
1100e2fb615bSYuanjiang Yu 				 SC27XX_FGU_LOW_OVERLOAD_INT,
1101e2fb615bSYuanjiang Yu 				 SC27XX_FGU_LOW_OVERLOAD_INT);
1102e2fb615bSYuanjiang Yu 	if (ret) {
1103e2fb615bSYuanjiang Yu 		dev_err(data->dev, "failed to enable low voltage interrupt\n");
1104e2fb615bSYuanjiang Yu 		return ret;
1105e2fb615bSYuanjiang Yu 	}
1106e2fb615bSYuanjiang Yu 
1107e2fb615bSYuanjiang Yu 	ret = sc27xx_fgu_get_vbat_ocv(data, &ocv);
1108e2fb615bSYuanjiang Yu 	if (ret)
1109e2fb615bSYuanjiang Yu 		goto disable_int;
1110e2fb615bSYuanjiang Yu 
1111e2fb615bSYuanjiang Yu 	/*
1112e2fb615bSYuanjiang Yu 	 * If current OCV is less than the minimum voltage, we should enable the
1113e2fb615bSYuanjiang Yu 	 * coulomb counter threshold interrupt to notify events to adjust the
1114e2fb615bSYuanjiang Yu 	 * battery capacity.
1115e2fb615bSYuanjiang Yu 	 */
1116e2fb615bSYuanjiang Yu 	if (ocv < data->min_volt) {
1117e2fb615bSYuanjiang Yu 		ret = regmap_update_bits(data->regmap,
1118e2fb615bSYuanjiang Yu 					 data->base + SC27XX_FGU_INT_EN,
1119e2fb615bSYuanjiang Yu 					 SC27XX_FGU_CLBCNT_DELTA_INT,
1120e2fb615bSYuanjiang Yu 					 SC27XX_FGU_CLBCNT_DELTA_INT);
1121e2fb615bSYuanjiang Yu 		if (ret) {
1122e2fb615bSYuanjiang Yu 			dev_err(data->dev,
1123e2fb615bSYuanjiang Yu 				"failed to enable coulomb threshold int\n");
1124e2fb615bSYuanjiang Yu 			goto disable_int;
1125e2fb615bSYuanjiang Yu 		}
1126e2fb615bSYuanjiang Yu 	}
1127e2fb615bSYuanjiang Yu 
1128e2fb615bSYuanjiang Yu 	return 0;
1129e2fb615bSYuanjiang Yu 
1130e2fb615bSYuanjiang Yu disable_int:
1131e2fb615bSYuanjiang Yu 	regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN,
1132e2fb615bSYuanjiang Yu 			   SC27XX_FGU_LOW_OVERLOAD_INT, 0);
1133e2fb615bSYuanjiang Yu 	return ret;
1134e2fb615bSYuanjiang Yu }
1135e2fb615bSYuanjiang Yu #endif
1136e2fb615bSYuanjiang Yu 
1137e2fb615bSYuanjiang Yu static const struct dev_pm_ops sc27xx_fgu_pm_ops = {
1138e2fb615bSYuanjiang Yu 	SET_SYSTEM_SLEEP_PM_OPS(sc27xx_fgu_suspend, sc27xx_fgu_resume)
1139e2fb615bSYuanjiang Yu };
1140e2fb615bSYuanjiang Yu 
1141195ca170SBaolin Wang static const struct of_device_id sc27xx_fgu_of_match[] = {
1142195ca170SBaolin Wang 	{ .compatible = "sprd,sc2731-fgu", },
1143195ca170SBaolin Wang 	{ }
1144195ca170SBaolin Wang };
1145195ca170SBaolin Wang 
1146195ca170SBaolin Wang static struct platform_driver sc27xx_fgu_driver = {
1147195ca170SBaolin Wang 	.probe = sc27xx_fgu_probe,
1148195ca170SBaolin Wang 	.driver = {
1149195ca170SBaolin Wang 		.name = "sc27xx-fgu",
1150195ca170SBaolin Wang 		.of_match_table = sc27xx_fgu_of_match,
1151e2fb615bSYuanjiang Yu 		.pm = &sc27xx_fgu_pm_ops,
1152195ca170SBaolin Wang 	}
1153195ca170SBaolin Wang };
1154195ca170SBaolin Wang 
1155195ca170SBaolin Wang module_platform_driver(sc27xx_fgu_driver);
1156195ca170SBaolin Wang 
1157195ca170SBaolin Wang MODULE_DESCRIPTION("Spreadtrum SC27XX PMICs Fual Gauge Unit Driver");
1158195ca170SBaolin Wang MODULE_LICENSE("GPL v2");
1159