// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ /* * * Filename: * --------- * mtk_charger.c * * Project: * -------- * Android_Software * * Description: * ------------ * This Module defines functions of Battery charging * * Author: * ------- * Wy Chuang * */ #include /* For init/exit macros */ #include /* For MODULE_ marcros */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mtk_charger.h" #include "mtk_battery.h" struct tag_bootmode { u32 size; u32 tag; u32 bootmode; u32 boottype; }; #ifdef MODULE static char __chg_cmdline[COMMAND_LINE_SIZE]; static char *chg_cmdline = __chg_cmdline; const char *chg_get_cmd(void) { struct device_node *of_chosen = NULL; char *bootargs = NULL; if (__chg_cmdline[0] != 0) return chg_cmdline; of_chosen = of_find_node_by_path("/chosen"); if (of_chosen) { bootargs = (char *)of_get_property( of_chosen, "bootargs", NULL); if (!bootargs) chr_err("%s: failed to get bootargs\n", __func__); else { strcpy(__chg_cmdline, bootargs); chr_err("%s: bootargs: %s\n", __func__, bootargs); } } else chr_err("%s: failed to get /chosen\n", __func__); return chg_cmdline; } #else const char *chg_get_cmd(void) { return saved_command_line; } #endif int chr_get_debug_level(void) { struct power_supply *psy; static struct mtk_charger *info; int ret; if (info == NULL) { psy = power_supply_get_by_name("mtk-master-charger"); if (psy == NULL) ret = CHRLOG_DEBUG_LEVEL; else { info = (struct mtk_charger *)power_supply_get_drvdata(psy); if (info == NULL) ret = CHRLOG_DEBUG_LEVEL; else ret = info->log_level; } } else ret = info->log_level; return ret; } EXPORT_SYMBOL(chr_get_debug_level); void _wake_up_charger(struct mtk_charger *info) { unsigned long flags; if (info == NULL) return; spin_lock_irqsave(&info->slock, flags); if (!info->charger_wakelock->active) __pm_stay_awake(info->charger_wakelock); spin_unlock_irqrestore(&info->slock, flags); info->charger_thread_timeout = true; wake_up_interruptible(&info->wait_que); } bool is_disable_charger(struct mtk_charger *info) { if (info == NULL) return true; if (info->disable_charger == true || IS_ENABLED(CONFIG_POWER_EXT)) return true; else return false; } int _mtk_enable_charging(struct mtk_charger *info, bool en) { chr_debug("%s en:%d\n", __func__, en); if (info->algo.enable_charging != NULL) return info->algo.enable_charging(info, en); return false; } int mtk_charger_notifier(struct mtk_charger *info, int event) { return srcu_notifier_call_chain(&info->evt_nh, event, NULL); } static void mtk_charger_parse_dt(struct mtk_charger *info, struct device *dev) { struct device_node *np = dev->of_node; u32 val = 0; struct device_node *boot_node = NULL; struct tag_bootmode *tag = NULL; boot_node = of_parse_phandle(dev->of_node, "bootmode", 0); if (!boot_node) chr_err("%s: failed to get boot mode phandle\n", __func__); else { tag = (struct tag_bootmode *)of_get_property(boot_node, "atag,boot", NULL); if (!tag) chr_err("%s: failed to get atag,boot\n", __func__); else { chr_err("%s: size:0x%x tag:0x%x bootmode:0x%x boottype:0x%x\n", __func__, tag->size, tag->tag, tag->bootmode, tag->boottype); info->bootmode = tag->bootmode; info->boottype = tag->boottype; } } if (of_property_read_string(np, "algorithm_name", &info->algorithm_name) < 0) { chr_err("%s: no algorithm_name name\n", __func__); info->algorithm_name = "Basic"; } if (strcmp(info->algorithm_name, "Basic") == 0) { chr_err("found Basic\n"); mtk_basic_charger_init(info); } else if (strcmp(info->algorithm_name, "Pulse") == 0) { chr_err("found Pulse\n"); mtk_pulse_charger_init(info); } info->disable_charger = of_property_read_bool(np, "disable_charger"); info->charger_unlimited = of_property_read_bool(np, "charger_unlimited"); info->atm_enabled = of_property_read_bool(np, "atm_is_enabled"); info->enable_sw_safety_timer = of_property_read_bool(np, "enable_sw_safety_timer"); info->sw_safety_timer_setting = info->enable_sw_safety_timer; info->disable_aicl = of_property_read_bool(np, "disable_aicl"); info->alg_new_arbitration = of_property_read_bool(np, "alg_new_arbitration"); info->alg_unchangeable = of_property_read_bool(np, "alg_unchangeable"); /* common */ if (of_property_read_u32(np, "charger_configuration", &val) >= 0) info->config = val; else { chr_err("use default charger_configuration:%d\n", SINGLE_CHARGER); info->config = SINGLE_CHARGER; } if (of_property_read_u32(np, "battery_cv", &val) >= 0) info->data.battery_cv = val; else { chr_err("use default BATTERY_CV:%d\n", BATTERY_CV); info->data.battery_cv = BATTERY_CV; } if (of_property_read_u32(np, "max_charger_voltage", &val) >= 0) info->data.max_charger_voltage = val; else { chr_err("use default V_CHARGER_MAX:%d\n", V_CHARGER_MAX); info->data.max_charger_voltage = V_CHARGER_MAX; } info->data.max_charger_voltage_setting = info->data.max_charger_voltage; if (of_property_read_u32(np, "vbus_sw_ovp_voltage", &val) >= 0) info->data.vbus_sw_ovp_voltage = val; else { chr_err("use default V_CHARGER_MAX:%d\n", V_CHARGER_MAX); info->data.vbus_sw_ovp_voltage = VBUS_OVP_VOLTAGE; } if (of_property_read_u32(np, "min_charger_voltage", &val) >= 0) info->data.min_charger_voltage = val; else { chr_err("use default V_CHARGER_MIN:%d\n", V_CHARGER_MIN); info->data.min_charger_voltage = V_CHARGER_MIN; } if (of_property_read_u32(np, "enable_vbat_mon", &val) >= 0) { info->enable_vbat_mon = val; info->enable_vbat_mon_bak = val; } else if (of_property_read_u32(np, "enable-vbat-mon", &val) >= 0) { info->enable_vbat_mon = val; info->enable_vbat_mon_bak = val; } else { chr_err("use default enable 6pin\n"); info->enable_vbat_mon = 0; info->enable_vbat_mon_bak = 0; } chr_err("use 6pin bat, enable_vbat_mon:%d\n", info->enable_vbat_mon); /* sw jeita */ info->enable_sw_jeita = of_property_read_bool(np, "enable_sw_jeita"); if (of_property_read_u32(np, "jeita_temp_above_t4_cv", &val) >= 0) info->data.jeita_temp_above_t4_cv = val; else { chr_err("use default JEITA_TEMP_ABOVE_T4_CV:%d\n", JEITA_TEMP_ABOVE_T4_CV); info->data.jeita_temp_above_t4_cv = JEITA_TEMP_ABOVE_T4_CV; } if (of_property_read_u32(np, "jeita_temp_t3_to_t4_cv", &val) >= 0) info->data.jeita_temp_t3_to_t4_cv = val; else { chr_err("use default JEITA_TEMP_T3_TO_T4_CV:%d\n", JEITA_TEMP_T3_TO_T4_CV); info->data.jeita_temp_t3_to_t4_cv = JEITA_TEMP_T3_TO_T4_CV; } if (of_property_read_u32(np, "jeita_temp_t2_to_t3_cv", &val) >= 0) info->data.jeita_temp_t2_to_t3_cv = val; else { chr_err("use default JEITA_TEMP_T2_TO_T3_CV:%d\n", JEITA_TEMP_T2_TO_T3_CV); info->data.jeita_temp_t2_to_t3_cv = JEITA_TEMP_T2_TO_T3_CV; } if (of_property_read_u32(np, "jeita_temp_t1_to_t2_cv", &val) >= 0) info->data.jeita_temp_t1_to_t2_cv = val; else { chr_err("use default JEITA_TEMP_T1_TO_T2_CV:%d\n", JEITA_TEMP_T1_TO_T2_CV); info->data.jeita_temp_t1_to_t2_cv = JEITA_TEMP_T1_TO_T2_CV; } if (of_property_read_u32(np, "jeita_temp_t0_to_t1_cv", &val) >= 0) info->data.jeita_temp_t0_to_t1_cv = val; else { chr_err("use default JEITA_TEMP_T0_TO_T1_CV:%d\n", JEITA_TEMP_T0_TO_T1_CV); info->data.jeita_temp_t0_to_t1_cv = JEITA_TEMP_T0_TO_T1_CV; } if (of_property_read_u32(np, "jeita_temp_below_t0_cv", &val) >= 0) info->data.jeita_temp_below_t0_cv = val; else { chr_err("use default JEITA_TEMP_BELOW_T0_CV:%d\n", JEITA_TEMP_BELOW_T0_CV); info->data.jeita_temp_below_t0_cv = JEITA_TEMP_BELOW_T0_CV; } if (of_property_read_u32(np, "temp_t4_thres", &val) >= 0) info->data.temp_t4_thres = val; else { chr_err("use default TEMP_T4_THRES:%d\n", TEMP_T4_THRES); info->data.temp_t4_thres = TEMP_T4_THRES; } if (of_property_read_u32(np, "temp_t4_thres_minus_x_degree", &val) >= 0) info->data.temp_t4_thres_minus_x_degree = val; else { chr_err("use default TEMP_T4_THRES_MINUS_X_DEGREE:%d\n", TEMP_T4_THRES_MINUS_X_DEGREE); info->data.temp_t4_thres_minus_x_degree = TEMP_T4_THRES_MINUS_X_DEGREE; } if (of_property_read_u32(np, "temp_t3_thres", &val) >= 0) info->data.temp_t3_thres = val; else { chr_err("use default TEMP_T3_THRES:%d\n", TEMP_T3_THRES); info->data.temp_t3_thres = TEMP_T3_THRES; } if (of_property_read_u32(np, "temp_t3_thres_minus_x_degree", &val) >= 0) info->data.temp_t3_thres_minus_x_degree = val; else { chr_err("use default TEMP_T3_THRES_MINUS_X_DEGREE:%d\n", TEMP_T3_THRES_MINUS_X_DEGREE); info->data.temp_t3_thres_minus_x_degree = TEMP_T3_THRES_MINUS_X_DEGREE; } if (of_property_read_u32(np, "temp_t2_thres", &val) >= 0) info->data.temp_t2_thres = val; else { chr_err("use default TEMP_T2_THRES:%d\n", TEMP_T2_THRES); info->data.temp_t2_thres = TEMP_T2_THRES; } if (of_property_read_u32(np, "temp_t2_thres_plus_x_degree", &val) >= 0) info->data.temp_t2_thres_plus_x_degree = val; else { chr_err("use default TEMP_T2_THRES_PLUS_X_DEGREE:%d\n", TEMP_T2_THRES_PLUS_X_DEGREE); info->data.temp_t2_thres_plus_x_degree = TEMP_T2_THRES_PLUS_X_DEGREE; } if (of_property_read_u32(np, "temp_t1_thres", &val) >= 0) info->data.temp_t1_thres = val; else { chr_err("use default TEMP_T1_THRES:%d\n", TEMP_T1_THRES); info->data.temp_t1_thres = TEMP_T1_THRES; } if (of_property_read_u32(np, "temp_t1_thres_plus_x_degree", &val) >= 0) info->data.temp_t1_thres_plus_x_degree = val; else { chr_err("use default TEMP_T1_THRES_PLUS_X_DEGREE:%d\n", TEMP_T1_THRES_PLUS_X_DEGREE); info->data.temp_t1_thres_plus_x_degree = TEMP_T1_THRES_PLUS_X_DEGREE; } if (of_property_read_u32(np, "temp_t0_thres", &val) >= 0) info->data.temp_t0_thres = val; else { chr_err("use default TEMP_T0_THRES:%d\n", TEMP_T0_THRES); info->data.temp_t0_thres = TEMP_T0_THRES; } if (of_property_read_u32(np, "temp_t0_thres_plus_x_degree", &val) >= 0) info->data.temp_t0_thres_plus_x_degree = val; else { chr_err("use default TEMP_T0_THRES_PLUS_X_DEGREE:%d\n", TEMP_T0_THRES_PLUS_X_DEGREE); info->data.temp_t0_thres_plus_x_degree = TEMP_T0_THRES_PLUS_X_DEGREE; } if (of_property_read_u32(np, "temp_neg_10_thres", &val) >= 0) info->data.temp_neg_10_thres = val; else { chr_err("use default TEMP_NEG_10_THRES:%d\n", TEMP_NEG_10_THRES); info->data.temp_neg_10_thres = TEMP_NEG_10_THRES; } /*prize LiuYong, modify for charging current config, 20230323 -start*/ if (of_property_read_u32(np, "jeita_temp_t0_to_t1_input_current", &val) >= 0) info->data.jeita_temp_t0_to_t1_input_current = val; else { chr_err("use default jeita_temp_t0_to_t1_input_current:%d\n", JEITA_TEMP_T0_TO_T1_INPUT_CURRENT); info->data.jeita_temp_t0_to_t1_input_current = JEITA_TEMP_T0_TO_T1_INPUT_CURRENT; } if (of_property_read_u32(np, "jeita_temp_t0_to_t1_charging_current", &val) >= 0) info->data.jeita_temp_t0_to_t1_charging_current = val; else { chr_err("use default jeita_temp_t0_to_t1_charging_current:%d\n", JEITA_TEMP_T0_TO_T1_CHARGING_CURRENT); info->data.jeita_temp_t0_to_t1_charging_current = JEITA_TEMP_T0_TO_T1_CHARGING_CURRENT; } if (of_property_read_u32(np, "jeita_temp_t1_to_t2_input_current", &val) >= 0) info->data.jeita_temp_t1_to_t2_input_current = val; else { chr_err("use default jeita_temp_t1_to_t2_input_current:%d\n", JEITA_TEMP_T1_TO_T2_INPUT_CURRENT); info->data.jeita_temp_t1_to_t2_input_current = JEITA_TEMP_T1_TO_T2_INPUT_CURRENT; } if (of_property_read_u32(np, "jeita_temp_t1_to_t2_charging_current", &val) >= 0) info->data.jeita_temp_t1_to_t2_charging_current = val; else { chr_err("use default jeita_temp_t1_to_t2_charging_current:%d\n", JEITA_TEMP_T1_TO_T2_CHARGING_CURRENT); info->data.jeita_temp_t1_to_t2_charging_current = JEITA_TEMP_T1_TO_T2_CHARGING_CURRENT; } if (of_property_read_u32(np, "jeita_temp_t2_to_t3_input_current", &val) >= 0) info->data.jeita_temp_t2_to_t3_input_current = val; else { chr_err("use default jeita_temp_t2_to_t3_input_current:%d\n", JEITA_TEMP_T2_TO_T3_INPUT_CURRENT); info->data.jeita_temp_t2_to_t3_input_current = JEITA_TEMP_T2_TO_T3_INPUT_CURRENT; } if (of_property_read_u32(np, "jeita_temp_t2_to_t3_charging_current", &val) >= 0) info->data.jeita_temp_t2_to_t3_charging_current = val; else { chr_err("use default jeita_temp_t2_to_t3_charging_current:%d\n", JEITA_TEMP_T2_TO_T3_CHARGING_CURRENT); info->data.jeita_temp_t2_to_t3_charging_current = JEITA_TEMP_T2_TO_T3_CHARGING_CURRENT; } if (of_property_read_u32(np, "jeita_temp_t3_to_t4_input_current", &val) >= 0) info->data.jeita_temp_t3_to_t4_input_current = val; else { chr_err("use default jeita_temp_t3_to_t4_input_current:%d\n", JEITA_TEMP_T3_TO_T4_INPUT_CURRENT); info->data.jeita_temp_t3_to_t4_input_current = JEITA_TEMP_T3_TO_T4_INPUT_CURRENT; } if (of_property_read_u32(np, "jeita_temp_t3_to_t4_charging_current", &val) >= 0) info->data.jeita_temp_t3_to_t4_charging_current = val; else { chr_err("use default jeita_temp_t3_to_t4_charging_current:%d\n", JEITA_TEMP_T3_TO_T4_CHARGING_CURRENT); info->data.jeita_temp_t3_to_t4_charging_current = JEITA_TEMP_T3_TO_T4_CHARGING_CURRENT; } if (of_property_read_u32(np, "temp_screen_on_input_current", &val) >= 0) info->data.temp_screen_on_input_current = val; else { chr_err("use default temp_screen_on_input_current:%d\n", 1500000); info->data.temp_screen_on_input_current = 1500000; } if (of_property_read_u32(np, "temp_screen_on_charging_current", &val) >= 0) info->data.temp_screen_on_charging_current = val; else { chr_err("use default temp_screen_on_charging_current:%d\n", 1500000); info->data.temp_screen_on_charging_current = 1500000; } /*prize LiuYong, modify for charging current config, 20230323 -end*/ /* battery temperature protection */ info->thermal.sm = BAT_TEMP_NORMAL; info->thermal.enable_min_charge_temp = of_property_read_bool(np, "enable_min_charge_temp"); if (of_property_read_u32(np, "min_charge_temp", &val) >= 0) info->thermal.min_charge_temp = val; else { chr_err("use default MIN_CHARGE_TEMP:%d\n", MIN_CHARGE_TEMP); info->thermal.min_charge_temp = MIN_CHARGE_TEMP; } if (of_property_read_u32(np, "min_charge_temp_plus_x_degree", &val) >= 0) { info->thermal.min_charge_temp_plus_x_degree = val; } else { chr_err("use default MIN_CHARGE_TEMP_PLUS_X_DEGREE:%d\n", MIN_CHARGE_TEMP_PLUS_X_DEGREE); info->thermal.min_charge_temp_plus_x_degree = MIN_CHARGE_TEMP_PLUS_X_DEGREE; } if (of_property_read_u32(np, "max_charge_temp", &val) >= 0) info->thermal.max_charge_temp = val; else { chr_err("use default MAX_CHARGE_TEMP:%d\n", MAX_CHARGE_TEMP); info->thermal.max_charge_temp = MAX_CHARGE_TEMP; } if (of_property_read_u32(np, "max_charge_temp_minus_x_degree", &val) >= 0) { info->thermal.max_charge_temp_minus_x_degree = val; } else { chr_err("use default MAX_CHARGE_TEMP_MINUS_X_DEGREE:%d\n", MAX_CHARGE_TEMP_MINUS_X_DEGREE); info->thermal.max_charge_temp_minus_x_degree = MAX_CHARGE_TEMP_MINUS_X_DEGREE; } /* charging current */ if (of_property_read_u32(np, "usb_charger_current", &val) >= 0) { info->data.usb_charger_current = val; } else { chr_err("use default USB_CHARGER_CURRENT:%d\n", USB_CHARGER_CURRENT); info->data.usb_charger_current = USB_CHARGER_CURRENT; } if (of_property_read_u32(np, "ac_charger_current", &val) >= 0) { info->data.ac_charger_current = val; } else { chr_err("use default AC_CHARGER_CURRENT:%d\n", AC_CHARGER_CURRENT); info->data.ac_charger_current = AC_CHARGER_CURRENT; } if (of_property_read_u32(np, "ac_charger_input_current", &val) >= 0) info->data.ac_charger_input_current = val; else { chr_err("use default AC_CHARGER_INPUT_CURRENT:%d\n", AC_CHARGER_INPUT_CURRENT); info->data.ac_charger_input_current = AC_CHARGER_INPUT_CURRENT; } if (of_property_read_u32(np, "charging_host_charger_current", &val) >= 0) { info->data.charging_host_charger_current = val; } else { chr_err("use default CHARGING_HOST_CHARGER_CURRENT:%d\n", CHARGING_HOST_CHARGER_CURRENT); info->data.charging_host_charger_current = CHARGING_HOST_CHARGER_CURRENT; } /* dynamic mivr */ info->enable_dynamic_mivr = of_property_read_bool(np, "enable_dynamic_mivr"); if (of_property_read_u32(np, "min_charger_voltage_1", &val) >= 0) info->data.min_charger_voltage_1 = val; else { chr_err("use default V_CHARGER_MIN_1: %d\n", V_CHARGER_MIN_1); info->data.min_charger_voltage_1 = V_CHARGER_MIN_1; } if (of_property_read_u32(np, "min_charger_voltage_2", &val) >= 0) info->data.min_charger_voltage_2 = val; else { chr_err("use default V_CHARGER_MIN_2: %d\n", V_CHARGER_MIN_2); info->data.min_charger_voltage_2 = V_CHARGER_MIN_2; } if (of_property_read_u32(np, "max_dmivr_charger_current", &val) >= 0) info->data.max_dmivr_charger_current = val; else { chr_err("use default MAX_DMIVR_CHARGER_CURRENT: %d\n", MAX_DMIVR_CHARGER_CURRENT); info->data.max_dmivr_charger_current = MAX_DMIVR_CHARGER_CURRENT; } /* fast charging algo support indicator */ info->enable_fast_charging_indicator = of_property_read_bool(np, "enable_fast_charging_indicator"); } static void mtk_charger_start_timer(struct mtk_charger *info) { struct timespec64 end_time, time_now; ktime_t ktime, ktime_now; int ret = 0; /* If the timer was already set, cancel it */ ret = alarm_try_to_cancel(&info->charger_timer); if (ret < 0) { chr_err("%s: callback was running, skip timer\n", __func__); return; } ktime_now = ktime_get_boottime(); time_now = ktime_to_timespec64(ktime_now); end_time.tv_sec = time_now.tv_sec + info->polling_interval; end_time.tv_nsec = time_now.tv_nsec + 0; info->endtime = end_time; ktime = ktime_set(info->endtime.tv_sec, info->endtime.tv_nsec); chr_err("%s: alarm timer start:%d, %ld %ld\n", __func__, ret, info->endtime.tv_sec, info->endtime.tv_nsec); alarm_start(&info->charger_timer, ktime); } static void check_battery_exist(struct mtk_charger *info) { unsigned int i = 0; int count = 0; //int boot_mode = get_boot_mode(); if (is_disable_charger(info)) return; for (i = 0; i < 3; i++) { if (is_battery_exist(info) == false) count++; } #ifdef FIXME if (count >= 3) { if (boot_mode == META_BOOT || boot_mode == ADVMETA_BOOT || boot_mode == ATE_FACTORY_BOOT) chr_info("boot_mode = %d, bypass battery check\n", boot_mode); else { chr_err("battery doesn't exist, shutdown\n"); orderly_poweroff(true); } } #endif } static void check_dynamic_mivr(struct mtk_charger *info) { int i = 0, ret = 0; int vbat = 0; bool is_fast_charge = false; struct chg_alg_device *alg = NULL; if (!info->enable_dynamic_mivr) return; for (i = 0; i < MAX_ALG_NO; i++) { alg = info->alg[i]; if (alg == NULL) continue; ret = chg_alg_is_algo_ready(alg); if (ret == ALG_RUNNING) { is_fast_charge = true; break; } } if (!is_fast_charge) { vbat = get_battery_voltage(info); if (vbat < info->data.min_charger_voltage_2 / 1000 - 200) charger_dev_set_mivr(info->chg1_dev, info->data.min_charger_voltage_2); else if (vbat < info->data.min_charger_voltage_1 / 1000 - 200) charger_dev_set_mivr(info->chg1_dev, info->data.min_charger_voltage_1); else charger_dev_set_mivr(info->chg1_dev, info->data.min_charger_voltage); } } /* sw jeita */ void do_sw_jeita_state_machine(struct mtk_charger *info) { struct sw_jeita_data *sw_jeita; sw_jeita = &info->sw_jeita; sw_jeita->pre_sm = sw_jeita->sm; sw_jeita->charging = true; /* JEITA battery temp Standard */ if (info->battery_temp >= info->data.temp_t4_thres) { chr_err("[SW_JEITA] Battery Over high Temperature(%d) !!\n", info->data.temp_t4_thres); sw_jeita->sm = TEMP_ABOVE_T4; sw_jeita->charging = false; } else if (info->battery_temp > info->data.temp_t3_thres) { /* control 45 degree to normal behavior */ if ((sw_jeita->sm == TEMP_ABOVE_T4) && (info->battery_temp >= info->data.temp_t4_thres_minus_x_degree)) { chr_err("[SW_JEITA] Battery Temperature between %d and %d,not allow charging yet!!\n", info->data.temp_t4_thres_minus_x_degree, info->data.temp_t4_thres); sw_jeita->charging = false; } else { chr_err("[SW_JEITA] Battery Temperature between %d and %d !!\n", info->data.temp_t3_thres, info->data.temp_t4_thres); sw_jeita->sm = TEMP_T3_TO_T4; } } else if (info->battery_temp >= info->data.temp_t2_thres) { if (((sw_jeita->sm == TEMP_T3_TO_T4) && (info->battery_temp >= info->data.temp_t3_thres_minus_x_degree)) || ((sw_jeita->sm == TEMP_T1_TO_T2) && (info->battery_temp <= info->data.temp_t2_thres_plus_x_degree))) { chr_err("[SW_JEITA] Battery Temperature not recovery to normal temperature charging mode yet!!\n"); } else { chr_err("[SW_JEITA] Battery Normal Temperature between %d and %d !!\n", info->data.temp_t2_thres, info->data.temp_t3_thres); sw_jeita->sm = TEMP_T2_TO_T3; } } else if (info->battery_temp >= info->data.temp_t1_thres) { if ((sw_jeita->sm == TEMP_T0_TO_T1 || sw_jeita->sm == TEMP_BELOW_T0) && (info->battery_temp <= info->data.temp_t1_thres_plus_x_degree)) { if (sw_jeita->sm == TEMP_T0_TO_T1) { chr_err("[SW_JEITA] Battery Temperature between %d and %d !!\n", info->data.temp_t1_thres_plus_x_degree, info->data.temp_t2_thres); } if (sw_jeita->sm == TEMP_BELOW_T0) { chr_err("[SW_JEITA] Battery Temperature between %d and %d,not allow charging yet!!\n", info->data.temp_t1_thres, info->data.temp_t1_thres_plus_x_degree); sw_jeita->charging = false; } } else { chr_err("[SW_JEITA] Battery Temperature between %d and %d !!\n", info->data.temp_t1_thres, info->data.temp_t2_thres); sw_jeita->sm = TEMP_T1_TO_T2; } } else if (info->battery_temp >= info->data.temp_t0_thres) { if ((sw_jeita->sm == TEMP_BELOW_T0) && (info->battery_temp <= info->data.temp_t0_thres_plus_x_degree)) { chr_err("[SW_JEITA] Battery Temperature between %d and %d,not allow charging yet!!\n", info->data.temp_t0_thres, info->data.temp_t0_thres_plus_x_degree); sw_jeita->charging = false; } else { chr_err("[SW_JEITA] Battery Temperature between %d and %d !!\n", info->data.temp_t0_thres, info->data.temp_t1_thres); sw_jeita->sm = TEMP_T0_TO_T1; } } else { chr_err("[SW_JEITA] Battery below low Temperature(%d) !!\n", info->data.temp_t0_thres); sw_jeita->sm = TEMP_BELOW_T0; sw_jeita->charging = false; } /* set CV after temperature changed */ /* In normal range, we adjust CV dynamically */ if (sw_jeita->sm != TEMP_T2_TO_T3) { if (sw_jeita->sm == TEMP_ABOVE_T4) sw_jeita->cv = info->data.jeita_temp_above_t4_cv; else if (sw_jeita->sm == TEMP_T3_TO_T4) sw_jeita->cv = info->data.jeita_temp_t3_to_t4_cv; else if (sw_jeita->sm == TEMP_T2_TO_T3) sw_jeita->cv = 0; else if (sw_jeita->sm == TEMP_T1_TO_T2) sw_jeita->cv = info->data.jeita_temp_t1_to_t2_cv; else if (sw_jeita->sm == TEMP_T0_TO_T1) sw_jeita->cv = info->data.jeita_temp_t0_to_t1_cv; else if (sw_jeita->sm == TEMP_BELOW_T0) sw_jeita->cv = info->data.jeita_temp_below_t0_cv; else sw_jeita->cv = info->data.battery_cv; } else { sw_jeita->cv = 0; } chr_err("[SW_JEITA]preState:%d newState:%d tmp:%d cv:%d\n", sw_jeita->pre_sm, sw_jeita->sm, info->battery_temp, sw_jeita->cv); } static int mtk_chgstat_notify(struct mtk_charger *info) { int ret = 0; char *env[2] = { "CHGSTAT=1", NULL }; chr_err("%s: 0x%x\n", __func__, info->notify_code); ret = kobject_uevent_env(&info->pdev->dev.kobj, KOBJ_CHANGE, env); if (ret) chr_err("%s: kobject_uevent_fail, ret=%d", __func__, ret); return ret; } static void mtk_charger_set_algo_log_level(struct mtk_charger *info, int level) { struct chg_alg_device *alg; int i = 0, ret = 0; for (i = 0; i < MAX_ALG_NO; i++) { alg = info->alg[i]; if (alg == NULL) continue; ret = chg_alg_set_prop(alg, ALG_LOG_LEVEL, level); if (ret < 0) chr_err("%s: set ALG_LOG_LEVEL fail, ret =%d", __func__, ret); } } static ssize_t sw_jeita_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mtk_charger *pinfo = dev->driver_data; chr_err("%s: %d\n", __func__, pinfo->enable_sw_jeita); return sprintf(buf, "%d\n", pinfo->enable_sw_jeita); } static ssize_t sw_jeita_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct mtk_charger *pinfo = dev->driver_data; signed int temp; if (kstrtoint(buf, 10, &temp) == 0) { if (temp == 0) pinfo->enable_sw_jeita = false; else pinfo->enable_sw_jeita = true; } else { chr_err("%s: format error!\n", __func__); } return size; } static DEVICE_ATTR_RW(sw_jeita); /* sw jeita end*/ static ssize_t sw_ovp_threshold_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mtk_charger *pinfo = dev->driver_data; chr_err("%s: %d\n", __func__, pinfo->data.max_charger_voltage); return sprintf(buf, "%d\n", pinfo->data.max_charger_voltage); } static ssize_t sw_ovp_threshold_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct mtk_charger *pinfo = dev->driver_data; signed int temp; if (kstrtoint(buf, 10, &temp) == 0) { if (temp < 0) pinfo->data.max_charger_voltage = pinfo->data.vbus_sw_ovp_voltage; else pinfo->data.max_charger_voltage = temp; chr_err("%s: %d\n", __func__, pinfo->data.max_charger_voltage); } else { chr_err("%s: format error!\n", __func__); } return size; } static DEVICE_ATTR_RW(sw_ovp_threshold); static ssize_t chr_type_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mtk_charger *pinfo = dev->driver_data; chr_err("%s: %d\n", __func__, pinfo->chr_type); return sprintf(buf, "%d\n", pinfo->chr_type); } static ssize_t chr_type_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct mtk_charger *pinfo = dev->driver_data; signed int temp; if (kstrtoint(buf, 10, &temp) == 0) pinfo->chr_type = temp; else chr_err("%s: format error!\n", __func__); return size; } static DEVICE_ATTR_RW(chr_type); static ssize_t pd_type_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mtk_charger *pinfo = dev->driver_data; char *pd_type_name = "None"; switch (pinfo->pd_type) { case MTK_PD_CONNECT_NONE: pd_type_name = "None"; break; case MTK_PD_CONNECT_PE_READY_SNK: pd_type_name = "PD"; break; case MTK_PD_CONNECT_PE_READY_SNK_PD30: pd_type_name = "PD"; break; case MTK_PD_CONNECT_PE_READY_SNK_APDO: pd_type_name = "PD with PPS"; break; case MTK_PD_CONNECT_TYPEC_ONLY_SNK: pd_type_name = "normal"; break; } chr_err("%s: %d\n", __func__, pinfo->pd_type); return sprintf(buf, "%s\n", pd_type_name); } static DEVICE_ATTR_RO(pd_type); static ssize_t Pump_Express_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret = 0, i = 0; bool is_ta_detected = false; struct mtk_charger *pinfo = dev->driver_data; struct chg_alg_device *alg = NULL; if (!pinfo) { chr_err("%s: pinfo is null\n", __func__); return sprintf(buf, "%d\n", is_ta_detected); } for (i = 0; i < MAX_ALG_NO; i++) { alg = pinfo->alg[i]; if (alg == NULL) continue; ret = chg_alg_is_algo_ready(alg); if (ret == ALG_RUNNING) { is_ta_detected = true; break; } } chr_err("%s: idx = %d, detect = %d\n", __func__, i, is_ta_detected); return sprintf(buf, "%d\n", is_ta_detected); } static DEVICE_ATTR_RO(Pump_Express); static ssize_t Charging_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret = 0, i = 0; char *alg_name = "normal"; bool is_ta_detected = false; struct mtk_charger *pinfo = dev->driver_data; struct chg_alg_device *alg = NULL; if (!pinfo) { chr_err("%s: pinfo is null\n", __func__); return sprintf(buf, "%d\n", is_ta_detected); } for (i = 0; i < MAX_ALG_NO; i++) { alg = pinfo->alg[i]; if (alg == NULL) continue; ret = chg_alg_is_algo_ready(alg); if (ret == ALG_RUNNING) { is_ta_detected = true; break; } } if (alg == NULL) return sprintf(buf, "%s\n", alg_name); switch (alg->alg_id) { case PE_ID: alg_name = "PE"; break; case PE2_ID: alg_name = "PE2"; break; case PDC_ID: alg_name = "PDC"; break; case PE4_ID: alg_name = "PE4"; break; case PE5_ID: alg_name = "P5"; break; case PE5P_ID: alg_name = "P5P"; break; } chr_err("%s: charging_mode: %s\n", __func__, alg_name); return sprintf(buf, "%s\n", alg_name); } static DEVICE_ATTR_RO(Charging_mode); static ssize_t High_voltage_chg_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mtk_charger *pinfo = dev->driver_data; chr_err("%s: hv_charging = %d\n", __func__, pinfo->enable_hv_charging); return sprintf(buf, "%d\n", pinfo->enable_hv_charging); } static DEVICE_ATTR_RO(High_voltage_chg_enable); static ssize_t Rust_detect_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mtk_charger *pinfo = dev->driver_data; chr_err("%s: Rust detect = %d\n", __func__, pinfo->record_water_detected); return sprintf(buf, "%d\n", pinfo->record_water_detected); } static DEVICE_ATTR_RO(Rust_detect); static ssize_t Thermal_throttle_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mtk_charger *pinfo = dev->driver_data; struct charger_data *chg_data = &(pinfo->chg_data[CHG1_SETTING]); return sprintf(buf, "%d\n", chg_data->thermal_throttle_record); } static DEVICE_ATTR_RO(Thermal_throttle); static ssize_t fast_chg_indicator_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mtk_charger *pinfo = dev->driver_data; chr_debug("%s: %d\n", __func__, pinfo->fast_charging_indicator); return sprintf(buf, "%d\n", pinfo->fast_charging_indicator); } static ssize_t fast_chg_indicator_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct mtk_charger *pinfo = dev->driver_data; unsigned int temp; if (kstrtouint(buf, 10, &temp) == 0) pinfo->fast_charging_indicator = temp; else chr_err("%s: format error!\n", __func__); if ((pinfo->fast_charging_indicator > 0) && (pinfo->bootmode == 8 || pinfo->bootmode == 9)) { pinfo->log_level = CHRLOG_DEBUG_LEVEL; mtk_charger_set_algo_log_level(pinfo, pinfo->log_level); } _wake_up_charger(pinfo); return size; } static DEVICE_ATTR_RW(fast_chg_indicator); static ssize_t alg_new_arbitration_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mtk_charger *pinfo = dev->driver_data; chr_debug("%s: %d\n", __func__, pinfo->alg_new_arbitration); return sprintf(buf, "%d\n", pinfo->alg_new_arbitration); } static ssize_t alg_new_arbitration_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct mtk_charger *pinfo = dev->driver_data; unsigned int temp; if (kstrtouint(buf, 10, &temp) == 0) pinfo->alg_new_arbitration = temp; else chr_err("%s: format error!\n", __func__); _wake_up_charger(pinfo); return size; } static DEVICE_ATTR_RW(alg_new_arbitration); static ssize_t alg_unchangeable_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mtk_charger *pinfo = dev->driver_data; chr_debug("%s: %d\n", __func__, pinfo->alg_unchangeable); return sprintf(buf, "%d\n", pinfo->alg_unchangeable); } static ssize_t alg_unchangeable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct mtk_charger *pinfo = dev->driver_data; unsigned int temp; if (kstrtouint(buf, 10, &temp) == 0) pinfo->alg_unchangeable = temp; else chr_err("%s: format error!\n", __func__); _wake_up_charger(pinfo); return size; } static DEVICE_ATTR_RW(alg_unchangeable); static ssize_t enable_meta_current_limit_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mtk_charger *pinfo = dev->driver_data; chr_debug("%s: %d\n", __func__, pinfo->enable_meta_current_limit); return sprintf(buf, "%d\n", pinfo->enable_meta_current_limit); } static ssize_t enable_meta_current_limit_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct mtk_charger *pinfo = dev->driver_data; unsigned int temp; if (kstrtouint(buf, 10, &temp) == 0) pinfo->enable_meta_current_limit = temp; else chr_err("%s: format error!\n", __func__); if (pinfo->enable_meta_current_limit > 0) { pinfo->log_level = CHRLOG_DEBUG_LEVEL; mtk_charger_set_algo_log_level(pinfo, pinfo->log_level); } _wake_up_charger(pinfo); return size; } static DEVICE_ATTR_RW(enable_meta_current_limit); static ssize_t vbat_mon_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mtk_charger *pinfo = dev->driver_data; chr_debug("%s: %d\n", __func__, pinfo->enable_vbat_mon); return sprintf(buf, "%d\n", pinfo->enable_vbat_mon); } static ssize_t vbat_mon_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct mtk_charger *pinfo = dev->driver_data; unsigned int temp; if (kstrtouint(buf, 10, &temp) == 0) { if (temp == 0) pinfo->enable_vbat_mon = false; else pinfo->enable_vbat_mon = true; } else { chr_err("%s: format error!\n", __func__); } _wake_up_charger(pinfo); return size; } static DEVICE_ATTR_RW(vbat_mon); static ssize_t ADC_Charger_Voltage_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mtk_charger *pinfo = dev->driver_data; int vbus = get_vbus(pinfo); /* mV */ chr_err("%s: %d\n", __func__, vbus); return sprintf(buf, "%d\n", vbus); } static DEVICE_ATTR_RO(ADC_Charger_Voltage); static ssize_t ADC_Charging_Current_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mtk_charger *pinfo = dev->driver_data; int ibat = get_battery_current(pinfo); /* mA */ chr_err("%s: %d\n", __func__, ibat); return sprintf(buf, "%d\n", ibat); } static DEVICE_ATTR_RO(ADC_Charging_Current); static ssize_t input_current_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mtk_charger *pinfo = dev->driver_data; int aicr = 0; aicr = pinfo->chg_data[CHG1_SETTING].thermal_input_current_limit; chr_err("%s: %d\n", __func__, aicr); return sprintf(buf, "%d\n", aicr); } static ssize_t input_current_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct mtk_charger *pinfo = dev->driver_data; struct charger_data *chg_data; signed int temp; chg_data = &pinfo->chg_data[CHG1_SETTING]; if (kstrtoint(buf, 10, &temp) == 0) { if (temp < 0) chg_data->thermal_input_current_limit = 0; else chg_data->thermal_input_current_limit = temp; } else { chr_err("%s: format error!\n", __func__); } return size; } static DEVICE_ATTR_RW(input_current); static ssize_t charger_log_level_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mtk_charger *pinfo = dev->driver_data; chr_err("%s: %d\n", __func__, pinfo->log_level); return sprintf(buf, "%d\n", pinfo->log_level); } static ssize_t charger_log_level_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct mtk_charger *pinfo = dev->driver_data; int temp; if (kstrtoint(buf, 10, &temp) == 0) { if (temp < 0) { chr_err("%s: val is invalid: %d\n", __func__, temp); temp = 0; } pinfo->log_level = temp; chr_err("%s: log_level=%d\n", __func__, pinfo->log_level); mtk_charger_set_algo_log_level(pinfo, pinfo->log_level); _wake_up_charger(pinfo); } else { chr_err("%s: format error!\n", __func__); } return size; } static DEVICE_ATTR_RW(charger_log_level); static ssize_t BatteryNotify_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mtk_charger *pinfo = dev->driver_data; chr_info("%s: 0x%x\n", __func__, pinfo->notify_code); return sprintf(buf, "%u\n", pinfo->notify_code); } static ssize_t BatteryNotify_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct mtk_charger *pinfo = dev->driver_data; unsigned int reg = 0; int ret = 0; if (buf != NULL && size != 0) { ret = kstrtouint(buf, 16, ®); if (ret < 0) { chr_err("%s: failed, ret = %d\n", __func__, ret); return ret; } pinfo->notify_code = reg; chr_info("%s: store code=0x%x\n", __func__, pinfo->notify_code); mtk_chgstat_notify(pinfo); } return size; } static DEVICE_ATTR_RW(BatteryNotify); /* procfs */ static int mtk_chg_set_cv_show(struct seq_file *m, void *data) { struct mtk_charger *pinfo = m->private; seq_printf(m, "%d\n", pinfo->data.battery_cv); return 0; } static int mtk_chg_set_cv_open(struct inode *node, struct file *file) { return single_open(file, mtk_chg_set_cv_show, PDE_DATA(node)); } static ssize_t mtk_chg_set_cv_write(struct file *file, const char *buffer, size_t count, loff_t *data) { int len = 0, ret = 0; char desc[32] = {0}; unsigned int cv = 0; struct mtk_charger *info = PDE_DATA(file_inode(file)); struct power_supply *psy = NULL; union power_supply_propval dynamic_cv; if (!info) return -EINVAL; if (count <= 0) return -EINVAL; len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1); if (copy_from_user(desc, buffer, len)) return -EFAULT; desc[len] = '\0'; ret = kstrtou32(desc, 10, &cv); if (ret == 0) { if (cv >= BATTERY_CV) { info->data.battery_cv = BATTERY_CV; chr_info("%s: adjust charge voltage %dV too high, use default cv\n", __func__, cv); } else { info->data.battery_cv = cv; chr_info("%s: adjust charge voltage = %dV\n", __func__, cv); } psy = power_supply_get_by_name("battery"); if (!IS_ERR_OR_NULL(psy)) { dynamic_cv.intval = info->data.battery_cv; ret = power_supply_set_property(psy, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, &dynamic_cv); if (ret < 0) chr_err("set gauge cv fail\n"); } return count; } chr_err("%s: bad argument\n", __func__); return count; } static const struct proc_ops mtk_chg_set_cv_fops = { .proc_open = mtk_chg_set_cv_open, .proc_read = seq_read, .proc_lseek = seq_lseek, .proc_release = single_release, .proc_write = mtk_chg_set_cv_write, }; static int mtk_chg_current_cmd_show(struct seq_file *m, void *data) { struct mtk_charger *pinfo = m->private; seq_printf(m, "%d %d\n", pinfo->usb_unlimited, pinfo->cmd_discharging); return 0; } static int mtk_chg_current_cmd_open(struct inode *node, struct file *file) { return single_open(file, mtk_chg_current_cmd_show, PDE_DATA(node)); } static ssize_t mtk_chg_current_cmd_write(struct file *file, const char *buffer, size_t count, loff_t *data) { int len = 0; char desc[32] = {0}; int current_unlimited = 0; int cmd_discharging = 0; struct mtk_charger *info = PDE_DATA(file_inode(file)); if (!info) return -EINVAL; if (count <= 0) return -EINVAL; len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1); if (copy_from_user(desc, buffer, len)) return -EFAULT; desc[len] = '\0'; if (sscanf(desc, "%d %d", ¤t_unlimited, &cmd_discharging) == 2) { info->usb_unlimited = current_unlimited; if (cmd_discharging == 1) { info->cmd_discharging = true; charger_dev_enable(info->chg1_dev, false); charger_dev_do_event(info->chg1_dev, EVENT_DISCHARGE, 0); } else if (cmd_discharging == 0) { info->cmd_discharging = false; charger_dev_enable(info->chg1_dev, true); charger_dev_do_event(info->chg1_dev, EVENT_RECHARGE, 0); } chr_info("%s: current_unlimited=%d, cmd_discharging=%d\n", __func__, current_unlimited, cmd_discharging); return count; } chr_err("bad argument, echo [usb_unlimited] [disable] > current_cmd\n"); return count; } static const struct proc_ops mtk_chg_current_cmd_fops = { .proc_open = mtk_chg_current_cmd_open, .proc_read = seq_read, .proc_lseek = seq_lseek, .proc_release = single_release, .proc_write = mtk_chg_current_cmd_write, }; static int mtk_chg_en_power_path_show(struct seq_file *m, void *data) { struct mtk_charger *pinfo = m->private; bool power_path_en = true; charger_dev_is_powerpath_enabled(pinfo->chg1_dev, &power_path_en); seq_printf(m, "%d\n", power_path_en); return 0; } static int mtk_chg_en_power_path_open(struct inode *node, struct file *file) { return single_open(file, mtk_chg_en_power_path_show, PDE_DATA(node)); } static ssize_t mtk_chg_en_power_path_write(struct file *file, const char *buffer, size_t count, loff_t *data) { int len = 0, ret = 0; char desc[32] = {0}; unsigned int enable = 0; struct mtk_charger *info = PDE_DATA(file_inode(file)); if (!info) return -EINVAL; if (count <= 0) return -EINVAL; len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1); if (copy_from_user(desc, buffer, len)) return -EFAULT; desc[len] = '\0'; ret = kstrtou32(desc, 10, &enable); if (ret == 0) { charger_dev_enable_powerpath(info->chg1_dev, enable); chr_info("%s: enable power path = %d\n", __func__, enable); return count; } chr_err("bad argument, echo [enable] > en_power_path\n"); return count; } static const struct proc_ops mtk_chg_en_power_path_fops = { .proc_open = mtk_chg_en_power_path_open, .proc_read = seq_read, .proc_lseek = seq_lseek, .proc_release = single_release, .proc_write = mtk_chg_en_power_path_write, }; static int mtk_chg_en_safety_timer_show(struct seq_file *m, void *data) { struct mtk_charger *pinfo = m->private; bool safety_timer_en = false; charger_dev_is_safety_timer_enabled(pinfo->chg1_dev, &safety_timer_en); seq_printf(m, "%d\n", safety_timer_en); return 0; } static int mtk_chg_en_safety_timer_open(struct inode *node, struct file *file) { return single_open(file, mtk_chg_en_safety_timer_show, PDE_DATA(node)); } static ssize_t mtk_chg_en_safety_timer_write(struct file *file, const char *buffer, size_t count, loff_t *data) { int len = 0, ret = 0; char desc[32] = {0}; unsigned int enable = 0; struct mtk_charger *info = PDE_DATA(file_inode(file)); if (!info) return -EINVAL; if (count <= 0) return -EINVAL; len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1); if (copy_from_user(desc, buffer, len)) return -EFAULT; desc[len] = '\0'; ret = kstrtou32(desc, 10, &enable); if (ret == 0) { charger_dev_enable_safety_timer(info->chg1_dev, enable); info->safety_timer_cmd = (int)enable; chr_info("%s: enable safety timer = %d\n", __func__, enable); /* SW safety timer */ if (info->sw_safety_timer_setting == true) { if (enable) info->enable_sw_safety_timer = true; else info->enable_sw_safety_timer = false; } return count; } chr_err("bad argument, echo [enable] > en_safety_timer\n"); return count; } static const struct proc_ops mtk_chg_en_safety_timer_fops = { .proc_open = mtk_chg_en_safety_timer_open, .proc_read = seq_read, .proc_lseek = seq_lseek, .proc_release = single_release, .proc_write = mtk_chg_en_safety_timer_write, }; int sc_get_sys_time(void) { struct rtc_time tm_android = {0}; struct timespec64 tv_android = {0}; int timep = 0; ktime_get_real_ts64(&tv_android); rtc_time64_to_tm(tv_android.tv_sec, &tm_android); tv_android.tv_sec -= (uint64_t)sys_tz.tz_minuteswest * 60; rtc_time64_to_tm(tv_android.tv_sec, &tm_android); timep = tm_android.tm_sec + tm_android.tm_min * 60 + tm_android.tm_hour * 3600; return timep; } int sc_get_left_time(int s, int e, int now) { if (e >= s) { if (now >= s && now < e) return e-now; } else { if (now >= s) return 86400 - now + e; else if (now < e) return e-now; } return 0; } char *sc_solToStr(int s) { switch (s) { case SC_IGNORE: return "ignore"; case SC_KEEP: return "keep"; case SC_DISABLE: return "disable"; case SC_REDUCE: return "reduce"; default: return "none"; } } int smart_charging(struct mtk_charger *info) { int time_to_target = 0; int time_to_full_default_current = -1; int time_to_full_default_current_limit = -1; int ret_value = SC_KEEP; int sc_real_time = sc_get_sys_time(); int sc_left_time = sc_get_left_time(info->sc.start_time, info->sc.end_time, sc_real_time); int sc_battery_percentage = get_uisoc(info) * 100; int sc_charger_current = get_battery_current(info); time_to_target = sc_left_time - info->sc.left_time_for_cv; if (info->sc.enable == false || sc_left_time <= 0 || sc_left_time < info->sc.left_time_for_cv || (sc_charger_current <= 0 && info->sc.last_solution != SC_DISABLE)) ret_value = SC_IGNORE; else { if (sc_battery_percentage > info->sc.target_percentage * 100) { if (time_to_target > 0) ret_value = SC_DISABLE; } else { if (sc_charger_current != 0) time_to_full_default_current = info->sc.battery_size * 3600 / 10000 * (10000 - sc_battery_percentage) / sc_charger_current; else time_to_full_default_current = info->sc.battery_size * 3600 / 10000 * (10000 - sc_battery_percentage); chr_err("sc1: %d %d %d %d %d\n", time_to_full_default_current, info->sc.battery_size, sc_battery_percentage, sc_charger_current, info->sc.current_limit); if (time_to_full_default_current < time_to_target && info->sc.current_limit > 0 && sc_charger_current > info->sc.current_limit) { time_to_full_default_current_limit = info->sc.battery_size / 10000 * (10000 - sc_battery_percentage) / info->sc.current_limit; chr_err("sc2: %d %d %d %d\n", time_to_full_default_current_limit, info->sc.battery_size, sc_battery_percentage, info->sc.current_limit); if (time_to_full_default_current_limit < time_to_target && sc_charger_current > info->sc.current_limit) ret_value = SC_REDUCE; } } } info->sc.last_solution = ret_value; if (info->sc.last_solution == SC_DISABLE) info->sc.disable_charger = true; else info->sc.disable_charger = false; chr_err("[sc]disable_charger: %d\n", info->sc.disable_charger); chr_err("[sc1]en:%d t:%d,%d,%d,%d t:%d,%d,%d,%d c:%d,%d ibus:%d uisoc: %d,%d s:%d ans:%s\n", info->sc.enable, info->sc.start_time, info->sc.end_time, sc_real_time, sc_left_time, info->sc.left_time_for_cv, time_to_target, time_to_full_default_current, time_to_full_default_current_limit, sc_charger_current, info->sc.current_limit, get_ibus(info), get_uisoc(info), info->sc.target_percentage, info->sc.battery_size, sc_solToStr(info->sc.last_solution)); return ret_value; } void sc_select_charging_current(struct mtk_charger *info, struct charger_data *pdata) { if (info->bootmode == 4 || info->bootmode == 1 || info->bootmode == 8 || info->bootmode == 9) { info->sc.sc_ibat = -1; /* not normal boot */ return; } info->sc.solution = info->sc.last_solution; chr_debug("debug: %d, %d, %d\n", info->bootmode, info->sc.disable_in_this_plug, info->sc.solution); if (info->sc.disable_in_this_plug == false) { chr_debug("sck: %d %d %d %d %d\n", info->sc.pre_ibat, info->sc.sc_ibat, pdata->charging_current_limit, pdata->thermal_charging_current_limit, info->sc.solution); if (info->sc.pre_ibat == -1 || info->sc.solution == SC_IGNORE || info->sc.solution == SC_DISABLE) { info->sc.sc_ibat = -1; } else { if (info->sc.pre_ibat == pdata->charging_current_limit && info->sc.solution == SC_REDUCE && ((pdata->charging_current_limit - 100000) >= 500000)) { if (info->sc.sc_ibat == -1) info->sc.sc_ibat = pdata->charging_current_limit - 100000; else { if (info->sc.sc_ibat - 100000 >= 500000) info->sc.sc_ibat = info->sc.sc_ibat - 100000; else info->sc.sc_ibat = 500000; } } } } info->sc.pre_ibat = pdata->charging_current_limit; if (pdata->thermal_charging_current_limit != -1) { if (pdata->thermal_charging_current_limit < pdata->charging_current_limit) pdata->charging_current_limit = pdata->thermal_charging_current_limit; info->sc.disable_in_this_plug = true; } else if ((info->sc.solution == SC_REDUCE || info->sc.solution == SC_KEEP) && info->sc.sc_ibat < pdata->charging_current_limit && info->sc.disable_in_this_plug == false) { pdata->charging_current_limit = info->sc.sc_ibat; } } void sc_init(struct smartcharging *sc) { sc->enable = false; sc->battery_size = 3000; sc->start_time = 0; sc->end_time = 80000; sc->current_limit = 2000; sc->target_percentage = 80; sc->left_time_for_cv = 3600; sc->pre_ibat = -1; } static ssize_t enable_sc_show( struct device *dev, struct device_attribute *attr, char *buf) { struct power_supply *chg_psy = NULL; struct mtk_charger *info = NULL; chg_psy = power_supply_get_by_name("mtk-master-charger"); if (chg_psy == NULL || IS_ERR(chg_psy)) { chr_err("%s Couldn't get chg_psy\n", __func__); return -EINVAL; } info = (struct mtk_charger *)power_supply_get_drvdata(chg_psy); if (info == NULL) return -EINVAL; chr_err( "[enable smartcharging] : %d\n", info->sc.enable); return sprintf(buf, "%d\n", info->sc.enable); } static ssize_t enable_sc_store( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { unsigned long val = 0; int ret; struct power_supply *chg_psy = NULL; struct mtk_charger *info = NULL; chg_psy = power_supply_get_by_name("mtk-master-charger"); if (chg_psy == NULL || IS_ERR(chg_psy)) { chr_err("%s Couldn't get chg_psy\n", __func__); return -EINVAL; } info = (struct mtk_charger *)power_supply_get_drvdata(chg_psy); if (info == NULL) return -EINVAL; if (buf != NULL && size != 0) { chr_err("[enable smartcharging] buf is %s\n", buf); ret = kstrtoul(buf, 10, &val); if (ret == -ERANGE || ret == -EINVAL) return -EINVAL; if (val == 0) info->sc.enable = false; else info->sc.enable = true; chr_err( "[enable smartcharging]enable smartcharging=%d\n", info->sc.enable); } return size; } static DEVICE_ATTR_RW(enable_sc); static ssize_t sc_stime_show( struct device *dev, struct device_attribute *attr, char *buf) { struct power_supply *chg_psy = NULL; struct mtk_charger *info = NULL; chg_psy = power_supply_get_by_name("mtk-master-charger"); if (chg_psy == NULL || IS_ERR(chg_psy)) { chr_err("%s Couldn't get chg_psy\n", __func__); return -EINVAL; } info = (struct mtk_charger *)power_supply_get_drvdata(chg_psy); if (info == NULL) return -EINVAL; chr_err( "[smartcharging stime] : %d\n", info->sc.start_time); return sprintf(buf, "%d\n", info->sc.start_time); } static ssize_t sc_stime_store( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { long val = 0; int ret; struct power_supply *chg_psy = NULL; struct mtk_charger *info = NULL; chg_psy = power_supply_get_by_name("mtk-master-charger"); if (chg_psy == NULL || IS_ERR(chg_psy)) { chr_err("%s Couldn't get chg_psy\n", __func__); return -EINVAL; } info = (struct mtk_charger *)power_supply_get_drvdata(chg_psy); if (info == NULL) return -EINVAL; if (buf != NULL && size != 0) { chr_err("[smartcharging stime] buf is %s\n", buf); ret = kstrtol(buf, 10, &val); if (ret == -ERANGE || ret == -EINVAL) return -EINVAL; if (val < 0) { chr_err( "[smartcharging stime] val is %ld ??\n", val); val = 0; } if (val >= 0) info->sc.start_time = (int)val; chr_err( "[smartcharging stime]enable smartcharging=%d\n", info->sc.start_time); } return size; } static DEVICE_ATTR_RW(sc_stime); static ssize_t sc_etime_show( struct device *dev, struct device_attribute *attr, char *buf) { struct power_supply *chg_psy = NULL; struct mtk_charger *info = NULL; chg_psy = power_supply_get_by_name("mtk-master-charger"); if (chg_psy == NULL || IS_ERR(chg_psy)) { chr_err("%s Couldn't get chg_psy\n", __func__); return -EINVAL; } info = (struct mtk_charger *)power_supply_get_drvdata(chg_psy); if (info == NULL) return -EINVAL; chr_err( "[smartcharging etime] : %d\n", info->sc.end_time); return sprintf(buf, "%d\n", info->sc.end_time); } static ssize_t sc_etime_store( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { long val = 0; int ret; struct power_supply *chg_psy = NULL; struct mtk_charger *info = NULL; chg_psy = power_supply_get_by_name("mtk-master-charger"); if (chg_psy == NULL || IS_ERR(chg_psy)) { chr_err("%s Couldn't get chg_psy\n", __func__); return -EINVAL; } info = (struct mtk_charger *)power_supply_get_drvdata(chg_psy); if (info == NULL) return -EINVAL; if (buf != NULL && size != 0) { chr_err("[smartcharging etime] buf is %s\n", buf); ret = kstrtol(buf, 10, &val); if (ret == -ERANGE || ret == -EINVAL) return -EINVAL; if (val < 0) { chr_err( "[smartcharging etime] val is %ld ??\n", val); val = 0; } if (val >= 0) info->sc.end_time = (int)val; chr_err( "[smartcharging stime]enable smartcharging=%d\n", info->sc.end_time); } return size; } static DEVICE_ATTR_RW(sc_etime); static ssize_t sc_tuisoc_show( struct device *dev, struct device_attribute *attr, char *buf) { struct power_supply *chg_psy = NULL; struct mtk_charger *info = NULL; chg_psy = power_supply_get_by_name("mtk-master-charger"); if (chg_psy == NULL || IS_ERR(chg_psy)) { chr_err("%s Couldn't get chg_psy\n", __func__); return -EINVAL; } info = (struct mtk_charger *)power_supply_get_drvdata(chg_psy); if (info == NULL) return -EINVAL; chr_err( "[smartcharging target uisoc] : %d\n", info->sc.target_percentage); return sprintf(buf, "%d\n", info->sc.target_percentage); } static ssize_t sc_tuisoc_store( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { long val = 0; int ret; struct power_supply *chg_psy = NULL; struct mtk_charger *info = NULL; chg_psy = power_supply_get_by_name("mtk-master-charger"); if (chg_psy == NULL || IS_ERR(chg_psy)) { chr_err("%s Couldn't get chg_psy\n", __func__); return -EINVAL; } info = (struct mtk_charger *)power_supply_get_drvdata(chg_psy); if (info == NULL) return -EINVAL; if (buf != NULL && size != 0) { chr_err("[smartcharging tuisoc] buf is %s\n", buf); ret = kstrtol(buf, 10, &val); if (ret == -ERANGE || ret == -EINVAL) return -EINVAL; if (val < 0) { chr_err( "[smartcharging tuisoc] val is %ld ??\n", val); val = 0; } if (val >= 0) info->sc.target_percentage = (int)val; chr_err( "[smartcharging stime]tuisoc=%d\n", info->sc.target_percentage); } return size; } static DEVICE_ATTR_RW(sc_tuisoc); static ssize_t sc_ibat_limit_show( struct device *dev, struct device_attribute *attr, char *buf) { struct power_supply *chg_psy = NULL; struct mtk_charger *info = NULL; chg_psy = power_supply_get_by_name("mtk-master-charger"); if (chg_psy == NULL || IS_ERR(chg_psy)) { chr_err("%s Couldn't get chg_psy\n", __func__); return -EINVAL; } info = (struct mtk_charger *)power_supply_get_drvdata(chg_psy); if (info == NULL) return -EINVAL; chr_err( "[smartcharging ibat limit] : %d\n", info->sc.current_limit); return sprintf(buf, "%d\n", info->sc.current_limit); } static ssize_t sc_ibat_limit_store( struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { long val = 0; int ret; struct power_supply *chg_psy = NULL; struct mtk_charger *info = NULL; chg_psy = power_supply_get_by_name("mtk-master-charger"); if (chg_psy == NULL || IS_ERR(chg_psy)) { chr_err("%s Couldn't get chg_psy\n", __func__); return -EINVAL; } info = (struct mtk_charger *)power_supply_get_drvdata(chg_psy); if (info == NULL) return -EINVAL; if (buf != NULL && size != 0) { chr_err("[smartcharging ibat limit] buf is %s\n", buf); ret = kstrtol(buf, 10, &val); if (ret == -ERANGE || ret == -EINVAL) return -EINVAL; if (val < 0) { chr_err( "[smartcharging ibat limit] val is %ld ??\n", (int)val); val = 0; } if (val >= 0) info->sc.current_limit = (int)val; chr_err( "[smartcharging ibat limit]=%d\n", info->sc.current_limit); } return size; } static DEVICE_ATTR_RW(sc_ibat_limit); int mtk_chg_enable_vbus_ovp(bool enable) { static struct mtk_charger *pinfo; int ret = 0; u32 sw_ovp = 0; struct power_supply *psy; if (pinfo == NULL) { psy = power_supply_get_by_name("mtk-master-charger"); if (psy == NULL) { chr_err("[%s]psy is not rdy\n", __func__); return -1; } pinfo = (struct mtk_charger *)power_supply_get_drvdata(psy); if (pinfo == NULL) { chr_err("[%s]mtk_gauge is not rdy\n", __func__); return -1; } } if (enable) sw_ovp = pinfo->data.max_charger_voltage_setting; else sw_ovp = pinfo->data.vbus_sw_ovp_voltage; /* Enable/Disable SW OVP status */ pinfo->data.max_charger_voltage = sw_ovp; disable_hw_ovp(pinfo, enable); chr_err("[%s] en:%d ovp:%d\n", __func__, enable, sw_ovp); return ret; } EXPORT_SYMBOL(mtk_chg_enable_vbus_ovp); /* return false if vbus is over max_charger_voltage */ static bool mtk_chg_check_vbus(struct mtk_charger *info) { int vchr = 0; vchr = get_vbus(info) * 1000; /* uV */ if (vchr > info->data.max_charger_voltage) { chr_err("%s: vbus(%d mV) > %d mV\n", __func__, vchr / 1000, info->data.max_charger_voltage / 1000); return false; } return true; } static void mtk_battery_notify_VCharger_check(struct mtk_charger *info) { #if defined(BATTERY_NOTIFY_CASE_0001_VCHARGER) int vchr = 0; vchr = get_vbus(info) * 1000; /* uV */ if (vchr < info->data.max_charger_voltage) info->notify_code &= ~CHG_VBUS_OV_STATUS; else { info->notify_code |= CHG_VBUS_OV_STATUS; chr_err("[BATTERY] charger_vol(%d mV) > %d mV\n", vchr / 1000, info->data.max_charger_voltage / 1000); mtk_chgstat_notify(info); } #endif } static void mtk_battery_notify_VBatTemp_check(struct mtk_charger *info) { #if defined(BATTERY_NOTIFY_CASE_0002_VBATTEMP) if (info->battery_temp >= info->thermal.max_charge_temp) { info->notify_code |= CHG_BAT_OT_STATUS; chr_err("[BATTERY] bat_temp(%d) out of range(too high)\n", info->battery_temp); mtk_chgstat_notify(info); } else { info->notify_code &= ~CHG_BAT_OT_STATUS; } if (info->enable_sw_jeita == true) { if (info->battery_temp < info->data.temp_neg_10_thres) { info->notify_code |= CHG_BAT_LT_STATUS; chr_err("bat_temp(%d) out of range(too low)\n", info->battery_temp); mtk_chgstat_notify(info); } else { info->notify_code &= ~CHG_BAT_LT_STATUS; } } else { #ifdef BAT_LOW_TEMP_PROTECT_ENABLE if (info->battery_temp < info->thermal.min_charge_temp) { info->notify_code |= CHG_BAT_LT_STATUS; chr_err("bat_temp(%d) out of range(too low)\n", info->battery_temp); mtk_chgstat_notify(info); } else { info->notify_code &= ~CHG_BAT_LT_STATUS; } #endif } #endif } static void mtk_battery_notify_UI_test(struct mtk_charger *info) { switch (info->notify_test_mode) { case 1: info->notify_code = CHG_VBUS_OV_STATUS; chr_debug("[%s] CASE_0001_VCHARGER\n", __func__); break; case 2: info->notify_code = CHG_BAT_OT_STATUS; chr_debug("[%s] CASE_0002_VBATTEMP\n", __func__); break; case 3: info->notify_code = CHG_OC_STATUS; chr_debug("[%s] CASE_0003_ICHARGING\n", __func__); break; case 4: info->notify_code = CHG_BAT_OV_STATUS; chr_debug("[%s] CASE_0004_VBAT\n", __func__); break; case 5: info->notify_code = CHG_ST_TMO_STATUS; chr_debug("[%s] CASE_0005_TOTAL_CHARGINGTIME\n", __func__); break; case 6: info->notify_code = CHG_BAT_LT_STATUS; chr_debug("[%s] CASE6: VBATTEMP_LOW\n", __func__); break; case 7: info->notify_code = CHG_TYPEC_WD_STATUS; chr_debug("[%s] CASE7: Moisture Detection\n", __func__); break; default: chr_debug("[%s] Unknown BN_TestMode Code: %x\n", __func__, info->notify_test_mode); } mtk_chgstat_notify(info); } static void mtk_battery_notify_check(struct mtk_charger *info) { if (info->notify_test_mode == 0x0000) { mtk_battery_notify_VCharger_check(info); mtk_battery_notify_VBatTemp_check(info); } else { mtk_battery_notify_UI_test(info); } } static void mtk_chg_get_tchg(struct mtk_charger *info) { int ret; int tchg_min = -127, tchg_max = -127; struct charger_data *pdata; bool en = false; pdata = &info->chg_data[CHG1_SETTING]; ret = charger_dev_get_temperature(info->chg1_dev, &tchg_min, &tchg_max); if (ret < 0) { pdata->junction_temp_min = -127; pdata->junction_temp_max = -127; } else { pdata->junction_temp_min = tchg_min; pdata->junction_temp_max = tchg_max; } if (info->chg2_dev) { pdata = &info->chg_data[CHG2_SETTING]; ret = charger_dev_get_temperature(info->chg2_dev, &tchg_min, &tchg_max); if (ret < 0) { pdata->junction_temp_min = -127; pdata->junction_temp_max = -127; } else { pdata->junction_temp_min = tchg_min; pdata->junction_temp_max = tchg_max; } } if (info->dvchg1_dev) { pdata = &info->chg_data[DVCHG1_SETTING]; pdata->junction_temp_min = -127; pdata->junction_temp_max = -127; ret = charger_dev_is_enabled(info->dvchg1_dev, &en); if (ret >= 0 && en) { ret = charger_dev_get_adc(info->dvchg1_dev, ADC_CHANNEL_TEMP_JC, &tchg_min, &tchg_max); if (ret >= 0) { pdata->junction_temp_min = tchg_min; pdata->junction_temp_max = tchg_max; } } } if (info->dvchg2_dev) { pdata = &info->chg_data[DVCHG2_SETTING]; pdata->junction_temp_min = -127; pdata->junction_temp_max = -127; ret = charger_dev_is_enabled(info->dvchg2_dev, &en); if (ret >= 0 && en) { ret = charger_dev_get_adc(info->dvchg2_dev, ADC_CHANNEL_TEMP_JC, &tchg_min, &tchg_max); if (ret >= 0) { pdata->junction_temp_min = tchg_min; pdata->junction_temp_max = tchg_max; } } } if (info->hvdvchg1_dev) { pdata = &info->chg_data[HVDVCHG1_SETTING]; pdata->junction_temp_min = -127; pdata->junction_temp_max = -127; ret = charger_dev_is_enabled(info->hvdvchg1_dev, &en); if (ret >= 0 && en) { ret = charger_dev_get_adc(info->hvdvchg1_dev, ADC_CHANNEL_TEMP_JC, &tchg_min, &tchg_max); if (ret >= 0) { pdata->junction_temp_min = tchg_min; pdata->junction_temp_max = tchg_max; } } } if (info->hvdvchg2_dev) { pdata = &info->chg_data[HVDVCHG2_SETTING]; pdata->junction_temp_min = -127; pdata->junction_temp_max = -127; ret = charger_dev_is_enabled(info->hvdvchg2_dev, &en); if (ret >= 0 && en) { ret = charger_dev_get_adc(info->hvdvchg2_dev, ADC_CHANNEL_TEMP_JC, &tchg_min, &tchg_max); if (ret >= 0) { pdata->junction_temp_min = tchg_min; pdata->junction_temp_max = tchg_max; } } } } static void charger_check_status(struct mtk_charger *info) { bool charging = true; bool chg_dev_chgen = true; int temperature; struct battery_thermal_protection_data *thermal; int uisoc = 0; if (get_charger_type(info) == POWER_SUPPLY_TYPE_UNKNOWN) return; temperature = info->battery_temp; thermal = &info->thermal; uisoc = get_uisoc(info); info->setting.vbat_mon_en = true; if (info->enable_sw_jeita == true || info->enable_vbat_mon != true || info->batpro_done == true) info->setting.vbat_mon_en = false; if (info->enable_sw_jeita == true) { do_sw_jeita_state_machine(info); if (info->sw_jeita.charging == false) { charging = false; goto stop_charging; } } else { if (thermal->enable_min_charge_temp) { if (temperature < thermal->min_charge_temp) { chr_err("Battery Under Temperature or NTC fail %d %d\n", temperature, thermal->min_charge_temp); thermal->sm = BAT_TEMP_LOW; charging = false; goto stop_charging; } else if (thermal->sm == BAT_TEMP_LOW) { if (temperature >= thermal->min_charge_temp_plus_x_degree) { chr_err("Battery Temperature raise from %d to %d(%d), allow charging!!\n", thermal->min_charge_temp, temperature, thermal->min_charge_temp_plus_x_degree); thermal->sm = BAT_TEMP_NORMAL; } else { charging = false; goto stop_charging; } } } if (temperature >= thermal->max_charge_temp) { chr_err("Battery over Temperature or NTC fail %d %d\n", temperature, thermal->max_charge_temp); thermal->sm = BAT_TEMP_HIGH; charging = false; goto stop_charging; } else if (thermal->sm == BAT_TEMP_HIGH) { if (temperature < thermal->max_charge_temp_minus_x_degree) { chr_err("Battery Temperature raise from %d to %d(%d), allow charging!!\n", thermal->max_charge_temp, temperature, thermal->max_charge_temp_minus_x_degree); thermal->sm = BAT_TEMP_NORMAL; } else { charging = false; goto stop_charging; } } } mtk_chg_get_tchg(info); if (!mtk_chg_check_vbus(info)) { charging = false; goto stop_charging; } if (info->cmd_discharging) charging = false; if (info->safety_timeout) charging = false; if (info->vbusov_stat) charging = false; if (info->sc.disable_charger == true) charging = false; stop_charging: mtk_battery_notify_check(info); if (charging && uisoc < 80 && info->batpro_done == true) { info->setting.vbat_mon_en = true; info->batpro_done = false; info->stop_6pin_re_en = false; } chr_err("tmp:%d (jeita:%d sm:%d cv:%d en:%d) (sm:%d) en:%d c:%d s:%d ov:%d sc:%d %d %d saf_cmd:%d bat_mon:%d %d\n", temperature, info->enable_sw_jeita, info->sw_jeita.sm, info->sw_jeita.cv, info->sw_jeita.charging, thermal->sm, charging, info->cmd_discharging, info->safety_timeout, info->vbusov_stat, info->sc.disable_charger, info->can_charging, charging, info->safety_timer_cmd, info->enable_vbat_mon, info->batpro_done); charger_dev_is_enabled(info->chg1_dev, &chg_dev_chgen); if (charging != info->can_charging) _mtk_enable_charging(info, charging); else if (charging == false && chg_dev_chgen == true) _mtk_enable_charging(info, charging); info->can_charging = charging; } static bool charger_init_algo(struct mtk_charger *info) { struct chg_alg_device *alg; int idx = 0; info->chg1_dev = get_charger_by_name("primary_chg"); if (info->chg1_dev) chr_err("%s, Found primary charger\n", __func__); else { chr_err("%s, *** Error : can't find primary charger ***\n" , __func__); return false; } alg = get_chg_alg_by_name("pe5p"); info->alg[idx] = alg; if (alg == NULL) chr_err("get pe5p fail\n"); else { chr_err("get pe5p success\n"); alg->config = info->config; alg->alg_id = PE5P_ID; chg_alg_init_algo(alg); register_chg_alg_notifier(alg, &info->chg_alg_nb); } idx++; alg = get_chg_alg_by_name("hvbp"); info->alg[idx] = alg; if (alg == NULL) chr_err("get hvbp fail\n"); else { chr_err("get hvbp success\n"); alg->config = info->config; alg->alg_id = HVBP_ID; chg_alg_init_algo(alg); register_chg_alg_notifier(alg, &info->chg_alg_nb); } idx++; alg = get_chg_alg_by_name("pe5"); info->alg[idx] = alg; if (alg == NULL) chr_err("get pe5 fail\n"); else { chr_err("get pe5 success\n"); alg->config = info->config; alg->alg_id = PE5_ID; chg_alg_init_algo(alg); register_chg_alg_notifier(alg, &info->chg_alg_nb); } idx++; alg = get_chg_alg_by_name("pe45"); info->alg[idx] = alg; if (alg == NULL) chr_err("get pe45 fail\n"); else { chr_err("get pe45 success\n"); alg->config = info->config; alg->alg_id = PE4_ID; chg_alg_init_algo(alg); register_chg_alg_notifier(alg, &info->chg_alg_nb); } idx++; alg = get_chg_alg_by_name("pe4"); info->alg[idx] = alg; if (alg == NULL) chr_err("get pe4 fail\n"); else { chr_err("get pe4 success\n"); alg->config = info->config; alg->alg_id = PE4_ID; chg_alg_init_algo(alg); register_chg_alg_notifier(alg, &info->chg_alg_nb); } idx++; alg = get_chg_alg_by_name("pd"); info->alg[idx] = alg; if (alg == NULL) chr_err("get pd fail\n"); else { chr_err("get pd success\n"); alg->config = info->config; alg->alg_id = PDC_ID; chg_alg_init_algo(alg); register_chg_alg_notifier(alg, &info->chg_alg_nb); } idx++; alg = get_chg_alg_by_name("pe2"); info->alg[idx] = alg; if (alg == NULL) chr_err("get pe2 fail\n"); else { chr_err("get pe2 success\n"); alg->config = info->config; alg->alg_id = PE2_ID; chg_alg_init_algo(alg); register_chg_alg_notifier(alg, &info->chg_alg_nb); } idx++; alg = get_chg_alg_by_name("pe"); info->alg[idx] = alg; if (alg == NULL) chr_err("get pe fail\n"); else { chr_err("get pe success\n"); alg->config = info->config; alg->alg_id = PE_ID; chg_alg_init_algo(alg); register_chg_alg_notifier(alg, &info->chg_alg_nb); } chr_err("config is %d\n", info->config); if (info->config == DUAL_CHARGERS_IN_SERIES) { info->chg2_dev = get_charger_by_name("secondary_chg"); if (info->chg2_dev) chr_err("Found secondary charger\n"); else { chr_err("*** Error : can't find secondary charger ***\n"); return false; } } else if (info->config == DIVIDER_CHARGER || info->config == DUAL_DIVIDER_CHARGERS) { info->dvchg1_dev = get_charger_by_name("primary_dvchg"); if (info->dvchg1_dev) chr_err("Found primary divider charger\n"); else { chr_err("*** Error : can't find primary divider charger ***\n"); return false; } if (info->config == DUAL_DIVIDER_CHARGERS) { info->dvchg2_dev = get_charger_by_name("secondary_dvchg"); if (info->dvchg2_dev) chr_err("Found secondary divider charger\n"); else { chr_err("*** Error : can't find secondary divider charger ***\n"); return false; } } } else if (info->config == HVDIVIDER_CHARGER || info->config == DUAL_HVDIVIDER_CHARGERS) { info->hvdvchg1_dev = get_charger_by_name("hvdiv2_chg1"); if (info->hvdvchg1_dev) chr_err("Found primary hvdivider charger\n"); else { chr_err("*** Error : can't find primary hvdivider charger ***\n"); return false; } if (info->config == DUAL_HVDIVIDER_CHARGERS) { info->hvdvchg2_dev = get_charger_by_name("hvdiv2_chg2"); if (info->hvdvchg2_dev) chr_err("Found secondary hvdivider charger\n"); else { chr_err("*** Error : can't find secondary hvdivider charger ***\n"); return false; } } } chr_err("register chg1 notifier %d %d\n", info->chg1_dev != NULL, info->algo.do_event != NULL); if (info->chg1_dev != NULL && info->algo.do_event != NULL) { chr_err("register chg1 notifier done\n"); info->chg1_nb.notifier_call = info->algo.do_event; register_charger_device_notifier(info->chg1_dev, &info->chg1_nb); charger_dev_set_drvdata(info->chg1_dev, info); } chr_err("register dvchg chg1 notifier %d %d\n", info->dvchg1_dev != NULL, info->algo.do_dvchg1_event != NULL); if (info->dvchg1_dev != NULL && info->algo.do_dvchg1_event != NULL) { chr_err("register dvchg chg1 notifier done\n"); info->dvchg1_nb.notifier_call = info->algo.do_dvchg1_event; register_charger_device_notifier(info->dvchg1_dev, &info->dvchg1_nb); charger_dev_set_drvdata(info->dvchg1_dev, info); } chr_err("register dvchg chg2 notifier %d %d\n", info->dvchg2_dev != NULL, info->algo.do_dvchg2_event != NULL); if (info->dvchg2_dev != NULL && info->algo.do_dvchg2_event != NULL) { chr_err("register dvchg chg2 notifier done\n"); info->dvchg2_nb.notifier_call = info->algo.do_dvchg2_event; register_charger_device_notifier(info->dvchg2_dev, &info->dvchg2_nb); charger_dev_set_drvdata(info->dvchg2_dev, info); } chr_err("register hvdvchg chg1 notifier %d %d\n", info->hvdvchg1_dev != NULL, info->algo.do_hvdvchg1_event != NULL); if (info->hvdvchg1_dev != NULL && info->algo.do_hvdvchg1_event != NULL) { chr_err("register hvdvchg chg1 notifier done\n"); info->hvdvchg1_nb.notifier_call = info->algo.do_hvdvchg1_event; register_charger_device_notifier(info->hvdvchg1_dev, &info->hvdvchg1_nb); charger_dev_set_drvdata(info->hvdvchg1_dev, info); } chr_err("register hvdvchg chg2 notifier %d %d\n", info->hvdvchg2_dev != NULL, info->algo.do_hvdvchg2_event != NULL); if (info->hvdvchg2_dev != NULL && info->algo.do_hvdvchg2_event != NULL) { chr_err("register hvdvchg chg2 notifier done\n"); info->hvdvchg2_nb.notifier_call = info->algo.do_hvdvchg2_event; register_charger_device_notifier(info->hvdvchg2_dev, &info->hvdvchg2_nb); charger_dev_set_drvdata(info->hvdvchg2_dev, info); } return true; } static int mtk_charger_force_disable_power_path(struct mtk_charger *info, int idx, bool disable); static int mtk_charger_plug_out(struct mtk_charger *info) { struct charger_data *pdata1 = &info->chg_data[CHG1_SETTING]; struct charger_data *pdata2 = &info->chg_data[CHG2_SETTING]; struct chg_alg_device *alg; struct chg_alg_notify notify; int i; chr_err("%s\n", __func__); info->chr_type = POWER_SUPPLY_TYPE_UNKNOWN; info->charger_thread_polling = false; info->pd_reset = false; pdata1->disable_charging_count = 0; pdata1->input_current_limit_by_aicl = -1; pdata2->disable_charging_count = 0; notify.evt = EVT_PLUG_OUT; notify.value = 0; for (i = 0; i < MAX_ALG_NO; i++) { alg = info->alg[i]; chg_alg_notifier_call(alg, ¬ify); chg_alg_plugout_reset(alg); } memset(&info->sc.data, 0, sizeof(struct scd_cmd_param_t_1)); charger_dev_set_input_current(info->chg1_dev, 100000); charger_dev_set_mivr(info->chg1_dev, info->data.min_charger_voltage); charger_dev_plug_out(info->chg1_dev); mtk_charger_force_disable_power_path(info, CHG1_SETTING, true); if (info->enable_vbat_mon) charger_dev_enable_6pin_battery_charging(info->chg1_dev, false); return 0; } static int mtk_charger_plug_in(struct mtk_charger *info, int chr_type) { struct chg_alg_device *alg; struct chg_alg_notify notify; int i, vbat; chr_debug("%s\n", __func__); info->chr_type = chr_type; info->usb_type = get_usb_type(info); info->charger_thread_polling = true; info->can_charging = true; //info->enable_dynamic_cv = true; info->safety_timeout = false; info->vbusov_stat = false; info->old_cv = 0; info->stop_6pin_re_en = false; info->batpro_done = false; smart_charging(info); chr_err("mtk_is_charger_on plug in, type:%d\n", chr_type); vbat = get_battery_voltage(info); notify.evt = EVT_PLUG_IN; notify.value = 0; for (i = 0; i < MAX_ALG_NO; i++) { alg = info->alg[i]; chg_alg_notifier_call(alg, ¬ify); chg_alg_set_prop(alg, ALG_REF_VBAT, vbat); } memset(&info->sc.data, 0, sizeof(struct scd_cmd_param_t_1)); info->sc.disable_in_this_plug = false; charger_dev_plug_in(info->chg1_dev); mtk_charger_force_disable_power_path(info, CHG1_SETTING, false); return 0; } static bool mtk_is_charger_on(struct mtk_charger *info) { int chr_type; chr_type = get_charger_type(info); if (chr_type == POWER_SUPPLY_TYPE_UNKNOWN) { if (info->chr_type != POWER_SUPPLY_TYPE_UNKNOWN) { mtk_charger_plug_out(info); mutex_lock(&info->cable_out_lock); info->cable_out_cnt = 0; mutex_unlock(&info->cable_out_lock); } } else { if (info->chr_type == POWER_SUPPLY_TYPE_UNKNOWN) mtk_charger_plug_in(info, chr_type); else { info->chr_type = chr_type; info->usb_type = get_usb_type(info); } if (info->cable_out_cnt > 0) { mtk_charger_plug_out(info); mtk_charger_plug_in(info, chr_type); mutex_lock(&info->cable_out_lock); info->cable_out_cnt = 0; mutex_unlock(&info->cable_out_lock); } } if (chr_type == POWER_SUPPLY_TYPE_UNKNOWN) return false; return true; } static void charger_send_kpoc_uevent(struct mtk_charger *info) { static bool first_time = true; ktime_t ktime_now; if (first_time) { info->uevent_time_check = ktime_get(); first_time = false; } else { ktime_now = ktime_get(); if ((ktime_ms_delta(ktime_now, info->uevent_time_check) / 1000) >= 60) { mtk_chgstat_notify(info); info->uevent_time_check = ktime_now; } } } static void kpoc_power_off_check(struct mtk_charger *info) { unsigned int boot_mode = info->bootmode; int vbus = 0; int counter = 0; /* 8 = KERNEL_POWER_OFF_CHARGING_BOOT */ /* 9 = LOW_POWER_OFF_CHARGING_BOOT */ if (boot_mode == 8 || boot_mode == 9) { vbus = get_vbus(info); if (vbus >= 0 && vbus < 2500 && !mtk_is_charger_on(info) && !info->pd_reset) { chr_err("Unplug Charger/USB in KPOC mode, vbus=%d, shutdown\n", vbus); while (1) { if (counter >= 20000) { chr_err("%s, wait too long\n", __func__); kernel_power_off(); break; } if (info->is_suspend == false) { chr_err("%s, not in suspend, shutdown\n", __func__); kernel_power_off(); break; } else { chr_err("%s, suspend! cannot shutdown\n", __func__); msleep(20); } counter++; } } charger_send_kpoc_uevent(info); } } static void charger_status_check(struct mtk_charger *info) { union power_supply_propval online, status; struct power_supply *chg_psy = NULL; int ret; bool charging = true; chg_psy = power_supply_get_by_name("primary_chg"); if (IS_ERR_OR_NULL(chg_psy)) { chr_err("%s Couldn't get chg_psy\n", __func__); } else { ret = power_supply_get_property(chg_psy, POWER_SUPPLY_PROP_ONLINE, &online); ret = power_supply_get_property(chg_psy, POWER_SUPPLY_PROP_STATUS, &status); if (!online.intval) charging = false; else { if (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) charging = false; } } if (charging != info->is_charging) power_supply_changed(info->psy1); info->is_charging = charging; } static char *dump_charger_type(int chg_type, int usb_type) { switch (chg_type) { case POWER_SUPPLY_TYPE_UNKNOWN: return "none"; case POWER_SUPPLY_TYPE_USB: if (usb_type == POWER_SUPPLY_USB_TYPE_SDP) return "usb"; else return "nonstd"; case POWER_SUPPLY_TYPE_USB_CDP: return "usb-h"; case POWER_SUPPLY_TYPE_USB_DCP: return "std"; //case POWER_SUPPLY_TYPE_USB_FLOAT: // return "nonstd"; default: return "unknown"; } } static int charger_routine_thread(void *arg) { struct mtk_charger *info = arg; unsigned long flags; unsigned int init_times = 3; static bool is_module_init_done; bool is_charger_on; int ret; int vbat_min, vbat_max; u32 chg_cv = 0; while (1) { ret = wait_event_interruptible(info->wait_que, (info->charger_thread_timeout == true)); if (ret < 0) { chr_err("%s: wait event been interrupted(%d)\n", __func__, ret); continue; } while (is_module_init_done == false) { if (charger_init_algo(info) == true) { is_module_init_done = true; if (info->charger_unlimited) { info->enable_sw_safety_timer = false; charger_dev_enable_safety_timer(info->chg1_dev, false); } } else { if (init_times > 0) { chr_err("retry to init charger\n"); init_times = init_times - 1; msleep(10000); } else { chr_err("holding to init charger\n"); msleep(60000); } } } mutex_lock(&info->charger_lock); spin_lock_irqsave(&info->slock, flags); if (!info->charger_wakelock->active) __pm_stay_awake(info->charger_wakelock); spin_unlock_irqrestore(&info->slock, flags); info->charger_thread_timeout = false; info->battery_temp = get_battery_temperature(info); ret = charger_dev_get_adc(info->chg1_dev, ADC_CHANNEL_VBAT, &vbat_min, &vbat_max); ret = charger_dev_get_constant_voltage(info->chg1_dev, &chg_cv); if (vbat_min != 0) vbat_min = vbat_min / 1000; chr_err("Vbat=%d vbats=%d vbus:%d ibus:%d I=%d T=%d uisoc:%d type:%s>%s pd:%d swchg_ibat:%d cv:%d\n", get_battery_voltage(info), vbat_min, get_vbus(info), get_ibus(info), get_battery_current(info), info->battery_temp, get_uisoc(info), dump_charger_type(info->chr_type, info->usb_type), dump_charger_type(get_charger_type(info), get_usb_type(info)), info->pd_type, get_ibat(info), chg_cv); is_charger_on = mtk_is_charger_on(info); if (info->charger_thread_polling == true) mtk_charger_start_timer(info); check_battery_exist(info); check_dynamic_mivr(info); charger_check_status(info); kpoc_power_off_check(info); if (is_disable_charger(info) == false && is_charger_on == true && info->can_charging == true) { if (info->algo.do_algorithm) info->algo.do_algorithm(info); charger_status_check(info); } else { chr_debug("disable charging %d %d %d\n", is_disable_charger(info), is_charger_on, info->can_charging); } if (info->bootmode != 1 && info->bootmode != 2 && info->bootmode != 4 && info->bootmode != 8 && info->bootmode != 9) smart_charging(info); spin_lock_irqsave(&info->slock, flags); __pm_relax(info->charger_wakelock); spin_unlock_irqrestore(&info->slock, flags); chr_debug("%s end , %d\n", __func__, info->charger_thread_timeout); mutex_unlock(&info->charger_lock); } return 0; } #ifdef CONFIG_PM static int charger_pm_event(struct notifier_block *notifier, unsigned long pm_event, void *unused) { ktime_t ktime_now; struct timespec64 now; struct mtk_charger *info; info = container_of(notifier, struct mtk_charger, pm_notifier); switch (pm_event) { case PM_SUSPEND_PREPARE: info->is_suspend = true; chr_debug("%s: enter PM_SUSPEND_PREPARE\n", __func__); break; case PM_POST_SUSPEND: info->is_suspend = false; chr_debug("%s: enter PM_POST_SUSPEND\n", __func__); ktime_now = ktime_get_boottime(); now = ktime_to_timespec64(ktime_now); if (timespec64_compare(&now, &info->endtime) >= 0 && info->endtime.tv_sec != 0 && info->endtime.tv_nsec != 0) { chr_err("%s: alarm timeout, wake up charger\n", __func__); __pm_relax(info->charger_wakelock); info->endtime.tv_sec = 0; info->endtime.tv_nsec = 0; _wake_up_charger(info); } break; default: break; } return NOTIFY_DONE; } #endif /* CONFIG_PM */ static enum alarmtimer_restart mtk_charger_alarm_timer_func(struct alarm *alarm, ktime_t now) { struct mtk_charger *info = container_of(alarm, struct mtk_charger, charger_timer); if (info->is_suspend == false) { _wake_up_charger(info); } else { __pm_stay_awake(info->charger_wakelock); } return ALARMTIMER_NORESTART; } static void mtk_charger_init_timer(struct mtk_charger *info) { alarm_init(&info->charger_timer, ALARM_BOOTTIME, mtk_charger_alarm_timer_func); mtk_charger_start_timer(info); } /* prize liuyong, add charging limit node 20230605 start */ static ssize_t show_cmd_charge_disable(struct device *dev, struct device_attribute *attr, char *buf) { struct mtk_charger *info = dev->driver_data; pr_info("[charge] %s : %d\n",__func__, info->cmd_discharging); return sprintf(buf, "%d\n",info->cmd_discharging); } static ssize_t store_cmd_charge_disable(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct mtk_charger *pinfo = dev->driver_data; unsigned int reg = 0; int ret = 0; //FIXME:prize-Solve 90% of the problems not as of charging-pengzhipeng-20220725-start bool dvchg1_chip_enabled = false; //struct chg_alg_device *alg; charger_dev_is_enabled(pinfo->dvchg1_dev, &dvchg1_chip_enabled); //FIXME:prize-Solve 90% of the problems not as of charging-pengzhipeng-20220725-end pr_info("[charge] %s\n", __func__); if (buf != NULL && size != 0) { pr_info("[store_cmd_charge_disable] buf is %s and size is %zu\n", buf, size); ret = kstrtouint(buf, 16, ®); if(reg == 1){ pinfo->cmd_discharging = true; #if IS_ENABLED(CONFIG_PRIZE_MT5728_SUPPORT_30W) /* Turn wireless charge off if support */ turn_off_5728(1); //turn_off_5725(1); //set_wireless_disable_flag(true); #endif /*CONFIG_PRIZE_MT5725_SUPPORT_15W*/ }else if(reg == 0){ #if IS_ENABLED(CONFIG_PRIZE_MT5728_SUPPORT_30W) /* Resume wireless charge on if support */ turn_off_5728(0); //set_wireless_disable_flag(false); //turn_off_5725(0); #endif /*CONFIG_PRIZE_MT5725_SUPPORT_15W*/ pinfo->cmd_discharging = false; }else{ pr_info("[store_cmd_charge_disable] input err please 0 or 1\n"); } if((pinfo->chr_type != POWER_SUPPLY_USB_TYPE_UNKNOWN) && (reg == 1)){ charger_dev_enable(pinfo->chg1_dev, false); //FIXME:prize-Solve 90% of the problems not as of charging-pengzhipeng-20220725-start if(dvchg1_chip_enabled){ charger_dev_enable(pinfo->dvchg1_dev, false); // charger_dev_enable_chip(pinfo->chg2_dev, false); } //FIXME:prize-Solve 90% of the problems not as of charging-pengzhipeng-20220725-end charger_dev_do_event(pinfo->chg1_dev,EVENT_DISCHARGE, 0); pr_info("[store_cmd_charge_disable] disable charge\n"); }else if((pinfo->chr_type != POWER_SUPPLY_USB_TYPE_UNKNOWN) && (reg == 0)){ /* for (i = 0; i < MAX_ALG_NO; i++) { alg = pinfo->alg[i]; chg_alg_plugout_reset(alg); } */ charger_dev_enable(pinfo->chg1_dev, true); //FIXME:prize-Solve 90% of the problems not as of charging-pengzhipeng-20220725-start charger_dev_enable(pinfo->dvchg1_dev, true); //mtk_pe50_set_is_enable(pinfo, true); //FIXME:prize-Solve 90% of the problems not as of charging-pengzhipeng-20220725-end charger_dev_do_event(pinfo->chg1_dev,EVENT_RECHARGE, 0); pr_info("[store_cmd_charge_disable] enable charge \n"); }else { pr_info("[store_cmd_charge_disable] No USB connection \n"); } } return size; } static DEVICE_ATTR(cmd_charge_disable, 0664, show_cmd_charge_disable, store_cmd_charge_disable); /* prize liuyong, add charging limit node 20230605 end */ static int mtk_charger_setup_files(struct platform_device *pdev) { int ret = 0; struct proc_dir_entry *battery_dir = NULL, *entry = NULL; struct mtk_charger *info = platform_get_drvdata(pdev); ret = device_create_file(&(pdev->dev), &dev_attr_sw_jeita); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_sw_ovp_threshold); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_chr_type); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_enable_meta_current_limit); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_fast_chg_indicator); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_Charging_mode); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_pd_type); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_High_voltage_chg_enable); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_Rust_detect); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_Thermal_throttle); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_alg_new_arbitration); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_alg_unchangeable); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_vbat_mon); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_Pump_Express); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_ADC_Charger_Voltage); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_ADC_Charging_Current); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_input_current); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_charger_log_level); if (ret) goto _out; /* Battery warning */ ret = device_create_file(&(pdev->dev), &dev_attr_BatteryNotify); if (ret) goto _out; /* sysfs node */ ret = device_create_file(&(pdev->dev), &dev_attr_enable_sc); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_sc_stime); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_sc_etime); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_sc_tuisoc); if (ret) goto _out; ret = device_create_file(&(pdev->dev), &dev_attr_sc_ibat_limit); if (ret) goto _out; /* prize liuyong, add charging limit node 20230605 start */ ret = device_create_file(&(pdev->dev), &dev_attr_cmd_charge_disable); if (ret) goto _out; /* prize liuyong, add charging limit node 20230605 end */ battery_dir = proc_mkdir("mtk_battery_cmd", NULL); if (!battery_dir) { chr_err("%s: mkdir /proc/mtk_battery_cmd failed\n", __func__); return -ENOMEM; } entry = proc_create_data("current_cmd", 0644, battery_dir, &mtk_chg_current_cmd_fops, info); if (!entry) { ret = -ENODEV; goto fail_procfs; } entry = proc_create_data("en_power_path", 0644, battery_dir, &mtk_chg_en_power_path_fops, info); if (!entry) { ret = -ENODEV; goto fail_procfs; } entry = proc_create_data("en_safety_timer", 0644, battery_dir, &mtk_chg_en_safety_timer_fops, info); if (!entry) { ret = -ENODEV; goto fail_procfs; } entry = proc_create_data("set_cv", 0644, battery_dir, &mtk_chg_set_cv_fops, info); if (!entry) { ret = -ENODEV; goto fail_procfs; } return 0; fail_procfs: remove_proc_subtree("mtk_battery_cmd", NULL); _out: return ret; } // drv add tankaikun, add facoryt charger class, 20231204 start #if IS_ENABLED(CONFIG_FACTORY_CHARGE) static int mtk_map_fast_chrg_type(int alg_id) { int i; struct mtk_fast_chg_type_map fast_chg_type_maps[] = { {MTK_FAST_CHARGER_TYPE_UNKNOWN, 0}, {MTK_FAST_CHARGER_TYPE_PEP, PE_ID}, {MTK_FAST_CHARGER_TYPE_PE20, PE2_ID}, {MTK_FAST_CHARGER_TYPE_PDC, PDC_ID}, {MTK_FAST_CHARGER_TYPE_PE40, PE4_ID}, {MTK_FAST_CHARGER_TYPE_PE50, PE5_ID}, {MTK_FAST_CHARGER_TYPE_HVBP, HVBP_ID}, {MTK_FAST_CHARGER_TYPE_PE5P, PE5P_ID}, {MTK_FAST_CHARGER_TYPE_WIRELESS_FAST, WL_ID}, }; for (i = 0; ipd_type == MTK_PD_CONNECT_PE_READY_SNK_APDO) { is_detected = true; goto out; } for (i = 0; i < MAX_ALG_NO; i++) { alg = info->alg[i]; if (alg == NULL) continue; ret = chg_alg_is_algo_ready(alg); if(alg->alg_id == PE5_ID) { if (ret == ALG_RUNNING && alg->pe_ready_check_done){ is_detected = true; chr_err("[factroy_charge] %s: i:%d,alg->id = %d, detect = %d\n", __func__, i,alg->alg_id, is_detected); break; } } else { if (ret == ALG_RUNNING) { is_detected = true; chr_err("[factroy_charge] %s: i:%d,alg->id = %d, detect = %d\n", __func__, i,alg->alg_id, is_detected); break; } } } out: return sprintf(buf, "%d\n", is_detected); } static ssize_t charger_algo_show(struct class *class, struct class_attribute *attr, char *buf) { int i, ret, alg_id=0; struct chg_alg_device *alg = NULL; struct mtk_charger *info = NULL; struct power_supply *chrg_psy = NULL; chrg_psy = power_supply_get_by_name("mtk-master-charger"); if(chrg_psy == NULL) { pr_err("[factroy_charge] get chrg_psy err\n"); goto failed; } info = (struct mtk_charger *)power_supply_get_drvdata(chrg_psy); if(!info) { pr_err("[factroy_charge] get chrg_psy err\n"); goto failed; } for (i = 0; i < MAX_ALG_NO; i++) { alg = info->alg[i]; if (alg == NULL) continue; ret = chg_alg_is_algo_ready(alg); if(alg->alg_id == PE5_ID) { if (ret == ALG_RUNNING && alg->pe_ready_check_done){ alg_id = alg->alg_id; break; } } else { if (ret == ALG_RUNNING) { alg_id = alg->alg_id; break; } } } failed: return sprintf(buf, "%s\n", mtk_fast_chg_algo_list[mtk_map_fast_chrg_type(alg_id)]); } static ssize_t fast_charger_enable_show(struct class *class, struct class_attribute *attr, char *buf) { bool is_detected = false; int i, ret; struct chg_alg_device *alg = NULL; struct mtk_charger *info = NULL; struct power_supply *chrg_psy = NULL; chrg_psy = power_supply_get_by_name("mtk-master-charger"); if(chrg_psy == NULL){ pr_err("[factroy_charge] get chrg_psy err\n"); goto failed; } info = (struct mtk_charger *)power_supply_get_drvdata(chrg_psy); if(!info) { pr_err("[factroy_charge] get chrg_psy err\n"); goto failed; } for (i = 0; i < MAX_ALG_NO; i++) { alg = info->alg[i]; if (alg == NULL) continue; ret = chg_alg_is_algo_ready(alg); if(alg->alg_id == PE5_ID) { if (ret == ALG_RUNNING && alg->pe_ready_check_done){ is_detected = true; chr_err("[factroy_charge] %s: i:%d,alg->id = %d, detect = %d\n", __func__, i,alg->alg_id, is_detected); break; } } else { if (ret == ALG_RUNNING) { is_detected = true; chr_err("[factroy_charge] %s: i:%d,alg->id = %d, detect = %d\n", __func__, i,alg->alg_id, is_detected); break; } } } failed: return sprintf(buf, "%d\n", is_detected); } static ssize_t fast_charger_support_show(struct class *class, struct class_attribute *attr, char *buf) { bool fast_charge_support = false; int i; struct chg_alg_device *alg = NULL; struct mtk_charger *info = NULL; struct power_supply *chrg_psy = NULL; chrg_psy = power_supply_get_by_name("mtk-master-charger"); if(chrg_psy == NULL) { pr_err("[factroy_charge] get chrg_psy err\n"); goto failed; } info = (struct mtk_charger *)power_supply_get_drvdata(chrg_psy); if(!info) { pr_err("[factroy_charge] get chrg_psy err\n"); goto failed; } for (i = 0; i < MAX_ALG_NO; i++) { alg = info->alg[i]; if (alg == NULL) continue; fast_charge_support = true; } failed: return sprintf(buf, "%d\n",fast_charge_support); } static ssize_t fast_charger_power_show(struct class *class, struct class_attribute *attr, char *buf) { int i, ret, pwr=10; struct chg_alg_device *alg = NULL; struct mtk_charger *info = NULL; struct power_supply *chrg_psy = NULL; chrg_psy = power_supply_get_by_name("mtk-master-charger"); if(chrg_psy == NULL){ pr_err("[factroy_charge] get chrg_psy err\n"); goto failed; } info = (struct mtk_charger *)power_supply_get_drvdata(chrg_psy); if(!info) { pr_err("[factroy_charge] get chrg_psy err\n"); goto failed; } for (i = 0; i < MAX_ALG_NO; i++) { alg = info->alg[i]; if (alg == NULL) continue; ret = chg_alg_is_algo_ready(alg); if (ret == ALG_RUNNING && alg->pe_ready_check_done) { chg_alg_get_prop(alg, ALG_CHARGE_PWR, &pwr); break; } } failed: return sprintf(buf, "%d\n", pwr); } #if 0 static ssize_t show_cmd_charge_disable(struct class *class, struct class_attribute *attr, char *buf) { struct mtk_charger *info = NULL; struct power_supply *chrg_psy = NULL; chrg_psy = power_supply_get_by_name("mtk-master-charger"); if(chrg_psy == NULL) { pr_err("get bat_psy err\n"); return 0; } info = (struct mtk_charger *)power_supply_get_drvdata(chrg_psy); if(!info) { pr_err("[factroy_charge] get chrg_psy err\n"); goto failed; } pr_info("[factroy_charge] %s : %d\n",__func__, info->cmd_discharging); return sprintf(buf, "%d\n",info->cmd_discharging); failed: return sprintf(buf, "%d\n",0); } static ssize_t store_cmd_charge_disable(struct class *class, struct class_attribute *attr, const char *buf, size_t count) { unsigned int reg = 0; int ret = 0,i = 0; struct chg_alg_device *alg; struct mtk_charger *info = NULL; struct power_supply *chrg_psy = NULL; chrg_psy = power_supply_get_by_name("mtk-master-charger"); if(chrg_psy == NULL){ pr_err("get bat_psy err\n"); return 0; } info = (struct mtk_charger *)power_supply_get_drvdata(chrg_psy); if(!info) { pr_err("[factroy_charge] get chrg_psy err\n"); goto failed; } pr_info("[factroy_charge] %s\n", __func__); if (buf != NULL && count != 0) { pr_info("[factroy_charge][store_cmd_charge_disable] buf is %s and size is %zu\n", buf, count); ret = kstrtouint(buf, 16, ®); if(reg == 1) { info->cmd_discharging = true; } else if(reg == 0){ info->cmd_discharging = false; } else{ pr_info("[factroy_charge][store_cmd_charge_disable] input err please 0 or 1\n"); } if((info->chr_type != POWER_SUPPLY_USB_TYPE_UNKNOWN) && (reg == 1)){ charger_dev_enable(info->chg1_dev, false); charger_dev_do_event(info->chg1_dev,EVENT_DISCHARGE, 0); charger_dev_enable_hz(info->chg1_dev, true); pr_info("[factroy_charge][store_cmd_charge_disable] disable charge\n"); } else if((info->chr_type != POWER_SUPPLY_USB_TYPE_UNKNOWN) && (reg == 0)){ for (i = 0; i < MAX_ALG_NO; i++) { alg = info->alg[i]; chg_alg_plugout_reset(alg); } charger_dev_enable_hz(info->chg1_dev, false); charger_dev_enable(info->chg1_dev, true); charger_dev_do_event(info->chg1_dev,EVENT_RECHARGE, 0); pr_info("[factroy_charge][store_cmd_charge_disable] enable charge \n"); } else { pr_info("[factroy_charge][store_cmd_charge_disable] No USB connection \n"); } } failed: return count; } #endif static ssize_t show_cmd_debug_temp(struct class *class, struct class_attribute *attr, char *buf) { struct mtk_charger *info = NULL; struct power_supply *chrg_psy = NULL; chrg_psy = power_supply_get_by_name("mtk-master-charger"); if(chrg_psy == NULL) { pr_err("get bat_psy err\n"); return 0; } info = (struct mtk_charger *)power_supply_get_drvdata(chrg_psy); if(!info) { pr_err("[factroy_charge] get chrg_psy err\n"); goto failed; } pr_info("[factroy_charge] %s : %d:%d\n",__func__, info->debug_temp_en,info->debug_temp); return sprintf(buf, "%d:%d\n",info->debug_temp_en, info->debug_temp); failed: return sprintf(buf, "%d:%d\n",0,0); } static ssize_t store_cmd_debug_temp(struct class *class, struct class_attribute *attr, const char *buf, size_t count) { int reg = 0; int ret = 0; struct mtk_charger *info = NULL; struct power_supply *chrg_psy = NULL; chrg_psy = power_supply_get_by_name("mtk-master-charger"); if(chrg_psy == NULL){ pr_err("get bat_psy err\n"); return 0; } info = (struct mtk_charger *)power_supply_get_drvdata(chrg_psy); if(!info) { pr_err("[factroy_charge] get chrg_psy err\n"); goto failed; } if (buf != NULL && count != 0) { pr_info("[factroy_charge][store_cmd_charge_disable] buf is %s and size is %zu\n", buf, count); ret = kstrtouint(buf, 10, ®); pr_err("[factroy_charge][store_cmd_charge_disable] reg=%d \n", reg); if(reg == MTK_DEBUG_TEMP_EN_CMD) { info->debug_temp_en = true; info->debug_temp = 25; } else if(reg == MTK_DEBUG_TEMP_DIS_CMD){ info->debug_temp_en = false; info->debug_temp = 25; } else{ info->debug_temp = reg; } } failed: return count; } static struct class * factory_charger_class; static struct class_attribute factory_charger_class_attrs[] = { #if IS_ENABLED(CONFIG_SECOND_CHARGER_SUPPORT) __ATTR(chg2_exist, S_IRUGO, chg2_exist_show, NULL), #endif /* CONFIG_SECOND_CHARGER_SUPPORT */ __ATTR(fast_charger, S_IRUGO, fast_charger_show, NULL), __ATTR(fast_charger_enable, S_IRUGO, fast_charger_enable_show, NULL), __ATTR(fast_charger_support, S_IRUGO, fast_charger_support_show, NULL), __ATTR(fast_charger_pwr, S_IRUGO,fast_charger_power_show, NULL), __ATTR(charger_type, S_IRUGO, charger_type_show, NULL), __ATTR(charger_algo, S_IRUGO, charger_algo_show, NULL), //__ATTR(cmd_charge_disable, S_IRUGO | S_IWUSR, show_cmd_charge_disable, store_cmd_charge_disable), __ATTR(cmd_debug_temp, S_IRUGO | S_IWUSR, show_cmd_debug_temp, store_cmd_debug_temp), __ATTR_NULL, }; static int factory_charger_sysfs_create(void) { int i = 0,ret = 0; factory_charger_class = class_create(THIS_MODULE, "factory_charger"); if (IS_ERR(factory_charger_class)) return PTR_ERR(factory_charger_class); for (i = 0; factory_charger_class_attrs[i].attr.name; i++) { ret = class_create_file(factory_charger_class,&factory_charger_class_attrs[i]); if (ret < 0) { pr_err("factory_charger sysfs create error !!\n"); return ret; } } return ret; } #endif /* CONFIG_FACTORY_CHARGE */ // drv add tankaikun, add facoryt charger class, 20231204 end void mtk_charger_get_atm_mode(struct mtk_charger *info) { char atm_str[64] = {0}; char *ptr = NULL, *ptr_e = NULL; char keyword[] = "androidboot.atm="; int size = 0; ptr = strstr(chg_get_cmd(), keyword); if (ptr != 0) { ptr_e = strstr(ptr, " "); if (ptr_e == 0) goto end; size = ptr_e - (ptr + strlen(keyword)); if (size <= 0) goto end; strncpy(atm_str, ptr + strlen(keyword), size); atm_str[size] = '\0'; chr_err("%s: atm_str: %s\n", __func__, atm_str); if (!strncmp(atm_str, "enable", strlen("enable"))) info->atm_enabled = true; } end: chr_err("%s: atm_enabled = %d\n", __func__, info->atm_enabled); } static int psy_charger_property_is_writeable(struct power_supply *psy, enum power_supply_property psp) { switch (psp) { case POWER_SUPPLY_PROP_VOLTAGE_MAX: return 1; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: return 1; case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: return 1; default: return 0; } } static enum power_supply_property charger_psy_properties[] = { POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_VOLTAGE_MAX, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, }; static int psy_charger_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { struct mtk_charger *info; struct charger_device *chg; struct charger_data *pdata; int ret = 0; struct chg_alg_device *alg = NULL; info = (struct mtk_charger *)power_supply_get_drvdata(psy); if (info == NULL) { chr_err("%s: get info failed\n", __func__); return -EINVAL; } chr_debug("%s psp:%d\n", __func__, psp); if (info->psy1 != NULL && info->psy1 == psy) chg = info->chg1_dev; /* prize liuyong add for usb supply config, 20231122, start */ else if (info->usb_psy != NULL && info->usb_psy == psy) chg = info->chg1_dev; /* prize liuyong add for usb supply config, 20231122, end */ else if (info->psy2 != NULL && info->psy2 == psy) chg = info->chg2_dev; else if (info->psy_dvchg1 != NULL && info->psy_dvchg1 == psy) chg = info->dvchg1_dev; else if (info->psy_dvchg2 != NULL && info->psy_dvchg2 == psy) chg = info->dvchg2_dev; else if (info->psy_hvdvchg1 != NULL && info->psy_hvdvchg1 == psy) chg = info->hvdvchg1_dev; else if (info->psy_hvdvchg2 != NULL && info->psy_hvdvchg2 == psy) chg = info->hvdvchg2_dev; else { chr_err("%s fail\n", __func__); return 0; } switch (psp) { case POWER_SUPPLY_PROP_ONLINE: if (chg == info->dvchg1_dev) { val->intval = false; alg = get_chg_alg_by_name("pe5"); if (alg == NULL) chr_err("get pe5 fail\n"); else { ret = chg_alg_is_algo_ready(alg); if (ret == ALG_RUNNING) val->intval = true; } break; } /* prize liuyong add for usb supply config, 20231122, start */ if (info->chr_type == POWER_SUPPLY_TYPE_USB || info->chr_type == POWER_SUPPLY_TYPE_USB_CDP) val->intval = is_charger_exist(info); /* prize liuyong add for usb supply config, 20231122, start */ break; case POWER_SUPPLY_PROP_PRESENT: if (chg != NULL) val->intval = true; else val->intval = false; break; case POWER_SUPPLY_PROP_VOLTAGE_MAX: val->intval = info->enable_hv_charging; break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: val->intval = get_vbus(info); break; case POWER_SUPPLY_PROP_TEMP: if (chg == info->chg1_dev) val->intval = info->chg_data[CHG1_SETTING].junction_temp_max * 10; else if (chg == info->chg2_dev) val->intval = info->chg_data[CHG2_SETTING].junction_temp_max * 10; else if (chg == info->dvchg1_dev) { pdata = &info->chg_data[DVCHG1_SETTING]; val->intval = pdata->junction_temp_max; } else if (chg == info->dvchg2_dev) { pdata = &info->chg_data[DVCHG2_SETTING]; val->intval = pdata->junction_temp_max; } else val->intval = -127; break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: val->intval = get_charger_charging_current(info, chg); break; case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: val->intval = get_charger_input_current(info, chg); break; case POWER_SUPPLY_PROP_USB_TYPE: val->intval = info->chr_type; break; case POWER_SUPPLY_PROP_VOLTAGE_BOOT: val->intval = get_charger_zcv(info, chg); break; default: return -EINVAL; } return 0; } static int mtk_charger_enable_power_path(struct mtk_charger *info, int idx, bool en) { int ret = 0; bool is_en = true; struct charger_device *chg_dev = NULL; if (!info) return -EINVAL; switch (idx) { case CHG1_SETTING: chg_dev = get_charger_by_name("primary_chg"); break; case CHG2_SETTING: chg_dev = get_charger_by_name("secondary_chg"); break; default: return -EINVAL; } if (IS_ERR_OR_NULL(chg_dev)) { chr_err("%s: chg_dev not found\n", __func__); return -EINVAL; } mutex_lock(&info->pp_lock[idx]); info->enable_pp[idx] = en; if (info->force_disable_pp[idx]) goto out; ret = charger_dev_is_powerpath_enabled(chg_dev, &is_en); if (ret < 0) { chr_err("%s: get is power path enabled failed\n", __func__); goto out; } if (is_en == en) { chr_err("%s: power path is already en = %d\n", __func__, is_en); goto out; } pr_info("%s: enable power path = %d\n", __func__, en); ret = charger_dev_enable_powerpath(chg_dev, en); out: mutex_unlock(&info->pp_lock[idx]); return ret; } static int mtk_charger_force_disable_power_path(struct mtk_charger *info, int idx, bool disable) { int ret = 0; struct charger_device *chg_dev = NULL; if (!info) return -EINVAL; switch (idx) { case CHG1_SETTING: chg_dev = get_charger_by_name("primary_chg"); break; case CHG2_SETTING: chg_dev = get_charger_by_name("secondary_chg"); break; default: return -EINVAL; } if (IS_ERR_OR_NULL(chg_dev)) { chr_err("%s: chg_dev not found\n", __func__); return -EINVAL; } mutex_lock(&info->pp_lock[idx]); if (disable == info->force_disable_pp[idx]) goto out; info->force_disable_pp[idx] = disable; ret = charger_dev_enable_powerpath(chg_dev, info->force_disable_pp[idx] ? false : info->enable_pp[idx]); out: mutex_unlock(&info->pp_lock[idx]); return ret; } int psy_charger_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val) { struct mtk_charger *info; int idx; chr_err("%s: prop:%d %d\n", __func__, psp, val->intval); info = (struct mtk_charger *)power_supply_get_drvdata(psy); if (info == NULL) { chr_err("%s: failed to get info\n", __func__); return -EINVAL; } if (info->psy1 != NULL && info->psy1 == psy) idx = CHG1_SETTING; /* prize liuyong add for usb supply config, 20231122, start */ else if (info->usb_psy != NULL && info->usb_psy == psy) idx = CHG1_SETTING; /* prize liuyong add for usb supply config, 20231122, end */ else if (info->psy2 != NULL && info->psy2 == psy) idx = CHG2_SETTING; else if (info->psy_dvchg1 != NULL && info->psy_dvchg1 == psy) idx = DVCHG1_SETTING; else if (info->psy_dvchg2 != NULL && info->psy_dvchg2 == psy) idx = DVCHG2_SETTING; else { chr_err("%s fail\n", __func__); return 0; } switch (psp) { case POWER_SUPPLY_PROP_VOLTAGE_MAX: if (val->intval > 0) info->enable_hv_charging = true; else info->enable_hv_charging = false; break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: info->chg_data[idx].thermal_charging_current_limit = val->intval; break; case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: info->chg_data[idx].thermal_input_current_limit = val->intval; break; case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: if (val->intval > 0) mtk_charger_enable_power_path(info, idx, false); else mtk_charger_enable_power_path(info, idx, true); break; case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX: if (val->intval > 0) mtk_charger_force_disable_power_path(info, idx, true); else mtk_charger_force_disable_power_path(info, idx, false); break; default: return -EINVAL; } _wake_up_charger(info); return 0; } static void mtk_charger_external_power_changed(struct power_supply *psy) { struct mtk_charger *info; union power_supply_propval prop = {0}; union power_supply_propval prop2 = {0}; union power_supply_propval vbat0 = {0}; struct power_supply *chg_psy = NULL; int ret; info = (struct mtk_charger *)power_supply_get_drvdata(psy); if (info == NULL) { pr_notice("%s: failed to get info\n", __func__); return; } chg_psy = info->chg_psy; if (IS_ERR_OR_NULL(chg_psy)) { pr_notice("%s Couldn't get chg_psy\n", __func__); chg_psy = power_supply_get_by_name("primary_chg"); info->chg_psy = chg_psy; } else { ret = power_supply_get_property(chg_psy, POWER_SUPPLY_PROP_ONLINE, &prop); ret = power_supply_get_property(chg_psy, POWER_SUPPLY_PROP_USB_TYPE, &prop2); ret = power_supply_get_property(chg_psy, POWER_SUPPLY_PROP_ENERGY_EMPTY, &vbat0); } if (info->vbat0_flag != vbat0.intval) { if (vbat0.intval) { info->enable_vbat_mon = false; charger_dev_enable_6pin_battery_charging(info->chg1_dev, false); } else info->enable_vbat_mon = info->enable_vbat_mon_bak; info->vbat0_flag = vbat0.intval; } pr_notice("%s event, name:%s online:%d type:%d vbus:%d\n", __func__, psy->desc->name, prop.intval, prop2.intval, get_vbus(info)); _wake_up_charger(info); } int notify_adapter_event(struct notifier_block *notifier, unsigned long evt, void *val) { struct mtk_charger *pinfo = NULL; chr_err("%s %lu\n", __func__, evt); pinfo = container_of(notifier, struct mtk_charger, pd_nb); switch (evt) { case MTK_PD_CONNECT_NONE: mutex_lock(&pinfo->pd_lock); chr_err("PD Notify Detach\n"); pinfo->pd_type = MTK_PD_CONNECT_NONE; pinfo->pd_reset = false; mutex_unlock(&pinfo->pd_lock); mtk_chg_alg_notify_call(pinfo, EVT_DETACH, 0); /* reset PE40 */ break; case MTK_PD_CONNECT_HARD_RESET: mutex_lock(&pinfo->pd_lock); chr_err("PD Notify HardReset\n"); pinfo->pd_type = MTK_PD_CONNECT_NONE; pinfo->pd_reset = true; mutex_unlock(&pinfo->pd_lock); mtk_chg_alg_notify_call(pinfo, EVT_HARDRESET, 0); _wake_up_charger(pinfo); /* reset PE40 */ break; case MTK_PD_CONNECT_PE_READY_SNK: mutex_lock(&pinfo->pd_lock); chr_err("PD Notify fixe voltage ready\n"); pinfo->pd_type = MTK_PD_CONNECT_PE_READY_SNK; pinfo->pd_reset = false; mutex_unlock(&pinfo->pd_lock); /* PD is ready */ break; case MTK_PD_CONNECT_PE_READY_SNK_PD30: mutex_lock(&pinfo->pd_lock); chr_err("PD Notify PD30 ready\r\n"); pinfo->pd_type = MTK_PD_CONNECT_PE_READY_SNK_PD30; pinfo->pd_reset = false; mutex_unlock(&pinfo->pd_lock); /* PD30 is ready */ break; case MTK_PD_CONNECT_PE_READY_SNK_APDO: mutex_lock(&pinfo->pd_lock); chr_err("PD Notify APDO Ready\n"); pinfo->pd_type = MTK_PD_CONNECT_PE_READY_SNK_APDO; pinfo->pd_reset = false; mutex_unlock(&pinfo->pd_lock); /* PE40 is ready */ _wake_up_charger(pinfo); break; case MTK_PD_CONNECT_TYPEC_ONLY_SNK: mutex_lock(&pinfo->pd_lock); chr_err("PD Notify Type-C Ready\n"); pinfo->pd_type = MTK_PD_CONNECT_TYPEC_ONLY_SNK; pinfo->pd_reset = false; mutex_unlock(&pinfo->pd_lock); /* type C is ready */ _wake_up_charger(pinfo); break; case MTK_TYPEC_WD_STATUS: chr_err("wd status = %d\n", *(bool *)val); pinfo->water_detected = *(bool *)val; if (pinfo->water_detected == true) { pinfo->notify_code |= CHG_TYPEC_WD_STATUS; pinfo->record_water_detected = true; } else pinfo->notify_code &= ~CHG_TYPEC_WD_STATUS; mtk_chgstat_notify(pinfo); break; } return NOTIFY_DONE; } int chg_alg_event(struct notifier_block *notifier, unsigned long event, void *data) { chr_err("%s: evt:%d\n", __func__, event); return NOTIFY_DONE; } #if IS_ENABLED(CONFIG_DRM_MEDIATEK) /* prize LiuYong 20240219, modify to full time start */ bool get_screen_on_status() { struct power_supply *psy; static struct mtk_charger *info; if (info == NULL) { psy = power_supply_get_by_name("mtk-master-charger"); if (psy == NULL) return false; else { info = (struct mtk_charger *)power_supply_get_drvdata(psy); if (info == NULL) return false; else return info->is_screen_on; } } else return info->is_screen_on; } EXPORT_SYMBOL(get_screen_on_status); /* prize LiuYong 20240219, modify to full time end */ static int charger_disp_notifier_callback(struct notifier_block *nb, unsigned long value, void *v) { struct mtk_charger *info = container_of(nb, struct mtk_charger, disp_notifier); int *data = (int *)v; if (info && v) { //cts_err("%s IN", __func__); if (value == MTK_DISP_EARLY_EVENT_BLANK) { if (*data == MTK_DISP_BLANK_POWERDOWN) { info->is_screen_on = false; } } else if (value == MTK_DISP_EVENT_BLANK) { if (*data == MTK_DISP_BLANK_UNBLANK) { info->is_screen_on = true; } } } else { return -1; } return 0; } #endif static char *mtk_charger_supplied_to[] = { "battery" }; static int mtk_charger_probe(struct platform_device *pdev) { struct mtk_charger *info = NULL; int i; char *name = NULL; /* prize add by liuyong, modify for screen on charging 20230315 start */ #if IS_ENABLED(CONFIG_DRM_MEDIATEK) int ret; #endif /* prize add by liuyong, modify for screen on charging 20230315 end */ chr_err("%s: starts\n", __func__); info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; platform_set_drvdata(pdev, info); info->pdev = pdev; mtk_charger_parse_dt(info, &pdev->dev); mutex_init(&info->cable_out_lock); mutex_init(&info->charger_lock); mutex_init(&info->pd_lock); for (i = 0; i < CHG2_SETTING + 1; i++) { mutex_init(&info->pp_lock[i]); info->force_disable_pp[i] = false; info->enable_pp[i] = true; } name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s", "charger suspend wakelock"); info->charger_wakelock = wakeup_source_register(NULL, name); spin_lock_init(&info->slock); init_waitqueue_head(&info->wait_que); info->polling_interval = CHARGING_INTERVAL; mtk_charger_init_timer(info); #ifdef CONFIG_PM if (register_pm_notifier(&info->pm_notifier)) { chr_err("%s: register pm failed\n", __func__); return -ENODEV; } info->pm_notifier.notifier_call = charger_pm_event; #endif /* CONFIG_PM */ srcu_init_notifier_head(&info->evt_nh); mtk_charger_setup_files(pdev); mtk_charger_get_atm_mode(info); for (i = 0; i < CHGS_SETTING_MAX; i++) { info->chg_data[i].thermal_charging_current_limit = -1; info->chg_data[i].thermal_input_current_limit = -1; info->chg_data[i].input_current_limit_by_aicl = -1; } info->enable_hv_charging = true; info->psy_desc1.name = "mtk-master-charger"; info->psy_desc1.type = POWER_SUPPLY_TYPE_UNKNOWN; info->psy_desc1.properties = charger_psy_properties; info->psy_desc1.num_properties = ARRAY_SIZE(charger_psy_properties); info->psy_desc1.get_property = psy_charger_get_property; info->psy_desc1.set_property = psy_charger_set_property; info->psy_desc1.property_is_writeable = psy_charger_property_is_writeable; info->psy_desc1.external_power_changed = mtk_charger_external_power_changed; info->psy_cfg1.drv_data = info; info->psy_cfg1.supplied_to = mtk_charger_supplied_to; info->psy_cfg1.num_supplicants = ARRAY_SIZE(mtk_charger_supplied_to); info->psy1 = power_supply_register(&pdev->dev, &info->psy_desc1, &info->psy_cfg1); info->chg_psy = power_supply_get_by_name("primary_chg"); if (IS_ERR_OR_NULL(info->chg_psy)) chr_err("%s: devm power fail to get chg_psy\n", __func__); info->bc12_psy = power_supply_get_by_name("primary_chg"); if (IS_ERR_OR_NULL(info->bc12_psy)) chr_err("%s: devm power fail to get bc12_psy\n", __func__); info->bat_psy = devm_power_supply_get_by_phandle(&pdev->dev, "gauge"); if (IS_ERR_OR_NULL(info->bat_psy)) chr_err("%s: devm power fail to get bat_psy\n", __func__); if (IS_ERR(info->psy1)) chr_err("register psy1 fail:%ld\n", PTR_ERR(info->psy1)); info->psy_desc2.name = "mtk-slave-charger"; info->psy_desc2.type = POWER_SUPPLY_TYPE_UNKNOWN; info->psy_desc2.properties = charger_psy_properties; info->psy_desc2.num_properties = ARRAY_SIZE(charger_psy_properties); info->psy_desc2.get_property = psy_charger_get_property; info->psy_desc2.set_property = psy_charger_set_property; info->psy_desc2.property_is_writeable = psy_charger_property_is_writeable; info->psy_cfg2.drv_data = info; info->psy2 = power_supply_register(&pdev->dev, &info->psy_desc2, &info->psy_cfg2); if (IS_ERR(info->psy2)) chr_err("register psy2 fail:%ld\n", PTR_ERR(info->psy2)); info->psy_dvchg_desc1.name = "mtk-mst-div-chg"; info->psy_dvchg_desc1.type = POWER_SUPPLY_TYPE_UNKNOWN; info->psy_dvchg_desc1.properties = charger_psy_properties; info->psy_dvchg_desc1.num_properties = ARRAY_SIZE(charger_psy_properties); info->psy_dvchg_desc1.get_property = psy_charger_get_property; info->psy_dvchg_desc1.set_property = psy_charger_set_property; info->psy_dvchg_desc1.property_is_writeable = psy_charger_property_is_writeable; info->psy_dvchg_cfg1.drv_data = info; info->psy_dvchg1 = power_supply_register(&pdev->dev, &info->psy_dvchg_desc1, &info->psy_dvchg_cfg1); if (IS_ERR(info->psy_dvchg1)) chr_err("register psy dvchg1 fail:%ld\n", PTR_ERR(info->psy_dvchg1)); info->psy_dvchg_desc2.name = "mtk-slv-div-chg"; info->psy_dvchg_desc2.type = POWER_SUPPLY_TYPE_UNKNOWN; info->psy_dvchg_desc2.properties = charger_psy_properties; info->psy_dvchg_desc2.num_properties = ARRAY_SIZE(charger_psy_properties); info->psy_dvchg_desc2.get_property = psy_charger_get_property; info->psy_dvchg_desc2.set_property = psy_charger_set_property; info->psy_dvchg_desc2.property_is_writeable = psy_charger_property_is_writeable; info->psy_dvchg_cfg2.drv_data = info; info->psy_dvchg2 = power_supply_register(&pdev->dev, &info->psy_dvchg_desc2, &info->psy_dvchg_cfg2); if (IS_ERR(info->psy_dvchg2)) chr_err("register psy dvchg2 fail:%ld\n", PTR_ERR(info->psy_dvchg2)); info->psy_hvdvchg_desc1.name = "mtk-mst-hvdiv-chg"; info->psy_hvdvchg_desc1.type = POWER_SUPPLY_TYPE_UNKNOWN; info->psy_hvdvchg_desc1.properties = charger_psy_properties; info->psy_hvdvchg_desc1.num_properties = ARRAY_SIZE(charger_psy_properties); info->psy_hvdvchg_desc1.get_property = psy_charger_get_property; info->psy_hvdvchg_desc1.set_property = psy_charger_set_property; info->psy_hvdvchg_desc1.property_is_writeable = psy_charger_property_is_writeable; info->psy_hvdvchg_cfg1.drv_data = info; info->psy_hvdvchg1 = power_supply_register(&pdev->dev, &info->psy_hvdvchg_desc1, &info->psy_hvdvchg_cfg1); if (IS_ERR(info->psy_hvdvchg1)) chr_err("register psy hvdvchg1 fail:%ld\n", PTR_ERR(info->psy_hvdvchg1)); info->psy_hvdvchg_desc2.name = "mtk-slv-hvdiv-chg"; info->psy_hvdvchg_desc2.type = POWER_SUPPLY_TYPE_UNKNOWN; info->psy_hvdvchg_desc2.properties = charger_psy_properties; info->psy_hvdvchg_desc2.num_properties = ARRAY_SIZE(charger_psy_properties); info->psy_hvdvchg_desc2.get_property = psy_charger_get_property; info->psy_hvdvchg_desc2.set_property = psy_charger_set_property; info->psy_hvdvchg_desc2.property_is_writeable = psy_charger_property_is_writeable; info->psy_hvdvchg_cfg2.drv_data = info; info->psy_hvdvchg2 = power_supply_register(&pdev->dev, &info->psy_hvdvchg_desc2, &info->psy_hvdvchg_cfg2); if (IS_ERR(info->psy_hvdvchg2)) chr_err("register psy hvdvchg2 fail:%ld\n", PTR_ERR(info->psy_hvdvchg2)); /* prize liuyong, add for charging config, 20231018, start*/ info->usb_desc.name = "usb"; info->usb_desc.type = POWER_SUPPLY_TYPE_USB; info->usb_desc.properties = charger_psy_properties; info->usb_desc.num_properties = ARRAY_SIZE(charger_psy_properties); info->usb_desc.get_property = psy_charger_get_property; info->usb_desc.set_property = psy_charger_set_property; info->usb_desc.property_is_writeable = psy_charger_property_is_writeable; info->usb_cfg.drv_data = info; info->usb_psy = power_supply_register(&pdev->dev, &info->usb_desc, &info->usb_cfg); if (IS_ERR(info->usb_psy)) chr_err("register psy usb_psy fail:%ld\n", PTR_ERR(info->usb_psy)); /* prize liuyong, add for charging config, 20231018, end*/ info->log_level = CHRLOG_ERROR_LEVEL; info->pd_adapter = get_adapter_by_name("pd_adapter"); if (!info->pd_adapter) chr_err("%s: No pd adapter found\n", __func__); else { info->pd_nb.notifier_call = notify_adapter_event; register_adapter_device_notifier(info->pd_adapter, &info->pd_nb); } // drv add tankaikun, add facoryt charger class, 20231204, start #if IS_ENABLED(CONFIG_FACTORY_CHARGE) factory_charger_sysfs_create(); #endif /*CONFIG_FACTORY_CHARGE*/ // drv add tankaikun, add facoryt charger class, 20231204, end sc_init(&info->sc); info->chg_alg_nb.notifier_call = chg_alg_event; info->fast_charging_indicator = 0; info->enable_meta_current_limit = 1; info->is_charging = false; info->safety_timer_cmd = -1; /* 8 = KERNEL_POWER_OFF_CHARGING_BOOT */ /* 9 = LOW_POWER_OFF_CHARGING_BOOT */ if (info != NULL && info->bootmode != 8 && info->bootmode != 9) mtk_charger_force_disable_power_path(info, CHG1_SETTING, true); /* prize add by liuyong, modify for screen on charging 20230315 start */ #if IS_ENABLED(CONFIG_DRM_MEDIATEK) info->disp_notifier.notifier_call = charger_disp_notifier_callback; ret = mtk_disp_notifier_register("screen monitor", &info->disp_notifier); if (ret) { pr_err("Failed to register screen monitor notifier client:%d", ret); //goto err_register_disp_notif_failed; } else{ pr_err("gezi screen monitor register success.\n"); } #endif /* prize add by liuyong, modify for screen on charging 20230315 end */ kthread_run(charger_routine_thread, info, "charger_thread"); return 0; } static int mtk_charger_remove(struct platform_device *dev) { return 0; } static void mtk_charger_shutdown(struct platform_device *dev) { struct mtk_charger *info = platform_get_drvdata(dev); int i; for (i = 0; i < MAX_ALG_NO; i++) { if (info->alg[i] == NULL) continue; chg_alg_stop_algo(info->alg[i]); } } static const struct of_device_id mtk_charger_of_match[] = { {.compatible = "mediatek,charger",}, {}, }; MODULE_DEVICE_TABLE(of, mtk_charger_of_match); struct platform_device mtk_charger_device = { .name = "charger", .id = -1, }; static struct platform_driver mtk_charger_driver = { .probe = mtk_charger_probe, .remove = mtk_charger_remove, .shutdown = mtk_charger_shutdown, .driver = { .name = "charger", .of_match_table = mtk_charger_of_match, }, }; static int __init mtk_charger_init(void) { return platform_driver_register(&mtk_charger_driver); } module_init(mtk_charger_init); static void __exit mtk_charger_exit(void) { platform_driver_unregister(&mtk_charger_driver); } module_exit(mtk_charger_exit); MODULE_AUTHOR("wy.chuang "); MODULE_DESCRIPTION("MTK Charger Driver"); MODULE_LICENSE("GPL");