| /* |
| * sec_cisd.c |
| * Samsung Mobile Battery Driver |
| * |
| * Copyright (C) 2018 Samsung Electronics, Inc. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation; either version 2 |
| * of the License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| * |
| */ |
| #include "include/sec_battery.h" |
| #include "include/sec_cisd.h" |
| |
| #if defined(CONFIG_SEC_ABC) |
| #include <linux/sti/abc_common.h> |
| #endif |
| |
| const char *cisd_data_str[] = { |
| "RESET_ALG", "ALG_INDEX", "FULL_CNT", "CAP_MAX", "CAP_MIN", "RECHARGING_CNT", "VALERT_CNT", |
| "BATT_CYCLE", "WIRE_CNT", "WIRELESS_CNT", "HIGH_SWELLING_CNT", "LOW_SWELLING_CNT", |
| "SWELLING_CHARGING", "SWELLING_FULL_CNT", "SWELLING_RECOVERY_CNT", "AICL_CNT", "BATT_THM_MAX", |
| "BATT_THM_MIN", "CHG_THM_MAX", "CHG_THM_MIN", "WPC_THM_MAX", "WPC_THM_MIN", "USB_THM_MAX", "USB_THM_MIN", |
| "CHG_BATT_THM_MAX", "CHG_BATT_THM_MIN", "CHG_CHG_THM_MAX", "CHG_CHG_THM_MIN", "CHG_WPC_THM_MAX", |
| "CHG_WPC_THM_MIN", "CHG_USB_THM_MAX", "CHG_USB_THM_MIN", "USB_OVERHEAT_CHARGING", "UNSAFETY_VOLT", |
| "UNSAFETY_TEMP", "SAFETY_TIMER", "VSYS_OVP", "VBAT_OVP", "AFC_FAIL", "BUCK_OFF", "WATER_DET", "DROP_SENSOR" |
| }; |
| const char *cisd_data_str_d[] = { |
| "FULL_CNT_D", "CAP_MAX_D", "CAP_MIN_D", "RECHARGING_CNT_D", "VALERT_CNT_D", "WIRE_CNT_D", "WIRELESS_CNT_D", |
| "HIGH_SWELLING_CNT_D", "LOW_SWELLING_CNT_D", "SWELLING_CHARGING_D", "SWELLING_FULL_CNT_D", |
| "SWELLING_RECOVERY_CNT_D", "AICL_CNT_D", "BATT_THM_MAX_D", "BATT_THM_MIN_D", "CHG_THM_MAX_D", |
| "CHG_THM_MIN_D", "WPC_THM_MAX_D", "WPC_THM_MIN_D", "USB_THM_MAX_D", "USB_THM_MIN_D", |
| "CHG_BATT_THM_MAX_D", "CHG_BATT_THM_MIN_D", "CHG_CHG_THM_MAX_D", "CHG_CHG_THM_MIN_D", |
| "CHG_WPC_THM_MAX_D", "CHG_WPC_THM_MIN_D", "CHG_USB_THM_MAX_D", "CHG_USB_THM_MIN_D", |
| "USB_OVERHEAT_CHARGING_D", "UNSAFETY_VOLT_D", "UNSAFETY_TEMP_D", "SAFETY_TIMER_D", "VSYS_OVP_D", |
| "VBAT_OVP_D", "AFC_FAIL_D", "BUCK_OFF_D", "WATER_DET_D", "DROP_SENSOR_D" |
| }; |
| |
| const char *cisd_wc_data_str[] = {"INDEX", "SNGL_NOBLE", "SNGL_VEHICLE", "SNGL_MINI", "SNGL_ZERO", "SNGL_DREAM", |
| "STAND_HERO", "STAND_DREAM", "EXT_PACK", "EXT_PACK_TA"}; |
| |
| bool sec_bat_cisd_check(struct sec_battery_info *battery) |
| { |
| union power_supply_propval incur_val = {0, }; |
| union power_supply_propval chgcur_val = {0, }; |
| union power_supply_propval capcurr_val = {0, }; |
| union power_supply_propval vbat_val = {0, }; |
| struct cisd *pcisd = &battery->cisd; |
| bool ret = false; |
| |
| if (battery->factory_mode || battery->is_jig_on || battery->skip_cisd) { |
| dev_dbg(battery->dev, "%s: No need to check in factory mode\n", |
| __func__); |
| return ret; |
| } |
| |
| if ((battery->status == POWER_SUPPLY_STATUS_CHARGING) || |
| (battery->status == POWER_SUPPLY_STATUS_FULL)) { |
| |
| /* check abnormal vbat */ |
| pcisd->ab_vbat_check_count = battery->voltage_now > pcisd->max_voltage_thr ? |
| pcisd->ab_vbat_check_count + 1 : 0; |
| |
| if ((pcisd->ab_vbat_check_count >= pcisd->ab_vbat_max_count) && |
| !(pcisd->state & CISD_STATE_OVER_VOLTAGE)) { |
| dev_info(battery->dev, "%s : [CISD] Battery Over Voltage Protection !! vbat(%d)mV\n", |
| __func__, battery->voltage_now); |
| /* IFPMIC specific error rate % on VBAT value needs to be applied for max_voltage_thr*/ |
| vbat_val.intval = true; |
| psy_do_property("battery", set, POWER_SUPPLY_EXT_PROP_VBAT_OVP, |
| vbat_val); |
| pcisd->data[CISD_DATA_VBAT_OVP]++; |
| pcisd->data[CISD_DATA_VBAT_OVP_PER_DAY]++; |
| pcisd->state |= CISD_STATE_OVER_VOLTAGE; |
| #if defined(CONFIG_SEC_ABC) |
| sec_abc_send_event("MODULE=battery@ERROR=over_voltage"); |
| #endif |
| } |
| |
| /* get actual input current */ |
| psy_do_property(battery->pdata->charger_name, get, |
| POWER_SUPPLY_PROP_CURRENT_AVG, incur_val); |
| |
| /* get actual charging current */ |
| psy_do_property(battery->pdata->charger_name, get, |
| POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, chgcur_val); |
| |
| if (battery->temperature > pcisd->data[CISD_DATA_CHG_BATT_TEMP_MAX]) |
| pcisd->data[CISD_DATA_CHG_BATT_TEMP_MAX] = battery->temperature; |
| if (battery->temperature < pcisd->data[CISD_DATA_CHG_BATT_TEMP_MIN]) |
| pcisd->data[CISD_DATA_CHG_BATT_TEMP_MIN] = battery->temperature; |
| |
| if (battery->chg_temp > pcisd->data[CISD_DATA_CHG_CHG_TEMP_MAX]) |
| pcisd->data[CISD_DATA_CHG_CHG_TEMP_MAX] = battery->chg_temp; |
| if (battery->chg_temp < pcisd->data[CISD_DATA_CHG_CHG_TEMP_MIN]) |
| pcisd->data[CISD_DATA_CHG_CHG_TEMP_MIN] = battery->chg_temp; |
| |
| if (battery->wpc_temp > pcisd->data[CISD_DATA_CHG_WPC_TEMP_MAX]) |
| pcisd->data[CISD_DATA_CHG_WPC_TEMP_MAX] = battery->wpc_temp; |
| if (battery->wpc_temp < pcisd->data[CISD_DATA_CHG_WPC_TEMP_MIN]) |
| pcisd->data[CISD_DATA_CHG_WPC_TEMP_MIN] = battery->wpc_temp; |
| |
| if (battery->usb_temp > pcisd->data[CISD_DATA_CHG_USB_TEMP_MAX]) |
| pcisd->data[CISD_DATA_CHG_USB_TEMP_MAX] = battery->usb_temp; |
| if (battery->usb_temp < pcisd->data[CISD_DATA_CHG_USB_TEMP_MIN]) |
| pcisd->data[CISD_DATA_CHG_USB_TEMP_MIN] = battery->usb_temp; |
| |
| if (battery->temperature > pcisd->data[CISD_DATA_CHG_BATT_TEMP_MAX_PER_DAY]) |
| pcisd->data[CISD_DATA_CHG_BATT_TEMP_MAX_PER_DAY] = battery->temperature; |
| if (battery->temperature < pcisd->data[CISD_DATA_BATT_TEMP_MIN_PER_DAY]) |
| pcisd->data[CISD_DATA_BATT_TEMP_MIN_PER_DAY] = battery->temperature; |
| |
| if (battery->chg_temp > pcisd->data[CISD_DATA_CHG_CHG_TEMP_MAX_PER_DAY]) |
| pcisd->data[CISD_DATA_CHG_CHG_TEMP_MAX_PER_DAY] = battery->chg_temp; |
| if (battery->chg_temp < pcisd->data[CISD_DATA_CHG_CHG_TEMP_MIN_PER_DAY]) |
| pcisd->data[CISD_DATA_CHG_CHG_TEMP_MIN_PER_DAY] = battery->chg_temp; |
| |
| if (battery->wpc_temp > pcisd->data[CISD_DATA_CHG_WPC_TEMP_MAX_PER_DAY]) |
| pcisd->data[CISD_DATA_CHG_WPC_TEMP_MAX_PER_DAY] = battery->wpc_temp; |
| if (battery->wpc_temp < pcisd->data[CISD_DATA_CHG_WPC_TEMP_MIN_PER_DAY]) |
| pcisd->data[CISD_DATA_CHG_WPC_TEMP_MIN_PER_DAY] = battery->wpc_temp; |
| |
| if (battery->usb_temp > pcisd->data[CISD_DATA_CHG_USB_TEMP_MAX_PER_DAY]) |
| pcisd->data[CISD_DATA_CHG_USB_TEMP_MAX_PER_DAY] = battery->usb_temp; |
| if (battery->usb_temp < pcisd->data[CISD_DATA_CHG_USB_TEMP_MIN_PER_DAY]) |
| pcisd->data[CISD_DATA_CHG_USB_TEMP_MIN_PER_DAY] = battery->usb_temp; |
| |
| if (battery->usb_temp > 800 && !battery->usb_overheat_check) { |
| battery->cisd.data[CISD_DATA_USB_OVERHEAT_CHARGING]++; |
| battery->cisd.data[CISD_DATA_USB_OVERHEAT_CHARGING_PER_DAY]++; |
| battery->usb_overheat_check = true; |
| } |
| |
| dev_info(battery->dev, "%s: [CISD] iavg: %d, incur: %d, chgcur: %d,\n" |
| "cc_T: %ld, lcd_off_T: %ld, passed_T: %ld, full_T: %ld, chg_end_T: %ld, cisd: 0x%x\n", __func__, |
| battery->current_avg, incur_val.intval, chgcur_val.intval, |
| pcisd->cc_start_time, pcisd->lcd_off_start_time, battery->charging_passed_time, |
| battery->charging_fullcharged_time, pcisd->charging_end_time, pcisd->state); |
| } else { |
| /* discharging */ |
| if (battery->status == POWER_SUPPLY_STATUS_NOT_CHARGING) { |
| /* check abnormal vbat */ |
| pcisd->ab_vbat_check_count = battery->voltage_now > pcisd->max_voltage_thr ? |
| pcisd->ab_vbat_check_count + 1 : 0; |
| |
| if ((pcisd->ab_vbat_check_count >= pcisd->ab_vbat_max_count) && |
| !(pcisd->state & CISD_STATE_OVER_VOLTAGE)) { |
| pcisd->data[CISD_DATA_VBAT_OVP]++; |
| pcisd->data[CISD_DATA_VBAT_OVP_PER_DAY]++; |
| pcisd->state |= CISD_STATE_OVER_VOLTAGE; |
| #if defined(CONFIG_SEC_ABC) |
| sec_abc_send_event("MODULE=battery@ERROR=over_voltage"); |
| #endif |
| } |
| } |
| |
| #if defined(CONFIG_FG_FULLCAP_FROM_BATTERY) |
| { |
| struct capacity_measure_info * info = &(battery->capacity_info); |
| capcurr_val.intval = info->capacity_full / 3600; |
| } |
| #else |
| capcurr_val.intval = SEC_BATTERY_CAPACITY_FULL; |
| psy_do_property(battery->pdata->fuelgauge_name, get, |
| POWER_SUPPLY_PROP_ENERGY_NOW, capcurr_val); |
| #endif |
| if (capcurr_val.intval == -1) { |
| dev_info(battery->dev, "%s: [CISD] FG I2C fail. skip cisd check \n", __func__); |
| return ret; |
| } |
| |
| if (capcurr_val.intval > pcisd->data[CISD_DATA_CAP_MAX]) |
| pcisd->data[CISD_DATA_CAP_MAX] = capcurr_val.intval; |
| if ((capcurr_val.intval < pcisd->data[CISD_DATA_CAP_MIN]) && |
| (capcurr_val.intval != 0)) |
| pcisd->data[CISD_DATA_CAP_MIN] = capcurr_val.intval; |
| |
| if (capcurr_val.intval > pcisd->data[CISD_DATA_CAP_MAX_PER_DAY]) |
| pcisd->data[CISD_DATA_CAP_MAX_PER_DAY] = capcurr_val.intval; |
| if ((capcurr_val.intval < pcisd->data[CISD_DATA_CAP_MIN_PER_DAY]) && |
| (capcurr_val.intval != 0)) |
| pcisd->data[CISD_DATA_CAP_MIN_PER_DAY] = capcurr_val.intval; |
| } |
| |
| if (battery->temperature > pcisd->data[CISD_DATA_BATT_TEMP_MAX]) |
| pcisd->data[CISD_DATA_BATT_TEMP_MAX] = battery->temperature; |
| if (battery->temperature < battery->cisd.data[CISD_DATA_BATT_TEMP_MIN]) |
| pcisd->data[CISD_DATA_BATT_TEMP_MIN] = battery->temperature; |
| |
| if (battery->chg_temp > pcisd->data[CISD_DATA_CHG_TEMP_MAX]) |
| pcisd->data[CISD_DATA_CHG_TEMP_MAX] = battery->chg_temp; |
| if (battery->chg_temp < pcisd->data[CISD_DATA_CHG_TEMP_MIN]) |
| pcisd->data[CISD_DATA_CHG_TEMP_MIN] = battery->chg_temp; |
| |
| if (battery->wpc_temp > pcisd->data[CISD_DATA_WPC_TEMP_MAX]) |
| pcisd->data[CISD_DATA_WPC_TEMP_MAX] = battery->wpc_temp; |
| if (battery->wpc_temp < battery->cisd.data[CISD_DATA_WPC_TEMP_MIN]) |
| pcisd->data[CISD_DATA_WPC_TEMP_MIN] = battery->wpc_temp; |
| |
| if (battery->usb_temp > pcisd->data[CISD_DATA_USB_TEMP_MAX]) |
| pcisd->data[CISD_DATA_USB_TEMP_MAX] = battery->usb_temp; |
| if (battery->usb_temp < pcisd->data[CISD_DATA_USB_TEMP_MIN]) |
| pcisd->data[CISD_DATA_USB_TEMP_MIN] = battery->usb_temp; |
| |
| if (battery->temperature > pcisd->data[CISD_DATA_BATT_TEMP_MAX_PER_DAY]) |
| pcisd->data[CISD_DATA_BATT_TEMP_MAX_PER_DAY] = battery->temperature; |
| if (battery->temperature < pcisd->data[CISD_DATA_BATT_TEMP_MIN_PER_DAY]) |
| pcisd->data[CISD_DATA_BATT_TEMP_MIN_PER_DAY] = battery->temperature; |
| |
| if (battery->chg_temp > pcisd->data[CISD_DATA_CHG_TEMP_MAX_PER_DAY]) |
| pcisd->data[CISD_DATA_CHG_TEMP_MAX_PER_DAY] = battery->chg_temp; |
| if (battery->chg_temp < pcisd->data[CISD_DATA_CHG_TEMP_MIN_PER_DAY]) |
| pcisd->data[CISD_DATA_CHG_TEMP_MIN_PER_DAY] = battery->chg_temp; |
| |
| if (battery->wpc_temp > pcisd->data[CISD_DATA_WPC_TEMP_MAX_PER_DAY]) |
| pcisd->data[CISD_DATA_WPC_TEMP_MAX_PER_DAY] = battery->wpc_temp; |
| if (battery->wpc_temp < pcisd->data[CISD_DATA_WPC_TEMP_MIN_PER_DAY]) |
| pcisd->data[CISD_DATA_WPC_TEMP_MIN_PER_DAY] = battery->wpc_temp; |
| |
| if (battery->usb_temp > pcisd->data[CISD_DATA_USB_TEMP_MAX_PER_DAY]) |
| pcisd->data[CISD_DATA_USB_TEMP_MAX_PER_DAY] = battery->usb_temp; |
| if (battery->usb_temp < pcisd->data[CISD_DATA_USB_TEMP_MIN_PER_DAY]) |
| pcisd->data[CISD_DATA_USB_TEMP_MIN_PER_DAY] = battery->usb_temp; |
| |
| return ret; |
| } |
| |
| void sec_battery_cisd_init(struct sec_battery_info *battery) |
| { |
| union power_supply_propval capfull_val; |
| |
| battery->cisd.state = CISD_STATE_NONE; |
| |
| battery->cisd.delay_time = 600; /* 10 min */ |
| battery->cisd.diff_volt_now = 40; |
| battery->cisd.diff_cap_now = 5; |
| |
| |
| #if defined(CONFIG_FG_FULLCAP_FROM_BATTERY) |
| { |
| struct capacity_measure_info * info = &(battery->capacity_info); |
| capfull_val.intval = info->capacity_full / 3600; |
| } |
| #else |
| capfull_val.intval = SEC_BATTERY_CAPACITY_FULL; |
| psy_do_property(battery->pdata->fuelgauge_name, get, |
| POWER_SUPPLY_PROP_ENERGY_NOW, capfull_val); |
| #endif |
| battery->cisd.curr_cap_max = capfull_val.intval; |
| battery->cisd.err_cap_high_thr = battery->pdata->cisd_cap_high_thr; |
| battery->cisd.err_cap_low_thr = battery->pdata->cisd_cap_low_thr; |
| battery->cisd.cc_delay_time = 3600; /* 60 min */ |
| battery->cisd.lcd_off_delay_time = 10200; /* 230 min */ |
| battery->cisd.full_delay_time = 3600; /* 60 min */ |
| battery->cisd.recharge_delay_time = 9000; /* 150 min */ |
| battery->cisd.cc_start_time = 0; |
| battery->cisd.full_start_time = 0; |
| battery->cisd.lcd_off_start_time = 0; |
| battery->cisd.overflow_start_time = 0; |
| battery->cisd.charging_end_time = 0; |
| battery->cisd.charging_end_time_2 = 0; |
| battery->cisd.recharge_count = 0; |
| battery->cisd.recharge_count_2 = 0; |
| battery->cisd.recharge_count_thres = 2; |
| battery->cisd.leakage_e_time = 3600; /* 60 min */ |
| battery->cisd.leakage_f_time = 7200; /* 120 min */ |
| battery->cisd.leakage_g_time = 14400; /* 240 min */ |
| battery->cisd.current_max_thres = 1600; |
| battery->cisd.charging_current_thres = 1000; |
| battery->cisd.current_avg_thres = 1000; |
| |
| battery->cisd.data[CISD_DATA_ALG_INDEX] = battery->pdata->cisd_alg_index; |
| battery->cisd.data[CISD_DATA_FULL_COUNT] = 1; |
| battery->cisd.data[CISD_DATA_BATT_TEMP_MAX] = -300; |
| battery->cisd.data[CISD_DATA_CHG_TEMP_MAX] = -300; |
| battery->cisd.data[CISD_DATA_WPC_TEMP_MAX] = -300; |
| battery->cisd.data[CISD_DATA_BATT_TEMP_MIN] = 1000; |
| battery->cisd.data[CISD_DATA_CHG_TEMP_MIN] = 1000; |
| battery->cisd.data[CISD_DATA_WPC_TEMP_MIN] = 1000; |
| battery->cisd.data[CISD_DATA_CAP_MIN] = 0xFFFF; |
| |
| battery->cisd.data[CISD_DATA_FULL_COUNT_PER_DAY] = 1; |
| battery->cisd.data[CISD_DATA_BATT_TEMP_MAX_PER_DAY] = -300; |
| battery->cisd.data[CISD_DATA_CHG_TEMP_MAX_PER_DAY] = -300; |
| battery->cisd.data[CISD_DATA_WPC_TEMP_MAX_PER_DAY] = -300; |
| battery->cisd.data[CISD_DATA_BATT_TEMP_MIN_PER_DAY] = 1000; |
| battery->cisd.data[CISD_DATA_CHG_TEMP_MIN_PER_DAY] = 1000; |
| battery->cisd.data[CISD_DATA_WPC_TEMP_MIN_PER_DAY] = 1000; |
| battery->cisd.data[CISD_DATA_CAP_MIN] = 0xFFFF; |
| |
| battery->cisd.data[CISD_DATA_CHG_BATT_TEMP_MAX_PER_DAY] = -300; |
| battery->cisd.data[CISD_DATA_CHG_CHG_TEMP_MAX_PER_DAY] = -300; |
| battery->cisd.data[CISD_DATA_CHG_WPC_TEMP_MAX_PER_DAY] = -300; |
| battery->cisd.data[CISD_DATA_CHG_BATT_TEMP_MIN_PER_DAY] = 1000; |
| battery->cisd.data[CISD_DATA_CHG_CHG_TEMP_MIN_PER_DAY] = 1000; |
| battery->cisd.data[CISD_DATA_CHG_WPC_TEMP_MIN_PER_DAY] = 1000; |
| |
| battery->cisd.capacity_now = capfull_val.intval; |
| battery->cisd.overflow_cap_thr = capfull_val.intval > battery->pdata->cisd_cap_limit ? |
| capfull_val.intval : battery->pdata->cisd_cap_limit; |
| |
| battery->cisd.ab_vbat_max_count = 2; /* should be 1 */ |
| battery->cisd.ab_vbat_check_count = 0; |
| battery->cisd.max_voltage_thr = battery->pdata->max_voltage_thr; |
| battery->cisd.cisd_alg_index = 6; |
| pr_info("%s: cisd.err_cap_high_thr:%d, cisd.err_cap_low_thr:%d, cisd.overflow_cap_thr:%d\n", __func__, |
| battery->cisd.err_cap_high_thr, battery->cisd.err_cap_low_thr, battery->cisd.overflow_cap_thr); |
| } |