Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

3 Commits   0 Branches   0 Tags
/*
 * rk817 battery  driver
 *
 * Copyright (C) 2018 Rockchip Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#define pr_fmt(fmt) "rk817-bat: " fmt

#include <linux/delay.h>
#include <linux/extcon.h>
#include <linux/fb.h>
#include <linux/gpio.h>
#include <linux/iio/consumer.h>
#include <linux/iio/iio.h>
#include <linux/irq.h>
#include <linux/jiffies.h>
#include <linux/mfd/rk808.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/power/rk_usbbc.h>
#include <linux/regmap.h>
#include <linux/rk_keys.h>
#include <linux/rtc.h>
#include <linux/timer.h>
#include <linux/wakelock.h>
#include <linux/workqueue.h>

static int dbg_enable;

module_param_named(dbg_level, dbg_enable, int, 0644);

#define DBG(args...) \
	do { \
		if (dbg_enable) { \
			pr_info(args); \
		} \
	} while (0)

#define BAT_INFO(fmt, args...) pr_info(fmt, ##args)

#define DRIVER_VERSION	"1.00"
#define SFT_SET_KB	1

#define DIV(x)	((x) ? (x) : 1)
#define ENABLE	0x01
#define DISABLE	0x00
#define MAX_INTERPOLATE		1000
#define MAX_PERCENTAGE		100
#define MAX_INT			0x7FFF

/* RK818_GGCON */
#define OCV_SAMP_MIN_MSK	0x0c
#define OCV_SAMP_8MIN		(0x00 << 2)

#define ADC_CAL_8MIN		0x00
#define RELAX_VOL12_UPD_MSK	(RELAX_VOL1_UPD | RELAX_VOL2_UPD)
#define MINUTE(x)	\
	((x) * 60)

#define ADC_TO_CURRENT(adc_value, samp_res)	\
	(adc_value * 172 / 1000 / samp_res)
#define CURRENT_TO_ADC(current, samp_res)	\
	(current * 1000 * samp_res / 172)

#define ADC_TO_CAPACITY(adc_value, samp_res)	\
	(adc_value / 1000 * 172 / 3600 / samp_res)
#define CAPACITY_TO_ADC(capacity, samp_res)	\
	(capacity * samp_res * 3600 / 172 * 1000)

#define ADC_TO_CAPACITY_UAH(adc_value, samp_res)	\
	(adc_value / 3600 * 172 / samp_res)
#define ADC_TO_CAPACITY_MAH(adc_value, samp_res)	\
	(adc_value / 1000 * 172 / 3600 / samp_res)

/* THREAML_REG */
#define TEMP_85C		(0x00 << 2)
#define TEMP_95C		(0x01 << 2)
#define TEMP_105C		(0x02 << 2)
#define TEMP_115C		(0x03 << 2)

#define ZERO_LOAD_LVL1			1400
#define ZERO_LOAD_LVL2			600

/* zero algorithm */
#define PWROFF_THRESD			3400
#define MIN_ZERO_DSOC_ACCURACY		10	/*0.01%*/
#define MIN_ZERO_OVERCNT		100
#define MIN_ACCURACY			1
#define DEF_PWRPATH_RES			50
#define WAIT_DSOC_DROP_SEC		15
#define WAIT_SHTD_DROP_SEC		30
#define MIN_ZERO_GAP_XSOC1		10
#define MIN_ZERO_GAP_XSOC2		5
#define MIN_ZERO_GAP_XSOC3		3
#define MIN_ZERO_GAP_CALIB		5

#define ADC_CALIB_THRESHOLD		4
#define ADC_CALIB_LMT_MIN		3
#define ADC_CALIB_CNT			5

/* default param */
#define DEFAULT_BAT_RES			135
#define DEFAULT_SLP_ENTER_CUR		300
#define DEFAULT_SLP_EXIT_CUR		300
#define DEFAULT_SLP_FILTER_CUR		100
#define DEFAULT_PWROFF_VOL_THRESD	3400
#define DEFAULT_MONITOR_SEC		5
#define DEFAULT_ALGR_VOL_THRESD1	3850
#define DEFAULT_ALGR_VOL_THRESD2	3950
#define DEFAULT_CHRG_VOL_SEL		CHRG_VOL4200MV
#define DEFAULT_CHRG_CUR_SEL		CHRG_CUR1400MA
#define DEFAULT_CHRG_CUR_INPUT		INPUT_CUR2000MA
#define DEFAULT_POFFSET			42
#define DEFAULT_MAX_SOC_OFFSET		60
#define DEFAULT_FB_TEMP			TEMP_115C
#define DEFAULT_ENERGY_MODE		0
#define DEFAULT_ZERO_RESERVE_DSOC	10
#define DEFAULT_SAMPLE_RES		20

/* sample resistor and division */
#define SAMPLE_RES_10MR			10
#define SAMPLE_RES_20MR			20
#define SAMPLE_RES_DIV1			1
#define SAMPLE_RES_DIV2			2

/* sleep */
#define SLP_CURR_MAX			40
#define SLP_CURR_MIN			6
#define LOW_PWR_SLP_CURR_MAX		20
#define LOW_PWR_SLP_CURR_MIN		1
#define DISCHRG_TIME_STEP1		MINUTE(10)
#define DISCHRG_TIME_STEP2		MINUTE(60)
#define SLP_DSOC_VOL_THRESD		3600
#define REBOOT_PERIOD_SEC		180
#define REBOOT_MAX_CNT			80

#define TIMER_MS_COUNTS		1000
/* fcc */
#define MIN_FCC				500
#define CAP_INVALID			0x80

/* virtual params */
#define VIRTUAL_CURRENT			1000
#define VIRTUAL_VOLTAGE			3888
#define VIRTUAL_SOC			66
#define VIRTUAL_PRESET			1
#define VIRTUAL_TEMPERATURE		188
#define VIRTUAL_STATUS			POWER_SUPPLY_STATUS_CHARGING

#define FINISH_CHRG_CUR1		1000
#define FINISH_CHRG_CUR2		1500
#define FINISH_MAX_SOC_DELAY		20
#define TERM_CHRG_DSOC			88
#define TERM_CHRG_CURR			600
#define TERM_CHRG_K			650
#define SIMULATE_CHRG_INTV		8
#define SIMULATE_CHRG_CURR		400
#define SIMULATE_CHRG_K			1500
#define FULL_CHRG_K			400

enum work_mode {
	MODE_ZERO = 0,
	MODE_FINISH,
	MODE_SMOOTH_CHRG,
	MODE_SMOOTH_DISCHRG,
	MODE_SMOOTH,
};

enum charge_status {
	CHRG_OFF,
	DEAD_CHRG,
	TRICKLE_CHRG,
	CC_OR_CV_CHRG,
	CHARGE_FINISH,
	USB_OVER_VOL,
	BAT_TMP_ERR,
	BAT_TIM_ERR,
};

enum bat_mode {
	MODE_BATTARY = 0,
	MODE_VIRTUAL,
};

enum rk817_sample_time {
	S_8_MIN,
	S_16_MIN,
	S_32_MIN,
	S_48_MIN,
};

enum rk817_output_mode {
	AVERAGE_MODE,
	INSTANT_MODE,
};

enum rk817_battery_fields {
	ADC_SLP_RATE, BAT_CUR_ADC_EN, BAT_VOL_ADC_EN,
	USB_VOL_ADC_EN, TS_ADC_EN, SYS_VOL_ADC_EN, GG_EN, /*ADC_CONFIG0*/
	CUR_ADC_DITH_SEL, CUR_ADC_DIH_EN, CUR_ADC_CHOP_EN,
	CUR_ADC_CHOP_SEL, CUR_ADC_CHOP_VREF_EN, /*CUR_ADC_CFG0*/
	CUR_ADC_VCOM_SEL, CUR_ADC_VCOM_BUF_INC, CUR_ADC_VREF_BUF_INC,
	CUR_ADC_BIAS_DEC, CUR_ADC_IBIAS_SEL,/*CUR_ADC_CFG1*/
	VOL_ADC_EXT_VREF_EN, VOL_ADC_DITH_SEL, VOL_ADC_DITH_EN,
	VOL_ADC_CHOP_EN, VOL_ADC_CHOP_SEL, VOL_ADC_CHOP_VREF_EN,
	VOL_ADC_VCOM_SEL, VOL_ADC_VCOM_BUF_INC, VOL_ADC_VREF_BUF_INC,
	VOL_ADC_IBIAS_SEL, /*VOL_ADC_CFG1*/
	RLX_CUR_FILTER, TS_FUN, VOL_ADC_TSCUR_SEL,
	VOL_CALIB_UPD, CUR_CALIB_UPD, /*ADC_CONFIG1*/
	CUR_OUT_MOD, VOL_OUT_MOD, FRAME_SMP_INTERV,
	ADC_OFF_CAL_INTERV, RLX_SPT, /*GG_CON*/
	OCV_UPD, RELAX_STS, RELAX_VOL2_UPD, RELAX_VOL1_UPD, BAT_CON,
	QMAX_UPD_SOFT, TERM_UPD, OCV_STS, /*GG_STS*/
	RELAX_THRE_H, RELAX_THRE_L, /*RELAX_THRE*/
	RELAX_VOL1_H, RELAX_VOL1_L,
	RELAX_VOL2_H, RELAX_VOL2_L,
	RELAX_CUR1_H, RELAX_CUR1_L,
	RELAX_CUR2_H, RELAX_CUR2_L,
	OCV_THRE_VOL,
	OCV_VOL_H, OCV_VOL_L,
	OCV_VOL0_H, OCV_VOL0_L,
	OCV_CUR_H, OCV_CUR_L,
	OCV_CUR0_H, OCV_CUR0_L,
	PWRON_VOL_H, PWRON_VOL_L,
	PWRON_CUR_H, PWRON_CUR_L,
	OFF_CNT,
	Q_INIT_H3, Q_INIT_H2, Q_INIT_L1, Q_INIT_L0,
	Q_PRESS_H3, Q_PRESS_H2, Q_PRESS_L1, Q_PRESS_L0,
	BAT_VOL_H, BAT_VOL_L,
	BAT_CUR_H, BAT_CUR_L,
	BAT_TS_H, BAT_TS_L,
	USB_VOL_H, USB_VOL_L,
	SYS_VOL_H, SYS_VOL_L,
	Q_MAX_H3, Q_MAX_H2, Q_MAX_L1, Q_MAX_L0,
	Q_TERM_H3, Q_TERM_H2, Q_TERM_L1, Q_TERM_L0,
	Q_OCV_H3, Q_OCV_H2, Q_OCV_L1, Q_OCV_L0,
	OCV_CNT,
	SLEEP_CON_SAMP_CUR_H, SLEEP_CON_SAMP_CUR_L,
	CAL_OFFSET_H, CAL_OFFSET_L,
	VCALIB0_H, VCALIB0_L,
	VCALIB1_H, VCALIB1_L,
	IOFFSET_H, IOFFSET_L,
	BAT_R0, SOC_REG0, SOC_REG1, SOC_REG2,
	REMAIN_CAP_REG2, REMAIN_CAP_REG1, REMAIN_CAP_REG0,
	NEW_FCC_REG2, NEW_FCC_REG1, NEW_FCC_REG0,
	RESET_MODE,
	FG_INIT, HALT_CNT_REG, CALC_REST_REGL, CALC_REST_REGH,
	VOL_ADC_B3,  VOL_ADC_B2, VOL_ADC_B1, VOL_ADC_B0,
	VOL_ADC_K3, VOL_ADC_K2, VOL_ADC_K1, VOL_ADC_K0,
	BAT_EXS, CHG_STS, BAT_OVP_STS, CHRG_IN_CLAMP,
	CHIP_NAME_H, CHIP_NAME_L,
	PLUG_IN_STS,
	F_MAX_FIELDS
};

static const struct reg_field rk817_battery_reg_fields[] = {
	[ADC_SLP_RATE] = REG_FIELD(0x50, 0, 0),
	[BAT_CUR_ADC_EN] = REG_FIELD(0x50, 2, 2),
	[BAT_VOL_ADC_EN] = REG_FIELD(0x50, 3, 3),
	[USB_VOL_ADC_EN] = REG_FIELD(0x50, 4, 4),
	[TS_ADC_EN] = REG_FIELD(0x50, 5, 5),
	[SYS_VOL_ADC_EN] = REG_FIELD(0x50, 6, 6),
	[GG_EN] = REG_FIELD(0x50, 7, 7),/*ADC_CONFIG0*/

	[CUR_ADC_DITH_SEL] = REG_FIELD(0x51, 1, 3),
	[CUR_ADC_DIH_EN] = REG_FIELD(0x51, 4, 4),
	[CUR_ADC_CHOP_EN] = REG_FIELD(0x51, 5, 5),
	[CUR_ADC_CHOP_SEL] = REG_FIELD(0x51, 6, 6),
	[CUR_ADC_CHOP_VREF_EN] = REG_FIELD(0x51, 7, 7), /*CUR_ADC_COFG0*/

	[CUR_ADC_VCOM_SEL] = REG_FIELD(0x52, 0, 1),
	[CUR_ADC_VCOM_BUF_INC] = REG_FIELD(0x52, 2, 2),
	[CUR_ADC_VREF_BUF_INC] = REG_FIELD(0x52, 3, 3),
	[CUR_ADC_BIAS_DEC] = REG_FIELD(0x52, 4, 4),
	[CUR_ADC_IBIAS_SEL] = REG_FIELD(0x52, 5, 6), /*CUR_ADC_COFG1*/

	[VOL_ADC_EXT_VREF_EN] = REG_FIELD(0x53, 0, 0),
	[VOL_ADC_DITH_SEL]  = REG_FIELD(0x53, 1, 3),
	[VOL_ADC_DITH_EN] = REG_FIELD(0x53, 4, 4),
	[VOL_ADC_CHOP_EN] = REG_FIELD(0x53, 5, 5),
	[VOL_ADC_CHOP_SEL] = REG_FIELD(0x53, 6, 6),
	[VOL_ADC_CHOP_VREF_EN] = REG_FIELD(0x53, 7, 7),/*VOL_ADC_COFG0*/

	[VOL_ADC_VCOM_SEL] = REG_FIELD(0x54, 0, 1),
	[VOL_ADC_VCOM_BUF_INC] = REG_FIELD(0x54, 2, 2),
	[VOL_ADC_VREF_BUF_INC] = REG_FIELD(0x54, 3, 3),
	[VOL_ADC_IBIAS_SEL] = REG_FIELD(0x54, 5, 6), /*VOL_ADC_COFG1*/

	[RLX_CUR_FILTER] = REG_FIELD(0x55, 0, 1),
	[TS_FUN] = REG_FIELD(0x55, 3, 3),
	[VOL_ADC_TSCUR_SEL] = REG_FIELD(0x55, 4, 5),
	[VOL_CALIB_UPD] = REG_FIELD(0x55, 6, 6),
	[CUR_CALIB_UPD] = REG_FIELD(0x55, 7, 7), /*ADC_CONFIG1*/

	[CUR_OUT_MOD] = REG_FIELD(0x56, 0, 0),
	[VOL_OUT_MOD] = REG_FIELD(0x56, 1, 1),
	[FRAME_SMP_INTERV] = REG_FIELD(0x56, 2, 3),
	[ADC_OFF_CAL_INTERV] = REG_FIELD(0x56, 4, 5),
	[RLX_SPT] = REG_FIELD(0x56, 6, 7), /*GG_CON*/

	[OCV_UPD] = REG_FIELD(0x57, 0, 0),
	[RELAX_STS] = REG_FIELD(0x57, 1, 1),
	[RELAX_VOL2_UPD] = REG_FIELD(0x57, 2, 2),
	[RELAX_VOL1_UPD] = REG_FIELD(0x57, 3, 3),
	[BAT_CON] = REG_FIELD(0x57, 4, 4),
	[QMAX_UPD_SOFT] = REG_FIELD(0x57, 5, 5),
	[TERM_UPD] = REG_FIELD(0x57, 6, 6),
	[OCV_STS] = REG_FIELD(0x57, 7, 7), /*GG_STS*/

	[RELAX_THRE_H] = REG_FIELD(0x58, 0, 7),
	[RELAX_THRE_L] = REG_FIELD(0x59, 0, 7),

	[RELAX_VOL1_H] = REG_FIELD(0x5A, 0, 7),
	[RELAX_VOL1_L] = REG_FIELD(0x5B, 0, 7),
	[RELAX_VOL2_H] = REG_FIELD(0x5C, 0, 7),
	[RELAX_VOL2_L] = REG_FIELD(0x5D, 0, 7),

	[RELAX_CUR1_H] = REG_FIELD(0x5E, 0, 7),
	[RELAX_CUR1_L] = REG_FIELD(0x5F, 0, 7),
	[RELAX_CUR2_H] = REG_FIELD(0x60, 0, 7),
	[RELAX_CUR2_L] = REG_FIELD(0x61, 0, 7),

	[OCV_THRE_VOL] = REG_FIELD(0x62, 0, 7),

	[OCV_VOL_H] = REG_FIELD(0x63, 0, 7),
	[OCV_VOL_L] = REG_FIELD(0x64, 0, 7),
	[OCV_VOL0_H] = REG_FIELD(0x65, 0, 7),
	[OCV_VOL0_L] = REG_FIELD(0x66, 0, 7),
	[OCV_CUR_H] = REG_FIELD(0x67, 0, 7),
	[OCV_CUR_L] = REG_FIELD(0x68, 0, 7),
	[OCV_CUR0_H] = REG_FIELD(0x69, 0, 7),
	[OCV_CUR0_L] = REG_FIELD(0x6A, 0, 7),
	[PWRON_VOL_H] = REG_FIELD(0x6B, 0, 7),
	[PWRON_VOL_L] = REG_FIELD(0x6C, 0, 7),
	[PWRON_CUR_H] = REG_FIELD(0x6D, 0, 7),
	[PWRON_CUR_L] = REG_FIELD(0x6E, 0, 7),
	[OFF_CNT] = REG_FIELD(0x6F, 0, 7),
	[Q_INIT_H3] = REG_FIELD(0x70, 0, 7),
	[Q_INIT_H2] = REG_FIELD(0x71, 0, 7),
	[Q_INIT_L1] = REG_FIELD(0x72, 0, 7),
	[Q_INIT_L0] = REG_FIELD(0x73, 0, 7),

	[Q_PRESS_H3] = REG_FIELD(0x74, 0, 7),
	[Q_PRESS_H2] = REG_FIELD(0x75, 0, 7),
	[Q_PRESS_L1] = REG_FIELD(0x76, 0, 7),
	[Q_PRESS_L0] = REG_FIELD(0x77, 0, 7),

	[BAT_VOL_H] = REG_FIELD(0x78, 0, 7),
	[BAT_VOL_L] = REG_FIELD(0x79, 0, 7),

	[BAT_CUR_H] = REG_FIELD(0x7A, 0, 7),
	[BAT_CUR_L] = REG_FIELD(0x7B, 0, 7),

	[BAT_TS_H] = REG_FIELD(0x7C, 0, 7),
	[BAT_TS_L] = REG_FIELD(0x7D, 0, 7),
	[USB_VOL_H] = REG_FIELD(0x7E, 0, 7),
	[USB_VOL_L] = REG_FIELD(0x7F, 0, 7),

	[SYS_VOL_H] = REG_FIELD(0x80, 0, 7),
	[SYS_VOL_L] = REG_FIELD(0x81, 0, 7),
	[Q_MAX_H3] = REG_FIELD(0x82, 0, 7),
	[Q_MAX_H2] = REG_FIELD(0x83, 0, 7),
	[Q_MAX_L1] = REG_FIELD(0x84, 0, 7),
	[Q_MAX_L0] = REG_FIELD(0x85, 0, 7),

	[Q_TERM_H3] = REG_FIELD(0x86, 0, 7),
	[Q_TERM_H2] = REG_FIELD(0x87, 0, 7),
	[Q_TERM_L1] = REG_FIELD(0x88, 0, 7),
	[Q_TERM_L0] = REG_FIELD(0x89, 0, 7),
	[Q_OCV_H3] = REG_FIELD(0x8A, 0, 7),
	[Q_OCV_H2] = REG_FIELD(0x8B, 0, 7),

	[Q_OCV_L1] = REG_FIELD(0x8C, 0, 7),
	[Q_OCV_L0] = REG_FIELD(0x8D, 0, 7),
	[OCV_CNT] = REG_FIELD(0x8E, 0, 7),
	[SLEEP_CON_SAMP_CUR_H] = REG_FIELD(0x8F, 0, 7),
	[SLEEP_CON_SAMP_CUR_L] = REG_FIELD(0x90, 0, 7),
	[CAL_OFFSET_H] = REG_FIELD(0x91, 0, 7),
	[CAL_OFFSET_L] = REG_FIELD(0x92, 0, 7),
	[VCALIB0_H] = REG_FIELD(0x93, 0, 7),
	[VCALIB0_L] = REG_FIELD(0x94, 0, 7),
	[VCALIB1_H] = REG_FIELD(0x95, 0, 7),
	[VCALIB1_L] = REG_FIELD(0x96, 0, 7),
	[IOFFSET_H] = REG_FIELD(0x97, 0, 7),
	[IOFFSET_L] = REG_FIELD(0x98, 0, 7),

	[BAT_R0] = REG_FIELD(0x99, 0, 7),
	[SOC_REG0] = REG_FIELD(0x9A, 0, 7),
	[SOC_REG1] = REG_FIELD(0x9B, 0, 7),
	[SOC_REG2] = REG_FIELD(0x9C, 0, 7),

	[REMAIN_CAP_REG0] = REG_FIELD(0x9D, 0, 7),
	[REMAIN_CAP_REG1] = REG_FIELD(0x9E, 0, 7),
	[REMAIN_CAP_REG2] = REG_FIELD(0x9F, 0, 7),
	[NEW_FCC_REG0] = REG_FIELD(0xA0, 0, 7),
	[NEW_FCC_REG1] = REG_FIELD(0xA1, 0, 7),
	[NEW_FCC_REG2] = REG_FIELD(0xA2, 0, 7),
	[RESET_MODE] = REG_FIELD(0xA3, 0, 3),
	[FG_INIT] = REG_FIELD(0xA5, 7, 7),

	[HALT_CNT_REG] = REG_FIELD(0xA6, 0, 7),
	[CALC_REST_REGL] = REG_FIELD(0xA7, 0, 7),
	[CALC_REST_REGH] = REG_FIELD(0xA8, 0, 7),

	[VOL_ADC_B3] = REG_FIELD(0xA9, 0, 7),
	[VOL_ADC_B2] = REG_FIELD(0xAA, 0, 7),
	[VOL_ADC_B1] = REG_FIELD(0xAB, 0, 7),
	[VOL_ADC_B0] = REG_FIELD(0xAC, 0, 7),

	[VOL_ADC_K3] = REG_FIELD(0xAD, 0, 7),
	[VOL_ADC_K2] = REG_FIELD(0xAE, 0, 7),
	[VOL_ADC_K1] = REG_FIELD(0xAF, 0, 7),
	[VOL_ADC_K0] = REG_FIELD(0xB0, 0, 7),
	[BAT_EXS] = REG_FIELD(0xEB, 7, 7),
	[CHG_STS] = REG_FIELD(0xEB, 4, 6),
	[BAT_OVP_STS] = REG_FIELD(0xEB, 3, 3),
	[CHRG_IN_CLAMP] = REG_FIELD(0xEB, 2, 2),
	[CHIP_NAME_H] = REG_FIELD(0xED, 0, 7),
	[CHIP_NAME_L] = REG_FIELD(0xEE, 0, 7),
	[PLUG_IN_STS] = REG_FIELD(0xF0, 6, 6),
};

struct battery_platform_data {
	u32 *ocv_table;
	u32 *zero_table;

	u32 table_t[4][21];
	int temp_t[4];
	u32 temp_t_num;

	u32 *ntc_table;
	u32 ocv_size;
	u32 ntc_size;
	int ntc_degree_from;
	u32 ntc_factor;
	u32 max_input_current;
	u32 max_chrg_current;
	u32 max_chrg_voltage;
	u32 lp_input_current;
	u32 lp_soc_min;
	u32 lp_soc_max;
	u32 pwroff_vol;
	u32 monitor_sec;
	u32 zero_algorithm_vol;
	u32 zero_reserve_dsoc;
	u32 bat_res;
	u32 design_capacity;
	u32 design_qmax;
	u32 sleep_enter_current;
	u32 sleep_exit_current;
	u32 sleep_filter_current;

	u32 power_dc2otg;
	u32 max_soc_offset;
	u32 bat_mode;
	u32 fb_temp;
	u32 energy_mode;
	u32 cccv_hour;
	u32 dc_det_adc;
	int dc_det_pin;
	u8  dc_det_level;
	u32 sample_res;
	u32 bat_res_up;
	u32 bat_res_down;
	u32 design_max_voltage;
	bool extcon;
	u32 low_pwr_sleep;
};

struct rk817_battery_device {
	struct platform_device		*pdev;
	struct device				*dev;
	struct i2c_client			*client;
	struct rk808			*rk817;
	struct power_supply			*bat;
	struct power_supply		*chg_psy;
	struct power_supply		*usb_psy;
	struct power_supply		*ac_psy;
	struct regmap			*regmap;
	struct regmap_field		*rmap_fields[F_MAX_FIELDS];
	struct battery_platform_data	*pdata;
	struct workqueue_struct		*bat_monitor_wq;
	struct delayed_work		bat_delay_work;
	struct delayed_work		calib_delay_work;
	struct work_struct		resume_work;
	struct wake_lock		wake_lock;
	struct timer_list		caltimer;

	int				res_div;
	int				bat_res;
	bool				is_first_power_on;
	int				chrg_status;
	int				res_fac;
	int				over_20mR;
	bool				is_initialized;
	bool				bat_first_power_on;
	u8				ac_in;
	u8				usb_in;
	u8				otg_in;
	u8				dc_in;
	u8				prop_status;
	int				cvtlmt_irq;
	int				current_avg;
	int				current_relax;
	int				voltage_usb;
	int				voltage_sys;
	int				voltage_avg;
	int				voltage_ocv;
	int				voltage_relax;
	int				voltage_k;/* VCALIB0 VCALIB1 */
	int				voltage_b;
	u32				remain_cap;
	int				design_cap;
	int				nac;
	int				fcc;
	int				lock_fcc;
	int				qmax;
	int				dsoc;
	int				rsoc;
	int				poffset;
	int				fake_offline;
	int				age_ocv_soc;
	bool				age_allow_update;
	int				age_level;
	int				age_ocv_cap;
	int				pwron_voltage;
	int				age_voltage;
	int				age_adjust_cap;
	unsigned long			age_keep_sec;
	int				zero_timeout_cnt;
	int				zero_remain_cap;
	int				zero_dsoc;
	int				zero_linek;
	u64				zero_drop_sec;
	u64				shtd_drop_sec;

	int				powerpatch_res;
	int				zero_voltage_avg;
	int				zero_current_avg;
	int				zero_vsys;
	int				zero_dead_voltage;
	int				zero_dead_soc;
	int				zero_dead_cap;
	int				zero_batvol_to_ocv;
	int				zero_batocv_to_soc;
	int				zero_batocv_to_cap;
	int				zero_xsoc;
	unsigned long			finish_base;
	time64_t			rtc_base;
	int				sm_remain_cap;
	int				sm_linek;
	int				sm_chrg_dsoc;
	int				sm_dischrg_dsoc;
	int				smooth_soc;
	int				algo_rest_val;
	int				algo_rest_mode;
	int				sleep_sum_cap;
	int				sleep_remain_cap;
	unsigned long			sleep_dischrg_sec;
	unsigned long			sleep_sum_sec;
	bool				sleep_chrg_online;
	u8				sleep_chrg_status;
	bool				adc_allow_update;
	int                             fb_blank;
	bool				s2r; /*suspend to resume*/
	u32				work_mode;
	int				temperature;
	int				chrg_cur_lp_input;
	int				chrg_vol_sel;
	int				chrg_cur_input;
	int				chrg_cur_sel;
	u32				monitor_ms;
	u32				pwroff_min;
	u32				adc_calib_cnt;
	unsigned long			chrg_finish_base;
	unsigned long			boot_base;
	unsigned long			flat_match_sec;
	unsigned long			plug_in_base;
	unsigned long			plug_out_base;
	u8				halt_cnt;
	bool				is_halt;
	bool				is_max_soc_offset;
	bool				is_sw_reset;
	bool				is_ocv_calib;
	bool				is_first_on;
	bool				is_force_calib;
	int				last_dsoc;
	u8				cvtlmt_int_event;
	u8				slp_dcdc_en_reg;
	int				ocv_pre_dsoc;
	int				ocv_new_dsoc;
	int				max_pre_dsoc;
	int				max_new_dsoc;
	int				force_pre_dsoc;
	int				force_new_dsoc;

	int				dbg_cap_low0;
	int				dbg_pwr_dsoc;
	int				dbg_pwr_rsoc;
	int				dbg_pwr_vol;
	int				dbg_chrg_min[10];
	int				dbg_meet_soc;
	int				dbg_calc_dsoc;
	int				dbg_calc_rsoc;
	int				is_charging;
	unsigned long			charge_count;
	u8				plugin_trigger;
	u8				plugout_trigger;
	int				plugin_irq;
	int				plugout_irq;
	int				chip_id;
	int				is_register_chg_psy;
	bool				change; /* Battery status change, report information */
};

static void rk817_bat_resume_work(struct work_struct *work);

static u64 get_boot_sec(void)
{
	struct timespec64 ts;

	ktime_get_boottime_ts64(&ts);

	return ts.tv_sec;
}

static unsigned long base2sec(unsigned long x)
{
	if (x)
		return (get_boot_sec() > x) ? (get_boot_sec() - x) : 0;
	else
		return 0;
}

static u32 interpolate(int value, u32 *table, int size)
{
	u8 i;
	u16 d;

	for (i = 0; i < size; i++) {
		if (value < table[i])
			break;
	}

	if ((i > 0) && (i < size)) {
		d = (value - table[i - 1]) * (MAX_INTERPOLATE / (size - 1));
		d /= table[i] - table[i - 1];
		d = d + (i - 1) * (MAX_INTERPOLATE / (size - 1));
	} else {
		d = i * ((MAX_INTERPOLATE + size / 2) / size);
	}

	if (d > 1000)
		d = 1000;

	return d;
}

/* (a * b) / c */
static int32_t ab_div_c(u32 a, u32 b, u32 c)
{
	bool sign;
	u32 ans = MAX_INT;
	int tmp;

	sign = ((((a ^ b) ^ c) & 0x80000000) != 0);
	if (c != 0) {
		if (sign)
			c = -c;
		tmp = (a * b + (c >> 1)) / c;
		if (tmp < MAX_INT)
			ans = tmp;
	}

	if (sign)
		ans = -ans;

	return ans;
}

static int rk817_bat_field_read(struct rk817_battery_device *battery,
				enum rk817_battery_fields field_id)
{
	int val;
	int ret;

	ret = regmap_field_read(battery->rmap_fields[field_id], &val);
	if (ret < 0)
		return ret;

	return val;
}

static int rk817_bat_field_write(struct rk817_battery_device *battery,
				 enum rk817_battery_fields field_id,
				 unsigned int val)
{
	return regmap_field_write(battery->rmap_fields[field_id], val);
}

/*cal_offset: current offset value*/
static int rk817_bat_get_coffset(struct rk817_battery_device *battery)
{
	int  coffset_value = 0;

	coffset_value |= rk817_bat_field_read(battery, CAL_OFFSET_H) << 8;
	coffset_value |= rk817_bat_field_read(battery, CAL_OFFSET_L);

	return coffset_value;
}

static void rk817_bat_set_coffset(struct rk817_battery_device *battery, int val)
{
	u8  buf = 0;

	buf = (val >> 8) & 0xff;
	rk817_bat_field_write(battery, CAL_OFFSET_H, buf);
	buf = (val >> 0) & 0xff;
	rk817_bat_field_write(battery, CAL_OFFSET_L, buf);
}

/* current offset value calculated */
static int rk817_bat_get_ioffset(struct rk817_battery_device *battery)
{
	int  ioffset_value = 0;

	ioffset_value |= rk817_bat_field_read(battery, IOFFSET_H) << 8;
	ioffset_value |= rk817_bat_field_read(battery, IOFFSET_L);

	return ioffset_value;
}

static void rk817_bat_current_calibration(struct rk817_battery_device *battery)
{
	int pwron_value, ioffset, cal_offset;

	pwron_value = rk817_bat_field_read(battery, PWRON_CUR_H) << 8;
	pwron_value |= rk817_bat_field_read(battery, PWRON_CUR_L);

	ioffset = rk817_bat_get_ioffset(battery);

	DBG("Caloffset: 0x%x\n", rk817_bat_get_coffset(battery));
	DBG("IOFFSET: 0x%x\n", ioffset);
	if (0)
		cal_offset = pwron_value + ioffset;
	else
		cal_offset = ioffset;

	rk817_bat_set_coffset(battery, cal_offset);
	DBG("Caloffset: 0x%x\n", rk817_bat_get_coffset(battery));

}

static int rk817_bat_get_vaclib0(struct rk817_battery_device *battery)
{
	int vcalib_value = 0;

	vcalib_value |= rk817_bat_field_read(battery, VCALIB0_H) << 8;
	vcalib_value |= rk817_bat_field_read(battery, VCALIB0_L);

	return vcalib_value;
}

static int rk817_bat_get_vaclib1(struct rk817_battery_device *battery)
{
	int vcalib_value = 0;

	vcalib_value |= rk817_bat_field_read(battery, VCALIB1_H) << 8;
	vcalib_value |= rk817_bat_field_read(battery, VCALIB1_L);

	return vcalib_value;
}

static void rk817_bat_init_voltage_kb(struct rk817_battery_device *battery)
{
	int vcalib0, vcalib1;

	vcalib0 = rk817_bat_get_vaclib0(battery);
	vcalib1 =  rk817_bat_get_vaclib1(battery);
	if (battery->chip_id == RK809_ID) {
		battery->voltage_k = (1050 - 600) * 1000 / DIV(vcalib1 - vcalib0);
		battery->voltage_b = 1050 - (battery->voltage_k * vcalib1) / 1000;
	} else {
		battery->voltage_k = (4025 - 2300) * 1000 / DIV(vcalib1 - vcalib0);
		battery->voltage_b = 4025 - (battery->voltage_k * vcalib1) / 1000;
	}
}

static void rk817_bat_restart_relax(struct rk817_battery_device *battery)
{
	rk817_bat_field_write(battery, RELAX_VOL1_UPD, 0x00);
	rk817_bat_field_write(battery, RELAX_VOL2_UPD, 0x00);
}

static bool is_rk817_bat_relax_mode(struct rk817_battery_device *battery)
{
	u8 relax_sts, relax_vol1_upd, relax_vol2_upd;

	relax_sts = rk817_bat_field_read(battery, RELAX_STS);
	relax_vol1_upd = rk817_bat_field_read(battery, RELAX_VOL1_UPD);
	relax_vol2_upd = rk817_bat_field_read(battery, RELAX_VOL2_UPD);

	DBG("RELAX_STS: %d\n", relax_sts);
	DBG("RELAX_VOL1_UPD: %d\n", relax_vol1_upd);
	DBG("RELAX_VOL2_UPD: %d\n", relax_vol2_upd);
	if (relax_sts && relax_vol1_upd && relax_vol2_upd)
		return true;
	else
		return false;
}

static u16 rk817_bat_get_relax_vol1(struct rk817_battery_device *battery)
{
	u16 vol, val = 0;

	val = rk817_bat_field_read(battery, RELAX_VOL1_H) << 8;
	val |= rk817_bat_field_read(battery, RELAX_VOL1_L);
	vol = battery->voltage_k * val / 1000 + battery->voltage_b;

	return vol;
}

static u16 rk817_bat_get_relax_vol2(struct rk817_battery_device *battery)
{
	u16 vol, val = 0;

	val = rk817_bat_field_read(battery, RELAX_VOL2_H) << 8;
	val |= rk817_bat_field_read(battery, RELAX_VOL2_L);
	vol = battery->voltage_k * val / 1000 + battery->voltage_b;

	return vol;
}

static u16 rk817_bat_get_relax_voltage(struct rk817_battery_device *battery)
{
	u16 relax_vol1, relax_vol2;

	if (!is_rk817_bat_relax_mode(battery))
		return 0;

	relax_vol1 = rk817_bat_get_relax_vol1(battery);
	relax_vol2 = rk817_bat_get_relax_vol2(battery);

	return relax_vol1 > relax_vol2 ? relax_vol1 : relax_vol2;
}

static void rk817_bat_set_relax_sample(struct rk817_battery_device *battery)
{
	u8 buf;
	int enter_thres, filter_thres;
	struct battery_platform_data *pdata = battery->pdata;

	filter_thres = pdata->sleep_filter_current * 1000 / 1506;

	enter_thres = CURRENT_TO_ADC(pdata->sleep_enter_current,
				     battery->res_div);
	filter_thres = CURRENT_TO_ADC(pdata->sleep_filter_current,
				      battery->res_div);

	/* set relax enter and exit threshold */
	buf = (enter_thres >> 8) & 0xff;
	rk817_bat_field_write(battery, RELAX_THRE_H, buf);
	buf = enter_thres & 0xff;
	rk817_bat_field_write(battery, RELAX_THRE_L, buf);
	/* set sample current threshold */
	buf = (filter_thres >> 8) & 0xff;
	rk817_bat_field_write(battery, SLEEP_CON_SAMP_CUR_H, buf);
	buf = filter_thres & 0xff;
	rk817_bat_field_write(battery, SLEEP_CON_SAMP_CUR_L, buf);

	/* reset relax update state */
	rk817_bat_restart_relax(battery);
	DBG("<%s>. sleep_enter_current = %d, sleep_exit_current = %d\n",
	    __func__, pdata->sleep_enter_current, pdata->sleep_exit_current);
}

/* runtime OCV voltage,  |RLX_VOL2 - RLX_VOL1| < OCV_THRE,
 * the OCV reg update every 120s
 */
static void rk817_bat_ocv_thre(struct rk817_battery_device *battery, int value)
{
	rk817_bat_field_write(battery, OCV_THRE_VOL, value);
}

static int rk817_bat_get_ocv_voltage(struct rk817_battery_device *battery)
{
	int vol, val = 0, vol_temp;

	val = rk817_bat_field_read(battery, OCV_VOL_H) << 8;
	val |= rk817_bat_field_read(battery, OCV_VOL_L);
	vol = battery->voltage_k * val / 1000 + battery->voltage_b;

	if (battery->chip_id == RK809_ID) {
		vol_temp = vol * battery->pdata->bat_res_up /
			   battery->pdata->bat_res_down + vol;
		vol = vol_temp;
	}

	return vol;
}

static int rk817_bat_get_ocv0_voltage0(struct rk817_battery_device *battery)
{
	int vol, val = 0, vol_temp;

	val = rk817_bat_field_read(battery, OCV_VOL0_H) << 8;
	val |= rk817_bat_field_read(battery, OCV_VOL0_L);
	vol = battery->voltage_k * val / 1000 + battery->voltage_b;
	if (battery->chip_id == RK809_ID) {
		vol_temp = vol * battery->pdata->bat_res_up /
			   battery->pdata->bat_res_down + vol;
		vol = vol_temp;
	}

	return vol;
}

/* power on battery voltage */
static int rk817_bat_get_pwron_voltage(struct rk817_battery_device *battery)
{
	int vol, val = 0, vol_temp;

	val = rk817_bat_field_read(battery, PWRON_VOL_H) << 8;
	val |= rk817_bat_field_read(battery, PWRON_VOL_L);
	vol = battery->voltage_k * val / 1000 + battery->voltage_b;
	if (battery->chip_id == RK809_ID) {
		vol_temp = vol * battery->pdata->bat_res_up /
			   battery->pdata->bat_res_down + vol;
		vol = vol_temp;
	}

	return vol;
}

static int rk817_bat_get_battery_voltage(struct rk817_battery_device *battery)
{
	int vol, val = 0, vol_temp;
	int vcalib0, vcalib1;

	vcalib0 = rk817_bat_get_vaclib0(battery);
	vcalib1 =  rk817_bat_get_vaclib1(battery);


	val = rk817_bat_field_read(battery, BAT_VOL_H) << 8;
	val |= rk817_bat_field_read(battery, BAT_VOL_L) << 0;

	vol = battery->voltage_k * val / 1000 + battery->voltage_b;

	if (battery->chip_id == RK809_ID) {
		vol_temp = vol * battery->pdata->bat_res_up /
			   battery->pdata->bat_res_down + vol;
		vol = vol_temp;
	}

	return vol;
}

static int rk817_bat_get_USB_voltage(struct rk817_battery_device *battery)
{
	int vol, val = 0, vol_temp;

	rk817_bat_field_write(battery, USB_VOL_ADC_EN, 0x01);

	val = rk817_bat_field_read(battery, USB_VOL_H) << 8;
	val |= rk817_bat_field_read(battery, USB_VOL_L) << 0;

	vol = (battery->voltage_k * val / 1000 + battery->voltage_b) * 60 / 46;

	if (battery->chip_id == RK809_ID) {
		vol_temp = vol * battery->pdata->bat_res_up /
			   battery->pdata->bat_res_down + vol;
		vol = vol_temp;
	}

	return vol;
}

static int rk817_bat_get_sys_voltage(struct rk817_battery_device *battery)
{
	int vol, val = 0, vol_temp;

	val = rk817_bat_field_read(battery, SYS_VOL_H) << 8;
	val |= rk817_bat_field_read(battery, SYS_VOL_L) << 0;

	vol = (battery->voltage_k * val / 1000 + battery->voltage_b) * 60 / 46;

	if (battery->chip_id == RK809_ID) {
		vol_temp = vol * battery->pdata->bat_res_up /
			   battery->pdata->bat_res_down + vol;
		vol = vol_temp;
	}

	return vol;
}

static int rk817_bat_get_avg_current(struct rk817_battery_device *battery)
{
	int cur, val = 0;

	val = rk817_bat_field_read(battery, BAT_CUR_H) << 8;
	val |= rk817_bat_field_read(battery, BAT_CUR_L);

	if (val & 0x8000)
		val -= 0x10000;

	cur = ADC_TO_CURRENT(val, battery->res_div);

	return cur;
}

static int rk817_bat_get_relax_cur1(struct rk817_battery_device *battery)
{
	int cur, val = 0;

	val = rk817_bat_field_read(battery, RELAX_CUR1_H) << 8;
	val |= rk817_bat_field_read(battery, RELAX_CUR1_L);

	if (val & 0x8000)
		val -= 0x10000;

	cur = ADC_TO_CURRENT(val, battery->res_div);

	return cur;
}

static int rk817_bat_get_relax_cur2(struct rk817_battery_device *battery)
{
	int cur, val = 0;

	val |= rk817_bat_field_read(battery, RELAX_CUR2_H) << 8;
	val = rk817_bat_field_read(battery, RELAX_CUR2_L);

	if (val & 0x8000)
		val -= 0x10000;

	cur = ADC_TO_CURRENT(val, battery->res_div);

	return cur;
}

static int rk817_bat_get_relax_current(struct rk817_battery_device *battery)
{
	int relax_cur1, relax_cur2;

	if (!is_rk817_bat_relax_mode(battery))
		return 0;

	relax_cur1 = rk817_bat_get_relax_cur1(battery);
	relax_cur2 = rk817_bat_get_relax_cur2(battery);

	return (relax_cur1 < relax_cur2) ? relax_cur1 : relax_cur2;
}

static int rk817_bat_get_ocv_current(struct rk817_battery_device *battery)
{
	int cur, val = 0;

	val = rk817_bat_field_read(battery, OCV_CUR_H) << 8;
	val |= rk817_bat_field_read(battery, OCV_CUR_L);

	if (val & 0x8000)
		val -= 0x10000;

	cur = ADC_TO_CURRENT(val, battery->res_div);

	return cur;
}

static int rk817_bat_get_ocv_current0(struct rk817_battery_device *battery)
{
	int cur, val = 0;

	val = rk817_bat_field_read(battery, OCV_CUR0_H) << 8;
	val |= rk817_bat_field_read(battery, OCV_CUR0_L);

	if (val & 0x8000)
		val -= 0x10000;

	cur = ADC_TO_CURRENT(val, battery->res_div);

	return cur;
}

static int rk817_bat_get_pwron_current(struct rk817_battery_device *battery)
{
	int cur, val = 0;

	val = rk817_bat_field_read(battery, PWRON_CUR_H) << 8;
	val |= rk817_bat_field_read(battery, PWRON_CUR_L);

	if (val & 0x8000)
		val -= 0x10000;
	cur = ADC_TO_CURRENT(val, battery->res_div);

	return cur;
}

static bool rk817_bat_remain_cap_is_valid(struct rk817_battery_device *battery)
{
	return !(rk817_bat_field_read(battery, Q_PRESS_H3) & CAP_INVALID);
}

static u32 rk817_bat_get_capacity_uah(struct rk817_battery_device *battery)
{
	u32 val = 0, capacity = 0;

	if (rk817_bat_remain_cap_is_valid(battery)) {
		val = rk817_bat_field_read(battery, Q_PRESS_H3) << 24;
		val |= rk817_bat_field_read(battery, Q_PRESS_H2) << 16;
		val |= rk817_bat_field_read(battery, Q_PRESS_L1) << 8;
		val |= rk817_bat_field_read(battery, Q_PRESS_L0) << 0;

		capacity = ADC_TO_CAPACITY_UAH(val, battery->res_div);
	}

	DBG("xxxxxxxxxxxxx capacity = %d\n", capacity);
	return  capacity;
}

static u32 rk817_bat_get_capacity_mah(struct rk817_battery_device *battery)
{
	u32 val, capacity = 0;

	if (rk817_bat_remain_cap_is_valid(battery)) {
		val = rk817_bat_field_read(battery, Q_PRESS_H3) << 24;
		val |= rk817_bat_field_read(battery, Q_PRESS_H2) << 16;
		val |= rk817_bat_field_read(battery, Q_PRESS_L1) << 8;
		val |= rk817_bat_field_read(battery, Q_PRESS_L0) << 0;

		capacity = ADC_TO_CAPACITY(val, battery->res_div);
	}
	DBG("Q_PRESS_H3 = 0x%x\n", rk817_bat_field_read(battery, Q_PRESS_H3));
	DBG("Q_PRESS_H2 = 0x%x\n", rk817_bat_field_read(battery, Q_PRESS_H2));
	DBG("Q_PRESS_H1 = 0x%x\n", rk817_bat_field_read(battery, Q_PRESS_L1));
	DBG("Q_PRESS_H0 = 0x%x\n", rk817_bat_field_read(battery, Q_PRESS_L0));

	DBG("xxxxxxxxxxxxx capacity = %d\n", capacity);
	return  capacity;
}

static void  fuel_gauge_q_init_info(struct rk817_battery_device *battery)
{
	DBG("Q_INIT_H3 = 0x%x\n", rk817_bat_field_read(battery, Q_INIT_H3));
	DBG("Q_INIT_H2 = 0x%x\n", rk817_bat_field_read(battery, Q_INIT_H2));
	DBG("Q_INIT_L1 = 0x%x\n", rk817_bat_field_read(battery, Q_INIT_L1));
	DBG("Q_INIT_L0 = 0x%x\n", rk817_bat_field_read(battery, Q_INIT_L0));
}

static void rk817_bat_init_coulomb_cap(struct rk817_battery_device *battery,
				       u32 capacity)
{
	u8 buf;
	u32 cap;

	fuel_gauge_q_init_info(battery);
	cap = CAPACITY_TO_ADC(capacity, battery->res_div);
	DBG("new cap: 0x%x\n", cap);
	buf = (cap >> 24) & 0xff;
	rk817_bat_field_write(battery, Q_INIT_H3, buf);
	buf = (cap >> 16) & 0xff;
	rk817_bat_field_write(battery, Q_INIT_H2, buf);
	buf = (cap >> 8) & 0xff;
	rk817_bat_field_write(battery, Q_INIT_L1, buf);
	buf = (cap >> 0) & 0xff;
	rk817_bat_field_write(battery, Q_INIT_L0, buf);

	battery->rsoc = capacity * 1000 * 100 / DIV(battery->fcc);
	battery->remain_cap = capacity * 1000;
	DBG("new remaincap: %d\n", battery->remain_cap);
	fuel_gauge_q_init_info(battery);
}

static void rk817_bat_save_cap(struct rk817_battery_device *battery,
			       int capacity)
{
	u8 buf;
	static u32 old_cap;

	if (capacity >= battery->qmax)
		capacity = battery->qmax;
	if (capacity <= 0)
		capacity = 0;
	if (old_cap == capacity)
		return;

	old_cap = capacity;
	buf = (capacity >> 16) & 0xff;
	rk817_bat_field_write(battery, REMAIN_CAP_REG2, buf);
	buf = (capacity >> 8) & 0xff;
	rk817_bat_field_write(battery, REMAIN_CAP_REG1, buf);
	buf = (capacity >> 0) & 0xff;
	rk817_bat_field_write(battery, REMAIN_CAP_REG0, buf);
}

static void rk817_bat_update_qmax(struct rk817_battery_device *battery,
				  u32 capacity)
{
	u8 buf;
	u32 cap_adc;

	cap_adc = CAPACITY_TO_ADC(capacity, battery->res_div);
	buf = (cap_adc >> 24) & 0xff;
	rk817_bat_field_write(battery, Q_MAX_H3, buf);
	buf = (cap_adc >> 16) & 0xff;
	rk817_bat_field_write(battery, Q_MAX_H2, buf);
	buf = (cap_adc >> 8) & 0xff;
	rk817_bat_field_write(battery, Q_MAX_L1, buf);
	buf = (cap_adc >> 0) & 0xff;
	rk817_bat_field_write(battery, Q_MAX_L0, buf);
	 battery->qmax = capacity;
}

static int rk817_bat_get_qmax(struct rk817_battery_device *battery)
{
	u32 capacity;
	int val = 0;

	val = rk817_bat_field_read(battery, Q_MAX_H3) << 24;
	val |= rk817_bat_field_read(battery, Q_MAX_H2) << 16;
	val |= rk817_bat_field_read(battery, Q_MAX_L1) << 8;
	val |= rk817_bat_field_read(battery, Q_MAX_L0) << 0;
	capacity = ADC_TO_CAPACITY(val, battery->res_div);
	battery->qmax = capacity;
	return capacity;
}

static void rk817_bat_save_fcc(struct rk817_battery_device *battery, int  fcc)
{
	u8 buf;

	buf = (fcc >> 16) & 0xff;
	rk817_bat_field_write(battery, NEW_FCC_REG2, buf);
	buf = (fcc >> 8) & 0xff;
	rk817_bat_field_write(battery, NEW_FCC_REG1, buf);
	buf = (fcc >> 0) & 0xff;
	rk817_bat_field_write(battery, NEW_FCC_REG0, buf);
}

static int rk817_bat_get_fcc(struct rk817_battery_device *battery)
{
	u32 fcc = 0;

	fcc |= rk817_bat_field_read(battery, NEW_FCC_REG2) << 16;
	fcc |= rk817_bat_field_read(battery, NEW_FCC_REG1) << 8;
	fcc |= rk817_bat_field_read(battery, NEW_FCC_REG0) << 0;

	if (fcc < MIN_FCC) {
		DBG("invalid fcc(%d), use design cap", fcc);
		fcc = battery->pdata->design_capacity;
		rk817_bat_save_fcc(battery, fcc);
	} else if (fcc > battery->pdata->design_qmax) {
		DBG("invalid fcc(%d), use qmax", fcc);
		fcc = battery->pdata->design_qmax;
		rk817_bat_save_fcc(battery, fcc);
	}

	return fcc;
}

static int rk817_bat_get_rsoc(struct rk817_battery_device *battery)
{
	int remain_cap;

	remain_cap = rk817_bat_get_capacity_uah(battery);

	return remain_cap * 100 / DIV(battery->fcc);
}

static int rk817_bat_get_off_count(struct rk817_battery_device *battery)
{
	return rk817_bat_field_read(battery, OFF_CNT);
}

static int rk817_bat_get_ocv_count(struct rk817_battery_device *battery)
{
	return rk817_bat_field_read(battery, OCV_CNT);
}

static int rk817_bat_vol_to_soc(struct rk817_battery_device *battery,
				int voltage)
{
	u32 *ocv_table, temp;
	int ocv_size, ocv_soc;

	ocv_table = battery->pdata->ocv_table;
	ocv_size = battery->pdata->ocv_size;
	temp = interpolate(voltage, ocv_table, ocv_size);
	ocv_soc = ab_div_c(temp, MAX_PERCENTAGE, MAX_INTERPOLATE);

	return ocv_soc;
}

static int rk817_bat_vol_to_cap(struct rk817_battery_device *battery,
				int voltage)
{
	u32 *ocv_table, temp;
	int ocv_size, capacity;

	ocv_table = battery->pdata->ocv_table;
	ocv_size = battery->pdata->ocv_size;
	temp = interpolate(voltage, ocv_table, ocv_size);
	capacity = ab_div_c(temp, battery->fcc, MAX_INTERPOLATE);

	return capacity;
}

static void rk817_bat_save_dsoc(struct rk817_battery_device *battery,
				int save_soc)
{
	static int last_soc = -1;

	if (last_soc != save_soc) {
		rk817_bat_field_write(battery, SOC_REG0,
				      save_soc & 0xff);
		rk817_bat_field_write(battery, SOC_REG1,
				      (save_soc >> 8) & 0xff);
		rk817_bat_field_write(battery, SOC_REG2,
				      (save_soc >> 16) & 0xff);

		last_soc = save_soc;
	}
}

static int rk817_bat_get_prev_dsoc(struct rk817_battery_device *battery)
{
	int soc_save;

	soc_save = rk817_bat_field_read(battery, SOC_REG0);
	soc_save |= (rk817_bat_field_read(battery, SOC_REG1) << 8);
	soc_save |= (rk817_bat_field_read(battery, SOC_REG2) << 16);

	return soc_save;
}

static bool is_rk817_bat_first_pwron(struct rk817_battery_device *battery)
{
	if (rk817_bat_field_read(battery, BAT_CON)) {
		rk817_bat_field_write(battery, BAT_CON, 0x00);
		return true;
	}

	return false;
}

static int rk817_bat_get_charge_status(struct rk817_battery_device *battery)
{
	int status;

	if (battery->chip_id == RK809_ID) {
		if ((battery->voltage_avg > battery->pdata->design_max_voltage) &&
		    (battery->current_avg > 0) &&
		    ((battery->current_avg < 500) ||
		     (battery->rsoc / 1000 == 100)))
			return CHARGE_FINISH;

		if (battery->plugin_trigger)
			return CC_OR_CV_CHRG;
		else
			return CHRG_OFF;
	}
	status = rk817_bat_field_read(battery, CHG_STS);

	if (status == CC_OR_CV_CHRG) {
		if (battery->rsoc == 100 * 1000) {
			DBG("charge to finish\n");
			status = CHARGE_FINISH;
		}
	}

	switch (status) {
	case CHRG_OFF:
		DBG("charge off...\n");
		break;
	case DEAD_CHRG:
		DBG("dead charge...\n");
		break;
	case TRICKLE_CHRG:
		DBG("trickle charge...\n");
		break;
	case CC_OR_CV_CHRG:
		DBG("CC or CV charge...\n");
		break;
	case CHARGE_FINISH:
		DBG("charge finish...\n");
		break;
	case USB_OVER_VOL:
		DBG("USB over voltage...\n");
		break;
	case BAT_TMP_ERR:
		DBG("battery temperature error...\n");
		break;
	case BAT_TIM_ERR:
		DBG("battery timer error..\n");
		break;
	default:
		break;
	}

	return status;
}

/*
 * cccv and finish switch all the time will cause dsoc freeze,
 * if so, do finish chrg, 100ma is less than min finish_ma.
 */
static bool rk817_bat_fake_finish_mode(struct rk817_battery_device *battery)
{
	if ((battery->rsoc == 100) &&
	    (rk817_bat_get_charge_status(battery) == CC_OR_CV_CHRG) &&
	    (abs(battery->current_avg) <= 100))
		return true;
	else
		return false;
}

static int get_charge_status(struct rk817_battery_device *battery)
{
	return rk817_bat_get_charge_status(battery);
}

static bool is_rk817_bat_ocv_valid(struct rk817_battery_device *battery)
{
	return (!battery->is_initialized && battery->pwroff_min >= 30);
}

static void rk817_bat_gas_gaugle_enable(struct rk817_battery_device *battery)
{
		rk817_bat_field_write(battery, GG_EN, ENABLE);
}

static void rk817_bat_gg_con_init(struct rk817_battery_device *battery)
{
	rk817_bat_field_write(battery, RLX_SPT, S_8_MIN);
	rk817_bat_field_write(battery, ADC_OFF_CAL_INTERV, S_8_MIN);
	rk817_bat_field_write(battery, VOL_OUT_MOD, AVERAGE_MODE);
	rk817_bat_field_write(battery, CUR_OUT_MOD, AVERAGE_MODE);
}

static void  rk817_bat_adc_init(struct rk817_battery_device *battery)
{
	rk817_bat_field_write(battery, SYS_VOL_ADC_EN, ENABLE);
	rk817_bat_field_write(battery, TS_ADC_EN, ENABLE);
	rk817_bat_field_write(battery, USB_VOL_ADC_EN, ENABLE);
	rk817_bat_field_write(battery, BAT_VOL_ADC_EN, ENABLE);
	rk817_bat_field_write(battery, BAT_CUR_ADC_EN, ENABLE);
}

static void rk817_bat_init_info(struct rk817_battery_device *battery)
{
	battery->design_cap = battery->pdata->design_capacity;
	battery->qmax = battery->pdata->design_qmax;
	battery->bat_res = battery->pdata->bat_res;
	battery->monitor_ms = battery->pdata->monitor_sec * TIMER_MS_COUNTS;
	battery->res_div = (battery->pdata->sample_res == SAMPLE_RES_20MR) ?
		       SAMPLE_RES_DIV2 : SAMPLE_RES_DIV1;
	DBG("battery->qmax :%d\n", battery->qmax);
}

static int rk817_bat_get_prev_cap(struct rk817_battery_device *battery)
{
	int val = 0;

	val = rk817_bat_field_read(battery, REMAIN_CAP_REG2) << 16;
	val |= rk817_bat_field_read(battery, REMAIN_CAP_REG1) << 8;
	val |= rk817_bat_field_read(battery, REMAIN_CAP_REG0) << 0;

	return val;
}

static u8 rk817_bat_get_halt_cnt(struct rk817_battery_device *battery)
{
	return rk817_bat_field_read(battery, HALT_CNT_REG);
}

static void rk817_bat_inc_halt_cnt(struct rk817_battery_device *battery)
{
	u8 cnt;

	cnt =  rk817_bat_field_read(battery, HALT_CNT_REG);
	rk817_bat_field_write(battery, HALT_CNT_REG, ++cnt);
}

static bool is_rk817_bat_last_halt(struct rk817_battery_device *battery)
{
	int pre_cap = rk817_bat_get_prev_cap(battery);
	int now_cap = rk817_bat_get_capacity_mah(battery);

	/* over 10%: system halt last time */
	if (abs(now_cap - pre_cap) > (battery->fcc / 10)) {
		rk817_bat_inc_halt_cnt(battery);
		return true;
	} else {
		return false;
	}
}

static u8 is_rk817_bat_initialized(struct rk817_battery_device *battery)
{
	u8 val = rk817_bat_field_read(battery, FG_INIT);

	if (val) {
		rk817_bat_field_write(battery, FG_INIT, 0x00);
		return true;
	} else {
		return false;
	}
}

static void rk817_bat_calc_sm_linek(struct rk817_battery_device *battery)
{
	int linek;
	int diff, delta;
	int current_avg = rk817_bat_get_avg_current(battery);

	delta = abs(battery->dsoc - battery->rsoc);
	diff = delta * 3;/* speed:3/4 */

	if (current_avg > 0) {
		if (battery->dsoc < battery->rsoc)
			linek = 1000 * (delta + diff) / DIV(diff);
		else if (battery->dsoc > battery->rsoc)
			linek = 1000 * diff / DIV(delta + diff);
		else
			linek = 1000;
	} else {
		if (battery->dsoc < battery->rsoc)
			linek = -1000 * diff / DIV(delta + diff);
		else if (battery->dsoc > battery->rsoc)
			linek = -1000 * (delta + diff) / DIV(diff);
		else
			linek = -1000;
	}

	battery->dbg_meet_soc = (battery->dsoc >= battery->rsoc) ?
		(battery->dsoc - diff) : (battery->rsoc - diff);

	battery->sm_linek = linek;
	battery->sm_remain_cap = battery->remain_cap;
	battery->dbg_calc_dsoc = battery->dsoc;
	battery->dbg_calc_rsoc = battery->rsoc;
}

static void rk817_bat_smooth_algo_prepare(struct rk817_battery_device *battery)
{
	battery->smooth_soc = battery->dsoc;

	DBG("<%s>. dsoc=%d, dsoc:smooth_soc=%d\n",
	    __func__, battery->dsoc, battery->smooth_soc);
	rk817_bat_calc_sm_linek(battery);
}

static void rk817_bat_finish_algo_prepare(struct rk817_battery_device *battery)
{
	battery->finish_base = get_boot_sec();

	if (!battery->finish_base)
		battery->finish_base = 1;
}

static void rk817_bat_init_dsoc_algorithm(struct rk817_battery_device *battery)
{
	if (battery->dsoc >= 100 * 1000)
		battery->dsoc = 100 * 1000;
	else if (battery->dsoc <= 0)
		battery->dsoc = 0;
	/* init current mode */
	battery->voltage_avg = rk817_bat_get_battery_voltage(battery);
	battery->current_avg = rk817_bat_get_avg_current(battery);

	if (get_charge_status(battery) == CHARGE_FINISH) {
		rk817_bat_finish_algo_prepare(battery);
		battery->work_mode = MODE_FINISH;
	} else {
		rk817_bat_smooth_algo_prepare(battery);
		battery->work_mode = MODE_SMOOTH;
	}
	DBG("%s, sm_remain_cap = %d, smooth_soc = %d\n",
	    __func__, battery->sm_remain_cap, battery->smooth_soc);
}

static void rk817_bat_first_pwron(struct rk817_battery_device *battery)
{
	battery->rsoc =
		rk817_bat_vol_to_soc(battery,
				     battery->pwron_voltage) * 1000;/* uAH */
	battery->dsoc = battery->rsoc;
	battery->fcc	= battery->pdata->design_capacity;
	battery->nac = rk817_bat_vol_to_cap(battery, battery->pwron_voltage);

	rk817_bat_update_qmax(battery, battery->qmax);
	rk817_bat_save_fcc(battery, battery->fcc);
	DBG("%s, rsoc = %d, dsoc = %d, fcc = %d, nac = %d\n",
	    __func__, battery->rsoc, battery->dsoc, battery->fcc, battery->nac);
}

static void rk817_bat_not_first_pwron(struct rk817_battery_device *battery)
{
	int now_cap, pre_soc, pre_cap, ocv_cap, ocv_soc, ocv_vol;

	battery->fcc = rk817_bat_get_fcc(battery);
	pre_soc = rk817_bat_get_prev_dsoc(battery);
	pre_cap = rk817_bat_get_prev_cap(battery);
	now_cap = rk817_bat_get_capacity_mah(battery);
	battery->remain_cap = pre_cap * 1000;
	battery->is_halt = is_rk817_bat_last_halt(battery);
	battery->halt_cnt = rk817_bat_get_halt_cnt(battery);
	battery->is_initialized = is_rk817_bat_initialized(battery);
	battery->is_ocv_calib = is_rk817_bat_ocv_valid(battery);

	if (battery->is_halt) {
		BAT_INFO("system halt last time... cap: pre=%d, now=%d\n",
			 pre_cap, now_cap);
		if (now_cap < 0)
			now_cap = 0;
		rk817_bat_init_coulomb_cap(battery, now_cap);
		pre_cap = now_cap;
		pre_soc = battery->rsoc;
		goto finish;
	} else if (battery->is_initialized) {
		/* uboot initialized */
		BAT_INFO("initialized yet..\n");
		goto finish;
	} else if (battery->is_ocv_calib) {
		/* not initialized and poweroff_cnt above 30 min */
		ocv_vol = rk817_bat_get_ocv_voltage(battery);
		ocv_soc = rk817_bat_vol_to_soc(battery, ocv_vol);
		ocv_cap = rk817_bat_vol_to_cap(battery, ocv_vol);
		pre_cap = ocv_cap;
		battery->ocv_pre_dsoc = pre_soc;
		battery->ocv_new_dsoc = ocv_soc;
		if (abs(ocv_soc - pre_soc) >= battery->pdata->max_soc_offset) {
			battery->ocv_pre_dsoc = pre_soc;
			battery->ocv_new_dsoc = ocv_soc;
			battery->is_max_soc_offset = true;
			BAT_INFO("trigger max soc offset, dsoc: %d -> %d\n",
				 pre_soc, ocv_soc);
			pre_soc = ocv_soc;
		}
		BAT_INFO("OCV calib: cap=%d, rsoc=%d\n", ocv_cap, ocv_soc);
	} else if (battery->pwroff_min > 0) {
		ocv_vol = rk817_bat_get_ocv_voltage(battery);
		ocv_soc = rk817_bat_vol_to_soc(battery, ocv_vol);
		ocv_cap = rk817_bat_vol_to_cap(battery, ocv_vol);
		battery->force_pre_dsoc = pre_soc;
		battery->force_new_dsoc = ocv_soc;
		if (abs(ocv_soc - pre_soc) >= 80) {
			battery->is_force_calib = true;
			BAT_INFO("dsoc force calib: %d -> %d\n",
				 pre_soc, ocv_soc);
			pre_soc = ocv_soc;
			pre_cap = ocv_cap;
		}
	}
finish:
	battery->dsoc = pre_soc;
	battery->nac = pre_cap;
	if (battery->nac < 0)
		battery->nac = 0;

	DBG("dsoc=%d cap=%d v=%d ov=%d rv=%d min=%d psoc=%d pcap=%d\n",
	    battery->dsoc, battery->nac, rk817_bat_get_battery_voltage(battery),
	    rk817_bat_get_ocv_voltage(battery),
	    rk817_bat_get_relax_voltage(battery),
	    battery->pwroff_min, rk817_bat_get_prev_dsoc(battery),
	    rk817_bat_get_prev_cap(battery));
}

static void rk817_bat_rsoc_init(struct rk817_battery_device *battery)
{
	battery->is_first_power_on = is_rk817_bat_first_pwron(battery);
	battery->pwroff_min = rk817_bat_get_off_count(battery);
	battery->pwron_voltage = rk817_bat_get_pwron_voltage(battery);

	DBG("%s, is_first_power_on = %d, pwroff_min = %d, pwron_voltage = %d\n",
	    __func__, battery->is_first_power_on,
	    battery->pwroff_min, battery->pwron_voltage);

	if (battery->is_first_power_on)
		rk817_bat_first_pwron(battery);
	else
		rk817_bat_not_first_pwron(battery);

	 rk817_bat_save_dsoc(battery, battery->dsoc);
}

static void rk817_bat_caltimer_isr(struct timer_list *t)
{
	struct rk817_battery_device *battery =
		from_timer(battery, t, caltimer);

	mod_timer(&battery->caltimer, jiffies + MINUTE(8) * HZ);
	queue_delayed_work(battery->bat_monitor_wq,
			   &battery->calib_delay_work,
			   msecs_to_jiffies(10));
}

static void rk817_bat_internal_calib(struct work_struct *work)
{
	struct rk817_battery_device *battery = container_of(work,
			struct rk817_battery_device, calib_delay_work.work);

	return;

	rk817_bat_current_calibration(battery);
	/* calib voltage kb */
	rk817_bat_init_voltage_kb(battery);

	DBG("caltimer:coffset=0x%x\n", rk817_bat_get_coffset(battery));
}

static void rk817_bat_init_caltimer(struct rk817_battery_device *battery)
{
	timer_setup(&battery->caltimer,
		    rk817_bat_caltimer_isr,
		    0);
	battery->caltimer.expires = jiffies + MINUTE(8) * HZ;
	add_timer(&battery->caltimer);
	INIT_DELAYED_WORK(&battery->calib_delay_work, rk817_bat_internal_calib);
}

static void rk817_bat_init_fg(struct rk817_battery_device *battery)
{
	rk817_bat_adc_init(battery);
	rk817_bat_gas_gaugle_enable(battery);
	rk817_bat_gg_con_init(battery);
	rk817_bat_init_voltage_kb(battery);
	rk817_bat_set_relax_sample(battery);
	rk817_bat_ocv_thre(battery, 0xff);
	rk817_bat_init_caltimer(battery);
	rk817_bat_rsoc_init(battery);
	rk817_bat_init_coulomb_cap(battery, battery->nac);
	DBG("rsoc%d, fcc = %d\n", battery->rsoc, battery->fcc);
	rk817_bat_init_dsoc_algorithm(battery);
	battery->qmax = rk817_bat_get_qmax(battery);
	battery->voltage_avg = rk817_bat_get_battery_voltage(battery);
	battery->voltage_sys = rk817_bat_get_sys_voltage(battery);

	battery->voltage_ocv = rk817_bat_get_ocv_voltage(battery);
	battery->voltage_relax = rk817_bat_get_relax_voltage(battery);
	battery->current_avg = rk817_bat_get_avg_current(battery);
	battery->dbg_pwr_dsoc = battery->dsoc;
	battery->dbg_pwr_rsoc = battery->rsoc;
	battery->dbg_pwr_vol = battery->voltage_avg;
	battery->temperature = VIRTUAL_TEMPERATURE;

	DBG("probe init: battery->dsoc = %d, rsoc = %d\n"
	    "remain_cap = %d\n, battery_vol = %d\n, system_vol = %d, qmax = %d\n",
	    battery->dsoc, battery->rsoc, battery->remain_cap,
	    battery->voltage_avg, battery->voltage_sys, battery->qmax);
	DBG("OCV_THRE_VOL: 0x%x", rk817_bat_field_read(battery, OCV_THRE_VOL));
}

static int rk817_bat_parse_dt(struct rk817_battery_device *battery)
{
	u32 out_value;
	int length, ret;
	size_t size;
	struct battery_platform_data *pdata;
	struct device *dev = battery->dev;
	struct device_node *np = battery->dev->of_node;

	pdata = devm_kzalloc(battery->dev, sizeof(*pdata), GFP_KERNEL);
	if (!pdata)
		return -ENOMEM;

	battery->pdata = pdata;
	/* init default param */
	pdata->bat_res = DEFAULT_BAT_RES;
	pdata->monitor_sec = DEFAULT_MONITOR_SEC;
	pdata->pwroff_vol = DEFAULT_PWROFF_VOL_THRESD;
	pdata->sleep_exit_current = DEFAULT_SLP_EXIT_CUR;
	pdata->sleep_enter_current = DEFAULT_SLP_ENTER_CUR;

	pdata->sleep_filter_current = DEFAULT_SLP_FILTER_CUR;
	pdata->bat_mode = MODE_BATTARY;
	pdata->max_soc_offset = DEFAULT_MAX_SOC_OFFSET;
	pdata->fb_temp = DEFAULT_FB_TEMP;
	pdata->energy_mode = DEFAULT_ENERGY_MODE;
	pdata->zero_reserve_dsoc = DEFAULT_ZERO_RESERVE_DSOC * 1000;

	pdata->sample_res = DEFAULT_SAMPLE_RES;

	/* parse necessary param */
	if (!of_find_property(np, "ocv_table", &length)) {
		dev_err(dev, "ocv_table not found!\n");
		return -EINVAL;
	}

	pdata->ocv_size = length / sizeof(u32);
	if (pdata->ocv_size <= 0) {
		dev_err(dev, "invalid ocv table\n");
		return -EINVAL;
	}

	size = sizeof(*pdata->ocv_table) * pdata->ocv_size;
	pdata->ocv_table = devm_kzalloc(battery->dev, size, GFP_KERNEL);
	if (!pdata->ocv_table)
		return -ENOMEM;

	ret = of_property_read_u32_array(np, "ocv_table", pdata->ocv_table,
					 pdata->ocv_size);
	if (ret < 0)
		return ret;

	ret = of_property_read_u32(np, "design_capacity", &out_value);
	if (ret < 0) {
		dev_err(dev, "design_capacity not found!\n");
		return ret;
	}
	pdata->design_capacity = out_value;

	ret = of_property_read_u32(np, "design_qmax", &out_value);
	if (ret < 0) {
		dev_err(dev, "design_qmax not found!\n");
		return ret;
	}
	pdata->design_qmax = out_value;

	/* parse unnecessary param */
	ret = of_property_read_u32(np, "sample_res", &pdata->sample_res);
	if (ret < 0)
		dev_err(dev, "sample_res missing!\n");

	ret = of_property_read_u32(np, "fb_temperature", &pdata->fb_temp);
	if (ret < 0)
		dev_err(dev, "fb_temperature missing!\n");

	ret = of_property_read_u32(np, "energy_mode", &pdata->energy_mode);
	if (ret < 0)
		dev_err(dev, "energy_mode missing!\n");

	ret = of_property_read_u32(np, "max_soc_offset",
				   &pdata->max_soc_offset);
	if (ret < 0)
		dev_err(dev, "max_soc_offset missing!\n");

	ret = of_property_read_u32(np, "monitor_sec", &pdata->monitor_sec);
	if (ret < 0)
		dev_err(dev, "monitor_sec missing!\n");

	ret = of_property_read_u32(np, "zero_algorithm_vol",
				   &pdata->zero_algorithm_vol);
	if (ret < 0)
		dev_err(dev, "zero_algorithm_vol missing!\n");

	ret = of_property_read_u32(np, "zero_reserve_dsoc",
				   &pdata->zero_reserve_dsoc);
	if (ret < 0)
		dev_err(dev, "zero_reserve_dsoc missing!\n");
	pdata->zero_reserve_dsoc *= 1000;

	ret = of_property_read_u32(np, "virtual_power", &pdata->bat_mode);
	if (ret < 0)
		dev_err(dev, "virtual_power missing!\n");

	ret = of_property_read_u32(np, "bat_res", &pdata->bat_res);
	if (ret < 0)
		dev_err(dev, "bat_res missing!\n");

	ret = of_property_read_u32(np, "sleep_enter_current",
				   &pdata->sleep_enter_current);
	if (ret < 0)
		dev_err(dev, "sleep_enter_current missing!\n");

	ret = of_property_read_u32(np, "sleep_exit_current",
				   &pdata->sleep_exit_current);
	if (ret < 0)
		dev_err(dev, "sleep_exit_current missing!\n");

	ret = of_property_read_u32(np, "sleep_filter_current",
				   &pdata->sleep_filter_current);
	if (ret < 0)
		dev_err(dev, "sleep_filter_current missing!\n");

	ret = of_property_read_u32(np, "power_off_thresd", &pdata->pwroff_vol);
	if (ret < 0)
		dev_err(dev, "power_off_thresd missing!\n");

	ret = of_property_read_u32(np, "low_power_sleep", &pdata->low_pwr_sleep);
	if (ret < 0)
		dev_info(dev, "low_power_sleep missing!\n");

	if (battery->chip_id == RK809_ID) {
		ret = of_property_read_u32(np, "bat_res_up",
					   &pdata->bat_res_up);
		if (ret < 0)
			dev_err(dev, "battery res_up missing\n");

		ret = of_property_read_u32(np, "bat_res_down",
					   &pdata->bat_res_down);
		if (ret < 0)
			dev_err(dev, "battery res_down missing!\n");

		ret = of_property_read_u32(np, "design_max_voltage",
					   &pdata->design_max_voltage);
		if (ret < 0)
			dev_err(dev, "battery design_max_voltage missing!\n");

		ret = of_property_read_u32(np, "register_chg_psy",
					   &battery->is_register_chg_psy);
		if (ret < 0 || !battery->is_register_chg_psy)
			dev_err(dev, "not have to register chg psy!\n");
	}

	DBG("the battery dts info dump:\n"
	    "bat_res:%d\n"
	    "res_sample:%d\n"
	    "design_capacity:%d\n"
	    "design_qmax :%d\n"
	    "sleep_enter_current:%d\n"
	    "sleep_exit_current:%d\n"
	    "sleep_filter_current:%d\n"
	    "zero_algorithm_vol:%d\n"
	    "zero_reserve_dsoc:%d\n"
	    "monitor_sec:%d\n"
	    "max_soc_offset:%d\n"
	    "virtual_power:%d\n"
	    "pwroff_vol:%d\n",
	    pdata->bat_res,
	    pdata->sample_res,
	    pdata->design_capacity,
	    pdata->design_qmax,
	    pdata->sleep_enter_current,
	    pdata->sleep_exit_current,
	    pdata->sleep_filter_current,
	    pdata->zero_algorithm_vol,
	    pdata->zero_reserve_dsoc,
	    pdata->monitor_sec,
	    pdata->max_soc_offset,
	    pdata->bat_mode,
	    pdata->pwroff_vol);

	return 0;
}

static enum power_supply_property rk817_bat_props[] = {
	POWER_SUPPLY_PROP_STATUS,
	POWER_SUPPLY_PROP_CURRENT_NOW,
	POWER_SUPPLY_PROP_VOLTAGE_NOW,
	POWER_SUPPLY_PROP_HEALTH,
	POWER_SUPPLY_PROP_CAPACITY,
	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
	POWER_SUPPLY_PROP_TEMP,
	POWER_SUPPLY_PROP_CHARGE_COUNTER,
	POWER_SUPPLY_PROP_CHARGE_FULL,
	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
	POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
};

static int rk817_bat_get_usb_psy(struct device *dev, void *data)
{
	struct rk817_battery_device *battery = data;
	struct power_supply *psy = dev_get_drvdata(dev);

	if (psy->desc->type == POWER_SUPPLY_TYPE_USB) {
		battery->usb_psy = psy;
		return 1;
	}

	return 0;
}

static int rk817_bat_get_ac_psy(struct device *dev, void *data)
{
	struct rk817_battery_device *battery = data;
	struct power_supply *psy = dev_get_drvdata(dev);

	if (psy->desc->type == POWER_SUPPLY_TYPE_MAINS) {
		battery->ac_psy = psy;
		return 1;
	}

	return 0;
}

static void rk817_bat_get_chrg_psy(struct rk817_battery_device *battery)
{
	if (!battery->usb_psy)
		class_for_each_device(power_supply_class, NULL, (void *)battery,
				      rk817_bat_get_usb_psy);
	if (!battery->ac_psy)
		class_for_each_device(power_supply_class, NULL, (void *)battery,
				      rk817_bat_get_ac_psy);
}

static int rk817_bat_get_charge_state(struct rk817_battery_device *battery)
{
	union power_supply_propval val;
	int ret;
	struct power_supply *psy;

	if (!battery->usb_psy || !battery->ac_psy)
		rk817_bat_get_chrg_psy(battery);

	psy = battery->usb_psy;
	if (psy) {
		ret = psy->desc->get_property(psy, POWER_SUPPLY_PROP_ONLINE,
					      &val);
		if (!ret)
			battery->usb_in = val.intval;
	}

	psy = battery->ac_psy;
	if (psy) {
		ret = psy->desc->get_property(psy, POWER_SUPPLY_PROP_ONLINE,
					      &val);
		if (!ret)
			battery->ac_in = val.intval;
	}

	DBG("%s: ac_online=%d, usb_online=%d\n",
	    __func__, battery->ac_in, battery->usb_in);

	return (battery->usb_in || battery->ac_in);
}

static int rk817_get_capacity_leve(struct rk817_battery_device *battery)
{
	int dsoc;

	if (battery->pdata->bat_mode == MODE_VIRTUAL)
		return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;

	dsoc = (battery->dsoc + 500) / 1000;
	if (dsoc < 1)
		return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
	else if (dsoc <= 20)
		return POWER_SUPPLY_CAPACITY_LEVEL_LOW;
	else if (dsoc <= 70)
		return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
	else if (dsoc <= 90)
		return POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
	else
		return POWER_SUPPLY_CAPACITY_LEVEL_FULL;
}

static int rk817_battery_time_to_full(struct rk817_battery_device *battery)
{
	int time_sec;
	int cap_temp;

	if (battery->pdata->bat_mode == MODE_VIRTUAL) {
		time_sec = 3600;
	} else if (battery->voltage_avg > 0) {
		cap_temp = battery->design_cap - (battery->remain_cap / 1000);
		if (cap_temp < 0)
			cap_temp = 0;
		time_sec = (3600 * cap_temp) / battery->voltage_avg;
	} else {
		time_sec = 3600 * 24; /* One day */
	}

	return time_sec;
}

static int rk817_battery_get_property(struct power_supply *psy,
				      enum power_supply_property psp,
				      union power_supply_propval *val)
{
	struct rk817_battery_device *battery = power_supply_get_drvdata(psy);

	switch (psp) {
	case POWER_SUPPLY_PROP_CURRENT_NOW:
		val->intval = battery->current_avg * 1000;/*uA*/
		if (battery->pdata->bat_mode == MODE_VIRTUAL)
			val->intval = VIRTUAL_CURRENT * 1000;
		break;
	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
		val->intval = battery->voltage_avg * 1000;/*uV*/
		if (battery->pdata->bat_mode == MODE_VIRTUAL)
			val->intval = VIRTUAL_VOLTAGE * 1000;
		break;
	case POWER_SUPPLY_PROP_CAPACITY:
		val->intval = (battery->dsoc  + 500) / 1000;
		if (battery->pdata->bat_mode == MODE_VIRTUAL)
			val->intval = VIRTUAL_SOC;
		break;
	case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
		val->intval = rk817_get_capacity_leve(battery);
		break;
	case POWER_SUPPLY_PROP_HEALTH:
		val->intval = POWER_SUPPLY_HEALTH_GOOD;
		break;
	case POWER_SUPPLY_PROP_TEMP:
		val->intval = battery->temperature;
		if (battery->pdata->bat_mode == MODE_VIRTUAL)
			val->intval = VIRTUAL_TEMPERATURE;
		break;
	case POWER_SUPPLY_PROP_STATUS:
		if (battery->pdata->bat_mode == MODE_VIRTUAL)
			val->intval = VIRTUAL_STATUS;
		else if (battery->dsoc == 100 * 1000)
			val->intval = POWER_SUPPLY_STATUS_FULL;
		else {
			if ((battery->chip_id != RK809_ID) &&
			    rk817_bat_get_charge_state(battery))
				val->intval = POWER_SUPPLY_STATUS_CHARGING;
			else if (battery->chip_id == RK809_ID &&
				 battery->plugin_trigger)
				val->intval = POWER_SUPPLY_STATUS_CHARGING;
			else
				val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
		}
		break;
	case POWER_SUPPLY_PROP_CHARGE_COUNTER:
		val->intval = battery->charge_count;
		break;
	case POWER_SUPPLY_PROP_CHARGE_FULL:
	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
		val->intval = battery->pdata->design_capacity * 1000;/* uAh */
		break;
	case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
		val->intval = rk817_battery_time_to_full(battery);
		break;
	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
		val->intval = 4500 * 1000;
		break;
	case POWER_SUPPLY_PROP_CURRENT_MAX:
		val->intval = 5000 * 1000;
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

static const struct power_supply_desc rk817_bat_desc = {
	.name		= "battery",
	.type		= POWER_SUPPLY_TYPE_BATTERY,
	.properties	= rk817_bat_props,
	.num_properties	= ARRAY_SIZE(rk817_bat_props),
	.get_property	= rk817_battery_get_property,
};

static int rk817_bat_init_power_supply(struct rk817_battery_device *battery)
{
	struct power_supply_config psy_cfg = { .drv_data = battery, };

	battery->bat = devm_power_supply_register(battery->dev,
						  &rk817_bat_desc,
						  &psy_cfg);
	if (IS_ERR(battery->bat)) {
		dev_err(battery->dev, "register bat power supply fail\n");
		return PTR_ERR(battery->bat);
	}

	return 0;
}

static enum power_supply_property rk809_chg_props[] = {
	POWER_SUPPLY_PROP_ONLINE,
	POWER_SUPPLY_PROP_STATUS,
};

static int rk809_chg_get_property(struct power_supply *psy,
				  enum power_supply_property psp,
				  union power_supply_propval *val)
{
	struct rk817_battery_device *battery = power_supply_get_drvdata(psy);
	int online = 0;
	int ret = 0;

	if (battery->plugin_trigger)
		online = 1;
	switch (psp) {
	case POWER_SUPPLY_PROP_ONLINE:
		val->intval = online;
		dev_dbg(battery->dev, "report online: %d\n", val->intval);
		break;
	case POWER_SUPPLY_PROP_STATUS:
		if (online)
			val->intval = POWER_SUPPLY_STATUS_CHARGING;
		else
			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
		dev_dbg(battery->dev, "report prop: %d\n", val->intval);
		break;
	default:
		ret = -EINVAL;
		break;
	}

	return ret;
}

static const struct power_supply_desc rk809_chg_desc = {
	.name		= "charger",
	.type		= POWER_SUPPLY_TYPE_USB,
	.properties	= rk809_chg_props,
	.num_properties	= ARRAY_SIZE(rk809_chg_props),
	.get_property	= rk809_chg_get_property,
};

static int rk809_chg_init_power_supply(struct rk817_battery_device *battery)
{
	struct power_supply_config psy_cfg = { .drv_data = battery, };

	battery->chg_psy =
		devm_power_supply_register(battery->dev, &rk809_chg_desc,
					   &psy_cfg);
	if (IS_ERR(battery->chg_psy)) {
		dev_err(battery->dev, "register chg psy power supply fail\n");
		return PTR_ERR(battery->chg_psy);
	}

	return 0;
}

static void rk817_bat_power_supply_changed(struct rk817_battery_device *battery)
{
	static int old_soc = -1;

	if (battery->dsoc > 100 * 1000)
		battery->dsoc = 100 * 1000;
	else if (battery->dsoc < 0)
		battery->dsoc = 0;

	if (battery->dsoc == old_soc && !battery->change)
		return;

	battery->change = false;
	old_soc = battery->dsoc;
	battery->last_dsoc = battery->dsoc;
	power_supply_changed(battery->bat);
	DBG("changed: dsoc=%d, rsoc=%d, v=%d, ov=%d c=%d, cap=%d, f=%d\n",
	    battery->dsoc, battery->rsoc, battery->voltage_avg,
	    battery->voltage_ocv, battery->current_avg,
	    battery->remain_cap, battery->fcc);

	DBG("dl=%d, rl=%d, v=%d, halt=%d, halt_n=%d, max=%d\n"
	    "init=%d, sw=%d, calib=%d, below0=%d, force=%d\n",
	    battery->dbg_pwr_dsoc, battery->dbg_pwr_rsoc,
	    battery->dbg_pwr_vol,
	    battery->is_halt, battery->halt_cnt,
	    battery->is_max_soc_offset,
	    battery->is_initialized, battery->is_sw_reset,
	    battery->is_ocv_calib,
	    battery->dbg_cap_low0, battery->is_force_calib);
}

static void rk817_battery_debug_info(struct rk817_battery_device *battery)
{
	rk817_bat_get_battery_voltage(battery);
	rk817_bat_get_sys_voltage(battery);
	rk817_bat_get_USB_voltage(battery);
	rk817_bat_get_pwron_voltage(battery);
	rk817_bat_get_ocv_voltage(battery);
	rk817_bat_get_ocv0_voltage0(battery);

	rk817_bat_current_calibration(battery);
	rk817_bat_get_avg_current(battery);
	rk817_bat_get_relax_cur1(battery);
	rk817_bat_get_relax_cur2(battery);
	rk817_bat_get_relax_current(battery);
	rk817_bat_get_ocv_current(battery);
	rk817_bat_get_ocv_current0(battery);
	rk817_bat_get_pwron_current(battery);
	rk817_bat_get_ocv_count(battery);
	rk817_bat_save_dsoc(battery, battery->dsoc);
	DBG("capactiy = %d\n", rk817_bat_get_capacity_mah(battery));
}

static void
rk817_bat_update_charging_status(struct rk817_battery_device *battery)
{
	int is_charging;

	is_charging = rk817_bat_get_charge_state(battery);
	if (is_charging == battery->is_charging)
		return;

	battery->change = true;
	battery->is_charging = is_charging;
	if (is_charging)
		battery->charge_count++;
}

static void rk817_bat_update_info(struct rk817_battery_device *battery)
{
	battery->voltage_avg = rk817_bat_get_battery_voltage(battery);
	battery->voltage_sys = rk817_bat_get_sys_voltage(battery);
	battery->current_avg = rk817_bat_get_avg_current(battery);
	battery->voltage_relax = rk817_bat_get_relax_voltage(battery);
	battery->rsoc = rk817_bat_get_rsoc(battery);
	battery->remain_cap = rk817_bat_get_capacity_uah(battery);
	battery->voltage_usb = rk817_bat_get_USB_voltage(battery);
	battery->chrg_status = get_charge_status(battery);
	rk817_bat_update_charging_status(battery);
	DBG("valtage usb: %d\n", battery->voltage_usb);
	DBG("UPDATE: voltage_avg = %d\n"
	    "voltage_sys = %d\n"
	    "curren_avg = %d\n"
	    "rsoc = %d\n"
	    "chrg_status = %d\n"
	    "PWRON_CUR = %d\n"
	    "remain_cap = %d\n",
	    battery->voltage_avg,
	    battery->voltage_sys,
	    battery->current_avg,
	    battery->rsoc,
	    battery->chrg_status,
	    rk817_bat_get_pwron_current(battery),
	    battery->remain_cap);

	/* smooth charge */
	if (battery->remain_cap / 1000 > battery->fcc) {
		/*battery->sm_remain_cap -=*/
		/*(battery->remain_cap - battery->fcc * 1000);*/
		battery->sm_remain_cap = battery->fcc * 1000;
		DBG("<%s>. cap: remain=%d, sm_remain=%d\n",
		    __func__, battery->remain_cap, battery->sm_remain_cap);
		DBG("fcc: %d\n", battery->fcc);
		rk817_bat_init_coulomb_cap(battery, battery->fcc + 100);
		rk817_bat_init_coulomb_cap(battery, battery->fcc);
		rk817_bat_get_capacity_mah(battery);
	}

	if (battery->chrg_status != CHARGE_FINISH)
		battery->finish_base = get_boot_sec();
}

static void rk817_bat_save_data(struct rk817_battery_device *battery)
{
	rk817_bat_save_dsoc(battery, battery->dsoc);
	rk817_bat_save_cap(battery, battery->remain_cap / 1000);
}

/* high load: current < 0 with charger in.
 * System will not shutdown while dsoc=0% with charging state(ac_in),
 * which will cause over discharge, so oppose status before report states.
 */
static void rk817_bat_lowpwr_check(struct rk817_battery_device *battery)
{
	static u64 time;
	int pwr_off_thresd = battery->pdata->pwroff_vol;

	if (battery->current_avg < 0 && battery->voltage_avg < pwr_off_thresd) {
		if (!time)
			time = get_boot_sec();

		if ((base2sec(time) > MINUTE(1)) ||
		    (battery->voltage_avg <= pwr_off_thresd - 50)) {
			battery->fake_offline = 1;
			if (battery->voltage_avg <= pwr_off_thresd - 50)
				battery->dsoc -= 1000;
			DBG("low power, soc=%d, current=%d\n",
			    battery->dsoc, battery->current_avg);
		}
	} else {
		time = 0;
		battery->fake_offline = 0;
	}

	DBG("<%s>. t=%lu, dsoc=%d, current=%d, fake_offline=%d\n",
	    __func__, base2sec(time), battery->dsoc,
	    battery->current_avg, battery->fake_offline);
}

static void rk817_bat_calc_smooth_dischrg(struct rk817_battery_device *battery)
{
	int tmp_soc = 0;

	/* check new dsoc */
	if (battery->smooth_soc < 0)
		battery->smooth_soc = 0;

	tmp_soc = battery->smooth_soc / 1000;

	if (tmp_soc != battery->dsoc / 1000) {
		if (battery->smooth_soc > battery->dsoc)
			return;

		if (battery->smooth_soc + 1000 > battery->dsoc)
			battery->dsoc = battery->smooth_soc;
		else
			battery->dsoc -= 1000;

		if (battery->dsoc <= 0)
			battery->dsoc = 0;
	}
}

static void rk817_bat_smooth_algorithm(struct rk817_battery_device *battery)
{
	int ydsoc = 0, delta_cap = 0, old_cap = 0, tmp_soc;
	/*int linek;*/
	int diff, delta;
	/*int current_avg = rk817_bat_get_avg_current(battery);*/

	delta = abs(battery->dsoc - battery->rsoc);
	diff = delta * 3;/* speed:3/4 */

	/* charge and discharge switch */
	if ((battery->sm_linek * battery->current_avg <= 0)) {
		DBG("<%s>. linek mode, retinit sm linek..\n", __func__);
		rk817_bat_calc_sm_linek(battery);
	}

	/*battery->sm_linek = linek;*/

	battery->remain_cap = rk817_bat_get_capacity_uah(battery);

	old_cap = battery->sm_remain_cap;
	DBG("smooth: smooth_soc = %d, dsoc = %d, battery->sm_linek = %d\n",
	    battery->smooth_soc, battery->dsoc, battery->sm_linek);

	/* discharge status: sm_remain_cap > remain_cap, delta_cap > 0 */
	/* from charge to discharge:
	 * remain_cap may be above sm_remain_cap, delta_cap <= 0
	 */
	delta_cap = battery->remain_cap - battery->sm_remain_cap;
	DBG("smooth: sm_remain_cap = %d, remain_cap = %d\n",
	    battery->sm_remain_cap, battery->remain_cap);
	DBG("smooth: delta_cap = %d, dsoc = %d\n",
	    delta_cap, battery->dsoc);

	if (delta_cap == 0) {
		DBG("<%s>. delta_cap = 0\n", __func__);
		return;
	}

	/* discharge: sm_linek < 0, if delate_cap <0, ydsoc > 0 */
	ydsoc = battery->sm_linek * abs(delta_cap / 10) / DIV(battery->fcc);

	DBG("smooth: ydsoc = %d, fcc = %d\n", ydsoc, battery->fcc);
	if (ydsoc == 0) {
		DBG("<%s>. ydsoc = 0\n", __func__);
		return;
	}
	battery->sm_remain_cap = battery->remain_cap;

	DBG("<%s>. k=%d, ydsoc=%d; cap:old=%d, new:%d; delta_cap=%d\n",
	    __func__, battery->sm_linek, ydsoc, old_cap,
	    battery->sm_remain_cap, delta_cap);

	/* discharge mode */
	/* discharge mode, but ydsoc > 0,
	 * from charge status to dischrage
	 */
	battery->smooth_soc += ydsoc;
	if (ydsoc < 0) {
		rk817_bat_calc_smooth_dischrg(battery);
	} else {
		if (battery->smooth_soc < 0)
			battery->smooth_soc = 0;

		tmp_soc = battery->smooth_soc / 1000;

		if (tmp_soc != battery->dsoc / 1000) {
			if (battery->smooth_soc < battery->dsoc)
				return;

			battery->dsoc = battery->smooth_soc;
			if (battery->dsoc <= 0)
				battery->dsoc = 0;
		}
	}

	if (battery->s2r) {
		battery->s2r = false;
		rk817_bat_calc_sm_linek(battery);
	}

	DBG("smooth: smooth_soc = %d, dsoc = %d\n",
	    battery->smooth_soc, battery->dsoc);
	DBG("smooth: delta_cap = %d, dsoc = %d\n",
	    delta_cap, battery->dsoc);
}

static void rk817_bat_calc_zero_linek(struct rk817_battery_device *battery)
{
	int dead_voltage, ocv_voltage;
	int voltage_avg, current_avg, vsys;
	int ocv_cap, dead_cap, xsoc;
	int ocv_soc, dead_soc;
	int pwroff_vol;
	int min_gap_xsoc;
	int powerpatch_res;

	if ((abs(battery->current_avg) < 400) && (battery->dsoc / 1000 > 5))
		pwroff_vol = battery->pdata->pwroff_vol + 50;
	else
		pwroff_vol = battery->pdata->pwroff_vol;

	/* calc estimate ocv voltage */
	voltage_avg = rk817_bat_get_battery_voltage(battery);
	current_avg = rk817_bat_get_avg_current(battery);
	vsys = voltage_avg + (current_avg * DEF_PWRPATH_RES) / 1000;

	powerpatch_res = (voltage_avg - vsys) * 1000 / current_avg;

	battery->zero_voltage_avg = voltage_avg;
	battery->zero_current_avg = current_avg;
	battery->zero_vsys = vsys;

	DBG("Zero: voltage_avg = %d, Vsys = %d\n", voltage_avg, vsys);
	DBG("Zero: powerpatch_res = %d\n", powerpatch_res);
	DBG("ZERO0: shtd_vol: poweroff_vol(usr) = %d\n"
	    "pwroff_vol = %d\n"
	    "zero_reserve_dsoc = %d\n",
	    battery->pdata->pwroff_vol,
	    pwroff_vol,
	    battery->pdata->zero_reserve_dsoc);

	/* get the dead ocv voltage, pwroff_vol is vsys */
	dead_voltage = pwroff_vol - current_avg *
				(battery->bat_res + DEF_PWRPATH_RES) / 1000;

	ocv_voltage = voltage_avg - (current_avg * battery->bat_res) / 1000;
	DBG("ZERO0: dead_voltage(shtd) = %d, ocv_voltage(now) = %d\n",
	    dead_voltage, ocv_voltage);

	/* calc estimate soc and cap */
	dead_soc = rk817_bat_vol_to_soc(battery, dead_voltage);
	dead_cap = rk817_bat_vol_to_cap(battery, dead_voltage);
	DBG("ZERO0: dead_soc = %d, dead_cap = %d\n",
	    dead_soc, dead_cap);

	ocv_soc = rk817_bat_vol_to_soc(battery, ocv_voltage);
	ocv_cap = rk817_bat_vol_to_cap(battery, ocv_voltage);
	DBG("ZERO0: ocv_soc = %d, ocv_cap = %d\n",
	    ocv_soc, ocv_cap);

	/* xsoc: available rsoc */
	xsoc = ocv_soc - dead_soc;

	battery->zero_dead_voltage = dead_voltage;
	battery->zero_dead_soc = dead_soc;
	battery->zero_dead_cap = dead_cap;

	battery->zero_batvol_to_ocv = ocv_voltage;
	battery->zero_batocv_to_soc = ocv_soc;
	battery->zero_batocv_to_cap = ocv_cap;

	battery->zero_xsoc = xsoc;

	DBG("Zero: xsoc = %d\n", xsoc);
	/* min_gap_xsoc: reserve xsoc */
	if (abs(current_avg) > ZERO_LOAD_LVL1)
		min_gap_xsoc = MIN_ZERO_GAP_XSOC3;
	else if (abs(current_avg) > ZERO_LOAD_LVL2)
		min_gap_xsoc = MIN_ZERO_GAP_XSOC2;
	else
		min_gap_xsoc = MIN_ZERO_GAP_XSOC1;

	if ((xsoc <= 30) &&
	    (battery->dsoc >= battery->pdata->zero_reserve_dsoc))
		min_gap_xsoc = min_gap_xsoc + MIN_ZERO_GAP_CALIB;

	battery->zero_remain_cap = battery->remain_cap;
	battery->zero_timeout_cnt = 0;
	if ((battery->dsoc / 1000 <= 1) && (xsoc > 0)) {
		battery->zero_linek = 400;
		battery->zero_drop_sec = 0;
	} else if (xsoc >= 0) {
		battery->zero_drop_sec = 0;
		battery->zero_linek =
			(battery->zero_dsoc + xsoc / 2) / DIV(xsoc);
		/* battery energy mode to use up voltage */
		if ((battery->pdata->energy_mode) &&
		    (xsoc - battery->dsoc / 1000 >= MIN_ZERO_GAP_XSOC3) &&
		    (battery->dsoc  / 1000 <= 10) && (battery->zero_linek < 300)) {
			battery->zero_linek = 300;
			DBG("ZERO-new: zero_linek adjust step0...\n");
		/* reserve enough power yet, slow down any way */
		} else if ((xsoc - battery->dsoc / 1000 >= min_gap_xsoc) ||
			   ((xsoc - battery->dsoc / 1000 >= MIN_ZERO_GAP_XSOC2) &&
			    (battery->dsoc / 1000 <= 10) && (xsoc > 15))) {
			if (xsoc <= 20 &&
			    battery->dsoc / 1000 >= battery->pdata->zero_reserve_dsoc)
				battery->zero_linek = 1200;
			else if (xsoc - battery->dsoc / 1000 >= 2 * min_gap_xsoc)
				battery->zero_linek = 400;
			else if (xsoc - battery->dsoc / 1000 >= 3 + min_gap_xsoc)
				battery->zero_linek = 600;
			else
				battery->zero_linek = 800;
			DBG("ZERO-new: zero_linek adjust step1...\n");
		/* control zero mode beginning enter */
		} else if ((battery->zero_linek > 1800) &&
			   (battery->dsoc / 1000 > 70)) {
			battery->zero_linek = 1800;
			DBG("ZERO-new: zero_linek adjust step2...\n");
		/* dsoc close to xsoc: it must reserve power */
		} else if ((battery->zero_linek > 1000) &&
			   (battery->zero_linek < 1200)) {
			battery->zero_linek = 1200;
			DBG("ZERO-new: zero_linek adjust step3...\n");
		/* dsoc[5~15], dsoc < xsoc */
		} else if ((battery->dsoc / 1000 <= 15 && battery->dsoc > 5) &&
			   (battery->zero_linek <= 1200)) {
			/* slow down */
			if ((xsoc - battery->dsoc / 1000) >= min_gap_xsoc)
				battery->zero_linek = 800;
			/* reserve power */
			else
				battery->zero_linek = 1200;
			DBG("ZERO-new: zero_linek adjust step4...\n");
		/* dsoc[5, 100], dsoc < xsoc */
		} else if ((battery->zero_linek < 1000) &&
			   (battery->dsoc / 1000 >= 5)) {
			if ((xsoc - battery->dsoc / 1000) < min_gap_xsoc) {
				/* reserve power */
				battery->zero_linek = 1200;
			} else {
				if (abs(battery->current_avg) > 500)/* heavy */
					battery->zero_linek = 900;
				else
					battery->zero_linek = 1000;
			}
			DBG("ZERO-new: zero_linek adjust step5...\n");
		/* dsoc[0~5], dsoc < xsoc */
		} else if ((battery->zero_linek < 1000) &&
			   (battery->dsoc  / 1000 <= 5)) {
			if ((xsoc - battery->dsoc / 1000) <= 3)
				battery->zero_linek = 1200;
			else
				battery->zero_linek = 800;
			DBG("ZERO-new: zero_linek adjust step6...\n");
		}
	} else {
		/* xsoc < 0 */
		battery->zero_linek = 1000;
		if (!battery->zero_drop_sec)
			battery->zero_drop_sec = get_boot_sec();
		if (base2sec(battery->zero_drop_sec) >= WAIT_DSOC_DROP_SEC) {
			DBG("ZERO0: t=%lu\n", base2sec(battery->zero_drop_sec));
			battery->zero_drop_sec = 0;
			battery->dsoc -= 1000;
			if (battery->dsoc < 0)
				battery->dsoc = 0;
			battery->zero_dsoc = battery->dsoc;
		}
	}

	if (voltage_avg < pwroff_vol - 70) {
		if (!battery->shtd_drop_sec)
			battery->shtd_drop_sec = get_boot_sec();
		if (base2sec(battery->shtd_drop_sec) > WAIT_SHTD_DROP_SEC) {
			DBG("voltage extreme low...soc:%d->0\n", battery->dsoc);
			battery->shtd_drop_sec = 0;
			battery->dsoc = 0;
		}
	} else {
		battery->shtd_drop_sec = 0;
	}

	DBG("Zero: zero_linek = %d\n", battery->zero_linek);
}

static void rk817_bat_zero_algo_prepare(struct rk817_battery_device *battery)
{
	int tmp_dsoc;

	tmp_dsoc = battery->zero_dsoc / 1000;

	if (tmp_dsoc != battery->smooth_soc / 1000)
		battery->zero_dsoc = battery->smooth_soc;

	DBG("zero_smooth: zero_dsoc = %d\n", battery->zero_dsoc);

	rk817_bat_calc_zero_linek(battery);
}

static void rk817_bat_calc_zero_algorithm(struct rk817_battery_device *battery)
{
	int tmp_soc;

	tmp_soc = battery->zero_dsoc / 1000;

	if (tmp_soc == battery->dsoc / 1000)
		return;

	if (battery->zero_dsoc > battery->dsoc)
		return;

	if (battery->zero_dsoc < battery->dsoc - 1000)
		battery->dsoc -= 1000;
	else
		battery->dsoc = battery->zero_dsoc;
}

static void rk817_bat_zero_algorithm(struct rk817_battery_device *battery)
{
	int delta_cap = 0, delta_soc = 0;

	battery->zero_timeout_cnt++;
	delta_cap = battery->zero_remain_cap - battery->remain_cap;
	delta_soc = battery->zero_linek * delta_cap / DIV(battery->fcc) / 10;

	DBG("zero algorithm start\n");
	DBG("DEAD: dead_voltage: %d\n"
	    "dead_soc: %d\n"
	    "dead_cap: %d\n"
	    "powoff_vol: %d\n",
	    battery->zero_dead_voltage,
	    battery->zero_dead_soc,
	    battery->zero_dead_cap,
	    battery->pdata->pwroff_vol);
	DBG("DEAD: bat_voltage: %d\n"
	    "bat_current: %d\n"
	    "batvol_to_ocv: %d\n"
	    "batocv_to_soc: %d\n"
	    "batocv_to_cap: %d\n",
	    battery->zero_voltage_avg,
	    battery->zero_current_avg,
	    battery->zero_batvol_to_ocv,
	    battery->zero_batocv_to_soc,
	    battery->zero_batocv_to_cap);
	DBG("DEAD: Xsoc: %d, zero_reserve_dsoc: %d\n",
	    battery->zero_xsoc, battery->pdata->zero_reserve_dsoc);
	DBG("CAP: zero_remain_cap = %d, remain_cap = %d\n",
	    battery->zero_remain_cap, battery->remain_cap);
	DBG("Zero: zero_delta_cap = %d, zero_link = %d, delta_soc = %d\n",
	    delta_cap, battery->zero_linek, delta_soc);
	DBG("zero algorithm end\n");

	if ((delta_soc >= MIN_ZERO_DSOC_ACCURACY) ||
	    (battery->zero_timeout_cnt > MIN_ZERO_OVERCNT) ||
	    (battery->zero_linek == 0)) {
		DBG("ZERO1:--------- enter calc -----------\n");
		battery->zero_timeout_cnt = 0;
		battery->zero_dsoc -= delta_soc;
		rk817_bat_calc_zero_algorithm(battery);
		DBG("Zero: dsoc: %d\n", battery->dsoc);
		rk817_bat_calc_zero_linek(battery);
	}

	if ((battery->rsoc / 1000 < 1) &&
	    (battery->zero_batocv_to_cap > battery->fcc / 100)) {
		DBG("ZERO2:---------check step1 -----------\n");
		rk817_bat_init_coulomb_cap(battery,
					   battery->zero_batocv_to_cap);
		rk817_bat_calc_zero_linek(battery);
	}
}

static void rk817_bat_finish_algorithm(struct rk817_battery_device *battery)
{
	unsigned long finish_sec, soc_sec;
	int plus_soc, finish_current, rest = 0;

	/* rsoc */
	if ((battery->remain_cap != battery->fcc) &&
	    (get_charge_status(battery) == CHARGE_FINISH)) {
		battery->age_adjust_cap +=
			(battery->fcc * 1000 - battery->remain_cap);
		rk817_bat_init_coulomb_cap(battery, battery->fcc);
		rk817_bat_get_capacity_mah(battery);
	}

	/* dsoc */
	if (battery->dsoc < 100 * 1000) {
		if (!battery->finish_base)
			battery->finish_base = get_boot_sec();

		finish_current = (battery->rsoc - battery->dsoc) > FINISH_MAX_SOC_DELAY ?
					FINISH_CHRG_CUR2 : FINISH_CHRG_CUR1;
		finish_sec = base2sec(battery->finish_base);

		soc_sec = battery->fcc * 3600 / 100 / DIV(finish_current);
		plus_soc = finish_sec / DIV(soc_sec);
		if (finish_sec > soc_sec) {
			rest = finish_sec % soc_sec;
			battery->dsoc += plus_soc * 1000;
			battery->finish_base = get_boot_sec();
			if (battery->finish_base > rest)
				battery->finish_base = get_boot_sec() - rest;
		}
		DBG("CHARGE_FINISH:dsoc<100,dsoc=%d\n"
		    "soc_time=%lu, sec_finish=%lu, plus_soc=%d, rest=%d\n",
		    battery->dsoc, soc_sec, finish_sec, plus_soc, rest);
		DBG("battery->age_adjust_cap = %d\n", battery->age_adjust_cap);
	}
}

static void rk817_bat_display_smooth(struct rk817_battery_device *battery)
{
	/* discharge: reinit "zero & smooth" algorithm to avoid handling dsoc */
	if (battery->s2r && !battery->sleep_chrg_online) {
		DBG("s2r: discharge, reset algorithm...\n");
		battery->s2r = false;
		rk817_bat_zero_algo_prepare(battery);
		rk817_bat_smooth_algo_prepare(battery);
		return;
	}

	if (battery->work_mode == MODE_FINISH) {
		DBG("step1: charge finish...\n");
		rk817_bat_finish_algorithm(battery);

		if ((get_charge_status(battery) != CHARGE_FINISH) &&
		    !rk817_bat_fake_finish_mode(battery)) {
			if ((battery->current_avg < 0) &&
			    (battery->voltage_avg < battery->pdata->zero_algorithm_vol)) {
				DBG("step1: change to zero mode...\n");
				rk817_bat_zero_algo_prepare(battery);
				battery->work_mode = MODE_ZERO;
			} else {
				DBG("step1: change to smooth mode...\n");
				rk817_bat_smooth_algo_prepare(battery);
				battery->work_mode = MODE_SMOOTH;
			}
		}
	} else if (battery->work_mode == MODE_ZERO) {
		DBG("step2: zero algorithm...\n");
		rk817_bat_zero_algorithm(battery);
		if ((battery->voltage_avg >=
		    battery->pdata->zero_algorithm_vol + 50) ||
		    (battery->current_avg >= 0)) {
			DBG("step2: change to smooth mode...\n");
			rk817_bat_smooth_algo_prepare(battery);
			battery->work_mode = MODE_SMOOTH;
		} else if ((get_charge_status(battery) == CHARGE_FINISH) ||
			   rk817_bat_fake_finish_mode(battery)) {
			DBG("step2: change to finish mode...\n");
			rk817_bat_finish_algo_prepare(battery);
			battery->work_mode = MODE_FINISH;
		}
	} else {
		DBG("step3: smooth algorithm...\n");
		rk817_bat_smooth_algorithm(battery);
		if ((battery->current_avg < 0) &&
		    (battery->voltage_avg <
		     battery->pdata->zero_algorithm_vol)) {
			DBG("step3: change to zero mode...\n");
			rk817_bat_zero_algo_prepare(battery);
			battery->work_mode = MODE_ZERO;
		} else if ((get_charge_status(battery) == CHARGE_FINISH) ||
			   rk817_bat_fake_finish_mode(battery)) {
			DBG("step3: change to finish mode...\n");
			rk817_bat_finish_algo_prepare(battery);
			battery->work_mode = MODE_FINISH;
		}
	}
}

static void rk817_bat_output_info(struct rk817_battery_device *battery)
{
	DBG("info start:\n");
	DBG("info: voltage_k = %d\n", battery->voltage_k);
	DBG("info: voltage_b = %d\n", battery->voltage_b);
	DBG("info: voltage = %d\n", battery->voltage_avg);
	DBG("info: voltage_sys = %d\n", battery->voltage_sys);
	DBG("info: current = %d\n", battery->current_avg);

	DBG("info: FCC = %d\n", battery->fcc);
	DBG("info: remain_cap = %d\n", battery->remain_cap);
	DBG("info: sm_remain_cap = %d\n", battery->sm_remain_cap);
	DBG("info: sm_link = %d\n", battery->sm_linek);
	DBG("info: smooth_soc = %d\n", battery->smooth_soc);

	DBG("info: zero_remain_cap = %d\n", battery->zero_remain_cap);
	DBG("info: zero_link = %d\n", battery->zero_linek);
	DBG("info: zero_dsoc = %d\n", battery->zero_dsoc);

	DBG("info: remain_cap = %d\n", battery->remain_cap);
	DBG("info: dsoc = %d, dsoc/1000 = %d\n",
	    battery->dsoc, battery->dsoc / 1000);
	DBG("info: rsoc = %d\n", battery->rsoc);
	DBG("info END.\n");
}

static void rk817_battery_work(struct work_struct *work)
{
	struct rk817_battery_device *battery =
		container_of(work,
			     struct rk817_battery_device,
			     bat_delay_work.work);

	rk817_bat_update_info(battery);
	rk817_bat_lowpwr_check(battery);
	rk817_bat_display_smooth(battery);
	rk817_bat_power_supply_changed(battery);
	rk817_bat_save_data(battery);
	rk817_bat_output_info(battery);

	if (rk817_bat_field_read(battery, CUR_CALIB_UPD)) {
		rk817_bat_current_calibration(battery);
		rk817_bat_init_voltage_kb(battery);
		rk817_bat_field_write(battery, CUR_CALIB_UPD, 0x01);
	}

	queue_delayed_work(battery->bat_monitor_wq, &battery->bat_delay_work,
			   msecs_to_jiffies(battery->monitor_ms));
}

static irqreturn_t rk809_plug_in_isr(int irq, void *cg)
{
	struct rk817_battery_device *battery;

	battery = (struct rk817_battery_device *)cg;
	battery->plugin_trigger = 1;
	battery->plugout_trigger = 0;
	power_supply_changed(battery->bat);
	if (battery->is_register_chg_psy)
		power_supply_changed(battery->chg_psy);

	return IRQ_HANDLED;
}

static irqreturn_t rk809_plug_out_isr(int irq, void *cg)
{
	struct rk817_battery_device *battery;

	battery = (struct rk817_battery_device *)cg;
	battery->plugin_trigger = 0;
	battery->plugout_trigger = 1;
	power_supply_changed(battery->bat);
	if (battery->is_register_chg_psy)
		power_supply_changed(battery->chg_psy);

	return IRQ_HANDLED;
}

static int rk809_charge_init_irqs(struct rk817_battery_device *battery)
{
	struct rk808 *rk817 = battery->rk817;
	struct platform_device *pdev = battery->pdev;
	int ret, plug_in_irq, plug_out_irq;

	battery->plugin_trigger = 0;
	battery->plugout_trigger = 0;

	plug_in_irq = regmap_irq_get_virq(rk817->irq_data, RK817_IRQ_PLUG_IN);
	if (plug_in_irq < 0) {
		dev_err(battery->dev, "plug_in_irq request failed!\n");
		return plug_in_irq;
	}

	plug_out_irq = regmap_irq_get_virq(rk817->irq_data, RK817_IRQ_PLUG_OUT);
	if (plug_out_irq < 0) {
		dev_err(battery->dev, "plug_out_irq request failed!\n");
		return plug_out_irq;
	}

	ret = devm_request_threaded_irq(battery->dev, plug_in_irq, NULL,
					rk809_plug_in_isr,
					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
					"rk817_plug_in", battery);
	if (ret) {
		dev_err(&pdev->dev, "plug_in_irq request failed!\n");
		return ret;
	}

	ret = devm_request_threaded_irq(battery->dev, plug_out_irq, NULL,
					rk809_plug_out_isr,
					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
					"rk817_plug_out", battery);
	if (ret) {
		dev_err(&pdev->dev, "plug_out_irq request failed!\n");
		return ret;
	}

	if (rk817_bat_field_read(battery, PLUG_IN_STS)) {
		battery->plugin_trigger = 1;
		battery->plugout_trigger = 0;
	}

	return 0;
}

#ifdef CONFIG_OF
static const struct of_device_id rk817_bat_of_match[] = {
	{ .compatible = "rk817,battery", },
	{ },
};
MODULE_DEVICE_TABLE(of, rk817_bat_of_match);
#else
static const struct of_device_id rk817_bat_of_match[] = {
	{ },
};
#endif

static int rk817_battery_probe(struct platform_device *pdev)
{
	const struct of_device_id *of_id =
			of_match_device(rk817_bat_of_match, &pdev->dev);
	struct rk817_battery_device *battery;
	struct rk808 *rk817 = dev_get_drvdata(pdev->dev.parent);
	struct i2c_client *client = rk817->i2c;
	int i,  ret;

	if (!of_id) {
		dev_err(&pdev->dev, "Failed to find matching dt id\n");
		return -ENODEV;
	}

	battery = devm_kzalloc(&client->dev, sizeof(*battery), GFP_KERNEL);
	if (!battery)
		return -EINVAL;

	battery->rk817 = rk817;
	battery->client = client;
	battery->dev = &pdev->dev;
	platform_set_drvdata(pdev, battery);
	battery->chip_id = rk817->variant;

	battery->regmap = rk817->regmap;
	if (IS_ERR(battery->regmap)) {
		dev_err(battery->dev, "Failed to initialize regmap\n");
		return -EINVAL;
	}

	for (i = 0; i < ARRAY_SIZE(rk817_battery_reg_fields); i++) {
		const struct reg_field *reg_fields = rk817_battery_reg_fields;

		battery->rmap_fields[i] =
			devm_regmap_field_alloc(battery->dev,
						battery->regmap,
						reg_fields[i]);
		if (IS_ERR(battery->rmap_fields[i])) {
			dev_err(battery->dev, "cannot allocate regmap field\n");
			return PTR_ERR(battery->rmap_fields[i]);
		}
	}

	ret = rk817_bat_parse_dt(battery);
	if (ret < 0) {
		dev_err(battery->dev, "battery parse dt failed!\n");
		return ret;
	}

	rk817_bat_init_info(battery);
	rk817_bat_init_fg(battery);

	rk817_battery_debug_info(battery);
	rk817_bat_update_info(battery);

	rk817_bat_output_info(battery);
	battery->bat_monitor_wq = alloc_ordered_workqueue("%s",
			WQ_MEM_RECLAIM | WQ_FREEZABLE, "rk817-bat-monitor-wq");
	INIT_DELAYED_WORK(&battery->bat_delay_work, rk817_battery_work);
	queue_delayed_work(battery->bat_monitor_wq, &battery->bat_delay_work,
			   msecs_to_jiffies(TIMER_MS_COUNTS * 5));
	INIT_WORK(&battery->resume_work, rk817_bat_resume_work);

	ret = rk817_bat_init_power_supply(battery);
	if (ret) {
		dev_err(battery->dev, "rk817 power supply register failed!\n");
		return ret;
	}
	if (battery->is_register_chg_psy) {
		ret = rk809_chg_init_power_supply(battery);
		if (ret) {
			dev_err(battery->dev, "rk809 chg psy init failed!\n");
			return ret;
		}
	}

	if (battery->chip_id == RK809_ID)
		rk809_charge_init_irqs(battery);

	wake_lock_init(&battery->wake_lock, WAKE_LOCK_SUSPEND,
		       "rk817_bat_lock");

	DBG("name: 0x%x", rk817_bat_field_read(battery, CHIP_NAME_H));
	DBG("%x\n", rk817_bat_field_read(battery, CHIP_NAME_L));
	DBG("driver version %s\n", DRIVER_VERSION);

	return 0;
}

static void rk817_battery_shutdown(struct platform_device *dev)
{
}

static time64_t rk817_get_rtc_sec(void)
{
	int err;
	struct rtc_time tm;
	struct rtc_device *rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);

	err = rtc_read_time(rtc, &tm);
	if (err) {
		dev_err(rtc->dev.parent, "read hardware clk failed\n");
		return 0;
	}

	err = rtc_valid_tm(&tm);
	if (err) {
		dev_err(rtc->dev.parent, "invalid date time\n");
		return 0;
	}

	return rtc_tm_to_time64(&tm);
}

#ifdef CONFIG_PM_SLEEP
static int  rk817_bat_pm_suspend(struct device *dev)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct rk817_battery_device *battery = dev_get_drvdata(&pdev->dev);

	cancel_delayed_work_sync(&battery->bat_delay_work);

	battery->s2r = false;
	battery->sleep_chrg_status = get_charge_status(battery);
	battery->current_avg = rk817_bat_get_avg_current(battery);
	if (battery->current_avg > 0 ||
	    (battery->sleep_chrg_status == CC_OR_CV_CHRG) ||
	    (battery->sleep_chrg_status == CHARGE_FINISH))
		battery->sleep_chrg_online = 1;
	else
		battery->sleep_chrg_online = 0;

	battery->remain_cap = rk817_bat_get_capacity_uah(battery);
	battery->rsoc = rk817_bat_get_rsoc(battery);

	battery->rtc_base = rk817_get_rtc_sec();
	rk817_bat_save_data(battery);

	if (battery->sleep_chrg_status != CHARGE_FINISH)
		battery->finish_base = get_boot_sec();

	if ((battery->work_mode == MODE_ZERO) &&
	    (battery->current_avg >= 0)) {
		DBG("suspend: MODE_ZERO exit...\n");
		/* it need't do prepare for mode finish and smooth, it will
		 * be done in display_smooth
		 */
		if (battery->sleep_chrg_status == CHARGE_FINISH) {
			battery->work_mode = MODE_FINISH;
			if (!battery->finish_base)
				battery->finish_base = get_boot_sec();
		} else {
			battery->work_mode = MODE_SMOOTH;
			rk817_bat_smooth_algo_prepare(battery);
		}
	}

	DBG("suspend get_boot_sec: %lld\n", get_boot_sec());

	DBG("suspend: dl=%d rl=%d c=%d v=%d cap=%d at=%ld ch=%d\n",
	    battery->dsoc, battery->rsoc, battery->current_avg,
	    rk817_bat_get_battery_voltage(battery),
	    rk817_bat_get_capacity_uah(battery),
	    battery->sleep_dischrg_sec, battery->sleep_chrg_online);
	DBG("battery->sleep_chrg_status=%d\n", battery->sleep_chrg_status);

	return 0;
}

static int rk817_bat_rtc_sleep_sec(struct rk817_battery_device *battery)
{
	int interval_sec;

	interval_sec = rk817_get_rtc_sec() - battery->rtc_base;

	return (interval_sec > 0) ? interval_sec : 0;
}

static void rk817_bat_relife_age_flag(struct rk817_battery_device *battery)
{
	u8 ocv_soc, ocv_cap, soc_level;

	if (battery->voltage_relax <= 0)
		return;

	ocv_soc = rk817_bat_vol_to_soc(battery, battery->voltage_relax);
	ocv_cap = rk817_bat_vol_to_cap(battery, battery->voltage_relax);
	DBG("<%s>. ocv_soc=%d, min=%lu, vol=%d\n", __func__,
	    ocv_soc, battery->sleep_dischrg_sec / 60, battery->voltage_relax);

	/* sleep enough time and ocv_soc enough low */
	if (!battery->age_allow_update && ocv_soc <= 10) {
		battery->age_voltage = battery->voltage_relax;
		battery->age_ocv_cap = ocv_cap;
		battery->age_ocv_soc = ocv_soc;
		battery->age_adjust_cap = 0;

		if (ocv_soc <= 1)
			battery->age_level = 100;
		else if (ocv_soc < 5)
			battery->age_level = 90;
		else
			battery->age_level = 80;

		/*soc_level = rk818_bat_get_age_level(battery);*/
		soc_level = 0;
		if (soc_level > battery->age_level) {
			battery->age_allow_update = false;
		} else {
			battery->age_allow_update = true;
			battery->age_keep_sec = get_boot_sec();
		}

		BAT_INFO("resume: age_vol:%d, age_ocv_cap:%d, age_ocv_soc:%d, "
			 "soc_level:%d, age_allow_update:%d, "
			 "age_level:%d\n",
			 battery->age_voltage, battery->age_ocv_cap,
			 ocv_soc, soc_level,
			 battery->age_allow_update, battery->age_level);
	}
}

static void rk817_bat_init_capacity(struct rk817_battery_device *battery,
				    u32 cap)
{
	int delta_cap;

	delta_cap = cap - battery->remain_cap;
	if (!delta_cap)
		return;

	battery->age_adjust_cap += delta_cap;
	rk817_bat_init_coulomb_cap(battery, cap);
	rk817_bat_smooth_algo_prepare(battery);
	rk817_bat_zero_algo_prepare(battery);
}

static void rk817_bat_relax_vol_calib(struct rk817_battery_device *battery)
{
	int soc, cap, vol;

	vol = battery->voltage_relax;
	soc = rk817_bat_vol_to_soc(battery, vol);
	cap = rk817_bat_vol_to_cap(battery, vol);
	rk817_bat_init_capacity(battery, cap);
	BAT_INFO("sleep ocv calib: rsoc=%d, cap=%d\n", soc, cap);
}

static int rk817_bat_sleep_dischrg(struct rk817_battery_device *battery)
{
	bool ocv_soc_updated = false;
	int tgt_dsoc, gap_soc, sleep_soc = 0;
	int pwroff_vol = battery->pdata->pwroff_vol;
	unsigned long sleep_sec = battery->sleep_dischrg_sec;
	int sleep_cur;

	DBG("<%s>. enter: dsoc=%d, rsoc=%d, rv=%d, v=%d, sleep_min=%lu\n",
	    __func__, battery->dsoc, battery->rsoc, battery->voltage_relax,
	    battery->voltage_avg, sleep_sec / 60);

	if (battery->voltage_relax >= battery->voltage_avg) {
		rk817_bat_relax_vol_calib(battery);
		rk817_bat_restart_relax(battery);
		rk817_bat_relife_age_flag(battery);
		ocv_soc_updated = true;
	}

	/* handle dsoc */
	if (battery->dsoc <= battery->rsoc) {
		if (battery->pdata->low_pwr_sleep)
			sleep_cur = LOW_PWR_SLP_CURR_MIN;
		else
			sleep_cur = SLP_CURR_MIN;
		battery->sleep_sum_cap = (sleep_cur * sleep_sec / 3600);
		sleep_soc = battery->sleep_sum_cap * 100 / DIV(battery->fcc);
		tgt_dsoc = battery->dsoc - sleep_soc * 1000;
		if (sleep_soc > 0) {
			BAT_INFO("calib0: rl=%d, dl=%d, intval=%d\n",
				 battery->rsoc, battery->dsoc, sleep_soc);
			if (battery->dsoc / 1000 < 5) {
				battery->dsoc -= 1000;
			} else if ((tgt_dsoc / 1000 < 5) &&
				   (battery->dsoc  / 1000 >= 5)) {
				if (battery->dsoc / 1000 == 5)
					battery->dsoc -= 1000;
				else
					battery->dsoc = 5 * 1000;
			} else if (tgt_dsoc / 1000 > 5) {
				battery->dsoc = tgt_dsoc;
			}
		}

		DBG("%s: dsoc<=rsoc, sum_cap=%d==>sleep_soc=%d, tgt_dsoc=%d\n",
		    __func__, battery->sleep_sum_cap, sleep_soc, tgt_dsoc);
	} else {
		/* di->dsoc > di->rsoc */
		if (battery->pdata->low_pwr_sleep)
			sleep_cur = LOW_PWR_SLP_CURR_MAX;
		else
			sleep_cur = SLP_CURR_MAX;
		battery->sleep_sum_cap = (sleep_cur * sleep_sec / 3600);
		sleep_soc = battery->sleep_sum_cap / DIV(battery->fcc / 100);
		gap_soc = battery->dsoc - battery->rsoc;

		DBG("calib1: rsoc=%d, dsoc=%d, intval=%d\n",
		    battery->rsoc, battery->dsoc, sleep_soc);
		if (gap_soc / 1000 > sleep_soc) {
			if ((gap_soc - 5000) > (sleep_soc * 2 * 1000))
				battery->dsoc -= (sleep_soc * 2 * 1000);
			else
				battery->dsoc -= sleep_soc * 1000;
		} else {
			battery->dsoc = battery->rsoc;
		}

		DBG("%s: dsoc>rsoc, sum_cap=%d=>sleep_soc=%d, gap_soc=%d\n",
		    __func__, battery->sleep_sum_cap, sleep_soc, gap_soc);
	}

	if (battery->voltage_avg <= pwroff_vol - 70) {
		battery->dsoc = 0;
		DBG("low power sleeping, shutdown... %d\n", battery->dsoc);
	}

	if (ocv_soc_updated && sleep_soc &&
	    (battery->rsoc - battery->dsoc) < 5000 &&
	    battery->dsoc < 40 * 1000) {
		battery->dsoc -= 1000;
		DBG("low power sleeping, reserved... %d\n", battery->dsoc);
	}

	if (battery->dsoc <= 0) {
		battery->dsoc = 0;
		DBG("sleep dsoc is %d...\n", battery->dsoc);
	}

	DBG("<%s>. out: dsoc=%d, rsoc=%d, sum_cap=%d\n",
	    __func__, battery->dsoc, battery->rsoc, battery->sleep_sum_cap);

	return sleep_soc;
}

static void rk817_bat_resume_work(struct work_struct *work)
{
	struct rk817_battery_device *battery = container_of(work, struct rk817_battery_device, resume_work);
	int interval_sec = 0, time_step = 0, pwroff_vol;

	battery->s2r = true;
	battery->current_avg = rk817_bat_get_avg_current(battery);
	battery->voltage_relax = rk817_bat_get_relax_voltage(battery);
	battery->voltage_avg = rk817_bat_get_battery_voltage(battery);
	battery->remain_cap = rk817_bat_get_capacity_uah(battery);
	battery->rsoc = rk817_bat_get_rsoc(battery);
	interval_sec = rk817_bat_rtc_sleep_sec(battery);
	battery->sleep_sum_sec += interval_sec;
	pwroff_vol = battery->pdata->pwroff_vol;

	if (!battery->sleep_chrg_online) {
		/* only add up discharge sleep seconds */
		battery->sleep_dischrg_sec += interval_sec;
		if (battery->voltage_avg <= pwroff_vol + 50)
			time_step = DISCHRG_TIME_STEP1;
		else
			time_step = DISCHRG_TIME_STEP2;
	}

	DBG("resume: dl=%d rl=%d c=%d v=%d rv=%d "
	    "cap=%d dt=%d at=%ld ch=%d, sec = %d\n",
	    battery->dsoc, battery->rsoc, battery->current_avg,
	    battery->voltage_avg, battery->voltage_relax,
	    rk817_bat_get_capacity_uah(battery), interval_sec,
	    battery->sleep_dischrg_sec, battery->sleep_chrg_online,
	    interval_sec);

	/* sleep: enough time and discharge */
	if ((!battery->sleep_chrg_online) &&
	    (battery->sleep_dischrg_sec > time_step)) {
		if (rk817_bat_sleep_dischrg(battery))
			battery->sleep_dischrg_sec = 0;
	}

	rk817_bat_save_data(battery);

	/* charge/lowpower lock: for battery work to update dsoc and rsoc */
	if ((battery->sleep_chrg_online) ||
	    (!battery->sleep_chrg_online &&
	    battery->voltage_avg < battery->pdata->pwroff_vol))
		wake_lock_timeout(&battery->wake_lock, msecs_to_jiffies(2000));

	queue_delayed_work(battery->bat_monitor_wq, &battery->bat_delay_work,
			   msecs_to_jiffies(1000));
}

static int rk817_bat_pm_resume(struct device *dev)
{
	struct rk817_battery_device *battery = dev_get_drvdata(dev);

	queue_work(battery->bat_monitor_wq, &battery->resume_work);

	return 0;
}
#endif

static SIMPLE_DEV_PM_OPS(rk817_bat_pm_ops,
			 rk817_bat_pm_suspend,
			 rk817_bat_pm_resume);

static struct platform_driver rk817_battery_driver = {
	.probe = rk817_battery_probe,
	.shutdown = rk817_battery_shutdown,
	.driver = {
		.name = "rk817-battery",
		.pm = &rk817_bat_pm_ops,
		.of_match_table = of_match_ptr(rk817_bat_of_match),
	},
};

static int __init rk817_battery_init(void)
{
	return platform_driver_register(&rk817_battery_driver);
}
fs_initcall_sync(rk817_battery_init);

static void __exit rk817_battery_exit(void)
{
	platform_driver_unregister(&rk817_battery_driver);
}
module_exit(rk817_battery_exit);

MODULE_DESCRIPTION("RK817 Battery driver");
MODULE_LICENSE("GPL");