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.
2963 lines
91 KiB
2963 lines
91 KiB
/*
|
|
* sm5714_fuelgauge.c
|
|
* Samsung sm5714 Fuel Gauge Driver
|
|
*
|
|
* Copyright (C) 2019 Samsung Electronics
|
|
*
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
/* #define BATTERY_LOG_MESSAGE */
|
|
#include <linux/mfd/sm/sm5714/sm5714-private.h>
|
|
#include "sm5714_fuelgauge.h"
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/seq_file.h>
|
|
|
|
static enum power_supply_property sm5714_fuelgauge_props[] = {
|
|
POWER_SUPPLY_PROP_STATUS,
|
|
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
|
POWER_SUPPLY_PROP_VOLTAGE_AVG,
|
|
POWER_SUPPLY_PROP_CURRENT_NOW,
|
|
POWER_SUPPLY_PROP_CURRENT_AVG,
|
|
POWER_SUPPLY_PROP_CHARGE_FULL,
|
|
POWER_SUPPLY_PROP_ENERGY_NOW,
|
|
POWER_SUPPLY_PROP_CAPACITY,
|
|
POWER_SUPPLY_PROP_TEMP,
|
|
POWER_SUPPLY_PROP_TEMP_AMBIENT,
|
|
#if defined(CONFIG_EN_OOPS)
|
|
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
|
|
#endif
|
|
POWER_SUPPLY_PROP_ENERGY_FULL,
|
|
POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
|
|
POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
|
|
POWER_SUPPLY_PROP_CHARGE_ENABLED,
|
|
#if defined(CONFIG_BATTERY_AGE_FORECAST)
|
|
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
|
|
#endif
|
|
};
|
|
|
|
static char *sm5714_fg_supplied_to[] = {
|
|
"sm5714-fuelgauge",
|
|
};
|
|
|
|
#define MINVAL(a, b) ((a <= b) ? a : b)
|
|
#define MAXVAL(a, b) ((a > b) ? a : b)
|
|
|
|
#define LIMIT_N_CURR_MIXFACTOR -2000
|
|
#define TABLE_READ_COUNT 2
|
|
#define FG_ABNORMAL_RESET -1
|
|
#define IGNORE_N_I_OFFSET 1
|
|
|
|
#define SM5714_FG_FULL_DEBUG 1
|
|
#define I2C_ERROR_COUNT_MAX 5
|
|
#if defined(CONFIG_SEC_FACTORY)
|
|
#define FG_REG_CHECK_TABLE_LEN 12
|
|
#endif
|
|
|
|
extern int factory_mode;
|
|
|
|
void sm5714_adabt_full_offset(struct sm5714_fuelgauge_data *fuelgauge);
|
|
static bool sm5714_fg_init(struct sm5714_fuelgauge_data *fuelgauge, bool is_surge);
|
|
|
|
|
|
static int sm5714_device_id = -1;
|
|
#if !defined(CONFIG_SEC_FACTORY)
|
|
static int sm5714_fg_debug_print = 0;
|
|
#endif
|
|
/* static unsigned int lpcharge = 0; */
|
|
|
|
enum sm5714_battery_table_type {
|
|
DISCHARGE_TABLE = 0,
|
|
SOC_TABLE,
|
|
TABLE_MAX,
|
|
};
|
|
|
|
static int sm5714_regs [] = {
|
|
0x0000, 0x0001, 0x0003, 0x0004, 0x0010, 0x0013, 0x0020, 0x0021, 0x0022, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F
|
|
};
|
|
|
|
static int sm5714_srams [] = {
|
|
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A,
|
|
0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C,
|
|
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
|
|
0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048,
|
|
0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
|
0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
|
|
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
|
|
0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x0087, 0x008A, 0x008B,
|
|
0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
|
|
0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
|
|
0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
|
|
0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
|
|
0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
|
|
0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
|
|
0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
|
|
0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
|
|
0x00D8, 0x00DF, 0x00ED, 0x00EE
|
|
};
|
|
#if defined(CONFIG_SEC_FACTORY)
|
|
static int sm5714_reg_check_table[2][12] = {
|
|
{0x60, 0x61, 0x62, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x7B, 0x7C},
|
|
{0x0800, 0x0000, 0x0800, 0x0000, 0x0800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0733, 0x0002},
|
|
};
|
|
#endif
|
|
static int sm5714_fg_find_addr(int addr)
|
|
{
|
|
int r_size = (int)ARRAY_SIZE(sm5714_regs);
|
|
int i;
|
|
|
|
for (i = 0; i < r_size; i++) {
|
|
if (sm5714_regs[i] == addr) {
|
|
pr_info("%s: addr: 0x%x == 0x%x is founded\n", __func__, sm5714_regs[i], addr);
|
|
return 1;
|
|
}
|
|
}
|
|
pr_info("%s: addr: 0x%x doesn't exist, cannot write data\n", __func__, addr);
|
|
return 0;
|
|
}
|
|
static int sm5714_fg_find_sram_addr(int addr)
|
|
{
|
|
int r_size = (int)ARRAY_SIZE(sm5714_srams);
|
|
int i;
|
|
|
|
for (i = 0; i < r_size; i++) {
|
|
if (sm5714_srams[i] == addr) {
|
|
pr_info("%s: addr: 0x%x == 0x%x is founded\n", __func__, sm5714_srams[i], addr);
|
|
return 1;
|
|
}
|
|
}
|
|
pr_info("%s: addr: 0x%x doesn't exist, cannot write data\n", __func__, addr);
|
|
return 0;
|
|
}
|
|
|
|
static int sm5714_fg_read_sram(struct sm5714_fuelgauge_data* fuelgauge, int sram_addr)
|
|
{
|
|
int data;
|
|
|
|
mutex_lock(&fuelgauge->fg_lock);
|
|
|
|
if (sm5714_write_word(fuelgauge->i2c, SM5714_FG_REG_SRAM_RADDR, sram_addr)) {
|
|
pr_info("%s: SM5714_FG_REG_SRAM_RADDR write ERROR!!!!!\n", __func__);
|
|
mutex_unlock(&fuelgauge->fg_lock);
|
|
return -1;
|
|
}
|
|
|
|
data = sm5714_read_word(fuelgauge->i2c, SM5714_FG_REG_SRAM_RDATA);
|
|
|
|
mutex_unlock(&fuelgauge->fg_lock);
|
|
|
|
return data;
|
|
}
|
|
|
|
static int sm5714_fg_write_sram(struct sm5714_fuelgauge_data* fuelgauge, int sram_addr, int data)
|
|
{
|
|
mutex_lock(&fuelgauge->fg_lock);
|
|
|
|
if (sm5714_write_word(fuelgauge->i2c, SM5714_FG_REG_SRAM_WADDR, sram_addr)) {
|
|
pr_info("%s: SM5714_FG_REG_SRAM_WADDR write ERROR!!!!!\n", __func__);
|
|
mutex_unlock(&fuelgauge->fg_lock);
|
|
return -1;
|
|
}
|
|
if (sm5714_write_word(fuelgauge->i2c, SM5714_FG_REG_SRAM_WDATA, data)) {
|
|
pr_info("%s: SM5714_FG_REG_SRAM_WDATA write ERROR!!!!!\n", __func__);
|
|
mutex_unlock(&fuelgauge->fg_lock);
|
|
return -1;
|
|
}
|
|
|
|
mutex_unlock(&fuelgauge->fg_lock);
|
|
|
|
return 1;
|
|
}
|
|
|
|
bool sm5714_fg_fuelalert_init(struct sm5714_fuelgauge_data *fuelgauge,
|
|
int soc);
|
|
|
|
#if !defined(CONFIG_SEC_FACTORY)
|
|
static void sm5714_fg_read_time(struct sm5714_fuelgauge_data *fuelgauge)
|
|
{
|
|
pr_info("%s: sm5714_fg_read_time\n", __func__);
|
|
|
|
return;
|
|
}
|
|
|
|
static void sm5714_fg_test_print(struct sm5714_fuelgauge_data *fuelgauge)
|
|
{
|
|
pr_info("%s: sm5714_fg_test_print\n", __func__);
|
|
|
|
sm5714_fg_read_time(fuelgauge);
|
|
}
|
|
|
|
static void sm5714_dump_all(struct sm5714_fuelgauge_data* fuelgauge) {
|
|
int val;
|
|
int i;
|
|
int r_size = (int)ARRAY_SIZE(sm5714_srams)/4;
|
|
char temp_buf[660] = {0,};
|
|
|
|
switch (sm5714_fg_debug_print%5) {
|
|
case 0:
|
|
for (i = 0; i < (int)ARRAY_SIZE(sm5714_regs); i++) {
|
|
val = -1;
|
|
val = sm5714_read_word(fuelgauge->i2c, sm5714_regs[i]);
|
|
sprintf(temp_buf+strlen(temp_buf), "%02x:%04x,", sm5714_regs[i], val);
|
|
}
|
|
pr_info("[sm5714_fg_all_regs] %s\n", temp_buf);
|
|
memset(temp_buf, 0x0, sizeof(temp_buf));
|
|
sm5714_fg_debug_print++;
|
|
break;
|
|
case 1:
|
|
for (i=0; i<r_size; i++) {
|
|
val = -1;
|
|
val = sm5714_fg_read_sram(fuelgauge, sm5714_srams[i]);
|
|
sprintf(temp_buf+strlen(temp_buf), "%02x:%04x,", sm5714_srams[i], val);
|
|
}
|
|
pr_info("[sm5714_fg_all_srams_1] %s\n", temp_buf);
|
|
memset(temp_buf, 0x0, sizeof(temp_buf));
|
|
sm5714_fg_debug_print++;
|
|
break;
|
|
case 2:
|
|
for (i=r_size; i<r_size*2; i++) {
|
|
val = -1;
|
|
val = sm5714_fg_read_sram(fuelgauge, sm5714_srams[i]);
|
|
sprintf(temp_buf+strlen(temp_buf), "%02x:%04x,", sm5714_srams[i], val);
|
|
}
|
|
pr_info("[sm5714_fg_all_srams_2] %s\n", temp_buf);
|
|
memset(temp_buf, 0x0, sizeof(temp_buf));
|
|
sm5714_fg_debug_print++;
|
|
break;
|
|
case 3:
|
|
for (i=r_size*2; i<r_size*3; i++) {
|
|
val = -1;
|
|
val = sm5714_fg_read_sram(fuelgauge, sm5714_srams[i]);
|
|
sprintf(temp_buf+strlen(temp_buf), "%02x:%04x,", sm5714_srams[i], val);
|
|
}
|
|
pr_info("[sm5714_fg_all_srams_3] %s\n", temp_buf);
|
|
memset(temp_buf, 0x0, sizeof(temp_buf));
|
|
sm5714_fg_debug_print++;
|
|
break;
|
|
case 4:
|
|
for (i=r_size*3; i<r_size*4; i++) {
|
|
val = -1;
|
|
val = sm5714_fg_read_sram(fuelgauge, sm5714_srams[i]);
|
|
sprintf(temp_buf+strlen(temp_buf), "%02x:%04x,", sm5714_srams[i], val);
|
|
}
|
|
pr_info("[sm5714_fg_all_srams_4] %s\n", temp_buf);
|
|
memset(temp_buf, 0x0, sizeof(temp_buf));
|
|
sm5714_fg_debug_print=0;
|
|
break;
|
|
|
|
|
|
}
|
|
}
|
|
#endif
|
|
#if defined(CONFIG_SEC_FACTORY)
|
|
static void sm5714_fuel_gauge_abnormal_reg_check(struct sm5714_fuelgauge_data *fuelgauge)
|
|
{
|
|
int ret = 0;
|
|
int addr, data;
|
|
int i, err_cnt = 0;
|
|
|
|
for (i = 0; i < FG_REG_CHECK_TABLE_LEN; i++) {
|
|
addr = sm5714_reg_check_table[0][i];
|
|
data = sm5714_reg_check_table[1][i];
|
|
ret = sm5714_fg_read_sram(fuelgauge, addr);
|
|
if (ret != data) {
|
|
pr_info("%s: addr:0x%02x, data:0x%04x, comp value:0x%04x\n",
|
|
__func__, addr, data, ret);
|
|
err_cnt++;
|
|
} else
|
|
pr_info("%s: addr:0x%02x, data:0x%04x, comp value:0x%04x\n",
|
|
__func__, addr, data, ret);
|
|
}
|
|
if (err_cnt)
|
|
panic("sm5714-fg abnormal reg");
|
|
}
|
|
#endif
|
|
|
|
static bool sm5714_fg_check_reg_init_need(struct sm5714_fuelgauge_data *fuelgauge)
|
|
{
|
|
int ret;
|
|
|
|
ret = sm5714_read_word(fuelgauge->i2c, SM5714_FG_REG_SYSTEM_STATUS);
|
|
|
|
if ((ret & INIT_CHECK_MASK) == DISABLE_RE_INIT) {
|
|
pr_info("%s: SM5714_REG_FG_SYSTEM_STATUS : 0x%x , return FALSE NO init need\n", __func__, ret);
|
|
return 0;
|
|
} else {
|
|
pr_info("%s: SM5714_REG_FG_SYSTEM_STATUS : 0x%x , return TRUE init need!!!!\n", __func__, ret);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static unsigned int sm5714_get_vbat(struct sm5714_fuelgauge_data *fuelgauge)
|
|
{
|
|
int ret1 = 0, ret2 = 0;
|
|
unsigned int vbat = 0, vbat_avg = 0; /* = 3500; 3500 means 3500mV*/
|
|
|
|
ret1 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_VBAT);
|
|
ret2 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_VBAT_AVG);
|
|
|
|
/* auto value change to vsys by jig */
|
|
if (sm5714_read_word(fuelgauge->i2c, SM5714_FG_REG_SYSTEM_STATUS) & 0x8000) {
|
|
if (fuelgauge->isjigmoderealvbat) {
|
|
ret1 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_VSYS);
|
|
pr_info("%s : nENQ4 high JIG_ON, BUT need real VBAT, return VSYS_REG 0x%x\n", __func__, ret1);
|
|
} else
|
|
pr_info("%s : nENQ4 high JIG_ON, return VBAT_REG 0x%x, VSYS_REG 0x%x\n", __func__,
|
|
ret1, sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_VSYS));
|
|
} else if (factory_mode) {
|
|
vbat = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_VSYS);
|
|
pr_info("%s : nENQ4 low but factory_mode, VBAT_REG 0x%x, return VSYS_REG 0x%x\n", __func__, ret1, vbat);
|
|
ret1 = vbat;
|
|
}
|
|
|
|
if (ret1 < 0) {
|
|
pr_err("%s: read vbat reg fail", __func__);
|
|
vbat = 4000;
|
|
} else {
|
|
if (ret1 & 0x8000)
|
|
vbat = 2700 - (((ret1 & 0x7fff) * 10) / 109);
|
|
else
|
|
vbat = ((ret1 * 10) / 109) + 2700;
|
|
}
|
|
fuelgauge->info.batt_voltage = vbat;
|
|
|
|
if (ret2 < 0) {
|
|
pr_err("%s: read vbat_avg reg fail", __func__);
|
|
vbat_avg = vbat;
|
|
} else {
|
|
if (ret2 & 0x8000)
|
|
vbat_avg = 2700 - (((ret2 & 0x7fff) * 10) / 109);
|
|
else
|
|
vbat_avg = ((ret2 * 10) / 109) + 2700;
|
|
}
|
|
fuelgauge->info.batt_avgvoltage = vbat_avg;
|
|
|
|
if ((fuelgauge->vempty_mode == VEMPTY_MODE_SW_VALERT) &&
|
|
(vbat >= fuelgauge->battery_data->sw_v_empty_recover_vol)) {
|
|
fuelgauge->vempty_mode = VEMPTY_MODE_SW_RECOVERY;
|
|
|
|
sm5714_fg_fuelalert_init(fuelgauge,
|
|
fuelgauge->pdata->fuel_alert_soc);
|
|
pr_info("%s : Recoverd from SW V EMPTY Activation\n", __func__);
|
|
}
|
|
|
|
return vbat;
|
|
}
|
|
|
|
static unsigned int sm5714_get_ocv(struct sm5714_fuelgauge_data *fuelgauge)
|
|
{
|
|
int ret;
|
|
unsigned int ocv; /* = 3500; *//*3500 means 3500mV*/
|
|
|
|
ret = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_OCV);
|
|
if (ret < 0) {
|
|
pr_err("%s: read ocv reg fail\n", __func__);
|
|
ocv = 4000;
|
|
} else {
|
|
ocv = (ret * 1000) >> 11;
|
|
}
|
|
|
|
fuelgauge->info.batt_ocv = ocv;
|
|
|
|
return ocv;
|
|
}
|
|
|
|
static int sm5714_get_curr(struct sm5714_fuelgauge_data *fuelgauge)
|
|
{
|
|
int ret, s_stat, a_stat;
|
|
int curr = 0, curr_avg = 0; /* = 1000; 1000 means 1000mA*/
|
|
|
|
ret = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_CURRENT);
|
|
s_stat = sm5714_read_word(fuelgauge->i2c, SM5714_FG_REG_SYSTEM_STATUS);
|
|
a_stat = sm5714_read_word(fuelgauge->i2c, SM5714_FG_REG_AUX_STAT);
|
|
|
|
if (ret < 0) {
|
|
pr_err("%s: read curr reg fail", __func__);
|
|
curr = 0;
|
|
} else {
|
|
curr = ((ret&0x7fff)*1000)/2044;
|
|
if (ret&0x8000) {
|
|
curr *= -1;
|
|
} else if ((!(a_stat & 0x0020)) && (!fuelgauge->info.flag_chg_status)) {
|
|
curr = 0;
|
|
}
|
|
}
|
|
|
|
ret = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_CURRENT_AVG);
|
|
if (ret < 0) {
|
|
pr_err("%s: read curr_avg reg fail", __func__);
|
|
curr_avg = 0;
|
|
} else {
|
|
curr_avg = ((ret&0x7fff)*1000)/2044;
|
|
if (ret&0x8000) {
|
|
curr_avg *= -1;
|
|
} else if ((!(a_stat & 0x0020)) && (!fuelgauge->info.flag_chg_status)) {
|
|
curr_avg = 0;
|
|
}
|
|
}
|
|
|
|
if (a_stat & 0x0400) {
|
|
curr = curr * 25 / 10;
|
|
curr_avg = curr_avg * 25 / 10;
|
|
}
|
|
|
|
if (s_stat & 0x8000) {
|
|
curr = 0;
|
|
curr_avg = 0;
|
|
}
|
|
|
|
fuelgauge->info.batt_current = curr;
|
|
fuelgauge->info.batt_avgcurrent = curr_avg;
|
|
|
|
return curr;
|
|
}
|
|
|
|
static int sm5714_get_temperature(struct sm5714_fuelgauge_data *fuelgauge)
|
|
{
|
|
int ret;
|
|
int temp; /* = 250; 250 means 25.0oC*/
|
|
|
|
ret = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_TEMPERATURE);
|
|
if (ret < 0) {
|
|
pr_err("%s: read temp reg fail", __func__);
|
|
temp = 0;
|
|
} else {
|
|
temp = (((ret&0x7FFF)*10) * 2989) >> 11 >> 8;
|
|
if (ret&0x8000) {
|
|
temp *= -1;
|
|
}
|
|
}
|
|
fuelgauge->info.temp_fg = temp;
|
|
|
|
return temp;
|
|
}
|
|
|
|
static int sm5714_get_cycle(struct sm5714_fuelgauge_data *fuelgauge)
|
|
{
|
|
int ret;
|
|
int cycle;
|
|
|
|
ret = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_SOC_CYCLE);
|
|
if (ret < 0) {
|
|
pr_err("%s: read cycle reg fail", __func__);
|
|
cycle = 0;
|
|
} else {
|
|
cycle = ret&0x00FF;
|
|
}
|
|
fuelgauge->info.batt_soc_cycle = cycle;
|
|
|
|
return cycle;
|
|
}
|
|
|
|
static int sm5714_get_asoc(struct sm5714_fuelgauge_data *fuelgauge)
|
|
{
|
|
int soh, pre_soh, h_flag, c_flag, delta_t, temp;
|
|
|
|
soh = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_AGING_RATE_FILT);
|
|
pre_soh = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_USER_RESERV_2);
|
|
c_flag = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_SOC_CYCLE);
|
|
|
|
pr_info("%s : asoc = %d, ic_soh = 0x%x, pre = 0x%x, cycle = %d, t = %d \n", __func__, fuelgauge->info.soh, soh, pre_soh, c_flag, fuelgauge->info.temperature);
|
|
|
|
soh = soh * 100 / 2048;
|
|
|
|
h_flag = (pre_soh & 0x80)>>7;
|
|
pre_soh = pre_soh & 0x7F;
|
|
c_flag = (c_flag >> 4) % 2;
|
|
|
|
delta_t = fuelgauge->info.temperature/10 - fuelgauge->info.temp_std;
|
|
if (delta_t >= 0) {
|
|
if (soh < pre_soh) {
|
|
if (c_flag != h_flag) {
|
|
pre_soh = pre_soh-1;
|
|
temp = (c_flag<<7) | pre_soh;
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_USER_RESERV_2, temp);
|
|
pr_info("%s : pre_soh update to %d, write 0x%x\n", __func__, pre_soh, temp);
|
|
}
|
|
}
|
|
}
|
|
soh = pre_soh;
|
|
|
|
fuelgauge->info.soh = soh;
|
|
|
|
return fuelgauge->info.soh;
|
|
}
|
|
|
|
|
|
static void sm5714_vbatocv_check(struct sm5714_fuelgauge_data *fuelgauge)
|
|
{
|
|
if ((fuelgauge->is_charging) && (fuelgauge->info.batt_current < (fuelgauge->info.top_off)) &&
|
|
(fuelgauge->info.batt_current > (fuelgauge->info.top_off/3)) && (fuelgauge->info.batt_soc >= 900)) {
|
|
if (abs(fuelgauge->info.batt_ocv-fuelgauge->info.batt_voltage) > 30) { /* 30mV over */
|
|
fuelgauge->info.iocv_error_count++;
|
|
}
|
|
|
|
pr_info("%s: sm5714 FG iocv_error_count (%d)\n", __func__, fuelgauge->info.iocv_error_count);
|
|
|
|
if (fuelgauge->info.iocv_error_count > 3) /* prevent to overflow */
|
|
fuelgauge->info.iocv_error_count = 4;
|
|
} else {
|
|
fuelgauge->info.iocv_error_count = 0;
|
|
}
|
|
|
|
if (fuelgauge->info.iocv_error_count > 3) {
|
|
pr_info("%s: p_v - v = (%d)\n", __func__, fuelgauge->info.p_batt_voltage - fuelgauge->info.batt_voltage);
|
|
if (abs(fuelgauge->info.p_batt_voltage - fuelgauge->info.batt_voltage) > 15) { /* 15mV over */
|
|
fuelgauge->info.iocv_error_count = 0;
|
|
} else {
|
|
/* mode change to mix RS manual mode */
|
|
pr_info("%s: mode change to mix RS manual mode\n", __func__);
|
|
/* RS manual value write */
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_RS_MAX, fuelgauge->info.rs_value[0]+5);
|
|
}
|
|
} else {
|
|
/* voltage mode with 3.4V */
|
|
if (fuelgauge->vempty_mode != VEMPTY_MODE_HW) {
|
|
if ((fuelgauge->info.p_batt_voltage < fuelgauge->info.n_tem_poff) &&
|
|
(fuelgauge->info.batt_voltage < fuelgauge->info.n_tem_poff) && (!fuelgauge->is_charging)) {
|
|
pr_info("%s: mode change to normal tem mix RS manual mode\n", __func__);
|
|
/* mode change to mix RS manual mode */
|
|
/* RS manual value write */
|
|
if ((fuelgauge->info.p_batt_voltage <
|
|
(fuelgauge->info.n_tem_poff - fuelgauge->info.n_tem_poff_offset)) &&
|
|
(fuelgauge->info.batt_voltage <
|
|
(fuelgauge->info.n_tem_poff - fuelgauge->info.n_tem_poff_offset))) {
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_RS_MAX, fuelgauge->info.rs_value[0]+5);
|
|
} else {
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_RS_MAX, (fuelgauge->info.rs_value[0]<<1)+5);
|
|
}
|
|
} else {
|
|
pr_info("%s: mode change to mix RS auto mode\n", __func__);
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_RS_MAX, 0x3800);
|
|
}
|
|
} else { /* voltage mode with 3.25V, VEMPTY_MODE_HW mode */
|
|
if ((fuelgauge->info.p_batt_voltage < fuelgauge->info.l_tem_poff) &&
|
|
(fuelgauge->info.batt_voltage < fuelgauge->info.l_tem_poff) && (!fuelgauge->is_charging)) {
|
|
pr_info("%s: mode change to normal tem mix RS manual mode\n", __func__);
|
|
/* mode change to mix RS manual mode */
|
|
/* RS manual value write */
|
|
if ((fuelgauge->info.p_batt_voltage <
|
|
(fuelgauge->info.l_tem_poff - fuelgauge->info.l_tem_poff_offset)) &&
|
|
(fuelgauge->info.batt_voltage <
|
|
(fuelgauge->info.l_tem_poff - fuelgauge->info.l_tem_poff_offset))) {
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_RS_MAX, fuelgauge->info.rs_value[0]+5);
|
|
} else {
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_RS_MAX, (fuelgauge->info.rs_value[0]<<1)+5);
|
|
}
|
|
} else {
|
|
pr_info("%s: mode change to mix RS auto mode\n", __func__);
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_RS_MAX, 0x3800);
|
|
}
|
|
}
|
|
}
|
|
fuelgauge->info.p_batt_voltage = fuelgauge->info.batt_voltage;
|
|
fuelgauge->info.p_batt_current = fuelgauge->info.batt_current;
|
|
/* iocv error case cover end */
|
|
}
|
|
|
|
static void sm5714_ical_setup (struct sm5714_fuelgauge_data *fuelgauge)
|
|
{
|
|
if (!fuelgauge->info.i_dp_default) {
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_DP_IOFFSET, fuelgauge->info.dp_i_off);
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_DP_IPSLOPE, fuelgauge->info.dp_i_pslo);
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_DP_INSLOPE, fuelgauge->info.dp_i_nslo);
|
|
|
|
}
|
|
if (!fuelgauge->info.i_alg_default) {
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_ALG_IOFFSET, fuelgauge->info.alg_i_off);
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_ALG_IPSLOPE, fuelgauge->info.alg_i_pslo);
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_ALG_INSLOPE, fuelgauge->info.alg_i_nslo);
|
|
}
|
|
|
|
pr_info("%s: dp %d: <0x%x 0x%x 0x%x> alg %d: <0x%x 0x%x 0x%x>\n",
|
|
__func__,
|
|
fuelgauge->info.i_dp_default, fuelgauge->info.dp_i_off, fuelgauge->info.dp_i_pslo, fuelgauge->info.dp_i_nslo,
|
|
fuelgauge->info.i_alg_default, fuelgauge->info.alg_i_off, fuelgauge->info.alg_i_pslo, fuelgauge->info.alg_i_nslo);
|
|
}
|
|
|
|
static void sm5714_vcal_setup(struct sm5714_fuelgauge_data *fuelgauge)
|
|
{
|
|
if (!fuelgauge->info.v_default) {
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_VOFFSET, fuelgauge->info.v_off);
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_VSLOPE, fuelgauge->info.v_slo);
|
|
}
|
|
|
|
if (!fuelgauge->info.vt_default) {
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_VVT, fuelgauge->info.vtt);
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_IVT, fuelgauge->info.ivt);
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_IVV, fuelgauge->info.ivv);
|
|
}
|
|
|
|
pr_info("%s: vcal %d: <0x%x 0x%x> T %d: <0x%x 0x%x 0x%x>\n",
|
|
__func__,
|
|
fuelgauge->info.v_default, fuelgauge->info.v_off, fuelgauge->info.v_slo,
|
|
fuelgauge->info.vt_default, fuelgauge->info.vtt, fuelgauge->info.ivt, fuelgauge->info.ivv);
|
|
}
|
|
|
|
static int sm5714_fg_verified_write_word(struct i2c_client *client,
|
|
u8 reg_addr, u16 data)
|
|
{
|
|
int ret;
|
|
|
|
ret = sm5714_write_word(client, reg_addr, data);
|
|
if (ret < 0) {
|
|
msleep(50);
|
|
pr_info("1st fail i2c write %s: ret = %d, addr = 0x%x, data = 0x%x\n",
|
|
__func__, ret, reg_addr, data);
|
|
ret = sm5714_write_word(client, reg_addr, data);
|
|
if (ret < 0) {
|
|
msleep(50);
|
|
pr_info("2nd fail i2c write %s: ret = %d, addr = 0x%x, data = 0x%x\n",
|
|
__func__, ret, reg_addr, data);
|
|
ret = sm5714_write_word(client, reg_addr, data);
|
|
if (ret < 0) {
|
|
pr_info("3rd fail i2c write %s: ret = %d, addr = 0x%x, data = 0x%x\n",
|
|
__func__, ret, reg_addr, data);
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int sm5714_fg_calculate_iocv(struct sm5714_fuelgauge_data *fuelgauge)
|
|
{
|
|
bool only_lb = false, sign_i_offset = 0; /*valid_cb=false, */
|
|
int roop_start = 0, roop_max = 0, i = 0, cb_last_index = 0, cb_pre_last_index = 0;
|
|
int lb_v_buffer[FG_INIT_B_LEN+1] = {0, 0, 0, 0, 0, 0, 0, 0};
|
|
int lb_i_buffer[FG_INIT_B_LEN+1] = {0, 0, 0, 0, 0, 0, 0, 0};
|
|
int cb_v_buffer[FG_INIT_B_LEN+1] = {0, 0, 0, 0, 0, 0, 0, 0};
|
|
int cb_i_buffer[FG_INIT_B_LEN+1] = {0, 0, 0, 0, 0, 0, 0, 0};
|
|
int i_offset_margin = 0x14, i_vset_margin = 0x67;
|
|
int v_max = 0, v_min = 0, v_sum = 0, lb_v_avg = 0, cb_v_avg = 0, lb_v_set = 0, lb_i_set = 0, i_offset = 0; /* lb_v_minmax_offset=0, */
|
|
int i_max = 0, i_min = 0, i_sum = 0, lb_i_avg = 0, cb_i_avg = 0, cb_v_set = 0, cb_i_set = 0; /* lb_i_minmax_offset=0, */
|
|
int lb_i_p_v_min = 0, lb_i_n_v_max = 0, cb_i_p_v_min = 0, cb_i_n_v_max = 0;
|
|
|
|
int v_ret = 0, i_ret = 0, ret = 0;
|
|
|
|
ret = sm5714_read_word(fuelgauge->i2c, SM5714_FG_REG_AUX_STAT);
|
|
pr_info("%s: iocv_status_read = addr : 0x%x , data : 0x%x\n", __func__, SM5714_FG_REG_AUX_STAT, ret);
|
|
|
|
/* init start */
|
|
if ((ret & 0x0800) == 0x0000) {
|
|
only_lb = true;
|
|
}
|
|
|
|
/*
|
|
if ((ret & 0x3000) == 0x3000) {
|
|
valid_cb = true;
|
|
}
|
|
*/
|
|
/* init end */
|
|
|
|
/* lb get start */
|
|
roop_max = (ret & 0xF000)>>12;
|
|
if (roop_max > FG_INIT_B_LEN+1)
|
|
roop_max = FG_INIT_B_LEN+1;
|
|
|
|
roop_start = SM5714_FG_ADDR_SRAM_START_LB_V;
|
|
for (i = roop_start; i < roop_start + roop_max; i++) {
|
|
v_ret = sm5714_fg_read_sram(fuelgauge, i);
|
|
i_ret = sm5714_fg_read_sram(fuelgauge, i+0x10);
|
|
|
|
if ((i_ret&0x8000) == 0x8000) {
|
|
i_ret = -(i_ret&0x7FFF);
|
|
}
|
|
|
|
lb_v_buffer[i-roop_start] = v_ret;
|
|
lb_i_buffer[i-roop_start] = i_ret;
|
|
|
|
if (i == roop_start) {
|
|
v_max = v_ret;
|
|
v_min = v_ret;
|
|
v_sum = v_ret;
|
|
i_max = i_ret;
|
|
i_min = i_ret;
|
|
i_sum = i_ret;
|
|
} else {
|
|
if (v_ret > v_max)
|
|
v_max = v_ret;
|
|
else if (v_ret < v_min)
|
|
v_min = v_ret;
|
|
v_sum = v_sum + v_ret;
|
|
|
|
if (i_ret > i_max)
|
|
i_max = i_ret;
|
|
else if (i_ret < i_min)
|
|
i_min = i_ret;
|
|
i_sum = i_sum + i_ret;
|
|
}
|
|
|
|
if (abs(i_ret) > i_vset_margin) {
|
|
if (i_ret > 0) {
|
|
if (lb_i_p_v_min == 0) {
|
|
lb_i_p_v_min = v_ret;
|
|
} else {
|
|
if (v_ret < lb_i_p_v_min)
|
|
lb_i_p_v_min = v_ret;
|
|
}
|
|
} else {
|
|
if (lb_i_n_v_max == 0) {
|
|
lb_i_n_v_max = v_ret;
|
|
} else {
|
|
if (v_ret > lb_i_n_v_max)
|
|
lb_i_n_v_max = v_ret;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
v_sum = v_sum - v_max - v_min;
|
|
i_sum = i_sum - i_max - i_min;
|
|
|
|
/*
|
|
lb_v_minmax_offset = v_max - v_min;
|
|
lb_i_minmax_offset = i_max - i_min;
|
|
*/
|
|
|
|
lb_v_avg = v_sum / (roop_max-2);
|
|
lb_i_avg = i_sum / (roop_max-2);
|
|
/* lb get end */
|
|
|
|
/* lb_vset start */
|
|
if (abs(lb_i_buffer[roop_max-1]) < i_vset_margin) {
|
|
if (abs(lb_i_buffer[roop_max-2]) < i_vset_margin) {
|
|
lb_v_set = MAXVAL(lb_v_buffer[roop_max-2], lb_v_buffer[roop_max-1]);
|
|
if (abs(lb_i_buffer[roop_max-3]) < i_vset_margin) {
|
|
lb_v_set = MAXVAL(lb_v_buffer[roop_max-3], lb_v_set);
|
|
}
|
|
} else {
|
|
lb_v_set = lb_v_buffer[roop_max-1];
|
|
}
|
|
} else {
|
|
lb_v_set = lb_v_avg;
|
|
}
|
|
|
|
if (lb_i_n_v_max > 0) {
|
|
lb_v_set = MAXVAL(lb_i_n_v_max, lb_v_set);
|
|
}
|
|
/*
|
|
else if (lb_i_p_v_min > 0) {
|
|
lb_v_set = MINVAL(lb_i_p_v_min, lb_v_set);
|
|
}
|
|
lb_vset end
|
|
|
|
lb offset make start
|
|
*/
|
|
if (roop_max > 3) {
|
|
lb_i_set = (lb_i_buffer[2] + lb_i_buffer[3]) / 2;
|
|
}
|
|
|
|
if ((abs(lb_i_buffer[roop_max-1]) < i_offset_margin) && (abs(lb_i_set) < i_offset_margin)) {
|
|
lb_i_set = MAXVAL(lb_i_buffer[roop_max-1], lb_i_set);
|
|
} else if (abs(lb_i_buffer[roop_max-1]) < i_offset_margin) {
|
|
lb_i_set = lb_i_buffer[roop_max-1];
|
|
} else if (abs(lb_i_set) < i_offset_margin) {
|
|
i_offset = lb_i_set;
|
|
} else {
|
|
lb_i_set = 0;
|
|
}
|
|
|
|
i_offset = lb_i_set;
|
|
|
|
i_offset = i_offset + 4; /* add extra offset */
|
|
|
|
if (i_offset <= 0) {
|
|
sign_i_offset = 1;
|
|
#ifdef IGNORE_N_I_OFFSET
|
|
i_offset = 0;
|
|
#else
|
|
i_offset = -i_offset;
|
|
#endif
|
|
}
|
|
|
|
i_offset = i_offset>>1;
|
|
|
|
if (sign_i_offset == 0) {
|
|
i_offset = i_offset|0x0080;
|
|
}
|
|
i_offset = i_offset | i_offset<<8;
|
|
|
|
pr_info("%s: iocv_l_max=0x%x, iocv_l_min=0x%x, iocv_l_avg=0x%x, lb_v_set=0x%x, roop_max=%d \n",
|
|
__func__, v_max, v_min, lb_v_avg, lb_v_set, roop_max);
|
|
pr_info("%s: ioci_l_max=0x%x, ioci_l_min=0x%x, ioci_l_avg=0x%x, lb_i_set=0x%x, i_offset=0x%x, sign_i_offset=%d\n",
|
|
__func__, i_max, i_min, lb_i_avg, lb_i_set, i_offset, sign_i_offset);
|
|
|
|
if (!only_lb) {
|
|
/* cb get start */
|
|
roop_start = SM5714_FG_ADDR_SRAM_START_CB_V;
|
|
roop_max = FG_INIT_B_LEN+1;
|
|
for (i = roop_start; i < roop_start + roop_max; i++) {
|
|
v_ret = sm5714_fg_read_sram(fuelgauge, i);
|
|
i_ret = sm5714_fg_read_sram(fuelgauge, i+0x10);
|
|
|
|
if ((i_ret&0x8000) == 0x8000) {
|
|
i_ret = -(i_ret&0x7FFF);
|
|
}
|
|
|
|
cb_v_buffer[i-roop_start] = v_ret;
|
|
cb_i_buffer[i-roop_start] = i_ret;
|
|
|
|
if (i == roop_start) {
|
|
v_max = v_ret;
|
|
v_min = v_ret;
|
|
v_sum = v_ret;
|
|
i_max = i_ret;
|
|
i_min = i_ret;
|
|
i_sum = i_ret;
|
|
} else {
|
|
if (v_ret > v_max)
|
|
v_max = v_ret;
|
|
else if (v_ret < v_min)
|
|
v_min = v_ret;
|
|
v_sum = v_sum + v_ret;
|
|
|
|
if (i_ret > i_max)
|
|
i_max = i_ret;
|
|
else if (i_ret < i_min)
|
|
i_min = i_ret;
|
|
i_sum = i_sum + i_ret;
|
|
}
|
|
|
|
if (abs(i_ret) > i_vset_margin) {
|
|
if (i_ret > 0) {
|
|
if (cb_i_p_v_min == 0) {
|
|
cb_i_p_v_min = v_ret;
|
|
} else {
|
|
if (v_ret < cb_i_p_v_min)
|
|
cb_i_p_v_min = v_ret;
|
|
}
|
|
} else {
|
|
if (cb_i_n_v_max == 0) {
|
|
cb_i_n_v_max = v_ret;
|
|
} else {
|
|
if (v_ret > cb_i_n_v_max)
|
|
cb_i_n_v_max = v_ret;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
v_sum = v_sum - v_max - v_min;
|
|
i_sum = i_sum - i_max - i_min;
|
|
|
|
cb_v_avg = v_sum / (roop_max-2);
|
|
cb_i_avg = i_sum / (roop_max-2);
|
|
/* cb get end */
|
|
|
|
/* cb_vset start */
|
|
cb_last_index = ((ret & 0xF000)>>12)-(FG_INIT_B_LEN+1)-1; /*-8-1 */
|
|
if (cb_last_index < 0) {
|
|
cb_last_index = FG_INIT_B_LEN;
|
|
}
|
|
|
|
for (i = roop_max; i > 0; i--) {
|
|
if (abs(cb_i_buffer[cb_last_index]) < i_vset_margin) {
|
|
cb_v_set = cb_v_buffer[cb_last_index];
|
|
if (abs(cb_i_buffer[cb_last_index]) < i_offset_margin) {
|
|
cb_i_set = cb_i_buffer[cb_last_index];
|
|
}
|
|
|
|
cb_pre_last_index = cb_last_index - 1;
|
|
if (cb_pre_last_index < 0) {
|
|
cb_pre_last_index = FG_INIT_B_LEN;
|
|
}
|
|
|
|
if (abs(cb_i_buffer[cb_pre_last_index]) < i_vset_margin) {
|
|
cb_v_set = MAXVAL(cb_v_buffer[cb_pre_last_index], cb_v_set);
|
|
if (abs(cb_i_buffer[cb_pre_last_index]) < i_offset_margin) {
|
|
cb_i_set = MAXVAL(cb_i_buffer[cb_pre_last_index], cb_i_set);
|
|
}
|
|
}
|
|
} else {
|
|
cb_last_index--;
|
|
if (cb_last_index < 0) {
|
|
cb_last_index = FG_INIT_B_LEN;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cb_v_set == 0) {
|
|
cb_v_set = cb_v_avg;
|
|
if (cb_i_set == 0) {
|
|
cb_i_set = cb_i_avg;
|
|
}
|
|
}
|
|
|
|
if (cb_i_n_v_max > 0) {
|
|
cb_v_set = MAXVAL(cb_i_n_v_max, cb_v_set);
|
|
}
|
|
/*
|
|
else if(cb_i_p_v_min > 0) {
|
|
cb_v_set = MINVAL(cb_i_p_v_min, cb_v_set);
|
|
}
|
|
cb_vset end
|
|
|
|
cb offset make start
|
|
*/
|
|
if (abs(cb_i_set) < i_offset_margin) {
|
|
if (cb_i_set > lb_i_set) {
|
|
i_offset = cb_i_set;
|
|
i_offset = i_offset + 4; /* add extra offset */
|
|
|
|
if (i_offset <= 0) {
|
|
sign_i_offset = 1;
|
|
#ifdef IGNORE_N_I_OFFSET
|
|
i_offset = 0;
|
|
#else
|
|
i_offset = -i_offset;
|
|
#endif
|
|
}
|
|
|
|
i_offset = i_offset>>1;
|
|
|
|
if (sign_i_offset == 0) {
|
|
i_offset = i_offset|0x0080;
|
|
}
|
|
i_offset = i_offset | i_offset<<8;
|
|
|
|
}
|
|
}
|
|
/* cb offset make end */
|
|
|
|
pr_info("%s: iocv_c_max=0x%x, iocv_c_min=0x%x, iocv_c_avg=0x%x, cb_v_set=0x%x, cb_last_index=%d\n",
|
|
__func__, v_max, v_min, cb_v_avg, cb_v_set, cb_last_index);
|
|
pr_info("%s: ioci_c_max=0x%x, ioci_c_min=0x%x, ioci_c_avg=0x%x, cb_i_set=0x%x, i_offset=0x%x, sign_i_offset=%d\n",
|
|
__func__, i_max, i_min, cb_i_avg, cb_i_set, i_offset, sign_i_offset);
|
|
|
|
}
|
|
|
|
/* final set */
|
|
if ((abs(cb_i_set) > i_vset_margin) || only_lb) {
|
|
ret = MAXVAL(lb_v_set, cb_i_n_v_max);
|
|
} else {
|
|
ret = cb_v_set;
|
|
}
|
|
|
|
if (ret > fuelgauge->info.battery_table[DISCHARGE_TABLE][FG_TABLE_LEN-1]) {
|
|
pr_info("%s: iocv ret change 0x%x -> 0x%x \n", __func__, ret, fuelgauge->info.battery_table[DISCHARGE_TABLE][FG_TABLE_LEN-1]);
|
|
ret = fuelgauge->info.battery_table[DISCHARGE_TABLE][FG_TABLE_LEN-1];
|
|
} else if (ret < fuelgauge->info.battery_table[DISCHARGE_TABLE][0]) {
|
|
pr_info("%s: iocv ret change 0x%x -> 0x%x \n", __func__, ret, (fuelgauge->info.battery_table[DISCHARGE_TABLE][0] + 0x10));
|
|
ret = fuelgauge->info.battery_table[DISCHARGE_TABLE][0] + 0x10;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void sm5714_set_aux_ctrl_cfg(struct sm5714_fuelgauge_data *fuelgauge)
|
|
{
|
|
int rsmanvalue;
|
|
|
|
sm5714_write_word(fuelgauge->i2c, SM5714_FG_REG_AUX_CTRL1, fuelgauge->info.aux_ctrl[0]);
|
|
sm5714_write_word(fuelgauge->i2c, SM5714_FG_REG_AUX_CTRL2, fuelgauge->info.aux_ctrl[1]);
|
|
|
|
rsmanvalue = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_RS_AUTO_MAN_VALUE);
|
|
|
|
pr_info("%s: aux_ctrl1 = 0x%x, aux_ctrl2 = 0x%x, rsmanvalue = 0x%x\n",
|
|
__func__, fuelgauge->info.aux_ctrl[0], fuelgauge->info.aux_ctrl[1], rsmanvalue);
|
|
|
|
return;
|
|
}
|
|
|
|
#ifdef ENABLE_BATT_LONG_LIFE
|
|
int get_v_max_index_by_cycle(struct sm5714_fuelgauge_data *fuelgauge)
|
|
|
|
{
|
|
int cycle_index = 0, len;
|
|
|
|
for (len = fuelgauge->pdata->num_age_step-1; len >= 0; --len) {
|
|
if (fuelgauge->chg_full_soc == fuelgauge->pdata->age_data[len].full_condition_soc) {
|
|
cycle_index = len;
|
|
break;
|
|
}
|
|
}
|
|
pr_info("%s: chg_full_soc = %d, index = %d \n", __func__, fuelgauge->chg_full_soc, cycle_index);
|
|
|
|
return cycle_index;
|
|
}
|
|
#endif
|
|
|
|
static bool sm5714_check_jig_status(struct sm5714_fuelgauge_data *fuelgauge)
|
|
{
|
|
bool ret = false;
|
|
|
|
if (fuelgauge->pdata->jig_gpio) {
|
|
if (fuelgauge->pdata->jig_low_active)
|
|
ret = !gpio_get_value(fuelgauge->pdata->jig_gpio);
|
|
else
|
|
ret = gpio_get_value(fuelgauge->pdata->jig_gpio);
|
|
}
|
|
pr_info("%s: jig_gpio(%d), ret(%d)\n",
|
|
__func__, fuelgauge->pdata->jig_gpio, ret);
|
|
return ret;
|
|
}
|
|
|
|
static bool sm5714_fg_reg_init(struct sm5714_fuelgauge_data *fuelgauge, bool is_surge)
|
|
{
|
|
int i, j, k, value, ret = 0;
|
|
uint8_t table_reg;
|
|
int write_table[TABLE_MAX][FG_TABLE_LEN+1];
|
|
|
|
pr_info("%s: sm5714_fg_reg_init START!!\n", __func__);
|
|
|
|
/* start first param_ctrl unlock */
|
|
sm5714_write_word(fuelgauge->i2c, SM5714_FG_REG_TABLE_UNLOCK, FG_PARAM_UNLOCK_CODE);
|
|
|
|
/* CAP write */
|
|
#ifdef ENABLE_BATT_LONG_LIFE
|
|
i = get_v_max_index_by_cycle(fuelgauge);
|
|
pr_info("%s: v_max_now is change %x -> %x \n", __func__, fuelgauge->info.v_max_now, fuelgauge->info.v_max_table[i]);
|
|
pr_info("%s: q_max_now is change %x -> %x \n", __func__, fuelgauge->info.q_max_now, fuelgauge->info.q_max_table[i]);
|
|
fuelgauge->info.v_max_now = fuelgauge->info.v_max_table[i];
|
|
fuelgauge->info.q_max_now = fuelgauge->info.q_max_table[i];
|
|
fuelgauge->info.cap = fuelgauge->info.q_max_now;
|
|
#endif
|
|
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_Q_MAX, fuelgauge->info.cap);
|
|
pr_info("%s: SM5714_FG_ADDR_SRAM_Q_MAX 0x%x\n",
|
|
__func__, fuelgauge->info.cap);
|
|
|
|
for (i = 0; i < TABLE_MAX; i++) {
|
|
for (j = 0; j <= FG_TABLE_LEN; j++) {
|
|
#ifdef ENABLE_BATT_LONG_LIFE
|
|
if (i == SOC_TABLE) {
|
|
write_table[i][j] = (fuelgauge->info.battery_table[i][j] * 2) - (fuelgauge->info.battery_table[i][j] * fuelgauge->info.q_max_now / fuelgauge->info.maxcap);
|
|
if (j == FG_TABLE_LEN) {
|
|
write_table[i][FG_TABLE_LEN-1] = 100*256;
|
|
write_table[i][FG_TABLE_LEN] = 100*256+26;
|
|
}
|
|
} else {
|
|
write_table[i][j] = fuelgauge->info.battery_table[i][j];
|
|
if (j == FG_TABLE_LEN-1) {
|
|
write_table[i][FG_TABLE_LEN-1] = fuelgauge->info.v_max_now;
|
|
if (write_table[i][FG_TABLE_LEN-1] < write_table[i][FG_TABLE_LEN-2]) {
|
|
write_table[i][FG_TABLE_LEN-2] = write_table[i][FG_TABLE_LEN-1] - 0x18; // ~11.7mV
|
|
write_table[SOC_TABLE][FG_TABLE_LEN-2] = (write_table[SOC_TABLE][FG_TABLE_LEN-1]*99)/100;
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
write_table[i][j] = fuelgauge->info.battery_table[i][j];
|
|
#endif
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < TABLE_MAX; i++) {
|
|
table_reg = SM5714_FG_ADDR_TABLE0_0 + (i*(FG_TABLE_LEN+1));
|
|
for (j = 0; j <= FG_TABLE_LEN; j++) {
|
|
sm5714_fg_write_sram(fuelgauge, (table_reg + j), write_table[i][j]);
|
|
pr_info("%s: TABLE write [%d][%d] = 0x%x : 0x%x\n",
|
|
__func__, i, j, (table_reg + j), write_table[i][j]);
|
|
}
|
|
}
|
|
|
|
/*for verify table data write*/
|
|
for (i = 0; i < TABLE_MAX; i++) {
|
|
table_reg = SM5714_FG_ADDR_TABLE0_0 + (i*(FG_TABLE_LEN+1));
|
|
for (j = 0; j <= FG_TABLE_LEN; j++) {
|
|
if (write_table[i][j] == sm5714_fg_read_sram(fuelgauge, (table_reg + j))) {
|
|
pr_info("%s: TABLE data verify OK [%d][%d] = 0x%x : 0x%x\n",
|
|
__func__, i, j, (table_reg + j), write_table[i][j]);
|
|
} else {
|
|
ret |= I2C_ERROR_CHECK;
|
|
for (k = 1; k <= I2C_ERROR_COUNT_MAX; k++) {
|
|
pr_info("%s: TABLE write data ERROR!!!! rewrite [%d][%d] = 0x%x : 0x%x, count=%d\n",
|
|
__func__, i, j, (table_reg + j), write_table[i][j], k);
|
|
sm5714_fg_write_sram(fuelgauge, (table_reg + j), write_table[i][j]);
|
|
msleep(30);
|
|
if (write_table[i][j] == sm5714_fg_read_sram(fuelgauge, (table_reg + j))) {
|
|
pr_info("%s: TABLE rewrite OK [%d][%d] = 0x%x : 0x%x, count=%d\n",
|
|
__func__, i, j, (table_reg + j), write_table[i][j], k);
|
|
break;
|
|
}
|
|
if (k == I2C_ERROR_COUNT_MAX)
|
|
ret |= I2C_ERROR_REMAIN;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
table_reg = SM5714_FG_ADDR_TABLE2_0;
|
|
for (j = 0; j <= FG_ADD_TABLE_LEN; j++) {
|
|
sm5714_fg_write_sram(fuelgauge, (table_reg + j), fuelgauge->info.battery_table[i][j]);
|
|
pr_info("%s: TABLE write OK [%d][%d] = 0x%x : 0x%x\n",
|
|
__func__, i, j, (table_reg + j), fuelgauge->info.battery_table[i][j]);
|
|
}
|
|
|
|
/* RS write */
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_RS_MIN, fuelgauge->info.rs_value[1]);
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_RS_MAX, fuelgauge->info.rs_value[2]);
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_RS_FACTOR, fuelgauge->info.rs_value[3]);
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_RS_CHG_FACTOR, fuelgauge->info.rs_value[4]);
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_RS_DISCHG_FACTOR, fuelgauge->info.rs_value[5]);
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_RS_AUTO_MAN_VALUE, fuelgauge->info.rs_value[6]);
|
|
|
|
pr_info("%s: spare=0x%x, MIN=0x%x, MAX=0x%x, FACTOR=0x%x, C_FACT=0x%x, D_FACT=0x%x, MAN_VALUE=0x%x\n", __func__,
|
|
fuelgauge->info.rs_value[0], fuelgauge->info.rs_value[1], fuelgauge->info.rs_value[2], fuelgauge->info.rs_value[3],
|
|
fuelgauge->info.rs_value[4], fuelgauge->info.rs_value[5], fuelgauge->info.rs_value[6]);
|
|
|
|
/* surge reset defence */
|
|
if (is_surge) {
|
|
value = ((fuelgauge->info.batt_ocv<<8)/125);
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_USER_RESERV_2, 0);
|
|
} else {
|
|
/* auto value change to vsys by jig */
|
|
if (factory_mode && !(sm5714_read_word(fuelgauge->i2c, SM5714_FG_REG_SYSTEM_STATUS) & 0x8000)) {
|
|
pr_info("%s: nENQ4 low but factory mode, iocv is vsys read value\n", __func__);
|
|
value = ((sm5714_get_vbat(fuelgauge)<<8)/125);
|
|
} else {
|
|
pr_info("%s: iocv is buffer value\n", __func__);
|
|
value = sm5714_fg_calculate_iocv(fuelgauge);
|
|
}
|
|
}
|
|
|
|
msleep(10);
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_INIT_OCV, value);
|
|
pr_info("%s: IOCV_MAN_WRITE = %d : 0x%x\n", __func__, SM5714_FG_ADDR_SRAM_INIT_OCV, value);
|
|
|
|
/* LOCK */
|
|
value = FG_PARAM_LOCK_CODE;
|
|
sm5714_write_word(fuelgauge->i2c, SM5714_FG_REG_TABLE_UNLOCK, value);
|
|
pr_info("%s: LAST PARAM CTRL VALUE = 0x%x : 0x%x\n", __func__, SM5714_FG_REG_TABLE_UNLOCK, value);
|
|
|
|
/* init delay */
|
|
msleep(20);
|
|
|
|
/* write batt data version */
|
|
ret |= (fuelgauge->info.data_ver << 4) & DATA_VERSION;
|
|
if (sm5714_check_jig_status(fuelgauge))
|
|
ret |= JIG_CONNECTED;
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_USER_RESERV_1, ret);
|
|
pr_info("%s: RESERVED = 0x%x : 0x%x\n", __func__, SM5714_FG_ADDR_SRAM_USER_RESERV_1, ret);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int sm5714_abnormal_reset_check(struct sm5714_fuelgauge_data *fuelgauge)
|
|
{
|
|
int cntl_read;
|
|
|
|
/* abnormal case process */
|
|
if (sm5714_fg_check_reg_init_need(fuelgauge)) {
|
|
cntl_read = sm5714_read_word(fuelgauge->i2c, SM5714_FG_REG_CTRL);
|
|
pr_info("%s: SM5714 FG abnormal case!!!! SM5714_FG_REG_CTRL : 0x%x, is_FG_initialised : %d\n", __func__,
|
|
cntl_read, fuelgauge->info.is_FG_initialised);
|
|
|
|
if (fuelgauge->info.is_FG_initialised == 1) {
|
|
/* SW reset code */
|
|
fuelgauge->info.is_FG_initialised = 0;
|
|
if (sm5714_fg_verified_write_word(fuelgauge->i2c, SM5714_FG_REG_RESET, SW_RESET_OTP_CODE) < 0) {
|
|
pr_info("%s: Warning!!!! SM5714 FG abnormal case.... SW reset FAIL \n", __func__);
|
|
} else {
|
|
pr_info("%s: SM5714 FG abnormal case.... SW reset OK\n", __func__);
|
|
}
|
|
/* delay 100ms */
|
|
msleep(100);
|
|
/* init code */
|
|
sm5714_fg_init(fuelgauge, true);
|
|
}
|
|
return FG_ABNORMAL_RESET;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int sm5714_get_device_id(struct sm5714_fuelgauge_data *fuelgauge)
|
|
{
|
|
int ret;
|
|
ret = sm5714_read_word(fuelgauge->i2c, SM5714_FG_REG_DEVICE_ID);
|
|
sm5714_device_id = ret;
|
|
pr_info("%s: SM5714 vender_id = 0x%x\n", __func__, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int sm5714_call_fg_device_id(void)
|
|
{
|
|
pr_info("%s: extern call SM5714 fg_device_id = 0x%x\n", __func__, sm5714_device_id);
|
|
|
|
return sm5714_device_id;
|
|
}
|
|
|
|
unsigned int sm5714_get_soc(struct sm5714_fuelgauge_data *fuelgauge)
|
|
{
|
|
int ret;
|
|
unsigned int soc;
|
|
|
|
ret = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_SOC);
|
|
if (ret < 0) {
|
|
pr_err("%s: Warning!!!! read soc reg fail\n", __func__);
|
|
soc = 500;
|
|
} else {
|
|
soc = (ret * 10) >> 8;
|
|
if (ret > 0x63ff) {
|
|
if (((fuelgauge->info.batt_avgcurrent - fuelgauge->info.top_off) > (fuelgauge->info.top_off/5))
|
|
|| (fuelgauge->info.batt_avgcurrent <= 0)) {
|
|
if (fuelgauge->info.batt_soc == 999)
|
|
soc = soc - 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (factory_mode && !(sm5714_read_word(fuelgauge->i2c, SM5714_FG_REG_SYSTEM_STATUS) & 0x8000)) {
|
|
pr_info("%s: nENQ4 low but factory mode, SOC = %d, return pre_SOC = %d\n", __func__,
|
|
soc, fuelgauge->info.batt_soc);
|
|
return fuelgauge->info.batt_soc;
|
|
}
|
|
|
|
if (sm5714_abnormal_reset_check(fuelgauge) < 0) {
|
|
pr_info("%s: FG init ERROR!! pre_SOC returned!!, read_SOC = %d, pre_SOC = %d\n", __func__,
|
|
soc, fuelgauge->info.batt_soc);
|
|
return fuelgauge->info.batt_soc;
|
|
}
|
|
|
|
/* ocv update */
|
|
sm5714_get_ocv(fuelgauge);
|
|
|
|
/* for low temp power off test */
|
|
if (fuelgauge->info.volt_alert_flag && (fuelgauge->info.temperature < -100)) {
|
|
pr_info("%s: volt_alert_flag is TRUE!!!! SOC make force ZERO!!!!\n", __func__);
|
|
fuelgauge->info.batt_soc = 0;
|
|
pr_info("%s: batt_soc = %d, soc = %d, ret = 0x%x\n", __func__, fuelgauge->info.batt_soc, soc, ret);
|
|
return 0;
|
|
} else {
|
|
fuelgauge->info.batt_soc = soc;
|
|
pr_info("%s: batt_soc = %d, soc = %d, ret = 0x%x\n", __func__, fuelgauge->info.batt_soc, soc, ret);
|
|
}
|
|
|
|
return soc;
|
|
}
|
|
|
|
static void sm5714_update_all_value(struct sm5714_fuelgauge_data *fuelgauge)
|
|
{
|
|
|
|
union power_supply_propval value;
|
|
|
|
fuelgauge->is_charging = (fuelgauge->info.flag_charge_health |
|
|
fuelgauge->ta_exist) && (fuelgauge->info.batt_current >= 30);
|
|
|
|
/* check charger status */
|
|
|
|
psy_do_property("battery", get,
|
|
POWER_SUPPLY_PROP_STATUS, value);
|
|
fuelgauge->info.flag_full_charge =
|
|
(value.intval == POWER_SUPPLY_STATUS_FULL) ? 1 : 0;
|
|
fuelgauge->info.flag_chg_status =
|
|
(value.intval == POWER_SUPPLY_STATUS_CHARGING) ? 1 : 0;
|
|
|
|
/* vbat */
|
|
sm5714_get_vbat(fuelgauge);
|
|
|
|
/* current */
|
|
sm5714_get_curr(fuelgauge);
|
|
|
|
/* temperature */
|
|
sm5714_get_temperature(fuelgauge);
|
|
|
|
/* cycle */
|
|
sm5714_get_cycle(fuelgauge);
|
|
|
|
/* carc */
|
|
sm5714_vbatocv_check(fuelgauge);
|
|
|
|
/* soc */
|
|
sm5714_get_soc(fuelgauge);
|
|
|
|
pr_info("%s: chg_h=%d, chg_f=%d, chg_s=%d, is_chg=%d, ta_exist=%d, "
|
|
"v=%d, v_avg=%d, i=%d, i_avg=%d, ocv=%d, fg_t=%d, b_t=%d, cycle=%d, soc=%d\n",
|
|
__func__, fuelgauge->info.flag_charge_health, fuelgauge->info.flag_full_charge,
|
|
fuelgauge->info.flag_chg_status, fuelgauge->is_charging, fuelgauge->ta_exist,
|
|
fuelgauge->info.batt_voltage, fuelgauge->info.batt_avgvoltage,
|
|
fuelgauge->info.batt_current, fuelgauge->info.batt_avgcurrent, fuelgauge->info.batt_ocv,
|
|
fuelgauge->info.temp_fg, fuelgauge->info.temperature, fuelgauge->info.batt_soc_cycle,
|
|
fuelgauge->info.batt_soc);
|
|
|
|
#if !defined(CONFIG_SEC_FACTORY)
|
|
sm5714_fg_test_print(fuelgauge);
|
|
/* for_debug */
|
|
sm5714_dump_all(fuelgauge);
|
|
#endif
|
|
|
|
#if defined(CONFIG_SEC_FACTORY)
|
|
sm5714_fuel_gauge_abnormal_reg_check(fuelgauge);
|
|
#endif
|
|
}
|
|
|
|
static int sm5714_fg_set_jig_mode_real_vbat(struct sm5714_fuelgauge_data *fuelgauge, int meas_mode)
|
|
{
|
|
int stat;
|
|
|
|
fuelgauge->isjigmoderealvbat = false;
|
|
|
|
if (sm5714_fg_check_reg_init_need(fuelgauge)) {
|
|
pr_info("%s: FG init fail!! \n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
/** meas_mode = 0 is inbat mode with jig **/
|
|
if (meas_mode == 0) {
|
|
stat = sm5714_read_word(fuelgauge->i2c, SM5714_FG_REG_SYSTEM_STATUS);
|
|
if (stat & 0x8000) {
|
|
fuelgauge->isjigmoderealvbat = true;
|
|
pr_info("%s: FG check jig_ON!! and after read real VBAT!! \n", __func__);
|
|
} else
|
|
pr_info("%s: isjigmoderealvbat = 1 but FG check nENQ4 LOW!! check jig!! \n", __func__);
|
|
} else
|
|
pr_info("%s: meas_mode = 1, isjigmoderealvbat = false!! \n", __func__);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void sm5714_fg_reset_capacity_by_jig_connection(struct sm5714_fuelgauge_data *fuelgauge)
|
|
{
|
|
int ret;
|
|
|
|
ret = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_USER_RESERV_1);
|
|
ret &= ~JIG_CONNECTED;
|
|
ret |= JIG_CONNECTED;
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_USER_RESERV_1, ret);
|
|
|
|
pr_info("%s: set JIG_CONNECTED(0x%04x) (Jig Connection or bypass)\n", __func__, ret);
|
|
}
|
|
|
|
int sm5714_fg_alert_init(struct sm5714_fuelgauge_data *fuelgauge, int soc)
|
|
{
|
|
int ret;
|
|
int value_soc_alarm;
|
|
|
|
fuelgauge->is_fuel_alerted = false;
|
|
|
|
/* check status */
|
|
ret = sm5714_read_word(fuelgauge->i2c, SM5714_FG_REG_STATUS);
|
|
if (ret < 0) {
|
|
pr_err("%s: Failed to read SM5714_FG_REG_STATUS\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
value_soc_alarm = soc;
|
|
|
|
pr_info("%s: fg_irq= 0x%x, INT_STATUS=0x%x, SOC_ALARM=0x%x \n",
|
|
__func__, fuelgauge->fg_irq, ret, value_soc_alarm);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void sm5714_asoc_init(struct sm5714_fuelgauge_data *fuelgauge)
|
|
{
|
|
int ret, temp;
|
|
|
|
ret = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_USER_RESERV_2);
|
|
temp = ret;
|
|
ret = ret & 0x7F;
|
|
if (ret == 0) {
|
|
fuelgauge->info.soh = 100;
|
|
temp = temp & 0x80;
|
|
temp = temp | fuelgauge->info.soh;
|
|
sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_USER_RESERV_2, temp);
|
|
pr_info("%s : soh reset %d, write 0x%x\n", __func__, fuelgauge->info.soh, temp);
|
|
} else {
|
|
fuelgauge->info.soh = ret;
|
|
pr_info("%s asoc restore : %d\n", __func__, fuelgauge->info.soh);
|
|
}
|
|
}
|
|
|
|
static irqreturn_t sm5714_jig_irq_thread(int irq, void *irq_data)
|
|
{
|
|
struct sm5714_fuelgauge_data *fuelgauge = irq_data;
|
|
|
|
if (sm5714_check_jig_status(fuelgauge))
|
|
sm5714_fg_reset_capacity_by_jig_connection(fuelgauge);
|
|
else
|
|
pr_info("%s: jig removed\n", __func__);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static void sm5714_fg_iocv_buffer_read(struct sm5714_fuelgauge_data *fuelgauge)
|
|
{
|
|
int ret0, ret1, ret2, ret3, ret4, ret5, ret6, ret7;
|
|
|
|
ret0 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_LB_V);
|
|
ret1 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_LB_V+1);
|
|
ret2 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_LB_V+2);
|
|
ret3 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_LB_V+3);
|
|
ret4 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_LB_V+4);
|
|
ret5 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_LB_V+5);
|
|
ret6 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_LB_V+6);
|
|
ret7 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_LB_V+7);
|
|
pr_info("%s: sm5714 FG buffer lb_V = 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x \n",
|
|
__func__, ret0, ret1, ret2, ret3, ret4, ret5, ret6, ret7);
|
|
|
|
ret0 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_CB_V);
|
|
ret1 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_CB_V+1);
|
|
ret2 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_CB_V+2);
|
|
ret3 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_CB_V+3);
|
|
ret4 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_CB_V+4);
|
|
ret5 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_CB_V+5);
|
|
ret6 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_CB_V+6);
|
|
ret7 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_CB_V+7);
|
|
pr_info("%s: sm5714 FG buffer cb_V = 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x \n",
|
|
__func__, ret0, ret1, ret2, ret3, ret4, ret5, ret6, ret7);
|
|
|
|
ret0 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_LB_I);
|
|
ret1 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_LB_I+1);
|
|
ret2 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_LB_I+2);
|
|
ret3 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_LB_I+3);
|
|
ret4 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_LB_I+4);
|
|
ret5 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_LB_I+5);
|
|
ret6 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_LB_I+6);
|
|
ret7 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_LB_I+7);
|
|
pr_info("%s: sm5714 FG buffer lb_I = 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x \n",
|
|
__func__, ret0, ret1, ret2, ret3, ret4, ret5, ret6, ret7);
|
|
|
|
ret0 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_CB_I);
|
|
ret1 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_CB_I+1);
|
|
ret2 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_CB_I+2);
|
|
ret3 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_CB_I+3);
|
|
ret4 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_CB_I+4);
|
|
ret5 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_CB_I+5);
|
|
ret6 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_CB_I+6);
|
|
ret7 = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_START_CB_I+7);
|
|
pr_info("%s: sm5714 FG buffer cb_I = 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x \n",
|
|
__func__, ret0, ret1, ret2, ret3, ret4, ret5, ret6, ret7);
|
|
|
|
return;
|
|
}
|
|
|
|
static bool sm5714_fg_init(struct sm5714_fuelgauge_data *fuelgauge, bool is_surge)
|
|
{
|
|
int error_remain, ret;
|
|
fuelgauge->info.is_FG_initialised = 0;
|
|
|
|
if (sm5714_get_device_id(fuelgauge) < 0) {
|
|
return false;
|
|
}
|
|
|
|
if (fuelgauge->pdata->jig_gpio) {
|
|
int ret;
|
|
/* if (fuelgauge->pdata->jig_low_active) { */
|
|
if (0) {
|
|
ret = request_threaded_irq(fuelgauge->pdata->jig_irq,
|
|
NULL, sm5714_jig_irq_thread,
|
|
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
|
"jig-irq", fuelgauge);
|
|
} else {
|
|
ret = request_threaded_irq(fuelgauge->pdata->jig_irq,
|
|
NULL, sm5714_jig_irq_thread,
|
|
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
|
"jig-irq", fuelgauge);
|
|
}
|
|
if (ret) {
|
|
pr_info("%s: Failed to Request IRQ\n",
|
|
__func__);
|
|
}
|
|
pr_info("%s: jig_result : %d\n", __func__, sm5714_check_jig_status(fuelgauge));
|
|
|
|
/* initial check for the JIG */
|
|
if (sm5714_check_jig_status(fuelgauge))
|
|
sm5714_fg_reset_capacity_by_jig_connection(fuelgauge);
|
|
}
|
|
|
|
#ifdef ENABLE_BATT_LONG_LIFE
|
|
fuelgauge->info.q_max_now = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_Q_MAX);
|
|
pr_info("%s: q_max_now = 0x%x\n", __func__, fuelgauge->info.q_max_now);
|
|
#endif
|
|
|
|
ret = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_USER_RESERV_1);
|
|
error_remain = (ret & I2C_ERROR_REMAIN) ? 1 : 0;
|
|
pr_info("%s: reserv_1 = 0x%x\n", __func__, ret);
|
|
|
|
if (sm5714_fg_check_reg_init_need(fuelgauge) || error_remain) {
|
|
if (sm5714_fg_reg_init(fuelgauge, is_surge))
|
|
pr_info("%s: boot time kernel init DONE!\n", __func__);
|
|
else
|
|
pr_info("%s: ERROR!! boot time kernel init ERROR!!\n", __func__);
|
|
}
|
|
|
|
// factory mode initial SOC make;
|
|
if (factory_mode && !(sm5714_read_word(fuelgauge->i2c, SM5714_FG_REG_SYSTEM_STATUS) & 0x8000)) {
|
|
ret = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_SOC);
|
|
if (ret < 0) {
|
|
pr_err("%s: Warning!!!! read soc reg fail\n", __func__);
|
|
fuelgauge->info.batt_soc = 500;
|
|
} else {
|
|
fuelgauge->info.batt_soc = (ret * 10) >> 8;
|
|
}
|
|
pr_info("%s: nENQ4 low but factory mode, initial SOC make= %d\n", __func__, fuelgauge->info.batt_soc);
|
|
}
|
|
|
|
// curr_cal setup
|
|
sm5714_ical_setup(fuelgauge);
|
|
sm5714_vcal_setup(fuelgauge);
|
|
|
|
/* write aux_ctrl cfg */
|
|
sm5714_set_aux_ctrl_cfg(fuelgauge);
|
|
|
|
sm5714_asoc_init(fuelgauge);
|
|
|
|
fuelgauge->info.is_FG_initialised = 1;
|
|
|
|
sm5714_fg_iocv_buffer_read(fuelgauge);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool sm5714_fg_fuelalert_init(struct sm5714_fuelgauge_data *fuelgauge,
|
|
int soc)
|
|
{
|
|
/* 1. Set sm5714 alert configuration. */
|
|
if (sm5714_fg_alert_init(fuelgauge, soc) > 0)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void sm5714_fg_fuelalert_set(struct sm5714_fuelgauge_data *fuelgauge,
|
|
int enable)
|
|
{
|
|
u16 ret;
|
|
|
|
ret = sm5714_read_word(fuelgauge->i2c, SM5714_FG_REG_STATUS);
|
|
pr_info("%s: SM5714_FG_REG_STATUS(0x%x)\n",
|
|
__func__, ret);
|
|
|
|
if (ret & ENABLE_VL_ALARM && !lpcharge && !fuelgauge->is_charging) {
|
|
pr_info("%s : Battery Voltage is Very Low!! SW V EMPTY ENABLE\n", __func__);
|
|
|
|
if (fuelgauge->vempty_mode == VEMPTY_MODE_SW ||
|
|
fuelgauge->vempty_mode == VEMPTY_MODE_SW_VALERT) {
|
|
fuelgauge->vempty_mode = VEMPTY_MODE_SW_VALERT;
|
|
}
|
|
#if defined(CONFIG_BATTERY_CISD)
|
|
else {
|
|
union power_supply_propval value;
|
|
value.intval = fuelgauge->vempty_mode;
|
|
psy_do_property("battery", set,
|
|
POWER_SUPPLY_PROP_VOLTAGE_MIN, value);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
bool sm5714_fg_fuelalert_process(void *irq_data)
|
|
{
|
|
struct sm5714_fuelgauge_data *fuelgauge =
|
|
(struct sm5714_fuelgauge_data *)irq_data;
|
|
|
|
sm5714_fg_fuelalert_set(fuelgauge, 0);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool sm5714_fg_reset(struct sm5714_fuelgauge_data *fuelgauge, bool is_quickstart)
|
|
{
|
|
if (fuelgauge->info.is_FG_initialised == 0) {
|
|
pr_info("%s: Not work reset! prev init working! return! \n", __func__);
|
|
return true;
|
|
}
|
|
|
|
pr_info("%s: Start fg reset\n", __func__);
|
|
/* SW reset code */
|
|
fuelgauge->info.is_FG_initialised = 0;
|
|
sm5714_fg_verified_write_word(fuelgauge->i2c, SM5714_FG_REG_RESET, SW_RESET_CODE);
|
|
/* delay 1000ms */
|
|
msleep(1000);
|
|
|
|
if (is_quickstart) {
|
|
if (sm5714_fg_init(fuelgauge, false)) {
|
|
pr_info("%s: Quick Start !!\n", __func__);
|
|
} else {
|
|
pr_info("%s: sm5714_fg_init ERROR!!!!\n", __func__);
|
|
return false;
|
|
}
|
|
}
|
|
#ifdef ENABLE_BATT_LONG_LIFE
|
|
else {
|
|
if (sm5714_fg_init(fuelgauge, true)) {
|
|
pr_info("%s: BATT_LONG_LIFE reset !!\n", __func__);
|
|
} else {
|
|
pr_info("%s: sm5714_fg_init ERROR!!!!\n", __func__);
|
|
return false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
pr_info("%s: End fg reset\n", __func__);
|
|
|
|
return true;
|
|
}
|
|
|
|
static void sm5714_fg_get_scaled_capacity(
|
|
struct sm5714_fuelgauge_data *fuelgauge,
|
|
union power_supply_propval *val)
|
|
{
|
|
val->intval = (val->intval < fuelgauge->pdata->capacity_min) ?
|
|
0 : ((val->intval - fuelgauge->pdata->capacity_min) * 1000 /
|
|
(fuelgauge->capacity_max - fuelgauge->pdata->capacity_min));
|
|
|
|
pr_info("%s: scaled capacity (%d.%d)\n",
|
|
__func__, val->intval/10, val->intval%10);
|
|
}
|
|
|
|
|
|
/* capacity is integer */
|
|
static void sm5714_fg_get_atomic_capacity(
|
|
struct sm5714_fuelgauge_data *fuelgauge,
|
|
union power_supply_propval *val)
|
|
{
|
|
|
|
pr_debug("%s : NOW(%d), OLD(%d)\n",
|
|
__func__, val->intval, fuelgauge->capacity_old);
|
|
|
|
if (fuelgauge->pdata->capacity_calculation_type &
|
|
SEC_FUELGAUGE_CAPACITY_TYPE_ATOMIC) {
|
|
if (fuelgauge->capacity_old < val->intval)
|
|
val->intval = fuelgauge->capacity_old + 1;
|
|
else if (fuelgauge->capacity_old > val->intval)
|
|
val->intval = fuelgauge->capacity_old - 1;
|
|
}
|
|
|
|
/* keep SOC stable in abnormal status */
|
|
if (fuelgauge->pdata->capacity_calculation_type &
|
|
SEC_FUELGAUGE_CAPACITY_TYPE_SKIP_ABNORMAL) {
|
|
if (!fuelgauge->is_charging &&
|
|
fuelgauge->capacity_old < val->intval) {
|
|
pr_err("%s: capacity (old %d : new %d)\n",
|
|
__func__, fuelgauge->capacity_old, val->intval);
|
|
val->intval = fuelgauge->capacity_old;
|
|
}
|
|
}
|
|
|
|
/* updated old capacity */
|
|
fuelgauge->capacity_old = val->intval;
|
|
}
|
|
|
|
static int sm5714_fg_calculate_dynamic_scale(
|
|
struct sm5714_fuelgauge_data *fuelgauge, int capacity)
|
|
{
|
|
union power_supply_propval raw_soc_val;
|
|
raw_soc_val.intval = sm5714_get_soc(fuelgauge);
|
|
|
|
fuelgauge->capacity_max =
|
|
(raw_soc_val.intval * 100 / (capacity + 1));
|
|
fuelgauge->capacity_old = capacity;
|
|
|
|
pr_info("%s: %d is used for capacity_max, capacity(%d)\n",
|
|
__func__, fuelgauge->capacity_max, capacity);
|
|
|
|
return fuelgauge->capacity_max;
|
|
}
|
|
|
|
#if defined(CONFIG_EN_OOPS)
|
|
static void sm5714_set_full_value(struct sm5714_fuelgauge_data *fuelgauge,
|
|
int cable_type)
|
|
{
|
|
pr_info("%s : sm5714 todo\n",
|
|
__func__);
|
|
}
|
|
#endif
|
|
|
|
static int calc_ttf(struct sm5714_fuelgauge_data *fuelgauge, union power_supply_propval *val)
|
|
{
|
|
int i;
|
|
int cc_time = 0, cv_time = 0;
|
|
|
|
int soc = fuelgauge->raw_capacity;
|
|
int charge_current = val->intval;
|
|
struct cv_slope *cv_data = fuelgauge->cv_data;
|
|
int design_cap = fuelgauge->battery_data->Capacity * fuelgauge->fg_resistor / 2;
|
|
|
|
if (!cv_data || (val->intval <= 0)) {
|
|
pr_info("%s: no cv_data or val: %d\n", __func__, val->intval);
|
|
return -1;
|
|
}
|
|
for (i = 0; i < fuelgauge->cv_data_length; i++) {
|
|
if (charge_current >= cv_data[i].fg_current)
|
|
break;
|
|
}
|
|
i = i >= fuelgauge->cv_data_length ? fuelgauge->cv_data_length - 1 : i;
|
|
if (cv_data[i].soc < soc) {
|
|
for (i = 0; i < fuelgauge->cv_data_length; i++) {
|
|
if (soc <= cv_data[i].soc)
|
|
break;
|
|
}
|
|
cv_time = ((cv_data[i-1].time - cv_data[i].time) * (cv_data[i].soc - soc)\
|
|
/ (cv_data[i].soc - cv_data[i-1].soc)) + cv_data[i].time;
|
|
} else { /* CC mode || NONE */
|
|
cv_time = cv_data[i].time;
|
|
cc_time = design_cap * (cv_data[i].soc - soc)\
|
|
/ val->intval * 3600 / 1000;
|
|
pr_debug("%s: cc_time: %d\n", __func__, cc_time);
|
|
if (cc_time < 0) {
|
|
cc_time = 0;
|
|
}
|
|
}
|
|
|
|
pr_debug("%s: cap: %d, soc: %4d, T: %6d, avg: %4d, cv soc: %4d, i: %4d, val: %d\n",
|
|
__func__, design_cap, soc, cv_time + cc_time, fuelgauge->current_avg, cv_data[i].soc, i, val->intval);
|
|
|
|
if (cv_time + cc_time >= 0)
|
|
return cv_time + cc_time + 60;
|
|
else
|
|
return 60; /* minimum 1minutes */
|
|
}
|
|
|
|
static void sm5714_fg_set_vempty(struct sm5714_fuelgauge_data *fuelgauge, int vempty_mode)
|
|
{
|
|
u16 data = 0;
|
|
u32 value_v_alarm = 0;
|
|
|
|
if (!fuelgauge->using_temp_compensation) {
|
|
pr_info("%s: does not use temp compensation, default hw vempty\n", __func__);
|
|
vempty_mode = VEMPTY_MODE_HW;
|
|
}
|
|
|
|
fuelgauge->vempty_mode = vempty_mode;
|
|
|
|
switch (vempty_mode) {
|
|
case VEMPTY_MODE_SW:
|
|
/* HW Vempty Disable */
|
|
|
|
/* set volt alert threshold */
|
|
value_v_alarm = (fuelgauge->battery_data->sw_v_empty_vol - 2700)*109/10;
|
|
|
|
if (sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_INTR_VL, value_v_alarm) < 0) {
|
|
pr_info("%s: Failed to write VALRT_THRESHOLD_REG\n", __func__);
|
|
return;
|
|
}
|
|
data = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_INTR_VL);
|
|
pr_info("%s: HW V EMPTY Disable, SW V EMPTY Enable with %d mV (%d) \n",
|
|
__func__, fuelgauge->battery_data->sw_v_empty_vol, data);
|
|
break;
|
|
default:
|
|
/* HW Vempty works together with CISD v_alarm */
|
|
value_v_alarm = (fuelgauge->battery_data->sw_v_empty_vol_cisd - 2700)*109/10;
|
|
|
|
if (sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_INTR_VL, value_v_alarm) < 0) {
|
|
pr_info("%s: Failed to write VALRT_THRESHOLD_REG\n", __func__);
|
|
return;
|
|
}
|
|
data = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_INTR_VL);
|
|
pr_info("%s: HW V EMPTY Enable, SW V EMPTY Disable %d mV (%d) \n",
|
|
__func__, fuelgauge->battery_data->sw_v_empty_vol_cisd, data);
|
|
break;
|
|
}
|
|
|
|
/* for v_alarm Hysteresis */
|
|
value_v_alarm = (fuelgauge->info.value_v_alarm_hys)*109/10;
|
|
if (sm5714_fg_write_sram(fuelgauge, SM5714_FG_ADDR_SRAM_INTR_VL_HYS, value_v_alarm) < 0) {
|
|
pr_info("%s: Failed to write VALRT_THRESHOLD_REG\n", __func__);
|
|
return;
|
|
}
|
|
data = sm5714_fg_read_sram(fuelgauge, SM5714_FG_ADDR_SRAM_INTR_VL_HYS);
|
|
pr_info("%s: VALRT_THRESHOLD hysteresis set %d mV (0x%x) \n",
|
|
__func__, fuelgauge->info.value_v_alarm_hys, data);
|
|
}
|
|
|
|
static int sm5714_fg_get_property(struct power_supply *psy,
|
|
enum power_supply_property psp,
|
|
union power_supply_propval *val)
|
|
{
|
|
struct sm5714_fuelgauge_data *fuelgauge = power_supply_get_drvdata(psy);
|
|
enum power_supply_ext_property ext_psp = (enum power_supply_ext_property) psp;
|
|
/*
|
|
static int abnormal_current_cnt = 0;
|
|
union power_supply_propval value;
|
|
*/
|
|
|
|
pr_info("%s: psp = 0x%x\n", __func__, psp);
|
|
switch (psp) {
|
|
/* Cell voltage (VCELL, mV) */
|
|
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
|
sm5714_get_vbat(fuelgauge);
|
|
val->intval = fuelgauge->info.batt_voltage;
|
|
break;
|
|
case POWER_SUPPLY_PROP_VOLTAGE_AVG:
|
|
switch (val->intval) {
|
|
case SEC_BATTERY_VOLTAGE_OCV:
|
|
val->intval = fuelgauge->info.batt_ocv;
|
|
break;
|
|
case SEC_BATTERY_VOLTAGE_AVERAGE:
|
|
val->intval = fuelgauge->info.batt_avgvoltage;
|
|
break;
|
|
}
|
|
break;
|
|
/* Current */
|
|
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
|
sm5714_get_curr(fuelgauge);
|
|
if (val->intval == SEC_BATTERY_CURRENT_UA)
|
|
val->intval = fuelgauge->info.batt_current * 1000;
|
|
else
|
|
val->intval = fuelgauge->info.batt_current;
|
|
break;
|
|
/* Average Current */
|
|
case POWER_SUPPLY_PROP_CURRENT_AVG:
|
|
if (val->intval == SEC_BATTERY_CURRENT_UA)
|
|
val->intval = fuelgauge->info.batt_avgcurrent * 1000;
|
|
else
|
|
val->intval = fuelgauge->info.batt_avgcurrent;
|
|
break;
|
|
/* Full Capacity */
|
|
case POWER_SUPPLY_PROP_ENERGY_NOW:
|
|
switch (val->intval) {
|
|
case SEC_BATTERY_CAPACITY_DESIGNED:
|
|
break;
|
|
case SEC_BATTERY_CAPACITY_ABSOLUTE:
|
|
break;
|
|
case SEC_BATTERY_CAPACITY_TEMPERARY:
|
|
break;
|
|
case SEC_BATTERY_CAPACITY_CURRENT:
|
|
break;
|
|
case SEC_BATTERY_CAPACITY_AGEDCELL:
|
|
break;
|
|
case SEC_BATTERY_CAPACITY_CYCLE:
|
|
sm5714_get_cycle(fuelgauge);
|
|
val->intval = fuelgauge->info.batt_soc_cycle;
|
|
break;
|
|
/* case SEC_BATTERY_CAPACITY_FULL: */
|
|
/* break; */
|
|
}
|
|
break;
|
|
/* SOC (%) */
|
|
case POWER_SUPPLY_PROP_CAPACITY:
|
|
sm5714_update_all_value(fuelgauge);
|
|
|
|
/* SM5714 F/G unit is 0.1%, raw ==> convert the unit to 0.01% */
|
|
if (val->intval == SEC_FUELGAUGE_CAPACITY_TYPE_RAW) {
|
|
val->intval = fuelgauge->info.batt_soc * 10;
|
|
break;
|
|
} else if (val->intval == SEC_FUELGAUGE_CAPACITY_TYPE_DYNAMIC_SCALE) {
|
|
val->intval = fuelgauge->raw_capacity;
|
|
break;
|
|
} else
|
|
val->intval = fuelgauge->info.batt_soc;
|
|
|
|
if (fuelgauge->pdata->capacity_calculation_type &
|
|
(SEC_FUELGAUGE_CAPACITY_TYPE_SCALE |
|
|
SEC_FUELGAUGE_CAPACITY_TYPE_DYNAMIC_SCALE))
|
|
sm5714_fg_get_scaled_capacity(fuelgauge, val);
|
|
|
|
/* capacity should be between 0% and 100%
|
|
* (0.1% degree)
|
|
*/
|
|
if (val->intval > 1000)
|
|
val->intval = 1000;
|
|
if (val->intval < 0)
|
|
val->intval = 0;
|
|
|
|
fuelgauge->raw_capacity = val->intval;
|
|
|
|
/* get only integer part */
|
|
val->intval /= 10;
|
|
|
|
/* SW/HW V Empty setting */
|
|
if (fuelgauge->using_hw_vempty) {
|
|
if (fuelgauge->info.temperature <= (int)fuelgauge->low_temp_limit) {
|
|
if (fuelgauge->raw_capacity <= 50) {
|
|
if (fuelgauge->vempty_mode != VEMPTY_MODE_HW) {
|
|
sm5714_fg_set_vempty(fuelgauge, VEMPTY_MODE_HW);
|
|
}
|
|
} else if (fuelgauge->vempty_mode == VEMPTY_MODE_HW) {
|
|
sm5714_fg_set_vempty(fuelgauge, VEMPTY_MODE_SW);
|
|
}
|
|
} else if (fuelgauge->vempty_mode != VEMPTY_MODE_HW) {
|
|
sm5714_fg_set_vempty(fuelgauge, VEMPTY_MODE_HW);
|
|
}
|
|
}
|
|
|
|
if (!fuelgauge->is_charging &&
|
|
fuelgauge->vempty_mode == VEMPTY_MODE_SW_VALERT && !lpcharge) {
|
|
pr_info("%s : SW V EMPTY. Decrease SOC\n", __func__);
|
|
if (fuelgauge->capacity_old > 0)
|
|
val->intval = fuelgauge->capacity_old - 1;
|
|
else
|
|
val->intval = 0;
|
|
} else if ((fuelgauge->vempty_mode == VEMPTY_MODE_SW_RECOVERY) &&
|
|
(val->intval == fuelgauge->capacity_old)) {
|
|
fuelgauge->vempty_mode = VEMPTY_MODE_SW;
|
|
}
|
|
|
|
/* check whether doing the wake_unlock */
|
|
if ((val->intval > fuelgauge->pdata->fuel_alert_soc) &&
|
|
fuelgauge->is_fuel_alerted) {
|
|
sm5714_fg_fuelalert_init(fuelgauge,
|
|
fuelgauge->pdata->fuel_alert_soc);
|
|
}
|
|
|
|
/* (Only for atomic capacity)
|
|
* In initial time, capacity_old is 0.
|
|
* and in resume from sleep,
|
|
* capacity_old is too different from actual soc.
|
|
* should update capacity_old
|
|
* by val->intval in booting or resume.
|
|
*/
|
|
if (fuelgauge->initial_update_of_soc) {
|
|
fuelgauge->initial_update_of_soc = false;
|
|
if (fuelgauge->vempty_mode != VEMPTY_MODE_SW_VALERT) {
|
|
/* updated old capacity */
|
|
fuelgauge->capacity_old = val->intval;
|
|
break;
|
|
}
|
|
}
|
|
if (fuelgauge->sleep_initial_update_of_soc) {
|
|
fuelgauge->sleep_initial_update_of_soc = false;
|
|
if (fuelgauge->is_charging ||
|
|
((!fuelgauge->is_charging) && (fuelgauge->capacity_old >= val->intval))) {
|
|
fuelgauge->capacity_old = val->intval;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fuelgauge->pdata->capacity_calculation_type &
|
|
(SEC_FUELGAUGE_CAPACITY_TYPE_ATOMIC |
|
|
SEC_FUELGAUGE_CAPACITY_TYPE_SKIP_ABNORMAL)) {
|
|
sm5714_fg_get_atomic_capacity(fuelgauge, val);
|
|
}
|
|
break;
|
|
/* Battery Temperature */
|
|
case POWER_SUPPLY_PROP_TEMP:
|
|
/* Target Temperature */
|
|
case POWER_SUPPLY_PROP_TEMP_AMBIENT:
|
|
sm5714_get_temperature(fuelgauge);
|
|
val->intval = fuelgauge->info.temp_fg;
|
|
break;
|
|
#if defined(CONFIG_EN_OOPS)
|
|
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
|
|
return -ENODATA;
|
|
#endif
|
|
case POWER_SUPPLY_PROP_ENERGY_FULL:
|
|
val->intval = sm5714_get_asoc(fuelgauge);
|
|
break;
|
|
case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
|
|
val->intval = fuelgauge->capacity_max;
|
|
break;
|
|
case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
|
|
val->intval = calc_ttf(fuelgauge, val);
|
|
break;
|
|
case POWER_SUPPLY_PROP_CHARGE_ENABLED:
|
|
break;
|
|
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
|
|
val->intval =
|
|
fuelgauge->battery_data->Capacity * fuelgauge->raw_capacity;
|
|
pr_info("%s: charge_counter = %d \n", __func__, val->intval);
|
|
break;
|
|
#if defined(CONFIG_BATTERY_AGE_FORECAST)
|
|
case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
|
|
return -ENODATA;
|
|
#endif
|
|
case POWER_SUPPLY_PROP_MAX ... POWER_SUPPLY_EXT_PROP_MAX:
|
|
switch (ext_psp) {
|
|
case POWER_SUPPLY_EXT_PROP_JIG_GPIO:
|
|
if (fuelgauge->pdata->jig_gpio)
|
|
val->intval = gpio_get_value(fuelgauge->pdata->jig_gpio);
|
|
else
|
|
val->intval = -1;
|
|
pr_info("%s: jig gpio = %d \n", __func__, val->intval);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_UPDATE_BATTERY_DATA)
|
|
static int sm5714_fuelgauge_parse_dt(struct sm5714_fuelgauge_data *fuelgauge);
|
|
#endif
|
|
static int sm5714_fg_set_property(struct power_supply *psy,
|
|
enum power_supply_property psp,
|
|
const union power_supply_propval *val)
|
|
{
|
|
struct sm5714_fuelgauge_data *fuelgauge = power_supply_get_drvdata(psy);
|
|
enum power_supply_ext_property ext_psp = (enum power_supply_ext_property) psp;
|
|
/*u8 data[2] = {0, 0}; */
|
|
|
|
switch (psp) {
|
|
case POWER_SUPPLY_PROP_STATUS:
|
|
#ifdef ENABLE_BATT_LONG_LIFE
|
|
pr_info("%s: POWER_SUPPLY_PROP_CHARGE_FULL : q_max_now = 0x%x \n", __func__, fuelgauge->info.q_max_now);
|
|
if (fuelgauge->info.q_max_now !=
|
|
fuelgauge->info.q_max_table[get_v_max_index_by_cycle(fuelgauge)]) {
|
|
if (!sm5714_fg_reset(fuelgauge, false))
|
|
return -EINVAL;
|
|
}
|
|
#endif
|
|
break;
|
|
case POWER_SUPPLY_PROP_CHARGE_FULL:
|
|
if (fuelgauge->pdata->capacity_calculation_type &
|
|
SEC_FUELGAUGE_CAPACITY_TYPE_DYNAMIC_SCALE)
|
|
sm5714_fg_calculate_dynamic_scale(fuelgauge, val->intval);
|
|
break;
|
|
#if defined(CONFIG_EN_OOPS)
|
|
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
|
|
sm5714_set_full_value(fuelgauge, val->intval);
|
|
break;
|
|
#endif
|
|
case POWER_SUPPLY_PROP_ONLINE:
|
|
fuelgauge->cable_type = val->intval;
|
|
if (is_nocharge_type(fuelgauge->cable_type)) {
|
|
fuelgauge->ta_exist = false;
|
|
fuelgauge->is_charging = false;
|
|
} else {
|
|
fuelgauge->ta_exist = true;
|
|
fuelgauge->is_charging = true;
|
|
/* enable alert */
|
|
if (fuelgauge->vempty_mode >= VEMPTY_MODE_SW_VALERT) {
|
|
sm5714_fg_set_vempty(fuelgauge, VEMPTY_MODE_HW);
|
|
fuelgauge->initial_update_of_soc = true;
|
|
sm5714_fg_fuelalert_init(fuelgauge,
|
|
fuelgauge->pdata->fuel_alert_soc);
|
|
}
|
|
}
|
|
break;
|
|
/* Battery Temperature */
|
|
case POWER_SUPPLY_PROP_CAPACITY:
|
|
if (val->intval == SEC_FUELGAUGE_CAPACITY_TYPE_RESET) {
|
|
if (!sm5714_fg_reset(fuelgauge, true))
|
|
return -EINVAL;
|
|
fuelgauge->initial_update_of_soc = true;
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_PROP_TEMP:
|
|
fuelgauge->info.temperature = val->intval;
|
|
if (val->intval < 0) {
|
|
pr_info("%s: set the low temp reset! temp : %d\n",
|
|
__func__, val->intval);
|
|
}
|
|
break;
|
|
case POWER_SUPPLY_PROP_TEMP_AMBIENT:
|
|
break;
|
|
case POWER_SUPPLY_PROP_HEALTH:
|
|
fuelgauge->info.flag_charge_health =
|
|
(val->intval == POWER_SUPPLY_HEALTH_GOOD) ? 1 : 0;
|
|
pr_info("%s: charge health from charger = 0x%x\n", __func__, val->intval);
|
|
break;
|
|
case POWER_SUPPLY_PROP_ENERGY_NOW:
|
|
sm5714_fg_reset_capacity_by_jig_connection(fuelgauge);
|
|
break;
|
|
case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
|
|
pr_info("%s: capacity_max changed, %d -> %d\n",
|
|
__func__, fuelgauge->capacity_max, val->intval);
|
|
fuelgauge->capacity_max = val->intval;
|
|
fuelgauge->initial_update_of_soc = true;
|
|
break;
|
|
case POWER_SUPPLY_PROP_CHARGE_ENABLED:
|
|
break;
|
|
#if defined(CONFIG_BATTERY_AGE_FORECAST)
|
|
case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
|
|
pr_info("%s: full condition soc changed, %d -> %d\n",
|
|
__func__, fuelgauge->chg_full_soc, val->intval);
|
|
fuelgauge->chg_full_soc = val->intval;
|
|
break;
|
|
#endif
|
|
|
|
#if defined(CONFIG_UPDATE_BATTERY_DATA)
|
|
case POWER_SUPPLY_PROP_POWER_DESIGN:
|
|
sm5714_fuelgauge_parse_dt(fuelgauge);
|
|
break;
|
|
#endif
|
|
case POWER_SUPPLY_PROP_MAX ... POWER_SUPPLY_EXT_PROP_MAX:
|
|
switch (ext_psp) {
|
|
case POWER_SUPPLY_EXT_PROP_FGSRC_SWITCHING:
|
|
sm5714_fg_set_jig_mode_real_vbat(fuelgauge, val->intval);
|
|
break;
|
|
case POWER_SUPPLY_EXT_PROP_FUELGAUGE_FACTORY:
|
|
if (val->intval) {
|
|
pr_info("%s: bypass mode is enabled\n", __func__);
|
|
sm5714_fg_reset_capacity_by_jig_connection(fuelgauge);
|
|
}
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void sm5714_fg_isr_work(struct work_struct *work)
|
|
{
|
|
struct sm5714_fuelgauge_data *fuelgauge =
|
|
container_of(work, struct sm5714_fuelgauge_data, isr_work.work);
|
|
|
|
/* process for fuel gauge chip */
|
|
sm5714_fg_fuelalert_process(fuelgauge);
|
|
|
|
__pm_relax(fuelgauge->fuel_alert_ws);
|
|
}
|
|
|
|
static irqreturn_t sm5714_fg_irq_thread(int irq, void *irq_data)
|
|
{
|
|
struct sm5714_fuelgauge_data *fuelgauge = irq_data;
|
|
|
|
pr_info("%s\n", __func__);
|
|
|
|
if (fuelgauge->is_fuel_alerted) {
|
|
return IRQ_HANDLED;
|
|
} else {
|
|
__pm_stay_awake(fuelgauge->fuel_alert_ws);
|
|
fuelgauge->is_fuel_alerted = true;
|
|
schedule_delayed_work(&fuelgauge->isr_work, 0);
|
|
}
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int sm5714_fuelgauge_debugfs_show(struct seq_file *s, void *data)
|
|
{
|
|
struct sm5714_fuelgauge_data *fuelgauge = s->private;
|
|
int i;
|
|
u8 reg;
|
|
u8 reg_data;
|
|
|
|
seq_printf(s, "SM5714 FUELGAUGE IC :\n");
|
|
seq_printf(s, "===================\n");
|
|
for (i = 0; i < 16; i++) {
|
|
if (i == 12)
|
|
continue;
|
|
for (reg = 0; reg < 0x10; reg++) {
|
|
reg_data = sm5714_read_word(fuelgauge->i2c, reg + i * 0x10);
|
|
seq_printf(s, "0x%02x:\t0x%04x\n", reg + i * 0x10, reg_data);
|
|
}
|
|
if (i == 4)
|
|
i = 10;
|
|
}
|
|
seq_printf(s, "\n");
|
|
return 0;
|
|
}
|
|
|
|
static int sm5714_fuelgauge_debugfs_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, sm5714_fuelgauge_debugfs_show, inode->i_private);
|
|
}
|
|
|
|
static const struct file_operations sm5714_fuelgauge_debugfs_fops = {
|
|
.open = sm5714_fuelgauge_debugfs_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
|
|
#ifdef CONFIG_OF
|
|
#define PROPERTY_NAME_SIZE 128
|
|
|
|
#define PINFO(format, args...) \
|
|
printk(KERN_INFO "%s() line-%d: " format, \
|
|
__func__, __LINE__, ## args)
|
|
|
|
#if defined(ENABLE_BATT_LONG_LIFE)
|
|
static int temp_parse_dt(struct sm5714_fuelgauge_data *fuelgauge)
|
|
{
|
|
struct device_node *np = of_find_node_by_name(NULL, "battery");
|
|
int len = 0, ret;
|
|
const u32 *p;
|
|
|
|
if (np == NULL) {
|
|
pr_err("%s np NULL\n", __func__);
|
|
} else {
|
|
p = of_get_property(np, "battery,age_data", &len);
|
|
if (p) {
|
|
fuelgauge->pdata->num_age_step = len / sizeof(sec_age_data_t);
|
|
fuelgauge->pdata->age_data = kzalloc(len, GFP_KERNEL);
|
|
ret = of_property_read_u32_array(np, "battery,age_data",
|
|
(u32 *)fuelgauge->pdata->age_data, len/sizeof(u32));
|
|
if (ret) {
|
|
pr_err("%s failed to read battery->pdata->age_data: %d\n",
|
|
__func__, ret);
|
|
kfree(fuelgauge->pdata->age_data);
|
|
fuelgauge->pdata->age_data = NULL;
|
|
fuelgauge->pdata->num_age_step = 0;
|
|
}
|
|
pr_info("%s num_age_step : %d\n", __func__, fuelgauge->pdata->num_age_step);
|
|
for (len = 0; len < fuelgauge->pdata->num_age_step; ++len) {
|
|
pr_info("[%d/%d]cycle:%d, float:%d, full_v:%d, recharge_v:%d, soc:%d\n",
|
|
len, fuelgauge->pdata->num_age_step-1,
|
|
fuelgauge->pdata->age_data[len].cycle,
|
|
fuelgauge->pdata->age_data[len].float_voltage,
|
|
fuelgauge->pdata->age_data[len].full_condition_vcell,
|
|
fuelgauge->pdata->age_data[len].recharge_condition_vcell,
|
|
fuelgauge->pdata->age_data[len].full_condition_soc);
|
|
}
|
|
} else {
|
|
fuelgauge->pdata->num_age_step = 0;
|
|
pr_err("%s there is not age_data\n", __func__);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int sm5714_fuelgauge_parse_dt(struct sm5714_fuelgauge_data *fuelgauge)
|
|
{
|
|
char prop_name[PROPERTY_NAME_SIZE];
|
|
int battery_id = -1;
|
|
int table[24];
|
|
#ifdef ENABLE_BATT_LONG_LIFE
|
|
int v_max_table[5];
|
|
int q_max_table[5];
|
|
#endif
|
|
int rs_value[7];
|
|
int battery_type[3];
|
|
int v_alarm[2];
|
|
int set_temp_poff[4];
|
|
int i_cal[8];
|
|
int v_cal[7];
|
|
int aux_ctrl[2];
|
|
|
|
int ret;
|
|
int i, j;
|
|
const u32 *p;
|
|
int len;
|
|
|
|
struct device_node *np = of_find_node_by_name(NULL, "sm5714-fuelgauge");
|
|
|
|
/* reset, irq gpio info */
|
|
if (np == NULL) {
|
|
pr_err("%s np NULL\n", __func__);
|
|
} else {
|
|
ret = of_property_read_u32(np, "fuelgauge,capacity_max",
|
|
&fuelgauge->pdata->capacity_max);
|
|
if (ret < 0)
|
|
pr_err("%s error reading capacity_max %d\n", __func__, ret);
|
|
|
|
ret = of_property_read_u32(np, "fuelgauge,capacity_min",
|
|
&fuelgauge->pdata->capacity_min);
|
|
if (ret < 0)
|
|
pr_err("%s error reading capacity_min %d\n", __func__, ret);
|
|
|
|
pr_info("%s: capacity_max: %d, "
|
|
"capacity_min: %d\n", __func__, fuelgauge->pdata->capacity_max,
|
|
fuelgauge->pdata->capacity_min);
|
|
|
|
ret = of_property_read_u32(np, "fuelgauge,capacity_calculation_type",
|
|
&fuelgauge->pdata->capacity_calculation_type);
|
|
if (ret < 0)
|
|
pr_err("%s error reading capacity_calculation_type %d\n",
|
|
__func__, ret);
|
|
ret = of_property_read_u32(np, "fuelgauge,fuel_alert_soc",
|
|
&fuelgauge->pdata->fuel_alert_soc);
|
|
if (ret < 0)
|
|
pr_err("%s error reading pdata->fuel_alert_soc %d\n",
|
|
__func__, ret);
|
|
fuelgauge->pdata->repeated_fuelalert = of_property_read_bool(np,
|
|
"fuelgaguge,repeated_fuelalert");
|
|
|
|
pr_info("%s: "
|
|
"calculation_type: 0x%x, fuel_alert_soc: %d,\n"
|
|
"repeated_fuelalert: %d\n", __func__,
|
|
fuelgauge->pdata->capacity_calculation_type,
|
|
fuelgauge->pdata->fuel_alert_soc, fuelgauge->pdata->repeated_fuelalert);
|
|
|
|
|
|
fuelgauge->using_temp_compensation = of_property_read_bool(np,
|
|
"fuelgauge,using_temp_compensation");
|
|
if (fuelgauge->using_temp_compensation) {
|
|
ret = of_property_read_u32(np, "fuelgauge,low_temp_limit",
|
|
&fuelgauge->low_temp_limit);
|
|
if (ret < 0)
|
|
pr_err("%s error reading low temp limit %d\n", __func__, ret);
|
|
|
|
pr_info("%s : LOW TEMP LIMIT(%d)\n",
|
|
__func__, fuelgauge->low_temp_limit);
|
|
}
|
|
|
|
fuelgauge->using_hw_vempty = of_property_read_bool(np,
|
|
"fuelgauge,using_hw_vempty");
|
|
if (fuelgauge->using_hw_vempty) {
|
|
ret = of_property_read_u32(np, "fuelgauge,v_empty",
|
|
&fuelgauge->battery_data->V_empty);
|
|
if (ret < 0)
|
|
pr_err("%s error reading v_empty %d\n",
|
|
__func__, ret);
|
|
|
|
ret = of_property_read_u32(np, "fuelgauge,v_empty_origin",
|
|
&fuelgauge->battery_data->V_empty_origin);
|
|
if (ret < 0)
|
|
pr_err("%s error reading v_empty_origin %d\n",
|
|
__func__, ret);
|
|
|
|
ret = of_property_read_u32(np, "fuelgauge,sw_v_empty_voltage_cisd",
|
|
&fuelgauge->battery_data->sw_v_empty_vol_cisd);
|
|
if (ret < 0) {
|
|
pr_err("%s error reading sw_v_empty_default_vol_cise %d\n",
|
|
__func__, ret);
|
|
fuelgauge->battery_data->sw_v_empty_vol_cisd = 3100;
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "fuelgauge,sw_v_empty_voltage",
|
|
&fuelgauge->battery_data->sw_v_empty_vol);
|
|
if (ret < 0) {
|
|
pr_err("%s error reading sw_v_empty_default_vol %d\n",
|
|
__func__, ret);
|
|
fuelgauge->battery_data->sw_v_empty_vol = 3200;
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "fuelgauge,sw_v_empty_recover_voltage",
|
|
&fuelgauge->battery_data->sw_v_empty_recover_vol);
|
|
if (ret < 0) {
|
|
pr_err("%s error reading sw_v_empty_recover_vol %d\n",
|
|
__func__, ret);
|
|
fuelgauge->battery_data->sw_v_empty_recover_vol = 3480;
|
|
}
|
|
|
|
ret = of_property_read_u32(np, "fuelgauge,vbat_ovp",
|
|
&fuelgauge->battery_data->vbat_ovp);
|
|
if (ret < 0) {
|
|
pr_err("%s error reading vbat_ovp %d\n",
|
|
__func__, ret);
|
|
fuelgauge->battery_data->vbat_ovp = 4400;
|
|
}
|
|
|
|
pr_info("%s : SW V Empty (%d)mV, SW V Empty recover (%d)mV, CISD V_Alarm (%d)mV, Vbat OVP (%d)mV \n",
|
|
__func__,
|
|
fuelgauge->battery_data->sw_v_empty_vol,
|
|
fuelgauge->battery_data->sw_v_empty_recover_vol,
|
|
fuelgauge->battery_data->sw_v_empty_vol_cisd,
|
|
fuelgauge->battery_data->vbat_ovp);
|
|
}
|
|
|
|
fuelgauge->pdata->jig_gpio = of_get_named_gpio(np, "fuelgauge,jig_gpio", 0);
|
|
if (fuelgauge->pdata->jig_gpio < 0) {
|
|
pr_err("%s error reading jig_gpio = %d\n",
|
|
__func__, fuelgauge->pdata->jig_gpio);
|
|
fuelgauge->pdata->jig_gpio = 0;
|
|
} else {
|
|
fuelgauge->pdata->jig_irq = gpio_to_irq(fuelgauge->pdata->jig_gpio);
|
|
}
|
|
/*
|
|
if (fuelgauge->pdata->jig_gpio) {
|
|
ret = of_property_read_u32(np, "fuelgauge,jig_low_active",
|
|
&fuelgauge->pdata->jig_low_active);
|
|
|
|
if (ret < 0) {
|
|
pr_err("%s error reading jig_low_active %d\n", __func__, ret);
|
|
fuelgauge->pdata->jig_low_active = 0;
|
|
}
|
|
}
|
|
*/
|
|
ret = of_property_read_u32(np, "fuelgauge,fg_resistor",
|
|
&fuelgauge->fg_resistor);
|
|
if (ret < 0) {
|
|
pr_err("%s error reading fg_resistor %d\n",
|
|
__func__, ret);
|
|
fuelgauge->fg_resistor = 1;
|
|
}
|
|
|
|
|
|
#if defined(CONFIG_EN_OOPS)
|
|
/* todo cap redesign */
|
|
#endif
|
|
|
|
ret = of_property_read_u32(np, "fuelgauge,capacity",
|
|
&fuelgauge->battery_data->Capacity);
|
|
if (ret < 0)
|
|
pr_err("%s error reading Capacity %d\n",
|
|
__func__, ret);
|
|
|
|
p = of_get_property(np, "fuelgauge,cv_data", &len);
|
|
if (p) {
|
|
fuelgauge->cv_data = kzalloc(len, GFP_KERNEL);
|
|
fuelgauge->cv_data_length = len / sizeof(struct cv_slope);
|
|
pr_err("%s len: %ld, length: %d, %d\n",
|
|
__func__, sizeof(int) * len, len, fuelgauge->cv_data_length);
|
|
ret = of_property_read_u32_array(np, "fuelgauge,cv_data",
|
|
(u32 *)fuelgauge->cv_data, len/sizeof(u32));
|
|
|
|
if (ret) {
|
|
pr_err("%s failed to read fuelgauge->cv_data: %d\n",
|
|
__func__, ret);
|
|
kfree(fuelgauge->cv_data);
|
|
fuelgauge->cv_data = NULL;
|
|
}
|
|
} else {
|
|
pr_err("%s there is not cv_data\n", __func__);
|
|
}
|
|
|
|
|
|
#if defined(CONFIG_BATTERY_AGE_FORECAST)
|
|
ret = of_property_read_u32(np, "battery,full_condition_soc",
|
|
&fuelgauge->pdata->full_condition_soc);
|
|
if (ret) {
|
|
fuelgauge->pdata->full_condition_soc = 93;
|
|
pr_info("%s : Full condition soc is Empty\n", __func__);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* get battery_params node for reg init */
|
|
np = of_find_node_by_name(of_node_get(np), "battery_params");
|
|
if (np == NULL) {
|
|
PINFO("Cannot find child node \"battery_params\"\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* get battery_id */
|
|
if (of_property_read_u32(np, "battery,id", &battery_id) < 0)
|
|
PINFO("not battery,id property\n");
|
|
PINFO("battery id = %d\n", battery_id);
|
|
|
|
/* get battery_table */
|
|
for (i = DISCHARGE_TABLE; i < TABLE_MAX; i++) {
|
|
snprintf(prop_name, PROPERTY_NAME_SIZE,
|
|
"battery%d,%s%d", battery_id, "battery_table", i);
|
|
|
|
ret = of_property_read_u32_array(np, prop_name, table, 24);
|
|
if (ret < 0) {
|
|
PINFO("Can get prop %s (%d)\n", prop_name, ret);
|
|
}
|
|
for (j = 0; j <= FG_TABLE_LEN; j++) {
|
|
fuelgauge->info.battery_table[i][j] = table[j];
|
|
PINFO("%s = <table[%d][%d] 0x%x>\n", prop_name, i, j, table[j]);
|
|
}
|
|
}
|
|
|
|
snprintf(prop_name, PROPERTY_NAME_SIZE,
|
|
"battery%d,%s%d", battery_id, "battery_table", i);
|
|
|
|
ret = of_property_read_u32_array(np, prop_name, table, 16);
|
|
if (ret < 0) {
|
|
PINFO("Can get prop %s (%d)\n", prop_name, ret);
|
|
}
|
|
for (j = 0; j <= FG_ADD_TABLE_LEN; j++) {
|
|
fuelgauge->info.battery_table[i][j] = table[j];
|
|
PINFO("%s = <table[%d][%d] 0x%x>\n", prop_name, i, j, table[j]);
|
|
}
|
|
|
|
/* get rs_value */
|
|
for (i = 0; i < 7; i++) {
|
|
snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "rs_value");
|
|
ret = of_property_read_u32_array(np, prop_name, rs_value, 7);
|
|
if (ret < 0) {
|
|
PINFO("Can get prop %s (%d)\n", prop_name, ret);
|
|
}
|
|
fuelgauge->info.rs_value[i] = rs_value[i];
|
|
}
|
|
/*spare min max factor chg_factor dischg_factor manvalue*/
|
|
PINFO("%s = <0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x>\n", prop_name,
|
|
rs_value[0], rs_value[1], rs_value[2], rs_value[3], rs_value[4], rs_value[5], rs_value[6]);
|
|
|
|
/* battery_type */
|
|
snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "battery_type");
|
|
ret = of_property_read_u32_array(np, prop_name, battery_type, 2);
|
|
if (ret < 0)
|
|
PINFO("Can get prop %s (%d)\n", prop_name, ret);
|
|
fuelgauge->info.batt_v_max = battery_type[0];
|
|
fuelgauge->info.cap = battery_type[1];
|
|
fuelgauge->info.maxcap = battery_type[1];
|
|
|
|
PINFO("%s = <%d 0x%x>\n", prop_name,
|
|
fuelgauge->info.batt_v_max, fuelgauge->info.cap);
|
|
|
|
#ifdef ENABLE_BATT_LONG_LIFE
|
|
snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "v_max_table");
|
|
ret = of_property_read_u32_array(np, prop_name, v_max_table, fuelgauge->pdata->num_age_step);
|
|
|
|
if (ret < 0) {
|
|
PINFO("Can get prop %s (%d)\n", prop_name, ret);
|
|
|
|
for (i = 0; i < fuelgauge->pdata->num_age_step; i++) {
|
|
fuelgauge->info.v_max_table[i] = fuelgauge->info.battery_table[DISCHARGE_TABLE][FG_TABLE_LEN-1];
|
|
PINFO("%s = <v_max_table[%d] 0x%x>\n", prop_name, i, fuelgauge->info.v_max_table[i]);
|
|
}
|
|
} else {
|
|
for (i = 0; i < fuelgauge->pdata->num_age_step; i++) {
|
|
fuelgauge->info.v_max_table[i] = v_max_table[i];
|
|
PINFO("%s = <v_max_table[%d] 0x%x>\n", prop_name, i, fuelgauge->info.v_max_table[i]);
|
|
}
|
|
}
|
|
|
|
snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "q_max_table");
|
|
ret = of_property_read_u32_array(np, prop_name, q_max_table, fuelgauge->pdata->num_age_step);
|
|
|
|
if (ret < 0) {
|
|
PINFO("Can get prop %s (%d)\n", prop_name, ret);
|
|
|
|
for (i = 0; i < fuelgauge->pdata->num_age_step; i++) {
|
|
fuelgauge->info.q_max_table[i] = fuelgauge->info.cap;
|
|
PINFO("%s = <q_max_table[%d] 0x%x>\n", prop_name, i, fuelgauge->info.q_max_table[i]);
|
|
}
|
|
} else {
|
|
for (i = 0; i < fuelgauge->pdata->num_age_step; i++) {
|
|
fuelgauge->info.q_max_table[i] = q_max_table[i];
|
|
PINFO("%s = <q_max_table[%d] 0x%x>\n", prop_name, i, fuelgauge->info.q_max_table[i]);
|
|
}
|
|
}
|
|
fuelgauge->chg_full_soc = fuelgauge->pdata->age_data[0].full_condition_soc;
|
|
fuelgauge->info.v_max_now = fuelgauge->info.v_max_table[0];
|
|
fuelgauge->info.q_max_now = fuelgauge->info.q_max_table[0];
|
|
PINFO("%s = <v_max_now = 0x%x>, <q_max_now = 0x%x>, <chg_full_soc = %d>\n", prop_name,
|
|
fuelgauge->info.v_max_now, fuelgauge->info.q_max_now, fuelgauge->chg_full_soc);
|
|
#endif
|
|
|
|
/* V_ALARM */
|
|
snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "v_alarm");
|
|
ret = of_property_read_u32_array(np, prop_name, v_alarm, 2);
|
|
if (ret < 0)
|
|
PINFO("Can get prop %s (%d)\n", prop_name, ret);
|
|
fuelgauge->info.value_v_alarm = v_alarm[0];
|
|
fuelgauge->info.value_v_alarm_hys = v_alarm[1];
|
|
PINFO("%s = <%d %d>\n", prop_name, fuelgauge->info.value_v_alarm, fuelgauge->info.value_v_alarm_hys);
|
|
|
|
/* TOP OFF */
|
|
snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "topoff");
|
|
ret = of_property_read_u32_array(np, prop_name, &fuelgauge->info.top_off, 1);
|
|
if (ret < 0)
|
|
PINFO("Can get prop %s (%d)\n", prop_name, ret);
|
|
PINFO("%s = <%d>\n", prop_name, fuelgauge->info.top_off);
|
|
|
|
/* i CAL */
|
|
snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "i_cal");
|
|
ret = of_property_read_u32_array(np, prop_name, i_cal, 8);
|
|
if (ret < 0) {
|
|
PINFO("Can get prop %s (%d)\n", prop_name, ret);
|
|
} else {
|
|
fuelgauge->info.i_dp_default = i_cal[0];
|
|
fuelgauge->info.dp_i_off = i_cal[1];
|
|
fuelgauge->info.dp_i_pslo = i_cal[2];
|
|
fuelgauge->info.dp_i_nslo = i_cal[3];
|
|
fuelgauge->info.i_alg_default = i_cal[4];
|
|
fuelgauge->info.alg_i_off = i_cal[5];
|
|
fuelgauge->info.alg_i_pslo = i_cal[6];
|
|
fuelgauge->info.alg_i_nslo = i_cal[7];
|
|
}
|
|
PINFO("%s = dp : [%d]<0x%x 0x%x 0x%x> alg : [%d]<0x%x 0x%x 0x%x>\n", prop_name,
|
|
fuelgauge->info.i_dp_default, fuelgauge->info.dp_i_off, fuelgauge->info.dp_i_pslo, fuelgauge->info.dp_i_nslo
|
|
, fuelgauge->info.i_alg_default, fuelgauge->info.alg_i_off, fuelgauge->info.alg_i_pslo, fuelgauge->info.alg_i_nslo);
|
|
|
|
/* v CAL */
|
|
snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "v_cal");
|
|
ret = of_property_read_u32_array(np, prop_name, v_cal, 7);
|
|
if (ret < 0) {
|
|
PINFO("Can get prop %s (%d)\n", prop_name, ret);
|
|
} else {
|
|
fuelgauge->info.v_default = v_cal[0];
|
|
fuelgauge->info.v_off = v_cal[1];
|
|
fuelgauge->info.v_slo = v_cal[2];
|
|
fuelgauge->info.vt_default = v_cal[3];
|
|
fuelgauge->info.vtt = v_cal[4];
|
|
fuelgauge->info.ivt = v_cal[5];
|
|
fuelgauge->info.ivv = v_cal[6];
|
|
}
|
|
PINFO("%s = v : [%d]<0x%x 0x%x> vt : [%d]<0x%x 0x%x 0x%x>\n", prop_name,
|
|
fuelgauge->info.v_default, fuelgauge->info.v_off, fuelgauge->info.v_slo
|
|
, fuelgauge->info.vt_default, fuelgauge->info.vtt, fuelgauge->info.ivt, fuelgauge->info.ivv);
|
|
|
|
/* temp_std */
|
|
snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "temp_std");
|
|
ret = of_property_read_u32_array(np, prop_name, &fuelgauge->info.temp_std, 1);
|
|
if (ret < 0)
|
|
PINFO("Can get prop %s (%d)\n", prop_name, ret);
|
|
PINFO("%s = <%d>\n", prop_name, fuelgauge->info.temp_std);
|
|
|
|
/* tem poff level */
|
|
snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "tem_poff");
|
|
ret = of_property_read_u32_array(np, prop_name, set_temp_poff, 4);
|
|
if (ret < 0)
|
|
PINFO("Can get prop %s (%d)\n", prop_name, ret);
|
|
fuelgauge->info.n_tem_poff = set_temp_poff[0];
|
|
fuelgauge->info.n_tem_poff_offset = set_temp_poff[1];
|
|
fuelgauge->info.l_tem_poff = set_temp_poff[2];
|
|
fuelgauge->info.l_tem_poff_offset = set_temp_poff[3];
|
|
|
|
PINFO("%s = <%d, %d, %d, %d>\n",
|
|
prop_name,
|
|
fuelgauge->info.n_tem_poff, fuelgauge->info.n_tem_poff_offset,
|
|
fuelgauge->info.l_tem_poff, fuelgauge->info.l_tem_poff_offset);
|
|
|
|
/* aux_ctrl setting */
|
|
snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "aux_ctrl");
|
|
ret = of_property_read_u32_array(np, prop_name, aux_ctrl, 2);
|
|
if (ret < 0)
|
|
PINFO("Can get prop %s (%d)\n", prop_name, ret);
|
|
fuelgauge->info.aux_ctrl[0] = aux_ctrl[0];
|
|
fuelgauge->info.aux_ctrl[1] = aux_ctrl[1];
|
|
|
|
/* batt data version */
|
|
snprintf(prop_name, PROPERTY_NAME_SIZE, "battery%d,%s", battery_id, "data_ver");
|
|
ret = of_property_read_u32_array(np, prop_name, &fuelgauge->info.data_ver, 1);
|
|
if (ret < 0)
|
|
PINFO("Can get prop %s (%d)\n", prop_name, ret);
|
|
PINFO("%s = <%d>\n", prop_name, fuelgauge->info.data_ver);
|
|
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static struct device_attribute sm5714_attrs[] = {
|
|
sm5714_FG_ATTR(chip_id),
|
|
sm5714_FG_ATTR(data),
|
|
sm5714_FG_ATTR(data_1),
|
|
sm5714_FG_ATTR(data_sram),
|
|
sm5714_FG_ATTR(data_sram_1),
|
|
};
|
|
static int sm5714_fg_create_attrs(struct device *dev)
|
|
{
|
|
int i, rc;
|
|
|
|
for (i = 0; i < (int)ARRAY_SIZE(sm5714_attrs); i++) {
|
|
rc = device_create_file(dev, &sm5714_attrs[i]);
|
|
if (rc)
|
|
goto create_attrs_failed;
|
|
}
|
|
return rc;
|
|
create_attrs_failed:
|
|
dev_err(dev, "%s: failed (%d)\n", __func__, rc);
|
|
while (i--)
|
|
device_remove_file(dev, &sm5714_attrs[i]);
|
|
return rc;
|
|
}
|
|
ssize_t sm5714_fg_show_attrs(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct power_supply *psy = dev_get_drvdata(dev);
|
|
struct sm5714_fuelgauge_data *fuelgauge = power_supply_get_drvdata(psy);
|
|
const ptrdiff_t offset = attr - sm5714_attrs;
|
|
int i = 0;
|
|
u16 data;
|
|
u8 addr;
|
|
|
|
switch (offset) {
|
|
case CHIP_ID:
|
|
case DATA:
|
|
for (addr = 0; addr < (int)ARRAY_SIZE(sm5714_regs); addr++) {
|
|
data = -1;
|
|
data = sm5714_read_word(fuelgauge->i2c, sm5714_regs[addr]);
|
|
i += scnprintf(buf + i, PAGE_SIZE - i,
|
|
"0x%02X[0x%04X],", sm5714_regs[addr], data);
|
|
}
|
|
i += scnprintf(buf + i, PAGE_SIZE - i, "\n");
|
|
break;
|
|
case DATA_1:
|
|
data = sm5714_read_word(fuelgauge->i2c, fuelgauge->read_reg);
|
|
i += scnprintf(buf + i, PAGE_SIZE - i,
|
|
"0x%02X : 0x%04X\n", fuelgauge->read_reg, data);
|
|
break;
|
|
case DATA_SRAM:
|
|
for (addr = 0; addr < (int)ARRAY_SIZE(sm5714_srams); addr++) {
|
|
data = -1;
|
|
data = sm5714_fg_read_sram(fuelgauge, sm5714_srams[addr]);
|
|
i += scnprintf(buf + i, PAGE_SIZE - i,
|
|
"0x%02X[0x%04X],", sm5714_srams[addr], data);
|
|
}
|
|
i += scnprintf(buf + i, PAGE_SIZE - i, "\n");
|
|
break;
|
|
case DATA_SRAM_1:
|
|
data = sm5714_fg_read_sram(fuelgauge, fuelgauge->read_sram_reg);
|
|
i += scnprintf(buf + i, PAGE_SIZE - i,
|
|
"0x%02X : 0x%04X\n", fuelgauge->read_sram_reg, data);
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return i;
|
|
}
|
|
ssize_t sm5714_fg_store_attrs(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct power_supply *psy = dev_get_drvdata(dev);
|
|
struct sm5714_fuelgauge_data *fuelgauge = power_supply_get_drvdata(psy);
|
|
const ptrdiff_t offset = attr - sm5714_attrs;
|
|
int ret = 0;
|
|
int addr, data;
|
|
|
|
switch (offset) {
|
|
case CHIP_ID:
|
|
ret = count;
|
|
break;
|
|
case DATA:
|
|
if (sscanf(buf, "0x%8x 0x%8x", &addr, &data) == 2) {
|
|
if (sm5714_fg_find_addr(addr)) {
|
|
if (sm5714_write_word(fuelgauge->i2c, addr, data) < 0) {
|
|
dev_info(fuelgauge->dev,
|
|
"%s: addr: 0x%x write fail\n", __func__, addr);
|
|
}
|
|
} else {
|
|
dev_info(fuelgauge->dev,
|
|
"%s: addr: 0x%x is wrong\n", __func__, addr);
|
|
}
|
|
}
|
|
ret = count;
|
|
break;
|
|
case DATA_1:
|
|
if (sscanf(buf, "0x%8x", &addr) == 1)
|
|
fuelgauge->read_reg = addr;
|
|
ret = count;
|
|
break;
|
|
case DATA_SRAM:
|
|
if (sscanf(buf, "0x%8x 0x%8x", &addr, &data) == 2) {
|
|
if (sm5714_fg_find_sram_addr(addr)) {
|
|
if (sm5714_fg_write_sram(fuelgauge, addr, data) < 0) {
|
|
dev_info(fuelgauge->dev,
|
|
"%s: addr: 0x%x write fail\n", __func__, addr);
|
|
}
|
|
} else {
|
|
dev_info(fuelgauge->dev,
|
|
"%s: addr: 0x%x is wrong\n", __func__, addr);
|
|
}
|
|
}
|
|
ret = count;
|
|
break;
|
|
case DATA_SRAM_1:
|
|
if (sscanf(buf, "0x%8x", &addr) == 1)
|
|
fuelgauge->read_sram_reg = addr;
|
|
ret = count;
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
}
|
|
return ret;
|
|
}
|
|
static const struct power_supply_desc sm5714_fuelgauge_power_supply_desc = {
|
|
.name = "sm5714-fuelgauge",
|
|
.type = POWER_SUPPLY_TYPE_UNKNOWN,
|
|
.properties = sm5714_fuelgauge_props,
|
|
.num_properties = ARRAY_SIZE(sm5714_fuelgauge_props),
|
|
.get_property = sm5714_fg_get_property,
|
|
.set_property = sm5714_fg_set_property,
|
|
};
|
|
|
|
static int sm5714_fuelgauge_probe(struct platform_device *pdev)
|
|
{
|
|
struct sm5714_dev *sm5714 = dev_get_drvdata(pdev->dev.parent);
|
|
struct sm5714_platform_data *pdata = dev_get_platdata(sm5714->dev);
|
|
struct sm5714_fuelgauge_data *fuelgauge;
|
|
sm5714_fuelgauge_platform_data_t *fuelgauge_data;
|
|
/* struct power_supply_config fuelgauge_cfg = {}; */
|
|
struct power_supply_config psy_fg = {};
|
|
int ret = 0;
|
|
union power_supply_propval raw_soc_val;
|
|
#if defined(CONFIG_DISABLE_SAVE_CAPACITY_MAX)
|
|
u16 reg_data;
|
|
#endif
|
|
|
|
pr_info("%s: SM5714 Fuelgauge Driver Loading probe start\n", __func__);
|
|
|
|
fuelgauge = kzalloc(sizeof(*fuelgauge), GFP_KERNEL);
|
|
if (!fuelgauge)
|
|
return -ENOMEM;
|
|
|
|
fuelgauge_data = kzalloc(sizeof(sm5714_fuelgauge_platform_data_t), GFP_KERNEL);
|
|
if (!fuelgauge_data) {
|
|
ret = -ENOMEM;
|
|
goto err_free;
|
|
}
|
|
|
|
mutex_init(&fuelgauge->fg_lock);
|
|
|
|
fuelgauge->dev = &pdev->dev;
|
|
fuelgauge->pdata = fuelgauge_data;
|
|
fuelgauge->i2c = sm5714->fuelgauge;
|
|
/* fuelgauge->pmic = sm5714->i2c; */
|
|
fuelgauge->sm5714_pdata = pdata;
|
|
|
|
fuelgauge->pmic_rev = sm5714->pmic_rev;
|
|
fuelgauge->vender_id = sm5714->vender_id;
|
|
|
|
#if defined(ENABLE_BATT_LONG_LIFE)
|
|
temp_parse_dt(fuelgauge);
|
|
#endif
|
|
|
|
#if defined(CONFIG_OF)
|
|
fuelgauge->battery_data = kzalloc(sizeof(struct battery_data_t),
|
|
GFP_KERNEL);
|
|
if (!fuelgauge->battery_data) {
|
|
pr_err("Failed to allocate memory\n");
|
|
ret = -ENOMEM;
|
|
goto err_pdata_free;
|
|
}
|
|
ret = sm5714_fuelgauge_parse_dt(fuelgauge);
|
|
if (ret < 0) {
|
|
pr_err("%s not found charger dt! ret[%d]\n",
|
|
__func__, ret);
|
|
}
|
|
#endif
|
|
|
|
/* initialize value */
|
|
fuelgauge->isjigmoderealvbat = false;
|
|
|
|
platform_set_drvdata(pdev, fuelgauge);
|
|
|
|
(void) debugfs_create_file("sm5714-fuelgauge-regs",
|
|
S_IRUGO, NULL, (void *)fuelgauge, &sm5714_fuelgauge_debugfs_fops);
|
|
|
|
if (!sm5714_fg_init(fuelgauge, false)) {
|
|
pr_err("%s: Failed to Initialize Fuelgauge\n", __func__);
|
|
goto err_data_free;
|
|
}
|
|
|
|
fuelgauge->capacity_max = fuelgauge->pdata->capacity_max;
|
|
raw_soc_val.intval = sm5714_get_soc(fuelgauge);
|
|
|
|
#if defined(CONFIG_DISABLE_SAVE_CAPACITY_MAX)
|
|
pr_info("%s : CONFIG_DISABLE_SAVE_CAPACITY_MAX\n",
|
|
__func__);
|
|
#endif
|
|
|
|
if (raw_soc_val.intval > fuelgauge->capacity_max)
|
|
sm5714_fg_calculate_dynamic_scale(fuelgauge, 100);
|
|
|
|
/* SW/HW init code. SW/HW V Empty mode must be opposite ! */
|
|
fuelgauge->info.temperature = 300; /* default value */
|
|
pr_info("%s: SW/HW V empty init \n", __func__);
|
|
sm5714_fg_set_vempty(fuelgauge, VEMPTY_MODE_HW);
|
|
|
|
psy_fg.drv_data = fuelgauge;
|
|
psy_fg.supplied_to = sm5714_fg_supplied_to;
|
|
psy_fg.num_supplicants = ARRAY_SIZE(sm5714_fg_supplied_to),
|
|
|
|
fuelgauge->psy_fg = power_supply_register(&pdev->dev, &sm5714_fuelgauge_power_supply_desc, &psy_fg);
|
|
if (!fuelgauge->psy_fg) {
|
|
dev_err(&pdev->dev, "%s: failed to power supply fg register", __func__);
|
|
goto err_data_free;
|
|
}
|
|
/*
|
|
fuelgauge->psy_fg.desc->name = "sm5714-fuelgauge";
|
|
fuelgauge->psy_fg.desc->type = POWER_SUPPLY_TYPE_UNKNOWN;
|
|
fuelgauge->psy_fg.desc->get_property = sm5714_fg_get_property;
|
|
fuelgauge->psy_fg.desc->set_property = sm5714_fg_set_property;
|
|
fuelgauge->psy_fg.desc->properties = sm5714_fuelgauge_props;
|
|
fuelgauge->psy_fg.desc->num_properties = ARRAY_SIZE(sm5714_fuelgauge_props);
|
|
|
|
ret = power_supply_register(&pdev->dev, &fuelgauge->psy_fg);
|
|
|
|
if (ret) {
|
|
pr_err("%s: Failed to Register psy_fg\n", __func__);
|
|
goto err_data_free;
|
|
}
|
|
*/
|
|
fuelgauge->fg_irq = pdata->irq_base + SM5714_FG_IRQ_INT_LOW_VOLTAGE;
|
|
pr_info("[%s]IRQ_BASE(%d) FG_IRQ(%d)\n",
|
|
__func__, pdata->irq_base, fuelgauge->fg_irq);
|
|
|
|
fuelgauge->is_fuel_alerted = false;
|
|
if (fuelgauge->pdata->fuel_alert_soc >= 0) {
|
|
if (sm5714_fg_fuelalert_init(fuelgauge,
|
|
fuelgauge->pdata->fuel_alert_soc)) {
|
|
battery_wakeup_source_init(&pdev->dev, &fuelgauge->fuel_alert_ws, "fuel_alerted");
|
|
if (fuelgauge->fg_irq) {
|
|
INIT_DELAYED_WORK(&fuelgauge->isr_work, sm5714_fg_isr_work);
|
|
|
|
ret = request_threaded_irq(fuelgauge->fg_irq,
|
|
NULL, sm5714_fg_irq_thread,
|
|
0,
|
|
"fuelgauge-irq", fuelgauge);
|
|
if (ret) {
|
|
pr_err("%s: Failed to Request IRQ\n", __func__);
|
|
goto err_supply_unreg;
|
|
}
|
|
}
|
|
} else {
|
|
pr_err("%s: Failed to Initialize Fuel-alert\n",
|
|
__func__);
|
|
goto err_supply_unreg;
|
|
}
|
|
}
|
|
|
|
fuelgauge->sleep_initial_update_of_soc = false;
|
|
fuelgauge->initial_update_of_soc = true;
|
|
ret = sm5714_fg_create_attrs(&fuelgauge->psy_fg->dev);
|
|
if (ret) {
|
|
dev_err(sm5714->dev,
|
|
"%s : Failed to create_attrs\n", __func__);
|
|
}
|
|
|
|
pr_info("%s: SM5714 Fuelgauge Driver Loaded\n", __func__);
|
|
return 0;
|
|
|
|
err_supply_unreg:
|
|
power_supply_unregister(fuelgauge->psy_fg);
|
|
err_data_free:
|
|
#if defined(CONFIG_OF)
|
|
kfree(fuelgauge->battery_data);
|
|
#endif
|
|
err_pdata_free:
|
|
kfree(fuelgauge_data);
|
|
mutex_destroy(&fuelgauge->fg_lock);
|
|
err_free:
|
|
kfree(fuelgauge);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int sm5714_fuelgauge_remove(struct platform_device *pdev)
|
|
{
|
|
struct sm5714_fuelgauge_data *fuelgauge =
|
|
platform_get_drvdata(pdev);
|
|
|
|
if (fuelgauge->pdata->fuel_alert_soc >= 0)
|
|
wakeup_source_unregister(fuelgauge->fuel_alert_ws);
|
|
|
|
mutex_destroy(&fuelgauge->fg_lock);
|
|
|
|
kfree(fuelgauge);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sm5714_fuelgauge_suspend(struct device *dev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int sm5714_fuelgauge_resume(struct device *dev)
|
|
{
|
|
struct sm5714_fuelgauge_data *fuelgauge = dev_get_drvdata(dev);
|
|
|
|
fuelgauge->sleep_initial_update_of_soc = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void sm5714_fuelgauge_shutdown(struct platform_device *pdev)
|
|
{
|
|
struct sm5714_fuelgauge_data *fuelgauge = platform_get_drvdata(pdev);
|
|
|
|
pr_info("%s: ++\n", __func__);
|
|
|
|
if (fuelgauge->using_hw_vempty)
|
|
sm5714_fg_set_vempty(fuelgauge, false);
|
|
|
|
pr_info("%s: --\n", __func__);
|
|
}
|
|
|
|
static SIMPLE_DEV_PM_OPS(sm5714_fuelgauge_pm_ops, sm5714_fuelgauge_suspend,
|
|
sm5714_fuelgauge_resume);
|
|
|
|
static struct platform_driver sm5714_fuelgauge_driver = {
|
|
.driver = {
|
|
.name = "sm5714-fuelgauge",
|
|
.owner = THIS_MODULE,
|
|
#ifdef CONFIG_PM
|
|
.pm = &sm5714_fuelgauge_pm_ops,
|
|
#endif
|
|
},
|
|
.probe = sm5714_fuelgauge_probe,
|
|
.remove = sm5714_fuelgauge_remove,
|
|
.shutdown = sm5714_fuelgauge_shutdown,
|
|
};
|
|
|
|
static int __init sm5714_fuelgauge_init(void)
|
|
{
|
|
pr_info("%s: \n", __func__);
|
|
return platform_driver_register(&sm5714_fuelgauge_driver);
|
|
}
|
|
|
|
static void __exit sm5714_fuelgauge_exit(void)
|
|
{
|
|
platform_driver_unregister(&sm5714_fuelgauge_driver);
|
|
}
|
|
module_init(sm5714_fuelgauge_init);
|
|
module_exit(sm5714_fuelgauge_exit);
|
|
|
|
MODULE_DESCRIPTION("Samsung SM5714 Fuel Gauge Driver");
|
|
MODULE_AUTHOR("Samsung Electronics");
|
|
MODULE_LICENSE("GPL");
|
|
|