blob: b5868fecf3782d6d3c8ae012017300efd0db1120 [file] [log] [blame]
/*
* stc3117_battery.c
* fuel-gauge systems for lithium-ion (Li+) batteries
*
* Copyright (C) 2011 STMicroelectronics.
*
* 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.
* Revision:
* 2016.11.30 1.00a: Origin - 10 level dynamic early empty compensation
* 2016.12.13 1.00b: 27 level deec and dynamic entry point for deec.
* 2016.12.15 1.00c: Avoid SOC deep drop when heavy to light loading.
* 2016.12.27 1.01a: Fine tune each loading for deec and voltage aging feature.
*/
#define pr_fmt(fmt) "STC311x: %s: " fmt, __func__
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/power_supply.h>
#include <linux/stc3117_battery.h>
#include <linux/slab.h>
#include <linux/debugfs.h>
#define GG_VERSION "1.01a"
#define CONFIG_ENABLE_STC311X_DUMPREG
//#define STC3117_AGING_FEATURE
//#define STC3117_AGING_VOLTAGE_FEATURE
/*Function declaration*/
int STC31xx_SetPowerSavingMode(void);
int STC31xx_StopPowerSavingMode(void);
int STC31xx_AlarmSet(void);
int STC31xx_AlarmStop(void);
int STC31xx_AlarmGet(void);
int STC31xx_AlarmClear(void);
int STC31xx_AlarmSetVoltageThreshold(int VoltThresh);
int STC31xx_AlarmSetSOCThreshold(int SOCThresh);
int STC31xx_RelaxTmrSet(int CurrentThreshold);
/* ******************************************************************************** */
/* STC311x DEVICE SELECTION */
/* STC3117 version only */
/* -------------------------------------------------------------------------------- */
#define STC3117
#define BATD_UC8
/* ******************************************************************************** */
/* Private define ------------------------------------------------------------*/
/* ******************************************************************************** */
/* SPECIAL FUNCTIONS */
/* -------------------------------------------------------------------------------- */
/* */
/* define TEMPCOMP_SOC to enable SOC temperature compensation */
#define TEMPCOMP_SOC
/* ******************************************************************************** */
/* ******************************************************************************** */
/* INTERNAL PARAMETERS */
/* TO BE ADJUSTED ACCORDING TO BATTERY/APPLICATION CHARACTERISTICS */
/* -------------------------------------------------------------------------------- */
/* */
#define BATT_CHG_VOLTAGE 4350 /* min voltage at the end of the charge (mV) */
#define BATT_MIN_VOLTAGE 3600 /* nearly empty battery detection level (mV) */
#define MAX_HRSOC 51200 /* 100% in 1/512% units*/
#define MAX_SOC 1000 /* 100% in 0.1% units */
#define FIRST_EEC_SOC 50
#define SECOND_EEC_SOC 30
#define THIRD_EEC_SOC 10
/* */
#define CHG_MIN_CURRENT 90 /* min charge current in mA */
#define CHG_END_CURRENT 20 /* end charge current in mA */
#define CHG_NOT_CHARGE 20 /* current to be consider as not charge in mA */
#define APP_MIN_CURRENT (-5) /* minimum application current consumption in mA ( <0 !) */
#define OFFSET_CURRENT (-3)
#define APP_MIN_VOLTAGE 3500 /* application cut-off voltage */
#define APP_MIN_VOLTAGE_EMPTY 3000 /* to darin all battery capacity, so we lock batt level at 1% for 3.0V ~ 3.5V(APP_MIN_VOLTAGE) */
#if defined(STC3117_AGING_VOLTAGE_FEATURE)
#define AGING_CHECK_VOLTAGE 3571 /* aging check point voltage */
#endif /* STC3117_AGING_VOLTAGE_FEATURE */
#define TEMP_MIN_ADJ (-5) /* minimum temperature for gain adjustment */
#define VMTEMPTABLE { 85, 90, 100, 160, 320, 440, 840 } /* normalized VM_CNF at 60, 40, 25, 10, 0, -10 degreeC, -20 degreeC */
#define AVGFILTER 4 /* average filter constant */
/* #define CurrentFactor (24084/SENSERESISTOR) LSB=5.88uV/R= ~24084/R/4096 - convert to mA */
#define VoltageFactor 9011 /* LSB=2.20mV ~9011/4096 - convert to mV */
/* ******************************************************************************** */
/* Private define ------------------------------------------------------------*/
#define STC31xx_SLAVE_ADDRESS 0xE0 /* STC31xx 8-bit address byte */
/*Address of the STC311x register --------------------------------------------*/
#define STC311x_REG_MODE 0x00 /* Mode Register */
#define STC311x_REG_CTRL 0x01 /* Control and Status Register */
#define STC311x_REG_SOC 0x02 /* SOC Data (2 bytes) */
#define STC311x REG_COUNTER 0x04 /* Number of Conversion (2 bytes) */
#define STC311x_REG_CURRENT 0x06 /* Battery Current (2 bytes) */
#define STC311x_REG_VOLTAGE 0x08 /* Battery Voltage (2 bytes) */
#define STC311x_REG_TEMPERATURE 0x0A /* Temperature */
#ifdef STC3117
#define STC311x_REG_AVG_CURRENT 0x0B /* Battery Average Current (2 bytes) */
#endif
#define STC311x_REG_OCV 0x0D /* Battery OCV (2 bytes) */
#define STC311x_REG_CC_CNF 0x0F /* CC configuration (2 bytes) */
#define STC311x_REG_VM_CNF 0x11 /* VM configuration (2 bytes) */
#define STC311x_REG_ALARM_SOC 0x13 /* SOC alarm level */
#define STC311x_REG_ALARM_VOLTAGE 0x14 /* Low voltage alarm level */
#define STC311x_REG_CURRENT_THRES 0x15 /* Current threshold for relaxation */
#define STC311x_REG_CMONIT_COUNT 0x16 /* Current monitoring counter */
#define STC311x_REG_CMONIT_MAX 0x17 /* Current monitoring max count */
#define STC311x_REG_CC_ADJ 0x1B /* CC adjustement (2 bytes) */
#define STC311x_REG_VM_ADJ 0x1D /* VM adjustement (2 bytes) */
/*Bit mask definition*/
#define STC311x_VMODE 0x01 /* Voltage mode bit mask */
#define STC311x_ALM_ENA 0x08 /* Alarm enable bit mask */
#define STC311x_GG_RUN 0x10 /* Alarm enable bit mask */
#define STC311x_FORCE_CC 0x20 /* Force CC bit mask */
#define STC311x_FORCE_VM 0x40 /* Force VM bit mask */
#define STC311x_SOFTPOR 0x11 /* soft reset */
#define STC311x_BATD_PU 0x02 /* Enable internal Pull-Up on BATD bit mask */
#define STC311x_FORCE_CD 0x04 /* Force CD high bit mask */
#define STC311x_REG_ID 0x18 /* Chip ID (1 byte) */
#define STC3117_ID 0x16 /* STC3117 ID */
#define STC311x_REG_RAM 0x20 /* General Purpose RAM Registers */
#define RAM_SIZE 16 /* Total RAM size of STC311x in bytes */
#define STC311x_REG_OCVTAB 0x30
#define OCVTAB_SIZE 16 /* OCVTAB size of STC311x */
#define STC311x_REG_SOCTAB 0x50
#define SOCTAB_SIZE 16 /* SOCTAB size of STC311x */
#define VCOUNT 0 /* counter value for 1st current/temp measurements */
#define M_STAT 0x1010 /* GG_RUN & PORDET mask in STC311x_BattDataTypeDef status word */
//#define M_RST 0x1800 /* BATFAIL & PORDET mask */
#define M_RST 0x9800 /* UVLOD & BATFAIL & PORDET mask */
#define M_RUN 0x0010 /* GG_RUN mask in STC311x_BattDataTypeDef status word */
#define M_GGVM 0x0400 /* GG_VM mask */
#define M_BATFAIL 0x0800 /* BATFAIL mask*/
#define M_UVLOD 0x8000 /* UVLOD mask (STC3117 only) */
#define M_VMOD 0x0001 /* VMODE mask */
#define OK 0
/* Battery charge state definition for BattState */
#define BATT_CHARGING 3
#define BATT_ENDCHARG 2
#define BATT_FULCHARG 1
#define BATT_IDLE 0
#define BATT_DISCHARG (-1)
#define BATT_LOWBATT (-2)
/* STC311x RAM test word */
#define RAM_TSTWORD 0x53A9
/* Gas gauge states */
#define GG_INIT 'I'
#define GG_RUNNING 'R'
#define GG_POWERDN 'D'
#define VM_MODE 1
#define CC_MODE 0
/* gas gauge structure definition ------------------------------------*/
/* Private constants ---------------------------------------------------------*/
#define NTEMP 7
static const int TempTable[NTEMP] = {60, 40, 25, 10, 0, -10, -20} ; /* temperature table from 60 degreeC to -20 degreeC (descending order!) */
static const int DefVMTempTable[NTEMP] = VMTEMPTABLE;
/* Dynamic early empty 7% voltage mapping */
static int deec_7_voltage_lvl[] = {
3655, 3660, 3648, 3625, 3594, 3554, 3523, 3495, 3486,
3462, 3448, 3420, 3367, 3347, 3325, 3303, 3290, 3281,
3254, 3226, 3220, 3228, 3232, 3236, 3236, 3234, 3230,
};
/* Dynamic early empty 3% voltage mapping */
static int deec_3_voltage_lvl[] = {
3493, 3498, 3493, 3481, 3462, 3429, 3402, 3377, 3369,
3350, 3335, 3312, 3266, 3248, 3227, 3208, 3197, 3187,
3157, 3128, 3125, 3134, 3139, 3143, 3145, 3140, 3139,
};
/* Dynamic early empty 1% voltage mapping */
static int deec_1_voltage_lvl[] = {
3307, 3311, 3306, 3295, 3281, 3259, 3242, 3223, 3218,
3205, 3192, 3176, 3141, 3132, 3119, 3105, 3098, 3093,
3073, 3054, 3055, 3060, 3063, 3067, 3070, 3062, 3065,
};
/* Bypass 3 secs heavy loading drop */
static int three_secs_voltage_filter_tbl[] = {
0, 0, 0, 5, 11, 18, 24, 30, 33,
33, 39, 45, 50, 56, 61, 67, 57, 62,
67, 72, 77, 75, 65, 70, 65, 70, 62,
};
#if defined(STC3117_AGING_VOLTAGE_FEATURE)
static bool aging_trigger_flag = false;
/* Defined three percentage level at 3.571V with difference current loading*/
static int aging_comparsion_tbl[] = {
45, /* 4.5% when current <= 100mA*/
58, /* 5.8% when current > 100mA && <=150 */
83, /* 8.3% when current > 150mA && <= 200mA*/
116 /* 11.6% when current > 200mA && <= 250mA*/
};
#endif /* STC3117_AGING_VOLTAGE_FEATURE */
#if defined(STC3117_AGING_FEATURE)
static int aging_counter;
static int battery_capacity_aging_level;
#endif /* STC3117_AGING_FEATURE */
/* Private variables ---------------------------------------------------------*/
/* structure of the STC311x battery monitoring parameters */
typedef struct {
int Voltage; /* battery voltage in mV */
int Current; /* battery current in mA */
int Temperature; /* battery temperature in 0.1 degreeC */
int SOC; /* battery relative SOC (%) in 0.1% */
int OCV;
int AvgSOC;
int AvgCurrent;
int AvgVoltage;
int AvgTemperature;
int ChargeValue; /* remaining capacity in mAh */
int RemTime; /* battery remaining operating time during discharge (min) */
int State; /* charge (>0)/discharge(<0) state */
int CalStat; /* Internal status */
/* -- parameters -- */
int Vmode; /* 1=Voltage mode, 0=mixed mode */
int Alm_SOC; /* SOC alm level */
int Alm_Vbat; /* Vbat alm level */
int CC_cnf; /* nominal CC_cnf */
int VM_cnf; /* nominal VM cnf */
int Cnom; /* nominal capacity in mAh */
int Rsense; /* sense resistor */
int Rint; /* battery internal resistance */
int RelaxCurrent; /* current for relaxation (< C/20) */
int Adaptive; /* adaptive mode */
int CapDerating[7]; /* capacity derating in 0.1%, for temp = 60, 40, 25, 10, 0, -10, -20 degreeC */
int OCVValue[16]; /* OCV curve values */
//int SOCValue[16]; /* SOC curve values */
int ExternalTemperature;
int ForceExternalTemperature;
int Ropt;
int Var1;
} GasGauge_DataTypeDef;
/* structure of the STC311x battery monitoring data */
typedef struct {
/* STC311x data */
int STC_Status; /* status word */
int Vmode; /* 1=Voltage mode, 0=mixed mode */
int Voltage; /* voltage in mV */
int Current; /* current in mA */
int Temperature; /* temperature in 0.1 degreeC */
int HRSOC; /* uncompensated SOC in 1/512% */
int OCV; /* OCV in mV*/
int ConvCounter; /* convertion counter */
int RelaxTimer; /* current relax timer value */
int CC_adj; /* CC adj */
int VM_adj; /* VM adj */
/* results & internals */
int SOC; /* compensated SOC in 0.1% */
int AvgSOC; /* in 0.1% */
int AvgVoltage;
int AvgCurrent;
int AvgTemperature;
int AccSOC;
int AccVoltage;
int AccCurrent;
int AccTemperature;
int BattState;
int GG_Mode; /* 1=VM active, 0=CC active */
int LastTemperature;
int BattOnline; /* BATD */
int IDCode;
/* parameters */
int Alm_SOC; /* SOC alm level in % */
int Alm_Vbat; /* Vbat alm level in mV */
int CC_cnf; /* nominal CC_cnf */
int VM_cnf; /* nominal VM cnf */
int Cnom; /* nominal capacity is mAh */
int Rsense; /* sense resistor in milliOhms */
int Rint; /* internal resistance in milliOhms */
int CurrentFactor;
int CRateFactor;
int RelaxThreshold; /* current threshold for VM (mA) */
int VM_TempTable[NTEMP];
int CapacityDerating[NTEMP];
int OCVValue[OCVTAB_SIZE];
//unsigned char SOCValue[SOCTAB_SIZE];
int Ropt;
int Nropt;
int LastSOC;
} STC311x_BattDataTypeDef;
static STC311x_BattDataTypeDef BattData; /* STC311x data */
/* structure of the STC311x RAM registers for the Gas Gauge algorithm data */
static union {
unsigned char db[RAM_SIZE]; /* last byte holds the CRC */
struct {
short int TstWord; /* 0-1 */
short int HRSOC; /* 2-3 SOC backup */
short int CC_cnf; /* 4-5 current CC_cnf */
short int VM_cnf; /* 6-7 current VM_cnf */
char SOC; /* 8 SOC for trace (in %) */
char GG_Status; /* 9 */
/* bytes ..RAM_SIZE-2 are free, last byte RAM_SIZE-1 is the CRC */
} reg;
} GG_Ram;
int Capacity_Adjust;
extern bool chg_full_flag;
extern bool force_full_flag;
/* -------------------------------------------------------------------------------- */
/* INTERNAL ANDROID DRIVER PARAMETERS */
/* TO BE ADJUSTED ACCORDING TO BATTERY/APPLICATION CHARACTERISTICS */
/* -------------------------------------------------------------------------------- */
#define STC3100_BATTERY_FULL 95
#define STC311x_DELAY 1000
/* ******************************************************************************** */
static struct i2c_client *sav_client;
struct stc311x_chip {
struct i2c_client *client;
struct delayed_work work;
struct power_supply battery;
struct stc311x_platform_data *pdata;
struct dentry *debug_root;
u32 peek_poke_address;
/* State Of Connect */
int online;
/* battery SOC (capacity) */
int batt_soc;
/* battery SOC (capacity) - previous value */
int batt_soc_last;
/* battery voltage */
int batt_voltage;
/* Current */
int batt_current;
/* Temperature */
int batt_temp;
#if defined(STC3117_AGING_FEATURE)
/* Charge cycle */
int batt_charge_cycle;
#endif /* STC3117_AGING_FEATURE */
/* State Of Charge */
int status;
};
static int stc311x_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct stc311x_chip *chip = container_of(psy,
struct stc311x_chip, battery);
/* from power_supply.h:
* All voltages, currents, charges, energies, time and temperatures in uV,
* uA, uAh, uWh, seconds and tenths of degree Celsius unless otherwise
* stated. It's driver's job to convert its raw values to units in which
* this class operates.
*/
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
val->intval = chip->status;
break;
case POWER_SUPPLY_PROP_ONLINE:
val->intval = chip->online;
break;
#if defined(STC3117_AGING_FEATURE)
case POWER_SUPPLY_PROP_CYCLE_COUNT:
val->intval = chip->batt_charge_cycle;
break;
#endif /* STC3117_AGING_FEATURE */
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
val->intval = chip->batt_voltage * 1000; /* in uV */
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
val->intval = chip->batt_current * 1000 * -1; /* in uA */
break;
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = chip->batt_soc;
break;
case POWER_SUPPLY_PROP_TEMP:
val->intval = chip->batt_temp;
break;
default:
return -EINVAL;
}
return 0;
}
static void stc311x_get_version(struct i2c_client *client)
{
dev_info(&client->dev, "STC3117 Fuel-Gauge Ver %s\n", GG_VERSION);
}
static void stc311x_get_online(struct i2c_client *client)
{
struct stc311x_chip *chip = i2c_get_clientdata(client);
if (chip->pdata && chip->pdata->battery_online)
chip->online = chip->pdata->battery_online();
else
chip->online = BattData.BattOnline;
}
static void stc311x_get_status(struct i2c_client *client)
{
struct stc311x_chip *chip = i2c_get_clientdata(client);
if (!chip->pdata || !chip->pdata->charger_online ||
!chip->pdata->charger_enable) {
chip->status = POWER_SUPPLY_STATUS_UNKNOWN;
return;
}
if (chip->pdata->charger_online()) {
if (chip->pdata->charger_enable())
chip->status = POWER_SUPPLY_STATUS_CHARGING;
else
chip->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
} else {
chip->status = POWER_SUPPLY_STATUS_DISCHARGING;
}
if (chip->batt_soc > STC3100_BATTERY_FULL)
chip->status = POWER_SUPPLY_STATUS_FULL;
}
/* -------------------------------------------------------------------------- */
/* I2C interface */
/* -----------------------------------------------------------------
The following routines interface with the I2C primitives
I2C_Read(u8_I2C_address, u8_NumberOfBytes, u8_RegAddress, pu8_RxBuffer);
I2C_Write(u8_I2C_address, u8_NumberOfBytes, u8_RegAddress, pu8_TxBuffer);
note: here I2C_Address is the 8-bit address byte
----------------------------------------------------------------- */
#define NBRETRY 5
/*******************************************************************************
* Function Name : STC31xx_Write
* Description : utility function to write several bytes to STC311x registers
* Input : NumberOfBytes, RegAddress, TxBuffer
* Return : error status
* Note: Recommended implementation is to used I2C block write. If not available,
* STC311x registers can be written by 2-byte words (unless NumberOfBytes=1)
* or byte per byte.
*******************************************************************************/
static int STC31xx_Write(int length, int reg , unsigned char *values)
{
int ret;
ret = i2c_smbus_write_i2c_block_data(sav_client, reg, length, values);
if (ret < 0)
dev_err(&sav_client->dev, "%s: err %d\n", __func__, ret);
return ret;
}
/*******************************************************************************
* Function Name : STC31xx_Read
* Description : utility function to read several bytes from STC311x registers
* Input : NumberOfBytes, RegAddress, , RxBuffer
* Return : error status
* Note: Recommended implementation is to used I2C block read. If not available,
* STC311x registers can be read by 2-byte words (unless NumberOfBytes=1)
* Using byte per byte read is not recommended since it doesn't ensure register data integrity
*******************************************************************************/
static int STC31xx_Read(int length, int reg , unsigned char *values)
{
int ret;
ret = i2c_smbus_read_i2c_block_data(sav_client, reg, length, values);
if (ret < 0)
dev_err(&sav_client->dev, "%s: err %d\n", __func__, ret);
return ret;
}
/* ---- end of I2C primitive interface --------------------------------------------- */
/*******************************************************************************
* Function Name : STC31xx_ReadByte
* Description : utility function to read the value stored in one register
* Input : RegAddress: STC311x register,
* Return : 8-bit value, or 0 if error
*******************************************************************************/
static int STC31xx_ReadByte(int RegAddress)
{
unsigned char data[2];
int res;
res=STC31xx_Read(1, RegAddress, data);
if (res >= 0) {
/* no error */
return data[0];
}
else
return 0;
}
/*******************************************************************************
* Function Name : STC31xx_WriteByte
* Description : utility function to write a 8-bit value into a register
* Input : RegAddress: STC311x register, Value: 8-bit value to write
* Return : error status (OK, !OK)
*******************************************************************************/
static int STC31xx_WriteByte(int RegAddress, unsigned char Value)
{
int res;
unsigned char data[2];
data[0]= Value;
res = STC31xx_Write(1, RegAddress, data);
return(res);
}
/*******************************************************************************
* Function Name : STC31xx_ReadWord
* Description : utility function to read the value stored in one register pair
* Input : RegAddress: STC311x register,
* Return : 16-bit value, or 0 if error
*******************************************************************************/
static int STC31xx_ReadWord(int RegAddress)
{
unsigned char data[2];
int res;
res=STC31xx_Read(2, RegAddress, data);
if (res >= 0) {
return ((data[1] << 8) + data[0]);
}
else
return 0;
}
/*******************************************************************************
* Function Name : STC31xx_WriteWord
* Description : utility function to write a 16-bit value into a register pair
* Input : RegAddress: STC311x register, Value: 16-bit value to write
* Return : error status (OK, !OK)
*******************************************************************************/
static int STC31xx_WriteWord(int RegAddress, int Value)
{
int res;
unsigned char data[2];
data[0]= Value & 0xff;
data[1]= (Value>>8) & 0xff;
res = STC31xx_Write(2, RegAddress, data);
return(res);
}
/*******************************************************************************
* Function Name : conv
* Description : conversion utility
* convert a raw 16-bit value from STC311x registers into user units (mA, mAh, mV, degreeC)
* (optimized routine for efficient operation on 8-bit processors such as STM8)
* Input : value, factor
* Return : result = value * factor / 4096
*******************************************************************************/
static int conv(short value, unsigned short factor)
{
int v;
v= ( (long) value * factor ) >> 11;
v= (v+1)/2;
return (v);
}
/* ---- end of I2C R/W interface --------------------------------------------- */
#define LAST_CFG_REG 0x1E
static int show_cfg_regs(struct seq_file *m, void *data)
{
//struct stc311x_chip *chip = m->private;
u8 reg;
u8 addr;
for (addr = 0; addr <= LAST_CFG_REG; addr++) {
reg = STC31xx_ReadByte(addr);
seq_printf(m, "0x%02x = 0x%02x\n", addr, reg);
}
return 0;
}
static int cfg_debugfs_open(struct inode *inode, struct file *file)
{
struct stc311x_chip *chip = inode->i_private;
return single_open(file, show_cfg_regs, chip);
}
static const struct file_operations cfg_debugfs_ops = {
.owner = THIS_MODULE,
.open = cfg_debugfs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int show_ocvtab_regs(struct seq_file *m, void *data)
{
int i;
u16 ocv_mv;
for (i = 0 ; i < OCVTAB_SIZE; i++) {
ocv_mv = STC31xx_ReadWord(STC311x_REG_OCVTAB + (i * 2));
//ocv_mv = get_val(&reg[1]);
ocv_mv &= 0x3fff; /* mask unused bits */
if (ocv_mv >= 0x02000) ocv_mv -= 0x4000; /* convert to signed value */
ocv_mv = conv(ocv_mv, VoltageFactor);
ocv_mv = (ocv_mv + 2) / 4; /* divide by 4 with rounding */
seq_printf(m, "0x%02x, ocv=%hu\n",
STC311x_REG_OCVTAB + (i * 2), ocv_mv);
}
return 0;
}
static int ocv_debugfs_open(struct inode *inode, struct file *file)
{
struct stc311x_chip *chip = inode->i_private;
return single_open(file, show_ocvtab_regs, chip);
}
static const struct file_operations ocv_debugfs_ops = {
.owner = THIS_MODULE,
.open = ocv_debugfs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int show_soctab_regs(struct seq_file *m, void *data)
{
int i;
u8 reg;
for (i = 0 ; i < SOCTAB_SIZE; i++) {
reg = STC31xx_ReadByte(STC311x_REG_SOCTAB + i);
seq_printf(m, "0x%02x = 0x%02x(%3d%%)\n",
STC311x_REG_SOCTAB + i, reg, reg / 2);
}
return 0;
}
static int soc_debugfs_open(struct inode *inode, struct file *file)
{
struct stc311x_chip *chip = inode->i_private;
return single_open(file, show_soctab_regs, chip);
}
static const struct file_operations soc_debugfs_ops = {
.owner = THIS_MODULE,
.open = soc_debugfs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int show_ram_regs(struct seq_file *m, void *data)
{
u8 reg;
u8 addr;
for (addr = STC311x_REG_RAM; addr < STC311x_REG_RAM + RAM_SIZE; addr++) {
reg = STC31xx_ReadByte(addr);
seq_printf(m, "0x%02x = 0x%02x\n", addr, reg);
}
return 0;
}
static int ram_debugfs_open(struct inode *inode, struct file *file)
{
struct stc311x_chip *chip = inode->i_private;
return single_open(file, show_ram_regs, chip);
}
static const struct file_operations ram_debugfs_ops = {
.owner = THIS_MODULE,
.open = ram_debugfs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int get_reg(void *data, u64 *val)
{
struct stc311x_chip *chip = data;
int rc;
u8 temp;
rc = STC31xx_Read(1, chip->peek_poke_address, &temp);
if (rc < 0) {
pr_err("failed to read reg %x rc = %d\n",
chip->peek_poke_address, rc);
return -EAGAIN;
}
*val = temp;
return 0;
}
static int set_reg(void *data, u64 val)
{
struct stc311x_chip *chip = data;
int rc;
u8 temp;
temp = (u8)val;
rc = STC31xx_Write(1, chip->peek_poke_address, &temp);
if (rc < 0) {
pr_err("failed to read reg %x rc = %d\n",
chip->peek_poke_address, rc);
return -EAGAIN;
}
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(poke_poke_debug_ops, get_reg, set_reg, "0x%02llx\n");
static void create_debugfs_entries(struct stc311x_chip *chip)
{
struct dentry *ent;
chip->debug_root = debugfs_create_dir("stc311x", NULL);
if (!chip->debug_root) {
pr_err("failed to create debug dir\n");
return;
}
ent = debugfs_create_file("config_registers", S_IFREG | S_IRUGO,
chip->debug_root, chip, &cfg_debugfs_ops);
if (!ent)
pr_err("failed to create config_resgisters debug file\n");
ent = debugfs_create_file("ocv_tab", S_IFREG | S_IRUGO,
chip->debug_root, chip, &ocv_debugfs_ops);
if (!ent)
pr_err("failed to create ocv_tab debug file\n");
ent = debugfs_create_file("soc_tab", S_IFREG | S_IRUGO,
chip->debug_root, chip, &soc_debugfs_ops);
if (!ent)
pr_err("failed to create soc_tab debug file\n");
ent = debugfs_create_file("ram_dump", S_IFREG | S_IRUGO,
chip->debug_root, chip, &ram_debugfs_ops);
if (!ent)
pr_err("failed to create ram_dump debug file\n");
ent = debugfs_create_x32("address", S_IFREG | S_IWUSR | S_IRUGO,
chip->debug_root, &chip->peek_poke_address);
if (!ent)
pr_err("failed to create address debug file\n");
ent = debugfs_create_file("data", S_IFREG | S_IWUSR | S_IRUGO,
chip->debug_root, chip, &poke_poke_debug_ops);
if (!ent)
pr_err("failed to create data debug file\n");
#if defined(STC3117_AGING_FEATURE)
ent = debugfs_create_u32("cycle_counter", S_IFREG | S_IWUSR | S_IRUGO,
chip->debug_root, &aging_counter);
if (!ent)
pr_err("failed to create data debug file\n");
#endif /* STC3117_AGING_FEATURE */
}
/* -------------------------------------------------------------------------- */
/*******************************************************************************
* Function Name : STC311x_Status
* Description : Read the STC311x status
* Input : None
* Return : status word (REG_MODE / REG_CTRL), -1 if error
*******************************************************************************/
static int STC311x_Status(void)
{
int value;
/* first, check the presence of the STC311x by reading first byte of dev. ID */
BattData.IDCode = STC31xx_ReadByte(STC311x_REG_ID);
if (BattData.IDCode!= STC3117_ID) return (-1);
/* read REG_MODE and REG_CTRL */
value = STC31xx_ReadWord(STC311x_REG_MODE);
return (value);
}
/*******************************************************************************
* Function Name : STC311x_SetParam
* Description : initialize the STC311x parameters
* Input : rst: init algo param
* Return : 0
*******************************************************************************/
static void STC311x_SetParam(void)
{
int value;
int ii;
STC31xx_WriteByte(STC311x_REG_MODE,0x01); /* set GG_RUN=0 before changing algo parameters */
/* init OCV curve */
for (ii=0;ii<OCVTAB_SIZE;ii++)
if (BattData.OCVValue[ii]!=0) STC31xx_WriteWord(STC311x_REG_OCVTAB+ii*2, BattData.OCVValue[ii]*100/55);
//if (BattData.SOCValue[1]!=0) STC31xx_Write(SOCTAB_SIZE, STC311x_REG_SOCTAB, (unsigned char *) BattData.SOCValue);
/* set alm level if different from default */
if (BattData.Alm_SOC !=0 )
STC31xx_WriteByte(STC311x_REG_ALARM_SOC,BattData.Alm_SOC*2);
if (BattData.Alm_Vbat !=0 ) {
value= ((BattData.Alm_Vbat << 9) / VoltageFactor); /* LSB=8*2.44mV */
STC31xx_WriteByte(STC311x_REG_ALARM_VOLTAGE, value);
}
// Avery
pr_err("%s: BattData.CurrentFactor=%d", __func__, BattData.CurrentFactor);
/* relaxation timer */
if (BattData.RelaxThreshold !=0) {
value= ((BattData.RelaxThreshold << 9) / BattData.CurrentFactor); /* LSB=8*5.88uV/Rsense */
value = value & 0x7f;
STC31xx_WriteByte(STC311x_REG_CURRENT_THRES,value);
}
/* set parameters if different from default, only if a restart is done (battery change) */
if (GG_Ram.reg.CC_cnf !=0 ) STC31xx_WriteWord(STC311x_REG_CC_CNF,GG_Ram.reg.CC_cnf);
if (GG_Ram.reg.VM_cnf !=0 ) STC31xx_WriteWord(STC311x_REG_VM_CNF,GG_Ram.reg.VM_cnf);
//STC31xx_WriteByte(STC311x_REG_CTRL,0x03); /* clear PORDET, BATFAIL and ALARMs free ALM pin, reset conv counter */
STC31xx_WriteByte(STC311x_REG_CTRL,0x83); /* clear BATFAIL and ALARMs, free ALM pin, reset conv counter */
if (BattData.Vmode)
STC31xx_WriteByte(STC311x_REG_MODE,0x19); /* set GG_RUN=1, voltage mode, alm enabled */
else
STC31xx_WriteByte(STC311x_REG_MODE,0x18); /* set GG_RUN=1, mixed mode, alm enabled */
return;
}
/*******************************************************************************
* Function Name : STC311x_Startup
* Description : initialize and start the STC311x at application startup
* Input : None
* Return : 0 if ok, -1 if error
*******************************************************************************/
static int STC311x_Startup(void)
{
int res;
int ocv, curr;
/* check STC310x status */
res = STC311x_Status();
if (res<0) return(res);
/* read OCV */
ocv=STC31xx_ReadWord(STC311x_REG_OCV);
STC311x_SetParam(); /* set parameters */
/* with STC3117, it is possible here to read the current and compensate OCV: */
curr=STC31xx_ReadWord(STC311x_REG_CURRENT);
curr &= 0x3fff; /* mask unused bits */
if (curr>=0x2000) curr -= 0x4000; /* convert to signed value */
ocv = ocv - BattData.Rint * curr * 588 / BattData.Rsense / 55000 ;
/* rewrite ocv to start SOC with updated OCV curve */
STC31xx_WriteWord(STC311x_REG_OCV,ocv);
return(0);
}
/*******************************************************************************
* Function Name : STC311x_Restore
* Description : Restore STC311x state
* Input : None
* Return :
*******************************************************************************/
static int STC311x_Restore(void)
{
int res;
int ocv;
/* check STC310x status */
res = STC311x_Status();
if (res<0)
return(res);
/* read OCV */
ocv=STC31xx_ReadWord(STC311x_REG_OCV);
STC311x_SetParam(); /* set parameters */
#if 1
/* if restore from unexpected reset, restore SOC (system dependent) */
if (GG_Ram.reg.GG_Status == GG_RUNNING)
if (GG_Ram.reg.SOC != 0)
STC31xx_WriteWord(STC311x_REG_SOC,GG_Ram.reg.HRSOC); /* restore SOC */
#else
/* rewrite ocv to start SOC with updated OCV curve */
STC31xx_WriteWord(STC311x_REG_OCV,ocv);
#endif
return(0);
}
/*******************************************************************************
* Function Name : STC311x_Powerdown
* Description : stop the STC311x at application power down
* Input : None
* Return : error status (OK, !OK)
*******************************************************************************/
static int STC311x_Powerdown(void)
{
int res;
/* write 0x01 into the REG_CTRL to release IO0 pin open, */
STC31xx_WriteByte(STC311x_REG_CTRL, 0x01);
/* write 0 into the REG_MODE register to put the STC311x in standby mode */
res = STC31xx_WriteByte(STC311x_REG_MODE, 0);
if (res!= OK)
return (res);
return (OK);
}
/*******************************************************************************
* Function Name : STC311x_xxxx
* Description : misc STC311x utility functions
* Input : None
* Return : None
*******************************************************************************/
static void STC311x_Reset(void)
{
STC31xx_WriteByte(STC311x_REG_CTRL, STC311x_SOFTPOR); /* set soft POR */
}
static void STC311x_SetSOC(int SOC)
{
STC31xx_WriteWord(STC311x_REG_SOC,SOC);
}
#if 0
static void STC311x_ForceVM(void)
{
int value;
value=STC31xx_ReadByte(STC311x_REG_MODE);
STC31xx_WriteByte(STC311x_REG_MODE,value | STC311x_FORCE_VM); /* force VM mode */
}
#endif
static void STC311x_ForceCC(void)
{
int value;
value=STC31xx_ReadByte(STC311x_REG_MODE);
STC31xx_WriteByte(STC311x_REG_MODE,value | STC311x_FORCE_CC); /* force CC mode */
}
#if 0
static int STC311x_SaveCnf(void)
{
int reg_mode,value;
/* mode register*/
reg_mode = BattData.STC_Status & 0xff;
reg_mode &= ~STC311x_GG_RUN; /* set GG_RUN=0 before changing algo parameters */
STC31xx_WriteByte(STC311x_REG_MODE, reg_mode);
STC31xx_ReadByte(STC311x_REG_ID);
STC31xx_WriteWord(STC311x_REG_VM_CNF,GG_Ram.reg.VM_cnf);
value = STC31xx_ReadWord(STC311x_REG_SOC);
STC31xx_WriteWord(STC311x_REG_SOC,value);
STC31xx_WriteWord(STC311x_REG_CC_CNF,GG_Ram.reg.CC_cnf);
if (BattData.Vmode) {
STC31xx_WriteByte(STC311x_REG_MODE,0x19); /* set GG_RUN=1, voltage mode, alm enabled */
}
else {
STC31xx_WriteByte(STC311x_REG_MODE,0x18); /* set GG_RUN=1, mixed mode, alm enabled */
if (BattData.GG_Mode == CC_MODE)
STC31xx_WriteByte(STC311x_REG_MODE,0x38); /* force CC mode */
else
STC31xx_WriteByte(STC311x_REG_MODE,0x58); /* force VM mode */
}
return(0);
}
#endif
#if defined(STC3117_AGING_FEATURE) || defined(STC3117_AGING_VOLTAGE_FEATURE)
static int STC311x_SaveCCCnf(void)
{
int reg_mode;
/* mode register*/
reg_mode = BattData.STC_Status & 0xff;
reg_mode &= ~STC311x_GG_RUN; /* set GG_RUN=0 before changing algo parameters */
STC31xx_WriteByte(STC311x_REG_MODE, reg_mode);
STC31xx_ReadByte(STC311x_REG_ID);
STC31xx_WriteWord(STC311x_REG_CC_CNF,GG_Ram.reg.CC_cnf);
if (BattData.Vmode) {
STC31xx_WriteByte(STC311x_REG_MODE,0x19); /* set GG_RUN=1, voltage mode, alm enabled */
} else {
STC31xx_WriteByte(STC311x_REG_MODE,0x18); /* set GG_RUN=1, mixed mode, alm enabled */
if (BattData.GG_Mode == CC_MODE)
STC31xx_WriteByte(STC311x_REG_MODE,0x38); /* force CC mode */
else
STC31xx_WriteByte(STC311x_REG_MODE,0x58); /* force VM mode */
}
return(0);
}
#endif /* STC3117_AGING_FEATURE || STC3117_AGING_VOLTAGE_FEATURE */
static int STC311x_SaveVMCnf(void)
{
int reg_mode;
/* mode register*/
reg_mode = BattData.STC_Status & 0xff;
reg_mode &= ~STC311x_GG_RUN; /* set GG_RUN=0 before changing algo parameters */
STC31xx_WriteByte(STC311x_REG_MODE, reg_mode);
STC31xx_ReadByte(STC311x_REG_ID);
STC31xx_WriteWord(STC311x_REG_VM_CNF,GG_Ram.reg.VM_cnf);
if (BattData.Vmode) {
STC31xx_WriteByte(STC311x_REG_MODE,0x19); /* set GG_RUN=1, voltage mode, alm enabled */
} else {
STC31xx_WriteByte(STC311x_REG_MODE,0x18); /* set GG_RUN=1, mixed mode, alm enabled */
if (BattData.GG_Mode == CC_MODE)
STC31xx_WriteByte(STC311x_REG_MODE,0x38); /* force CC mode */
else
STC31xx_WriteByte(STC311x_REG_MODE,0x58); /* force VM mode */
}
return(0);
}
/*******************************************************************************
* Function Name : STC311x_ReadBatteryData
* Description : utility function to read the battery data from STC311x
* to be called every 5s or so
* Input : ref to BattData structure
* Return : error status (OK, !OK)
*******************************************************************************/
static int STC311x_ReadBatteryData(STC311x_BattDataTypeDef *BattData)
{
unsigned char data[16];
int res;
int value;
res = STC311x_Status();
if (res < 0)
return(res); /* return if I2C error or STC3117 not responding */
/* STC311x status */
BattData->STC_Status = res;
if (BattData->STC_Status & M_GGVM)
BattData->GG_Mode = VM_MODE; /* VM active */
else
BattData->GG_Mode = CC_MODE; /* CC active */
/* read STC3117 registers 0 to 14 */
res = STC31xx_Read(15, 0, data);
if (res < 0)
return(res); /* read failed */
/* fill the battery status data */
/* SOC */
value = (data[3]<<8) + data[2];
BattData->HRSOC = value; /* result in 1/512% */
/* conversion counter */
value = (data[5]<<8) + data[4];
BattData->ConvCounter = value;
/* current */
value = (data[7]<<8) + data[6];
value &= 0x3fff; /* mask unused bits */
if (value >= 0x2000)
value -= 0x4000; /* convert to signed value */
BattData->Current = conv(value, BattData->CurrentFactor); /* result in mA */
BattData->Current += OFFSET_CURRENT; /* result in mA */
/* voltage */
value = (data[9]<<8) + data[8];
value &= 0x0fff; /* mask unused bits */
if (value >= 0x0800)
value -= 0x1000; /* convert to signed value */
value = conv(value,VoltageFactor); /* result in mV */
BattData->Voltage = value; /* result in mV */
/* temperature */
value = data[10];
if (value >= 0x80)
value -= 0x100; /* convert to signed value */
BattData->Temperature = value * 10; /* result in 0.1 degreeC */
/* Avg current */
value = (data[12]<<8) + data[11];
if (value >= 0x8000)
value -= 0x10000; /* convert to signed value */
if (BattData->Vmode == 0) {
value = conv(value, BattData->CurrentFactor);
value = value / 4; /* divide by 4 */
}
else {
value = conv(value, BattData->CRateFactor);
}
BattData->AvgCurrent = value; /* result in mA */
/* OCV */
value = (data[14]<<8) + data[13];
value &= 0x3fff; /* mask unused bits */
if (value >= 0x02000)
value -= 0x4000; /* convert to signed value */
value = conv(value,VoltageFactor);
value = (value+2) / 4; /* divide by 4 with rounding */
BattData->OCV = value; /* result in mV */
/* read STC3117 registers CC & VM adj */
res = STC31xx_Read(4, STC311x_REG_CC_ADJ, data);
if (res < 0)
return(res); /* read failed */
/* CC & VM adjustment counters */
value = (data[1]<<8) + data[0];
if (value >= 0x8000)
value -= 0x10000; /* convert to signed value */
BattData->CC_adj = value; /* in 1/512% */
value = (data[3]<<8) + data[2];
if (value >= 0x8000)
value -= 0x10000; /* convert to signed value */
BattData->VM_adj = value; /* in 1/512% */
/* relax counter */
res = STC31xx_Read(1, STC311x_REG_CMONIT_COUNT, data);
if (res<0)
return(res); /* read failed */
BattData->RelaxTimer = data[0];
return(OK);
}
/*******************************************************************************
* Function Name : STC311x_ReadRamData
* Description : utility function to read the RAM data from STC311x
* Input : ref to RAM data array
* Return : error status (OK, !OK)
*******************************************************************************/
static int STC311x_ReadRamData(unsigned char *RamData)
{
return(STC31xx_Read(RAM_SIZE, STC311x_REG_RAM, RamData));
}
/*******************************************************************************
* Function Name : STC311x_WriteRamData
* Description : utility function to write the RAM data into STC311x
* Input : ref to RAM data array
* Return : error status (OK, !OK)
*******************************************************************************/
static int STC311x_WriteRamData(unsigned char *RamData)
{
return(STC31xx_Write(RAM_SIZE, STC311x_REG_RAM, RamData));
}
/*******************************************************************************
* Function Name : Interpolate
* Description : interpolate a Y value from a X value and X, Y tables (n points)
* Input : x
* Return : y
*******************************************************************************/
static int interpolate(int x, int n, int const *tabx, int const *taby )
{
int index;
int y;
if (x >= tabx[0])
y = taby[0];
else if (x <= tabx[n-1])
y = taby[n-1];
else {
/* find interval */
for (index= 1;index<n;index++)
if (x > tabx[index]) break;
/* interpolate */
y = (taby[index-1] - taby[index]) * (x - tabx[index]) * 2 / (tabx[index-1] - tabx[index]);
y = (y+1) / 2;
y += taby[index];
}
return y;
}
/*******************************************************************************
* Function Name : calcCRC8
* Description : calculate the CRC8
* Input : data: pointer to byte array, n: number of vytes
* Return : CRC calue
*******************************************************************************/
static int calcCRC8(unsigned char *data, int n)
{
int crc = 0; /* initial value */
int i, j;
for (i=0;i<n;i++) {
crc ^= data[i];
for (j=0;j<8;j++) {
crc <<= 1;
if (crc & 0x100)
crc ^= 7;
}
}
return(crc & 255);
}
/*******************************************************************************
* Function Name : UpdateRamCrc
* Description : calculate the RAM CRC
* Input : none
* Return : CRC value
*******************************************************************************/
static int UpdateRamCrc(void)
{
int res;
res = calcCRC8(GG_Ram.db,RAM_SIZE-1);
GG_Ram.db[RAM_SIZE-1] = res; /* last byte holds the CRC */
return(res);
}
/*******************************************************************************
* Function Name : Init_RAM
* Description : Init the STC311x RAM registers with valid test word and CRC
* Input : none
* Return : none
*******************************************************************************/
static void Init_RAM(void)
{
int index;
for (index=0;index<RAM_SIZE;index++)
GG_Ram.db[index] = 0;
GG_Ram.reg.TstWord = RAM_TSTWORD; /* id. to check RAM integrity */
GG_Ram.reg.CC_cnf = BattData.CC_cnf;
GG_Ram.reg.VM_cnf = BattData.VM_cnf;
/* update the crc */
UpdateRamCrc();
}
/* compensate SOC with temperature, SOC in 0.1% units */
static int CompensateSOC(int value, int temp)
{
int r, v;
r = 0;
#ifdef TEMPCOMP_SOC
r = interpolate(temp/10,NTEMP,TempTable,BattData.CapacityDerating); /* for APP_TYP_CURRENT */
#endif
v = (long) (value-r) * MAX_SOC * 2 / (MAX_SOC-r); /* compensate */
v = (v+1)/2; /* rounding */
if (v < 0) v = 0;
if (v > MAX_SOC) v = MAX_SOC;
return(v);
}
/*******************************************************************************
* Function Name : MM_FSM
* Description : process the Gas Gauge state machine in mixed mode
* Input : BattData
* Return :
* Affect : Global Gas Gauge data
*******************************************************************************/
static void MM_FSM(void)
{
switch (BattData.BattState) {
case BATT_CHARGING:
if (BattData.AvgCurrent < CHG_MIN_CURRENT)
BattData.BattState = BATT_ENDCHARG; /* end of charge */
break;
case BATT_ENDCHARG: /* end of charge state. check if fully charged or charge interrupted */
if ( BattData.Current > CHG_MIN_CURRENT )
BattData.BattState = BATT_CHARGING;
else if (BattData.AvgCurrent < CHG_END_CURRENT )
BattData.BattState = BATT_IDLE; /* charge interrupted */
else if ( (BattData.Current > CHG_END_CURRENT ) && ( BattData.Voltage > BATT_CHG_VOLTAGE ) )
BattData.BattState = BATT_FULCHARG; /* end of charge */
break;
case BATT_FULCHARG: /* full charge state. wait for actual end of charge current */
if ( (BattData.Current > CHG_MIN_CURRENT))
BattData.BattState = BATT_CHARGING; /* charge again */
else if ( BattData.AvgCurrent < CHG_END_CURRENT ) {
if ( BattData.AvgVoltage > BATT_CHG_VOLTAGE ) {
/* end of charge detected */
STC311x_SetSOC(MAX_HRSOC);
BattData.SOC=MAX_SOC; /* 100% */
}
BattData.BattState = BATT_IDLE; /* end of charge cycle */
}
break;
case BATT_IDLE: /* no charging, no discharging */
if (BattData.Current > CHG_END_CURRENT) {
BattData.BattState = BATT_CHARGING; /* charging again */
}
else if (BattData.Current < APP_MIN_CURRENT)
BattData.BattState = BATT_DISCHARG; /* discharging again */
break;
case BATT_DISCHARG:
if (BattData.Current > APP_MIN_CURRENT)
BattData.BattState = BATT_IDLE;
else if (BattData.AvgVoltage < BATT_MIN_VOLTAGE)
BattData.BattState = BATT_LOWBATT;
break;
case BATT_LOWBATT: /* battery nearly empty... */
if ( BattData.AvgVoltage > (BATT_MIN_VOLTAGE+50) )
BattData.BattState = BATT_IDLE; /* idle */
else
break;
default:
BattData.BattState = BATT_IDLE; /* idle */
} /* end switch */
}
static void CompensateVM(int temp)
{
int r;
#ifdef TEMPCOMP_SOC
r=interpolate(temp/10,NTEMP,TempTable,BattData.VM_TempTable);
GG_Ram.reg.VM_cnf = (BattData.VM_cnf * r) / 100;
STC311x_SaveVMCnf(); /* save new VM cnf values to STC311x */
#endif
}
/*******************************************************************************
* Function Name : VM_FSM
* Description : process the Gas Gauge machine in voltage mode
* Input : BattData
* Return :
* Affect : Global Gas Gauge data
*******************************************************************************/
static void VM_FSM(void)
{
#define DELTA_TEMP 30 /* 3 degreeC */
/* in voltage mode, monitor temperature to compensate voltage mode gain */
if ( ( BattData.AvgTemperature > (BattData.LastTemperature+DELTA_TEMP)) ||
( BattData.AvgTemperature < (BattData.LastTemperature-DELTA_TEMP)) ) {
BattData.LastTemperature = BattData.AvgTemperature;
CompensateVM(BattData.AvgTemperature);
}
}
#if defined(STC3117_AGING_VOLTAGE_FEATURE)
static void CompensateVolCCCnf(int cc_cnf_offset)
{
pr_err("[Alan] 1. BattData.CC_cnf=%d\n", BattData.CC_cnf);
pr_err("[Alan] 1. GG_Ram.reg.CC_cnf=%d\n", GG_Ram.reg.CC_cnf);
pr_err("[Alan] aging trigger, update the cc_cnf %d + (%d)\n", GG_Ram.reg.CC_cnf, cc_cnf_offset);
GG_Ram.reg.CC_cnf += cc_cnf_offset;
STC311x_SaveCCCnf(); /* save new CC cnf values to STC311x */
pr_err("[Alan] 2. BattData.CC_cnf=%d\n", BattData.CC_cnf);
pr_err("[Alan] 2. GG_Ram.reg.CC_cnf=%d\n", GG_Ram.reg.CC_cnf);
}
#endif /* STC3117_AGING_VOLTAGE_FEATURE */
#if defined(STC3117_AGING_FEATURE)
static void CompensateCCCnf(int battery_capacity_aging_level)
{
int aging_cc_cnf = 0;
pr_err("[Alan] 1. BattData.CC_cnf=%d\n", BattData.CC_cnf);
pr_err("[Alan] 1. GG_Ram.reg.CC_cnf=%d\n", GG_Ram.reg.CC_cnf);
aging_cc_cnf = (BattData.CC_cnf * battery_capacity_aging_level) / 100;
//GG_Ram.reg.CC_cnf = (BattData.CC_cnf * battery_capacity_aging_level) / 100;
if (GG_Ram.reg.CC_cnf != aging_cc_cnf) {
pr_err("[Alan] aging trigger, update the cc_cnf from %d to %d\n", GG_Ram.reg.CC_cnf, aging_cc_cnf);
GG_Ram.reg.CC_cnf = aging_cc_cnf;
STC311x_SaveCCCnf(); /* save new CC cnf values to STC311x */
} else {
pr_err("[Alan] cc_cnf is the same value, no need to update\n");
}
pr_err("[Alan] 2. BattData.CC_cnf=%d\n", BattData.CC_cnf);
pr_err("[Alan] 2. GG_Ram.reg.CC_cnf=%d\n", GG_Ram.reg.CC_cnf);
}
#endif /* STC3117_AGING_FEATURE */
/*******************************************************************************
* Function Name : Reset_FSM_GG
* Description : reset the gas gauge state machine and flags
* Input : None
* Return : None
*******************************************************************************/
static void Reset_FSM_GG(void)
{
BattData.BattState = BATT_IDLE;
}
/* -------------------- Algo functions ------------------------------------------- */
/* Disable SOC_correction because accurecy issue */
/* #define OG2 */
void SOC_correction (GasGauge_DataTypeDef *GG)
{
#ifdef OG2
int Var1 = 0;
int Var2,Var3,Var4;
int SOCopt;
#define CURRENT_TH (GG->Cnom/10)
#define GAIN 10
#define A_Var3 500
#define VAR1MAX 64
#define VAR2MAX 128
#define VAR4MAX 128
if (BattData.SOC>800) Var3 = 600;
else if (BattData.SOC>500) Var3 = 400;
else if (BattData.SOC>250) Var3 = 200;
else if (BattData.SOC>100) Var3 = 300;
else Var3 = 400;
Var1 = 256*BattData.AvgCurrent*A_Var3/Var3/CURRENT_TH;
Var1 = 32768 * GAIN / (256+Var1*Var1/256) / 10;
Var1 = (Var1+1)/2;
if (Var1 == 0) Var1 = 1;
if (Var1 >= VAR1MAX) Var1 = VAR1MAX - 1;
GG->Var1 = Var1;
Var4 = BattData.CC_adj - BattData.VM_adj;
if (BattData.GG_Mode == CC_MODE)
SOCopt = BattData.HRSOC + Var1 * Var4 / 64;
else
SOCopt = BattData.HRSOC - BattData.CC_adj + Var1 * Var4 / 64;
Var2 = BattData.Nropt;
if ( (BattData.AvgCurrent < -CURRENT_TH) || (BattData.AvgCurrent > CURRENT_TH) ) {
if (Var2<VAR2MAX) Var2++;
BattData.Ropt = BattData.Ropt + ( 1000 * (BattData.Voltage-BattData.OCV) / BattData.AvgCurrent - BattData.Ropt / Var2);
BattData.Nropt = Var2;
}
if (Var2>0)
GG->Ropt = BattData.Ropt / Var2;
else
GG->Ropt = 0; // not available
if (SOCopt <= 0 )
SOCopt = 0;
if (SOCopt >= MAX_HRSOC)
SOCopt = MAX_HRSOC;
BattData.SOC = (SOCopt*10+256)/512;
if ( (Var4<(-VAR4MAX)) || (Var4>=VAR4MAX) ) {
// rewrite SOCopt into STC311x and clear acc registers
STC311x_SetSOC(SOCopt);
}
#endif
}
/* --------------------------------------------------------------------------------------------- */
/* -------------------- firmware interface functions ------------------------------------------- */
/*******************************************************************************
* Function Name : GasGauge_Start
* Description : Start the Gas Gauge system
* Input : algo parameters in GG structure
* Return : 0 is ok, -1 if STC310x not found or I2C error
* Affect : global STC310x data and gas gauge variables
*******************************************************************************/
int GasGauge_Start(GasGauge_DataTypeDef *GG)
{
int res, i;
BattData.Cnom = GG->Cnom;
BattData.Rsense = GG->Rsense;
BattData.Rint = GG->Rint;
BattData.Vmode = GG->Vmode;
BattData.CC_cnf = GG->CC_cnf;
BattData.VM_cnf = GG->VM_cnf;
BattData.Alm_SOC = GG-> Alm_SOC;
BattData.Alm_Vbat = GG->Alm_Vbat;
BattData.RelaxThreshold = GG->RelaxCurrent;
/* Init averaging */
BattData.AvgVoltage = 0;
BattData.AvgCurrent = 0;
BattData.AvgTemperature = 0;
BattData.AvgSOC = 0; /* in 0.1% unit */
BattData.AccVoltage = 0;
BattData.AccCurrent = 0;
BattData.AccTemperature = 0;
BattData.AccSOC = 0;
// BATD
BattData.BattOnline = 1;
/* LastSOC init */
BattData.LastSOC = -1;
if (BattData.Rsense == 0) BattData.Rsense = 10; /* default value in case, to avoid divide by 0 */
BattData.CurrentFactor = 24084 / BattData.Rsense; /* LSB=5.88uV/R= ~24084/R/4096 - convert to mA */
BattData.CRateFactor = 36 * BattData.Cnom; /* LSB=0.008789.Cnom= 36*Cnom/4096 - convert to mA */
if (BattData.CC_cnf == 0) BattData.CC_cnf = 395; /* default values */
if (BattData.VM_cnf == 0) BattData.VM_cnf = 321;
for (i=0;i<NTEMP;i++)
BattData.CapacityDerating[i] = GG->CapDerating[i];
for (i=0;i<OCVTAB_SIZE;i++) {
BattData.OCVValue[i] = GG->OCVValue[i];
//BattData.SOCValue[i] = GG->SOCValue[i];
}
for (i=0;i<NTEMP;i++)
BattData.VM_TempTable[i] = DefVMTempTable[i];
BattData.Ropt = 0;
BattData.Nropt = 0;
/* check RAM valid */
STC311x_ReadRamData(GG_Ram.db);
if ( (GG_Ram.reg.TstWord != RAM_TSTWORD) || (calcCRC8(GG_Ram.db,RAM_SIZE) != 0) ) {
/* RAM invalid */
Init_RAM();
res=STC311x_Startup(); /* return -1 if I2C error or STC3117 not present */
}
else {
/* check STC3117 status */
if ((STC311x_Status() & M_RST) != 0 ) {
res=STC311x_Startup(); /* return -1 if I2C error or STC3117 not present */
}
else {
res=STC311x_Restore(); /* recover from last SOC */
}
}
GG_Ram.reg.GG_Status = GG_INIT;
/* update the crc */
UpdateRamCrc();
STC311x_WriteRamData(GG_Ram.db);
Reset_FSM_GG();
return(res); /* return -1 if I2C error or STC3117 not present */
}
/*******************************************************************************
Restart sequence:
Usage:
call GasGaugeReset()
powerdown everything
wait 500ms
call GasGaugeStart(GG)
continue
*******************************************************************************/
/*******************************************************************************
* Function Name : GasGauge_Reset
* Description : Reset the Gas Gauge system
* Input : None
* Return : 0 is ok, -1 if I2C error
*******************************************************************************/
void GasGauge_Reset(void)
{
GG_Ram.reg.TstWord = 0; /* reset RAM */
GG_Ram.reg.GG_Status = 0;
STC311x_WriteRamData(GG_Ram.db);
STC311x_Reset();
}
/*******************************************************************************
* Function Name : GasGauge_Stop
* Description : Stop the Gas Gauge system
* Input : None
* Return : 0 is ok, -1 if I2C error
*******************************************************************************/
int GasGauge_Stop(void)
{
int res;
STC311x_ReadRamData(GG_Ram.db);
GG_Ram.reg.GG_Status = GG_POWERDN;
/* update the crc */
UpdateRamCrc();
STC311x_WriteRamData(GG_Ram.db);
res=STC311x_Powerdown();
if (res != 0) return (-1); /* error */
return(0);
}
#ifdef CONFIG_ENABLE_STC311X_DUMPREG
/* Reg info:
* data[0]: Mode reg
* data[1]: Control reg
* data[2-3]: SOC reg
* data[4-5]: Counter reg
* data[6-7]: Current reg
* data[8-9]: Voltage reg
* data[10]: Temperature reg
* data[11-12]: Avg. Temperature reg
* data[13-14]: OCV reg
* data[22]: CMONIT count
* data[23]: CMONIT max
* data[27-28]: CC_ADJ
* data[29-30]: VM_ADJ
*/
#define get_val(x) (((*x) << 8) | *(x - 1))
static void show_cfg_regs_debug(STC311x_BattDataTypeDef *BattData)
{
unsigned char data[32];
int res;
int value;
res = STC311x_Status();
if (res < 0) {
/* return if I2C error or STC3117 not responding */
pr_err("I2C error\n");
return;
}
/* read STC3117 registers 0 to 14 */
res = STC31xx_Read(31, 0, data);
if (res < 0) {
/* return if STC3117 not responding */
pr_err("Read regs failed, res=%d\n", res);
return;
}
pr_debug("REG[00] MODE = 0x%x\n", data[0]);
pr_debug("REG[01] CTRL = 0x%x, run_mode = %s\n", data[1],
(data[1] & STC311x_VMODE ? VM_MODE : CC_MODE) ? "VM_MODE" : "CC_MODE");
/* SOC */
pr_debug("REG[02-03] SoC = %d, %d\n", get_val(&data[3]), DIV_ROUND_CLOSEST(get_val(&data[3]), 512));
/* Counter */
pr_debug("REG[04-05] COUNTER = %d\n", get_val(&data[5]));
/* current */
value = get_val(&data[7]);
value &= 0x3fff; /* mask unused bits */
if (value >= 0x2000) value -= 0x4000; /* convert to signed value */
pr_debug("REG[06-07] CURRENT = %d mA\n", conv(value, BattData->CurrentFactor));
/* voltage */
value = get_val(&data[9]);
value &= 0x0fff; /* mask unused bits */
if (value >= 0x0800) value -= 0x1000; /* convert to signed value */
value = conv(value,VoltageFactor); /* result in mV */
pr_debug("REG[08-09] VOLTAGE = %d mV\n", value);
/* temperature */
value = data[10];
if (value >= 0x80) value -= 0x100; /* convert to signed value */
pr_debug("REG[10] TEMP = %d degC\n", value);
/* Avg current */
value = get_val(&data[12]);
if (value >= 0x8000) value -= 0x10000; /* convert to signed value */
if (BattData->Vmode == 0) {
value = conv(value, BattData->CurrentFactor);
value = value / 4; /* divide by 4 */
}
else {
value = conv(value, BattData->CRateFactor);
}
pr_debug("REG[11-12] AVG CURRENT = %d mA\n", value);
/* OCV */
value = get_val(&data[14]);
value &= 0x3fff; /* mask unused bits */
if (value >= 0x02000) value -= 0x4000; /* convert to signed value */
value = conv(value,VoltageFactor);
value = (value+2) / 4; /* divide by 4 with rounding */
pr_debug("REG[13-14] OCV = %d mV\n", value);
/* CC_CNF */
pr_debug("REG[15-16] CC_CNF = %d\n", get_val(&data[16]));
/* VM_CNF */
pr_debug("REG[17-18] VM_CNF = %d\n", get_val(&data[18]));
/* Current threshold */
pr_debug("REG[21] CURRENT_THRES = %d\n", data[21]);
/* CMONIT count */
//chip->cmonit_max = reg[1];
pr_debug("REG[22] CMONIT_COUNT = %d\n", data[22]);
/* CMONIT max */
//chip->cmonit_max = reg[1];
pr_debug("REG[23] CMONIT_MAX = %d\n", data[23]);
/* cc & vm adjustment counters */
value = get_val(&data[28]);
if (value >= 0x8000) value -= 0x10000; /* convert to signed value */
pr_debug("REG[27-28] CC_ADJ = %d\n", value);
value = get_val(&data[30]);
if (value >= 0x8000) value -= 0x10000; /* convert to signed value */
pr_debug("REG[29-30] VM_ADJ = %d\n", value);
}
static void show_soc_ocvtab_regs_debug(void)
{
int rc, i;
int ocv_mv;
pr_debug("============SOC OCV tab ============\n");
for (i = 0 ; i < OCVTAB_SIZE; i++) {
ocv_mv = STC31xx_ReadWord(STC311x_REG_OCVTAB + (i * 2));
rc = STC31xx_ReadByte(STC311x_REG_SOCTAB + i);
ocv_mv &= 0x3fff; /* mask unused bits */
if (ocv_mv >= 0x02000) ocv_mv -= 0x4000; /* convert to signed value */
ocv_mv = conv(ocv_mv, VoltageFactor);
ocv_mv = (ocv_mv + 2) / 4; /* divide by 4 with rounding */
pr_debug("0x%02x = 0x%02x(%3d%%), 0x%02x ocv=%hu mV\n",
STC311x_REG_SOCTAB + i, rc, rc / 2,
STC311x_REG_OCVTAB + (i * 2), ocv_mv);
}
return;
}
static void show_ram_regs_debug(void)
{
int rc;
u8 addr;
pr_debug("============ RAM registers ============\n");
for (addr = STC311x_REG_RAM; addr < STC311x_REG_RAM + RAM_SIZE; addr++) {
rc = STC31xx_ReadByte(addr);
pr_debug("0x%02x = 0x%02x\n", addr, rc);
}
return;
}
#endif /* CONFIG_ENABLE_STC311X_DUMPREG */
#if defined(STC3117_AGING_VOLTAGE_FEATURE)
static void voltage_aging_compensation(int SOC, int avg_current) {
int level = 0;
int aging_comparsion_array_num = 0;
pr_debug("Voltage aging compensation, avg_current=%d\n", avg_current);
if (avg_current >= -100 && avg_current < 0) {
level = 0;
} else if (avg_current >= -150 && avg_current < -100) {
level = 1;
} else if (avg_current >= -200 && avg_current < -150) {
level = 2;
} else if (avg_current >= -250 && avg_current < -200) {
level = 3;
} else {
pr_err("Other case, no need to aging\n");
return;
}
aging_comparsion_array_num = sizeof(aging_comparsion_tbl) / sizeof(aging_comparsion_tbl[0]);
pr_debug("Level %d aging compensation\n", level);
if ((level < 0) | (level >= aging_comparsion_array_num)) {
pr_err("Aging level out of range\n");
return;
}
if (SOC > aging_comparsion_tbl[level]+20) {
CompensateVolCCCnf(-1);
} else if (SOC < aging_comparsion_tbl[level]-20) {
CompensateVolCCCnf(1);
} else {
pr_err("Voltage aging in range, no need to do compensation\n");
return;
}
}
#endif /* STC3117_AGING_VOLTAGE_FEATURE */
int Dynamic_Early_Empty(int SOC, int voltage, int current_vol, int avg_current) {
int rc = 0;
int level = 0;
int deec_3_array_num = 0;
int deec_1_array_num = 0;
pr_debug("Dynamic early empty compensation, SOC=%d, voltage=%d, avg_current=%d\n", SOC, voltage, avg_current);
if (avg_current >= -15 && avg_current < 0 && voltage < deec_7_voltage_lvl[0]) {
level = 0;
} else if (avg_current >= -30 && avg_current < -15 && voltage < deec_7_voltage_lvl[1]) {
level = 1;
} else if (avg_current >= -50 && avg_current < -30 && voltage < deec_7_voltage_lvl[2]) {
level = 2;
} else if (avg_current >= -100 && avg_current < -50 && voltage < deec_7_voltage_lvl[3]) {
level = 3;
} else if (avg_current >= -150 && avg_current < -100 && voltage < deec_7_voltage_lvl[4]) {
level = 4;
} else if (avg_current >= -200 && avg_current < -150 && voltage < deec_7_voltage_lvl[5]) {
level = 5;
} else if (avg_current >= -250 && avg_current < -200 && voltage < deec_7_voltage_lvl[6]) {
level = 6;
} else if (avg_current >= -300 && avg_current < -250 && voltage < deec_7_voltage_lvl[7]) {
level = 7;
} else if (avg_current >= -325 && avg_current < -300 && voltage < deec_7_voltage_lvl[8]) {
level = 8;
} else if (avg_current >= -375 && avg_current < -325 && voltage < deec_7_voltage_lvl[9]) {
level = 9;
} else if (avg_current >= -425 && avg_current < -375 && voltage < deec_7_voltage_lvl[10]) {
level = 10;
} else if (avg_current >= -475 && avg_current < -425 && voltage < deec_7_voltage_lvl[11]) {
level = 11;
} else if (avg_current >= -525 && avg_current < -475 && voltage < deec_7_voltage_lvl[12]) {
level = 12;
} else if (avg_current >= -575 && avg_current < -525 && voltage < deec_7_voltage_lvl[13]) {
level = 13;
} else if (avg_current >= -625 && avg_current < -575 && voltage < deec_7_voltage_lvl[14]) {
level = 14;
} else if (avg_current >= -675 && avg_current < -625 && voltage < deec_7_voltage_lvl[15]) {
level = 15;
} else if (avg_current >= -725 && avg_current < -675 && voltage < deec_7_voltage_lvl[16]) {
level = 16;
} else if (avg_current >= -775 && avg_current < -725 && voltage < deec_7_voltage_lvl[17]) {
level = 17;
} else if (avg_current >= -825 && avg_current < -775 && voltage < deec_7_voltage_lvl[18]) {
level = 18;
} else if (avg_current >= -875 && avg_current < -825 && voltage < deec_7_voltage_lvl[19]) {
level = 19;
} else if (avg_current >= -925 && avg_current < -875 && voltage < deec_7_voltage_lvl[20]) {
level = 20;
} else if (avg_current >= -975 && avg_current < -925 && voltage < deec_7_voltage_lvl[21]) {
level = 21;
} else if (avg_current >= -1025 && avg_current < -975 && voltage < deec_7_voltage_lvl[22]) {
level = 22;
} else if (avg_current >= -1075 && avg_current < -1025 && voltage < deec_7_voltage_lvl[23]) {
level = 23;
} else if (avg_current >= -1125 && avg_current < -1075 && voltage < deec_7_voltage_lvl[24]) {
level = 24;
} else if (avg_current >= -1175 && avg_current < -1125 && voltage < deec_7_voltage_lvl[25]) {
level = 25;
} else if (avg_current < -1175 && voltage < deec_7_voltage_lvl[26]) {
level = 26;
} else {
pr_err("Don't do dynamic early empty compensation\n");
rc = SOC;
return rc;
}
pr_debug("Case %d compensation\n", level);
// Dynamic early empty range check
deec_3_array_num = sizeof(deec_3_voltage_lvl) / sizeof(deec_3_voltage_lvl[0]);
deec_1_array_num = sizeof(deec_1_voltage_lvl) / sizeof(deec_1_voltage_lvl[0]);
if ((level < 0) | (level >= deec_3_array_num) | (level >= deec_1_array_num)) {
pr_err("Level out of range\n");
return SOC;
}
if (voltage > deec_3_voltage_lvl[level]) {
if (SOC < SECOND_EEC_SOC) {
pr_err("SOC less 3 percentage, keep current SOC\n");
return SOC;
}
pr_debug("Case %d-1 compensation\n", level);
rc = SECOND_EEC_SOC + (SOC - SECOND_EEC_SOC) * (voltage - deec_3_voltage_lvl[level]) / (deec_7_voltage_lvl[level] - deec_3_voltage_lvl[level]);
} else if (voltage > deec_1_voltage_lvl[level] && voltage <= deec_3_voltage_lvl[level]) {
pr_debug("Case %d-2 compensation\n", level);
rc = THIRD_EEC_SOC + (SECOND_EEC_SOC - THIRD_EEC_SOC) * (voltage - deec_1_voltage_lvl[level]) / (deec_3_voltage_lvl[level] - deec_1_voltage_lvl[level]);
} else {
pr_debug("Case %d-3 compensation\n", level);
rc = THIRD_EEC_SOC;
}
/* if current voltage higher than average voltage, means transfer to light loading. *
* Meanwhile the SOC drop exceed 5% is abnormal case because the Avg vol and current *
* decouple trend. */
if (current_vol > (voltage + 20) && (SOC - rc) > 50) {
pr_err("DEEC result exceed 5 percent and current voltage(%d) great than avg voltage(%d) 20mV up, then bypass this DEEC data. SOC=%d rc=%d\n",
current_vol, voltage, SOC, rc);
rc = SOC;
}
/* Use 3 secs voltage offset to bypass the heavy loading. */
if (level >= 3 && current_vol < voltage - three_secs_voltage_filter_tbl[level] ) {
pr_err("Current voltage(%d) less than avg voltage(%d) (%d)mV up, then hold real SOC. SOC=%d rc=%d\n",
current_vol, voltage, three_secs_voltage_filter_tbl[level], SOC, rc);
rc = SOC;
}
return rc;
}
/*******************************************************************************
* Function Name : GasGauge_Task
* Description : Periodic Gas Gauge task, to be called e.g. every 5 sec.
* Input : pointer to gas gauge data structure
* Return : 1 if data available, 0 if no data, -1 if error
* Affect : global STC310x data and gas gauge variables
*******************************************************************************/
int GasGauge_Task(GasGauge_DataTypeDef *GG)
{
int res, value;
BattData.Cnom = GG->Cnom;
BattData.Rsense = GG->Rsense;
BattData.Vmode = GG->Vmode;
BattData.Rint = GG->Rint;
BattData.CC_cnf = GG->CC_cnf;
BattData.VM_cnf = GG->VM_cnf;
BattData.Alm_SOC = GG-> Alm_SOC;
BattData.Alm_Vbat = GG->Alm_Vbat;
BattData.RelaxThreshold = GG->RelaxCurrent;
res=STC311x_ReadBatteryData(&BattData); /* read battery data into global variables */
if (res != 0) return(-1); /* abort in case of I2C failure */
#ifdef CONFIG_ENABLE_STC311X_DUMPREG
pr_debug("===== Before task =====\n");
show_cfg_regs_debug(&BattData);
show_soc_ocvtab_regs_debug();
show_ram_regs_debug();
#endif /* CONFIG_ENABLE_STC311X_DUMPREG */
/* check if RAM data is ok (battery has not been changed) */
STC311x_ReadRamData(GG_Ram.db);
if ( (GG_Ram.reg.TstWord!= RAM_TSTWORD) || (calcCRC8(GG_Ram.db,RAM_SIZE)!=0) ) {
/* if RAM non ok, reset it and set init state */
Init_RAM();
GG_Ram.reg.GG_Status = GG_INIT;
}
/* Check battery presence */
if ((BattData.STC_Status & M_BATFAIL) != 0) {
BattData.BattOnline = 0;
}
/* check STC3117 status */
#ifdef BATD_UC8
/* check STC3117 status */
if ((BattData.STC_Status & (M_BATFAIL | M_UVLOD)) != 0) {
/* BATD or UVLO detected */
if(BattData.ConvCounter > 0) {
GG->Voltage=BattData.Voltage;
GG->SOC=(BattData.HRSOC*10+256)/512;
pr_err("BATD or UVLO, voltage:%d, SOC:%d\n", GG->Voltage, GG->SOC);
}
/* BATD or UVLO detected */
GasGauge_Reset();
return (-1);
}
#endif
if ((BattData.STC_Status & M_RUN) == 0) {
/* if not running, restore STC3117 */
STC311x_Restore();
GG_Ram.reg.GG_Status = GG_INIT;
}
BattData.SOC = (BattData.HRSOC*10+256)/512; /* in 0.1% unit */
//Force an external temperature
if(GG->ForceExternalTemperature == 1)
BattData.Temperature = GG->ExternalTemperature;
/* check INIT state */
if (GG_Ram.reg.GG_Status == GG_INIT) {
/* INIT state, wait for current & temperature value available: */
if (BattData.ConvCounter>VCOUNT) {
/* update VM_cnf */
CompensateVM(BattData.Temperature);
BattData.LastTemperature=BattData.Temperature;
/* Init averaging */
BattData.AvgVoltage = BattData.Voltage;
BattData.AvgCurrent = BattData.Current;
BattData.AvgTemperature = BattData.Temperature;
BattData.AvgSOC = CompensateSOC(BattData.SOC,BattData.Temperature); /* in 0.1% unit */
BattData.AccVoltage = BattData.AvgVoltage*AVGFILTER;
BattData.AccCurrent = BattData.AvgCurrent*AVGFILTER;
BattData.AccTemperature = BattData.AvgTemperature*AVGFILTER;
BattData.AccSOC = BattData.AvgSOC*AVGFILTER;
GG_Ram.reg.GG_Status = GG_RUNNING;
}
}
if (GG_Ram.reg.GG_Status != GG_RUNNING) {
GG->SOC = CompensateSOC(BattData.SOC,250);
GG->Voltage=BattData.Voltage;
GG->OCV = BattData.OCV;
GG->Current=0;
GG->RemTime = -1; /* means no estimated time available */
GG->Temperature=250;
} else {
//Check battery presence
if ((BattData.STC_Status & M_BATFAIL) == 0) {
BattData.BattOnline = 1;
}
SOC_correction (GG);
#if defined(STC3117_AGING_FEATURE)
CompensateCCCnf(battery_capacity_aging_level);
#endif /* STC3117_AGING_FEATURE */
/* SOC derating with temperature */
BattData.SOC = CompensateSOC(BattData.SOC,BattData.Temperature);
//early empty compensation
value=BattData.AvgVoltage;
if (BattData.Voltage < value) value = BattData.Voltage;
#if defined(STC3117_AGING_VOLTAGE_FEATURE)
pr_debug("[Alan] aging voltage enter check:%d, aging_trigger_flag:%d, GG_Ram.reg.CC_cnf=%d\n", value, aging_trigger_flag, GG_Ram.reg.CC_cnf);
if (value <= (AGING_CHECK_VOLTAGE + 5) && value >= (AGING_CHECK_VOLTAGE - 5)) {
pr_err("[Alan] aging enter check:%d\n", aging_trigger_flag);
if (aging_trigger_flag) {
//do aging comarsion
voltage_aging_compensation(BattData.AvgCurrent, BattData.SOC);
//if (BattData.AvgCurrent < 0 && BattData.AvgCurrent >= -50)
aging_trigger_flag = false;
}
}
#endif /* STC3117_AGING_VOLTAGE_FEATURE */
if (value < (APP_MIN_VOLTAGE + 160)) {
// Voltage drop below 3000 mV, then 0% and shutdown
if (value < APP_MIN_VOLTAGE_EMPTY) {
BattData.SOC=0;
//hardcode HRSOC to gauge reg.
STC311x_SetSOC(0);
BattData.LastSOC = BattData.SOC;
} else {
//Legacy early empty compensation
//BattData.SOC = BattData.SOC * (value - APP_MIN_VOLTAGE) / 100;
/* Use current loading choose voltage baseline */
BattData.SOC = Dynamic_Early_Empty(BattData.SOC, value, BattData.Voltage, BattData.AvgCurrent);
//Avoid SOC rebounce
if (BattData.LastSOC == -1 || BattData.SOC < BattData.LastSOC) {
BattData.LastSOC = BattData.SOC;
} else {
pr_err("SoC rebound occur, BattData.SOC=%d, BattData.LastSOC=%d\n",
BattData.SOC, BattData.LastSOC);
BattData.SOC = BattData.LastSOC;
}
}
} else {
if (BattData.LastSOC != -1) {
pr_err("Recovery BattData.LastSOC to -1.\n");
BattData.LastSOC = -1;
}
}
BattData.AccVoltage += (BattData.Voltage - BattData.AvgVoltage);
BattData.AccCurrent += (BattData.Current - BattData.AvgCurrent);
BattData.AccTemperature += (BattData.Temperature - BattData.AvgTemperature);
BattData.AccSOC += (BattData.SOC - BattData.AvgSOC);
BattData.AvgVoltage = (BattData.AccVoltage+AVGFILTER/2)/AVGFILTER;
BattData.AvgTemperature = (BattData.AccTemperature+AVGFILTER/2)/AVGFILTER;
BattData.AvgSOC = (BattData.AccSOC+AVGFILTER/2)/AVGFILTER;
/* ---------- process the Gas Gauge algorithm -------- */
if (BattData.Vmode)
VM_FSM(); /* in voltage mode */
else
MM_FSM(); /* in mixed mode */
#ifdef DEBUG_SOC
pr_err("HRSOC=%d, SOC=%d, chg_full_flag=%d, avg_curr=%d\n", BattData.HRSOC, BattData.SOC, chg_full_flag, BattData.AvgCurrent);
#endif
if (BattData.Vmode==0) {
// Lately fully compensation
// toDO: 98_to_100_no_current.txt, no charging and soc goes to 100 even hrsoc is
// [ 736.722530] STC311x: GasGauge_Task: SMB348: HRSOC=50448, SOC=1000, chg_full_flag=0, avg=5
if (force_full_flag) {
BattData.SOC = MAX_SOC;
STC311x_SetSOC(MAX_HRSOC);
force_full_flag = false;
#if defined(STC3117_AGING_VOLTAGE_FEATURE)
pr_debug("[Alan] Full Charge, aging_trigger_flag set true\n");
aging_trigger_flag = true;
#endif /* STC3117_AGING_VOLTAGE_FEATURE */
#ifdef DEBUG_SOC
pr_err("Lately fully_100: HRSOC=%d, SOC=%d, chg_full_flag=%d\n", BattData.HRSOC, BattData.SOC, chg_full_flag);
#endif
} else if (BattData.AvgCurrent > 50 && BattData.SOC >= 990 && BattData.SOC < 999) {
BattData.SOC = 990;
STC311x_SetSOC(99*512);
#ifdef DEBUG_SOC
pr_err("Lately fully_99: HRSOC=%d, SOC=%d, chg_full_flag=%d\n", BattData.HRSOC, BattData.SOC, chg_full_flag);
#endif
}
// Lately empty compensation
if (BattData.AvgCurrent < 0 && BattData.SOC < 10 && BattData.Voltage > (APP_MIN_VOLTAGE_EMPTY)) {
BattData.SOC = 10;
STC311x_SetSOC(1*512);
}
#if defined(STC3117_AGING_VOLTAGE_FEATURE)
if (BattData.AvgCurrent > CHG_NOT_CHARGE && BattData.SOC <= 980) {
pr_debug("[Alan] Charging now, reset aging_trigger_flag\n");
aging_trigger_flag = false;
}
#endif /* STC3117_AGING_VOLTAGE_FEATURE */
}
if (BattData.HRSOC > MAX_HRSOC) {
BattData.SOC = MAX_SOC;
STC311x_SetSOC(MAX_HRSOC);
}
#ifdef DEBUG_SOC
pr_err("report HRSOC=%d, SOC=%d\n", BattData.HRSOC, BattData.SOC);
#endif
/* -------- APPLICATION RESULTS ------------ */
/* fill gas gauge data with battery data */
GG->Voltage=BattData.Voltage;
GG->Current=BattData.Current;
GG->Temperature=BattData.Temperature;
GG->SOC = BattData.SOC;
GG->OCV = BattData.OCV;
GG->AvgVoltage = BattData.AvgVoltage;
GG->AvgCurrent = BattData.AvgCurrent;
GG->AvgTemperature = BattData.AvgTemperature;
GG->AvgSOC = BattData.AvgSOC;
if (BattData.Vmode) {
/* no current value in voltage mode */
GG->Current = 0;
GG->AvgCurrent = 0;
}
GG->ChargeValue = (long) BattData.Cnom * BattData.AvgSOC / MAX_SOC;
if (GG->AvgCurrent<APP_MIN_CURRENT) {
GG->State=BATT_DISCHARG;
value = GG->ChargeValue * 60 / (-GG->AvgCurrent); /* in minutes */
if (value<0) value=0;
GG->RemTime = value;
} else {
GG->RemTime = -1; /* means no estimated time available */
if (GG->AvgCurrent>CHG_END_CURRENT)
GG->State=BATT_CHARGING;
else
GG->State=BATT_IDLE;
}
}
/* save SOC */
GG_Ram.reg.HRSOC = BattData.HRSOC;
GG_Ram.reg.SOC = (GG->SOC+5)/10; /* trace SOC in % */
UpdateRamCrc();
STC311x_WriteRamData(GG_Ram.db);
#ifdef CONFIG_ENABLE_STC311X_DUMPREG
pr_debug("===== After task =====\n");
show_cfg_regs_debug(&BattData);
show_soc_ocvtab_regs_debug();
show_ram_regs_debug();
#endif /* CONFIG_ENABLE_STC311X_DUMPREG */
if (GG_Ram.reg.GG_Status==GG_RUNNING)
return(1);
else
return(0); /* only SOC, OCV and voltage are valid */
}
/*******************************************************************************
* Function Name : STC31xx_SetPowerSavingMode
* Description : Set the power saving mode
* Input : None
* Return : error status (OK, !OK)
*******************************************************************************/
int STC31xx_SetPowerSavingMode(void)
{
int res;
/* Read the mode register*/
res = STC31xx_ReadByte(STC311x_REG_MODE);
/* Set the VMODE bit to 1 */
res = STC31xx_WriteByte(STC311x_REG_MODE, (res | STC311x_VMODE));
if (res != OK) return (res);
return (OK);
}
/*******************************************************************************
* Function Name : STC31xx_StopPowerSavingMode
* Description : Stop the power saving mode
* Input : None
* Return : error status (OK, !OK)
*******************************************************************************/
int STC31xx_StopPowerSavingMode(void)
{
int res;
/* Read the mode register*/
res = STC31xx_ReadByte(STC311x_REG_MODE);
/* Set the VMODE bit to 0 */
res = STC31xx_WriteByte(STC311x_REG_MODE, (res & ~STC311x_VMODE));
if (res != OK) return (res);
return (OK);
}
/*******************************************************************************
* Function Name : STC31xx_AlarmSet
* Description : Set the alarm function and set the alarm threshold
* Input : None
* Return : error status (OK, !OK)
*******************************************************************************/
int STC31xx_AlarmSet(void)
{
int res;
/* Read the mode register*/
res = STC31xx_ReadByte(STC311x_REG_MODE);
/* Set the ALM_ENA bit to 1 */
res = STC31xx_WriteByte(STC311x_REG_MODE, (res | STC311x_ALM_ENA));
if (res != OK) return (res);
return (OK);
}
/*******************************************************************************
* Function Name : STC31xx_AlarmStop
* Description : Stop the alarm function
* Input : None
* Return : error status (OK, !OK)
*******************************************************************************/
int STC31xx_AlarmStop(void)
{
int res;
/* Read the mode register*/
res = STC31xx_ReadByte(STC311x_REG_MODE);
/* Set the ALM_ENA bit to 0 */
res = STC31xx_WriteByte(STC311x_REG_MODE, (res & ~STC311x_ALM_ENA));
if (res != OK) return (res);
return (OK);
}
/*******************************************************************************
* Function Name : STC31xx_AlarmGet
* Description : Return the ALM status
* Input : None
* Return : ALM status 00 : no alarm
* 01 : SOC alarm
* 10 : Voltage alarm
* 11 : SOC and voltage alarm
*******************************************************************************/
int STC31xx_AlarmGet(void)
{
int res;
/* Read the mode register*/
res = STC31xx_ReadByte(STC311x_REG_CTRL);
res = res >> 5;
return (res);
}
/*******************************************************************************
* Function Name : STC31xx_AlarmClear
* Description : Clear the alarm signal
* Input : None
* Return : error status (OK, !OK)
*******************************************************************************/
int STC31xx_AlarmClear(void)
{
int res;
/* clear ALM bits*/
res = STC31xx_WriteByte(STC311x_REG_CTRL, 0x01);
if (res != OK) return (res);
return (res);
}
/*******************************************************************************
* Function Name : STC31xx_AlarmSetVoltageThreshold
* Description : Set the alarm threshold
* Input : int voltage threshold
* Return : error status (OK, !OK)
*******************************************************************************/
int STC31xx_AlarmSetVoltageThreshold(int VoltThresh)
{
int res;
int value;
BattData.Alm_Vbat = VoltThresh;
value= ((BattData.Alm_Vbat << 9) / VoltageFactor); /* LSB=8*2.44mV */
res = STC31xx_WriteByte(STC311x_REG_ALARM_VOLTAGE, value);
if (res != OK) return (res);
return (OK);
}
/*******************************************************************************
* Function Name : STC31xx_AlarmSetSOCThreshold
* Description : Set the alarm threshold
* Input : int voltage threshold
* Return : error status (OK, !OK)
*******************************************************************************/
int STC31xx_AlarmSetSOCThreshold(int SOCThresh)
{
int res;
BattData.Alm_SOC = SOCThresh;
res = STC31xx_WriteByte(STC311x_REG_ALARM_SOC, BattData.Alm_SOC * 2);
if (res != OK) return (res);
return (OK);
}
/*******************************************************************************
* Function Name : STC31xx_RelaxTmrSet
* Description : Set the current threshold register to the passed value in mA
* Input : int current threshold
* Return : error status (OK, !OK)
*******************************************************************************/
int STC31xx_RelaxTmrSet(int CurrentThreshold)
{
int res, value;
BattData.RelaxThreshold = CurrentThreshold;
if (BattData.CurrentFactor != 0) {
value= ((BattData.RelaxThreshold << 9) / BattData.CurrentFactor); /* LSB=8*5.88uV/Rsense */
value = value & 0x7f;
res = STC31xx_WriteByte(STC311x_REG_CURRENT_THRES,value);
if (res != OK) return (res);
}
return (OK);
}
/*******************************************************************************
* Function Name : STC31xx_ForceCC
* Description : Force the CC mode for CC eval
* Input :
* Return : error status (OK, !OK)
*******************************************************************************/
int STC31xx_ForceCC(void)
{
STC311x_ForceCC();
return (OK);
}
#if 0
/*******************************************************************************
* Function Name : STC311x_ForceCD_high
* Description : Force the CD pin to high level (charger disable)
* Input :
* Return :
*******************************************************************************/
static void STC311x_ForceCD_high(void)
{
int value;
value = STC31xx_ReadByte(STC311x_REG_MODE);
STC31xx_WriteByte(STC311x_REG_MODE,value | STC311x_FORCE_CD); /* force CD high */
}
/*******************************************************************************
* Function Name : STC311x_ForceCD_low
* Description : Force the CD pin to low level (charger enable)
* Input :
* Return :
*******************************************************************************/
static void STC311x_ForceCD_low(void)
{
int value;
value = STC31xx_ReadByte(STC311x_REG_MODE);
STC31xx_WriteByte(STC311x_REG_MODE,value & (~STC311x_FORCE_CD)); /* force CD low */
}
#endif
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------- */
static void stc311x_work(struct work_struct *work)
{
struct stc311x_chip *chip;
GasGauge_DataTypeDef GasGaugeData;
int res,Loop;
/* Initial GasGaugeData structure */
memset(&GasGaugeData, 0, sizeof(GasGauge_DataTypeDef));
chip = container_of(work, struct stc311x_chip, work.work);
sav_client = chip->client;
if (chip->pdata) {
GasGaugeData.Vmode = chip->pdata->Vmode; /* 1=Voltage mode, 0=mixed mode */
GasGaugeData.Alm_SOC = chip->pdata->Alm_SOC; /* SOC alm level %*/
GasGaugeData.Alm_Vbat = chip->pdata->Alm_Vbat; /* Vbat alm level mV*/
GasGaugeData.CC_cnf = chip->pdata->CC_cnf; /* nominal CC_cnf */
GasGaugeData.VM_cnf = chip->pdata->VM_cnf; /* nominal VM cnf */
GasGaugeData.Rint = chip->pdata->Rint; /* nominal Rint */
GasGaugeData.Cnom = chip->pdata->Cnom; /* nominal capacity in mAh */
GasGaugeData.Rsense = chip->pdata->Rsense; /* sense resistor mOhms*/
GasGaugeData.RelaxCurrent = chip->pdata->RelaxCurrent; /* current for relaxation in mA (< C/20) */
GasGaugeData.Adaptive = chip->pdata->Adaptive; /* 1=Adaptive mode enabled, 0=Adaptive mode disabled */
/* capacity derating in 0.1%, for temp = 60, 40, 25, 10, 0, -10 degreeC */
for(Loop=0;Loop<NTEMP;Loop++)
GasGaugeData.CapDerating[Loop] = chip->pdata->CapDerating[Loop];
/* OCV curve adjustment */
for(Loop=0;Loop<16;Loop++)
GasGaugeData.OCVValue[Loop] = chip->pdata->OCVValue[Loop];
GasGaugeData.ExternalTemperature = chip->pdata->ExternalTemperature(); /*External temperature fonction, return degreeC*/
GasGaugeData.ForceExternalTemperature = chip->pdata->ForceExternalTemperature; /* 1=External temperature, 0=STC3117 temperature */
}
res = GasGauge_Task(&GasGaugeData); /* process gas gauge algorithm, returns results */
if (res > 0) {
/* results available */
chip->batt_soc = (GasGaugeData.SOC+5)/10;
if ((chip->batt_soc != chip->batt_soc_last) && (chip->batt_soc <= 3)) {
pr_err("Battery soc is %d, notify power_supply\n", chip->batt_soc);
power_supply_changed(&chip->battery);
}
#if 0
/* This code is try to send power supply change to avoid watch deep sleep */
/* so that the GasGauge_Task() late to keep capacity on 99%. */
if ((chip->batt_soc == 99) && (GasGaugeData.AvgCurrent > CHG_NOT_CHARGE)) {
pr_err("Battery soc is %d, notify power_supply\n", chip->batt_soc);
power_supply_changed(&chip->battery);
}
#endif
#ifdef DEBUG_SOC
pr_err("last_soc=%d, soc=%d, volt=%d, avg_volt=%d\n", chip->batt_soc_last, chip->batt_soc, GasGaugeData.Voltage, BattData.AvgVoltage);
#endif
if ((GasGaugeData.AvgCurrent < CHG_NOT_CHARGE) && chip->batt_soc > chip->batt_soc_last && GasGaugeData.Voltage < BATT_CHG_VOLTAGE) {
chip->batt_soc = chip->batt_soc_last;
} else {
#if defined(STC3117_AGING_FEATURE)
pr_err("Before aging calculate, aging_counter=%d\n", aging_counter);
if ((GasGaugeData.AvgCurrent > CHG_NOT_CHARGE && chip->batt_soc > chip->batt_soc_last)) {
aging_counter += (chip->batt_soc - chip->batt_soc_last);//counter++
}
pr_err("after aging calculate, aging_counter=%d\n", aging_counter);
battery_capacity_aging_level = 100 - ((aging_counter / 100) * 20 / 500);
pr_err("battery_capacity_aging_level=%d\n", battery_capacity_aging_level);
#endif /* STC3117_AGING_FEATURE */
chip->batt_soc_last = chip->batt_soc;
}
chip->batt_voltage = GasGaugeData.Voltage;
chip->batt_current = GasGaugeData.Current;
#if defined(STC3117_AGING_FEATURE)
chip->batt_charge_cycle = aging_counter / 100;
#endif /* STC3117_AGING_FEATURE */
chip->batt_temp = GasGaugeData.Temperature;
} else if(res == -1) {
chip->batt_voltage = GasGaugeData.Voltage;
chip->batt_soc = (GasGaugeData.SOC+5)/10;
}
stc311x_get_status(sav_client);
stc311x_get_online(sav_client);
schedule_delayed_work(&chip->work, STC311x_DELAY);
}
static enum power_supply_property stc311x_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_ONLINE,
#if defined(STC3117_AGING_FEATURE)
POWER_SUPPLY_PROP_CYCLE_COUNT,
#endif /* STC3117_AGING_FEATURE */
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_TEMP,
};
int null_fn(void)
{
return 0; // for discharging status
}
int Temperature_fn(void)
{
return (25);
}
static int stc3317_parse_dt(struct stc311x_chip *chip, struct stc311x_platform_data *pdata)
{
int rc = -1;
struct device_node *node = chip->client->dev.of_node;
pr_err("%s: stc3317_parse_dt()\n", __func__);
if (!node) {
pr_err("%s: No DT data Failing Probe\n", __func__);
return -EINVAL;
}
#if 0
rc = of_property_read_u32(node, "stc,battery_online", (int *)pdata->battery_online);
if (rc < 0) pr_debug("read of stc,battery_online failure, rc = %d\n", rc);
//pr_err ("%s: battery_online=0x%x\n", (int *)pdata->battery_online, __func__);
rc = of_property_read_u32(node, "stc,charger_online", (int *)pdata->charger_online);
if (rc < 0) pr_debug("read of stc,charger_online failure, rc = %d\n", rc);
//pr_err ("%s: charger_online=0x%x\n", (int *)pdata->charger_online, __func__);
rc = of_property_read_u32(node, "stc,charger_enable", (int *)pdata->charger_enable);
if (rc < 0) pr_debug("read of stc,charger_enable failure, rc = %d\n", rc);
//pr_err ("%s: charger_enable=0x%x\n", (int *)pdata->charger_enable, __func__);
rc = of_property_read_u32(node, "stc,power_supply_register", (int *)pdata->power_supply_register);
if (rc < 0) pr_debug("read of stc,power_supply_register failure, rc = %d\n", rc);
//pr_err ("%s: power_supply_register=%d\n", (int *)pdata->power_supply_register, __func__);
rc = of_property_read_u32(node, "stc,power_supply_unregister", (int *)pdata->power_supply_unregister);
if (rc < 0) pr_debug("read of stc,power_supply_unregister failure, rc = %d\n", rc);
//pr_err ("%s: power_supply_unregister=%d\n", pdata->power_supply_unregister, __func__);
#endif
pdata->battery_online = NULL;
pdata->charger_online = null_fn;
pdata->charger_enable = null_fn;
pdata->power_supply_register = NULL;
pdata->power_supply_unregister = NULL;
rc = of_property_read_u32(node, "stc,Vmode", &(pdata->Vmode));
if (rc < 0) pr_debug("read of stc,Vmode failure, rc = %d\n", rc);
pr_err ("%s: Vmode=%d\n", __func__, pdata->Vmode);
rc = of_property_read_u32(node, "stc,Alm_SOC", &(pdata->Alm_SOC));
if (rc < 0) pr_debug("read of stc,Alm_SOC failure, rc = %d\n", rc);
pr_err ("%s: Alm_SOC=%d\n", __func__, pdata->Alm_SOC);
rc = of_property_read_u32(node, "stc,Alm_Vbat", &(pdata->Alm_Vbat));
if (rc < 0) pr_debug("read of stc,Alm_Vbat failure, rc = %d\n", rc);
pr_err ("%s: Alm_Vbat=%d\n", __func__, pdata->Alm_Vbat);
rc = of_property_read_u32(node, "stc,CC_cnf", &(pdata->CC_cnf));
if (rc < 0) pr_debug("read of stc,CC_cnf failure, rc = %d\n", rc);
pr_err ("%s: CC_cnf=%d\n", __func__, pdata->CC_cnf);
rc = of_property_read_u32(node, "stc,VM_cnf", &(pdata->VM_cnf));
if (rc < 0) pr_debug("read of stc,VM_cnf failure, rc = %d\n", rc);
pr_err ("%s: VM_cnf=%d\n", __func__, pdata->VM_cnf);
rc = of_property_read_u32(node, "stc,Rint", &(pdata->Rint));
if (rc < 0) pr_debug("read of stc,Rint failure, rc = %d\n", rc);
pr_err ("%s: Rint=%d\n", __func__, pdata->Rint);
rc = of_property_read_u32(node, "stc,Cnom", &(pdata->Cnom));
if (rc < 0) pr_debug("read of stc,Cnom failure, rc = %d\n", rc);
pr_err ("%s: Cnom=%d\n", __func__, pdata->Cnom);
rc = of_property_read_u32(node, "stc,Rsense", &(pdata->Rsense));
if (rc < 0) pr_debug("read of stc,Rsense failure, rc = %d\n", rc);
pr_err ("%s: Rsense=%d\n", __func__, pdata->Rsense);
rc = of_property_read_u32(node, "stc,RelaxCurrent", &(pdata->RelaxCurrent));
if (rc < 0) pr_debug("read of stc,RelaxCurrent failure, rc = %d\n", rc);
pr_err ("%s: RelaxCurrent=%d\n", __func__, pdata->RelaxCurrent);
rc = of_property_read_u32(node, "stc,Adaptive", &(pdata->Adaptive));
if (rc < 0) pr_debug("read of stc,Adaptive failure, rc = %d\n", rc);
pr_err ("%s: Adaptive=%d\n", __func__, pdata->Adaptive);
rc = of_property_read_u32_array(node, "stc,CapDerating", pdata->CapDerating, 7);
if (rc < 0) pr_debug("read of stc,CapDerating failure, rc = %d\n", rc);
pr_err ("%s: CapDerating[0]=%d, [6]=%d\n", __func__, pdata->CapDerating[0], pdata->CapDerating[6]);
#if 0
rc = of_property_read_u32_array(node, "stc,SOCValue", pdata->SOCValue, 16);
if (rc < 0) pr_debug("read of stc,SOCValue failure, rc = %d\n", rc);
pr_err ("%s: SOCValue[0]=%d, [15]=%d\n", __func__, pdata->SOCValue[0], pdata->SOCValue[15]);
#endif
rc = of_property_read_u32_array(node, "stc,OCVValue", pdata->OCVValue, 16);
if (rc < 0) pr_debug("read of stc,OCVValue failure, rc = %d\n", rc);
pr_err ("%s: OCVValue[0]=%d, [15]=%d\n", __func__, pdata->OCVValue[0], pdata->OCVValue[15]);
//rc = of_property_read_u32(node, "stc,ExternalTemperature", (int *)pdata->ExternalTemperature);
//if (rc < 0) pr_debug("read of stc,ExternalTemperature failure, rc = %d\n", rc);
//pr_err ("%s: ExternalTemperature=%d\n", __func__, pdata->ExternalTemperature);
pdata->ExternalTemperature = Temperature_fn;
rc = of_property_read_u32(node, "stc,ForceExternalTemperature", &(pdata->ForceExternalTemperature));
if (rc < 0) pr_debug("read of stc,ForceExternalTemperature failure, rc = %d\n", rc);
pr_err ("%s: ForceExternalTemperature=%d\n", __func__, pdata->ForceExternalTemperature);
pr_err("%s: end of dt parsing\n", __func__);
return 0;
}
static int stc311x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct stc311x_chip *chip;
int ret,res,Loop;
GasGauge_DataTypeDef GasGaugeData;
/* Initial GasGaugeData structure */
memset(&GasGaugeData, 0, sizeof(GasGauge_DataTypeDef));
/*First check the functionality supported by the host*/
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK))
return -EIO;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_I2C_BLOCK))
return -EIO;
/*OK. For now, we presume we have a valid client. We now create the
client structure*/
chip = kzalloc(sizeof(struct stc311x_chip), GFP_KERNEL);
if (!chip) {
printk("Out of memory to create client structure for stc311x\n");
return -ENOMEM; /*Out of memory*/
}
printk("\n\nstc311x probe started\n\n");
/* The common I2C client data is placed right specific data. */
chip->client = client;
#if 0 // Avery: populate devices parameters via device tree
chip->pdata = client->dev.platform_data;
#endif
chip->pdata = kzalloc(sizeof(struct stc311x_platform_data), GFP_KERNEL);
ret = stc3317_parse_dt(chip, chip->pdata);
if (ret < 0) {
pr_err("%s: Couldn't to parse STC3317 DT, ret = %d\n", __func__, ret);
return ret;
}
i2c_set_clientdata(client, chip);
chip->battery.name = "bms";
chip->battery.type = POWER_SUPPLY_TYPE_BMS;
chip->battery.get_property = stc311x_get_property;
chip->battery.properties = stc311x_battery_props;
chip->battery.num_properties = ARRAY_SIZE(stc311x_battery_props);
if (chip->pdata && chip->pdata->power_supply_register)
ret = chip->pdata->power_supply_register(&client->dev, &chip->battery);
else
ret = power_supply_register(&client->dev, &chip->battery);
if (ret) {
//dev_err(&client->dev, "failed: power supply register\n");
kfree(chip);
return ret;
}
dev_info(&client->dev, "power supply register,%d\n",ret);
stc311x_get_version(client);
/* init gas gauge system */
sav_client = chip->client;
if (chip->pdata) {
GasGaugeData.Vmode = chip->pdata->Vmode; /* 1=Voltage mode, 0=mixed mode */
GasGaugeData.Alm_SOC = chip->pdata->Alm_SOC; /* SOC alm level %*/
GasGaugeData.Alm_Vbat = chip->pdata->Alm_Vbat; /* Vbat alm level mV*/
GasGaugeData.CC_cnf = chip->pdata->CC_cnf; /* nominal CC_cnf */
GasGaugeData.VM_cnf = chip->pdata->VM_cnf; /* nominal VM cnf */
GasGaugeData.Rint = chip->pdata->Rint; /* nominal Rint */
GasGaugeData.Cnom = chip->pdata->Cnom; /* nominal capacity in mAh */
GasGaugeData.Rsense = chip->pdata->Rsense; /* sense resistor mOhms*/
GasGaugeData.RelaxCurrent = chip->pdata->RelaxCurrent; /* current for relaxation in mA (< C/20) */
GasGaugeData.Adaptive = chip->pdata->Adaptive; /* 1=Adaptive mode enabled, 0=Adaptive mode disabled */
/* capacity derating in 0.1%, for temp = 60, 40, 25, 10, 0, -10 degreeC */
for(Loop=0;Loop<NTEMP;Loop++)
GasGaugeData.CapDerating[Loop] = chip->pdata->CapDerating[Loop];
/* OCV curve adjustment */
for(Loop=0;Loop<16;Loop++)
GasGaugeData.OCVValue[Loop] = chip->pdata->OCVValue[Loop];
GasGaugeData.ExternalTemperature = chip->pdata->ExternalTemperature(); /*External temperature fonction, return degreeC*/
GasGaugeData.ForceExternalTemperature = chip->pdata->ForceExternalTemperature; /* 1=External temperature, 0=STC3117 temperature */
}
GasGauge_Start(&GasGaugeData);
msleep(200);
res = GasGauge_Task(&GasGaugeData); /* process gas gauge algorithm, returns results */
if (res > 0) {
/* results available */
chip->batt_soc = (GasGaugeData.SOC+5)/10;
chip->batt_voltage = GasGaugeData.Voltage;
chip->batt_current = GasGaugeData.Current;
} else if(res == 0) {
/* SOC and Voltage available */
chip->batt_soc = (GasGaugeData.SOC+5)/10;
chip->batt_voltage = GasGaugeData.Voltage;
chip->batt_current = 0;
} else if(res == -1) {
chip->batt_voltage = GasGaugeData.Voltage;
chip->batt_soc = (GasGaugeData.SOC+5)/10;
}
chip->batt_soc_last = chip->batt_soc;
INIT_DEFERRABLE_WORK(&chip->work, stc311x_work);
create_debugfs_entries(chip);
schedule_delayed_work(&chip->work, STC311x_DELAY);
//The specified delay depends of every platform and Linux kernel. It has to be checked physically during the driver integration
//a delay of about 5 seconds is correct but 30 seconds is enough compare to the battery SOC evolution speed
pr_info("stc3117-204 FG successfully probed\n");
return 0;
}
static int stc311x_remove(struct i2c_client *client)
{
struct stc311x_chip *chip = i2c_get_clientdata(client);
/* stop gas gauge system */
sav_client = chip->client;
GasGauge_Stop();
if (chip->pdata && chip->pdata->power_supply_unregister)
chip->pdata->power_supply_unregister(&chip->battery);
else
power_supply_unregister(&chip->battery);
cancel_delayed_work(&chip->work);
kfree(chip);
return 0;
}
#ifdef CONFIG_PM
static int stc311x_suspend(struct i2c_client *client,
pm_message_t state)
{
struct stc311x_chip *chip = i2c_get_clientdata(client);
cancel_delayed_work(&chip->work);
return 0;
}
static int stc311x_resume(struct i2c_client *client)
{
struct stc311x_chip *chip = i2c_get_clientdata(client);
schedule_delayed_work(&chip->work, 0);
return 0;
}
#else
#define stc311x_suspend NULL
#define stc311x_resume NULL
#endif /* CONFIG_PM */
static const struct of_device_id stc3317_match[] = {
{ .compatible = "STC,stc3117-fg", },
{ },
};
/* Every chip have a unique id */
static const struct i2c_device_id stc311x_id[] = {
{ "stc3117-204", 0 },
{ }
};
/* Every chip have a unique id and we need to register this ID using MODULE_DEVICE_TABLE*/
MODULE_DEVICE_TABLE(i2c, stc311x_id);
static struct i2c_driver stc311x_i2c_driver = {
.driver = {
.name = "stc3117-204",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(stc3317_match),
},
.probe = stc311x_probe,
.remove = stc311x_remove,
.suspend = stc311x_suspend,
.resume = stc311x_resume,
.id_table = stc311x_id,
};
/*To register this I2C chip driver, the function i2c_add_driver should be called
with a pointer to the struct i2c_driver*/
static int __init stc311x_init(void)
{
return i2c_add_driver(&stc311x_i2c_driver);
}
module_init(stc311x_init);
/*To unregister the I2C chip driver, the i2c_del_driver function should be called
with the same pointer to the struct i2c_driver*/
static void __exit stc311x_exit(void)
{
i2c_del_driver(&stc311x_i2c_driver);
}
module_exit(stc311x_exit);
MODULE_AUTHOR("STMICROELECTRONICS");
MODULE_DESCRIPTION("STC311x Fuel Gauge");
MODULE_LICENSE("GPL");