/* * Initialize the battery cell state structure, * Setup initial parameters and timers * NOTE: make sure *cell is 0-filled! */ void fg_init(struct cell_state *cell, short voltage, short temperature) { unsigned short i; struct timespec ts; cell->fcc = cell->config->design_capacity; cell->qmax = cell->config->design_qmax; cell->new_fcc = cell->fcc; cell->voltage = voltage; cell->prev_voltage = voltage; cell->av_voltage = voltage; for (i = 0; i < AV_SIZE; i++) { av_v[i] = voltage; av_c[i] = 0; } av_v_index = 0; av_c_index = 0; /*Check for faulty temperature sensor error. Valid operating temerature range -40 to +60 degC */ if(temperature < -400 || temperature > 600 ) { dev_dbg(cell->dev, "Battery temperature overrange (%d) using room temperature as default. \n", temperature); cell->temperature = 250; /* 25C room temperature */ } else { cell->temperature = temperature; } /* On init, by-pass temperature debounce check */ cell->seq_cc_temperature = FG_TEMP_CHANGE_COUNT; cell->cycle_count = 1; #ifdef CONFIG_JET_V2 cell->temp_index = -1; cell->current_bounce_counter=0; #endif cell->learned_cycle = cell->cycle_count; cell->prev_soc = -1; /*On init, get correct EDV table from temperature compensation*/ fg_temperature_compensate(cell); /* On init, get SOC from OCV */ fg_ocv(cell); printk(KERN_INFO "FG: Init (%dv, %dmAh, %d%%, %dC)\n", voltage, cell->nac, cell->soc, cell->temperature/10); /* Update EDV flags */ fg_update_edv_flags(cell, true); getrawmonotonic(&ts); cell->last_correction.tv_sec = ts.tv_sec; cell->init = true; }
/* * Initialize the battery cell state structure, * Setup initial parameters and timers * NOTE: make sure *cell is 0-filled! */ void fg_init(struct cell_state *cell, short voltage) { unsigned short i; cell->fcc = cell->config->design_capacity; cell->qmax = cell->config->design_qmax; cell->new_fcc = cell->fcc; cell->voltage = voltage; cell->av_voltage = voltage; for (i = 0; i < AV_SIZE; i++) { av_v[i] = voltage; av_c[i] = 0; } av_v_index = 0; av_c_index = 0; cell->temperature = 200; cell->cycle_count = 1; cell->learned_cycle = cell->cycle_count; cell->prev_soc = -1; /* On init, get SOC from OCV */ fg_ocv(cell); dev_dbg(cell->dev, "FG: Init (%dv, %dmAh, %d%%)\n", voltage, cell->nac, cell->soc); /* Update EDV flags */ fg_update_edv_flags(cell); do_gettimeofday(&cell->last_correction); cell->init = true; }
/* Main FG entry point. This function needs to be called periodically.*/ void fg_process(struct cell_state *cell, short delta_q, short voltage, short cur, short temperature) { int i, tmp; struct timeval now; if (!cell->init) return; /* Update voltage and add it to the buffer, update average*/ tmp = 0; cell->voltage = voltage; av_v_index++; av_v_index %= AV_SIZE; av_v[av_v_index] = voltage; for (i = 0; i < AV_SIZE; i++) tmp += av_v[i]; cell->av_voltage = tmp/AV_SIZE; /* Update current and add it to the buffer, update average*/ tmp = 0; cell->cur = cur; av_c_index++; av_c_index %= AV_SIZE; av_c[av_c_index] = cur; for (i = 0; i < AV_SIZE; i++) tmp += av_c[i]; cell->av_current = tmp/AV_SIZE; /* Update temperature*/ cell->temperature = temperature; /* Check time since last_call */ do_gettimeofday(&now); tmp = now.tv_sec - cell->last_correction.tv_sec; /* Check what capacity currection algorithm should we use: OCV or CC */ if ((tmp > cell->config->ocv->relax_period) && (abs(cell->cur) < cell->config->ocv->long_sleep_current)) { fg_ocv(cell); } else if (fg_check_relaxed(cell)) { /* We are not doing any active CHG/DSG, clear flags this does not compromise learning cycles */ cell->chg = false; cell->dsg = false; /* Checking if we can do an OCV correction */ if (fg_can_ocv(cell)) fg_ocv(cell); else fg_cc(cell, delta_q); } else /* Not Relaxed: actively charging or discharging */ fg_cc(cell, delta_q); /* Charge / Discharge spesific functionality */ if (!cell->sleep) { if (cell->cur > 0) fg_charging(cell, delta_q); else if (cell->cur < 0) fg_discharging(cell, delta_q); } /* Update Regular SOC */ cell->soc = DIV_ROUND_CLOSEST(cell->nac * MAX_PERCENTAGE, cell->fcc); fg_update_edv_flags(cell); /* Check if battery is full */ if (cell->nac >= cell->fcc) { cell->full = true; } else { cell->full = false; if (cell->nac <= (cell->fcc - cell->config->recharge)) cell->cc = false; } /* Checking if we need to set an updated flag (is SOC changed) */ if (cell->prev_soc != cell->soc) { cell->prev_soc = cell->soc; cell->updated = true; } cell->last_correction.tv_sec = now.tv_sec; #ifdef DEBUG /* Printing Debug Data */ dev_dbg(cell->dev, "FG: ACC;%2d ; RM;%4d;mAh ; SOC;%3d;%% ; VOLT;%4d;%4d;mV ; " "CUR;%5d;%5d;mA ; " "EDV;%dmV/%02d%% ; " "LLL;%4d;%4d;%4d ; " "N/O;%4d;%4d ; " "CS;%4d ; EL;%5d ; " "F/C;%d;%d ; CP;%d", delta_q, cell->nac, cell->soc, cell->voltage, cell->av_voltage, cell->cur, cell->av_current, cell->edv.voltage, cell->edv.percent, cell->learn_q, cell->learn_offset, cell->ocv_total_q, cell->negative_q, cell->overcharge_q, cell->cumulative_sleep, cell->electronics_load, cell->fcc, cell->cycle_count, tmp); print_flags(cell); #endif }
/* Main FG entry point. This function needs to be called periodically.*/ void fg_process(struct cell_state *cell, short delta_q, short raw_voltage, short norm_voltage, short cur, short temperature) { int i, tmp; struct timespec now; if (!cell->init) return; /* Update voltage and add it to the buffer, update average*/ tmp = 0; cell->voltage = norm_voltage; av_v_index++; av_v_index %= AV_SIZE; av_v[av_v_index] = norm_voltage; for (i = 0; i < AV_SIZE; i++) { tmp += av_v[i]; } cell->av_voltage = tmp/AV_SIZE; /* Update current and add it to the buffer, update average*/ tmp = 0; cell->cur = cur; av_c_index++; av_c_index %= AV_SIZE; av_c[av_c_index] = cur; for (i = 0; i < AV_SIZE; i++) tmp += av_c[i]; cell->av_current = tmp/AV_SIZE; /* Update temperature*/ cell->temperature = temperature; /*Update FCC and EDV table from temperature compensation*/ fg_temperature_compensate(cell); /* Check time since last_call */ getrawmonotonic(&now); tmp = now.tv_sec - cell->last_correction.tv_sec; /* Check what capacity correction algorithm should we use: OCV or CC */ if ((tmp > cell->config->ocv->relax_period) && (abs(cell->cur) < cell->config->ocv->long_sleep_current)) { #ifdef CONFIG_POWER_SUPPLY_DEBUG printk(KERN_DEBUG "OCV check 1\n"); #endif fg_ocv(cell); } else if (fg_check_relaxed(cell)) { /* We are not doing any active CHG/DSG, clear flags this does not compromise learning cycles */ cell->chg = false; cell->dsg = false; /* Checking if we can do an OCV correction */ if (fg_can_ocv(cell)) fg_ocv(cell); else fg_cc(cell, delta_q); } else /* Not Relaxed: actively charging or discharging */ fg_cc(cell, delta_q); /* Charge / Discharge spesific functionality */ if (!cell->sleep) { if (cell->cur > 0) fg_charging(cell, delta_q); else if (cell->cur < 0) fg_discharging(cell, delta_q); } /* Update Regular SOC */ cell->soc = DIV_ROUND_CLOSEST(cell->nac * MAX_PERCENTAGE, cell->fcc); /* The voltage will vibrate a lot when close to shutdown voltage. This happens especially when running high power * consumption app. So need to take care of the end check */ /*Add here to check both nac and EDV0*/ if(cell->soc > EDV_FIRST_CHECK_POINT+5){ fg_ocv_check(cell); } else if(cell->soc==0){ #ifdef BATTERY_DRAIN_CONFIG cell->soc=1; #else fg_check_end(cell); #endif } /* When SOC is below 15%, check if raw voltage level reached hardware shutdown threshold */ if(cell->soc < EDV_FIRST_CHECK_POINT+5){ fg_check_shutdown_voltage(cell, raw_voltage); } fg_update_edv_flags(cell, false); /* Check if battery is full */ if (cell->cc) { cell->full = true; } if (cell->soc < MAX_PERCENTAGE) { cell->full = false; if (cell->nac <= (cell->fcc - cell->config->recharge)) cell->cc = false; } /* Check if SOC reached 100% and battery is still charging, then keep SOC at 99% */ if((*cell->charge_status == POWER_SUPPLY_STATUS_CHARGING) && (cell->soc == MAX_PERCENTAGE) && !cell->cc && !cell->full) { #ifdef DEBUG printk(KERN_DEBUG "End of charge not reach yet\n"); #endif cell->soc = MAX_PERCENTAGE-1; } /* Checking if we need to set an updated flag (is SOC changed) */ if (cell->prev_soc != cell->soc) { cell->prev_soc = cell->soc; cell->updated = true; } cell->last_correction.tv_sec = now.tv_sec; #if 0 //JET-376 test, run calibration every 5-min to check cc_offset value cal_cntr++; if(cal_cntr >= 30) { cell->calibrate = true; cal_cntr = 0; printk(KERN_DEBUG "PMIC Calibrate now! \n"); } #endif #ifdef DEBUG /* Printing Debug Data */ printk(KERN_DEBUG "FG:ACC:%2d; RM:%4d,mAh; SOC:%3d%%; VOLT:%4d,%4d,mV; " "CUR:%5d,%5d,mA; TEMP:%3d; " "EDV:%d,mV,%02d%%; " "LLL:%4d,%4d,%4d; " "N/O:%4d,%4d; " "CS:%4d; EL:%5d; " "FCC:%d,%d; CP:%d; \n", delta_q, cell->nac, cell->soc, cell->voltage, cell->av_voltage, cell->cur, cell->av_current, cell->temperature, cell->edv.voltage, cell->edv.percent, cell->learn_q, cell->learn_offset, cell->ocv_total_q, cell->negative_q, cell->overcharge_q, cell->cumulative_sleep, cell->electronics_load, cell->fcc, cell->cycle_count, tmp); print_flags(cell); #endif }
static void fg_temperature_compensate(struct cell_state *cell) { int i; #ifdef SOC_CHANGE_ON_TEMPERATURE short fcc_start,uc; short soc_new,soc_start,soc_new_start; #else short soc; #endif int table_size = ARRAY_SIZE(temp_config_table); short temp=cell->temperature; /* Check for faulty temperature sensor error. Valid operating temerature range -40 to +60 degC */ if(temp < -400 || temp > 600 ) { dev_dbg(cell->dev, "Battery temperature overrange (%d) using room temperature as default. \n", temp); i=0; //ENV25 room temp curve } else { //Round to closest integer if(temp>=0) temp=(temp+5)/10; else temp=(temp-5)/10; for(i=0; i<table_size; i++) { if(temp>temp_config_table[i].temp_th) break; } /*if exceed the lowest threshold*/ if(i>=table_size) i=table_size-1; } if(cell->temp_index!=i){//temperature range changed /* temperature debounce check */ cell->seq_cc_temperature++; dev_dbg(cell->dev, "Temperature range change detected #%d [%d:%d %dC]\n", cell->seq_cc_temperature,cell->temp_index,i,temp); } else { cell->seq_cc_temperature = 0; } if (cell->seq_cc_temperature > FG_TEMP_CHANGE_COUNT) { cell->seq_cc_temperature = 0; #ifdef SOC_CHANGE_ON_TEMPERATURE /*scaling fcc and nac due to temperature change*/ fcc_start=cell->fcc; cell->fcc=temp_config_table[i].temp_max_capacity; uc= cell->nac_start - cell->nac; /*Calculate new SOC*/ soc_start=DIV_ROUND_CLOSEST(cell->nac_start * MAX_PERCENTAGE, fcc_start); soc_new_start=DIV_ROUND_CLOSEST(soc_start * cell->fcc, fcc_start); soc_new=soc_new_start - DIV_ROUND_CLOSEST(uc * MAX_PERCENTAGE, cell->fcc); if((cell->soc > EDV_FIRST_CHECK_POINT)&&(soc_new > 0) ) cell->soc= soc_new; /*Else let EDV check to determine the real change here*/ cell->nac=DIV_ROUND_CLOSEST(cell->fcc * cell->soc, MAX_PERCENTAGE); dev_dbg(cell->dev, "Temperature Correction %d:NS%d:SOCn%d\n", temp, cell->nac_start, soc_new); /*Update nac_start*/ cell->nac_start=cell->nac; #else /*SOC didn't change from temperature variation*/ //TODO - reverting back to original code, it prevents the SOC increase when temperature increase. //TODO - needs to revisit when doing temperature changes. if(cell->temp_index>i)//Battery temperature increase { if(fg_current_debounce_check(cell)==false) return; if(cell->cur < 0){ soc=voltage_cap_table(i, cell->av_voltage); cell->prev_voltage=cell->av_voltage; dev_dbg(cell->dev, "Temperature Increase soc=%d\n",soc); if(soc> cell->soc){ #ifdef CONFIG_JET_SUN//Assume sun has more accurate fuel gauge cell->soc=soc; #else cell->soc=(soc+cell->soc)/2; #endif } } } cell->fcc=temp_config_table[i].temp_max_capacity; cell->new_fcc = cell->fcc; //FA found BUG: otherwise overrdes FCC=470 (default) in fg_charging() cell->nac=DIV_ROUND_CLOSEST(cell->fcc * cell->soc, MAX_PERCENTAGE); dev_dbg(cell->dev, "Temperature Correction %dC:FCC:%d,NAC:%d,SOC:%d%%\n", temp,cell->fcc,cell->nac,cell->soc); #endif cell->config->edv->edv[0].voltage=temp_config_table[i].edv_voltage[0]; cell->config->edv->edv[1].voltage=temp_config_table[i].edv_voltage[1]; cell->config->edv->edv[2].voltage=temp_config_table[i].edv_voltage[2]; fg_update_edv_flags(cell, true); cell->temp_index=i; } }