lineage_kernel_xcoverpro/drivers/battery_v2/sec_battery.c

8422 lines
292 KiB
C
Executable File

/*
* sec_battery.c
* Samsung Mobile Battery Driver
*
* Copyright (C) 2012 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"
#include "include/sec_battery_sysfs.h"
#include "include/sec_battery_dt.h"
#include <linux/sec_ext.h>
#include <linux/sec_debug.h>
#if defined(CONFIG_SEC_ABC)
#include <linux/sti/abc_common.h>
#endif
bool sleep_mode = false;
bool batt_boot_complete = false;
static enum power_supply_property sec_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_VOLTAGE_AVG,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CURRENT_AVG,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CHARGE_NOW,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TEMP_AMBIENT,
#if defined(CONFIG_FUELGAUGE_MAX77705)
POWER_SUPPLY_PROP_POWER_NOW,
POWER_SUPPLY_PROP_POWER_AVG,
#endif
#if defined(CONFIG_CALC_TIME_TO_FULL)
POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
#endif
POWER_SUPPLY_PROP_CHARGE_COUNTER_SHADOW,
POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL,
POWER_SUPPLY_PROP_CHARGE_UNO_CONTROL,
POWER_SUPPLY_PROP_CHARGE_COUNTER,
};
static enum power_supply_property sec_power_props[] = {
POWER_SUPPLY_PROP_ONLINE,
};
static enum power_supply_property sec_wireless_props[] = {
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_PRESENT,
};
static enum power_supply_property sec_ac_props[] = {
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_TEMP,
};
static enum power_supply_property sec_ps_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_ONLINE,
};
static char *supply_list[] = {
"battery",
};
char *sec_cable_type[SEC_BATTERY_CABLE_MAX] = {
"UNKNOWN", /* 0 */
"NONE", /* 1 */
"PREAPARE_TA", /* 2 */
"TA", /* 3 */
"USB", /* 4 */
"USB_CDP", /* 5 */
"9V_TA", /* 6 */
"9V_ERR", /* 7 */
"9V_UNKNOWN", /* 8 */
"12V_TA", /* 9 */
"WC", /* 10 */
"HV_WC", /* 11 */
"PMA_WC", /* 12 */
"WC_PACK", /* 13 */
"WC_HV_PACK", /* 14 */
"WC_STAND", /* 15 */
"WC_HV_STAND", /* 16 */
"OC20", /* 17 */
"QC30", /* 18 */
"PDIC", /* 19 */
"UARTOFF", /* 20 */
"OTG", /* 21 */
"LAN_HUB", /* 22 */
"POWER_SHARGING", /* 23 */
"HMT_CONNECTED", /* 24 */
"HMT_CHARGE", /* 25 */
"HV_TA_CHG_LIMIT", /* 26 */
"WC_VEHICLE", /* 27 */
"WC_HV_VEHICLE", /* 28 */
"WC_HV_PREPARE", /* 29 */
"TIMEOUT", /* 30 */
"SMART_OTG", /* 31 */
"SMART_NOTG", /* 32 */
"WC_TX", /* 33 */
"HV_WC_20", /* 34 */
"HV_WC_20_LIMIT", /* 35 */
"WC_FAKE", /* 36 */
"HV_WC_20_PREPARE", /* 37 */
"PDIC_APDO", /* 38 */
};
char *sec_bat_charging_mode_str[] = {
"None",
"Normal",
"Additional",
"Re-Charging",
"ABS"
};
char *sec_bat_status_str[] = {
"Unknown",
"Charging",
"Discharging",
"Not-charging",
"Full"
};
char *sec_bat_health_str[] = {
"Unknown",
"Good",
"Overheat",
"Warm",
"Dead",
"OverVoltage",
"UnspecFailure",
"Cold",
"Cool",
"WatchdogTimerExpire",
"SafetyTimerExpire",
"UnderVoltage",
"OverheatLimit",
"VsysOVP",
"VbatOVP",
};
char *sec_bat_charge_mode_str[] = {
"Charging-On",
"Charging-Off",
"Buck-Off",
};
char *sec_bat_rx_type_str[] = {
"No Dev",
"Other DEV",
"SS Gear",
"SS Phone",
"SS Buds",
};
char *vout_control_mode_str[] = {
"Set VOUT Off",
"Set VOUT NV",
"Set Vout Rsv",
"Set Vout HV",
"Set Vout CV",
"Set Vout Call",
"Set Vout 5V",
"Set Vout 9V",
"Set Vout 10V",
"Set Vout 11V",
"Set Vout 12V",
"Set Vout 12.5V",
"Set Vout 5V Step",
"Set Vout 5.5V Step",
"Set Vout 9V Step",
"Set Vout 10V Step",
};
extern int bootmode;
bool mfc_fw_update;
EXPORT_SYMBOL(mfc_fw_update);
bool boot_complete;
EXPORT_SYMBOL(boot_complete);
#if defined(CONFIG_PREVENT_USB_CONN_OVERHEAT)
extern int muic_set_hiccup_mode(int on_off);
extern void pdic_manual_ccopen_request(int is_on);
#endif
extern int muic_afc_set_voltage(int vol);
#if !defined(CONFIG_SEC_FACTORY)
extern bool sales_code_is(char* str);
#endif
#if 0
extern int muic_hv_charger_disable(bool en);
#endif
void sec_bat_set_misc_event(struct sec_battery_info *battery,
unsigned int misc_event_val, unsigned int misc_event_mask) {
unsigned int temp = battery->misc_event;
mutex_lock(&battery->misclock);
battery->misc_event &= ~misc_event_mask;
battery->misc_event |= misc_event_val;
pr_info("%s: misc event before(0x%x), after(0x%x)\n",
__func__, temp, battery->misc_event);
mutex_unlock(&battery->misclock);
if (battery->prev_misc_event != battery->misc_event) {
cancel_delayed_work(&battery->misc_event_work);
wake_lock(&battery->misc_event_wake_lock);
queue_delayed_work(battery->monitor_wqueue,
&battery->misc_event_work, 0);
}
}
void sec_bat_set_tx_event(struct sec_battery_info *battery,
unsigned int tx_event_val, unsigned int tx_event_mask) {
unsigned int temp = battery->tx_event;
mutex_lock(&battery->txeventlock);
battery->tx_event &= ~tx_event_mask;
battery->tx_event |= tx_event_val;
pr_info("@Tx_Mode %s: val(0x%x), mask(0x%x), tx event before(0x%x), after(0x%x)\n",
__func__, tx_event_val, tx_event_mask, temp, battery->tx_event);
pr_info("@Tx_Mode %s: tx event before(0x%x), after(0x%x)\n",
__func__, temp, battery->tx_event);
if (temp != battery->tx_event) {
/* Assure receiving tx_event to App for sleep case */
wake_lock_timeout(&battery->tx_event_wake_lock, HZ * 2);
power_supply_changed(battery->psy_bat);
}
mutex_unlock(&battery->txeventlock);
}
void sec_bat_set_current_event(struct sec_battery_info *battery,
unsigned int current_event_val, unsigned int current_event_mask)
{
unsigned int temp = battery->current_event;
mutex_lock(&battery->current_eventlock);
battery->current_event &= ~current_event_mask;
battery->current_event |= current_event_val;
pr_info("%s: current event before(0x%x), after(0x%x)\n",
__func__, temp, battery->current_event);
mutex_unlock(&battery->current_eventlock);
}
void sec_bat_set_temp_control_test(struct sec_battery_info *battery,
bool temp_enable)
{
if (temp_enable) {
pr_info("%s : BATT_TEMP_CONTROL_TEST ENABLE\n", __func__);
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_TEMP_CTRL_TEST,
SEC_BAT_CURRENT_EVENT_TEMP_CTRL_TEST);
battery ->pdata->usb_temp_check_type_backup = battery->pdata->usb_temp_check_type;
battery->pdata->usb_temp_check_type = 0;
battery->pdata->temp_highlimit_threshold_normal_backup =
battery->pdata->temp_highlimit_threshold_normal;
battery->pdata->temp_highlimit_threshold_normal = 990;
} else {
pr_info("%s : BATT_TEMP_CONTROL_TEST END\n", __func__);
sec_bat_set_current_event(battery, 0,
SEC_BAT_CURRENT_EVENT_TEMP_CTRL_TEST);
if (!battery ->pdata->usb_temp_check_type)
battery ->pdata->usb_temp_check_type = battery->pdata->usb_temp_check_type_backup;
if (battery->pdata->temp_highlimit_threshold_normal == 990)
battery->pdata->temp_highlimit_threshold_normal =
battery->pdata->temp_highlimit_threshold_normal_backup;
}
}
static void sec_bat_change_default_current(struct sec_battery_info *battery,
int cable_type, int input, int output)
{
#if defined(CONFIG_ENG_BATTERY_CONCEPT)
if(!battery->test_max_current)
#endif
battery->pdata->charging_current[cable_type].input_current_limit = input;
#if defined(CONFIG_ENG_BATTERY_CONCEPT)
if(!battery->test_charge_current)
#endif
battery->pdata->charging_current[cable_type].fast_charging_current = output;
pr_info("%s: cable_type: %d input: %d output: %d\n",
__func__,
cable_type,
battery->pdata->charging_current[cable_type].input_current_limit,
battery->pdata->charging_current[cable_type].fast_charging_current);
}
static int sec_bat_get_wireless_current(struct sec_battery_info *battery, int incurr)
{
union power_supply_propval value = {0, };
int is_otg_on = 0;
psy_do_property(battery->pdata->charger_name, get,
POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL, value);
is_otg_on = value.intval;
if (is_otg_on) {
pr_info("%s: both wireless chg and otg recognized.\n", __func__);
incurr = battery->pdata->wireless_otg_input_current;
}
/* 2. WPC_SLEEP_MODE */
if (is_hv_wireless_type(battery->cable_type) && sleep_mode) {
if(incurr > battery->pdata->sleep_mode_limit_current)
incurr = battery->pdata->sleep_mode_limit_current;
pr_info("%s sleep_mode =%d, chg_limit =%d, in_curr = %d \n", __func__,
sleep_mode, battery->chg_limit, incurr);
if(!battery->auto_mode) {
/* send cmd once */
battery->auto_mode = true;
value.intval = WIRELESS_SLEEP_MODE_ENABLE;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, value);
}
}
/* 3. WPC_TEMP_MODE */
if (is_wireless_type(battery->cable_type) && battery->chg_limit) {
if ((battery->siop_level >= 100 && !battery->lcd_status) &&
(incurr > battery->pdata->wpc_input_limit_current)) {
if (battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_TX &&
battery->pdata->wpc_input_limit_by_tx_check)
incurr = battery->pdata->wpc_input_limit_current_by_tx;
else
incurr = battery->pdata->wpc_input_limit_current;
} else if ((battery->siop_level < 100 || battery->lcd_status) &&
(incurr > battery->pdata->wpc_lcd_on_input_limit_current))
incurr = battery->pdata->wpc_lcd_on_input_limit_current;
}
/* 5. Full-Additional state */
if (battery->status == POWER_SUPPLY_STATUS_FULL && battery->charging_mode == SEC_BATTERY_CHARGING_2ND) {
if (incurr > battery->pdata->siop_hv_wireless_input_limit_current)
incurr = battery->pdata->siop_hv_wireless_input_limit_current;
}
/* 6. Hero Stand Pad CV */
if (battery->capacity >= battery->pdata->wc_hero_stand_cc_cv) {
if (battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_STAND) {
if (incurr > battery->pdata->wc_hero_stand_cv_current)
incurr = battery->pdata->wc_hero_stand_cv_current;
} else if (battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_HV_STAND) {
if (battery->chg_limit &&
incurr > battery->pdata->wc_hero_stand_cv_current) {
incurr = battery->pdata->wc_hero_stand_cv_current;
} else if (!battery->chg_limit &&
incurr > battery->pdata->wc_hero_stand_hv_cv_current) {
incurr = battery->pdata->wc_hero_stand_hv_cv_current;
}
}
}
/* 7. Full-None state && SIOP_LEVEL 100 */
if ((battery->siop_level >= 100 && !battery->lcd_status) &&
battery->status == POWER_SUPPLY_STATUS_FULL && battery->charging_mode == SEC_BATTERY_CHARGING_NONE) {
incurr = battery->pdata->wc_full_input_limit_current;
}
return incurr;
}
static void sec_bat_get_charging_current_by_siop(struct sec_battery_info *battery,
int *input_current, int *charging_current) {
if (battery->siop_level < 100) {
int max_charging_current;
if (is_wireless_type(battery->cable_type)) {
max_charging_current = 1000; /* 1 step(70) */
if (battery->siop_level == 0) { /* 3 step(0) */
max_charging_current = 0;
} else if (battery->siop_level <= 10) { /* 2 step(10) */
max_charging_current = 500;
}
} else {
max_charging_current = 1800; /* 1 step(70) */
}
/* do forced set charging current */
if (*charging_current > max_charging_current)
*charging_current = max_charging_current;
if (is_nv_wireless_type(battery->cable_type)) {
if (*input_current > battery->pdata->siop_wireless_input_limit_current)
*input_current = battery->pdata->siop_wireless_input_limit_current;
if (*charging_current > battery->pdata->siop_wireless_charging_limit_current)
*charging_current = battery->pdata->siop_wireless_charging_limit_current;
} else if (is_hv_wireless_type(battery->cable_type)) {
if (*input_current > battery->pdata->siop_hv_wireless_input_limit_current)
*input_current = battery->pdata->siop_hv_wireless_input_limit_current;
if (*charging_current > battery->pdata->siop_hv_wireless_charging_limit_current)
*charging_current = battery->pdata->siop_hv_wireless_charging_limit_current;
} else if (is_hv_wire_type(battery->cable_type) && is_hv_wire_type(battery->wire_status)) {
if (is_hv_wire_12v_type(battery->cable_type)) {
if (*input_current > battery->pdata->siop_hv_12v_input_limit_current)
*input_current = battery->pdata->siop_hv_12v_input_limit_current;
} else {
if (*input_current > battery->pdata->siop_hv_input_limit_current)
*input_current = battery->pdata->siop_hv_input_limit_current;
/* 2 step(0) for hv_wire_type */
if (battery->siop_level == 0 &&
*input_current > battery->pdata->siop_hv_input_limit_current_2nd)
*input_current = battery->pdata->siop_hv_input_limit_current_2nd;
}
#if defined(CONFIG_CCIC_NOTIFIER)
} else if (is_pd_wire_type(battery->cable_type)) {
if (*input_current > (6000 / battery->input_voltage))
*input_current = 6000 / battery->input_voltage;
/* 2 step(0) for PD type */
if (battery->siop_level == 0 &&
*input_current > battery->pdata->siop_hv_input_limit_current_2nd)
*input_current = battery->pdata->siop_hv_input_limit_current_2nd;
#endif
} else {
if (battery->siop_level == 20 && battery->pdata->input_current_by_siop_20 > 0) {
if (*input_current > battery->pdata->input_current_by_siop_20)
*input_current = battery->pdata->input_current_by_siop_20;
} else if (*input_current > battery->pdata->siop_input_limit_current) {
*input_current = battery->pdata->siop_input_limit_current;
/* 2 step(0) for 9v to 5v hv_wire_type */
if (battery->siop_level == 0 &&
*input_current > battery->pdata->siop_hv_input_limit_current_2nd)
*input_current = battery->pdata->siop_hv_input_limit_current_2nd;
}
}
}
pr_info("%s: incurr(%d), chgcurr(%d)\n", __func__, *input_current, *charging_current);
}
static void sec_bat_change_pdo(struct sec_battery_info *battery, int vol)
{
unsigned int target_pd_index = 0;
if (is_pd_wire_type(battery->wire_status)) {
if (vol == SEC_INPUT_VOLTAGE_9V) {
/* select PDO greater than 5V */
target_pd_index = battery->pd_list.max_pd_count - 1;
} else {
/* select 5V PDO */
target_pd_index = 0;
}
pr_info("%s: target_pd_index: %d, now_pd_index: %d\n", __func__,
target_pd_index, battery->pd_list.now_pd_index);
if (target_pd_index != battery->pd_list.now_pd_index) {
/* change input current before request new pdo if new pdo's input current is less than now */
if (battery->pd_list.pd_info[target_pd_index].input_current < battery->input_current) {
union power_supply_propval value = {0, };
value.intval = battery->pd_list.pd_info[target_pd_index].input_current;
battery->input_current = value.intval;
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_SELECT_PDO,
SEC_BAT_CURRENT_EVENT_SELECT_PDO);
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CURRENT_MAX, value);
}
battery->pdic_ps_rdy = false;
select_pdo(battery->pd_list.pd_info[target_pd_index].pdo_index);
}
}
}
#if !defined(CONFIG_SEC_FACTORY)
static int sec_bat_get_temp_by_temp_control_source(struct sec_battery_info *battery,
enum sec_battery_temp_control_source 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;
}
}
static int sec_bat_check_mix_temp(struct sec_battery_info *battery, int input_current)
{
int chg_temp;
if (battery->pdata->temp_check_type == SEC_BATTERY_TEMP_CHECK_NONE ||
battery->pdata->chg_temp_check_type == SEC_BATTERY_TEMP_CHECK_NONE)
return input_current;
chg_temp = battery->chg_temp;
if (battery->siop_level >= 100 && !battery->lcd_status &&
is_not_wireless_type(battery->cable_type)) {
if ((!battery->mix_limit &&
(battery->temperature >= battery->pdata->mix_high_temp) &&
(chg_temp >= battery->pdata->mix_high_chg_temp)) ||
(battery->mix_limit &&
(battery->temperature > battery->pdata->mix_high_temp_recovery))) {
int max_input_current =
battery->pdata->full_check_current_1st + 50;
/* inpu 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);
#if defined(CONFIG_WIRELESS_TX_MODE)
if (battery->wc_tx_enable) {
pr_info("@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;
if (battery->tx_retry_case & SEC_BAT_TX_RETRY_MIX_TEMP) {
pr_info("@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, battery->temperature, chg_temp, input_current);
} else {
battery->mix_limit = false;
}
return input_current;
}
static void sec_bat_check_wpc_temp(struct sec_battery_info *battery, int *input_current, int *charging_current)
{
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 = 0;
bool flicker_wa = false;
if (battery->cable_type == SEC_BATTERY_CABLE_HV_WIRELESS_20)
wpc_vout_level = battery->wpc_max_vout_level;
else
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;
if (battery->cable_type == SEC_BATTERY_CABLE_HV_WIRELESS_20)
wpc_vout_level = (battery->capacity < 95) ?
WIRELESS_VOUT_5_5V_STEP : battery->wpc_max_vout_level;
else
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)) {
bool skip_wa = false;
if (battery->wpc_vout_ctrl_lcd_on) {
psy_do_property(battery->pdata->wireless_charger_name, get,
POWER_SUPPLY_EXT_PROP_WIRELESS_TX_ID, value);
if (value.intval == WC_PAD_ID_UNKNOWN ||
value.intval == WC_PAD_ID_SNGL_DREAM ||
value.intval == WC_PAD_ID_STAND_DREAM) {
pr_info("%s: do not use flicker w/a\n", __func__);
skip_wa = true;
}
}
#if defined(CONFIG_ISDB_CHARGING_CONTROL)
if ((battery->current_event & SEC_BAT_CURRENT_EVENT_HIGH_TEMP_SWELLING) ||
(battery->wpc_vout_ctrl_lcd_on && battery->lcd_status && !skip_wa) ||
(battery->current_event & SEC_BAT_CURRENT_EVENT_ISDB) || sleep_mode) {
pr_info("%s: vout 5.5V set. WPC_VOUT_CTRL_LCD_ON(%d), LCD(%d)\n",
__func__, battery->wpc_vout_ctrl_lcd_on, battery->lcd_status);
#else
if ((battery->current_event & SEC_BAT_CURRENT_EVENT_HIGH_TEMP_SWELLING) ||
(battery->wpc_vout_ctrl_lcd_on && battery->lcd_status && !skip_wa) || sleep_mode) {
pr_info("%s: vout 5.5V set. WPC_VOUT_CTRL_LCD_ON(%d), LCD(%d)\n",
__func__, battery->wpc_vout_ctrl_lcd_on, battery->lcd_status);
#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(%s) because otg on\n",
__func__, vout_control_mode_str[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(%s)",
__func__, vout_control_mode_str[battery->wpc_vout_level]);
battery->aicl_current = 0; /* reset aicl current */
flicker_wa = true;
}
} else if (battery->chg_limit && battery->wpc_vout_ctrl_lcd_on && !skip_wa) {
value.intval = battery->lcd_status;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_EXT_PROP_PAD_VOLT_CTRL, value);
flicker_wa = true;
} else if ((battery->wpc_vout_level == WIRELESS_VOUT_10V || battery->wpc_vout_level == battery->wpc_max_vout_level) && !battery->chg_limit)
/* reset aicl current to recover current for unexpected aicl during before vout boosting completion */
battery->aicl_current = 0;
value.intval = 0;
psy_do_property(battery->pdata->wireless_charger_name, get,
POWER_SUPPLY_EXT_PROP_PAD_VOLT_CTRL, value);
if (battery->wpc_vout_ctrl_lcd_on && !flicker_wa && !skip_wa && (value.intval == battery->lcd_status)) {
pr_info("%s : Different pad voltage and lcd on/off\n. Change Pad Voltage.\n", __func__);
value.intval = battery->lcd_status;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_EXT_PROP_PAD_VOLT_CTRL, value);
}
}
mutex_unlock(&battery->voutlock);
pr_info("%s: change input_current(%d), change charge_current(%d), vout_level(%s), chg_limit(%d)\n",
__func__, *input_current, *charging_current, vout_control_mode_str[battery->wpc_vout_level], battery->chg_limit);
}
}
static bool sec_bat_change_vbus(struct sec_battery_info *battery, int *input_current)
{
#if defined(CONFIG_SUPPORT_HV_CTRL)
union power_supply_propval value;
unsigned int target_vbus = SEC_INPUT_VOLTAGE_0V;
#endif
if (battery->pdata->chg_temp_check_type == SEC_BATTERY_TEMP_CHECK_NONE)
return false;
#if defined(CONFIG_SUPPORT_HV_CTRL)
if (battery->store_mode)
return false;
if (is_hv_wire_type(battery->cable_type) &&
#if defined(CONFIG_MUIC_HV_SUPPORT_POGO_DOCK)
(battery->muic_cable_type != ATTACHED_DEV_POGO_DOCK_9V_MUIC) &&
#endif
(battery->cable_type != SEC_BATTERY_CABLE_QC30)) {
if (battery->current_event & SEC_BAT_CURRENT_EVENT_AFC) {
pr_info("%s: skip during current_event(0x%x)\n",
__func__, battery->current_event);
return false;
}
/* check target vbus */
if (battery->vbus_limit)
target_vbus = SEC_INPUT_VOLTAGE_0V;
else if (battery->vbus_chg_by_full)
target_vbus = SEC_INPUT_VOLTAGE_5V;
else if (battery->siop_level >= 100) {
if (is_hv_wire_12v_type(battery->cable_type))
target_vbus = SEC_INPUT_VOLTAGE_12V;
else
target_vbus = SEC_INPUT_VOLTAGE_9V;
if (battery->vbus_chg_by_siop == SEC_INPUT_VOLTAGE_NONE)
battery->vbus_chg_by_siop = target_vbus;
} else if (battery->status == POWER_SUPPLY_STATUS_CHARGING)
target_vbus = SEC_INPUT_VOLTAGE_5V;
if (target_vbus == SEC_INPUT_VOLTAGE_0V) {
pr_info("%s: skip set vbus %dV, level(%d), Cable(%s, %s, %d, %d)\n",
__func__, target_vbus, battery->siop_level,
sec_cable_type[battery->cable_type], sec_cable_type[battery->wire_status],
battery->muic_cable_type, battery->pd_usb_attached);
return false;
}
if (battery->vbus_chg_by_siop != target_vbus) {
/* change input current to pre_afc_input_current */
*input_current = battery->pdata->pre_afc_input_current;
battery->charge_power = battery->input_voltage * (*input_current);
value.intval = *input_current;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CURRENT_MAX, value);
battery->input_current = *input_current;
/* set current event */
cancel_delayed_work(&battery->afc_work);
wake_unlock(&battery->afc_wake_lock);
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_AFC,
(SEC_BAT_CURRENT_EVENT_CHG_LIMIT | SEC_BAT_CURRENT_EVENT_AFC));
battery->chg_limit = false;
battery->vbus_chg_by_siop = target_vbus;
pr_info("%s: vbus set %dV by level(%d), Cable(%s, %s, %d, %d)\n",
__func__, target_vbus, battery->siop_level,
sec_cable_type[battery->cable_type], sec_cable_type[battery->wire_status],
battery->muic_cable_type, battery->pd_usb_attached);
muic_afc_set_voltage(target_vbus);
return true;
}
}
#endif
return false;
}
static void sec_bat_check_afc_temp(struct sec_battery_info *battery, int *input_current, int *charging_current)
{
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;
} 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;
} else if (battery->chg_limit && is_hv_wire_type(battery->cable_type)) {
if (battery->chg_temp <= battery->pdata->chg_high_temp_recovery) {
*input_current = battery->pdata->charging_current[battery->cable_type].input_current_limit;
*charging_current = battery->pdata->charging_current[battery->cable_type].fast_charging_current;
battery->chg_limit = false;
} else {
*input_current = battery->pdata->chg_input_limit_current;
*charging_current = battery->pdata->chg_charging_limit_current;
battery->chg_limit = true;
}
} 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) {
*input_current = battery->pdata->charging_current[battery->cable_type].input_current_limit;
*charging_current = battery->pdata->charging_current[battery->cable_type].fast_charging_current;
battery->chg_limit = false;
} else {
*input_current = battery->pdata->chg_input_limit_current;
*charging_current = battery->pdata->chg_charging_limit_current;
battery->chg_limit = true;
}
}
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))) {
*input_current = battery->pdata->chg_input_limit_current;
*charging_current = battery->pdata->chg_charging_limit_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)) {
*input_current = battery->pdata->charging_current[battery->cable_type].input_current_limit;
*charging_current = battery->pdata->charging_current[battery->cable_type].fast_charging_current;
battery->chg_limit = false;
}
#endif
}
static void sec_bat_chg_temperature_check(struct sec_battery_info *battery,
int *input_current, int *charging_current)
{
if ((battery->siop_level >= 100 && !battery->lcd_status) &&
is_hv_wire_type(battery->cable_type)) {
if (!battery->chg_limit &&
(battery->chg_temp > battery->pdata->chg_high_temp)) {
battery->chg_limit = true;
*input_current = battery->pdata->chg_input_limit_current;
*charging_current = battery->pdata->chg_charging_limit_current;
dev_info(battery->dev,"%s: Chg current is reduced by Temp: %d\n",
__func__, battery->chg_temp);
} else if (battery->chg_limit &&
(battery->chg_temp < battery->pdata->chg_high_temp_recovery)) {
battery->chg_limit = false;
*input_current = battery->pdata->charging_current
[battery->cable_type].input_current_limit;
*charging_current = battery->pdata->charging_current
[battery->cable_type].fast_charging_current;
dev_info(battery->dev,"%s: Chg current is recovered by Temp: %d\n",
__func__, battery->chg_temp);
} else if (battery->chg_limit &&
(battery->chg_temp > battery->pdata->chg_high_temp)) {
*input_current = battery->pdata->chg_input_limit_current;
*charging_current = battery->pdata->chg_charging_limit_current;
}
}
pr_info("%s: cable_type(%d), chg_limit(%d)\n", __func__,
battery->cable_type, battery->chg_limit);
}
#if defined(CONFIG_CCIC_NOTIFIER)
extern void select_pdo(int num);
static bool sec_bat_change_vbus_pd(struct sec_battery_info *battery, int *input_current)
{
#if defined(CONFIG_SUPPORT_HV_CTRL)
unsigned int target_pd_index = 0;
if (battery->pdata->chg_temp_check_type == SEC_BATTERY_TEMP_CHECK_NONE)
return false;
if (battery->store_mode)
return false;
if (battery->cable_type == SEC_BATTERY_CABLE_PDIC) {
if (battery->current_event & SEC_BAT_CURRENT_EVENT_SELECT_PDO) {
pr_info("%s: skip during current_event(0x%x)\n",
__func__, battery->current_event);
return false;
}
if (battery->siop_level >= 100) {
/* select PDO greater than 5V */
target_pd_index = battery->pd_list.max_pd_count - 1;
} else {
/* select 5V PDO */
target_pd_index = 0;
}
pr_info("%s: target_pd_index: %d, now_pd_index: %d\n", __func__,
target_pd_index, battery->pd_list.now_pd_index);
if (target_pd_index != battery->pd_list.now_pd_index) {
/* change input current before request new pdo if new pdo's input current is less than now */
if (battery->pd_list.pd_info[target_pd_index].input_current < battery->input_current) {
union power_supply_propval value = {0, };
*input_current = battery->pd_list.pd_info[target_pd_index].input_current;
value.intval = *input_current;
battery->input_current = *input_current;
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_SELECT_PDO,
SEC_BAT_CURRENT_EVENT_SELECT_PDO);
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CURRENT_MAX, value);
}
battery->pdic_ps_rdy = false;
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_SELECT_PDO,
SEC_BAT_CURRENT_EVENT_SELECT_PDO);
select_pdo(battery->pd_list.pd_info[target_pd_index].pdo_index);
return true;
}
}
#endif
return false;
}
static void sec_bat_check_pdic_temp(struct sec_battery_info *battery, int *input_current, int *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;
battery->chg_limit = true;
} else if (battery->chg_limit && battery->chg_temp <= battery->pdata->chg_high_temp_recovery) {
*input_current = battery->pdata->charging_current[battery->cable_type].input_current_limit;
*charging_current = battery->pdata->charging_current[battery->cable_type].fast_charging_current;
battery->chg_limit = false;
}
pr_info("%s: cable_type(%d), chg_limit(%d)\n", __func__,
battery->cable_type, battery->chg_limit);
}
}
static int sec_bat_check_pd_input_current(struct sec_battery_info *battery, int input_current)
{
if (battery->current_event & SEC_BAT_CURRENT_EVENT_SELECT_PDO) {
input_current = battery->input_current;
pr_info("%s: change input_current(%d), cable_type(%d)\n", __func__, input_current, battery->cable_type);
}
return input_current;
}
#endif
#endif
static int sec_bat_check_afc_input_current(struct sec_battery_info *battery, int input_current)
{
if (battery->current_event & SEC_BAT_CURRENT_EVENT_AFC) {
int work_delay = 0;
if (!is_wireless_type(battery->cable_type)) {
input_current = battery->pdata->pre_afc_input_current; // 1000mA
work_delay = battery->pdata->pre_afc_work_delay;
} else {
input_current = battery->pdata->pre_wc_afc_input_current;
/* do not reduce this time, this is for noble pad */
work_delay = battery->pdata->pre_wc_afc_work_delay;
}
wake_lock(&battery->afc_wake_lock);
if (!delayed_work_pending(&battery->afc_work))
queue_delayed_work(battery->monitor_wqueue,
&battery->afc_work , msecs_to_jiffies(work_delay));
pr_info("%s: change input_current(%d), cable_type(%d)\n", __func__, input_current, battery->cable_type);
}
return input_current;
}
#if defined(CONFIG_CCIC_NOTIFIER)
static void sec_bat_get_input_current_in_power_list(struct sec_battery_info *battery)
{
int pdo_num = battery->pdic_info.sink_status.current_pdo_num;
int max_input_current = 0;
max_input_current = battery->pdata->charging_current[SEC_BATTERY_CABLE_PDIC].input_current_limit =
battery->pdic_info.sink_status.power_list[pdo_num].max_current;
pr_info("%s:max_input_current : %dmA\n", __func__, max_input_current);
}
static void sec_bat_get_charging_current_in_power_list(struct sec_battery_info *battery)
{
int max_charging_current = 0, pd_power = 0;
int pdo_num = battery->pdic_info.sink_status.current_pdo_num;
pd_power = (battery->pdic_info.sink_status.power_list[pdo_num].max_voltage *
battery->pdic_info.sink_status.power_list[pdo_num].max_current);
/* We assume that output voltage to float voltage */
max_charging_current = pd_power / (battery->pdata->chg_float_voltage / battery->pdata->chg_float_voltage_conv);
max_charging_current = max_charging_current > battery->pdata->max_charging_current ?
battery->pdata->max_charging_current : max_charging_current;
battery->pdata->charging_current[SEC_BATTERY_CABLE_PDIC].fast_charging_current = max_charging_current;
battery->charge_power = pd_power / 1000;
pr_info("%s:pd_charge_power : %dmW, max_charging_current : %dmA\n", __func__,
battery->charge_power, max_charging_current);
}
#endif
int sec_bat_set_charging_current(struct sec_battery_info *battery)
{
static int afc_init = false;
union power_supply_propval value = {0, };
unsigned int input_current = battery->pdata->charging_current[battery->cable_type].input_current_limit,
charging_current = battery->pdata->charging_current[battery->cable_type].fast_charging_current,
topoff_current = battery->pdata->full_check_current_1st;
if (battery->aicl_current)
input_current = battery->aicl_current;
mutex_lock(&battery->iolock);
if (is_nocharge_type(battery->cable_type)) {
} else {
#if !defined(CONFIG_SEC_FACTORY)
if (!(battery->current_event & SEC_BAT_CURRENT_EVENT_SKIP_HEATING_CONTROL)) {
input_current = sec_bat_check_mix_temp(battery, input_current);
}
#endif
/* check input current */
#if !defined(CONFIG_SEC_FACTORY)
if (!(battery->current_event & SEC_BAT_CURRENT_EVENT_SKIP_HEATING_CONTROL)) {
if (is_wireless_type(battery->cable_type))
sec_bat_check_wpc_temp(battery, &input_current, &charging_current);
#if defined(CONFIG_CCIC_NOTIFIER)
else if (battery->cable_type == SEC_BATTERY_CABLE_PDIC) {
if (!sec_bat_change_vbus_pd(battery, &input_current)) {
sec_bat_check_pdic_temp(battery, &input_current, &charging_current);
input_current = sec_bat_check_pd_input_current(battery, input_current);
}
}
#endif
else if (battery->pdata->chg_temp_check_type) {
switch (battery->pdata->chg_heating_prevention_method) {
case SEC_BATTERY_BY_CHANGING_VOLTAGE:
if (!sec_bat_change_vbus(battery, &input_current))
sec_bat_check_afc_temp(battery, &input_current, &charging_current);
break;
case SEC_BATTERY_BY_CHANGING_CURRENT:
default:
sec_bat_chg_temperature_check(battery, &input_current, &charging_current);
break;
}
}
}
#endif
/* set limited charging current during wireless power sharing with cable charging */
if (battery->pdata->charging_limit_by_tx_check &&
battery->wc_tx_enable &&
(is_hv_wire_type(battery->cable_type) || is_pd_wire_type(battery->cable_type)))
if (charging_current > battery->pdata->charging_limit_current_by_tx)
charging_current = battery->pdata->charging_limit_current_by_tx;
input_current = sec_bat_check_afc_input_current(battery, input_current);
/* Set limited max power when store mode is set and LDU
Limited max power should be set with over 5% capacity since target could be turned off during boot up */
if (battery->store_mode && (battery->capacity >= 5)) {
if (input_current > (battery->pdata->store_mode_max_input_power / battery->input_voltage))
input_current = battery->pdata->store_mode_max_input_power / battery->input_voltage;
}
sec_bat_get_charging_current_by_siop(battery, &input_current, &charging_current);
/* Calculate wireless input current under the specific conditions (wpc_sleep_mode, chg_limit)*/
if (battery->wc_status != SEC_WIRELESS_PAD_NONE) {
input_current = sec_bat_get_wireless_current(battery, input_current);
}
/* check topoff current */
if (battery->charging_mode == SEC_BATTERY_CHARGING_2ND &&
((battery->pdata->full_check_type_2nd == SEC_BATTERY_FULLCHARGED_CHGPSY) ||
(battery->pdata->full_check_type_2nd == SEC_BATTERY_FULLCHARGED_FG_CURRENT))) {
topoff_current =
battery->pdata->full_check_current_2nd;
}
/* check swelling state */
if (is_wireless_type(battery->cable_type)) {
if (battery->current_event & SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING_2ND) {
charging_current = (charging_current > battery->pdata->swelling_wc_low_temp_current_2nd) ?
battery->pdata->swelling_wc_low_temp_current_2nd : charging_current;
topoff_current = (topoff_current > battery->pdata->swelling_low_temp_topoff) ?
battery->pdata->swelling_low_temp_topoff : topoff_current;
} else if (battery->current_event & SEC_BAT_CURRENT_EVENT_HIGH_TEMP_SWELLING) {
charging_current = (charging_current > battery->pdata->swelling_wc_high_temp_current) ?
battery->pdata->swelling_wc_high_temp_current : charging_current;
topoff_current = (topoff_current > battery->pdata->swelling_high_temp_topoff) ?
battery->pdata->swelling_high_temp_topoff : topoff_current;
} else if (battery->current_event & SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING) {
charging_current = (charging_current > battery->pdata->swelling_wc_low_temp_current) ?
battery->pdata->swelling_wc_low_temp_current : charging_current;
} else if (battery->swelling_low_temp_3rd_ctrl && (battery->current_event & SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING_3RD)) {
charging_current = (charging_current > battery->pdata->swelling_wc_low_temp_current_3rd) ?
battery->pdata->swelling_wc_low_temp_current_3rd : charging_current;
}
if (is_hv_wireless_type(battery->cable_type) && (battery->wpc_vout_ctrl_lcd_on && battery->lcd_status)) {
input_current = 500;
if (input_current != battery->input_current) {
value.intval = input_current;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_EXT_PROP_PAD_VOLT_CTRL, value);
}
}
} else {
if (battery->current_event & SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING_2ND) {
if (charging_current > battery->pdata->swelling_low_temp_current_2nd) {
charging_current = battery->pdata->swelling_low_temp_current_2nd;
}
topoff_current = (topoff_current > battery->pdata->swelling_low_temp_topoff) ?
battery->pdata->swelling_low_temp_topoff : topoff_current;
} else if (battery->current_event & SEC_BAT_CURRENT_EVENT_HIGH_TEMP_SWELLING) {
if (charging_current > battery->pdata->swelling_high_temp_current) {
charging_current = battery->pdata->swelling_high_temp_current;
}
topoff_current = (topoff_current > battery->pdata->swelling_high_temp_topoff) ?
battery->pdata->swelling_high_temp_topoff : topoff_current;
} else if (battery->current_event & SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING) {
if (charging_current > battery->pdata->swelling_low_temp_current) {
charging_current = battery->pdata->swelling_low_temp_current;
}
} else if (battery->swelling_low_temp_3rd_ctrl && (battery->current_event & SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING_3RD)) {
if (charging_current > battery->pdata->swelling_low_temp_current_3rd) {
charging_current = battery->pdata->swelling_low_temp_current_3rd;
}
}
#if defined(CONFIG_ENABLE_100MA_CHARGING_BEFORE_USB_CONFIGURED)
/* usb unconfigured or suspend*/
if ((battery->cable_type == SEC_BATTERY_CABLE_USB) && !lpcharge &&
(battery->pdic_info.sink_status.rp_currentlvl == RP_CURRENT_LEVEL_DEFAULT)) {
if (battery->current_event & SEC_BAT_CURRENT_EVENT_USB_100MA) {
pr_info("%s: usb unconfigured\n", __func__);
input_current = USB_CURRENT_UNCONFIGURED;
charging_current = USB_CURRENT_UNCONFIGURED;
}
}
#endif
}/* is_wireless_type */
}/* is_nocharge_type(battery->cable_type) */
/* set input current, charging current */
if ((battery->input_current != input_current) ||
(battery->charging_current != charging_current)) {
/* update charge power */
battery->charge_power = battery->input_voltage * input_current;
if (battery->charge_power > battery->max_charge_power)
battery->max_charge_power = battery->charge_power;
/* In wireless charging, must be set charging current before input current. */
if (is_wireless_type(battery->cable_type)) {
if(battery->charging_current < charging_current) {
/* common charging current */
value.intval = charging_current;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CURRENT_AVG, value);
/* common input current */
if (battery->input_current != input_current) {
value.intval = input_current;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CURRENT_MAX, value);
battery->input_current = input_current;
}
} else {
/* common input current */
value.intval = input_current;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CURRENT_MAX, value);
battery->input_current = input_current;
/* wired charging current */
value.intval = charging_current;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CURRENT_AVG, value);
}
} else {
/* common input current */
value.intval = input_current;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CURRENT_MAX, value);
battery->input_current = input_current;
/* wired charging current */
value.intval = charging_current;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CURRENT_NOW, value);
}
if (charging_current <= 100)
battery->charging_current = 100;
else
battery->charging_current = charging_current;
pr_info("%s: power(%d), input(%d), charge(%d)\n", __func__,
battery->charge_power, battery->input_current, battery->charging_current);
}
/* set topoff current */
if (battery->topoff_current != topoff_current) {
value.intval = topoff_current;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CURRENT_FULL, value);
battery->topoff_current = topoff_current;
}
if (!afc_init) {
afc_init = true;
#if defined(CONFIG_AFC_CHARGER_MODE)
value.intval = 1;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_AFC_CHARGER_MODE,
value);
#endif
}
mutex_unlock(&battery->iolock);
return 0;
}
int sec_bat_set_charge(struct sec_battery_info *battery,
int chg_mode)
{
union power_supply_propval val = {0, };
ktime_t current_time = {0, };
struct timespec ts = {0, };
#if defined(CONFIG_PREVENT_USB_CONN_OVERHEAT)
if ((battery->cable_type == SEC_BATTERY_CABLE_HMT_CONNECTED) ||
((battery->usb_temp_flag || (battery->misc_event & BATT_MISC_EVENT_TEMP_HICCUP_TYPE)) && (chg_mode != SEC_BAT_CHG_MODE_BUCK_OFF)))
return 0;
#else
if (battery->cable_type == SEC_BATTERY_CABLE_HMT_CONNECTED)
return 0;
#endif
if ((battery->current_event & SEC_BAT_CURRENT_EVENT_CHARGE_DISABLE) &&
(chg_mode == SEC_BAT_CHG_MODE_CHARGING)) {
dev_info(battery->dev, "%s: charge disable by HMT\n", __func__);
chg_mode = SEC_BAT_CHG_MODE_CHARGING_OFF;
}
battery->charger_mode = chg_mode;
pr_info("%s set %s mode\n", __func__, sec_bat_charge_mode_str[chg_mode]);
val.intval = battery->status;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_STATUS, val);
current_time = ktime_get_boottime();
ts = ktime_to_timespec(current_time);
if (chg_mode == SEC_BAT_CHG_MODE_CHARGING) {
/*Reset charging start time only in initial charging start */
if (battery->charging_start_time == 0) {
if (ts.tv_sec < 1)
ts.tv_sec = 1;
battery->charging_start_time = ts.tv_sec;
battery->charging_next_time =
battery->pdata->charging_reset_time;
}
battery->charging_block = false;
} else {
battery->charging_start_time = 0;
battery->charging_passed_time = 0;
battery->charging_next_time = 0;
battery->charging_fullcharged_time = 0;
battery->full_check_cnt = 0;
battery->charging_block = true;
#if defined(CONFIG_STEP_CHARGING)
sec_bat_reset_step_charging(battery);
#endif
#if defined(CONFIG_BATTERY_CISD)
battery->usb_overheat_check = false;
battery->cisd.ab_vbat_check_count = 0;
if (chg_mode == SEC_BAT_CHG_MODE_BUCK_OFF) {
battery->cisd.data[CISD_DATA_BUCK_OFF]++;
battery->cisd.data[CISD_DATA_BUCK_OFF_PER_DAY]++;
}
#endif
}
battery->temp_highlimit_cnt = 0;
battery->temp_high_cnt = 0;
battery->temp_low_cnt = 0;
battery->temp_recover_cnt = 0;
if ((battery->wc_tx_enable && battery->buck_cntl_by_tx) &&
(chg_mode != SEC_BAT_CHG_MODE_BUCK_OFF)) {
dev_info(battery->dev, "@Tx_Mode %s: buck disable by Tx mode\n", __func__);
chg_mode = SEC_BAT_CHG_MODE_BUCK_OFF;
}
val.intval = chg_mode;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CHARGING_ENABLED, val);
psy_do_property(battery->pdata->fuelgauge_name, set,
POWER_SUPPLY_PROP_CHARGING_ENABLED, val);
return 0;
}
static bool sec_bat_check_by_psy(struct sec_battery_info *battery)
{
char *psy_name = NULL;
union power_supply_propval value = {0, };
bool ret = true;
switch (battery->pdata->battery_check_type) {
case SEC_BATTERY_CHECK_PMIC:
psy_name = battery->pdata->pmic_name;
break;
case SEC_BATTERY_CHECK_FUELGAUGE:
psy_name = battery->pdata->fuelgauge_name;
break;
case SEC_BATTERY_CHECK_CHARGER:
psy_name = battery->pdata->charger_name;
break;
default:
dev_err(battery->dev,
"%s: Invalid Battery Check Type\n", __func__);
ret = false;
goto battery_check_error;
break;
}
psy_do_property(psy_name, get,
POWER_SUPPLY_PROP_PRESENT, value);
ret = (bool)value.intval;
battery_check_error:
return ret;
}
static bool sec_bat_check(struct sec_battery_info *battery)
{
bool ret = true;
if (battery->factory_mode || battery->is_jig_on || factory_mode) {
dev_dbg(battery->dev, "%s: No need to check in factory mode\n",
__func__);
return ret;
}
if (battery->health != POWER_SUPPLY_HEALTH_GOOD &&
battery->health != POWER_SUPPLY_HEALTH_UNSPEC_FAILURE) {
dev_dbg(battery->dev, "%s: No need to check\n", __func__);
return ret;
}
switch (battery->pdata->battery_check_type) {
case SEC_BATTERY_CHECK_ADC:
if (is_nocharge_type(battery->cable_type))
ret = battery->present;
else
ret = sec_bat_check_vf_adc(battery);
break;
case SEC_BATTERY_CHECK_INT:
case SEC_BATTERY_CHECK_CALLBACK:
if (is_nocharge_type(battery->cable_type)) {
ret = battery->present;
} else {
if (battery->pdata->check_battery_callback)
ret = battery->pdata->check_battery_callback();
}
break;
case SEC_BATTERY_CHECK_PMIC:
case SEC_BATTERY_CHECK_FUELGAUGE:
case SEC_BATTERY_CHECK_CHARGER:
ret = sec_bat_check_by_psy(battery);
break;
case SEC_BATTERY_CHECK_NONE:
dev_dbg(battery->dev, "%s: No Check\n", __func__);
default:
break;
}
return ret;
}
static bool sec_bat_get_cable_type(
struct sec_battery_info *battery,
int cable_source_type)
{
bool ret = false;
int cable_type = battery->cable_type;
if (cable_source_type & SEC_BATTERY_CABLE_SOURCE_CALLBACK) {
if (battery->pdata->check_cable_callback)
cable_type =
battery->pdata->check_cable_callback();
}
if (cable_source_type & SEC_BATTERY_CABLE_SOURCE_ADC) {
if (gpio_get_value_cansleep(
battery->pdata->bat_gpio_ta_nconnected) ^
battery->pdata->bat_polarity_ta_nconnected)
cable_type = SEC_BATTERY_CABLE_NONE;
else
cable_type =
sec_bat_get_charger_type_adc(battery);
}
if (battery->cable_type == cable_type) {
dev_dbg(battery->dev,
"%s: No need to change cable status\n", __func__);
} else {
if (cable_type < SEC_BATTERY_CABLE_NONE ||
cable_type >= SEC_BATTERY_CABLE_MAX) {
dev_err(battery->dev,
"%s: Invalid cable type\n", __func__);
} else {
battery->cable_type = cable_type;
if (battery->pdata->check_cable_result_callback)
battery->pdata->check_cable_result_callback(
battery->cable_type);
ret = true;
dev_dbg(battery->dev, "%s: Cable Changed (%d)\n",
__func__, battery->cable_type);
}
}
return ret;
}
static void sec_bat_set_charging_status(struct sec_battery_info *battery,
int status) {
union power_supply_propval value = {0, };
switch (status) {
case POWER_SUPPLY_STATUS_CHARGING:
if (battery->siop_level < 100 || battery->lcd_status || battery->wc_tx_enable)
battery->stop_timer = true;
break;
case POWER_SUPPLY_STATUS_NOT_CHARGING:
case POWER_SUPPLY_STATUS_DISCHARGING:
if ((battery->status == POWER_SUPPLY_STATUS_FULL ||
(battery->capacity == 100 && !is_slate_mode(battery))) &&
!battery->store_mode) {
pr_info("%s : Update fg scale to 101%%\n", __func__);
value.intval = 100;
psy_do_property(battery->pdata->fuelgauge_name, set,
POWER_SUPPLY_PROP_CHARGE_FULL, value);
/* To get SOC value (NOT raw SOC), need to reset value */
value.intval = 0;
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_CAPACITY, value);
battery->capacity = value.intval;
}
battery->expired_time = battery->pdata->expired_time;
battery->prev_safety_time = 0;
break;
case POWER_SUPPLY_STATUS_FULL:
if (is_wireless_type(battery->cable_type)) {
bool send_cs100_cmd = true;
#ifdef CONFIG_CS100_JPNCONCEPT
psy_do_property(battery->pdata->wireless_charger_name, get,
POWER_SUPPLY_EXT_PROP_WIRELESS_TX_ID, value);
/* In case of the JPN PAD, this pad blocks the charge after give the cs100 command. */
send_cs100_cmd = (battery->charging_mode == SEC_BATTERY_CHARGING_2ND || value.intval);
#endif
if (send_cs100_cmd) {
value.intval = POWER_SUPPLY_STATUS_FULL;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_PROP_STATUS, value);
}
}
break;
default:
break;
}
battery->status = status;
}
static bool sec_bat_battery_cable_check(struct sec_battery_info *battery)
{
if (!sec_bat_check(battery)) {
if (battery->check_count < battery->pdata->check_count)
battery->check_count++;
else {
dev_err(battery->dev,
"%s: Battery Disconnected\n", __func__);
battery->present = false;
battery->health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
if (battery->status !=
POWER_SUPPLY_STATUS_DISCHARGING) {
sec_bat_set_charging_status(battery,
POWER_SUPPLY_STATUS_NOT_CHARGING);
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_BUCK_OFF);
}
if (battery->pdata->check_battery_result_callback)
battery->pdata->
check_battery_result_callback();
return false;
}
} else
battery->check_count = 0;
battery->present = true;
if (battery->health == POWER_SUPPLY_HEALTH_UNSPEC_FAILURE) {
battery->health = POWER_SUPPLY_HEALTH_GOOD;
if (battery->status == POWER_SUPPLY_STATUS_NOT_CHARGING) {
sec_bat_set_charging_status(battery,
POWER_SUPPLY_STATUS_CHARGING);
#if defined(CONFIG_BATTERY_SWELLING)
if (!battery->swelling_mode)
#endif
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING);
}
}
dev_dbg(battery->dev, "%s: Battery Connected\n", __func__);
if (battery->pdata->cable_check_type &
SEC_BATTERY_CABLE_CHECK_POLLING) {
if (sec_bat_get_cable_type(battery,
battery->pdata->cable_source_type)) {
wake_lock(&battery->cable_wake_lock);
queue_delayed_work(battery->monitor_wqueue,
&battery->cable_work, 0);
}
}
return true;
}
static int sec_bat_ovp_uvlo_by_psy(struct sec_battery_info *battery)
{
char *psy_name = NULL;
union power_supply_propval value = {0, };
value.intval = POWER_SUPPLY_HEALTH_GOOD;
switch (battery->pdata->ovp_uvlo_check_type) {
case SEC_BATTERY_OVP_UVLO_PMICPOLLING:
psy_name = battery->pdata->pmic_name;
break;
case SEC_BATTERY_OVP_UVLO_CHGPOLLING:
psy_name = battery->pdata->charger_name;
break;
default:
dev_err(battery->dev,
"%s: Invalid OVP/UVLO Check Type\n", __func__);
goto ovp_uvlo_check_error;
break;
}
psy_do_property(psy_name, get,
POWER_SUPPLY_PROP_HEALTH, value);
ovp_uvlo_check_error:
return value.intval;
}
static bool sec_bat_ovp_uvlo_result(
struct sec_battery_info *battery, int health)
{
if (battery->health != health) {
battery->health = health;
switch (health) {
case POWER_SUPPLY_HEALTH_GOOD:
dev_info(battery->dev, "%s: Safe voltage\n", __func__);
dev_info(battery->dev, "%s: is_recharging : %d\n", __func__, battery->is_recharging);
sec_bat_set_charging_status(battery,
POWER_SUPPLY_STATUS_CHARGING);
battery->charging_mode = SEC_BATTERY_CHARGING_1ST;
#if defined(CONFIG_BATTERY_SWELLING)
if (!battery->swelling_mode)
#endif
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING);
battery->health_check_count = 0;
break;
case POWER_SUPPLY_HEALTH_OVERVOLTAGE:
case POWER_SUPPLY_HEALTH_UNDERVOLTAGE:
dev_info(battery->dev,
"%s: Unsafe voltage (%d)\n",
__func__, health);
sec_bat_set_charging_status(battery,
POWER_SUPPLY_STATUS_NOT_CHARGING);
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING_OFF);
battery->charging_mode = SEC_BATTERY_CHARGING_NONE;
battery->is_recharging = false;
battery->health_check_count = DEFAULT_HEALTH_CHECK_COUNT;
#if defined(CONFIG_BATTERY_CISD)
battery->cisd.data[CISD_DATA_UNSAFETY_VOLTAGE]++;
battery->cisd.data[CISD_DATA_UNSAFE_VOLTAGE_PER_DAY]++;
#endif
/* Take the wakelock during 10 seconds
when over-voltage status is detected */
wake_lock_timeout(&battery->vbus_wake_lock, HZ * 10);
break;
}
power_supply_changed(battery->psy_bat);
return true;
}
return false;
}
static bool sec_bat_ovp_uvlo(struct sec_battery_info *battery)
{
int health = POWER_SUPPLY_HEALTH_GOOD;
if (battery->wdt_kick_disable) {
dev_dbg(battery->dev,
"%s: No need to check in wdt test\n",
__func__);
return false;
} else if ((battery->status == POWER_SUPPLY_STATUS_FULL) &&
(battery->charging_mode == SEC_BATTERY_CHARGING_NONE)) {
dev_dbg(battery->dev, "%s: No need to check in Full status", __func__);
return false;
}
if (battery->health != POWER_SUPPLY_HEALTH_GOOD &&
battery->health != POWER_SUPPLY_HEALTH_OVERVOLTAGE &&
battery->health != POWER_SUPPLY_HEALTH_UNDERVOLTAGE) {
dev_dbg(battery->dev, "%s: No need to check\n", __func__);
return false;
}
health = battery->health;
switch (battery->pdata->ovp_uvlo_check_type) {
case SEC_BATTERY_OVP_UVLO_CALLBACK:
if (battery->pdata->ovp_uvlo_callback)
health = battery->pdata->ovp_uvlo_callback();
break;
case SEC_BATTERY_OVP_UVLO_PMICPOLLING:
case SEC_BATTERY_OVP_UVLO_CHGPOLLING:
health = sec_bat_ovp_uvlo_by_psy(battery);
break;
case SEC_BATTERY_OVP_UVLO_PMICINT:
case SEC_BATTERY_OVP_UVLO_CHGINT:
/* nothing for interrupt check */
default:
break;
}
/*
* Move the location for calling the get_health
* in case of attaching the jig
*/
if (battery->factory_mode || battery->is_jig_on) {
dev_dbg(battery->dev,
"%s: No need to check in factory mode\n",
__func__);
return false;
}
return sec_bat_ovp_uvlo_result(battery, health);
}
static bool sec_bat_check_recharge(struct sec_battery_info *battery)
{
#if defined(CONFIG_BATTERY_SWELLING)
if (battery->swelling_mode == SWELLING_MODE_CHARGING ||
battery->swelling_mode == SWELLING_MODE_FULL) {
pr_info("%s: Skip normal recharge check routine for swelling mode\n",
__func__);
return false;
}
#endif
if ((battery->status == POWER_SUPPLY_STATUS_CHARGING) &&
(battery->pdata->full_condition_type &
SEC_BATTERY_FULL_CONDITION_NOTIMEFULL) &&
(battery->charging_mode == SEC_BATTERY_CHARGING_NONE)) {
dev_info(battery->dev,
"%s: Re-charging by NOTIMEFULL (%d)\n",
__func__, battery->capacity);
goto check_recharge_check_count;
}
if (battery->status == POWER_SUPPLY_STATUS_FULL &&
battery->charging_mode == SEC_BATTERY_CHARGING_NONE) {
int recharging_voltage = battery->pdata->recharge_condition_vcell;
if (battery->current_event & SEC_BAT_CURRENT_EVENT_LOW_TEMP_MODE) {
/* float voltage - 150mV */
recharging_voltage =\
(battery->pdata->chg_float_voltage /\
battery->pdata->chg_float_voltage_conv) - battery->pdata->swelling_low_rechg_thr;
dev_info(battery->dev, "%s: recharging voltage changed by low temp(%d)\n",
__func__, recharging_voltage);
}
dev_info(battery->dev, "%s: recharging voltage (%d)\n",
__func__, recharging_voltage);
if ((battery->pdata->recharge_condition_type &
SEC_BATTERY_RECHARGE_CONDITION_SOC) &&
(battery->capacity <=
battery->pdata->recharge_condition_soc)) {
battery->expired_time = battery->pdata->recharging_expired_time;
battery->prev_safety_time = 0;
dev_info(battery->dev,
"%s: Re-charging by SOC (%d)\n",
__func__, battery->capacity);
goto check_recharge_check_count;
}
if ((battery->pdata->recharge_condition_type &
SEC_BATTERY_RECHARGE_CONDITION_AVGVCELL) &&
(battery->voltage_avg <= recharging_voltage)) {
battery->expired_time = battery->pdata->recharging_expired_time;
battery->prev_safety_time = 0;
dev_info(battery->dev,
"%s: Re-charging by average VCELL (%d)\n",
__func__, battery->voltage_avg);
goto check_recharge_check_count;
}
if ((battery->pdata->recharge_condition_type &
SEC_BATTERY_RECHARGE_CONDITION_VCELL) &&
(battery->voltage_now <= recharging_voltage)) {
battery->expired_time = battery->pdata->recharging_expired_time;
battery->prev_safety_time = 0;
dev_info(battery->dev,
"%s: Re-charging by VCELL (%d)\n",
__func__, battery->voltage_now);
goto check_recharge_check_count;
}
}
battery->recharge_check_cnt = 0;
return false;
check_recharge_check_count:
if (battery->recharge_check_cnt <
battery->pdata->recharge_check_count)
battery->recharge_check_cnt++;
dev_dbg(battery->dev,
"%s: recharge count = %d\n",
__func__, battery->recharge_check_cnt);
if (battery->recharge_check_cnt >=
battery->pdata->recharge_check_count)
return true;
else
return false;
}
static bool sec_bat_voltage_check(struct sec_battery_info *battery)
{
union power_supply_propval value = {0, };
if (battery->status == POWER_SUPPLY_STATUS_DISCHARGING ||
battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_FAKE) {
dev_dbg(battery->dev,
"%s: Charging Disabled\n", __func__);
return true;
}
/* OVP/UVLO check */
if (sec_bat_ovp_uvlo(battery)) {
if (battery->pdata->ovp_uvlo_result_callback)
battery->pdata->
ovp_uvlo_result_callback(battery->health);
return false;
}
#if defined(CONFIG_ENABLE_FULL_BY_SOC)
if ((battery->status == POWER_SUPPLY_STATUS_FULL) &&
(battery->charging_mode != SEC_BATTERY_CHARGING_NONE || battery->swelling_mode)) {
pr_info("%s: chg mode (%d), swelling_mode(%d) \n",
__func__, battery->charging_mode, battery->swelling_mode);
value.intval = 0;
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_CAPACITY, value);
if (value.intval < 98 &&
battery->voltage_now <
(battery->pdata->recharge_condition_vcell - 50)) {
sec_bat_set_charging_status(battery,
POWER_SUPPLY_STATUS_CHARGING);
battery->is_recharging = false;
battery->charging_mode = SEC_BATTERY_CHARGING_1ST;
pr_info("%s: battery status full -> charging, RepSOC(%d)\n", __func__, value.intval);
return false;
}
}
#else
if ((battery->status == POWER_SUPPLY_STATUS_FULL) &&
#if defined(CONFIG_BATTERY_SWELLING)
(battery->charging_mode == SEC_BATTERY_CHARGING_2ND ||
battery->is_recharging || battery->swelling_mode)) {
#else
(battery->charging_mode == SEC_BATTERY_CHARGING_2ND ||
battery->is_recharging)) {
#endif
value.intval = 0;
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_CAPACITY, value);
if (value.intval <
battery->pdata->full_condition_soc &&
battery->voltage_now <
(battery->pdata->recharge_condition_vcell - 50)) {
sec_bat_set_charging_status(battery,
POWER_SUPPLY_STATUS_CHARGING);
battery->is_recharging = false;
battery->charging_mode = SEC_BATTERY_CHARGING_1ST;
dev_info(battery->dev,
"%s: battery status full -> charging, RepSOC(%d)\n", __func__, value.intval);
return false;
}
}
#endif
/* Re-Charging check */
if (sec_bat_check_recharge(battery)) {
if (battery->pdata->full_check_type !=
SEC_BATTERY_FULLCHARGED_NONE)
battery->charging_mode = SEC_BATTERY_CHARGING_1ST;
else
battery->charging_mode = SEC_BATTERY_CHARGING_2ND;
battery->is_recharging = true;
#if defined(CONFIG_BATTERY_CISD)
battery->cisd.data[CISD_DATA_RECHARGING_COUNT]++;
battery->cisd.data[CISD_DATA_RECHARGING_COUNT_PER_DAY]++;
#endif
#if defined(CONFIG_BATTERY_SWELLING)
if (!battery->swelling_mode)
#endif
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING);
return false;
}
return true;
}
#if defined(CONFIG_BATTERY_SWELLING)
static void sec_bat_swelling_check(struct sec_battery_info *battery)
{
union power_supply_propval val = {0, };
int swelling_rechg_voltage = battery->pdata->swelling_high_rechg_voltage;
bool en_swelling = false, en_rechg = false;
int swelling_high_recovery = battery->pdata->swelling_high_temp_recov;
int swelling_high_block = battery->pdata->swelling_high_temp_block;
if (battery->pdata->temp_check_type == SEC_BATTERY_TEMP_CHECK_NONE)
return;
if (is_wireless_type(battery->cable_type)) {
swelling_high_recovery = battery->pdata->swelling_wc_high_temp_recov;
swelling_high_block = battery->pdata->swelling_wc_high_temp_block;
}
pr_info("%s: swelling highblock(%d), highrecov(%d)\n", __func__, swelling_high_block, swelling_high_recovery);
psy_do_property(battery->pdata->charger_name, get,
POWER_SUPPLY_PROP_VOLTAGE_MAX, val);
pr_info("%s: status(%d), swell_mode(%d:%d:%d), cv(%d)mV, temp(%d)\n",
__func__, battery->status, battery->swelling_mode,
battery->charging_block, (battery->current_event & SEC_BAT_CURRENT_EVENT_LOW_TEMP_MODE),
val.intval, battery->temperature);
/* swelling_mode
under voltage over voltage, battery missing */
if ((battery->status == POWER_SUPPLY_STATUS_DISCHARGING) ||\
(battery->status == POWER_SUPPLY_STATUS_NOT_CHARGING) ||
battery->skip_swelling) {
pr_debug("%s: DISCHARGING or NOT-CHARGING or 15 test mode. stop swelling mode\n", __func__);
battery->swelling_mode = SWELLING_MODE_NONE;
sec_bat_set_current_event(battery, 0, SEC_BAT_CURRENT_EVENT_SWELLING_MODE);
goto skip_swelling_check;
}
if (!battery->swelling_mode) {
if (battery->temperature >= swelling_high_block) {
pr_info("%s: swelling mode start. stop charging\n", __func__);
battery->swelling_mode = SWELLING_MODE_CHARGING;
battery->swelling_full_check_cnt = 0;
if (battery->wc_tx_enable &&
(is_hv_wire_type(battery->cable_type) || is_pd_wire_type(battery->cable_type)))
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING_OFF);
else
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_BUCK_OFF);
if (battery->temperature >= swelling_high_block) {
#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_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_HIGH_TEMP_SWELLING,
SEC_BAT_CURRENT_EVENT_SWELLING_MODE);
}
en_swelling = true;
} else if ((battery->temperature <= battery->pdata->swelling_low_temp_block_2nd) &&
!(battery->current_event & SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING_2ND)) {
pr_info("%s: 2nd low temperature swelling step!! reduce current\n", __func__);
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING_2ND,
SEC_BAT_CURRENT_EVENT_SWELLING_MODE);
if (battery->pdata->swelling_drop_float_voltage_lowtemp) {
pr_info("%s: swelling_drop_float_voltage_lowtemp. set float volt (%d)\n",
__func__, battery->pdata->swelling_drop_float_voltage);
battery->swelling_mode = SWELLING_MODE_CHARGING;
battery->swelling_full_check_cnt = 0;
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING_OFF);
en_swelling = true;
}
} else if ((battery->temperature <= battery->pdata->swelling_low_temp_block_1st) &&
!(battery->current_event & (SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING | SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING_2ND))) {
pr_info("%s: low temperature reduce current\n", __func__);
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING,
SEC_BAT_CURRENT_EVENT_SWELLING_MODE);
} else if (battery->swelling_low_temp_3rd_ctrl && (battery->temperature <= battery->pdata->swelling_low_temp_block_3rd) &&
!(battery->current_event & SEC_BAT_CURRENT_EVENT_LOW_TEMP_MODE)) {
pr_info("%s: 3rd low temperature swelling step!! reduce current\n", __func__);
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING_3RD,
SEC_BAT_CURRENT_EVENT_SWELLING_MODE);
} else if (battery->swelling_low_temp_3rd_ctrl && (battery->temperature >= battery->pdata->swelling_low_temp_recov_3rd) &&
(battery->current_event & SEC_BAT_CURRENT_EVENT_LOW_TEMP_MODE)) {
pr_info("%s: normal temperature temperature recover current\n", __func__);
sec_bat_set_current_event(battery, 0, SEC_BAT_CURRENT_EVENT_LOW_TEMP_MODE);
} else if ((battery->temperature >= battery->pdata->swelling_low_temp_recov_1st) &&
(battery->current_event & (SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING | SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING_2ND))) {
if (battery->swelling_low_temp_3rd_ctrl) {
pr_info("%s: upto 1st low temperature swelling recovery temp! 3rd low temp swelling current set\n", __func__);
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING_3RD,
SEC_BAT_CURRENT_EVENT_SWELLING_MODE);
} else {
pr_info("%s: normal temperature temperature recover current\n", __func__);
sec_bat_set_current_event(battery, 0, SEC_BAT_CURRENT_EVENT_LOW_TEMP_MODE);
}
} else if ((battery->temperature >= battery->pdata->swelling_low_temp_recov_2nd) &&
(battery->current_event & SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING_2ND)) {
pr_info("%s: upto 2nd low temperature swelling recovery temp! 1st low temp swelling current set\n", __func__);
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING,
SEC_BAT_CURRENT_EVENT_SWELLING_MODE);
}
}
if (!battery->voltage_now)
return;
if (battery->swelling_mode) {
if ((battery->temperature <= swelling_high_recovery && !battery->pdata->swelling_drop_float_voltage_lowtemp) ||
(battery->pdata->swelling_drop_float_voltage_lowtemp &&
battery->temperature >= battery->pdata->swelling_low_temp_recov_2nd && battery->temperature <= swelling_high_recovery)) {
pr_info("%s: swelling mode end. restart charging\n", __func__);
battery->swelling_mode = SWELLING_MODE_NONE;
battery->charging_mode = SEC_BATTERY_CHARGING_1ST;
sec_bat_set_current_event(battery, 0, SEC_BAT_CURRENT_EVENT_SWELLING_MODE);
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING);
/* restore 4.4V float voltage */
val.intval = battery->pdata->swelling_normal_float_voltage;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_VOLTAGE_MAX, val);
#if defined(CONFIG_BATTERY_CISD)
battery->cisd.data[CISD_DATA_SWELLING_RECOVERY_CNT]++;
battery->cisd.data[CISD_DATA_SWELLING_RECOVERY_CNT_PER_DAY]++;
#endif
} else if (battery->voltage_now < swelling_rechg_voltage &&
battery->charging_block) {
pr_info("%s: swelling mode recharging start. Vbatt(%d)\n",
__func__, battery->voltage_now);
battery->charging_mode = SEC_BATTERY_CHARGING_1ST;
en_rechg = true;
/* change drop float voltage */
val.intval = battery->pdata->swelling_drop_float_voltage;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_VOLTAGE_MAX, val);
/* set charging enable */
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING);
if (battery->temperature > swelling_high_recovery) {
pr_info("%s: swelling mode reduce charging current(HIGH-temp:%d)\n",
__func__, battery->temperature);
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_HIGH_TEMP_SWELLING,
SEC_BAT_CURRENT_EVENT_SWELLING_MODE);
}
#if defined(CONFIG_BATTERY_CISD)
battery->cisd.data[CISD_DATA_SWELLING_CHARGING_COUNT]++;
battery->cisd.data[CISD_DATA_SWELLING_CHARGING_COUNT_PER_DAY]++;
#endif
}
}
if (en_swelling && !en_rechg) {
pr_info("%s : SAFETY TIME RESET (SWELLING MODE CHARING STOP!)\n", __func__);
battery->expired_time = battery->pdata->expired_time;
battery->prev_safety_time = 0;
}
skip_swelling_check:
dev_dbg(battery->dev, "%s end\n", __func__);
}
#endif
#if defined(CONFIG_BATTERY_AGE_FORECAST)
static bool sec_bat_set_aging_step(struct sec_battery_info *battery, int step)
{
union power_supply_propval value = {0, };
int property_type = POWER_SUPPLY_PROP_CAPACITY_LEVEL;
if (battery->pdata->num_age_step <= 0 || step < 0 || step >= battery->pdata->num_age_step) {
pr_info("%s: [AGE] abnormal age step : %d/%d\n",
__func__, step, battery->pdata->num_age_step-1);
return false;
}
battery->pdata->age_step = step;
/* float voltage */
battery->pdata->chg_float_voltage =
battery->pdata->age_data[battery->pdata->age_step].float_voltage;
battery->pdata->swelling_normal_float_voltage =
battery->pdata->chg_float_voltage;
#if defined(CONFIG_STEP_CHARGING)
if (!battery->swelling_mode && (battery->step_charging_status < 0)) {
#else
if (!battery->swelling_mode) {
#endif
value.intval = battery->pdata->chg_float_voltage;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_VOLTAGE_MAX, value);
}
/* full/recharge condition */
battery->pdata->recharge_condition_vcell =
battery->pdata->age_data[battery->pdata->age_step].recharge_condition_vcell;
battery->pdata->full_condition_soc =
battery->pdata->age_data[battery->pdata->age_step].full_condition_soc;
battery->pdata->full_condition_vcell =
battery->pdata->age_data[battery->pdata->age_step].full_condition_vcell;
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_MODEL_NAME, value);
switch (value.intval) {
case IC_TYPE_IFPMIC_S2MU106:
value.intval = battery->pdata->age_step;
property_type = POWER_SUPPLY_EXT_PROP_UPDATE_BATTERY_DATA;
break;
case IC_TYPE_IFPMIC_SM5713:
case IC_TYPE_UNKNOWN:
default:
value.intval = battery->pdata->full_condition_soc;
break;
}
psy_do_property(battery->pdata->fuelgauge_name, set, property_type, value);
#if defined(CONFIG_STEP_CHARGING)
sec_bat_set_aging_info_step_charging(battery);
#endif
dev_info(battery->dev,
"%s: Step(%d/%d), Cycle(%d), float_v(%d), r_v(%d), f_s(%d), f_vl(%d)\n",
__func__,
battery->pdata->age_step, battery->pdata->num_age_step-1, battery->batt_cycle,
battery->pdata->chg_float_voltage,
battery->pdata->recharge_condition_vcell,
battery->pdata->full_condition_soc,
battery->pdata->full_condition_vcell);
#if defined(CONFIG_BATTERY_AGE_FORECAST_B2B)
{
int i;
bool bChanged = false;
battery->pdata->max_charging_current =
battery->pdata->age_data[battery->pdata->age_step].max_charging_current;
for (i = 0; i < SEC_BATTERY_CABLE_MAX; i++) {
if (battery->pdata->charging_current[i].fast_charging_current >
battery->pdata->max_charging_current) {
dev_info(battery->dev, "%s: cable(%d) charging current(%d->%d)\n",
__func__, i,
battery->pdata->charging_current[i].fast_charging_current,
battery->pdata->max_charging_current);
battery->pdata->charging_current[i].fast_charging_current =
battery->pdata->max_charging_current;
if (battery->cable_type == i)
bChanged = true;
}
}
if (bChanged)
sec_bat_set_charging_current(battery);
}
#endif
return true;
}
void sec_bat_aging_check(struct sec_battery_info *battery)
{
int prev_step = battery->pdata->age_step;
int calc_step = -1;
bool ret = 0;
if ((battery->pdata->num_age_step <= 0) || (battery->batt_cycle <= 0))
return;
if (battery->temperature < 50) {
pr_info("%s: [AGE] skip (temperature:%d)\n", __func__, battery->temperature);
return;
}
for (calc_step = battery->pdata->num_age_step - 1; calc_step >= 0; calc_step--) {
if (battery->pdata->age_data[calc_step].cycle <= battery->batt_cycle)
break;
}
if (calc_step == prev_step)
return;
ret = sec_bat_set_aging_step(battery, calc_step);
dev_info(battery->dev,
"%s: %s change step (%d->%d), Cycle(%d)\n",
__func__, ret ? "Succeed in" : "Fail to",
prev_step, battery->pdata->age_step, battery->batt_cycle);
}
#endif
#if defined(CONFIG_BATTERY_AGE_FORECAST_DETACHABLE)
void sec_bat_check_battery_health(struct sec_battery_info *battery)
{
/* no need to check in detachable battery model */
}
#else
void sec_bat_check_battery_health(struct sec_battery_info *battery)
{
union power_supply_propval value;
battery_health_condition state;
int i, battery_health;
#if defined(CONFIG_BATTERY_AGE_FORECAST_DETACHABLE)
pr_err("%s: detachable battery not support\n", __func__);
return;
#endif
/* check to support ASoC and Cycle */
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_ENERGY_FULL, value);
#if !defined(CONFIG_BATTERY_AGE_FORECAST)
value.intval = -1;
#endif
if (value.intval <= 0 || battery->pdata->health_condition == NULL) {
pr_err("%s: does not support cycle or asoc or health_condition\n", __func__);
return;
}
/* Checking Cycle and ASoC */
state.cycle = state.asoc = BATTERY_HEALTH_BAD;
for (i = BATTERY_HEALTH_MAX - 1; i >= 0; i--) {
if (battery->pdata->health_condition[i].cycle >= (battery->batt_cycle % 10000))
state.cycle = i + BATTERY_HEALTH_GOOD;
if (battery->pdata->health_condition[i].asoc <= battery->batt_asoc)
state.asoc = i + BATTERY_HEALTH_GOOD;
}
battery_health = max(state.cycle, state.asoc);
pr_info("%s: update battery_health(%d), (%d - %d)\n",
__func__, battery_health, state.cycle, state.asoc);
/* Update battery health */
sec_bat_set_misc_event(battery,
(battery_health << BATTERY_HEALTH_SHIFT), BATT_MISC_EVENT_BATTERY_HEALTH);
}
#endif
static bool sec_bat_temperature(
struct sec_battery_info *battery)
{
bool ret;
ret = true;
if (is_wireless_type(battery->cable_type) || battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_FAKE) {
battery->temp_highlimit_threshold =
battery->pdata->temp_highlimit_threshold_normal;
battery->temp_highlimit_recovery =
battery->pdata->temp_highlimit_recovery_normal;
battery->temp_high_threshold =
battery->pdata->wpc_high_threshold_normal;
battery->temp_high_recovery =
battery->pdata->wpc_high_recovery_normal;
battery->temp_low_recovery =
battery->pdata->wpc_low_recovery_normal;
battery->temp_low_threshold =
battery->pdata->wpc_low_threshold_normal;
} else {
if (lpcharge) {
battery->temp_highlimit_threshold =
battery->pdata->temp_highlimit_threshold_lpm;
battery->temp_highlimit_recovery =
battery->pdata->temp_highlimit_recovery_lpm;
battery->temp_high_threshold =
battery->pdata->temp_high_threshold_lpm;
battery->temp_high_recovery =
battery->pdata->temp_high_recovery_lpm;
battery->temp_low_recovery =
battery->pdata->temp_low_recovery_lpm;
battery->temp_low_threshold =
battery->pdata->temp_low_threshold_lpm;
} else {
battery->temp_highlimit_threshold =
battery->pdata->temp_highlimit_threshold_normal;
battery->temp_highlimit_recovery =
battery->pdata->temp_highlimit_recovery_normal;
battery->temp_high_threshold =
battery->pdata->temp_high_threshold_normal;
battery->temp_high_recovery =
battery->pdata->temp_high_recovery_normal;
battery->temp_low_recovery =
battery->pdata->temp_low_recovery_normal;
battery->temp_low_threshold =
battery->pdata->temp_low_threshold_normal;
}
}
return ret;
}
static bool sec_bat_temperature_check(
struct sec_battery_info *battery)
{
int pre_health = POWER_SUPPLY_HEALTH_GOOD;
if (battery->status == POWER_SUPPLY_STATUS_DISCHARGING) {
battery->health_change = false;
dev_dbg(battery->dev,
"%s: Charging Disabled\n", __func__);
return true;
}
if (battery->health != POWER_SUPPLY_HEALTH_GOOD &&
battery->health != POWER_SUPPLY_HEALTH_OVERHEAT &&
battery->health != POWER_SUPPLY_HEALTH_COLD &&
battery->health != POWER_SUPPLY_HEALTH_OVERHEATLIMIT) {
dev_dbg(battery->dev, "%s: No need to check\n", __func__);
return false;
}
#if defined(CONFIG_ENG_BATTERY_CONCEPT) || defined(CONFIG_SEC_FACTORY)
if (!battery->cooldown_mode) {
dev_err(battery->dev, "%s: Forced temp check block\n", __func__);
return true;
}
#endif
sec_bat_temperature(battery);
if (battery->pdata->temp_check_type == SEC_BATTERY_TEMP_CHECK_NONE) {
dev_err(battery->dev,
"%s: Invalid Temp Check Type\n", __func__);
return true;
}
pre_health = battery->health;
if (battery->pdata->usb_temp_check_type && (battery->usb_temp >= battery->temp_highlimit_threshold)) {
if (battery->health != POWER_SUPPLY_HEALTH_OVERHEATLIMIT) {
if (battery->temp_highlimit_cnt <
battery->pdata->temp_check_count) {
battery->temp_highlimit_cnt++;
battery->temp_high_cnt = 0;
battery->temp_low_cnt = 0;
battery->temp_recover_cnt = 0;
}
dev_err(battery->dev,
"%s: usb therm highlimit count = %d\n",
__func__, battery->temp_highlimit_cnt);
}
} else if (battery->pdata->usb_temp_check_type && (battery->usb_temp > battery->temp_highlimit_recovery)
&& (battery->health == POWER_SUPPLY_HEALTH_OVERHEATLIMIT)) {
dev_err(battery->dev,
"%s: usb therm highlimit \n",__func__);
} else if (battery->temperature >= battery->temp_highlimit_threshold && !battery->pdata->usb_temp_check_type) {
if (battery->health != POWER_SUPPLY_HEALTH_OVERHEATLIMIT) {
if (battery->temp_highlimit_cnt <
battery->pdata->temp_check_count) {
battery->temp_highlimit_cnt++;
battery->temp_high_cnt = 0;
battery->temp_low_cnt = 0;
battery->temp_recover_cnt = 0;
}
dev_err(battery->dev,
"%s: highlimit count = %d\n",
__func__, battery->temp_highlimit_cnt);
}
} else if (battery->temperature >= battery->temp_high_threshold) {
if (battery->health == POWER_SUPPLY_HEALTH_OVERHEATLIMIT && !battery->pdata->usb_temp_check_type) {
if (battery->temperature <= battery->temp_highlimit_recovery) {
if (battery->temp_recover_cnt <
battery->pdata->temp_check_count) {
battery->temp_recover_cnt++;
battery->temp_highlimit_cnt = 0;
battery->temp_high_cnt = 0;
battery->temp_low_cnt = 0;
}
dev_err(battery->dev,
"%s: recovery count = %d\n",
__func__, battery->temp_recover_cnt);
}
} else if (battery->health != POWER_SUPPLY_HEALTH_OVERHEAT) {
if (battery->temp_high_cnt <
battery->pdata->temp_check_count) {
battery->temp_high_cnt++;
battery->temp_highlimit_cnt = 0;
battery->temp_low_cnt = 0;
battery->temp_recover_cnt = 0;
}
dev_err(battery->dev,
"%s: high count = %d\n",
__func__, battery->temp_high_cnt);
}
} else if ((battery->temperature <= battery->temp_high_recovery) &&
(battery->temperature >= battery->temp_low_recovery)) {
if (battery->health == POWER_SUPPLY_HEALTH_OVERHEAT ||
battery->health == POWER_SUPPLY_HEALTH_OVERHEATLIMIT ||
battery->health == POWER_SUPPLY_HEALTH_COLD) {
if (battery->temp_recover_cnt <
battery->pdata->temp_check_count) {
battery->temp_recover_cnt++;
battery->temp_highlimit_cnt = 0;
battery->temp_high_cnt = 0;
battery->temp_low_cnt = 0;
}
dev_err(battery->dev,
"%s: recovery count = %d\n",
__func__, battery->temp_recover_cnt);
}
} else if (battery->temperature <= battery->temp_low_threshold) {
if (battery->health != POWER_SUPPLY_HEALTH_COLD) {
if (battery->temp_low_cnt <
battery->pdata->temp_check_count) {
battery->temp_low_cnt++;
battery->temp_highlimit_cnt = 0;
battery->temp_high_cnt = 0;
battery->temp_recover_cnt = 0;
}
dev_err(battery->dev,
"%s: low count = %d\n",
__func__, battery->temp_low_cnt);
}
} else {
battery->temp_highlimit_cnt = 0;
battery->temp_high_cnt = 0;
battery->temp_low_cnt = 0;
battery->temp_recover_cnt = 0;
}
#if defined(CONFIG_PREVENT_USB_CONN_OVERHEAT)
if (battery->pdata->usb_thermal_source && !battery->usb_temp_flag) {
int gap = 0;
if (battery->usb_temp > battery->temperature)
gap = battery->usb_temp - battery->temperature;
if (battery->usb_temp >= battery->temp_highlimit_threshold) {
pr_info("%s: Usb Temp over %d(%d)\n", __func__, battery->temp_highlimit_threshold, battery->usb_temp);
battery->cisd.data[CISD_DATA_USB_OVERHEAT_CHARGING]++;
battery->cisd.data[CISD_DATA_USB_OVERHEAT_CHARGING_PER_DAY]++;
battery->usb_temp_flag = true;
} 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);
battery->usb_temp_flag = true;
battery->cisd.data[CISD_DATA_USB_OVERHEAT_RAPID_CHANGE]++;
battery->cisd.data[CISD_DATA_USB_OVERHEAT_RAPID_CHANGE_PER_DAY]++;
if (gap > battery->cisd.data[CISD_DATA_USB_OVERHEAT_ALONE_PER_DAY])
battery->cisd.data[CISD_DATA_USB_OVERHEAT_ALONE_PER_DAY] = gap;
}
if (battery->usb_temp_flag) {
pr_info("%s: Usb temp flag %d\n", __func__, battery->usb_temp_flag);
if (lpcharge && (battery->health != POWER_SUPPLY_HEALTH_OVERHEATLIMIT)) {
battery->temp_highlimit_cnt = battery->pdata->temp_check_count;
} else if (is_pd_wire_type(battery->cable_type)) {
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_BUCK_OFF);
select_pdo(1);
muic_set_hiccup_mode(1);
pdic_manual_ccopen_request(1);
return false;
} else {
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_BUCK_OFF);
muic_set_hiccup_mode(1);
return false;
}
}
}
#endif
if (battery->temp_highlimit_cnt >=
battery->pdata->temp_check_count) {
#if defined(CONFIG_PREVENT_USB_CONN_OVERHEAT)
if (lpcharge) {
battery->health = POWER_SUPPLY_HEALTH_OVERHEATLIMIT;
battery->temp_highlimit_cnt = 0;
}
#else
battery->health = POWER_SUPPLY_HEALTH_OVERHEATLIMIT;
battery->temp_highlimit_cnt = 0;
#endif
} else {
#if defined(CONFIG_PREVENT_USB_CONN_OVERHEAT)
if (lpcharge && battery->usb_temp_flag)
return true;
#endif
if (battery->temp_high_cnt >=
battery->pdata->temp_check_count) {
battery->health = POWER_SUPPLY_HEALTH_OVERHEAT;
battery->temp_high_cnt = 0;
} else if (battery->temp_low_cnt >=
battery->pdata->temp_check_count) {
battery->health = POWER_SUPPLY_HEALTH_COLD;
battery->temp_low_cnt = 0;
} else if (battery->temp_recover_cnt >=
battery->pdata->temp_check_count) {
if (battery->health == POWER_SUPPLY_HEALTH_OVERHEATLIMIT &&
battery->temperature > battery->temp_high_recovery) {
battery->health = POWER_SUPPLY_HEALTH_OVERHEAT;
} else {
battery->health = POWER_SUPPLY_HEALTH_GOOD;
}
battery->temp_recover_cnt = 0;
}
}
if (pre_health != battery->health) {
battery->health_change = true;
dev_info(battery->dev, "%s, health_change true\n", __func__);
} else {
battery->health_change = false;
}
if ((battery->health == POWER_SUPPLY_HEALTH_OVERHEAT) ||
(battery->health == POWER_SUPPLY_HEALTH_COLD) ||
(battery->health == POWER_SUPPLY_HEALTH_OVERHEATLIMIT)) {
if (battery->health_change) {
union power_supply_propval val = {0, };
battery->is_abnormal_temp = true;
if (is_wireless_type(battery->cable_type) || battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_FAKE) {
val.intval = battery->health;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_PROP_HEALTH, val);
}
dev_info(battery->dev,
"%s: Unsafe Temperature\n", __func__);
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
if (battery->health == POWER_SUPPLY_HEALTH_OVERHEATLIMIT) {
/* change charging current to battery (default 0mA) */
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_BUCK_OFF);
if (is_hv_afc_wire_type(battery->cable_type) && !battery->vbus_limit) {
#if 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 (battery->health == POWER_SUPPLY_HEALTH_OVERHEAT) {
/* to discharge battery */
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_BUCK_OFF);
} else {
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING_OFF);
}
return false;
}
/* dose not need buck control at low temperature */
if (battery->health == POWER_SUPPLY_HEALTH_OVERHEAT) {
if((battery->charger_mode == SEC_BAT_CHG_MODE_BUCK_OFF) &&
(battery->voltage_now < (battery->pdata->swelling_drop_float_voltage / battery->pdata->chg_float_voltage_conv))) {
pr_info("%s: Vnow(%dmV) < %dmV has dropped enough to get buck on mode \n", __func__,
battery->voltage_now,
(battery->pdata->swelling_drop_float_voltage / battery->pdata->chg_float_voltage_conv));
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING_OFF);
}
}
} else {
/* if recovered from not charging */
if ((battery->health == POWER_SUPPLY_HEALTH_GOOD) &&
(battery->status ==
POWER_SUPPLY_STATUS_NOT_CHARGING)) {
battery->is_abnormal_temp = false;
dev_info(battery->dev,
"%s: Safe Temperature\n", __func__);
if (battery->capacity >= 100)
sec_bat_set_charging_status(battery,
POWER_SUPPLY_STATUS_FULL);
else /* Normal Charging */
sec_bat_set_charging_status(battery,
POWER_SUPPLY_STATUS_CHARGING);
#if defined(CONFIG_BATTERY_SWELLING)
if (battery->temperature > battery->pdata->swelling_high_temp_recov) {
pr_info("%s: swelling mode start. stop charging\n", __func__);
battery->swelling_mode = SWELLING_MODE_CHARGING;
battery->swelling_full_check_cnt = 0;
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_BUCK_OFF);
if (battery->temperature > battery->pdata->swelling_high_temp_recov) {
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_HIGH_TEMP_SWELLING,
SEC_BAT_CURRENT_EVENT_HIGH_TEMP_SWELLING);
}
} else {
union power_supply_propval val = {0, };
/* restore 4.4V float voltage */
val.intval = battery->pdata->swelling_normal_float_voltage;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_VOLTAGE_MAX, val);
/* turn on charger by cable type */
if((battery->status == POWER_SUPPLY_STATUS_FULL) &&
(battery->charging_mode == SEC_BATTERY_CHARGING_NONE)) {
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING_OFF);
} else {
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING);
}
if (pre_health == POWER_SUPPLY_HEALTH_COLD) {
if (battery->temperature <= battery->pdata->swelling_low_temp_recov_2nd) {
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING_2ND,
SEC_BAT_CURRENT_EVENT_SWELLING_MODE);
if (battery->pdata->swelling_drop_float_voltage_lowtemp) {
pr_info("%s: low swelling mode start. stop charging\n", __func__);
battery->swelling_mode = SWELLING_MODE_CHARGING;
battery->swelling_full_check_cnt = 0;
val.intval = battery->pdata->swelling_drop_float_voltage;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_VOLTAGE_MAX, val);
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING_OFF);
}
} else if (battery->temperature <= battery->pdata->swelling_low_temp_block_1st) {
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING,
SEC_BAT_CURRENT_EVENT_SWELLING_MODE);
} else if (battery->swelling_low_temp_3rd_ctrl && battery->temperature <= battery->pdata->swelling_low_temp_block_3rd) {
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_LOW_TEMP_SWELLING_3RD,
SEC_BAT_CURRENT_EVENT_SWELLING_MODE);
}
}
}
#else
/* turn on charger by cable type */
if((battery->status == POWER_SUPPLY_STATUS_FULL) &&
(battery->charging_mode == SEC_BATTERY_CHARGING_NONE)) {
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING_OFF);
} else {
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING);
}
#endif
return false;
}
}
return true;
}
static bool sec_bat_check_fullcharged_condition(struct sec_battery_info *battery) {
int full_check_type = SEC_BATTERY_FULLCHARGED_NONE;
if (battery->charging_mode == SEC_BATTERY_CHARGING_1ST)
full_check_type = battery->pdata->full_check_type;
else
full_check_type = battery->pdata->full_check_type_2nd;
switch (full_check_type) {
case SEC_BATTERY_FULLCHARGED_ADC:
case SEC_BATTERY_FULLCHARGED_FG_CURRENT:
case SEC_BATTERY_FULLCHARGED_SOC:
case SEC_BATTERY_FULLCHARGED_CHGGPIO:
case SEC_BATTERY_FULLCHARGED_CHGPSY:
break;
/* If these is NOT full check type or NONE full check type,
* it is full-charged
*/
case SEC_BATTERY_FULLCHARGED_CHGINT:
case SEC_BATTERY_FULLCHARGED_TIME:
case SEC_BATTERY_FULLCHARGED_NONE:
default:
return true;
break;
}
#if defined(CONFIG_ENABLE_FULL_BY_SOC)
if (battery->capacity >= 100 &&
!battery->is_recharging) {
dev_info(battery->dev,
"%s: enough SOC (%d%%), skip other full_condition_type\n",
__func__, battery->capacity);
return true;
}
#endif
if (battery->pdata->full_condition_type &
SEC_BATTERY_FULL_CONDITION_SOC) {
if (battery->capacity <
battery->pdata->full_condition_soc) {
dev_dbg(battery->dev,
"%s: Not enough SOC (%d%%)\n",
__func__, battery->capacity);
return false;
}
}
if (battery->pdata->full_condition_type &
SEC_BATTERY_FULL_CONDITION_VCELL) {
if (battery->voltage_now <
battery->pdata->full_condition_vcell) {
dev_dbg(battery->dev,
"%s: Not enough VCELL (%dmV)\n",
__func__, battery->voltage_now);
return false;
}
}
if (battery->pdata->full_condition_type &
SEC_BATTERY_FULL_CONDITION_AVGVCELL) {
if (battery->voltage_avg <
battery->pdata->full_condition_avgvcell) {
dev_dbg(battery->dev,
"%s: Not enough AVGVCELL (%dmV)\n",
__func__, battery->voltage_avg);
return false;
}
}
if (battery->pdata->full_condition_type &
SEC_BATTERY_FULL_CONDITION_OCV) {
if (battery->voltage_ocv <
battery->pdata->full_condition_ocv) {
dev_dbg(battery->dev,
"%s: Not enough OCV (%dmV)\n",
__func__, battery->voltage_ocv);
return false;
}
}
return true;
}
static void sec_bat_do_test_function(struct sec_battery_info *battery) {
union power_supply_propval value = {0, };
switch (battery->test_mode) {
case 1:
if (battery->status == POWER_SUPPLY_STATUS_CHARGING) {
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING_OFF);
sec_bat_set_charging_status(battery,
POWER_SUPPLY_STATUS_DISCHARGING);
}
break;
case 2:
if(battery->status == POWER_SUPPLY_STATUS_DISCHARGING) {
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING);
psy_do_property(battery->pdata->charger_name, get,
POWER_SUPPLY_PROP_STATUS, value);
sec_bat_set_charging_status(battery, value.intval);
}
battery->test_mode = 0;
break;
case 3: // clear temp block
battery->health = POWER_SUPPLY_HEALTH_GOOD;
sec_bat_set_charging_status(battery,
POWER_SUPPLY_STATUS_DISCHARGING);
break;
case 4:
if(battery->status == POWER_SUPPLY_STATUS_DISCHARGING) {
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING);
psy_do_property(battery->pdata->charger_name, get,
POWER_SUPPLY_PROP_STATUS, value);
sec_bat_set_charging_status(battery, value.intval);
}
break;
default:
pr_info("%s: error test: unknown state\n", __func__);
break;
}
}
static bool sec_bat_time_management(struct sec_battery_info *battery) {
struct timespec ts = {0, };
unsigned long charging_time;
if (battery->charging_start_time == 0 || !battery->safety_timer_set) {
dev_dbg(battery->dev,
"%s: Charging Disabled\n", __func__);
return true;
}
get_monotonic_boottime(&ts);
if (ts.tv_sec >= battery->charging_start_time) {
charging_time = ts.tv_sec - battery->charging_start_time;
} else {
charging_time = 0xFFFFFFFF - battery->charging_start_time
+ ts.tv_sec;
}
battery->charging_passed_time = charging_time;
switch (battery->status) {
case POWER_SUPPLY_STATUS_FULL:
if (battery->expired_time == 0) {
dev_info(battery->dev,
"%s: Recharging Timer Expired\n", __func__);
battery->charging_mode = SEC_BATTERY_CHARGING_NONE;
battery->health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
sec_bat_set_charging_status(battery, POWER_SUPPLY_STATUS_NOT_CHARGING);
battery->is_recharging = false;
if (sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING_OFF)) {
dev_err(battery->dev,
"%s: Fail to Set Charger\n", __func__);
return true;
}
return false;
}
break;
case POWER_SUPPLY_STATUS_CHARGING:
if ((battery->pdata->full_condition_type &
SEC_BATTERY_FULL_CONDITION_NOTIMEFULL) &&
(battery->is_recharging && (battery->expired_time == 0))) {
dev_info(battery->dev,
"%s: Recharging Timer Expired\n", __func__);
battery->charging_mode = SEC_BATTERY_CHARGING_NONE;
battery->health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
sec_bat_set_charging_status(battery, POWER_SUPPLY_STATUS_NOT_CHARGING);
battery->is_recharging = false;
if (sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING_OFF)) {
dev_err(battery->dev,
"%s: Fail to Set Charger\n", __func__);
return true;
}
return false;
} else if (!battery->is_recharging &&
(battery->expired_time == 0)) {
dev_info(battery->dev,
"%s: Charging Timer Expired\n", __func__);
battery->charging_mode = SEC_BATTERY_CHARGING_NONE;
battery->health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
sec_bat_set_charging_status(battery, POWER_SUPPLY_STATUS_NOT_CHARGING);
#if defined(CONFIG_BATTERY_CISD)
battery->cisd.data[CISD_DATA_SAFETY_TIMER]++;
battery->cisd.data[CISD_DATA_SAFETY_TIMER_PER_DAY]++;
#endif
#if defined(CONFIG_SEC_ABC)
sec_abc_send_event("MODULE=battery@ERROR=safety_timer");
#endif
if (sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING_OFF)) {
dev_err(battery->dev,
"%s: Fail to Set Charger\n", __func__);
return true;
}
return false;
}
break;
default:
dev_err(battery->dev,
"%s: Undefine Battery Status\n", __func__);
return true;
}
return true;
}
static bool sec_bat_check_fullcharged(struct sec_battery_info *battery) {
union power_supply_propval value = {0, };
int current_adc = 0;
int full_check_type = SEC_BATTERY_FULLCHARGED_NONE;
bool ret = false;
int err = 0;
if (!sec_bat_check_fullcharged_condition(battery))
goto not_full_charged;
if (battery->charging_mode == SEC_BATTERY_CHARGING_1ST)
full_check_type = battery->pdata->full_check_type;
else
full_check_type = battery->pdata->full_check_type_2nd;
switch (full_check_type) {
case SEC_BATTERY_FULLCHARGED_ADC:
current_adc =
sec_bat_get_adc_data(battery,
SEC_BAT_ADC_CHANNEL_FULL_CHECK,
battery->pdata->adc_check_count);
dev_dbg(battery->dev,
"%s: Current ADC (%d)\n",
__func__, current_adc);
if (current_adc < 0)
break;
battery->current_adc = current_adc;
if (battery->current_adc <
(battery->charging_mode ==
SEC_BATTERY_CHARGING_1ST ?
battery->pdata->full_check_current_1st :
battery->pdata->full_check_current_2nd)) {
battery->full_check_cnt++;
dev_dbg(battery->dev,
"%s: Full Check ADC (%d)\n",
__func__,
battery->full_check_cnt);
} else
battery->full_check_cnt = 0;
break;
case SEC_BATTERY_FULLCHARGED_FG_CURRENT:
if ((battery->current_now > 0 && battery->current_now <
(battery->charging_mode ==
SEC_BATTERY_CHARGING_1ST ?
battery->pdata->full_check_current_1st :
battery->pdata->full_check_current_2nd)) &&
(battery->current_avg > 0 && battery->current_avg <
(battery->charging_mode ==
SEC_BATTERY_CHARGING_1ST ?
battery->pdata->full_check_current_1st :
battery->pdata->full_check_current_2nd))) {
battery->full_check_cnt++;
dev_dbg(battery->dev,
"%s: Full Check Current (%d)\n",
__func__,
battery->full_check_cnt);
} else
battery->full_check_cnt = 0;
break;
case SEC_BATTERY_FULLCHARGED_TIME:
if ((battery->charging_mode ==
SEC_BATTERY_CHARGING_2ND ?
(battery->charging_passed_time -
battery->charging_fullcharged_time) :
battery->charging_passed_time) >
(battery->charging_mode ==
SEC_BATTERY_CHARGING_1ST ?
battery->pdata->full_check_current_1st :
battery->pdata->full_check_current_2nd)) {
battery->full_check_cnt++;
dev_dbg(battery->dev,
"%s: Full Check Time (%d)\n",
__func__,
battery->full_check_cnt);
} else
battery->full_check_cnt = 0;
break;
case SEC_BATTERY_FULLCHARGED_SOC:
if (battery->capacity <=
(battery->charging_mode ==
SEC_BATTERY_CHARGING_1ST ?
battery->pdata->full_check_current_1st :
battery->pdata->full_check_current_2nd)) {
battery->full_check_cnt++;
dev_dbg(battery->dev,
"%s: Full Check SOC (%d)\n",
__func__,
battery->full_check_cnt);
} else
battery->full_check_cnt = 0;
break;
case SEC_BATTERY_FULLCHARGED_CHGGPIO:
err = gpio_request(
battery->pdata->chg_gpio_full_check,
"GPIO_CHG_FULL");
if (err) {
dev_err(battery->dev,
"%s: Error in Request of GPIO\n", __func__);
break;
}
if (!(gpio_get_value_cansleep(
battery->pdata->chg_gpio_full_check) ^
!battery->pdata->chg_polarity_full_check)) {
battery->full_check_cnt++;
dev_dbg(battery->dev,
"%s: Full Check GPIO (%d)\n",
__func__, battery->full_check_cnt);
} else
battery->full_check_cnt = 0;
gpio_free(battery->pdata->chg_gpio_full_check);
break;
case SEC_BATTERY_FULLCHARGED_CHGINT:
case SEC_BATTERY_FULLCHARGED_CHGPSY:
psy_do_property(battery->pdata->charger_name, get,
POWER_SUPPLY_PROP_STATUS, value);
if (value.intval == POWER_SUPPLY_STATUS_FULL) {
battery->full_check_cnt++;
dev_info(battery->dev,
"%s: Full Check Charger (%d)\n",
__func__, battery->full_check_cnt);
} else
battery->full_check_cnt = 0;
break;
/* If these is NOT full check type or NONE full check type,
* it is full-charged
*/
case SEC_BATTERY_FULLCHARGED_NONE:
battery->full_check_cnt = 0;
ret = true;
break;
default:
dev_err(battery->dev,
"%s: Invalid Full Check\n", __func__);
break;
}
#if defined(CONFIG_ENABLE_FULL_BY_SOC)
if (battery->capacity >= 100 &&
battery->charging_mode == SEC_BATTERY_CHARGING_1ST &&
!battery->is_recharging) {
battery->full_check_cnt++;
dev_info(battery->dev,
"%s: enough SOC to make FULL(%d%%)\n",
__func__, battery->capacity);
}
#endif
if (battery->full_check_cnt >=
battery->pdata->full_check_count) {
battery->full_check_cnt = 0;
ret = true;
}
not_full_charged:
return ret;
}
static void sec_bat_do_fullcharged(struct sec_battery_info *battery, bool force_fullcharged) {
union power_supply_propval value = {0, };
/* To let charger/fuel gauge know the full status,
* set status before calling sec_bat_set_charge()
*/
#if defined(CONFIG_BATTERY_CISD)
if (battery->status != POWER_SUPPLY_STATUS_FULL) {
battery->cisd.data[CISD_DATA_FULL_COUNT]++;
battery->cisd.data[CISD_DATA_FULL_COUNT_PER_DAY]++;
}
#endif
sec_bat_set_charging_status(battery,
POWER_SUPPLY_STATUS_FULL);
if (battery->charging_mode == SEC_BATTERY_CHARGING_1ST &&
battery->pdata->full_check_type_2nd != SEC_BATTERY_FULLCHARGED_NONE && !force_fullcharged) {
battery->charging_mode = SEC_BATTERY_CHARGING_2ND;
battery->charging_fullcharged_time = battery->charging_passed_time;
value.intval = SEC_BAT_CHG_MODE_CHARGING_OFF;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CHARGING_ENABLED, value);
sec_bat_set_charging_current(battery);
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING);
pr_info("%s: 1st charging is done\n", __func__);
} else {
battery->charging_mode = SEC_BATTERY_CHARGING_NONE;
battery->is_recharging = false;
if (!battery->wdt_kick_disable) {
pr_info("%s: wdt kick enable -> Charger Off, %d\n",
__func__, battery->wdt_kick_disable);
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING_OFF);
pr_info("%s: 2nd charging is done\n", __func__);
} else {
pr_info("%s: wdt kick disabled -> skip charger off, %d\n",
__func__, battery->wdt_kick_disable);
}
#if defined(CONFIG_BATTERY_AGE_FORECAST)
sec_bat_aging_check(battery);
#endif
/* this concept is only for power-off charging mode*/
if (is_hv_wire_type(battery->cable_type) && is_hv_wire_type(battery->wire_status) &&
!battery->store_mode && (battery->cable_type != SEC_BATTERY_CABLE_QC30) &&
#if defined(CONFIG_MUIC_HV_SUPPORT_POGO_DOCK)
(battery->muic_cable_type != ATTACHED_DEV_POGO_DOCK_9V_MUIC) &&
#endif
lpcharge && !battery->vbus_chg_by_full) {
/* vbus level : 9V --> 5V */
battery->vbus_chg_by_full = true;
battery->vbus_chg_by_siop = SEC_INPUT_VOLTAGE_5V;
muic_afc_set_voltage(SEC_INPUT_VOLTAGE_5V);
pr_info("%s: vbus is set 5V by 2nd full\n", __func__);
}
value.intval = POWER_SUPPLY_STATUS_FULL;
psy_do_property(battery->pdata->fuelgauge_name, set,
POWER_SUPPLY_PROP_STATUS, value);
}
/* platform can NOT get information of battery
* because wakeup time is too short to check uevent
* To make sure that target is wakeup if full-charged,
* activated wake lock in a few seconds
*/
if (battery->pdata->polling_type == SEC_BATTERY_MONITOR_ALARM)
wake_lock_timeout(&battery->vbus_wake_lock, HZ * 10);
}
static bool sec_bat_fullcharged_check(struct sec_battery_info *battery) {
if ((battery->charging_mode == SEC_BATTERY_CHARGING_NONE) ||
(battery->status == POWER_SUPPLY_STATUS_NOT_CHARGING)) {
dev_dbg(battery->dev,
"%s: No Need to Check Full-Charged\n", __func__);
return true;
}
if (sec_bat_check_fullcharged(battery)) {
union power_supply_propval value = {0, };
if (battery->capacity < 100) {
#if defined(CONFIG_ENABLE_FULL_BY_SOC)
/* update capacity max */
value.intval = battery->capacity;
psy_do_property(battery->pdata->fuelgauge_name, set,
POWER_SUPPLY_PROP_CHARGE_FULL, value);
pr_info("%s : forced full-charged sequence for the capacity(%d)\n",
__func__, battery->capacity);
#endif
battery->full_check_cnt = battery->pdata->full_check_count;
} else {
sec_bat_do_fullcharged(battery, false);
}
#if !defined(CONFIG_ENABLE_FULL_BY_SOC)
/* update capacity max */
value.intval = battery->capacity;
psy_do_property(battery->pdata->fuelgauge_name, set,
POWER_SUPPLY_PROP_CHARGE_FULL, value);
pr_info("%s : forced full-charged sequence for the capacity(%d)\n",
__func__, battery->capacity);
#endif
}
dev_info(battery->dev,
"%s: Charging Mode : %s\n", __func__,
battery->is_recharging ?
sec_bat_charging_mode_str[SEC_BATTERY_CHARGING_RECHARGING] :
sec_bat_charging_mode_str[battery->charging_mode]);
return true;
}
static void sec_bat_get_temperature_info(struct sec_battery_info *battery) {
union power_supply_propval value = {0, };
static bool shipmode_en = false;
/* get battery thm info */
switch (battery->pdata->thermal_source) {
case SEC_BATTERY_THERMAL_SOURCE_FG:
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_TEMP, value);
battery->temperature = value.intval;
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_TEMP_AMBIENT, value);
battery->temper_amb = value.intval;
break;
case SEC_BATTERY_THERMAL_SOURCE_CALLBACK:
if (battery->pdata->get_temperature_callback) {
battery->pdata->get_temperature_callback(
POWER_SUPPLY_PROP_TEMP, &value);
battery->temperature = value.intval;
psy_do_property(battery->pdata->fuelgauge_name, set,
POWER_SUPPLY_PROP_TEMP, value);
battery->pdata->get_temperature_callback(
POWER_SUPPLY_PROP_TEMP_AMBIENT, &value);
battery->temper_amb = value.intval;
psy_do_property(battery->pdata->fuelgauge_name, set,
POWER_SUPPLY_PROP_TEMP_AMBIENT, value);
}
break;
case SEC_BATTERY_THERMAL_SOURCE_ADC:
if(sec_bat_get_value_by_adc(battery,
SEC_BAT_ADC_CHANNEL_TEMP, &value, battery->pdata->temp_check_type)) {
battery->temperature = value.intval;
battery->temper_amb = value.intval;
} else {
battery->temperature = 0;
battery->temper_amb = 0;
}
break;
default:
break;
}
/* get usb thm info */
switch (battery->pdata->usb_thermal_source) {
case SEC_BATTERY_THERMAL_SOURCE_FG:
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_TEMP, value);
battery->usb_temp = value.intval;
break;
case SEC_BATTERY_THERMAL_SOURCE_CALLBACK:
break;
case SEC_BATTERY_THERMAL_SOURCE_ADC:
if(sec_bat_get_value_by_adc(battery,
SEC_BAT_ADC_CHANNEL_USB_TEMP, &value, battery->pdata->usb_temp_check_type)) {
battery->usb_temp = value.intval;
/* this shoud be moved */
if (battery->vbus_limit && battery->usb_temp <= battery->temp_highlimit_recovery)
battery->vbus_limit = false;
} else
battery->usb_temp = 0;
break;
default:
break;
}
/* get chg thm info */
switch (battery->pdata->chg_thermal_source) {
case SEC_BATTERY_THERMAL_SOURCE_FG:
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_TEMP, value);
battery->chg_temp = value.intval;
break;
case SEC_BATTERY_THERMAL_SOURCE_CALLBACK:
break;
case SEC_BATTERY_THERMAL_SOURCE_ADC:
if(sec_bat_get_value_by_adc(battery,
SEC_BAT_ADC_CHANNEL_CHG_TEMP, &value, battery->pdata->chg_temp_check_type)) {
battery->chg_temp = value.intval;
} else
battery->chg_temp = 0;
break;
default:
break;
}
/* get wpc thm info */
switch (battery->pdata->wpc_thermal_source) {
case SEC_BATTERY_THERMAL_SOURCE_FG:
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_TEMP, value);
battery->wpc_temp = value.intval;
break;
case SEC_BATTERY_THERMAL_SOURCE_CALLBACK:
break;
case SEC_BATTERY_THERMAL_SOURCE_ADC:
if(sec_bat_get_value_by_adc(battery,
SEC_BAT_ADC_CHANNEL_WPC_TEMP, &value, battery->pdata->wpc_temp_check_type)) {
battery->wpc_temp = value.intval;
} else
battery->wpc_temp = 0;
break;
default:
break;
}
/* get slave thm info */
switch (battery->pdata->slave_thermal_source) {
case SEC_BATTERY_THERMAL_SOURCE_FG:
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_TEMP, value);
battery->slave_chg_temp = value.intval;
break;
case SEC_BATTERY_THERMAL_SOURCE_CALLBACK:
break;
case SEC_BATTERY_THERMAL_SOURCE_ADC:
if(sec_bat_get_value_by_adc(battery,
SEC_BAT_ADC_CHANNEL_SLAVE_CHG_TEMP, &value, battery->pdata->slave_chg_temp_check_type)) {
battery->slave_chg_temp = value.intval;
/* set temperature */
value.intval = ((battery->slave_chg_temp) << 16) | (battery->chg_temp);
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_TEMP, value);
} else
battery->slave_chg_temp = 0;
break;
default:
break;
}
#if defined(CONFIG_ENG_BATTERY_CONCEPT)
if (battery->temperature_test_battery > -300 && battery->temperature_test_battery < 3000) {
pr_info("%s : battery temperature test %d\n", __func__, battery->temperature_test_battery);
battery->temperature = battery->temperature_test_battery;
}
if (battery->temperature_test_usb > -300 && battery->temperature_test_usb < 3000) {
pr_info("%s : usb temperature test %d\n", __func__, battery->temperature_test_usb);
battery->usb_temp = battery->temperature_test_usb;
}
if (battery->temperature_test_wpc > -300 && battery->temperature_test_wpc < 3000) {
pr_info("%s : wpc temperature test %d\n", __func__, battery->temperature_test_wpc);
battery->wpc_temp = battery->temperature_test_wpc;
}
if (battery->temperature_test_chg > -300 && battery->temperature_test_chg < 3000) {
pr_info("%s : chg temperature test %d\n", __func__, battery->temperature_test_chg);
battery->chg_temp = battery->temperature_test_chg;
}
#endif
#if defined(CONFIG_SEC_FACTORY)
if (battery->pdata->usb_temp_check_type) {
if (battery->temperature <= (-200))
value.intval = (battery->usb_temp <= (-200) ? battery->chg_temp : battery->usb_temp);
else
value.intval = battery->temperature;
}
#else
value.intval = battery->temperature;
#endif
psy_do_property(battery->pdata->fuelgauge_name, set,
POWER_SUPPLY_PROP_TEMP, value);
psy_do_property(battery->pdata->fuelgauge_name, set,
POWER_SUPPLY_PROP_TEMP_AMBIENT, value);
if (!battery->pdata->dis_auto_shipmode_temp_ctrl) {
if (battery->temperature < 0 && !shipmode_en) {
value.intval = 0;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_EXT_PROP_AUTO_SHIPMODE_CONTROL, value);
shipmode_en = true;
} else if (battery->temperature >= 50 && shipmode_en) {
value.intval = 1;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_EXT_PROP_AUTO_SHIPMODE_CONTROL, value);
shipmode_en = false;
}
}
}
void sec_bat_get_battery_info(struct sec_battery_info *battery) {
union power_supply_propval value = {0, };
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_VOLTAGE_NOW, value);
battery->voltage_now = value.intval;
value.intval = SEC_BATTERY_VOLTAGE_AVERAGE;
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_VOLTAGE_AVG, value);
battery->voltage_avg = value.intval;
/* Do not call it to reduce time after cable_work, this funtion call FG full log*/
if (!(battery->current_event & SEC_BAT_CURRENT_EVENT_SKIP_HEATING_CONTROL)) {
value.intval = SEC_BATTERY_VOLTAGE_OCV;
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_VOLTAGE_AVG, value);
battery->voltage_ocv = value.intval;
}
value.intval = SEC_BATTERY_CURRENT_MA;
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_CURRENT_NOW, value);
battery->current_now = value.intval;
value.intval = SEC_BATTERY_CURRENT_MA;
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_CURRENT_AVG, value);
battery->current_avg = value.intval;
value.intval = SEC_BATTERY_ISYS_AVG_MA;
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_EXT_PROP_MEASURE_SYS, value);
battery->current_sys_avg = value.intval;
value.intval = SEC_BATTERY_ISYS_MA;
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_EXT_PROP_MEASURE_SYS, value);
battery->current_sys = value.intval;
/* input current limit in charger */
psy_do_property(battery->pdata->charger_name, get,
POWER_SUPPLY_PROP_CURRENT_MAX, value);
battery->current_max = value.intval;
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_CHARGE_COUNTER, value);
battery->charge_counter = value.intval;
/* check abnormal status for wireless charging */
if (!(battery->current_event & SEC_BAT_CURRENT_EVENT_SKIP_HEATING_CONTROL) &&
(is_wireless_type(battery->cable_type) ||battery->wc_tx_enable)) {
value.intval = (battery->status == POWER_SUPPLY_STATUS_FULL) ?
100 : battery->capacity;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_PROP_ENERGY_NOW, value);
}
value.intval = (battery->status == POWER_SUPPLY_STATUS_FULL) ?
100 : battery->capacity;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_PROP_CAPACITY, value);
sec_bat_get_temperature_info(battery);
/* To get SOC value (NOT raw SOC), need to reset value */
value.intval = 0;
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_CAPACITY, value);
/* if the battery status was full, and SOC wasn't 100% yet,
then ignore FG SOC, and report (previous SOC +1)% */
battery->capacity = value.intval;
pr_info("%s:Vnow(%dmV),Vavg(%dmV),Inow(%dmA),Iavg(%dmA),Isysavg(%dmA),Imax(%dmA),Ichg(%dmA),SOC(%d%%),"
"Tbat(%d),Tusb(%d),Tchg(%d),Twpc(%d)\n", __func__,
battery->voltage_now, battery->voltage_avg, battery->current_now,
battery->current_avg, battery->current_sys_avg,
battery->current_max, battery->charging_current,
battery->capacity, battery->temperature,
battery->usb_temp, battery->chg_temp, battery->wpc_temp
);
dev_dbg(battery->dev,
"%s,Vavg(%dmV),Vocv(%dmV),Tamb(%d),"
"Iavg(%dmA),Iadc(%d)\n",
battery->present ? "Connected" : "Disconnected",
battery->voltage_avg, battery->voltage_ocv,
battery->temper_amb,
battery->current_avg, battery->current_adc);
#if defined(CONFIG_SEC_DEBUG_EXTRA_INFO)
sec_debug_set_extra_info_batt(battery->capacity, battery->voltage_avg, battery->temperature, battery->current_avg);
#endif
}
static void sec_bat_polling_work(struct work_struct *work) {
struct sec_battery_info *battery = container_of(
work, struct sec_battery_info, polling_work.work);
wake_lock(&battery->monitor_wake_lock);
queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
dev_dbg(battery->dev, "%s: Activated\n", __func__);
}
static void sec_bat_program_alarm(struct sec_battery_info *battery, int seconds) {
alarm_start(&battery->polling_alarm,
ktime_add(battery->last_poll_time, ktime_set(seconds, 0)));
}
static unsigned int sec_bat_get_polling_time(struct sec_battery_info *battery) {
if (battery->status ==
POWER_SUPPLY_STATUS_FULL)
battery->polling_time =
battery->pdata->polling_time[
POWER_SUPPLY_STATUS_CHARGING];
else
battery->polling_time =
battery->pdata->polling_time[
battery->status];
battery->polling_short = true;
switch (battery->status) {
case POWER_SUPPLY_STATUS_CHARGING:
if (battery->polling_in_sleep)
battery->polling_short = false;
break;
case POWER_SUPPLY_STATUS_DISCHARGING:
if (battery->polling_in_sleep && (battery->ps_enable != true)) {
battery->polling_time =
battery->pdata->polling_time[
SEC_BATTERY_POLLING_TIME_SLEEP];
} else
battery->polling_time =
battery->pdata->polling_time[
battery->status];
if (!battery->wc_enable) {
battery->polling_time = battery->pdata->polling_time[
SEC_BATTERY_POLLING_TIME_CHARGING];
pr_info("%s: wc_enable is false, polling time is 30sec\n", __func__);
}
battery->polling_short = false;
break;
case POWER_SUPPLY_STATUS_FULL:
if (battery->polling_in_sleep) {
if (!(battery->pdata->full_condition_type &
SEC_BATTERY_FULL_CONDITION_NOSLEEPINFULL) &&
battery->charging_mode ==
SEC_BATTERY_CHARGING_NONE) {
battery->polling_time =
battery->pdata->polling_time[
SEC_BATTERY_POLLING_TIME_SLEEP];
}
battery->polling_short = false;
} else {
if (battery->charging_mode ==
SEC_BATTERY_CHARGING_NONE)
battery->polling_short = false;
}
break;
case POWER_SUPPLY_STATUS_NOT_CHARGING:
if ((battery->health == POWER_SUPPLY_HEALTH_OVERVOLTAGE ||
(battery->health == POWER_SUPPLY_HEALTH_UNDERVOLTAGE)) &&
(battery->health_check_count > 0)) {
battery->health_check_count--;
battery->polling_time = 1;
battery->polling_short = false;
}
break;
}
#if defined(CONFIG_WIRELESS_TX_MODE)
if (battery->wc_tx_enable) {
battery->polling_time = 10;
battery->polling_short = false;
pr_info("%s: Tx mode enable polling time is 10sec\n", __func__);
}
#endif
if (battery->polling_short)
return battery->pdata->polling_time[
SEC_BATTERY_POLLING_TIME_BASIC];
/* set polling time to 46s to reduce current noise on wc */
else if (battery->cable_type == SEC_BATTERY_CABLE_WIRELESS &&
battery->status == POWER_SUPPLY_STATUS_CHARGING)
battery->polling_time = 46;
return battery->polling_time;
}
static bool sec_bat_is_short_polling(struct sec_battery_info *battery) {
/* Change the full and short monitoring sequence
* Originally, full monitoring was the last time of polling_count
* But change full monitoring to first time
* because temperature check is too late
*/
if (!battery->polling_short || battery->polling_count == 1)
return false;
else
return true;
}
static void sec_bat_update_polling_count(
struct sec_battery_info *battery)
{
/* do NOT change polling count in sleep
* even though it is short polling
* to keep polling count along sleep/wakeup
*/
if (battery->polling_short && battery->polling_in_sleep)
return;
if (battery->polling_short &&
((battery->polling_time /
battery->pdata->polling_time[
SEC_BATTERY_POLLING_TIME_BASIC])
> battery->polling_count))
battery->polling_count++;
else
battery->polling_count = 1; /* initial value = 1 */
}
static void sec_bat_set_polling(
struct sec_battery_info *battery)
{
unsigned int polling_time_temp = 0;
dev_dbg(battery->dev, "%s: Start\n", __func__);
polling_time_temp = sec_bat_get_polling_time(battery);
dev_dbg(battery->dev,
"%s: Status:%s, Sleep:%s, Charging:%s, Short Poll:%s\n",
__func__, sec_bat_status_str[battery->status],
battery->polling_in_sleep ? "Yes" : "No",
(battery->charging_mode ==
SEC_BATTERY_CHARGING_NONE) ? "No" : "Yes",
battery->polling_short ? "Yes" : "No");
dev_info(battery->dev,
"%s: Polling time %d/%d sec.\n", __func__,
battery->polling_short ?
(polling_time_temp * battery->polling_count) :
polling_time_temp, battery->polling_time);
/* To sync with log above,
* change polling count after log is displayed
* Do NOT update polling count in initial monitor
*/
if (!battery->pdata->monitor_initial_count)
sec_bat_update_polling_count(battery);
else
dev_dbg(battery->dev,
"%s: Initial monitor %d times left.\n", __func__,
battery->pdata->monitor_initial_count);
switch (battery->pdata->polling_type) {
case SEC_BATTERY_MONITOR_WORKQUEUE:
if (battery->pdata->monitor_initial_count) {
battery->pdata->monitor_initial_count--;
schedule_delayed_work(&battery->polling_work, HZ);
} else
schedule_delayed_work(&battery->polling_work,
polling_time_temp * HZ);
break;
case SEC_BATTERY_MONITOR_ALARM:
battery->last_poll_time = ktime_get_boottime();
if (battery->pdata->monitor_initial_count) {
battery->pdata->monitor_initial_count--;
sec_bat_program_alarm(battery, 1);
} else
sec_bat_program_alarm(battery, polling_time_temp);
break;
case SEC_BATTERY_MONITOR_TIMER:
break;
default:
break;
}
dev_dbg(battery->dev, "%s: End\n", __func__);
}
/* OTG during HV wireless charging or sleep mode have 4.5W normal wireless charging UI */
static bool sec_bat_hv_wc_normal_mode_check(struct sec_battery_info *battery)
{
union power_supply_propval value = {0, };
psy_do_property(battery->pdata->charger_name, get,
POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL, value);
if (value.intval || sleep_mode) {
pr_info("%s: otg(%d), sleep_mode(%d)\n", __func__, value.intval, sleep_mode);
return true;
}
return false;
}
#if defined(CONFIG_BATTERY_SWELLING)
static void sec_bat_swelling_fullcharged_check(struct sec_battery_info *battery)
{
union power_supply_propval value = {0, };
int topoff_current = battery->pdata->full_check_current_1st;
switch (battery->pdata->full_check_type_2nd) {
case SEC_BATTERY_FULLCHARGED_FG_CURRENT:
if (battery->current_event & SEC_BAT_CURRENT_EVENT_LOW_TEMP_MODE)
topoff_current = (topoff_current > battery->pdata->swelling_low_temp_topoff) ?
battery->pdata->swelling_low_temp_topoff : topoff_current;
else if (battery->current_event & SEC_BAT_CURRENT_EVENT_HIGH_TEMP_SWELLING)
topoff_current = (topoff_current > battery->pdata->swelling_high_temp_topoff) ?
battery->pdata->swelling_high_temp_topoff : topoff_current;
if ((battery->current_now > 0 && battery->current_now < topoff_current) &&
(battery->current_avg > 0 && battery->current_avg < topoff_current) &&
((battery->pdata->swelling_drop_float_voltage - 100) < battery->voltage_now)) {
value.intval = POWER_SUPPLY_STATUS_FULL;
}
break;
default:
psy_do_property(battery->pdata->charger_name, get,
POWER_SUPPLY_PROP_STATUS, value);
break;
}
if (value.intval == POWER_SUPPLY_STATUS_FULL) {
battery->swelling_full_check_cnt++;
pr_info("%s: Swelling mode full-charged check (%d)\n",
__func__, battery->swelling_full_check_cnt);
} else
battery->swelling_full_check_cnt = 0;
if (battery->swelling_full_check_cnt >=
battery->pdata->full_check_count) {
battery->swelling_full_check_cnt = 0;
battery->charging_mode = SEC_BATTERY_CHARGING_NONE;
battery->is_recharging = false;
battery->swelling_mode = SWELLING_MODE_FULL;
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING_OFF);
battery->expired_time = battery->pdata->expired_time;
battery->prev_safety_time = 0;
#if defined(CONFIG_BATTERY_CISD)
battery->cisd.data[CISD_DATA_SWELLING_FULL_CNT]++;
battery->cisd.data[CISD_DATA_SWELLING_FULL_CNT_PER_DAY]++;
#endif
}
}
#endif
static void sec_bat_handle_tx_misalign(struct sec_battery_info *battery, bool trigger_misalign)
{
struct timespec ts = {0, };
if (trigger_misalign) {
if (battery->tx_misalign_start_time == 0) {
ts = ktime_to_timespec(ktime_get_boottime());
battery->tx_misalign_start_time = ts.tv_sec;
}
pr_info("@Tx_Mode %s: misalign is triggered!!(%d) \n", __func__, ++battery->tx_misalign_cnt);
/* Attention!! in this case, 0x00(TX_OFF) is sent first,
and then 0x8000(RETRY) is sent */
if (battery->tx_misalign_cnt < 3) {
battery->tx_retry_case |= SEC_BAT_TX_RETRY_MISALIGN;
sec_wireless_set_tx_enable(battery, false);
sec_bat_set_tx_event(battery, BATT_TX_EVENT_WIRELESS_TX_RETRY, BATT_TX_EVENT_WIRELESS_TX_RETRY);
} else {
battery->tx_retry_case &= ~SEC_BAT_TX_RETRY_MISALIGN;
battery->tx_misalign_start_time = 0;
battery->tx_misalign_cnt = 0;
pr_info("@Tx_Mode %s: Misalign over 3 times, TX OFF (cancel misalign)\n", __func__);
sec_bat_set_tx_event(battery, BATT_TX_EVENT_WIRELESS_TX_MISALIGN, BATT_TX_EVENT_WIRELESS_TX_MISALIGN);
sec_wireless_set_tx_enable(battery, false);
}
} else if (battery->tx_retry_case & SEC_BAT_TX_RETRY_MISALIGN) {
get_monotonic_boottime(&ts);
if (ts.tv_sec >= battery->tx_misalign_start_time) {
battery->tx_misalign_passed_time = ts.tv_sec - battery->tx_misalign_start_time;
} else {
battery->tx_misalign_passed_time = 0xFFFFFFFF - battery->tx_misalign_start_time
+ ts.tv_sec;
}
pr_info("@Tx_Mode %s: already misaligned, passed time(%ld)\n", __func__, battery->tx_misalign_passed_time);
if (battery->tx_misalign_passed_time >= 60) {
pr_info("@Tx_Mode %s: after 1min\n", __func__);
if (battery->wc_tx_enable) {
if (battery->wc_rx_connected) {
pr_info("@Tx_Mode %s: RX Dev, Keep TX ON status (cancel misalign)\n", __func__);
} else {
pr_info("@Tx_Mode %s: NO RX Dev, TX OFF (cancel misalign)\n", __func__);
sec_bat_set_tx_event(battery, BATT_TX_EVENT_WIRELESS_TX_MISALIGN, BATT_TX_EVENT_WIRELESS_TX_MISALIGN);
sec_wireless_set_tx_enable(battery, false);
}
} else {
pr_info("@Tx_Mode %s: Keep TX OFF status (cancel misalign)\n", __func__);
//sec_bat_set_tx_event(battery, BATT_TX_EVENT_WIRELESS_TX_ETC, BATT_TX_EVENT_WIRELESS_TX_ETC);
}
battery->tx_retry_case &= ~SEC_BAT_TX_RETRY_MISALIGN;
battery->tx_misalign_start_time = 0;
battery->tx_misalign_cnt = 0;
}
}
}
static void sec_bat_wireless_minduty_cntl(struct sec_battery_info *battery, unsigned int duty_val)
{
union power_supply_propval value = {0, };
if (duty_val != battery->tx_minduty) {
value.intval = duty_val;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_EXT_PROP_WIRELESS_MIN_DUTY, value);
pr_info("@Tx_Mode %s : Min duty chagned (%d -> %d)\n", __func__, battery->tx_minduty, duty_val);
battery->tx_minduty = duty_val;
}
}
static void sec_bat_wireless_uno_cntl(struct sec_battery_info *battery, bool en)
{
union power_supply_propval value = {0, };
battery->uno_en = value.intval = en;
pr_info("@Tx_Mode %s : Uno control %d\n", __func__, battery->uno_en);
if (value.intval) {
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_EXT_PROP_WIRELESS_TX_ENABLE, value);
} else {
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_EXT_PROP_WIRELESS_RX_CONNECTED, value);
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CHARGE_UNO_CONTROL, value);
}
}
static void sec_bat_wireless_iout_cntl(struct sec_battery_info *battery, int uno_iout, int mfc_iout) {
union power_supply_propval value = {0, };
if (battery->tx_uno_iout != uno_iout) {
pr_info("@Tx_Mode %s : set uno iout(%d) -> (%d)\n", __func__, battery->tx_uno_iout, uno_iout);
value.intval = battery->tx_uno_iout = uno_iout;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_EXT_PROP_WIRELESS_TX_IOUT, value);
} else {
pr_info("@Tx_Mode %s : Already set Uno Iout(%d == %d)\n", __func__, battery->tx_uno_iout, uno_iout);
}
#if !defined(CONFIG_SEC_FACTORY)
if (battery->lcd_status && (mfc_iout == battery->pdata->tx_mfc_iout_phone)) {
pr_info("@Tx_Mode %s Reduce Tx MFC Iout. LCD ON\n", __func__);
mfc_iout = battery->pdata->tx_mfc_iout_lcd_on;
}
#endif
if (battery->tx_mfc_iout != mfc_iout) {
pr_info("@Tx_Mode %s : set mfc iout(%d) -> (%d)\n", __func__, battery->tx_mfc_iout, mfc_iout);
value.intval = battery->tx_mfc_iout = mfc_iout;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_EXT_PROP_WIRELESS_TX_IOUT, value);
} else {
pr_info("@Tx_Mode %s : Already set MFC Iout(%d == %d)\n", __func__, battery->tx_mfc_iout, mfc_iout);
}
}
static void sec_bat_wireless_vout_cntl(struct sec_battery_info *battery, int vout_now)
{
union power_supply_propval value = {0, };
int vout_mv, vout_now_mv;
vout_mv = battery->wc_tx_vout == 0 ? 5000 : (5000 + (battery->wc_tx_vout * 500));
vout_now_mv = vout_now == 0 ? 5000 : (5000 + (vout_now * 500));
pr_info("@Tx_Mode %s : set uno & mfc vout (%dmV -> %dmV)\n", __func__, vout_mv, vout_now_mv);
if (battery->wc_tx_vout >= vout_now) {
battery->wc_tx_vout = value.intval = vout_now;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_EXT_PROP_WIRELESS_TX_VOUT, value);
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_EXT_PROP_WIRELESS_TX_VOUT, value);
} else if (vout_now > battery->wc_tx_vout) {
battery->wc_tx_vout = value.intval = vout_now;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_EXT_PROP_WIRELESS_TX_VOUT, value);
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_EXT_PROP_WIRELESS_TX_VOUT, value);
}
}
#if defined(CONFIG_WIRELESS_TX_MODE)
#if !defined(CONFIG_SEC_FACTORY)
static void sec_bat_check_tx_battery_drain(struct sec_battery_info *battery)
{
if(battery->capacity <= battery->pdata->tx_stop_capacity &&
is_nocharge_type(battery->cable_type)) {
pr_info("@Tx_Mode battery level is drained, TX mode should turn off \n", __func__);
/* set tx event */
sec_bat_set_tx_event(battery, BATT_TX_EVENT_WIRELESS_TX_SOC_DRAIN, BATT_TX_EVENT_WIRELESS_TX_SOC_DRAIN);
sec_wireless_set_tx_enable(battery, false);
}
}
static void sec_bat_check_tx_current(struct sec_battery_info *battery)
{
if (battery->lcd_status && (battery->tx_mfc_iout > battery->pdata->tx_mfc_iout_lcd_on)) {
sec_bat_wireless_iout_cntl(battery, battery->pdata->tx_uno_iout, battery->pdata->tx_mfc_iout_lcd_on);
pr_info("@Tx_Mode %s Reduce Tx MFC Iout. LCD ON\n", __func__);
} else if (!battery->lcd_status && (battery->tx_mfc_iout == battery->pdata->tx_mfc_iout_lcd_on)) {
union power_supply_propval value = {0, };
sec_bat_wireless_iout_cntl(battery, battery->pdata->tx_uno_iout, battery->pdata->tx_mfc_iout_phone);
pr_info("@Tx_Mode %s Recovery Tx MFC Iout. LCD OFF\n", __func__);
value.intval = true;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_EXT_PROP_WIRELESS_SEND_FSK, value);
}
}
static 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);
}
}
}
#endif
static void sec_bat_check_tx_switch_mode(struct sec_battery_info *battery) {
union power_supply_propval value = {0, };
if (battery->current_event & SEC_BAT_CURRENT_EVENT_AFC) {
pr_info("@Tx_mode %s Do not switch switch mode! AFC Event set\n", __func__);
return;
}
value.intval = SEC_FUELGAUGE_CAPACITY_TYPE_CAPACITY_POINT;
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_CAPACITY, value);
if ((battery->tx_switch_mode == TX_SWITCH_UNO_ONLY) && (!battery->buck_cntl_by_tx)) {
battery->buck_cntl_by_tx = true;
sec_bat_set_charge(battery, battery->charger_mode);
sec_bat_wireless_iout_cntl(battery, battery->pdata->tx_uno_iout, battery->pdata->tx_mfc_iout_phone);
sec_bat_wireless_vout_cntl(battery, WC_TX_VOUT_7_5V);
sec_bat_wireless_minduty_cntl(battery, battery->pdata->tx_minduty_default);
} else if ((battery->tx_switch_mode == TX_SWITCH_CHG_ONLY) && (battery->buck_cntl_by_tx)) {
sec_bat_wireless_iout_cntl(battery, battery->pdata->tx_uno_iout, battery->pdata->tx_mfc_iout_phone_5v);
sec_bat_wireless_vout_cntl(battery, WC_TX_VOUT_5_0V);
sec_bat_wireless_minduty_cntl(battery, battery->pdata->tx_minduty_5V);
battery->buck_cntl_by_tx = false;
sec_bat_set_charge(battery, battery->charger_mode);
}
if (battery->status == POWER_SUPPLY_STATUS_FULL) {
if (battery->charging_mode == SEC_BATTERY_CHARGING_NONE) {
if (battery->tx_switch_mode == TX_SWITCH_CHG_ONLY)
battery->tx_switch_mode_change = true;
} else {
if (battery->tx_switch_mode == TX_SWITCH_UNO_ONLY) {
if (battery->tx_switch_start_soc >= 100) {
if ((battery->capacity < 99) || ((battery->capacity == 99) && (value.intval <= 1)))
battery->tx_switch_mode_change = true;
} else {
if (((battery->capacity == battery->tx_switch_start_soc) && (value.intval <= 1)) ||
(battery->capacity < battery->tx_switch_start_soc))
battery->tx_switch_mode_change = true;
}
} else if (battery->tx_switch_mode == TX_SWITCH_CHG_ONLY) {
if (battery->capacity >= 100)
battery->tx_switch_mode_change = true;
}
}
} else {
if (battery->tx_switch_mode == TX_SWITCH_UNO_ONLY) {
if (((battery->capacity == battery->tx_switch_start_soc) && (value.intval <= 1)) ||
(battery->capacity < battery->tx_switch_start_soc))
battery->tx_switch_mode_change = true;
} else if (battery->tx_switch_mode == TX_SWITCH_CHG_ONLY) {
if (((battery->capacity == (battery->tx_switch_start_soc + 1)) && (value.intval >= 8)) ||
(battery->capacity > (battery->tx_switch_start_soc + 1)))
battery->tx_switch_mode_change = true;
}
}
pr_info("@Tx_mode Tx mode(%d) tx_switch_mode_chage(%d) start soc(%d) now soc(%d.%d)\n",
battery->tx_switch_mode, battery->tx_switch_mode_change,
battery->tx_switch_start_soc, battery->capacity, value.intval);
}
#endif
#if defined(CONFIG_CALC_TIME_TO_FULL)
static void sec_bat_calc_time_to_full(struct sec_battery_info * battery)
{
if (delayed_work_pending(&battery->timetofull_work)) {
pr_info("%s: keep time_to_full(%5d sec)\n", __func__, battery->timetofull);
} else if ((battery->status == POWER_SUPPLY_STATUS_CHARGING ||
(battery->status == POWER_SUPPLY_STATUS_FULL && battery->capacity != 100)) && !battery->wc_tx_enable) {
union power_supply_propval value = {0, };
int charge = 0;
if (is_hv_wire_12v_type(battery->cable_type)) {
charge = battery->pdata->ttf_hv_12v_charge_current;
} else if (is_hv_wireless_type(battery->cable_type) ||
battery->cable_type == SEC_BATTERY_CABLE_PREPARE_WIRELESS_HV ||
battery->cable_type == SEC_BATTERY_CABLE_PREPARE_WIRELESS_20) {
if (sec_bat_hv_wc_normal_mode_check(battery))
charge = battery->pdata->ttf_wireless_charge_current;
else if((battery->cable_type == SEC_BATTERY_CABLE_PREPARE_WIRELESS_20 && !lpcharge) ||
battery->cable_type == SEC_BATTERY_CABLE_HV_WIRELESS_20)
charge = battery->ttf_predict_wc20_charge_current;
else
charge = battery->pdata->ttf_hv_wireless_charge_current;
} else if (is_hv_wire_type(battery->cable_type)) {
charge = battery->pdata->ttf_hv_charge_current;
} else if (is_nv_wireless_type(battery->cable_type)) {
charge = battery->pdata->ttf_wireless_charge_current;
} else if (is_pd_wire_type(battery->cable_type) && battery->hv_pdo) {
if (battery->pd_max_charge_power > HV_CHARGER_STATUS_STANDARD4)
charge = battery->pdata->ttf_dc45_charge_current;
else if (battery->pd_max_charge_power > HV_CHARGER_STATUS_STANDARD3)
charge = battery->pdata->ttf_dc25_charge_current;
else if (battery->pd_max_charge_power <= battery->pdata->pd_charging_charge_power &&
battery->pdata->charging_current[battery->cable_type].fast_charging_current >= \
battery->pdata->max_charging_current)
charge = battery->pdata->ttf_hv_charge_current; /* same PD power with AFC */
else
charge = (battery->pd_max_charge_power / 5) > battery->pdata->charging_current[battery->cable_type].fast_charging_current ?
battery->pdata->charging_current[battery->cable_type].fast_charging_current : (battery->pd_max_charge_power / 5);
} else {
charge = (battery->max_charge_power / 5) > battery->pdata->charging_current[battery->cable_type].fast_charging_current ?
battery->pdata->charging_current[battery->cable_type].fast_charging_current : (battery->max_charge_power / 5);
/* if rp3 ttf current > ttf_hv_current, set ttf_hv_current */
if ((battery->pdic_info.sink_status.rp_currentlvl == RP_CURRENT_LEVEL3) &&
(charge > battery->pdata->ttf_hv_charge_current))
charge = battery->pdata->ttf_hv_charge_current;
}
value.intval = charge;
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, value);
dev_info(battery->dev, "%s: T: %5d sec, passed time: %5ld, current: %d\n",
__func__, value.intval, battery->charging_passed_time, charge);
battery->timetofull = value.intval;
} else {
battery->timetofull = -1;
}
}
static void sec_bat_time_to_full_work(struct work_struct *work)
{
struct sec_battery_info *battery = container_of(work,
struct sec_battery_info, timetofull_work.work);
union power_supply_propval value = {0, };
psy_do_property(battery->pdata->charger_name, get,
POWER_SUPPLY_PROP_CURRENT_MAX, value);
battery->current_max = value.intval;
value.intval = SEC_BATTERY_CURRENT_MA;
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_CURRENT_NOW, value);
battery->current_now = value.intval;
value.intval = SEC_BATTERY_CURRENT_MA;
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_CURRENT_AVG, value);
battery->current_avg = value.intval;
sec_bat_calc_time_to_full(battery);
dev_info(battery->dev, "%s: \n",__func__);
if (battery->voltage_now > 0)
battery->voltage_now--;
power_supply_changed(battery->psy_bat);
}
#endif
#if defined(CONFIG_WIRELESS_TX_MODE)
static void sec_bat_txpower_calc(struct sec_battery_info * battery)
{
if (delayed_work_pending(&battery->wpc_txpower_calc_work)) {
pr_info("%s: keep average tx power(%5d mA)\n", __func__, battery->tx_avg_curr);
} else if (battery->wc_tx_enable) {
int tx_vout=0, tx_iout=0, vbatt=0;
union power_supply_propval value = {0, };
if(battery->tx_clear) {
battery->tx_time_cnt = 0;
battery->tx_avg_curr = 0;
battery->tx_total_power = 0;
battery->tx_clear = false;
}
if(battery->tx_clear_cisd) {
battery->tx_total_power_cisd = 0;
battery->tx_clear_cisd = false;
}
psy_do_property(battery->pdata->wireless_charger_name, get,
POWER_SUPPLY_EXT_PROP_WIRELESS_TX_UNO_VIN, value);
tx_vout = value.intval;
psy_do_property(battery->pdata->wireless_charger_name, get,
POWER_SUPPLY_EXT_PROP_WIRELESS_TX_UNO_IIN, value);
tx_iout = value.intval;
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_VOLTAGE_NOW, value);
vbatt = value.intval;
battery->tx_time_cnt++;
/* AVG curr will be calculated only when the battery is discharged */
if (battery->current_avg <= 0) {
tx_iout = (tx_vout / vbatt) * tx_iout;
} else {
tx_iout = 0;
}
/* monitor work will be scheduled every 10s when wc_tx_enable is true */
battery->tx_avg_curr = ((battery->tx_avg_curr * battery->tx_time_cnt) + tx_iout) / (battery->tx_time_cnt + 1);
battery->tx_total_power = (battery->tx_avg_curr * battery->tx_time_cnt) / (60*60/10);
/* tx_total_power_cisd : daily accumulated power consumption by Tx, will be cleared when cisd data is sent */
battery->tx_total_power_cisd = battery->tx_total_power_cisd + battery->tx_total_power;
dev_info(battery->dev,
"%s:tx_time_cnt(%ds), UNO_Vin(%dV), UNU_Iin(%dmA), tx_avg_curr(%dmA), tx_total_power(%dmAh), tx_total_power_cisd(%dmAh))\n", __func__,
battery->tx_time_cnt*10, tx_vout, tx_iout, battery->tx_avg_curr, battery->tx_total_power, battery->tx_total_power_cisd);
}
}
static void sec_bat_txpower_calc_work(struct work_struct *work)
{
struct sec_battery_info *battery = container_of(work,
struct sec_battery_info, wpc_txpower_calc_work.work);
sec_bat_txpower_calc(battery);
}
#endif
#if defined(CONFIG_ENABLE_100MA_CHARGING_BEFORE_USB_CONFIGURED)
extern bool get_usb_enumeration_state(void);
/* To disaply slow charging when usb charging 100MA*/
static void sec_bat_check_slowcharging_work(struct work_struct *work)
{
struct sec_battery_info *battery = container_of(work,
struct sec_battery_info, slowcharging_work.work);
if (battery->pdic_info.sink_status.rp_currentlvl == RP_CURRENT_LEVEL_DEFAULT &&
battery->cable_type == SEC_BATTERY_CABLE_USB) {
if (!get_usb_enumeration_state() &&
(battery->current_event & SEC_BAT_CURRENT_EVENT_USB_100MA)) {
sec_bat_set_misc_event(battery, BATT_MISC_EVENT_TIMEOUT_OPEN_TYPE, BATT_MISC_EVENT_TIMEOUT_OPEN_TYPE);
battery->max_charge_power = battery->input_voltage * battery->current_max;
}
}
dev_info(battery->dev, "%s: \n",__func__);
}
#endif
static void sec_bat_wc_cv_mode_check(struct sec_battery_info *battery)
{
union power_supply_propval value = {0, };
int is_otg_on = 0;
psy_do_property(battery->pdata->charger_name, get,
POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL, value);
is_otg_on = value.intval;
pr_info("%s: battery->wc_cv_mode = %d, otg(%d) \n", __func__, battery->wc_cv_mode, is_otg_on);
if (battery->capacity >= battery->pdata->wireless_cc_cv && !is_otg_on) {
pr_info("%s: 4.5W WC Changed Vout input current limit\n", __func__);
battery->wc_cv_mode = true;
sec_bat_set_charging_current(battery);
value.intval = WIRELESS_VOUT_CC_CV_VOUT; // 5.5V
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, value);
value.intval = WIRELESS_VRECT_ADJ_ROOM_5; // 80mv
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, value);
if ((battery->cable_type == SEC_BATTERY_CABLE_WIRELESS ||
battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_STAND ||
battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_TX)) {
value.intval = WIRELESS_CLAMP_ENABLE;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, value);
}
/* Change FOD values for CV mode */
value.intval = POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_PROP_STATUS, value);
}
}
static void sec_bat_siop_level_work(struct work_struct *work)
{
struct sec_battery_info *battery = container_of(work,
struct sec_battery_info, siop_level_work.work);
pr_info("%s : set current by siop level(%d)\n",__func__, battery->siop_level);
sec_bat_set_charging_current(battery);
wake_unlock(&battery->siop_level_wake_lock);
}
static void sec_bat_wc_headroom_work(struct work_struct *work)
{
struct sec_battery_info *battery = container_of(work,
struct sec_battery_info, wc_headroom_work.work);
union power_supply_propval value = {0, };
/* The default headroom is high, because initial wireless charging state is unstable.
After 10sec wireless charging, however, recover headroom level to avoid chipset damage */
if (battery->wc_status != SEC_WIRELESS_PAD_NONE) {
/* When the capacity is higher than 99, and the device is in 5V wireless charging state,
then Vrect headroom has to be headroom_2.
Refer to the sec_bat_siop_work function. */
if (battery->capacity < 99 && battery->status != POWER_SUPPLY_STATUS_FULL) {
if (is_nv_wireless_type(battery->cable_type)) {
if (battery->capacity < battery->pdata->wireless_cc_cv)
value.intval = WIRELESS_VRECT_ADJ_ROOM_4; /* WPC 4.5W, Vrect Room 30mV */
else
value.intval = WIRELESS_VRECT_ADJ_ROOM_5; /* WPC 4.5W, Vrect Room 80mV */
} else if (is_hv_wireless_type(battery->cable_type)) {
value.intval = WIRELESS_VRECT_ADJ_ROOM_5;
} else {
value.intval = WIRELESS_VRECT_ADJ_OFF;
}
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, value);
pr_info("%s: Changed Vrect adjustment from Rx activation(10seconds)", __func__);
}
if (is_nv_wireless_type(battery->cable_type))
sec_bat_wc_cv_mode_check(battery);
}
wake_unlock(&battery->wc_headroom_wake_lock);
}
static void sec_bat_ext_event_work(struct work_struct *work)
{
struct sec_battery_info *battery = container_of(work,
struct sec_battery_info, ext_event_work.work);
union power_supply_propval value = {0, };
if (battery->wc_tx_enable) { /* TX ON state */
if (battery->ext_event & BATT_EXT_EVENT_CAMERA) {
pr_info("@Tx_Mode %s: Camera ON, TX OFF\n", __func__);
sec_bat_set_tx_event(battery, BATT_TX_EVENT_WIRELESS_TX_CAMERA_ON, BATT_TX_EVENT_WIRELESS_TX_CAMERA_ON);
sec_wireless_set_tx_enable(battery, false);
} else if (battery->ext_event & BATT_EXT_EVENT_DEX) {
pr_info("@Tx_Mode %s: Dex ON, TX OFF\n", __func__);
sec_bat_set_tx_event(battery, BATT_TX_EVENT_WIRELESS_TX_OTG_ON, BATT_TX_EVENT_WIRELESS_TX_OTG_ON);
sec_wireless_set_tx_enable(battery, false);
} else if (battery->ext_event & BATT_EXT_EVENT_CALL) {
pr_info("@Tx_Mode %s: Call ON, TX OFF\n", __func__);
battery->tx_retry_case |= SEC_BAT_TX_RETRY_CALL;
sec_wireless_set_tx_enable(battery, false);
}
} else { /* TX OFF state, it has only call scenario */
if (battery->ext_event & BATT_EXT_EVENT_CALL) {
pr_info("@Tx_Mode %s: Call ON\n", __func__);
value.intval = BATT_EXT_EVENT_CALL;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_EXT_PROP_CALL_EVENT, value);
if (battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_PACK ||
battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_HV_PACK ||
battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_TX) {
pr_info("%s : Call is on during Wireless Pack or TX\n",__func__);
battery->wc_rx_phm_mode = true;
}
if (battery->tx_retry_case != SEC_BAT_TX_RETRY_NONE) {
pr_info("@Tx_Mode %s: TX OFF because of other reason(retry:0x%x), save call retry case\n",
__func__, battery->tx_retry_case);
battery->tx_retry_case |= SEC_BAT_TX_RETRY_CALL;
}
} else if (!(battery->ext_event & BATT_EXT_EVENT_CALL)) {
pr_info("@Tx_Mode %s: Call OFF\n", __func__);
value.intval = BATT_EXT_EVENT_NONE;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_EXT_PROP_CALL_EVENT, value);
/* check the diff between current and previous ext_event state */
if (battery->tx_retry_case & SEC_BAT_TX_RETRY_CALL) {
battery->tx_retry_case &= ~SEC_BAT_TX_RETRY_CALL;
if (!battery->tx_retry_case) {
pr_info("@Tx_Mode %s: Call OFF, TX Retry\n", __func__);
sec_bat_set_tx_event(battery, BATT_TX_EVENT_WIRELESS_TX_RETRY, BATT_TX_EVENT_WIRELESS_TX_RETRY);
}
} else if (battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_PACK ||
battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_HV_PACK ||
battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_TX) {
pr_info("%s : Call is off during Wireless Pack or TX\n",__func__);
}
/* process escape phm */
if(battery->wc_rx_phm_mode) {
pr_info("%s: ESCAPE PHM STEP 1 - WC CONTROL: Enable", __func__);
gpio_direction_output(battery->pdata->wpc_en, 0);
msleep(100);
pr_info("%s: ESCAPE PHM STEP 2 - WC CONTROL: Disable", __func__);
gpio_direction_output(battery->pdata->wpc_en, 1);
msleep(510);
pr_info("%s: ESCAPE PHM STEP 3 - WC CONTROL: Enable", __func__);
gpio_direction_output(battery->pdata->wpc_en, 0);
}
battery->wc_rx_phm_mode = false;
}
}
wake_unlock(&battery->ext_event_wake_lock);
}
#if defined(CONFIG_WIRELESS_FIRMWARE_UPDATE)
bool sec_bat_check_boost_mfc_condition(struct sec_battery_info *battery, int mode)
{
union power_supply_propval value = {0, };
int boost_status = 0, wpc_det = 0, mst_pwr_en = 0;
dev_info(battery->dev, "%s \n", __func__);
if (mode != SEC_WIRELESS_RX_SPU_MODE) {
psy_do_property(battery->pdata->wireless_charger_name, get,
POWER_SUPPLY_EXT_PROP_WIRELESS_INITIAL_WC_CHECK, value);
wpc_det = value.intval;
}
if (gpio_is_valid(battery->pdata->mst_pwr_en))
mst_pwr_en = gpio_get_value(battery->pdata->mst_pwr_en);
else
pr_info("%s: invalid gpio(mst_pwr_en)\n", __func__);
psy_do_property(battery->pdata->charger_name, get,
POWER_SUPPLY_EXT_PROP_CHARGE_BOOST, value);
boost_status = value.intval;
pr_info("%s wpc_det(%d), mst_pwr_en(%d), boost_status(%d)\n",
__func__, wpc_det, mst_pwr_en, boost_status);
if (!boost_status && !wpc_det && !mst_pwr_en)
return true;
return false;
}
void sec_bat_fw_update_work(struct sec_battery_info *battery, int mode)
{
union power_supply_propval value = {0, };
int ret = 0;
dev_info(battery->dev, "%s \n", __func__);
wake_lock_timeout(&battery->vbus_wake_lock, HZ * 10);
switch (mode) {
case SEC_WIRELESS_RX_SDCARD_MODE:
case SEC_WIRELESS_RX_BUILT_IN_MODE:
case SEC_WIRELESS_RX_SPU_MODE:
mfc_fw_update = true;
value.intval = mode;
ret = psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_PROP_CHARGE_POWERED_OTG_CONTROL, value);
if (ret < 0)
mfc_fw_update = false;
break;
case SEC_WIRELESS_TX_ON_MODE:
value.intval = true;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CHARGE_UNO_CONTROL, value);
value.intval = mode;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_PROP_CHARGE_POWERED_OTG_CONTROL, value);
break;
case SEC_WIRELESS_TX_OFF_MODE:
value.intval = false;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CHARGE_UNO_CONTROL, value);
break;
default:
break;
}
}
static void sec_bat_fw_init_work(struct work_struct *work)
{
struct sec_battery_info *battery = container_of(work,
struct sec_battery_info, fw_init_work.work);
union power_supply_propval value = {0, };
int ret = 0;
#if defined(CONFIG_WIRELESS_IC_PARAM)
psy_do_property(battery->pdata->wireless_charger_name, get,
POWER_SUPPLY_EXT_PROP_WIRELESS_CHECK_FW_VER, value);
if (value.intval) {
pr_info("%s: wireless firmware is already updated.\n", __func__);
return;
}
#endif
if (sec_bat_check_boost_mfc_condition(battery, SEC_WIRELESS_RX_INIT) &&
battery->capacity > 30 && !lpcharge) {
mfc_fw_update = true;
value.intval = SEC_WIRELESS_RX_INIT;
ret = psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_PROP_CHARGE_POWERED_OTG_CONTROL, value);
if (ret < 0)
mfc_fw_update = false;
}
}
#endif
#if defined(CONFIG_UPDATE_BATTERY_DATA)
static void sec_bat_update_data_work(struct work_struct *work)
{
struct sec_battery_info *battery = container_of(work,
struct sec_battery_info, batt_data_work.work);
sec_battery_update_data(battery->data_path);
wake_unlock(&battery->batt_data_wake_lock);
}
#endif
static void sec_bat_misc_event_work(struct work_struct *work)
{
struct sec_battery_info *battery = container_of(work,
struct sec_battery_info, misc_event_work.work);
int xor_misc_event = battery->prev_misc_event ^ battery->misc_event;
if ((xor_misc_event & (BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE |
BATT_MISC_EVENT_HICCUP_TYPE | BATT_MISC_EVENT_TEMP_HICCUP_TYPE)) &&
is_nocharge_type(battery->cable_type)) {
if (battery->misc_event & (BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE |
BATT_MISC_EVENT_HICCUP_TYPE | BATT_MISC_EVENT_TEMP_HICCUP_TYPE)) {
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_BUCK_OFF);
} else if (battery->prev_misc_event & (BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE |
BATT_MISC_EVENT_HICCUP_TYPE | BATT_MISC_EVENT_TEMP_HICCUP_TYPE)) {
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING_OFF);
}
}
pr_info("%s: change misc event(0x%x --> 0x%x)\n",
__func__, battery->prev_misc_event, battery->misc_event);
battery->prev_misc_event = battery->misc_event;
wake_unlock(&battery->misc_event_wake_lock);
wake_lock(&battery->monitor_wake_lock);
queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
}
static void sec_bat_calculate_safety_time(struct sec_battery_info *battery)
{
unsigned long long expired_time = battery->expired_time;
struct timespec ts = {0, };
int curr = 0;
int input_power = battery->current_max * battery->input_voltage * 1000;
int charging_power = battery->charging_current * (battery->pdata->chg_float_voltage / battery->pdata->chg_float_voltage_conv);
static int discharging_cnt = 0;
if (battery->current_avg < 0) {
discharging_cnt++;
} else {
discharging_cnt = 0;
}
if (discharging_cnt >= 5) {
battery->expired_time = battery->pdata->expired_time;
battery->prev_safety_time = 0;
pr_info("%s : SAFETY TIME RESET! DISCHARGING CNT(%d)\n",
__func__, discharging_cnt);
discharging_cnt = 0;
return;
} else if ((battery->lcd_status || battery->wc_tx_enable) && battery->stop_timer) {
battery->prev_safety_time = 0;
return;
}
get_monotonic_boottime(&ts);
if (battery->prev_safety_time == 0) {
battery->prev_safety_time = ts.tv_sec;
}
if (input_power > charging_power) {
curr = battery->charging_current;
} else {
curr = input_power / (battery->pdata->chg_float_voltage / battery->pdata->chg_float_voltage_conv);
curr = (curr * 9) / 10;
}
if ((battery->lcd_status || battery->wc_tx_enable) && !battery->stop_timer) {
battery->stop_timer = true;
} else if (!(battery->lcd_status || battery->wc_tx_enable) && battery->stop_timer) {
battery->stop_timer = false;
}
pr_info("%s : EXPIRED_TIME(%llu), IP(%d), CP(%d), CURR(%d), STANDARD(%d)\n",
__func__, expired_time, input_power, charging_power, curr, battery->pdata->standard_curr);
if (curr == 0)
return;
else if (curr > battery->pdata->standard_curr)
curr = battery->pdata->standard_curr;
expired_time = (expired_time * battery->pdata->standard_curr) / curr;
pr_info("%s : CAL_EXPIRED_TIME(%llu) TIME NOW(%ld) TIME PREV(%ld)\n", __func__, expired_time, ts.tv_sec, battery->prev_safety_time);
if (expired_time <= ((ts.tv_sec - battery->prev_safety_time) * 1000))
expired_time = 0;
else
expired_time -= ((ts.tv_sec - battery->prev_safety_time) * 1000);
battery->cal_safety_time = expired_time;
expired_time = (expired_time * curr) / battery->pdata->standard_curr;
battery->expired_time = expired_time;
battery->prev_safety_time = ts.tv_sec;
pr_info("%s : REMAIN_TIME(%ld) CAL_REMAIN_TIME(%ld)\n", __func__, battery->expired_time, battery->cal_safety_time);
}
static void sec_bat_monitor_work(
struct work_struct *work)
{
struct sec_battery_info *battery =
container_of(work, struct sec_battery_info,
monitor_work.work);
static struct timespec old_ts = {0, };
struct timespec c_ts = {0, };
union power_supply_propval val = {0, };
union power_supply_propval value = {0, };
dev_dbg(battery->dev, "%s: Start\n", __func__);
c_ts = ktime_to_timespec(ktime_get_boottime());
mutex_lock(&battery->wclock);
if (!battery->wc_enable) {
pr_info("%s: wc_enable(%d), cnt(%d)\n",
__func__, battery->wc_enable, battery->wc_enable_cnt);
if (battery->wc_enable_cnt > battery->wc_enable_cnt_value) {
battery->wc_enable = true;
battery->wc_enable_cnt = 0;
if (battery->pdata->wpc_en) {
gpio_direction_output(battery->pdata->wpc_en, 0);
pr_info("%s: WC CONTROL: Enable", __func__);
}
pr_info("%s: wpc_en(%d)\n",
__func__, gpio_get_value(battery->pdata->wpc_en));
}
battery->wc_enable_cnt++;
}
mutex_unlock(&battery->wclock);
/* monitor once after wakeup */
if (battery->polling_in_sleep) {
battery->polling_in_sleep = false;
if ((battery->status == POWER_SUPPLY_STATUS_DISCHARGING) &&
((battery->ps_enable != true) && !battery->wc_tx_enable)) {
if ((unsigned long)(c_ts.tv_sec - old_ts.tv_sec) < 10 * 60) {
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_VOLTAGE_NOW, value);
battery->voltage_now = value.intval;
value.intval = 0;
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_CAPACITY, value);
battery->capacity = value.intval;
sec_bat_get_temperature_info(battery);
#if defined(CONFIG_BATTERY_CISD)
sec_bat_cisd_check(battery);
#endif
power_supply_changed(battery->psy_bat);
pr_info("Skip monitor work(%ld, Vnow:%d(mV), SoC:%d(%%), Tbat:%d(0.1'C))\n",
c_ts.tv_sec - old_ts.tv_sec, battery->voltage_now, battery->capacity, battery->temperature);
goto skip_monitor;
}
}
}
/* update last monitor time */
old_ts = c_ts;
sec_bat_get_battery_info(battery);
#if defined(CONFIG_BATTERY_CISD)
sec_bat_cisd_check(battery);
#endif
#if defined(CONFIG_STEP_CHARGING)
sec_bat_check_step_charging(battery);
#endif
#if defined(CONFIG_CALC_TIME_TO_FULL)
/* time to full check */
sec_bat_calc_time_to_full(battery);
#endif
#if defined(CONFIG_WIRELESS_TX_MODE)
/* tx mode check */
if (battery->wc_tx_enable) {
pr_info("@Tx_Mode %s: tx_retry(0x%x), tx_switch(0x%x)",
__func__, battery->tx_retry_case, battery->tx_switch_mode);
#if !defined(CONFIG_SEC_FACTORY)
sec_bat_check_tx_battery_drain(battery);
sec_bat_check_tx_temperature(battery);
if ((battery->wc_rx_type == SS_PHONE) || (battery->wc_rx_type == OTHER_DEV))
sec_bat_check_tx_current(battery);
#endif
sec_bat_txpower_calc(battery);
sec_bat_handle_tx_misalign(battery, false);
if (battery->tx_switch_mode != TX_SWITCH_MODE_OFF && battery->tx_switch_start_soc != 0)
sec_bat_check_tx_switch_mode(battery);
} else if (battery->tx_retry_case != SEC_BAT_TX_RETRY_NONE) {
pr_info("@Tx_Mode %s: tx_retry(0x%x)",__func__, battery->tx_retry_case);
#if !defined(CONFIG_SEC_FACTORY)
sec_bat_check_tx_temperature(battery);
#endif
sec_bat_handle_tx_misalign(battery, false);
}
#endif
/* 0. test mode */
if (battery->test_mode) {
dev_err(battery->dev, "%s: Test Mode\n", __func__);
sec_bat_do_test_function(battery);
if (battery->test_mode != 0)
goto continue_monitor;
}
/* 1. battery check */
if (!sec_bat_battery_cable_check(battery))
goto continue_monitor;
/* 2. voltage check */
if (!sec_bat_voltage_check(battery))
goto continue_monitor;
/* monitor short routine in initial monitor */
if (battery->pdata->monitor_initial_count || sec_bat_is_short_polling(battery))
goto skip_current_monitor;
/* 3. time management */
if (!sec_bat_time_management(battery))
goto continue_monitor;
/* 4. temperature check */
if (!sec_bat_temperature_check(battery))
goto continue_monitor;
#if defined(CONFIG_BATTERY_SWELLING)
/* 5. swelling check */
sec_bat_swelling_check(battery);
/* 6. full charging check */
if ((battery->swelling_mode == SWELLING_MODE_CHARGING || battery->swelling_mode == SWELLING_MODE_FULL) &&
(!battery->charging_block))
sec_bat_swelling_fullcharged_check(battery);
else
sec_bat_fullcharged_check(battery);
#else
/* 5. full charging check */
sec_bat_fullcharged_check(battery);
#endif /* CONFIG_BATTERY_SWELLING */
/* 7. additional check */
if (battery->pdata->monitor_additional_check)
battery->pdata->monitor_additional_check();
if (is_nv_wireless_type(battery->cable_type) &&
(!battery->wc_cv_mode) &&
(battery->charging_passed_time > 10))
sec_bat_wc_cv_mode_check(battery);
continue_monitor:
/* clear HEATING_CONTROL*/
sec_bat_set_current_event(battery, 0, SEC_BAT_CURRENT_EVENT_SKIP_HEATING_CONTROL);
/* calculate safety time */
if (!battery->charging_block)
sec_bat_calculate_safety_time(battery);
/* set charging current */
sec_bat_set_charging_current(battery);
skip_current_monitor:
psy_do_property(battery->pdata->charger_name, get,
POWER_SUPPLY_EXT_PROP_MONITOR_WORK, val);
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_EXT_PROP_MONITOR_WORK, val);
dev_dbg(battery->dev,
"%s: HLT(%d) HLR(%d) HT(%d), HR(%d), LT(%d), LR(%d), lpcharge(%d)\n",
__func__, battery->temp_highlimit_threshold, battery->temp_highlimit_recovery,
battery->temp_high_threshold, battery->temp_high_recovery,
battery->temp_low_threshold, battery->temp_low_recovery, lpcharge);
pr_info("%s: Status(%s), mode(%s), Health(%s), Cable(%s, %s, %d, %d), rp(%d), level(%d%%), lcd(%d), slate_mode(%d), store_mode(%d)"
#if defined(CONFIG_AFC_CHARGER_MODE)
", HV(%s, %d), sleep_mode(%d)"
#endif
#if defined(CONFIG_BATTERY_AGE_FORECAST)
#if defined(CONFIG_BATTERY_AGE_FORECAST_DETACHABLE)
", Cycle(%dw)"
#else
", Cycle(%d)"
#endif
#endif
"\n", __func__,
sec_bat_status_str[battery->status],
sec_bat_charging_mode_str[battery->charging_mode],
sec_bat_health_str[battery->health],
sec_cable_type[battery->cable_type],
sec_cable_type[battery->wire_status],
battery->muic_cable_type,
battery->pd_usb_attached,
battery->pdic_info.sink_status.rp_currentlvl,
battery->siop_level,
battery->lcd_status,
is_slate_mode(battery),
battery->store_mode
#if defined(CONFIG_AFC_CHARGER_MODE)
, battery->hv_chg_name, battery->vbus_chg_by_siop, sleep_mode
#endif
#if defined(CONFIG_BATTERY_AGE_FORECAST)
, battery->batt_cycle
#endif
);
#if defined(CONFIG_WIRELESS_TX_MODE)
if (battery->wc_tx_enable) {
unsigned int vout;
vout = battery->wc_tx_vout == 0 ? 5000 : (5000 + (battery->wc_tx_vout * 500));
pr_info("@Tx_Mode %s: Rx(%s), WC_TX_VOUT(%dmV), UNO_IOUT(%d), MFC_IOUT(%d) AFC_DISABLE(%d)\n",
__func__, sec_bat_rx_type_str[battery->wc_rx_type],
vout, battery->tx_uno_iout, battery->tx_mfc_iout, battery->afc_disable);
}
#endif
#if defined(CONFIG_ENG_BATTERY_CONCEPT)
pr_info("%s: battery->stability_test(%d), battery->eng_not_full_status(%d)\n",
__func__, battery->stability_test, battery->eng_not_full_status);
#endif
#if defined(CONFIG_SEC_FACTORY)
if (!is_nocharge_type(battery->cable_type)) {
#else
if (!is_nocharge_type(battery->cable_type) && battery->store_mode) {
#endif
pr_info("%s: @battery->capacity = (%d), battery->status= (%d), battery->store_mode=(%d)\n",
__func__, battery->capacity, battery->status, battery->store_mode);
if (battery->capacity >= battery->pdata->store_mode_charging_max) {
int chg_mode = battery->misc_event &
(BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE | BATT_MISC_EVENT_HICCUP_TYPE | BATT_MISC_EVENT_TEMP_HICCUP_TYPE) ?
SEC_BAT_CHG_MODE_BUCK_OFF : SEC_BAT_CHG_MODE_CHARGING_OFF;
/* to discharge the battery, off buck */
if (battery->capacity > battery->pdata->store_mode_charging_max)
chg_mode = SEC_BAT_CHG_MODE_BUCK_OFF;
sec_bat_set_charging_status(battery,
POWER_SUPPLY_STATUS_DISCHARGING);
sec_bat_set_charge(battery, chg_mode);
}
if ((battery->capacity <= battery->pdata->store_mode_charging_min) && (battery->status == POWER_SUPPLY_STATUS_DISCHARGING)) {
sec_bat_set_charging_status(battery,
POWER_SUPPLY_STATUS_CHARGING);
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING);
}
}
power_supply_changed(battery->psy_bat);
skip_monitor:
sec_bat_set_polling(battery);
#if defined(CONFIG_WIRELESS_TX_MODE)
if (battery->tx_switch_mode_change) {
cancel_delayed_work(&battery->wpc_tx_work);
wake_lock(&battery->wpc_tx_wake_lock);
queue_delayed_work(battery->monitor_wqueue,
&battery->wpc_tx_work, 0);
}
#endif
if (battery->capacity <= 0 || battery->health_change)
wake_lock_timeout(&battery->monitor_wake_lock, HZ * 5);
else
wake_unlock(&battery->monitor_wake_lock);
dev_dbg(battery->dev, "%s: End\n", __func__);
return;
}
static enum alarmtimer_restart sec_bat_alarm(
struct alarm *alarm, ktime_t now)
{
struct sec_battery_info *battery = container_of(alarm,
struct sec_battery_info, polling_alarm);
dev_dbg(battery->dev,
"%s\n", __func__);
/* In wake up, monitor work will be queued in complete function
* To avoid duplicated queuing of monitor work,
* do NOT queue monitor work in wake up by polling alarm
*/
if (!battery->polling_in_sleep) {
wake_lock(&battery->monitor_wake_lock);
queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
dev_dbg(battery->dev, "%s: Activated\n", __func__);
}
return ALARMTIMER_NORESTART;
}
#if defined(CONFIG_CALC_TIME_TO_FULL)
static void sec_bat_predict_wireless20_time_to_full_current(struct sec_battery_info *battery, int rx_power)
{
battery->ttf_predict_wc20_charge_current = battery->pdata->wireless_power_info[rx_power].ttf_charge_current;
pr_info("%s: %dmA \n", __func__, battery->ttf_predict_wc20_charge_current);
}
#endif
static void sec_bat_set_wireless20_current(struct sec_battery_info *battery, int rx_power)
{
battery->wc20_vout = battery->pdata->wireless_power_info[rx_power].vout / 100;
pr_info("%s: vout= %d \n", __func__, battery->wc20_vout);
if (battery->wc_status == SEC_WIRELESS_PAD_WPC_HV_20) {
sec_bat_change_default_current(battery, SEC_BATTERY_CABLE_HV_WIRELESS_20,
battery->pdata->wireless_power_info[rx_power].input_current_limit,
battery->pdata->wireless_power_info[rx_power].fast_charging_current);
if (battery->pdata->wireless_power_info[rx_power].rx_power <= 4500)
battery->wc20_power_class = 0;
else if (battery->pdata->wireless_power_info[rx_power].rx_power <= 7500)
battery->wc20_power_class = SEC_WIRELESS_RX_POWER_CLASS_1;
else if (battery->pdata->wireless_power_info[rx_power].rx_power <= 12000)
battery->wc20_power_class = SEC_WIRELESS_RX_POWER_CLASS_2;
else if (battery->pdata->wireless_power_info[rx_power].rx_power <= 20000)
battery->wc20_power_class = SEC_WIRELESS_RX_POWER_CLASS_3;
else
battery->wc20_power_class = SEC_WIRELESS_RX_POWER_CLASS_4;
sec_bat_set_charging_current(battery);
}
}
u8 sec_bat_get_wireless20_power_class(struct sec_battery_info *battery)
{
u8 power_class = 0;
if (battery->cable_type == SEC_BATTERY_CABLE_PREPARE_WIRELESS_20) {
power_class = battery->wc20_power_class;
} else
power_class = SEC_WIRELESS_RX_POWER_CLASS_1;
pr_info("%s: power class = %d \n", __func__, power_class);
return power_class;
}
static void sec_bat_check_input_voltage(struct sec_battery_info *battery)
{
unsigned int voltage = 0;
int input_current = battery->pdata->charging_current[battery->cable_type].input_current_limit;
if (is_pd_wire_type(battery->cable_type)) {
battery->max_charge_power = battery->pd_max_charge_power;
return;
}
else if (is_hv_wire_12v_type(battery->cable_type))
voltage = SEC_INPUT_VOLTAGE_12V;
else if (is_hv_wire_9v_type(battery->cable_type))
voltage = SEC_INPUT_VOLTAGE_9V;
else if (battery->cable_type == SEC_BATTERY_CABLE_PREPARE_WIRELESS_20 ||
battery->cable_type == SEC_BATTERY_CABLE_HV_WIRELESS_20)
voltage = battery->wc20_vout;
else if (is_hv_wireless_type(battery->cable_type) ||
battery->cable_type == SEC_BATTERY_CABLE_PREPARE_WIRELESS_HV)
voltage = SEC_INPUT_VOLTAGE_10V;
else if (is_nv_wireless_type(battery->cable_type))
voltage = SEC_INPUT_VOLTAGE_5_5V;
else
voltage = SEC_INPUT_VOLTAGE_5V;
battery->input_voltage = voltage;
battery->charge_power = voltage * input_current;
#if !defined(CONFIG_SEC_FACTORY)
if (battery->charge_power > battery->max_charge_power)
#endif
battery->max_charge_power = battery->charge_power;
pr_info("%s: battery->input_voltage : %dV, %dmW, %dmW)\n", __func__,
battery->input_voltage, battery->charge_power, battery->max_charge_power);
}
static void sec_bat_wpc_tx_work(struct work_struct *work)
{
struct sec_battery_info *battery = container_of(work,
struct sec_battery_info, wpc_tx_work.work);
dev_info(battery->dev, "@Tx_Mode %s: Start\n", __func__);
switch (battery->wc_rx_type) {
case NO_DEV:
if (is_hv_wire_type(battery->wire_status)) {
pr_info("@Tx_Mode %s : charging voltage change(9V -> 5V).\n", __func__);
muic_afc_set_voltage(SEC_INPUT_VOLTAGE_5V/10);
break;
}
if (is_pd_wire_type(battery->wire_status) && battery->hv_pdo) {
pr_info("@Tx_Mode %s: PD charnge pdo (9V -> 5V). Because Tx Start.\n", __func__);
sec_bat_change_pdo(battery, SEC_INPUT_VOLTAGE_5V);
break;
}
if (battery->afc_disable) {
battery->afc_disable = false;
#if 0
muic_hv_charger_disable(battery->afc_disable);
#endif
}
if (!battery->buck_cntl_by_tx) {
battery->buck_cntl_by_tx = true;
sec_bat_set_charge(battery, battery->charger_mode);
}
if (!battery->uno_en) {
battery->buck_cntl_by_tx = true;
sec_bat_wireless_uno_cntl(battery, true);
}
sec_bat_wireless_vout_cntl(battery, WC_TX_VOUT_5_0V);
sec_bat_wireless_iout_cntl(battery, battery->pdata->tx_uno_iout, battery->pdata->tx_mfc_iout_gear);
break;
case SS_GEAR:
if (!battery->afc_disable) {
battery->afc_disable = true;
#if 0
muic_hv_charger_disable(battery->afc_disable);
#endif
}
if (is_hv_wire_type(battery->wire_status)) {
pr_info("@Tx_Mode %s : charging voltage change(9V -> 5V).\n", __func__);
muic_afc_set_voltage(SEC_INPUT_VOLTAGE_5V/10);
break;
} else if (is_pd_wire_type(battery->wire_status) && battery->hv_pdo) {
pr_info("@Tx_Mode %s: PD charnge pdo (9V -> 5V). Because Tx Start.\n", __func__);
sec_bat_change_pdo(battery, SEC_INPUT_VOLTAGE_5V);
break;
}
if (is_wired_type(battery->wire_status) && battery->buck_cntl_by_tx) {
battery->buck_cntl_by_tx = false;
sec_bat_set_charge(battery, battery->charger_mode);
} else if ((battery->wire_status == SEC_BATTERY_CABLE_NONE) && (!battery->buck_cntl_by_tx)) {
battery->buck_cntl_by_tx = true;
sec_bat_set_charge(battery, battery->charger_mode);
}
sec_bat_wireless_vout_cntl(battery, WC_TX_VOUT_5_0V);
sec_bat_wireless_iout_cntl(battery, battery->pdata->tx_uno_iout, battery->pdata->tx_mfc_iout_gear);
break;
case SS_PHONE:
case OTHER_DEV:
if (battery->wire_status == SEC_BATTERY_CABLE_HV_TA_CHG_LIMIT) {
pr_info("@Tx_Mode %s : charging voltage change(5V -> 9V)\n", __func__);
muic_afc_set_voltage(SEC_INPUT_VOLTAGE_9V/10);
break;
} else if (is_pd_wire_type(battery->wire_status) && !battery->hv_pdo) {
pr_info("@Tx_Mode %s: PD charnge pdo (5V -> 9V). Because Tx Start.\n", __func__);
sec_bat_change_pdo(battery, SEC_INPUT_VOLTAGE_9V);
break;
}
if (battery->wire_status == SEC_BATTERY_CABLE_NONE) {
battery->tx_switch_mode = TX_SWITCH_MODE_OFF;
battery->tx_switch_start_soc = 0;
battery->tx_switch_mode_change = false;
if (!battery->buck_cntl_by_tx) {
battery->buck_cntl_by_tx = true;
sec_bat_set_charge(battery, battery->charger_mode);
}
sec_bat_wireless_vout_cntl(battery, WC_TX_VOUT_7_5V);
sec_bat_wireless_iout_cntl(battery, battery->pdata->tx_uno_iout, battery->pdata->tx_mfc_iout_phone);
sec_bat_wireless_minduty_cntl(battery, battery->pdata->tx_minduty_default);
} else if (is_hv_wire_type(battery->wire_status) || (is_pd_wire_type(battery->wire_status) && battery->hv_pdo)) {
battery->tx_switch_mode = TX_SWITCH_MODE_OFF;
battery->tx_switch_start_soc = 0;
battery->tx_switch_mode_change = false;
if (battery->buck_cntl_by_tx) {
battery->buck_cntl_by_tx = false;
sec_bat_set_charge(battery, battery->charger_mode);
}
sec_bat_wireless_iout_cntl(battery, battery->pdata->tx_uno_iout, battery->pdata->tx_mfc_iout_phone);
sec_bat_wireless_minduty_cntl(battery, battery->pdata->tx_minduty_default);
} else if (is_pd_wire_type(battery->wire_status) && battery->hv_pdo) {
pr_info("@Tx_Mode %s: PD cable attached. HV PDO(%d)\n", __func__, battery->hv_pdo);
battery->tx_switch_mode = TX_SWITCH_MODE_OFF;
battery->tx_switch_start_soc = 0;
battery->tx_switch_mode_change = false;
if (battery->buck_cntl_by_tx) {
battery->buck_cntl_by_tx = false;
sec_bat_set_charge(battery, battery->charger_mode);
}
sec_bat_wireless_iout_cntl(battery, battery->pdata->tx_uno_iout, battery->pdata->tx_mfc_iout_phone);
sec_bat_wireless_minduty_cntl(battery, battery->pdata->tx_minduty_default);
} else if (is_wired_type(battery->wire_status) && !is_hv_wire_type(battery->wire_status) && (battery->wire_status != SEC_BATTERY_CABLE_HV_TA_CHG_LIMIT)) {
if (battery->current_event & SEC_BAT_CURRENT_EVENT_AFC) {
if (!battery->buck_cntl_by_tx) {
battery->buck_cntl_by_tx = true;
sec_bat_set_charge(battery, battery->charger_mode);
}
battery->tx_switch_mode = TX_SWITCH_MODE_OFF;
battery->tx_switch_start_soc = 0;
battery->tx_switch_mode_change = false;
sec_bat_wireless_iout_cntl(battery, battery->pdata->tx_uno_iout, battery->pdata->tx_mfc_iout_phone);
sec_bat_wireless_vout_cntl(battery, WC_TX_VOUT_7_5V);
sec_bat_wireless_minduty_cntl(battery, battery->pdata->tx_minduty_default);
} else if (battery->tx_switch_mode == TX_SWITCH_MODE_OFF) {
battery->tx_switch_mode = TX_SWITCH_UNO_ONLY;
battery->tx_switch_start_soc = battery->capacity;
if (!battery->buck_cntl_by_tx) {
battery->buck_cntl_by_tx = true;
sec_bat_set_charge(battery, battery->charger_mode);
}
sec_bat_wireless_iout_cntl(battery, battery->pdata->tx_uno_iout, battery->pdata->tx_mfc_iout_phone);
sec_bat_wireless_vout_cntl(battery, WC_TX_VOUT_7_5V);
sec_bat_wireless_minduty_cntl(battery, battery->pdata->tx_minduty_default);
} else if (battery->tx_switch_mode_change == true) {
battery->tx_switch_start_soc = battery->capacity;
pr_info("@Tx_mode: Switch Mode Change(%d -> %d)\n",
battery->tx_switch_mode,
battery->tx_switch_mode == TX_SWITCH_UNO_ONLY ?
TX_SWITCH_CHG_ONLY : TX_SWITCH_UNO_ONLY);
if (battery->tx_switch_mode == TX_SWITCH_UNO_ONLY) {
battery->tx_switch_mode = TX_SWITCH_CHG_ONLY;
sec_bat_wireless_iout_cntl(battery, battery->pdata->tx_uno_iout, battery->pdata->tx_mfc_iout_phone_5v);
sec_bat_wireless_vout_cntl(battery, WC_TX_VOUT_5_0V);
sec_bat_wireless_minduty_cntl(battery, battery->pdata->tx_minduty_5V);
if (battery->buck_cntl_by_tx) {
battery->buck_cntl_by_tx = false;
sec_bat_set_charge(battery, battery->charger_mode);
}
} else if (battery->tx_switch_mode == TX_SWITCH_CHG_ONLY) {
union power_supply_propval value = {0, };
battery->tx_switch_mode = TX_SWITCH_UNO_ONLY;
if (!battery->buck_cntl_by_tx) {
battery->buck_cntl_by_tx = true;
sec_bat_set_charge(battery, battery->charger_mode);
}
sec_bat_wireless_iout_cntl(battery, battery->pdata->tx_uno_iout, battery->pdata->tx_mfc_iout_phone);
sec_bat_wireless_vout_cntl(battery, WC_TX_VOUT_7_5V);
sec_bat_wireless_minduty_cntl(battery, battery->pdata->tx_minduty_default);
value.intval = true;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_EXT_PROP_WIRELESS_SEND_FSK, value);
}
battery->tx_switch_mode_change = false;
}
}
break;
default:
break;
}
wake_unlock(&battery->wpc_tx_wake_lock);
dev_info(battery->dev, "@Tx_Mode %s End\n", __func__);
}
static void sec_bat_cable_work(struct work_struct *work)
{
struct sec_battery_info *battery = container_of(work,
struct sec_battery_info, cable_work.work);
union power_supply_propval val = {0, };
int current_cable_type = SEC_BATTERY_CABLE_NONE;
int monitor_work_delay = 0;
dev_info(battery->dev, "%s: Start\n", __func__);
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_SKIP_HEATING_CONTROL,
SEC_BAT_CURRENT_EVENT_SKIP_HEATING_CONTROL);
#if defined(CONFIG_CCIC_NOTIFIER)
if (is_pd_wire_type(battery->wire_status)) {
if (battery->pdic_info.sink_status.selected_pdo_num ==
battery->pdic_info.sink_status.current_pdo_num)
sec_bat_set_current_event(battery, 0, SEC_BAT_CURRENT_EVENT_SELECT_PDO);
sec_bat_get_input_current_in_power_list(battery);
sec_bat_get_charging_current_in_power_list(battery);
#if defined(CONFIG_STEP_CHARGING)
sec_bat_reset_step_charging(battery);
#endif
}
#endif
if (battery->wc_status && battery->wc_enable) {
int wireless_current, wire_current;
int temp_current_type;
if (battery->wc_status == SEC_WIRELESS_PAD_WPC)
current_cable_type = SEC_BATTERY_CABLE_WIRELESS;
else if (battery->wc_status == SEC_WIRELESS_PAD_WPC_HV)
current_cable_type = SEC_BATTERY_CABLE_HV_WIRELESS;
else if (battery->wc_status == SEC_WIRELESS_PAD_WPC_PACK)
current_cable_type = SEC_BATTERY_CABLE_WIRELESS_PACK;
else if (battery->wc_status == SEC_WIRELESS_PAD_WPC_PACK_HV)
current_cable_type = SEC_BATTERY_CABLE_WIRELESS_HV_PACK;
else if (battery->wc_status == SEC_WIRELESS_PAD_WPC_STAND)
current_cable_type = SEC_BATTERY_CABLE_WIRELESS_STAND;
else if (battery->wc_status == SEC_WIRELESS_PAD_WPC_STAND_HV)
current_cable_type = SEC_BATTERY_CABLE_WIRELESS_HV_STAND;
else if (battery->wc_status == SEC_WIRELESS_PAD_VEHICLE)
current_cable_type = SEC_BATTERY_CABLE_WIRELESS_VEHICLE;
else if (battery->wc_status == SEC_WIRELESS_PAD_VEHICLE_HV)
current_cable_type = SEC_BATTERY_CABLE_WIRELESS_HV_VEHICLE;
else if (battery->wc_status == SEC_WIRELESS_PAD_PREPARE_HV)
current_cable_type = SEC_BATTERY_CABLE_PREPARE_WIRELESS_HV;
else if (battery->wc_status == SEC_WIRELESS_PAD_TX)
current_cable_type = SEC_BATTERY_CABLE_WIRELESS_TX;
else if (battery->wc_status == SEC_WIRELESS_PAD_WPC_PREPARE_HV_20)
current_cable_type = SEC_BATTERY_CABLE_PREPARE_WIRELESS_20;
else if (battery->wc_status == SEC_WIRELESS_PAD_WPC_HV_20)
current_cable_type = SEC_BATTERY_CABLE_HV_WIRELESS_20;
else if (battery->wc_status == SEC_WIRELESS_PAD_FAKE)
current_cable_type = SEC_BATTERY_CABLE_WIRELESS_FAKE;
else
current_cable_type = SEC_BATTERY_CABLE_PMA_WIRELESS;
if (current_cable_type == SEC_BATTERY_CABLE_PREPARE_WIRELESS_HV)
temp_current_type = SEC_BATTERY_CABLE_HV_WIRELESS;
else if (current_cable_type == SEC_BATTERY_CABLE_PREPARE_WIRELESS_20)
temp_current_type = SEC_BATTERY_CABLE_HV_WIRELESS_20;
else
temp_current_type = current_cable_type;
if (!is_nocharge_type(battery->wire_status)) {
wireless_current = battery->pdata->charging_current[temp_current_type].input_current_limit;
if (is_nv_wireless_type(temp_current_type))
wireless_current = (wireless_current * SEC_INPUT_VOLTAGE_5_5V);
else if (temp_current_type == SEC_BATTERY_CABLE_HV_WIRELESS_20)
wireless_current = (wireless_current * battery->wc20_vout);
else
wireless_current = (wireless_current * SEC_INPUT_VOLTAGE_10V);
wire_current = (battery->wire_status == SEC_BATTERY_CABLE_PREPARE_TA ?
battery->pdata->charging_current[SEC_BATTERY_CABLE_TA].input_current_limit :
battery->pdata->charging_current[battery->wire_status].input_current_limit);
wire_current = wire_current * (is_hv_wire_type(battery->wire_status) ?
(battery->wire_status == SEC_BATTERY_CABLE_12V_TA ? SEC_INPUT_VOLTAGE_12V : SEC_INPUT_VOLTAGE_9V)
: SEC_INPUT_VOLTAGE_5V);
if (is_pd_wire_type(battery->wire_status)) {
pr_info("%s: wl_cur(%d), pd_max_power(%d), wc_cable_type(%d), wire_cable_type(%d)\n",
__func__, wireless_current, battery->pd_max_charge_power, current_cable_type, battery->wire_status);
} else {
pr_info("%s: wl_cur(%d), wr_cur(%d), wc_cable_type(%d), wire_cable_type(%d)\n",
__func__, wireless_current, wire_current, current_cable_type, battery->wire_status);
}
if ((is_pd_wire_type(battery->wire_status) && wireless_current < battery->pd_max_charge_power) ||
(!is_pd_wire_type(battery->wire_status) && wireless_current <= wire_current)) {
current_cable_type = battery->wire_status;
pr_info("%s : switch charging path to cable\n", __func__);
/* set limited charging current before switching cable charging from wireless charging,
this step for wireless 2.0 -> HV cable charging */
if((battery->cable_type == SEC_BATTERY_CABLE_HV_WIRELESS_20) &&
(temp_current_type == SEC_BATTERY_CABLE_HV_WIRELESS_20)) {
val.intval = battery->pdata->wpc_charging_limit_current;
pr_info("%s : set TA charging current %dmA for a moment in case of TA OCP\n", __func__, val.intval);
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CURRENT_AVG, val);
msleep(100);
}
battery->wc_need_ldo_on = true;
val.intval = MFC_LDO_OFF;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_PROP_CHARGE_EMPTY, val);
/* Turn off TX to charge by cable charging having more power */
if (battery->wc_status == SEC_WIRELESS_PAD_TX) {
pr_info("@Tx_Mode %s : It is RX device with TA, notify TX device of this info\n", __func__);
val.intval = true;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_EXT_PROP_WIRELESS_SWITCH, val);
}
} else {
pr_info("%s : switch charging path to wireless\n", __func__);
battery->wc_need_ldo_on = false;
val.intval = MFC_LDO_ON;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_PROP_CHARGE_EMPTY, val);
}
} else {
/* turn on ldo when ldo was off because of TA, ldo is supposed to turn on automatically except force off by sw.
do not turn on ldo every wireless connection just in case ldo re-toggle by ic */
if(battery->wc_need_ldo_on) {
battery->wc_need_ldo_on = false;
val.intval = MFC_LDO_ON;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_PROP_CHARGE_EMPTY, val);
}
}
} else
current_cable_type = battery->wire_status;
if (is_pd_wire_type(current_cable_type) &&
is_pd_wire_type(battery->cable_type) &&
!is_slate_mode(battery)) {
cancel_delayed_work(&battery->afc_work);
wake_unlock(&battery->afc_wake_lock);
sec_bat_set_current_event(battery, 0,
SEC_BAT_CURRENT_EVENT_AFC | SEC_BAT_CURRENT_EVENT_AICL);
battery->aicl_current = 0;
sec_bat_set_charging_current(battery);
power_supply_changed(battery->psy_bat);
goto end_of_cable_work;
}
/* to clear this value when cable type switched without dettach */
if ((is_wired_type(battery->cable_type) && is_wireless_type(current_cable_type))
|| (is_wireless_type(battery->cable_type) && is_wired_type(current_cable_type))
|| (battery->muic_cable_type == ATTACHED_DEV_AFC_CHARGER_DISABLED_MUIC))
battery->max_charge_power = 0;
if ((current_cable_type == battery->cable_type) &&
!is_slate_mode(battery) &&
!(battery->current_event & SEC_BAT_CURRENT_EVENT_USB_SUSPENDED)) {
if (battery->prev_usb_conf != USB_CURRENT_NONE) {
dev_info(battery->dev, "%s: set usb charging current to %d mA\n",
__func__, battery->prev_usb_conf);
sec_bat_set_charging_current(battery);
battery->prev_usb_conf = USB_CURRENT_NONE;
}
dev_dbg(battery->dev,
"%s: Cable is NOT Changed(%d)\n",
__func__, battery->cable_type);
/* Do NOT activate cable work for NOT changed */
goto end_of_cable_work;
}
#if defined(CONFIG_BATTERY_SWELLING)
if (is_nocharge_type(current_cable_type) ||
(is_nocharge_type(battery->cable_type) && battery->swelling_mode == SWELLING_MODE_NONE)) {
battery->swelling_mode = SWELLING_MODE_NONE;
/* restore 4.4V float voltage */
val.intval = battery->pdata->swelling_normal_float_voltage;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_VOLTAGE_MAX, val);
pr_info("%s: float voltage = %d\n", __func__, val.intval);
} else {
pr_info("%s: skip float_voltage setting, swelling_mode(%d)\n",
__func__, battery->swelling_mode);
}
#endif
if (current_cable_type == SEC_BATTERY_CABLE_HV_TA_CHG_LIMIT)
current_cable_type = SEC_BATTERY_CABLE_9V_TA;
battery->cable_type = current_cable_type;
if (is_wireless_type(battery->cable_type)) {
power_supply_changed(battery->psy_bat);
/* After 10sec wireless charging, Vrect headroom has to be reduced */
wake_lock(&battery->wc_headroom_wake_lock);
queue_delayed_work(battery->monitor_wqueue, &battery->wc_headroom_work,
msecs_to_jiffies(10000));
} else if (battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_FAKE) {
power_supply_changed(battery->psy_bat);
}
if (battery->pdata->check_cable_result_callback)
battery->pdata->check_cable_result_callback(battery->cable_type);
/* platform can NOT get information of cable connection
* because wakeup time is too short to check uevent
* To make sure that target is wakeup
* if cable is connected and disconnected,
* activated wake lock in a few seconds
*/
wake_lock_timeout(&battery->vbus_wake_lock, HZ * 10);
if (is_nocharge_type(battery->cable_type) ||
((battery->pdata->cable_check_type &
SEC_BATTERY_CABLE_CHECK_NOINCOMPATIBLECHARGE) &&
battery->cable_type == SEC_BATTERY_CABLE_UNKNOWN)) {
/* initialize all status */
battery->charging_mode = SEC_BATTERY_CHARGING_NONE;
battery->vbus_chg_by_siop = SEC_INPUT_VOLTAGE_NONE;
battery->vbus_chg_by_full = false;
battery->is_recharging = false;
#if defined(CONFIG_BATTERY_CISD)
battery->cisd.ab_vbat_check_count = 0;
battery->cisd.state &= ~CISD_STATE_OVER_VOLTAGE;
#endif
battery->wc20_power_class = 0;
#if defined(CONFIG_CALC_TIME_TO_FULL)
battery->ttf_predict_wc20_charge_current = 0;
#endif
battery->wc20_vout = 0;
battery->input_voltage = 0;
battery->charge_power = 0;
battery->max_charge_power = 0;
battery->pd_max_charge_power = 0;
sec_bat_set_charging_status(battery,
POWER_SUPPLY_STATUS_DISCHARGING);
battery->chg_limit = false;
battery->mix_limit = false;
battery->chg_limit_recovery_cable = SEC_BATTERY_CABLE_NONE;
battery->wc_heating_start_time = 0;
battery->health = POWER_SUPPLY_HEALTH_GOOD;
battery->prev_usb_conf = USB_CURRENT_NONE;
battery->ta_alert_mode = OCP_NONE;
cancel_delayed_work(&battery->afc_work);
wake_unlock(&battery->afc_wake_lock);
sec_bat_change_default_current(battery, SEC_BATTERY_CABLE_USB,
battery->pdata->default_usb_input_current,
battery->pdata->default_usb_charging_current);
sec_bat_change_default_current(battery, SEC_BATTERY_CABLE_TA,
battery->pdata->default_input_current,
battery->pdata->default_charging_current);
sec_bat_change_default_current(battery, SEC_BATTERY_CABLE_HV_WIRELESS_20,
battery->pdata->default_wc20_input_current,
battery->pdata->default_wc20_charging_current);
/* usb default current is 100mA before configured*/
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_USB_100MA,
(SEC_BAT_CURRENT_EVENT_CHARGE_DISABLE |
SEC_BAT_CURRENT_EVENT_AFC |
SEC_BAT_CURRENT_EVENT_VBAT_OVP |
SEC_BAT_CURRENT_EVENT_VSYS_OVP |
SEC_BAT_CURRENT_EVENT_CHG_LIMIT |
SEC_BAT_CURRENT_EVENT_AICL |
SEC_BAT_CURRENT_EVENT_SELECT_PDO |
SEC_BAT_CURRENT_EVENT_25W_OCP |
SEC_BAT_CURRENT_EVENT_DC_ERR |
SEC_BAT_CURRENT_EVENT_USB_STATE));
#if defined(CONFIG_ENABLE_100MA_CHARGING_BEFORE_USB_CONFIGURED)
cancel_delayed_work(&battery->slowcharging_work);
#endif
battery->wc_cv_mode = false;
battery->is_sysovlo = false;
battery->is_vbatovlo = false;
battery->is_abnormal_temp = false;
battery->auto_mode = false;
#if defined(CONFIG_PREVENT_USB_CONN_OVERHEAT)
if (lpcharge)
battery->usb_temp_flag = false;
#endif
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING_OFF);
} else if (is_slate_mode(battery) ||
(battery->current_event & SEC_BAT_CURRENT_EVENT_USB_SUSPENDED)) {
dev_info(battery->dev,
"%s:slate mode on or set usb suspend\n",__func__);
battery->is_recharging = false;
battery->cable_type = SEC_BATTERY_CABLE_NONE;
battery->charging_mode = SEC_BATTERY_CHARGING_NONE;
battery->health = POWER_SUPPLY_HEALTH_GOOD;
battery->is_sysovlo = false;
battery->is_vbatovlo = false;
battery->is_abnormal_temp = false;
sec_bat_set_charging_status(battery,
POWER_SUPPLY_STATUS_DISCHARGING);
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_BUCK_OFF);
if (battery->current_event & SEC_BAT_CURRENT_EVENT_USB_SUSPENDED) {
battery->prev_usb_conf = USB_CURRENT_NONE;
monitor_work_delay = 3000;
goto run_monitor_work;
}
} else {
#if defined(CONFIG_EN_OOPS)
val.intval = battery->cable_type;
psy_do_property(battery->pdata->fuelgauge_name, set,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, val);
#endif
/* Do NOT display the charging icon when OTG or HMT_CONNECTED is enabled */
if (battery->cable_type == SEC_BATTERY_CABLE_OTG ||
battery->cable_type == SEC_BATTERY_CABLE_POWER_SHARING) {
battery->charging_mode = SEC_BATTERY_CHARGING_NONE;
battery->status = POWER_SUPPLY_STATUS_DISCHARGING;
} else if (!battery->is_sysovlo && !battery->is_vbatovlo && !battery->is_abnormal_temp &&
(!battery->charging_block || !battery->swelling_mode)) {
if (battery->pdata->full_check_type !=
SEC_BATTERY_FULLCHARGED_NONE)
battery->charging_mode =
SEC_BATTERY_CHARGING_1ST;
else
battery->charging_mode =
SEC_BATTERY_CHARGING_2ND;
if (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);
}
if (!battery->is_sysovlo && !battery->is_vbatovlo && !battery->is_abnormal_temp)
battery->health = POWER_SUPPLY_HEALTH_GOOD;
if (battery->cable_type == SEC_BATTERY_CABLE_TA ||
battery->cable_type == SEC_BATTERY_CABLE_WIRELESS ||
battery->cable_type == SEC_BATTERY_CABLE_PMA_WIRELESS ||
(is_hv_wire_type(battery->cable_type) &&
(battery->wc_status == SEC_WIRELESS_PAD_WPC_PREPARE_HV_20 ||
battery->wc_status == SEC_WIRELESS_PAD_WPC_HV_20))) {
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_AFC, SEC_BAT_CURRENT_EVENT_AFC);
} else {
cancel_delayed_work(&battery->afc_work);
wake_unlock(&battery->afc_wake_lock);
sec_bat_set_current_event(battery, 0, SEC_BAT_CURRENT_EVENT_AFC);
}
if (battery->cable_type == SEC_BATTERY_CABLE_OTG ||
battery->cable_type == SEC_BATTERY_CABLE_POWER_SHARING) {
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING_OFF);
goto end_of_cable_work;
} else if (!battery->is_sysovlo && !battery->is_vbatovlo && !battery->is_abnormal_temp &&
(!battery->charging_block || !battery->swelling_mode)) {
#if defined(CONFIG_ENABLE_FULL_BY_SOC)
if (battery->capacity >= 100) {
sec_bat_do_fullcharged(battery, true);
dev_info(battery->dev,
"%s: charging start at full, do not turn on charging\n", __func__);
} else
#endif
if (is_pd_wire_type(battery->wire_status) && (battery->charge_power == 0))
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_BUCK_OFF);
else
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING);
}
#if defined(CONFIG_ENABLE_100MA_CHARGING_BEFORE_USB_CONFIGURED)
if (battery->cable_type == SEC_BATTERY_CABLE_USB && !lpcharge)
queue_delayed_work(battery->monitor_wqueue, &battery->slowcharging_work,
msecs_to_jiffies(3000));
#endif
#if defined(CONFIG_CALC_TIME_TO_FULL)
if (lpcharge) {
cancel_delayed_work(&battery->timetofull_work);
if (battery->current_event & SEC_BAT_CURRENT_EVENT_AFC) {
int work_delay = 0;
if (!is_wireless_type(battery->cable_type)) {
work_delay = battery->pdata->pre_afc_work_delay;
} else {
work_delay = battery->pdata->pre_wc_afc_work_delay;
}
queue_delayed_work(battery->monitor_wqueue,
&battery->timetofull_work, msecs_to_jiffies(work_delay));
}
}
#endif
}
if (battery->cable_type != SEC_BATTERY_CABLE_WIRELESS_FAKE) {
/* set online(cable type) */
val.intval = battery->cable_type;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_ONLINE, val);
psy_do_property(battery->pdata->fuelgauge_name, set,
POWER_SUPPLY_PROP_ONLINE, val);
/* set charging current */
psy_do_property(battery->pdata->charger_name, get,
POWER_SUPPLY_PROP_CURRENT_AVG, val);
battery->aicl_current = 0;
sec_bat_set_current_event(battery, 0, SEC_BAT_CURRENT_EVENT_AICL);
battery->input_current = val.intval;
/* to init battery type current when wireless charging -> battery case */
if (is_nocharge_type(battery->cable_type))
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CURRENT_MAX, val);
if (battery->status != POWER_SUPPLY_STATUS_DISCHARGING)
sec_bat_check_input_voltage(battery);
sec_bat_set_charging_current(battery);
}
/* polling time should be reset when cable is changed
* polling_in_sleep should be reset also
* before polling time is re-calculated
* to prevent from counting 1 for events
* right after cable is connected
*/
battery->polling_in_sleep = false;
sec_bat_get_polling_time(battery);
dev_info(battery->dev,
"%s: Status:%s, Sleep:%s, Charging:%s, Short Poll:%s\n",
__func__, sec_bat_status_str[battery->status],
battery->polling_in_sleep ? "Yes" : "No",
(battery->charging_mode ==
SEC_BATTERY_CHARGING_NONE) ? "No" : "Yes",
battery->polling_short ? "Yes" : "No");
dev_info(battery->dev,
"%s: Polling time is reset to %d sec.\n", __func__,
battery->polling_time);
battery->polling_count = 1; /* initial value = 1 */
run_monitor_work:
cancel_delayed_work(&battery->monitor_work);
wake_lock(&battery->monitor_wake_lock);
queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, msecs_to_jiffies(monitor_work_delay));
end_of_cable_work:
wake_unlock(&battery->cable_wake_lock);
dev_info(battery->dev, "%s: End\n", __func__);
}
static void sec_bat_afc_work(struct work_struct *work)
{
struct sec_battery_info *battery = container_of(work,
struct sec_battery_info, afc_work.work);
union power_supply_propval value = {0, };
dev_info(battery->dev, "%s: start\n", __func__);
psy_do_property(battery->pdata->charger_name, get,
POWER_SUPPLY_PROP_CURRENT_MAX, value);
battery->current_max = value.intval;
if (battery->current_event & SEC_BAT_CURRENT_EVENT_AFC) {
sec_bat_set_current_event(battery, 0, SEC_BAT_CURRENT_EVENT_AFC);
if ((battery->wc_status != SEC_WIRELESS_PAD_NONE &&
battery->current_max >= battery->pdata->pre_wc_afc_input_current) ||
((is_hv_wire_type(battery->cable_type) || battery->cable_type == SEC_BATTERY_CABLE_TA) &&
battery->current_max >= battery->pdata->pre_afc_input_current)) {
sec_bat_set_charging_current(battery);
#if defined(CONFIG_BATTERY_CISD)
if (battery->cable_type == SEC_BATTERY_CABLE_TA)
battery->cisd.cable_data[CISD_CABLE_TA]++;
#endif
}
if (battery->wc_tx_enable) {
cancel_delayed_work(&battery->wpc_tx_work);
wake_lock(&battery->wpc_tx_wake_lock);
queue_delayed_work(battery->monitor_wqueue,
&battery->wpc_tx_work, 0);
}
}
dev_info(battery->dev, "%s: End\n", __func__);
wake_unlock(&battery->afc_wake_lock);
}
static int sec_bat_set_property(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *val)
{
struct sec_battery_info *battery = power_supply_get_drvdata(psy);
int current_cable_type = SEC_BATTERY_CABLE_NONE;
int full_check_type = SEC_BATTERY_FULLCHARGED_NONE;
union power_supply_propval value = {0, };
enum power_supply_ext_property ext_psp = (enum power_supply_ext_property) psp;
dev_dbg(battery->dev,
"%s: (%d,%d)\n", __func__, psp, val->intval);
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
if (battery->charging_mode == SEC_BATTERY_CHARGING_1ST)
full_check_type = battery->pdata->full_check_type;
else
full_check_type = battery->pdata->full_check_type_2nd;
if ((full_check_type == SEC_BATTERY_FULLCHARGED_CHGINT) &&
(val->intval == POWER_SUPPLY_STATUS_FULL))
sec_bat_do_fullcharged(battery, false);
sec_bat_set_charging_status(battery, val->intval);
break;
case POWER_SUPPLY_PROP_HEALTH:
if (battery->cable_type != SEC_BATTERY_CABLE_WIRELESS_FAKE)
sec_bat_ovp_uvlo_result(battery, val->intval);
break;
case POWER_SUPPLY_PROP_ONLINE:
current_cable_type = val->intval;
#if !defined(CONFIG_CCIC_NOTIFIER)
if ((battery->muic_cable_type != ATTACHED_DEV_SMARTDOCK_TA_MUIC)
&& ((current_cable_type == SEC_BATTERY_CABLE_SMART_OTG) ||
(current_cable_type == SEC_BATTERY_CABLE_SMART_NOTG)))
break;
#endif
if (current_cable_type < 0) {
dev_info(battery->dev,
"%s: ignore event(%d)\n",
__func__, current_cable_type);
} else if (current_cable_type == SEC_BATTERY_CABLE_OTG) {
battery->charging_mode = SEC_BATTERY_CHARGING_NONE;
battery->is_recharging = false;
sec_bat_set_charging_status(battery,
POWER_SUPPLY_STATUS_DISCHARGING);
battery->cable_type = current_cable_type;
wake_lock(&battery->monitor_wake_lock);
queue_delayed_work(battery->monitor_wqueue,
&battery->monitor_work, 0);
break;
} else {
battery->wire_status = current_cable_type;
if (is_nocharge_type(battery->wire_status) &&
(battery->wc_status != SEC_WIRELESS_PAD_NONE) )
current_cable_type = SEC_BATTERY_CABLE_WIRELESS;
}
dev_info(battery->dev,
"%s: current_cable(%d), wc_status(%d), wire_status(%d)\n",
__func__, current_cable_type, battery->wc_status,
battery->wire_status);
/* cable is attached or detached
* if current_cable_type is minus value,
* check cable by sec_bat_get_cable_type()
* although SEC_BATTERY_CABLE_SOURCE_EXTERNAL is set
* (0 is SEC_BATTERY_CABLE_UNKNOWN)
*/
if ((current_cable_type >= 0) &&
(current_cable_type < SEC_BATTERY_CABLE_MAX) &&
(battery->pdata->cable_source_type &
SEC_BATTERY_CABLE_SOURCE_EXTERNAL)) {
wake_lock(&battery->cable_wake_lock);
queue_delayed_work(battery->monitor_wqueue,
&battery->cable_work,0);
} else {
if (sec_bat_get_cable_type(battery,
battery->pdata->cable_source_type)) {
wake_lock(&battery->cable_wake_lock);
queue_delayed_work(battery->monitor_wqueue,
&battery->cable_work,0);
}
}
break;
case POWER_SUPPLY_PROP_CAPACITY:
battery->capacity = val->intval;
power_supply_changed(battery->psy_bat);
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
/* If JIG is attached, the voltage is set as 1079 */
pr_info("%s : set to the battery history : (%d)\n",__func__, val->intval);
if(val->intval == 1079) {
battery->voltage_now = 1079;
battery->voltage_avg = 1079;
power_supply_changed(battery->psy_bat);
}
break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
wake_lock(&battery->monitor_wake_lock);
queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
break;
case POWER_SUPPLY_PROP_PRESENT:
battery->present = val->intval;
wake_lock(&battery->monitor_wake_lock);
queue_delayed_work(battery->monitor_wqueue,
&battery->monitor_work, 0);
break;
#if defined(CONFIG_BATTERY_SWELLING)
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
break;
#endif
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION:
case POWER_SUPPLY_PROP_CHARGE_COUNTER_SHADOW:
break;
case POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL:
value.intval = val->intval;
pr_info("%s: CHGIN-OTG %s\n", __func__, value.intval > 0 ? "on" : "off");
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL, value);
break;
case POWER_SUPPLY_PROP_CHARGE_UNO_CONTROL:
value.intval = val->intval;
pr_info("%s: WCIN-UNO %s\n", __func__, value.intval > 0 ? "on" : "off");
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CHARGE_UNO_CONTROL, value);
break;
#if defined(CONFIG_UPDATE_BATTERY_DATA)
case POWER_SUPPLY_PROP_POWER_DESIGN:
sec_bat_parse_dt(battery->dev, battery);
break;
#endif
#if defined(CONFIG_BATTERY_CISD)
case POWER_SUPPLY_PROP_VOLTAGE_MIN:
pr_info("%s: Valert was occured! run monitor work for updating cisd data!\n", __func__);
battery->cisd.data[CISD_DATA_VALERT_COUNT]++;
battery->cisd.data[CISD_DATA_VALERT_COUNT_PER_DAY]++;
wake_lock(&battery->monitor_wake_lock);
queue_delayed_work_on(0, battery->monitor_wqueue,
&battery->monitor_work, 0);
break;
#endif
#if !defined(CONFIG_ENG_BATTERY_CONCEPT) && !defined(CONFIG_SEC_FACTORY)
case POWER_SUPPLY_PROP_SCOPE:
battery->block_water_event = val->intval;
break;
#endif
case POWER_SUPPLY_PROP_MAX ... POWER_SUPPLY_EXT_PROP_MAX:
switch (ext_psp) {
case POWER_SUPPLY_EXT_PROP_AICL_CURRENT:
battery->aicl_current = val->intval;
battery->max_charge_power = battery->charge_power = battery->input_voltage * val->intval;
pr_info("%s: aicl : %dmA, %dmW)\n", __func__,
battery->aicl_current, battery->charge_power);
if (is_wired_type(battery->cable_type))
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_AICL,
SEC_BAT_CURRENT_EVENT_AICL);
#if defined(CONFIG_BATTERY_CISD)
battery->cisd.data[CISD_DATA_AICL_COUNT]++;
battery->cisd.data[CISD_DATA_AICL_COUNT_PER_DAY]++;
#endif
break;
case POWER_SUPPLY_EXT_PROP_SYSOVLO:
if (battery->status != POWER_SUPPLY_STATUS_DISCHARGING) {
pr_info("%s: Vsys is ovlo !!\n", __func__);
battery->is_sysovlo = true;
battery->is_recharging = false;
battery->charging_mode = SEC_BATTERY_CHARGING_NONE;
battery->health = POWER_SUPPLY_HEALTH_VSYS_OVP;
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_VSYS_OVP, SEC_BAT_CURRENT_EVENT_VSYS_OVP);
sec_bat_set_charging_status(battery, POWER_SUPPLY_STATUS_NOT_CHARGING);
#if defined(CONFIG_BATTERY_CISD)
battery->cisd.data[CISD_DATA_VSYS_OVP]++;
battery->cisd.data[CISD_DATA_VSYS_OVP_PER_DAY]++;
#endif
#if defined(CONFIG_SEC_ABC)
sec_abc_send_event("MODULE=battery@ERROR=vsys_ovp");
#endif
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING_OFF);
wake_lock(&battery->monitor_wake_lock);
queue_delayed_work(battery->monitor_wqueue,
&battery->monitor_work, 0);
}
break;
case POWER_SUPPLY_EXT_PROP_VBAT_OVP:
if (battery->status != POWER_SUPPLY_STATUS_DISCHARGING) {
pr_info("%s: Vbat is ovlo !!\n", __func__);
battery->is_vbatovlo = true;
battery->is_recharging = false;
battery->charging_mode = SEC_BATTERY_CHARGING_NONE;
battery->health = POWER_SUPPLY_HEALTH_VBAT_OVP;
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_VBAT_OVP, SEC_BAT_CURRENT_EVENT_VBAT_OVP);
sec_bat_set_charging_status(battery, POWER_SUPPLY_STATUS_NOT_CHARGING);
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING_OFF);
wake_lock(&battery->monitor_wake_lock);
queue_delayed_work(battery->monitor_wqueue,
&battery->monitor_work, 0);
}
break;
case POWER_SUPPLY_EXT_PROP_USB_CONFIGURE:
if (battery->pdic_info.sink_status.rp_currentlvl > RP_CURRENT_LEVEL_DEFAULT)
return 0;
pr_info("%s: usb configured %d\n", __func__, val->intval);
if (val->intval != battery->prev_usb_conf) {
int cable_work_delay = 0;
if (val->intval == USB_CURRENT_UNCONFIGURED) {
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_USB_100MA, SEC_BAT_CURRENT_EVENT_USB_STATE);
} else if (val->intval == USB_CURRENT_HIGH_SPEED) {
sec_bat_set_misc_event(battery, 0, BATT_MISC_EVENT_TIMEOUT_OPEN_TYPE);
sec_bat_set_current_event(battery, 0, SEC_BAT_CURRENT_EVENT_USB_STATE);
sec_bat_change_default_current(battery, SEC_BATTERY_CABLE_USB,
battery->pdata->default_usb_input_current,
battery->pdata->default_usb_charging_current);
} else if (val->intval == USB_CURRENT_SUPER_SPEED) {
sec_bat_set_misc_event(battery, 0, BATT_MISC_EVENT_TIMEOUT_OPEN_TYPE);
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_USB_SUPER, SEC_BAT_CURRENT_EVENT_USB_STATE);
sec_bat_change_default_current(battery, SEC_BATTERY_CABLE_USB,
USB_CURRENT_SUPER_SPEED, USB_CURRENT_SUPER_SPEED);
} else if (val->intval == USB_CURRENT_SUSPENDED) {
sec_bat_set_misc_event(battery, 0, BATT_MISC_EVENT_TIMEOUT_OPEN_TYPE);
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_USB_SUSPENDED, SEC_BAT_CURRENT_EVENT_USB_STATE);
cable_work_delay = 500;
}
battery->prev_usb_conf = val->intval;
cancel_delayed_work(&battery->cable_work);
wake_lock(&battery->cable_wake_lock);
queue_delayed_work(battery->monitor_wqueue, &battery->cable_work, msecs_to_jiffies(cable_work_delay));
}
break;
case POWER_SUPPLY_EXT_PROP_OVERHEAT_NOTIFY:
pr_info("%s: POWER_SUPPLY_EXT_PROP_OVERHEAT_NOTIFY!\n", __func__);
wake_lock(&battery->monitor_wake_lock);
queue_delayed_work(battery->monitor_wqueue,
&battery->monitor_work, 0);
break;
case POWER_SUPPLY_EXT_PROP_HV_DISABLE:
pr_info("HV wired charging mode is %s\n", (val->intval == CH_MODE_AFC_DISABLE_VAL ? "Disabled" : "Enabled"));
if (val->intval == CH_MODE_AFC_DISABLE_VAL)
sec_bat_set_current_event(battery,
SEC_BAT_CURRENT_EVENT_HV_DISABLE, SEC_BAT_CURRENT_EVENT_HV_DISABLE);
else
sec_bat_set_current_event(battery,
0, SEC_BAT_CURRENT_EVENT_HV_DISABLE);
/* For lsi, sm pd, if pdo is the same, pd noti is not transmitted.
so, it requests a different pdo than current one. */
if (is_pd_wire_type(battery->cable_type)) {
int target_pd_index = battery->pd_list.max_pd_count - 1;
battery->update_pd_list = true;
pr_info("%s: update pd list\n", __func__);
if (battery->pdic_info.sink_status.current_pdo_num != 1)
target_pd_index = 0;
if (target_pd_index >= 0 && target_pd_index < MAX_PDO_NUM)
select_pdo(battery->pd_list.pd_info[target_pd_index].pdo_index);
}
break;
case POWER_SUPPLY_EXT_PROP_WC_CONTROL:
pr_info("%s: Recover MFC IC (wc_enable: %d)\n",
__func__, battery->wc_enable);
if (battery->pdata->wpc_en) {
mutex_lock(&battery->wclock);
if (battery->wc_enable) {
gpio_direction_output(battery->pdata->wpc_en, 1);
msleep(500);
gpio_direction_output(battery->pdata->wpc_en, 0);
}
mutex_unlock(&battery->wclock);
}
#if defined(CONFIG_BATTERY_CISD)
increase_cisd_count(CISD_DATA_DROP_VALUE);
#endif
break;
case POWER_SUPPLY_EXT_PROP_CURRENT_EVENT:
if (!(battery->current_event & val->intval)) {
pr_info("%s: set new current_event %d\n", __func__, val->intval);
#if 0
if (val->intval == SEC_BAT_CURRENT_EVENT_DC_ERR)
battery->cisd.event_data[EVENT_DC_ERR]++;
#endif
sec_bat_set_current_event(battery, val->intval, val->intval);
}
break;
case POWER_SUPPLY_EXT_PROP_CURRENT_EVENT_CLEAR:
pr_info("%s: new current_event clear %d\n", __func__, val->intval);
sec_bat_set_current_event(battery, 0, val->intval);
break;
#if defined(CONFIG_WIRELESS_TX_MODE)
case POWER_SUPPLY_EXT_PROP_WIRELESS_TX_AVG_CURR:
break;
#endif
case POWER_SUPPLY_EXT_PROP_SRCCAP:
if (val->intval)
battery->init_src_cap = true;
pr_info("%s: set init src cap %d", __func__, battery->init_src_cap);
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
}
return 0;
}
static int sec_bat_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct sec_battery_info *battery = power_supply_get_drvdata(psy);
union power_supply_propval value = {0, };
enum power_supply_ext_property ext_psp = (enum power_supply_ext_property) psp;
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
if ((battery->health == POWER_SUPPLY_HEALTH_OVERVOLTAGE) ||
(battery->health == POWER_SUPPLY_HEALTH_UNDERVOLTAGE)) {
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
} else {
if ((battery->pdata->cable_check_type &
SEC_BATTERY_CABLE_CHECK_NOUSBCHARGE) &&
!lpcharge) {
switch (battery->cable_type) {
case SEC_BATTERY_CABLE_USB:
case SEC_BATTERY_CABLE_USB_CDP:
val->intval =
POWER_SUPPLY_STATUS_DISCHARGING;
return 0;
}
}
#if defined(CONFIG_STORE_MODE)
if (battery->store_mode && !lpcharge &&
!is_nocharge_type(battery->cable_type) &&
battery->status == POWER_SUPPLY_STATUS_DISCHARGING) {
val->intval = POWER_SUPPLY_STATUS_CHARGING;
} else
#endif
val->intval = battery->status;
}
break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
if (is_nocharge_type(battery->cable_type)) {
val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
} else if (is_hv_wire_type(battery->cable_type) || is_pd_wire_type(battery->cable_type)) {
val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
} else {
psy_do_property(battery->pdata->charger_name, get,
POWER_SUPPLY_PROP_CHARGE_TYPE, value);
if (value.intval == SEC_BATTERY_CABLE_UNKNOWN)
/* if error in CHARGE_TYPE of charger
* set CHARGE_TYPE as NONE
*/
val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
else
val->intval = value.intval;
}
break;
case POWER_SUPPLY_PROP_HEALTH:
if (lpcharge &&
(battery->health == POWER_SUPPLY_HEALTH_OVERVOLTAGE ||
battery->health == POWER_SUPPLY_HEALTH_UNDERVOLTAGE ||
battery->health == POWER_SUPPLY_HEALTH_DC_ERR))
val->intval = POWER_SUPPLY_HEALTH_GOOD;
else if (battery->health >= POWER_SUPPLY_HEALTH_MAX)
val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
else
val->intval = battery->health;
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = battery->present;
break;
case POWER_SUPPLY_PROP_ONLINE:
if (is_hv_wireless_type(battery->cable_type) ||
(battery->cable_type == SEC_BATTERY_CABLE_PREPARE_WIRELESS_HV) ||
(battery->cable_type == SEC_BATTERY_CABLE_PREPARE_WIRELESS_20)) {
if (sec_bat_hv_wc_normal_mode_check(battery))
val->intval = SEC_BATTERY_CABLE_WIRELESS;
else
val->intval = SEC_BATTERY_CABLE_HV_WIRELESS_ETX;
}
else if(battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_PACK ||
battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_STAND ||
battery->cable_type == SEC_BATTERY_CABLE_PMA_WIRELESS ||
battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_VEHICLE ||
battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_TX ||
battery->cable_type == SEC_BATTERY_CABLE_HV_WIRELESS_20 ||
battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_FAKE)
val->intval = SEC_BATTERY_CABLE_WIRELESS;
else
val->intval = battery->cable_type;
pr_info("%s cable type = %d sleep_mode = %d\n", __func__, val->intval, sleep_mode);
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = battery->pdata->technology;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
#ifdef CONFIG_SEC_FACTORY
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_VOLTAGE_NOW, value);
battery->voltage_now = value.intval;
dev_err(battery->dev,
"%s: voltage now(%d)\n", __func__, battery->voltage_now);
#endif
/* voltage value should be in uV */
val->intval = battery->voltage_now * 1000;
break;
case POWER_SUPPLY_PROP_VOLTAGE_AVG:
#ifdef CONFIG_SEC_FACTORY
value.intval = SEC_BATTERY_VOLTAGE_AVERAGE;
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_VOLTAGE_AVG, value);
battery->voltage_avg = value.intval;
dev_err(battery->dev,
"%s: voltage avg(%d)\n", __func__, battery->voltage_avg);
#endif
/* voltage value should be in uV */
val->intval = battery->voltage_avg * 1000;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
value.intval = SEC_BATTERY_CURRENT_MA;
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_CURRENT_NOW, value);
val->intval = value.intval;
break;
case POWER_SUPPLY_PROP_CURRENT_AVG:
value.intval = SEC_BATTERY_CURRENT_MA;
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_CURRENT_AVG, value);
val->intval = value.intval;
break;
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
case POWER_SUPPLY_PROP_CHARGE_FULL:
#if defined(CONFIG_BATTERY_CISD)
val->intval = battery->pdata->battery_full_capacity * 1000;
#else
val->intval = 0;
#endif
break;
/* charging mode (differ from power supply) */
case POWER_SUPPLY_PROP_CHARGE_NOW:
val->intval = battery->charging_mode;
break;
case POWER_SUPPLY_PROP_CAPACITY:
if (battery->pdata->fake_capacity) {
val->intval = 90;
pr_info("%s : capacity(%d)\n", __func__, val->intval);
} else {
#if defined(CONFIG_ENG_BATTERY_CONCEPT)
if (battery->status == POWER_SUPPLY_STATUS_FULL) {
if(battery->eng_not_full_status)
val->intval = battery->capacity;
else
val->intval = 100;
} else {
val->intval = battery->capacity;
}
#else
if (battery->status == POWER_SUPPLY_STATUS_FULL)
val->intval = 100;
else
val->intval = battery->capacity;
#endif
}
break;
case POWER_SUPPLY_PROP_TEMP:
val->intval = battery->temperature;
break;
case POWER_SUPPLY_PROP_TEMP_AMBIENT:
val->intval = battery->temper_amb;
break;
#if defined(CONFIG_FUELGAUGE_MAX77705)
case POWER_SUPPLY_PROP_POWER_NOW:
value.intval = SEC_BATTERY_ISYS_MA;
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_EXT_PROP_MEASURE_SYS, value);
val->intval = value.intval;
break;
case POWER_SUPPLY_PROP_POWER_AVG:
value.intval = SEC_BATTERY_ISYS_AVG_MA;
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_EXT_PROP_MEASURE_SYS, value);
val->intval = value.intval;
break;
#endif
#if defined(CONFIG_CALC_TIME_TO_FULL)
case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
if (battery->capacity == 100) {
val->intval = 0;
break;
}
if (((battery->status == POWER_SUPPLY_STATUS_CHARGING) ||
(battery->status == POWER_SUPPLY_STATUS_FULL && battery->capacity != 100)) &&
!battery->swelling_mode)
val->intval = battery->timetofull;
else
val->intval = 0;
break;
#endif
#if defined(CONFIG_BATTERY_SWELLING)
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
if (battery->swelling_mode)
val->intval = 1;
else
val->intval = 0;
break;
#endif
case POWER_SUPPLY_PROP_CHARGE_COUNTER_SHADOW:
val->intval = battery->wire_status;
break;
case POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL:
case POWER_SUPPLY_PROP_CHARGE_UNO_CONTROL:
break;
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
val->intval = battery->charge_counter;
break;
case POWER_SUPPLY_PROP_MAX ... POWER_SUPPLY_EXT_PROP_MAX:
switch (ext_psp) {
case POWER_SUPPLY_EXT_PROP_SUB_PBA_TEMP_REC:
val->intval = !battery->vbus_limit;
break;
case POWER_SUPPLY_EXT_PROP_CHARGE_POWER:
val->intval = battery->charge_power;
break;
case POWER_SUPPLY_EXT_PROP_CURRENT_EVENT:
val->intval = battery->current_event;
break;
#if defined(CONFIG_WIRELESS_TX_MODE)
case POWER_SUPPLY_EXT_PROP_WIRELESS_TX_AVG_CURR:
val->intval = battery->tx_avg_curr;
break;
case POWER_SUPPLY_EXT_PROP_WIRELESS_TX_ENABLE:
val->intval = battery->wc_tx_enable;
break;
#endif
case POWER_SUPPLY_EXT_PROP_PAD_VOLT_CTRL:
if (battery->wpc_vout_ctrl_lcd_on)
val->intval = battery->lcd_status;
else
val->intval = false;
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
}
return 0;
}
static int sec_usb_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct sec_battery_info *battery = power_supply_get_drvdata(psy);
if (psp != POWER_SUPPLY_PROP_ONLINE)
return -EINVAL;
if ((battery->health == POWER_SUPPLY_HEALTH_OVERVOLTAGE) ||
(battery->health == POWER_SUPPLY_HEALTH_UNDERVOLTAGE)) {
val->intval = 0;
return 0;
}
/* Set enable=1 only if the USB charger is connected */
switch (battery->wire_status) {
case SEC_BATTERY_CABLE_USB:
case SEC_BATTERY_CABLE_USB_CDP:
val->intval = 1;
break;
case SEC_BATTERY_CABLE_PDIC:
case SEC_BATTERY_CABLE_NONE:
val->intval = (battery->pd_usb_attached) ? 1:0;
break;
default:
val->intval = 0;
break;
}
if (is_slate_mode(battery))
val->intval = 0;
return 0;
}
static int sec_ac_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct sec_battery_info *battery = power_supply_get_drvdata(psy);
enum power_supply_ext_property ext_psp = (enum power_supply_ext_property) psp;
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
if ((battery->health == POWER_SUPPLY_HEALTH_OVERVOLTAGE) ||
(battery->health == POWER_SUPPLY_HEALTH_UNDERVOLTAGE)) {
val->intval = 0;
return 0;
}
/* Set enable=1 only if the AC charger is connected */
switch (battery->cable_type) {
case SEC_BATTERY_CABLE_TA:
case SEC_BATTERY_CABLE_UARTOFF:
case SEC_BATTERY_CABLE_LAN_HUB:
case SEC_BATTERY_CABLE_UNKNOWN:
case SEC_BATTERY_CABLE_PREPARE_TA:
case SEC_BATTERY_CABLE_9V_ERR:
case SEC_BATTERY_CABLE_9V_UNKNOWN:
case SEC_BATTERY_CABLE_9V_TA:
case SEC_BATTERY_CABLE_12V_TA:
case SEC_BATTERY_CABLE_HMT_CONNECTED:
case SEC_BATTERY_CABLE_HMT_CHARGE:
case SEC_BATTERY_CABLE_HV_TA_CHG_LIMIT:
case SEC_BATTERY_CABLE_QC20:
case SEC_BATTERY_CABLE_QC30:
case SEC_BATTERY_CABLE_TIMEOUT:
case SEC_BATTERY_CABLE_SMART_OTG:
case SEC_BATTERY_CABLE_SMART_NOTG:
val->intval = 1;
break;
case SEC_BATTERY_CABLE_PDIC:
val->intval = (battery->pd_usb_attached) ? 0:1;
break;
default:
val->intval = 0;
break;
}
break;
case POWER_SUPPLY_PROP_TEMP:
val->intval = battery->chg_temp;
break;
case POWER_SUPPLY_PROP_MAX ... POWER_SUPPLY_EXT_PROP_MAX:
switch (ext_psp) {
case POWER_SUPPLY_EXT_PROP_WATER_DETECT:
if (battery->misc_event & (BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE |
BATT_MISC_EVENT_HICCUP_TYPE)) {
val->intval = 1;
pr_info("%s: Water Detect\n", __func__);
} else {
val->intval = 0;
}
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
}
if (lpcharge && (battery->misc_event & BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE)) {
val->intval = 1;
}
return 0;
}
static int sec_wireless_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct sec_battery_info *battery = power_supply_get_drvdata(psy);
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
val->intval = (is_wireless_type(battery->cable_type) ||
battery->cable_type == SEC_BATTERY_CABLE_WIRELESS_FAKE) ?
1 : 0;
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = (battery->pdata->wireless_charger_name) ?
1 : 0;
break;
case POWER_SUPPLY_PROP_POWER_DESIGN:
if (battery->cable_type == SEC_BATTERY_CABLE_PREPARE_WIRELESS_20)
val->intval = battery->wc20_power_class;
else
val->intval = 0;
break;
default:
return -EINVAL;
}
return 0;
}
void sec_wireless_set_tx_enable(struct sec_battery_info *battery, bool wc_tx_enable)
{
union power_supply_propval value = {0, };
pr_info("@Tx_Mode %s: TX Power enable ? (%d)\n", __func__, wc_tx_enable);
battery->wc_tx_enable = wc_tx_enable;
battery->tx_minduty = battery->pdata->tx_minduty_default;
battery->tx_switch_mode = TX_SWITCH_MODE_OFF;
battery->tx_switch_start_soc = 0;
battery->tx_switch_mode_change = false;
if (wc_tx_enable) {
/* set tx event */
sec_bat_set_tx_event(battery, BATT_TX_EVENT_WIRELESS_TX_STATUS,
(BATT_TX_EVENT_WIRELESS_TX_STATUS | BATT_TX_EVENT_WIRELESS_TX_RETRY));
if (is_hv_wire_type(battery->wire_status)) {
muic_afc_set_voltage(SEC_INPUT_VOLTAGE_5V);
} else if (is_pd_wire_type(battery->wire_status) && battery->hv_pdo) {
pr_info("@Tx_Mode %s: PD charnge pdo (9V -> 5V). Because Tx Start.\n", __func__);
sec_bat_change_pdo(battery, SEC_INPUT_VOLTAGE_5V);
} else {
battery->buck_cntl_by_tx = true;
sec_bat_wireless_uno_cntl(battery, true);
sec_bat_wireless_vout_cntl(battery, WC_TX_VOUT_5_0V);
sec_bat_wireless_iout_cntl(battery, battery->pdata->tx_uno_iout, battery->pdata->tx_mfc_iout_gear);
}
#if defined(CONFIG_WIRELESS_TX_MODE)
pr_info("@Tx_Mode %s: TX Power Calculation start.\n", __func__);
queue_delayed_work(battery->monitor_wqueue,
&battery->wpc_txpower_calc_work, 0);
#endif
} else {
battery->uno_en = false;
sec_bat_wireless_minduty_cntl(battery, battery->pdata->tx_minduty_default);
value.intval = false;
battery->wc_rx_type = NO_DEV;
battery->wc_rx_connected = false;
battery->tx_uno_iout = 0;
battery->tx_mfc_iout = 0;
if (battery->afc_disable) {
battery->afc_disable = false;
#if 0
muic_hv_charger_disable(battery->afc_disable);
#endif
}
if (battery->buck_cntl_by_tx) {
battery->buck_cntl_by_tx = false;
sec_bat_set_charge(battery, battery->charger_mode);
}
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_EXT_PROP_WIRELESS_TX_ENABLE, value);
battery->wc_tx_vout = WC_TX_VOUT_5_0V;
msleep(50);
/* clear tx all event */
sec_bat_set_tx_event(battery,
0,
BATT_TX_EVENT_WIRELESS_ALL_MASK);
if (is_hv_wire_type(battery->cable_type)) {
muic_afc_set_voltage(SEC_INPUT_VOLTAGE_9V);
/* for 1) not supporting DC and charging bia PD20/DC on Tx
2) supporting DC and charging bia PD20 on Tx */
} else if (is_pd_fpdo_wire_type(battery->cable_type) && !battery->hv_pdo) {
sec_bat_change_pdo(battery, SEC_INPUT_VOLTAGE_9V);
}
cancel_delayed_work(&battery->wpc_tx_work);
#if defined(CONFIG_WIRELESS_TX_MODE)
cancel_delayed_work(&battery->wpc_txpower_calc_work);
#endif
wake_unlock(&battery->wpc_tx_wake_lock);
}
}
static void sec_wireless_otg_control(struct sec_battery_info *battery, int enable)
{
union power_supply_propval value = {0, };
if (enable) {
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_WPC_VOUT_LOCK,
SEC_BAT_CURRENT_EVENT_WPC_VOUT_LOCK);
} else {
sec_bat_set_current_event(battery, 0,
SEC_BAT_CURRENT_EVENT_WPC_VOUT_LOCK);
}
value.intval = enable;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL, value);
if (is_hv_wireless_type(battery->cable_type)) {
int cnt;
mutex_lock(&battery->voutlock);
value.intval = (enable) ? WIRELESS_VOUT_5V :
battery->wpc_vout_level;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, value);
battery->aicl_current = 0; /* reset aicl current */
mutex_unlock(&battery->voutlock);
for (cnt = 0; cnt < 5; cnt++) {
msleep(100);
psy_do_property(battery->pdata->wireless_charger_name, get,
POWER_SUPPLY_PROP_ENERGY_NOW, value);
if (value.intval <= 6000) {
pr_info("%s: wireless vout goes to 5V Vout(%d).\n",
__func__, value.intval);
break;
}
}
} else if (is_nv_wireless_type(battery->cable_type)) {
union power_supply_propval value = {0, };
if (enable) {
pr_info("%s: wireless 5V with OTG\n", __func__);
value.intval = WIRELESS_VOUT_5V;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, value);
} else {
pr_info("%s: wireless 5.5V without OTG\n", __func__);
value.intval = WIRELESS_VOUT_CC_CV_VOUT;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, value);
}
} else if(battery->wc_tx_enable && enable) {
/* TX power should turn off during otg on */
pr_info("@Tx_Mode %s: OTG is going to work, TX power should off\n", __func__);
/* set tx event */
sec_bat_set_tx_event(battery, BATT_TX_EVENT_WIRELESS_TX_OTG_ON, BATT_TX_EVENT_WIRELESS_TX_OTG_ON);
sec_wireless_set_tx_enable(battery, false);
}
}
static int sec_wireless_set_property(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *val)
{
struct sec_battery_info *battery = power_supply_get_drvdata(psy);
enum power_supply_ext_property ext_psp = (enum power_supply_ext_property) psp;
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
#if defined(CONFIG_BATTERY_CISD)
if (val->intval != SEC_WIRELESS_PAD_NONE && battery->wc_status == SEC_WIRELESS_PAD_NONE) {
battery->cisd.data[CISD_DATA_WIRELESS_COUNT]++;
battery->cisd.data[CISD_DATA_WIRELESS_COUNT_PER_DAY]++;
}
#endif
pr_info("%s : wireless_type(0x%x)\n", __func__, val->intval);
/* Clear the FOD , AUTH State */
sec_bat_set_misc_event(battery, 0, BATT_MISC_EVENT_WIRELESS_FOD);
battery->wc_status = val->intval;
if ((battery->ext_event & BATT_EXT_EVENT_CALL) &&
(battery->wc_status == SEC_WIRELESS_PAD_WPC_PACK ||
battery->wc_status == SEC_WIRELESS_PAD_WPC_PACK_HV ||
battery->wc_status == SEC_WIRELESS_PAD_TX)) {
battery->wc_rx_phm_mode = true;
}
if (battery->wc_status == SEC_WIRELESS_PAD_NONE) {
battery->wpc_vout_level = WIRELESS_VOUT_10V;
battery->wpc_max_vout_level = WIRELESS_VOUT_12_5V;
battery->auto_mode = false;
if (delayed_work_pending(&battery->wc_headroom_work)) {
wake_unlock(&battery->wc_headroom_wake_lock);
cancel_delayed_work(&battery->wc_headroom_work);
}
sec_bat_set_misc_event(battery, 0,
(BATT_MISC_EVENT_WIRELESS_DET_LEVEL | /* clear wpc_det level status */
BATT_MISC_EVENT_WIRELESS_AUTH_START |
BATT_MISC_EVENT_WIRELESS_AUTH_RECVED |
BATT_MISC_EVENT_WIRELESS_AUTH_FAIL |
BATT_MISC_EVENT_WIRELESS_AUTH_PASS));
} else if (battery->wc_status != SEC_WIRELESS_PAD_FAKE) {
sec_bat_set_misc_event(battery, BATT_MISC_EVENT_WIRELESS_DET_LEVEL, /* set wpc_det level status */
BATT_MISC_EVENT_WIRELESS_DET_LEVEL);
if (battery->wc_status == SEC_WIRELESS_PAD_WPC_HV_20) {
sec_bat_set_misc_event(battery, BATT_MISC_EVENT_WIRELESS_AUTH_PASS,
BATT_MISC_EVENT_WIRELESS_AUTH_PASS);
}
}
wake_lock(&battery->cable_wake_lock);
queue_delayed_work(battery->monitor_wqueue,
&battery->cable_work, 0);
if (battery->wc_status == SEC_WIRELESS_PAD_NONE ||
battery->wc_status == SEC_WIRELESS_PAD_WPC_PACK ||
battery->wc_status == SEC_WIRELESS_PAD_WPC_PACK_HV ||
battery->wc_status == SEC_WIRELESS_PAD_VEHICLE) {
sec_bat_set_misc_event(battery,
(battery->wc_status == SEC_WIRELESS_PAD_NONE ? 0 : BATT_MISC_EVENT_WIRELESS_BACKPACK_TYPE),
BATT_MISC_EVENT_WIRELESS_BACKPACK_TYPE);
}
break;
case POWER_SUPPLY_PROP_AUTHENTIC:
#if defined(CONFIG_BATTERY_CISD)
pr_info("%s : tx_type(0x%x)\n", __func__, val->intval);
count_cisd_pad_data(&battery->cisd, val->intval);
#endif
break;
case POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL:
sec_wireless_otg_control(battery, val->intval);
break;
case POWER_SUPPLY_PROP_CURRENT_MAX:
battery->aicl_current = 0; /* reset aicl current */
pr_info("%s: reset aicl\n", __func__);
break;
case POWER_SUPPLY_PROP_MAX ... POWER_SUPPLY_EXT_PROP_MAX:
switch (ext_psp) {
case POWER_SUPPLY_EXT_PROP_WIRELESS_TX_ENABLE:
sec_wireless_set_tx_enable(battery, val->intval);
break;
case POWER_SUPPLY_EXT_PROP_WIRELESS_ERR:
if (is_wireless_type(battery->cable_type))
sec_bat_set_misc_event(battery, val->intval ? BATT_MISC_EVENT_WIRELESS_FOD : 0,
BATT_MISC_EVENT_WIRELESS_FOD);
break;
case POWER_SUPPLY_EXT_PROP_WIRELESS_TX_ERR:
if (val->intval & BATT_TX_EVENT_WIRELESS_TX_MISALIGN) {
sec_bat_handle_tx_misalign(battery, true);
} else {
sec_bat_set_tx_event(battery, val->intval, val->intval);
sec_wireless_set_tx_enable(battery, false);
}
break;
case POWER_SUPPLY_EXT_PROP_WIRELESS_RX_CONNECTED:
sec_bat_set_tx_event(battery, val->intval ? BATT_TX_EVENT_WIRELESS_RX_CONNECT : 0,
BATT_TX_EVENT_WIRELESS_RX_CONNECT);
battery->wc_rx_connected = val->intval;
if(!val->intval) {
battery->wc_rx_type = NO_DEV;
battery->tx_switch_mode = TX_SWITCH_MODE_OFF;
battery->tx_switch_mode_change = false;
battery->tx_switch_start_soc = 0;
if (battery->afc_disable) {
battery->afc_disable = false;
#if 0
muic_hv_charger_disable(battery->afc_disable);
#endif
}
if (battery->wc_tx_enable) {
pr_info("@Tx_Mode %s: Device detached.\n", __func__);
if (is_hv_wire_type(battery->wire_status)) {
pr_info("@Tx_Mode %s : charging voltage change(9V -> 5V).\n", __func__);
muic_afc_set_voltage(SEC_INPUT_VOLTAGE_5V/10);
break; /* do not set buck off/uno off untill vbus level get real 5V */
} else if (is_pd_wire_type(battery->wire_status) && battery->hv_pdo) {
pr_info("@Tx_Mode %s: PD charnge pdo (9V -> 5V). Because Tx Start.\n", __func__);
sec_bat_change_pdo(battery, SEC_INPUT_VOLTAGE_5V);
break; /* do not set buck off/uno off untill vbus level get real 5V */
}
if (!battery->buck_cntl_by_tx) {
battery->buck_cntl_by_tx = true;
sec_bat_set_charge(battery, battery->charger_mode);
}
sec_bat_wireless_iout_cntl(battery, battery->pdata->tx_uno_iout, battery->pdata->tx_mfc_iout_gear);
sec_bat_wireless_vout_cntl(battery, WC_TX_VOUT_5_0V);
sec_bat_wireless_minduty_cntl(battery, battery->pdata->tx_minduty_default);
}
}
break;
case POWER_SUPPLY_EXT_PROP_WIRELESS_RX_TYPE:
battery->wc_rx_type = val->intval;
cancel_delayed_work(&battery->wpc_tx_work);
wake_lock(&battery->wpc_tx_wake_lock);
queue_delayed_work(battery->monitor_wqueue,
&battery->wpc_tx_work, 0);
break;
case POWER_SUPPLY_EXT_PROP_WIRELESS_AUTH_ADT_STATUS:
if(val->intval == WIRELESS_AUTH_START)
sec_bat_set_misc_event(battery, BATT_MISC_EVENT_WIRELESS_AUTH_START, BATT_MISC_EVENT_WIRELESS_AUTH_START);
else if(val->intval == WIRELESS_AUTH_RECEIVED)
sec_bat_set_misc_event(battery, BATT_MISC_EVENT_WIRELESS_AUTH_RECVED, BATT_MISC_EVENT_WIRELESS_AUTH_RECVED);
else if(val->intval == WIRELESS_AUTH_SENT) /* need to be clear this value when data is sent */
sec_bat_set_misc_event(battery, 0, BATT_MISC_EVENT_WIRELESS_AUTH_START | BATT_MISC_EVENT_WIRELESS_AUTH_RECVED);
else if(val->intval == WIRELESS_AUTH_FAIL)
sec_bat_set_misc_event(battery, BATT_MISC_EVENT_WIRELESS_AUTH_FAIL, BATT_MISC_EVENT_WIRELESS_AUTH_FAIL);
break;
case POWER_SUPPLY_EXT_PROP_CALL_EVENT:
if(val->intval == 1) {
pr_info("%s : PHM enabled\n",__func__);
battery->wc_rx_phm_mode = true;
}
break;
case POWER_SUPPLY_PROP_WIRELESS_RX_POWER:
pr_info("%s : rx power %d\n", __func__, val->intval);
sec_bat_set_wireless20_current(battery, val->intval);
#if defined(CONFIG_CALC_TIME_TO_FULL)
sec_bat_predict_wireless20_time_to_full_current(battery, val->intval);
#endif
break;
case POWER_SUPPLY_PROP_WIRELESS_MAX_VOUT:
pr_info("%s : max vout %s\n", __func__, vout_control_mode_str[val->intval]);
battery->wpc_max_vout_level = val->intval;
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
}
return 0;
}
static int sec_ps_set_property(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *val)
{
struct sec_battery_info *battery = power_supply_get_drvdata(psy);
union power_supply_propval value;
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
if (val->intval == 0 && battery->ps_enable == true) {
battery->ps_enable = false;
value.intval = val->intval;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL, value);
} else if ((val->intval == 1) && (battery->ps_enable == false) &&
(battery->ps_status == true)) {
battery->ps_enable = true;
value.intval = val->intval;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL, value);
} else {
dev_err(battery->dev,
"%s: invalid setting (%d)\n", __func__, val->intval);
}
break;
case POWER_SUPPLY_PROP_ONLINE:
if (val->intval == SEC_BATTERY_CABLE_POWER_SHARING) {
battery->ps_status = true;
battery->ps_enable = true;
value.intval = battery->ps_enable;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL, value);
} else {
battery->ps_status = false;
battery->ps_enable = false;
value.intval = battery->ps_enable;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL, value);
}
break;
default:
return -EINVAL;
}
return 0;
}
static int sec_ps_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct sec_battery_info *battery = power_supply_get_drvdata(psy);
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
val->intval = (battery->ps_enable) ? 1 : 0;
break;
case POWER_SUPPLY_PROP_ONLINE:
val->intval = (battery->ps_status) ? 1 : 0;
break;
default:
return -EINVAL;
}
return 0;
}
#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER) || defined(CONFIG_MUIC_NOTIFIER)
static int sec_bat_cable_check(struct sec_battery_info *battery,
muic_attached_dev_t attached_dev)
{
int current_cable_type = -1;
union power_supply_propval val = {0, };
psy_do_property(battery->pdata->charger_name, get,
POWER_SUPPLY_PROP_MODEL_NAME, val);
pr_info("[%s]ATTACHED(%d) - model type(%d)\n",
__func__, attached_dev, val.intval);
switch (attached_dev)
{
case ATTACHED_DEV_JIG_UART_OFF_MUIC:
case ATTACHED_DEV_JIG_UART_ON_MUIC:
battery->is_jig_on = true;
#if defined(CONFIG_BATTERY_CISD)
battery->skip_cisd = true;
#endif
current_cable_type = SEC_BATTERY_CABLE_NONE;
break;
case ATTACHED_DEV_SMARTDOCK_MUIC:
case ATTACHED_DEV_DESKDOCK_MUIC:
case ATTACHED_DEV_JIG_USB_ON_MUIC:
current_cable_type = SEC_BATTERY_CABLE_NONE;
break;
case ATTACHED_DEV_UNDEFINED_CHARGING_MUIC:
case ATTACHED_DEV_UNDEFINED_RANGE_MUIC:
current_cable_type = SEC_BATTERY_CABLE_NONE;
break;
case ATTACHED_DEV_HICCUP_MUIC:
current_cable_type = SEC_BATTERY_CABLE_NONE;
break;
case ATTACHED_DEV_OTG_MUIC:
case ATTACHED_DEV_JIG_UART_OFF_VB_OTG_MUIC:
case ATTACHED_DEV_HMT_MUIC:
current_cable_type = SEC_BATTERY_CABLE_OTG;
break;
case ATTACHED_DEV_TIMEOUT_OPEN_MUIC:
current_cable_type = SEC_BATTERY_CABLE_TIMEOUT;
break;
case ATTACHED_DEV_USB_MUIC:
case ATTACHED_DEV_JIG_USB_OFF_MUIC:
case ATTACHED_DEV_SMARTDOCK_USB_MUIC:
case ATTACHED_DEV_UNOFFICIAL_ID_USB_MUIC:
current_cable_type = SEC_BATTERY_CABLE_USB;
break;
case ATTACHED_DEV_JIG_UART_OFF_VB_MUIC:
if (val.intval == IC_TYPE_IFPMIC_S2MU106) {
current_cable_type = SEC_BATTERY_CABLE_NONE;
break;
}
case ATTACHED_DEV_JIG_UART_ON_VB_MUIC:
case ATTACHED_DEV_JIG_UART_OFF_VB_FG_MUIC:
current_cable_type = (battery->factory_mode_boot_on) ?
SEC_BATTERY_CABLE_NONE : SEC_BATTERY_CABLE_UARTOFF;
if (battery->block_water_event) {
current_cable_type = SEC_BATTERY_CABLE_UARTOFF;
break;
}
break;
case ATTACHED_DEV_RDU_TA_MUIC:
battery->store_mode = true;
wake_lock(&battery->parse_mode_dt_wake_lock);
queue_delayed_work(battery->monitor_wqueue, &battery->parse_mode_dt_work, 0);
current_cable_type = SEC_BATTERY_CABLE_TA;
break;
case ATTACHED_DEV_TA_MUIC:
case ATTACHED_DEV_CARDOCK_MUIC:
case ATTACHED_DEV_DESKDOCK_VB_MUIC:
case ATTACHED_DEV_SMARTDOCK_TA_MUIC:
case ATTACHED_DEV_UNOFFICIAL_TA_MUIC:
case ATTACHED_DEV_UNOFFICIAL_ID_TA_MUIC:
case ATTACHED_DEV_UNOFFICIAL_ID_ANY_MUIC:
case ATTACHED_DEV_UNSUPPORTED_ID_VB_MUIC:
case ATTACHED_DEV_AFC_CHARGER_DISABLED_MUIC:
#if defined(CONFIG_MUIC_HV_SUPPORT_POGO_DOCK)
case ATTACHED_DEV_POGO_DOCK_MUIC:
case ATTACHED_DEV_POGO_DOCK_5V_MUIC:
#endif
current_cable_type = SEC_BATTERY_CABLE_TA;
break;
case ATTACHED_DEV_AFC_CHARGER_5V_MUIC:
case ATTACHED_DEV_QC_CHARGER_5V_MUIC:
case ATTACHED_DEV_AFC_CHARGER_5V_DUPLI_MUIC:
current_cable_type = SEC_BATTERY_CABLE_HV_TA_CHG_LIMIT;
break;
case ATTACHED_DEV_CDP_MUIC:
case ATTACHED_DEV_UNOFFICIAL_ID_CDP_MUIC:
current_cable_type = SEC_BATTERY_CABLE_USB_CDP;
break;
case ATTACHED_DEV_USB_LANHUB_MUIC:
current_cable_type = SEC_BATTERY_CABLE_LAN_HUB;
break;
case ATTACHED_DEV_CHARGING_CABLE_MUIC:
current_cable_type = SEC_BATTERY_CABLE_POWER_SHARING;
break;
case ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC:
case ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC:
current_cable_type = SEC_BATTERY_CABLE_PREPARE_TA;
break;
case ATTACHED_DEV_QC_CHARGER_9V_MUIC:
current_cable_type = SEC_BATTERY_CABLE_9V_TA;
#if defined(CONFIG_BATTERY_CISD)
if ((battery->cable_type == SEC_BATTERY_CABLE_TA) ||
(battery->cable_type == SEC_BATTERY_CABLE_NONE))
battery->cisd.cable_data[CISD_CABLE_QC]++;
#endif
break;
case ATTACHED_DEV_AFC_CHARGER_9V_MUIC:
case ATTACHED_DEV_AFC_CHARGER_9V_DUPLI_MUIC:
#if defined(CONFIG_MUIC_HV_SUPPORT_POGO_DOCK)
case ATTACHED_DEV_POGO_DOCK_9V_MUIC:
#endif
current_cable_type = SEC_BATTERY_CABLE_9V_TA;
#if defined(CONFIG_BATTERY_CISD)
if ((battery->cable_type == SEC_BATTERY_CABLE_TA) ||
(battery->cable_type == SEC_BATTERY_CABLE_NONE))
battery->cisd.cable_data[CISD_CABLE_AFC]++;
#endif
break;
#if defined(CONFIG_MUIC_HV_12V)
case ATTACHED_DEV_AFC_CHARGER_12V_MUIC:
case ATTACHED_DEV_AFC_CHARGER_12V_DUPLI_MUIC:
current_cable_type = SEC_BATTERY_CABLE_12V_TA;
break;
#endif
case ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC:
case ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC:
#if defined(CONFIG_BATTERY_CISD)
battery->cisd.cable_data[CISD_CABLE_AFC_FAIL]++;
#endif
break;
case ATTACHED_DEV_QC_CHARGER_ERR_V_MUIC:
#if defined(CONFIG_BATTERY_CISD)
battery->cisd.cable_data[CISD_CABLE_QC_FAIL]++;
#endif
break;
case ATTACHED_DEV_HV_ID_ERR_UNDEFINED_MUIC:
case ATTACHED_DEV_HV_ID_ERR_UNSUPPORTED_MUIC:
case ATTACHED_DEV_HV_ID_ERR_SUPPORTED_MUIC:
current_cable_type = SEC_BATTERY_CABLE_9V_UNKNOWN;
break;
case ATTACHED_DEV_VZW_INCOMPATIBLE_MUIC:
current_cable_type = SEC_BATTERY_CABLE_UNKNOWN;
break;
default:
pr_err("%s: invalid type for charger:%d\n",
__func__, attached_dev);
break;
}
if (battery->is_jig_on && !battery->pdata->support_fgsrc_change)
psy_do_property(battery->pdata->fuelgauge_name, set,
POWER_SUPPLY_PROP_ENERGY_NOW, val);
if (val.intval == IC_TYPE_IFPMIC_S2MU106) {
switch (attached_dev) {
case ATTACHED_DEV_JIG_USB_ON_MUIC:
case ATTACHED_DEV_JIG_UART_OFF_VB_MUIC:
val.intval = 1;
if (!battery->factory_mode_boot_on)
factory_mode = 1;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_ENERGY_NOW, val);
pr_err("%s : FACTORY MODE TEST! (%d, %d)\n", __func__, val.intval,
battery->factory_mode_boot_on);
break;
case ATTACHED_DEV_JIG_UART_ON_VB_MUIC:
val.intval = 0;
if (!battery->factory_mode_boot_on)
factory_mode = 0;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_ENERGY_NOW, val);
pr_err("%s : FACTORY MODE TEST! (%d, %d)\n", __func__, val.intval,
battery->factory_mode_boot_on);
break;
case ATTACHED_DEV_JIG_UART_OFF_MUIC:
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_EXT_PROP_ENABLE_HW_FACTORY_MODE, val);
pr_err("%s : HW FACTORY MODE ENABLE TEST! (%d)\n", __func__, val.intval);
break;
default:
break;
}
}
return current_cable_type;
}
#endif
#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)
#if defined(CONFIG_CCIC_NOTIFIER)
static int sec_bat_get_pd_list_index(PDIC_SINK_STATUS *sink_status, struct sec_bat_pdic_list *pd_list)
{
int i = 0;
for (i = 0; i < pd_list->max_pd_count; i++) {
if (pd_list->pd_info[i].pdo_index == sink_status->current_pdo_num)
return i;
}
return 0;
}
static void sec_bat_set_rp_current(struct sec_battery_info *battery, int cable_type)
{
if (battery->pdic_info.sink_status.rp_currentlvl == RP_CURRENT_ABNORMAL) {
sec_bat_change_default_current(battery, cable_type,
battery->pdata->rp_current_abnormal_rp3, battery->pdata->rp_current_abnormal_rp3);
} else if (battery->pdic_info.sink_status.rp_currentlvl == RP_CURRENT_LEVEL3) {
if (battery->current_event & SEC_BAT_CURRENT_EVENT_HV_DISABLE)
sec_bat_change_default_current(battery, cable_type,
battery->pdata->default_input_current, battery->pdata->default_charging_current);
else {
if(battery->store_mode)
sec_bat_change_default_current(battery, cable_type,
battery->pdata->rp_current_rdu_rp3, battery->pdata->max_charging_current);
else
sec_bat_change_default_current(battery, cable_type,
battery->pdata->rp_current_rp3, battery->pdata->max_charging_current);
}
} else if (battery->pdic_info.sink_status.rp_currentlvl == RP_CURRENT_LEVEL2) {
sec_bat_change_default_current(battery, cable_type,
battery->pdata->rp_current_rp2, battery->pdata->rp_current_rp2);
} else if (battery->pdic_info.sink_status.rp_currentlvl == RP_CURRENT_LEVEL_DEFAULT) {
if (cable_type == SEC_BATTERY_CABLE_USB) {
if (battery->current_event & SEC_BAT_CURRENT_EVENT_USB_SUPER)
sec_bat_change_default_current(battery, SEC_BATTERY_CABLE_USB,
USB_CURRENT_SUPER_SPEED, USB_CURRENT_SUPER_SPEED);
else
sec_bat_change_default_current(battery, cable_type,
battery->pdata->default_usb_input_current,
battery->pdata->default_usb_charging_current);
} else if (cable_type == SEC_BATTERY_CABLE_TA) {
sec_bat_change_default_current(battery, cable_type,
battery->pdata->default_input_current,
battery->pdata->default_charging_current);
}
}
battery->aicl_current = 0;
pr_info("%s:(%d)\n", __func__, battery->pdic_info.sink_status.rp_currentlvl);
battery->max_charge_power = 0;
if (battery->status != POWER_SUPPLY_STATUS_DISCHARGING)
sec_bat_check_input_voltage(battery);
/* prevent TA ocp */
if(!is_hv_wireless_type(battery->cable_type) &&
battery->cable_type != SEC_BATTERY_CABLE_PREPARE_WIRELESS_20)
sec_bat_set_charging_current(battery);
}
#endif
static int make_pd_list(struct sec_battery_info *battery)
{
int i = 0;
int base_charge_power = 0, selected_pdo_voltage = 0, selected_pdo_power = 0, selected_pdo_num = 0;
int pd_list_index = 0, temp_power = 0, num_pd_list = 0, pd_list_select = 0;
int pd_charging_charge_power = battery->current_event & SEC_BAT_CURRENT_EVENT_HV_DISABLE ?
battery->pdata->nv_charge_power : battery->pdata->pd_charging_charge_power;
union power_supply_propval value = {0, };
POWER_LIST* pPower_list;
/* If PD charger is attached first, current_pdo_num should be 1 supports 5V */
battery->pd_list.pd_info[0].input_voltage =
battery->pdic_info.sink_status.power_list[1].max_voltage;
battery->pd_list.pd_info[0].input_current =
battery->pdic_info.sink_status.power_list[1].max_current;
battery->pd_list.pd_info[0].pdo_index = 1;
pd_list_index++;
base_charge_power =
battery->pdic_info.sink_status.power_list[1].max_voltage * battery->pdic_info.sink_status.power_list[1].max_current;
selected_pdo_voltage = SEC_INPUT_VOLTAGE_5V * 1000;
selected_pdo_power = 0;
selected_pdo_num = 0;
for (i = 1; i <= battery->pdic_info.sink_status.available_pdo_num; i++)
{
pPower_list = &battery->pdic_info.sink_status.power_list[i];
temp_power = pPower_list->max_voltage * pPower_list->max_current;
if ((temp_power >= base_charge_power - 1000000) && (temp_power <= pd_charging_charge_power * 1000))
{
if (temp_power >= selected_pdo_power &&
pPower_list->max_voltage > selected_pdo_voltage && pPower_list->max_voltage <= battery->pdata->max_input_voltage)
{
selected_pdo_voltage = pPower_list->max_voltage;
selected_pdo_power = temp_power;
selected_pdo_num = i;
}
}
}
if (selected_pdo_num)
{
POWER_LIST* pSelected_power_list =
&battery->pdic_info.sink_status.power_list[selected_pdo_num];
battery->pd_list.pd_info[pd_list_index].pdo_index = selected_pdo_num;
battery->pd_list.pd_info[pd_list_index].input_voltage = pSelected_power_list->max_voltage;
battery->pd_list.pd_info[pd_list_index].input_current = pSelected_power_list->max_current;
pd_list_index++;
}
battery->pd_list.num_fpdo = pd_list_index;
num_pd_list = pd_list_index;
if (num_pd_list <= 0) {
pr_info("%s : PDO list is empty!!\n", __func__);
return 0;
} else {
pr_info("%s: total num_pd_list: %d\n", __func__, num_pd_list);
}
pd_list_select = num_pd_list - 1;
for (i = 0; i < num_pd_list; i++) {
pr_info("%s: Made pd_list[%d],%s[%d,FIXED] voltage : %d, current : %d\n",
__func__, i, i == pd_list_select ? "**" : " ",
battery->pd_list.pd_info[i].pdo_index,
battery->pd_list.pd_info[i].input_voltage,
battery->pd_list.pd_info[i].input_current);
}
battery->pd_list.max_pd_count = num_pd_list;
battery->max_charge_power = battery->pdic_info.sink_status.power_list[ \
battery->pd_list.pd_info[pd_list_select].pdo_index].max_voltage * \
battery->pdic_info.sink_status.power_list[battery->pd_list.pd_info[ \
pd_list_select].pdo_index].max_current / 1000;
battery->pd_max_charge_power = battery->max_charge_power;
#if defined(CONFIG_BATTERY_CISD)
if (battery->cable_type == SEC_BATTERY_CABLE_NONE) {
if (battery->pd_max_charge_power > 12000)
battery->cisd.cable_data[CISD_CABLE_PD_HIGH]++;
else
battery->cisd.cable_data[CISD_CABLE_PD]++;
}
#endif
if (battery->pdic_info.sink_status.selected_pdo_num == battery->pd_list.pd_info[pd_list_select].pdo_index) {
battery->pdic_ps_rdy = true;
dev_info(battery->dev, "%s: battery->pdic_ps_rdy(%d)\n", __func__, battery->pdic_ps_rdy);
} else if (battery->wc_rx_type != SS_GEAR) {
/* change input current before request new pdo if new pdo's input current is less than now */
if (battery->pd_list.pd_info[pd_list_select].input_current < battery->input_current) {
int input_current = battery->pd_list.pd_info[pd_list_select].input_current;
value.intval = input_current;
battery->input_current = input_current;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CURRENT_MAX, value);
}
battery->pdic_ps_rdy = false;
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_SELECT_PDO,
SEC_BAT_CURRENT_EVENT_SELECT_PDO);
select_pdo(battery->pd_list.pd_info[pd_list_select].pdo_index);
}
battery->pd_list.now_pd_index = sec_bat_get_pd_list_index(&battery->pdic_info.sink_status,
&battery->pd_list);
pr_info("%s : now_pd_index : %d\n", __func__, battery->pd_list.now_pd_index);
return battery->pd_list.max_pd_count;
}
static int usb_typec_handle_notification(struct notifier_block *nb,
unsigned long action, void *data)
{
const char *cmd = "NONE";
struct sec_battery_info *battery =
container_of(nb, struct sec_battery_info, usb_typec_nb);
int cable_type = SEC_BATTERY_CABLE_NONE, i = 0, current_pdo = 0;
int pd_charging_charge_power = battery->current_event & SEC_BAT_CURRENT_EVENT_HV_DISABLE ?
battery->pdata->nv_charge_power : battery->pdata->pd_charging_charge_power;
CC_NOTI_ATTACH_TYPEDEF usb_typec_info = *(CC_NOTI_ATTACH_TYPEDEF *)data;
bool bPdIndexChanged = false;
int max_power = 0;
dev_info(battery->dev, "%s: action (%ld) dump(0x%01x, 0x%01x, 0x%02x, 0x%04x, 0x%04x, 0x%04x)\n",
__func__, action, usb_typec_info.src, usb_typec_info.dest, usb_typec_info.id,
usb_typec_info.attach, usb_typec_info.rprd, usb_typec_info.cable_type);
if (usb_typec_info.dest != CCIC_NOTIFY_DEV_BATTERY) {
dev_info(battery->dev, "%s: skip handler dest(%d)\n",
__func__, usb_typec_info.dest);
return 0;
}
mutex_lock(&battery->typec_notylock);
switch (usb_typec_info.id) {
case CCIC_NOTIFY_ID_WATER:
case CCIC_NOTIFY_ID_ATTACH:
switch (usb_typec_info.attach) {
case MUIC_NOTIFY_CMD_DETACH:
case MUIC_NOTIFY_CMD_LOGICALLY_DETACH:
cmd = "DETACH";
battery->is_jig_on = false;
battery->pd_usb_attached = false;
cable_type = SEC_BATTERY_CABLE_NONE;
battery->muic_cable_type = ATTACHED_DEV_NONE_MUIC;
battery->pdic_info.sink_status.rp_currentlvl = RP_CURRENT_LEVEL_NONE;
break;
case MUIC_NOTIFY_CMD_ATTACH:
case MUIC_NOTIFY_CMD_LOGICALLY_ATTACH:
/* Skip notify from MUIC if PDIC is attached already */
if (is_pd_wire_type(battery->wire_status) || battery->init_src_cap) {
if (lpcharge) {
mutex_unlock(&battery->typec_notylock);
return 0;
} else if (!battery->usb_temp_flag && !(battery->misc_event & BATT_MISC_EVENT_TEMP_HICCUP_TYPE)) {
mutex_unlock(&battery->typec_notylock);
return 0;
}
}
cmd = "ATTACH";
battery->muic_cable_type = usb_typec_info.cable_type;
cable_type = sec_bat_cable_check(battery, battery->muic_cable_type);
if (battery->cable_type != cable_type &&
battery->pdic_info.sink_status.rp_currentlvl >= RP_CURRENT_LEVEL_DEFAULT &&
(cable_type == SEC_BATTERY_CABLE_USB || cable_type == SEC_BATTERY_CABLE_TA)) {
sec_bat_set_rp_current(battery, cable_type);
} else if ((struct pdic_notifier_struct *)usb_typec_info.pd != NULL &&
(*(struct pdic_notifier_struct *)usb_typec_info.pd).event == PDIC_NOTIFY_EVENT_CCIC_ATTACH &&
(*(struct pdic_notifier_struct *)usb_typec_info.pd).sink_status.rp_currentlvl >= RP_CURRENT_LEVEL_DEFAULT &&
(cable_type == SEC_BATTERY_CABLE_USB || cable_type == SEC_BATTERY_CABLE_TA)) {
battery->pdic_info.sink_status.rp_currentlvl =
(*(struct pdic_notifier_struct *)usb_typec_info.pd).sink_status.rp_currentlvl;
sec_bat_set_rp_current(battery, cable_type);
}
break;
default:
cmd = "ERROR";
cable_type = -1;
battery->muic_cable_type = usb_typec_info.cable_type;
break;
}
battery->pdic_attach = false;
battery->pdic_ps_rdy = false;
battery->init_src_cap = false;
#if defined(CONFIG_AFC_CHARGER_MODE)
if (battery->muic_cable_type == ATTACHED_DEV_QC_CHARGER_9V_MUIC ||
battery->muic_cable_type == ATTACHED_DEV_QC_CHARGER_ERR_V_MUIC)
battery->hv_chg_name = "QC";
else if (battery->muic_cable_type == ATTACHED_DEV_AFC_CHARGER_9V_MUIC ||
battery->muic_cable_type == ATTACHED_DEV_AFC_CHARGER_9V_DUPLI_MUIC ||
battery->muic_cable_type == ATTACHED_DEV_AFC_CHARGER_ERR_V_MUIC ||
battery->muic_cable_type == ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC)
battery->hv_chg_name = "AFC";
#if defined(CONFIG_MUIC_HV_12V)
else if (battery->muic_cable_type == ATTACHED_DEV_AFC_CHARGER_12V_MUIC ||
battery->muic_cable_type == ATTACHED_DEV_AFC_CHARGER_12V_DUPLI_MUIC)
battery->hv_chg_name = "12V";
#endif
else
battery->hv_chg_name = "NONE";
#endif
break;
case CCIC_NOTIFY_ID_POWER_STATUS:
#ifdef CONFIG_SEC_FACTORY
dev_info(battery->dev, "%s: pd_event(%d)\n", __func__,
(*(struct pdic_notifier_struct *)usb_typec_info.pd).event);
#endif
battery->init_src_cap = false;
if ((*(struct pdic_notifier_struct *)usb_typec_info.pd).event == PDIC_NOTIFY_EVENT_DETACH) {
dev_info(battery->dev, "%s: skip pd operation - attach(%d)\n", __func__, usb_typec_info.attach);
battery->pdic_attach = false;
battery->pdic_ps_rdy = false;
battery->hv_pdo = false;
battery->pd_list.now_pd_index = 0;
battery->pd_list.num_fpdo = 0;
mutex_unlock(&battery->typec_notylock);
return 0;
} else if ((*(struct pdic_notifier_struct *)usb_typec_info.pd).event == PDIC_NOTIFY_EVENT_PD_PRSWAP_SNKTOSRC) {
cmd = "PD_PRWAP";
dev_info(battery->dev, "%s: PRSWAP_SNKTOSRC(%d)\n", __func__, usb_typec_info.attach);
cable_type = SEC_BATTERY_CABLE_NONE;
battery->pdic_attach = false;
battery->pdic_ps_rdy = false;
battery->hv_pdo = false;
battery->pd_list.now_pd_index = 0;
goto skip_cable_check;
} else if (!lpcharge && (battery->usb_temp_flag || (battery->misc_event & BATT_MISC_EVENT_TEMP_HICCUP_TYPE))) {
goto skip_cable_check;
}
cmd = "PD_ATTACH";
if ((*(struct pdic_notifier_struct *)usb_typec_info.pd).event == PDIC_NOTIFY_EVENT_CCIC_ATTACH) {
battery->pdic_info.sink_status.rp_currentlvl =
(*(struct pdic_notifier_struct *)usb_typec_info.pd).sink_status.rp_currentlvl;
dev_info(battery->dev, "%s: battery->rp_currentlvl(%d)\n", __func__, battery->pdic_info.sink_status.rp_currentlvl);
if (battery->wire_status == SEC_BATTERY_CABLE_USB || battery->wire_status == SEC_BATTERY_CABLE_TA) {
cable_type = battery->wire_status;
battery->chg_limit = false;
sec_bat_set_rp_current(battery, cable_type);
goto skip_cable_check;
}
mutex_unlock(&battery->typec_notylock);
return 0;
}
if ((*(struct pdic_notifier_struct *)usb_typec_info.pd).event == PDIC_NOTIFY_EVENT_PD_SINK_CAP ||
battery->update_pd_list) {
pr_info("%s : update_pd_list(%d)\n", __func__, battery->update_pd_list);
battery->pdic_attach = false;
battery->update_pd_list = false;
}
if (!battery->pdic_attach) {
battery->pdic_info = *(struct pdic_notifier_struct *)usb_typec_info.pd;
battery->pd_list.now_pd_index = 0;
battery->hv_pdo = false;
bPdIndexChanged = true;
} else {
unsigned int prev_pd_index = battery->pd_list.now_pd_index;
battery->pdic_info.sink_status.selected_pdo_num =
(*(struct pdic_notifier_struct *)usb_typec_info.pd).sink_status.selected_pdo_num;
battery->pdic_info.sink_status.current_pdo_num =
(*(struct pdic_notifier_struct *)usb_typec_info.pd).sink_status.current_pdo_num;
battery->pd_list.now_pd_index = sec_bat_get_pd_list_index(&battery->pdic_info.sink_status,
&battery->pd_list);
dev_info(battery->dev, "%s: battery->pd_list.now_pd_index(%d), prev_pd_index(%d)\n",
__func__, battery->pd_list.now_pd_index, prev_pd_index);
if (battery->pd_list.now_pd_index != prev_pd_index) {
bPdIndexChanged = true;
}
if (battery->pd_list.now_pd_index > 0)
battery->hv_pdo = true;
else
battery->hv_pdo = false;
battery->pdic_ps_rdy = true;
dev_info(battery->dev, "%s: battery->pdic_ps_rdy(%d), hv_pdo(%d)\n",
__func__, battery->pdic_ps_rdy, battery->hv_pdo);
}
current_pdo = battery->pdic_info.sink_status.current_pdo_num;
cable_type = SEC_BATTERY_CABLE_PDIC;
#if defined(CONFIG_AFC_CHARGER_MODE)
battery->hv_chg_name = "PDIC";
#endif
battery->muic_cable_type = ATTACHED_DEV_NONE_MUIC;
battery->input_voltage =
battery->pdic_info.sink_status.power_list[current_pdo].max_voltage / 1000;
dev_info(battery->dev, "%s: available pdo : %d, current pdo : %d\n", __func__,
battery->pdic_info.sink_status.available_pdo_num, current_pdo);
for(i=1; i<= battery->pdic_info.sink_status.available_pdo_num; i++) {
bool isUpdated = false;
if (!battery->pdic_attach &&
(battery->pdic_info.sink_status.power_list[i].max_voltage *
battery->pdic_info.sink_status.power_list[i].max_current) > max_power) {
max_power = battery->pdic_info.sink_status.power_list[i].max_voltage *
battery->pdic_info.sink_status.power_list[i].max_current / 1000;
pr_info("%s: max_power = %dmW\n", __func__, max_power);
}
pr_info("%s:%spower_list[%d], voltage : %d, current : %d, power : %d\n",
__func__, i == current_pdo ? "**" : " ", i,
battery->pdic_info.sink_status.power_list[i].max_voltage,
battery->pdic_info.sink_status.power_list[i].max_current,
battery->pdic_info.sink_status.power_list[i].max_voltage *
battery->pdic_info.sink_status.power_list[i].max_current);
if ((battery->pdic_info.sink_status.power_list[i].max_voltage *
battery->pdic_info.sink_status.power_list[i].max_current) >
(pd_charging_charge_power * 1000)) {
battery->pdic_info.sink_status.power_list[i].max_current =
(pd_charging_charge_power * 1000) /
battery->pdic_info.sink_status.power_list[i].max_voltage;
isUpdated = true;
}
if(battery->pdic_info.sink_status.power_list[i].max_current >
battery->pdata->max_input_current) {
isUpdated = true;
battery->pdic_info.sink_status.power_list[i].max_current =
battery->pdata->max_input_current;
}
if (isUpdated) {
pr_info("%s: ->updated [%d], voltage : %d, current : %d, power : %d\n", __func__, i,
battery->pdic_info.sink_status.power_list[i].max_voltage,
battery->pdic_info.sink_status.power_list[i].max_current,
battery->pdic_info.sink_status.power_list[i].max_voltage *
battery->pdic_info.sink_status.power_list[i].max_current);
}
}
if (!battery->pdic_attach) {
if (make_pd_list(battery) <= 0)
goto skip_cable_work;
}
battery->pdic_attach = true;
break;
case CCIC_NOTIFY_ID_USB:
if(usb_typec_info.cable_type == PD_USB_TYPE)
battery->pd_usb_attached = true;
dev_info(battery->dev, "%s: CCIC_NOTIFY_ID_USB: %d\n",__func__, battery->pd_usb_attached);
wake_lock(&battery->monitor_wake_lock);
queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
mutex_unlock(&battery->typec_notylock);
return 0;
default:
cmd = "ERROR";
cable_type = -1;
battery->muic_cable_type = ATTACHED_DEV_NONE_MUIC;
#if defined(CONFIG_AFC_CHARGER_MODE)
battery->hv_chg_name = "NONE";
#endif
break;
}
skip_cable_check:
sec_bat_set_misc_event(battery,
(battery->muic_cable_type == ATTACHED_DEV_UNDEFINED_CHARGING_MUIC ? BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE : 0) |
(battery->muic_cable_type == ATTACHED_DEV_UNDEFINED_RANGE_MUIC ? BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE : 0),
BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE);
if (battery->muic_cable_type == ATTACHED_DEV_HICCUP_MUIC) {
if (battery->usb_temp_flag || (battery->misc_event & BATT_MISC_EVENT_TEMP_HICCUP_TYPE)) {
pr_info("%s: Hiccup Set because of USB Temp\n", __func__);
sec_bat_set_misc_event(battery, BATT_MISC_EVENT_TEMP_HICCUP_TYPE, BATT_MISC_EVENT_TEMP_HICCUP_TYPE);
battery->usb_temp_flag = false;
} else {
pr_info("%s: Hiccup Set because of Water detect\n", __func__);
sec_bat_set_misc_event(battery, BATT_MISC_EVENT_HICCUP_TYPE, BATT_MISC_EVENT_HICCUP_TYPE);
}
battery->hiccup_status = 1;
} else {
battery->hiccup_status = 0;
if (battery->hiccup_clear) {
sec_bat_set_misc_event(battery,
0, (BATT_MISC_EVENT_HICCUP_TYPE | BATT_MISC_EVENT_TEMP_HICCUP_TYPE));
battery->hiccup_clear = false;
pr_info("%s : Hiccup event clear! hiccup clear bit set (%d)\n", __func__, battery->hiccup_clear);
} else if (battery->misc_event & (BATT_MISC_EVENT_HICCUP_TYPE | BATT_MISC_EVENT_TEMP_HICCUP_TYPE)) {
wake_lock(&battery->monitor_wake_lock);
queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
}
}
/* showing charging icon and noti(no sound, vi, haptic) only
if slow insertion is detected by MUIC */
sec_bat_set_misc_event(battery,
(battery->muic_cable_type == ATTACHED_DEV_TIMEOUT_OPEN_MUIC ? BATT_MISC_EVENT_TIMEOUT_OPEN_TYPE : 0),
BATT_MISC_EVENT_TIMEOUT_OPEN_TYPE);
if (cable_type < 0 || cable_type > SEC_BATTERY_CABLE_MAX) {
dev_info(battery->dev, "%s: ignore event(%d)\n",
__func__, battery->muic_cable_type);
goto skip_cable_work;
} else if ((cable_type == SEC_BATTERY_CABLE_UNKNOWN) &&
(battery->status != POWER_SUPPLY_STATUS_DISCHARGING)) {
battery->cable_type = cable_type;
wake_lock(&battery->monitor_wake_lock);
queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
dev_info(battery->dev, "%s: UNKNOWN cable plugin\n", __func__);
goto skip_cable_work;
}
battery->wire_status = cable_type;
if (battery->wc_tx_enable) {
if (battery->wire_status == SEC_BATTERY_CABLE_NONE) {
battery->buck_cntl_by_tx = true;
battery->tx_switch_mode = TX_SWITCH_MODE_OFF;
battery->tx_switch_mode_change = false;
battery->tx_switch_start_soc = 0;
}
cancel_delayed_work(&battery->wpc_tx_work);
wake_lock(&battery->wpc_tx_wake_lock);
queue_delayed_work(battery->monitor_wqueue,
&battery->wpc_tx_work, 0);
}
cancel_delayed_work(&battery->cable_work);
wake_unlock(&battery->cable_wake_lock);
if (cable_type == SEC_BATTERY_CABLE_HV_TA_CHG_LIMIT) {
/* set current event */
cancel_delayed_work(&battery->afc_work);
wake_unlock(&battery->afc_wake_lock);
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_CHG_LIMIT,
(SEC_BAT_CURRENT_EVENT_CHG_LIMIT | SEC_BAT_CURRENT_EVENT_AFC));
wake_lock(&battery->monitor_wake_lock);
battery->polling_count = 1; /* initial value = 1 */
queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
} else if ((battery->wire_status == battery->cable_type) &&
(((battery->wire_status == SEC_BATTERY_CABLE_USB || battery->wire_status == SEC_BATTERY_CABLE_TA) &&
battery->pdic_info.sink_status.rp_currentlvl > RP_CURRENT_LEVEL_DEFAULT &&
!(battery->current_event & SEC_BAT_CURRENT_EVENT_AFC)) ||
is_hv_wire_type(battery->wire_status))) {
cancel_delayed_work(&battery->afc_work);
wake_unlock(&battery->afc_wake_lock);
sec_bat_set_current_event(battery, 0, SEC_BAT_CURRENT_EVENT_AFC);
wake_lock(&battery->monitor_wake_lock);
battery->polling_count = 1; /* initial value = 1 */
queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
} else if (cable_type == SEC_BATTERY_CABLE_PREPARE_TA) {
cancel_delayed_work(&battery->afc_work);
wake_unlock(&battery->afc_wake_lock);
sec_bat_set_current_event(battery,
SEC_BAT_CURRENT_EVENT_AFC, SEC_BAT_CURRENT_EVENT_AFC);
sec_bat_set_charging_current(battery);
} else {
wake_lock(&battery->cable_wake_lock);
if (battery->ta_alert_wa && battery->ta_alert_mode != OCP_NONE) {
if (!strcmp(cmd, "DETACH")) {
queue_delayed_work(battery->monitor_wqueue,
&battery->cable_work, msecs_to_jiffies(3000));
} else {
queue_delayed_work(battery->monitor_wqueue,
&battery->cable_work, 0);
}
} else {
queue_delayed_work(battery->monitor_wqueue,
&battery->cable_work, 0);
}
}
skip_cable_work:
dev_info(battery->dev, "%s: CMD[%s], CABLE_TYPE[%d]\n", __func__, cmd, cable_type);
mutex_unlock(&battery->typec_notylock);
return 0;
}
#else
#if defined(CONFIG_CCIC_NOTIFIER)
static int batt_pdic_handle_notification(struct notifier_block *nb,
unsigned long action, void *data)
{
const char *cmd;
struct sec_battery_info *battery =
container_of(nb, struct sec_battery_info,
pdic_nb);
battery->pdic_info = *(struct pdic_notifier_struct *)data;
mutex_lock(&battery->batt_handlelock);
pr_info("%s: pdic_event: %d\n", __func__, battery->pdic_info.event);
switch (battery->pdic_info.event) {
int i, selected_pdo;
case PDIC_NOTIFY_EVENT_DETACH:
cmd = "DETACH";
battery->pdic_attach = false;
battery->init_src_cap = false;
if (battery->wire_status == SEC_BATTERY_CABLE_PDIC) {
battery->wire_status = SEC_BATTERY_CABLE_NONE;
wake_lock(&battery->cable_wake_lock);
queue_delayed_work(battery->monitor_wqueue,
&battery->cable_work, 0);
}
break;
case PDIC_NOTIFY_EVENT_CCIC_ATTACH:
cmd = "ATTACH";
break;
case PDIC_NOTIFY_EVENT_PD_SINK:
selected_pdo = battery->pdic_info.sink_status.selected_pdo_num;
cmd = "ATTACH";
battery->wire_status = SEC_BATTERY_CABLE_PDIC;
battery->pdic_attach = true;
battery->input_voltage =
battery->pdic_info.sink_status.power_list[selected_pdo].max_voltage / 1000;
pr_info("%s: total pdo : %d, selected pdo : %d\n", __func__,
battery->pdic_info.sink_status.available_pdo_num, selected_pdo);
for(i=1; i<= battery->pdic_info.sink_status.available_pdo_num; i++)
{
pr_info("%s: power_list[%d], voltage : %d, current : %d, power : %d\n", __func__, i,
battery->pdic_info.sink_status.power_list[i].max_voltage,
battery->pdic_info.sink_status.power_list[i].max_current,
battery->pdic_info.sink_status.power_list[i].max_voltage *
battery->pdic_info.sink_status.power_list[i].max_current);
}
wake_lock(&battery->cable_wake_lock);
queue_delayed_work(battery->monitor_wqueue,
&battery->cable_work, 0);
break;
case PDIC_NOTIFY_EVENT_PD_SOURCE:
cmd = "ATTACH";
break;
default:
cmd = "ERROR";
break;
}
pr_info("%s: CMD=%s, cable_type : %d\n", __func__, cmd, battery->cable_type);
mutex_unlock(&battery->batt_handlelock);
return 0;
}
#endif
#if defined(CONFIG_MUIC_NOTIFIER)
static int batt_handle_notification(struct notifier_block *nb,
unsigned long action, void *data)
{
const char *cmd;
int cable_type = SEC_BATTERY_CABLE_NONE;
struct sec_battery_info *battery =
container_of(nb, struct sec_battery_info,
batt_nb);
union power_supply_propval value = {0, };
#if defined(CONFIG_CCIC_NOTIFIER)
CC_NOTI_ATTACH_TYPEDEF *p_noti = (CC_NOTI_ATTACH_TYPEDEF *)data;
muic_attached_dev_t attached_dev = p_noti->cable_type;
#else
muic_attached_dev_t attached_dev = *(muic_attached_dev_t *)data;
#endif
unsigned int block_water_event = 0;
mutex_lock(&battery->batt_handlelock);
switch (action) {
case MUIC_NOTIFY_CMD_DETACH:
case MUIC_NOTIFY_CMD_LOGICALLY_DETACH:
cmd = "DETACH";
battery->is_jig_on = false;
cable_type = SEC_BATTERY_CABLE_NONE;
battery->muic_cable_type = ATTACHED_DEV_NONE_MUIC;
break;
case MUIC_NOTIFY_CMD_ATTACH:
case MUIC_NOTIFY_CMD_LOGICALLY_ATTACH:
cmd = "ATTACH";
cable_type = sec_bat_cable_check(battery, attached_dev);
battery->muic_cable_type = attached_dev;
break;
default:
cmd = "ERROR";
cable_type = -1;
battery->muic_cable_type = ATTACHED_DEV_NONE_MUIC;
break;
}
#if !defined(CONFIG_ENG_BATTERY_CONCEPT) && !defined(CONFIG_SEC_FACTORY)
if (battery->pdata->enable_water_resistance) {
if (!battery->block_water_event)
block_water_event =
(battery->muic_cable_type == ATTACHED_DEV_JIG_UART_ON_MUIC ? BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE : 0) |
(battery->muic_cable_type == ATTACHED_DEV_JIG_USB_ON_MUIC ? BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE : 0);
}
#endif
block_water_event |= (battery->muic_cable_type == ATTACHED_DEV_UNDEFINED_RANGE_MUIC ? BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE : 0);
sec_bat_set_misc_event(battery, block_water_event, BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE);
if (battery->muic_cable_type == ATTACHED_DEV_HICCUP_MUIC) {
if (battery->usb_temp_flag || (battery->misc_event & BATT_MISC_EVENT_TEMP_HICCUP_TYPE)) {
pr_info("%s: Hiccup Set because of USB Temp\n", __func__);
sec_bat_set_misc_event(battery, BATT_MISC_EVENT_TEMP_HICCUP_TYPE, BATT_MISC_EVENT_TEMP_HICCUP_TYPE);
battery->usb_temp_flag = false;
} else {
pr_info("%s: Hiccup Set because of Water detect\n", __func__);
sec_bat_set_misc_event(battery, BATT_MISC_EVENT_HICCUP_TYPE, BATT_MISC_EVENT_HICCUP_TYPE);
}
battery->hiccup_status = 1;
} else {
battery->hiccup_status = 0;
if (battery->misc_event & (BATT_MISC_EVENT_HICCUP_TYPE | BATT_MISC_EVENT_TEMP_HICCUP_TYPE)) {
wake_lock(&battery->monitor_wake_lock);
queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
}
}
#if defined(CONFIG_CCIC_NOTIFIER)
/* If PD cable is already attached, return this function */
if(battery->pdic_attach) {
dev_info(battery->dev, "%s: ignore event pdic attached(%d)\n",
__func__, battery->pdic_attach);
mutex_unlock(&battery->batt_handlelock);
return 0;
}
#endif
if (attached_dev == ATTACHED_DEV_MHL_MUIC) {
mutex_unlock(&battery->batt_handlelock);
return 0;
}
if (cable_type < 0) {
dev_info(battery->dev, "%s: ignore event(%d)\n",
__func__, cable_type);
} else if (cable_type == SEC_BATTERY_CABLE_POWER_SHARING) {
battery->ps_status = true;
battery->ps_enable = true;
battery->wire_status = cable_type;
dev_info(battery->dev, "%s: power sharing cable plugin\n", __func__);
} else if (cable_type == SEC_BATTERY_CABLE_WIRELESS) {
battery->wc_status = SEC_WIRELESS_PAD_WPC;
} else if (cable_type == SEC_BATTERY_CABLE_WIRELESS_PACK) {
battery->wc_status = SEC_WIRELESS_PAD_WPC_PACK;
} else if (cable_type == SEC_BATTERY_CABLE_WIRELESS_HV_PACK) {
battery->wc_status = SEC_WIRELESS_PAD_WPC_PACK_HV;
} else if (cable_type == SEC_BATTERY_CABLE_HV_WIRELESS) {
battery->wc_status = SEC_WIRELESS_PAD_WPC_HV;
} else if (cable_type == SEC_BATTERY_CABLE_WIRELESS_STAND) {
battery->wc_status = SEC_WIRELESS_PAD_WPC_STAND;
} else if (cable_type == SEC_BATTERY_CABLE_WIRELESS_HV_STAND) {
battery->wc_status = SEC_WIRELESS_PAD_WPC_STAND_HV;
} else if (cable_type == SEC_BATTERY_CABLE_PMA_WIRELESS) {
battery->wc_status = SEC_WIRELESS_PAD_PMA;
} else if (cable_type == SEC_BATTERY_CABLE_WIRELESS_VEHICLE) {
battery->wc_status = SEC_WIRELESS_PAD_VEHICLE;
} else if (cable_type == SEC_BATTERY_CABLE_WIRELESS_HV_VEHICLE) {
battery->wc_status = SEC_WIRELESS_PAD_VEHICLE_HV;
} else if (cable_type == SEC_BATTERY_CABLE_WIRELESS_TX) {
battery->wc_status = SEC_WIRELESS_PAD_TX;
} else if (cable_type == SEC_BATTERY_CABLE_PREPARE_WIRELESS_20) {
battery->wc_status = SEC_WIRELESS_PAD_WPC_PREPARE_HV_20;
} else if (cable_type == SEC_BATTERY_CABLE_HV_WIRELESS_20) {
battery->wc_status = SEC_WIRELESS_PAD_WPC_HV_20;
} else if ((cable_type == SEC_BATTERY_CABLE_UNKNOWN) &&
(battery->status != POWER_SUPPLY_STATUS_DISCHARGING)) {
battery->cable_type = cable_type;
wake_lock(&battery->monitor_wake_lock);
queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
dev_info(battery->dev,
"%s: UNKNOWN cable plugin\n", __func__);
mutex_unlock(&battery->batt_handlelock);
return 0;
} else {
battery->wire_status = cable_type;
if (is_nocharge_type(battery->wire_status) &&
(battery->wc_status) && (!battery->ps_status))
cable_type = SEC_BATTERY_CABLE_WIRELESS;
}
dev_info(battery->dev,
"%s: current_cable(%d), wc_status(%d), wire_status(%d)\n",
__func__, cable_type, battery->wc_status,
battery->wire_status);
mutex_unlock(&battery->batt_handlelock);
if (attached_dev == ATTACHED_DEV_USB_LANHUB_MUIC) {
if (!strcmp(cmd, "ATTACH")) {
value.intval = true;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CHARGE_POWERED_OTG_CONTROL,
value);
dev_info(battery->dev,
"%s: Powered OTG cable attached\n", __func__);
} else {
value.intval = false;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CHARGE_POWERED_OTG_CONTROL,
value);
dev_info(battery->dev,
"%s: Powered OTG cable detached\n", __func__);
}
}
#if defined(CONFIG_AFC_CHARGER_MODE)
if (!strcmp(cmd, "ATTACH")) {
if ((battery->muic_cable_type >= ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC) &&
(battery->muic_cable_type <= ATTACHED_DEV_QC_CHARGER_9V_MUIC)) {
battery->hv_chg_name = "QC";
} else if ((battery->muic_cable_type >= ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC) &&
(battery->muic_cable_type <= ATTACHED_DEV_AFC_CHARGER_ERR_V_DUPLI_MUIC)) {
battery->hv_chg_name = "AFC";
#if defined(CONFIG_MUIC_HV_12V)
} else if (battery->muic_cable_type == ATTACHED_DEV_AFC_CHARGER_12V_MUIC ||
battery->muic_cable_type == ATTACHED_DEV_AFC_CHARGER_12V_DUPLI_MUIC) {
battery->hv_chg_name = "12V";
#endif
} else
battery->hv_chg_name = "NONE";
} else {
battery->hv_chg_name = "NONE";
}
pr_info("%s : HV_CHARGER_NAME(%s)\n",
__func__, battery->hv_chg_name);
#endif
if ((cable_type >= 0) &&
cable_type <= SEC_BATTERY_CABLE_MAX) {
if (cable_type == SEC_BATTERY_CABLE_POWER_SHARING) {
value.intval = battery->ps_enable;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL, value);
wake_lock(&battery->monitor_wake_lock);
queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
} else if((cable_type == SEC_BATTERY_CABLE_NONE) && (battery->ps_status)) {
if (battery->ps_enable) {
battery->ps_enable = false;
value.intval = battery->ps_enable;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL, value);
}
battery->ps_status = false;
wake_lock(&battery->monitor_wake_lock);
queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
} else if(cable_type != battery->cable_type) {
wake_lock(&battery->cable_wake_lock);
queue_delayed_work(battery->monitor_wqueue,
&battery->cable_work, 0);
} else {
dev_info(battery->dev,
"%s: Cable is Not Changed(%d)\n",
__func__, battery->cable_type);
}
}
pr_info("%s: CMD=%s, attached_dev=%d\n", __func__, cmd, attached_dev);
return 0;
}
#endif /* CONFIG_MUIC_NOTIFIER */
#endif
#if defined(CONFIG_VBUS_NOTIFIER)
static int vbus_handle_notification(struct notifier_block *nb,
unsigned long action, void *data)
{
vbus_status_t vbus_status = *(vbus_status_t *)data;
struct sec_battery_info *battery =
container_of(nb, struct sec_battery_info,
vbus_nb);
union power_supply_propval value = {0, };
mutex_lock(&battery->batt_handlelock);
if (battery->muic_cable_type == ATTACHED_DEV_HMT_MUIC &&
battery->muic_vbus_status != vbus_status &&
battery->muic_vbus_status == STATUS_VBUS_HIGH &&
vbus_status == STATUS_VBUS_LOW) {
msleep(500);
value.intval = true;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL,
value);
dev_info(battery->dev,
"%s: changed to OTG cable attached\n", __func__);
battery->wire_status = SEC_BATTERY_CABLE_OTG;
wake_lock(&battery->cable_wake_lock);
queue_delayed_work(battery->monitor_wqueue, &battery->cable_work, 0);
}
pr_info("%s: action=%d, vbus_status=%d\n", __func__, (int)action, vbus_status);
mutex_unlock(&battery->batt_handlelock);
battery->muic_vbus_status = vbus_status;
return 0;
}
#endif
#if !defined(CONFIG_MUIC_NOTIFIER)
static void cable_initial_check(struct sec_battery_info *battery)
{
union power_supply_propval value;
pr_info("%s : current_cable_type : (%d)\n", __func__, battery->cable_type);
if (SEC_BATTERY_CABLE_NONE != battery->cable_type) {
if (battery->cable_type == SEC_BATTERY_CABLE_POWER_SHARING) {
value.intval = battery->cable_type;
psy_do_property("ps", set,
POWER_SUPPLY_PROP_ONLINE, value);
} else {
value.intval = battery->cable_type;
psy_do_property("battery", set,
POWER_SUPPLY_PROP_ONLINE, value);
}
} else {
psy_do_property(battery->pdata->charger_name, get,
POWER_SUPPLY_PROP_ONLINE, value);
if (value.intval == SEC_BATTERY_CABLE_WIRELESS) {
value.intval = 1;
psy_do_property("wireless", set,
POWER_SUPPLY_PROP_ONLINE, value);
}
}
}
#endif
static void sec_bat_init_chg_work(struct work_struct *work)
{
struct sec_battery_info *battery = container_of(work,
struct sec_battery_info, init_chg_work.work);
if (battery->cable_type == SEC_BATTERY_CABLE_NONE &&
!(battery->misc_event & (BATT_MISC_EVENT_UNDEFINED_RANGE_TYPE |
BATT_MISC_EVENT_HICCUP_TYPE | BATT_MISC_EVENT_TEMP_HICCUP_TYPE))) {
pr_info("%s: disable charging\n", __func__);
sec_bat_set_charge(battery, SEC_BAT_CHG_MODE_CHARGING_OFF);
}
}
static const struct power_supply_desc battery_power_supply_desc = {
.name = "battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
.properties = sec_battery_props,
.num_properties = ARRAY_SIZE(sec_battery_props),
.get_property = sec_bat_get_property,
.set_property = sec_bat_set_property,
};
static const struct power_supply_desc usb_power_supply_desc = {
.name = "usb",
.type = POWER_SUPPLY_TYPE_USB,
.properties = sec_power_props,
.num_properties = ARRAY_SIZE(sec_power_props),
.get_property = sec_usb_get_property,
};
static const struct power_supply_desc ac_power_supply_desc = {
.name = "ac",
.type = POWER_SUPPLY_TYPE_MAINS,
.properties = sec_ac_props,
.num_properties = ARRAY_SIZE(sec_ac_props),
.get_property = sec_ac_get_property,
};
static const struct power_supply_desc wireless_power_supply_desc = {
.name = "wireless",
.type = POWER_SUPPLY_TYPE_WIRELESS,
.properties = sec_wireless_props,
.num_properties = ARRAY_SIZE(sec_wireless_props),
.get_property = sec_wireless_get_property,
.set_property = sec_wireless_set_property,
};
static const struct power_supply_desc ps_power_supply_desc = {
.name = "ps",
.type = POWER_SUPPLY_TYPE_POWER_SHARING,
.properties = sec_ps_props,
.num_properties = ARRAY_SIZE(sec_ps_props),
.get_property = sec_ps_get_property,
.set_property = sec_ps_set_property,
};
static int sec_battery_probe(struct platform_device *pdev)
{
sec_battery_platform_data_t *pdata = NULL;
struct sec_battery_info *battery;
struct power_supply_config battery_cfg = {};
int ret = 0;
#ifndef CONFIG_OF
int i = 0;
#endif
union power_supply_propval value = {0, };
dev_info(&pdev->dev,
"%s: SEC Battery Driver Loading\n", __func__);
battery = kzalloc(sizeof(*battery), GFP_KERNEL);
if (!battery)
return -ENOMEM;
if (pdev->dev.of_node) {
pdata = devm_kzalloc(&pdev->dev,
sizeof(sec_battery_platform_data_t),
GFP_KERNEL);
if (!pdata) {
dev_err(&pdev->dev, "Failed to allocate memory\n");
ret = -ENOMEM;
goto err_bat_free;
}
battery->pdata = pdata;
if (sec_bat_parse_dt(&pdev->dev, battery)) {
dev_err(&pdev->dev,
"%s: Failed to get battery dt\n", __func__);
ret = -EINVAL;
goto err_bat_free;
}
} else {
pdata = dev_get_platdata(&pdev->dev);
battery->pdata = pdata;
}
platform_set_drvdata(pdev, battery);
battery->dev = &pdev->dev;
mutex_init(&battery->adclock);
mutex_init(&battery->iolock);
mutex_init(&battery->misclock);
mutex_init(&battery->txeventlock);
mutex_init(&battery->batt_handlelock);
mutex_init(&battery->current_eventlock);
mutex_init(&battery->typec_notylock);
mutex_init(&battery->wclock);
mutex_init(&battery->voutlock);
dev_dbg(battery->dev, "%s: ADC init\n", __func__);
#ifdef CONFIG_OF
adc_init(pdev, battery);
#else
for (i = 0; i < SEC_BAT_ADC_CHANNEL_NUM; i++)
adc_init(pdev, pdata, i);
#endif
wake_lock_init(&battery->monitor_wake_lock, WAKE_LOCK_SUSPEND,
"sec-battery-monitor");
wake_lock_init(&battery->cable_wake_lock, WAKE_LOCK_SUSPEND,
"sec-battery-cable");
wake_lock_init(&battery->vbus_wake_lock, WAKE_LOCK_SUSPEND,
"sec-battery-vbus");
wake_lock_init(&battery->afc_wake_lock, WAKE_LOCK_SUSPEND,
"sec-battery-afc");
wake_lock_init(&battery->siop_level_wake_lock, WAKE_LOCK_SUSPEND,
"sec-battery-siop_level");
wake_lock_init(&battery->ext_event_wake_lock, WAKE_LOCK_SUSPEND,
"sec-battery-ext_event");
wake_lock_init(&battery->wc_headroom_wake_lock, WAKE_LOCK_SUSPEND,
"sec-battery-wc_headroom");
wake_lock_init(&battery->wpc_tx_wake_lock, WAKE_LOCK_SUSPEND,
"sec-battery-wcp-tx");
#if defined(CONFIG_UPDATE_BATTERY_DATA)
wake_lock_init(&battery->batt_data_wake_lock, WAKE_LOCK_SUSPEND,
"sec-battery-update-data");
#endif
wake_lock_init(&battery->misc_event_wake_lock, WAKE_LOCK_SUSPEND,
"sec-battery-misc-event");
wake_lock_init(&battery->tx_event_wake_lock, WAKE_LOCK_SUSPEND,
"sec-battery-tx-event");
#ifdef CONFIG_OF
wake_lock_init(&battery->parse_mode_dt_wake_lock, WAKE_LOCK_SUSPEND,
"sec-battery-parse_mode_dt");
#endif
/* initialization of battery info */
sec_bat_set_charging_status(battery,
POWER_SUPPLY_STATUS_DISCHARGING);
battery->health = POWER_SUPPLY_HEALTH_GOOD;
battery->ta_alert_mode = OCP_NONE;
battery->present = true;
battery->is_jig_on = false;
battery->wdt_kick_disable = 0;
battery->polling_count = 1; /* initial value = 1 */
battery->polling_time = pdata->polling_time[
SEC_BATTERY_POLLING_TIME_DISCHARGING];
battery->polling_in_sleep = false;
battery->polling_short = false;
battery->check_count = 0;
battery->check_adc_count = 0;
battery->check_adc_value = 0;
battery->input_current = 0;
battery->charging_current = 0;
battery->topoff_current = 0;
battery->wpc_vout_level = WIRELESS_VOUT_10V;
battery->wpc_max_vout_level = WIRELESS_VOUT_12_5V;
battery->charging_start_time = 0;
battery->charging_passed_time = 0;
battery->wc_heating_start_time = 0;
battery->wc_heating_passed_time = 0;
battery->charging_next_time = 0;
battery->charging_fullcharged_time = 0;
battery->siop_level = 100;
battery->wc_enable = 1;
battery->wc_enable_cnt = 0;
battery->wc_enable_cnt_value = 3;
#if defined(CONFIG_ENG_BATTERY_CONCEPT)
battery->stability_test = 0;
battery->eng_not_full_status = 0;
battery->temperature_test_battery = 0x7FFF;
battery->temperature_test_usb = 0x7FFF;
battery->temperature_test_wpc = 0x7FFF;
battery->temperature_test_chg = 0x7FFF;
battery->test_max_current = false;
battery->test_charge_current = false;
#if defined(CONFIG_STEP_CHARGING)
battery->test_step_condition = 0x7FFF;
#endif
#endif
battery->ps_enable = false;
battery->wc_status = SEC_WIRELESS_PAD_NONE;
battery->wc_cv_mode = false;
battery->wire_status = SEC_BATTERY_CABLE_NONE;
battery->wc_rx_phm_mode = false;
battery->wc_tx_enable = false;
battery->uno_en = false;
battery->afc_disable = false;
battery->buck_cntl_by_tx = false;
battery->wc_tx_vout = WC_TX_VOUT_5_0V;
battery->wc_rx_type = NO_DEV;
battery->tx_mfc_iout = 0;
battery->tx_uno_iout = 0;
battery->wc_need_ldo_on = false;
battery->tx_minduty = battery->pdata->tx_minduty_default;
#if defined(CONFIG_WIRELESS_TX_MODE)
battery->tx_clear = true;
battery->tx_clear_cisd = true;
#endif
#if defined(CONFIG_BATTERY_SWELLING)
battery->swelling_mode = SWELLING_MODE_NONE;
#endif
battery->charging_block = false;
battery->chg_limit = false;
battery->mix_limit = false;
battery->vbus_limit = false;
battery->vbus_chg_by_siop = SEC_INPUT_VOLTAGE_0V;
battery->vbus_chg_by_full = false;
battery->usb_temp = 0;
#if defined(CONFIG_ENG_BATTERY_CONCEPT) || defined(CONFIG_SEC_FACTORY)
battery->cooldown_mode = true;
#endif
battery->skip_swelling = false;
battery->led_cover = 0;
battery->hiccup_status = 0;
battery->hiccup_clear = false;
battery->ext_event = BATT_EXT_EVENT_NONE;
battery->tx_retry_case = SEC_BAT_TX_RETRY_NONE;
battery->tx_misalign_cnt = 0;
battery->auto_mode = false;
battery->update_pd_list = false;
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_USB_100MA, SEC_BAT_CURRENT_EVENT_USB_100MA);
if (lpcharge) {
battery->temp_highlimit_threshold =
battery->pdata->temp_highlimit_threshold_lpm;
battery->temp_highlimit_recovery =
battery->pdata->temp_highlimit_recovery_lpm;
battery->temp_high_threshold =
battery->pdata->temp_high_threshold_lpm;
battery->temp_high_recovery =
battery->pdata->temp_high_recovery_lpm;
battery->temp_low_recovery =
battery->pdata->temp_low_recovery_lpm;
battery->temp_low_threshold =
battery->pdata->temp_low_threshold_lpm;
} else {
battery->temp_highlimit_threshold =
battery->pdata->temp_highlimit_threshold_normal;
battery->temp_highlimit_recovery =
battery->pdata->temp_highlimit_recovery_normal;
battery->temp_high_threshold =
battery->pdata->temp_high_threshold_normal;
battery->temp_high_recovery =
battery->pdata->temp_high_recovery_normal;
battery->temp_low_recovery =
battery->pdata->temp_low_recovery_normal;
battery->temp_low_threshold =
battery->pdata->temp_low_threshold_normal;
}
battery->charging_mode = SEC_BATTERY_CHARGING_NONE;
battery->is_recharging = false;
battery->cable_type = SEC_BATTERY_CABLE_NONE;
battery->test_mode = 0;
battery->factory_mode = false;
battery->store_mode = false;
battery->prev_usb_conf = USB_CURRENT_NONE;
battery->is_hc_usb = false;
battery->is_sysovlo = false;
battery->is_vbatovlo = false;
battery->is_abnormal_temp = false;
battery->hv_pdo = false;
battery->safety_timer_set = true;
battery->stop_timer = false;
battery->prev_safety_time = 0;
battery->lcd_status = false;
battery->wc20_power_class = 0;
#if defined(CONFIG_CALC_TIME_TO_FULL)
battery->ttf_predict_wc20_charge_current = 0;
#endif
battery->wc20_vout = 0;
#if defined(CONFIG_BATTERY_CISD)
battery->usb_overheat_check = false;
battery->skip_cisd = false;
#endif
#if defined(CONFIG_BATTERY_AGE_FORECAST)
battery->batt_cycle = -1;
battery->pdata->age_step = 0;
#endif
battery->batt_asoc = 100;
battery->health_change = false;
battery->usb_temp_flag = false;
/* Check High Voltage charging option for wireless charging */
/* '1' means disabling High Voltage charging */
if (charging_night_mode == '1')
sleep_mode = true;
else
sleep_mode = false;
if (factory_mode)
battery->factory_mode_boot_on = true;
else
battery->factory_mode_boot_on = false;
/* '1' means disable usb temp check & high temp/highlimit temp */
if (temp_control_test == '1')
sec_bat_set_temp_control_test(battery, true);
else
sec_bat_set_temp_control_test(battery, false);
/* Check High Voltage charging option for wired charging */
if (get_afc_mode() == CH_MODE_AFC_DISABLE_VAL) {
pr_info("HV wired charging mode is disabled\n");
sec_bat_set_current_event(battery,
SEC_BAT_CURRENT_EVENT_HV_DISABLE, SEC_BAT_CURRENT_EVENT_HV_DISABLE);
}
if(fg_reset)
sec_bat_set_current_event(battery, SEC_BAT_CURRENT_EVENT_FG_RESET,
SEC_BAT_CURRENT_EVENT_FG_RESET);
battery->pdata->store_mode_charging_max = STORE_MODE_CHARGING_MAX;
battery->pdata->store_mode_charging_min = STORE_MODE_CHARGING_MIN;
#if !defined(CONFIG_SEC_FACTORY)
if (sales_code_is("VZW")) {
dev_err(battery->dev, "%s: Sales is VZW\n", __func__);
battery->pdata->store_mode_charging_max = STORE_MODE_CHARGING_MAX_VZW;
battery->pdata->store_mode_charging_min = STORE_MODE_CHARGING_MIN_VZW;
}
#endif
#if defined(CONFIG_CALC_TIME_TO_FULL)
battery->timetofull = -1;
#endif
#if !defined(CONFIG_ENG_BATTERY_CONCEPT) && !defined(CONFIG_SEC_FACTORY)
battery->block_water_event = (battery->pdata->enable_water_resistance) ? 0 : 1;
pr_info("%s: init block_water_event = %d\n", __func__, battery->block_water_event);
#endif
if (battery->pdata->charger_name == NULL)
battery->pdata->charger_name = "sec-charger";
if (battery->pdata->fuelgauge_name == NULL)
battery->pdata->fuelgauge_name = "sec-fuelgauge";
/* create work queue */
battery->monitor_wqueue =
create_singlethread_workqueue(dev_name(&pdev->dev));
if (!battery->monitor_wqueue) {
dev_err(battery->dev,
"%s: Fail to Create Workqueue\n", __func__);
goto err_irq;
}
INIT_DELAYED_WORK(&battery->monitor_work, sec_bat_monitor_work);
INIT_DELAYED_WORK(&battery->cable_work, sec_bat_cable_work);
INIT_DELAYED_WORK(&battery->wpc_tx_work, sec_bat_wpc_tx_work);
#if defined(CONFIG_CALC_TIME_TO_FULL)
INIT_DELAYED_WORK(&battery->timetofull_work, sec_bat_time_to_full_work);
#endif
#if defined(CONFIG_WIRELESS_TX_MODE)
INIT_DELAYED_WORK(&battery->wpc_txpower_calc_work, sec_bat_txpower_calc_work);
#endif
#if defined(CONFIG_ENABLE_100MA_CHARGING_BEFORE_USB_CONFIGURED)
INIT_DELAYED_WORK(&battery->slowcharging_work, sec_bat_check_slowcharging_work);
#endif
INIT_DELAYED_WORK(&battery->afc_work, sec_bat_afc_work);
INIT_DELAYED_WORK(&battery->ext_event_work, sec_bat_ext_event_work);
INIT_DELAYED_WORK(&battery->siop_level_work, sec_bat_siop_level_work);
INIT_DELAYED_WORK(&battery->wc_headroom_work, sec_bat_wc_headroom_work);
#if defined(CONFIG_WIRELESS_FIRMWARE_UPDATE)
INIT_DELAYED_WORK(&battery->fw_init_work, sec_bat_fw_init_work);
#endif
#if defined(CONFIG_UPDATE_BATTERY_DATA)
INIT_DELAYED_WORK(&battery->batt_data_work, sec_bat_update_data_work);
#endif
INIT_DELAYED_WORK(&battery->misc_event_work, sec_bat_misc_event_work);
#ifdef CONFIG_OF
INIT_DELAYED_WORK(&battery->parse_mode_dt_work, sec_bat_parse_mode_dt_work);
#endif
INIT_DELAYED_WORK(&battery->init_chg_work, sec_bat_init_chg_work);
switch (pdata->polling_type) {
case SEC_BATTERY_MONITOR_WORKQUEUE:
INIT_DELAYED_WORK(&battery->polling_work,
sec_bat_polling_work);
break;
case SEC_BATTERY_MONITOR_ALARM:
battery->last_poll_time = ktime_get_boottime();
alarm_init(&battery->polling_alarm, ALARM_BOOTTIME,
sec_bat_alarm);
break;
default:
break;
}
#if defined(CONFIG_BATTERY_CISD)
sec_battery_cisd_init(battery);
#endif
/* updates temperatures on boot */
sec_bat_get_temperature_info(battery);
battery_cfg.drv_data = battery;
/* init power supplier framework */
battery->psy_ps = power_supply_register(&pdev->dev, &ps_power_supply_desc, &battery_cfg);
if (!battery->psy_ps) {
dev_err(battery->dev,
"%s: Failed to Register psy_ps\n", __func__);
goto err_workqueue;
}
battery->psy_ps->supplied_to = supply_list;
battery->psy_ps->num_supplicants = ARRAY_SIZE(supply_list);
battery->psy_usb = power_supply_register(&pdev->dev, &usb_power_supply_desc, &battery_cfg);
if (!battery->psy_usb) {
dev_err(battery->dev,
"%s: Failed to Register psy_usb\n", __func__);
goto err_supply_unreg_ps;
}
battery->psy_usb->supplied_to = supply_list;
battery->psy_usb->num_supplicants = ARRAY_SIZE(supply_list);
battery->psy_ac = power_supply_register(&pdev->dev, &ac_power_supply_desc, &battery_cfg);
if (!battery->psy_ac) {
dev_err(battery->dev,
"%s: Failed to Register psy_ac\n", __func__);
goto err_supply_unreg_usb;
}
battery->psy_ac->supplied_to = supply_list;
battery->psy_ac->num_supplicants = ARRAY_SIZE(supply_list);
battery->psy_bat = power_supply_register(&pdev->dev, &battery_power_supply_desc, &battery_cfg);
if (!battery->psy_bat) {
dev_err(battery->dev,
"%s: Failed to Register psy_bat\n", __func__);
goto err_supply_unreg_ac;
}
battery->psy_wireless = power_supply_register(&pdev->dev, &wireless_power_supply_desc, &battery_cfg);
if (!battery->psy_wireless) {
dev_err(battery->dev,
"%s: Failed to Register psy_wireless\n", __func__);
goto err_supply_unreg_bat;
}
battery->psy_wireless->supplied_to = supply_list;
battery->psy_wireless->num_supplicants = ARRAY_SIZE(supply_list);
ret = sec_bat_create_attrs(&battery->psy_bat->dev);
if (ret) {
dev_err(battery->dev,
"%s : Failed to create_attrs\n", __func__);
goto err_req_irq;
}
/* initialize battery level*/
value.intval = 0;
psy_do_property(battery->pdata->fuelgauge_name, get,
POWER_SUPPLY_PROP_CAPACITY, value);
battery->capacity = value.intval;
#if defined(CONFIG_WIRELESS_FIRMWARE_UPDATE)
queue_delayed_work(battery->monitor_wqueue, &battery->fw_init_work, msecs_to_jiffies(2000));
#endif
/* notify wireless charger driver when sec_battery probe is done,
if wireless charging is possible, POWER_SUPPLY_PROP_ONLINE of wireless property will be called. */
value.intval = 0;
psy_do_property(battery->pdata->wireless_charger_name, set,
POWER_SUPPLY_PROP_CHARGE_TYPE, value);
#if defined(CONFIG_STORE_MODE) && !defined(CONFIG_SEC_FACTORY)
battery->store_mode = true;
sec_bat_parse_mode_dt(battery);
#endif
#if defined(CONFIG_USB_TYPEC_MANAGER_NOTIFIER)
battery->pdic_info.sink_status.rp_currentlvl = RP_CURRENT_LEVEL_NONE;
manager_notifier_register(&battery->usb_typec_nb,
usb_typec_handle_notification, MANAGER_NOTIFY_CCIC_BATTERY);
#else
#if defined(CONFIG_MUIC_NOTIFIER)
muic_notifier_register(&battery->batt_nb,
batt_handle_notification, MUIC_NOTIFY_DEV_CHARGER);
#else
cable_initial_check(battery);
#endif
#if defined(CONFIG_CCIC_NOTIFIER)
pr_info("%s: Registering PDIC_NOTIFY.\n", __func__);
pdic_notifier_register(&battery->pdic_nb,
batt_pdic_handle_notification, PDIC_NOTIFY_DEV_BATTERY);
#endif
#endif
#if defined(CONFIG_VBUS_NOTIFIER)
vbus_notifier_register(&battery->vbus_nb,
vbus_handle_notification, VBUS_NOTIFY_DEV_CHARGER);
#endif
#if defined(CONFIG_WIRELESS_AUTH)
sec_bat_misc_init(battery);
#endif
value.intval = true;
psy_do_property(battery->pdata->charger_name, set,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, value);
/* make fg_reset true again for actual normal booting after recovery kernel is done */
if (fg_reset == 2) {
psy_do_property(battery->pdata->fuelgauge_name, set,
POWER_SUPPLY_PROP_ENERGY_NOW, value);
pr_info("%s: make fg_reset true again for actual normal booting\n", __func__);
}
if ((battery->cable_type == SEC_BATTERY_CABLE_NONE) ||
(battery->cable_type == SEC_BATTERY_CABLE_PREPARE_TA)) {
queue_delayed_work(battery->monitor_wqueue, &battery->init_chg_work, 0);
dev_info(&pdev->dev,
"%s: SEC Battery Driver Monitorwork\n", __func__);
wake_lock(&battery->monitor_wake_lock);
queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
}
if (battery->pdata->check_battery_callback)
battery->present = battery->pdata->check_battery_callback();
dev_info(battery->dev,
"%s: SEC Battery Driver Loaded\n", __func__);
return 0;
err_req_irq:
if (battery->pdata->bat_irq)
free_irq(battery->pdata->bat_irq, battery);
power_supply_unregister(battery->psy_wireless);
err_supply_unreg_bat:
power_supply_unregister(battery->psy_bat);
err_supply_unreg_ac:
power_supply_unregister(battery->psy_ac);
err_supply_unreg_usb:
power_supply_unregister(battery->psy_usb);
err_supply_unreg_ps:
power_supply_unregister(battery->psy_ps);
err_workqueue:
destroy_workqueue(battery->monitor_wqueue);
err_irq:
wake_lock_destroy(&battery->monitor_wake_lock);
wake_lock_destroy(&battery->cable_wake_lock);
wake_lock_destroy(&battery->vbus_wake_lock);
wake_lock_destroy(&battery->afc_wake_lock);
wake_lock_destroy(&battery->siop_level_wake_lock);
wake_lock_destroy(&battery->ext_event_wake_lock);
wake_lock_destroy(&battery->wc_headroom_wake_lock);
wake_lock_destroy(&battery->wpc_tx_wake_lock);
#if defined(CONFIG_UPDATE_BATTERY_DATA)
wake_lock_destroy(&battery->batt_data_wake_lock);
#endif
wake_lock_destroy(&battery->misc_event_wake_lock);
wake_lock_destroy(&battery->tx_event_wake_lock);
#ifdef CONFIG_OF
wake_lock_destroy(&battery->parse_mode_dt_wake_lock);
#endif
mutex_destroy(&battery->adclock);
mutex_destroy(&battery->iolock);
mutex_destroy(&battery->misclock);
mutex_destroy(&battery->txeventlock);
mutex_destroy(&battery->batt_handlelock);
mutex_destroy(&battery->current_eventlock);
mutex_destroy(&battery->typec_notylock);
mutex_destroy(&battery->wclock);
mutex_destroy(&battery->voutlock);
kfree(pdata);
err_bat_free:
kfree(battery);
return ret;
}
static int sec_battery_remove(struct platform_device *pdev)
{
struct sec_battery_info *battery = platform_get_drvdata(pdev);
#ifndef CONFIG_OF
int i;
#endif
pr_info("%s: ++\n", __func__);
switch (battery->pdata->polling_type) {
case SEC_BATTERY_MONITOR_WORKQUEUE:
cancel_delayed_work(&battery->polling_work);
break;
case SEC_BATTERY_MONITOR_ALARM:
alarm_cancel(&battery->polling_alarm);
break;
default:
break;
}
flush_workqueue(battery->monitor_wqueue);
destroy_workqueue(battery->monitor_wqueue);
wake_lock_destroy(&battery->monitor_wake_lock);
wake_lock_destroy(&battery->cable_wake_lock);
wake_lock_destroy(&battery->vbus_wake_lock);
wake_lock_destroy(&battery->afc_wake_lock);
wake_lock_destroy(&battery->siop_level_wake_lock);
wake_lock_destroy(&battery->ext_event_wake_lock);
wake_lock_destroy(&battery->misc_event_wake_lock);
wake_lock_destroy(&battery->tx_event_wake_lock);
wake_lock_destroy(&battery->wc_headroom_wake_lock);
wake_lock_destroy(&battery->wpc_tx_wake_lock);
#if defined(CONFIG_UPDATE_BATTERY_DATA)
wake_lock_destroy(&battery->batt_data_wake_lock);
#endif
#ifdef CONFIG_OF
wake_lock_destroy(&battery->parse_mode_dt_wake_lock);
#endif
mutex_destroy(&battery->adclock);
mutex_destroy(&battery->iolock);
mutex_destroy(&battery->misclock);
mutex_destroy(&battery->txeventlock);
mutex_destroy(&battery->batt_handlelock);
mutex_destroy(&battery->current_eventlock);
mutex_destroy(&battery->typec_notylock);
mutex_destroy(&battery->wclock);
mutex_destroy(&battery->voutlock);
#ifdef CONFIG_OF
adc_exit(battery);
#else
for (i = 0; i < SEC_BAT_ADC_CHANNEL_NUM; i++)
adc_exit(battery->pdata, i);
#endif
power_supply_unregister(battery->psy_ps);
power_supply_unregister(battery->psy_wireless);
power_supply_unregister(battery->psy_ac);
power_supply_unregister(battery->psy_usb);
power_supply_unregister(battery->psy_bat);
kfree(battery);
pr_info("%s: --\n", __func__);
return 0;
}
static int sec_battery_prepare(struct device *dev)
{
struct sec_battery_info *battery
= dev_get_drvdata(dev);
dev_info(battery->dev, "%s: Start\n", __func__);
switch (battery->pdata->polling_type) {
case SEC_BATTERY_MONITOR_WORKQUEUE:
cancel_delayed_work(&battery->polling_work);
break;
case SEC_BATTERY_MONITOR_ALARM:
alarm_cancel(&battery->polling_alarm);
break;
default:
break;
}
/* monitor_wake_lock should be unlocked before cancle monitor_work */
wake_unlock(&battery->monitor_wake_lock);
cancel_delayed_work_sync(&battery->monitor_work);
battery->polling_in_sleep = true;
sec_bat_set_polling(battery);
/* cancel work for polling
* that is set in sec_bat_set_polling()
* no need for polling in sleep
*/
if (battery->pdata->polling_type ==
SEC_BATTERY_MONITOR_WORKQUEUE)
cancel_delayed_work(&battery->polling_work);
dev_info(battery->dev, "%s: End\n", __func__);
return 0;
}
static int sec_battery_suspend(struct device *dev)
{
return 0;
}
static int sec_battery_resume(struct device *dev)
{
return 0;
}
static void sec_battery_complete(struct device *dev)
{
struct sec_battery_info *battery
= dev_get_drvdata(dev);
dev_info(battery->dev, "%s: Start\n", __func__);
/* cancel current alarm and reset after monitor work */
if (battery->pdata->polling_type == SEC_BATTERY_MONITOR_ALARM)
alarm_cancel(&battery->polling_alarm);
wake_lock(&battery->monitor_wake_lock);
queue_delayed_work(battery->monitor_wqueue,
&battery->monitor_work, 0);
dev_info(battery->dev, "%s: End\n", __func__);
return;
}
static void sec_battery_shutdown(struct platform_device *pdev)
{
struct sec_battery_info *battery
= platform_get_drvdata(pdev);
pr_info("%s: ++\n", __func__);
switch (battery->pdata->polling_type) {
case SEC_BATTERY_MONITOR_WORKQUEUE:
cancel_delayed_work(&battery->polling_work);
break;
case SEC_BATTERY_MONITOR_ALARM:
alarm_cancel(&battery->polling_alarm);
break;
default:
break;
}
pr_info("%s: --\n", __func__);
}
#ifdef CONFIG_OF
static struct of_device_id sec_battery_dt_ids[] = {
{ .compatible = "samsung,sec-battery" },
{ }
};
MODULE_DEVICE_TABLE(of, sec_battery_dt_ids);
#endif /* CONFIG_OF */
static const struct dev_pm_ops sec_battery_pm_ops = {
.prepare = sec_battery_prepare,
.suspend = sec_battery_suspend,
.resume = sec_battery_resume,
.complete = sec_battery_complete,
};
static struct platform_driver sec_battery_driver = {
.driver = {
.name = "sec-battery",
.owner = THIS_MODULE,
.pm = &sec_battery_pm_ops,
#ifdef CONFIG_OF
.of_match_table = sec_battery_dt_ids,
#endif
},
.probe = sec_battery_probe,
.remove = sec_battery_remove,
.shutdown = sec_battery_shutdown,
};
static int __init sec_battery_init(void)
{
return platform_driver_register(&sec_battery_driver);
}
static void __exit sec_battery_exit(void)
{
platform_driver_unregister(&sec_battery_driver);
}
late_initcall(sec_battery_init);
module_exit(sec_battery_exit);
MODULE_DESCRIPTION("Samsung Battery Driver");
MODULE_AUTHOR("Samsung Electronics");
MODULE_LICENSE("GPL");