xref: /linux/drivers/power/supply/sc27xx_fuel_gauge.c (revision 1c5dfc5e3f2df5892a849621d729daa87cfef436)
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
65058d4256SBaolin Wang /* micro Ohms */
66058d4256SBaolin Wang #define SC27XX_FGU_IDEAL_RESISTANCE	20000
67195ca170SBaolin Wang 
68195ca170SBaolin Wang /*
69195ca170SBaolin Wang  * struct sc27xx_fgu_data: describe the FGU device
70195ca170SBaolin Wang  * @regmap: regmap for register access
71195ca170SBaolin Wang  * @dev: platform device
72195ca170SBaolin Wang  * @battery: battery power supply
73195ca170SBaolin Wang  * @base: the base offset for the controller
74195ca170SBaolin Wang  * @lock: protect the structure
75195ca170SBaolin Wang  * @gpiod: GPIO for battery detection
76195ca170SBaolin Wang  * @channel: IIO channel to get battery temperature
770a4f97a1SBaolin Wang  * @charge_chan: IIO channel to get charge voltage
78195ca170SBaolin Wang  * @internal_resist: the battery internal resistance in mOhm
79195ca170SBaolin Wang  * @total_cap: the total capacity of the battery in mAh
80195ca170SBaolin Wang  * @init_cap: the initial capacity of the battery in mAh
81edcb1c0aSYuanjiang Yu  * @alarm_cap: the alarm capacity
82195ca170SBaolin Wang  * @init_clbcnt: the initial coulomb counter
83195ca170SBaolin Wang  * @max_volt: the maximum constant input voltage in millivolt
84edcb1c0aSYuanjiang Yu  * @min_volt: the minimum drained battery voltage in microvolt
85195ca170SBaolin Wang  * @table_len: the capacity table length
866af82888SYuanjiang Yu  * @resist_table_len: the resistance table length
8765c9fab7SBaolin Wang  * @cur_1000ma_adc: ADC value corresponding to 1000 mA
8865c9fab7SBaolin Wang  * @vol_1000mv_adc: ADC value corresponding to 1000 mV
89058d4256SBaolin Wang  * @calib_resist: the real resistance of coulomb counter chip in uOhm
90195ca170SBaolin Wang  * @cap_table: capacity table with corresponding ocv
916af82888SYuanjiang Yu  * @resist_table: resistance percent table with corresponding temperature
92195ca170SBaolin Wang  */
93195ca170SBaolin Wang struct sc27xx_fgu_data {
94195ca170SBaolin Wang 	struct regmap *regmap;
95195ca170SBaolin Wang 	struct device *dev;
96195ca170SBaolin Wang 	struct power_supply *battery;
97195ca170SBaolin Wang 	u32 base;
98195ca170SBaolin Wang 	struct mutex lock;
99195ca170SBaolin Wang 	struct gpio_desc *gpiod;
100195ca170SBaolin Wang 	struct iio_channel *channel;
1010a4f97a1SBaolin Wang 	struct iio_channel *charge_chan;
102195ca170SBaolin Wang 	bool bat_present;
103195ca170SBaolin Wang 	int internal_resist;
104195ca170SBaolin Wang 	int total_cap;
105195ca170SBaolin Wang 	int init_cap;
106edcb1c0aSYuanjiang Yu 	int alarm_cap;
107195ca170SBaolin Wang 	int init_clbcnt;
108195ca170SBaolin Wang 	int max_volt;
109edcb1c0aSYuanjiang Yu 	int min_volt;
110195ca170SBaolin Wang 	int table_len;
1116af82888SYuanjiang Yu 	int resist_table_len;
11265c9fab7SBaolin Wang 	int cur_1000ma_adc;
11365c9fab7SBaolin Wang 	int vol_1000mv_adc;
114058d4256SBaolin Wang 	int calib_resist;
115195ca170SBaolin Wang 	struct power_supply_battery_ocv_table *cap_table;
1166af82888SYuanjiang Yu 	struct power_supply_resistance_temp_table *resist_table;
117195ca170SBaolin Wang };
118195ca170SBaolin Wang 
119edcb1c0aSYuanjiang Yu static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity);
12058066527SYuanjiang Yu static void sc27xx_fgu_capacity_calibration(struct sc27xx_fgu_data *data,
12158066527SYuanjiang Yu 					    int cap, bool int_mode);
1227cfd33d9SYuanjiang Yu static void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap);
1236af82888SYuanjiang Yu static int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp);
124edcb1c0aSYuanjiang Yu 
125195ca170SBaolin Wang static const char * const sc27xx_charger_supply_name[] = {
126195ca170SBaolin Wang 	"sc2731_charger",
127195ca170SBaolin Wang 	"sc2720_charger",
128195ca170SBaolin Wang 	"sc2721_charger",
129195ca170SBaolin Wang 	"sc2723_charger",
130195ca170SBaolin Wang };
131195ca170SBaolin Wang 
13265c9fab7SBaolin Wang static int sc27xx_fgu_adc_to_current(struct sc27xx_fgu_data *data, int adc)
133195ca170SBaolin Wang {
13465c9fab7SBaolin Wang 	return DIV_ROUND_CLOSEST(adc * 1000, data->cur_1000ma_adc);
135195ca170SBaolin Wang }
136195ca170SBaolin Wang 
13765c9fab7SBaolin Wang static int sc27xx_fgu_adc_to_voltage(struct sc27xx_fgu_data *data, int adc)
138195ca170SBaolin Wang {
13965c9fab7SBaolin Wang 	return DIV_ROUND_CLOSEST(adc * 1000, data->vol_1000mv_adc);
140195ca170SBaolin Wang }
141195ca170SBaolin Wang 
142edcb1c0aSYuanjiang Yu static int sc27xx_fgu_voltage_to_adc(struct sc27xx_fgu_data *data, int vol)
143edcb1c0aSYuanjiang Yu {
144edcb1c0aSYuanjiang Yu 	return DIV_ROUND_CLOSEST(vol * data->vol_1000mv_adc, 1000);
145edcb1c0aSYuanjiang Yu }
146edcb1c0aSYuanjiang Yu 
1474a040e7cSYuanjiang Yu static bool sc27xx_fgu_is_first_poweron(struct sc27xx_fgu_data *data)
1484a040e7cSYuanjiang Yu {
1494a040e7cSYuanjiang Yu 	int ret, status, cap, mode;
1504a040e7cSYuanjiang Yu 
1514a040e7cSYuanjiang Yu 	ret = regmap_read(data->regmap,
1524a040e7cSYuanjiang Yu 			  data->base + SC27XX_FGU_USER_AREA_STATUS, &status);
1534a040e7cSYuanjiang Yu 	if (ret)
1544a040e7cSYuanjiang Yu 		return false;
1554a040e7cSYuanjiang Yu 
1564a040e7cSYuanjiang Yu 	/*
1574a040e7cSYuanjiang Yu 	 * We use low 4 bits to save the last battery capacity and high 12 bits
1584a040e7cSYuanjiang Yu 	 * to save the system boot mode.
1594a040e7cSYuanjiang Yu 	 */
1604a040e7cSYuanjiang Yu 	mode = (status & SC27XX_FGU_MODE_AREA_MASK) >> SC27XX_FGU_MODE_AREA_SHIFT;
1614a040e7cSYuanjiang Yu 	cap = status & SC27XX_FGU_CAP_AREA_MASK;
1624a040e7cSYuanjiang Yu 
1634a040e7cSYuanjiang Yu 	/*
1644a040e7cSYuanjiang Yu 	 * When FGU has been powered down, the user area registers became
1654a040e7cSYuanjiang Yu 	 * default value (0xffff), which can be used to valid if the system is
1664a040e7cSYuanjiang Yu 	 * first power on or not.
1674a040e7cSYuanjiang Yu 	 */
1684a040e7cSYuanjiang Yu 	if (mode == SC27XX_FGU_FIRST_POWERTON || cap == SC27XX_FGU_DEFAULT_CAP)
1694a040e7cSYuanjiang Yu 		return true;
1704a040e7cSYuanjiang Yu 
1714a040e7cSYuanjiang Yu 	return false;
1724a040e7cSYuanjiang Yu }
1734a040e7cSYuanjiang Yu 
1744a040e7cSYuanjiang Yu static int sc27xx_fgu_save_boot_mode(struct sc27xx_fgu_data *data,
1754a040e7cSYuanjiang Yu 				     int boot_mode)
1764a040e7cSYuanjiang Yu {
1774a040e7cSYuanjiang Yu 	int ret;
1784a040e7cSYuanjiang Yu 
1794a040e7cSYuanjiang Yu 	ret = regmap_update_bits(data->regmap,
1804a040e7cSYuanjiang Yu 				 data->base + SC27XX_FGU_USER_AREA_CLEAR,
1814a040e7cSYuanjiang Yu 				 SC27XX_FGU_MODE_AREA_MASK,
1824a040e7cSYuanjiang Yu 				 SC27XX_FGU_MODE_AREA_MASK);
1834a040e7cSYuanjiang Yu 	if (ret)
1844a040e7cSYuanjiang Yu 		return ret;
1854a040e7cSYuanjiang Yu 
186d3e67c94SYuanjiang Yu 	/*
187d3e67c94SYuanjiang Yu 	 * Since the user area registers are put on power always-on region,
188d3e67c94SYuanjiang Yu 	 * then these registers changing time will be a little long. Thus
189d3e67c94SYuanjiang Yu 	 * here we should delay 200us to wait until values are updated
190d3e67c94SYuanjiang Yu 	 * successfully according to the datasheet.
191d3e67c94SYuanjiang Yu 	 */
192d3e67c94SYuanjiang Yu 	udelay(200);
193d3e67c94SYuanjiang Yu 
194d3e67c94SYuanjiang Yu 	ret = regmap_update_bits(data->regmap,
1954a040e7cSYuanjiang Yu 				 data->base + SC27XX_FGU_USER_AREA_SET,
1964a040e7cSYuanjiang Yu 				 SC27XX_FGU_MODE_AREA_MASK,
1974a040e7cSYuanjiang Yu 				 boot_mode << SC27XX_FGU_MODE_AREA_SHIFT);
198d3e67c94SYuanjiang Yu 	if (ret)
199d3e67c94SYuanjiang Yu 		return ret;
200d3e67c94SYuanjiang Yu 
201d3e67c94SYuanjiang Yu 	/*
202d3e67c94SYuanjiang Yu 	 * Since the user area registers are put on power always-on region,
203d3e67c94SYuanjiang Yu 	 * then these registers changing time will be a little long. Thus
204d3e67c94SYuanjiang Yu 	 * here we should delay 200us to wait until values are updated
205d3e67c94SYuanjiang Yu 	 * successfully according to the datasheet.
206d3e67c94SYuanjiang Yu 	 */
207d3e67c94SYuanjiang Yu 	udelay(200);
208d3e67c94SYuanjiang Yu 
209d3e67c94SYuanjiang Yu 	/*
210d3e67c94SYuanjiang Yu 	 * According to the datasheet, we should set the USER_AREA_CLEAR to 0 to
211d3e67c94SYuanjiang Yu 	 * make the user area data available, otherwise we can not save the user
212d3e67c94SYuanjiang Yu 	 * area data.
213d3e67c94SYuanjiang Yu 	 */
214d3e67c94SYuanjiang Yu 	return regmap_update_bits(data->regmap,
215d3e67c94SYuanjiang Yu 				  data->base + SC27XX_FGU_USER_AREA_CLEAR,
216d3e67c94SYuanjiang Yu 				  SC27XX_FGU_MODE_AREA_MASK, 0);
2174a040e7cSYuanjiang Yu }
2184a040e7cSYuanjiang Yu 
2194a040e7cSYuanjiang Yu static int sc27xx_fgu_save_last_cap(struct sc27xx_fgu_data *data, int cap)
2204a040e7cSYuanjiang Yu {
2214a040e7cSYuanjiang Yu 	int ret;
2224a040e7cSYuanjiang Yu 
2234a040e7cSYuanjiang Yu 	ret = regmap_update_bits(data->regmap,
2244a040e7cSYuanjiang Yu 				 data->base + SC27XX_FGU_USER_AREA_CLEAR,
2254a040e7cSYuanjiang Yu 				 SC27XX_FGU_CAP_AREA_MASK,
2264a040e7cSYuanjiang Yu 				 SC27XX_FGU_CAP_AREA_MASK);
2274a040e7cSYuanjiang Yu 	if (ret)
2284a040e7cSYuanjiang Yu 		return ret;
2294a040e7cSYuanjiang Yu 
230d3e67c94SYuanjiang Yu 	/*
231d3e67c94SYuanjiang Yu 	 * Since the user area registers are put on power always-on region,
232d3e67c94SYuanjiang Yu 	 * then these registers changing time will be a little long. Thus
233d3e67c94SYuanjiang Yu 	 * here we should delay 200us to wait until values are updated
234d3e67c94SYuanjiang Yu 	 * successfully according to the datasheet.
235d3e67c94SYuanjiang Yu 	 */
236d3e67c94SYuanjiang Yu 	udelay(200);
237d3e67c94SYuanjiang Yu 
238d3e67c94SYuanjiang Yu 	ret = regmap_update_bits(data->regmap,
2394a040e7cSYuanjiang Yu 				 data->base + SC27XX_FGU_USER_AREA_SET,
2404a040e7cSYuanjiang Yu 				 SC27XX_FGU_CAP_AREA_MASK, cap);
241d3e67c94SYuanjiang Yu 	if (ret)
242d3e67c94SYuanjiang Yu 		return ret;
243d3e67c94SYuanjiang Yu 
244d3e67c94SYuanjiang Yu 	/*
245d3e67c94SYuanjiang Yu 	 * Since the user area registers are put on power always-on region,
246d3e67c94SYuanjiang Yu 	 * then these registers changing time will be a little long. Thus
247d3e67c94SYuanjiang Yu 	 * here we should delay 200us to wait until values are updated
248d3e67c94SYuanjiang Yu 	 * successfully according to the datasheet.
249d3e67c94SYuanjiang Yu 	 */
250d3e67c94SYuanjiang Yu 	udelay(200);
251d3e67c94SYuanjiang Yu 
252d3e67c94SYuanjiang Yu 	/*
253d3e67c94SYuanjiang Yu 	 * According to the datasheet, we should set the USER_AREA_CLEAR to 0 to
254d3e67c94SYuanjiang Yu 	 * make the user area data available, otherwise we can not save the user
255d3e67c94SYuanjiang Yu 	 * area data.
256d3e67c94SYuanjiang Yu 	 */
257d3e67c94SYuanjiang Yu 	return regmap_update_bits(data->regmap,
258d3e67c94SYuanjiang Yu 				  data->base + SC27XX_FGU_USER_AREA_CLEAR,
259d3e67c94SYuanjiang Yu 				  SC27XX_FGU_CAP_AREA_MASK, 0);
2604a040e7cSYuanjiang Yu }
2614a040e7cSYuanjiang Yu 
2624a040e7cSYuanjiang Yu static int sc27xx_fgu_read_last_cap(struct sc27xx_fgu_data *data, int *cap)
2634a040e7cSYuanjiang Yu {
2644a040e7cSYuanjiang Yu 	int ret, value;
2654a040e7cSYuanjiang Yu 
2664a040e7cSYuanjiang Yu 	ret = regmap_read(data->regmap,
2674a040e7cSYuanjiang Yu 			  data->base + SC27XX_FGU_USER_AREA_STATUS, &value);
2684a040e7cSYuanjiang Yu 	if (ret)
2694a040e7cSYuanjiang Yu 		return ret;
2704a040e7cSYuanjiang Yu 
2714a040e7cSYuanjiang Yu 	*cap = value & SC27XX_FGU_CAP_AREA_MASK;
2724a040e7cSYuanjiang Yu 	return 0;
2734a040e7cSYuanjiang Yu }
2744a040e7cSYuanjiang Yu 
275195ca170SBaolin Wang /*
276195ca170SBaolin Wang  * When system boots on, we can not read battery capacity from coulomb
277195ca170SBaolin Wang  * registers, since now the coulomb registers are invalid. So we should
278195ca170SBaolin Wang  * calculate the battery open circuit voltage, and get current battery
279195ca170SBaolin Wang  * capacity according to the capacity table.
280195ca170SBaolin Wang  */
281195ca170SBaolin Wang static int sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data *data, int *cap)
282195ca170SBaolin Wang {
283195ca170SBaolin Wang 	int volt, cur, oci, ocv, ret;
2844a040e7cSYuanjiang Yu 	bool is_first_poweron = sc27xx_fgu_is_first_poweron(data);
2854a040e7cSYuanjiang Yu 
2864a040e7cSYuanjiang Yu 	/*
2874a040e7cSYuanjiang Yu 	 * If system is not the first power on, we should use the last saved
2884a040e7cSYuanjiang Yu 	 * battery capacity as the initial battery capacity. Otherwise we should
2894a040e7cSYuanjiang Yu 	 * re-calculate the initial battery capacity.
2904a040e7cSYuanjiang Yu 	 */
2914a040e7cSYuanjiang Yu 	if (!is_first_poweron) {
2924a040e7cSYuanjiang Yu 		ret = sc27xx_fgu_read_last_cap(data, cap);
2934a040e7cSYuanjiang Yu 		if (ret)
2944a040e7cSYuanjiang Yu 			return ret;
2954a040e7cSYuanjiang Yu 
2964a040e7cSYuanjiang Yu 		return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON);
2974a040e7cSYuanjiang Yu 	}
298195ca170SBaolin Wang 
299195ca170SBaolin Wang 	/*
300195ca170SBaolin Wang 	 * After system booting on, the SC27XX_FGU_CLBCNT_QMAXL register saved
301195ca170SBaolin Wang 	 * the first sampled open circuit current.
302195ca170SBaolin Wang 	 */
303195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_QMAXL,
304195ca170SBaolin Wang 			  &cur);
305195ca170SBaolin Wang 	if (ret)
306195ca170SBaolin Wang 		return ret;
307195ca170SBaolin Wang 
308195ca170SBaolin Wang 	cur <<= 1;
30965c9fab7SBaolin Wang 	oci = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC);
310195ca170SBaolin Wang 
311195ca170SBaolin Wang 	/*
312195ca170SBaolin Wang 	 * Should get the OCV from SC27XX_FGU_POCV register at the system
313195ca170SBaolin Wang 	 * beginning. It is ADC values reading from registers which need to
314195ca170SBaolin Wang 	 * convert the corresponding voltage.
315195ca170SBaolin Wang 	 */
316195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_POCV, &volt);
317195ca170SBaolin Wang 	if (ret)
318195ca170SBaolin Wang 		return ret;
319195ca170SBaolin Wang 
32065c9fab7SBaolin Wang 	volt = sc27xx_fgu_adc_to_voltage(data, volt);
321195ca170SBaolin Wang 	ocv = volt * 1000 - oci * data->internal_resist;
322195ca170SBaolin Wang 
323195ca170SBaolin Wang 	/*
324195ca170SBaolin Wang 	 * Parse the capacity table to look up the correct capacity percent
325195ca170SBaolin Wang 	 * according to current battery's corresponding OCV values.
326195ca170SBaolin Wang 	 */
327195ca170SBaolin Wang 	*cap = power_supply_ocv2cap_simple(data->cap_table, data->table_len,
328195ca170SBaolin Wang 					   ocv);
329195ca170SBaolin Wang 
3304a040e7cSYuanjiang Yu 	ret = sc27xx_fgu_save_last_cap(data, *cap);
3314a040e7cSYuanjiang Yu 	if (ret)
3324a040e7cSYuanjiang Yu 		return ret;
3334a040e7cSYuanjiang Yu 
3344a040e7cSYuanjiang Yu 	return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON);
335195ca170SBaolin Wang }
336195ca170SBaolin Wang 
337195ca170SBaolin Wang static int sc27xx_fgu_set_clbcnt(struct sc27xx_fgu_data *data, int clbcnt)
338195ca170SBaolin Wang {
339195ca170SBaolin Wang 	int ret;
340195ca170SBaolin Wang 
341195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap,
342195ca170SBaolin Wang 				 data->base + SC27XX_FGU_CLBCNT_SETL,
343195ca170SBaolin Wang 				 SC27XX_FGU_CLBCNT_MASK, clbcnt);
344195ca170SBaolin Wang 	if (ret)
345195ca170SBaolin Wang 		return ret;
346195ca170SBaolin Wang 
347195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap,
348195ca170SBaolin Wang 				 data->base + SC27XX_FGU_CLBCNT_SETH,
349195ca170SBaolin Wang 				 SC27XX_FGU_CLBCNT_MASK,
350195ca170SBaolin Wang 				 clbcnt >> SC27XX_FGU_CLBCNT_SHIFT);
351195ca170SBaolin Wang 	if (ret)
352195ca170SBaolin Wang 		return ret;
353195ca170SBaolin Wang 
354195ca170SBaolin Wang 	return regmap_update_bits(data->regmap, data->base + SC27XX_FGU_START,
355195ca170SBaolin Wang 				 SC27XX_WRITE_SELCLB_EN,
356195ca170SBaolin Wang 				 SC27XX_WRITE_SELCLB_EN);
357195ca170SBaolin Wang }
358195ca170SBaolin Wang 
359195ca170SBaolin Wang static int sc27xx_fgu_get_clbcnt(struct sc27xx_fgu_data *data, int *clb_cnt)
360195ca170SBaolin Wang {
361195ca170SBaolin Wang 	int ccl, cch, ret;
362195ca170SBaolin Wang 
363195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALL,
364195ca170SBaolin Wang 			  &ccl);
365195ca170SBaolin Wang 	if (ret)
366195ca170SBaolin Wang 		return ret;
367195ca170SBaolin Wang 
368195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALH,
369195ca170SBaolin Wang 			  &cch);
370195ca170SBaolin Wang 	if (ret)
371195ca170SBaolin Wang 		return ret;
372195ca170SBaolin Wang 
373195ca170SBaolin Wang 	*clb_cnt = ccl & SC27XX_FGU_CLBCNT_MASK;
374195ca170SBaolin Wang 	*clb_cnt |= (cch & SC27XX_FGU_CLBCNT_MASK) << SC27XX_FGU_CLBCNT_SHIFT;
375195ca170SBaolin Wang 
376195ca170SBaolin Wang 	return 0;
377195ca170SBaolin Wang }
378195ca170SBaolin Wang 
379195ca170SBaolin Wang static int sc27xx_fgu_get_capacity(struct sc27xx_fgu_data *data, int *cap)
380195ca170SBaolin Wang {
381195ca170SBaolin Wang 	int ret, cur_clbcnt, delta_clbcnt, delta_cap, temp;
382195ca170SBaolin Wang 
383195ca170SBaolin Wang 	/* Get current coulomb counters firstly */
384195ca170SBaolin Wang 	ret = sc27xx_fgu_get_clbcnt(data, &cur_clbcnt);
385195ca170SBaolin Wang 	if (ret)
386195ca170SBaolin Wang 		return ret;
387195ca170SBaolin Wang 
388195ca170SBaolin Wang 	delta_clbcnt = cur_clbcnt - data->init_clbcnt;
389195ca170SBaolin Wang 
390195ca170SBaolin Wang 	/*
391195ca170SBaolin Wang 	 * Convert coulomb counter to delta capacity (mAh), and set multiplier
3927384b0e7SYuanjiang Yu 	 * as 10 to improve the precision.
393195ca170SBaolin Wang 	 */
3947384b0e7SYuanjiang Yu 	temp = DIV_ROUND_CLOSEST(delta_clbcnt * 10, 36 * SC27XX_FGU_SAMPLE_HZ);
3957384b0e7SYuanjiang Yu 	temp = sc27xx_fgu_adc_to_current(data, temp / 1000);
396195ca170SBaolin Wang 
397195ca170SBaolin Wang 	/*
398195ca170SBaolin Wang 	 * Convert to capacity percent of the battery total capacity,
399195ca170SBaolin Wang 	 * and multiplier is 100 too.
400195ca170SBaolin Wang 	 */
401195ca170SBaolin Wang 	delta_cap = DIV_ROUND_CLOSEST(temp * 100, data->total_cap);
402195ca170SBaolin Wang 	*cap = delta_cap + data->init_cap;
403195ca170SBaolin Wang 
40458066527SYuanjiang Yu 	/* Calibrate the battery capacity in a normal range. */
40558066527SYuanjiang Yu 	sc27xx_fgu_capacity_calibration(data, *cap, false);
40658066527SYuanjiang Yu 
407195ca170SBaolin Wang 	return 0;
408195ca170SBaolin Wang }
409195ca170SBaolin Wang 
410195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_vol(struct sc27xx_fgu_data *data, int *val)
411195ca170SBaolin Wang {
412195ca170SBaolin Wang 	int ret, vol;
413195ca170SBaolin Wang 
414195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_VOLTAGE, &vol);
415195ca170SBaolin Wang 	if (ret)
416195ca170SBaolin Wang 		return ret;
417195ca170SBaolin Wang 
418195ca170SBaolin Wang 	/*
419195ca170SBaolin Wang 	 * It is ADC values reading from registers which need to convert to
420195ca170SBaolin Wang 	 * corresponding voltage values.
421195ca170SBaolin Wang 	 */
42265c9fab7SBaolin Wang 	*val = sc27xx_fgu_adc_to_voltage(data, vol);
423195ca170SBaolin Wang 
424195ca170SBaolin Wang 	return 0;
425195ca170SBaolin Wang }
426195ca170SBaolin Wang 
427195ca170SBaolin Wang static int sc27xx_fgu_get_current(struct sc27xx_fgu_data *data, int *val)
428195ca170SBaolin Wang {
429195ca170SBaolin Wang 	int ret, cur;
430195ca170SBaolin Wang 
431195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CURRENT, &cur);
432195ca170SBaolin Wang 	if (ret)
433195ca170SBaolin Wang 		return ret;
434195ca170SBaolin Wang 
435195ca170SBaolin Wang 	/*
436195ca170SBaolin Wang 	 * It is ADC values reading from registers which need to convert to
437195ca170SBaolin Wang 	 * corresponding current values.
438195ca170SBaolin Wang 	 */
43965c9fab7SBaolin Wang 	*val = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC);
440195ca170SBaolin Wang 
441195ca170SBaolin Wang 	return 0;
442195ca170SBaolin Wang }
443195ca170SBaolin Wang 
444195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val)
445195ca170SBaolin Wang {
4466af82888SYuanjiang Yu 	int vol, cur, ret, temp, resistance;
447195ca170SBaolin Wang 
448195ca170SBaolin Wang 	ret = sc27xx_fgu_get_vbat_vol(data, &vol);
449195ca170SBaolin Wang 	if (ret)
450195ca170SBaolin Wang 		return ret;
451195ca170SBaolin Wang 
452195ca170SBaolin Wang 	ret = sc27xx_fgu_get_current(data, &cur);
453195ca170SBaolin Wang 	if (ret)
454195ca170SBaolin Wang 		return ret;
455195ca170SBaolin Wang 
4566af82888SYuanjiang Yu 	resistance = data->internal_resist;
4576af82888SYuanjiang Yu 	if (data->resist_table_len > 0) {
4586af82888SYuanjiang Yu 		ret = sc27xx_fgu_get_temp(data, &temp);
4596af82888SYuanjiang Yu 		if (ret)
4606af82888SYuanjiang Yu 			return ret;
4616af82888SYuanjiang Yu 
4626af82888SYuanjiang Yu 		resistance = power_supply_temp2resist_simple(data->resist_table,
4636af82888SYuanjiang Yu 						data->resist_table_len, temp);
4646af82888SYuanjiang Yu 		resistance = data->internal_resist * resistance / 100;
4656af82888SYuanjiang Yu 	}
4666af82888SYuanjiang Yu 
467195ca170SBaolin Wang 	/* Return the battery OCV in micro volts. */
4686af82888SYuanjiang Yu 	*val = vol * 1000 - cur * resistance;
469195ca170SBaolin Wang 
470195ca170SBaolin Wang 	return 0;
471195ca170SBaolin Wang }
472195ca170SBaolin Wang 
4730a4f97a1SBaolin Wang static int sc27xx_fgu_get_charge_vol(struct sc27xx_fgu_data *data, int *val)
4740a4f97a1SBaolin Wang {
4750a4f97a1SBaolin Wang 	int ret, vol;
4760a4f97a1SBaolin Wang 
4770a4f97a1SBaolin Wang 	ret = iio_read_channel_processed(data->charge_chan, &vol);
4780a4f97a1SBaolin Wang 	if (ret < 0)
4790a4f97a1SBaolin Wang 		return ret;
4800a4f97a1SBaolin Wang 
4810a4f97a1SBaolin Wang 	*val = vol * 1000;
4820a4f97a1SBaolin Wang 	return 0;
4830a4f97a1SBaolin Wang }
4840a4f97a1SBaolin Wang 
485195ca170SBaolin Wang static int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp)
486195ca170SBaolin Wang {
487195ca170SBaolin Wang 	return iio_read_channel_processed(data->channel, temp);
488195ca170SBaolin Wang }
489195ca170SBaolin Wang 
490195ca170SBaolin Wang static int sc27xx_fgu_get_health(struct sc27xx_fgu_data *data, int *health)
491195ca170SBaolin Wang {
492195ca170SBaolin Wang 	int ret, vol;
493195ca170SBaolin Wang 
494195ca170SBaolin Wang 	ret = sc27xx_fgu_get_vbat_vol(data, &vol);
495195ca170SBaolin Wang 	if (ret)
496195ca170SBaolin Wang 		return ret;
497195ca170SBaolin Wang 
498195ca170SBaolin Wang 	if (vol > data->max_volt)
499195ca170SBaolin Wang 		*health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
500195ca170SBaolin Wang 	else
501195ca170SBaolin Wang 		*health = POWER_SUPPLY_HEALTH_GOOD;
502195ca170SBaolin Wang 
503195ca170SBaolin Wang 	return 0;
504195ca170SBaolin Wang }
505195ca170SBaolin Wang 
506195ca170SBaolin Wang static int sc27xx_fgu_get_status(struct sc27xx_fgu_data *data, int *status)
507195ca170SBaolin Wang {
508195ca170SBaolin Wang 	union power_supply_propval val;
509195ca170SBaolin Wang 	struct power_supply *psy;
510195ca170SBaolin Wang 	int i, ret = -EINVAL;
511195ca170SBaolin Wang 
512195ca170SBaolin Wang 	for (i = 0; i < ARRAY_SIZE(sc27xx_charger_supply_name); i++) {
513195ca170SBaolin Wang 		psy = power_supply_get_by_name(sc27xx_charger_supply_name[i]);
514195ca170SBaolin Wang 		if (!psy)
515195ca170SBaolin Wang 			continue;
516195ca170SBaolin Wang 
517195ca170SBaolin Wang 		ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS,
518195ca170SBaolin Wang 						&val);
519195ca170SBaolin Wang 		power_supply_put(psy);
520195ca170SBaolin Wang 		if (ret)
521195ca170SBaolin Wang 			return ret;
522195ca170SBaolin Wang 
523195ca170SBaolin Wang 		*status = val.intval;
524195ca170SBaolin Wang 	}
525195ca170SBaolin Wang 
526195ca170SBaolin Wang 	return ret;
527195ca170SBaolin Wang }
528195ca170SBaolin Wang 
529195ca170SBaolin Wang static int sc27xx_fgu_get_property(struct power_supply *psy,
530195ca170SBaolin Wang 				   enum power_supply_property psp,
531195ca170SBaolin Wang 				   union power_supply_propval *val)
532195ca170SBaolin Wang {
533195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy);
534195ca170SBaolin Wang 	int ret = 0;
535195ca170SBaolin Wang 	int value;
536195ca170SBaolin Wang 
537195ca170SBaolin Wang 	mutex_lock(&data->lock);
538195ca170SBaolin Wang 
539195ca170SBaolin Wang 	switch (psp) {
540195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_STATUS:
541195ca170SBaolin Wang 		ret = sc27xx_fgu_get_status(data, &value);
542195ca170SBaolin Wang 		if (ret)
543195ca170SBaolin Wang 			goto error;
544195ca170SBaolin Wang 
545195ca170SBaolin Wang 		val->intval = value;
546195ca170SBaolin Wang 		break;
547195ca170SBaolin Wang 
548195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_HEALTH:
549195ca170SBaolin Wang 		ret = sc27xx_fgu_get_health(data, &value);
550195ca170SBaolin Wang 		if (ret)
551195ca170SBaolin Wang 			goto error;
552195ca170SBaolin Wang 
553195ca170SBaolin Wang 		val->intval = value;
554195ca170SBaolin Wang 		break;
555195ca170SBaolin Wang 
556195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_PRESENT:
557195ca170SBaolin Wang 		val->intval = data->bat_present;
558195ca170SBaolin Wang 		break;
559195ca170SBaolin Wang 
560195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_TEMP:
561195ca170SBaolin Wang 		ret = sc27xx_fgu_get_temp(data, &value);
562195ca170SBaolin Wang 		if (ret)
563195ca170SBaolin Wang 			goto error;
564195ca170SBaolin Wang 
565195ca170SBaolin Wang 		val->intval = value;
566195ca170SBaolin Wang 		break;
567195ca170SBaolin Wang 
568195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_TECHNOLOGY:
569195ca170SBaolin Wang 		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
570195ca170SBaolin Wang 		break;
571195ca170SBaolin Wang 
572195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_CAPACITY:
573195ca170SBaolin Wang 		ret = sc27xx_fgu_get_capacity(data, &value);
574195ca170SBaolin Wang 		if (ret)
575195ca170SBaolin Wang 			goto error;
576195ca170SBaolin Wang 
577195ca170SBaolin Wang 		val->intval = value;
578195ca170SBaolin Wang 		break;
579195ca170SBaolin Wang 
580195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
581195ca170SBaolin Wang 		ret = sc27xx_fgu_get_vbat_vol(data, &value);
582195ca170SBaolin Wang 		if (ret)
583195ca170SBaolin Wang 			goto error;
584195ca170SBaolin Wang 
585195ca170SBaolin Wang 		val->intval = value * 1000;
586195ca170SBaolin Wang 		break;
587195ca170SBaolin Wang 
588195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_VOLTAGE_OCV:
589195ca170SBaolin Wang 		ret = sc27xx_fgu_get_vbat_ocv(data, &value);
590195ca170SBaolin Wang 		if (ret)
591195ca170SBaolin Wang 			goto error;
592195ca170SBaolin Wang 
593195ca170SBaolin Wang 		val->intval = value;
594195ca170SBaolin Wang 		break;
595195ca170SBaolin Wang 
5960a4f97a1SBaolin Wang 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
5970a4f97a1SBaolin Wang 		ret = sc27xx_fgu_get_charge_vol(data, &value);
5980a4f97a1SBaolin Wang 		if (ret)
5990a4f97a1SBaolin Wang 			goto error;
6000a4f97a1SBaolin Wang 
6010a4f97a1SBaolin Wang 		val->intval = value;
6020a4f97a1SBaolin Wang 		break;
6030a4f97a1SBaolin Wang 
604195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_CURRENT_NOW:
605195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_CURRENT_AVG:
606195ca170SBaolin Wang 		ret = sc27xx_fgu_get_current(data, &value);
607195ca170SBaolin Wang 		if (ret)
608195ca170SBaolin Wang 			goto error;
609195ca170SBaolin Wang 
610195ca170SBaolin Wang 		val->intval = value * 1000;
611195ca170SBaolin Wang 		break;
612195ca170SBaolin Wang 
6137cff19b9SYuanjiang Yu 	case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
6147cff19b9SYuanjiang Yu 		val->intval = data->total_cap * 1000;
6157cff19b9SYuanjiang Yu 		break;
6167cff19b9SYuanjiang Yu 
617*1c5dfc5eSBaolin Wang 	case POWER_SUPPLY_PROP_CHARGE_NOW:
618*1c5dfc5eSBaolin Wang 		ret = sc27xx_fgu_get_clbcnt(data, &value);
619*1c5dfc5eSBaolin Wang 		if (ret)
620*1c5dfc5eSBaolin Wang 			goto error;
621*1c5dfc5eSBaolin Wang 
622*1c5dfc5eSBaolin Wang 		value = DIV_ROUND_CLOSEST(value * 10,
623*1c5dfc5eSBaolin Wang 					  36 * SC27XX_FGU_SAMPLE_HZ);
624*1c5dfc5eSBaolin Wang 		val->intval = sc27xx_fgu_adc_to_current(data, value);
625*1c5dfc5eSBaolin Wang 
626*1c5dfc5eSBaolin Wang 		break;
627*1c5dfc5eSBaolin Wang 
628195ca170SBaolin Wang 	default:
629195ca170SBaolin Wang 		ret = -EINVAL;
630195ca170SBaolin Wang 		break;
631195ca170SBaolin Wang 	}
632195ca170SBaolin Wang 
633195ca170SBaolin Wang error:
634195ca170SBaolin Wang 	mutex_unlock(&data->lock);
635195ca170SBaolin Wang 	return ret;
636195ca170SBaolin Wang }
637195ca170SBaolin Wang 
6384a040e7cSYuanjiang Yu static int sc27xx_fgu_set_property(struct power_supply *psy,
6394a040e7cSYuanjiang Yu 				   enum power_supply_property psp,
6404a040e7cSYuanjiang Yu 				   const union power_supply_propval *val)
6414a040e7cSYuanjiang Yu {
6424a040e7cSYuanjiang Yu 	struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy);
6434a040e7cSYuanjiang Yu 	int ret;
6444a040e7cSYuanjiang Yu 
6454a040e7cSYuanjiang Yu 	mutex_lock(&data->lock);
6464a040e7cSYuanjiang Yu 
6477cfd33d9SYuanjiang Yu 	switch (psp) {
6487cfd33d9SYuanjiang Yu 	case POWER_SUPPLY_PROP_CAPACITY:
6494a040e7cSYuanjiang Yu 		ret = sc27xx_fgu_save_last_cap(data, val->intval);
6504a040e7cSYuanjiang Yu 		if (ret < 0)
6514a040e7cSYuanjiang Yu 			dev_err(data->dev, "failed to save battery capacity\n");
6527cfd33d9SYuanjiang Yu 		break;
6537cfd33d9SYuanjiang Yu 
6547cfd33d9SYuanjiang Yu 	case POWER_SUPPLY_PROP_CALIBRATE:
6557cfd33d9SYuanjiang Yu 		sc27xx_fgu_adjust_cap(data, val->intval);
6567cfd33d9SYuanjiang Yu 		ret = 0;
6577cfd33d9SYuanjiang Yu 		break;
6587cfd33d9SYuanjiang Yu 
6597cfd33d9SYuanjiang Yu 	default:
6607cfd33d9SYuanjiang Yu 		ret = -EINVAL;
6617cfd33d9SYuanjiang Yu 	}
6627cfd33d9SYuanjiang Yu 
6637cfd33d9SYuanjiang Yu 	mutex_unlock(&data->lock);
6644a040e7cSYuanjiang Yu 
6654a040e7cSYuanjiang Yu 	return ret;
6664a040e7cSYuanjiang Yu }
6674a040e7cSYuanjiang Yu 
668195ca170SBaolin Wang static void sc27xx_fgu_external_power_changed(struct power_supply *psy)
669195ca170SBaolin Wang {
670195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy);
671195ca170SBaolin Wang 
672195ca170SBaolin Wang 	power_supply_changed(data->battery);
673195ca170SBaolin Wang }
674195ca170SBaolin Wang 
6754a040e7cSYuanjiang Yu static int sc27xx_fgu_property_is_writeable(struct power_supply *psy,
6764a040e7cSYuanjiang Yu 					    enum power_supply_property psp)
6774a040e7cSYuanjiang Yu {
6787cfd33d9SYuanjiang Yu 	return psp == POWER_SUPPLY_PROP_CAPACITY ||
6797cfd33d9SYuanjiang Yu 		psp == POWER_SUPPLY_PROP_CALIBRATE;
6804a040e7cSYuanjiang Yu }
6814a040e7cSYuanjiang Yu 
682195ca170SBaolin Wang static enum power_supply_property sc27xx_fgu_props[] = {
683195ca170SBaolin Wang 	POWER_SUPPLY_PROP_STATUS,
684195ca170SBaolin Wang 	POWER_SUPPLY_PROP_HEALTH,
685195ca170SBaolin Wang 	POWER_SUPPLY_PROP_PRESENT,
686195ca170SBaolin Wang 	POWER_SUPPLY_PROP_TEMP,
687195ca170SBaolin Wang 	POWER_SUPPLY_PROP_TECHNOLOGY,
688195ca170SBaolin Wang 	POWER_SUPPLY_PROP_CAPACITY,
689195ca170SBaolin Wang 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
690195ca170SBaolin Wang 	POWER_SUPPLY_PROP_VOLTAGE_OCV,
691195ca170SBaolin Wang 	POWER_SUPPLY_PROP_CURRENT_NOW,
692195ca170SBaolin Wang 	POWER_SUPPLY_PROP_CURRENT_AVG,
6930a4f97a1SBaolin Wang 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
6947cff19b9SYuanjiang Yu 	POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
6957cfd33d9SYuanjiang Yu 	POWER_SUPPLY_PROP_CALIBRATE,
696*1c5dfc5eSBaolin Wang 	POWER_SUPPLY_PROP_CHARGE_NOW
697195ca170SBaolin Wang };
698195ca170SBaolin Wang 
699195ca170SBaolin Wang static const struct power_supply_desc sc27xx_fgu_desc = {
700195ca170SBaolin Wang 	.name			= "sc27xx-fgu",
701195ca170SBaolin Wang 	.type			= POWER_SUPPLY_TYPE_BATTERY,
702195ca170SBaolin Wang 	.properties		= sc27xx_fgu_props,
703195ca170SBaolin Wang 	.num_properties		= ARRAY_SIZE(sc27xx_fgu_props),
704195ca170SBaolin Wang 	.get_property		= sc27xx_fgu_get_property,
7054a040e7cSYuanjiang Yu 	.set_property		= sc27xx_fgu_set_property,
706195ca170SBaolin Wang 	.external_power_changed	= sc27xx_fgu_external_power_changed,
7074a040e7cSYuanjiang Yu 	.property_is_writeable	= sc27xx_fgu_property_is_writeable,
708195ca170SBaolin Wang };
709195ca170SBaolin Wang 
710edcb1c0aSYuanjiang Yu static void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap)
711edcb1c0aSYuanjiang Yu {
71258066527SYuanjiang Yu 	int ret;
71358066527SYuanjiang Yu 
714edcb1c0aSYuanjiang Yu 	data->init_cap = cap;
71558066527SYuanjiang Yu 	ret = sc27xx_fgu_get_clbcnt(data, &data->init_clbcnt);
71658066527SYuanjiang Yu 	if (ret)
71758066527SYuanjiang Yu 		dev_err(data->dev, "failed to get init coulomb counter\n");
71858066527SYuanjiang Yu }
71958066527SYuanjiang Yu 
72058066527SYuanjiang Yu static void sc27xx_fgu_capacity_calibration(struct sc27xx_fgu_data *data,
72158066527SYuanjiang Yu 					    int cap, bool int_mode)
72258066527SYuanjiang Yu {
72358066527SYuanjiang Yu 	int ret, ocv, chg_sts, adc;
72458066527SYuanjiang Yu 
72558066527SYuanjiang Yu 	ret = sc27xx_fgu_get_vbat_ocv(data, &ocv);
72658066527SYuanjiang Yu 	if (ret) {
72758066527SYuanjiang Yu 		dev_err(data->dev, "get battery ocv error.\n");
72858066527SYuanjiang Yu 		return;
72958066527SYuanjiang Yu 	}
73058066527SYuanjiang Yu 
73158066527SYuanjiang Yu 	ret = sc27xx_fgu_get_status(data, &chg_sts);
73258066527SYuanjiang Yu 	if (ret) {
73358066527SYuanjiang Yu 		dev_err(data->dev, "get charger status error.\n");
73458066527SYuanjiang Yu 		return;
73558066527SYuanjiang Yu 	}
73658066527SYuanjiang Yu 
73758066527SYuanjiang Yu 	/*
73858066527SYuanjiang Yu 	 * If we are in charging mode, then we do not need to calibrate the
73958066527SYuanjiang Yu 	 * lower capacity.
74058066527SYuanjiang Yu 	 */
74158066527SYuanjiang Yu 	if (chg_sts == POWER_SUPPLY_STATUS_CHARGING)
74258066527SYuanjiang Yu 		return;
74358066527SYuanjiang Yu 
74458066527SYuanjiang Yu 	if ((ocv > data->cap_table[0].ocv && cap < 100) || cap > 100) {
74558066527SYuanjiang Yu 		/*
74658066527SYuanjiang Yu 		 * If current OCV value is larger than the max OCV value in
74758066527SYuanjiang Yu 		 * OCV table, or the current capacity is larger than 100,
74858066527SYuanjiang Yu 		 * we should force the inititial capacity to 100.
74958066527SYuanjiang Yu 		 */
75058066527SYuanjiang Yu 		sc27xx_fgu_adjust_cap(data, 100);
75158066527SYuanjiang Yu 	} else if (ocv <= data->cap_table[data->table_len - 1].ocv) {
75258066527SYuanjiang Yu 		/*
75358066527SYuanjiang Yu 		 * If current OCV value is leass than the minimum OCV value in
75458066527SYuanjiang Yu 		 * OCV table, we should force the inititial capacity to 0.
75558066527SYuanjiang Yu 		 */
75658066527SYuanjiang Yu 		sc27xx_fgu_adjust_cap(data, 0);
75758066527SYuanjiang Yu 	} else if ((ocv > data->cap_table[data->table_len - 1].ocv && cap <= 0) ||
75858066527SYuanjiang Yu 		   (ocv > data->min_volt && cap <= data->alarm_cap)) {
75958066527SYuanjiang Yu 		/*
76058066527SYuanjiang Yu 		 * If current OCV value is not matchable with current capacity,
76158066527SYuanjiang Yu 		 * we should re-calculate current capacity by looking up the
76258066527SYuanjiang Yu 		 * OCV table.
76358066527SYuanjiang Yu 		 */
76458066527SYuanjiang Yu 		int cur_cap = power_supply_ocv2cap_simple(data->cap_table,
76558066527SYuanjiang Yu 							  data->table_len, ocv);
76658066527SYuanjiang Yu 
76758066527SYuanjiang Yu 		sc27xx_fgu_adjust_cap(data, cur_cap);
76858066527SYuanjiang Yu 	} else if (ocv <= data->min_volt) {
76958066527SYuanjiang Yu 		/*
77058066527SYuanjiang Yu 		 * If current OCV value is less than the low alarm voltage, but
77158066527SYuanjiang Yu 		 * current capacity is larger than the alarm capacity, we should
77258066527SYuanjiang Yu 		 * adjust the inititial capacity to alarm capacity.
77358066527SYuanjiang Yu 		 */
77458066527SYuanjiang Yu 		if (cap > data->alarm_cap) {
77558066527SYuanjiang Yu 			sc27xx_fgu_adjust_cap(data, data->alarm_cap);
77658066527SYuanjiang Yu 		} else {
77758066527SYuanjiang Yu 			int cur_cap;
77858066527SYuanjiang Yu 
77958066527SYuanjiang Yu 			/*
78058066527SYuanjiang Yu 			 * If current capacity is equal with 0 or less than 0
78158066527SYuanjiang Yu 			 * (some error occurs), we should adjust inititial
78258066527SYuanjiang Yu 			 * capacity to the capacity corresponding to current OCV
78358066527SYuanjiang Yu 			 * value.
78458066527SYuanjiang Yu 			 */
78558066527SYuanjiang Yu 			cur_cap = power_supply_ocv2cap_simple(data->cap_table,
78658066527SYuanjiang Yu 							      data->table_len,
78758066527SYuanjiang Yu 							      ocv);
78858066527SYuanjiang Yu 			sc27xx_fgu_adjust_cap(data, cur_cap);
78958066527SYuanjiang Yu 		}
79058066527SYuanjiang Yu 
79158066527SYuanjiang Yu 		if (!int_mode)
79258066527SYuanjiang Yu 			return;
79358066527SYuanjiang Yu 
79458066527SYuanjiang Yu 		/*
79558066527SYuanjiang Yu 		 * After adjusting the battery capacity, we should set the
79658066527SYuanjiang Yu 		 * lowest alarm voltage instead.
79758066527SYuanjiang Yu 		 */
79858066527SYuanjiang Yu 		data->min_volt = data->cap_table[data->table_len - 1].ocv;
79958066527SYuanjiang Yu 		data->alarm_cap = power_supply_ocv2cap_simple(data->cap_table,
80058066527SYuanjiang Yu 							      data->table_len,
80158066527SYuanjiang Yu 							      data->min_volt);
80258066527SYuanjiang Yu 
80358066527SYuanjiang Yu 		adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000);
80458066527SYuanjiang Yu 		regmap_update_bits(data->regmap,
80558066527SYuanjiang Yu 				   data->base + SC27XX_FGU_LOW_OVERLOAD,
80658066527SYuanjiang Yu 				   SC27XX_FGU_LOW_OVERLOAD_MASK, adc);
80758066527SYuanjiang Yu 	}
808edcb1c0aSYuanjiang Yu }
809edcb1c0aSYuanjiang Yu 
810edcb1c0aSYuanjiang Yu static irqreturn_t sc27xx_fgu_interrupt(int irq, void *dev_id)
811edcb1c0aSYuanjiang Yu {
812edcb1c0aSYuanjiang Yu 	struct sc27xx_fgu_data *data = dev_id;
81358066527SYuanjiang Yu 	int ret, cap;
814edcb1c0aSYuanjiang Yu 	u32 status;
815edcb1c0aSYuanjiang Yu 
816edcb1c0aSYuanjiang Yu 	mutex_lock(&data->lock);
817edcb1c0aSYuanjiang Yu 
818edcb1c0aSYuanjiang Yu 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_INT_STS,
819edcb1c0aSYuanjiang Yu 			  &status);
820edcb1c0aSYuanjiang Yu 	if (ret)
821edcb1c0aSYuanjiang Yu 		goto out;
822edcb1c0aSYuanjiang Yu 
823edcb1c0aSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR,
824edcb1c0aSYuanjiang Yu 				 status, status);
825edcb1c0aSYuanjiang Yu 	if (ret)
826edcb1c0aSYuanjiang Yu 		goto out;
827edcb1c0aSYuanjiang Yu 
828edcb1c0aSYuanjiang Yu 	/*
829edcb1c0aSYuanjiang Yu 	 * When low overload voltage interrupt happens, we should calibrate the
830edcb1c0aSYuanjiang Yu 	 * battery capacity in lower voltage stage.
831edcb1c0aSYuanjiang Yu 	 */
832edcb1c0aSYuanjiang Yu 	if (!(status & SC27XX_FGU_LOW_OVERLOAD_INT))
833edcb1c0aSYuanjiang Yu 		goto out;
834edcb1c0aSYuanjiang Yu 
835edcb1c0aSYuanjiang Yu 	ret = sc27xx_fgu_get_capacity(data, &cap);
836edcb1c0aSYuanjiang Yu 	if (ret)
837edcb1c0aSYuanjiang Yu 		goto out;
838edcb1c0aSYuanjiang Yu 
83958066527SYuanjiang Yu 	sc27xx_fgu_capacity_calibration(data, cap, true);
840edcb1c0aSYuanjiang Yu 
841edcb1c0aSYuanjiang Yu out:
842edcb1c0aSYuanjiang Yu 	mutex_unlock(&data->lock);
843edcb1c0aSYuanjiang Yu 
844edcb1c0aSYuanjiang Yu 	power_supply_changed(data->battery);
845edcb1c0aSYuanjiang Yu 	return IRQ_HANDLED;
846edcb1c0aSYuanjiang Yu }
847edcb1c0aSYuanjiang Yu 
848195ca170SBaolin Wang static irqreturn_t sc27xx_fgu_bat_detection(int irq, void *dev_id)
849195ca170SBaolin Wang {
850195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = dev_id;
851195ca170SBaolin Wang 	int state;
852195ca170SBaolin Wang 
853195ca170SBaolin Wang 	mutex_lock(&data->lock);
854195ca170SBaolin Wang 
855195ca170SBaolin Wang 	state = gpiod_get_value_cansleep(data->gpiod);
856195ca170SBaolin Wang 	if (state < 0) {
857195ca170SBaolin Wang 		dev_err(data->dev, "failed to get gpio state\n");
858195ca170SBaolin Wang 		mutex_unlock(&data->lock);
859195ca170SBaolin Wang 		return IRQ_RETVAL(state);
860195ca170SBaolin Wang 	}
861195ca170SBaolin Wang 
862195ca170SBaolin Wang 	data->bat_present = !!state;
863195ca170SBaolin Wang 
864195ca170SBaolin Wang 	mutex_unlock(&data->lock);
865195ca170SBaolin Wang 
866195ca170SBaolin Wang 	power_supply_changed(data->battery);
867195ca170SBaolin Wang 	return IRQ_HANDLED;
868195ca170SBaolin Wang }
869195ca170SBaolin Wang 
870195ca170SBaolin Wang static void sc27xx_fgu_disable(void *_data)
871195ca170SBaolin Wang {
872195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = _data;
873195ca170SBaolin Wang 
874195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0);
875195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0);
876195ca170SBaolin Wang }
877195ca170SBaolin Wang 
878195ca170SBaolin Wang static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity)
879195ca170SBaolin Wang {
880195ca170SBaolin Wang 	/*
881195ca170SBaolin Wang 	 * Get current capacity (mAh) = battery total capacity (mAh) *
882195ca170SBaolin Wang 	 * current capacity percent (capacity / 100).
883195ca170SBaolin Wang 	 */
884195ca170SBaolin Wang 	int cur_cap = DIV_ROUND_CLOSEST(data->total_cap * capacity, 100);
885195ca170SBaolin Wang 
886195ca170SBaolin Wang 	/*
887195ca170SBaolin Wang 	 * Convert current capacity (mAh) to coulomb counter according to the
888195ca170SBaolin Wang 	 * formula: 1 mAh =3.6 coulomb.
889195ca170SBaolin Wang 	 */
8907384b0e7SYuanjiang Yu 	return DIV_ROUND_CLOSEST(cur_cap * 36 * data->cur_1000ma_adc * SC27XX_FGU_SAMPLE_HZ, 10);
891195ca170SBaolin Wang }
892195ca170SBaolin Wang 
89365c9fab7SBaolin Wang static int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data)
89465c9fab7SBaolin Wang {
89565c9fab7SBaolin Wang 	struct nvmem_cell *cell;
89665c9fab7SBaolin Wang 	int calib_data, cal_4200mv;
89765c9fab7SBaolin Wang 	void *buf;
89865c9fab7SBaolin Wang 	size_t len;
89965c9fab7SBaolin Wang 
90065c9fab7SBaolin Wang 	cell = nvmem_cell_get(data->dev, "fgu_calib");
90165c9fab7SBaolin Wang 	if (IS_ERR(cell))
90265c9fab7SBaolin Wang 		return PTR_ERR(cell);
90365c9fab7SBaolin Wang 
90465c9fab7SBaolin Wang 	buf = nvmem_cell_read(cell, &len);
90565c9fab7SBaolin Wang 	nvmem_cell_put(cell);
90665c9fab7SBaolin Wang 
90765c9fab7SBaolin Wang 	if (IS_ERR(buf))
90865c9fab7SBaolin Wang 		return PTR_ERR(buf);
90965c9fab7SBaolin Wang 
91065c9fab7SBaolin Wang 	memcpy(&calib_data, buf, min(len, sizeof(u32)));
91165c9fab7SBaolin Wang 
91265c9fab7SBaolin Wang 	/*
91365c9fab7SBaolin Wang 	 * Get the ADC value corresponding to 4200 mV from eFuse controller
91465c9fab7SBaolin Wang 	 * according to below formula. Then convert to ADC values corresponding
91565c9fab7SBaolin Wang 	 * to 1000 mV and 1000 mA.
91665c9fab7SBaolin Wang 	 */
91765c9fab7SBaolin Wang 	cal_4200mv = (calib_data & 0x1ff) + 6963 - 4096 - 256;
91865c9fab7SBaolin Wang 	data->vol_1000mv_adc = DIV_ROUND_CLOSEST(cal_4200mv * 10, 42);
919058d4256SBaolin Wang 	data->cur_1000ma_adc =
920058d4256SBaolin Wang 		DIV_ROUND_CLOSEST(data->vol_1000mv_adc * 4 * data->calib_resist,
921058d4256SBaolin Wang 				  SC27XX_FGU_IDEAL_RESISTANCE);
92265c9fab7SBaolin Wang 
92365c9fab7SBaolin Wang 	kfree(buf);
92465c9fab7SBaolin Wang 	return 0;
92565c9fab7SBaolin Wang }
92665c9fab7SBaolin Wang 
927195ca170SBaolin Wang static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data)
928195ca170SBaolin Wang {
929195ca170SBaolin Wang 	struct power_supply_battery_info info = { };
930195ca170SBaolin Wang 	struct power_supply_battery_ocv_table *table;
931edcb1c0aSYuanjiang Yu 	int ret, delta_clbcnt, alarm_adc;
932195ca170SBaolin Wang 
933195ca170SBaolin Wang 	ret = power_supply_get_battery_info(data->battery, &info);
934195ca170SBaolin Wang 	if (ret) {
935195ca170SBaolin Wang 		dev_err(data->dev, "failed to get battery information\n");
936195ca170SBaolin Wang 		return ret;
937195ca170SBaolin Wang 	}
938195ca170SBaolin Wang 
939195ca170SBaolin Wang 	data->total_cap = info.charge_full_design_uah / 1000;
940195ca170SBaolin Wang 	data->max_volt = info.constant_charge_voltage_max_uv / 1000;
941195ca170SBaolin Wang 	data->internal_resist = info.factory_internal_resistance_uohm / 1000;
942edcb1c0aSYuanjiang Yu 	data->min_volt = info.voltage_min_design_uv;
943195ca170SBaolin Wang 
944195ca170SBaolin Wang 	/*
945195ca170SBaolin Wang 	 * For SC27XX fuel gauge device, we only use one ocv-capacity
946195ca170SBaolin Wang 	 * table in normal temperature 20 Celsius.
947195ca170SBaolin Wang 	 */
948195ca170SBaolin Wang 	table = power_supply_find_ocv2cap_table(&info, 20, &data->table_len);
949195ca170SBaolin Wang 	if (!table)
950195ca170SBaolin Wang 		return -EINVAL;
951195ca170SBaolin Wang 
952195ca170SBaolin Wang 	data->cap_table = devm_kmemdup(data->dev, table,
953195ca170SBaolin Wang 				       data->table_len * sizeof(*table),
954195ca170SBaolin Wang 				       GFP_KERNEL);
955195ca170SBaolin Wang 	if (!data->cap_table) {
956195ca170SBaolin Wang 		power_supply_put_battery_info(data->battery, &info);
957195ca170SBaolin Wang 		return -ENOMEM;
958195ca170SBaolin Wang 	}
959195ca170SBaolin Wang 
960edcb1c0aSYuanjiang Yu 	data->alarm_cap = power_supply_ocv2cap_simple(data->cap_table,
961edcb1c0aSYuanjiang Yu 						      data->table_len,
962edcb1c0aSYuanjiang Yu 						      data->min_volt);
963ff062d06SYuanjiang Yu 	if (!data->alarm_cap)
964ff062d06SYuanjiang Yu 		data->alarm_cap += 1;
965edcb1c0aSYuanjiang Yu 
9666af82888SYuanjiang Yu 	data->resist_table_len = info.resist_table_size;
9676af82888SYuanjiang Yu 	if (data->resist_table_len > 0) {
9686af82888SYuanjiang Yu 		data->resist_table = devm_kmemdup(data->dev, info.resist_table,
9696af82888SYuanjiang Yu 						  data->resist_table_len *
9706af82888SYuanjiang Yu 						  sizeof(struct power_supply_resistance_temp_table),
9716af82888SYuanjiang Yu 						  GFP_KERNEL);
9726af82888SYuanjiang Yu 		if (!data->resist_table) {
9736af82888SYuanjiang Yu 			power_supply_put_battery_info(data->battery, &info);
9746af82888SYuanjiang Yu 			return -ENOMEM;
9756af82888SYuanjiang Yu 		}
9766af82888SYuanjiang Yu 	}
9776af82888SYuanjiang Yu 
978195ca170SBaolin Wang 	power_supply_put_battery_info(data->battery, &info);
979195ca170SBaolin Wang 
98065c9fab7SBaolin Wang 	ret = sc27xx_fgu_calibration(data);
98165c9fab7SBaolin Wang 	if (ret)
98265c9fab7SBaolin Wang 		return ret;
98365c9fab7SBaolin Wang 
984195ca170SBaolin Wang 	/* Enable the FGU module */
985195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap, SC27XX_MODULE_EN0,
986195ca170SBaolin Wang 				 SC27XX_FGU_EN, SC27XX_FGU_EN);
987195ca170SBaolin Wang 	if (ret) {
988195ca170SBaolin Wang 		dev_err(data->dev, "failed to enable fgu\n");
989195ca170SBaolin Wang 		return ret;
990195ca170SBaolin Wang 	}
991195ca170SBaolin Wang 
992195ca170SBaolin Wang 	/* Enable the FGU RTC clock to make it work */
993195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap, SC27XX_CLK_EN0,
994195ca170SBaolin Wang 				 SC27XX_FGU_RTC_EN, SC27XX_FGU_RTC_EN);
995195ca170SBaolin Wang 	if (ret) {
996195ca170SBaolin Wang 		dev_err(data->dev, "failed to enable fgu RTC clock\n");
997195ca170SBaolin Wang 		goto disable_fgu;
998195ca170SBaolin Wang 	}
999195ca170SBaolin Wang 
1000edcb1c0aSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR,
1001edcb1c0aSYuanjiang Yu 				 SC27XX_FGU_INT_MASK, SC27XX_FGU_INT_MASK);
1002edcb1c0aSYuanjiang Yu 	if (ret) {
1003edcb1c0aSYuanjiang Yu 		dev_err(data->dev, "failed to clear interrupt status\n");
1004edcb1c0aSYuanjiang Yu 		goto disable_clk;
1005edcb1c0aSYuanjiang Yu 	}
1006edcb1c0aSYuanjiang Yu 
1007edcb1c0aSYuanjiang Yu 	/*
1008edcb1c0aSYuanjiang Yu 	 * Set the voltage low overload threshold, which means when the battery
1009edcb1c0aSYuanjiang Yu 	 * voltage is lower than this threshold, the controller will generate
1010edcb1c0aSYuanjiang Yu 	 * one interrupt to notify.
1011edcb1c0aSYuanjiang Yu 	 */
1012edcb1c0aSYuanjiang Yu 	alarm_adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000);
1013edcb1c0aSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_LOW_OVERLOAD,
1014edcb1c0aSYuanjiang Yu 				 SC27XX_FGU_LOW_OVERLOAD_MASK, alarm_adc);
1015edcb1c0aSYuanjiang Yu 	if (ret) {
1016edcb1c0aSYuanjiang Yu 		dev_err(data->dev, "failed to set fgu low overload\n");
1017edcb1c0aSYuanjiang Yu 		goto disable_clk;
1018edcb1c0aSYuanjiang Yu 	}
1019edcb1c0aSYuanjiang Yu 
1020edcb1c0aSYuanjiang Yu 	/*
1021edcb1c0aSYuanjiang Yu 	 * Set the coulomb counter delta threshold, that means when the coulomb
1022edcb1c0aSYuanjiang Yu 	 * counter change is multiples of the delta threshold, the controller
1023edcb1c0aSYuanjiang Yu 	 * will generate one interrupt to notify the users to update the battery
1024edcb1c0aSYuanjiang Yu 	 * capacity. Now we set the delta threshold as a counter value of 1%
1025edcb1c0aSYuanjiang Yu 	 * capacity.
1026edcb1c0aSYuanjiang Yu 	 */
1027edcb1c0aSYuanjiang Yu 	delta_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, 1);
1028edcb1c0aSYuanjiang Yu 
1029edcb1c0aSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTL,
1030edcb1c0aSYuanjiang Yu 				 SC27XX_FGU_CLBCNT_MASK, delta_clbcnt);
1031edcb1c0aSYuanjiang Yu 	if (ret) {
1032edcb1c0aSYuanjiang Yu 		dev_err(data->dev, "failed to set low delta coulomb counter\n");
1033edcb1c0aSYuanjiang Yu 		goto disable_clk;
1034edcb1c0aSYuanjiang Yu 	}
1035edcb1c0aSYuanjiang Yu 
1036edcb1c0aSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTH,
1037edcb1c0aSYuanjiang Yu 				 SC27XX_FGU_CLBCNT_MASK,
1038edcb1c0aSYuanjiang Yu 				 delta_clbcnt >> SC27XX_FGU_CLBCNT_SHIFT);
1039edcb1c0aSYuanjiang Yu 	if (ret) {
1040edcb1c0aSYuanjiang Yu 		dev_err(data->dev, "failed to set high delta coulomb counter\n");
1041edcb1c0aSYuanjiang Yu 		goto disable_clk;
1042edcb1c0aSYuanjiang Yu 	}
1043edcb1c0aSYuanjiang Yu 
1044195ca170SBaolin Wang 	/*
1045195ca170SBaolin Wang 	 * Get the boot battery capacity when system powers on, which is used to
1046195ca170SBaolin Wang 	 * initialize the coulomb counter. After that, we can read the coulomb
1047195ca170SBaolin Wang 	 * counter to measure the battery capacity.
1048195ca170SBaolin Wang 	 */
1049195ca170SBaolin Wang 	ret = sc27xx_fgu_get_boot_capacity(data, &data->init_cap);
1050195ca170SBaolin Wang 	if (ret) {
1051195ca170SBaolin Wang 		dev_err(data->dev, "failed to get boot capacity\n");
1052195ca170SBaolin Wang 		goto disable_clk;
1053195ca170SBaolin Wang 	}
1054195ca170SBaolin Wang 
1055195ca170SBaolin Wang 	/*
1056195ca170SBaolin Wang 	 * Convert battery capacity to the corresponding initial coulomb counter
1057195ca170SBaolin Wang 	 * and set into coulomb counter registers.
1058195ca170SBaolin Wang 	 */
1059195ca170SBaolin Wang 	data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap);
1060195ca170SBaolin Wang 	ret = sc27xx_fgu_set_clbcnt(data, data->init_clbcnt);
1061195ca170SBaolin Wang 	if (ret) {
1062195ca170SBaolin Wang 		dev_err(data->dev, "failed to initialize coulomb counter\n");
1063195ca170SBaolin Wang 		goto disable_clk;
1064195ca170SBaolin Wang 	}
1065195ca170SBaolin Wang 
1066195ca170SBaolin Wang 	return 0;
1067195ca170SBaolin Wang 
1068195ca170SBaolin Wang disable_clk:
1069195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0);
1070195ca170SBaolin Wang disable_fgu:
1071195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0);
1072195ca170SBaolin Wang 
1073195ca170SBaolin Wang 	return ret;
1074195ca170SBaolin Wang }
1075195ca170SBaolin Wang 
1076195ca170SBaolin Wang static int sc27xx_fgu_probe(struct platform_device *pdev)
1077195ca170SBaolin Wang {
107808614b40SFuqian Huang 	struct device *dev = &pdev->dev;
107908614b40SFuqian Huang 	struct device_node *np = dev->of_node;
1080195ca170SBaolin Wang 	struct power_supply_config fgu_cfg = { };
1081195ca170SBaolin Wang 	struct sc27xx_fgu_data *data;
1082195ca170SBaolin Wang 	int ret, irq;
1083195ca170SBaolin Wang 
108408614b40SFuqian Huang 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
1085195ca170SBaolin Wang 	if (!data)
1086195ca170SBaolin Wang 		return -ENOMEM;
1087195ca170SBaolin Wang 
108808614b40SFuqian Huang 	data->regmap = dev_get_regmap(dev->parent, NULL);
1089195ca170SBaolin Wang 	if (!data->regmap) {
109008614b40SFuqian Huang 		dev_err(dev, "failed to get regmap\n");
1091195ca170SBaolin Wang 		return -ENODEV;
1092195ca170SBaolin Wang 	}
1093195ca170SBaolin Wang 
109408614b40SFuqian Huang 	ret = device_property_read_u32(dev, "reg", &data->base);
1095195ca170SBaolin Wang 	if (ret) {
109608614b40SFuqian Huang 		dev_err(dev, "failed to get fgu address\n");
1097195ca170SBaolin Wang 		return ret;
1098195ca170SBaolin Wang 	}
1099195ca170SBaolin Wang 
1100058d4256SBaolin Wang 	ret = device_property_read_u32(&pdev->dev,
1101058d4256SBaolin Wang 				       "sprd,calib-resistance-micro-ohms",
1102058d4256SBaolin Wang 				       &data->calib_resist);
1103058d4256SBaolin Wang 	if (ret) {
1104058d4256SBaolin Wang 		dev_err(&pdev->dev,
1105058d4256SBaolin Wang 			"failed to get fgu calibration resistance\n");
1106058d4256SBaolin Wang 		return ret;
1107058d4256SBaolin Wang 	}
1108058d4256SBaolin Wang 
110908614b40SFuqian Huang 	data->channel = devm_iio_channel_get(dev, "bat-temp");
1110195ca170SBaolin Wang 	if (IS_ERR(data->channel)) {
111108614b40SFuqian Huang 		dev_err(dev, "failed to get IIO channel\n");
1112195ca170SBaolin Wang 		return PTR_ERR(data->channel);
1113195ca170SBaolin Wang 	}
1114195ca170SBaolin Wang 
111508614b40SFuqian Huang 	data->charge_chan = devm_iio_channel_get(dev, "charge-vol");
11160a4f97a1SBaolin Wang 	if (IS_ERR(data->charge_chan)) {
111708614b40SFuqian Huang 		dev_err(dev, "failed to get charge IIO channel\n");
11180a4f97a1SBaolin Wang 		return PTR_ERR(data->charge_chan);
11190a4f97a1SBaolin Wang 	}
11200a4f97a1SBaolin Wang 
112108614b40SFuqian Huang 	data->gpiod = devm_gpiod_get(dev, "bat-detect", GPIOD_IN);
1122195ca170SBaolin Wang 	if (IS_ERR(data->gpiod)) {
112308614b40SFuqian Huang 		dev_err(dev, "failed to get battery detection GPIO\n");
1124195ca170SBaolin Wang 		return PTR_ERR(data->gpiod);
1125195ca170SBaolin Wang 	}
1126195ca170SBaolin Wang 
1127195ca170SBaolin Wang 	ret = gpiod_get_value_cansleep(data->gpiod);
1128195ca170SBaolin Wang 	if (ret < 0) {
112908614b40SFuqian Huang 		dev_err(dev, "failed to get gpio state\n");
1130195ca170SBaolin Wang 		return ret;
1131195ca170SBaolin Wang 	}
1132195ca170SBaolin Wang 
1133195ca170SBaolin Wang 	data->bat_present = !!ret;
1134195ca170SBaolin Wang 	mutex_init(&data->lock);
113508614b40SFuqian Huang 	data->dev = dev;
1136e2fb615bSYuanjiang Yu 	platform_set_drvdata(pdev, data);
1137195ca170SBaolin Wang 
1138195ca170SBaolin Wang 	fgu_cfg.drv_data = data;
1139195ca170SBaolin Wang 	fgu_cfg.of_node = np;
114008614b40SFuqian Huang 	data->battery = devm_power_supply_register(dev, &sc27xx_fgu_desc,
1141195ca170SBaolin Wang 						   &fgu_cfg);
1142195ca170SBaolin Wang 	if (IS_ERR(data->battery)) {
114308614b40SFuqian Huang 		dev_err(dev, "failed to register power supply\n");
1144195ca170SBaolin Wang 		return PTR_ERR(data->battery);
1145195ca170SBaolin Wang 	}
1146195ca170SBaolin Wang 
1147195ca170SBaolin Wang 	ret = sc27xx_fgu_hw_init(data);
1148195ca170SBaolin Wang 	if (ret) {
114908614b40SFuqian Huang 		dev_err(dev, "failed to initialize fgu hardware\n");
1150195ca170SBaolin Wang 		return ret;
1151195ca170SBaolin Wang 	}
1152195ca170SBaolin Wang 
115320420583SFuqian Huang 	ret = devm_add_action_or_reset(dev, sc27xx_fgu_disable, data);
1154195ca170SBaolin Wang 	if (ret) {
115508614b40SFuqian Huang 		dev_err(dev, "failed to add fgu disable action\n");
1156195ca170SBaolin Wang 		return ret;
1157195ca170SBaolin Wang 	}
1158195ca170SBaolin Wang 
1159edcb1c0aSYuanjiang Yu 	irq = platform_get_irq(pdev, 0);
1160edcb1c0aSYuanjiang Yu 	if (irq < 0) {
116108614b40SFuqian Huang 		dev_err(dev, "no irq resource specified\n");
1162edcb1c0aSYuanjiang Yu 		return irq;
1163edcb1c0aSYuanjiang Yu 	}
1164edcb1c0aSYuanjiang Yu 
1165edcb1c0aSYuanjiang Yu 	ret = devm_request_threaded_irq(data->dev, irq, NULL,
1166edcb1c0aSYuanjiang Yu 					sc27xx_fgu_interrupt,
1167edcb1c0aSYuanjiang Yu 					IRQF_NO_SUSPEND | IRQF_ONESHOT,
1168edcb1c0aSYuanjiang Yu 					pdev->name, data);
1169edcb1c0aSYuanjiang Yu 	if (ret) {
1170edcb1c0aSYuanjiang Yu 		dev_err(data->dev, "failed to request fgu IRQ\n");
1171edcb1c0aSYuanjiang Yu 		return ret;
1172edcb1c0aSYuanjiang Yu 	}
1173edcb1c0aSYuanjiang Yu 
1174195ca170SBaolin Wang 	irq = gpiod_to_irq(data->gpiod);
1175195ca170SBaolin Wang 	if (irq < 0) {
117608614b40SFuqian Huang 		dev_err(dev, "failed to translate GPIO to IRQ\n");
1177195ca170SBaolin Wang 		return irq;
1178195ca170SBaolin Wang 	}
1179195ca170SBaolin Wang 
118008614b40SFuqian Huang 	ret = devm_request_threaded_irq(dev, irq, NULL,
1181195ca170SBaolin Wang 					sc27xx_fgu_bat_detection,
1182195ca170SBaolin Wang 					IRQF_ONESHOT | IRQF_TRIGGER_RISING |
1183195ca170SBaolin Wang 					IRQF_TRIGGER_FALLING,
1184195ca170SBaolin Wang 					pdev->name, data);
1185195ca170SBaolin Wang 	if (ret) {
118608614b40SFuqian Huang 		dev_err(dev, "failed to request IRQ\n");
1187195ca170SBaolin Wang 		return ret;
1188195ca170SBaolin Wang 	}
1189195ca170SBaolin Wang 
1190195ca170SBaolin Wang 	return 0;
1191195ca170SBaolin Wang }
1192195ca170SBaolin Wang 
1193e2fb615bSYuanjiang Yu #ifdef CONFIG_PM_SLEEP
1194e2fb615bSYuanjiang Yu static int sc27xx_fgu_resume(struct device *dev)
1195e2fb615bSYuanjiang Yu {
1196e2fb615bSYuanjiang Yu 	struct sc27xx_fgu_data *data = dev_get_drvdata(dev);
1197e2fb615bSYuanjiang Yu 	int ret;
1198e2fb615bSYuanjiang Yu 
1199e2fb615bSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN,
1200e2fb615bSYuanjiang Yu 				 SC27XX_FGU_LOW_OVERLOAD_INT |
1201e2fb615bSYuanjiang Yu 				 SC27XX_FGU_CLBCNT_DELTA_INT, 0);
1202e2fb615bSYuanjiang Yu 	if (ret) {
1203e2fb615bSYuanjiang Yu 		dev_err(data->dev, "failed to disable fgu interrupts\n");
1204e2fb615bSYuanjiang Yu 		return ret;
1205e2fb615bSYuanjiang Yu 	}
1206e2fb615bSYuanjiang Yu 
1207e2fb615bSYuanjiang Yu 	return 0;
1208e2fb615bSYuanjiang Yu }
1209e2fb615bSYuanjiang Yu 
1210e2fb615bSYuanjiang Yu static int sc27xx_fgu_suspend(struct device *dev)
1211e2fb615bSYuanjiang Yu {
1212e2fb615bSYuanjiang Yu 	struct sc27xx_fgu_data *data = dev_get_drvdata(dev);
1213e2fb615bSYuanjiang Yu 	int ret, status, ocv;
1214e2fb615bSYuanjiang Yu 
1215e2fb615bSYuanjiang Yu 	ret = sc27xx_fgu_get_status(data, &status);
1216e2fb615bSYuanjiang Yu 	if (ret)
1217e2fb615bSYuanjiang Yu 		return ret;
1218e2fb615bSYuanjiang Yu 
1219e2fb615bSYuanjiang Yu 	/*
1220e2fb615bSYuanjiang Yu 	 * If we are charging, then no need to enable the FGU interrupts to
1221e2fb615bSYuanjiang Yu 	 * adjust the battery capacity.
1222e2fb615bSYuanjiang Yu 	 */
1223168e68d0SYuanjiang Yu 	if (status != POWER_SUPPLY_STATUS_NOT_CHARGING &&
1224168e68d0SYuanjiang Yu 	    status != POWER_SUPPLY_STATUS_DISCHARGING)
1225e2fb615bSYuanjiang Yu 		return 0;
1226e2fb615bSYuanjiang Yu 
1227e2fb615bSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN,
1228e2fb615bSYuanjiang Yu 				 SC27XX_FGU_LOW_OVERLOAD_INT,
1229e2fb615bSYuanjiang Yu 				 SC27XX_FGU_LOW_OVERLOAD_INT);
1230e2fb615bSYuanjiang Yu 	if (ret) {
1231e2fb615bSYuanjiang Yu 		dev_err(data->dev, "failed to enable low voltage interrupt\n");
1232e2fb615bSYuanjiang Yu 		return ret;
1233e2fb615bSYuanjiang Yu 	}
1234e2fb615bSYuanjiang Yu 
1235e2fb615bSYuanjiang Yu 	ret = sc27xx_fgu_get_vbat_ocv(data, &ocv);
1236e2fb615bSYuanjiang Yu 	if (ret)
1237e2fb615bSYuanjiang Yu 		goto disable_int;
1238e2fb615bSYuanjiang Yu 
1239e2fb615bSYuanjiang Yu 	/*
1240e2fb615bSYuanjiang Yu 	 * If current OCV is less than the minimum voltage, we should enable the
1241e2fb615bSYuanjiang Yu 	 * coulomb counter threshold interrupt to notify events to adjust the
1242e2fb615bSYuanjiang Yu 	 * battery capacity.
1243e2fb615bSYuanjiang Yu 	 */
1244e2fb615bSYuanjiang Yu 	if (ocv < data->min_volt) {
1245e2fb615bSYuanjiang Yu 		ret = regmap_update_bits(data->regmap,
1246e2fb615bSYuanjiang Yu 					 data->base + SC27XX_FGU_INT_EN,
1247e2fb615bSYuanjiang Yu 					 SC27XX_FGU_CLBCNT_DELTA_INT,
1248e2fb615bSYuanjiang Yu 					 SC27XX_FGU_CLBCNT_DELTA_INT);
1249e2fb615bSYuanjiang Yu 		if (ret) {
1250e2fb615bSYuanjiang Yu 			dev_err(data->dev,
1251e2fb615bSYuanjiang Yu 				"failed to enable coulomb threshold int\n");
1252e2fb615bSYuanjiang Yu 			goto disable_int;
1253e2fb615bSYuanjiang Yu 		}
1254e2fb615bSYuanjiang Yu 	}
1255e2fb615bSYuanjiang Yu 
1256e2fb615bSYuanjiang Yu 	return 0;
1257e2fb615bSYuanjiang Yu 
1258e2fb615bSYuanjiang Yu disable_int:
1259e2fb615bSYuanjiang Yu 	regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN,
1260e2fb615bSYuanjiang Yu 			   SC27XX_FGU_LOW_OVERLOAD_INT, 0);
1261e2fb615bSYuanjiang Yu 	return ret;
1262e2fb615bSYuanjiang Yu }
1263e2fb615bSYuanjiang Yu #endif
1264e2fb615bSYuanjiang Yu 
1265e2fb615bSYuanjiang Yu static const struct dev_pm_ops sc27xx_fgu_pm_ops = {
1266e2fb615bSYuanjiang Yu 	SET_SYSTEM_SLEEP_PM_OPS(sc27xx_fgu_suspend, sc27xx_fgu_resume)
1267e2fb615bSYuanjiang Yu };
1268e2fb615bSYuanjiang Yu 
1269195ca170SBaolin Wang static const struct of_device_id sc27xx_fgu_of_match[] = {
1270195ca170SBaolin Wang 	{ .compatible = "sprd,sc2731-fgu", },
1271195ca170SBaolin Wang 	{ }
1272195ca170SBaolin Wang };
1273195ca170SBaolin Wang 
1274195ca170SBaolin Wang static struct platform_driver sc27xx_fgu_driver = {
1275195ca170SBaolin Wang 	.probe = sc27xx_fgu_probe,
1276195ca170SBaolin Wang 	.driver = {
1277195ca170SBaolin Wang 		.name = "sc27xx-fgu",
1278195ca170SBaolin Wang 		.of_match_table = sc27xx_fgu_of_match,
1279e2fb615bSYuanjiang Yu 		.pm = &sc27xx_fgu_pm_ops,
1280195ca170SBaolin Wang 	}
1281195ca170SBaolin Wang };
1282195ca170SBaolin Wang 
1283195ca170SBaolin Wang module_platform_driver(sc27xx_fgu_driver);
1284195ca170SBaolin Wang 
1285195ca170SBaolin Wang MODULE_DESCRIPTION("Spreadtrum SC27XX PMICs Fual Gauge Unit Driver");
1286195ca170SBaolin Wang MODULE_LICENSE("GPL v2");
1287