881 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			881 lines
		
	
	
		
			26 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;
 | |
| 	}
 | |
| 
 | |
| #if HTC_ENABLE_POWER_DEBUG
 | |
| 	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]);
 | |
| #endif
 | |
| 
 | |
| 	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;
 | |
| #if HTC_ENABLE_POWER_DEBUG
 | |
| 	printk(DRIVER_ZONE " battery.id_adc pre clip: %d\n", battery->id_adc);
 | |
| 	printk(DRIVER_ZONE " battery.temp_adc pre clip: %d\n", battery->temp_adc);
 | |
| #endif
 | |
| 	/* 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->temp_adc;
 | |
| #if HTC_ENABLE_POWER_DEBUG
 | |
| 	printk(DRIVER_ZONE " battery.id_ohm pre calibrate: %d\n", battery->id_ohm);
 | |
| #endif /* HTC_ENABLE_POWER_DEBUG*/
 | |
| 	//calibrate_id_ohm(battery);
 | |
| #if HTC_ENABLE_POWER_DEBUG
 | |
| 	printk(DRIVER_ZONE " battery.id_ohm post calibrate: %d\n", battery->id_ohm);
 | |
| #endif /* HTC_ENABLE_POWER_DEBUG*/
 | |
| 
 | |
| 	batt_id_index = get_id_index(battery);
 | |
| #if HTC_ENABLE_POWER_DEBUG
 | |
| 	printk(DRIVER_ZONE " batt_id: %d\n", batt_id_index);
 | |
| #endif /* HTC_ENABLE_POWER_DEBUG*/
 | |
| 	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;
 | |
| 		}
 | |
| 	}
 | |
| 	temp_01c = get_temp_01c(battery);
 | |
| 	if (batt_id_index == 5)
 | |
| 	{
 | |
| 		//battery->temp_adc = battery->temp_adc * 7;
 | |
| 		temp_01c = 650 - battery->temp_adc*10;
 | |
| 	}
 | |
| 	/* calculate temperature*/
 | |
| 	//    battery->temp_01c 			  = get_temp_c((float)temp_R_kohm / ((float)temp_adc_resl/battery->temp_adc - 1))*10;
 | |
| 	if(temp_01c == 700)
 | |
| 		{
 | |
| 		FL_25[battery->id_index] = 2300;
 | |
| 		battery->charge_full_real_mAh = FL_25[battery->id_index];
 | |
| 		battery->charge_full_design_mAh = battery->charge_full_real_mAh;
 | |
| 		temp_01c = 650 - battery->temp_adc*10;
 | |
| 		}
 | |
| 	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);*/
 | |
| }
 | |
| 
 |