/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2021 MediaTek Inc. */ #include #include #include #include #include #include "mtk_charger_intf.h" #include "mtk_dual_switch_charging.h" static int _uA_to_mA(int uA) { if (uA == -1) return -1; else return uA / 1000; } static bool is_in_pe40_state(struct charger_manager *info) { struct dual_switch_charging_alg_data *swchgalg = info->algorithm_data; if (swchgalg->state == CHR_PE40_CC || swchgalg->state == CHR_PE40_TUNING || swchgalg->state == CHR_PE40_POSTCC || swchgalg->state == CHR_PE40_INIT) return true; return false; } static void _disable_all_charging(struct charger_manager *info) { bool chg2_chip_enabled = false; charger_dev_is_chip_enabled(info->chg2_dev, &chg2_chip_enabled); charger_dev_enable(info->chg1_dev, false); if (chg2_chip_enabled) { charger_dev_enable(info->chg2_dev, false); charger_dev_enable_chip(info->chg2_dev, false); } if (mtk_pe20_get_is_enable(info)) { mtk_pe20_set_is_enable(info, false); if (mtk_pe20_get_is_connect(info)) mtk_pe20_reset_ta_vchr(info); } if (mtk_pe_get_is_enable(info)) { mtk_pe_set_is_enable(info, false); if (mtk_pe_get_is_connect(info)) mtk_pe_reset_ta_vchr(info); } if (mtk_pe40_get_is_enable(info)) { if (mtk_pe40_get_is_connect(info)) mtk_pe40_end(info, 3, true); } if (mtk_pdc_check_charger(info)) mtk_pdc_reset(info); } static bool dual_swchg_check_pd_leave(struct charger_manager *info) { struct mtk_pdc *pd = &info->pdc; int ichg = 0; if (info->disable_pd_dual) return true; if (pd->pd_cap_max_watt < 10000000) return true; if (info->enable_hv_charging == false) return true; ichg = battery_get_bat_current() * 100; if (battery_get_soc() >= info->data.pd_stop_battery_soc || battery_get_uisoc() == -1) return true; return false; } static void dual_swchg_select_charging_current_limit(struct charger_manager *info) { struct charger_data *pdata, *pdata2; struct dual_switch_charging_alg_data *swchgalg = info->algorithm_data; u32 ichg1_min = 0, ichg2_min = 0, aicr1_min = 0, aicr2_min = 0; int ret = 0; bool chg2_chip_enabled = false; bool chg2_enabled = false; charger_dev_is_chip_enabled(info->chg2_dev, &chg2_chip_enabled); charger_dev_is_enabled(info->chg2_dev, &chg2_enabled); pdata = &info->chg1_data; pdata2 = &info->chg2_data; mutex_lock(&swchgalg->ichg_aicr_access_mutex); /* AICL */ if (!mtk_pe20_get_is_connect(info) && !mtk_pe_get_is_connect(info) && !mtk_is_TA_support_pd_pps(info) && !mtk_pdc_check_charger(info)) { charger_dev_run_aicl(info->chg1_dev, &pdata->input_current_limit_by_aicl); if (info->enable_dynamic_mivr) { if (pdata->input_current_limit_by_aicl > info->data.max_dmivr_charger_current) pdata->input_current_limit_by_aicl = info->data.max_dmivr_charger_current; } } if (pdata->force_charging_current > 0) { pdata->charging_current_limit = pdata->force_charging_current; if (pdata->force_charging_current <= 450000) pdata->input_current_limit = 500000; else pdata->input_current_limit = info->data.ac_charger_input_current; goto done; } if (info->usb_unlimited) { pdata->input_current_limit = info->data.ac_charger_input_current; pdata->charging_current_limit = info->data.ac_charger_current; goto done; } if (info->water_detected) { pdata->input_current_limit = info->data.usb_charger_current; pdata->charging_current_limit = info->data.usb_charger_current; goto done; } // workaround for mt6768 // if ((get_boot_mode() == META_BOOT) || // (get_boot_mode() == ADVMETA_BOOT)) { // pdata->input_current_limit = 200000; /* 200mA */ // goto done; // } if (info->atm_enabled == true && (info->chr_type == STANDARD_HOST || info->chr_type == CHARGING_HOST)) { pdata->input_current_limit = 100000; /* 100mA */ goto done; } if (mtk_pe40_get_is_connect(info)) { if (is_dual_charger_supported(info)) { /* Slave charger may not have input current control */ pdata->input_current_limit = info->data.pe40_dual_charger_input_current; pdata2->input_current_limit = info->data.pe40_dual_charger_input_current; switch (swchgalg->state) { case CHR_PE40_INIT: case CHR_PE40_CC: pdata->charging_current_limit = info->data.pe40_dual_charger_chg1_current; pdata2->charging_current_limit = info->data.pe40_dual_charger_chg2_current; break; case CHR_PE40_TUNING: pdata->charging_current_limit = info->data.pe40_dual_charger_chg1_current; break; default: break; } } else { pdata->input_current_limit = info->data.pe40_single_charger_input_current; pdata->charging_current_limit = info->data.pe40_single_charger_current; } } else if (is_typec_adapter(info)) { if (adapter_dev_get_property(info->pd_adapter, TYPEC_RP_LEVEL) == 3000) { pdata->input_current_limit = 3000000; pdata->charging_current_limit = 3000000; } else if (adapter_dev_get_property(info->pd_adapter, TYPEC_RP_LEVEL) == 1500) { pdata->input_current_limit = 1500000; pdata->charging_current_limit = 2000000; } else { chr_err("type-C: inquire rp error\n"); pdata->input_current_limit = 500000; pdata->charging_current_limit = 500000; } chr_err("type-C:%d current:%d\n", info->pd_type, adapter_dev_get_property(info->pd_adapter, TYPEC_RP_LEVEL)); } else if (mtk_pdc_check_charger(info)) { int vbus = 0, cur = 0, idx = 0; ret = mtk_pdc_get_setting(info, &vbus, &cur, &idx); if (ret != -1 && idx != -1) { pdata->input_current_limit = cur * 1000; pdata->charging_current_limit = info->data.pd_charger_current; mtk_pdc_setup(info, idx); } else { pdata->input_current_limit = info->data.usb_charger_current_configured; pdata->charging_current_limit = info->data.usb_charger_current_configured; } if (!dual_swchg_check_pd_leave(info)) { /* Slave charger may not have input current control */ pdata2->input_current_limit = cur * 1000; switch (swchgalg->state) { case CHR_CC: pdata->charging_current_limit = info->data.chg1_ta_ac_charger_current; pdata2->charging_current_limit = info->data.chg2_ta_ac_charger_current; break; case CHR_TUNING: pdata->charging_current_limit = info->data.chg1_ta_ac_charger_current; break; default: break; } } chr_info("[%s]vbus:%d input_cur:%d idx:%d current:%d\n", __func__, vbus, cur, idx, info->data.pd_charger_current); } else if (info->chr_type == STANDARD_HOST) { if (IS_ENABLED(CONFIG_USBIF_COMPLIANCE)) { if (info->usb_state == USB_SUSPEND) pdata->input_current_limit = info->data.usb_charger_current_suspend; else if (info->usb_state == USB_UNCONFIGURED) pdata->input_current_limit = info->data.usb_charger_current_unconfigured; else if (info->usb_state == USB_CONFIGURED) pdata->input_current_limit = info->data.usb_charger_current_configured; else pdata->input_current_limit = info->data.usb_charger_current_unconfigured; pdata->charging_current_limit = pdata->input_current_limit; } else { pdata->input_current_limit = info->data.usb_charger_current; /* it can be larger */ pdata->charging_current_limit = info->data.usb_charger_current; } } else if (info->chr_type == NONSTANDARD_CHARGER) { pdata->input_current_limit = info->data.non_std_ac_charger_current; pdata->charging_current_limit = info->data.non_std_ac_charger_current; } else if (info->chr_type == STANDARD_CHARGER) { pdata->input_current_limit = info->data.ac_charger_input_current; pdata->charging_current_limit = info->data.ac_charger_current; mtk_pe20_set_charging_current(info, &pdata->charging_current_limit, &pdata->input_current_limit); mtk_pe_set_charging_current(info, &pdata->charging_current_limit, &pdata->input_current_limit); /* Only enable slave charger when PE+/PE+2.0 is connected */ if ((mtk_pe20_get_is_enable(info) && mtk_pe20_get_is_connect(info)) || (mtk_pe_get_is_enable(info) && mtk_pe_get_is_connect(info))) { /* Slave charger may not have input current control */ pdata2->input_current_limit = info->data.ac_charger_input_current; switch (swchgalg->state) { case CHR_CC: pdata->charging_current_limit = info->data.chg1_ta_ac_charger_current; pdata2->charging_current_limit = info->data.chg2_ta_ac_charger_current; break; case CHR_TUNING: pdata->charging_current_limit = info->data.chg1_ta_ac_charger_current; break; default: break; } } } else if (info->chr_type == CHARGING_HOST) { pdata->input_current_limit = info->data.charging_host_charger_current; pdata->charging_current_limit = info->data.charging_host_charger_current; } else if (info->chr_type == APPLE_1_0A_CHARGER) { pdata->input_current_limit = info->data.apple_1_0a_charger_current; pdata->charging_current_limit = info->data.apple_1_0a_charger_current; } else if (info->chr_type == APPLE_2_1A_CHARGER) { pdata->input_current_limit = info->data.apple_2_1a_charger_current; pdata->charging_current_limit = info->data.apple_2_1a_charger_current; } if (info->enable_sw_jeita) { if (IS_ENABLED(CONFIG_USBIF_COMPLIANCE) && info->chr_type == STANDARD_HOST) pr_debug("USBIF & STAND_HOST skip current check\n"); else { if (info->sw_jeita.sm == TEMP_T0_TO_T1) { pdata->input_current_limit = 500000; pdata->charging_current_limit = 350000; } } } /* * If thermal current limit is less than charging IC's minimum * current setting, disable the charger by setting its current * setting to 0. */ 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; ret = charger_dev_get_min_charging_current(info->chg1_dev, &ichg1_min); if (ret != -ENOTSUPP && pdata->thermal_charging_current_limit < ichg1_min) pdata->charging_current_limit = 0; } if (pdata2->thermal_charging_current_limit != -1) { if (pdata2->thermal_charging_current_limit < pdata2->charging_current_limit) pdata2->charging_current_limit = pdata2->thermal_charging_current_limit; ret = charger_dev_get_min_charging_current(info->chg2_dev, &ichg2_min); if (ret != -ENOTSUPP && pdata2->thermal_charging_current_limit < ichg2_min) pdata2->charging_current_limit = 0; } if (pdata->thermal_input_current_limit != -1) { if (pdata->thermal_input_current_limit < pdata->input_current_limit) pdata->input_current_limit = pdata->thermal_input_current_limit; ret = charger_dev_get_min_input_current(info->chg1_dev, &aicr1_min); if (ret != -ENOTSUPP && pdata->thermal_input_current_limit < aicr1_min) pdata->input_current_limit = 0; } if (pdata2->thermal_input_current_limit != -1) { if (pdata2->thermal_input_current_limit < pdata2->input_current_limit) pdata2->input_current_limit = pdata2->thermal_input_current_limit; ret = charger_dev_get_min_input_current(info->chg2_dev, &aicr2_min); if (ret != -ENOTSUPP && pdata2->thermal_input_current_limit < aicr2_min) pdata2->input_current_limit = 0; } if (mtk_pe40_get_is_connect(info)) { if (info->pe4.pe4_input_current_limit != -1 && info->pe4.pe4_input_current_limit < pdata->input_current_limit) { pdata->input_current_limit = info->pe4.pe4_input_current_limit; if (info->data.parallel_vbus) pdata2->input_current_limit = info->pe4.pe4_input_current_limit; } info->pe4.input_current_limit = pdata->input_current_limit; if (info->pe4.pe4_input_current_limit_setting != -1 && info->pe4.pe4_input_current_limit_setting < pdata->input_current_limit) { pdata->input_current_limit = info->pe4.pe4_input_current_limit_setting; if (info->data.parallel_vbus) pdata2->input_current_limit = info->pe4.pe4_input_current_limit_setting; } } if (pdata->input_current_limit_by_aicl != -1 && !mtk_pe20_get_is_connect(info) && !mtk_pe_get_is_connect(info) && !mtk_is_TA_support_pd_pps(info)) { if (pdata->input_current_limit_by_aicl < pdata->input_current_limit) pdata->input_current_limit = pdata->input_current_limit_by_aicl; } done: if (info->data.parallel_vbus) { pdata->input_current_limit = pdata->input_current_limit / 2; pdata2->input_current_limit = pdata2->input_current_limit / 2; } pr_notice("force:%d %d thermal:(%d %d,%d %d)(%d %d %d)setting:(%d %d)(%d %d)", _uA_to_mA(pdata->force_charging_current), _uA_to_mA(pdata2->force_charging_current), _uA_to_mA(pdata->thermal_input_current_limit), _uA_to_mA(pdata->thermal_charging_current_limit), _uA_to_mA(pdata2->thermal_input_current_limit), _uA_to_mA(pdata2->thermal_charging_current_limit), _uA_to_mA(info->pe4.pe4_input_current_limit), _uA_to_mA(info->pe4.pe4_input_current_limit_setting), _uA_to_mA(info->pe4.input_current_limit), _uA_to_mA(pdata->input_current_limit), _uA_to_mA(pdata->charging_current_limit), _uA_to_mA(pdata2->input_current_limit), _uA_to_mA(pdata2->charging_current_limit)); pr_notice("type:%d usb_unlimited:%d usbif:%d usbsm:%d aicl:%d atm:%d parallel:%d\n", info->chr_type, info->usb_unlimited, IS_ENABLED(CONFIG_USBIF_COMPLIANCE), info->usb_state, _uA_to_mA(pdata->input_current_limit_by_aicl), info->atm_enabled, info->data.parallel_vbus); charger_dev_set_input_current(info->chg1_dev, pdata->input_current_limit); charger_dev_set_charging_current(info->chg1_dev, pdata->charging_current_limit); if ((mtk_pe20_get_is_enable(info) && mtk_pe20_get_is_connect(info)) || (mtk_pe_get_is_enable(info) && mtk_pe_get_is_connect(info)) || mtk_pe40_get_is_connect(info) || (mtk_pdc_check_charger(info) && !dual_swchg_check_pd_leave(info))) { if (chg2_chip_enabled) { charger_dev_set_input_current(info->chg2_dev, pdata2->input_current_limit); charger_dev_set_charging_current(info->chg2_dev, pdata2->charging_current_limit); } } ret = charger_dev_get_min_charging_current(info->chg1_dev, &ichg1_min); if (ret < 0) chr_err("charger_dev_get_min_charging_current not support."); ret = charger_dev_get_min_input_current(info->chg1_dev, &aicr1_min); if (ret < 0) chr_err("charger_dev_get_min_charging_current not support."); /* * If thermal current limit is larger than charging IC's minimum * current setting, enable the charger immediately */ if (pdata->input_current_limit > aicr1_min && pdata->charging_current_limit > ichg1_min && info->can_charging) charger_dev_enable(info->chg1_dev, true); if (pdata->thermal_input_current_limit == -1 && pdata->thermal_charging_current_limit == -1 && pdata2->thermal_input_current_limit == -1 && pdata2->thermal_charging_current_limit == -1) { if (!mtk_pe20_get_is_enable(info) && info->can_charging) { swchgalg->state = CHR_CC; mtk_pe20_set_is_enable(info, true); mtk_pe20_set_to_check_chr_type(info, true); } if (!mtk_pe_get_is_enable(info) && info->can_charging) { swchgalg->state = CHR_CC; mtk_pe_set_is_enable(info, true); mtk_pe_set_to_check_chr_type(info, true); } } mutex_unlock(&swchgalg->ichg_aicr_access_mutex); } static void swchg_select_cv(struct charger_manager *info) { u32 constant_voltage; bool chg2_chip_enabled = false; charger_dev_is_chip_enabled(info->chg2_dev, &chg2_chip_enabled); if (info->enable_sw_jeita) if (info->sw_jeita.cv != 0) { charger_dev_set_constant_voltage(info->chg1_dev, info->sw_jeita.cv); return; } /* dynamic cv*/ constant_voltage = info->data.battery_cv; mtk_get_dynamic_cv(info, &constant_voltage); charger_dev_set_constant_voltage(info->chg1_dev, constant_voltage); /* Set slave charger's CV to 200mV higher than master's */ if (chg2_chip_enabled) charger_dev_set_constant_voltage(info->chg2_dev, constant_voltage + 200000); } static void dual_swchg_turn_on_charging(struct charger_manager *info) { struct dual_switch_charging_alg_data *swchgalg = info->algorithm_data; bool chg1_enable = true; bool chg2_enable = true; bool chg2_chip_enabled = false; charger_dev_is_chip_enabled(info->chg2_dev, &chg2_chip_enabled); if (is_dual_charger_supported(info) == false) chg2_enable = false; if (swchgalg->state == CHR_ERROR) { chg1_enable = false; chg2_enable = false; pr_notice("Charging Error, disable charging!\n"); // workaround for mt6768 // } else if ((get_boot_mode() == META_BOOT) || // (get_boot_mode() == ADVMETA_BOOT)) { // chg1_enable = false; // chg2_enable = false; // pr_notice("In meta mode, disable charging\n"); } else { mtk_pe20_start_algorithm(info); if (mtk_pe20_get_is_connect(info) == false) mtk_pe_start_algorithm(info); dual_swchg_select_charging_current_limit(info); if (info->chg1_data.input_current_limit == 0 || info->chg1_data.charging_current_limit == 0) { chg1_enable = false; chg2_enable = false; pr_notice("chg1's aicr is set to 0mA, turn off\n"); } if ((mtk_pe20_get_is_enable(info) && mtk_pe20_get_is_connect(info)) || (mtk_pe_get_is_enable(info) && mtk_pe_get_is_connect(info)) || mtk_pe40_get_is_connect(info) || (mtk_pdc_check_charger(info) && !dual_swchg_check_pd_leave(info))) { if (info->chg2_data.input_current_limit == 0 || info->chg2_data.charging_current_limit == 0) { chg2_enable = false; pr_notice("chg2's aicr is 0mA, turn off\n"); } } if (chg1_enable) swchg_select_cv(info); } charger_dev_enable(info->chg1_dev, chg1_enable); if (chg2_enable == true) { if ((mtk_pe20_get_is_enable(info) && mtk_pe20_get_is_connect(info)) || (mtk_pe_get_is_enable(info) && mtk_pe_get_is_connect(info)) || mtk_pe40_get_is_connect(info) || (mtk_pdc_check_charger(info) && !dual_swchg_check_pd_leave(info))) { if (!chg2_chip_enabled) charger_dev_enable_chip(info->chg2_dev, true); if (swchgalg->state != CHR_POSTCC && swchgalg->state != CHR_PE40_POSTCC) { charger_dev_enable(info->chg2_dev, true); charger_dev_set_eoc_current(info->chg1_dev, info->data.dual_polling_ieoc); charger_dev_enable_termination(info->chg1_dev, false); } else { charger_dev_set_eoc_current(info->chg1_dev, 150000); if (mtk_pe40_get_is_connect(info) == false) charger_dev_enable_termination( info->chg1_dev, true); } } else { if (chg2_chip_enabled) { charger_dev_enable(info->chg2_dev, false); charger_dev_enable_chip(info->chg2_dev, false); } charger_dev_set_eoc_current(info->chg1_dev, 150000); charger_dev_enable_termination(info->chg1_dev, true); } } else { if (chg2_chip_enabled) { charger_dev_enable(info->chg2_dev, false); charger_dev_enable_chip(info->chg2_dev, false); } } /* If chg1 or chg2 is disabled, leave PE+/PE+20 charging */ if (chg1_enable == false || chg2_enable == false) { if (mtk_pe20_get_is_enable(info)) { mtk_pe20_set_is_enable(info, false); if (mtk_pe20_get_is_connect(info)) mtk_pe20_reset_ta_vchr(info); } if (mtk_pe_get_is_enable(info)) { mtk_pe_set_is_enable(info, false); if (mtk_pe_get_is_connect(info)) mtk_pe_reset_ta_vchr(info); } } charger_dev_is_enabled(info->chg2_dev, &chg2_enable); charger_dev_is_chip_enabled(info->chg2_dev, &chg2_chip_enabled); if (info->data.parallel_vbus) { if (!chg2_enable) { charger_dev_set_input_current(info->chg1_dev, info->chg1_data.input_current_limit * 2); } } chr_err("chg1:%d chg2:%d chg2_chip_en:%d\n", chg1_enable, chg2_enable, chg2_chip_enabled); } static int mtk_dual_switch_charging_plug_in(struct charger_manager *info) { struct dual_switch_charging_alg_data *swchgalg = info->algorithm_data; swchgalg->state = CHR_CC; info->polling_interval = CHARGING_INTERVAL; swchgalg->disable_charging = false; return 0; } static int mtk_dual_switch_charging_plug_out(struct charger_manager *info) { mtk_pe20_set_is_cable_out_occur(info, true); mtk_pe_set_is_cable_out_occur(info, true); mtk_pdc_plugout(info); mtk_pe40_plugout_reset(info); /* charger_dev_enable(info->chg2_dev, false); */ charger_dev_enable_chip(info->chg2_dev, false); return 0; } static int mtk_dual_switch_charging_do_charging(struct charger_manager *info, bool en) { struct dual_switch_charging_alg_data *swchgalg = info->algorithm_data; pr_info("[%s] en:%d %s\n", __func__, en, info->algorithm_name); if (en) { swchgalg->disable_charging = false; swchgalg->state = CHR_CC; charger_manager_notifier(info, CHARGER_NOTIFY_NORMAL); mtk_pe40_set_is_enable(info, en); } else { /* disable charging might change state, so call it first */ _disable_all_charging(info); swchgalg->disable_charging = true; swchgalg->state = CHR_ERROR; charger_manager_notifier(info, CHARGER_NOTIFY_ERROR); } return 0; } static int mtk_dual_switch_chr_pe40_init(struct charger_manager *info) { dual_swchg_turn_on_charging(info); return mtk_pe40_init_state(info); } static int mtk_dual_switch_chr_pe40_cc(struct charger_manager *info) { struct dual_switch_charging_alg_data *swchgalg = info->algorithm_data; bool chg2_en = false; struct charger_data *pdata = &info->chg1_data; dual_swchg_turn_on_charging(info); charger_dev_is_enabled(info->chg2_dev, &chg2_en); /* Check whether eoc condition is met */ if (swchgalg->state != CHR_POSTCC && swchgalg->state != CHR_PE40_POSTCC && chg2_en && (pdata->thermal_charging_current_limit > 500000 || pdata->thermal_charging_current_limit == -1)) { charger_dev_safety_check(info->chg1_dev, info->data.dual_polling_ieoc); } return mtk_pe40_cc_state(info); } static int mtk_dual_switch_chr_cc(struct charger_manager *info) { bool chg_done = false; bool chg2_en = false; struct dual_switch_charging_alg_data *swchgalg = info->algorithm_data; struct charger_data *pdata = &info->chg1_data; /* check bif */ if (IS_ENABLED(CONFIG_MTK_BIF_SUPPORT)) { if (pmic_is_bif_exist() != 1) { pr_notice("No BIF battery, stop charging\n"); swchgalg->state = CHR_ERROR; charger_manager_notifier(info, CHARGER_NOTIFY_ERROR); } } if (mtk_pe40_is_ready(info)) { chr_err("enter PE4.0!\n"); swchgalg->state = CHR_PE40_INIT; info->pe4.is_connect = true; return 1; } dual_swchg_turn_on_charging(info); charger_dev_is_enabled(info->chg2_dev, &chg2_en); chr_err("safety_check state:%d en:%d thermal:%d", swchgalg->state, chg2_en, pdata->thermal_charging_current_limit); /* Check whether eoc condition is met */ if (swchgalg->state != CHR_POSTCC && swchgalg->state != CHR_PE40_POSTCC && chg2_en && (pdata->thermal_charging_current_limit > 500000 || pdata->thermal_charging_current_limit == -1)) { charger_dev_safety_check(info->chg1_dev, info->data.dual_polling_ieoc); } if (info->enable_sw_jeita) { if (info->sw_jeita.pre_sm != TEMP_T2_TO_T3 && info->sw_jeita.sm == TEMP_T2_TO_T3) { /* set to CC state to reset chg2's ichg */ pr_info("back to normal temp, reset state\n"); swchgalg->state = CHR_CC; } } charger_dev_is_charging_done(info->chg1_dev, &chg_done); if (chg_done) { swchgalg->state = CHR_BATFULL; charger_dev_do_event(info->chg1_dev, EVENT_EOC, 0); chr_err("battery full!\n"); } /* If it is not disabled by throttling, * enable PE+/PE+20, if it is disabled */ if (info->chg1_data.thermal_input_current_limit != -1 && info->chg1_data.thermal_input_current_limit < 300000) return 0; return 0; } int mtk_dual_switch_chr_err(struct charger_manager *info) { struct dual_switch_charging_alg_data *swchgalg = info->algorithm_data; if (info->can_charging) { if (info->enable_sw_jeita) { if ((info->sw_jeita.sm == TEMP_BELOW_T0) || (info->sw_jeita.sm == TEMP_ABOVE_T4)) info->sw_jeita.error_recovery_flag = false; if ((info->sw_jeita.error_recovery_flag == false) && (info->sw_jeita.sm != TEMP_BELOW_T0) && (info->sw_jeita.sm != TEMP_ABOVE_T4)) { info->sw_jeita.error_recovery_flag = true; swchgalg->state = CHR_CC; } } else { if (info->thermal.sm == BAT_TEMP_NORMAL) swchgalg->state = CHR_CC; } } _disable_all_charging(info); return 0; } int mtk_dual_switch_chr_full(struct charger_manager *info) { bool chg_done = false; struct dual_switch_charging_alg_data *swchgalg = info->algorithm_data; /* turn off LED */ /* * If CV is set to lower value by JEITA, * Reset CV to normal value if temperture is in normal zone */ swchg_select_cv(info); info->polling_interval = CHARGING_FULL_INTERVAL; charger_dev_is_charging_done(info->chg1_dev, &chg_done); if (!chg_done) { swchgalg->state = CHR_CC; charger_dev_do_event(info->chg1_dev, EVENT_RECHARGE, 0); mtk_pe20_set_to_check_chr_type(info, true); mtk_pe_set_to_check_chr_type(info, true); mtk_pe40_set_is_enable(info, true); info->enable_dynamic_cv = true; chr_err("battery recharging!\n"); info->polling_interval = CHARGING_INTERVAL; } return 0; } static int mtk_dual_switch_charge_current(struct charger_manager *info) { dual_swchg_select_charging_current_limit(info); return 0; } static int mtk_dual_switch_charging_run(struct charger_manager *info) { struct dual_switch_charging_alg_data *swchgalg = info->algorithm_data; int ret = 10; bool chg2_en = false; pr_info("%s [%d]\n", __func__, swchgalg->state); if (mtk_pdc_check_charger(info) == false && mtk_is_TA_support_pd_pps(info) == false) { mtk_pe20_check_charger(info); if (mtk_pe20_get_is_connect(info) == false) mtk_pe_check_charger(info); } switch (swchgalg->state) { case CHR_CC: case CHR_TUNING: case CHR_POSTCC: ret = mtk_dual_switch_chr_cc(info); break; case CHR_PE40_INIT: ret = mtk_dual_switch_chr_pe40_init(info); break; case CHR_PE40_CC: case CHR_PE40_TUNING: case CHR_PE40_POSTCC: ret = mtk_dual_switch_chr_pe40_cc(info); break; case CHR_BATFULL: ret = mtk_dual_switch_chr_full(info); break; case CHR_ERROR: ret = mtk_dual_switch_chr_err(info); break; } charger_dev_dump_registers(info->chg1_dev); charger_dev_is_enabled(info->chg2_dev, &chg2_en); pr_debug_ratelimited("chg2_en: %d\n", chg2_en); if (chg2_en) charger_dev_dump_registers(info->chg2_dev); return 0; } int dual_charger_dev_event(struct notifier_block *nb, unsigned long event, void *v) { struct charger_manager *info = container_of(nb, struct charger_manager, chg1_nb); struct chgdev_notify *data = v; struct charger_data *pdata2 = &info->chg2_data; struct dual_switch_charging_alg_data *swchgalg = info->algorithm_data; u32 ichg2, ichg2_min; bool chg_en = false; bool chg2_chip_enabled = false; charger_dev_is_chip_enabled(info->chg2_dev, &chg2_chip_enabled); chr_info("charger_dev_event %ld\n", event); if (event == CHARGER_DEV_NOTIFY_EOC) { charger_dev_is_enabled(info->chg2_dev, &chg_en); if (!chg_en || !chg2_chip_enabled) { swchgalg->state = CHR_BATFULL; charger_manager_notifier(info, CHARGER_NOTIFY_EOC); if (info->chg1_dev->is_polling_mode == false) _wake_up_charger(info); } else { charger_dev_get_charging_current(info->chg2_dev, &ichg2); charger_dev_get_min_charging_current(info->chg2_dev, &ichg2_min); chr_info("ichg2:%d, ichg2_min:%d state:%d\n", ichg2, ichg2_min, swchgalg->state); if (ichg2 - 500000 < ichg2_min) { if (is_in_pe40_state(info)) swchgalg->state = CHR_PE40_POSTCC; else swchgalg->state = CHR_POSTCC; charger_dev_enable(info->chg2_dev, false); charger_dev_set_eoc_current(info->chg1_dev, 150000); if (mtk_pe40_get_is_connect(info) == false) charger_dev_enable_termination( info->chg1_dev, true); } else { if (is_in_pe40_state(info)) swchgalg->state = CHR_PE40_TUNING; else swchgalg->state = CHR_TUNING; mutex_lock(&swchgalg->ichg_aicr_access_mutex); if (pdata2->charging_current_limit >= 500000) pdata2->charging_current_limit = ichg2 - 500000; else pdata2->charging_current_limit = 0; charger_dev_set_charging_current(info->chg2_dev, pdata2->charging_current_limit); mutex_unlock(&swchgalg->ichg_aicr_access_mutex); } charger_dev_reset_eoc_state(info->chg1_dev); _wake_up_charger(info); } return NOTIFY_DONE; } switch (event) { case CHARGER_DEV_NOTIFY_RECHG: charger_manager_notifier(info, CHARGER_NOTIFY_START_CHARGING); pr_info("%s: recharge\n", __func__); break; case CHARGER_DEV_NOTIFY_SAFETY_TIMEOUT: info->safety_timeout = true; pr_info("%s: safety timer timeout\n", __func__); break; case CHARGER_DEV_NOTIFY_VBUS_OVP: info->vbusov_stat = data->vbusov_stat; pr_info("%s: vbus ovp = %d\n", __func__, info->vbusov_stat); break; default: return NOTIFY_DONE; } if (info->chg1_dev->is_polling_mode == false) _wake_up_charger(info); return NOTIFY_DONE; } int mtk_dual_switch_charging_init(struct charger_manager *info) { struct dual_switch_charging_alg_data *swch_alg; swch_alg = devm_kzalloc(&info->pdev->dev, sizeof(*swch_alg), GFP_KERNEL); if (!swch_alg) return -ENOMEM; info->chg1_dev = get_charger_by_name("primary_chg"); if (info->chg1_dev) chr_info("Found primary charger [%s]\n", info->chg1_dev->props.alias_name); else chr_err("*** Error: can't find primary charger ***\n"); info->chg2_dev = get_charger_by_name("secondary_chg"); if (info->chg2_dev) chr_info("Found secondary charger [%s]\n", info->chg2_dev->props.alias_name); else chr_err("*** Error: can't find secondary charger\n"); mutex_init(&swch_alg->ichg_aicr_access_mutex); info->algorithm_data = swch_alg; info->do_algorithm = mtk_dual_switch_charging_run; info->plug_in = mtk_dual_switch_charging_plug_in; info->plug_out = mtk_dual_switch_charging_plug_out; info->do_charging = mtk_dual_switch_charging_do_charging; info->do_event = dual_charger_dev_event; info->change_current_setting = mtk_dual_switch_charge_current; return 0; }