blob: df67169d4e104f4dca900f0fa17417112831eb3d [file] [log] [blame]
/*
* stc3117_battery.c
* STC3117 fuel-gauge systems for lithium-ion (Li+) batteries
*
* Copyright (C) 2011 STMicroelectronics.
* Copyright (c) 2016 The Linux Foundation. All rights reserved.
*
* 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 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/debugfs.h>
#include <linux/power_supply.h>
#include <linux/power/stc3117_battery.h>
#include <linux/slab.h>
#include <linux/qpnp/qpnp-adc.h>
#include <linux/wakelock.h>
#define GG_VERSION "1.00a"
/*Function declaration*/
/* ************************************************************************ */
/* 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 */
/* ------------------------------------------------------------------------ */
/* */
/* min voltage at the end of the charge (mV) */
#define BATT_CHG_VOLTAGE 4330 //4250
/* nearly empty battery detection level (mV) */
#define BATT_MIN_VOLTAGE 3300
#define MAX_HRSOC 51200 /* 100% in 1/512% units*/
#define MAX_SOC 1000 /* 100% in 0.1% units */
#define CHG_MIN_CURRENT 200 /* min charge current in mA*/
#define CHG_END_CURRENT 40 //20 /* end charge current in mA*/
/* minimum application current consumption in mA ( <0 !) */
#define APP_MIN_CURRENT (-5)
#define APP_MIN_VOLTAGE 3000 /* application cut-off voltage*/
#define TEMP_MIN_ADJ (-5) /* minimum temperature for gain adjustment */
/* normalized VM_CNF at 60, 40, 25, 10, 0, -10°C, -20°C */
#define VMTEMPTABLE { 85, 90, 100, 160, 320, 440, 840 }
#define AVGFILTER 4 /* average filter constant */
/* ************************************************************************ */
/* 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 GG_VM_BIT BIT(2)
#define GG_RUN_BIT BIT(4)
#define PORDET_BIT BIT(4)
#define STC311x_REG_SOC 0x02 /* SOC Data (2 bytes) */
/* Number of Conversion (2 bytes) */
#define STC311x_REG_COUNTER 0x04
/* Battery Current (2 bytes) */
#define STC311x_REG_CURRENT 0x06
/* Battery Voltage (2 bytes) */
#define STC311x_REG_VOLTAGE 0x08
/* Temperature */
#define STC311x_REG_TEMPERATURE 0x0A
#ifdef STC3117
/* Battery Average Current (2 bytes) */
#define STC311x_REG_AVG_CURRENT 0x0B
#endif
#define STC311x_REG_OCV 0x0D /* Battery OCV (2 bytes) */
/* CC configuration (2 bytes) */
#define STC311x_REG_CC_CNF 0x0F
/* VM configuration (2 bytes) */
#define STC311x_REG_VM_CNF 0x11
#define STC311x_REG_ALARM_SOC 0x13 /* SOC alarm level */
#define STC311x_REG_ALARM_VOLTAGE 0x14 /* Low voltage alarm level */
/* Current threshold for relaxation */
#define STC311x_REG_CURRENT_THRES 0x15
/* Current monitoring counter */
#define STC311x_REG_CMONIT_COUNT 0x16
/* Current monitoring max count */
#define STC311x_REG_CMONIT_MAX 0x17
#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 */
/* Enable internal Pull-Up on BATD bit mask */
#define STC311x_BATD_PU 0x02
#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 */
/* General Purpose RAM Registers */
#define STC311x_REG_RAM 0x20
/* Total RAM size of STC311x in bytes */
#define RAM_SIZE 16
#define STC311x_REG_OCVTAB 0x30
#define STC311x_REG_SOCTAB 0x50
/* counter value for 1st current/temp measurements */
#define VCOUNT 0
/* GG_RUN & PORDET mask in STC311x_BattDataTypeDef status word */
#define M_STAT 0x1010
#define M_RST 0x9800 /* UVLOD & BATFAIL & PORDET mask */
/* GG_RUN mask in STC311x_BattDataTypeDef status word */
#define M_RUN 0x0010
#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
#define SMB231_REG0_DEFAULT 0x54
#define SOC_CHANGE_COUNT_NORMAL 3
#define SOC_CHANGE_COUNT_LOW_BATTERY 2
/* gas gauge structure definition ------------------------------------*/
/* Private constants -------------------------------------------------------*/
#define NTEMP 7
/* temperature table from 60°C to -20°C (descending order!) */
static const int TempTable[NTEMP] = {60, 40, 25, 10, 0, -10, -20};
static const int DefVMTempTable[NTEMP] = VMTEMPTABLE;
static const char *charger_name = "battery";
static bool g_debug, g_standby_mode;
static int g_new_soc, g_last_status, g_ocv;
static const char * const charge_status[] = {
"unknown",
"charging",
"discharging",
"not charging",
"full",
};
static int STC311x_Status(void);
static int STC31xx_Read(int length, int reg , unsigned char *values);
static int STC31xx_ReadByte(int RegAddress);
static int STC31xx_ReadWord(int RegAddress);
static int conv(short value, unsigned short factor);
/* Private variables -------------------------------------------------------*/
/* structure of the STC311x battery monitoring parameters */
struct GasGauge_DataTypeDef {
int Voltage; /* battery voltage in mV */
int Current; /* battery current in mA */
int Temperature; /* battery temperature in 0.1°C */
int SOC; /* battery relative SOC (%) in 0.1% */
int OCV;
int AvgSOC;
int AvgCurrent;
int AvgVoltage;
int AvgTemperature;
int ChargeValue; /* remaining capacity in mAh */
/* battery remaining operating time during discharge (min) */
int RemTime;
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 */
/* capacity derating in 0.1%, for temp = 60, 40, 25, 10, 0, -10, -20
* °C */
int CapDerating[7];
int OCVValue[OCVTAB_SIZE]; /* OCV curve values */
int SOCValue[SOCTAB_SIZE]; /* SOC curve values */
int ExternalTemperature;
int ForceExternalTemperature;
int Ropt;
int Var1;
};
/* structure of the STC311x battery monitoring data */
struct STC311x_BattDataTypeDef {
/* 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°C */
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[OCVTAB_SIZE];
int Ropt;
int Nropt;
};
static struct 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;
/* ------------------------------------------------------------------------ */
/* INTERNAL ANDROID DRIVER PARAMETERS */
/* TO BE ADJUSTED ACCORDING TO BATTERY/APPLICATION CHARACTERISTICS */
/* ------------------------------------------------------------------------ */
#define STC311x_BATTERY_FULL 100
#define STC311x_DELAY 3000 //30 sec
#define STC311x_DELAY_LOW_BATT 500 //5 sec
#define STC311x_SOC_THRESHOLD 5
/* ************************************************************************ */
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 wake_lock wlock;
/* State Of Connect */
int online;
/* battery SOC (capacity) */
int batt_soc;
/* battery voltage */
int batt_voltage;
/* Current */
int batt_current;
/* State Of Charge */
int status;
int Temperature;
struct qpnp_vadc_chip *vadc_dev;
};
int null_fn(void)
{
/*for discharging status*/
return 0;
}
int Temperature_fn(void)
{
return 25;
}
static struct stc311x_platform_data stc3117_data = {
.battery_online = NULL,
/* used in stc311x_get_status()*/
.charger_online = NULL,
/* used in stc311x_get_status()*/
.charger_enable = NULL,
.power_supply_register = NULL,
.power_supply_unregister = NULL,
.Vmode = 0,/*REG_MODE, BIT_VMODE 1=Voltage mode, 0=mixed mode */
.Alm_SOC = 15,/* SOC alm level %*/
.Alm_Vbat = 3600,/* Vbat alm level mV*/
/* nominal CC_cnf, coming from battery characterisation*/
.CC_cnf = 82,
/* nominal VM cnf , coming from battery characterisation*/
.VM_cnf = 340,
/* nominal internal impedance*/
.Rint = 818,
/* nominal capacity in mAh, coming from battery characterisation*/
.Cnom = 400,
.Rsense = 10, /* sense resistor mOhms*/
.RelaxCurrent = 20, /* current for relaxation in mA (< C/20) */
.Adaptive = 1, /* 1=Adaptive mode enabled, 0=Adaptive mode disabled */
/* Elentec Co Ltd Battery pack - 80 means 8% */
.CapDerating[6] = 720,/* capacity derating in 0.1%, for temp = -20 degC */
.CapDerating[5] = 195,/* capacity derating in 0.1%, for temp = -10 degC */
.CapDerating[4] = 37,/* capacity derating in 0.1%, for temp = 0 degC */
.CapDerating[3] = 37, /* capacity derating in 0.1%, for temp = 10 degC */
.CapDerating[2] = 0, /* capacity derating in 0.1%, for temp = 25 degC */
.CapDerating[1] = 0, /* capacity derating in 0.1%, for temp = 40 degC */
.CapDerating[0] = 0, /* capacity derating in 0.1%, for temp = 60 degC */
/*OCV curve example for a 4.35V li-ion battery*/
.OCVValue[15] = 4306, /* OCV curve adjustment */
.OCVValue[14] = 4193, /* OCV curve adjustment */
.OCVValue[13] = 4093, /* OCV curve adjustment */
.OCVValue[12] = 3985, /* OCV curve adjustment */
.OCVValue[11] = 3953, /* OCV curve adjustment */
.OCVValue[10] = 3907, /* OCV curve adjustment */
.OCVValue[9] = 3840, /* OCV curve adjustment */
.OCVValue[8] = 3802, /* OCV curve adjustment */
.OCVValue[7] = 3770, /* OCV curve adjustment */
.OCVValue[6] = 3749, /* OCV curve adjustment */
.OCVValue[5] = 3732, /* OCV curve adjustment */
.OCVValue[4] = 3703, /* OCV curve adjustment */
.OCVValue[3] = 3685, /* OCV curve adjustment */
.OCVValue[2] = 3674, /* OCV curve adjustment */
.OCVValue[1] = 3568, /* OCV curve adjustment */
.OCVValue[0] = 3300, /* OCV curve adjustment */
/* SOC_TAB data */
.SOCValue[15] = 100,
.SOCValue[14] = 90,
.SOCValue[13] = 80,
.SOCValue[12] = 70,
.SOCValue[11] = 65,
.SOCValue[10] = 60,
.SOCValue[9] = 50,
.SOCValue[8] = 40,
.SOCValue[7] = 30,
.SOCValue[6] = 25,
.SOCValue[5] = 20,
.SOCValue[4] = 15,
.SOCValue[3] = 10,
.SOCValue[2] = 6,
.SOCValue[1] = 3,
.SOCValue[0] = 0,
/*if the application temperature data is preferred than the STC3117
* temperature*/
/*External temperature fonction, return degC*/
.ExternalTemperature = Temperature_fn,
/* 1=External temperature, 0=STC3117 temperature */
.ForceExternalTemperature = 0,
};
static int stc311x_set_property(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *val)
{
switch (psp) {
case POWER_SUPPLY_PROP_TECHNOLOGY:
if(val->intval)
g_debug = 1;
else
g_debug = 0;
default:
return -EINVAL;
}
return 0;
}
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,
* µA, µAh, µWh, 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_VOLTAGE_NOW:
val->intval = chip->batt_voltage * 1000; /* in uV */
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
val->intval = chip->batt_current * 1000; /* in uA */
break;
case POWER_SUPPLY_PROP_CAPACITY:
//val->intval = chip->batt_soc;
val->intval = g_new_soc;
break;
case POWER_SUPPLY_PROP_TEMP:
val->intval = chip->Temperature;
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = g_debug;
break;
default:
return -EINVAL;
}
return 0;
}
static int stc311x_battery_is_writeable(struct power_supply *psy,
enum power_supply_property prop)
{
switch (prop) {
case POWER_SUPPLY_PROP_TECHNOLOGY:
return 1;
default:
break;
}
return 0;
}
static void stc311x_get_version(struct i2c_client *client)
{
dev_info(&client->dev, "STC3117 Fuel-Gauge Ver %s\n", GG_VERSION);
}
/* -------------------------------------------------------------------------- */
/* 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)
{
int value;
unsigned char data[2];
int res;
res = STC31xx_Read(1, RegAddress, data);
if (res >= 0) {
/* no error */
value = data[0];
} else
value = 0;
return value;
}
/****************************************************************************
* 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)
{
int value;
unsigned char data[2];
int res;
res = STC31xx_Read(2, RegAddress, data);
if (res >= 0) {
/* no error */
value = data[1];
value = (value << 8) + data[0];
} else
value = 0;
return value;
}
/****************************************************************************
* 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;
}
/* ---- end of I2C R/W interface ------------------------------------------ */
/* ------------------------------------------------------------------------ */
/* #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*/
/*****************************************************************************
* 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;
int ret;
/* 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) {
ret = -1;
return ret;
}
/* 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;
/* set GG_RUN=0 before changing algo parameters */
STC31xx_WriteByte(STC311x_REG_MODE, 0x01);
/* 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);
}
/* init SOC Table */
for (ii = 0; ii < SOCTAB_SIZE; ii++)
STC31xx_WriteByte(STC311x_REG_SOCTAB+ii,
(BattData.SOCValue[ii]*2));
/* 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) {
/* LSB=8*2.44mV */
value = ((BattData.Alm_Vbat << 9) / VoltageFactor);
STC31xx_WriteByte(STC311x_REG_ALARM_VOLTAGE, value);
}
/* 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);
/* clear PORDET, BATFAIL, free ALM pin, reset conv counter */
STC31xx_WriteByte(STC311x_REG_CTRL, 0x83);
if (BattData.Vmode) {
/* set GG_RUN=1, voltage mode, alm enabled */
STC31xx_WriteByte(STC311x_REG_MODE, 0x19);
} else {
/* set GG_RUN=1, mixed mode, alm enabled */
STC31xx_WriteByte(STC311x_REG_MODE, 0x18);
}
}
/*****************************************************************************
* 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 */
if (BattData.Rsense != 0) {
/*avoid divide by 0*/
ocv = ocv - BattData.Rint * curr * 588 /
BattData.Rsense / 55000;
} else
return (-1);
/* 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 restore from unexpected reset, restore SOC (system dependent) */
if (GG_Ram.reg.GG_Status == GG_RUNNING) {
if (GG_Ram.reg.SOC != 0) {
/* restore SOC */
STC31xx_WriteWord(STC311x_REG_SOC, GG_Ram.reg.HRSOC);
}
}
/* rewrite ocv to start SOC with updated OCV curve*/
/*STC31xx_WriteWord(STC311x_REG_OCV, ocv);*/
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)
{
/* set soft POR */
STC31xx_WriteByte(STC311x_REG_CTRL, STC311x_SOFTPOR);
}
static void STC311x_SetSOC(int SOC)
{
STC31xx_WriteWord(STC311x_REG_SOC, SOC);
}
static void STC311x_ForceCC(void)
{
int value;
value = STC31xx_ReadByte(STC311x_REG_MODE);
/* force CC mode */
STC31xx_WriteByte(STC311x_REG_MODE, value | STC311x_FORCE_CC);
}
static int STC311x_SaveVMCnf(void)
{
int reg_mode;
/* mode register*/
reg_mode = BattData.STC_Status & 0xff;
/* set GG_RUN=0 before changing algo parameters */
reg_mode &= ~STC311x_GG_RUN;
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) {
/* set GG_RUN=1, voltage mode, alm enabled */
STC31xx_WriteByte(STC311x_REG_MODE, 0x19);
} else {
/* set GG_RUN=1, mixed mode, alm enabled */
STC31xx_WriteByte(STC311x_REG_MODE, 0x18);
if (BattData.GG_Mode == CC_MODE) {
/* force CC mode */
STC31xx_WriteByte(STC311x_REG_MODE, 0x38);
} else {
/* force VM mode */
STC31xx_WriteByte(STC311x_REG_MODE, 0x58);
}
}
return 0;
}
/*****************************************************************************
* Function Name : conv
* Description : conversion utility
* convert a raw 16-bit value from STC311x registers into user units
(mA, mAh, mV, °C)
* (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;
}
/*****************************************************************************
* 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(struct STC311x_BattDataTypeDef *BattData)
{
unsigned char data[16];
int res;
int value;
res = STC311x_Status();
/* return if I2C error or STC3117 not responding */
if (res < 0)
return res;
/* 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];
value = (value<<8) + data[2];
BattData->HRSOC = value; /* result in 1/512% */
/* conversion counter */
value = data[5];
value = (value<<8) + data[4];
BattData->ConvCounter = value;
/* current */
value = data[7];
value = (value<<8) + data[6];
value &= 0x3fff; /* mask unused bits */
if (value >= 0x2000)
value -= 0x4000; /* convert to signed value */
/* result in mA */
BattData->Current = conv(value, BattData->CurrentFactor);
/* voltage */
value = data[9];
value = (value<<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°C */
/* Avg current */
value = data[12];
value = (value<<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];
value = (value<<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 */
g_ocv = BattData->OCV;
/* 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];
value = (value<<8) + data[0];
if (value >= 0x8000)
value -= 0x10000; /* convert to signed value */
BattData->CC_adj = value; /* in 1/512% */
value = data[3];
value = (value<<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)
{
int ret;
ret = STC31xx_Read(RAM_SIZE, STC311x_REG_RAM, RamData);
return ret;
}
/*****************************************************************************
* 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)
{
int ret;
ret = STC31xx_Write(RAM_SIZE, STC311x_REG_RAM, RamData);
return ret;
}
/*****************************************************************************
* 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 */
if ((tabx[index-1] - tabx[index]) != 0) {
/*avoid divide by 0*/
y = (taby[index-1] - taby[index]) *
(x - tabx[index]) * 2 /
(tabx[index-1] - tabx[index]);
y = (y+1) / 2;
y += taby[index];
} else
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, ret; /* 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;
}
}
ret = crc & 255;
return ret;
}
/*****************************************************************************
* 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);
/* last byte holds the CRC */
GG_Ram.db[RAM_SIZE-1] = res;
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
/* for APP_TYP_CURRENT */
r = interpolate(temp/10, NTEMP, TempTable, BattData.CapacityDerating);
#endif
if ((MAX_SOC-r) != 0) {
/*avoid divide by 0*/
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) {
/* end of charge */
BattData.BattState = BATT_ENDCHARG;
}
break;
/* end of charge state. check if fully charged or charge interrupted */
case BATT_ENDCHARG:
if (BattData.Current > CHG_MIN_CURRENT)
BattData.BattState = BATT_CHARGING;
else if (BattData.AvgCurrent < CHG_END_CURRENT) {
/* charge interrupted */
BattData.BattState = BATT_IDLE;
} else if ((BattData.Current > CHG_END_CURRENT) &&
(BattData.Voltage > BATT_CHG_VOLTAGE)) {
/* end of charge */
BattData.BattState = BATT_FULCHARG;
}
break;
/* full charge state. wait for actual end of charge current */
case BATT_FULCHARG:
if (BattData.Current > CHG_MIN_CURRENT)
BattData.BattState = BATT_CHARGING; /* charge again */
else if ((BattData.AvgCurrent < CHG_END_CURRENT) && (BattData.AvgCurrent >= 0)) {
if (BattData.AvgVoltage > BATT_CHG_VOLTAGE) {
/* end of charge detected */
STC311x_SetSOC(MAX_HRSOC);
BattData.SOC = MAX_SOC; /* 100% */
}
/* end of charge cycle */
BattData.BattState = BATT_IDLE;
}
break;
case BATT_IDLE: /* no charging, no discharging */
if (BattData.Current > CHG_END_CURRENT) {
/* charging again */
BattData.BattState = BATT_CHARGING;
} else if (BattData.Current < APP_MIN_CURRENT) {
/* discharging again */
BattData.BattState = BATT_DISCHARG;
}
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;
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 °C */
/* 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);
}
}
/*****************************************************************************
* 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 ------------------------------------- */
#define OG2
#define CURRENT_TH (GG->Cnom/10)
#define GAIN 10
#define A_Var3 500
#define VAR1MAX 64
#define VAR2MAX 128
#define VAR4MAX 128
void SOC_correction(struct GasGauge_DataTypeDef *GG)
{
#ifdef OG2
int Var1 = 0;
int Var2, Var3, Var4;
int SOCopt;
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++;
if ((BattData.AvgCurrent != 0) && (Var2 != 0)) {
/*avoid divide by 0*/
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*/
pr_err("SOC_correction() set new SOC: %d\n", SOCopt);
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(struct 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;
/* default value in case, to avoid divide by 0 */
if (BattData.Rsense == 0)
BattData.Rsense = 10;
/* LSB=5.88uV/R= ~24084/R/4096 - convert to mA */
BattData.CurrentFactor = 24084/BattData.Rsense;
/* LSB=0.008789.Cnom= 36*Cnom/4096 - convert to mA */
BattData.CRateFactor = 36*BattData.Cnom;
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();
/* return -1 if I2C error or STC3117 not present */
res = STC311x_Startup();
} else {
/* check STC3117 status */
if (((STC311x_Status() & M_RST) != 0) || g_standby_mode) {
/* return -1 if I2C error or STC3117 not present */
pr_err("UVLO, Battery fail, Power on reset, or in standby mode, do STC311x_Startup \n");
res = STC311x_Startup();
} 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, ret;
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) {
ret = -1;
return ret; /* error */
}
return 0;
}
void stc311x_debug_info(void)
{
int value,value2, value3, value4, i, run_mode, data_ocv, data_soc, data_voltage;
unsigned char data[31], ram[16];
unsigned char OCVTAB[32]; /* 48~79 */
unsigned char SOCTAB[16]; /* 80~95 */
/* read STC3117 registers from 0 to 30 */
value = STC31xx_Read(31, 0, data);
if (value < 0)
return;
// read ram data from 32-47
value = STC31xx_Read(16, 32, ram);
pr_info("Ram data: [0-1] TstWord = 0x%x, [2-3] HRSOC = %d \n", (ram[1]<<8)+ram[0], ( (ram[3]<<8)+ram[2])/512);
pr_info("Ram data: [4-5] CC_cnf = %d, [6-7] VM_cnf = %d \n", (ram[5]<<8)+ram[4], (ram[7]<<8)+ram[6]);
pr_info("Ram data: [8] SOC = %d, [9] GG_Status = %d \n", ram[8], ram[9]);
/* MODE */
pr_err("REG[00] MODE = 0x%x \n", data[0]);
/* CTRL */
run_mode = data[1] & GG_VM_BIT ? VM_MODE : CC_MODE;
pr_err("REG[01] CTRL = 0x%x, %s \n", data[1], run_mode ? "VM_MODE" : "CC_MODE");
/* SOC */
value = data[3];
value = (value<<8) + data[2];
data_soc = (int)(value/512);
pr_err("REG[02-03] HRSOC = %d, SOC = %d\n", value, (int)(value/512));
/* COUNTER */
value = data[5];
value = (value<<8) + data[4];
pr_err("REG[04-05] COUNTER = %d \n", value);
/* CURRENT */
value = data[7];
value = (value<<8) + data[6];
value &= 0x3fff; /* mask unused bits */
if (value >= 0x2000)
value -= 0x4000; /* convert to signed value */
/* result in mA */
value = conv(value, (24084/10)); //BattData->CurrentFactor
pr_err("REG[06-07] CURRENT = %d mA \n", value);
/* VOLTAGE */
value = data[9];
value = (value<<8) + data[8];
value &= 0x0fff; /* mask unused bits */
if (value >= 0x0800)
value -= 0x1000; /* convert to signed value */
value = conv(value, VoltageFactor); /* result in mV */
data_voltage = value;
pr_err("REG[08-09] VOLTAGE = %d mv \n", value);
/* temperature */
value = data[10];
if (value >= 0x80)
value -= 0x100; /* convert to signed value */
//BattData->Temperature = value*10; /* result in 0.1°C */
pr_err("REG[10] temperature = %d degC \n", value*10);
/* Avg current */
value = data[12];
value = (value<<8) + data[11];
if (value >= 0x8000)
value -= 0x10000; /* convert to signed value */
if ((data[0] & 0x01) == 0) {//mix mode
value = conv(value, (24084/10)); //BattData->CurrentFactor
value = value / 4; /* divide by 4 */
} else {
value = conv(value, (36*400)); //BattData->CRateFactor
}
pr_err("REG[11-12] AVG Current = %d degC \n", value);
/* OCV */
value = data[14];
value = (value<<8) + data[13];
value &= 0x3fff; /* mask unused bits */
if (value >= 0x02000)
value -= 0x4000; /* convert to signed value */
value = conv(value, VoltageFactor);
data_ocv = (value+2) / 4; /* divide by 4 with rounding */
//BattData->OCV = value; /* result in mV */
pr_err("REG[13-14] OCV = %d mv \n", data_ocv);
/* CC_CNF */
value = data[16];
value = (value<<8) + data[15];
pr_err("REG[15-16] CC_CNF = %d \n", value);
/* VM_CNF */
value = data[18];
value = (value<<8) + data[17];
pr_err("REG[17-18] VM_CNF = %d \n", value);
/* ALARM_SOC */
//pr_err("REG[19] ALARM_SOC = %d \n", (int)(data[19]/2));
/* ALARM_VOLTAGE */
//pr_err("REG[20] ALARM_VOLTAGE = %d mv \n", (int)((data[20]*176)/10));
/* CURRENT_THRES */
pr_err("REG[21] CURRENT_THRES = %d \n", (int)(data[21]*47/10));
/* CMONIT_COUNT */
pr_err("REG[22] CMONIT_COUNT = %d \n", data[22]);
/* CMONIT_MAX */
pr_err("REG[23] CMONIT_MAX = %d \n", data[23]);
/* ID */
//pr_err("REG[24] ID = %d \n", data[24]);
/* read STC3117 registers CC & VM adj */
/* CC & VM adjustment counters */
value = data[28];
value = (value<<8) + data[27];
if (value >= 0x8000)
value -= 0x10000; /* convert to signed value */
//BattData->CC_adj = value; /* in 1/512% */
pr_err("REG[[27-28] CC_ADJ = %d \n", (int)(value/512));
value = data[30];
value = (value<<8) + data[29];
if (value >= 0x8000)
value -= 0x10000; /* convert to signed value */
//BattData->VM_adj = value; /* in 1/512% */
pr_err("REG[29-30] VM_ADJ = %d \n", (int)(value/512));
// read OCVTAB registers from 48 to 79
value = STC31xx_Read(32, 48, OCVTAB);
if (value < 0)
return;
else {
for (i=0; i<32; i+=8) {
value = OCVTAB[i+1];
value = (value<<8) + OCVTAB[i];
value2 = OCVTAB[i+3];
value2 = (value2<<8) + OCVTAB[i+2];
value3 = OCVTAB[i+5];
value3 = (value3<<8) + OCVTAB[i+4];
value4 = OCVTAB[i+7];
value4 = (value4<<8) + OCVTAB[i+6];
pr_err("OCV_TAB[%d]: %d, [%d]: %d, [%d]: %d, [%d]: %d \n",
(i/2), (value*55/100), (i+2)/2, (value2*55/100), (i+4)/2, (value3*55/100), (i+6)/2, (value4*55/100));
}
}
// read SOCTAB registers from 80 to 95
value = STC31xx_Read(16, 80, SOCTAB);
if (value < 0)
return;
else {
for (i=0; i<16; i+=4)
pr_err("SOC_TAB[%d]: %d, [%d]: %d, [%d]: %d, [%d]: %d \n",
i, (SOCTAB[i]/2), i+1, (SOCTAB[i+1]/2), i+2, (SOCTAB[i+2]/2), i+3, (SOCTAB[i+3]/2));
}
pr_info("end: mode = 0x%x, ctrl = 0x%x, CMONIT_COUNT = %d, SOC = %d, voltage = %d, OCV = %d \n",data[0], data[1], data[22], data_soc, data_voltage, data_ocv);
}
static void STC311x_Rewrite_OCV(void)
{
int mode, ocv, curr, voltage, soc;
mode = STC31xx_ReadByte(STC311x_REG_MODE);
if (mode < 0)
return ;
if(mode & GG_RUN_BIT)
return;
pr_info("Rewrite OCV due to standby mode, reg_mode = 0x%x \n", mode);
//set operation mode to get current
STC31xx_WriteByte(STC311x_REG_MODE, 0x18);
mdelay(200);
/* read OCV */
ocv = STC31xx_ReadWord(STC311x_REG_OCV);
pr_info("reg_OCV = %d \n", (ocv * 55 / 100));
/* 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 */
pr_info("BattData.Rsense = %d, BattData.Rint = %d, curr = %d \n", BattData.Rsense, BattData.Rint, curr);
if (BattData.Rsense != 0) {
/*avoid divide by 0*/
ocv = ocv - BattData.Rint * curr * 588 /
BattData.Rsense / 55000;
} else
return;
//back to standby state
STC31xx_WriteByte(STC311x_REG_MODE, mode);
/* rewrite ocv to start SOC with updated OCV curve */
pr_info("rewrite ocv = %d \n", (ocv * 55 / 100));
STC31xx_WriteWord(STC311x_REG_OCV, ocv);
mdelay(200);
mode = STC31xx_ReadByte(STC311x_REG_MODE);
soc = STC31xx_ReadWord(STC311x_REG_SOC);
ocv = STC31xx_ReadWord(STC311x_REG_OCV);
voltage = STC31xx_ReadWord(STC311x_REG_VOLTAGE);
pr_info("mode = 0x%x, soc = %d, OCV = %d, voltage = %d \n", mode, (soc/512), (ocv * 55 / 100), (voltage * 22 / 10));
}
/*****************************************************************************
* 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(struct GasGauge_DataTypeDef *GG)
{
int res, value, ret, soc;
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;
/* read battery data into global variables */
res = STC311x_ReadBatteryData(&BattData);
if (res != 0) {
ret = -1;
return ret; /* abort in case of I2C failure */
}
/* 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) || g_standby_mode) {
/* BATD or UVLO detected */
if (BattData.ConvCounter > 0) {
GG->Voltage = BattData.Voltage;
GG->SOC = (BattData.HRSOC*10+256)/512;
}
if ((BattData.STC_Status & M_BATFAIL) != 0)
pr_err("stc3117 BATD error, gauge reset.\n");
else if ((BattData.STC_Status & M_UVLOD) != 0)
pr_err("stc3117 UVLOD error, gauge reset.\n");
else if (g_standby_mode)
pr_err("stc3117 in standby mode, gauge reset.\n");
g_standby_mode = 0;
/* BATD or UVLO detected */
GasGauge_Reset();
ret = -1;
return ret;
}
#endif
if ((BattData.STC_Status & M_RUN) == 0) {
pr_err("stc311x in standby mode, rewrite OCV \n");
STC311x_Rewrite_OCV();
/* if not running, restore STC3117 */
STC311x_Restore();
GG_Ram.reg.GG_Status = GG_INIT;
//update SOC
mdelay(500);
soc = STC31xx_ReadWord(STC311x_REG_SOC);
pr_info("update new SOC = %d \n", soc);
BattData.HRSOC = soc;
}
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;
/* in 0.1% unit */
BattData.AvgSOC = CompensateSOC(BattData.SOC,
BattData.Temperature);
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);
/* 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 (value < (APP_MIN_VOLTAGE+200) &&
value > (APP_MIN_VOLTAGE-500)) {
if (value < APP_MIN_VOLTAGE)
BattData.SOC = 0;
else
BattData.SOC = BattData.SOC *
(value - APP_MIN_VOLTAGE) / 200;
}
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 */
if (BattData.Vmode == 0) {
/* Lately fully compensation*/
if (BattData.AvgCurrent > 0 &&
BattData.SOC >= 990 &&
BattData.SOC < 995 &&
BattData.AvgCurrent > 100) {
BattData.SOC = 990;
STC311x_SetSOC(99*512);
}
/* Lately empty compensation*/
if (BattData.AvgCurrent < 0 &&
BattData.SOC >= 15 &&
BattData.SOC < 20 &&
BattData.Voltage > (APP_MIN_VOLTAGE+50)) {
BattData.SOC = 20;
STC311x_SetSOC(2*512);
}
}
/* -------- 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;
/* in minutes */
if (GG->AvgCurrent != 0) {
/*avoid divide by 0*/
value = GG->ChargeValue * 60 /
(-GG->AvgCurrent);
if (value < 0)
value = 0;
GG->RemTime = value;
}
} else {
/* means no estimated time available */
GG->RemTime = -1;
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);
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 OK;
}
/*****************************************************************************
* 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) {
/*avoid divide by 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;
}
/*
static int stc311x_read_pmic_adc_temperature(struct stc311x_chip *chip)
{
struct qpnp_vadc_result result;
int res;
// get pm8916 mpp3 adc
if(NULL == chip->vadc_dev) {
chip->vadc_dev = qpnp_get_vadc(chip->dev, "pm8916");
if (IS_ERR(chip->vadc_dev)) {
res = PTR_ERR(chip->vadc_dev);
if (res == -EPROBE_DEFER)
pr_err("stc311x - pm8916 vadc not found - defer rc \n");
else
pr_err("stc311x - fail to get the pm8916 vadc \n");
chip->vadc_dev = NULL;
return -1;
}
}
res=qpnp_vadc_read(chip->vadc_dev, P_MUX3_1_1, &result);//get channel 0x12
if (res < 0) {
pr_err("stc311x - Error reading VADC chennal_12: %d\n", res);
return res;
}
else {
// update the tempetature from pmic adc
pr_debug("stc311x - read pmic mpp3, temperature = %lld \n", result.physical);
chip->Temperature = (result.physical * 10);
}
return 0;
}
*/
/* -------------------------------------------------------------- */
int stc311x_updata(void)
{
struct stc311x_chip *chip = i2c_get_clientdata(sav_client);
power_supply_changed(&chip->battery);
return 0;
}
/* -------------------------------------------------------------- */
void stc311x_check_charger_state(struct stc311x_chip *chip)
{
int rc;
union power_supply_propval ret = {0,};
struct power_supply *charger_psy = power_supply_get_by_name((char *)charger_name);
if (!charger_psy) {
pr_err("%s not registered \n", charger_name);
return;
}
else {
//get charging status
ret.intval = 0;
rc = charger_psy->get_property(charger_psy, POWER_SUPPLY_PROP_STATUS, &ret);
if (rc) {
pr_err("stc311x can't get smb23x register data \n");
return;
} else {
g_last_status = chip->status;
chip->status = ret.intval;
if (chip->status == POWER_SUPPLY_STATUS_DISCHARGING)
return;
}
ret.intval = 0;
rc = charger_psy->get_property(charger_psy,
POWER_SUPPLY_PROP_RESISTANCE, &ret);
if (rc) {
pr_err("stc311x can't get smb23x register data \n");
return;
}
if (ret.intval > 0) {
//if charger setting is reset to default, trigger charger to set new setting
if (ret.intval == SMB231_REG0_DEFAULT) {
pr_err("smb23x register is default, call smb23x to set new setting \n");
ret.intval = 1;
charger_psy->set_property(charger_psy, POWER_SUPPLY_PROP_STATUS, &ret);
}
}
}
}
/*
* 1. If charge is fulled and charger exists, keep soc = 100%
* 2. The moment when charger is removed, if soc = 100% and drops, keep 100%. Else, update soc
* 3. When discharging, soc can only decrease
*/
void UI_soc_adjustment(struct stc311x_chip *chip)
{
pr_err("enter: status = %d, original soc = %d, ST soc = %d \n", chip->status, g_new_soc, chip->batt_soc);
if ((g_new_soc == STC311x_BATTERY_FULL) && (chip->status != POWER_SUPPLY_STATUS_DISCHARGING))
return;
if (chip->status == POWER_SUPPLY_STATUS_DISCHARGING) {
//charger is plugged out
if (g_last_status != POWER_SUPPLY_STATUS_DISCHARGING) {
if (g_new_soc == STC311x_BATTERY_FULL)
return;
else {
g_new_soc = chip->batt_soc;
pr_info("charger plugged out, update SOC = %d \n", g_new_soc);
return;
}
}
//when discharging, the new SOC can only decrease
if (chip->batt_soc > g_new_soc) {
pr_err("Discharging, new soc = %d > original soc = %d, abort \n", chip->batt_soc, g_new_soc);
return;
} else
g_new_soc = chip->batt_soc;
} else
g_new_soc = chip->batt_soc;
}
static void stc311x_work(struct work_struct *work)
{
struct stc311x_chip *chip;
struct GasGauge_DataTypeDef GasGaugeData;
int res, Loop;
chip = container_of(work, struct stc311x_chip, work.work);
sav_client = chip->client;
if (chip->pdata) {
/* 1=Voltage mode, 0=mixed mode */
GasGaugeData.Vmode = chip->pdata->Vmode;
/* SOC alm level %*/
GasGaugeData.Alm_SOC = chip->pdata->Alm_SOC;
/* Vbat alm level mV*/
GasGaugeData.Alm_Vbat = chip->pdata->Alm_Vbat;
/* nominal CC_cnf */
GasGaugeData.CC_cnf = chip->pdata->CC_cnf;
/* nominal VM cnf */
GasGaugeData.VM_cnf = chip->pdata->VM_cnf;
/* nominal Rint */
GasGaugeData.Rint = chip->pdata->Rint;
/* nominal capacity in mAh */
GasGaugeData.Cnom = chip->pdata->Cnom;
/* sense resistor mOhms*/
GasGaugeData.Rsense = chip->pdata->Rsense;
/* current for relaxation in mA (< C/20) */
GasGaugeData.RelaxCurrent = chip->pdata->RelaxCurrent;
/* 1=Adaptive mode enabled, 0=Adaptive mode disabled */
GasGaugeData.Adaptive = chip->pdata->Adaptive;
/* capacity derating in 0.1%, for temp = 60, 40, 25, 10, 0,
* -10 °C */
for (Loop = 0; Loop < NTEMP; Loop++) {
GasGaugeData.CapDerating[Loop] =
chip->pdata->CapDerating[Loop];
}
/* OCV curve adjustment */
for (Loop = 0; Loop < OCVTAB_SIZE; Loop++) {
GasGaugeData.OCVValue[Loop] =
chip->pdata->OCVValue[Loop];
}
/* SOC TAB */
for (Loop = 0; Loop < SOCTAB_SIZE; Loop++) {
GasGaugeData.SOCValue[Loop] =
chip->pdata->SOCValue[Loop];
}
/*External temperature fonction, return °C*/
GasGaugeData.ExternalTemperature =
chip->pdata->ExternalTemperature();
/* 1=External temperature, 0=STC3117 temperature */
GasGaugeData.ForceExternalTemperature =
chip->pdata->ForceExternalTemperature;
}
if (g_debug) {
pr_err(" === Before GasGauge_Task() === \n");
stc311x_debug_info();
}
/* process gas gauge algorithm, returns results */
res = GasGauge_Task(&GasGaugeData);
if (res > 0) {
/* results available */
chip->batt_soc = (GasGaugeData.SOC+5)/10;
chip->batt_voltage = GasGaugeData.Voltage;
chip->batt_current = GasGaugeData.Current;
chip->Temperature = GasGaugeData.Temperature;
} else if (res == 0) {
/* SOC and Voltage available */
chip->batt_soc = (GasGaugeData.SOC+5)/10;
chip->batt_voltage = GasGaugeData.Voltage;
chip->batt_current = 0;
chip->Temperature = 250;
pr_err("GasGauge_Task return (0) \n");
} else if (res == -1) {
chip->batt_voltage = GasGaugeData.Voltage;
chip->batt_soc = (GasGaugeData.SOC+5)/10;
chip->Temperature = 250;
pr_err("GasGauge_Task return (-1) \n");
}
if (g_debug) {
pr_err(" === After GasGauge_Task() === \n");
stc311x_debug_info();
}
stc311x_check_charger_state(chip);
if ((res > 0) && (chip->batt_soc ^ g_new_soc))
UI_soc_adjustment(chip);
stc311x_updata();
if (g_debug)
pr_err("*** ST_SOC = %d, UI_SOC = %d, voltage = %d mv, OCV = %d mv, current = %d mA, Temperature = %d, charging_status = %d *** \n", chip->batt_soc, g_new_soc, chip->batt_voltage, g_ocv, chip->batt_current, chip->Temperature, chip->status);
if (chip->batt_soc > STC311x_SOC_THRESHOLD)
schedule_delayed_work(&chip->work, STC311x_DELAY);
else
schedule_delayed_work(&chip->work, STC311x_DELAY_LOW_BATT);
if (wake_lock_active(&chip->wlock)) {
wake_unlock(&chip->wlock);
pr_info("stc311x_wake_unlock \n");
}
}
static enum power_supply_property stc311x_battery_props[] = {
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TECHNOLOGY,
};
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, reg_mode, reg_ctrl;
struct GasGauge_DataTypeDef GasGaugeData;
/*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);
chip = devm_kzalloc(&client->dev, sizeof(struct stc311x_chip),
GFP_KERNEL);
if (!chip)
return -ENOMEM; /*Out of memory*/
pr_err("\n\nstc311x probe started\n\n");
g_debug = 0;
/* The common I2C client data is placed right specific data. */
chip->client = client;
chip->pdata = &stc3117_data;
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.set_property = stc311x_set_property;
chip->battery.properties = stc311x_battery_props;
chip->battery.num_properties = ARRAY_SIZE(stc311x_battery_props);
chip->battery.property_is_writeable = stc311x_battery_is_writeable;
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) {
/* 1=Voltage mode, 0=mixed mode */
GasGaugeData.Vmode = chip->pdata->Vmode;
/* SOC alm level %*/
GasGaugeData.Alm_SOC = chip->pdata->Alm_SOC;
/* Vbat alm level mV*/
GasGaugeData.Alm_Vbat = chip->pdata->Alm_Vbat;
/* nominal CC_cnf */
GasGaugeData.CC_cnf = chip->pdata->CC_cnf;
/* nominal VM cnf */
GasGaugeData.VM_cnf = chip->pdata->VM_cnf;
/* nominal Rint */
GasGaugeData.Rint = chip->pdata->Rint;
/* nominal capacity in mAh */
GasGaugeData.Cnom = chip->pdata->Cnom;
/* sense resistor mOhms*/
GasGaugeData.Rsense = chip->pdata->Rsense;
/* current for relaxation in mA (< C/20) */
GasGaugeData.RelaxCurrent = chip->pdata->RelaxCurrent;
/* 1=Adaptive mode enabled, 0=Adaptive mode disabled */
GasGaugeData.Adaptive = chip->pdata->Adaptive;
/* capacity derating in 0.1%, for temp
* = 60, 40, 25, 10, 0, -10 °C */
for (Loop = 0; Loop < NTEMP; Loop++) {
GasGaugeData.CapDerating[Loop] =
chip->pdata->CapDerating[Loop];
}
/* OCV curve adjustment */
for (Loop = 0; Loop < OCVTAB_SIZE; Loop++) {
GasGaugeData.OCVValue[Loop] =
chip->pdata->OCVValue[Loop];
}
/* SOC_TAB */
for (Loop = 0; Loop < SOCTAB_SIZE; Loop++) {
GasGaugeData.SOCValue[Loop] =
chip->pdata->SOCValue[Loop];
}
/*External temperature fonction, return °C*/
GasGaugeData.ExternalTemperature =
chip->pdata->ExternalTemperature();
/* 1=External temperature, 0=STC3117 temperature */
GasGaugeData.ForceExternalTemperature =
chip->pdata->ForceExternalTemperature;
}
if (g_debug) {
pr_info(" === Before GasGauge_start() === \n");
stc311x_debug_info();
}
//standby mode check
reg_mode = 0;
reg_ctrl = 0;
g_standby_mode = 0;
reg_mode = STC31xx_ReadByte(STC311x_REG_MODE);
reg_ctrl = STC31xx_ReadByte(STC311x_REG_CTRL);
pr_info("mode = 0x%x, (reg_mode & GG_RUN_BIT) = %d \n", reg_mode, (int)(reg_mode & GG_RUN_BIT));
if(((reg_mode & GG_RUN_BIT) == 0) && ((reg_ctrl & PORDET_BIT) == 0)) {
g_standby_mode = 1;
pr_err("stc311x in standby mode \n");
}
GasGauge_Start(&GasGaugeData);
msleep(200);
if (g_debug) {
pr_info(" === Before GasGauge_Task() === \n");
stc311x_debug_info();
}
/* process gas gauge algorithm, returns results */
res = GasGauge_Task(&GasGaugeData);
if (res > 0) {
/* results available */
chip->batt_soc = (GasGaugeData.SOC+5)/10;
chip->batt_voltage = GasGaugeData.Voltage;
chip->batt_current = GasGaugeData.Current;
chip->Temperature = GasGaugeData.Temperature;
} else if (res == 0) {
/* SOC and Voltage available */
chip->batt_soc = (GasGaugeData.SOC+5)/10;
chip->batt_voltage = GasGaugeData.Voltage;
chip->batt_current = 0;
chip->Temperature = 250;
pr_err("GasGauge_Task return 0 \n");
} else if (res == -1) {
chip->batt_voltage = GasGaugeData.Voltage;
chip->batt_soc = (GasGaugeData.SOC+5)/10;
chip->Temperature = 250;
pr_err("GasGauge_Task return (-1) \n");
}
g_new_soc = chip->batt_soc;
chip->status = POWER_SUPPLY_STATUS_UNKNOWN;
g_last_status = POWER_SUPPLY_STATUS_UNKNOWN;
wake_lock_init(&chip->wlock, WAKE_LOCK_SUSPEND, "stc311x");
if (g_debug) {
pr_info(" === After GasGauge_Task() === \n");
stc311x_debug_info();
}
INIT_DEFERRABLE_WORK(&chip->work, stc311x_work);
//if gauge need to do reset, start work after 5 seconds
if (res == -1)
schedule_delayed_work(&chip->work, 500);
else
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*/
if (g_debug)
pr_err("SOC = %d, voltage = %d, OCV = %d, temp = %d \n", chip->batt_soc, chip->batt_voltage, g_ocv, chip->Temperature);
pr_info("stc311x 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);
wake_lock_destroy(&chip->wlock);
kfree(chip);
return 0;
}
#ifdef CONFIG_PM
static int stc311x_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct stc311x_chip *chip = i2c_get_clientdata(client);
pr_info("stc311x_suspend \n");
cancel_delayed_work(&chip->work);
return 0;
}
static int stc311x_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct stc311x_chip *chip = i2c_get_clientdata(client);
pr_info("stc311x_resume \n");
if (!wake_lock_active(&chip->wlock)) {
wake_lock(&chip->wlock);
pr_info("stc311x_wake_lock \n");
}
schedule_delayed_work(&chip->work, 0);
return 0;
}
static SIMPLE_DEV_PM_OPS(stc311x_pm_ops, stc311x_suspend, stc311x_resume);
#define stc311x_PM_OPS (&stc311x_pm_ops)
#else
#define stc311x_suspend NULL
#define stc311x_resume NULL
#endif /* CONFIG_PM */
static struct of_device_id stc3117_match_table[] = {
{ .compatible = "st,stc3117",},
{ },
};
/* Every chip have a unique id */
static const struct i2c_device_id stc311x_id[] = {
{"stc3117", 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",
.owner = THIS_MODULE,
.pm = stc311x_PM_OPS,
.of_match_table = stc3117_match_table,
},
.probe = stc311x_probe,
.remove = stc311x_remove,
.id_table = stc311x_id,
};
module_i2c_driver(stc311x_i2c_driver);
MODULE_AUTHOR("STMICROELECTRONICS");
MODULE_DESCRIPTION("STC311x Fuel Gauge");
MODULE_LICENSE("GPL");