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>
8ac31585fSChunyan Zhang #include <linux/math64.h>
9195ca170SBaolin Wang #include <linux/module.h>
1065c9fab7SBaolin Wang #include <linux/nvmem-consumer.h>
11195ca170SBaolin Wang #include <linux/of.h>
12195ca170SBaolin Wang #include <linux/platform_device.h>
13195ca170SBaolin Wang #include <linux/power_supply.h>
14195ca170SBaolin Wang #include <linux/regmap.h>
1565c9fab7SBaolin Wang #include <linux/slab.h>
16195ca170SBaolin Wang 
17195ca170SBaolin Wang /* PMIC global control registers definition */
18195ca170SBaolin Wang #define SC27XX_MODULE_EN0		0xc08
19195ca170SBaolin Wang #define SC27XX_CLK_EN0			0xc18
20195ca170SBaolin Wang #define SC27XX_FGU_EN			BIT(7)
21195ca170SBaolin Wang #define SC27XX_FGU_RTC_EN		BIT(6)
22195ca170SBaolin Wang 
23195ca170SBaolin Wang /* FGU registers definition */
24195ca170SBaolin Wang #define SC27XX_FGU_START		0x0
25195ca170SBaolin Wang #define SC27XX_FGU_CONFIG		0x4
26195ca170SBaolin Wang #define SC27XX_FGU_ADC_CONFIG		0x8
27195ca170SBaolin Wang #define SC27XX_FGU_STATUS		0xc
28195ca170SBaolin Wang #define SC27XX_FGU_INT_EN		0x10
29195ca170SBaolin Wang #define SC27XX_FGU_INT_CLR		0x14
30195ca170SBaolin Wang #define SC27XX_FGU_INT_STS		0x1c
31195ca170SBaolin Wang #define SC27XX_FGU_VOLTAGE		0x20
32195ca170SBaolin Wang #define SC27XX_FGU_OCV			0x24
33195ca170SBaolin Wang #define SC27XX_FGU_POCV			0x28
34195ca170SBaolin Wang #define SC27XX_FGU_CURRENT		0x2c
35edcb1c0aSYuanjiang Yu #define SC27XX_FGU_LOW_OVERLOAD		0x34
36195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_SETH		0x50
37195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_SETL		0x54
38edcb1c0aSYuanjiang Yu #define SC27XX_FGU_CLBCNT_DELTH		0x58
39edcb1c0aSYuanjiang Yu #define SC27XX_FGU_CLBCNT_DELTL		0x5c
40195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_VALH		0x68
41195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_VALL		0x6c
42195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_QMAXL		0x74
434a040e7cSYuanjiang Yu #define SC27XX_FGU_USER_AREA_SET	0xa0
444a040e7cSYuanjiang Yu #define SC27XX_FGU_USER_AREA_CLEAR	0xa4
454a040e7cSYuanjiang Yu #define SC27XX_FGU_USER_AREA_STATUS	0xa8
46e5431c34SYuanjiang Yu #define SC27XX_FGU_VOLTAGE_BUF		0xd0
47e5431c34SYuanjiang Yu #define SC27XX_FGU_CURRENT_BUF		0xf0
48195ca170SBaolin Wang 
49195ca170SBaolin Wang #define SC27XX_WRITE_SELCLB_EN		BIT(0)
50195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_MASK		GENMASK(15, 0)
51195ca170SBaolin Wang #define SC27XX_FGU_CLBCNT_SHIFT		16
52edcb1c0aSYuanjiang Yu #define SC27XX_FGU_LOW_OVERLOAD_MASK	GENMASK(12, 0)
53edcb1c0aSYuanjiang Yu 
54edcb1c0aSYuanjiang Yu #define SC27XX_FGU_INT_MASK		GENMASK(9, 0)
55edcb1c0aSYuanjiang Yu #define SC27XX_FGU_LOW_OVERLOAD_INT	BIT(0)
56edcb1c0aSYuanjiang Yu #define SC27XX_FGU_CLBCNT_DELTA_INT	BIT(2)
57195ca170SBaolin Wang 
584a040e7cSYuanjiang Yu #define SC27XX_FGU_MODE_AREA_MASK	GENMASK(15, 12)
594a040e7cSYuanjiang Yu #define SC27XX_FGU_CAP_AREA_MASK	GENMASK(11, 0)
604a040e7cSYuanjiang Yu #define SC27XX_FGU_MODE_AREA_SHIFT	12
614a040e7cSYuanjiang Yu 
624a040e7cSYuanjiang Yu #define SC27XX_FGU_FIRST_POWERTON	GENMASK(3, 0)
634a040e7cSYuanjiang Yu #define SC27XX_FGU_DEFAULT_CAP		GENMASK(11, 0)
644a040e7cSYuanjiang Yu #define SC27XX_FGU_NORMAIL_POWERTON	0x5
654a040e7cSYuanjiang Yu 
66195ca170SBaolin Wang #define SC27XX_FGU_CUR_BASIC_ADC	8192
67195ca170SBaolin Wang #define SC27XX_FGU_SAMPLE_HZ		2
68058d4256SBaolin Wang /* micro Ohms */
69058d4256SBaolin Wang #define SC27XX_FGU_IDEAL_RESISTANCE	20000
70195ca170SBaolin Wang 
71195ca170SBaolin Wang /*
72195ca170SBaolin Wang  * struct sc27xx_fgu_data: describe the FGU device
73195ca170SBaolin Wang  * @regmap: regmap for register access
74195ca170SBaolin Wang  * @dev: platform device
75195ca170SBaolin Wang  * @battery: battery power supply
76195ca170SBaolin Wang  * @base: the base offset for the controller
77195ca170SBaolin Wang  * @lock: protect the structure
78195ca170SBaolin Wang  * @gpiod: GPIO for battery detection
79195ca170SBaolin Wang  * @channel: IIO channel to get battery temperature
800a4f97a1SBaolin Wang  * @charge_chan: IIO channel to get charge voltage
81195ca170SBaolin Wang  * @internal_resist: the battery internal resistance in mOhm
82195ca170SBaolin Wang  * @total_cap: the total capacity of the battery in mAh
83195ca170SBaolin Wang  * @init_cap: the initial capacity of the battery in mAh
84edcb1c0aSYuanjiang Yu  * @alarm_cap: the alarm capacity
85195ca170SBaolin Wang  * @init_clbcnt: the initial coulomb counter
86195ca170SBaolin Wang  * @max_volt: the maximum constant input voltage in millivolt
87edcb1c0aSYuanjiang Yu  * @min_volt: the minimum drained battery voltage in microvolt
887c1c5e38SYuanjiang Yu  * @boot_volt: the voltage measured during boot in microvolt
89195ca170SBaolin Wang  * @table_len: the capacity table length
906af82888SYuanjiang Yu  * @resist_table_len: the resistance table length
9165c9fab7SBaolin Wang  * @cur_1000ma_adc: ADC value corresponding to 1000 mA
9265c9fab7SBaolin Wang  * @vol_1000mv_adc: ADC value corresponding to 1000 mV
93058d4256SBaolin Wang  * @calib_resist: the real resistance of coulomb counter chip in uOhm
94195ca170SBaolin Wang  * @cap_table: capacity table with corresponding ocv
956af82888SYuanjiang Yu  * @resist_table: resistance percent table with corresponding temperature
96195ca170SBaolin Wang  */
97195ca170SBaolin Wang struct sc27xx_fgu_data {
98195ca170SBaolin Wang 	struct regmap *regmap;
99195ca170SBaolin Wang 	struct device *dev;
100195ca170SBaolin Wang 	struct power_supply *battery;
101195ca170SBaolin Wang 	u32 base;
102195ca170SBaolin Wang 	struct mutex lock;
103195ca170SBaolin Wang 	struct gpio_desc *gpiod;
104195ca170SBaolin Wang 	struct iio_channel *channel;
1050a4f97a1SBaolin Wang 	struct iio_channel *charge_chan;
106195ca170SBaolin Wang 	bool bat_present;
107195ca170SBaolin Wang 	int internal_resist;
108195ca170SBaolin Wang 	int total_cap;
109195ca170SBaolin Wang 	int init_cap;
110edcb1c0aSYuanjiang Yu 	int alarm_cap;
111195ca170SBaolin Wang 	int init_clbcnt;
112195ca170SBaolin Wang 	int max_volt;
113edcb1c0aSYuanjiang Yu 	int min_volt;
1147c1c5e38SYuanjiang Yu 	int boot_volt;
115195ca170SBaolin Wang 	int table_len;
1166af82888SYuanjiang Yu 	int resist_table_len;
11765c9fab7SBaolin Wang 	int cur_1000ma_adc;
11865c9fab7SBaolin Wang 	int vol_1000mv_adc;
119058d4256SBaolin Wang 	int calib_resist;
120195ca170SBaolin Wang 	struct power_supply_battery_ocv_table *cap_table;
1216af82888SYuanjiang Yu 	struct power_supply_resistance_temp_table *resist_table;
122195ca170SBaolin Wang };
123195ca170SBaolin Wang 
124edcb1c0aSYuanjiang Yu static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity);
12558066527SYuanjiang Yu static void sc27xx_fgu_capacity_calibration(struct sc27xx_fgu_data *data,
12658066527SYuanjiang Yu 					    int cap, bool int_mode);
1277cfd33d9SYuanjiang Yu static void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap);
1286af82888SYuanjiang Yu static int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp);
129edcb1c0aSYuanjiang Yu 
130195ca170SBaolin Wang static const char * const sc27xx_charger_supply_name[] = {
131195ca170SBaolin Wang 	"sc2731_charger",
132195ca170SBaolin Wang 	"sc2720_charger",
133195ca170SBaolin Wang 	"sc2721_charger",
134195ca170SBaolin Wang 	"sc2723_charger",
135195ca170SBaolin Wang };
136195ca170SBaolin Wang 
sc27xx_fgu_adc_to_current(struct sc27xx_fgu_data * data,s64 adc)137ac31585fSChunyan Zhang static int sc27xx_fgu_adc_to_current(struct sc27xx_fgu_data *data, s64 adc)
138195ca170SBaolin Wang {
139ac31585fSChunyan Zhang 	return DIV_S64_ROUND_CLOSEST(adc * 1000, data->cur_1000ma_adc);
140195ca170SBaolin Wang }
141195ca170SBaolin Wang 
sc27xx_fgu_adc_to_voltage(struct sc27xx_fgu_data * data,s64 adc)142ac31585fSChunyan Zhang static int sc27xx_fgu_adc_to_voltage(struct sc27xx_fgu_data *data, s64 adc)
143195ca170SBaolin Wang {
144ac31585fSChunyan Zhang 	return DIV_S64_ROUND_CLOSEST(adc * 1000, data->vol_1000mv_adc);
145195ca170SBaolin Wang }
146195ca170SBaolin Wang 
sc27xx_fgu_voltage_to_adc(struct sc27xx_fgu_data * data,int vol)147edcb1c0aSYuanjiang Yu static int sc27xx_fgu_voltage_to_adc(struct sc27xx_fgu_data *data, int vol)
148edcb1c0aSYuanjiang Yu {
149edcb1c0aSYuanjiang Yu 	return DIV_ROUND_CLOSEST(vol * data->vol_1000mv_adc, 1000);
150edcb1c0aSYuanjiang Yu }
151edcb1c0aSYuanjiang Yu 
sc27xx_fgu_is_first_poweron(struct sc27xx_fgu_data * data)1524a040e7cSYuanjiang Yu static bool sc27xx_fgu_is_first_poweron(struct sc27xx_fgu_data *data)
1534a040e7cSYuanjiang Yu {
1544a040e7cSYuanjiang Yu 	int ret, status, cap, mode;
1554a040e7cSYuanjiang Yu 
1564a040e7cSYuanjiang Yu 	ret = regmap_read(data->regmap,
1574a040e7cSYuanjiang Yu 			  data->base + SC27XX_FGU_USER_AREA_STATUS, &status);
1584a040e7cSYuanjiang Yu 	if (ret)
1594a040e7cSYuanjiang Yu 		return false;
1604a040e7cSYuanjiang Yu 
1614a040e7cSYuanjiang Yu 	/*
1624a040e7cSYuanjiang Yu 	 * We use low 4 bits to save the last battery capacity and high 12 bits
1634a040e7cSYuanjiang Yu 	 * to save the system boot mode.
1644a040e7cSYuanjiang Yu 	 */
1654a040e7cSYuanjiang Yu 	mode = (status & SC27XX_FGU_MODE_AREA_MASK) >> SC27XX_FGU_MODE_AREA_SHIFT;
1664a040e7cSYuanjiang Yu 	cap = status & SC27XX_FGU_CAP_AREA_MASK;
1674a040e7cSYuanjiang Yu 
1684a040e7cSYuanjiang Yu 	/*
1694a040e7cSYuanjiang Yu 	 * When FGU has been powered down, the user area registers became
1704a040e7cSYuanjiang Yu 	 * default value (0xffff), which can be used to valid if the system is
1714a040e7cSYuanjiang Yu 	 * first power on or not.
1724a040e7cSYuanjiang Yu 	 */
1734a040e7cSYuanjiang Yu 	if (mode == SC27XX_FGU_FIRST_POWERTON || cap == SC27XX_FGU_DEFAULT_CAP)
1744a040e7cSYuanjiang Yu 		return true;
1754a040e7cSYuanjiang Yu 
1764a040e7cSYuanjiang Yu 	return false;
1774a040e7cSYuanjiang Yu }
1784a040e7cSYuanjiang Yu 
sc27xx_fgu_save_boot_mode(struct sc27xx_fgu_data * data,int boot_mode)1794a040e7cSYuanjiang Yu static int sc27xx_fgu_save_boot_mode(struct sc27xx_fgu_data *data,
1804a040e7cSYuanjiang Yu 				     int boot_mode)
1814a040e7cSYuanjiang Yu {
1824a040e7cSYuanjiang Yu 	int ret;
1834a040e7cSYuanjiang Yu 
1844a040e7cSYuanjiang Yu 	ret = regmap_update_bits(data->regmap,
1854a040e7cSYuanjiang Yu 				 data->base + SC27XX_FGU_USER_AREA_CLEAR,
1864a040e7cSYuanjiang Yu 				 SC27XX_FGU_MODE_AREA_MASK,
1874a040e7cSYuanjiang Yu 				 SC27XX_FGU_MODE_AREA_MASK);
1884a040e7cSYuanjiang Yu 	if (ret)
1894a040e7cSYuanjiang Yu 		return ret;
1904a040e7cSYuanjiang Yu 
191d3e67c94SYuanjiang Yu 	/*
192d3e67c94SYuanjiang Yu 	 * Since the user area registers are put on power always-on region,
193d3e67c94SYuanjiang Yu 	 * then these registers changing time will be a little long. Thus
194d3e67c94SYuanjiang Yu 	 * here we should delay 200us to wait until values are updated
195d3e67c94SYuanjiang Yu 	 * successfully according to the datasheet.
196d3e67c94SYuanjiang Yu 	 */
197d3e67c94SYuanjiang Yu 	udelay(200);
198d3e67c94SYuanjiang Yu 
199d3e67c94SYuanjiang Yu 	ret = regmap_update_bits(data->regmap,
2004a040e7cSYuanjiang Yu 				 data->base + SC27XX_FGU_USER_AREA_SET,
2014a040e7cSYuanjiang Yu 				 SC27XX_FGU_MODE_AREA_MASK,
2024a040e7cSYuanjiang Yu 				 boot_mode << SC27XX_FGU_MODE_AREA_SHIFT);
203d3e67c94SYuanjiang Yu 	if (ret)
204d3e67c94SYuanjiang Yu 		return ret;
205d3e67c94SYuanjiang Yu 
206d3e67c94SYuanjiang Yu 	/*
207d3e67c94SYuanjiang Yu 	 * Since the user area registers are put on power always-on region,
208d3e67c94SYuanjiang Yu 	 * then these registers changing time will be a little long. Thus
209d3e67c94SYuanjiang Yu 	 * here we should delay 200us to wait until values are updated
210d3e67c94SYuanjiang Yu 	 * successfully according to the datasheet.
211d3e67c94SYuanjiang Yu 	 */
212d3e67c94SYuanjiang Yu 	udelay(200);
213d3e67c94SYuanjiang Yu 
214d3e67c94SYuanjiang Yu 	/*
215d3e67c94SYuanjiang Yu 	 * According to the datasheet, we should set the USER_AREA_CLEAR to 0 to
216d3e67c94SYuanjiang Yu 	 * make the user area data available, otherwise we can not save the user
217d3e67c94SYuanjiang Yu 	 * area data.
218d3e67c94SYuanjiang Yu 	 */
219d3e67c94SYuanjiang Yu 	return regmap_update_bits(data->regmap,
220d3e67c94SYuanjiang Yu 				  data->base + SC27XX_FGU_USER_AREA_CLEAR,
221d3e67c94SYuanjiang Yu 				  SC27XX_FGU_MODE_AREA_MASK, 0);
2224a040e7cSYuanjiang Yu }
2234a040e7cSYuanjiang Yu 
sc27xx_fgu_save_last_cap(struct sc27xx_fgu_data * data,int cap)2244a040e7cSYuanjiang Yu static int sc27xx_fgu_save_last_cap(struct sc27xx_fgu_data *data, int cap)
2254a040e7cSYuanjiang Yu {
2264a040e7cSYuanjiang Yu 	int ret;
2274a040e7cSYuanjiang Yu 
2284a040e7cSYuanjiang Yu 	ret = regmap_update_bits(data->regmap,
2294a040e7cSYuanjiang Yu 				 data->base + SC27XX_FGU_USER_AREA_CLEAR,
2304a040e7cSYuanjiang Yu 				 SC27XX_FGU_CAP_AREA_MASK,
2314a040e7cSYuanjiang Yu 				 SC27XX_FGU_CAP_AREA_MASK);
2324a040e7cSYuanjiang Yu 	if (ret)
2334a040e7cSYuanjiang Yu 		return ret;
2344a040e7cSYuanjiang Yu 
235d3e67c94SYuanjiang Yu 	/*
236d3e67c94SYuanjiang Yu 	 * Since the user area registers are put on power always-on region,
237d3e67c94SYuanjiang Yu 	 * then these registers changing time will be a little long. Thus
238d3e67c94SYuanjiang Yu 	 * here we should delay 200us to wait until values are updated
239d3e67c94SYuanjiang Yu 	 * successfully according to the datasheet.
240d3e67c94SYuanjiang Yu 	 */
241d3e67c94SYuanjiang Yu 	udelay(200);
242d3e67c94SYuanjiang Yu 
243d3e67c94SYuanjiang Yu 	ret = regmap_update_bits(data->regmap,
2444a040e7cSYuanjiang Yu 				 data->base + SC27XX_FGU_USER_AREA_SET,
2454a040e7cSYuanjiang Yu 				 SC27XX_FGU_CAP_AREA_MASK, cap);
246d3e67c94SYuanjiang Yu 	if (ret)
247d3e67c94SYuanjiang Yu 		return ret;
248d3e67c94SYuanjiang Yu 
249d3e67c94SYuanjiang Yu 	/*
250d3e67c94SYuanjiang Yu 	 * Since the user area registers are put on power always-on region,
251d3e67c94SYuanjiang Yu 	 * then these registers changing time will be a little long. Thus
252d3e67c94SYuanjiang Yu 	 * here we should delay 200us to wait until values are updated
253d3e67c94SYuanjiang Yu 	 * successfully according to the datasheet.
254d3e67c94SYuanjiang Yu 	 */
255d3e67c94SYuanjiang Yu 	udelay(200);
256d3e67c94SYuanjiang Yu 
257d3e67c94SYuanjiang Yu 	/*
258d3e67c94SYuanjiang Yu 	 * According to the datasheet, we should set the USER_AREA_CLEAR to 0 to
259d3e67c94SYuanjiang Yu 	 * make the user area data available, otherwise we can not save the user
260d3e67c94SYuanjiang Yu 	 * area data.
261d3e67c94SYuanjiang Yu 	 */
262d3e67c94SYuanjiang Yu 	return regmap_update_bits(data->regmap,
263d3e67c94SYuanjiang Yu 				  data->base + SC27XX_FGU_USER_AREA_CLEAR,
264d3e67c94SYuanjiang Yu 				  SC27XX_FGU_CAP_AREA_MASK, 0);
2654a040e7cSYuanjiang Yu }
2664a040e7cSYuanjiang Yu 
sc27xx_fgu_read_last_cap(struct sc27xx_fgu_data * data,int * cap)2674a040e7cSYuanjiang Yu static int sc27xx_fgu_read_last_cap(struct sc27xx_fgu_data *data, int *cap)
2684a040e7cSYuanjiang Yu {
2694a040e7cSYuanjiang Yu 	int ret, value;
2704a040e7cSYuanjiang Yu 
2714a040e7cSYuanjiang Yu 	ret = regmap_read(data->regmap,
2724a040e7cSYuanjiang Yu 			  data->base + SC27XX_FGU_USER_AREA_STATUS, &value);
2734a040e7cSYuanjiang Yu 	if (ret)
2744a040e7cSYuanjiang Yu 		return ret;
2754a040e7cSYuanjiang Yu 
2764a040e7cSYuanjiang Yu 	*cap = value & SC27XX_FGU_CAP_AREA_MASK;
2774a040e7cSYuanjiang Yu 	return 0;
2784a040e7cSYuanjiang Yu }
2794a040e7cSYuanjiang Yu 
280195ca170SBaolin Wang /*
281195ca170SBaolin Wang  * When system boots on, we can not read battery capacity from coulomb
282195ca170SBaolin Wang  * registers, since now the coulomb registers are invalid. So we should
283195ca170SBaolin Wang  * calculate the battery open circuit voltage, and get current battery
284195ca170SBaolin Wang  * capacity according to the capacity table.
285195ca170SBaolin Wang  */
sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data * data,int * cap)286195ca170SBaolin Wang static int sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data *data, int *cap)
287195ca170SBaolin Wang {
288195ca170SBaolin Wang 	int volt, cur, oci, ocv, ret;
2894a040e7cSYuanjiang Yu 	bool is_first_poweron = sc27xx_fgu_is_first_poweron(data);
2904a040e7cSYuanjiang Yu 
2914a040e7cSYuanjiang Yu 	/*
2924a040e7cSYuanjiang Yu 	 * If system is not the first power on, we should use the last saved
2934a040e7cSYuanjiang Yu 	 * battery capacity as the initial battery capacity. Otherwise we should
2944a040e7cSYuanjiang Yu 	 * re-calculate the initial battery capacity.
2954a040e7cSYuanjiang Yu 	 */
2964a040e7cSYuanjiang Yu 	if (!is_first_poweron) {
2974a040e7cSYuanjiang Yu 		ret = sc27xx_fgu_read_last_cap(data, cap);
2984a040e7cSYuanjiang Yu 		if (ret)
2994a040e7cSYuanjiang Yu 			return ret;
3004a040e7cSYuanjiang Yu 
3014a040e7cSYuanjiang Yu 		return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON);
3024a040e7cSYuanjiang Yu 	}
303195ca170SBaolin Wang 
304195ca170SBaolin Wang 	/*
305195ca170SBaolin Wang 	 * After system booting on, the SC27XX_FGU_CLBCNT_QMAXL register saved
306195ca170SBaolin Wang 	 * the first sampled open circuit current.
307195ca170SBaolin Wang 	 */
308195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_QMAXL,
309195ca170SBaolin Wang 			  &cur);
310195ca170SBaolin Wang 	if (ret)
311195ca170SBaolin Wang 		return ret;
312195ca170SBaolin Wang 
313195ca170SBaolin Wang 	cur <<= 1;
31465c9fab7SBaolin Wang 	oci = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC);
315195ca170SBaolin Wang 
316195ca170SBaolin Wang 	/*
317195ca170SBaolin Wang 	 * Should get the OCV from SC27XX_FGU_POCV register at the system
318195ca170SBaolin Wang 	 * beginning. It is ADC values reading from registers which need to
319195ca170SBaolin Wang 	 * convert the corresponding voltage.
320195ca170SBaolin Wang 	 */
321195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_POCV, &volt);
322195ca170SBaolin Wang 	if (ret)
323195ca170SBaolin Wang 		return ret;
324195ca170SBaolin Wang 
32565c9fab7SBaolin Wang 	volt = sc27xx_fgu_adc_to_voltage(data, volt);
326195ca170SBaolin Wang 	ocv = volt * 1000 - oci * data->internal_resist;
3277c1c5e38SYuanjiang Yu 	data->boot_volt = ocv;
328195ca170SBaolin Wang 
329195ca170SBaolin Wang 	/*
330195ca170SBaolin Wang 	 * Parse the capacity table to look up the correct capacity percent
331195ca170SBaolin Wang 	 * according to current battery's corresponding OCV values.
332195ca170SBaolin Wang 	 */
333195ca170SBaolin Wang 	*cap = power_supply_ocv2cap_simple(data->cap_table, data->table_len,
334195ca170SBaolin Wang 					   ocv);
335195ca170SBaolin Wang 
3364a040e7cSYuanjiang Yu 	ret = sc27xx_fgu_save_last_cap(data, *cap);
3374a040e7cSYuanjiang Yu 	if (ret)
3384a040e7cSYuanjiang Yu 		return ret;
3394a040e7cSYuanjiang Yu 
3404a040e7cSYuanjiang Yu 	return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON);
341195ca170SBaolin Wang }
342195ca170SBaolin Wang 
sc27xx_fgu_set_clbcnt(struct sc27xx_fgu_data * data,int clbcnt)343195ca170SBaolin Wang static int sc27xx_fgu_set_clbcnt(struct sc27xx_fgu_data *data, int clbcnt)
344195ca170SBaolin Wang {
345195ca170SBaolin Wang 	int ret;
346195ca170SBaolin Wang 
347195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap,
348195ca170SBaolin Wang 				 data->base + SC27XX_FGU_CLBCNT_SETL,
349195ca170SBaolin Wang 				 SC27XX_FGU_CLBCNT_MASK, clbcnt);
350195ca170SBaolin Wang 	if (ret)
351195ca170SBaolin Wang 		return ret;
352195ca170SBaolin Wang 
353195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap,
354195ca170SBaolin Wang 				 data->base + SC27XX_FGU_CLBCNT_SETH,
355195ca170SBaolin Wang 				 SC27XX_FGU_CLBCNT_MASK,
356195ca170SBaolin Wang 				 clbcnt >> SC27XX_FGU_CLBCNT_SHIFT);
357195ca170SBaolin Wang 	if (ret)
358195ca170SBaolin Wang 		return ret;
359195ca170SBaolin Wang 
360195ca170SBaolin Wang 	return regmap_update_bits(data->regmap, data->base + SC27XX_FGU_START,
361195ca170SBaolin Wang 				 SC27XX_WRITE_SELCLB_EN,
362195ca170SBaolin Wang 				 SC27XX_WRITE_SELCLB_EN);
363195ca170SBaolin Wang }
364195ca170SBaolin Wang 
sc27xx_fgu_get_clbcnt(struct sc27xx_fgu_data * data,int * clb_cnt)365195ca170SBaolin Wang static int sc27xx_fgu_get_clbcnt(struct sc27xx_fgu_data *data, int *clb_cnt)
366195ca170SBaolin Wang {
367195ca170SBaolin Wang 	int ccl, cch, ret;
368195ca170SBaolin Wang 
369195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALL,
370195ca170SBaolin Wang 			  &ccl);
371195ca170SBaolin Wang 	if (ret)
372195ca170SBaolin Wang 		return ret;
373195ca170SBaolin Wang 
374195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALH,
375195ca170SBaolin Wang 			  &cch);
376195ca170SBaolin Wang 	if (ret)
377195ca170SBaolin Wang 		return ret;
378195ca170SBaolin Wang 
379195ca170SBaolin Wang 	*clb_cnt = ccl & SC27XX_FGU_CLBCNT_MASK;
380195ca170SBaolin Wang 	*clb_cnt |= (cch & SC27XX_FGU_CLBCNT_MASK) << SC27XX_FGU_CLBCNT_SHIFT;
381195ca170SBaolin Wang 
382195ca170SBaolin Wang 	return 0;
383195ca170SBaolin Wang }
384195ca170SBaolin Wang 
sc27xx_fgu_get_vol_now(struct sc27xx_fgu_data * data,int * val)385e5431c34SYuanjiang Yu static int sc27xx_fgu_get_vol_now(struct sc27xx_fgu_data *data, int *val)
386e5431c34SYuanjiang Yu {
387e5431c34SYuanjiang Yu 	int ret;
388e5431c34SYuanjiang Yu 	u32 vol;
389e5431c34SYuanjiang Yu 
390e5431c34SYuanjiang Yu 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_VOLTAGE_BUF,
391e5431c34SYuanjiang Yu 			  &vol);
392e5431c34SYuanjiang Yu 	if (ret)
393e5431c34SYuanjiang Yu 		return ret;
394e5431c34SYuanjiang Yu 
395e5431c34SYuanjiang Yu 	/*
396e5431c34SYuanjiang Yu 	 * It is ADC values reading from registers which need to convert to
397e5431c34SYuanjiang Yu 	 * corresponding voltage values.
398e5431c34SYuanjiang Yu 	 */
399e5431c34SYuanjiang Yu 	*val = sc27xx_fgu_adc_to_voltage(data, vol);
400e5431c34SYuanjiang Yu 
401e5431c34SYuanjiang Yu 	return 0;
402e5431c34SYuanjiang Yu }
403e5431c34SYuanjiang Yu 
sc27xx_fgu_get_cur_now(struct sc27xx_fgu_data * data,int * val)404e5431c34SYuanjiang Yu static int sc27xx_fgu_get_cur_now(struct sc27xx_fgu_data *data, int *val)
405e5431c34SYuanjiang Yu {
406e5431c34SYuanjiang Yu 	int ret;
407e5431c34SYuanjiang Yu 	u32 cur;
408e5431c34SYuanjiang Yu 
409e5431c34SYuanjiang Yu 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CURRENT_BUF,
410e5431c34SYuanjiang Yu 			  &cur);
411e5431c34SYuanjiang Yu 	if (ret)
412e5431c34SYuanjiang Yu 		return ret;
413e5431c34SYuanjiang Yu 
414e5431c34SYuanjiang Yu 	/*
415e5431c34SYuanjiang Yu 	 * It is ADC values reading from registers which need to convert to
416e5431c34SYuanjiang Yu 	 * corresponding current values.
417e5431c34SYuanjiang Yu 	 */
418e5431c34SYuanjiang Yu 	*val = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC);
419e5431c34SYuanjiang Yu 
420e5431c34SYuanjiang Yu 	return 0;
421e5431c34SYuanjiang Yu }
422e5431c34SYuanjiang Yu 
sc27xx_fgu_get_capacity(struct sc27xx_fgu_data * data,int * cap)423195ca170SBaolin Wang static int sc27xx_fgu_get_capacity(struct sc27xx_fgu_data *data, int *cap)
424195ca170SBaolin Wang {
425195ca170SBaolin Wang 	int ret, cur_clbcnt, delta_clbcnt, delta_cap, temp;
426195ca170SBaolin Wang 
427195ca170SBaolin Wang 	/* Get current coulomb counters firstly */
428195ca170SBaolin Wang 	ret = sc27xx_fgu_get_clbcnt(data, &cur_clbcnt);
429195ca170SBaolin Wang 	if (ret)
430195ca170SBaolin Wang 		return ret;
431195ca170SBaolin Wang 
432195ca170SBaolin Wang 	delta_clbcnt = cur_clbcnt - data->init_clbcnt;
433195ca170SBaolin Wang 
434195ca170SBaolin Wang 	/*
435195ca170SBaolin Wang 	 * Convert coulomb counter to delta capacity (mAh), and set multiplier
4367384b0e7SYuanjiang Yu 	 * as 10 to improve the precision.
437195ca170SBaolin Wang 	 */
4387384b0e7SYuanjiang Yu 	temp = DIV_ROUND_CLOSEST(delta_clbcnt * 10, 36 * SC27XX_FGU_SAMPLE_HZ);
4397384b0e7SYuanjiang Yu 	temp = sc27xx_fgu_adc_to_current(data, temp / 1000);
440195ca170SBaolin Wang 
441195ca170SBaolin Wang 	/*
442195ca170SBaolin Wang 	 * Convert to capacity percent of the battery total capacity,
443195ca170SBaolin Wang 	 * and multiplier is 100 too.
444195ca170SBaolin Wang 	 */
445195ca170SBaolin Wang 	delta_cap = DIV_ROUND_CLOSEST(temp * 100, data->total_cap);
446195ca170SBaolin Wang 	*cap = delta_cap + data->init_cap;
447195ca170SBaolin Wang 
44858066527SYuanjiang Yu 	/* Calibrate the battery capacity in a normal range. */
44958066527SYuanjiang Yu 	sc27xx_fgu_capacity_calibration(data, *cap, false);
45058066527SYuanjiang Yu 
451195ca170SBaolin Wang 	return 0;
452195ca170SBaolin Wang }
453195ca170SBaolin Wang 
sc27xx_fgu_get_vbat_vol(struct sc27xx_fgu_data * data,int * val)454195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_vol(struct sc27xx_fgu_data *data, int *val)
455195ca170SBaolin Wang {
456195ca170SBaolin Wang 	int ret, vol;
457195ca170SBaolin Wang 
458195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_VOLTAGE, &vol);
459195ca170SBaolin Wang 	if (ret)
460195ca170SBaolin Wang 		return ret;
461195ca170SBaolin Wang 
462195ca170SBaolin Wang 	/*
463195ca170SBaolin Wang 	 * It is ADC values reading from registers which need to convert to
464195ca170SBaolin Wang 	 * corresponding voltage values.
465195ca170SBaolin Wang 	 */
46665c9fab7SBaolin Wang 	*val = sc27xx_fgu_adc_to_voltage(data, vol);
467195ca170SBaolin Wang 
468195ca170SBaolin Wang 	return 0;
469195ca170SBaolin Wang }
470195ca170SBaolin Wang 
sc27xx_fgu_get_current(struct sc27xx_fgu_data * data,int * val)471195ca170SBaolin Wang static int sc27xx_fgu_get_current(struct sc27xx_fgu_data *data, int *val)
472195ca170SBaolin Wang {
473195ca170SBaolin Wang 	int ret, cur;
474195ca170SBaolin Wang 
475195ca170SBaolin Wang 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CURRENT, &cur);
476195ca170SBaolin Wang 	if (ret)
477195ca170SBaolin Wang 		return ret;
478195ca170SBaolin Wang 
479195ca170SBaolin Wang 	/*
480195ca170SBaolin Wang 	 * It is ADC values reading from registers which need to convert to
481195ca170SBaolin Wang 	 * corresponding current values.
482195ca170SBaolin Wang 	 */
48365c9fab7SBaolin Wang 	*val = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC);
484195ca170SBaolin Wang 
485195ca170SBaolin Wang 	return 0;
486195ca170SBaolin Wang }
487195ca170SBaolin Wang 
sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data * data,int * val)488195ca170SBaolin Wang static int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val)
489195ca170SBaolin Wang {
4906af82888SYuanjiang Yu 	int vol, cur, ret, temp, resistance;
491195ca170SBaolin Wang 
492195ca170SBaolin Wang 	ret = sc27xx_fgu_get_vbat_vol(data, &vol);
493195ca170SBaolin Wang 	if (ret)
494195ca170SBaolin Wang 		return ret;
495195ca170SBaolin Wang 
496195ca170SBaolin Wang 	ret = sc27xx_fgu_get_current(data, &cur);
497195ca170SBaolin Wang 	if (ret)
498195ca170SBaolin Wang 		return ret;
499195ca170SBaolin Wang 
5006af82888SYuanjiang Yu 	resistance = data->internal_resist;
5016af82888SYuanjiang Yu 	if (data->resist_table_len > 0) {
5026af82888SYuanjiang Yu 		ret = sc27xx_fgu_get_temp(data, &temp);
5036af82888SYuanjiang Yu 		if (ret)
5046af82888SYuanjiang Yu 			return ret;
5056af82888SYuanjiang Yu 
5066af82888SYuanjiang Yu 		resistance = power_supply_temp2resist_simple(data->resist_table,
5076af82888SYuanjiang Yu 						data->resist_table_len, temp);
5086af82888SYuanjiang Yu 		resistance = data->internal_resist * resistance / 100;
5096af82888SYuanjiang Yu 	}
5106af82888SYuanjiang Yu 
511195ca170SBaolin Wang 	/* Return the battery OCV in micro volts. */
5126af82888SYuanjiang Yu 	*val = vol * 1000 - cur * resistance;
513195ca170SBaolin Wang 
514195ca170SBaolin Wang 	return 0;
515195ca170SBaolin Wang }
516195ca170SBaolin Wang 
sc27xx_fgu_get_charge_vol(struct sc27xx_fgu_data * data,int * val)5170a4f97a1SBaolin Wang static int sc27xx_fgu_get_charge_vol(struct sc27xx_fgu_data *data, int *val)
5180a4f97a1SBaolin Wang {
5190a4f97a1SBaolin Wang 	int ret, vol;
5200a4f97a1SBaolin Wang 
5210a4f97a1SBaolin Wang 	ret = iio_read_channel_processed(data->charge_chan, &vol);
5220a4f97a1SBaolin Wang 	if (ret < 0)
5230a4f97a1SBaolin Wang 		return ret;
5240a4f97a1SBaolin Wang 
5250a4f97a1SBaolin Wang 	*val = vol * 1000;
5260a4f97a1SBaolin Wang 	return 0;
5270a4f97a1SBaolin Wang }
5280a4f97a1SBaolin Wang 
sc27xx_fgu_get_temp(struct sc27xx_fgu_data * data,int * temp)529195ca170SBaolin Wang static int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp)
530195ca170SBaolin Wang {
531195ca170SBaolin Wang 	return iio_read_channel_processed(data->channel, temp);
532195ca170SBaolin Wang }
533195ca170SBaolin Wang 
sc27xx_fgu_get_health(struct sc27xx_fgu_data * data,int * health)534195ca170SBaolin Wang static int sc27xx_fgu_get_health(struct sc27xx_fgu_data *data, int *health)
535195ca170SBaolin Wang {
536195ca170SBaolin Wang 	int ret, vol;
537195ca170SBaolin Wang 
538195ca170SBaolin Wang 	ret = sc27xx_fgu_get_vbat_vol(data, &vol);
539195ca170SBaolin Wang 	if (ret)
540195ca170SBaolin Wang 		return ret;
541195ca170SBaolin Wang 
542195ca170SBaolin Wang 	if (vol > data->max_volt)
543195ca170SBaolin Wang 		*health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
544195ca170SBaolin Wang 	else
545195ca170SBaolin Wang 		*health = POWER_SUPPLY_HEALTH_GOOD;
546195ca170SBaolin Wang 
547195ca170SBaolin Wang 	return 0;
548195ca170SBaolin Wang }
549195ca170SBaolin Wang 
sc27xx_fgu_get_status(struct sc27xx_fgu_data * data,int * status)550195ca170SBaolin Wang static int sc27xx_fgu_get_status(struct sc27xx_fgu_data *data, int *status)
551195ca170SBaolin Wang {
552195ca170SBaolin Wang 	union power_supply_propval val;
553195ca170SBaolin Wang 	struct power_supply *psy;
554195ca170SBaolin Wang 	int i, ret = -EINVAL;
555195ca170SBaolin Wang 
556195ca170SBaolin Wang 	for (i = 0; i < ARRAY_SIZE(sc27xx_charger_supply_name); i++) {
557195ca170SBaolin Wang 		psy = power_supply_get_by_name(sc27xx_charger_supply_name[i]);
558195ca170SBaolin Wang 		if (!psy)
559195ca170SBaolin Wang 			continue;
560195ca170SBaolin Wang 
561195ca170SBaolin Wang 		ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS,
562195ca170SBaolin Wang 						&val);
563195ca170SBaolin Wang 		power_supply_put(psy);
564195ca170SBaolin Wang 		if (ret)
565195ca170SBaolin Wang 			return ret;
566195ca170SBaolin Wang 
567195ca170SBaolin Wang 		*status = val.intval;
568195ca170SBaolin Wang 	}
569195ca170SBaolin Wang 
570195ca170SBaolin Wang 	return ret;
571195ca170SBaolin Wang }
572195ca170SBaolin Wang 
sc27xx_fgu_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)573195ca170SBaolin Wang static int sc27xx_fgu_get_property(struct power_supply *psy,
574195ca170SBaolin Wang 				   enum power_supply_property psp,
575195ca170SBaolin Wang 				   union power_supply_propval *val)
576195ca170SBaolin Wang {
577195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy);
578195ca170SBaolin Wang 	int ret = 0;
579195ca170SBaolin Wang 	int value;
580195ca170SBaolin Wang 
581195ca170SBaolin Wang 	mutex_lock(&data->lock);
582195ca170SBaolin Wang 
583195ca170SBaolin Wang 	switch (psp) {
584195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_STATUS:
585195ca170SBaolin Wang 		ret = sc27xx_fgu_get_status(data, &value);
586195ca170SBaolin Wang 		if (ret)
587195ca170SBaolin Wang 			goto error;
588195ca170SBaolin Wang 
589195ca170SBaolin Wang 		val->intval = value;
590195ca170SBaolin Wang 		break;
591195ca170SBaolin Wang 
592195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_HEALTH:
593195ca170SBaolin Wang 		ret = sc27xx_fgu_get_health(data, &value);
594195ca170SBaolin Wang 		if (ret)
595195ca170SBaolin Wang 			goto error;
596195ca170SBaolin Wang 
597195ca170SBaolin Wang 		val->intval = value;
598195ca170SBaolin Wang 		break;
599195ca170SBaolin Wang 
600195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_PRESENT:
601195ca170SBaolin Wang 		val->intval = data->bat_present;
602195ca170SBaolin Wang 		break;
603195ca170SBaolin Wang 
604195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_TEMP:
605195ca170SBaolin Wang 		ret = sc27xx_fgu_get_temp(data, &value);
606195ca170SBaolin Wang 		if (ret)
607195ca170SBaolin Wang 			goto error;
608195ca170SBaolin Wang 
609195ca170SBaolin Wang 		val->intval = value;
610195ca170SBaolin Wang 		break;
611195ca170SBaolin Wang 
612195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_TECHNOLOGY:
613195ca170SBaolin Wang 		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
614195ca170SBaolin Wang 		break;
615195ca170SBaolin Wang 
616195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_CAPACITY:
617195ca170SBaolin Wang 		ret = sc27xx_fgu_get_capacity(data, &value);
618195ca170SBaolin Wang 		if (ret)
619195ca170SBaolin Wang 			goto error;
620195ca170SBaolin Wang 
621195ca170SBaolin Wang 		val->intval = value;
622195ca170SBaolin Wang 		break;
623195ca170SBaolin Wang 
624e5431c34SYuanjiang Yu 	case POWER_SUPPLY_PROP_VOLTAGE_AVG:
625195ca170SBaolin Wang 		ret = sc27xx_fgu_get_vbat_vol(data, &value);
626195ca170SBaolin Wang 		if (ret)
627195ca170SBaolin Wang 			goto error;
628195ca170SBaolin Wang 
629195ca170SBaolin Wang 		val->intval = value * 1000;
630195ca170SBaolin Wang 		break;
631195ca170SBaolin Wang 
632195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_VOLTAGE_OCV:
633195ca170SBaolin Wang 		ret = sc27xx_fgu_get_vbat_ocv(data, &value);
634195ca170SBaolin Wang 		if (ret)
635195ca170SBaolin Wang 			goto error;
636195ca170SBaolin Wang 
637195ca170SBaolin Wang 		val->intval = value;
638195ca170SBaolin Wang 		break;
639195ca170SBaolin Wang 
6400a4f97a1SBaolin Wang 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
6410a4f97a1SBaolin Wang 		ret = sc27xx_fgu_get_charge_vol(data, &value);
6420a4f97a1SBaolin Wang 		if (ret)
6430a4f97a1SBaolin Wang 			goto error;
6440a4f97a1SBaolin Wang 
6450a4f97a1SBaolin Wang 		val->intval = value;
6460a4f97a1SBaolin Wang 		break;
6470a4f97a1SBaolin Wang 
648195ca170SBaolin Wang 	case POWER_SUPPLY_PROP_CURRENT_AVG:
649195ca170SBaolin Wang 		ret = sc27xx_fgu_get_current(data, &value);
650195ca170SBaolin Wang 		if (ret)
651195ca170SBaolin Wang 			goto error;
652195ca170SBaolin Wang 
653195ca170SBaolin Wang 		val->intval = value * 1000;
654195ca170SBaolin Wang 		break;
655195ca170SBaolin Wang 
6567cff19b9SYuanjiang Yu 	case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
6577cff19b9SYuanjiang Yu 		val->intval = data->total_cap * 1000;
6587cff19b9SYuanjiang Yu 		break;
6597cff19b9SYuanjiang Yu 
6601c5dfc5eSBaolin Wang 	case POWER_SUPPLY_PROP_CHARGE_NOW:
6611c5dfc5eSBaolin Wang 		ret = sc27xx_fgu_get_clbcnt(data, &value);
6621c5dfc5eSBaolin Wang 		if (ret)
6631c5dfc5eSBaolin Wang 			goto error;
6641c5dfc5eSBaolin Wang 
6651c5dfc5eSBaolin Wang 		value = DIV_ROUND_CLOSEST(value * 10,
6661c5dfc5eSBaolin Wang 					  36 * SC27XX_FGU_SAMPLE_HZ);
6671c5dfc5eSBaolin Wang 		val->intval = sc27xx_fgu_adc_to_current(data, value);
6681c5dfc5eSBaolin Wang 
6691c5dfc5eSBaolin Wang 		break;
6701c5dfc5eSBaolin Wang 
671e5431c34SYuanjiang Yu 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
672e5431c34SYuanjiang Yu 		ret = sc27xx_fgu_get_vol_now(data, &value);
673e5431c34SYuanjiang Yu 		if (ret)
674e5431c34SYuanjiang Yu 			goto error;
675e5431c34SYuanjiang Yu 
676e5431c34SYuanjiang Yu 		val->intval = value * 1000;
677e5431c34SYuanjiang Yu 		break;
678e5431c34SYuanjiang Yu 
679e5431c34SYuanjiang Yu 	case POWER_SUPPLY_PROP_CURRENT_NOW:
680e5431c34SYuanjiang Yu 		ret = sc27xx_fgu_get_cur_now(data, &value);
681e5431c34SYuanjiang Yu 		if (ret)
682e5431c34SYuanjiang Yu 			goto error;
683e5431c34SYuanjiang Yu 
684e5431c34SYuanjiang Yu 		val->intval = value * 1000;
685e5431c34SYuanjiang Yu 		break;
686e5431c34SYuanjiang Yu 
6877c1c5e38SYuanjiang Yu 	case POWER_SUPPLY_PROP_VOLTAGE_BOOT:
6887c1c5e38SYuanjiang Yu 		val->intval = data->boot_volt;
6897c1c5e38SYuanjiang Yu 		break;
6907c1c5e38SYuanjiang Yu 
691195ca170SBaolin Wang 	default:
692195ca170SBaolin Wang 		ret = -EINVAL;
693195ca170SBaolin Wang 		break;
694195ca170SBaolin Wang 	}
695195ca170SBaolin Wang 
696195ca170SBaolin Wang error:
697195ca170SBaolin Wang 	mutex_unlock(&data->lock);
698195ca170SBaolin Wang 	return ret;
699195ca170SBaolin Wang }
700195ca170SBaolin Wang 
sc27xx_fgu_set_property(struct power_supply * psy,enum power_supply_property psp,const union power_supply_propval * val)7014a040e7cSYuanjiang Yu static int sc27xx_fgu_set_property(struct power_supply *psy,
7024a040e7cSYuanjiang Yu 				   enum power_supply_property psp,
7034a040e7cSYuanjiang Yu 				   const union power_supply_propval *val)
7044a040e7cSYuanjiang Yu {
7054a040e7cSYuanjiang Yu 	struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy);
7064a040e7cSYuanjiang Yu 	int ret;
7074a040e7cSYuanjiang Yu 
7084a040e7cSYuanjiang Yu 	mutex_lock(&data->lock);
7094a040e7cSYuanjiang Yu 
7107cfd33d9SYuanjiang Yu 	switch (psp) {
7117cfd33d9SYuanjiang Yu 	case POWER_SUPPLY_PROP_CAPACITY:
7124a040e7cSYuanjiang Yu 		ret = sc27xx_fgu_save_last_cap(data, val->intval);
7134a040e7cSYuanjiang Yu 		if (ret < 0)
7144a040e7cSYuanjiang Yu 			dev_err(data->dev, "failed to save battery capacity\n");
7157cfd33d9SYuanjiang Yu 		break;
7167cfd33d9SYuanjiang Yu 
7177cfd33d9SYuanjiang Yu 	case POWER_SUPPLY_PROP_CALIBRATE:
7187cfd33d9SYuanjiang Yu 		sc27xx_fgu_adjust_cap(data, val->intval);
7197cfd33d9SYuanjiang Yu 		ret = 0;
7207cfd33d9SYuanjiang Yu 		break;
7217cfd33d9SYuanjiang Yu 
7228720b255SYuanjiang Yu 	case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
7238720b255SYuanjiang Yu 		data->total_cap = val->intval / 1000;
7248720b255SYuanjiang Yu 		ret = 0;
7258720b255SYuanjiang Yu 		break;
7268720b255SYuanjiang Yu 
7277cfd33d9SYuanjiang Yu 	default:
7287cfd33d9SYuanjiang Yu 		ret = -EINVAL;
7297cfd33d9SYuanjiang Yu 	}
7307cfd33d9SYuanjiang Yu 
7317cfd33d9SYuanjiang Yu 	mutex_unlock(&data->lock);
7324a040e7cSYuanjiang Yu 
7334a040e7cSYuanjiang Yu 	return ret;
7344a040e7cSYuanjiang Yu }
7354a040e7cSYuanjiang Yu 
sc27xx_fgu_property_is_writeable(struct power_supply * psy,enum power_supply_property psp)7364a040e7cSYuanjiang Yu static int sc27xx_fgu_property_is_writeable(struct power_supply *psy,
7374a040e7cSYuanjiang Yu 					    enum power_supply_property psp)
7384a040e7cSYuanjiang Yu {
7397cfd33d9SYuanjiang Yu 	return psp == POWER_SUPPLY_PROP_CAPACITY ||
7408720b255SYuanjiang Yu 		psp == POWER_SUPPLY_PROP_CALIBRATE ||
7418720b255SYuanjiang Yu 		psp == POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
7424a040e7cSYuanjiang Yu }
7434a040e7cSYuanjiang Yu 
744195ca170SBaolin Wang static enum power_supply_property sc27xx_fgu_props[] = {
745195ca170SBaolin Wang 	POWER_SUPPLY_PROP_STATUS,
746195ca170SBaolin Wang 	POWER_SUPPLY_PROP_HEALTH,
747195ca170SBaolin Wang 	POWER_SUPPLY_PROP_PRESENT,
748195ca170SBaolin Wang 	POWER_SUPPLY_PROP_TEMP,
749195ca170SBaolin Wang 	POWER_SUPPLY_PROP_TECHNOLOGY,
750195ca170SBaolin Wang 	POWER_SUPPLY_PROP_CAPACITY,
751195ca170SBaolin Wang 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
752195ca170SBaolin Wang 	POWER_SUPPLY_PROP_VOLTAGE_OCV,
753e5431c34SYuanjiang Yu 	POWER_SUPPLY_PROP_VOLTAGE_AVG,
7547c1c5e38SYuanjiang Yu 	POWER_SUPPLY_PROP_VOLTAGE_BOOT,
755195ca170SBaolin Wang 	POWER_SUPPLY_PROP_CURRENT_NOW,
756195ca170SBaolin Wang 	POWER_SUPPLY_PROP_CURRENT_AVG,
7570a4f97a1SBaolin Wang 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
7587cff19b9SYuanjiang Yu 	POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
7597cfd33d9SYuanjiang Yu 	POWER_SUPPLY_PROP_CALIBRATE,
7601c5dfc5eSBaolin Wang 	POWER_SUPPLY_PROP_CHARGE_NOW
761195ca170SBaolin Wang };
762195ca170SBaolin Wang 
763195ca170SBaolin Wang static const struct power_supply_desc sc27xx_fgu_desc = {
764195ca170SBaolin Wang 	.name			= "sc27xx-fgu",
765195ca170SBaolin Wang 	.type			= POWER_SUPPLY_TYPE_BATTERY,
766195ca170SBaolin Wang 	.properties		= sc27xx_fgu_props,
767195ca170SBaolin Wang 	.num_properties		= ARRAY_SIZE(sc27xx_fgu_props),
768195ca170SBaolin Wang 	.get_property		= sc27xx_fgu_get_property,
7694a040e7cSYuanjiang Yu 	.set_property		= sc27xx_fgu_set_property,
770*4d5c129dSHans de Goede 	.external_power_changed	= power_supply_changed,
7714a040e7cSYuanjiang Yu 	.property_is_writeable	= sc27xx_fgu_property_is_writeable,
772f3912a5dSYuanjiang Yu 	.no_thermal		= true,
773195ca170SBaolin Wang };
774195ca170SBaolin Wang 
sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data * data,int cap)775edcb1c0aSYuanjiang Yu static void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap)
776edcb1c0aSYuanjiang Yu {
77758066527SYuanjiang Yu 	int ret;
77858066527SYuanjiang Yu 
779edcb1c0aSYuanjiang Yu 	data->init_cap = cap;
78058066527SYuanjiang Yu 	ret = sc27xx_fgu_get_clbcnt(data, &data->init_clbcnt);
78158066527SYuanjiang Yu 	if (ret)
78258066527SYuanjiang Yu 		dev_err(data->dev, "failed to get init coulomb counter\n");
78358066527SYuanjiang Yu }
78458066527SYuanjiang Yu 
sc27xx_fgu_capacity_calibration(struct sc27xx_fgu_data * data,int cap,bool int_mode)78558066527SYuanjiang Yu static void sc27xx_fgu_capacity_calibration(struct sc27xx_fgu_data *data,
78658066527SYuanjiang Yu 					    int cap, bool int_mode)
78758066527SYuanjiang Yu {
78858066527SYuanjiang Yu 	int ret, ocv, chg_sts, adc;
78958066527SYuanjiang Yu 
79058066527SYuanjiang Yu 	ret = sc27xx_fgu_get_vbat_ocv(data, &ocv);
79158066527SYuanjiang Yu 	if (ret) {
79258066527SYuanjiang Yu 		dev_err(data->dev, "get battery ocv error.\n");
79358066527SYuanjiang Yu 		return;
79458066527SYuanjiang Yu 	}
79558066527SYuanjiang Yu 
79658066527SYuanjiang Yu 	ret = sc27xx_fgu_get_status(data, &chg_sts);
79758066527SYuanjiang Yu 	if (ret) {
79858066527SYuanjiang Yu 		dev_err(data->dev, "get charger status error.\n");
79958066527SYuanjiang Yu 		return;
80058066527SYuanjiang Yu 	}
80158066527SYuanjiang Yu 
80258066527SYuanjiang Yu 	/*
80358066527SYuanjiang Yu 	 * If we are in charging mode, then we do not need to calibrate the
80458066527SYuanjiang Yu 	 * lower capacity.
80558066527SYuanjiang Yu 	 */
80658066527SYuanjiang Yu 	if (chg_sts == POWER_SUPPLY_STATUS_CHARGING)
80758066527SYuanjiang Yu 		return;
80858066527SYuanjiang Yu 
80958066527SYuanjiang Yu 	if ((ocv > data->cap_table[0].ocv && cap < 100) || cap > 100) {
81058066527SYuanjiang Yu 		/*
81158066527SYuanjiang Yu 		 * If current OCV value is larger than the max OCV value in
81258066527SYuanjiang Yu 		 * OCV table, or the current capacity is larger than 100,
81358066527SYuanjiang Yu 		 * we should force the inititial capacity to 100.
81458066527SYuanjiang Yu 		 */
81558066527SYuanjiang Yu 		sc27xx_fgu_adjust_cap(data, 100);
81658066527SYuanjiang Yu 	} else if (ocv <= data->cap_table[data->table_len - 1].ocv) {
81758066527SYuanjiang Yu 		/*
81858066527SYuanjiang Yu 		 * If current OCV value is leass than the minimum OCV value in
81958066527SYuanjiang Yu 		 * OCV table, we should force the inititial capacity to 0.
82058066527SYuanjiang Yu 		 */
82158066527SYuanjiang Yu 		sc27xx_fgu_adjust_cap(data, 0);
82258066527SYuanjiang Yu 	} else if ((ocv > data->cap_table[data->table_len - 1].ocv && cap <= 0) ||
82358066527SYuanjiang Yu 		   (ocv > data->min_volt && cap <= data->alarm_cap)) {
82458066527SYuanjiang Yu 		/*
82558066527SYuanjiang Yu 		 * If current OCV value is not matchable with current capacity,
82658066527SYuanjiang Yu 		 * we should re-calculate current capacity by looking up the
82758066527SYuanjiang Yu 		 * OCV table.
82858066527SYuanjiang Yu 		 */
82958066527SYuanjiang Yu 		int cur_cap = power_supply_ocv2cap_simple(data->cap_table,
83058066527SYuanjiang Yu 							  data->table_len, ocv);
83158066527SYuanjiang Yu 
83258066527SYuanjiang Yu 		sc27xx_fgu_adjust_cap(data, cur_cap);
83358066527SYuanjiang Yu 	} else if (ocv <= data->min_volt) {
83458066527SYuanjiang Yu 		/*
83558066527SYuanjiang Yu 		 * If current OCV value is less than the low alarm voltage, but
83658066527SYuanjiang Yu 		 * current capacity is larger than the alarm capacity, we should
83758066527SYuanjiang Yu 		 * adjust the inititial capacity to alarm capacity.
83858066527SYuanjiang Yu 		 */
83958066527SYuanjiang Yu 		if (cap > data->alarm_cap) {
84058066527SYuanjiang Yu 			sc27xx_fgu_adjust_cap(data, data->alarm_cap);
84158066527SYuanjiang Yu 		} else {
84258066527SYuanjiang Yu 			int cur_cap;
84358066527SYuanjiang Yu 
84458066527SYuanjiang Yu 			/*
84558066527SYuanjiang Yu 			 * If current capacity is equal with 0 or less than 0
84658066527SYuanjiang Yu 			 * (some error occurs), we should adjust inititial
84758066527SYuanjiang Yu 			 * capacity to the capacity corresponding to current OCV
84858066527SYuanjiang Yu 			 * value.
84958066527SYuanjiang Yu 			 */
85058066527SYuanjiang Yu 			cur_cap = power_supply_ocv2cap_simple(data->cap_table,
85158066527SYuanjiang Yu 							      data->table_len,
85258066527SYuanjiang Yu 							      ocv);
85358066527SYuanjiang Yu 			sc27xx_fgu_adjust_cap(data, cur_cap);
85458066527SYuanjiang Yu 		}
85558066527SYuanjiang Yu 
85658066527SYuanjiang Yu 		if (!int_mode)
85758066527SYuanjiang Yu 			return;
85858066527SYuanjiang Yu 
85958066527SYuanjiang Yu 		/*
86058066527SYuanjiang Yu 		 * After adjusting the battery capacity, we should set the
86158066527SYuanjiang Yu 		 * lowest alarm voltage instead.
86258066527SYuanjiang Yu 		 */
86358066527SYuanjiang Yu 		data->min_volt = data->cap_table[data->table_len - 1].ocv;
86458066527SYuanjiang Yu 		data->alarm_cap = power_supply_ocv2cap_simple(data->cap_table,
86558066527SYuanjiang Yu 							      data->table_len,
86658066527SYuanjiang Yu 							      data->min_volt);
86758066527SYuanjiang Yu 
86858066527SYuanjiang Yu 		adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000);
86958066527SYuanjiang Yu 		regmap_update_bits(data->regmap,
87058066527SYuanjiang Yu 				   data->base + SC27XX_FGU_LOW_OVERLOAD,
87158066527SYuanjiang Yu 				   SC27XX_FGU_LOW_OVERLOAD_MASK, adc);
87258066527SYuanjiang Yu 	}
873edcb1c0aSYuanjiang Yu }
874edcb1c0aSYuanjiang Yu 
sc27xx_fgu_interrupt(int irq,void * dev_id)875edcb1c0aSYuanjiang Yu static irqreturn_t sc27xx_fgu_interrupt(int irq, void *dev_id)
876edcb1c0aSYuanjiang Yu {
877edcb1c0aSYuanjiang Yu 	struct sc27xx_fgu_data *data = dev_id;
87858066527SYuanjiang Yu 	int ret, cap;
879edcb1c0aSYuanjiang Yu 	u32 status;
880edcb1c0aSYuanjiang Yu 
881edcb1c0aSYuanjiang Yu 	mutex_lock(&data->lock);
882edcb1c0aSYuanjiang Yu 
883edcb1c0aSYuanjiang Yu 	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_INT_STS,
884edcb1c0aSYuanjiang Yu 			  &status);
885edcb1c0aSYuanjiang Yu 	if (ret)
886edcb1c0aSYuanjiang Yu 		goto out;
887edcb1c0aSYuanjiang Yu 
888edcb1c0aSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR,
889edcb1c0aSYuanjiang Yu 				 status, status);
890edcb1c0aSYuanjiang Yu 	if (ret)
891edcb1c0aSYuanjiang Yu 		goto out;
892edcb1c0aSYuanjiang Yu 
893edcb1c0aSYuanjiang Yu 	/*
894edcb1c0aSYuanjiang Yu 	 * When low overload voltage interrupt happens, we should calibrate the
895edcb1c0aSYuanjiang Yu 	 * battery capacity in lower voltage stage.
896edcb1c0aSYuanjiang Yu 	 */
897edcb1c0aSYuanjiang Yu 	if (!(status & SC27XX_FGU_LOW_OVERLOAD_INT))
898edcb1c0aSYuanjiang Yu 		goto out;
899edcb1c0aSYuanjiang Yu 
900edcb1c0aSYuanjiang Yu 	ret = sc27xx_fgu_get_capacity(data, &cap);
901edcb1c0aSYuanjiang Yu 	if (ret)
902edcb1c0aSYuanjiang Yu 		goto out;
903edcb1c0aSYuanjiang Yu 
90458066527SYuanjiang Yu 	sc27xx_fgu_capacity_calibration(data, cap, true);
905edcb1c0aSYuanjiang Yu 
906edcb1c0aSYuanjiang Yu out:
907edcb1c0aSYuanjiang Yu 	mutex_unlock(&data->lock);
908edcb1c0aSYuanjiang Yu 
909edcb1c0aSYuanjiang Yu 	power_supply_changed(data->battery);
910edcb1c0aSYuanjiang Yu 	return IRQ_HANDLED;
911edcb1c0aSYuanjiang Yu }
912edcb1c0aSYuanjiang Yu 
sc27xx_fgu_bat_detection(int irq,void * dev_id)913195ca170SBaolin Wang static irqreturn_t sc27xx_fgu_bat_detection(int irq, void *dev_id)
914195ca170SBaolin Wang {
915195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = dev_id;
916195ca170SBaolin Wang 	int state;
917195ca170SBaolin Wang 
918195ca170SBaolin Wang 	mutex_lock(&data->lock);
919195ca170SBaolin Wang 
920195ca170SBaolin Wang 	state = gpiod_get_value_cansleep(data->gpiod);
921195ca170SBaolin Wang 	if (state < 0) {
922195ca170SBaolin Wang 		dev_err(data->dev, "failed to get gpio state\n");
923195ca170SBaolin Wang 		mutex_unlock(&data->lock);
924195ca170SBaolin Wang 		return IRQ_RETVAL(state);
925195ca170SBaolin Wang 	}
926195ca170SBaolin Wang 
927195ca170SBaolin Wang 	data->bat_present = !!state;
928195ca170SBaolin Wang 
929195ca170SBaolin Wang 	mutex_unlock(&data->lock);
930195ca170SBaolin Wang 
931195ca170SBaolin Wang 	power_supply_changed(data->battery);
932195ca170SBaolin Wang 	return IRQ_HANDLED;
933195ca170SBaolin Wang }
934195ca170SBaolin Wang 
sc27xx_fgu_disable(void * _data)935195ca170SBaolin Wang static void sc27xx_fgu_disable(void *_data)
936195ca170SBaolin Wang {
937195ca170SBaolin Wang 	struct sc27xx_fgu_data *data = _data;
938195ca170SBaolin Wang 
939195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0);
940195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0);
941195ca170SBaolin Wang }
942195ca170SBaolin Wang 
sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data * data,int capacity)943195ca170SBaolin Wang static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity)
944195ca170SBaolin Wang {
945195ca170SBaolin Wang 	/*
946195ca170SBaolin Wang 	 * Get current capacity (mAh) = battery total capacity (mAh) *
947195ca170SBaolin Wang 	 * current capacity percent (capacity / 100).
948195ca170SBaolin Wang 	 */
949195ca170SBaolin Wang 	int cur_cap = DIV_ROUND_CLOSEST(data->total_cap * capacity, 100);
950195ca170SBaolin Wang 
951195ca170SBaolin Wang 	/*
952195ca170SBaolin Wang 	 * Convert current capacity (mAh) to coulomb counter according to the
953195ca170SBaolin Wang 	 * formula: 1 mAh =3.6 coulomb.
954195ca170SBaolin Wang 	 */
9557384b0e7SYuanjiang Yu 	return DIV_ROUND_CLOSEST(cur_cap * 36 * data->cur_1000ma_adc * SC27XX_FGU_SAMPLE_HZ, 10);
956195ca170SBaolin Wang }
957195ca170SBaolin Wang 
sc27xx_fgu_calibration(struct sc27xx_fgu_data * data)95865c9fab7SBaolin Wang static int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data)
95965c9fab7SBaolin Wang {
96065c9fab7SBaolin Wang 	struct nvmem_cell *cell;
96165c9fab7SBaolin Wang 	int calib_data, cal_4200mv;
96265c9fab7SBaolin Wang 	void *buf;
96365c9fab7SBaolin Wang 	size_t len;
96465c9fab7SBaolin Wang 
96565c9fab7SBaolin Wang 	cell = nvmem_cell_get(data->dev, "fgu_calib");
96665c9fab7SBaolin Wang 	if (IS_ERR(cell))
96765c9fab7SBaolin Wang 		return PTR_ERR(cell);
96865c9fab7SBaolin Wang 
96965c9fab7SBaolin Wang 	buf = nvmem_cell_read(cell, &len);
97065c9fab7SBaolin Wang 	nvmem_cell_put(cell);
97165c9fab7SBaolin Wang 
97265c9fab7SBaolin Wang 	if (IS_ERR(buf))
97365c9fab7SBaolin Wang 		return PTR_ERR(buf);
97465c9fab7SBaolin Wang 
97565c9fab7SBaolin Wang 	memcpy(&calib_data, buf, min(len, sizeof(u32)));
97665c9fab7SBaolin Wang 
97765c9fab7SBaolin Wang 	/*
97865c9fab7SBaolin Wang 	 * Get the ADC value corresponding to 4200 mV from eFuse controller
97965c9fab7SBaolin Wang 	 * according to below formula. Then convert to ADC values corresponding
98065c9fab7SBaolin Wang 	 * to 1000 mV and 1000 mA.
98165c9fab7SBaolin Wang 	 */
98265c9fab7SBaolin Wang 	cal_4200mv = (calib_data & 0x1ff) + 6963 - 4096 - 256;
98365c9fab7SBaolin Wang 	data->vol_1000mv_adc = DIV_ROUND_CLOSEST(cal_4200mv * 10, 42);
984058d4256SBaolin Wang 	data->cur_1000ma_adc =
985058d4256SBaolin Wang 		DIV_ROUND_CLOSEST(data->vol_1000mv_adc * 4 * data->calib_resist,
986058d4256SBaolin Wang 				  SC27XX_FGU_IDEAL_RESISTANCE);
98765c9fab7SBaolin Wang 
98865c9fab7SBaolin Wang 	kfree(buf);
98965c9fab7SBaolin Wang 	return 0;
99065c9fab7SBaolin Wang }
99165c9fab7SBaolin Wang 
sc27xx_fgu_hw_init(struct sc27xx_fgu_data * data)992195ca170SBaolin Wang static int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data)
993195ca170SBaolin Wang {
99425fd3303SLinus Walleij 	struct power_supply_battery_info *info;
995195ca170SBaolin Wang 	struct power_supply_battery_ocv_table *table;
996edcb1c0aSYuanjiang Yu 	int ret, delta_clbcnt, alarm_adc;
997195ca170SBaolin Wang 
998195ca170SBaolin Wang 	ret = power_supply_get_battery_info(data->battery, &info);
999195ca170SBaolin Wang 	if (ret) {
1000195ca170SBaolin Wang 		dev_err(data->dev, "failed to get battery information\n");
1001195ca170SBaolin Wang 		return ret;
1002195ca170SBaolin Wang 	}
1003195ca170SBaolin Wang 
100425fd3303SLinus Walleij 	data->total_cap = info->charge_full_design_uah / 1000;
100525fd3303SLinus Walleij 	data->max_volt = info->constant_charge_voltage_max_uv / 1000;
100625fd3303SLinus Walleij 	data->internal_resist = info->factory_internal_resistance_uohm / 1000;
100725fd3303SLinus Walleij 	data->min_volt = info->voltage_min_design_uv;
1008195ca170SBaolin Wang 
1009195ca170SBaolin Wang 	/*
1010195ca170SBaolin Wang 	 * For SC27XX fuel gauge device, we only use one ocv-capacity
1011195ca170SBaolin Wang 	 * table in normal temperature 20 Celsius.
1012195ca170SBaolin Wang 	 */
101325fd3303SLinus Walleij 	table = power_supply_find_ocv2cap_table(info, 20, &data->table_len);
1014195ca170SBaolin Wang 	if (!table)
1015195ca170SBaolin Wang 		return -EINVAL;
1016195ca170SBaolin Wang 
1017195ca170SBaolin Wang 	data->cap_table = devm_kmemdup(data->dev, table,
1018195ca170SBaolin Wang 				       data->table_len * sizeof(*table),
1019195ca170SBaolin Wang 				       GFP_KERNEL);
1020195ca170SBaolin Wang 	if (!data->cap_table) {
102125fd3303SLinus Walleij 		power_supply_put_battery_info(data->battery, info);
1022195ca170SBaolin Wang 		return -ENOMEM;
1023195ca170SBaolin Wang 	}
1024195ca170SBaolin Wang 
1025edcb1c0aSYuanjiang Yu 	data->alarm_cap = power_supply_ocv2cap_simple(data->cap_table,
1026edcb1c0aSYuanjiang Yu 						      data->table_len,
1027edcb1c0aSYuanjiang Yu 						      data->min_volt);
1028ff062d06SYuanjiang Yu 	if (!data->alarm_cap)
1029ff062d06SYuanjiang Yu 		data->alarm_cap += 1;
1030edcb1c0aSYuanjiang Yu 
103125fd3303SLinus Walleij 	data->resist_table_len = info->resist_table_size;
10326af82888SYuanjiang Yu 	if (data->resist_table_len > 0) {
103325fd3303SLinus Walleij 		data->resist_table = devm_kmemdup(data->dev, info->resist_table,
10346af82888SYuanjiang Yu 						  data->resist_table_len *
10356af82888SYuanjiang Yu 						  sizeof(struct power_supply_resistance_temp_table),
10366af82888SYuanjiang Yu 						  GFP_KERNEL);
10376af82888SYuanjiang Yu 		if (!data->resist_table) {
103825fd3303SLinus Walleij 			power_supply_put_battery_info(data->battery, info);
10396af82888SYuanjiang Yu 			return -ENOMEM;
10406af82888SYuanjiang Yu 		}
10416af82888SYuanjiang Yu 	}
10426af82888SYuanjiang Yu 
104325fd3303SLinus Walleij 	power_supply_put_battery_info(data->battery, info);
1044195ca170SBaolin Wang 
104565c9fab7SBaolin Wang 	ret = sc27xx_fgu_calibration(data);
104665c9fab7SBaolin Wang 	if (ret)
104765c9fab7SBaolin Wang 		return ret;
104865c9fab7SBaolin Wang 
1049195ca170SBaolin Wang 	/* Enable the FGU module */
1050195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap, SC27XX_MODULE_EN0,
1051195ca170SBaolin Wang 				 SC27XX_FGU_EN, SC27XX_FGU_EN);
1052195ca170SBaolin Wang 	if (ret) {
1053195ca170SBaolin Wang 		dev_err(data->dev, "failed to enable fgu\n");
1054195ca170SBaolin Wang 		return ret;
1055195ca170SBaolin Wang 	}
1056195ca170SBaolin Wang 
1057195ca170SBaolin Wang 	/* Enable the FGU RTC clock to make it work */
1058195ca170SBaolin Wang 	ret = regmap_update_bits(data->regmap, SC27XX_CLK_EN0,
1059195ca170SBaolin Wang 				 SC27XX_FGU_RTC_EN, SC27XX_FGU_RTC_EN);
1060195ca170SBaolin Wang 	if (ret) {
1061195ca170SBaolin Wang 		dev_err(data->dev, "failed to enable fgu RTC clock\n");
1062195ca170SBaolin Wang 		goto disable_fgu;
1063195ca170SBaolin Wang 	}
1064195ca170SBaolin Wang 
1065edcb1c0aSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR,
1066edcb1c0aSYuanjiang Yu 				 SC27XX_FGU_INT_MASK, SC27XX_FGU_INT_MASK);
1067edcb1c0aSYuanjiang Yu 	if (ret) {
1068edcb1c0aSYuanjiang Yu 		dev_err(data->dev, "failed to clear interrupt status\n");
1069edcb1c0aSYuanjiang Yu 		goto disable_clk;
1070edcb1c0aSYuanjiang Yu 	}
1071edcb1c0aSYuanjiang Yu 
1072edcb1c0aSYuanjiang Yu 	/*
1073edcb1c0aSYuanjiang Yu 	 * Set the voltage low overload threshold, which means when the battery
1074edcb1c0aSYuanjiang Yu 	 * voltage is lower than this threshold, the controller will generate
1075edcb1c0aSYuanjiang Yu 	 * one interrupt to notify.
1076edcb1c0aSYuanjiang Yu 	 */
1077edcb1c0aSYuanjiang Yu 	alarm_adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000);
1078edcb1c0aSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_LOW_OVERLOAD,
1079edcb1c0aSYuanjiang Yu 				 SC27XX_FGU_LOW_OVERLOAD_MASK, alarm_adc);
1080edcb1c0aSYuanjiang Yu 	if (ret) {
1081edcb1c0aSYuanjiang Yu 		dev_err(data->dev, "failed to set fgu low overload\n");
1082edcb1c0aSYuanjiang Yu 		goto disable_clk;
1083edcb1c0aSYuanjiang Yu 	}
1084edcb1c0aSYuanjiang Yu 
1085edcb1c0aSYuanjiang Yu 	/*
1086edcb1c0aSYuanjiang Yu 	 * Set the coulomb counter delta threshold, that means when the coulomb
1087edcb1c0aSYuanjiang Yu 	 * counter change is multiples of the delta threshold, the controller
1088edcb1c0aSYuanjiang Yu 	 * will generate one interrupt to notify the users to update the battery
1089edcb1c0aSYuanjiang Yu 	 * capacity. Now we set the delta threshold as a counter value of 1%
1090edcb1c0aSYuanjiang Yu 	 * capacity.
1091edcb1c0aSYuanjiang Yu 	 */
1092edcb1c0aSYuanjiang Yu 	delta_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, 1);
1093edcb1c0aSYuanjiang Yu 
1094edcb1c0aSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTL,
1095edcb1c0aSYuanjiang Yu 				 SC27XX_FGU_CLBCNT_MASK, delta_clbcnt);
1096edcb1c0aSYuanjiang Yu 	if (ret) {
1097edcb1c0aSYuanjiang Yu 		dev_err(data->dev, "failed to set low delta coulomb counter\n");
1098edcb1c0aSYuanjiang Yu 		goto disable_clk;
1099edcb1c0aSYuanjiang Yu 	}
1100edcb1c0aSYuanjiang Yu 
1101edcb1c0aSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTH,
1102edcb1c0aSYuanjiang Yu 				 SC27XX_FGU_CLBCNT_MASK,
1103edcb1c0aSYuanjiang Yu 				 delta_clbcnt >> SC27XX_FGU_CLBCNT_SHIFT);
1104edcb1c0aSYuanjiang Yu 	if (ret) {
1105edcb1c0aSYuanjiang Yu 		dev_err(data->dev, "failed to set high delta coulomb counter\n");
1106edcb1c0aSYuanjiang Yu 		goto disable_clk;
1107edcb1c0aSYuanjiang Yu 	}
1108edcb1c0aSYuanjiang Yu 
1109195ca170SBaolin Wang 	/*
1110195ca170SBaolin Wang 	 * Get the boot battery capacity when system powers on, which is used to
1111195ca170SBaolin Wang 	 * initialize the coulomb counter. After that, we can read the coulomb
1112195ca170SBaolin Wang 	 * counter to measure the battery capacity.
1113195ca170SBaolin Wang 	 */
1114195ca170SBaolin Wang 	ret = sc27xx_fgu_get_boot_capacity(data, &data->init_cap);
1115195ca170SBaolin Wang 	if (ret) {
1116195ca170SBaolin Wang 		dev_err(data->dev, "failed to get boot capacity\n");
1117195ca170SBaolin Wang 		goto disable_clk;
1118195ca170SBaolin Wang 	}
1119195ca170SBaolin Wang 
1120195ca170SBaolin Wang 	/*
1121195ca170SBaolin Wang 	 * Convert battery capacity to the corresponding initial coulomb counter
1122195ca170SBaolin Wang 	 * and set into coulomb counter registers.
1123195ca170SBaolin Wang 	 */
1124195ca170SBaolin Wang 	data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap);
1125195ca170SBaolin Wang 	ret = sc27xx_fgu_set_clbcnt(data, data->init_clbcnt);
1126195ca170SBaolin Wang 	if (ret) {
1127195ca170SBaolin Wang 		dev_err(data->dev, "failed to initialize coulomb counter\n");
1128195ca170SBaolin Wang 		goto disable_clk;
1129195ca170SBaolin Wang 	}
1130195ca170SBaolin Wang 
1131195ca170SBaolin Wang 	return 0;
1132195ca170SBaolin Wang 
1133195ca170SBaolin Wang disable_clk:
1134195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0);
1135195ca170SBaolin Wang disable_fgu:
1136195ca170SBaolin Wang 	regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0);
1137195ca170SBaolin Wang 
1138195ca170SBaolin Wang 	return ret;
1139195ca170SBaolin Wang }
1140195ca170SBaolin Wang 
sc27xx_fgu_probe(struct platform_device * pdev)1141195ca170SBaolin Wang static int sc27xx_fgu_probe(struct platform_device *pdev)
1142195ca170SBaolin Wang {
114308614b40SFuqian Huang 	struct device *dev = &pdev->dev;
114408614b40SFuqian Huang 	struct device_node *np = dev->of_node;
1145195ca170SBaolin Wang 	struct power_supply_config fgu_cfg = { };
1146195ca170SBaolin Wang 	struct sc27xx_fgu_data *data;
1147195ca170SBaolin Wang 	int ret, irq;
1148195ca170SBaolin Wang 
114908614b40SFuqian Huang 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
1150195ca170SBaolin Wang 	if (!data)
1151195ca170SBaolin Wang 		return -ENOMEM;
1152195ca170SBaolin Wang 
115308614b40SFuqian Huang 	data->regmap = dev_get_regmap(dev->parent, NULL);
1154195ca170SBaolin Wang 	if (!data->regmap) {
115508614b40SFuqian Huang 		dev_err(dev, "failed to get regmap\n");
1156195ca170SBaolin Wang 		return -ENODEV;
1157195ca170SBaolin Wang 	}
1158195ca170SBaolin Wang 
115908614b40SFuqian Huang 	ret = device_property_read_u32(dev, "reg", &data->base);
1160195ca170SBaolin Wang 	if (ret) {
116108614b40SFuqian Huang 		dev_err(dev, "failed to get fgu address\n");
1162195ca170SBaolin Wang 		return ret;
1163195ca170SBaolin Wang 	}
1164195ca170SBaolin Wang 
1165058d4256SBaolin Wang 	ret = device_property_read_u32(&pdev->dev,
1166058d4256SBaolin Wang 				       "sprd,calib-resistance-micro-ohms",
1167058d4256SBaolin Wang 				       &data->calib_resist);
1168058d4256SBaolin Wang 	if (ret) {
1169058d4256SBaolin Wang 		dev_err(&pdev->dev,
1170058d4256SBaolin Wang 			"failed to get fgu calibration resistance\n");
1171058d4256SBaolin Wang 		return ret;
1172058d4256SBaolin Wang 	}
1173058d4256SBaolin Wang 
117408614b40SFuqian Huang 	data->channel = devm_iio_channel_get(dev, "bat-temp");
1175195ca170SBaolin Wang 	if (IS_ERR(data->channel)) {
117608614b40SFuqian Huang 		dev_err(dev, "failed to get IIO channel\n");
1177195ca170SBaolin Wang 		return PTR_ERR(data->channel);
1178195ca170SBaolin Wang 	}
1179195ca170SBaolin Wang 
118008614b40SFuqian Huang 	data->charge_chan = devm_iio_channel_get(dev, "charge-vol");
11810a4f97a1SBaolin Wang 	if (IS_ERR(data->charge_chan)) {
118208614b40SFuqian Huang 		dev_err(dev, "failed to get charge IIO channel\n");
11830a4f97a1SBaolin Wang 		return PTR_ERR(data->charge_chan);
11840a4f97a1SBaolin Wang 	}
11850a4f97a1SBaolin Wang 
118608614b40SFuqian Huang 	data->gpiod = devm_gpiod_get(dev, "bat-detect", GPIOD_IN);
1187195ca170SBaolin Wang 	if (IS_ERR(data->gpiod)) {
118808614b40SFuqian Huang 		dev_err(dev, "failed to get battery detection GPIO\n");
1189195ca170SBaolin Wang 		return PTR_ERR(data->gpiod);
1190195ca170SBaolin Wang 	}
1191195ca170SBaolin Wang 
1192195ca170SBaolin Wang 	ret = gpiod_get_value_cansleep(data->gpiod);
1193195ca170SBaolin Wang 	if (ret < 0) {
119408614b40SFuqian Huang 		dev_err(dev, "failed to get gpio state\n");
1195195ca170SBaolin Wang 		return ret;
1196195ca170SBaolin Wang 	}
1197195ca170SBaolin Wang 
1198195ca170SBaolin Wang 	data->bat_present = !!ret;
1199195ca170SBaolin Wang 	mutex_init(&data->lock);
120008614b40SFuqian Huang 	data->dev = dev;
1201e2fb615bSYuanjiang Yu 	platform_set_drvdata(pdev, data);
1202195ca170SBaolin Wang 
1203195ca170SBaolin Wang 	fgu_cfg.drv_data = data;
1204195ca170SBaolin Wang 	fgu_cfg.of_node = np;
120508614b40SFuqian Huang 	data->battery = devm_power_supply_register(dev, &sc27xx_fgu_desc,
1206195ca170SBaolin Wang 						   &fgu_cfg);
1207195ca170SBaolin Wang 	if (IS_ERR(data->battery)) {
120808614b40SFuqian Huang 		dev_err(dev, "failed to register power supply\n");
1209195ca170SBaolin Wang 		return PTR_ERR(data->battery);
1210195ca170SBaolin Wang 	}
1211195ca170SBaolin Wang 
1212195ca170SBaolin Wang 	ret = sc27xx_fgu_hw_init(data);
1213195ca170SBaolin Wang 	if (ret) {
121408614b40SFuqian Huang 		dev_err(dev, "failed to initialize fgu hardware\n");
1215195ca170SBaolin Wang 		return ret;
1216195ca170SBaolin Wang 	}
1217195ca170SBaolin Wang 
121820420583SFuqian Huang 	ret = devm_add_action_or_reset(dev, sc27xx_fgu_disable, data);
1219195ca170SBaolin Wang 	if (ret) {
122008614b40SFuqian Huang 		dev_err(dev, "failed to add fgu disable action\n");
1221195ca170SBaolin Wang 		return ret;
1222195ca170SBaolin Wang 	}
1223195ca170SBaolin Wang 
1224edcb1c0aSYuanjiang Yu 	irq = platform_get_irq(pdev, 0);
1225daaca315STang Bin 	if (irq < 0)
1226edcb1c0aSYuanjiang Yu 		return irq;
1227edcb1c0aSYuanjiang Yu 
1228edcb1c0aSYuanjiang Yu 	ret = devm_request_threaded_irq(data->dev, irq, NULL,
1229edcb1c0aSYuanjiang Yu 					sc27xx_fgu_interrupt,
1230edcb1c0aSYuanjiang Yu 					IRQF_NO_SUSPEND | IRQF_ONESHOT,
1231edcb1c0aSYuanjiang Yu 					pdev->name, data);
1232edcb1c0aSYuanjiang Yu 	if (ret) {
1233edcb1c0aSYuanjiang Yu 		dev_err(data->dev, "failed to request fgu IRQ\n");
1234edcb1c0aSYuanjiang Yu 		return ret;
1235edcb1c0aSYuanjiang Yu 	}
1236edcb1c0aSYuanjiang Yu 
1237195ca170SBaolin Wang 	irq = gpiod_to_irq(data->gpiod);
1238195ca170SBaolin Wang 	if (irq < 0) {
123908614b40SFuqian Huang 		dev_err(dev, "failed to translate GPIO to IRQ\n");
1240195ca170SBaolin Wang 		return irq;
1241195ca170SBaolin Wang 	}
1242195ca170SBaolin Wang 
124308614b40SFuqian Huang 	ret = devm_request_threaded_irq(dev, irq, NULL,
1244195ca170SBaolin Wang 					sc27xx_fgu_bat_detection,
1245195ca170SBaolin Wang 					IRQF_ONESHOT | IRQF_TRIGGER_RISING |
1246195ca170SBaolin Wang 					IRQF_TRIGGER_FALLING,
1247195ca170SBaolin Wang 					pdev->name, data);
1248195ca170SBaolin Wang 	if (ret) {
124908614b40SFuqian Huang 		dev_err(dev, "failed to request IRQ\n");
1250195ca170SBaolin Wang 		return ret;
1251195ca170SBaolin Wang 	}
1252195ca170SBaolin Wang 
1253195ca170SBaolin Wang 	return 0;
1254195ca170SBaolin Wang }
1255195ca170SBaolin Wang 
1256e2fb615bSYuanjiang Yu #ifdef CONFIG_PM_SLEEP
sc27xx_fgu_resume(struct device * dev)1257e2fb615bSYuanjiang Yu static int sc27xx_fgu_resume(struct device *dev)
1258e2fb615bSYuanjiang Yu {
1259e2fb615bSYuanjiang Yu 	struct sc27xx_fgu_data *data = dev_get_drvdata(dev);
1260e2fb615bSYuanjiang Yu 	int ret;
1261e2fb615bSYuanjiang Yu 
1262e2fb615bSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN,
1263e2fb615bSYuanjiang Yu 				 SC27XX_FGU_LOW_OVERLOAD_INT |
1264e2fb615bSYuanjiang Yu 				 SC27XX_FGU_CLBCNT_DELTA_INT, 0);
1265e2fb615bSYuanjiang Yu 	if (ret) {
1266e2fb615bSYuanjiang Yu 		dev_err(data->dev, "failed to disable fgu interrupts\n");
1267e2fb615bSYuanjiang Yu 		return ret;
1268e2fb615bSYuanjiang Yu 	}
1269e2fb615bSYuanjiang Yu 
1270e2fb615bSYuanjiang Yu 	return 0;
1271e2fb615bSYuanjiang Yu }
1272e2fb615bSYuanjiang Yu 
sc27xx_fgu_suspend(struct device * dev)1273e2fb615bSYuanjiang Yu static int sc27xx_fgu_suspend(struct device *dev)
1274e2fb615bSYuanjiang Yu {
1275e2fb615bSYuanjiang Yu 	struct sc27xx_fgu_data *data = dev_get_drvdata(dev);
1276e2fb615bSYuanjiang Yu 	int ret, status, ocv;
1277e2fb615bSYuanjiang Yu 
1278e2fb615bSYuanjiang Yu 	ret = sc27xx_fgu_get_status(data, &status);
1279e2fb615bSYuanjiang Yu 	if (ret)
1280e2fb615bSYuanjiang Yu 		return ret;
1281e2fb615bSYuanjiang Yu 
1282e2fb615bSYuanjiang Yu 	/*
1283e2fb615bSYuanjiang Yu 	 * If we are charging, then no need to enable the FGU interrupts to
1284e2fb615bSYuanjiang Yu 	 * adjust the battery capacity.
1285e2fb615bSYuanjiang Yu 	 */
1286168e68d0SYuanjiang Yu 	if (status != POWER_SUPPLY_STATUS_NOT_CHARGING &&
1287168e68d0SYuanjiang Yu 	    status != POWER_SUPPLY_STATUS_DISCHARGING)
1288e2fb615bSYuanjiang Yu 		return 0;
1289e2fb615bSYuanjiang Yu 
1290e2fb615bSYuanjiang Yu 	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN,
1291e2fb615bSYuanjiang Yu 				 SC27XX_FGU_LOW_OVERLOAD_INT,
1292e2fb615bSYuanjiang Yu 				 SC27XX_FGU_LOW_OVERLOAD_INT);
1293e2fb615bSYuanjiang Yu 	if (ret) {
1294e2fb615bSYuanjiang Yu 		dev_err(data->dev, "failed to enable low voltage interrupt\n");
1295e2fb615bSYuanjiang Yu 		return ret;
1296e2fb615bSYuanjiang Yu 	}
1297e2fb615bSYuanjiang Yu 
1298e2fb615bSYuanjiang Yu 	ret = sc27xx_fgu_get_vbat_ocv(data, &ocv);
1299e2fb615bSYuanjiang Yu 	if (ret)
1300e2fb615bSYuanjiang Yu 		goto disable_int;
1301e2fb615bSYuanjiang Yu 
1302e2fb615bSYuanjiang Yu 	/*
1303e2fb615bSYuanjiang Yu 	 * If current OCV is less than the minimum voltage, we should enable the
1304e2fb615bSYuanjiang Yu 	 * coulomb counter threshold interrupt to notify events to adjust the
1305e2fb615bSYuanjiang Yu 	 * battery capacity.
1306e2fb615bSYuanjiang Yu 	 */
1307e2fb615bSYuanjiang Yu 	if (ocv < data->min_volt) {
1308e2fb615bSYuanjiang Yu 		ret = regmap_update_bits(data->regmap,
1309e2fb615bSYuanjiang Yu 					 data->base + SC27XX_FGU_INT_EN,
1310e2fb615bSYuanjiang Yu 					 SC27XX_FGU_CLBCNT_DELTA_INT,
1311e2fb615bSYuanjiang Yu 					 SC27XX_FGU_CLBCNT_DELTA_INT);
1312e2fb615bSYuanjiang Yu 		if (ret) {
1313e2fb615bSYuanjiang Yu 			dev_err(data->dev,
1314e2fb615bSYuanjiang Yu 				"failed to enable coulomb threshold int\n");
1315e2fb615bSYuanjiang Yu 			goto disable_int;
1316e2fb615bSYuanjiang Yu 		}
1317e2fb615bSYuanjiang Yu 	}
1318e2fb615bSYuanjiang Yu 
1319e2fb615bSYuanjiang Yu 	return 0;
1320e2fb615bSYuanjiang Yu 
1321e2fb615bSYuanjiang Yu disable_int:
1322e2fb615bSYuanjiang Yu 	regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN,
1323e2fb615bSYuanjiang Yu 			   SC27XX_FGU_LOW_OVERLOAD_INT, 0);
1324e2fb615bSYuanjiang Yu 	return ret;
1325e2fb615bSYuanjiang Yu }
1326e2fb615bSYuanjiang Yu #endif
1327e2fb615bSYuanjiang Yu 
1328e2fb615bSYuanjiang Yu static const struct dev_pm_ops sc27xx_fgu_pm_ops = {
1329e2fb615bSYuanjiang Yu 	SET_SYSTEM_SLEEP_PM_OPS(sc27xx_fgu_suspend, sc27xx_fgu_resume)
1330e2fb615bSYuanjiang Yu };
1331e2fb615bSYuanjiang Yu 
1332195ca170SBaolin Wang static const struct of_device_id sc27xx_fgu_of_match[] = {
1333195ca170SBaolin Wang 	{ .compatible = "sprd,sc2731-fgu", },
1334195ca170SBaolin Wang 	{ }
1335195ca170SBaolin Wang };
1336603fcfb9SZou Wei MODULE_DEVICE_TABLE(of, sc27xx_fgu_of_match);
1337195ca170SBaolin Wang 
1338195ca170SBaolin Wang static struct platform_driver sc27xx_fgu_driver = {
1339195ca170SBaolin Wang 	.probe = sc27xx_fgu_probe,
1340195ca170SBaolin Wang 	.driver = {
1341195ca170SBaolin Wang 		.name = "sc27xx-fgu",
1342195ca170SBaolin Wang 		.of_match_table = sc27xx_fgu_of_match,
1343e2fb615bSYuanjiang Yu 		.pm = &sc27xx_fgu_pm_ops,
1344195ca170SBaolin Wang 	}
1345195ca170SBaolin Wang };
1346195ca170SBaolin Wang 
1347195ca170SBaolin Wang module_platform_driver(sc27xx_fgu_driver);
1348195ca170SBaolin Wang 
1349195ca170SBaolin Wang MODULE_DESCRIPTION("Spreadtrum SC27XX PMICs Fual Gauge Unit Driver");
1350195ca170SBaolin Wang MODULE_LICENSE("GPL v2");
1351