53a7287c1b
This table is not absolutly exact, but good
856 lines
25 KiB
C
856 lines
25 KiB
C
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
Copyright (c) 2010 High Tech Computer Corporation
|
|
|
|
Module Name:
|
|
|
|
ds2784_param.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the battery formula based on power spec, including below concepts:
|
|
1. adc converter
|
|
2. voltage mapping to capacity
|
|
3. over temperature algorithm
|
|
4. id range algorithm
|
|
5. ACR maintainance
|
|
|
|
Add from TPE PMA:
|
|
1. temperature index
|
|
2. pd_m_coef_boot
|
|
3. preserved_capacity_by_temp
|
|
Remove from TAO PMA:
|
|
1. pd_temp
|
|
|
|
To adapt different PMA/projects, we need to modify below tables:
|
|
1. ID_RANGE: which battery is used in the project?
|
|
2. FL_25: the full capacity in temp 25C.
|
|
3. pd_m_bias_mA: the discharge current threshold to calculating pd_m
|
|
4. M_PARAMTER_TABLE: the voltage-capacity mapping table
|
|
5. TEMP_RANGE: how many temp condition we need to consider
|
|
6. PD_M_COEF_TABLE(BOOT)/PD_M_RESL_TABLE(BOOT): voltage compensation based on current
|
|
7. PD_T_COEF: voltage compensation based on temp
|
|
8. CAPACITY_DEDUCTION_01p: the capacity deduction due to low temperature
|
|
|
|
Original Auther:
|
|
|
|
Andy.ys Wang June-01-2010
|
|
|
|
---------------------------------------------------------------------------------*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/ds2746_battery.h>
|
|
#include <linux/ds2746_param.h>
|
|
#include <linux/ds2746_param_config.h>
|
|
#include <linux/wrapper_types.h>
|
|
#include <linux/time.h>
|
|
#include <asm/setup.h>
|
|
|
|
/*========================================================================================
|
|
|
|
build flags
|
|
|
|
========================================================================================*/
|
|
|
|
#define HTC_ENABLE_POWER_DEBUG 0
|
|
#define HTC_ENABLE_DUMMY_BATTERY 0
|
|
|
|
/*========================================================================================
|
|
|
|
battery common parameter defines (independent on battery id yet...)
|
|
|
|
========================================================================================*/
|
|
|
|
#define BATTERY_VOLTAGE_MIN 2000
|
|
#define BATTERY_VOLTAGE_MAX 20000
|
|
|
|
/*========================================================================================
|
|
|
|
battery parameter helper functions
|
|
|
|
========================================================================================*/
|
|
|
|
static INT32 get_id_index(struct battery_type *battery)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < BATTERY_ID_NUM - 1; i++) {
|
|
/* minus 1, unknown battery is not in ID_RANGE
|
|
[min, max)*/
|
|
UINT32 resister_min = ID_RANGE[i*2];
|
|
UINT32 resister_max = ID_RANGE[i*2 + 1];
|
|
|
|
if (resister_min <= battery->id_ohm && resister_max > battery->id_ohm) {
|
|
return i + 1;
|
|
}
|
|
}
|
|
|
|
return BATTERY_ID_UNKNOWN;
|
|
}
|
|
|
|
static INT32 get_temp_index(struct battery_type *battery)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 255; i++) {
|
|
/* the table size shall not be greater then 255, to ensure no infinite looping
|
|
[min, max)*/
|
|
INT32 temp = TEMP_RANGE[i];
|
|
if (battery->temp_01c >= temp)
|
|
return i;
|
|
}
|
|
|
|
printk(DRIVER_ZONE " invalid batt_temp (%d) or temp range mapping.\n", battery->temp_01c);
|
|
return -1;
|
|
}
|
|
|
|
/*========================================================================================
|
|
|
|
temperature formula definitions
|
|
|
|
========================================================================================*/
|
|
|
|
static INT32 get_temp_01c(struct battery_type *battery)
|
|
{
|
|
int current_index = battery->temp_check_index;
|
|
int search_direction = 0;
|
|
|
|
if (battery->last_temp_adc > battery->temp_adc) {
|
|
search_direction = -1;
|
|
} else {
|
|
search_direction = 1;
|
|
}
|
|
|
|
while (current_index >= 0 && current_index < TEMP_NUM-1) {
|
|
|
|
UINT32 temp_min = TEMP_MAP[current_index];
|
|
UINT32 temp_max = TEMP_MAP[current_index + 1];
|
|
|
|
if (temp_max > battery->temp_adc && temp_min <= battery->temp_adc) {
|
|
battery->temp_check_index = current_index;
|
|
battery->last_temp_adc = battery->temp_adc;
|
|
return (TEMP_MAX-current_index)*10;
|
|
}
|
|
current_index += search_direction;
|
|
}
|
|
|
|
return (TEMP_MIN-1)*10;
|
|
}
|
|
|
|
/*========================================================================================
|
|
|
|
over temperature protection
|
|
|
|
========================================================================================*/
|
|
|
|
static BOOL is_over_temp(struct battery_type *battery)
|
|
{
|
|
/* stop charging*/
|
|
if (battery->temp_01c < over_low_temp_lock_01c || battery->temp_01c >= over_high_temp_lock_01c) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL is_not_over_temp(struct battery_type *battery)
|
|
{
|
|
/* start charging*/
|
|
if (battery->temp_01c >= over_low_temp_release_01c &&
|
|
battery->temp_01c < over_high_temp_release_01c) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void __protect_flags_update(struct battery_type *battery,
|
|
struct protect_flags_type *flags)
|
|
{
|
|
/* Flags:
|
|
is_charging_enable_available - Over temperature, need to stop charging
|
|
is_charging_high_current_avaialble - Temperature is too high so that we have to slow charge*/
|
|
|
|
if (is_over_temp(battery)) {
|
|
/* Ex: T<0 or T>45 */
|
|
flags->is_charging_enable_available = FALSE;
|
|
flags->is_charging_high_current_avaialble = FALSE;
|
|
#if 0
|
|
flags->is_battery_overtemp = TRUE;
|
|
#endif
|
|
} else if (is_not_over_temp(battery)) {
|
|
/* Ex: T<42 or T>3*/
|
|
flags->is_charging_enable_available = TRUE;
|
|
flags->is_charging_high_current_avaialble = TRUE;
|
|
#if 0
|
|
flags->is_battery_overtemp = FALSE;
|
|
#endif
|
|
}
|
|
|
|
/* Flags:
|
|
is_battery_dead - If battery is dead, show special indicator for it*/
|
|
if (battery->voltage_mV < BATTERY_DEAD_VOLTAGE_LEVEL) {
|
|
flags->is_battery_dead = TRUE;
|
|
}
|
|
else if (battery->voltage_mV > BATTERY_DEAD_VOLTAGE_RELEASE) {
|
|
flags->is_battery_dead = FALSE;
|
|
}
|
|
}
|
|
|
|
/*========================================================================================
|
|
|
|
Voltage-Percentage mapping
|
|
|
|
========================================================================================*/
|
|
|
|
/*------------------------------------------------------------------------
|
|
Example:
|
|
p0 = (4200, 10000); 4.2V for 100%
|
|
p1 = (3900, 8000); 3.9V for 80%
|
|
p2 = (3700, 2000); 3.7V for 20%
|
|
p3 = (3300, 0); 3.3V for 0%
|
|
|
|
if V = 4000, (3900<4000<4200)
|
|
P = (4000-3900) * (10000-8000)/(4200-3900) + 8000 = 8666*/
|
|
|
|
#define NUM_SAMPLED_POINTS_MAX 12
|
|
|
|
struct sampled_point_type {
|
|
|
|
DWORD voltage;
|
|
DWORD capacity;
|
|
};
|
|
|
|
struct voltage_curve_translator {
|
|
|
|
DWORD voltage_min;
|
|
DWORD voltage_max;
|
|
DWORD capacity_min;
|
|
DWORD capacity_max;
|
|
int sampled_point_count;
|
|
struct sampled_point_type sampled_points[NUM_SAMPLED_POINTS_MAX];
|
|
};
|
|
|
|
static void voltage_curve_translator_init(struct voltage_curve_translator *t)
|
|
{
|
|
memset(t, 0, sizeof(*t));
|
|
}
|
|
|
|
static void voltage_curve_translator_add(struct voltage_curve_translator *t, DWORD voltage, DWORD capacity)
|
|
{
|
|
struct sampled_point_type *pt;
|
|
|
|
if (t->sampled_point_count >= NUM_SAMPLED_POINTS_MAX) {
|
|
return;
|
|
}
|
|
|
|
t->sampled_points[t->sampled_point_count].voltage = voltage;
|
|
t->sampled_points[t->sampled_point_count].capacity = capacity;
|
|
pt = &t->sampled_points[t->sampled_point_count];
|
|
|
|
t->sampled_point_count++;
|
|
|
|
if (pt->voltage > t->voltage_max)
|
|
t->voltage_max = pt->voltage;
|
|
if (pt->voltage < t->voltage_min)
|
|
t->voltage_min = pt->voltage;
|
|
if (pt->capacity > t->capacity_max)
|
|
t->capacity_max = pt->capacity;
|
|
if (pt->capacity < t->capacity_min)
|
|
t->capacity_min = pt->capacity;
|
|
|
|
#if HTC_ENABLE_POWER_DEBUG
|
|
printk(DRIVER_ZONE " kadc t: capacity=%d voltage=%d\n", capacity, voltage);
|
|
#endif /* HTC_ENABLE_POWER_DEBUG*/
|
|
}
|
|
|
|
static INT32 voltage_curve_translator_get(struct voltage_curve_translator *t, DWORD voltage)
|
|
{
|
|
struct sampled_point_type *p0, *p1;
|
|
INT32 capacity;
|
|
int i;
|
|
|
|
if (voltage > t->voltage_max)
|
|
voltage = t->voltage_max;
|
|
if (voltage < t->voltage_min)
|
|
voltage = t->voltage_min;
|
|
|
|
p0 = &t->sampled_points[0];
|
|
p1 = p0 + 1;
|
|
for (i = 0; i < t->sampled_point_count - 1 && voltage < p1->voltage; i++) {
|
|
p0++;
|
|
p1++;
|
|
}
|
|
|
|
/* DIV ZERO check*/
|
|
if (p0->voltage - p1->voltage == 0) {
|
|
return 0;
|
|
}
|
|
|
|
/* INT32 overflow check: mv(4200) * capacity(10000), shall be no problem at all...*/
|
|
capacity = (voltage - p1->voltage) * (p0->capacity - p1->capacity) / (p0->voltage - p1->voltage) + p1->capacity;
|
|
if (capacity > t->capacity_max) {
|
|
capacity = t->capacity_max;
|
|
}
|
|
if (capacity < t->capacity_min) {
|
|
capacity = t->capacity_min;
|
|
}
|
|
return capacity;
|
|
}
|
|
|
|
/*========================================================================================
|
|
|
|
KADC mapping functions
|
|
|
|
========================================================================================*/
|
|
|
|
static struct voltage_curve_translator __get_kadc_t;
|
|
|
|
static INT32 get_kadc_001p(struct battery_type *battery)
|
|
{
|
|
INT32 pd_m = 0;
|
|
INT32 pd_temp = 0;
|
|
|
|
INT32 temp_01c = battery->temp_01c;
|
|
INT32 current_mA = battery->current_mA;
|
|
|
|
UINT32 *m_paramtable;
|
|
|
|
INT32 pd_m_coef;
|
|
INT32 pd_m_resl;
|
|
|
|
INT32 capacity_deduction_01p = CAPACITY_DEDUCTION_01p[battery->temp_index];
|
|
INT32 capacity_predict_001p;
|
|
/* 1. INT32 overflow check: assert abs(iChgCurrent_ma) <= 3000, iBattTemp_01c>-250, pd_t_coef <= 1000
|
|
when calculating pd_temp: 0x7FFFFFFF / (500 * 3000 * 1000) =:= 1.4*/
|
|
|
|
if (battery->current_mA > 3000)
|
|
current_mA = 3000;
|
|
else if (battery->current_mA < -3000)
|
|
current_mA = -3000;
|
|
if (battery->temp_01c <= -250)
|
|
temp_01c = -250;
|
|
|
|
/* 2. calculate pd_m and pd_temp*/
|
|
|
|
if (battery->is_power_on_reset) {
|
|
pd_m_coef = PD_M_COEF_TABLE_BOOT[battery->temp_index][battery->id_index];
|
|
pd_m_resl = PD_M_RESL_TABLE_BOOT[battery->temp_index][battery->id_index];
|
|
}
|
|
else{
|
|
pd_m_coef = PD_M_COEF_TABLE[battery->temp_index][battery->id_index];
|
|
pd_m_resl = PD_M_RESL_TABLE[battery->temp_index][battery->id_index];
|
|
}
|
|
|
|
if (battery->current_mA < -pd_m_bias_mA) {
|
|
/* ex: -150mA < -130mA*/
|
|
pd_m = (abs(battery->current_mA) - pd_m_bias_mA) * pd_m_coef /pd_m_resl;
|
|
}
|
|
|
|
if (battery->temp_01c < 250) {
|
|
pd_temp = ((250 - battery->temp_01c) * (abs(battery->current_mA) * PD_T_COEF[battery->id_index])) / (10 * 10000);
|
|
}
|
|
battery->pd_m = pd_m;
|
|
|
|
/* 3. calculate KADC using M_PARAMTER_TABLE*/
|
|
|
|
m_paramtable = M_PARAMTER_TABLE[battery->id_index];
|
|
if (m_paramtable) {
|
|
int i = 0; /* assume that m_paramtable has at least 2 items...the last capacity item must be 0 to end the loop...*/
|
|
|
|
voltage_curve_translator_init(&__get_kadc_t);
|
|
|
|
while (1) {
|
|
INT32 capacity = m_paramtable[i];
|
|
INT32 voltage = m_paramtable[i + 1];
|
|
if (capacity == 10000) {
|
|
/* full capacity, no need to fix voltage level*/
|
|
voltage_curve_translator_add(&__get_kadc_t, voltage, capacity);
|
|
}
|
|
else {
|
|
voltage_curve_translator_add(&__get_kadc_t, voltage - pd_temp, capacity);
|
|
}
|
|
|
|
if (capacity == 0)
|
|
break;
|
|
|
|
i += 2;
|
|
}
|
|
|
|
#if HTC_ENABLE_POWER_DEBUG
|
|
printk(DRIVER_ZONE " pd_m=%d, pd_temp=%d\n", pd_m, pd_temp);
|
|
#endif /* HTC_ENABLE_POWER_DEBUG*/
|
|
|
|
capacity_predict_001p = voltage_curve_translator_get(&__get_kadc_t, battery->voltage_mV + pd_m);
|
|
}
|
|
else{
|
|
capacity_predict_001p = (battery->voltage_mV - 3400) * 10000 / (4200 - 3400);
|
|
}
|
|
|
|
return (capacity_predict_001p - capacity_deduction_01p * 10) * 10000 / (10000 - capacity_deduction_01p * 10);
|
|
}
|
|
|
|
/*========================================================================================
|
|
|
|
coulomb counter+curve tracer
|
|
|
|
========================================================================================*/
|
|
|
|
static INT32 get_software_acr_revise(struct battery_type *battery, UINT32 ms)
|
|
{
|
|
INT32 kadc_01p = battery->KADC_01p;
|
|
INT32 ccbi_01p = battery->RARC_01p;
|
|
INT32 delta_01p = kadc_01p - ccbi_01p;
|
|
|
|
DWORD C = 5; /* KADC = 15%~100%*/
|
|
if (kadc_01p <= 150) {
|
|
C = 5;
|
|
} /* KADC = 0%~15%*/
|
|
|
|
if (delta_01p < 0) {
|
|
/* if KADC is less than RARC, p shall be lower*/
|
|
return -(INT32) (C * ms * delta_01p * delta_01p) / 1000;
|
|
}
|
|
else{
|
|
/* if KADC is larger than RARC, p shall be higher*/
|
|
return (INT32) (C * ms * delta_01p * delta_01p) / 1000;
|
|
}
|
|
}
|
|
|
|
/*========================================================================================
|
|
|
|
ds2746 gauge ic functions, to access ds2746 registers and convert ADC to battery param
|
|
|
|
========================================================================================*/
|
|
|
|
static void __ds2746_clear_porf(void)
|
|
{
|
|
UINT8 reg_data;
|
|
if (!ds2746_i2c_read_u8(®_data, 0x01)) {
|
|
printk(DRIVER_ZONE " clear porf error in read.\n");
|
|
return;
|
|
}
|
|
|
|
if (!ds2746_i2c_write_u8((reg_data & (~DS2746_STATUS_PORF)), 0x01)) {
|
|
printk(DRIVER_ZONE " clear porf error in write.\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void __ds2746_acr_update(struct battery_type *battery, int capacity_01p)
|
|
{
|
|
printk(DRIVER_ZONE " acr update: P=%d, C=%d.\n",
|
|
capacity_01p,
|
|
battery->charge_counter_adc);
|
|
|
|
ds2746_i2c_write_u8((battery->charge_counter_adc & 0xFF00) >> 8, 0x10);
|
|
ds2746_i2c_write_u8((battery->charge_counter_adc & 0x00FF), 0x11);
|
|
|
|
if (battery->is_power_on_reset) {
|
|
__ds2746_clear_porf();
|
|
}
|
|
}
|
|
|
|
static void __ds2746_init_config(struct battery_type *battery)
|
|
{
|
|
UINT8 reg_data;
|
|
|
|
if (!ds2746_i2c_read_u8(®_data, 0x01)) {
|
|
printk(DRIVER_ZONE " init config error in read.\n");
|
|
return;
|
|
}
|
|
|
|
/* Erase SMOD and NBEN value in DS2746 status/config register*/
|
|
reg_data &= ~(DS2746_STATUS_SMOD | DS2746_STATUS_NBEN);
|
|
if (!ds2746_i2c_write_u8(reg_data, 0x01)) {
|
|
printk(DRIVER_ZONE " init config error in write.\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
static BOOL __ds2746_get_reg_data(UINT8 *reg)
|
|
{
|
|
memset(reg, 0, 12);
|
|
|
|
if (!ds2746_i2c_read_u8(®[0], 0x01))
|
|
return FALSE;
|
|
if (!ds2746_i2c_read_u8(®[2], 0x08))
|
|
return FALSE;
|
|
if (!ds2746_i2c_read_u8(®[3], 0x09))
|
|
return FALSE;
|
|
if (!ds2746_i2c_read_u8(®[4], 0x0a))
|
|
return FALSE;
|
|
if (!ds2746_i2c_read_u8(®[5], 0x0b))
|
|
return FALSE;
|
|
if (!ds2746_i2c_read_u8(®[6], 0x0c))
|
|
return FALSE;
|
|
if (!ds2746_i2c_read_u8(®[7], 0x0d))
|
|
return FALSE;
|
|
if (!ds2746_i2c_read_u8(®[8], 0x0e))
|
|
return FALSE;
|
|
if (!ds2746_i2c_read_u8(®[9], 0x0f))
|
|
return FALSE;
|
|
if (!ds2746_i2c_read_u8(®[10], 0x10))
|
|
return FALSE;
|
|
if (!ds2746_i2c_read_u8(®[11], 0x11))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL __ds2746_battery_adc_udpate(struct battery_type *battery)
|
|
{
|
|
UINT8 reg[12];
|
|
|
|
if (!__ds2746_get_reg_data((UINT8 *) ®)) {
|
|
printk(DRIVER_ZONE " get ds2746 data failed...\n");
|
|
return FALSE;
|
|
}
|
|
|
|
printk(DRIVER_ZONE " [x0]%x [x8]%x %x %x %x %x %x %x %x %x %x\n",
|
|
reg[0],
|
|
reg[2],
|
|
reg[3],
|
|
reg[4],
|
|
reg[5],
|
|
reg[6],
|
|
reg[7],
|
|
reg[8],
|
|
reg[9],
|
|
reg[10],
|
|
reg[11]);
|
|
|
|
if (!(reg[0] & DS2746_STATUS_AIN0) || !(reg[0] & DS2746_STATUS_AIN1)) {
|
|
printk(DRIVER_ZONE " AIN not ready...\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if (reg[0] & DS2746_STATUS_PORF) {
|
|
battery->is_power_on_reset = TRUE;
|
|
}
|
|
else{
|
|
battery->is_power_on_reset = FALSE;
|
|
}
|
|
|
|
/* adc register value*/
|
|
battery->voltage_adc = MAKEWORD(reg[7], reg[6]) >> 4;
|
|
battery->current_adc = MAKEWORD(reg[9], reg[8]);
|
|
if (battery->current_adc & 0x8000) {
|
|
battery->current_adc = -(0x10000 - battery->current_adc);
|
|
}
|
|
battery->current_adc /= 4;
|
|
battery->charge_counter_adc = MAKEWORD(reg[11], reg[10]);
|
|
if (battery->charge_counter_adc & 0x8000) {
|
|
battery->charge_counter_adc = -(0x10000 - battery->charge_counter_adc);
|
|
}
|
|
battery->id_adc = MAKEWORD(reg[5], reg[4]) >> 4;
|
|
battery->temp_adc = MAKEWORD(reg[3], reg[2]) >> 4;
|
|
if (support_ds2746_gauge_ic) {
|
|
/* we preserve 500mAh for capacity lower than 0%, however the 500mAh is still drained out...we need to do predict for correct ACR*/
|
|
if ((battery->charge_counter_adc & 0xFFFF) >= 0xF000){
|
|
printk(DRIVER_ZONE " ACR out of range (x%x)...\n",
|
|
battery->charge_counter_adc);
|
|
battery->is_power_on_reset = TRUE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*========================================================================================
|
|
|
|
softwar acr functions, to accumulate ACR by software and revise by battery parameter
|
|
|
|
========================================================================================*/
|
|
|
|
static void __software_charge_counter_update(struct battery_type *battery, UINT32 ms)
|
|
{
|
|
/* if the charge counter is maintained by sw, batt_alg shall use this routine to update charge counter and related parameters*/
|
|
INT32 capacity_deduction_01p = CAPACITY_DEDUCTION_01p[battery->temp_index];
|
|
/* AEL(mAh): A low temp unusable battery capacity, calculated in runtime*/
|
|
INT32 ael_mAh = capacity_deduction_01p *battery->charge_full_real_mAh / 1000;
|
|
#if HTC_ENABLE_POWER_DEBUG
|
|
printk(DRIVER_ZONE "chgctr update: I=%d ms=%d.\n", battery->current_mA, ms);
|
|
#endif /* HTC_ENABLE_POWER_DEBUG*/
|
|
|
|
/* ACRt(mAh): The total capacity battery owns, stored in battery->charge_counter_mAh*/
|
|
battery->software_charge_counter_mAms += (INT32) (battery->current_mA * ms);
|
|
battery->charge_counter_mAh += (battery->software_charge_counter_mAms / 3600000);
|
|
battery->software_charge_counter_mAms -= (battery->software_charge_counter_mAms / 3600000) * 3600000;
|
|
|
|
/* CCBI(0.1%): A software RARC*/
|
|
battery->RARC_01p = (battery->charge_counter_mAh - ael_mAh) * 1000 / (battery->charge_full_real_mAh - ael_mAh);
|
|
/* store back the battery->charge_counter_mAh to battery->charge_counter_adc*/
|
|
battery->charge_counter_adc = (battery->charge_counter_mAh + charge_counter_zero_base_mAh) * acr_adc_to_mv_coef / acr_adc_to_mv_resl;
|
|
}
|
|
|
|
static void __software_charge_counter_revise(struct battery_type *battery, UINT32 ms)
|
|
{
|
|
if (battery->current_mA < 0) {
|
|
#if HTC_ENABLE_POWER_DEBUG
|
|
printk(DRIVER_ZONE "chgctr revise: delta=%d.\n", get_software_acr_revise(battery, ms));
|
|
#endif /* HTC_ENABLE_POWER_DEBUG*/
|
|
|
|
/* revise software charge counter by coulomb counter+curve tracer*/
|
|
battery->software_charge_counter_mAms += get_software_acr_revise(battery, ms);
|
|
battery->charge_counter_mAh += (battery->software_charge_counter_mAms / 3600000);
|
|
battery->software_charge_counter_mAms -= (battery->software_charge_counter_mAms / 3600000) * 3600000;
|
|
/* store back the battery->charge_counter_mAh to battery->charge_counter_adc*/
|
|
battery->charge_counter_adc = (battery->charge_counter_mAh + charge_counter_zero_base_mAh) * acr_adc_to_mv_coef / acr_adc_to_mv_resl;
|
|
}
|
|
}
|
|
|
|
static void __software_acr_update(struct battery_type *battery)
|
|
{
|
|
static BOOL s_bFirstEntry = TRUE;
|
|
static DWORD last_time_ms;
|
|
DWORD now_time_ms = BAHW_MyGetMSecs();
|
|
|
|
if (s_bFirstEntry) {
|
|
s_bFirstEntry = FALSE;
|
|
last_time_ms = now_time_ms;
|
|
}
|
|
|
|
#if HTC_ENABLE_POWER_DEBUG
|
|
printk(DRIVER_ZONE "+acr update: adc=%d C=%d mams=%d.\n",
|
|
battery->charge_counter_adc,
|
|
battery->charge_counter_mAh,
|
|
battery->software_charge_counter_mAms);
|
|
#endif /* HTC_ENABLE_POWER_DEBUG*/
|
|
|
|
__software_charge_counter_update(battery, now_time_ms - last_time_ms);
|
|
__software_charge_counter_revise(battery, now_time_ms - last_time_ms);
|
|
|
|
#if HTC_ENABLE_POWER_DEBUG
|
|
printk(DRIVER_ZONE "-acr update: adc=%d C=%d mams=%d.\n",
|
|
battery->charge_counter_adc,
|
|
battery->charge_counter_mAh,
|
|
battery->software_charge_counter_mAms);
|
|
#endif /* HTC_ENABLE_POWER_DEBUG*/
|
|
|
|
last_time_ms = now_time_ms;
|
|
}
|
|
|
|
/*========================================================================================
|
|
|
|
battery param update, the coef are referenced from power spec
|
|
|
|
========================================================================================*/
|
|
|
|
static BOOL __battery_param_udpate(struct battery_type *battery)
|
|
{
|
|
static int batt_id_stable_counter = 0;
|
|
INT32 batt_id_index;
|
|
INT32 temp_01c;
|
|
|
|
if (support_ds2746_gauge_ic) {
|
|
/* adc register value are read from __ds2746_battery_adc_udpate()*/
|
|
if (!__ds2746_battery_adc_udpate(battery))
|
|
return FALSE;
|
|
}
|
|
else{
|
|
/* adc register value are read from BAHW_get_batt_info_all()
|
|
if ( !BAHW_get_batt_info_all(battery) ) return FALSE;*/
|
|
}
|
|
|
|
/*real physical value*/
|
|
battery->voltage_mV = (battery->voltage_adc * voltage_adc_to_mv_coef / voltage_adc_to_mv_resl);
|
|
battery->current_mA = (battery->current_adc * current_adc_to_mv_coef / current_adc_to_mv_resl);
|
|
battery->discharge_mA = (battery->discharge_adc * discharge_adc_to_mv_coef / discharge_adc_to_mv_resl);
|
|
battery->charge_counter_mAh = (battery->charge_counter_adc * acr_adc_to_mv_coef / acr_adc_to_mv_resl) - charge_counter_zero_base_mAh;
|
|
battery->current_mA = battery->current_mA - battery->discharge_mA;
|
|
/* prevent from adc out of range*/
|
|
if (battery->id_adc >= id_adc_resl) {
|
|
battery->id_adc = id_adc_resl - 1;
|
|
}
|
|
if (battery->id_adc <= 0) {
|
|
battery->id_adc = 1;
|
|
}
|
|
if (battery->temp_adc >= temp_adc_resl) {
|
|
battery->temp_adc = temp_adc_resl - 1;
|
|
}
|
|
if (battery->temp_adc <= 0) {
|
|
battery->temp_adc = 1;
|
|
}
|
|
|
|
/* battery ID shall be ready first for temp/kadc calculation*/
|
|
// if ( id_conversion ) battery->id_ohm = ((float)id_R_kohm / ((float)id_adc_resl/battery->id_adc - 1)) * 1000; // kohm -> ohm
|
|
// else battery->id_ohm = battery->id_adc;
|
|
battery->id_ohm = battery->id_adc;
|
|
calibrate_id_ohm(battery);
|
|
|
|
batt_id_index = get_id_index(battery);
|
|
|
|
if (is_allow_batt_id_change) {
|
|
/*! TODO: batt_id changes immediately; may need to modify in future*/
|
|
if (batt_id_stable_counter >= 3 && batt_id_index != battery->id_index){
|
|
/* if batt_id is stable but is different from previous one*/
|
|
batt_id_stable_counter = 0; /* reset stable counter and set batt_id to new one*/
|
|
}
|
|
}
|
|
|
|
if (batt_id_stable_counter < 3) {
|
|
if (batt_id_stable_counter == 0) {
|
|
/* first time to get the batt id*/
|
|
battery->id_index = batt_id_index;
|
|
battery->charge_full_design_mAh = FL_25[battery->id_index];
|
|
battery->charge_full_real_mAh = battery->charge_full_design_mAh;
|
|
batt_id_stable_counter = 1;
|
|
}
|
|
else{
|
|
/* 2nd and further time to get the batt id*/
|
|
if (batt_id_index == battery->id_index)
|
|
batt_id_stable_counter++;
|
|
else
|
|
batt_id_stable_counter = 0;
|
|
}
|
|
}
|
|
|
|
/* calculate temperature*/
|
|
// battery->temp_01c = get_temp_c((float)temp_R_kohm / ((float)temp_adc_resl/battery->temp_adc - 1))*10;
|
|
temp_01c = get_temp_01c(battery);
|
|
if (temp_01c >= TEMP_MIN*10)
|
|
battery->temp_01c = temp_01c;
|
|
else
|
|
printk(DRIVER_ZONE " get temp_01c(%d) failed...\n", temp_01c);
|
|
battery->temp_index = get_temp_index(battery);
|
|
|
|
/* calculate KADC and RARC*/
|
|
battery->KADC_01p = CEILING(get_kadc_001p(battery), 10);
|
|
battery->RARC_01p = CEILING(10000 * battery->charge_counter_mAh / battery->charge_full_real_mAh, 10);
|
|
if (!support_ds2746_gauge_ic) {
|
|
__software_acr_update(battery);
|
|
}
|
|
|
|
if (battery->voltage_mV <BATTERY_VOLTAGE_MIN ||
|
|
battery->voltage_mV> BATTERY_VOLTAGE_MAX) {
|
|
printk(DRIVER_ZONE " invalid V(%d).\n", battery->voltage_mV);
|
|
return FALSE;
|
|
}
|
|
|
|
/*! star_lee 20100426 - minimum RARC is 0%*/
|
|
if (battery->RARC_01p <= 0) {
|
|
battery->RARC_01p = 0;
|
|
}
|
|
|
|
printk(DRIVER_ZONE " V=%d(%x) I=%d(%x) C=%d.%d/%d(%x) id=%d(%x) T=%d(%x) KADC=%d\n",
|
|
battery->voltage_mV,
|
|
battery->voltage_adc,
|
|
battery->current_mA,
|
|
battery->current_adc,
|
|
battery->charge_counter_mAh,
|
|
battery->software_charge_counter_mAms,
|
|
battery->charge_full_real_mAh,
|
|
battery->charge_counter_adc,
|
|
battery->id_index,
|
|
battery->id_adc,
|
|
battery->temp_01c,
|
|
battery->temp_adc,
|
|
battery->KADC_01p);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*========================================================================================
|
|
|
|
time functions
|
|
|
|
========================================================================================*/
|
|
|
|
DWORD BAHW_MyGetMSecs(void)
|
|
{
|
|
struct timespec now;
|
|
getnstimeofday(&now);
|
|
/*struct timespec t;
|
|
t.tv_sec = t.tv_nsec = 0;
|
|
clock_gettime(CLOCK_MONOTONIC, &t);*/
|
|
return now.tv_sec * 1000 + now.tv_nsec / 1000000;
|
|
}
|
|
|
|
/*========================================================================================
|
|
|
|
battery param public function
|
|
|
|
========================================================================================*/
|
|
|
|
void battery_capacity_update(struct battery_type *battery, int capacity_01p)
|
|
{
|
|
|
|
/* ACR 500~500+FULL mapping to capacity 0~FULL*/
|
|
battery->charge_counter_mAh = capacity_01p * battery->charge_full_real_mAh / 1000;
|
|
battery->charge_counter_adc = (battery->charge_counter_mAh + charge_counter_zero_base_mAh) * acr_adc_to_mv_resl / acr_adc_to_mv_coef;
|
|
battery->RARC_01p = capacity_01p;
|
|
if (support_ds2746_gauge_ic) {
|
|
__ds2746_acr_update(battery, capacity_01p);
|
|
}
|
|
|
|
printk(DRIVER_ZONE "new RARC=%d C=%dmAh adc=%d.\n",
|
|
battery->RARC_01p,
|
|
battery->charge_counter_mAh,
|
|
battery->charge_counter_adc);
|
|
battery->is_power_on_reset = FALSE;
|
|
}
|
|
|
|
BOOL battery_param_update(struct battery_type *battery, struct protect_flags_type *flags)
|
|
{
|
|
if (!__battery_param_udpate(battery)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (flags->is_fake_room_temp) {
|
|
battery->temp_01c = 250;
|
|
printk(DRIVER_ZONE "fake temp=%d(%x)\n",
|
|
battery->temp_01c,
|
|
battery->temp_adc);
|
|
}
|
|
__protect_flags_update(battery, flags);
|
|
|
|
#if ! HTC_ENABLE_DUMMY_BATTERY
|
|
if (battery->id_index == BATTERY_ID_UNKNOWN) {
|
|
flags->is_charging_enable_available = FALSE;
|
|
}
|
|
#else /* HTC_ENABLE_DUMMY_BATTERY*/
|
|
/* do not disable charging for debug stage*/
|
|
flags->is_charging_enable_available = TRUE;
|
|
#endif /* HTC_ENABLE_DUMMY_BATTERY*/
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void battery_param_init(struct battery_type *battery)
|
|
{
|
|
/* set battery id to unknown to get battery id and related characters*/
|
|
battery->id_index = BATTERY_ID_UNKNOWN;
|
|
|
|
/* default to 25C unless we can get valid battery temp from adc*/
|
|
battery->temp_01c = 250;
|
|
battery->last_temp_01c = battery->temp_01c;
|
|
battery->temp_check_index = 0;
|
|
battery->last_temp_adc = 0;
|
|
|
|
battery->voltage_mV = 3800;
|
|
|
|
/* this is used when accumulate current by software; initial it as 0mAs*/
|
|
battery->software_charge_counter_mAms = 0;
|
|
|
|
/* set POR at first by software; gauge ic will has correct value*/
|
|
battery->is_power_on_reset = TRUE;
|
|
|
|
if (support_ds2746_gauge_ic) {
|
|
__ds2746_init_config(battery);
|
|
}
|
|
|
|
if (battery->thermal_id == THERMAL_1000) {
|
|
TEMP_MAP = TEMP_MAP_1000K;
|
|
printk(DRIVER_ZONE "Use 1000 Kohm thermal resistance");
|
|
} else if (battery->thermal_id == THERMAL_600) {
|
|
TEMP_MAP = TEMP_MAP_600K;
|
|
printk(DRIVER_ZONE "Use 600 Kohm thermal resistance");
|
|
} else {
|
|
printk(DRIVER_ZONE "Use default(300 Kohm) thermal resistance");
|
|
}
|
|
|
|
/*printk(DRIVER_ZONE "battery param inited with board name <%s>\n", HTC_BATT_BOARD_NAME);*/
|
|
}
|
|
|