blob: f752ecfb51a669b32926ceec302594912d38f887 [file] [log] [blame]
/*
* sec_battery_thermal.c
* Samsung Mobile Battery Driver
*
* Copyright (C) 2019 Samsung Electronics
*
*
* 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.
*/
#include "include/sec_battery.h"
#if defined(CONFIG_PREVENT_USB_CONN_OVERHEAT)
extern int muic_set_hiccup_mode(int on_off);
#endif
char *sec_bat_thermal_zone[] = {
"COLD",
"COOL3",
"COOL2",
"COOL1",
"NORMAL",
"WARM",
"OVERHEAT",
"OVERHEATLIMIT",
};
//#if defined(CONFIG_DUAL_BATTERY)
int sec_bat_get_high_priority_temp(struct sec_battery_info *battery)
{
int standard_temp = 250;
int priority_temp = battery->temperature;
if (is_wireless_type(battery->cable_type) && battery->temperature >= 400)
priority_temp -= 5;
if((battery->temperature > standard_temp) && (battery->sub_bat_temp > standard_temp)) {
if(battery->temperature < battery->sub_bat_temp)
priority_temp = battery->sub_bat_temp;
} else {
if(battery->temperature > battery->sub_bat_temp)
priority_temp = battery->sub_bat_temp;
}
pr_info("%s priority_temp = %d \n", __func__, priority_temp);
return priority_temp;
}
//#endif
void sec_bat_check_mix_temp(struct sec_battery_info *battery)
{
int temperature = battery->pdata->blkt_temp_check_type ? battery->blkt_temp : battery->temperature;
int chg_temp;
int input_current = 0;
if (battery->pdata->temp_check_type == SEC_BATTERY_TEMP_CHECK_NONE ||
battery->pdata->chg_temp_check_type == SEC_BATTERY_TEMP_CHECK_NONE)
return;
#if defined(CONFIG_DIRECT_CHARGING)
if (is_pd_apdo_wire_type(battery->wire_status) && battery->pd_list.now_isApdo)
chg_temp = battery->dchg_temp;
else
chg_temp = battery->chg_temp;
#else
chg_temp = battery->chg_temp;
#endif
#if defined(CONFIG_DUAL_BATTERY)
temperature = sec_bat_get_high_priority_temp(battery);
#endif
if (battery->siop_level >= 100 && !battery->lcd_status && is_not_wireless_type(battery->cable_type)) {
if ((!battery->mix_limit && (temperature >= battery->pdata->mix_high_temp) &&
(chg_temp >= battery->pdata->mix_high_chg_temp)) ||
(battery->mix_limit && (temperature > battery->pdata->mix_high_temp_recovery))) {
int max_input_current = battery->pdata->full_check_current_1st + 50;
/* input current = float voltage * (topoff_current_1st + 50mA(margin)) / (vbus_level * 0.9) */
input_current = ((battery->pdata->chg_float_voltage / battery->pdata->chg_float_voltage_conv) *
max_input_current) / (battery->input_voltage * 90) / 10;
if (input_current > max_input_current)
input_current = max_input_current;
battery->mix_limit = true;
/* skip other heating control */
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_SKIP_HEATING_CONTROL,
SEC_BAT_CURRENT_EVENT_SKIP_HEATING_CONTROL);
sec_vote(battery->input_vote, VOTER_MIX_LIMIT, true, input_current);
#if defined(CONFIG_WIRELESS_TX_MODE)
if (battery->wc_tx_enable) {
pr_info("%s @Tx_Mode enter mix_temp_limit, TX mode should turn off \n", __func__);
sec_bat_set_tx_event(battery, BATT_TX_EVENT_WIRELESS_TX_HIGH_TEMP,
BATT_TX_EVENT_WIRELESS_TX_HIGH_TEMP);
battery->tx_retry_case |= SEC_BAT_TX_RETRY_MIX_TEMP;
sec_wireless_set_tx_enable(battery, false);
}
#endif
} else if (battery->mix_limit) {
battery->mix_limit = false;
sec_vote(battery->input_vote, VOTER_MIX_LIMIT, false, 0);
if (battery->tx_retry_case & SEC_BAT_TX_RETRY_MIX_TEMP) {
pr_info("%s @Tx_Mode recovery mix_temp_limit, TX mode should be retried \n", __func__);
if ((battery->tx_retry_case & ~SEC_BAT_TX_RETRY_MIX_TEMP) == 0)
sec_bat_set_tx_event(battery, BATT_TX_EVENT_WIRELESS_TX_RETRY,
BATT_TX_EVENT_WIRELESS_TX_RETRY);
battery->tx_retry_case &= ~SEC_BAT_TX_RETRY_MIX_TEMP;
}
}
pr_info("%s: mix_limit(%d), temp(%d), chg_temp(%d), input_current(%d)\n",
__func__, battery->mix_limit, temperature, chg_temp, input_current);
} else {
if (battery->mix_limit) {
battery->mix_limit = false;
sec_vote(battery->input_vote, VOTER_MIX_LIMIT, false, 0);
}
}
}
static int sec_bat_get_temp_by_temp_control_source(struct sec_battery_info *battery, int tcs)
{
switch (tcs) {
case TEMP_CONTROL_SOURCE_CHG_THM:
return battery->chg_temp;
case TEMP_CONTROL_SOURCE_USB_THM:
return battery->usb_temp;
case TEMP_CONTROL_SOURCE_WPC_THM:
return battery->wpc_temp;
case TEMP_CONTROL_SOURCE_NONE:
case TEMP_CONTROL_SOURCE_BAT_THM:
default:
return battery->temperature;
}
}
void sec_bat_check_wpc_temp(struct sec_battery_info *battery)
{
int input_current = INT_MAX, charging_current = INT_MAX;
if (battery->pdata->wpc_temp_check_type == SEC_BATTERY_TEMP_CHECK_NONE)
return;
if (is_wireless_type(battery->cable_type)) {
union power_supply_propval value = {0, };
int wpc_vout_level = WIRELESS_VOUT_10V;
mutex_lock(&battery->voutlock);
/* get vout level */
psy_do_property(battery->pdata->wireless_charger_name, get,
POWER_SUPPLY_EXT_PROP_WIRELESS_RX_VOUT, value);
if(is_hv_wireless_type(battery->cable_type) &&
value.intval == WIRELESS_VOUT_5_5V_STEP &&
battery->wpc_vout_level != WIRELESS_VOUT_5_5V_STEP) {
pr_info("%s: real vout was not 10V \n", __func__);
battery->wpc_vout_level = WIRELESS_VOUT_5_5V_STEP;
}
if (battery->siop_level >= 100 && !battery->lcd_status) {
int temp_val = sec_bat_get_temp_by_temp_control_source(battery,
battery->pdata->wpc_temp_control_source);
if ((!battery->chg_limit && temp_val >= battery->pdata->wpc_high_temp) ||
(battery->chg_limit && temp_val > battery->pdata->wpc_high_temp_recovery)) {
battery->chg_limit = true;
if(input_current > battery->pdata->wpc_input_limit_current) {
if (battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_TX &&
battery->pdata->wpc_input_limit_by_tx_check)
input_current = battery->pdata->wpc_input_limit_current_by_tx;
else
input_current = battery->pdata->wpc_input_limit_current;
}
if(charging_current > battery->pdata->wpc_charging_limit_current)
charging_current = battery->pdata->wpc_charging_limit_current;
wpc_vout_level = WIRELESS_VOUT_5_5V_STEP;
} else if (battery->chg_limit) {
battery->chg_limit = false;
}
} else {
if ((is_hv_wireless_type(battery->cable_type) &&
battery->cable_type != SEC_BATTERY_CABLE_WIRELESS_HV_VEHICLE) ||
battery->cable_type == SEC_BATTERY_CABLE_PREPARE_WIRELESS_HV ||
battery->cable_type == SEC_BATTERY_CABLE_PREPARE_WIRELESS_20) {
int temp_val = sec_bat_get_temp_by_temp_control_source(battery,
battery->pdata->wpc_temp_lcd_on_control_source);
if ((!battery->chg_limit &&
temp_val >= battery->pdata->wpc_lcd_on_high_temp) ||
(battery->chg_limit &&
temp_val > battery->pdata->wpc_lcd_on_high_temp_rec)) {
if(input_current > battery->pdata->wpc_lcd_on_input_limit_current)
input_current = battery->pdata->wpc_lcd_on_input_limit_current;
if(charging_current > battery->pdata->wpc_charging_limit_current)
charging_current = battery->pdata->wpc_charging_limit_current;
battery->chg_limit = true;
wpc_vout_level = (battery->capacity < 95) ?
WIRELESS_VOUT_5_5V_STEP : WIRELESS_VOUT_10V;
} else if (battery->chg_limit) {
battery->chg_limit = false;
}
} else if (battery->chg_limit) {
battery->chg_limit = false;
}
}
if (is_hv_wireless_type(battery->cable_type)) {
#if defined(CONFIG_ISDB_CHARGING_CONTROL)
if ((battery->current_event & SEC_BAT_CURRENT_EVENT_HIGH_TEMP_SWELLING) ||
(battery->current_event & SEC_BAT_CURRENT_EVENT_ISDB))
#else
if (battery->current_event & SEC_BAT_CURRENT_EVENT_HIGH_TEMP_SWELLING)
#endif
wpc_vout_level = WIRELESS_VOUT_5_5V_STEP;
if (wpc_vout_level != battery->wpc_vout_level) {
battery->wpc_vout_level = wpc_vout_level;
if (battery->current_event & SEC_BAT_CURRENT_EVENT_WPC_VOUT_LOCK) {
pr_info("%s: block to set wpc vout level(%d) because otg on\n",
__func__, wpc_vout_level);
} else {
value.intval = wpc_vout_level;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, value);
pr_info("%s: change vout level(%d)",
__func__, battery->wpc_vout_level);
sec_vote(battery->input_vote, VOTER_AICL, false, 0);
}
} else if (battery->wpc_vout_level == WIRELESS_VOUT_10V && !battery->chg_limit)
/* reset aicl current to recover current for unexpected aicl during */
/* before vout boosting completion */
sec_vote(battery->input_vote, VOTER_AICL, false, 0);
}
mutex_unlock(&battery->voutlock);
pr_info("%s: vout_level(%d), chg_limit(%d)\n",
__func__, battery->wpc_vout_level, battery->chg_limit);
if (battery->chg_limit) {
if ((battery->siop_level >= 100 && !battery->lcd_status) &&
(input_current > battery->pdata->wpc_input_limit_current))
input_current = battery->pdata->wpc_input_limit_current;
else if ((battery->siop_level < 100 || battery->lcd_status) &&
(input_current > battery->pdata->wpc_lcd_on_input_limit_current))
input_current = battery->pdata->wpc_lcd_on_input_limit_current;
}
if (input_current != INT_MAX) {
sec_vote(battery->input_vote, VOTER_CHG_TEMP, true, input_current);
sec_vote(battery->fcc_vote, VOTER_CHG_TEMP, true, charging_current);
} else {
sec_vote(battery->fcc_vote, VOTER_CHG_TEMP, false, 0);
sec_vote(battery->input_vote, VOTER_CHG_TEMP, false, 0);
}
}
}
void sec_bat_check_tx_temperature(struct sec_battery_info *battery)
{
if (battery->wc_tx_enable) {
if(battery->temperature >= battery->pdata->tx_high_threshold) {
pr_info("@Tx_Mode : %s: Battery temperature is too high. Tx mode should turn off \n", __func__);
/* set tx event */
sec_bat_set_tx_event(battery, BATT_TX_EVENT_WIRELESS_TX_HIGH_TEMP, BATT_TX_EVENT_WIRELESS_TX_HIGH_TEMP);
battery->tx_retry_case |= SEC_BAT_TX_RETRY_HIGH_TEMP;
sec_wireless_set_tx_enable(battery, false);
} else if (battery->temperature <= battery->pdata->tx_low_threshold) {
pr_info("@Tx_Mode : %s: Battery temperature is too low. Tx mode should turn off \n", __func__);
/* set tx event */
sec_bat_set_tx_event(battery, BATT_TX_EVENT_WIRELESS_TX_LOW_TEMP, BATT_TX_EVENT_WIRELESS_TX_LOW_TEMP);
battery->tx_retry_case |= SEC_BAT_TX_RETRY_LOW_TEMP;
sec_wireless_set_tx_enable(battery, false);
}
} else if (battery->tx_retry_case & SEC_BAT_TX_RETRY_HIGH_TEMP) {
if (battery->temperature <= battery->pdata->tx_high_recovery) {
pr_info("@Tx_Mode : %s: Battery temperature goes to normal(High). Retry TX mode\n", __func__);
battery->tx_retry_case &= ~SEC_BAT_TX_RETRY_HIGH_TEMP;
if (!battery->tx_retry_case)
sec_bat_set_tx_event(battery, BATT_TX_EVENT_WIRELESS_TX_RETRY, BATT_TX_EVENT_WIRELESS_TX_RETRY);
}
} else if (battery->tx_retry_case & SEC_BAT_TX_RETRY_LOW_TEMP) {
if (battery->temperature >= battery->pdata->tx_low_recovery) {
pr_info("@Tx_Mode : %s: Battery temperature goes to normal(Low). Retry TX mode\n", __func__);
battery->tx_retry_case &= ~SEC_BAT_TX_RETRY_LOW_TEMP;
if (!battery->tx_retry_case)
sec_bat_set_tx_event(battery, BATT_TX_EVENT_WIRELESS_TX_RETRY, BATT_TX_EVENT_WIRELESS_TX_RETRY);
}
}
}
#if defined(CONFIG_DIRECT_CHARGING) && defined(CONFIG_CCIC_NOTIFIER)
void sec_bat_check_direct_chg_temp(struct sec_battery_info *battery)
{
int input_current = 0, charging_current = 0;
if (battery->pdata->dchg_temp_check_type == SEC_BATTERY_TEMP_CHECK_NONE)
return;
if (battery->siop_level >= 100 && !battery->lcd_status) {
if (!battery->chg_limit && battery->pd_list.now_isApdo &&
(battery->dchg_temp >= battery->pdata->dchg_high_temp)) {
input_current = battery->pdata->dchg_input_limit_current;
charging_current = battery->pdata->dchg_charging_limit_current;
battery->chg_limit = true;
sec_vote(battery->fcc_vote, VOTER_CHG_TEMP, battery->chg_limit, charging_current);
sec_vote(battery->input_vote, VOTER_CHG_TEMP, battery->chg_limit, input_current);
} else if (!battery->chg_limit && (!battery->pd_list.now_isApdo) &&
(battery->chg_temp >= battery->pdata->chg_high_temp)) {
if (battery->input_voltage == SEC_INPUT_VOLTAGE_5V) {
input_current = battery->pdata->default_input_current;
charging_current = battery->pdata->default_charging_current;
} else {
input_current = battery->pdata->chg_input_limit_current;
charging_current = battery->pdata->chg_charging_limit_current;
}
battery->chg_limit = true;
sec_vote(battery->fcc_vote, VOTER_CHG_TEMP, battery->chg_limit, charging_current);
sec_vote(battery->input_vote, VOTER_CHG_TEMP, battery->chg_limit, input_current);
} else if (battery->chg_limit) {
if (((battery->dchg_temp <= battery->pdata->dchg_high_temp_recovery) &&
battery->pd_list.now_isApdo) || ((battery->chg_temp <= battery->pdata->chg_high_temp_recovery) &&
(!battery->pd_list.now_isApdo))) {
battery->chg_limit = false;
sec_vote(battery->fcc_vote, VOTER_CHG_TEMP, battery->chg_limit, charging_current);
sec_vote(battery->input_vote, VOTER_CHG_TEMP, battery->chg_limit, input_current);
} else {
if (battery->pd_list.now_isApdo) {
input_current = battery->pdata->dchg_input_limit_current;
charging_current = battery->pdata->dchg_charging_limit_current;
} else {
if (battery->input_voltage == SEC_INPUT_VOLTAGE_5V) {
input_current = battery->pdata->default_input_current;
charging_current = battery->pdata->default_charging_current;
} else {
input_current = battery->pdata->chg_input_limit_current;
charging_current = battery->pdata->chg_charging_limit_current;
}
}
battery->chg_limit = true;
sec_vote(battery->fcc_vote, VOTER_CHG_TEMP, battery->chg_limit, charging_current);
sec_vote(battery->input_vote, VOTER_CHG_TEMP, battery->chg_limit, input_current);
}
}
pr_info("%s: cable_type(%d), chg_limit(%d) vbus_by_siop(%d)\n", __func__,
battery->cable_type, battery->chg_limit, battery->vbus_chg_by_siop);
}
}
#endif
#if defined(CONFIG_CCIC_NOTIFIER)
void sec_bat_check_pdic_temp(struct sec_battery_info *battery)
{
int input_current, charging_current;
if (battery->pdata->chg_temp_check_type == SEC_BATTERY_TEMP_CHECK_NONE)
return;
if (battery->pdic_ps_rdy && battery->siop_level >= 100 && !battery->lcd_status) {
if ((!battery->chg_limit && (battery->chg_temp >= battery->pdata->chg_high_temp)) ||
(battery->chg_limit && (battery->chg_temp >= battery->pdata->chg_high_temp_recovery))) {
input_current =
(battery->pdata->chg_input_limit_current * SEC_INPUT_VOLTAGE_9V) / battery->input_voltage;
charging_current = battery->pdata->chg_charging_limit_current;
sec_vote(battery->fcc_vote, VOTER_PDIC_TEMP, true, charging_current);
sec_vote(battery->input_vote, VOTER_PDIC_TEMP, true, input_current);
battery->chg_limit = true;
} else if (battery->chg_limit && battery->chg_temp <= battery->pdata->chg_high_temp_recovery) {
sec_vote(battery->fcc_vote, VOTER_PDIC_TEMP, false, 0);
sec_vote(battery->input_vote, VOTER_PDIC_TEMP, false, 0);
battery->chg_limit = false;
}
pr_info("%s: cable_type(%d), chg_limit(%d)\n", __func__,
battery->cable_type, battery->chg_limit);
}
}
#endif
void sec_bat_check_afc_temp(struct sec_battery_info *battery)
{
int input_current = 0, charging_current = 0;
if (battery->pdata->chg_temp_check_type == SEC_BATTERY_TEMP_CHECK_NONE)
return;
#if defined(CONFIG_SUPPORT_HV_CTRL)
if (battery->siop_level >= 100 && !battery->lcd_status) {
if (!battery->chg_limit && is_hv_wire_type(battery->cable_type) &&
(battery->chg_temp >= battery->pdata->chg_high_temp)) {
input_current = battery->pdata->chg_input_limit_current;
charging_current = battery->pdata->chg_charging_limit_current;
battery->chg_limit = true;
sec_vote(battery->fcc_vote, VOTER_CHG_TEMP, battery->chg_limit, charging_current);
sec_vote(battery->input_vote, VOTER_CHG_TEMP, battery->chg_limit, input_current);
} else if (!battery->chg_limit && battery->max_charge_power >= (battery->pdata->pd_charging_charge_power - 500) &&
(battery->chg_temp >= battery->pdata->chg_high_temp)) {
input_current = battery->pdata->default_input_current;
charging_current = battery->pdata->default_charging_current;
battery->chg_limit = true;
sec_vote(battery->fcc_vote, VOTER_CHG_TEMP, battery->chg_limit, charging_current);
sec_vote(battery->input_vote, VOTER_CHG_TEMP, battery->chg_limit, input_current);
} else if (battery->chg_limit && is_hv_wire_type(battery->cable_type)) {
if (battery->chg_temp <= battery->pdata->chg_high_temp_recovery) {
battery->chg_limit = false;
sec_vote(battery->fcc_vote, VOTER_CHG_TEMP, battery->chg_limit, charging_current);
sec_vote(battery->input_vote, VOTER_CHG_TEMP, battery->chg_limit, input_current);
}
} else if (battery->chg_limit && battery->max_charge_power >= (battery->pdata->pd_charging_charge_power - 500)) {
if (battery->chg_temp <= battery->pdata->chg_high_temp_recovery) {
battery->chg_limit = false;
sec_vote(battery->fcc_vote, VOTER_CHG_TEMP, battery->chg_limit, charging_current);
sec_vote(battery->input_vote, VOTER_CHG_TEMP, battery->chg_limit, input_current);
}
}
pr_info("%s: cable_type(%d), chg_limit(%d) vbus_by_siop(%d)\n", __func__,
battery->cable_type, battery->chg_limit, battery->vbus_chg_by_siop);
}
#else
if ((!battery->chg_limit && is_hv_wire_type(battery->cable_type) &&
(battery->chg_temp >= battery->pdata->chg_high_temp)) ||
(battery->chg_limit && is_hv_wire_type(battery->cable_type) &&
(battery->chg_temp > battery->pdata->chg_high_temp_recovery))) {
if (!battery->chg_limit) {
input_current = battery->pdata->chg_input_limit_current;
charging_current = battery->pdata->chg_charging_limit_current;
sec_vote(battery->fcc_vote, VOTER_AFC_TEMP, true, charging_current);
sec_vote(battery->input_vote, VOTER_AFC_TEMP, true, input_current);
battery->chg_limit = true;
}
} else if (battery->chg_limit && is_hv_wire_type(battery->cable_type) &&
(battery->chg_temp <= battery->pdata->chg_high_temp_recovery)) {
sec_vote(battery->fcc_vote, VOTER_AFC_TEMP, false, 0);
sec_vote(battery->input_vote, VOTER_AFC_TEMP, false, 0);
battery->chg_limit = false;
}
#endif
}
void sec_bat_set_threshold(struct sec_battery_info *battery)
{
if (is_wireless_type(battery->cable_type) || battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_FAKE) {
battery->cold_cool3_thresh = battery->pdata->wireless_cold_cool3_thresh;
battery->cool3_cool2_thresh = battery->pdata->wireless_cool3_cool2_thresh;
battery->cool2_cool1_thresh = battery->pdata->wireless_cool2_cool1_thresh;
battery->cool1_normal_thresh = battery->pdata->wireless_cool1_normal_thresh;
battery->normal_warm_thresh = battery->pdata->wireless_normal_warm_thresh;
battery->warm_overheat_thresh = battery->pdata->wireless_warm_overheat_thresh;
} else {
battery->cold_cool3_thresh = battery->pdata->wire_cold_cool3_thresh;
battery->cool3_cool2_thresh = battery->pdata->wire_cool3_cool2_thresh;
battery->cool2_cool1_thresh = battery->pdata->wire_cool2_cool1_thresh;
battery->cool1_normal_thresh = battery->pdata->wire_cool1_normal_thresh;
battery->normal_warm_thresh = battery->pdata->wire_normal_warm_thresh;
battery->warm_overheat_thresh = battery->pdata->wire_warm_overheat_thresh;
}
}
bool sec_usb_thm_overheatlimit(struct sec_battery_info *battery)
{
#if defined(CONFIG_PREVENT_USB_CONN_OVERHEAT)
int gap = 0;
int bat_thm = battery->temperature;
#endif
if (battery->pdata->usb_temp_check_type == SEC_BATTERY_TEMP_CHECK_NONE) {
pr_err("%s: USB_THM, Invalid Temp Check Type, usb_thm <- bat_thm\n", __func__);
battery->usb_temp = battery->temperature;
}
if (battery->usb_thm_status == USB_THM_NORMAL) {
#if defined(CONFIG_PREVENT_USB_CONN_OVERHEAT)
#if defined(CONFIG_DUAL_BATTERY)
/* select low temp thermistor */
if (battery->temperature > battery->sub_bat_temp)
bat_thm = battery->sub_bat_temp;
#endif
if (battery->usb_temp > bat_thm)
gap = battery->usb_temp - bat_thm;
#endif
if (battery->usb_temp >= battery->overheatlimit_threshold) {
pr_info("%s: Usb Temp over than %d (usb_thm : %d)\n", __func__,
battery->overheatlimit_threshold, battery->usb_temp);
battery->usb_thm_status = USB_THM_OVERHEATLIMIT;
return true;
#if defined(CONFIG_PREVENT_USB_CONN_OVERHEAT)
} else if ((battery->usb_temp >= battery->usb_protection_temp) &&
(gap >= battery->temp_gap_bat_usb)) {
pr_info("%s: Temp gap between Usb temp and Bat temp : %d\n", __func__, gap);
#if defined(CONFIG_BATTERY_CISD)
if (gap > battery->cisd.data[CISD_DATA_USB_OVERHEAT_ALONE_PER_DAY])
battery->cisd.data[CISD_DATA_USB_OVERHEAT_ALONE_PER_DAY] = gap;
#endif
battery->usb_thm_status = USB_THM_GAP_OVER;
return true;
#endif
} else {
battery->usb_thm_status = USB_THM_NORMAL;
return false;
}
} else if (battery->usb_thm_status == USB_THM_OVERHEATLIMIT) {
if (battery->usb_temp <= battery->overheatlimit_recovery) {
battery->usb_thm_status = USB_THM_NORMAL;
return false;
} else {
return true;
}
#if defined(CONFIG_PREVENT_USB_CONN_OVERHEAT)
} else if (battery->usb_thm_status == USB_THM_GAP_OVER) {
if (battery->usb_temp < battery->usb_protection_temp) {
battery->usb_thm_status = USB_THM_NORMAL;
return false;
} else {
return true;
}
#endif
} else {
battery->usb_thm_status = USB_THM_NORMAL;
return false;
}
return false;
}
#define THERMAL_HYSTERESIS_2 19
#define THERMAL_HYSTERESIS_5 49
void sec_bat_thermal_check(struct sec_battery_info *battery)
{
int bat_thm = battery->temperature;
int pre_thermal_zone = battery->thermal_zone;
int voter_swelling_status = SEC_BAT_CHG_MODE_CHARGING;
#if defined(CONFIG_DUAL_BATTERY)
bat_thm = sec_bat_get_high_priority_temp(battery);
#endif
pr_err("%s: co_c3: %d, c3_c2: %d, c2_c1: %d, c1_no: %d, no_wa: %d, wa_ov: %d, tz(%s)\n", __func__,
battery->cold_cool3_thresh, battery->cool3_cool2_thresh, battery->cool2_cool1_thresh,
battery->cool1_normal_thresh, battery->normal_warm_thresh, battery->warm_overheat_thresh,
sec_bat_thermal_zone[battery->thermal_zone]);
if (battery->status == POWER_SUPPLY_STATUS_DISCHARGING ||
battery->skip_swelling) {
battery->health_change = false;
pr_debug("%s: DISCHARGING or 15 test mode. stop thermal check\n", __func__);
battery->thermal_zone = BAT_THERMAL_NORMAL;
sec_vote(battery->topoff_vote, VOTER_SWELLING, false, 0);
sec_vote(battery->fcc_vote, VOTER_SWELLING, false, 0);
sec_vote(battery->fv_vote, VOTER_SWELLING, false, 0);
sec_vote(battery->chgen_vote, VOTER_SWELLING, false, 0);
sec_bat_set_current_event(battery, 0, SEC_BAT_CURRENT_EVENT_SWELLING_MODE);
sec_bat_set_threshold(battery);
return;
}
if (battery->pdata->temp_check_type == SEC_BATTERY_TEMP_CHECK_NONE) {
pr_err("%s: BAT_THM, Invalid Temp Check Type\n", __func__);
return;
} else {
/* COLD - COOL3 - COOL2 - COOL1 - NORMAL - WARM - OVERHEAT - OVERHEATLIMIT*/
if (sec_usb_thm_overheatlimit(battery)) {
battery->thermal_zone = BAT_THERMAL_OVERHEATLIMIT;
} else if (bat_thm >= battery->normal_warm_thresh) {
if (bat_thm >= battery->warm_overheat_thresh) {
battery->thermal_zone = BAT_THERMAL_OVERHEAT;
} else {
battery->thermal_zone = BAT_THERMAL_WARM;
}
} else if (bat_thm <= battery->cool1_normal_thresh) {
if (bat_thm <= battery->cold_cool3_thresh) {
battery->thermal_zone = BAT_THERMAL_COLD;
} else if (bat_thm <= battery->cool3_cool2_thresh) {
battery->thermal_zone = BAT_THERMAL_COOL3;
} else if (bat_thm <= battery->cool2_cool1_thresh) {
battery->thermal_zone = BAT_THERMAL_COOL2;
} else {
battery->thermal_zone = BAT_THERMAL_COOL1;
}
} else {
battery->thermal_zone = BAT_THERMAL_NORMAL;
}
}
if (pre_thermal_zone != battery->thermal_zone) {
battery->bat_thm_count++;
if (battery->bat_thm_count < battery->pdata->temp_check_count) {
pr_info("%s : bat_thm_count %d/%d\n", __func__,
battery->bat_thm_count, battery->pdata->temp_check_count);
battery->thermal_zone = pre_thermal_zone;
return;
}
pr_info("%s: thermal zone update (%s -> %s), bat_thm(%d), usb_thm(%d)\n", __func__,
sec_bat_thermal_zone[pre_thermal_zone],
sec_bat_thermal_zone[battery->thermal_zone], bat_thm, battery->usb_temp);
battery->health_change = true;
battery->bat_thm_count = 0;
pr_info("%s : SAFETY TIME RESET!\n", __func__);
battery->expired_time = battery->pdata->expired_time;
battery->prev_safety_time = 0;
sec_bat_set_threshold(battery);
sec_bat_set_current_event(battery, 0, SEC_BAT_CURRENT_EVENT_SWELLING_MODE);
switch (battery->thermal_zone) {
case BAT_THERMAL_OVERHEATLIMIT:
battery->warm_overheat_thresh -= THERMAL_HYSTERESIS_2;
battery->normal_warm_thresh -= THERMAL_HYSTERESIS_2;
if (battery->usb_thm_status == USB_THM_OVERHEATLIMIT) {
pr_info("%s: USB_THM_OVERHEATLIMIT\n", __func__);
#if defined(CONFIG_BATTERY_CISD)
battery->cisd.data[CISD_DATA_USB_OVERHEAT_CHARGING]++;
battery->cisd.data[CISD_DATA_USB_OVERHEAT_CHARGING_PER_DAY]++;
#endif
#if defined(CONFIG_PREVENT_USB_CONN_OVERHEAT)
} else if (battery->usb_thm_status == USB_THM_GAP_OVER) {
pr_info("%s: USB_THM_GAP_OVER : %d\n", __func__, gap);
#if defined(CONFIG_BATTERY_CISD)
battery->cisd.data[CISD_DATA_USB_OVERHEAT_RAPID_CHANGE]++;
battery->cisd.data[CISD_DATA_USB_OVERHEAT_RAPID_CHANGE_PER_DAY]++;
#endif
#endif
}
battery->health = POWER_SUPPLY_HEALTH_OVERHEATLIMIT;
sec_bat_set_charging_status(battery, POWER_SUPPLY_STATUS_NOT_CHARGING);
sec_vote(battery->chgen_vote, VOTER_SWELLING, true, SEC_BAT_CHG_MODE_BUCK_OFF);
#if defined(CONFIG_BATTERY_CISD)
battery->cisd.data[CISD_DATA_UNSAFETY_TEMPERATURE]++;
battery->cisd.data[CISD_DATA_UNSAFE_TEMPERATURE_PER_DAY]++;
#endif
#if defined(CONFIG_PREVENT_USB_CONN_OVERHEAT)
if (lpcharge) {
if (is_hv_afc_wire_type(battery->cable_type) && !battery->vbus_limit) {
#if defined(CONFIG_MUIC_HV) || defined(CONFIG_SUPPORT_HV_CTRL)
battery->vbus_chg_by_siop = SEC_INPUT_VOLTAGE_0V;
muic_afc_set_voltage(SEC_INPUT_VOLTAGE_0V);
#endif
battery->vbus_limit = true;
pr_info("%s: Set AFC TA to 0V\n", __func__);
} else if (is_pd_wire_type(battery->cable_type)) {
select_pdo(1);
pr_info("%s: Set PD TA to PDO 0\n", __func__);
}
} else if (is_pd_wire_type(battery->cable_type)) {
select_pdo(1);
muic_set_hiccup_mode(1);
} else {
muic_set_hiccup_mode(1);
}
#endif
break;
case BAT_THERMAL_OVERHEAT:
battery->swelling_mode = true;
battery->usb_thm_status = USB_THM_NORMAL;
battery->warm_overheat_thresh -= THERMAL_HYSTERESIS_2;
battery->normal_warm_thresh -= THERMAL_HYSTERESIS_2;
sec_vote(battery->chgen_vote, VOTER_SWELLING, true, SEC_BAT_CHG_MODE_CHARGING_OFF);
battery->health = POWER_SUPPLY_HEALTH_OVERHEAT;
sec_bat_set_charging_status(battery, POWER_SUPPLY_STATUS_NOT_CHARGING);
#if defined(CONFIG_BATTERY_CISD)
battery->cisd.data[CISD_DATA_UNSAFETY_TEMPERATURE]++;
battery->cisd.data[CISD_DATA_UNSAFE_TEMPERATURE_PER_DAY]++;
#endif
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_HIGH_TEMP_SWELLING,
SEC_BAT_CURRENT_EVENT_SWELLING_MODE);
break;
case BAT_THERMAL_WARM:
battery->swelling_mode = true;
battery->usb_thm_status = USB_THM_NORMAL;
battery->normal_warm_thresh -= THERMAL_HYSTERESIS_2;
if (is_wireless_type(battery->cable_type) ||
battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_FAKE) {
sec_vote(battery->fcc_vote, VOTER_SWELLING, true, battery->pdata->wireless_warm_current);
#if defined(CONFIG_BATTERY_CISD)
battery->cisd.data[CISD_DATA_WC_HIGH_TEMP_SWELLING]++;
battery->cisd.data[CISD_DATA_WC_HIGH_TEMP_SWELLING_PER_DAY]++;
#endif
} else {
sec_vote(battery->fcc_vote, VOTER_SWELLING, true, battery->pdata->wire_warm_current);
#if defined(CONFIG_BATTERY_CISD)
battery->cisd.data[CISD_DATA_HIGH_TEMP_SWELLING]++;
battery->cisd.data[CISD_DATA_HIGH_TEMP_SWELLING_PER_DAY]++;
#endif
}
sec_vote(battery->topoff_vote, VOTER_SWELLING, true, battery->pdata->high_temp_topoff);
if (battery->voltage_now > battery->pdata->high_temp_float) {
sec_vote(battery->chgen_vote, VOTER_SWELLING, true, SEC_BAT_CHG_MODE_CHARGING_OFF);
} else {
sec_vote(battery->chgen_vote, VOTER_SWELLING, true, SEC_BAT_CHG_MODE_CHARGING);
sec_vote(battery->fv_vote, VOTER_SWELLING, true, battery->pdata->high_temp_float);
}
battery->health = POWER_SUPPLY_HEALTH_GOOD;
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_HIGH_TEMP_SWELLING,
SEC_BAT_CURRENT_EVENT_SWELLING_MODE);
break;
case BAT_THERMAL_COOL1:
battery->swelling_mode = true;
battery->usb_thm_status = USB_THM_NORMAL;
battery->cool1_normal_thresh += THERMAL_HYSTERESIS_2;
if (is_wireless_type(battery->cable_type) ||
battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_FAKE) {
sec_vote(battery->fcc_vote, VOTER_SWELLING, true, battery->pdata->wireless_cool1_current);
} else {
sec_vote(battery->fcc_vote, VOTER_SWELLING, true, battery->pdata->wire_cool1_current);
}
sec_vote(battery->fv_vote, VOTER_SWELLING, true, battery->pdata->low_temp_float);
sec_vote(battery->topoff_vote, VOTER_SWELLING, true, battery->pdata->low_temp_topoff);
sec_vote(battery->chgen_vote, VOTER_SWELLING, true, SEC_BAT_CHG_MODE_CHARGING);
battery->health = POWER_SUPPLY_HEALTH_GOOD;
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING_COOL1,
SEC_BAT_CURRENT_EVENT_SWELLING_MODE);
break;
case BAT_THERMAL_COOL2:
battery->swelling_mode = true;
battery->usb_thm_status = USB_THM_NORMAL;
battery->cool2_cool1_thresh += THERMAL_HYSTERESIS_2;
battery->cool1_normal_thresh += THERMAL_HYSTERESIS_2;
if (is_wireless_type(battery->cable_type) ||
battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_FAKE) {
sec_vote(battery->fcc_vote, VOTER_SWELLING, true, battery->pdata->wireless_cool2_current);
} else {
sec_vote(battery->fcc_vote, VOTER_SWELLING, true, battery->pdata->wire_cool2_current);
}
sec_vote(battery->fv_vote, VOTER_SWELLING, true, battery->pdata->low_temp_float);
sec_vote(battery->topoff_vote, VOTER_SWELLING, true, battery->pdata->low_temp_topoff);
sec_vote(battery->chgen_vote, VOTER_SWELLING, true, SEC_BAT_CHG_MODE_CHARGING);
battery->health = POWER_SUPPLY_HEALTH_GOOD;
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING_COOL2,
SEC_BAT_CURRENT_EVENT_SWELLING_MODE);
break;
case BAT_THERMAL_COOL3:
battery->swelling_mode = true;
battery->usb_thm_status = USB_THM_NORMAL;
battery->cool3_cool2_thresh += THERMAL_HYSTERESIS_2;
battery->cool2_cool1_thresh += THERMAL_HYSTERESIS_2;
battery->cool1_normal_thresh += THERMAL_HYSTERESIS_2;
if (is_wireless_type(battery->cable_type) ||
battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_FAKE) {
sec_vote(battery->fcc_vote, VOTER_SWELLING, true, battery->pdata->wireless_cool3_current);
} else {
sec_vote(battery->fcc_vote, VOTER_SWELLING, true, battery->pdata->wire_cool3_current);
}
sec_vote(battery->fv_vote, VOTER_SWELLING, true, battery->pdata->low_temp_float);
sec_vote(battery->topoff_vote, VOTER_SWELLING, true, battery->pdata->low_temp_topoff);
sec_vote(battery->chgen_vote, VOTER_SWELLING, true, SEC_BAT_CHG_MODE_CHARGING);
battery->health = POWER_SUPPLY_HEALTH_GOOD;
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING_COOL3,
SEC_BAT_CURRENT_EVENT_SWELLING_MODE);
break;
case BAT_THERMAL_COLD:
battery->swelling_mode = true;
battery->usb_thm_status = USB_THM_NORMAL;
battery->cold_cool3_thresh += THERMAL_HYSTERESIS_2;
battery->cool3_cool2_thresh += THERMAL_HYSTERESIS_2;
battery->cool2_cool1_thresh += THERMAL_HYSTERESIS_2;
battery->cool1_normal_thresh += THERMAL_HYSTERESIS_2;
sec_vote(battery->chgen_vote, VOTER_SWELLING, true, SEC_BAT_CHG_MODE_CHARGING_OFF);
battery->health = POWER_SUPPLY_HEALTH_COLD;
sec_bat_set_charging_status(battery, POWER_SUPPLY_STATUS_NOT_CHARGING);
#if defined(CONFIG_BATTERY_CISD)
battery->cisd.data[CISD_DATA_UNSAFETY_TEMPERATURE]++;
battery->cisd.data[CISD_DATA_UNSAFE_TEMPERATURE_PER_DAY]++;
#endif
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING_COOL3,
SEC_BAT_CURRENT_EVENT_SWELLING_MODE);
break;
case BAT_THERMAL_NORMAL:
default:
battery->swelling_mode = false;
battery->usb_thm_status = USB_THM_NORMAL;
sec_vote(battery->fcc_vote, VOTER_SWELLING, false, 0);
sec_vote(battery->fv_vote, VOTER_SWELLING, false, 0);
sec_vote(battery->topoff_vote, VOTER_SWELLING, false, 0);
sec_vote(battery->chgen_vote, VOTER_SWELLING, false, 0);
battery->health = POWER_SUPPLY_HEALTH_GOOD;
break;
}
if ((battery->thermal_zone >= BAT_THERMAL_COOL3) && (battery->thermal_zone <= BAT_THERMAL_WARM)) {
#if defined(CONFIG_ENABLE_FULL_BY_SOC)
if ((battery->capacity >= 100) || (battery->status == POWER_SUPPLY_STATUS_FULL))
sec_bat_set_charging_status(battery, POWER_SUPPLY_STATUS_FULL);
else
sec_bat_set_charging_status(battery, POWER_SUPPLY_STATUS_CHARGING);
#else
if (battery->status != POWER_SUPPLY_STATUS_FULL)
sec_bat_set_charging_status(battery, POWER_SUPPLY_STATUS_CHARGING);
#endif
}
} else { /* pre_thermal_zone == battery->thermal_zone */
battery->health_change = false;
switch (battery->thermal_zone) {
case BAT_THERMAL_WARM:
if (battery->health == POWER_SUPPLY_HEALTH_GOOD) {
voter_swelling_status = get_sec_vote_result(battery->chgen_vote);
if (voter_swelling_status == SEC_BAT_CHG_MODE_CHARGING) {
if (sec_bat_check_full(battery, battery->pdata->full_check_type)) {
pr_info("%s: battery thermal zone WARM. Full charged.\n", __func__);
sec_vote(battery->chgen_vote, VOTER_SWELLING, true,
SEC_BAT_CHG_MODE_CHARGING_OFF);
}
} else if (voter_swelling_status == SEC_BAT_CHG_MODE_CHARGING_OFF) {
if (battery->voltage_now <= battery->pdata->swelling_high_rechg_voltage) {
pr_info("%s: thermal zone WARM. charging recovery. Vnow: %d\n",
__func__, battery->voltage_now);
battery->expired_time = battery->pdata->expired_time;
battery->prev_safety_time = 0;
sec_vote(battery->fv_vote, VOTER_SWELLING, true,
battery->pdata->high_temp_float);
sec_vote(battery->chgen_vote, VOTER_FULL_CHARGE, false, 0);
sec_vote(battery->chgen_vote, VOTER_SWELLING, true,
SEC_BAT_CHG_MODE_CHARGING);
}
}
}
break;
default:
break;
}
}
return;
}