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; }
BOOL do_power_alg(BOOL is_event_triggered) { /* is_event_triggered - TRUE: handle event only, do not update capacity; FALSE; always update capacity*/ static BOOL s_bFirstEntry = TRUE; static UINT32 s_pre_time_ms; static INT32 s_level; UINT32 now_time_ms = BAHW_MyGetMSecs(); #if !HTC_BATTERY_DS2746_DEBUG_ENABLE BOOL show_debug_message = FALSE; #endif /*printk(DRIVER_ZONE "%s(%d) {\n",__func__, is_event_triggered);*/ /*------------------------------------------------------ 1 get battery data and update charge state*/ if (!battery_param_update(&poweralg.battery, &poweralg.protect_flags)){ printk(DRIVER_ZONE "battery_param_update fail, please retry next time.\n"); return FALSE; } update_next_charge_state(); /*----------------------------------------------------- 2 calculate battery capacity (predict if necessary)*/ if (s_bFirstEntry || now_time_ms - s_pre_time_ms > 10000 || !is_event_triggered){ /* DO not update capacity when plug/unplug cable less than 10 seconds*/ __update_capacity(); s_bFirstEntry = FALSE; s_pre_time_ms = now_time_ms; } if (config.debug_disable_shutdown){ if (poweralg.capacity_01p <= 0){ poweralg.capacity_01p = 1; } } s_level = CEILING(poweralg.capacity_01p, 10); #if 0 if (CEILING(poweralg.last_capacity_01p, 10) != s_level || poweralg.battery.last_temp_01c != poweralg.battery.temp_01c) { poweralg.battery.last_temp_01c = poweralg.battery.temp_01c; poweralg.last_capacity_01p = poweralg.capacity_01p; ds2746_blocking_notify(DS2746_LEVEL_UPDATE, &s_level); #if !HTC_BATTERY_DS2746_DEBUG_ENABLE show_debug_message = TRUE; #endif } #endif bounding_fullly_charged_level(config.full_level); /* is_superchg_software_charger_timeout: only triggered when superAC adapter in*/ if (config.superchg_software_charger_timeout_sec && poweralg.is_super_ac && FALSE==poweralg.is_superchg_software_charger_timeout){ super_chg_on_time_sec += delta_time_sec; if (config.superchg_software_charger_timeout_sec <= super_chg_on_time_sec){ printk(DRIVER_ZONE "superchg charger on timer timeout: %u sec\n", super_chg_on_time_sec); poweralg.is_superchg_software_charger_timeout = TRUE; } } /*------------------------------------------------------ 3 charging function change*/ check_charging_function(); /*------------------------------------------------------ 4 debug messages and update battery change to userspace if need */ /*powerlog_to_file(&poweralg);*/ htc_battery_update_change(); #if HTC_BATTERY_DS2746_DEBUG_ENABLE printk(DRIVER_ZONE "S=%d P=%d chg=%d cable=%d%d%d flg=%d%d%d%d dbg=%d%d%d%d fst_dischg=%d/%d [%u]\n", poweralg.charge_state, poweralg.capacity_01p, poweralg.charging_enable, poweralg.is_cable_in, poweralg.is_china_ac_in, poweralg.is_super_ac, poweralg.protect_flags.is_charging_enable_available, poweralg.protect_flags.is_charging_high_current_avaialble, poweralg.protect_flags.is_battery_dead, poweralg.protect_flags.is_temperature_fault, config.debug_disable_shutdown, config.debug_fake_room_temp, config.debug_disable_hw_timer, config.debug_always_predict, poweralg.fst_discharge_capacity_01p, poweralg.fst_discharge_acr_mAh, BAHW_MyGetMSecs()); #else if (show_debug_message == TRUE) printk(DRIVER_ZONE "P=%d V=%d T=%d I=%d ACR=%d/%d KADC=%d charger=%d%d \n", poweralg.capacity_01p, poweralg.battery.voltage_mV, poweralg.battery.temp_01c, poweralg.battery.current_mA, poweralg.battery.charge_counter_mAh, poweralg.battery.charge_full_real_mAh, poweralg.battery.KADC_01p, poweralg.charging_source, poweralg.charging_enable); #endif /*printk(DRIVER_ZONE "};\n");*/ return TRUE; }
static void update_next_charge_state(void) { static UINT32 count_charging_full_condition; static UINT32 count_charge_over_load; int next_charge_state; int i; /* unknown -> prediction -> unknown -> discharge/charging/pending charging -> full-wait-stable -> full-charging -> full-pending full-pending -> full-charging -> charging *(cable in group) -> discharge, charge-pending, dead *(cable out group), full-wait-stable, charge-pending, dead -> charging*/ for (i = 0; i < 25; i++) /* maximun 25 times state transition to prevent from busy loop; ideally the transition time shall be less than 5 times.*/ { /*printk(DRIVER_ZONE "%s.run[%d]: poweralg.charge_state=%d\n", __func__, i,poweralg.charge_state);*/ next_charge_state = poweralg.charge_state; /* 0. enter prediction state or not*/ if (poweralg.charge_state == CHARGE_STATE_UNKNOWN){ if (poweralg.battery.is_prediction || config.debug_always_predict){ if (poweralg.protect_flags.is_battery_dead){ /* keep poweralg.charge_state unchanged, set capacity to 0% directly*/ printk(DRIVER_ZONE " dead battery, p=0%% (skip prediction)\n"); poweralg.capacity_01p = 0; battery_capacity_update(&poweralg.battery, poweralg.capacity_01p); poweralg.fst_discharge_capacity_01p = poweralg.capacity_01p; poweralg.fst_discharge_acr_mAh = poweralg.battery.charge_counter_mAh; } else{ /* battery replaced, recalculate capacity based on battery voltage*/ printk(DRIVER_ZONE " start predict discharge...\n"); next_charge_state = CHARGE_STATE_PREDICTION; } config.debug_always_predict = FALSE; } } if (((poweralg.charge_state == CHARGE_STATE_UNKNOWN) || (poweralg.charge_state == CHARGE_STATE_DISCHARGE)) && (200 < (poweralg.battery.KADC_01p - poweralg.battery.RARC_01p))) { printk(DRIVER_ZONE " KADC[%d] - RARC[%d] > 20%% => start prediction discharge...\n", poweralg.battery.KADC_01p/10, poweralg.battery.RARC_01p/10); next_charge_state = CHARGE_STATE_PREDICTION; poweralg.battery.is_prediction = TRUE; } if (next_charge_state == poweralg.charge_state){ /*---------------------------------------------------------------------------------------------------*/ /* 1. cable in group*/ if (poweralg.charge_state == CHARGE_STATE_UNKNOWN || poweralg.charge_state == CHARGE_STATE_CHARGING || poweralg.charge_state == CHARGE_STATE_PENDING || poweralg.charge_state == CHARGE_STATE_FULL_WAIT_STABLE || poweralg.charge_state == CHARGE_STATE_FULL_CHARGING || poweralg.charge_state == CHARGE_STATE_FULL_RECHARGING || poweralg.charge_state == CHARGE_STATE_FULL_PENDING){ if (!poweralg.is_cable_in){ next_charge_state = CHARGE_STATE_DISCHARGE; } else if (!poweralg.protect_flags.is_charging_enable_available){ next_charge_state = CHARGE_STATE_PENDING; } } /*---------------------------------------------------------------------------------------------------*/ /* 2. cable out group*/ if (poweralg.charge_state == CHARGE_STATE_UNKNOWN || poweralg.charge_state == CHARGE_STATE_DISCHARGE){ if (poweralg.is_cable_in){ next_charge_state = CHARGE_STATE_CHARGING; } } } /*---------------------------------------------------------------------------------------------------*/ /* 3. state handler/transition, if the charge state is not changed due to cable/protect flags*/ if (next_charge_state == poweralg.charge_state){ switch (poweralg.charge_state){ case CHARGE_STATE_PREDICTION: { UINT32 end_time_ms = BAHW_MyGetMSecs(); if (end_time_ms - poweralg.state_start_time_ms >= 50 * 1000){ /* symptom: time will shift a lot at booting */ /* workaround: if time difference is unreasonable large (50 sec), ignore last start time, and reassign it as now. */ poweralg.state_start_time_ms = end_time_ms; printk(DRIVER_ZONE "reassign prediction start timestamp as [%u]\n", end_time_ms); } else if (end_time_ms - poweralg.state_start_time_ms >= config.predict_timeout_sec * 1000){ printk(DRIVER_ZONE "predict done [%u->%u]\n", poweralg.state_start_time_ms, end_time_ms); next_charge_state = CHARGE_STATE_UNKNOWN; } } break; case CHARGE_STATE_CHARGING: if (!poweralg.battery.is_prediction){ /* -> full-charging, pending, dead*/ if (poweralg.capacity_01p > 990){ /* only ever charge-full, the capacity can be larger than 99.0%*/ next_charge_state = CHARGE_STATE_FULL_CHARGING; // MATT: should be FULL_RECHARGING ? } else if (poweralg.battery.voltage_mV >= config.full_charging_mv && poweralg.battery.current_mA >= 0 && poweralg.battery.current_mA <= config.full_charging_ma){ /* meet charge full terminate condition, check again*/ next_charge_state = CHARGE_STATE_FULL_WAIT_STABLE; } } if (poweralg.battery.current_mA <= 0){ /* count_charge_over_load is 5 as max*/ if (count_charge_over_load < 5) count_charge_over_load++; else poweralg.is_charge_over_load = TRUE; } else{ count_charge_over_load = 0; poweralg.is_charge_over_load = FALSE; } /* is_software_charger_timeout: only triggered when AC adapter in*/ if (config.software_charger_timeout_sec && poweralg.is_china_ac_in){ /* software charger timer is enabled; for AC charge only*/ UINT32 end_time_ms = BAHW_MyGetMSecs(); if (end_time_ms - poweralg.state_start_time_ms >= config.software_charger_timeout_sec * 1000){ printk(DRIVER_ZONE "software charger timer timeout [%u->%u]\n", poweralg.state_start_time_ms, end_time_ms); poweralg.is_software_charger_timeout = TRUE; } } /* is_superchg_software_charger_timeout: only triggered when superAC adapter in*/ #if 0 if (config.superchg_software_charger_timeout_sec && poweralg.is_super_ac && FALSE==poweralg.is_superchg_software_charger_timeout){ /* software charger timer is enabled; for superAC charge*/ UINT32 end_time_ms = BAHW_MyGetMSecs(); if (end_time_ms - poweralg.state_start_time_ms >= config.superchg_software_charger_timeout_sec * 1000){ printk(DRIVER_ZONE "superchg software charger timer timeout [%d->%d]\n", poweralg.state_start_time_ms, end_time_ms); poweralg.is_superchg_software_charger_timeout = TRUE; } } #endif /* Software should also toggle MCHG_EN within 4 hrs to prevent charger HW safety timer expired. */ #if 0 if (config.charger_hw_safety_timer_watchdog_sec && poweralg.is_cable_in){ UINT32 end_time_ms = BAHW_MyGetMSecs(); if (end_time_ms - poweralg.last_charger_enable_toggled_time_ms >= config.charger_hw_safety_timer_watchdog_sec * 1000){ poweralg.last_charger_enable_toggled_time_ms = BAHW_MyGetMSecs(); poweralg.is_need_toggle_charger = TRUE; printk(DRIVER_ZONE "need software toggle charger [%d->%d]\n", poweralg.last_charger_enable_toggled_time_ms, end_time_ms); } } #endif break; case CHARGE_STATE_FULL_WAIT_STABLE: { /* -> full-charging, pending, dead*/ if (poweralg.battery.voltage_mV >= config.full_charging_mv && poweralg.battery.current_mA >= 0 && poweralg.battery.current_mA <= config.full_charging_ma){ count_charging_full_condition++; } else{ count_charging_full_condition = 0; next_charge_state = CHARGE_STATE_CHARGING; } if (count_charging_full_condition >= 3){ poweralg.capacity_01p = 1000; battery_capacity_update(&poweralg.battery, poweralg.capacity_01p); next_charge_state = CHARGE_STATE_FULL_CHARGING; } } break; case CHARGE_STATE_FULL_CHARGING: { /* -> full-pending, charging*/ UINT32 end_time_ms = BAHW_MyGetMSecs(); if (poweralg.battery.voltage_mV < config.voltage_exit_full_mv){ if (poweralg.capacity_01p > 990) poweralg.capacity_01p = 990; next_charge_state = CHARGE_STATE_CHARGING; } else if (config.full_pending_ma != 0 && poweralg.battery.current_mA >= 0 && poweralg.battery.current_mA <= config.full_pending_ma){ printk(DRIVER_ZONE " charge-full pending(%dmA)(%u:%u)\n", poweralg.battery.current_mA, poweralg.state_start_time_ms, end_time_ms); next_charge_state = CHARGE_STATE_FULL_PENDING; } else if (end_time_ms - poweralg.state_start_time_ms >= config.full_charging_timeout_sec * 1000){ printk(DRIVER_ZONE " charge-full (expect:%dsec)(%u:%u)\n", config.full_charging_timeout_sec, poweralg.state_start_time_ms, end_time_ms); next_charge_state = CHARGE_STATE_FULL_PENDING; } } break; case CHARGE_STATE_FULL_PENDING: if ((poweralg.battery.voltage_mV >= 0 && poweralg.battery.voltage_mV < config.voltage_recharge_mv) || (poweralg.battery.RARC_01p >= 0 && poweralg.battery.RARC_01p <= config.capacity_recharge_p * 10)){ /* -> full-recharging*/ next_charge_state = CHARGE_STATE_FULL_RECHARGING; } break; case CHARGE_STATE_FULL_RECHARGING: { if (poweralg.battery.voltage_mV < config.voltage_exit_full_mv){ if (poweralg.capacity_01p > 990) poweralg.capacity_01p = 990; next_charge_state = CHARGE_STATE_CHARGING; } else if (poweralg.battery.voltage_mV >= config.full_charging_mv && poweralg.battery.current_mA >= 0 && poweralg.battery.current_mA <= config.full_charging_ma){ /* meet charge full terminate condition, check again*/ next_charge_state = CHARGE_STATE_FULL_CHARGING; } } break; case CHARGE_STATE_PENDING: case CHARGE_STATE_DISCHARGE: { UINT32 end_time_ms = BAHW_MyGetMSecs(); if (!poweralg.is_voltage_stable){ if (end_time_ms - poweralg.state_start_time_ms >= config.wait_votlage_statble_sec * 1000){ printk(DRIVER_ZONE " voltage stable\n"); poweralg.is_voltage_stable = TRUE; } } } if (poweralg.is_cable_in && poweralg.protect_flags.is_charging_enable_available){ /* -> charging*/ next_charge_state = CHARGE_STATE_CHARGING; } break; } } /*---------------------------------------------------------------------------------------------------*/ /* 4. state transition*/ if (next_charge_state != poweralg.charge_state){ /* state exit*/ switch (poweralg.charge_state){ case CHARGE_STATE_UNKNOWN: poweralg.capacity_01p = poweralg.battery.RARC_01p; if (poweralg.capacity_01p > 990) poweralg.capacity_01p = 990; if (poweralg.capacity_01p < 0) poweralg.capacity_01p = 0; poweralg.fst_discharge_capacity_01p = poweralg.capacity_01p; poweralg.fst_discharge_acr_mAh = poweralg.battery.charge_counter_mAh; break; case CHARGE_STATE_PREDICTION: battery_param_update(&poweralg.battery, &poweralg.protect_flags); poweralg.capacity_01p = poweralg.battery.KADC_01p; if (poweralg.capacity_01p > 990) poweralg.capacity_01p = 990; if (poweralg.capacity_01p < 0) poweralg.capacity_01p = 0; battery_capacity_update(&poweralg.battery, poweralg.capacity_01p); poweralg.fst_discharge_capacity_01p = poweralg.capacity_01p; poweralg.fst_discharge_acr_mAh = poweralg.battery.charge_counter_mAh; break; } /* state init*/ poweralg.state_start_time_ms = BAHW_MyGetMSecs(); switch (next_charge_state){ case CHARGE_STATE_DISCHARGE: case CHARGE_STATE_PENDING: /*! star_lee 20100426 - always set ACR=FULL when discharge starts and ACR>FULL*/ if (poweralg.battery.RARC_01p > 1000) battery_capacity_update(&poweralg.battery, 1000); poweralg.is_need_calibrate_at_49p = TRUE; poweralg.is_need_calibrate_at_14p = TRUE; poweralg.fst_discharge_capacity_01p = poweralg.capacity_01p; poweralg.fst_discharge_acr_mAh = poweralg.battery.charge_counter_mAh; poweralg.is_voltage_stable = FALSE; break; case CHARGE_STATE_CHARGING: poweralg.is_need_toggle_charger = FALSE; poweralg.last_charger_enable_toggled_time_ms = BAHW_MyGetMSecs(); poweralg.is_software_charger_timeout = FALSE; /* reset software charger timer every time when charging re-starts*/ poweralg.is_charge_over_load = FALSE; count_charge_over_load = 0; poweralg.battery.charge_full_real_mAh = poweralg.battery.charge_full_design_mAh; battery_capacity_update(&poweralg.battery, poweralg.capacity_01p); break; case CHARGE_STATE_FULL_WAIT_STABLE: /* set to 0 first; the cournter will be add to 1 soon in CHARGE_STATE_FULL_WAIT_STABLE state handler*/ count_charging_full_condition = 0; break; } printk(DRIVER_ZONE " state change(%d->%d), full count=%d, over load count=%d [%u]\n", poweralg.charge_state, next_charge_state, count_charging_full_condition, count_charge_over_load, poweralg.state_start_time_ms); poweralg.charge_state = next_charge_state; continue; } break; } }