162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Battery driver for Marvell 88PM860x PMIC
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2012 Marvell International Ltd.
662306a36Sopenharmony_ci * Author:	Jett Zhou <jtzhou@marvell.com>
762306a36Sopenharmony_ci *		Haojian Zhuang <haojian.zhuang@marvell.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/platform_device.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <linux/mutex.h>
1562306a36Sopenharmony_ci#include <linux/string.h>
1662306a36Sopenharmony_ci#include <linux/power_supply.h>
1762306a36Sopenharmony_ci#include <linux/mfd/88pm860x.h>
1862306a36Sopenharmony_ci#include <linux/delay.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/* bit definitions of Status Query Interface 2 */
2162306a36Sopenharmony_ci#define STATUS2_CHG			(1 << 2)
2262306a36Sopenharmony_ci#define STATUS2_BAT			(1 << 3)
2362306a36Sopenharmony_ci#define STATUS2_VBUS			(1 << 4)
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/* bit definitions of Measurement Enable 1 Register */
2662306a36Sopenharmony_ci#define MEAS1_TINT			(1 << 3)
2762306a36Sopenharmony_ci#define MEAS1_GP1			(1 << 5)
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/* bit definitions of Measurement Enable 3 Register */
3062306a36Sopenharmony_ci#define MEAS3_IBAT			(1 << 0)
3162306a36Sopenharmony_ci#define MEAS3_BAT_DET			(1 << 1)
3262306a36Sopenharmony_ci#define MEAS3_CC			(1 << 2)
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* bit definitions of Measurement Off Time Register */
3562306a36Sopenharmony_ci#define MEAS_OFF_SLEEP_EN		(1 << 1)
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/* bit definitions of GPADC Bias Current 2 Register */
3862306a36Sopenharmony_ci#define GPBIAS2_GPADC1_SET		(2 << 4)
3962306a36Sopenharmony_ci/* GPADC1 Bias Current value in uA unit */
4062306a36Sopenharmony_ci#define GPBIAS2_GPADC1_UA		((GPBIAS2_GPADC1_SET >> 4) * 5 + 1)
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* bit definitions of GPADC Misc 1 Register */
4362306a36Sopenharmony_ci#define GPMISC1_GPADC_EN		(1 << 0)
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/* bit definitions of Charger Control 6 Register */
4662306a36Sopenharmony_ci#define CC6_BAT_DET_GPADC1		1
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* bit definitions of Coulomb Counter Reading Register */
4962306a36Sopenharmony_ci#define CCNT_AVG_SEL			(4 << 3)
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/* bit definitions of RTC miscellaneous Register1 */
5262306a36Sopenharmony_ci#define RTC_SOC_5LSB		(0x1F << 3)
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/* bit definitions of RTC Register1 */
5562306a36Sopenharmony_ci#define RTC_SOC_3MSB		(0x7)
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/* bit definitions of Power up Log register */
5862306a36Sopenharmony_ci#define BAT_WU_LOG			(1<<6)
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci/* coulomb counter index */
6162306a36Sopenharmony_ci#define CCNT_POS1			0
6262306a36Sopenharmony_ci#define CCNT_POS2			1
6362306a36Sopenharmony_ci#define CCNT_NEG1			2
6462306a36Sopenharmony_ci#define CCNT_NEG2			3
6562306a36Sopenharmony_ci#define CCNT_SPOS			4
6662306a36Sopenharmony_ci#define CCNT_SNEG			5
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/* OCV -- Open Circuit Voltage */
6962306a36Sopenharmony_ci#define OCV_MODE_ACTIVE			0
7062306a36Sopenharmony_ci#define OCV_MODE_SLEEP			1
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/* Vbat range of CC for measuring Rbat */
7362306a36Sopenharmony_ci#define LOW_BAT_THRESHOLD		3600
7462306a36Sopenharmony_ci#define VBATT_RESISTOR_MIN		3800
7562306a36Sopenharmony_ci#define VBATT_RESISTOR_MAX		4100
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/* TBAT for batt, TINT for chip itself */
7862306a36Sopenharmony_ci#define PM860X_TEMP_TINT		(0)
7962306a36Sopenharmony_ci#define PM860X_TEMP_TBAT		(1)
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci/*
8262306a36Sopenharmony_ci * Battery temperature based on NTC resistor, defined
8362306a36Sopenharmony_ci * corresponding resistor value  -- Ohm / C degeree.
8462306a36Sopenharmony_ci */
8562306a36Sopenharmony_ci#define TBAT_NEG_25D		127773	/* -25 */
8662306a36Sopenharmony_ci#define TBAT_NEG_10D		54564	/* -10 */
8762306a36Sopenharmony_ci#define TBAT_0D			32330	/* 0 */
8862306a36Sopenharmony_ci#define TBAT_10D		19785	/* 10 */
8962306a36Sopenharmony_ci#define TBAT_20D		12468	/* 20 */
9062306a36Sopenharmony_ci#define TBAT_30D		8072	/* 30 */
9162306a36Sopenharmony_ci#define TBAT_40D		5356	/* 40 */
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistruct pm860x_battery_info {
9462306a36Sopenharmony_ci	struct pm860x_chip *chip;
9562306a36Sopenharmony_ci	struct i2c_client *i2c;
9662306a36Sopenharmony_ci	struct device *dev;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	struct power_supply *battery;
9962306a36Sopenharmony_ci	struct mutex lock;
10062306a36Sopenharmony_ci	int status;
10162306a36Sopenharmony_ci	int irq_cc;
10262306a36Sopenharmony_ci	int irq_batt;
10362306a36Sopenharmony_ci	int max_capacity;
10462306a36Sopenharmony_ci	int resistor;		/* Battery Internal Resistor */
10562306a36Sopenharmony_ci	int last_capacity;
10662306a36Sopenharmony_ci	int start_soc;
10762306a36Sopenharmony_ci	unsigned present:1;
10862306a36Sopenharmony_ci	unsigned temp_type:1;	/* TINT or TBAT */
10962306a36Sopenharmony_ci};
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistruct ccnt {
11262306a36Sopenharmony_ci	unsigned long long pos;
11362306a36Sopenharmony_ci	unsigned long long neg;
11462306a36Sopenharmony_ci	unsigned int spos;
11562306a36Sopenharmony_ci	unsigned int sneg;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	int total_chg;		/* mAh(3.6C) */
11862306a36Sopenharmony_ci	int total_dischg;	/* mAh(3.6C) */
11962306a36Sopenharmony_ci};
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci/*
12262306a36Sopenharmony_ci * State of Charge.
12362306a36Sopenharmony_ci * The first number is mAh(=3.6C), and the second number is percent point.
12462306a36Sopenharmony_ci */
12562306a36Sopenharmony_cistatic int array_soc[][2] = {
12662306a36Sopenharmony_ci	{4170, 100}, {4154, 99}, {4136, 98}, {4122, 97}, {4107, 96},
12762306a36Sopenharmony_ci	{4102, 95}, {4088, 94}, {4081, 93}, {4070, 92}, {4060, 91},
12862306a36Sopenharmony_ci	{4053, 90}, {4044, 89}, {4035, 88}, {4028, 87}, {4019, 86},
12962306a36Sopenharmony_ci	{4013, 85}, {4006, 84}, {3995, 83}, {3987, 82}, {3982, 81},
13062306a36Sopenharmony_ci	{3976, 80}, {3968, 79}, {3962, 78}, {3954, 77}, {3946, 76},
13162306a36Sopenharmony_ci	{3941, 75}, {3934, 74}, {3929, 73}, {3922, 72}, {3916, 71},
13262306a36Sopenharmony_ci	{3910, 70}, {3904, 69}, {3898, 68}, {3892, 67}, {3887, 66},
13362306a36Sopenharmony_ci	{3880, 65}, {3874, 64}, {3868, 63}, {3862, 62}, {3854, 61},
13462306a36Sopenharmony_ci	{3849, 60}, {3843, 59}, {3840, 58}, {3833, 57}, {3829, 56},
13562306a36Sopenharmony_ci	{3824, 55}, {3818, 54}, {3815, 53}, {3810, 52}, {3808, 51},
13662306a36Sopenharmony_ci	{3804, 50}, {3801, 49}, {3798, 48}, {3796, 47}, {3792, 46},
13762306a36Sopenharmony_ci	{3789, 45}, {3785, 44}, {3784, 43}, {3782, 42}, {3780, 41},
13862306a36Sopenharmony_ci	{3777, 40}, {3776, 39}, {3774, 38}, {3772, 37}, {3771, 36},
13962306a36Sopenharmony_ci	{3769, 35}, {3768, 34}, {3764, 33}, {3763, 32}, {3760, 31},
14062306a36Sopenharmony_ci	{3760, 30}, {3754, 29}, {3750, 28}, {3749, 27}, {3744, 26},
14162306a36Sopenharmony_ci	{3740, 25}, {3734, 24}, {3732, 23}, {3728, 22}, {3726, 21},
14262306a36Sopenharmony_ci	{3720, 20}, {3716, 19}, {3709, 18}, {3703, 17}, {3698, 16},
14362306a36Sopenharmony_ci	{3692, 15}, {3683, 14}, {3675, 13}, {3670, 12}, {3665, 11},
14462306a36Sopenharmony_ci	{3661, 10}, {3649, 9}, {3637, 8}, {3622, 7}, {3609, 6},
14562306a36Sopenharmony_ci	{3580, 5}, {3558, 4}, {3540, 3}, {3510, 2}, {3429, 1},
14662306a36Sopenharmony_ci};
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic struct ccnt ccnt_data;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci/*
15162306a36Sopenharmony_ci * register 1 bit[7:0] -- bit[11:4] of measured value of voltage
15262306a36Sopenharmony_ci * register 0 bit[3:0] -- bit[3:0] of measured value of voltage
15362306a36Sopenharmony_ci */
15462306a36Sopenharmony_cistatic int measure_12bit_voltage(struct pm860x_battery_info *info,
15562306a36Sopenharmony_ci				 int offset, int *data)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	unsigned char buf[2];
15862306a36Sopenharmony_ci	int ret;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	ret = pm860x_bulk_read(info->i2c, offset, 2, buf);
16162306a36Sopenharmony_ci	if (ret < 0)
16262306a36Sopenharmony_ci		return ret;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	*data = ((buf[0] & 0xff) << 4) | (buf[1] & 0x0f);
16562306a36Sopenharmony_ci	/* V_MEAS(mV) = data * 1.8 * 1000 / (2^12) */
16662306a36Sopenharmony_ci	*data = ((*data & 0xfff) * 9 * 25) >> 9;
16762306a36Sopenharmony_ci	return 0;
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic int measure_vbatt(struct pm860x_battery_info *info, int state,
17162306a36Sopenharmony_ci			 int *data)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	unsigned char buf[5];
17462306a36Sopenharmony_ci	int ret;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	switch (state) {
17762306a36Sopenharmony_ci	case OCV_MODE_ACTIVE:
17862306a36Sopenharmony_ci		ret = measure_12bit_voltage(info, PM8607_VBAT_MEAS1, data);
17962306a36Sopenharmony_ci		if (ret)
18062306a36Sopenharmony_ci			return ret;
18162306a36Sopenharmony_ci		/* V_BATT_MEAS(mV) = value * 3 * 1.8 * 1000 / (2^12) */
18262306a36Sopenharmony_ci		*data *= 3;
18362306a36Sopenharmony_ci		break;
18462306a36Sopenharmony_ci	case OCV_MODE_SLEEP:
18562306a36Sopenharmony_ci		/*
18662306a36Sopenharmony_ci		 * voltage value of VBATT in sleep mode is saved in different
18762306a36Sopenharmony_ci		 * registers.
18862306a36Sopenharmony_ci		 * bit[11:10] -- bit[7:6] of LDO9(0x18)
18962306a36Sopenharmony_ci		 * bit[9:8] -- bit[7:6] of LDO8(0x17)
19062306a36Sopenharmony_ci		 * bit[7:6] -- bit[7:6] of LDO7(0x16)
19162306a36Sopenharmony_ci		 * bit[5:4] -- bit[7:6] of LDO6(0x15)
19262306a36Sopenharmony_ci		 * bit[3:0] -- bit[7:4] of LDO5(0x14)
19362306a36Sopenharmony_ci		 */
19462306a36Sopenharmony_ci		ret = pm860x_bulk_read(info->i2c, PM8607_LDO5, 5, buf);
19562306a36Sopenharmony_ci		if (ret < 0)
19662306a36Sopenharmony_ci			return ret;
19762306a36Sopenharmony_ci		ret = ((buf[4] >> 6) << 10) | ((buf[3] >> 6) << 8)
19862306a36Sopenharmony_ci		    | ((buf[2] >> 6) << 6) | ((buf[1] >> 6) << 4)
19962306a36Sopenharmony_ci		    | (buf[0] >> 4);
20062306a36Sopenharmony_ci		/* V_BATT_MEAS(mV) = data * 3 * 1.8 * 1000 / (2^12) */
20162306a36Sopenharmony_ci		*data = ((*data & 0xff) * 27 * 25) >> 9;
20262306a36Sopenharmony_ci		break;
20362306a36Sopenharmony_ci	default:
20462306a36Sopenharmony_ci		return -EINVAL;
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci	return 0;
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci/*
21062306a36Sopenharmony_ci * Return value is signed data.
21162306a36Sopenharmony_ci * Negative value means discharging, and positive value means charging.
21262306a36Sopenharmony_ci */
21362306a36Sopenharmony_cistatic int measure_current(struct pm860x_battery_info *info, int *data)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	unsigned char buf[2];
21662306a36Sopenharmony_ci	short s;
21762306a36Sopenharmony_ci	int ret;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	ret = pm860x_bulk_read(info->i2c, PM8607_IBAT_MEAS1, 2, buf);
22062306a36Sopenharmony_ci	if (ret < 0)
22162306a36Sopenharmony_ci		return ret;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	s = ((buf[0] & 0xff) << 8) | (buf[1] & 0xff);
22462306a36Sopenharmony_ci	/* current(mA) = value * 0.125 */
22562306a36Sopenharmony_ci	*data = s >> 3;
22662306a36Sopenharmony_ci	return 0;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic int set_charger_current(struct pm860x_battery_info *info, int data,
23062306a36Sopenharmony_ci			       int *old)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	int ret;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (data < 50 || data > 1600 || !old)
23562306a36Sopenharmony_ci		return -EINVAL;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	data = ((data - 50) / 50) & 0x1f;
23862306a36Sopenharmony_ci	*old = pm860x_reg_read(info->i2c, PM8607_CHG_CTRL2);
23962306a36Sopenharmony_ci	*old = (*old & 0x1f) * 50 + 50;
24062306a36Sopenharmony_ci	ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL2, 0x1f, data);
24162306a36Sopenharmony_ci	if (ret < 0)
24262306a36Sopenharmony_ci		return ret;
24362306a36Sopenharmony_ci	return 0;
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic int read_ccnt(struct pm860x_battery_info *info, int offset,
24762306a36Sopenharmony_ci		     int *ccnt)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	unsigned char buf[2];
25062306a36Sopenharmony_ci	int ret;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	ret = pm860x_set_bits(info->i2c, PM8607_CCNT, 7, offset & 7);
25362306a36Sopenharmony_ci	if (ret < 0)
25462306a36Sopenharmony_ci		goto out;
25562306a36Sopenharmony_ci	ret = pm860x_bulk_read(info->i2c, PM8607_CCNT_MEAS1, 2, buf);
25662306a36Sopenharmony_ci	if (ret < 0)
25762306a36Sopenharmony_ci		goto out;
25862306a36Sopenharmony_ci	*ccnt = ((buf[0] & 0xff) << 8) | (buf[1] & 0xff);
25962306a36Sopenharmony_ci	return 0;
26062306a36Sopenharmony_ciout:
26162306a36Sopenharmony_ci	return ret;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic int calc_ccnt(struct pm860x_battery_info *info, struct ccnt *ccnt)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	unsigned int sum;
26762306a36Sopenharmony_ci	int ret;
26862306a36Sopenharmony_ci	int data;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	ret = read_ccnt(info, CCNT_POS1, &data);
27162306a36Sopenharmony_ci	if (ret)
27262306a36Sopenharmony_ci		goto out;
27362306a36Sopenharmony_ci	sum = data & 0xffff;
27462306a36Sopenharmony_ci	ret = read_ccnt(info, CCNT_POS2, &data);
27562306a36Sopenharmony_ci	if (ret)
27662306a36Sopenharmony_ci		goto out;
27762306a36Sopenharmony_ci	sum |= (data & 0xffff) << 16;
27862306a36Sopenharmony_ci	ccnt->pos += sum;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	ret = read_ccnt(info, CCNT_NEG1, &data);
28162306a36Sopenharmony_ci	if (ret)
28262306a36Sopenharmony_ci		goto out;
28362306a36Sopenharmony_ci	sum = data & 0xffff;
28462306a36Sopenharmony_ci	ret = read_ccnt(info, CCNT_NEG2, &data);
28562306a36Sopenharmony_ci	if (ret)
28662306a36Sopenharmony_ci		goto out;
28762306a36Sopenharmony_ci	sum |= (data & 0xffff) << 16;
28862306a36Sopenharmony_ci	sum = ~sum + 1;		/* since it's negative */
28962306a36Sopenharmony_ci	ccnt->neg += sum;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	ret = read_ccnt(info, CCNT_SPOS, &data);
29262306a36Sopenharmony_ci	if (ret)
29362306a36Sopenharmony_ci		goto out;
29462306a36Sopenharmony_ci	ccnt->spos += data;
29562306a36Sopenharmony_ci	ret = read_ccnt(info, CCNT_SNEG, &data);
29662306a36Sopenharmony_ci	if (ret)
29762306a36Sopenharmony_ci		goto out;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	/*
30062306a36Sopenharmony_ci	 * charge(mAh)  = count * 1.6984 * 1e(-8)
30162306a36Sopenharmony_ci	 *              = count * 16984 * 1.024 * 1.024 * 1.024 / (2 ^ 40)
30262306a36Sopenharmony_ci	 *              = count * 18236 / (2 ^ 40)
30362306a36Sopenharmony_ci	 */
30462306a36Sopenharmony_ci	ccnt->total_chg = (int) ((ccnt->pos * 18236) >> 40);
30562306a36Sopenharmony_ci	ccnt->total_dischg = (int) ((ccnt->neg * 18236) >> 40);
30662306a36Sopenharmony_ci	return 0;
30762306a36Sopenharmony_ciout:
30862306a36Sopenharmony_ci	return ret;
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic int clear_ccnt(struct pm860x_battery_info *info, struct ccnt *ccnt)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	int data;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	memset(ccnt, 0, sizeof(*ccnt));
31662306a36Sopenharmony_ci	/* read to clear ccnt */
31762306a36Sopenharmony_ci	read_ccnt(info, CCNT_POS1, &data);
31862306a36Sopenharmony_ci	read_ccnt(info, CCNT_POS2, &data);
31962306a36Sopenharmony_ci	read_ccnt(info, CCNT_NEG1, &data);
32062306a36Sopenharmony_ci	read_ccnt(info, CCNT_NEG2, &data);
32162306a36Sopenharmony_ci	read_ccnt(info, CCNT_SPOS, &data);
32262306a36Sopenharmony_ci	read_ccnt(info, CCNT_SNEG, &data);
32362306a36Sopenharmony_ci	return 0;
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci/* Calculate Open Circuit Voltage */
32762306a36Sopenharmony_cistatic int calc_ocv(struct pm860x_battery_info *info, int *ocv)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	int ret;
33062306a36Sopenharmony_ci	int i;
33162306a36Sopenharmony_ci	int data;
33262306a36Sopenharmony_ci	int vbatt_avg;
33362306a36Sopenharmony_ci	int vbatt_sum;
33462306a36Sopenharmony_ci	int ibatt_avg;
33562306a36Sopenharmony_ci	int ibatt_sum;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	if (!ocv)
33862306a36Sopenharmony_ci		return -EINVAL;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	for (i = 0, ibatt_sum = 0, vbatt_sum = 0; i < 10; i++) {
34162306a36Sopenharmony_ci		ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data);
34262306a36Sopenharmony_ci		if (ret)
34362306a36Sopenharmony_ci			goto out;
34462306a36Sopenharmony_ci		vbatt_sum += data;
34562306a36Sopenharmony_ci		ret = measure_current(info, &data);
34662306a36Sopenharmony_ci		if (ret)
34762306a36Sopenharmony_ci			goto out;
34862306a36Sopenharmony_ci		ibatt_sum += data;
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci	vbatt_avg = vbatt_sum / 10;
35162306a36Sopenharmony_ci	ibatt_avg = ibatt_sum / 10;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	mutex_lock(&info->lock);
35462306a36Sopenharmony_ci	if (info->present)
35562306a36Sopenharmony_ci		*ocv = vbatt_avg - ibatt_avg * info->resistor / 1000;
35662306a36Sopenharmony_ci	else
35762306a36Sopenharmony_ci		*ocv = vbatt_avg;
35862306a36Sopenharmony_ci	mutex_unlock(&info->lock);
35962306a36Sopenharmony_ci	dev_dbg(info->dev, "VBAT average:%d, OCV:%d\n", vbatt_avg, *ocv);
36062306a36Sopenharmony_ci	return 0;
36162306a36Sopenharmony_ciout:
36262306a36Sopenharmony_ci	return ret;
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci/* Calculate State of Charge (percent points) */
36662306a36Sopenharmony_cistatic int calc_soc(struct pm860x_battery_info *info, int state, int *soc)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	int i;
36962306a36Sopenharmony_ci	int ocv;
37062306a36Sopenharmony_ci	int count;
37162306a36Sopenharmony_ci	int ret = -EINVAL;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	if (!soc)
37462306a36Sopenharmony_ci		return -EINVAL;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	switch (state) {
37762306a36Sopenharmony_ci	case OCV_MODE_ACTIVE:
37862306a36Sopenharmony_ci		ret = calc_ocv(info, &ocv);
37962306a36Sopenharmony_ci		break;
38062306a36Sopenharmony_ci	case OCV_MODE_SLEEP:
38162306a36Sopenharmony_ci		ret = measure_vbatt(info, OCV_MODE_SLEEP, &ocv);
38262306a36Sopenharmony_ci		break;
38362306a36Sopenharmony_ci	}
38462306a36Sopenharmony_ci	if (ret)
38562306a36Sopenharmony_ci		return ret;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	count = ARRAY_SIZE(array_soc);
38862306a36Sopenharmony_ci	if (ocv < array_soc[count - 1][0]) {
38962306a36Sopenharmony_ci		*soc = 0;
39062306a36Sopenharmony_ci		return 0;
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
39462306a36Sopenharmony_ci		if (ocv >= array_soc[i][0]) {
39562306a36Sopenharmony_ci			*soc = array_soc[i][1];
39662306a36Sopenharmony_ci			break;
39762306a36Sopenharmony_ci		}
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ci	return 0;
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistatic irqreturn_t pm860x_coulomb_handler(int irq, void *data)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	struct pm860x_battery_info *info = data;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	calc_ccnt(info, &ccnt_data);
40762306a36Sopenharmony_ci	return IRQ_HANDLED;
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_cistatic irqreturn_t pm860x_batt_handler(int irq, void *data)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	struct pm860x_battery_info *info = data;
41362306a36Sopenharmony_ci	int ret;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	mutex_lock(&info->lock);
41662306a36Sopenharmony_ci	ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
41762306a36Sopenharmony_ci	if (ret & STATUS2_BAT) {
41862306a36Sopenharmony_ci		info->present = 1;
41962306a36Sopenharmony_ci		info->temp_type = PM860X_TEMP_TBAT;
42062306a36Sopenharmony_ci	} else {
42162306a36Sopenharmony_ci		info->present = 0;
42262306a36Sopenharmony_ci		info->temp_type = PM860X_TEMP_TINT;
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci	mutex_unlock(&info->lock);
42562306a36Sopenharmony_ci	/* clear ccnt since battery is attached or dettached */
42662306a36Sopenharmony_ci	clear_ccnt(info, &ccnt_data);
42762306a36Sopenharmony_ci	return IRQ_HANDLED;
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_cistatic void pm860x_init_battery(struct pm860x_battery_info *info)
43162306a36Sopenharmony_ci{
43262306a36Sopenharmony_ci	unsigned char buf[2];
43362306a36Sopenharmony_ci	int ret;
43462306a36Sopenharmony_ci	int data;
43562306a36Sopenharmony_ci	int bat_remove;
43662306a36Sopenharmony_ci	int soc = 0;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	/* measure enable on GPADC1 */
43962306a36Sopenharmony_ci	data = MEAS1_GP1;
44062306a36Sopenharmony_ci	if (info->temp_type == PM860X_TEMP_TINT)
44162306a36Sopenharmony_ci		data |= MEAS1_TINT;
44262306a36Sopenharmony_ci	ret = pm860x_set_bits(info->i2c, PM8607_MEAS_EN1, data, data);
44362306a36Sopenharmony_ci	if (ret)
44462306a36Sopenharmony_ci		goto out;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	/* measure enable on IBAT, BAT_DET, CC. IBAT is depend on CC. */
44762306a36Sopenharmony_ci	data = MEAS3_IBAT | MEAS3_BAT_DET | MEAS3_CC;
44862306a36Sopenharmony_ci	ret = pm860x_set_bits(info->i2c, PM8607_MEAS_EN3, data, data);
44962306a36Sopenharmony_ci	if (ret)
45062306a36Sopenharmony_ci		goto out;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	/* measure disable CC in sleep time  */
45362306a36Sopenharmony_ci	ret = pm860x_reg_write(info->i2c, PM8607_MEAS_OFF_TIME1, 0x82);
45462306a36Sopenharmony_ci	if (ret)
45562306a36Sopenharmony_ci		goto out;
45662306a36Sopenharmony_ci	ret = pm860x_reg_write(info->i2c, PM8607_MEAS_OFF_TIME2, 0x6c);
45762306a36Sopenharmony_ci	if (ret)
45862306a36Sopenharmony_ci		goto out;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	/* enable GPADC */
46162306a36Sopenharmony_ci	ret = pm860x_set_bits(info->i2c, PM8607_GPADC_MISC1,
46262306a36Sopenharmony_ci			    GPMISC1_GPADC_EN, GPMISC1_GPADC_EN);
46362306a36Sopenharmony_ci	if (ret < 0)
46462306a36Sopenharmony_ci		goto out;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	/* detect battery via GPADC1 */
46762306a36Sopenharmony_ci	ret = pm860x_set_bits(info->i2c, PM8607_CHG_CTRL6,
46862306a36Sopenharmony_ci			    CC6_BAT_DET_GPADC1, CC6_BAT_DET_GPADC1);
46962306a36Sopenharmony_ci	if (ret < 0)
47062306a36Sopenharmony_ci		goto out;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	ret = pm860x_set_bits(info->i2c, PM8607_CCNT, 7 << 3,
47362306a36Sopenharmony_ci			      CCNT_AVG_SEL);
47462306a36Sopenharmony_ci	if (ret < 0)
47562306a36Sopenharmony_ci		goto out;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	/* set GPADC1 bias */
47862306a36Sopenharmony_ci	ret = pm860x_set_bits(info->i2c, PM8607_GP_BIAS2, 0xF << 4,
47962306a36Sopenharmony_ci			      GPBIAS2_GPADC1_SET);
48062306a36Sopenharmony_ci	if (ret < 0)
48162306a36Sopenharmony_ci		goto out;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	/* check whether battery present) */
48462306a36Sopenharmony_ci	mutex_lock(&info->lock);
48562306a36Sopenharmony_ci	ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
48662306a36Sopenharmony_ci	if (ret < 0) {
48762306a36Sopenharmony_ci		mutex_unlock(&info->lock);
48862306a36Sopenharmony_ci		goto out;
48962306a36Sopenharmony_ci	}
49062306a36Sopenharmony_ci	if (ret & STATUS2_BAT) {
49162306a36Sopenharmony_ci		info->present = 1;
49262306a36Sopenharmony_ci		info->temp_type = PM860X_TEMP_TBAT;
49362306a36Sopenharmony_ci	} else {
49462306a36Sopenharmony_ci		info->present = 0;
49562306a36Sopenharmony_ci		info->temp_type = PM860X_TEMP_TINT;
49662306a36Sopenharmony_ci	}
49762306a36Sopenharmony_ci	mutex_unlock(&info->lock);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	ret = calc_soc(info, OCV_MODE_ACTIVE, &soc);
50062306a36Sopenharmony_ci	if (ret < 0)
50162306a36Sopenharmony_ci		goto out;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	data = pm860x_reg_read(info->i2c, PM8607_POWER_UP_LOG);
50462306a36Sopenharmony_ci	bat_remove = data & BAT_WU_LOG;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	dev_dbg(info->dev, "battery wake up? %s\n",
50762306a36Sopenharmony_ci		bat_remove != 0 ? "yes" : "no");
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	/* restore SOC from RTC domain register */
51062306a36Sopenharmony_ci	if (bat_remove == 0) {
51162306a36Sopenharmony_ci		buf[0] = pm860x_reg_read(info->i2c, PM8607_RTC_MISC2);
51262306a36Sopenharmony_ci		buf[1] = pm860x_reg_read(info->i2c, PM8607_RTC1);
51362306a36Sopenharmony_ci		data = ((buf[1] & 0x3) << 5) | ((buf[0] >> 3) & 0x1F);
51462306a36Sopenharmony_ci		if (data > soc + 15)
51562306a36Sopenharmony_ci			info->start_soc = soc;
51662306a36Sopenharmony_ci		else if (data < soc - 15)
51762306a36Sopenharmony_ci			info->start_soc = soc;
51862306a36Sopenharmony_ci		else
51962306a36Sopenharmony_ci			info->start_soc = data;
52062306a36Sopenharmony_ci		dev_dbg(info->dev, "soc_rtc %d, soc_ocv :%d\n", data, soc);
52162306a36Sopenharmony_ci	} else {
52262306a36Sopenharmony_ci		pm860x_set_bits(info->i2c, PM8607_POWER_UP_LOG,
52362306a36Sopenharmony_ci				BAT_WU_LOG, BAT_WU_LOG);
52462306a36Sopenharmony_ci		info->start_soc = soc;
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci	info->last_capacity = info->start_soc;
52762306a36Sopenharmony_ci	dev_dbg(info->dev, "init soc : %d\n", info->last_capacity);
52862306a36Sopenharmony_ciout:
52962306a36Sopenharmony_ci	return;
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic void set_temp_threshold(struct pm860x_battery_info *info,
53362306a36Sopenharmony_ci			       int min, int max)
53462306a36Sopenharmony_ci{
53562306a36Sopenharmony_ci	int data;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	/* (tmp << 8) / 1800 */
53862306a36Sopenharmony_ci	if (min <= 0)
53962306a36Sopenharmony_ci		data = 0;
54062306a36Sopenharmony_ci	else
54162306a36Sopenharmony_ci		data = (min << 8) / 1800;
54262306a36Sopenharmony_ci	pm860x_reg_write(info->i2c, PM8607_GPADC1_HIGHTH, data);
54362306a36Sopenharmony_ci	dev_dbg(info->dev, "TEMP_HIGHTH : min: %d, 0x%x\n", min, data);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	if (max <= 0)
54662306a36Sopenharmony_ci		data = 0xff;
54762306a36Sopenharmony_ci	else
54862306a36Sopenharmony_ci		data = (max << 8) / 1800;
54962306a36Sopenharmony_ci	pm860x_reg_write(info->i2c, PM8607_GPADC1_LOWTH, data);
55062306a36Sopenharmony_ci	dev_dbg(info->dev, "TEMP_LOWTH:max : %d, 0x%x\n", max, data);
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_cistatic int measure_temp(struct pm860x_battery_info *info, int *data)
55462306a36Sopenharmony_ci{
55562306a36Sopenharmony_ci	int ret;
55662306a36Sopenharmony_ci	int temp;
55762306a36Sopenharmony_ci	int min;
55862306a36Sopenharmony_ci	int max;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	if (info->temp_type == PM860X_TEMP_TINT) {
56162306a36Sopenharmony_ci		ret = measure_12bit_voltage(info, PM8607_TINT_MEAS1, data);
56262306a36Sopenharmony_ci		if (ret)
56362306a36Sopenharmony_ci			return ret;
56462306a36Sopenharmony_ci		*data = (*data - 884) * 1000 / 3611;
56562306a36Sopenharmony_ci	} else {
56662306a36Sopenharmony_ci		ret = measure_12bit_voltage(info, PM8607_GPADC1_MEAS1, data);
56762306a36Sopenharmony_ci		if (ret)
56862306a36Sopenharmony_ci			return ret;
56962306a36Sopenharmony_ci		/* meausered Vtbat(mV) / Ibias_current(11uA)*/
57062306a36Sopenharmony_ci		*data = (*data * 1000) / GPBIAS2_GPADC1_UA;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci		if (*data > TBAT_NEG_25D) {
57362306a36Sopenharmony_ci			temp = -30;	/* over cold , suppose -30 roughly */
57462306a36Sopenharmony_ci			max = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
57562306a36Sopenharmony_ci			set_temp_threshold(info, 0, max);
57662306a36Sopenharmony_ci		} else if (*data > TBAT_NEG_10D) {
57762306a36Sopenharmony_ci			temp = -15;	/* -15 degree, code */
57862306a36Sopenharmony_ci			max = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
57962306a36Sopenharmony_ci			set_temp_threshold(info, 0, max);
58062306a36Sopenharmony_ci		} else if (*data > TBAT_0D) {
58162306a36Sopenharmony_ci			temp = -5;	/* -5 degree */
58262306a36Sopenharmony_ci			min = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
58362306a36Sopenharmony_ci			max = TBAT_40D * GPBIAS2_GPADC1_UA / 1000;
58462306a36Sopenharmony_ci			set_temp_threshold(info, min, max);
58562306a36Sopenharmony_ci		} else if (*data > TBAT_10D) {
58662306a36Sopenharmony_ci			temp = 5;	/* in range of (0, 10) */
58762306a36Sopenharmony_ci			min = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
58862306a36Sopenharmony_ci			max = TBAT_40D * GPBIAS2_GPADC1_UA / 1000;
58962306a36Sopenharmony_ci			set_temp_threshold(info, min, max);
59062306a36Sopenharmony_ci		} else if (*data > TBAT_20D) {
59162306a36Sopenharmony_ci			temp = 15;	/* in range of (10, 20) */
59262306a36Sopenharmony_ci			min = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
59362306a36Sopenharmony_ci			max = TBAT_40D * GPBIAS2_GPADC1_UA / 1000;
59462306a36Sopenharmony_ci			set_temp_threshold(info, min, max);
59562306a36Sopenharmony_ci		} else if (*data > TBAT_30D) {
59662306a36Sopenharmony_ci			temp = 25;	/* in range of (20, 30) */
59762306a36Sopenharmony_ci			min = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
59862306a36Sopenharmony_ci			max = TBAT_40D * GPBIAS2_GPADC1_UA / 1000;
59962306a36Sopenharmony_ci			set_temp_threshold(info, min, max);
60062306a36Sopenharmony_ci		} else if (*data > TBAT_40D) {
60162306a36Sopenharmony_ci			temp = 35;	/* in range of (30, 40) */
60262306a36Sopenharmony_ci			min = TBAT_NEG_10D * GPBIAS2_GPADC1_UA / 1000;
60362306a36Sopenharmony_ci			max = TBAT_40D * GPBIAS2_GPADC1_UA / 1000;
60462306a36Sopenharmony_ci			set_temp_threshold(info, min, max);
60562306a36Sopenharmony_ci		} else {
60662306a36Sopenharmony_ci			min = TBAT_40D * GPBIAS2_GPADC1_UA / 1000;
60762306a36Sopenharmony_ci			set_temp_threshold(info, min, 0);
60862306a36Sopenharmony_ci			temp = 45;	/* over heat ,suppose 45 roughly */
60962306a36Sopenharmony_ci		}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci		dev_dbg(info->dev, "temp_C:%d C,temp_mv:%d mv\n", temp, *data);
61262306a36Sopenharmony_ci		*data = temp;
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci	return 0;
61562306a36Sopenharmony_ci}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_cistatic int calc_resistor(struct pm860x_battery_info *info)
61862306a36Sopenharmony_ci{
61962306a36Sopenharmony_ci	int vbatt_sum1;
62062306a36Sopenharmony_ci	int vbatt_sum2;
62162306a36Sopenharmony_ci	int chg_current;
62262306a36Sopenharmony_ci	int ibatt_sum1;
62362306a36Sopenharmony_ci	int ibatt_sum2;
62462306a36Sopenharmony_ci	int data;
62562306a36Sopenharmony_ci	int ret;
62662306a36Sopenharmony_ci	int i;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	ret = measure_current(info, &data);
62962306a36Sopenharmony_ci	/* make sure that charging is launched by data > 0 */
63062306a36Sopenharmony_ci	if (ret || data < 0)
63162306a36Sopenharmony_ci		goto out;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data);
63462306a36Sopenharmony_ci	if (ret)
63562306a36Sopenharmony_ci		goto out;
63662306a36Sopenharmony_ci	/* calculate resistor only in CC charge mode */
63762306a36Sopenharmony_ci	if (data < VBATT_RESISTOR_MIN || data > VBATT_RESISTOR_MAX)
63862306a36Sopenharmony_ci		goto out;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	/* current is saved */
64162306a36Sopenharmony_ci	if (set_charger_current(info, 500, &chg_current))
64262306a36Sopenharmony_ci		goto out;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	/*
64562306a36Sopenharmony_ci	 * set charge current as 500mA, wait about 500ms till charging
64662306a36Sopenharmony_ci	 * process is launched and stable with the newer charging current.
64762306a36Sopenharmony_ci	 */
64862306a36Sopenharmony_ci	msleep(500);
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	for (i = 0, vbatt_sum1 = 0, ibatt_sum1 = 0; i < 10; i++) {
65162306a36Sopenharmony_ci		ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data);
65262306a36Sopenharmony_ci		if (ret)
65362306a36Sopenharmony_ci			goto out_meas;
65462306a36Sopenharmony_ci		vbatt_sum1 += data;
65562306a36Sopenharmony_ci		ret = measure_current(info, &data);
65662306a36Sopenharmony_ci		if (ret)
65762306a36Sopenharmony_ci			goto out_meas;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci		if (data < 0)
66062306a36Sopenharmony_ci			ibatt_sum1 = ibatt_sum1 - data;	/* discharging */
66162306a36Sopenharmony_ci		else
66262306a36Sopenharmony_ci			ibatt_sum1 = ibatt_sum1 + data;	/* charging */
66362306a36Sopenharmony_ci	}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	if (set_charger_current(info, 100, &ret))
66662306a36Sopenharmony_ci		goto out_meas;
66762306a36Sopenharmony_ci	/*
66862306a36Sopenharmony_ci	 * set charge current as 100mA, wait about 500ms till charging
66962306a36Sopenharmony_ci	 * process is launched and stable with the newer charging current.
67062306a36Sopenharmony_ci	 */
67162306a36Sopenharmony_ci	msleep(500);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	for (i = 0, vbatt_sum2 = 0, ibatt_sum2 = 0; i < 10; i++) {
67462306a36Sopenharmony_ci		ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data);
67562306a36Sopenharmony_ci		if (ret)
67662306a36Sopenharmony_ci			goto out_meas;
67762306a36Sopenharmony_ci		vbatt_sum2 += data;
67862306a36Sopenharmony_ci		ret = measure_current(info, &data);
67962306a36Sopenharmony_ci		if (ret)
68062306a36Sopenharmony_ci			goto out_meas;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci		if (data < 0)
68362306a36Sopenharmony_ci			ibatt_sum2 = ibatt_sum2 - data;	/* discharging */
68462306a36Sopenharmony_ci		else
68562306a36Sopenharmony_ci			ibatt_sum2 = ibatt_sum2 + data;	/* charging */
68662306a36Sopenharmony_ci	}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	/* restore current setting */
68962306a36Sopenharmony_ci	if (set_charger_current(info, chg_current, &ret))
69062306a36Sopenharmony_ci		goto out_meas;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	if ((vbatt_sum1 > vbatt_sum2) && (ibatt_sum1 > ibatt_sum2) &&
69362306a36Sopenharmony_ci			(ibatt_sum2 > 0)) {
69462306a36Sopenharmony_ci		/* calculate resistor in discharging case */
69562306a36Sopenharmony_ci		data = 1000 * (vbatt_sum1 - vbatt_sum2)
69662306a36Sopenharmony_ci		    / (ibatt_sum1 - ibatt_sum2);
69762306a36Sopenharmony_ci		if ((data - info->resistor > 0) &&
69862306a36Sopenharmony_ci				(data - info->resistor < info->resistor))
69962306a36Sopenharmony_ci			info->resistor = data;
70062306a36Sopenharmony_ci		if ((info->resistor - data > 0) &&
70162306a36Sopenharmony_ci				(info->resistor - data < data))
70262306a36Sopenharmony_ci			info->resistor = data;
70362306a36Sopenharmony_ci	}
70462306a36Sopenharmony_ci	return 0;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ciout_meas:
70762306a36Sopenharmony_ci	set_charger_current(info, chg_current, &ret);
70862306a36Sopenharmony_ciout:
70962306a36Sopenharmony_ci	return -EINVAL;
71062306a36Sopenharmony_ci}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_cistatic int calc_capacity(struct pm860x_battery_info *info, int *cap)
71362306a36Sopenharmony_ci{
71462306a36Sopenharmony_ci	int ret;
71562306a36Sopenharmony_ci	int data;
71662306a36Sopenharmony_ci	int ibat;
71762306a36Sopenharmony_ci	int cap_ocv = 0;
71862306a36Sopenharmony_ci	int cap_cc = 0;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	ret = calc_ccnt(info, &ccnt_data);
72162306a36Sopenharmony_ci	if (ret)
72262306a36Sopenharmony_ci		goto out;
72362306a36Sopenharmony_cisoc:
72462306a36Sopenharmony_ci	data = info->max_capacity * info->start_soc / 100;
72562306a36Sopenharmony_ci	if (ccnt_data.total_dischg - ccnt_data.total_chg <= data) {
72662306a36Sopenharmony_ci		cap_cc =
72762306a36Sopenharmony_ci		    data + ccnt_data.total_chg - ccnt_data.total_dischg;
72862306a36Sopenharmony_ci	} else {
72962306a36Sopenharmony_ci		clear_ccnt(info, &ccnt_data);
73062306a36Sopenharmony_ci		calc_soc(info, OCV_MODE_ACTIVE, &info->start_soc);
73162306a36Sopenharmony_ci		dev_dbg(info->dev, "restart soc = %d !\n",
73262306a36Sopenharmony_ci			info->start_soc);
73362306a36Sopenharmony_ci		goto soc;
73462306a36Sopenharmony_ci	}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	cap_cc = cap_cc * 100 / info->max_capacity;
73762306a36Sopenharmony_ci	if (cap_cc < 0)
73862306a36Sopenharmony_ci		cap_cc = 0;
73962306a36Sopenharmony_ci	else if (cap_cc > 100)
74062306a36Sopenharmony_ci		cap_cc = 100;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	dev_dbg(info->dev, "%s, last cap : %d", __func__,
74362306a36Sopenharmony_ci		info->last_capacity);
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	ret = measure_current(info, &ibat);
74662306a36Sopenharmony_ci	if (ret)
74762306a36Sopenharmony_ci		goto out;
74862306a36Sopenharmony_ci	/* Calculate the capacity when discharging(ibat < 0) */
74962306a36Sopenharmony_ci	if (ibat < 0) {
75062306a36Sopenharmony_ci		ret = calc_soc(info, OCV_MODE_ACTIVE, &cap_ocv);
75162306a36Sopenharmony_ci		if (ret)
75262306a36Sopenharmony_ci			cap_ocv = info->last_capacity;
75362306a36Sopenharmony_ci		ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data);
75462306a36Sopenharmony_ci		if (ret)
75562306a36Sopenharmony_ci			goto out;
75662306a36Sopenharmony_ci		if (data <= LOW_BAT_THRESHOLD) {
75762306a36Sopenharmony_ci			/* choose the lower capacity value to report
75862306a36Sopenharmony_ci			 * between vbat and CC when vbat < 3.6v;
75962306a36Sopenharmony_ci			 * than 3.6v;
76062306a36Sopenharmony_ci			 */
76162306a36Sopenharmony_ci			*cap = min(cap_ocv, cap_cc);
76262306a36Sopenharmony_ci		} else {
76362306a36Sopenharmony_ci			/* when detect vbat > 3.6v, but cap_cc < 15,and
76462306a36Sopenharmony_ci			 * cap_ocv is 10% larger than cap_cc, we can think
76562306a36Sopenharmony_ci			 * CC have some accumulation error, switch to OCV
76662306a36Sopenharmony_ci			 * to estimate capacity;
76762306a36Sopenharmony_ci			 * */
76862306a36Sopenharmony_ci			if (cap_cc < 15 && cap_ocv - cap_cc > 10)
76962306a36Sopenharmony_ci				*cap = cap_ocv;
77062306a36Sopenharmony_ci			else
77162306a36Sopenharmony_ci				*cap = cap_cc;
77262306a36Sopenharmony_ci		}
77362306a36Sopenharmony_ci		/* when discharging, make sure current capacity
77462306a36Sopenharmony_ci		 * is lower than last*/
77562306a36Sopenharmony_ci		if (*cap > info->last_capacity)
77662306a36Sopenharmony_ci			*cap = info->last_capacity;
77762306a36Sopenharmony_ci	} else {
77862306a36Sopenharmony_ci		*cap = cap_cc;
77962306a36Sopenharmony_ci	}
78062306a36Sopenharmony_ci	info->last_capacity = *cap;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	dev_dbg(info->dev, "%s, cap_ocv:%d cap_cc:%d, cap:%d\n",
78362306a36Sopenharmony_ci		(ibat < 0) ? "discharging" : "charging",
78462306a36Sopenharmony_ci		 cap_ocv, cap_cc, *cap);
78562306a36Sopenharmony_ci	/*
78662306a36Sopenharmony_ci	 * store the current capacity to RTC domain register,
78762306a36Sopenharmony_ci	 * after next power up , it will be restored.
78862306a36Sopenharmony_ci	 */
78962306a36Sopenharmony_ci	pm860x_set_bits(info->i2c, PM8607_RTC_MISC2, RTC_SOC_5LSB,
79062306a36Sopenharmony_ci			(*cap & 0x1F) << 3);
79162306a36Sopenharmony_ci	pm860x_set_bits(info->i2c, PM8607_RTC1, RTC_SOC_3MSB,
79262306a36Sopenharmony_ci			((*cap >> 5) & 0x3));
79362306a36Sopenharmony_ci	return 0;
79462306a36Sopenharmony_ciout:
79562306a36Sopenharmony_ci	return ret;
79662306a36Sopenharmony_ci}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_cistatic void pm860x_external_power_changed(struct power_supply *psy)
79962306a36Sopenharmony_ci{
80062306a36Sopenharmony_ci	struct pm860x_battery_info *info = dev_get_drvdata(psy->dev.parent);
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	calc_resistor(info);
80362306a36Sopenharmony_ci}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_cistatic int pm860x_batt_get_prop(struct power_supply *psy,
80662306a36Sopenharmony_ci				enum power_supply_property psp,
80762306a36Sopenharmony_ci				union power_supply_propval *val)
80862306a36Sopenharmony_ci{
80962306a36Sopenharmony_ci	struct pm860x_battery_info *info = dev_get_drvdata(psy->dev.parent);
81062306a36Sopenharmony_ci	int data;
81162306a36Sopenharmony_ci	int ret;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	switch (psp) {
81462306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_PRESENT:
81562306a36Sopenharmony_ci		val->intval = info->present;
81662306a36Sopenharmony_ci		break;
81762306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CAPACITY:
81862306a36Sopenharmony_ci		ret = calc_capacity(info, &data);
81962306a36Sopenharmony_ci		if (ret)
82062306a36Sopenharmony_ci			return ret;
82162306a36Sopenharmony_ci		if (data < 0)
82262306a36Sopenharmony_ci			data = 0;
82362306a36Sopenharmony_ci		else if (data > 100)
82462306a36Sopenharmony_ci			data = 100;
82562306a36Sopenharmony_ci		/* return 100 if battery is not attached */
82662306a36Sopenharmony_ci		if (!info->present)
82762306a36Sopenharmony_ci			data = 100;
82862306a36Sopenharmony_ci		val->intval = data;
82962306a36Sopenharmony_ci		break;
83062306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_TECHNOLOGY:
83162306a36Sopenharmony_ci		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
83262306a36Sopenharmony_ci		break;
83362306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
83462306a36Sopenharmony_ci		/* return real vbatt Voltage */
83562306a36Sopenharmony_ci		ret = measure_vbatt(info, OCV_MODE_ACTIVE, &data);
83662306a36Sopenharmony_ci		if (ret)
83762306a36Sopenharmony_ci			return ret;
83862306a36Sopenharmony_ci		val->intval = data * 1000;
83962306a36Sopenharmony_ci		break;
84062306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_VOLTAGE_AVG:
84162306a36Sopenharmony_ci		/* return Open Circuit Voltage (not measured voltage) */
84262306a36Sopenharmony_ci		ret = calc_ocv(info, &data);
84362306a36Sopenharmony_ci		if (ret)
84462306a36Sopenharmony_ci			return ret;
84562306a36Sopenharmony_ci		val->intval = data * 1000;
84662306a36Sopenharmony_ci		break;
84762306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CURRENT_NOW:
84862306a36Sopenharmony_ci		ret = measure_current(info, &data);
84962306a36Sopenharmony_ci		if (ret)
85062306a36Sopenharmony_ci			return ret;
85162306a36Sopenharmony_ci		val->intval = data;
85262306a36Sopenharmony_ci		break;
85362306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_TEMP:
85462306a36Sopenharmony_ci		if (info->present) {
85562306a36Sopenharmony_ci			ret = measure_temp(info, &data);
85662306a36Sopenharmony_ci			if (ret)
85762306a36Sopenharmony_ci				return ret;
85862306a36Sopenharmony_ci			data *= 10;
85962306a36Sopenharmony_ci		} else {
86062306a36Sopenharmony_ci			/* Fake Temp 25C Without Battery */
86162306a36Sopenharmony_ci			data = 250;
86262306a36Sopenharmony_ci		}
86362306a36Sopenharmony_ci		val->intval = data;
86462306a36Sopenharmony_ci		break;
86562306a36Sopenharmony_ci	default:
86662306a36Sopenharmony_ci		return -ENODEV;
86762306a36Sopenharmony_ci	}
86862306a36Sopenharmony_ci	return 0;
86962306a36Sopenharmony_ci}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_cistatic int pm860x_batt_set_prop(struct power_supply *psy,
87262306a36Sopenharmony_ci				       enum power_supply_property psp,
87362306a36Sopenharmony_ci				       const union power_supply_propval *val)
87462306a36Sopenharmony_ci{
87562306a36Sopenharmony_ci	struct pm860x_battery_info *info = dev_get_drvdata(psy->dev.parent);
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	switch (psp) {
87862306a36Sopenharmony_ci	case POWER_SUPPLY_PROP_CHARGE_FULL:
87962306a36Sopenharmony_ci		clear_ccnt(info, &ccnt_data);
88062306a36Sopenharmony_ci		info->start_soc = 100;
88162306a36Sopenharmony_ci		dev_dbg(info->dev, "chg done, update soc = %d\n",
88262306a36Sopenharmony_ci			info->start_soc);
88362306a36Sopenharmony_ci		break;
88462306a36Sopenharmony_ci	default:
88562306a36Sopenharmony_ci		return -EPERM;
88662306a36Sopenharmony_ci	}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	return 0;
88962306a36Sopenharmony_ci}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_cistatic enum power_supply_property pm860x_batt_props[] = {
89362306a36Sopenharmony_ci	POWER_SUPPLY_PROP_PRESENT,
89462306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CAPACITY,
89562306a36Sopenharmony_ci	POWER_SUPPLY_PROP_TECHNOLOGY,
89662306a36Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_NOW,
89762306a36Sopenharmony_ci	POWER_SUPPLY_PROP_VOLTAGE_AVG,
89862306a36Sopenharmony_ci	POWER_SUPPLY_PROP_CURRENT_NOW,
89962306a36Sopenharmony_ci	POWER_SUPPLY_PROP_TEMP,
90062306a36Sopenharmony_ci};
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_cistatic const struct power_supply_desc pm860x_battery_desc = {
90362306a36Sopenharmony_ci	.name			= "battery-monitor",
90462306a36Sopenharmony_ci	.type			= POWER_SUPPLY_TYPE_BATTERY,
90562306a36Sopenharmony_ci	.properties		= pm860x_batt_props,
90662306a36Sopenharmony_ci	.num_properties		= ARRAY_SIZE(pm860x_batt_props),
90762306a36Sopenharmony_ci	.get_property		= pm860x_batt_get_prop,
90862306a36Sopenharmony_ci	.set_property		= pm860x_batt_set_prop,
90962306a36Sopenharmony_ci	.external_power_changed	= pm860x_external_power_changed,
91062306a36Sopenharmony_ci};
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_cistatic int pm860x_battery_probe(struct platform_device *pdev)
91362306a36Sopenharmony_ci{
91462306a36Sopenharmony_ci	struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
91562306a36Sopenharmony_ci	struct pm860x_battery_info *info;
91662306a36Sopenharmony_ci	struct pm860x_power_pdata *pdata;
91762306a36Sopenharmony_ci	int ret;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
92062306a36Sopenharmony_ci	if (!info)
92162306a36Sopenharmony_ci		return -ENOMEM;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	info->irq_cc = platform_get_irq(pdev, 0);
92462306a36Sopenharmony_ci	if (info->irq_cc <= 0)
92562306a36Sopenharmony_ci		return -EINVAL;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	info->irq_batt = platform_get_irq(pdev, 1);
92862306a36Sopenharmony_ci	if (info->irq_batt <= 0)
92962306a36Sopenharmony_ci		return -EINVAL;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	info->chip = chip;
93262306a36Sopenharmony_ci	info->i2c =
93362306a36Sopenharmony_ci	    (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
93462306a36Sopenharmony_ci	info->dev = &pdev->dev;
93562306a36Sopenharmony_ci	info->status = POWER_SUPPLY_STATUS_UNKNOWN;
93662306a36Sopenharmony_ci	pdata = pdev->dev.platform_data;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	mutex_init(&info->lock);
93962306a36Sopenharmony_ci	platform_set_drvdata(pdev, info);
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	pm860x_init_battery(info);
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	if (pdata && pdata->max_capacity)
94462306a36Sopenharmony_ci		info->max_capacity = pdata->max_capacity;
94562306a36Sopenharmony_ci	else
94662306a36Sopenharmony_ci		info->max_capacity = 1500;	/* set default capacity */
94762306a36Sopenharmony_ci	if (pdata && pdata->resistor)
94862306a36Sopenharmony_ci		info->resistor = pdata->resistor;
94962306a36Sopenharmony_ci	else
95062306a36Sopenharmony_ci		info->resistor = 300;	/* set default internal resistor */
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	info->battery = devm_power_supply_register(&pdev->dev,
95362306a36Sopenharmony_ci						   &pm860x_battery_desc,
95462306a36Sopenharmony_ci						   NULL);
95562306a36Sopenharmony_ci	if (IS_ERR(info->battery))
95662306a36Sopenharmony_ci		return PTR_ERR(info->battery);
95762306a36Sopenharmony_ci	info->battery->dev.parent = &pdev->dev;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	ret = devm_request_threaded_irq(chip->dev, info->irq_cc, NULL,
96062306a36Sopenharmony_ci					pm860x_coulomb_handler, IRQF_ONESHOT,
96162306a36Sopenharmony_ci					"coulomb", info);
96262306a36Sopenharmony_ci	if (ret < 0) {
96362306a36Sopenharmony_ci		dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
96462306a36Sopenharmony_ci			info->irq_cc, ret);
96562306a36Sopenharmony_ci		return ret;
96662306a36Sopenharmony_ci	}
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	ret = devm_request_threaded_irq(chip->dev, info->irq_batt, NULL,
96962306a36Sopenharmony_ci					pm860x_batt_handler,
97062306a36Sopenharmony_ci					IRQF_ONESHOT, "battery", info);
97162306a36Sopenharmony_ci	if (ret < 0) {
97262306a36Sopenharmony_ci		dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
97362306a36Sopenharmony_ci			info->irq_batt, ret);
97462306a36Sopenharmony_ci		return ret;
97562306a36Sopenharmony_ci	}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	return 0;
97962306a36Sopenharmony_ci}
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
98262306a36Sopenharmony_cistatic int pm860x_battery_suspend(struct device *dev)
98362306a36Sopenharmony_ci{
98462306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
98562306a36Sopenharmony_ci	struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	if (device_may_wakeup(dev))
98862306a36Sopenharmony_ci		chip->wakeup_flag |= 1 << PM8607_IRQ_CC;
98962306a36Sopenharmony_ci	return 0;
99062306a36Sopenharmony_ci}
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_cistatic int pm860x_battery_resume(struct device *dev)
99362306a36Sopenharmony_ci{
99462306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
99562306a36Sopenharmony_ci	struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	if (device_may_wakeup(dev))
99862306a36Sopenharmony_ci		chip->wakeup_flag &= ~(1 << PM8607_IRQ_CC);
99962306a36Sopenharmony_ci	return 0;
100062306a36Sopenharmony_ci}
100162306a36Sopenharmony_ci#endif
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(pm860x_battery_pm_ops,
100462306a36Sopenharmony_ci			pm860x_battery_suspend, pm860x_battery_resume);
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_cistatic struct platform_driver pm860x_battery_driver = {
100762306a36Sopenharmony_ci	.driver = {
100862306a36Sopenharmony_ci		   .name = "88pm860x-battery",
100962306a36Sopenharmony_ci		   .pm = &pm860x_battery_pm_ops,
101062306a36Sopenharmony_ci	},
101162306a36Sopenharmony_ci	.probe = pm860x_battery_probe,
101262306a36Sopenharmony_ci};
101362306a36Sopenharmony_cimodule_platform_driver(pm860x_battery_driver);
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ciMODULE_DESCRIPTION("Marvell 88PM860x Battery driver");
101662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1017