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.

1390 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),
};