You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1389 lines
36 KiB
1389 lines
36 KiB
/*
|
|
* (C) Copyright 2018 Rockchip Electronics Co., Ltd
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
*/
|
|
#include <dm.h>
|
|
#include <errno.h>
|
|
#include <common.h>
|
|
#include <malloc.h>
|
|
#include <fdtdec.h>
|
|
#include <asm/gpio.h>
|
|
#include <common.h>
|
|
#include <power/pmic.h>
|
|
#include <dm/uclass-internal.h>
|
|
#include <power/charge_display.h>
|
|
#include <power/charge_animation.h>
|
|
#include <power/fuel_gauge.h>
|
|
#include <power/rk8xx_pmic.h>
|
|
#include <linux/usb/phy-rockchip-usb2.h>
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
static int dbg_enable = 0;
|
|
#define DBG(args...) \
|
|
do { \
|
|
if (dbg_enable) { \
|
|
printf(args); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define DIV(value) ((value) ? (value) : 1)
|
|
#define ENABLE 0x01
|
|
#define DISABLE 0x00
|
|
#define MAX_INTERPOLATE 1000
|
|
#define MAX_PERCENTAGE 100
|
|
#define MAX_INT 0x7FFF
|
|
|
|
#define ADC_CONFIG0 0x0050
|
|
#define CUR_ADC_CFG0 0x0051
|
|
#define CUR_ADC_CFG1 0x0052
|
|
#define VOL_ADC_CFG0 0x0053
|
|
#define VOL_ADC_CFG1 0x0054
|
|
#define ADC_CONFIG1 0x0055
|
|
#define GG_CON 0x0056
|
|
#define GG_STS 0x0057
|
|
#define RELAX_THRE_H 0x0058
|
|
#define RELAX_THRE_L 0x0059
|
|
#define RELAX_VOL1_H 0x005a
|
|
#define RELAX_VOL1_L 0x005b
|
|
#define RELAX_VOL2_H 0x005c
|
|
#define RELAX_VOL2_L 0x005d
|
|
#define RELAX_CUR1_H 0x005e
|
|
#define RELAX_CUR1_L 0x005f
|
|
#define RELAX_CUR2_H 0x0060
|
|
#define RELAX_CUR2_L 0x0061
|
|
#define OCV_THRE_VOL 0x0062
|
|
#define OCV_VOL_H 0x0063
|
|
#define OCV_VOL_L 0x0064
|
|
#define OCV_VOL0_H 0x0065
|
|
#define OCV_VOL0_L 0x0066
|
|
#define OCV_CUR_H 0x0067
|
|
#define OCV_CUR_L 0x0068
|
|
#define OCV_CUR0_H 0x0069
|
|
#define OCV_CUR0_L 0x006a
|
|
#define PWRON_VOL_H 0x006b
|
|
#define PWRON_VOL_L 0x006c
|
|
#define PWRON_CUR_H 0x006d
|
|
#define PWRON_CUR_L 0x006e
|
|
#define OFF_CNT 0x006f
|
|
#define Q_INIT_H3 0x0070
|
|
#define Q_INIT_H2 0x0071
|
|
#define Q_INIT_L1 0x0072
|
|
#define Q_INIT_L0 0x0073
|
|
#define Q_PRES_H3 0x0074
|
|
#define Q_PRES_H2 0x0075
|
|
#define Q_PRES_L1 0x0076
|
|
#define Q_PRES_L0 0x0077
|
|
#define BAT_VOL_H 0x0078
|
|
#define BAT_VOL_L 0x0079
|
|
#define BAT_CUR_H 0x007a
|
|
#define BAT_CUR 0x007b
|
|
#define BAT_TS_H 0x007c
|
|
#define BAT_TS_L 0x007d
|
|
#define USB_VOL_H 0x007e
|
|
#define USB_VOL_L 0x007f
|
|
#define SYS_VOL_H 0x0080
|
|
#define SYS_VOL_L 0x0081
|
|
#define Q_MAX_H3 0x0082
|
|
#define Q_MAX_H2 0x0083
|
|
#define Q_MAX_L1 0x0084
|
|
#define Q_MAX_L0 0x0085
|
|
#define Q_TERM_H3 0x0086
|
|
#define Q_TERM_H2 0x0087
|
|
#define Q_TERM_L1 0x0088
|
|
#define Q_TERM_L0 0x0089
|
|
#define Q_OCV_H3 0x008a
|
|
#define Q_OCV_H2 0x008b
|
|
#define Q_OCV_L1 0x008c
|
|
#define Q_OCV_L0 0x008d
|
|
#define OCV_CNT 0x008e
|
|
#define SLEEP_CON_SAMP_CUR_H 0x008f
|
|
#define SLEEP_CON_SAMP_CUR 0x0090
|
|
#define CAL_OFFSET_H 0x0091
|
|
#define CAL_OFFSET_L 0x0092
|
|
#define VCALIB0_H 0x0093
|
|
#define VCALIB0_L 0x0094
|
|
#define VCALIB1_H 0x0095
|
|
#define VCALIB1_L 0x0096
|
|
#define IOFFSET_H 0x0097
|
|
#define IOFFSET_L 0x0098
|
|
#define BAT_R0 0x0099
|
|
#define SOC_REG0 0x009a
|
|
#define SOC_REG1 0x009b
|
|
#define SOC_REG2 0x009c
|
|
#define REMAIN_CAP_REG0 0x9d
|
|
#define REMAIN_CAP_REG1 0x9e
|
|
#define REMAIN_CAP_REG2 0x9f
|
|
#define NEW_FCC_REG0 0x00a0
|
|
#define NEW_FCC_REG1 0x00a1
|
|
#define NEW_FCC_REG2 0x00a2
|
|
#define DRV_VERSION 0x00a3
|
|
#define DATA7 0x00a4
|
|
#define FG_INIT 0x00a5
|
|
#define HALT_CNT_REG 0x00a6
|
|
#define DATA10 0x00a7
|
|
#define DATA11 0x00a8
|
|
#define VOL_ADC_B3 0x00a9
|
|
#define VOL_ADC_B2 0x00aa
|
|
#define VOL_ADC_B1 0x00ab
|
|
#define VOL_ADC_B_7_0 0x00ac
|
|
#define CUR_ADC_K3 0x00ad
|
|
#define CUR_ADC_K2 0x00ae
|
|
#define CUR_ADC_K1 0x00af
|
|
#define CUR_ADC_K0 0x00b0
|
|
#define PMIC_CHRG_STS 0x00eb
|
|
#define BAT_DISCHRG 0x00ec
|
|
#define BAT_CON BIT(4)
|
|
|
|
#define USB_CTRL_REG 0x00E5
|
|
#define PMIC_SYS_STS 0x00f0
|
|
#define PLUG_IN_STS BIT(6)
|
|
|
|
#define CHRG_TERM_DSOC 90
|
|
#define CHRG_TERM_K 650
|
|
#define CHRG_FULL_K 400
|
|
#define CHARGE_FINISH (0x04 << 4)
|
|
|
|
/* CALI PARAM */
|
|
#define FINISH_CALI_CURR 1500
|
|
#define TERM_CALI_CURR 600
|
|
#define VIRTUAL_POWER_VOL 4200
|
|
#define VIRTUAL_POWER_CUR 1000
|
|
#define VIRTUAL_POWER_SOC 66
|
|
#define SECONDS(n) ((n) * 1000)
|
|
|
|
/* CHRG_CTRL_REG */
|
|
#define ILIM_450MA (0x00)
|
|
#define ILIM_2000MA (0x07)
|
|
#define ILIM_1500MA (0x03)
|
|
#define VLIM_4500MV (0x50)
|
|
|
|
/* sample resistor and division */
|
|
#define SAMPLE_RES_10mR 10
|
|
#define SAMPLE_RES_20mR 20
|
|
#define SAMPLE_RES_DIV1 1
|
|
#define SAMPLE_RES_DIV2 2
|
|
|
|
#define CHRG_CT_EN BIT(1)
|
|
#define MIN_FCC 500
|
|
#define CAP_INVALID BIT(7)
|
|
#define DIS_ILIM_EN BIT(3)
|
|
|
|
/* USB_CTRL_REG */
|
|
#define INPUT_CUR_MSK 0x0f
|
|
#define INPUT_VOL_MSK 0xf0
|
|
#define VOL_OUPUT_INSTANT_MODE 0x02
|
|
|
|
#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)
|
|
|
|
/* charger type definition */
|
|
enum charger_type {
|
|
NO_CHARGER = 0,
|
|
USB_CHARGER,
|
|
AC_CHARGER,
|
|
DC_CHARGER,
|
|
UNDEF_CHARGER,
|
|
};
|
|
|
|
enum power_supply_type {
|
|
POWER_SUPPLY_TYPE_UNKNOWN = 0,
|
|
POWER_SUPPLY_TYPE_USB, /* Standard Downstream Port */
|
|
POWER_SUPPLY_TYPE_USB_DCP, /* Dedicated Charging Port */
|
|
POWER_SUPPLY_TYPE_USB_CDP, /* Charging Downstream Port */
|
|
POWER_SUPPLY_TYPE_USB_FLOATING, /* DCP without shorting D+/D- */
|
|
};
|
|
|
|
struct rk817_battery_device {
|
|
struct udevice *dev;
|
|
int res_div;
|
|
bool is_first_power_on;
|
|
bool is_initialized;
|
|
bool bat_first_power_on;
|
|
int current_avg;
|
|
int current_pwron;
|
|
int voltage_usb;
|
|
int voltage_sys;
|
|
int voltage_avg;
|
|
int voltage_k;/* VCALIB0 VCALIB1 */
|
|
int voltage_b;
|
|
u32 remain_cap;
|
|
int design_cap;
|
|
int nac;
|
|
int fcc;
|
|
int qmax;
|
|
int dsoc;
|
|
int rsoc;
|
|
int pwron_voltage;
|
|
int sm_linek;
|
|
int sm_old_cap;
|
|
int calc_dsoc;
|
|
int calc_rsoc;
|
|
int sm_chrg_dsoc;
|
|
u8 halt_cnt;
|
|
bool is_halt;
|
|
int dbg_pwr_dsoc;
|
|
int dbg_pwr_rsoc;
|
|
int dbg_pwr_vol;
|
|
int dbg_meet_soc;
|
|
int dbg_calc_dsoc;
|
|
int dbg_calc_rsoc;
|
|
int adc_allow_update;
|
|
int pwroff_min;
|
|
int chrg_cur_input;
|
|
u32 *ocv_table;
|
|
int ocv_size;
|
|
u32 design_capacity;
|
|
u32 max_soc_offset;
|
|
u32 virtual_power;
|
|
u32 chrg_type;
|
|
ulong finish_chrg_base;
|
|
ulong term_sig_base;
|
|
int sm_meet_soc;
|
|
u32 bat_res_up;
|
|
u32 bat_res_down;
|
|
u32 variant;
|
|
int drv_version;
|
|
};
|
|
|
|
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_read(struct rk817_battery_device *battery, u8 reg)
|
|
{
|
|
return pmic_reg_read(battery->dev->parent, reg);
|
|
}
|
|
|
|
static void rk817_bat_write(struct rk817_battery_device *battery,
|
|
u8 reg, u8 buf)
|
|
{
|
|
pmic_reg_write(battery->dev->parent, reg, buf);
|
|
}
|
|
|
|
static int rk817_bat_get_vaclib0(struct rk817_battery_device *battery)
|
|
{
|
|
int vcalib_value = 0;
|
|
|
|
vcalib_value |= rk817_bat_read(battery, VCALIB0_H) << 8;
|
|
vcalib_value |= rk817_bat_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_read(battery, VCALIB1_H) << 8;
|
|
vcalib_value |= rk817_bat_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->variant == 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;
|
|
}
|
|
}
|
|
|
|
/* 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_read(battery, PWRON_VOL_H) << 8;
|
|
val |= rk817_bat_read(battery, PWRON_VOL_L);
|
|
vol = battery->voltage_k * val / 1000 + battery->voltage_b;
|
|
if (battery->variant == RK809_ID) {
|
|
vol_temp = (vol * battery->bat_res_up / battery->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;
|
|
|
|
val = rk817_bat_read(battery, USB_VOL_L) << 0;
|
|
val |= rk817_bat_read(battery, USB_VOL_H) << 8;
|
|
|
|
vol = (battery->voltage_k * val / 1000 + battery->voltage_b) * 60 / 46;
|
|
if (battery->variant == RK809_ID) {
|
|
vol_temp = vol * battery->bat_res_up / battery->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_read(battery, SYS_VOL_H) << 8;
|
|
val |= rk817_bat_read(battery, SYS_VOL_L) << 0;
|
|
|
|
vol = (battery->voltage_k * val / 1000 + battery->voltage_b) * 60 / 46;
|
|
if (battery->variant == RK809_ID) {
|
|
vol_temp = vol * battery->bat_res_up / battery->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;
|
|
|
|
val = rk817_bat_read(battery, BAT_VOL_H) << 8;
|
|
val |= rk817_bat_read(battery, BAT_VOL_L) << 0;
|
|
|
|
vol = battery->voltage_k * val / 1000 + battery->voltage_b;
|
|
if (battery->variant == RK809_ID) {
|
|
vol_temp = (vol * battery->bat_res_up / battery->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_read(battery, BAT_CUR);
|
|
val |= rk817_bat_read(battery, BAT_CUR_H) << 8;
|
|
|
|
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_read(battery, PWRON_CUR_L);
|
|
val |= rk817_bat_read(battery, PWRON_CUR_H) << 8;
|
|
|
|
if (val & 0x8000)
|
|
val -= 0x10000;
|
|
cur = ADC_TO_CURRENT(val, battery->res_div);
|
|
|
|
return cur;
|
|
}
|
|
|
|
static void rk817_bat_calibration(struct rk817_battery_device *battery)
|
|
{
|
|
int ioffset_value = 0;
|
|
u8 buf = 0;
|
|
|
|
if (rk817_bat_read(battery, ADC_CONFIG1) & 0x80) {
|
|
ioffset_value = rk817_bat_read(battery, IOFFSET_H) << 8;
|
|
ioffset_value |= rk817_bat_read(battery, IOFFSET_L);
|
|
|
|
buf = (ioffset_value >> 8) & 0xff;
|
|
rk817_bat_write(battery, CAL_OFFSET_H, buf);
|
|
buf = (ioffset_value >> 0) & 0xff;
|
|
rk817_bat_write(battery, CAL_OFFSET_L, buf);
|
|
|
|
rk817_bat_init_voltage_kb(battery);
|
|
rk817_bat_write(battery, ADC_CONFIG1, 0x80);
|
|
}
|
|
}
|
|
|
|
static int rk817_bat_get_rsoc(struct rk817_battery_device *battery);
|
|
|
|
static void rk817_bat_init_coulomb_cap(struct rk817_battery_device *battery,
|
|
u32 capacity)
|
|
{
|
|
u8 buf;
|
|
u32 cap;
|
|
u32 val;
|
|
|
|
cap = CAPACITY_TO_ADC(capacity, battery->res_div);
|
|
|
|
do {
|
|
buf = (cap >> 24) & 0xff;
|
|
rk817_bat_write(battery, Q_INIT_H3, buf);
|
|
buf = (cap >> 16) & 0xff;
|
|
rk817_bat_write(battery, Q_INIT_H2, buf);
|
|
buf = (cap >> 8) & 0xff;
|
|
rk817_bat_write(battery, Q_INIT_L1, buf);
|
|
buf = (cap >> 0) & 0xff;
|
|
rk817_bat_write(battery, Q_INIT_L0, buf);
|
|
|
|
val = rk817_bat_read(battery, Q_INIT_H3) << 24;
|
|
val |= rk817_bat_read(battery, Q_INIT_H2) << 16;
|
|
val |= rk817_bat_read(battery, Q_INIT_L1) << 8;
|
|
val |= rk817_bat_read(battery, Q_INIT_L0) << 0;
|
|
} while (cap != val);
|
|
|
|
battery->rsoc = rk817_bat_get_rsoc(battery);
|
|
battery->remain_cap = capacity * 1000;
|
|
}
|
|
|
|
static bool rk817_bat_remain_cap_is_valid(struct rk817_battery_device *battery)
|
|
{
|
|
return !(rk817_bat_read(battery, Q_PRES_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_read(battery, Q_PRES_H3) << 24;
|
|
val |= rk817_bat_read(battery, Q_PRES_H2) << 16;
|
|
val |= rk817_bat_read(battery, Q_PRES_L1) << 8;
|
|
val |= rk817_bat_read(battery, Q_PRES_L0) << 0;
|
|
|
|
capacity = ADC_TO_CAPACITY_UAH(val, battery->res_div);
|
|
}
|
|
|
|
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_read(battery, Q_PRES_H3) << 24;
|
|
val |= rk817_bat_read(battery, Q_PRES_H2) << 16;
|
|
val |= rk817_bat_read(battery, Q_PRES_L1) << 8;
|
|
val |= rk817_bat_read(battery, Q_PRES_L0) << 0;
|
|
capacity = ADC_TO_CAPACITY(val, battery->res_div);
|
|
}
|
|
|
|
return capacity;
|
|
}
|
|
|
|
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_write(battery, REMAIN_CAP_REG2, buf);
|
|
buf = (capacity >> 8) & 0xff;
|
|
rk817_bat_write(battery, REMAIN_CAP_REG1, buf);
|
|
buf = (capacity >> 0) & 0xff;
|
|
rk817_bat_write(battery, REMAIN_CAP_REG0, buf);
|
|
}
|
|
|
|
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_vol_to_soc(struct rk817_battery_device *battery,
|
|
int voltage)
|
|
{
|
|
u32 *ocv_table, temp;
|
|
int ocv_size, ocv_soc;
|
|
|
|
ocv_table = battery->ocv_table;
|
|
ocv_size = battery->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->ocv_table;
|
|
ocv_size = battery->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_write(battery, SOC_REG0, save_soc & 0xff);
|
|
rk817_bat_write(battery, SOC_REG1, (save_soc >> 8) & 0xff);
|
|
rk817_bat_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 value;
|
|
|
|
value = rk817_bat_read(battery, SOC_REG0);
|
|
value |= rk817_bat_read(battery, SOC_REG1) << 8;
|
|
value |= rk817_bat_read(battery, SOC_REG2) << 16;
|
|
|
|
return value;
|
|
}
|
|
|
|
static int rk817_bat_get_prev_cap(struct rk817_battery_device *battery)
|
|
{
|
|
int val = 0;
|
|
|
|
val = rk817_bat_read(battery, REMAIN_CAP_REG2) << 16;
|
|
val |= rk817_bat_read(battery, REMAIN_CAP_REG1) << 8;
|
|
val |= rk817_bat_read(battery, REMAIN_CAP_REG0) << 0;
|
|
|
|
return val;
|
|
}
|
|
|
|
static void rk817_bat_gas_gaugle_enable(struct rk817_battery_device *battery)
|
|
{
|
|
int value;
|
|
|
|
value = rk817_bat_read(battery, ADC_CONFIG0);
|
|
rk817_bat_write(battery, ADC_CONFIG0, value | 0x80);
|
|
}
|
|
|
|
static bool is_rk817_bat_first_pwron(struct rk817_battery_device *battery)
|
|
{
|
|
int value;
|
|
|
|
value = rk817_bat_read(battery, GG_STS);
|
|
|
|
if (value & BAT_CON) {
|
|
rk817_bat_write(battery, GG_STS, value & (~BAT_CON));
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static int rk817_bat_get_off_count(struct rk817_battery_device *battery)
|
|
{
|
|
int value;
|
|
|
|
value = rk817_bat_read(battery, OFF_CNT);
|
|
rk817_bat_write(battery, OFF_CNT, 0x00);
|
|
|
|
return value;
|
|
}
|
|
|
|
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_write(battery, Q_MAX_H3, buf);
|
|
buf = (cap_adc >> 16) & 0xff;
|
|
rk817_bat_write(battery, Q_MAX_H2, buf);
|
|
buf = (cap_adc >> 8) & 0xff;
|
|
rk817_bat_write(battery, Q_MAX_L1, buf);
|
|
buf = (cap_adc >> 0) & 0xff;
|
|
rk817_bat_write(battery, Q_MAX_L0, buf);
|
|
|
|
battery->qmax = capacity;
|
|
}
|
|
|
|
static void rk817_bat_save_fcc(struct rk817_battery_device *battery, int fcc)
|
|
{
|
|
u8 buf;
|
|
|
|
buf = (fcc >> 16) & 0xff;
|
|
rk817_bat_write(battery, NEW_FCC_REG2, buf);
|
|
buf = (fcc >> 8) & 0xff;
|
|
rk817_bat_write(battery, NEW_FCC_REG1, buf);
|
|
buf = (fcc >> 0) & 0xff;
|
|
rk817_bat_write(battery, NEW_FCC_REG0, buf);
|
|
}
|
|
|
|
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->design_cap;
|
|
|
|
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 int rk817_bat_get_fcc(struct rk817_battery_device *battery)
|
|
{
|
|
u32 fcc = 0;
|
|
|
|
fcc = rk817_bat_read(battery, NEW_FCC_REG2) << 16;
|
|
fcc |= rk817_bat_read(battery, NEW_FCC_REG1) << 8;
|
|
fcc |= rk817_bat_read(battery, NEW_FCC_REG0) << 0;
|
|
|
|
if (fcc < MIN_FCC) {
|
|
DBG("invalid fcc(%d), use design cap", fcc);
|
|
fcc = battery->design_capacity;
|
|
rk817_bat_save_fcc(battery, fcc);
|
|
} else if (fcc > battery->qmax) {
|
|
DBG("invalid fcc(%d), use qmax", fcc);
|
|
fcc = battery->qmax;
|
|
rk817_bat_save_fcc(battery, fcc);
|
|
}
|
|
|
|
return fcc;
|
|
}
|
|
|
|
static void rk817_bat_inc_halt_cnt(struct rk817_battery_device *battery)
|
|
{
|
|
u8 cnt;
|
|
|
|
cnt = rk817_bat_read(battery, HALT_CNT_REG);
|
|
rk817_bat_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);
|
|
|
|
battery->nac = rk817_bat_vol_to_cap(battery,
|
|
battery->pwron_voltage);
|
|
|
|
/* over 10%: system halt last time */
|
|
if (now_cap > pre_cap) {
|
|
if (abs(now_cap - pre_cap) > (battery->fcc / 10)) {
|
|
rk817_bat_inc_halt_cnt(battery);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
} else {
|
|
if (abs(battery->nac - pre_cap) > (battery->fcc / 5)) {
|
|
rk817_bat_inc_halt_cnt(battery);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
static u8 rk817_bat_get_halt_cnt(struct rk817_battery_device *battery)
|
|
{
|
|
return rk817_bat_read(battery, HALT_CNT_REG);
|
|
}
|
|
|
|
static int rk817_bat_is_initialized(struct rk817_battery_device *battery)
|
|
{
|
|
u8 val = rk817_bat_read(battery, FG_INIT);
|
|
|
|
return (val & 0x80);
|
|
}
|
|
|
|
static void rk817_bat_set_initialized_flag(struct rk817_battery_device *battery)
|
|
{
|
|
u8 val = rk817_bat_read(battery, FG_INIT);
|
|
|
|
rk817_bat_write(battery, FG_INIT, val | (0x80));
|
|
}
|
|
|
|
static void rk817_bat_not_first_pwron(struct rk817_battery_device *battery)
|
|
{
|
|
int now_soc, now_cap, pre_soc, pre_cap;
|
|
|
|
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->halt_cnt = rk817_bat_get_halt_cnt(battery);
|
|
battery->nac = rk817_bat_vol_to_cap(battery,
|
|
battery->pwron_voltage);
|
|
battery->remain_cap = pre_cap * 1000;
|
|
battery->is_halt = is_rk817_bat_last_halt(battery);
|
|
|
|
DBG("now_cap: %d, pre_cap: %d\n", now_cap, pre_cap);
|
|
|
|
if (now_cap > pre_cap) {
|
|
if (now_cap >= battery->fcc)
|
|
now_cap = battery->fcc;
|
|
|
|
now_soc = now_cap * 1000 * 100 / battery->fcc;
|
|
if (pre_soc < 100 * 1000)
|
|
pre_soc += (now_soc - pre_cap * 1000 * 100 / battery->fcc);
|
|
pre_cap = now_cap;
|
|
|
|
if (pre_soc >= 100 * 1000)
|
|
pre_soc = 100 * 1000;
|
|
if (now_cap >= battery->fcc)
|
|
pre_soc = 100 * 1000;
|
|
}
|
|
|
|
rk817_bat_init_coulomb_cap(battery, pre_cap);
|
|
rk817_bat_init_coulomb_cap(battery, pre_cap + 1);
|
|
rk817_bat_get_capacity_mah(battery);
|
|
|
|
battery->dsoc = pre_soc;
|
|
if (battery->dsoc > 100000)
|
|
battery->dsoc = 100000;
|
|
battery->nac = pre_cap;
|
|
if (battery->nac < 0)
|
|
battery->nac = 0;
|
|
|
|
DBG("dsoc=%d cap=%d v=%d pwron_v =%d min=%d psoc=%d pcap=%d\n",
|
|
battery->dsoc, battery->nac, rk817_bat_get_battery_voltage(battery),
|
|
rk817_bat_get_pwron_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)
|
|
{
|
|
int version, value;
|
|
|
|
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);
|
|
|
|
value = rk817_bat_read(battery, DRV_VERSION);
|
|
/* drv_version: bit0~bit3 */
|
|
version = value & 0x0f;
|
|
/* drv_version: [0 15] */
|
|
battery->drv_version &= 0x0f;
|
|
DBG("reg read version:%d dts read version:%d\n", version, battery->drv_version);
|
|
if (battery->drv_version != version) {
|
|
battery->is_first_power_on = 1;
|
|
value &= 0xf0;
|
|
value |= battery->drv_version;
|
|
rk817_bat_write(battery, DRV_VERSION, value);
|
|
}
|
|
|
|
DBG("battery = %d\n", rk817_bat_get_battery_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);
|
|
rk817_bat_save_cap(battery, battery->nac);
|
|
}
|
|
|
|
static int rk817_bat_calc_linek(struct rk817_battery_device *battery)
|
|
{
|
|
int linek, diff, delta;
|
|
|
|
battery->calc_dsoc = battery->dsoc;
|
|
battery->calc_rsoc = battery->rsoc;
|
|
battery->sm_old_cap = battery->remain_cap;
|
|
|
|
delta = abs(battery->dsoc - battery->rsoc);
|
|
diff = delta * 3;
|
|
battery->sm_meet_soc = (battery->dsoc >= battery->rsoc) ?
|
|
(battery->dsoc + diff) : (battery->rsoc + diff);
|
|
|
|
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;
|
|
|
|
battery->sm_chrg_dsoc = battery->dsoc;
|
|
|
|
DBG("<%s>. meet=%d, diff=%d, link=%d, calc: dsoc=%d, rsoc=%d\n",
|
|
__func__, battery->sm_meet_soc, diff, linek,
|
|
battery->calc_dsoc, battery->calc_rsoc);
|
|
|
|
return linek;
|
|
}
|
|
|
|
static int rk817_bat_get_est_voltage(struct rk817_battery_device *battery)
|
|
{
|
|
return rk817_bat_get_battery_voltage(battery);
|
|
}
|
|
|
|
static int rk817_bat_update_get_voltage(struct udevice *dev)
|
|
{
|
|
struct rk817_battery_device *battery = dev_get_priv(dev);
|
|
|
|
if (!battery->virtual_power && battery->voltage_k)
|
|
return rk817_bat_get_est_voltage(battery);
|
|
else
|
|
return VIRTUAL_POWER_VOL;
|
|
}
|
|
|
|
static int rk817_bat_update_get_current(struct udevice *dev)
|
|
{
|
|
struct rk817_battery_device *battery = dev_get_priv(dev);
|
|
|
|
if (!battery->virtual_power && battery->voltage_k)
|
|
return rk817_bat_get_avg_current(battery);
|
|
else
|
|
return VIRTUAL_POWER_CUR;
|
|
}
|
|
|
|
static int rk817_bat_dwc_otg_check_dpdm(struct rk817_battery_device *battery)
|
|
{
|
|
if (battery->variant == RK809_ID) {
|
|
if (rk817_bat_read(battery, PMIC_SYS_STS) & PLUG_IN_STS)
|
|
return AC_CHARGER;
|
|
else
|
|
return NO_CHARGER;
|
|
} else {
|
|
return rockchip_chg_get_type();
|
|
}
|
|
}
|
|
|
|
static bool rk817_bat_update_get_chrg_online(struct udevice *dev)
|
|
{
|
|
struct rk817_battery_device *battery = dev_get_priv(dev);
|
|
|
|
return rk817_bat_dwc_otg_check_dpdm(battery);
|
|
}
|
|
|
|
static int rk817_bat_get_usb_state(struct rk817_battery_device *battery)
|
|
{
|
|
int charger_type;
|
|
|
|
switch (rk817_bat_dwc_otg_check_dpdm(battery)) {
|
|
case POWER_SUPPLY_TYPE_UNKNOWN:
|
|
if ((rk817_bat_read(battery, PMIC_SYS_STS) & PLUG_IN_STS) != 0)
|
|
charger_type = DC_CHARGER;
|
|
else
|
|
charger_type = NO_CHARGER;
|
|
break;
|
|
case POWER_SUPPLY_TYPE_USB:
|
|
charger_type = USB_CHARGER;
|
|
break;
|
|
case POWER_SUPPLY_TYPE_USB_DCP:
|
|
case POWER_SUPPLY_TYPE_USB_CDP:
|
|
case POWER_SUPPLY_TYPE_USB_FLOATING:
|
|
charger_type = AC_CHARGER;
|
|
break;
|
|
default:
|
|
charger_type = NO_CHARGER;
|
|
}
|
|
|
|
return charger_type;
|
|
}
|
|
|
|
static int rk817_bat_get_charger_type(struct rk817_battery_device *battery)
|
|
{
|
|
struct rk8xx_priv *rk8xx = dev_get_priv(battery->dev->parent);
|
|
u32 chrg_type;
|
|
u8 val;
|
|
|
|
/* check by ic hardware: this check make check work safer */
|
|
if ((rk817_bat_read(battery, PMIC_SYS_STS) & PLUG_IN_STS) == 0)
|
|
return NO_CHARGER;
|
|
|
|
/* virtual or bat not exist */
|
|
if (battery->virtual_power)
|
|
return DC_CHARGER;
|
|
|
|
/* check USB secondly */
|
|
chrg_type = rk817_bat_get_usb_state(battery);
|
|
if (chrg_type != NO_CHARGER && (battery->rsoc + 500) / 1000 >= 100)
|
|
chrg_type = CHARGE_FINISH;
|
|
|
|
if (rk8xx->variant == RK817_ID) {
|
|
val = rk817_bat_read(battery, PMIC_CHRG_STS);
|
|
if ((val & 0x70) == CHARGE_FINISH)
|
|
chrg_type = CHARGE_FINISH;
|
|
}
|
|
|
|
return chrg_type;
|
|
}
|
|
|
|
static void rk817_bat_set_input_current(struct rk817_battery_device *battery,
|
|
int input_current)
|
|
{
|
|
u8 usb_ctrl;
|
|
|
|
usb_ctrl = rk817_bat_read(battery, USB_CTRL_REG);
|
|
usb_ctrl &= ~INPUT_CUR_MSK;
|
|
usb_ctrl |= ((input_current) | 0x08);
|
|
rk817_bat_write(battery, USB_CTRL_REG, usb_ctrl);
|
|
}
|
|
|
|
static void rk817_bat_set_input_voltage(struct rk817_battery_device *battery,
|
|
int input_voltage)
|
|
{
|
|
u8 usb_ctrl;
|
|
|
|
usb_ctrl = rk817_bat_read(battery, USB_CTRL_REG);
|
|
usb_ctrl &= ~INPUT_VOL_MSK;
|
|
usb_ctrl |= ((input_voltage) | 0x80);
|
|
rk817_bat_write(battery, USB_CTRL_REG, usb_ctrl);
|
|
}
|
|
|
|
static void rk817_bat_charger_setting(struct rk817_battery_device *battery,
|
|
int charger)
|
|
{
|
|
static u8 old_charger = UNDEF_CHARGER;
|
|
|
|
rk817_bat_set_input_voltage(battery, VLIM_4500MV);
|
|
/* charger changed */
|
|
if (old_charger != charger) {
|
|
if (charger == NO_CHARGER) {
|
|
DBG("NO_CHARGER\n");
|
|
rk817_bat_set_input_current(battery, ILIM_450MA);
|
|
} else if (charger == USB_CHARGER) {
|
|
DBG("USB_CHARGER\n");
|
|
rk817_bat_set_input_current(battery, ILIM_450MA);
|
|
} else if (charger == DC_CHARGER || charger == AC_CHARGER) {
|
|
DBG("DC OR AC CHARGE\n");
|
|
rk817_bat_set_input_current(battery, ILIM_1500MA);
|
|
} else {
|
|
DBG("charger setting error %d\n", charger);
|
|
}
|
|
|
|
old_charger = charger;
|
|
}
|
|
}
|
|
|
|
static void rk817_bat_linek_algorithm(struct rk817_battery_device *battery)
|
|
{
|
|
int delta_cap, ydsoc, tmp;
|
|
u8 chg_st = rk817_bat_get_charger_type(battery);
|
|
|
|
/* slow down */
|
|
if (battery->dsoc / 1000 == 99)
|
|
battery->sm_linek = CHRG_FULL_K;
|
|
else if (battery->dsoc / 1000 >= CHRG_TERM_DSOC &&
|
|
battery->current_avg > TERM_CALI_CURR)
|
|
battery->sm_linek = CHRG_TERM_K;
|
|
|
|
delta_cap = battery->remain_cap - battery->sm_old_cap;
|
|
ydsoc = battery->sm_linek * (delta_cap / DIV(battery->fcc)) / 10;
|
|
battery->sm_chrg_dsoc += ydsoc;
|
|
|
|
tmp = battery->sm_chrg_dsoc / 1000;
|
|
|
|
if (ydsoc > 0) {
|
|
if (battery->sm_chrg_dsoc < 0)
|
|
battery->sm_chrg_dsoc = 0;
|
|
|
|
tmp = battery->sm_chrg_dsoc / 1000;
|
|
|
|
if (tmp != battery->dsoc / 1000) {
|
|
if (battery->sm_chrg_dsoc < battery->dsoc)
|
|
return;
|
|
|
|
battery->dsoc = battery->sm_chrg_dsoc;
|
|
if (battery->dsoc <= 0)
|
|
battery->dsoc = 0;
|
|
}
|
|
|
|
battery->sm_old_cap = battery->remain_cap;
|
|
if (battery->dsoc / 1000 == battery->rsoc / 1000 &&
|
|
battery->sm_linek != CHRG_FULL_K &&
|
|
battery->sm_linek != CHRG_TERM_K)
|
|
battery->sm_linek = 1000;
|
|
}
|
|
|
|
if ((battery->sm_linek == 1000 || battery->dsoc >= 100 * 1000) &&
|
|
(chg_st != CHARGE_FINISH)) {
|
|
if (battery->sm_linek == 1000)
|
|
battery->dsoc = battery->rsoc;
|
|
battery->sm_chrg_dsoc = battery->dsoc;
|
|
}
|
|
}
|
|
|
|
static void rk817_bat_finish_chrg(struct rk817_battery_device *battery)
|
|
{
|
|
u32 tgt_sec = 0;
|
|
|
|
if (battery->dsoc / 1000 < 100) {
|
|
tgt_sec = battery->fcc * 3600 / 100 / FINISH_CALI_CURR;
|
|
if (get_timer(battery->finish_chrg_base) > SECONDS(tgt_sec)) {
|
|
battery->finish_chrg_base = get_timer(0);
|
|
battery->dsoc += 1000;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void rk817_bat_debug_info(struct rk817_battery_device *battery)
|
|
{
|
|
DBG("debug info:\n");
|
|
DBG("CAL_OFFSET = 0x%x", rk817_bat_read(battery, CAL_OFFSET_H));
|
|
DBG("%x\n", rk817_bat_read(battery, CAL_OFFSET_L));
|
|
DBG("current_avg = %d\n", rk817_bat_get_avg_current(battery));
|
|
DBG("k = %d, b = %d\n", battery->voltage_k, battery->voltage_b);
|
|
DBG("battery: %d\n", rk817_bat_get_battery_voltage(battery));
|
|
DBG("voltage_sys = %d\n", rk817_bat_get_sys_voltage(battery));
|
|
DBG("voltage_usb = %d\n", rk817_bat_get_USB_voltage(battery));
|
|
DBG("current_avg = %d\n", rk817_bat_get_avg_current(battery));
|
|
DBG("dsoc = %d\n", battery->dsoc);
|
|
DBG("rsoc = %d\n", rk817_bat_get_rsoc(battery));
|
|
DBG("remain_cap = %d\n", rk817_bat_get_capacity_uah(battery));
|
|
DBG("fcc = %d\n", battery->fcc);
|
|
DBG("qmax = %d\n", battery->qmax);
|
|
}
|
|
|
|
static void rk817_bat_smooth_charge(struct rk817_battery_device *battery)
|
|
{
|
|
u8 chg_st = rk817_bat_get_charger_type(battery);
|
|
|
|
rk817_bat_debug_info(battery);
|
|
rk817_bat_calibration(battery);
|
|
/* set terminal charge mode */
|
|
if (battery->term_sig_base &&
|
|
get_timer(battery->term_sig_base) > SECONDS(1))
|
|
battery->term_sig_base = 0;
|
|
|
|
/* not charge mode and not keep in uboot charge: exit */
|
|
if ((battery->chrg_type == NO_CHARGER) ||
|
|
!rk817_bat_is_initialized(battery)) {
|
|
DBG("chrg=%d\n", battery->chrg_type);
|
|
rk817_bat_set_initialized_flag(battery);
|
|
goto out;
|
|
}
|
|
|
|
/* update rsoc and remain cap */
|
|
battery->remain_cap = rk817_bat_get_capacity_uah(battery);
|
|
battery->rsoc = rk817_bat_get_rsoc(battery);
|
|
if (battery->remain_cap / 1000 > battery->fcc) {
|
|
battery->sm_old_cap -=
|
|
(battery->remain_cap - battery->fcc * 1000);
|
|
rk817_bat_init_coulomb_cap(battery, battery->fcc + 100);
|
|
rk817_bat_init_coulomb_cap(battery, battery->fcc);
|
|
}
|
|
|
|
/* finish charge step */
|
|
if (chg_st == CHARGE_FINISH) {
|
|
rk817_bat_finish_chrg(battery);
|
|
rk817_bat_init_coulomb_cap(battery, battery->fcc + 100);
|
|
rk817_bat_init_coulomb_cap(battery, battery->fcc);
|
|
} else {
|
|
DBG("smooth charge step...\n");
|
|
battery->adc_allow_update = true;
|
|
battery->finish_chrg_base = get_timer(0);
|
|
rk817_bat_linek_algorithm(battery);
|
|
}
|
|
|
|
/* dsoc limit */
|
|
if (battery->dsoc / 1000 > 100)
|
|
battery->dsoc = 100 * 1000;
|
|
else if (battery->dsoc < 0)
|
|
battery->dsoc = 0;
|
|
|
|
rk817_bat_save_dsoc(battery, battery->dsoc);
|
|
rk817_bat_save_cap(battery, battery->remain_cap / 1000);
|
|
out:
|
|
return;
|
|
}
|
|
|
|
static int rk817_bat_update_get_soc(struct udevice *dev)
|
|
{
|
|
struct rk817_battery_device *battery = dev_get_priv(dev);
|
|
static ulong seconds;
|
|
|
|
rk817_bat_debug_info(battery);
|
|
/* set charge current */
|
|
battery->chrg_type =
|
|
rk817_bat_get_charger_type(battery);
|
|
rk817_bat_charger_setting(battery, battery->chrg_type);
|
|
|
|
/* fg calc every 5 seconds */
|
|
if (!seconds)
|
|
seconds = get_timer(0);
|
|
if (get_timer(seconds) >= SECONDS(5)) {
|
|
seconds = get_timer(0);
|
|
rk817_bat_smooth_charge(battery);
|
|
}
|
|
|
|
/* bat exist, fg init success(dts pass) and uboot charge: report data */
|
|
if (!battery->virtual_power && battery->voltage_k)
|
|
return battery->dsoc / 1000;
|
|
else
|
|
return VIRTUAL_POWER_SOC;
|
|
}
|
|
|
|
static int rk817_is_bat_exist(struct rk817_battery_device *battery)
|
|
{
|
|
struct rk8xx_priv *rk8xx = dev_get_priv(battery->dev->parent);
|
|
|
|
if (rk8xx->variant == RK817_ID)
|
|
return (rk817_bat_read(battery, PMIC_CHRG_STS) & 0x80) ? 1 : 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int rk817_bat_bat_is_exist(struct udevice *dev)
|
|
{
|
|
struct rk817_battery_device *battery = dev_get_priv(dev);
|
|
|
|
return rk817_is_bat_exist(battery);
|
|
}
|
|
|
|
|
|
static struct dm_fuel_gauge_ops fg_ops = {
|
|
.bat_is_exist = rk817_bat_bat_is_exist,
|
|
.get_soc = rk817_bat_update_get_soc,
|
|
.get_voltage = rk817_bat_update_get_voltage,
|
|
.get_current = rk817_bat_update_get_current,
|
|
.get_chrg_online = rk817_bat_update_get_chrg_online,
|
|
};
|
|
|
|
static int rk817_fg_ofdata_to_platdata(struct udevice *dev)
|
|
{
|
|
struct rk8xx_priv *rk8xx = dev_get_priv(dev->parent);
|
|
struct rk817_battery_device *battery = dev_get_priv(dev);
|
|
const char *prop;
|
|
int len, value;
|
|
int i;
|
|
|
|
if ((rk8xx->variant != RK817_ID) && (rk8xx->variant != RK809_ID)) {
|
|
debug("%s: Not support pmic variant: rk%x\n",
|
|
__func__, rk8xx->variant);
|
|
return -EINVAL;
|
|
}
|
|
|
|
battery->dev = dev;
|
|
battery->variant = rk8xx->variant;
|
|
/* Parse ocv table */
|
|
prop = dev_read_prop(dev, "ocv_table", &len);
|
|
if (!prop) {
|
|
printf("can't find ocv_table prop\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
battery->ocv_table = calloc(len, 1);
|
|
if (!battery->ocv_table) {
|
|
printf("can't calloc ocv_table\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
battery->ocv_size = len / 4;
|
|
if (dev_read_u32_array(dev, "ocv_table",
|
|
battery->ocv_table, battery->ocv_size)) {
|
|
printf("can't read ocv_table\n");
|
|
free(battery->ocv_table);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Parse neccessay */
|
|
battery->design_cap = dev_read_u32_default(dev, "design_capacity", -1);
|
|
if (battery->design_cap < 0) {
|
|
printf("can't read design_capacity\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
battery->qmax = dev_read_u32_default(dev, "design_qmax", -1);
|
|
if (battery->qmax < 0) {
|
|
printf("can't read design_qmax\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
battery->virtual_power = dev_read_u32_default(dev, "virtual_power", 0);
|
|
if (!rk817_is_bat_exist(battery))
|
|
battery->virtual_power = 1;
|
|
|
|
if (rk8xx->variant == RK809_ID) {
|
|
battery->bat_res_up = dev_read_u32_default(dev, "bat_res_up", -1);
|
|
if (battery->bat_res_up < 0) {
|
|
printf("can't read bat_res_up\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
battery->bat_res_down = dev_read_u32_default(dev, "bat_res_down", -1);
|
|
if (battery->bat_res_down < 0) {
|
|
printf("can't read bat_res_down\n");
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
battery->drv_version = dev_read_u32_default(dev, "drv_version", -1);
|
|
if (battery->drv_version < 0)
|
|
battery->drv_version = 0;
|
|
|
|
value = dev_read_u32_default(dev, "sample_res", -1);
|
|
if (battery->res_div < 0)
|
|
printf("read sample_res error\n");
|
|
|
|
battery->res_div = (value == SAMPLE_RES_20mR) ?
|
|
SAMPLE_RES_DIV2 : SAMPLE_RES_DIV1;
|
|
|
|
DBG("OCV Value:");
|
|
for (i = 0; i < battery->ocv_size; i++)
|
|
DBG("%d ", battery->ocv_table[i]);
|
|
DBG("ocvsize: %d\n", battery->ocv_size);
|
|
DBG("battery->design_cap: %d\n", battery->design_cap);
|
|
DBG("battery->qmax: %d\n", battery->qmax);
|
|
DBG("battery->bat_res_up: %d\n", battery->bat_res_up);
|
|
DBG("battery->bat_res_down: %d\n", battery->bat_res_down);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rk817_fg_init(struct rk817_battery_device *battery)
|
|
{
|
|
int value;
|
|
|
|
value = rk817_bat_read(battery, GG_CON);
|
|
rk817_bat_write(battery, GG_CON, value | VOL_OUPUT_INSTANT_MODE);
|
|
if (battery->variant == RK817_ID) {
|
|
value = rk817_bat_read(battery, BAT_DISCHRG);
|
|
rk817_bat_write(battery, BAT_DISCHRG, value & (~DIS_ILIM_EN));
|
|
}
|
|
rk817_bat_gas_gaugle_enable(battery);
|
|
rk817_bat_init_voltage_kb(battery);
|
|
rk817_bat_calibration(battery);
|
|
rk817_bat_rsoc_init(battery);
|
|
rk817_bat_init_coulomb_cap(battery, battery->nac);
|
|
rk817_bat_set_initialized_flag(battery);
|
|
|
|
battery->voltage_avg = rk817_bat_get_battery_voltage(battery);
|
|
battery->voltage_sys = rk817_bat_get_sys_voltage(battery);
|
|
battery->voltage_usb = rk817_bat_get_USB_voltage(battery);
|
|
battery->current_avg = rk817_bat_get_avg_current(battery);
|
|
battery->current_pwron = rk817_bat_get_pwron_current(battery);
|
|
battery->remain_cap = rk817_bat_get_capacity_uah(battery);
|
|
battery->rsoc = rk817_bat_get_rsoc(battery);
|
|
battery->sm_linek = rk817_bat_calc_linek(battery);
|
|
battery->chrg_type = rk817_bat_get_charger_type(battery);
|
|
battery->finish_chrg_base = get_timer(0);
|
|
battery->term_sig_base = get_timer(0);
|
|
|
|
battery->dbg_pwr_dsoc = battery->dsoc;
|
|
battery->dbg_pwr_rsoc = battery->rsoc;
|
|
battery->dbg_pwr_vol = battery->voltage_avg;
|
|
|
|
if (battery->variant == RK817_ID)
|
|
rk817_bat_charger_setting(battery, battery->chrg_type);
|
|
|
|
DBG("voltage_k = %d, voltage_b = %d\n",
|
|
battery->voltage_k, battery->voltage_b);
|
|
DBG("voltage_sys = %d\n", battery->voltage_sys);
|
|
DBG("voltage usb: %d\n", battery->voltage_avg);
|
|
DBG("battery: %d\n", battery->voltage_avg);
|
|
DBG("current_avg = %d\n", battery->current_avg);
|
|
DBG("current_pwron = %d\n", battery->current_pwron);
|
|
DBG("remain_cap = %d\n", battery->remain_cap);
|
|
DBG("fcc = %d\n", battery->fcc);
|
|
DBG("qmax = %d\n", battery->qmax);
|
|
DBG("dsoc = %d\n", battery->dsoc);
|
|
DBG("rsoc = %d\n", battery->rsoc);
|
|
DBG("charge type: %d\n", battery->chrg_type);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rk817_fg_probe(struct udevice *dev)
|
|
{
|
|
struct rk8xx_priv *priv = dev_get_priv(dev->parent);
|
|
struct rk817_battery_device *battery = dev_get_priv(dev);
|
|
|
|
if ((priv->variant != RK817_ID) && ((priv->variant != RK809_ID))) {
|
|
debug("Not support pmic variant: rk%x\n", priv->variant);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return rk817_fg_init(battery);
|
|
}
|
|
|
|
U_BOOT_DRIVER(rk817_fg) = {
|
|
.name = "rk817_fg",
|
|
.id = UCLASS_FG,
|
|
.probe = rk817_fg_probe,
|
|
.ops = &fg_ops,
|
|
.ofdata_to_platdata = rk817_fg_ofdata_to_platdata,
|
|
.priv_auto_alloc_size = sizeof(struct rk817_battery_device),
|
|
};
|
|
|