/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2021 MediaTek Inc. */ /* * * Filename: * --------- * mtk_switch_charging.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 */ /* FIXME */ #include "mtk_charger_intf.h" #include "mtk_switch_charging.h" struct tag_bootmode { u32 size; u32 tag; u32 bootmode; u32 boottype; }; static int _uA_to_mA(int uA) { if (uA == -1) return -1; else return uA / 1000; } static void _disable_all_charging(struct charger_manager *info) { charger_dev_enable(info->chg1_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_pe50_get_is_enable(info)) { if (mtk_pe50_get_is_connect(info)) mtk_pe50_stop_algo(info, true); } 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 void swchg_select_charging_current_limit(struct charger_manager *info) { struct charger_data *pdata = NULL; struct switch_charging_alg_data *swchgalg = info->algorithm_data; u32 ichg1_min = 0, aicr1_min = 0; int ret = 0; struct device *dev = NULL; struct device_node *boot_node = NULL; struct tag_bootmode *tag = NULL; int boot_mode = 11;//UNKNOWN_BOOT // workaround for mt6768 dev = &(info->pdev->dev); if (dev != 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 boot_mode = tag->bootmode; } } if (info->pe5.online) { chr_err("In PE5.0\n"); return; } pdata = &info->chg1_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; pdata->charging_current_limit = info->data.ac_charger_current; } goto done; } if (info->usb_unlimited) { if (pdata->input_current_limit_by_aicl != -1) { pdata->input_current_limit = pdata->input_current_limit_by_aicl; } else { pdata->input_current_limit = info->data.usb_unlimited_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 ((boot_mode == META_BOOT) || (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_is_TA_support_pd_pps(info)) { 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; info->is_pdc_run = true; 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; } chr_err("[%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); } 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 (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; } 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; } 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; 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 (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: ret = charger_dev_get_min_charging_current(info->chg1_dev, &ichg1_min); if (ret != -ENOTSUPP && pdata->charging_current_limit < ichg1_min) pdata->charging_current_limit = 0; ret = charger_dev_get_min_input_current(info->chg1_dev, &aicr1_min); if (ret != -ENOTSUPP && pdata->input_current_limit < aicr1_min) pdata->input_current_limit = 0; chr_err("force:%d thermal:%d,%d pe4:%d,%d,%d setting:%d %d type:%d usb_unlimited:%d usbif:%d usbsm:%d aicl:%d atm:%d\n", _uA_to_mA(pdata->force_charging_current), _uA_to_mA(pdata->thermal_input_current_limit), _uA_to_mA(pdata->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), info->chr_type, info->usb_unlimited, IS_ENABLED(CONFIG_USBIF_COMPLIANCE), info->usb_state, pdata->input_current_limit_by_aicl, info->atm_enabled); 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 AICR < 300mA, stop PE+/PE+20 */ if (pdata->input_current_limit < 300000) { 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 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); mutex_unlock(&swchgalg->ichg_aicr_access_mutex); } static void swchg_select_cv(struct charger_manager *info) { u32 constant_voltage; 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); } static void swchg_turn_on_charging(struct charger_manager *info) { struct switch_charging_alg_data *swchgalg = info->algorithm_data; bool charging_enable = true; struct device *dev = NULL; struct device_node *boot_node = NULL; struct tag_bootmode *tag = NULL; int boot_mode = 11;//UNKNOWN_BOOT // workaround for mt6768 dev = &(info->pdev->dev); if (dev != 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 boot_mode = tag->bootmode; } } if (swchgalg->state == CHR_ERROR) { charging_enable = false; chr_err("[charger]Charger Error, turn OFF charging !\n"); } // workaround for mt6768 else if ((boot_mode == META_BOOT) || (boot_mode == ADVMETA_BOOT)) { charging_enable = false; info->chg1_data.input_current_limit = 200000; /* 200mA */ charger_dev_set_input_current(info->chg1_dev, info->chg1_data.input_current_limit); chr_err("In meta mode, disable charging and set input current limit to 200mA\n"); } else { mtk_pe20_start_algorithm(info); if (mtk_pe20_get_is_connect(info) == false) mtk_pe_start_algorithm(info); swchg_select_charging_current_limit(info); if (info->chg1_data.input_current_limit == 0 || info->chg1_data.charging_current_limit == 0) { charging_enable = false; chr_err("[charger]charging current is set 0mA, turn off charging !\n"); } else { swchg_select_cv(info); } } charger_dev_enable(info->chg1_dev, charging_enable); } static int mtk_switch_charging_plug_in(struct charger_manager *info) { struct switch_charging_alg_data *swchgalg = info->algorithm_data; swchgalg->state = CHR_CC; info->polling_interval = CHARGING_INTERVAL; swchgalg->disable_charging = false; get_monotonic_boottime(&swchgalg->charging_begin_time); return 0; } static int mtk_switch_charging_plug_out(struct charger_manager *info) { struct switch_charging_alg_data *swchgalg = info->algorithm_data; swchgalg->total_charging_time = 0; 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); mtk_pe50_plugout_reset(info); return 0; } static int mtk_switch_charging_do_charging(struct charger_manager *info, bool en) { struct switch_charging_alg_data *swchgalg = info->algorithm_data; chr_err("%s: en:%d %s\n", __func__, en, info->algorithm_name); if (en) { swchgalg->disable_charging = false; swchgalg->state = CHR_CC; get_monotonic_boottime(&swchgalg->charging_begin_time); charger_manager_notifier(info, CHARGER_NOTIFY_NORMAL); mtk_pe40_set_is_enable(info, en); mtk_pe50_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_switch_chr_pe40_init(struct charger_manager *info) { swchg_turn_on_charging(info); return mtk_pe40_init_state(info); } static int mtk_switch_chr_pe40_cc(struct charger_manager *info) { swchg_turn_on_charging(info); return mtk_pe40_cc_state(info); } static int mtk_switch_chr_pe50_ready(struct charger_manager *info) { int ret; struct switch_charging_alg_data *swchgalg = info->algorithm_data; ret = mtk_pe50_start(info); if (ret < 0) { info->pe5.online = false; swchgalg->state = CHR_CC; } else swchgalg->state = CHR_PE50_RUNNING; return 0; } static int mtk_switch_chr_pe50_running(struct charger_manager *info) { struct switch_charging_alg_data *swchgalg = info->algorithm_data; struct charger_data *dvchg_data = &info->dvchg1_data; if (!mtk_pe50_is_running(info)) goto stop; if (!info->enable_hv_charging) { mtk_pe50_stop_algo(info, true); goto stop; } mtk_pe50_thermal_throttling(info, dvchg_data->thermal_input_current_limit); if (info->enable_sw_jeita) mtk_pe50_set_jeita_vbat_cv(info, info->sw_jeita.cv); return 0; stop: chr_info("%s PE5 stops\n", __func__); info->pe5.online = false; swchgalg->state = CHR_CC; /* Let charging algorithm run CHR_CC immediately */ return -EINVAL; } /* return false if total charging time exceeds max_charging_time */ static bool mtk_switch_check_charging_time(struct charger_manager *info) { struct switch_charging_alg_data *swchgalg = info->algorithm_data; struct timespec time_now; if (info->enable_sw_safety_timer) { get_monotonic_boottime(&time_now); chr_debug("%s: begin: %ld, now: %ld\n", __func__, swchgalg->charging_begin_time.tv_sec, time_now.tv_sec); if (swchgalg->total_charging_time >= info->data.max_charging_time) { chr_err("%s: SW safety timeout: %d sec > %d sec\n", __func__, swchgalg->total_charging_time, info->data.max_charging_time); charger_dev_notify(info->chg1_dev, CHARGER_DEV_NOTIFY_SAFETY_TIMEOUT); return false; } } return true; } static int mtk_switch_chr_cc(struct charger_manager *info) { bool chg_done = false; struct switch_charging_alg_data *swchgalg = info->algorithm_data; struct timespec time_now, charging_time; /* check bif */ if (IS_ENABLED(CONFIG_MTK_BIF_SUPPORT)) { if (pmic_is_bif_exist() != 1) { chr_err("CONFIG_MTK_BIF_SUPPORT but no bif , stop charging\n"); swchgalg->state = CHR_ERROR; charger_manager_notifier(info, CHARGER_NOTIFY_ERROR); } } get_monotonic_boottime(&time_now); charging_time = timespec_sub(time_now, swchgalg->charging_begin_time); swchgalg->total_charging_time = charging_time.tv_sec; if (mtk_pe50_is_ready(info)) { chr_err("enter PE5.0\n"); swchgalg->state = CHR_PE50_READY; info->pe5.online = true; 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); } return 1; } if (mtk_pe40_is_ready(info)) { chr_err("enter PE4.0!\n"); swchgalg->state = CHR_PE40_INIT; info->pe4.is_connect = true; 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); } return 1; } swchg_turn_on_charging(info); 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 < 300) return 0; if (!mtk_pe20_get_is_enable(info)) { mtk_pe20_set_is_enable(info, true); mtk_pe20_set_to_check_chr_type(info, true); } if (!mtk_pe_get_is_enable(info)) { mtk_pe_set_is_enable(info, true); mtk_pe_set_to_check_chr_type(info, true); } return 0; } int mtk_switch_chr_err(struct charger_manager *info) { struct switch_charging_alg_data *swchgalg = info->algorithm_data; 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; get_monotonic_boottime(&swchgalg->charging_begin_time); } } swchgalg->total_charging_time = 0; _disable_all_charging(info); return 0; } int mtk_switch_chr_full(struct charger_manager *info) { bool chg_done = false; struct switch_charging_alg_data *swchgalg = info->algorithm_data; swchgalg->total_charging_time = 0; /* 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); mtk_pe50_set_is_enable(info, true); info->enable_dynamic_cv = true; get_monotonic_boottime(&swchgalg->charging_begin_time); chr_err("battery recharging!\n"); info->polling_interval = CHARGING_INTERVAL; } return 0; } static int mtk_switch_charging_current(struct charger_manager *info) { swchg_select_charging_current_limit(info); return 0; } static int mtk_switch_charging_run(struct charger_manager *info) { struct switch_charging_alg_data *swchgalg = info->algorithm_data; int ret = 0; chr_err("%s [%d %d], timer=%d\n", __func__, swchgalg->state, info->pd_type, swchgalg->total_charging_time); if (mtk_pdc_check_charger(info) == false && mtk_is_TA_support_pd_pps(info) == false && !info->pe5.online) { mtk_pe20_check_charger(info); if (mtk_pe20_get_is_connect(info) == false) mtk_pe_check_charger(info); } if (mtk_pe40_get_is_connect(info)) { if (mtk_pe50_is_ready(info)) mtk_pe40_end(info, 4, true); } do { switch (swchgalg->state) { chr_err("%s_2 [%d] %d\n", __func__, swchgalg->state, info->pd_type); case CHR_CC: ret = mtk_switch_chr_cc(info); break; case CHR_PE40_INIT: ret = mtk_switch_chr_pe40_init(info); break; case CHR_PE40_CC: ret = mtk_switch_chr_pe40_cc(info); break; case CHR_PE50_READY: ret = mtk_switch_chr_pe50_ready(info); break; case CHR_PE50_RUNNING: ret = mtk_switch_chr_pe50_running(info); break; case CHR_BATFULL: ret = mtk_switch_chr_full(info); break; case CHR_ERROR: ret = mtk_switch_chr_err(info); break; } } while (ret != 0); mtk_switch_check_charging_time(info); charger_dev_dump_registers(info->chg1_dev); return 0; } int 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; chr_info("%s %ld", __func__, event); switch (event) { case CHARGER_DEV_NOTIFY_EOC: charger_manager_notifier(info, CHARGER_NOTIFY_EOC); pr_info("%s: end of charge\n", __func__); break; case CHARGER_DEV_NOTIFY_RECHG: #ifdef CONFIG_TCPC_CLASS if (mt_check_cable_in() > 0) { charger_manager_notifier(info, CHARGER_NOTIFY_START_CHARGING); pr_info("%s: recharge\n", __func__); break; } pr_info("%s: cable not in, cannot recharge\n", __func__); break; #else charger_manager_notifier(info, CHARGER_NOTIFY_START_CHARGING); pr_info("%s: recharge\n", __func__); break; #endif case CHARGER_DEV_NOTIFY_SAFETY_TIMEOUT: info->safety_timeout = true; chr_err("%s: safety timer timeout\n", __func__); /* If sw safety timer timeout, do not wake up charger thread */ if (info->enable_sw_safety_timer) return NOTIFY_DONE; break; case CHARGER_DEV_NOTIFY_VBUS_OVP: info->vbusov_stat = data->vbusov_stat; chr_err("%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; } static int dvchg1_dev_event(struct notifier_block *nb, unsigned long event, void *data) { struct charger_manager *info = container_of(nb, struct charger_manager, dvchg1_nb); struct switch_charging_alg_data *swchgalg = info->algorithm_data; chr_info("%s %ld", __func__, event); if (swchgalg->state == CHR_PE50_READY || swchgalg->state == CHR_PE50_RUNNING) return mtk_pe50_notifier_call(info, MTK_PE50_NOTISRC_CHG, event, data); return 0; } static int dvchg2_dev_event(struct notifier_block *nb, unsigned long event, void *data) { struct charger_manager *info = container_of(nb, struct charger_manager, dvchg2_nb); struct switch_charging_alg_data *swchgalg = info->algorithm_data; chr_info("%s %ld", __func__, event); if (swchgalg->state == CHR_PE50_READY || swchgalg->state == CHR_PE50_RUNNING) return mtk_pe50_notifier_call(info, MTK_PE50_NOTISRC_CHG, event, data); return 0; } int mtk_switch_charging_init(struct charger_manager *info) { int ret = 0; struct 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_err("Found primary charger [%s]\n", info->chg1_dev->props.alias_name); else chr_err("*** Error : can't find primary charger ***\n"); info->dvchg1_dev = get_charger_by_name("primary_divider_chg"); if (info->dvchg1_dev) { chr_err("Found primary divider charger [%s]\n", info->dvchg1_dev->props.alias_name); info->dvchg1_nb.notifier_call = dvchg1_dev_event; register_charger_device_notifier(info->dvchg1_dev, &info->dvchg1_nb); } else chr_err("*** Error : can't find primary divider charger ***\n"); info->dvchg2_dev = get_charger_by_name("secondary_divider_chg"); if (info->dvchg2_dev) { chr_err("Found secondary divider charger [%s]\n", info->dvchg2_dev->props.alias_name); info->dvchg2_nb.notifier_call = dvchg2_dev_event; register_charger_device_notifier(info->dvchg2_dev, &info->dvchg2_nb); } else chr_err("*** Error : can't find secondary divider charger ***\n"); mutex_init(&swch_alg->ichg_aicr_access_mutex); info->algorithm_data = swch_alg; info->do_algorithm = mtk_switch_charging_run; info->plug_in = mtk_switch_charging_plug_in; info->plug_out = mtk_switch_charging_plug_out; info->do_charging = mtk_switch_charging_do_charging; info->do_event = charger_dev_event; info->change_current_setting = mtk_switch_charging_current; return ret; }