162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci// Copyright (C) 2018 Spreadtrum Communications Inc.
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
562306a36Sopenharmony_ci#include <linux/iio/consumer.h>
662306a36Sopenharmony_ci#include <linux/interrupt.h>
762306a36Sopenharmony_ci#include <linux/kernel.h>
862306a36Sopenharmony_ci#include <linux/math64.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/nvmem-consumer.h>
1162306a36Sopenharmony_ci#include <linux/of.h>
1262306a36Sopenharmony_ci#include <linux/platform_device.h>
1362306a36Sopenharmony_ci#include <linux/power_supply.h>
1462306a36Sopenharmony_ci#include <linux/regmap.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/* PMIC global control registers definition */
1862306a36Sopenharmony_ci#define SC27XX_MODULE_EN0		0xc08
1962306a36Sopenharmony_ci#define SC27XX_CLK_EN0			0xc18
2062306a36Sopenharmony_ci#define SC27XX_FGU_EN			BIT(7)
2162306a36Sopenharmony_ci#define SC27XX_FGU_RTC_EN		BIT(6)
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* FGU registers definition */
2462306a36Sopenharmony_ci#define SC27XX_FGU_START		0x0
2562306a36Sopenharmony_ci#define SC27XX_FGU_CONFIG		0x4
2662306a36Sopenharmony_ci#define SC27XX_FGU_ADC_CONFIG		0x8
2762306a36Sopenharmony_ci#define SC27XX_FGU_STATUS		0xc
2862306a36Sopenharmony_ci#define SC27XX_FGU_INT_EN		0x10
2962306a36Sopenharmony_ci#define SC27XX_FGU_INT_CLR		0x14
3062306a36Sopenharmony_ci#define SC27XX_FGU_INT_STS		0x1c
3162306a36Sopenharmony_ci#define SC27XX_FGU_VOLTAGE		0x20
3262306a36Sopenharmony_ci#define SC27XX_FGU_OCV			0x24
3362306a36Sopenharmony_ci#define SC27XX_FGU_POCV			0x28
3462306a36Sopenharmony_ci#define SC27XX_FGU_CURRENT		0x2c
3562306a36Sopenharmony_ci#define SC27XX_FGU_LOW_OVERLOAD		0x34
3662306a36Sopenharmony_ci#define SC27XX_FGU_CLBCNT_SETH		0x50
3762306a36Sopenharmony_ci#define SC27XX_FGU_CLBCNT_SETL		0x54
3862306a36Sopenharmony_ci#define SC27XX_FGU_CLBCNT_DELTH		0x58
3962306a36Sopenharmony_ci#define SC27XX_FGU_CLBCNT_DELTL		0x5c
4062306a36Sopenharmony_ci#define SC27XX_FGU_CLBCNT_VALH		0x68
4162306a36Sopenharmony_ci#define SC27XX_FGU_CLBCNT_VALL		0x6c
4262306a36Sopenharmony_ci#define SC27XX_FGU_CLBCNT_QMAXL		0x74
4362306a36Sopenharmony_ci#define SC27XX_FGU_USER_AREA_SET	0xa0
4462306a36Sopenharmony_ci#define SC27XX_FGU_USER_AREA_CLEAR	0xa4
4562306a36Sopenharmony_ci#define SC27XX_FGU_USER_AREA_STATUS	0xa8
4662306a36Sopenharmony_ci#define SC27XX_FGU_VOLTAGE_BUF		0xd0
4762306a36Sopenharmony_ci#define SC27XX_FGU_CURRENT_BUF		0xf0
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define SC27XX_WRITE_SELCLB_EN		BIT(0)
5062306a36Sopenharmony_ci#define SC27XX_FGU_CLBCNT_MASK		GENMASK(15, 0)
5162306a36Sopenharmony_ci#define SC27XX_FGU_CLBCNT_SHIFT		16
5262306a36Sopenharmony_ci#define SC27XX_FGU_LOW_OVERLOAD_MASK	GENMASK(12, 0)
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#define SC27XX_FGU_INT_MASK		GENMASK(9, 0)
5562306a36Sopenharmony_ci#define SC27XX_FGU_LOW_OVERLOAD_INT	BIT(0)
5662306a36Sopenharmony_ci#define SC27XX_FGU_CLBCNT_DELTA_INT	BIT(2)
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#define SC27XX_FGU_MODE_AREA_MASK	GENMASK(15, 12)
5962306a36Sopenharmony_ci#define SC27XX_FGU_CAP_AREA_MASK	GENMASK(11, 0)
6062306a36Sopenharmony_ci#define SC27XX_FGU_MODE_AREA_SHIFT	12
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci#define SC27XX_FGU_FIRST_POWERTON	GENMASK(3, 0)
6362306a36Sopenharmony_ci#define SC27XX_FGU_DEFAULT_CAP		GENMASK(11, 0)
6462306a36Sopenharmony_ci#define SC27XX_FGU_NORMAIL_POWERTON	0x5
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci#define SC27XX_FGU_CUR_BASIC_ADC	8192
6762306a36Sopenharmony_ci#define SC27XX_FGU_SAMPLE_HZ		2
6862306a36Sopenharmony_ci/* micro Ohms */
6962306a36Sopenharmony_ci#define SC27XX_FGU_IDEAL_RESISTANCE	20000
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/*
7262306a36Sopenharmony_ci * struct sc27xx_fgu_data: describe the FGU device
7362306a36Sopenharmony_ci * @regmap: regmap for register access
7462306a36Sopenharmony_ci * @dev: platform device
7562306a36Sopenharmony_ci * @battery: battery power supply
7662306a36Sopenharmony_ci * @base: the base offset for the controller
7762306a36Sopenharmony_ci * @lock: protect the structure
7862306a36Sopenharmony_ci * @gpiod: GPIO for battery detection
7962306a36Sopenharmony_ci * @channel: IIO channel to get battery temperature
8062306a36Sopenharmony_ci * @charge_chan: IIO channel to get charge voltage
8162306a36Sopenharmony_ci * @internal_resist: the battery internal resistance in mOhm
8262306a36Sopenharmony_ci * @total_cap: the total capacity of the battery in mAh
8362306a36Sopenharmony_ci * @init_cap: the initial capacity of the battery in mAh
8462306a36Sopenharmony_ci * @alarm_cap: the alarm capacity
8562306a36Sopenharmony_ci * @init_clbcnt: the initial coulomb counter
8662306a36Sopenharmony_ci * @max_volt: the maximum constant input voltage in millivolt
8762306a36Sopenharmony_ci * @min_volt: the minimum drained battery voltage in microvolt
8862306a36Sopenharmony_ci * @boot_volt: the voltage measured during boot in microvolt
8962306a36Sopenharmony_ci * @table_len: the capacity table length
9062306a36Sopenharmony_ci * @resist_table_len: the resistance table length
9162306a36Sopenharmony_ci * @cur_1000ma_adc: ADC value corresponding to 1000 mA
9262306a36Sopenharmony_ci * @vol_1000mv_adc: ADC value corresponding to 1000 mV
9362306a36Sopenharmony_ci * @calib_resist: the real resistance of coulomb counter chip in uOhm
9462306a36Sopenharmony_ci * @cap_table: capacity table with corresponding ocv
9562306a36Sopenharmony_ci * @resist_table: resistance percent table with corresponding temperature
9662306a36Sopenharmony_ci */
9762306a36Sopenharmony_cistruct sc27xx_fgu_data {
9862306a36Sopenharmony_ci	struct regmap *regmap;
9962306a36Sopenharmony_ci	struct device *dev;
10062306a36Sopenharmony_ci	struct power_supply *battery;
10162306a36Sopenharmony_ci	u32 base;
10262306a36Sopenharmony_ci	struct mutex lock;
10362306a36Sopenharmony_ci	struct gpio_desc *gpiod;
10462306a36Sopenharmony_ci	struct iio_channel *channel;
10562306a36Sopenharmony_ci	struct iio_channel *charge_chan;
10662306a36Sopenharmony_ci	bool bat_present;
10762306a36Sopenharmony_ci	int internal_resist;
10862306a36Sopenharmony_ci	int total_cap;
10962306a36Sopenharmony_ci	int init_cap;
11062306a36Sopenharmony_ci	int alarm_cap;
11162306a36Sopenharmony_ci	int init_clbcnt;
11262306a36Sopenharmony_ci	int max_volt;
11362306a36Sopenharmony_ci	int min_volt;
11462306a36Sopenharmony_ci	int boot_volt;
11562306a36Sopenharmony_ci	int table_len;
11662306a36Sopenharmony_ci	int resist_table_len;
11762306a36Sopenharmony_ci	int cur_1000ma_adc;
11862306a36Sopenharmony_ci	int vol_1000mv_adc;
11962306a36Sopenharmony_ci	int calib_resist;
12062306a36Sopenharmony_ci	struct power_supply_battery_ocv_table *cap_table;
12162306a36Sopenharmony_ci	struct power_supply_resistance_temp_table *resist_table;
12262306a36Sopenharmony_ci};
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity);
12562306a36Sopenharmony_cistatic void sc27xx_fgu_capacity_calibration(struct sc27xx_fgu_data *data,
12662306a36Sopenharmony_ci					    int cap, bool int_mode);
12762306a36Sopenharmony_cistatic void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap);
12862306a36Sopenharmony_cistatic int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic const char * const sc27xx_charger_supply_name[] = {
13162306a36Sopenharmony_ci	"sc2731_charger",
13262306a36Sopenharmony_ci	"sc2720_charger",
13362306a36Sopenharmony_ci	"sc2721_charger",
13462306a36Sopenharmony_ci	"sc2723_charger",
13562306a36Sopenharmony_ci};
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic int sc27xx_fgu_adc_to_current(struct sc27xx_fgu_data *data, s64 adc)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	return DIV_S64_ROUND_CLOSEST(adc * 1000, data->cur_1000ma_adc);
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic int sc27xx_fgu_adc_to_voltage(struct sc27xx_fgu_data *data, s64 adc)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	return DIV_S64_ROUND_CLOSEST(adc * 1000, data->vol_1000mv_adc);
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic int sc27xx_fgu_voltage_to_adc(struct sc27xx_fgu_data *data, int vol)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	return DIV_ROUND_CLOSEST(vol * data->vol_1000mv_adc, 1000);
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic bool sc27xx_fgu_is_first_poweron(struct sc27xx_fgu_data *data)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	int ret, status, cap, mode;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	ret = regmap_read(data->regmap,
15762306a36Sopenharmony_ci			  data->base + SC27XX_FGU_USER_AREA_STATUS, &status);
15862306a36Sopenharmony_ci	if (ret)
15962306a36Sopenharmony_ci		return false;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	/*
16262306a36Sopenharmony_ci	 * We use low 4 bits to save the last battery capacity and high 12 bits
16362306a36Sopenharmony_ci	 * to save the system boot mode.
16462306a36Sopenharmony_ci	 */
16562306a36Sopenharmony_ci	mode = (status & SC27XX_FGU_MODE_AREA_MASK) >> SC27XX_FGU_MODE_AREA_SHIFT;
16662306a36Sopenharmony_ci	cap = status & SC27XX_FGU_CAP_AREA_MASK;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	/*
16962306a36Sopenharmony_ci	 * When FGU has been powered down, the user area registers became
17062306a36Sopenharmony_ci	 * default value (0xffff), which can be used to valid if the system is
17162306a36Sopenharmony_ci	 * first power on or not.
17262306a36Sopenharmony_ci	 */
17362306a36Sopenharmony_ci	if (mode == SC27XX_FGU_FIRST_POWERTON || cap == SC27XX_FGU_DEFAULT_CAP)
17462306a36Sopenharmony_ci		return true;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	return false;
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic int sc27xx_fgu_save_boot_mode(struct sc27xx_fgu_data *data,
18062306a36Sopenharmony_ci				     int boot_mode)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	int ret;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	ret = regmap_update_bits(data->regmap,
18562306a36Sopenharmony_ci				 data->base + SC27XX_FGU_USER_AREA_CLEAR,
18662306a36Sopenharmony_ci				 SC27XX_FGU_MODE_AREA_MASK,
18762306a36Sopenharmony_ci				 SC27XX_FGU_MODE_AREA_MASK);
18862306a36Sopenharmony_ci	if (ret)
18962306a36Sopenharmony_ci		return ret;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	/*
19262306a36Sopenharmony_ci	 * Since the user area registers are put on power always-on region,
19362306a36Sopenharmony_ci	 * then these registers changing time will be a little long. Thus
19462306a36Sopenharmony_ci	 * here we should delay 200us to wait until values are updated
19562306a36Sopenharmony_ci	 * successfully according to the datasheet.
19662306a36Sopenharmony_ci	 */
19762306a36Sopenharmony_ci	udelay(200);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	ret = regmap_update_bits(data->regmap,
20062306a36Sopenharmony_ci				 data->base + SC27XX_FGU_USER_AREA_SET,
20162306a36Sopenharmony_ci				 SC27XX_FGU_MODE_AREA_MASK,
20262306a36Sopenharmony_ci				 boot_mode << SC27XX_FGU_MODE_AREA_SHIFT);
20362306a36Sopenharmony_ci	if (ret)
20462306a36Sopenharmony_ci		return ret;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	/*
20762306a36Sopenharmony_ci	 * Since the user area registers are put on power always-on region,
20862306a36Sopenharmony_ci	 * then these registers changing time will be a little long. Thus
20962306a36Sopenharmony_ci	 * here we should delay 200us to wait until values are updated
21062306a36Sopenharmony_ci	 * successfully according to the datasheet.
21162306a36Sopenharmony_ci	 */
21262306a36Sopenharmony_ci	udelay(200);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	/*
21562306a36Sopenharmony_ci	 * According to the datasheet, we should set the USER_AREA_CLEAR to 0 to
21662306a36Sopenharmony_ci	 * make the user area data available, otherwise we can not save the user
21762306a36Sopenharmony_ci	 * area data.
21862306a36Sopenharmony_ci	 */
21962306a36Sopenharmony_ci	return regmap_update_bits(data->regmap,
22062306a36Sopenharmony_ci				  data->base + SC27XX_FGU_USER_AREA_CLEAR,
22162306a36Sopenharmony_ci				  SC27XX_FGU_MODE_AREA_MASK, 0);
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic int sc27xx_fgu_save_last_cap(struct sc27xx_fgu_data *data, int cap)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	int ret;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	ret = regmap_update_bits(data->regmap,
22962306a36Sopenharmony_ci				 data->base + SC27XX_FGU_USER_AREA_CLEAR,
23062306a36Sopenharmony_ci				 SC27XX_FGU_CAP_AREA_MASK,
23162306a36Sopenharmony_ci				 SC27XX_FGU_CAP_AREA_MASK);
23262306a36Sopenharmony_ci	if (ret)
23362306a36Sopenharmony_ci		return ret;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	/*
23662306a36Sopenharmony_ci	 * Since the user area registers are put on power always-on region,
23762306a36Sopenharmony_ci	 * then these registers changing time will be a little long. Thus
23862306a36Sopenharmony_ci	 * here we should delay 200us to wait until values are updated
23962306a36Sopenharmony_ci	 * successfully according to the datasheet.
24062306a36Sopenharmony_ci	 */
24162306a36Sopenharmony_ci	udelay(200);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	ret = regmap_update_bits(data->regmap,
24462306a36Sopenharmony_ci				 data->base + SC27XX_FGU_USER_AREA_SET,
24562306a36Sopenharmony_ci				 SC27XX_FGU_CAP_AREA_MASK, cap);
24662306a36Sopenharmony_ci	if (ret)
24762306a36Sopenharmony_ci		return ret;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	/*
25062306a36Sopenharmony_ci	 * Since the user area registers are put on power always-on region,
25162306a36Sopenharmony_ci	 * then these registers changing time will be a little long. Thus
25262306a36Sopenharmony_ci	 * here we should delay 200us to wait until values are updated
25362306a36Sopenharmony_ci	 * successfully according to the datasheet.
25462306a36Sopenharmony_ci	 */
25562306a36Sopenharmony_ci	udelay(200);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	/*
25862306a36Sopenharmony_ci	 * According to the datasheet, we should set the USER_AREA_CLEAR to 0 to
25962306a36Sopenharmony_ci	 * make the user area data available, otherwise we can not save the user
26062306a36Sopenharmony_ci	 * area data.
26162306a36Sopenharmony_ci	 */
26262306a36Sopenharmony_ci	return regmap_update_bits(data->regmap,
26362306a36Sopenharmony_ci				  data->base + SC27XX_FGU_USER_AREA_CLEAR,
26462306a36Sopenharmony_ci				  SC27XX_FGU_CAP_AREA_MASK, 0);
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic int sc27xx_fgu_read_last_cap(struct sc27xx_fgu_data *data, int *cap)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	int ret, value;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	ret = regmap_read(data->regmap,
27262306a36Sopenharmony_ci			  data->base + SC27XX_FGU_USER_AREA_STATUS, &value);
27362306a36Sopenharmony_ci	if (ret)
27462306a36Sopenharmony_ci		return ret;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	*cap = value & SC27XX_FGU_CAP_AREA_MASK;
27762306a36Sopenharmony_ci	return 0;
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci/*
28162306a36Sopenharmony_ci * When system boots on, we can not read battery capacity from coulomb
28262306a36Sopenharmony_ci * registers, since now the coulomb registers are invalid. So we should
28362306a36Sopenharmony_ci * calculate the battery open circuit voltage, and get current battery
28462306a36Sopenharmony_ci * capacity according to the capacity table.
28562306a36Sopenharmony_ci */
28662306a36Sopenharmony_cistatic int sc27xx_fgu_get_boot_capacity(struct sc27xx_fgu_data *data, int *cap)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	int volt, cur, oci, ocv, ret;
28962306a36Sopenharmony_ci	bool is_first_poweron = sc27xx_fgu_is_first_poweron(data);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	/*
29262306a36Sopenharmony_ci	 * If system is not the first power on, we should use the last saved
29362306a36Sopenharmony_ci	 * battery capacity as the initial battery capacity. Otherwise we should
29462306a36Sopenharmony_ci	 * re-calculate the initial battery capacity.
29562306a36Sopenharmony_ci	 */
29662306a36Sopenharmony_ci	if (!is_first_poweron) {
29762306a36Sopenharmony_ci		ret = sc27xx_fgu_read_last_cap(data, cap);
29862306a36Sopenharmony_ci		if (ret)
29962306a36Sopenharmony_ci			return ret;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci		return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON);
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	/*
30562306a36Sopenharmony_ci	 * After system booting on, the SC27XX_FGU_CLBCNT_QMAXL register saved
30662306a36Sopenharmony_ci	 * the first sampled open circuit current.
30762306a36Sopenharmony_ci	 */
30862306a36Sopenharmony_ci	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_QMAXL,
30962306a36Sopenharmony_ci			  &cur);
31062306a36Sopenharmony_ci	if (ret)
31162306a36Sopenharmony_ci		return ret;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	cur <<= 1;
31462306a36Sopenharmony_ci	oci = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	/*
31762306a36Sopenharmony_ci	 * Should get the OCV from SC27XX_FGU_POCV register at the system
31862306a36Sopenharmony_ci	 * beginning. It is ADC values reading from registers which need to
31962306a36Sopenharmony_ci	 * convert the corresponding voltage.
32062306a36Sopenharmony_ci	 */
32162306a36Sopenharmony_ci	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_POCV, &volt);
32262306a36Sopenharmony_ci	if (ret)
32362306a36Sopenharmony_ci		return ret;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	volt = sc27xx_fgu_adc_to_voltage(data, volt);
32662306a36Sopenharmony_ci	ocv = volt * 1000 - oci * data->internal_resist;
32762306a36Sopenharmony_ci	data->boot_volt = ocv;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	/*
33062306a36Sopenharmony_ci	 * Parse the capacity table to look up the correct capacity percent
33162306a36Sopenharmony_ci	 * according to current battery's corresponding OCV values.
33262306a36Sopenharmony_ci	 */
33362306a36Sopenharmony_ci	*cap = power_supply_ocv2cap_simple(data->cap_table, data->table_len,
33462306a36Sopenharmony_ci					   ocv);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	ret = sc27xx_fgu_save_last_cap(data, *cap);
33762306a36Sopenharmony_ci	if (ret)
33862306a36Sopenharmony_ci		return ret;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	return sc27xx_fgu_save_boot_mode(data, SC27XX_FGU_NORMAIL_POWERTON);
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic int sc27xx_fgu_set_clbcnt(struct sc27xx_fgu_data *data, int clbcnt)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	int ret;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	ret = regmap_update_bits(data->regmap,
34862306a36Sopenharmony_ci				 data->base + SC27XX_FGU_CLBCNT_SETL,
34962306a36Sopenharmony_ci				 SC27XX_FGU_CLBCNT_MASK, clbcnt);
35062306a36Sopenharmony_ci	if (ret)
35162306a36Sopenharmony_ci		return ret;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	ret = regmap_update_bits(data->regmap,
35462306a36Sopenharmony_ci				 data->base + SC27XX_FGU_CLBCNT_SETH,
35562306a36Sopenharmony_ci				 SC27XX_FGU_CLBCNT_MASK,
35662306a36Sopenharmony_ci				 clbcnt >> SC27XX_FGU_CLBCNT_SHIFT);
35762306a36Sopenharmony_ci	if (ret)
35862306a36Sopenharmony_ci		return ret;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	return regmap_update_bits(data->regmap, data->base + SC27XX_FGU_START,
36162306a36Sopenharmony_ci				 SC27XX_WRITE_SELCLB_EN,
36262306a36Sopenharmony_ci				 SC27XX_WRITE_SELCLB_EN);
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic int sc27xx_fgu_get_clbcnt(struct sc27xx_fgu_data *data, int *clb_cnt)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	int ccl, cch, ret;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALL,
37062306a36Sopenharmony_ci			  &ccl);
37162306a36Sopenharmony_ci	if (ret)
37262306a36Sopenharmony_ci		return ret;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CLBCNT_VALH,
37562306a36Sopenharmony_ci			  &cch);
37662306a36Sopenharmony_ci	if (ret)
37762306a36Sopenharmony_ci		return ret;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	*clb_cnt = ccl & SC27XX_FGU_CLBCNT_MASK;
38062306a36Sopenharmony_ci	*clb_cnt |= (cch & SC27XX_FGU_CLBCNT_MASK) << SC27XX_FGU_CLBCNT_SHIFT;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	return 0;
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic int sc27xx_fgu_get_vol_now(struct sc27xx_fgu_data *data, int *val)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	int ret;
38862306a36Sopenharmony_ci	u32 vol;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_VOLTAGE_BUF,
39162306a36Sopenharmony_ci			  &vol);
39262306a36Sopenharmony_ci	if (ret)
39362306a36Sopenharmony_ci		return ret;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	/*
39662306a36Sopenharmony_ci	 * It is ADC values reading from registers which need to convert to
39762306a36Sopenharmony_ci	 * corresponding voltage values.
39862306a36Sopenharmony_ci	 */
39962306a36Sopenharmony_ci	*val = sc27xx_fgu_adc_to_voltage(data, vol);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	return 0;
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic int sc27xx_fgu_get_cur_now(struct sc27xx_fgu_data *data, int *val)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	int ret;
40762306a36Sopenharmony_ci	u32 cur;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CURRENT_BUF,
41062306a36Sopenharmony_ci			  &cur);
41162306a36Sopenharmony_ci	if (ret)
41262306a36Sopenharmony_ci		return ret;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	/*
41562306a36Sopenharmony_ci	 * It is ADC values reading from registers which need to convert to
41662306a36Sopenharmony_ci	 * corresponding current values.
41762306a36Sopenharmony_ci	 */
41862306a36Sopenharmony_ci	*val = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	return 0;
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic int sc27xx_fgu_get_capacity(struct sc27xx_fgu_data *data, int *cap)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	int ret, cur_clbcnt, delta_clbcnt, delta_cap, temp;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	/* Get current coulomb counters firstly */
42862306a36Sopenharmony_ci	ret = sc27xx_fgu_get_clbcnt(data, &cur_clbcnt);
42962306a36Sopenharmony_ci	if (ret)
43062306a36Sopenharmony_ci		return ret;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	delta_clbcnt = cur_clbcnt - data->init_clbcnt;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	/*
43562306a36Sopenharmony_ci	 * Convert coulomb counter to delta capacity (mAh), and set multiplier
43662306a36Sopenharmony_ci	 * as 10 to improve the precision.
43762306a36Sopenharmony_ci	 */
43862306a36Sopenharmony_ci	temp = DIV_ROUND_CLOSEST(delta_clbcnt * 10, 36 * SC27XX_FGU_SAMPLE_HZ);
43962306a36Sopenharmony_ci	temp = sc27xx_fgu_adc_to_current(data, temp / 1000);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	/*
44262306a36Sopenharmony_ci	 * Convert to capacity percent of the battery total capacity,
44362306a36Sopenharmony_ci	 * and multiplier is 100 too.
44462306a36Sopenharmony_ci	 */
44562306a36Sopenharmony_ci	delta_cap = DIV_ROUND_CLOSEST(temp * 100, data->total_cap);
44662306a36Sopenharmony_ci	*cap = delta_cap + data->init_cap;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	/* Calibrate the battery capacity in a normal range. */
44962306a36Sopenharmony_ci	sc27xx_fgu_capacity_calibration(data, *cap, false);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	return 0;
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_cistatic int sc27xx_fgu_get_vbat_vol(struct sc27xx_fgu_data *data, int *val)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	int ret, vol;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_VOLTAGE, &vol);
45962306a36Sopenharmony_ci	if (ret)
46062306a36Sopenharmony_ci		return ret;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	/*
46362306a36Sopenharmony_ci	 * It is ADC values reading from registers which need to convert to
46462306a36Sopenharmony_ci	 * corresponding voltage values.
46562306a36Sopenharmony_ci	 */
46662306a36Sopenharmony_ci	*val = sc27xx_fgu_adc_to_voltage(data, vol);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	return 0;
46962306a36Sopenharmony_ci}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_cistatic int sc27xx_fgu_get_current(struct sc27xx_fgu_data *data, int *val)
47262306a36Sopenharmony_ci{
47362306a36Sopenharmony_ci	int ret, cur;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_CURRENT, &cur);
47662306a36Sopenharmony_ci	if (ret)
47762306a36Sopenharmony_ci		return ret;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	/*
48062306a36Sopenharmony_ci	 * It is ADC values reading from registers which need to convert to
48162306a36Sopenharmony_ci	 * corresponding current values.
48262306a36Sopenharmony_ci	 */
48362306a36Sopenharmony_ci	*val = sc27xx_fgu_adc_to_current(data, cur - SC27XX_FGU_CUR_BASIC_ADC);
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	return 0;
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_cistatic int sc27xx_fgu_get_vbat_ocv(struct sc27xx_fgu_data *data, int *val)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	int vol, cur, ret, temp, resistance;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	ret = sc27xx_fgu_get_vbat_vol(data, &vol);
49362306a36Sopenharmony_ci	if (ret)
49462306a36Sopenharmony_ci		return ret;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	ret = sc27xx_fgu_get_current(data, &cur);
49762306a36Sopenharmony_ci	if (ret)
49862306a36Sopenharmony_ci		return ret;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	resistance = data->internal_resist;
50162306a36Sopenharmony_ci	if (data->resist_table_len > 0) {
50262306a36Sopenharmony_ci		ret = sc27xx_fgu_get_temp(data, &temp);
50362306a36Sopenharmony_ci		if (ret)
50462306a36Sopenharmony_ci			return ret;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci		resistance = power_supply_temp2resist_simple(data->resist_table,
50762306a36Sopenharmony_ci						data->resist_table_len, temp);
50862306a36Sopenharmony_ci		resistance = data->internal_resist * resistance / 100;
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	/* Return the battery OCV in micro volts. */
51262306a36Sopenharmony_ci	*val = vol * 1000 - cur * resistance;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	return 0;
51562306a36Sopenharmony_ci}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_cistatic int sc27xx_fgu_get_charge_vol(struct sc27xx_fgu_data *data, int *val)
51862306a36Sopenharmony_ci{
51962306a36Sopenharmony_ci	int ret, vol;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	ret = iio_read_channel_processed(data->charge_chan, &vol);
52262306a36Sopenharmony_ci	if (ret < 0)
52362306a36Sopenharmony_ci		return ret;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	*val = vol * 1000;
52662306a36Sopenharmony_ci	return 0;
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic int sc27xx_fgu_get_temp(struct sc27xx_fgu_data *data, int *temp)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci	return iio_read_channel_processed(data->channel, temp);
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_cistatic int sc27xx_fgu_get_health(struct sc27xx_fgu_data *data, int *health)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	int ret, vol;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	ret = sc27xx_fgu_get_vbat_vol(data, &vol);
53962306a36Sopenharmony_ci	if (ret)
54062306a36Sopenharmony_ci		return ret;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	if (vol > data->max_volt)
54362306a36Sopenharmony_ci		*health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
54462306a36Sopenharmony_ci	else
54562306a36Sopenharmony_ci		*health = POWER_SUPPLY_HEALTH_GOOD;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	return 0;
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_cistatic int sc27xx_fgu_get_status(struct sc27xx_fgu_data *data, int *status)
55162306a36Sopenharmony_ci{
55262306a36Sopenharmony_ci	union power_supply_propval val;
55362306a36Sopenharmony_ci	struct power_supply *psy;
55462306a36Sopenharmony_ci	int i, ret = -EINVAL;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(sc27xx_charger_supply_name); i++) {
55762306a36Sopenharmony_ci		psy = power_supply_get_by_name(sc27xx_charger_supply_name[i]);
55862306a36Sopenharmony_ci		if (!psy)
55962306a36Sopenharmony_ci			continue;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci		ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS,
56262306a36Sopenharmony_ci						&val);
56362306a36Sopenharmony_ci		power_supply_put(psy);
56462306a36Sopenharmony_ci		if (ret)
56562306a36Sopenharmony_ci			return ret;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci		*status = val.intval;
56862306a36Sopenharmony_ci	}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	return ret;
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistatic int sc27xx_fgu_get_property(struct power_supply *psy,
57462306a36Sopenharmony_ci				   enum power_supply_property psp,
57562306a36Sopenharmony_ci				   union power_supply_propval *val)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy);
57862306a36Sopenharmony_ci	int ret = 0;
57962306a36Sopenharmony_ci	int value;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	mutex_lock(&data->lock);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	switch (psp) {
58462306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_STATUS:
58562306a36Sopenharmony_ci		ret = sc27xx_fgu_get_status(data, &value);
58662306a36Sopenharmony_ci		if (ret)
58762306a36Sopenharmony_ci			goto error;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci		val->intval = value;
59062306a36Sopenharmony_ci		break;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_HEALTH:
59362306a36Sopenharmony_ci		ret = sc27xx_fgu_get_health(data, &value);
59462306a36Sopenharmony_ci		if (ret)
59562306a36Sopenharmony_ci			goto error;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci		val->intval = value;
59862306a36Sopenharmony_ci		break;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_PRESENT:
60162306a36Sopenharmony_ci		val->intval = data->bat_present;
60262306a36Sopenharmony_ci		break;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_TEMP:
60562306a36Sopenharmony_ci		ret = sc27xx_fgu_get_temp(data, &value);
60662306a36Sopenharmony_ci		if (ret)
60762306a36Sopenharmony_ci			goto error;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci		val->intval = value;
61062306a36Sopenharmony_ci		break;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_TECHNOLOGY:
61362306a36Sopenharmony_ci		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
61462306a36Sopenharmony_ci		break;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CAPACITY:
61762306a36Sopenharmony_ci		ret = sc27xx_fgu_get_capacity(data, &value);
61862306a36Sopenharmony_ci		if (ret)
61962306a36Sopenharmony_ci			goto error;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci		val->intval = value;
62262306a36Sopenharmony_ci		break;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_AVG:
62562306a36Sopenharmony_ci		ret = sc27xx_fgu_get_vbat_vol(data, &value);
62662306a36Sopenharmony_ci		if (ret)
62762306a36Sopenharmony_ci			goto error;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci		val->intval = value * 1000;
63062306a36Sopenharmony_ci		break;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_OCV:
63362306a36Sopenharmony_ci		ret = sc27xx_fgu_get_vbat_ocv(data, &value);
63462306a36Sopenharmony_ci		if (ret)
63562306a36Sopenharmony_ci			goto error;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci		val->intval = value;
63862306a36Sopenharmony_ci		break;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
64162306a36Sopenharmony_ci		ret = sc27xx_fgu_get_charge_vol(data, &value);
64262306a36Sopenharmony_ci		if (ret)
64362306a36Sopenharmony_ci			goto error;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci		val->intval = value;
64662306a36Sopenharmony_ci		break;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CURRENT_AVG:
64962306a36Sopenharmony_ci		ret = sc27xx_fgu_get_current(data, &value);
65062306a36Sopenharmony_ci		if (ret)
65162306a36Sopenharmony_ci			goto error;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci		val->intval = value * 1000;
65462306a36Sopenharmony_ci		break;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
65762306a36Sopenharmony_ci		val->intval = data->total_cap * 1000;
65862306a36Sopenharmony_ci		break;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CHARGE_NOW:
66162306a36Sopenharmony_ci		ret = sc27xx_fgu_get_clbcnt(data, &value);
66262306a36Sopenharmony_ci		if (ret)
66362306a36Sopenharmony_ci			goto error;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci		value = DIV_ROUND_CLOSEST(value * 10,
66662306a36Sopenharmony_ci					  36 * SC27XX_FGU_SAMPLE_HZ);
66762306a36Sopenharmony_ci		val->intval = sc27xx_fgu_adc_to_current(data, value);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci		break;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
67262306a36Sopenharmony_ci		ret = sc27xx_fgu_get_vol_now(data, &value);
67362306a36Sopenharmony_ci		if (ret)
67462306a36Sopenharmony_ci			goto error;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci		val->intval = value * 1000;
67762306a36Sopenharmony_ci		break;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CURRENT_NOW:
68062306a36Sopenharmony_ci		ret = sc27xx_fgu_get_cur_now(data, &value);
68162306a36Sopenharmony_ci		if (ret)
68262306a36Sopenharmony_ci			goto error;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci		val->intval = value * 1000;
68562306a36Sopenharmony_ci		break;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_BOOT:
68862306a36Sopenharmony_ci		val->intval = data->boot_volt;
68962306a36Sopenharmony_ci		break;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	default:
69262306a36Sopenharmony_ci		ret = -EINVAL;
69362306a36Sopenharmony_ci		break;
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_cierror:
69762306a36Sopenharmony_ci	mutex_unlock(&data->lock);
69862306a36Sopenharmony_ci	return ret;
69962306a36Sopenharmony_ci}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_cistatic int sc27xx_fgu_set_property(struct power_supply *psy,
70262306a36Sopenharmony_ci				   enum power_supply_property psp,
70362306a36Sopenharmony_ci				   const union power_supply_propval *val)
70462306a36Sopenharmony_ci{
70562306a36Sopenharmony_ci	struct sc27xx_fgu_data *data = power_supply_get_drvdata(psy);
70662306a36Sopenharmony_ci	int ret;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	mutex_lock(&data->lock);
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	switch (psp) {
71162306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CAPACITY:
71262306a36Sopenharmony_ci		ret = sc27xx_fgu_save_last_cap(data, val->intval);
71362306a36Sopenharmony_ci		if (ret < 0)
71462306a36Sopenharmony_ci			dev_err(data->dev, "failed to save battery capacity\n");
71562306a36Sopenharmony_ci		break;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CALIBRATE:
71862306a36Sopenharmony_ci		sc27xx_fgu_adjust_cap(data, val->intval);
71962306a36Sopenharmony_ci		ret = 0;
72062306a36Sopenharmony_ci		break;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
72362306a36Sopenharmony_ci		data->total_cap = val->intval / 1000;
72462306a36Sopenharmony_ci		ret = 0;
72562306a36Sopenharmony_ci		break;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	default:
72862306a36Sopenharmony_ci		ret = -EINVAL;
72962306a36Sopenharmony_ci	}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	mutex_unlock(&data->lock);
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	return ret;
73462306a36Sopenharmony_ci}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_cistatic int sc27xx_fgu_property_is_writeable(struct power_supply *psy,
73762306a36Sopenharmony_ci					    enum power_supply_property psp)
73862306a36Sopenharmony_ci{
73962306a36Sopenharmony_ci	return psp == POWER_SUPPLY_PROP_CAPACITY ||
74062306a36Sopenharmony_ci		psp == POWER_SUPPLY_PROP_CALIBRATE ||
74162306a36Sopenharmony_ci		psp == POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
74262306a36Sopenharmony_ci}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_cistatic enum power_supply_property sc27xx_fgu_props[] = {
74562306a36Sopenharmony_ci	POWER_SUPPLY_PROP_STATUS,
74662306a36Sopenharmony_ci	POWER_SUPPLY_PROP_HEALTH,
74762306a36Sopenharmony_ci	POWER_SUPPLY_PROP_PRESENT,
74862306a36Sopenharmony_ci	POWER_SUPPLY_PROP_TEMP,
74962306a36Sopenharmony_ci	POWER_SUPPLY_PROP_TECHNOLOGY,
75062306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CAPACITY,
75162306a36Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_NOW,
75262306a36Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_OCV,
75362306a36Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_AVG,
75462306a36Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_BOOT,
75562306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CURRENT_NOW,
75662306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CURRENT_AVG,
75762306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
75862306a36Sopenharmony_ci	POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
75962306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CALIBRATE,
76062306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CHARGE_NOW
76162306a36Sopenharmony_ci};
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_cistatic const struct power_supply_desc sc27xx_fgu_desc = {
76462306a36Sopenharmony_ci	.name			= "sc27xx-fgu",
76562306a36Sopenharmony_ci	.type			= POWER_SUPPLY_TYPE_BATTERY,
76662306a36Sopenharmony_ci	.properties		= sc27xx_fgu_props,
76762306a36Sopenharmony_ci	.num_properties		= ARRAY_SIZE(sc27xx_fgu_props),
76862306a36Sopenharmony_ci	.get_property		= sc27xx_fgu_get_property,
76962306a36Sopenharmony_ci	.set_property		= sc27xx_fgu_set_property,
77062306a36Sopenharmony_ci	.external_power_changed	= power_supply_changed,
77162306a36Sopenharmony_ci	.property_is_writeable	= sc27xx_fgu_property_is_writeable,
77262306a36Sopenharmony_ci	.no_thermal		= true,
77362306a36Sopenharmony_ci};
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_cistatic void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap)
77662306a36Sopenharmony_ci{
77762306a36Sopenharmony_ci	int ret;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	data->init_cap = cap;
78062306a36Sopenharmony_ci	ret = sc27xx_fgu_get_clbcnt(data, &data->init_clbcnt);
78162306a36Sopenharmony_ci	if (ret)
78262306a36Sopenharmony_ci		dev_err(data->dev, "failed to get init coulomb counter\n");
78362306a36Sopenharmony_ci}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_cistatic void sc27xx_fgu_capacity_calibration(struct sc27xx_fgu_data *data,
78662306a36Sopenharmony_ci					    int cap, bool int_mode)
78762306a36Sopenharmony_ci{
78862306a36Sopenharmony_ci	int ret, ocv, chg_sts, adc;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	ret = sc27xx_fgu_get_vbat_ocv(data, &ocv);
79162306a36Sopenharmony_ci	if (ret) {
79262306a36Sopenharmony_ci		dev_err(data->dev, "get battery ocv error.\n");
79362306a36Sopenharmony_ci		return;
79462306a36Sopenharmony_ci	}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	ret = sc27xx_fgu_get_status(data, &chg_sts);
79762306a36Sopenharmony_ci	if (ret) {
79862306a36Sopenharmony_ci		dev_err(data->dev, "get charger status error.\n");
79962306a36Sopenharmony_ci		return;
80062306a36Sopenharmony_ci	}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	/*
80362306a36Sopenharmony_ci	 * If we are in charging mode, then we do not need to calibrate the
80462306a36Sopenharmony_ci	 * lower capacity.
80562306a36Sopenharmony_ci	 */
80662306a36Sopenharmony_ci	if (chg_sts == POWER_SUPPLY_STATUS_CHARGING)
80762306a36Sopenharmony_ci		return;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	if ((ocv > data->cap_table[0].ocv && cap < 100) || cap > 100) {
81062306a36Sopenharmony_ci		/*
81162306a36Sopenharmony_ci		 * If current OCV value is larger than the max OCV value in
81262306a36Sopenharmony_ci		 * OCV table, or the current capacity is larger than 100,
81362306a36Sopenharmony_ci		 * we should force the inititial capacity to 100.
81462306a36Sopenharmony_ci		 */
81562306a36Sopenharmony_ci		sc27xx_fgu_adjust_cap(data, 100);
81662306a36Sopenharmony_ci	} else if (ocv <= data->cap_table[data->table_len - 1].ocv) {
81762306a36Sopenharmony_ci		/*
81862306a36Sopenharmony_ci		 * If current OCV value is leass than the minimum OCV value in
81962306a36Sopenharmony_ci		 * OCV table, we should force the inititial capacity to 0.
82062306a36Sopenharmony_ci		 */
82162306a36Sopenharmony_ci		sc27xx_fgu_adjust_cap(data, 0);
82262306a36Sopenharmony_ci	} else if ((ocv > data->cap_table[data->table_len - 1].ocv && cap <= 0) ||
82362306a36Sopenharmony_ci		   (ocv > data->min_volt && cap <= data->alarm_cap)) {
82462306a36Sopenharmony_ci		/*
82562306a36Sopenharmony_ci		 * If current OCV value is not matchable with current capacity,
82662306a36Sopenharmony_ci		 * we should re-calculate current capacity by looking up the
82762306a36Sopenharmony_ci		 * OCV table.
82862306a36Sopenharmony_ci		 */
82962306a36Sopenharmony_ci		int cur_cap = power_supply_ocv2cap_simple(data->cap_table,
83062306a36Sopenharmony_ci							  data->table_len, ocv);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci		sc27xx_fgu_adjust_cap(data, cur_cap);
83362306a36Sopenharmony_ci	} else if (ocv <= data->min_volt) {
83462306a36Sopenharmony_ci		/*
83562306a36Sopenharmony_ci		 * If current OCV value is less than the low alarm voltage, but
83662306a36Sopenharmony_ci		 * current capacity is larger than the alarm capacity, we should
83762306a36Sopenharmony_ci		 * adjust the inititial capacity to alarm capacity.
83862306a36Sopenharmony_ci		 */
83962306a36Sopenharmony_ci		if (cap > data->alarm_cap) {
84062306a36Sopenharmony_ci			sc27xx_fgu_adjust_cap(data, data->alarm_cap);
84162306a36Sopenharmony_ci		} else {
84262306a36Sopenharmony_ci			int cur_cap;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci			/*
84562306a36Sopenharmony_ci			 * If current capacity is equal with 0 or less than 0
84662306a36Sopenharmony_ci			 * (some error occurs), we should adjust inititial
84762306a36Sopenharmony_ci			 * capacity to the capacity corresponding to current OCV
84862306a36Sopenharmony_ci			 * value.
84962306a36Sopenharmony_ci			 */
85062306a36Sopenharmony_ci			cur_cap = power_supply_ocv2cap_simple(data->cap_table,
85162306a36Sopenharmony_ci							      data->table_len,
85262306a36Sopenharmony_ci							      ocv);
85362306a36Sopenharmony_ci			sc27xx_fgu_adjust_cap(data, cur_cap);
85462306a36Sopenharmony_ci		}
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci		if (!int_mode)
85762306a36Sopenharmony_ci			return;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci		/*
86062306a36Sopenharmony_ci		 * After adjusting the battery capacity, we should set the
86162306a36Sopenharmony_ci		 * lowest alarm voltage instead.
86262306a36Sopenharmony_ci		 */
86362306a36Sopenharmony_ci		data->min_volt = data->cap_table[data->table_len - 1].ocv;
86462306a36Sopenharmony_ci		data->alarm_cap = power_supply_ocv2cap_simple(data->cap_table,
86562306a36Sopenharmony_ci							      data->table_len,
86662306a36Sopenharmony_ci							      data->min_volt);
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci		adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000);
86962306a36Sopenharmony_ci		regmap_update_bits(data->regmap,
87062306a36Sopenharmony_ci				   data->base + SC27XX_FGU_LOW_OVERLOAD,
87162306a36Sopenharmony_ci				   SC27XX_FGU_LOW_OVERLOAD_MASK, adc);
87262306a36Sopenharmony_ci	}
87362306a36Sopenharmony_ci}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_cistatic irqreturn_t sc27xx_fgu_interrupt(int irq, void *dev_id)
87662306a36Sopenharmony_ci{
87762306a36Sopenharmony_ci	struct sc27xx_fgu_data *data = dev_id;
87862306a36Sopenharmony_ci	int ret, cap;
87962306a36Sopenharmony_ci	u32 status;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	mutex_lock(&data->lock);
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	ret = regmap_read(data->regmap, data->base + SC27XX_FGU_INT_STS,
88462306a36Sopenharmony_ci			  &status);
88562306a36Sopenharmony_ci	if (ret)
88662306a36Sopenharmony_ci		goto out;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR,
88962306a36Sopenharmony_ci				 status, status);
89062306a36Sopenharmony_ci	if (ret)
89162306a36Sopenharmony_ci		goto out;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	/*
89462306a36Sopenharmony_ci	 * When low overload voltage interrupt happens, we should calibrate the
89562306a36Sopenharmony_ci	 * battery capacity in lower voltage stage.
89662306a36Sopenharmony_ci	 */
89762306a36Sopenharmony_ci	if (!(status & SC27XX_FGU_LOW_OVERLOAD_INT))
89862306a36Sopenharmony_ci		goto out;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	ret = sc27xx_fgu_get_capacity(data, &cap);
90162306a36Sopenharmony_ci	if (ret)
90262306a36Sopenharmony_ci		goto out;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	sc27xx_fgu_capacity_calibration(data, cap, true);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ciout:
90762306a36Sopenharmony_ci	mutex_unlock(&data->lock);
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	power_supply_changed(data->battery);
91062306a36Sopenharmony_ci	return IRQ_HANDLED;
91162306a36Sopenharmony_ci}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_cistatic irqreturn_t sc27xx_fgu_bat_detection(int irq, void *dev_id)
91462306a36Sopenharmony_ci{
91562306a36Sopenharmony_ci	struct sc27xx_fgu_data *data = dev_id;
91662306a36Sopenharmony_ci	int state;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	mutex_lock(&data->lock);
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	state = gpiod_get_value_cansleep(data->gpiod);
92162306a36Sopenharmony_ci	if (state < 0) {
92262306a36Sopenharmony_ci		dev_err(data->dev, "failed to get gpio state\n");
92362306a36Sopenharmony_ci		mutex_unlock(&data->lock);
92462306a36Sopenharmony_ci		return IRQ_RETVAL(state);
92562306a36Sopenharmony_ci	}
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	data->bat_present = !!state;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	mutex_unlock(&data->lock);
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	power_supply_changed(data->battery);
93262306a36Sopenharmony_ci	return IRQ_HANDLED;
93362306a36Sopenharmony_ci}
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_cistatic void sc27xx_fgu_disable(void *_data)
93662306a36Sopenharmony_ci{
93762306a36Sopenharmony_ci	struct sc27xx_fgu_data *data = _data;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0);
94062306a36Sopenharmony_ci	regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0);
94162306a36Sopenharmony_ci}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_cistatic int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity)
94462306a36Sopenharmony_ci{
94562306a36Sopenharmony_ci	/*
94662306a36Sopenharmony_ci	 * Get current capacity (mAh) = battery total capacity (mAh) *
94762306a36Sopenharmony_ci	 * current capacity percent (capacity / 100).
94862306a36Sopenharmony_ci	 */
94962306a36Sopenharmony_ci	int cur_cap = DIV_ROUND_CLOSEST(data->total_cap * capacity, 100);
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	/*
95262306a36Sopenharmony_ci	 * Convert current capacity (mAh) to coulomb counter according to the
95362306a36Sopenharmony_ci	 * formula: 1 mAh =3.6 coulomb.
95462306a36Sopenharmony_ci	 */
95562306a36Sopenharmony_ci	return DIV_ROUND_CLOSEST(cur_cap * 36 * data->cur_1000ma_adc * SC27XX_FGU_SAMPLE_HZ, 10);
95662306a36Sopenharmony_ci}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_cistatic int sc27xx_fgu_calibration(struct sc27xx_fgu_data *data)
95962306a36Sopenharmony_ci{
96062306a36Sopenharmony_ci	struct nvmem_cell *cell;
96162306a36Sopenharmony_ci	int calib_data, cal_4200mv;
96262306a36Sopenharmony_ci	void *buf;
96362306a36Sopenharmony_ci	size_t len;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	cell = nvmem_cell_get(data->dev, "fgu_calib");
96662306a36Sopenharmony_ci	if (IS_ERR(cell))
96762306a36Sopenharmony_ci		return PTR_ERR(cell);
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	buf = nvmem_cell_read(cell, &len);
97062306a36Sopenharmony_ci	nvmem_cell_put(cell);
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	if (IS_ERR(buf))
97362306a36Sopenharmony_ci		return PTR_ERR(buf);
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	memcpy(&calib_data, buf, min(len, sizeof(u32)));
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	/*
97862306a36Sopenharmony_ci	 * Get the ADC value corresponding to 4200 mV from eFuse controller
97962306a36Sopenharmony_ci	 * according to below formula. Then convert to ADC values corresponding
98062306a36Sopenharmony_ci	 * to 1000 mV and 1000 mA.
98162306a36Sopenharmony_ci	 */
98262306a36Sopenharmony_ci	cal_4200mv = (calib_data & 0x1ff) + 6963 - 4096 - 256;
98362306a36Sopenharmony_ci	data->vol_1000mv_adc = DIV_ROUND_CLOSEST(cal_4200mv * 10, 42);
98462306a36Sopenharmony_ci	data->cur_1000ma_adc =
98562306a36Sopenharmony_ci		DIV_ROUND_CLOSEST(data->vol_1000mv_adc * 4 * data->calib_resist,
98662306a36Sopenharmony_ci				  SC27XX_FGU_IDEAL_RESISTANCE);
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	kfree(buf);
98962306a36Sopenharmony_ci	return 0;
99062306a36Sopenharmony_ci}
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_cistatic int sc27xx_fgu_hw_init(struct sc27xx_fgu_data *data)
99362306a36Sopenharmony_ci{
99462306a36Sopenharmony_ci	struct power_supply_battery_info *info;
99562306a36Sopenharmony_ci	struct power_supply_battery_ocv_table *table;
99662306a36Sopenharmony_ci	int ret, delta_clbcnt, alarm_adc;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	ret = power_supply_get_battery_info(data->battery, &info);
99962306a36Sopenharmony_ci	if (ret) {
100062306a36Sopenharmony_ci		dev_err(data->dev, "failed to get battery information\n");
100162306a36Sopenharmony_ci		return ret;
100262306a36Sopenharmony_ci	}
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	data->total_cap = info->charge_full_design_uah / 1000;
100562306a36Sopenharmony_ci	data->max_volt = info->constant_charge_voltage_max_uv / 1000;
100662306a36Sopenharmony_ci	data->internal_resist = info->factory_internal_resistance_uohm / 1000;
100762306a36Sopenharmony_ci	data->min_volt = info->voltage_min_design_uv;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	/*
101062306a36Sopenharmony_ci	 * For SC27XX fuel gauge device, we only use one ocv-capacity
101162306a36Sopenharmony_ci	 * table in normal temperature 20 Celsius.
101262306a36Sopenharmony_ci	 */
101362306a36Sopenharmony_ci	table = power_supply_find_ocv2cap_table(info, 20, &data->table_len);
101462306a36Sopenharmony_ci	if (!table)
101562306a36Sopenharmony_ci		return -EINVAL;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	data->cap_table = devm_kmemdup(data->dev, table,
101862306a36Sopenharmony_ci				       data->table_len * sizeof(*table),
101962306a36Sopenharmony_ci				       GFP_KERNEL);
102062306a36Sopenharmony_ci	if (!data->cap_table) {
102162306a36Sopenharmony_ci		power_supply_put_battery_info(data->battery, info);
102262306a36Sopenharmony_ci		return -ENOMEM;
102362306a36Sopenharmony_ci	}
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	data->alarm_cap = power_supply_ocv2cap_simple(data->cap_table,
102662306a36Sopenharmony_ci						      data->table_len,
102762306a36Sopenharmony_ci						      data->min_volt);
102862306a36Sopenharmony_ci	if (!data->alarm_cap)
102962306a36Sopenharmony_ci		data->alarm_cap += 1;
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	data->resist_table_len = info->resist_table_size;
103262306a36Sopenharmony_ci	if (data->resist_table_len > 0) {
103362306a36Sopenharmony_ci		data->resist_table = devm_kmemdup(data->dev, info->resist_table,
103462306a36Sopenharmony_ci						  data->resist_table_len *
103562306a36Sopenharmony_ci						  sizeof(struct power_supply_resistance_temp_table),
103662306a36Sopenharmony_ci						  GFP_KERNEL);
103762306a36Sopenharmony_ci		if (!data->resist_table) {
103862306a36Sopenharmony_ci			power_supply_put_battery_info(data->battery, info);
103962306a36Sopenharmony_ci			return -ENOMEM;
104062306a36Sopenharmony_ci		}
104162306a36Sopenharmony_ci	}
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	power_supply_put_battery_info(data->battery, info);
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	ret = sc27xx_fgu_calibration(data);
104662306a36Sopenharmony_ci	if (ret)
104762306a36Sopenharmony_ci		return ret;
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	/* Enable the FGU module */
105062306a36Sopenharmony_ci	ret = regmap_update_bits(data->regmap, SC27XX_MODULE_EN0,
105162306a36Sopenharmony_ci				 SC27XX_FGU_EN, SC27XX_FGU_EN);
105262306a36Sopenharmony_ci	if (ret) {
105362306a36Sopenharmony_ci		dev_err(data->dev, "failed to enable fgu\n");
105462306a36Sopenharmony_ci		return ret;
105562306a36Sopenharmony_ci	}
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	/* Enable the FGU RTC clock to make it work */
105862306a36Sopenharmony_ci	ret = regmap_update_bits(data->regmap, SC27XX_CLK_EN0,
105962306a36Sopenharmony_ci				 SC27XX_FGU_RTC_EN, SC27XX_FGU_RTC_EN);
106062306a36Sopenharmony_ci	if (ret) {
106162306a36Sopenharmony_ci		dev_err(data->dev, "failed to enable fgu RTC clock\n");
106262306a36Sopenharmony_ci		goto disable_fgu;
106362306a36Sopenharmony_ci	}
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_CLR,
106662306a36Sopenharmony_ci				 SC27XX_FGU_INT_MASK, SC27XX_FGU_INT_MASK);
106762306a36Sopenharmony_ci	if (ret) {
106862306a36Sopenharmony_ci		dev_err(data->dev, "failed to clear interrupt status\n");
106962306a36Sopenharmony_ci		goto disable_clk;
107062306a36Sopenharmony_ci	}
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	/*
107362306a36Sopenharmony_ci	 * Set the voltage low overload threshold, which means when the battery
107462306a36Sopenharmony_ci	 * voltage is lower than this threshold, the controller will generate
107562306a36Sopenharmony_ci	 * one interrupt to notify.
107662306a36Sopenharmony_ci	 */
107762306a36Sopenharmony_ci	alarm_adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000);
107862306a36Sopenharmony_ci	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_LOW_OVERLOAD,
107962306a36Sopenharmony_ci				 SC27XX_FGU_LOW_OVERLOAD_MASK, alarm_adc);
108062306a36Sopenharmony_ci	if (ret) {
108162306a36Sopenharmony_ci		dev_err(data->dev, "failed to set fgu low overload\n");
108262306a36Sopenharmony_ci		goto disable_clk;
108362306a36Sopenharmony_ci	}
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	/*
108662306a36Sopenharmony_ci	 * Set the coulomb counter delta threshold, that means when the coulomb
108762306a36Sopenharmony_ci	 * counter change is multiples of the delta threshold, the controller
108862306a36Sopenharmony_ci	 * will generate one interrupt to notify the users to update the battery
108962306a36Sopenharmony_ci	 * capacity. Now we set the delta threshold as a counter value of 1%
109062306a36Sopenharmony_ci	 * capacity.
109162306a36Sopenharmony_ci	 */
109262306a36Sopenharmony_ci	delta_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, 1);
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTL,
109562306a36Sopenharmony_ci				 SC27XX_FGU_CLBCNT_MASK, delta_clbcnt);
109662306a36Sopenharmony_ci	if (ret) {
109762306a36Sopenharmony_ci		dev_err(data->dev, "failed to set low delta coulomb counter\n");
109862306a36Sopenharmony_ci		goto disable_clk;
109962306a36Sopenharmony_ci	}
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_CLBCNT_DELTH,
110262306a36Sopenharmony_ci				 SC27XX_FGU_CLBCNT_MASK,
110362306a36Sopenharmony_ci				 delta_clbcnt >> SC27XX_FGU_CLBCNT_SHIFT);
110462306a36Sopenharmony_ci	if (ret) {
110562306a36Sopenharmony_ci		dev_err(data->dev, "failed to set high delta coulomb counter\n");
110662306a36Sopenharmony_ci		goto disable_clk;
110762306a36Sopenharmony_ci	}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	/*
111062306a36Sopenharmony_ci	 * Get the boot battery capacity when system powers on, which is used to
111162306a36Sopenharmony_ci	 * initialize the coulomb counter. After that, we can read the coulomb
111262306a36Sopenharmony_ci	 * counter to measure the battery capacity.
111362306a36Sopenharmony_ci	 */
111462306a36Sopenharmony_ci	ret = sc27xx_fgu_get_boot_capacity(data, &data->init_cap);
111562306a36Sopenharmony_ci	if (ret) {
111662306a36Sopenharmony_ci		dev_err(data->dev, "failed to get boot capacity\n");
111762306a36Sopenharmony_ci		goto disable_clk;
111862306a36Sopenharmony_ci	}
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	/*
112162306a36Sopenharmony_ci	 * Convert battery capacity to the corresponding initial coulomb counter
112262306a36Sopenharmony_ci	 * and set into coulomb counter registers.
112362306a36Sopenharmony_ci	 */
112462306a36Sopenharmony_ci	data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap);
112562306a36Sopenharmony_ci	ret = sc27xx_fgu_set_clbcnt(data, data->init_clbcnt);
112662306a36Sopenharmony_ci	if (ret) {
112762306a36Sopenharmony_ci		dev_err(data->dev, "failed to initialize coulomb counter\n");
112862306a36Sopenharmony_ci		goto disable_clk;
112962306a36Sopenharmony_ci	}
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	return 0;
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_cidisable_clk:
113462306a36Sopenharmony_ci	regmap_update_bits(data->regmap, SC27XX_CLK_EN0, SC27XX_FGU_RTC_EN, 0);
113562306a36Sopenharmony_cidisable_fgu:
113662306a36Sopenharmony_ci	regmap_update_bits(data->regmap, SC27XX_MODULE_EN0, SC27XX_FGU_EN, 0);
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	return ret;
113962306a36Sopenharmony_ci}
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_cistatic int sc27xx_fgu_probe(struct platform_device *pdev)
114262306a36Sopenharmony_ci{
114362306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
114462306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
114562306a36Sopenharmony_ci	struct power_supply_config fgu_cfg = { };
114662306a36Sopenharmony_ci	struct sc27xx_fgu_data *data;
114762306a36Sopenharmony_ci	int ret, irq;
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
115062306a36Sopenharmony_ci	if (!data)
115162306a36Sopenharmony_ci		return -ENOMEM;
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	data->regmap = dev_get_regmap(dev->parent, NULL);
115462306a36Sopenharmony_ci	if (!data->regmap) {
115562306a36Sopenharmony_ci		dev_err(dev, "failed to get regmap\n");
115662306a36Sopenharmony_ci		return -ENODEV;
115762306a36Sopenharmony_ci	}
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	ret = device_property_read_u32(dev, "reg", &data->base);
116062306a36Sopenharmony_ci	if (ret) {
116162306a36Sopenharmony_ci		dev_err(dev, "failed to get fgu address\n");
116262306a36Sopenharmony_ci		return ret;
116362306a36Sopenharmony_ci	}
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	ret = device_property_read_u32(&pdev->dev,
116662306a36Sopenharmony_ci				       "sprd,calib-resistance-micro-ohms",
116762306a36Sopenharmony_ci				       &data->calib_resist);
116862306a36Sopenharmony_ci	if (ret) {
116962306a36Sopenharmony_ci		dev_err(&pdev->dev,
117062306a36Sopenharmony_ci			"failed to get fgu calibration resistance\n");
117162306a36Sopenharmony_ci		return ret;
117262306a36Sopenharmony_ci	}
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	data->channel = devm_iio_channel_get(dev, "bat-temp");
117562306a36Sopenharmony_ci	if (IS_ERR(data->channel)) {
117662306a36Sopenharmony_ci		dev_err(dev, "failed to get IIO channel\n");
117762306a36Sopenharmony_ci		return PTR_ERR(data->channel);
117862306a36Sopenharmony_ci	}
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	data->charge_chan = devm_iio_channel_get(dev, "charge-vol");
118162306a36Sopenharmony_ci	if (IS_ERR(data->charge_chan)) {
118262306a36Sopenharmony_ci		dev_err(dev, "failed to get charge IIO channel\n");
118362306a36Sopenharmony_ci		return PTR_ERR(data->charge_chan);
118462306a36Sopenharmony_ci	}
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	data->gpiod = devm_gpiod_get(dev, "bat-detect", GPIOD_IN);
118762306a36Sopenharmony_ci	if (IS_ERR(data->gpiod)) {
118862306a36Sopenharmony_ci		dev_err(dev, "failed to get battery detection GPIO\n");
118962306a36Sopenharmony_ci		return PTR_ERR(data->gpiod);
119062306a36Sopenharmony_ci	}
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	ret = gpiod_get_value_cansleep(data->gpiod);
119362306a36Sopenharmony_ci	if (ret < 0) {
119462306a36Sopenharmony_ci		dev_err(dev, "failed to get gpio state\n");
119562306a36Sopenharmony_ci		return ret;
119662306a36Sopenharmony_ci	}
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	data->bat_present = !!ret;
119962306a36Sopenharmony_ci	mutex_init(&data->lock);
120062306a36Sopenharmony_ci	data->dev = dev;
120162306a36Sopenharmony_ci	platform_set_drvdata(pdev, data);
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	fgu_cfg.drv_data = data;
120462306a36Sopenharmony_ci	fgu_cfg.of_node = np;
120562306a36Sopenharmony_ci	data->battery = devm_power_supply_register(dev, &sc27xx_fgu_desc,
120662306a36Sopenharmony_ci						   &fgu_cfg);
120762306a36Sopenharmony_ci	if (IS_ERR(data->battery)) {
120862306a36Sopenharmony_ci		dev_err(dev, "failed to register power supply\n");
120962306a36Sopenharmony_ci		return PTR_ERR(data->battery);
121062306a36Sopenharmony_ci	}
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	ret = sc27xx_fgu_hw_init(data);
121362306a36Sopenharmony_ci	if (ret) {
121462306a36Sopenharmony_ci		dev_err(dev, "failed to initialize fgu hardware\n");
121562306a36Sopenharmony_ci		return ret;
121662306a36Sopenharmony_ci	}
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	ret = devm_add_action_or_reset(dev, sc27xx_fgu_disable, data);
121962306a36Sopenharmony_ci	if (ret) {
122062306a36Sopenharmony_ci		dev_err(dev, "failed to add fgu disable action\n");
122162306a36Sopenharmony_ci		return ret;
122262306a36Sopenharmony_ci	}
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
122562306a36Sopenharmony_ci	if (irq < 0)
122662306a36Sopenharmony_ci		return irq;
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	ret = devm_request_threaded_irq(data->dev, irq, NULL,
122962306a36Sopenharmony_ci					sc27xx_fgu_interrupt,
123062306a36Sopenharmony_ci					IRQF_NO_SUSPEND | IRQF_ONESHOT,
123162306a36Sopenharmony_ci					pdev->name, data);
123262306a36Sopenharmony_ci	if (ret) {
123362306a36Sopenharmony_ci		dev_err(data->dev, "failed to request fgu IRQ\n");
123462306a36Sopenharmony_ci		return ret;
123562306a36Sopenharmony_ci	}
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	irq = gpiod_to_irq(data->gpiod);
123862306a36Sopenharmony_ci	if (irq < 0) {
123962306a36Sopenharmony_ci		dev_err(dev, "failed to translate GPIO to IRQ\n");
124062306a36Sopenharmony_ci		return irq;
124162306a36Sopenharmony_ci	}
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	ret = devm_request_threaded_irq(dev, irq, NULL,
124462306a36Sopenharmony_ci					sc27xx_fgu_bat_detection,
124562306a36Sopenharmony_ci					IRQF_ONESHOT | IRQF_TRIGGER_RISING |
124662306a36Sopenharmony_ci					IRQF_TRIGGER_FALLING,
124762306a36Sopenharmony_ci					pdev->name, data);
124862306a36Sopenharmony_ci	if (ret) {
124962306a36Sopenharmony_ci		dev_err(dev, "failed to request IRQ\n");
125062306a36Sopenharmony_ci		return ret;
125162306a36Sopenharmony_ci	}
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	return 0;
125462306a36Sopenharmony_ci}
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
125762306a36Sopenharmony_cistatic int sc27xx_fgu_resume(struct device *dev)
125862306a36Sopenharmony_ci{
125962306a36Sopenharmony_ci	struct sc27xx_fgu_data *data = dev_get_drvdata(dev);
126062306a36Sopenharmony_ci	int ret;
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN,
126362306a36Sopenharmony_ci				 SC27XX_FGU_LOW_OVERLOAD_INT |
126462306a36Sopenharmony_ci				 SC27XX_FGU_CLBCNT_DELTA_INT, 0);
126562306a36Sopenharmony_ci	if (ret) {
126662306a36Sopenharmony_ci		dev_err(data->dev, "failed to disable fgu interrupts\n");
126762306a36Sopenharmony_ci		return ret;
126862306a36Sopenharmony_ci	}
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	return 0;
127162306a36Sopenharmony_ci}
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_cistatic int sc27xx_fgu_suspend(struct device *dev)
127462306a36Sopenharmony_ci{
127562306a36Sopenharmony_ci	struct sc27xx_fgu_data *data = dev_get_drvdata(dev);
127662306a36Sopenharmony_ci	int ret, status, ocv;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	ret = sc27xx_fgu_get_status(data, &status);
127962306a36Sopenharmony_ci	if (ret)
128062306a36Sopenharmony_ci		return ret;
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	/*
128362306a36Sopenharmony_ci	 * If we are charging, then no need to enable the FGU interrupts to
128462306a36Sopenharmony_ci	 * adjust the battery capacity.
128562306a36Sopenharmony_ci	 */
128662306a36Sopenharmony_ci	if (status != POWER_SUPPLY_STATUS_NOT_CHARGING &&
128762306a36Sopenharmony_ci	    status != POWER_SUPPLY_STATUS_DISCHARGING)
128862306a36Sopenharmony_ci		return 0;
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	ret = regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN,
129162306a36Sopenharmony_ci				 SC27XX_FGU_LOW_OVERLOAD_INT,
129262306a36Sopenharmony_ci				 SC27XX_FGU_LOW_OVERLOAD_INT);
129362306a36Sopenharmony_ci	if (ret) {
129462306a36Sopenharmony_ci		dev_err(data->dev, "failed to enable low voltage interrupt\n");
129562306a36Sopenharmony_ci		return ret;
129662306a36Sopenharmony_ci	}
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	ret = sc27xx_fgu_get_vbat_ocv(data, &ocv);
129962306a36Sopenharmony_ci	if (ret)
130062306a36Sopenharmony_ci		goto disable_int;
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	/*
130362306a36Sopenharmony_ci	 * If current OCV is less than the minimum voltage, we should enable the
130462306a36Sopenharmony_ci	 * coulomb counter threshold interrupt to notify events to adjust the
130562306a36Sopenharmony_ci	 * battery capacity.
130662306a36Sopenharmony_ci	 */
130762306a36Sopenharmony_ci	if (ocv < data->min_volt) {
130862306a36Sopenharmony_ci		ret = regmap_update_bits(data->regmap,
130962306a36Sopenharmony_ci					 data->base + SC27XX_FGU_INT_EN,
131062306a36Sopenharmony_ci					 SC27XX_FGU_CLBCNT_DELTA_INT,
131162306a36Sopenharmony_ci					 SC27XX_FGU_CLBCNT_DELTA_INT);
131262306a36Sopenharmony_ci		if (ret) {
131362306a36Sopenharmony_ci			dev_err(data->dev,
131462306a36Sopenharmony_ci				"failed to enable coulomb threshold int\n");
131562306a36Sopenharmony_ci			goto disable_int;
131662306a36Sopenharmony_ci		}
131762306a36Sopenharmony_ci	}
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	return 0;
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_cidisable_int:
132262306a36Sopenharmony_ci	regmap_update_bits(data->regmap, data->base + SC27XX_FGU_INT_EN,
132362306a36Sopenharmony_ci			   SC27XX_FGU_LOW_OVERLOAD_INT, 0);
132462306a36Sopenharmony_ci	return ret;
132562306a36Sopenharmony_ci}
132662306a36Sopenharmony_ci#endif
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_cistatic const struct dev_pm_ops sc27xx_fgu_pm_ops = {
132962306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(sc27xx_fgu_suspend, sc27xx_fgu_resume)
133062306a36Sopenharmony_ci};
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_cistatic const struct of_device_id sc27xx_fgu_of_match[] = {
133362306a36Sopenharmony_ci	{ .compatible = "sprd,sc2731-fgu", },
133462306a36Sopenharmony_ci	{ }
133562306a36Sopenharmony_ci};
133662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sc27xx_fgu_of_match);
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_cistatic struct platform_driver sc27xx_fgu_driver = {
133962306a36Sopenharmony_ci	.probe = sc27xx_fgu_probe,
134062306a36Sopenharmony_ci	.driver = {
134162306a36Sopenharmony_ci		.name = "sc27xx-fgu",
134262306a36Sopenharmony_ci		.of_match_table = sc27xx_fgu_of_match,
134362306a36Sopenharmony_ci		.pm = &sc27xx_fgu_pm_ops,
134462306a36Sopenharmony_ci	}
134562306a36Sopenharmony_ci};
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_cimodule_platform_driver(sc27xx_fgu_driver);
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ciMODULE_DESCRIPTION("Spreadtrum SC27XX PMICs Fual Gauge Unit Driver");
135062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
1351