| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * Copyright (C) 2018-2020 Oplus. All rights reserved. |
| */ |
| #include <linux/delay.h> |
| #include <linux/power_supply.h> |
| #include <linux/proc_fs.h> |
| #include <linux/uaccess.h> |
| |
| #ifdef CONFIG_OPLUS_CHARGER_MTK |
| #include <linux/slab.h> |
| #else |
| #include <linux/spinlock.h> |
| #include <linux/gpio.h> |
| #include <linux/errno.h> |
| #include <linux/delay.h> |
| #include <linux/module.h> |
| #include <linux/interrupt.h> |
| #include <linux/slab.h> |
| #include <linux/sched.h> |
| #include <linux/of.h> |
| #include <linux/of_gpio.h> |
| #include <linux/bitops.h> |
| #include <linux/regulator/driver.h> |
| #include <linux/regulator/of_regulator.h> |
| #include <linux/regulator/machine.h> |
| #include <linux/spmi.h> |
| #include <linux/printk.h> |
| #include <linux/ratelimit.h> |
| #include <linux/debugfs.h> |
| #include <linux/leds.h> |
| #include <linux/rtc.h> |
| #include <linux/version.h> |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)) |
| #include <linux/qpnp/qpnp-adc.h> |
| #endif |
| #include <linux/batterydata-lib.h> |
| #include <linux/of_batterydata.h> |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)) |
| #include <linux/msm_bcl.h> |
| #endif |
| #include <linux/ktime.h> |
| #include <linux/kernel.h> |
| #include <soc/oplus/system/boot_mode.h> |
| #endif |
| |
| #include "oplus_charger.h" |
| #include "oplus_short.h" |
| #include "oplus_vooc.h" |
| #include "charger_ic/oplus_short_ic.h" |
| |
| #define OPLUS_CHG_UPDATE_INTERVAL_SEC 5 |
| |
| #ifdef CONFIG_OPLUS_SHORT_C_BATT_CHECK |
| static bool short_d_on = false; |
| #define short_debug(fmt,...) \ |
| do { \ |
| if (short_d_on == true) { \ |
| printk(KERN_CRIT pr_fmt("=====[OPLUS_CHG]"fmt),##__VA_ARGS__); \ |
| } \ |
| } while (0) |
| |
| static int short_c_battery_status = SHORT_C_BATT_STATUS__NORMAL; |
| static int short_c_switch_status = SHORT_C_BATT_SW_STATUS__OFF; |
| static int short_c_feature_sw_status = SHORT_C_BATT_FEATURE_SW_STATUS__ON; |
| static int short_c_feature_hw_status = SHORT_C_BATT_FEATURE_HW_STATUS__OFF; |
| |
| static int __init oplus_short_c_battery_status_init(char *str) |
| { |
| sscanf(str, "%d", &short_c_battery_status); |
| chg_err("short_c_battery_status[%d]\n", short_c_battery_status); |
| return 0; |
| } |
| __setup("short_c_battery_status=", oplus_short_c_battery_status_init); |
| |
| static int __init oplus_short_c_switch_status_init(char *str) |
| { |
| sscanf(str, "%d", &short_c_switch_status); |
| chg_err("short_c_switch_status[%d]\n", short_c_switch_status); |
| return 0; |
| } |
| __setup("short_c_switch_status=", oplus_short_c_switch_status_init); |
| |
| static int __init oplus_short_c_feature_sw_status_init(char *str) |
| { |
| sscanf(str, "%d", &short_c_feature_sw_status); |
| chg_err("short_c_feature_sw_status[%d]\n", short_c_feature_sw_status); |
| return 0; |
| } |
| __setup("short_c_feature_sw_status=", oplus_short_c_feature_sw_status_init); |
| |
| static int __init oplus_short_c_feature_hw_status_init(char *str) |
| { |
| sscanf(str, "%d", &short_c_feature_hw_status); |
| chg_err("short_c_feature_hw_status[%d]\n", short_c_feature_hw_status); |
| return 0; |
| } |
| __setup("short_c_feature_hw_status=", oplus_short_c_feature_hw_status_init); |
| |
| int oplus_short_c_batt_err_code_init(void) |
| { |
| return short_c_battery_status; |
| } |
| |
| int oplus_short_c_batt_chg_switch_init(void) |
| { |
| return short_c_switch_status; |
| } |
| |
| int oplus_short_c_batt_feature_sw_status_init(void) |
| { |
| return short_c_feature_sw_status; |
| } |
| |
| int oplus_short_c_batt_feature_hw_status_init(void) |
| { |
| return short_c_feature_hw_status; |
| } |
| |
| static struct short_c_batt_item short_c_batt_items[] = { |
| /*batt chging cycle*/ |
| { |
| .name = "batt_chging_cycle_threshold", |
| .value = 0 |
| }, |
| { |
| .name = "batt_chging_cycles", |
| .value = 0 |
| }, |
| /*timer*/ |
| { |
| .name = "cv_timer1", |
| .value = 0 |
| }, |
| { |
| .name = "full_timer2", |
| .value = 0 |
| }, |
| { |
| .name = "full_timer3", |
| .value = 0 |
| }, |
| { |
| .name = "full_timer4", |
| .value = 0 |
| }, |
| { |
| .name = "full_timer5", |
| .value = 0 |
| }, |
| /*rbatt*/ |
| { |
| .name = "cool_temp_rbatt", |
| .value = 0 |
| }, |
| { |
| .name = "little_cool_temp_rbatt", |
| .value = 0 |
| }, |
| { |
| .name = "normal_temp_rbatt", |
| .value = 0 |
| }, |
| /*delta vbatt*/ |
| { |
| .name = "full_delta_vbatt1_mv", |
| .value = 0 |
| }, |
| { |
| .name = "full_delta_vbatt2_mv", |
| .value = 0 |
| }, |
| /*exception2*/ |
| { |
| .name = "ex2_lower_ibatt_ma", |
| .value = 0 |
| }, |
| { |
| .name = "ex2_low_ibatt_ma", |
| .value = 0 |
| }, |
| { |
| .name = "ex2_high_ibatt_ma", |
| .value = 0 |
| }, |
| { |
| .name = "ex2_lower_ibatt_count", |
| .value = 0 |
| }, |
| { |
| .name = "ex2_low_ibatt_count", |
| .value = 0 |
| }, |
| { |
| .name = "ex2_high_ibatt_count", |
| .value = 0 |
| }, |
| /*dynamic delta vbatt*/ |
| { |
| .name = "dyna1_low_avg_dv_mv", |
| .value = 0 |
| }, |
| { |
| .name = "dyna1_high_avg_dv_mv", |
| .value = 0 |
| }, |
| { |
| .name = "dyna1_delta_dv_mv", |
| .value = 0 |
| }, |
| { |
| .name = "dyna2_low_avg_dv_mv", |
| .value = 0 |
| }, |
| { |
| .name = "dyna2_high_avg_dv_mv", |
| .value = 0 |
| }, |
| { |
| .name = "dyna2_delta_dv_mv", |
| .value = 0 |
| }, |
| /*control switch*/ |
| { |
| .name = "is_recheck_on", |
| .value = 0 |
| }, |
| { |
| .name = "is_switch_on", |
| .value = 0 |
| }, |
| { |
| .name = "is_feature_on", |
| .value = 0 |
| }, |
| }; |
| |
| #ifdef CONFIG_OPLUS_CHARGER_MTK |
| static bool oplus_is_power_off_chg(struct oplus_chg_chip *chip) |
| { |
| #ifdef CONFIG_MTK_KERNEL_POWER_OFF_CHARGING |
| if (chip->boot_mode == KERNEL_POWER_OFF_CHARGING_BOOT) |
| return true; |
| else |
| #endif |
| return false; |
| } |
| |
| static bool oplus_is_normal_boot(struct oplus_chg_chip *chip) |
| { |
| if (chip->boot_mode == NORMAL_BOOT) |
| return true; |
| return false; |
| } |
| #else /* CONFIG_OPLUS_CHARGER_MTK */ |
| extern bool qpnp_is_power_off_charging(void); |
| |
| static bool oplus_is_power_off_chg(struct oplus_chg_chip *chip) |
| { |
| return qpnp_is_power_off_charging(); |
| } |
| |
| static bool oplus_is_normal_boot(struct oplus_chg_chip *chip) |
| { |
| if (chip->boot_mode == MSM_BOOT_MODE__NORMAL) |
| return true; |
| return false; |
| } |
| #endif /* CONFIG_OPLUS_CHARGER_MTK */ |
| |
| extern void sort(void *base, size_t num, size_t size, |
| int (*cmp_func)(const void *, const void *), |
| void (*swap_func)(void *, void *, int size)); |
| |
| static int oplus_digit_num_len(const char *str) |
| { |
| int len = 0; |
| |
| while (*str) { |
| if (*str >= '0' && *str <= '9') { |
| len++; |
| } else { |
| break; |
| } |
| str++; |
| } |
| return len; |
| } |
| |
| static int oplus_cmpint(const void *a, const void *b) |
| { |
| return *(int *)a - *(int *)b; |
| } |
| |
| bool oplus_short_c_batt_is_prohibit_chg(struct oplus_chg_chip *chip) |
| { |
| if (chip->short_c_batt.limit_chg) { |
| chg_err("limit chg!\n"); |
| return true; |
| } |
| if (chip->short_c_batt.is_feature_hw_on |
| == SHORT_C_BATT_FEATURE_HW_STATUS__ON) { |
| if (chip->short_c_batt.shortc_gpio_status == 0) { |
| return true; |
| } |
| } |
| if (chip->short_c_batt.is_feature_hw_on |
| == SHORT_C_BATT_FEATURE_HW_STATUS__ON) { |
| if (chip->short_c_batt.ic_short_otp_st == false) { |
| return true; |
| } |
| } |
| if (chip->short_c_batt.is_feature_sw_on |
| == SHORT_C_BATT_FEATURE_SW_STATUS__OFF) { |
| return false; |
| } |
| if (chip->short_c_batt.is_switch_on) { |
| if (chip->short_c_batt.err_code == SHORT_C_BATT_STATUS__CV_ERR_CODE1 |
| || chip->short_c_batt.err_code == SHORT_C_BATT_STATUS__FULL_ERR_CODE2 |
| || chip->short_c_batt.err_code == SHORT_C_BATT_STATUS__FULL_ERR_CODE3 |
| || chip->short_c_batt.err_code == SHORT_C_BATT_STATUS__DYNAMIC_ERR_CODE4 |
| || chip->short_c_batt.err_code == SHORT_C_BATT_STATUS__DYNAMIC_ERR_CODE5) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool oplus_short_c_batt_is_disable_rechg(struct oplus_chg_chip *chip) |
| { |
| if (chip->short_c_batt.limit_rechg) { |
| chg_err("limit rechg!\n"); |
| return true; |
| } |
| if (chip->short_c_batt.is_feature_sw_on == SHORT_C_BATT_FEATURE_SW_STATUS__OFF) { |
| return false; |
| } |
| return chip->short_c_batt.disable_rechg; |
| } |
| |
| static void oplus_short_c_batt_set_disable_rechg(struct oplus_chg_chip *chip, bool disable) |
| { |
| chip->short_c_batt.disable_rechg = disable; |
| } |
| |
| bool oplus_short_c_batt_get_cv_status(struct oplus_chg_chip *chip) |
| { |
| return chip->short_c_batt.cv_satus; |
| } |
| |
| static bool oplus_short_c_batt_data_is_mounted(void) |
| { |
| struct file *fp; |
| |
| fp = filp_open(BAD_CONFIG_FILE, O_RDONLY, 0644); |
| if (IS_ERR(fp)) { |
| return false; |
| } else { |
| filp_close(fp, NULL); |
| return true; |
| } |
| } |
| |
| static int oplus_short_c_batt_read_file(struct oplus_chg_chip *chip) |
| { |
| struct file *fp; |
| mm_segment_t old_fs; |
| loff_t pos; |
| char *buf; |
| char *p; |
| char buf_tmp[32] = {'\0'}; |
| int ret; |
| int i; |
| ssize_t nread; |
| |
| fp = filp_open(BAD_CONFIG_FILE, O_RDONLY, 0644); |
| if (IS_ERR(fp)) { |
| chg_err("open %s file error\n", BAD_CONFIG_FILE); |
| return -1; |
| } |
| |
| buf = kmalloc(PAGE_SIZE, GFP_KERNEL); |
| if (buf == NULL) { |
| chg_err("failed to allocate buffer\n"); |
| filp_close(fp, NULL); |
| return -ENOMEM; |
| } |
| memset(buf, '\0', PAGE_SIZE - 1); |
| |
| old_fs = get_fs(); |
| set_fs(KERNEL_DS); |
| pos = 0; |
| nread = vfs_read(fp, buf, PAGE_SIZE - 1, &pos); |
| filp_close(fp, NULL); |
| set_fs(old_fs); |
| if (nread < 0) { |
| chg_err("read %s file error\n", BAD_CONFIG_FILE); |
| goto err; |
| } |
| |
| for (i = 0; i < ARRAY_SIZE(short_c_batt_items); i++) { |
| p = strstr(buf, short_c_batt_items[i].name); |
| if (p != 0) { |
| p = p + strlen(short_c_batt_items[i].name) + strlen(":"); |
| strncpy(buf_tmp, p, oplus_digit_num_len(p)); |
| buf_tmp[oplus_digit_num_len(p)] = '\0'; |
| ret = kstrtouint(buf_tmp, 10, &(short_c_batt_items[i].value)); |
| if (ret != 0) { |
| short_c_batt_items[i].value = INVALID_DATA; |
| chg_err("err: %s[%d]\n", short_c_batt_items[i].name, short_c_batt_items[i].value); |
| } else { |
| /*userspace write abs(INVALID_DATA) as invalid data*/ |
| if (short_c_batt_items[i].value == -INVALID_DATA) |
| short_c_batt_items[i].value = INVALID_DATA; |
| chg_err("%s[%d]\n", short_c_batt_items[i].name, short_c_batt_items[i].value); |
| short_debug("%s[%d]\n", short_c_batt_items[i].name, short_c_batt_items[i].value); |
| } |
| } else { |
| short_c_batt_items[i].value = INVALID_DATA; |
| chg_err("CAN NOT find %s, set to INVALID_DATA\n", short_c_batt_items[i].name); |
| } |
| } |
| |
| chip->short_c_batt.batt_chging_cycle_threshold = short_c_batt_items[0].value; |
| chip->short_c_batt.batt_chging_cycles = short_c_batt_items[1].value; |
| chip->short_c_batt.cv_timer1 |
| = short_c_batt_items[2].value / OPLUS_CHG_UPDATE_INTERVAL_SEC; |
| chip->short_c_batt.full_timer2 |
| = short_c_batt_items[3].value / OPLUS_CHG_UPDATE_INTERVAL_SEC; |
| chip->short_c_batt.full_timer3 |
| = short_c_batt_items[4].value / OPLUS_CHG_UPDATE_INTERVAL_SEC; |
| chip->short_c_batt.full_timer4 |
| = short_c_batt_items[5].value / OPLUS_CHG_UPDATE_INTERVAL_SEC; |
| chip->short_c_batt.full_timer5 |
| = short_c_batt_items[6].value / OPLUS_CHG_UPDATE_INTERVAL_SEC; |
| chip->short_c_batt.cool_temp_rbatt = short_c_batt_items[7].value; |
| chip->short_c_batt.little_cool_temp_rbatt = short_c_batt_items[8].value; |
| chip->short_c_batt.normal_temp_rbatt = short_c_batt_items[9].value; |
| |
| chip->short_c_batt.full_delta_vbatt1_mv = short_c_batt_items[10].value; |
| chip->short_c_batt.full_delta_vbatt2_mv = short_c_batt_items[11].value; |
| |
| chip->short_c_batt.ex2_lower_ibatt_ma = short_c_batt_items[12].value; |
| chip->short_c_batt.ex2_low_ibatt_ma = short_c_batt_items[13].value; |
| chip->short_c_batt.ex2_high_ibatt_ma = short_c_batt_items[14].value; |
| chip->short_c_batt.ex2_lower_ibatt_count = short_c_batt_items[15].value; |
| chip->short_c_batt.ex2_low_ibatt_count = short_c_batt_items[16].value; |
| chip->short_c_batt.ex2_high_ibatt_count = short_c_batt_items[17].value; |
| |
| chip->short_c_batt.dyna1_low_avg_dv_mv = short_c_batt_items[18].value; |
| chip->short_c_batt.dyna1_high_avg_dv_mv = short_c_batt_items[19].value; |
| chip->short_c_batt.dyna1_delta_dv_mv = short_c_batt_items[20].value; |
| chip->short_c_batt.dyna2_low_avg_dv_mv = short_c_batt_items[21].value; |
| chip->short_c_batt.dyna2_high_avg_dv_mv = short_c_batt_items[22].value; |
| chip->short_c_batt.dyna2_delta_dv_mv = short_c_batt_items[23].value; |
| |
| chip->short_c_batt.is_recheck_on = short_c_batt_items[24].value; |
| chip->short_c_batt.is_switch_on = short_c_batt_items[25].value; |
| chip->short_c_batt.is_feature_sw_on = short_c_batt_items[26].value; |
| |
| err: |
| kfree(buf); |
| return 0; |
| } |
| |
| static bool oplus_is_algorithm_parameters_valid(struct oplus_chg_chip *chip) |
| { |
| if (chip->short_c_batt.batt_chging_cycle_threshold < 1 |
| || chip->short_c_batt.batt_chging_cycles < 0 |
| || chip->short_c_batt.cv_timer1 < 1 |
| || chip->short_c_batt.cv_timer1 > chip->limits.max_chg_time_sec |
| || chip->short_c_batt.full_timer2 < 1 |
| || chip->short_c_batt.full_timer2 > chip->limits.max_chg_time_sec |
| || chip->short_c_batt.full_timer3 < 1 |
| || chip->short_c_batt.full_timer3 > chip->limits.max_chg_time_sec |
| || chip->short_c_batt.full_timer4 < 1 |
| || chip->short_c_batt.full_timer4 > chip->limits.max_chg_time_sec |
| || chip->short_c_batt.full_timer5 < 1 |
| || chip->short_c_batt.full_timer5 > chip->limits.max_chg_time_sec |
| || chip->short_c_batt.cool_temp_rbatt < 1 |
| || chip->short_c_batt.little_cool_temp_rbatt < 1 |
| || chip->short_c_batt.normal_temp_rbatt < 1 |
| || chip->short_c_batt.full_delta_vbatt1_mv < 1 |
| || chip->short_c_batt.full_delta_vbatt2_mv < 1 |
| || chip->short_c_batt.ex2_lower_ibatt_ma < 1 |
| || chip->short_c_batt.ex2_lower_ibatt_ma > 2000 |
| || chip->short_c_batt.ex2_low_ibatt_ma < 1 |
| || chip->short_c_batt.ex2_low_ibatt_ma > 2000 |
| || chip->short_c_batt.ex2_high_ibatt_ma < 1 |
| || chip->short_c_batt.ex2_high_ibatt_ma > 2000 |
| || chip->short_c_batt.ex2_lower_ibatt_count < 1 |
| || chip->short_c_batt.ex2_low_ibatt_count < 1 |
| || chip->short_c_batt.ex2_high_ibatt_count < 1 |
| || chip->short_c_batt.dyna1_delta_dv_mv < 1 |
| || chip->short_c_batt.dyna2_delta_dv_mv < 1) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* return < 0 for error, return >= 0 for OK */ |
| /* err_code1:T1,T2,-1,-1,-1 |
| * err_code2:T1,Tn,VBAT1,VBAT2,TIMER3_COUNTS |
| * err_code3:T1,Tn,VBAT1,VBAT2,TIMER4_COUNTS |
| */ |
| static int oplus_short_c_batt_write_err_code(int err_code, |
| int temp1,int temp2, int vbatt1_mv, int vbatt2_mv, int timer_counts) |
| { |
| struct file *fp; |
| mm_segment_t old_fs; |
| loff_t pos; |
| ssize_t len; |
| char buf[256] = {'\0'}; |
| char buf_log[256] = {'\0'}; |
| |
| fp = filp_open(ERR_CODE_FILE, O_RDWR | O_CREAT, 0644); |
| if (IS_ERR(fp)) { |
| chg_err("create %s file error\n", ERR_CODE_FILE); |
| return -1; |
| } |
| |
| /*add ",#" and the end of the string for userspace*/ |
| sprintf(buf, "err_code%d:%d,%d,%d,%d,%d,#", err_code, |
| temp1, temp2, vbatt1_mv, vbatt2_mv, timer_counts); |
| old_fs = get_fs(); |
| set_fs(KERNEL_DS); |
| pos = 0; |
| len = vfs_write(fp, buf, strlen(buf), &pos); |
| if (len < 0) { |
| chg_err("write %s file error\n", ERR_CODE_FILE); |
| } |
| pos = 0; |
| vfs_read(fp, buf_log, sizeof(buf), &pos); |
| chg_err("%s\n", buf_log); |
| |
| filp_close(fp, NULL); |
| set_fs(old_fs); |
| |
| return len; |
| } |
| |
| #define INVALID_CODE INVALID_DATA |
| #define EXIT_CODE__OVER_DELTA_TEMP_IN_TIMER3 1 |
| #define EXIT_CODE__TIMER4_OVERFLOW 2 |
| #define EXIT_CODE__OVER_DELTA_TEMP_IN_TIMER4 3 |
| #define EXIT_CODE__EX2_OVER_LOW_IBATT 5 |
| #define EXIT_CODE__EX2_OVER_HIGH_IBATT 6 |
| #define EXIT_CODE__EX2_OVER_LOWER_IBATT 7 |
| #define EXIT_CODE__EX5_OUT_OF_TEMP_0_45 11 |
| #define EXIT_CODE__OVER_RBATT 12 |
| #define EXIT_CODE__NOT_OVER_RBATT 100 |
| #define EXIT_CODE__TIMER3_OVERFLOW_INFO 101 |
| |
| /* exit_code1:T1,Tn,VBAT1,VBAT2 */ |
| static int oplus_short_c_batt_write_exit_code(int exit_code, |
| int temp1, int temp2, int vbatt1, int vbatt2) |
| { |
| struct file *fp; |
| mm_segment_t old_fs; |
| loff_t pos; |
| ssize_t len; |
| char buf[256] = {'\0'}; |
| char buf_log[256] = {'\0'}; |
| |
| fp = filp_open(EXIT_CODE_FILE, O_RDWR | O_CREAT, 0644); |
| if (IS_ERR(fp)) { |
| chg_err("create %s file error\n", EXIT_CODE_FILE); |
| return -1; |
| } |
| |
| /*add ",#" and the end of the string for userspace*/ |
| sprintf(buf, "exit_code%d:%d,%d,%d,%d,#", exit_code, temp1, temp2, vbatt1, vbatt2); |
| |
| old_fs = get_fs(); |
| set_fs(KERNEL_DS); |
| pos = 0; |
| len = vfs_write(fp, buf, strlen(buf), &pos); |
| if (len < 0) { |
| chg_err("write %s file error\n", EXIT_CODE_FILE); |
| } |
| pos = 0; |
| vfs_read(fp, buf_log, sizeof(buf), &pos); |
| chg_err("%s\n", buf_log); |
| |
| filp_close(fp, NULL); |
| set_fs(old_fs); |
| |
| return len; |
| } |
| |
| static int oplus_short_c_batt_write_chg_data(int current_ma, int volt_mv, |
| int lower_count, int low_count, int high_count) |
| { |
| struct file *fp; |
| mm_segment_t old_fs; |
| loff_t pos; |
| ssize_t len; |
| char buf[256] = {'\0'}; |
| char buf_log[256] = {'\0'}; |
| static int pre_lower_count = -1; |
| static int pre_low_count = -1; |
| static int pre_high_count = -1; |
| |
| fp = filp_open(CHG_DATA_FILE, O_RDWR | O_CREAT, 0644); |
| if (IS_ERR(fp)) { |
| chg_err("create %s file error\n", CHG_DATA_FILE); |
| return -1; |
| } |
| |
| /*add ",#" and the end of the string for userspace*/ |
| sprintf(buf, "chg_data:%d,%d,%d,%d,%d,#", current_ma, volt_mv, |
| lower_count, low_count, high_count); |
| |
| old_fs = get_fs(); |
| set_fs(KERNEL_DS); |
| pos = 0; |
| len = vfs_write(fp, buf, strlen(buf), &pos); |
| if (len < 0) |
| chg_err("write %s file error\n", CHG_DATA_FILE); |
| |
| pos = 0; |
| vfs_read(fp, buf_log, sizeof(buf), &pos); |
| if (pre_lower_count != lower_count || pre_low_count != low_count |
| || pre_high_count != high_count) { |
| short_debug("%s\n", buf_log); |
| pre_lower_count = lower_count; |
| pre_low_count = low_count; |
| pre_high_count = high_count; |
| } |
| |
| filp_close(fp, NULL); |
| set_fs(old_fs); |
| |
| return len; |
| } |
| |
| #define SHORT_C_BATT_UPDATE_RESET 0 |
| #define SHORT_C_BATT_UPDATE_PARM 1 |
| #define SHORT_C_BATT_CLR_ERR_CODE 2 |
| #define SHORT_C_BATT_DEBUG_ON 98 |
| void oplus_short_c_batt_update_change(struct oplus_chg_chip *chip, int update_value) |
| { |
| if (update_value == SHORT_C_BATT_UPDATE_PARM) { |
| oplus_short_c_batt_read_file(chip); |
| } else if (update_value == SHORT_C_BATT_CLR_ERR_CODE) { |
| oplus_short_c_batt_write_err_code(0, 0, 0, 0, 0, 0); |
| }else if (update_value == SHORT_C_BATT_DEBUG_ON) { |
| short_d_on = true; |
| } |
| } |
| |
| /* when get 10 valid vbatt, return avg_vbatt_mv, or return 0 */ |
| static int oplus_short_c_batt_get_average_vbatt_mv(struct oplus_chg_chip *chip, bool reset) |
| { |
| static int valid_vbatt_count = 0; |
| static int buf[30] = {0}; |
| static int avg_vbatt_mv = 0; |
| int i; |
| |
| if (reset == true) { |
| valid_vbatt_count = 0; |
| avg_vbatt_mv = 0; |
| return 0; |
| } |
| |
| if (abs(chip->icharging) < 10) { |
| if (valid_vbatt_count == 0) { |
| for (i = 0; i < ARRAY_SIZE(buf); i++) { |
| buf[i] = chip->batt_volt; |
| } |
| } |
| if (valid_vbatt_count < ARRAY_SIZE(buf)) { |
| buf[valid_vbatt_count] = chip->batt_volt; |
| } |
| valid_vbatt_count++; |
| } |
| #if 0 |
| if (valid_vbatt_count == ARRAY_SIZE(buf)) { |
| avg_vbatt_mv = 0; |
| sort(buf, ARRAY_SIZE(buf), sizeof(buf[0]), oplus_cmpint, NULL); |
| for (i = 3; i < ARRAY_SIZE(buf) - 3; i++) |
| avg_vbatt_mv = avg_vbatt_mv + buf[i]; |
| valid_vbatt_count = 0; |
| } |
| |
| return avg_vbatt_mv / 4; |
| #else |
| /* 2017-12-18 use the max value instead of avg value */ |
| if (valid_vbatt_count == ARRAY_SIZE(buf)) { |
| avg_vbatt_mv = 0; |
| sort(buf, ARRAY_SIZE(buf), sizeof(buf[0]), oplus_cmpint, NULL); |
| avg_vbatt_mv = buf[ARRAY_SIZE(buf) - 1]; |
| valid_vbatt_count = 0; |
| } |
| |
| return avg_vbatt_mv; |
| #endif |
| } |
| |
| |
| #define D_SHORTC_HW_CNT 3 |
| void oplus_chg_short_c_hw_check(struct oplus_chg_chip *chip) |
| { |
| static int short_c_hw_check_counts = 0; |
| bool shortc_gpio_status = true; |
| if (!chip || !chip->chg_ops->get_shortc_hw_gpio_status) { |
| return; |
| } |
| shortc_gpio_status = chip->short_c_batt.shortc_gpio_status; |
| if (chip->chg_ops->get_shortc_hw_gpio_status() == 0) { |
| short_c_hw_check_counts ++; |
| chg_debug("[Shortc_HW] shorc_HW gpio low, count = %d, status = %d\n", |
| short_c_hw_check_counts, chip->short_c_batt.shortc_gpio_status); |
| if(short_c_hw_check_counts >= D_SHORTC_HW_CNT) { |
| short_c_hw_check_counts = 0; |
| chip->short_c_batt.shortc_gpio_status = 0; |
| } |
| } else { |
| short_c_hw_check_counts = 0; |
| chip->short_c_batt.shortc_gpio_status = 1; |
| } |
| if (chip->short_c_batt.is_feature_hw_on |
| == SHORT_C_BATT_FEATURE_HW_STATUS__OFF) { |
| return; |
| } |
| if (chip->charger_exist && (shortc_gpio_status |
| != chip->short_c_batt.shortc_gpio_status)) { |
| if (oplus_vooc_get_fastchg_started() == true) { |
| oplus_chg_set_chargerid_switch_val(0); |
| oplus_vooc_switch_mode(NORMAL_CHARGER_MODE); |
| } |
| oplus_chg_turn_on_charging(chip); |
| chg_debug("[Shortc_HW] shorc_HW gpio status changed! \ |
| gpio_stat_pre = %d, gpio_stat = %d\n", |
| shortc_gpio_status, chip->short_c_batt.shortc_gpio_status); |
| } |
| } |
| |
| void oplus_chg_short_c_ic_check(struct oplus_chg_chip *chip) |
| { |
| bool short_ic_otp_st = true; |
| |
| if (!chip) { |
| return; |
| } |
| |
| short_ic_otp_st = chip->short_c_batt.ic_short_otp_st; |
| chip->short_c_batt.ic_short_otp_st = oplus_short_ic_otp_check(); |
| if (chip->short_c_batt.is_feature_hw_on |
| == SHORT_C_BATT_FEATURE_HW_STATUS__OFF) { |
| return; |
| } |
| if (chip->charger_exist && (short_ic_otp_st |
| != chip->short_c_batt.ic_short_otp_st)) { |
| if (oplus_vooc_get_fastchg_started() == true) { |
| oplus_chg_set_chargerid_switch_val(0); |
| oplus_vooc_switch_mode(NORMAL_CHARGER_MODE); |
| } |
| oplus_chg_turn_on_charging(chip); |
| chg_debug("[Shortc_ic] oplus_shorc_ic status changed! \ |
| ic_otp_pre = %d, ic_otp = %d\n", |
| short_ic_otp_st, chip->short_c_batt.ic_short_otp_st); |
| } |
| } |
| |
| #define INPUT_LIMIT_MA_THRESHOLD 900 |
| //#define CV_VBATT_MV_THRESHOLD 4350 |
| #define CV_IBATT_MA_THRESHOLD 300 |
| #define CV_PRE_CHECK_COUNT 5 |
| #define CV_DELTA_IBATT_COUNT 10 |
| #define CV_DELTA_IBATT_MA_THRESHOLD 50 |
| |
| #define CV_ITERM_COUNT 3 |
| #define CV_VBATT_COUNT 10 |
| |
| #define LITTLE_COLD_TEMP_THRESHOLD 20 |
| #define COOL_TEMP_THRESHOLD 120 |
| #define NORMAL_TEMP_THRESHOLD 250 |
| #define WARM_TEMP_THRESHOLD 430 |
| #define DELTA_TEMP_THRESHOLD 50 |
| |
| //#define FULL_TIMER2_30MIN_COUNT 360 //1800/5 |
| //#define FULL_TIMER3_20MIN_COUNT 240 //1200/5 |
| //#define FULL_TIMER4_40MIN_COUNT 480 //2400/5 |
| |
| #define INVALID_TEMP INVALID_DATA |
| |
| void oplus_chg_short_c_battery_check(struct oplus_chg_chip *chip) |
| { |
| static int cv_pre_check_count = 0; |
| static int cv_timer1_count = 0; |
| //static int cv_timer1_count_limit = 0; |
| static int cv_delta_ibatt_count = 0; |
| static int cv_temp = INVALID_TEMP; |
| |
| static int cv_iterm_count = 0; |
| static int cv_avg_vbatt = 0; |
| static int cv_vbatt_count = 0; |
| |
| static int full_over_10ma_count = 0; |
| static int full_over_20ma_count = 0; |
| static int full_over_50ma_count = 0; |
| static int full_timer2_count = 0; |
| static int full_timer3_count = 0; |
| static int full_timer4_count = 0; |
| static int full_timer5_count = 0; |
| static int full_vbatt1 = 0; |
| static int full_temp1 = INVALID_TEMP; |
| int full_vbatt_n; |
| int full_vbatt_2; |
| int rbatt; |
| |
| static int pre_icharging = 0; |
| static bool exit_check_status = false; |
| |
| static int data_mounted_count = 0; |
| |
| oplus_chg_short_c_hw_check(chip); |
| oplus_chg_short_c_ic_check(chip); |
| |
| if (chip->short_c_batt.short_c_bat_cv_mv == -EINVAL |
| || chip->limits.short_c_bat_vfloat_mv == -EINVAL |
| || chip->limits.short_c_bat_fastchg_current_ma == -EINVAL) { |
| return; |
| } |
| /*If this feature is OFF, clear anything about charging control and goto cv_reset for doing nothing*/ |
| if (chip->short_c_batt.is_feature_sw_on == SHORT_C_BATT_FEATURE_SW_STATUS__OFF) { |
| chip->short_c_batt.err_code = SHORT_C_BATT_STATUS__NORMAL; |
| goto cv_reset; |
| } |
| |
| if (chip->charger_type == POWER_SUPPLY_TYPE_USB_DCP) { |
| if (oplus_is_power_off_chg(chip) == true) { |
| data_mounted_count++; |
| if (data_mounted_count < 30 |
| && oplus_short_c_batt_data_is_mounted() == true) {//3min |
| oplus_short_c_batt_read_file(chip); |
| data_mounted_count = 30; |
| } |
| if (data_mounted_count > 30) { |
| data_mounted_count = 30; |
| } |
| } else if (oplus_is_normal_boot(chip) == false) { |
| chg_err("oplus_is_normal_boot[false]\n"); |
| return; |
| } |
| } |
| |
| if (chip->charger_type == POWER_SUPPLY_TYPE_UNKNOWN |
| && chip->short_c_batt.is_recheck_on != 0) { |
| if (chip->short_c_batt.err_code != 0) { |
| chg_err("clear errcode\n"); |
| chip->short_c_batt.err_code = 0; |
| } |
| } |
| |
| if (chip->short_c_batt.update_change == SHORT_C_BATT_UPDATE_PARM) { |
| chip->short_c_batt.update_change = SHORT_C_BATT_UPDATE_RESET; |
| /*bootup phone and algorithm parameters change, userspace will set update_change |
| *to 1, if is_recheck_on==1 we need to reset err_code for userspace to count err_codes |
| */ |
| if (chip->short_c_batt.is_recheck_on != 0) { |
| chg_err("clear errcode\n"); |
| chip->short_c_batt.err_code = 0; |
| } |
| goto cv_reset; |
| } |
| |
| if (chip->charger_type != POWER_SUPPLY_TYPE_USB_DCP) { |
| short_debug("chip->charger_type != POWER_SUPPLY_TYPE_USB_DCP\n"); |
| goto exit_check_reset; |
| } |
| |
| /* complete this check */ |
| if (exit_check_status == true) { |
| goto cv_reset; |
| } |
| |
| /* check algorithm parameters */ |
| if (oplus_is_algorithm_parameters_valid(chip) == false) { |
| chg_err("invalid parameters\n"); |
| goto cv_reset; |
| } |
| |
| if ((chip->chg_ops->get_dyna_aicl_result) |
| && (chip->chg_ops->get_dyna_aicl_result() < INPUT_LIMIT_MA_THRESHOLD)) { |
| short_debug("get_dyna_aicl_result[%d]\n", chip->chg_ops->get_dyna_aicl_result()); |
| goto cv_reset; |
| } |
| |
| if (!chip->batt_exist || !chip->mmi_chg) { |
| goto cv_reset; |
| } |
| |
| /* known the battery status, no need to check */ |
| if (chip->short_c_batt.err_code == SHORT_C_BATT_STATUS__CV_ERR_CODE1 |
| || (chip->short_c_batt.err_code == SHORT_C_BATT_STATUS__FULL_ERR_CODE2 |
| && chip->batt_full != true) //set errcode2 at the 1st time, need to check errcode3 |
| || chip->short_c_batt.err_code == SHORT_C_BATT_STATUS__FULL_ERR_CODE3 |
| || (chip->short_c_batt.err_code == SHORT_C_BATT_STATUS__DYNAMIC_ERR_CODE4 |
| && chip->batt_full != true) //set errcode4 at the 1st time, need to check errcode3 |
| || chip->short_c_batt.err_code == SHORT_C_BATT_STATUS__DYNAMIC_ERR_CODE5) { |
| chg_err("exit_check, errcode[%d]\n", chip->short_c_batt.err_code); |
| short_debug("exit_check, errcode[%d]\n", chip->short_c_batt.err_code); |
| exit_check_status = true; |
| return; |
| } |
| |
| if (chip->temperature <= LITTLE_COLD_TEMP_THRESHOLD || chip->temperature > WARM_TEMP_THRESHOLD) {//0~45 |
| short_debug("temperature[%d]\n", chip->temperature); |
| if (oplus_short_c_batt_get_cv_status(chip) == true) { |
| oplus_short_c_batt_write_exit_code(EXIT_CODE__EX5_OUT_OF_TEMP_0_45, |
| INVALID_CODE, chip->temperature, INVALID_CODE, INVALID_CODE); |
| chg_err("don't exit_check, temperature[%d]\n", chip->temperature); |
| short_debug("don't exit_check, out of temp\n"); |
| goto cv_reset; |
| } else { |
| goto cv_reset; |
| } |
| } |
| |
| if (chip->short_c_batt.in_idle == false) { |
| short_debug("don't exit_check, idle false\n"); |
| chg_err("don't exit_check, idle false\n"); |
| goto cv_reset; |
| } |
| |
| /* delta vbatt check after full */ |
| if (chip->batt_full == true && chip->chging_on == false |
| && chip->charging_state == CHARGING_STATUS_FULL) { |
| chip->short_c_batt.cv_satus = true; |
| if (chip->icharging > chip->short_c_batt.ex2_lower_ibatt_ma) { |
| full_over_10ma_count++; |
| } else { |
| full_over_10ma_count = 0; |
| } |
| if (chip->icharging > chip->short_c_batt.ex2_low_ibatt_ma) { |
| full_over_20ma_count++; |
| } |
| if (chip->icharging > chip->short_c_batt.ex2_high_ibatt_ma) { |
| full_over_50ma_count++; |
| } else { |
| full_over_50ma_count = 0; |
| } |
| oplus_short_c_batt_write_chg_data(chip->icharging, chip->batt_volt, |
| full_over_10ma_count, full_over_20ma_count, full_over_50ma_count); |
| |
| if (full_over_10ma_count >= chip->short_c_batt.ex2_lower_ibatt_count) { |
| oplus_short_c_batt_write_exit_code(EXIT_CODE__EX2_OVER_LOWER_IBATT, |
| INVALID_CODE, INVALID_CODE, INVALID_CODE, INVALID_CODE); |
| chg_err("don't exit_check, over [%d]mA [%d]times\n", |
| chip->short_c_batt.ex2_lower_ibatt_ma, chip->short_c_batt.ex2_lower_ibatt_count); |
| short_debug("don't exit_check, full_over_10ma_count[%d]\n", full_over_10ma_count); |
| goto cv_reset; |
| } |
| if (full_over_20ma_count >= chip->short_c_batt.ex2_low_ibatt_count) { |
| oplus_short_c_batt_write_exit_code(EXIT_CODE__EX2_OVER_LOW_IBATT, |
| INVALID_CODE, INVALID_CODE, INVALID_CODE, INVALID_CODE); |
| chg_err("don't exit_check, over [%d]mA [%d]times\n", |
| chip->short_c_batt.ex2_low_ibatt_ma, chip->short_c_batt.ex2_low_ibatt_count); |
| short_debug("don't exit_check, full_over_20ma_count[%d]\n", full_over_20ma_count); |
| goto cv_reset; |
| } |
| if (full_over_50ma_count >= chip->short_c_batt.ex2_high_ibatt_count) { |
| oplus_short_c_batt_write_exit_code(EXIT_CODE__EX2_OVER_HIGH_IBATT, |
| INVALID_CODE, INVALID_CODE, INVALID_CODE, INVALID_CODE); |
| chg_err("don't exit_check, over [%d]mA [%d]times\n", |
| chip->short_c_batt.ex2_high_ibatt_ma, chip->short_c_batt.ex2_high_ibatt_count); |
| short_debug("don't exit_check, full_over_50ma_count[%d]\n", full_over_50ma_count); |
| goto cv_reset; |
| } |
| |
| /* start timer5 */ |
| full_timer5_count++; |
| /* timer5 not overflow */ |
| if (full_timer5_count <= chip->short_c_batt.full_timer5) { |
| short_debug("full_timer5_count[%d]\n", full_timer5_count); |
| oplus_short_c_batt_set_disable_rechg(chip, true); |
| oplus_short_c_batt_get_average_vbatt_mv(chip, true);//reset |
| return; |
| } |
| /* timer5 overflow then get VBAT2 */ |
| if (full_timer5_count > chip->short_c_batt.full_timer5 |
| && full_timer5_count < chip->short_c_batt.full_timer5 + 2) { |
| full_timer5_count = chip->short_c_batt.full_timer5; |
| full_vbatt_2 = oplus_short_c_batt_get_average_vbatt_mv(chip, false); |
| short_debug("full_timer5_count overflow, full_vbatt_2[%d]\n", full_vbatt_2); |
| if (full_vbatt_2 > 0 && chip->limits.iterm_ma != 0) { |
| full_timer5_count = chip->short_c_batt.full_timer5 + 2;//goto the next step |
| if (cv_avg_vbatt > chip->short_c_batt.short_c_bat_cv_mv + 1000 |
| || cv_avg_vbatt < chip->short_c_batt.short_c_bat_cv_mv - 1000) { |
| cv_avg_vbatt = -1 * chip->short_c_batt.short_c_bat_cv_mv; |
| } |
| if (chip->limits.iterm_ma != 0) { |
| rbatt = (cv_avg_vbatt - full_vbatt_2) * 1000 / chip->limits.iterm_ma; |
| } else { |
| rbatt = -1; |
| } |
| if (chip->temperature <= COOL_TEMP_THRESHOLD) { //2~12 |
| if (rbatt > chip->short_c_batt.cool_temp_rbatt) { |
| exit_check_status = true; |
| } |
| } else if (chip->temperature <= NORMAL_TEMP_THRESHOLD) {//12~25 |
| if (rbatt > chip->short_c_batt.little_cool_temp_rbatt) { |
| exit_check_status = true; |
| } |
| } else { //25~43 |
| if (rbatt > chip->short_c_batt.normal_temp_rbatt) { |
| exit_check_status = true; |
| } |
| } |
| short_debug("cv_avg_vbatt[%d], full_vbatt_2[%d], rbatt[%d]\n", cv_avg_vbatt, full_vbatt_2, rbatt); |
| if (exit_check_status == true) { |
| oplus_short_c_batt_write_exit_code(EXIT_CODE__OVER_RBATT, |
| chip->temperature, chip->temperature, full_vbatt_2, rbatt); |
| chg_err("exit_check, temp [%d] full_vbatt_2[%d] rbatt[%d]\n", |
| chip->temperature, full_vbatt_2, rbatt); |
| short_debug("exit_check, rbatt[%d]\n", rbatt); |
| return; |
| } else { |
| oplus_short_c_batt_write_exit_code(EXIT_CODE__NOT_OVER_RBATT, |
| chip->temperature, chip->temperature, full_vbatt_2, rbatt); |
| short_debug("exit_code: EXIT_CODE__NOT_OVER_RBATT\n"); |
| } |
| } |
| return; |
| } |
| |
| /* start timer2 */ |
| full_timer2_count++; |
| /* timer2 not overflow */ |
| if (full_timer2_count <= chip->short_c_batt.full_timer2) { |
| short_debug("full_timer2_count[%d]\n", full_timer2_count); |
| oplus_short_c_batt_get_average_vbatt_mv(chip, true);//reset |
| return; |
| } |
| /* timer2 overflow then get VBAT1 and T1 */ |
| if (full_timer2_count > chip->short_c_batt.full_timer2 |
| && full_timer2_count < chip->short_c_batt.full_timer2 + 2) { |
| full_timer2_count = chip->short_c_batt.full_timer2; |
| full_vbatt_n = oplus_short_c_batt_get_average_vbatt_mv(chip, false); |
| short_debug("full_timer2_count overflow, full_vbatt_1[%d]\n", full_vbatt_n); |
| if (full_vbatt_n > 0 && full_vbatt1 == 0) { |
| full_timer2_count = chip->short_c_batt.full_timer2 + 2;//goto the next step |
| full_vbatt1 = full_vbatt_n; |
| full_temp1 = chip->temperature; |
| chg_err("full_vbatt1[%d], full_temp1[%d]\n", full_vbatt1, full_temp1); |
| } |
| return; |
| } |
| |
| /* start timer3 */ |
| full_timer3_count++; |
| /* timer3 not overflow */ |
| if (full_timer3_count <= chip->short_c_batt.full_timer3) { |
| short_debug("full_timer3_count[%d]\n", full_timer3_count); |
| oplus_short_c_batt_get_average_vbatt_mv(chip, true);//reset |
| if (full_temp1 != INVALID_TEMP && abs(full_temp1 - chip->temperature) > DELTA_TEMP_THRESHOLD) { |
| oplus_short_c_batt_write_exit_code(EXIT_CODE__OVER_DELTA_TEMP_IN_TIMER3, |
| full_temp1, chip->temperature, INVALID_CODE, INVALID_CODE); |
| chg_err("don't exit_check, temperature[%d]\n", chip->temperature); |
| short_debug("don't exit_check, T1[%d], T2[%d]\n", full_temp1, chip->temperature); |
| goto cv_reset; |
| } |
| return; |
| } |
| /* timer3 overflow then check err_code2 and dynamic err_code4 */ |
| if (full_timer3_count > chip->short_c_batt.full_timer3 |
| && full_timer3_count < chip->short_c_batt.full_timer3 + 3) { |
| full_vbatt_n = oplus_short_c_batt_get_average_vbatt_mv(chip, false); |
| short_debug("full_timer3_count overflow, full_vbatt_n[%d]\n", full_vbatt_n); |
| if (full_vbatt_n > 0 && full_vbatt1 > 0) { |
| if (full_timer3_count == chip->short_c_batt.full_timer3 + 1) { |
| if ((chip->short_c_batt.batt_chging_cycles < chip->short_c_batt.batt_chging_cycle_threshold) |
| && (full_vbatt1 - full_vbatt_n > chip->short_c_batt.full_delta_vbatt1_mv)) { |
| //set err_code2; |
| short_debug("set err_code[%d]\n", SHORT_C_BATT_STATUS__FULL_ERR_CODE2); |
| chip->short_c_batt.err_code = SHORT_C_BATT_STATUS__FULL_ERR_CODE2; |
| oplus_short_c_batt_write_err_code(chip->short_c_batt.err_code, full_temp1, |
| chip->temperature, full_vbatt1, full_vbatt_n, full_timer3_count * 5); |
| } |
| } |
| /* dynamic */ |
| if (full_timer3_count == chip->short_c_batt.full_timer3 + 2) { |
| full_timer3_count = chip->short_c_batt.full_timer3 + 3;//goto the next step |
| if (chip->temperature <= COOL_TEMP_THRESHOLD) {//2~12 |
| if (chip->short_c_batt.dyna1_low_avg_dv_mv != INVALID_DATA |
| && chip->short_c_batt.dyna1_low_avg_dv_mv >= 0) { |
| if ((full_vbatt1 - full_vbatt_n - chip->short_c_batt.dyna1_low_avg_dv_mv > chip->short_c_batt.dyna1_delta_dv_mv) |
| && (full_vbatt1 - full_vbatt_n > chip->short_c_batt.full_delta_vbatt1_mv)) { |
| //set err_code4; |
| short_debug("set err_code[%d]\n", SHORT_C_BATT_STATUS__DYNAMIC_ERR_CODE4); |
| chip->short_c_batt.err_code = SHORT_C_BATT_STATUS__DYNAMIC_ERR_CODE4; |
| oplus_short_c_batt_write_err_code(chip->short_c_batt.err_code, full_temp1, |
| chip->temperature, full_vbatt1, full_vbatt_n, INVALID_CODE); |
| } |
| } |
| } else {//12~43 |
| if (chip->short_c_batt.dyna1_high_avg_dv_mv != INVALID_DATA |
| && chip->short_c_batt.dyna1_high_avg_dv_mv >= 0) { |
| if ((full_vbatt1 - full_vbatt_n - chip->short_c_batt.dyna1_high_avg_dv_mv > chip->short_c_batt.dyna1_delta_dv_mv) |
| && (full_vbatt1 - full_vbatt_n > chip->short_c_batt.full_delta_vbatt1_mv)) { |
| //set err_code4; |
| short_debug("set err_code[%d]\n", SHORT_C_BATT_STATUS__DYNAMIC_ERR_CODE4); |
| chip->short_c_batt.err_code = SHORT_C_BATT_STATUS__DYNAMIC_ERR_CODE4; |
| oplus_short_c_batt_write_err_code(chip->short_c_batt.err_code, full_temp1, |
| chip->temperature, full_vbatt1, full_vbatt_n, INVALID_CODE); |
| } |
| } |
| } |
| if (chip->short_c_batt.err_code != SHORT_C_BATT_STATUS__DYNAMIC_ERR_CODE4) { |
| short_debug("exit_code, EXIT_CODE__TIMER3_OVERFLOW_INFO\n"); |
| oplus_short_c_batt_write_exit_code(EXIT_CODE__TIMER3_OVERFLOW_INFO, |
| full_temp1, chip->temperature, full_vbatt1, full_vbatt_n); |
| } |
| } |
| } else { |
| full_timer3_count = chip->short_c_batt.full_timer3; |
| } |
| return; |
| } |
| |
| /* start timer4 */ |
| full_timer4_count++; |
| /* timer4 not overflow */ |
| if (full_timer4_count <= chip->short_c_batt.full_timer4) { |
| short_debug("full_timer4_count[%d]\n", full_timer4_count); |
| oplus_short_c_batt_get_average_vbatt_mv(chip, true);//reset |
| if (full_temp1 != INVALID_TEMP && abs(full_temp1 - chip->temperature) > DELTA_TEMP_THRESHOLD) { |
| oplus_short_c_batt_write_exit_code(EXIT_CODE__OVER_DELTA_TEMP_IN_TIMER4, |
| full_temp1, chip->temperature, INVALID_CODE, INVALID_CODE); |
| chg_err("don't exit_check, temperature[%d]\n", chip->temperature); |
| short_debug("don't exit_check, T1[%d], T2[%d]\n", full_temp1, chip->temperature); |
| goto cv_reset; |
| } |
| return; |
| } |
| /* timer4 overflow then check err_code3 and dynamic err_code5 */ |
| if (full_timer4_count > chip->short_c_batt.full_timer4 |
| && full_timer4_count < chip->short_c_batt.full_timer4 + 3) { |
| full_vbatt_n = oplus_short_c_batt_get_average_vbatt_mv(chip, false); |
| short_debug("full_timer4_count overflow, full_vbatt_n[%d]\n", full_vbatt_n); |
| if (full_vbatt_n > 0 && full_vbatt1 > 0) { |
| if (full_timer4_count == chip->short_c_batt.full_timer4 + 1) { |
| if ((chip->short_c_batt.batt_chging_cycles < chip->short_c_batt.batt_chging_cycle_threshold) |
| && (full_vbatt1 - full_vbatt_n > chip->short_c_batt.full_delta_vbatt2_mv)) { |
| //set err_code3; |
| short_debug("set err_code[%d]\n", SHORT_C_BATT_STATUS__FULL_ERR_CODE3); |
| chip->short_c_batt.err_code = SHORT_C_BATT_STATUS__FULL_ERR_CODE3; |
| oplus_short_c_batt_write_err_code(chip->short_c_batt.err_code, full_temp1, |
| chip->temperature, full_vbatt1, full_vbatt_n, full_timer4_count * 5); |
| } |
| } |
| /* dynamic */ |
| if (full_timer4_count == chip->short_c_batt.full_timer4 + 2) { |
| full_timer4_count = chip->short_c_batt.full_timer4 + 3;//goto the next step |
| if (chip->temperature <= COOL_TEMP_THRESHOLD) {//2~12 |
| if (chip->short_c_batt.dyna2_low_avg_dv_mv != INVALID_DATA |
| && chip->short_c_batt.dyna2_low_avg_dv_mv >= 0) { |
| if ((full_vbatt1 - full_vbatt_n - chip->short_c_batt.dyna2_low_avg_dv_mv > chip->short_c_batt.dyna2_delta_dv_mv) |
| && (full_vbatt1 - full_vbatt_n > chip->short_c_batt.full_delta_vbatt2_mv)) { |
| //set err_code5; |
| short_debug("set err_code[%d]\n", SHORT_C_BATT_STATUS__DYNAMIC_ERR_CODE5); |
| chip->short_c_batt.err_code = SHORT_C_BATT_STATUS__DYNAMIC_ERR_CODE5; |
| oplus_short_c_batt_write_err_code(chip->short_c_batt.err_code, full_temp1, |
| chip->temperature, full_vbatt1, full_vbatt_n, INVALID_CODE); |
| } |
| } |
| } else {//12~43 |
| if (chip->short_c_batt.dyna2_high_avg_dv_mv != INVALID_DATA |
| && chip->short_c_batt.dyna2_high_avg_dv_mv >= 0) { |
| if ((full_vbatt1 - full_vbatt_n - chip->short_c_batt.dyna2_high_avg_dv_mv > chip->short_c_batt.dyna2_delta_dv_mv) |
| && (full_vbatt1 - full_vbatt_n > chip->short_c_batt.full_delta_vbatt2_mv)) { |
| //set err_code5; |
| short_debug("set err_code[%d]\n", SHORT_C_BATT_STATUS__DYNAMIC_ERR_CODE5); |
| chip->short_c_batt.err_code = SHORT_C_BATT_STATUS__DYNAMIC_ERR_CODE5; |
| oplus_short_c_batt_write_err_code(chip->short_c_batt.err_code, full_temp1, |
| chip->temperature, full_vbatt1, full_vbatt_n, INVALID_CODE); |
| } |
| } |
| } |
| if (chip->short_c_batt.err_code != SHORT_C_BATT_STATUS__DYNAMIC_ERR_CODE5) { |
| short_debug("exit_check, EXIT_CODE__TIMER4_OVERFLOW\n"); |
| oplus_short_c_batt_write_exit_code(EXIT_CODE__TIMER4_OVERFLOW, |
| full_temp1, chip->temperature, full_vbatt1, full_vbatt_n); |
| } |
| //timer4 overflow, exit check |
| exit_check_status = true; |
| return; |
| } |
| } else { |
| full_timer4_count = chip->short_c_batt.full_timer4; |
| } |
| return; |
| } |
| return; |
| } |
| |
| /* pre CV check */ |
| if (chip->batt_volt > chip->short_c_batt.short_c_bat_cv_mv && chip->icharging < 0 |
| && (-1 * chip->icharging) < CV_IBATT_MA_THRESHOLD) { |
| cv_pre_check_count++; |
| if (chip->short_c_batt.cv_satus == false) { |
| short_debug("cv_pre_check_count[%d]\n", cv_pre_check_count); |
| } |
| if (cv_pre_check_count == CV_PRE_CHECK_COUNT) { |
| if (chip->short_c_batt.cv_satus == false) { |
| //reset exit code |
| oplus_short_c_batt_write_exit_code(0, 0, 0, 0, 0); |
| chip->short_c_batt.cv_satus = true; |
| cv_temp = chip->temperature; |
| } |
| } |
| if (cv_pre_check_count > CV_PRE_CHECK_COUNT) { |
| cv_pre_check_count = CV_PRE_CHECK_COUNT + 1; |
| } |
| } else { |
| if (chip->short_c_batt.cv_satus == false) { |
| short_debug("short_c_batt.cv_satus == false\n"); |
| goto cv_reset; |
| } |
| } |
| |
| /* CV check */ |
| if (chip->short_c_batt.cv_satus == true) { |
| cv_timer1_count++; |
| short_debug("cv_timer1_count[%d]\n", cv_timer1_count); |
| if (chip->batt_volt <= chip->short_c_batt.short_c_bat_cv_mv |
| || (chip->icharging < 0 && (-1 * chip->icharging) >= CV_IBATT_MA_THRESHOLD) |
| || abs(pre_icharging - chip->icharging) >= CV_DELTA_IBATT_MA_THRESHOLD) { |
| cv_delta_ibatt_count++; |
| if (cv_delta_ibatt_count == CV_DELTA_IBATT_COUNT) { |
| goto cv_reset; |
| } |
| } else { |
| if ((-1 * chip->icharging) < chip->limits.iterm_ma + 50) { |
| cv_iterm_count++; |
| if (cv_vbatt_count < CV_VBATT_COUNT) { |
| short_debug("cv_iterm_count[%d], cv_avg_vbatt[%d]\n", cv_iterm_count, cv_avg_vbatt); |
| } |
| if (cv_iterm_count > CV_ITERM_COUNT) { |
| cv_iterm_count = CV_ITERM_COUNT; |
| } |
| } else if (cv_iterm_count < CV_ITERM_COUNT) { |
| cv_iterm_count = 0; |
| } |
| |
| if (cv_iterm_count == CV_ITERM_COUNT && cv_vbatt_count < CV_VBATT_COUNT) { |
| #if 0 |
| cv_avg_vbatt = cv_avg_vbatt + chip->batt_volt; |
| cv_vbatt_count++; |
| if (cv_vbatt_count == CV_VBATT_COUNT) |
| cv_avg_vbatt = cv_avg_vbatt / CV_VBATT_COUNT; |
| short_debug("cv_vbatt_count[%d], cv_avg_vbatt[%d]\n", cv_vbatt_count, cv_avg_vbatt); |
| #else |
| /* 2017-12-18 use the max value instead of avg value */ |
| cv_avg_vbatt = max(cv_avg_vbatt, chip->batt_volt); |
| cv_vbatt_count++; |
| short_debug("cv_vbatt_count[%d], max cv_avg_vbatt[%d]\n", cv_vbatt_count, cv_avg_vbatt); |
| #endif |
| } |
| } |
| |
| if (cv_timer1_count == chip->short_c_batt.cv_timer1) { |
| //set err_code1 |
| short_debug("set err_code[%d]\n", SHORT_C_BATT_STATUS__CV_ERR_CODE1); |
| chip->short_c_batt.err_code = SHORT_C_BATT_STATUS__CV_ERR_CODE1; |
| oplus_short_c_batt_write_err_code(SHORT_C_BATT_STATUS__CV_ERR_CODE1, |
| cv_temp, chip->temperature, INVALID_CODE, INVALID_CODE, INVALID_CODE); |
| //turnoff charging |
| if (chip->short_c_batt.is_switch_on) { |
| chip->chg_ops->charging_disable(); |
| } |
| } |
| } |
| |
| pre_icharging = chip->icharging; |
| return; |
| |
| exit_check_reset: |
| exit_check_status = false; |
| |
| cv_reset: |
| cv_pre_check_count = 0; |
| chip->short_c_batt.cv_satus = false; |
| cv_temp = INVALID_TEMP; |
| cv_timer1_count = 0; |
| cv_delta_ibatt_count = 0; |
| cv_iterm_count = 0; |
| cv_avg_vbatt = 0; |
| cv_vbatt_count = 0; |
| |
| //full_reset: |
| full_over_10ma_count = 0; |
| full_over_20ma_count = 0; |
| full_over_50ma_count = 0; |
| full_timer2_count = 0; |
| full_timer3_count = 0; |
| full_vbatt1 = 0; |
| full_temp1 = INVALID_TEMP; |
| full_timer4_count = 0; |
| full_timer5_count = 0; |
| |
| pre_icharging = chip->icharging; |
| |
| oplus_short_c_batt_set_disable_rechg(chip, false); |
| oplus_short_c_batt_get_average_vbatt_mv(chip, true); |
| |
| return; |
| } |
| #else /* CONFIG_OPLUS_SHORT_C_BATT_CHECK */ |
| int oplus_short_c_batt_err_code_init(void) |
| { |
| return SHORT_C_BATT_STATUS__NORMAL; |
| } |
| |
| int oplus_short_c_batt_chg_switch_init(void) |
| { |
| return SHORT_C_BATT_SW_STATUS__OFF; |
| } |
| |
| int oplus_short_c_batt_feature_sw_status_init(void) |
| { |
| return SHORT_C_BATT_FEATURE_SW_STATUS__ON; |
| } |
| |
| int oplus_short_c_batt_feature_hw_status_init(void) |
| { |
| return SHORT_C_BATT_FEATURE_HW_STATUS__ON; |
| } |
| |
| bool oplus_short_c_batt_is_prohibit_chg(struct oplus_chg_chip *chip) |
| { |
| return false; |
| } |
| |
| bool oplus_short_c_batt_is_disable_rechg(struct oplus_chg_chip *chip) |
| { |
| return false; |
| } |
| |
| bool oplus_short_c_batt_get_cv_status(struct oplus_chg_chip *chip) |
| { |
| return false; |
| } |
| |
| void oplus_short_c_batt_update_change(struct oplus_chg_chip *chip, int update_value) |
| { |
| return; |
| } |
| |
| void oplus_chg_short_c_battery_check(struct oplus_chg_chip *chip) |
| { |
| return; |
| } |
| #endif /* CONFIG_OPLUS_SHORT_C_BATT_CHECK */ |