// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ /* * * Filename: * --------- * mtk_pd.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 "mtk_pd.h" #include "mtk_charger_algorithm_class.h" static int pd_dbg_level = PD_DEBUG_LEVEL; #define PD_VBUS_IR_DROP_THRESHOLD 1200 int pd_get_debug_level(void) { return pd_dbg_level; } static char *pd_state_to_str(int state) { switch (state) { case PD_HW_UNINIT: return "PD_HW_UNINIT"; case PD_HW_FAIL: return "PD_HW_FAIL"; case PD_HW_READY: return "PD_HW_READY"; case PD_TA_NOT_SUPPORT: return "PD_TA_NOT_SUPPORT"; case PD_RUN: return "PD_RUN"; case PD_TUNING: return "PD_TUNING"; case PD_POSTCC: return "PD_POSTCC"; default: break; } pd_err("%s unknown state:%d\n", __func__ , state); return "PD_UNKNOWN"; } static int _pd_init_algo(struct chg_alg_device *alg) { struct mtk_pd *pd; int cnt; pd = dev_get_drvdata(&alg->dev); pd_dbg("%s\n", __func__); mutex_lock(&pd->access_lock); if (pd_hal_init_hardware(alg) != 0) { pd->state = PD_HW_FAIL; pd_err("%s:init hw fail\n", __func__); } else pd->state = PD_HW_READY; if (alg->config == DUAL_CHARGERS_IN_PARALLEL) { pd_err("%s does not support DUAL_CHARGERS_IN_PARALLEL\n", __func__); alg->config = SINGLE_CHARGER; } else if (alg->config == DUAL_CHARGERS_IN_SERIES) { cnt = pd_hal_get_charger_cnt(alg); if (cnt == 2) alg->config = DUAL_CHARGERS_IN_SERIES; else alg->config = SINGLE_CHARGER; } else alg->config = SINGLE_CHARGER; pd->pdc_max_watt_setting = -1; pd->check_impedance = true; pd->pd_cap_max_watt = -1; pd->pd_idx = -1; pd->pd_reset_idx = -1; pd->pd_boost_idx = 0; pd->pd_buck_idx = 0; mutex_unlock(&pd->access_lock); return 0; } static int _pd_is_algo_ready(struct chg_alg_device *alg) { struct mtk_pd *pd = dev_get_drvdata(&alg->dev); int ret_value; int uisoc; pd_err("%s %d\n", __func__, pd->state); switch (pd->state) { case PD_HW_UNINIT: case PD_HW_FAIL: ret_value = ALG_INIT_FAIL; break; case PD_HW_READY: ret_value = pd_hal_is_pd_adapter_ready(alg); if (ret_value == ALG_READY) { uisoc = pd_hal_get_uisoc(alg); if (pd->input_current_limit1 != -1 || pd->charging_current_limit1 != -1 || pd->input_current_limit2 != -1 || pd->charging_current_limit2 != -1 || uisoc >= pd->pd_stop_battery_soc) ret_value = ALG_NOT_READY; } else if (ret_value == ALG_TA_NOT_SUPPORT) pd->state = PD_TA_NOT_SUPPORT; else if (ret_value == ALG_TA_CHECKING) pd->state = PD_HW_READY; else pd->state = PD_TA_NOT_SUPPORT; break; case PD_TA_NOT_SUPPORT: ret_value = ALG_TA_NOT_SUPPORT; break; case PD_RUN: case PD_TUNING: case PD_POSTCC: ret_value = ALG_RUNNING; break; default: pd_err("PD unknown state:%d\n", pd->state); ret_value = ALG_INIT_FAIL; break; } return ret_value; } void __mtk_pdc_init_table(struct chg_alg_device *alg) { struct mtk_pd *pd = dev_get_drvdata(&alg->dev); pd->cap.nr = 0; pd->cap.selected_cap_idx = -1; if (pd_hal_is_pd_adapter_ready(alg) == ALG_READY) pd_hal_get_adapter_cap(alg, &pd->cap); else pd_err("mtk_is_pdc_ready is fail\n"); pd_err("[%s] nr:%d default:%d\n", __func__, pd->cap.nr, pd->cap.selected_cap_idx); } void __mtk_pdc_get_reset_idx(struct chg_alg_device *alg) { struct mtk_pd *pd = dev_get_drvdata(&alg->dev); struct pd_power_cap *cap; int i = 0; int idx = 0; cap = &pd->cap; if (pd->pd_reset_idx == -1) { for (i = 0; i < cap->nr; i++) { if (cap->min_mv[i] < pd->vbus_l || cap->max_mv[i] < pd->vbus_l || cap->min_mv[i] > pd->vbus_l || cap->max_mv[i] > pd->vbus_l) { continue; } idx = i; } pd->pd_reset_idx = idx; pd_err("[%s]reset idx:%d vbus:%d %d\n", __func__, idx, cap->min_mv[idx], cap->max_mv[idx]); } } void __mtk_pdc_get_cap_max_watt(struct chg_alg_device *alg) { struct mtk_pd *pd = dev_get_drvdata(&alg->dev); struct pd_power_cap *cap; int i = 0; int idx = 0; cap = &pd->cap; if (pd->pd_cap_max_watt == -1) { for (i = 0; i < cap->nr; i++) { if (cap->min_mv[i] <= pd->vbus_h && cap->min_mv[i] >= pd->vbus_l && cap->max_mv[i] <= pd->vbus_h && cap->max_mv[i] >= pd->vbus_l) { if (cap->maxwatt[i] > pd->pd_cap_max_watt) { pd->pd_cap_max_watt = cap->maxwatt[i]; idx = i; } pd_err("%d %d %d %d %d %d\n", cap->min_mv[i], cap->max_mv[i], pd->vbus_h, pd->vbus_l, cap->maxwatt[i], pd->pd_cap_max_watt); continue; } } pd_err("[%s]idx:%d vbus:%d %d maxwatt:%d\n", __func__, idx, cap->min_mv[idx], cap->max_mv[idx], pd->pd_cap_max_watt); } } int __mtk_pdc_get_idx(struct chg_alg_device *alg, int selected_idx, int *boost_idx, int *buck_idx) { struct mtk_pd *pd = dev_get_drvdata(&alg->dev); struct pd_power_cap *cap; int i = 0; int idx = 0; cap = &pd->cap; idx = selected_idx; if (idx < 0) { pd_err("[%s] invalid idx:%d\n", __func__, idx); *boost_idx = 0; *buck_idx = 0; return -1; } /* get boost_idx */ for (i = 0; i < cap->nr; i++) { if (cap->min_mv[i] < pd->vbus_l || cap->max_mv[i] < pd->vbus_l) { pd_err("min_mv error:%d %d %d\n", cap->min_mv[i], cap->max_mv[i], pd->vbus_l); continue; } if (cap->min_mv[i] > pd->vbus_h || cap->max_mv[i] > pd->vbus_h) { pd_err("max_mv error:%d %d %d\n", cap->min_mv[i], cap->max_mv[i], pd->vbus_h); continue; } if (idx == selected_idx) { if (cap->maxwatt[i] > cap->maxwatt[idx]) idx = i; } else { if (cap->maxwatt[i] < cap->maxwatt[idx] && cap->maxwatt[i] > cap->maxwatt[selected_idx]) idx = i; } } *boost_idx = idx; idx = selected_idx; /* get buck_idx */ for (i = 0; i < cap->nr; i++) { if (cap->min_mv[i] < pd->vbus_l || cap->max_mv[i] < pd->vbus_l) { pd_err("min_mv error:%d %d %d\n", cap->min_mv[i], cap->max_mv[i], pd->vbus_l); continue; } if (cap->min_mv[i] > pd->vbus_h || cap->max_mv[i] > pd->vbus_h) { pd_err("max_mv error:%d %d %d\n", cap->min_mv[i], cap->max_mv[i], pd->vbus_h); continue; } if (idx == selected_idx) { if (cap->maxwatt[i] < cap->maxwatt[idx]) idx = i; } else { if (cap->maxwatt[i] > cap->maxwatt[idx] && cap->maxwatt[i] < cap->maxwatt[selected_idx]) idx = i; } } *buck_idx = idx; return 0; } int __mtk_pdc_setup(struct chg_alg_device *alg, int idx) { int ret = -100; unsigned int mivr; unsigned int oldmivr = 4600000; unsigned int oldmA = 3000000; bool force_update = false; int chg_cnt, is_chip_enabled, i; struct mtk_pd *pd = dev_get_drvdata(&alg->dev); if (pd->pd_idx == idx) { pd_hal_get_mivr(alg, CHG1, &oldmivr); if (pd->cap.max_mv[idx] - oldmivr / 1000 > PD_VBUS_IR_DROP_THRESHOLD) force_update = true; chg_cnt = pd_hal_get_charger_cnt(alg); if (chg_cnt > 1 && alg->config == DUAL_CHARGERS_IN_SERIES) { for (i = CHG2; i < CHG_MAX; i++) { is_chip_enabled = pd_hal_is_chip_enable(alg, i); if (is_chip_enabled) { pd_hal_get_mivr(alg, CHG2, &oldmivr); if (pd->cap.max_mv[idx] - oldmivr / 1000 > PD_VBUS_IR_DROP_THRESHOLD - pd->slave_mivr_diff / 1000) force_update = true; } } } } if (pd->pd_idx != idx || force_update) { if (pd->cap.max_mv[idx] > 5000) pd_hal_enable_vbus_ovp(alg, false); else pd_hal_enable_vbus_ovp(alg, true); pd_hal_get_mivr(alg, CHG1, &oldmivr); mivr = pd->min_charger_voltage / 1000; pd_hal_set_mivr(alg, CHG1, pd->min_charger_voltage); pd_hal_get_input_current(alg, CHG1, &oldmA); oldmA = oldmA / 1000; #ifdef FIXME if (info->data.parallel_vbus && (oldmA * 2 > pd->cap.ma[idx])) { charger_dev_set_input_current(info->chg1_dev, pd->cap.ma[idx] * 1000 / 2); charger_dev_set_input_current(info->chg2_dev, pd->cap.ma[idx] * 1000 / 2); } else if (info->data.parallel_vbus == false && (oldmA > pd->cap.ma[idx])) charger_dev_set_input_current(info->chg1_dev, pd->cap.ma[idx] * 1000); #endif if (oldmA > pd->cap.ma[idx]) pd_hal_set_input_current(alg, CHG1, pd->cap.ma[idx] * 1000); ret = pd_hal_set_adapter_cap(alg, pd->cap.max_mv[idx], pd->cap.ma[idx]); if (ret == 0) { #ifdef FIXME if (info->data.parallel_vbus && (oldmA * 2 < pd->cap.ma[idx])) { charger_dev_set_input_current(info->chg1_dev, pd->cap.ma[idx] * 1000 / 2); charger_dev_set_input_current(info->chg2_dev, pd->cap.ma[idx] * 1000 / 2); } else if (info->data.parallel_vbus == false && (oldmA < pd->cap.ma[idx])) charger_dev_set_input_current(info->chg1_dev, pd->cap.ma[idx] * 1000); #endif if (oldmA < pd->cap.ma[idx]) pd_hal_set_input_current(alg, CHG1, pd->cap.ma[idx] * 1000); if ((pd->cap.max_mv[idx] - PD_VBUS_IR_DROP_THRESHOLD) > mivr) mivr = pd->cap.max_mv[idx] - PD_VBUS_IR_DROP_THRESHOLD; pd_hal_set_mivr(alg, CHG1, mivr * 1000); } else { #ifdef FIXME if (info->data.parallel_vbus && (oldmA * 2 > pd->cap.ma[idx])) { charger_dev_set_input_current(info->chg1_dev, oldmA * 1000 / 2); charger_dev_set_input_current(info->chg2_dev, oldmA * 1000 / 2); } else if (info->data.parallel_vbus == false && (oldmA > pd->cap.ma[idx])) charger_dev_set_input_current(info->chg1_dev, oldmA * 1000); #endif if (oldmA > pd->cap.ma[idx]) pd_hal_set_input_current(alg, CHG1, oldmA * 1000); pd_hal_set_mivr(alg, CHG1, oldmivr); } __mtk_pdc_get_idx(alg, idx, &pd->pd_boost_idx, &pd->pd_buck_idx); } pd_err("[%s]idx:%d:%d:%d:%d vbus:%d cur:%d ret:%d\n", __func__, pd->pd_idx, idx, pd->pd_boost_idx, pd->pd_buck_idx, pd->cap.max_mv[idx], pd->cap.ma[idx], ret); pd->pd_idx = idx; return ret; } void mtk_pdc_reset(struct chg_alg_device *alg) { struct mtk_pd *pd = dev_get_drvdata(&alg->dev); pd_err("%s: reset to default profile\n", __func__); __mtk_pdc_init_table(alg); __mtk_pdc_get_reset_idx(alg); __mtk_pdc_setup(alg, pd->pd_reset_idx); } int __mtk_pdc_get_setting(struct chg_alg_device *alg, int *newvbus, int *newcur, int *newidx) { int ret = 0; int idx, selected_idx; unsigned int pd_max_watt, pd_min_watt, now_max_watt; struct mtk_pd *pd = dev_get_drvdata(&alg->dev); int ibus = 0, vbus; int chg2_watt = 0; bool boost = false, buck = false; struct pd_power_cap *cap; unsigned int mivr1 = 0; unsigned int mivr2 = 0; bool chg1_mivr = false; bool chg2_mivr = false; int chg_cnt, i, is_chip_enabled; __mtk_pdc_init_table(alg); __mtk_pdc_get_reset_idx(alg); __mtk_pdc_get_cap_max_watt(alg); cap = &pd->cap; if (cap->nr == 0) return -1; ret = pd_hal_get_ibus(alg, &ibus); if (ret < 0) { pd_err("[%s] get ibus fail, keep default voltage\n", __func__); return -1; } #ifdef FIXME if (info->data.parallel_vbus) { ret = charger_dev_get_ibat(info->chg1_dev, &chg1_ibat); if (ret < 0) pd_err("[%s] get ibat fail\n", __func__); ret = charger_dev_get_ibat(info->chg2_dev, &chg2_ibat); if (ret < 0) { ibat = battery_get_bat_current(); chg2_ibat = ibat * 100 - chg1_ibat; } if (ibat < 0 || chg2_ibat < 0) chg2_watt = 0; else chg2_watt = chg2_ibat / 1000 * battery_get_bat_voltage() / info->data.chg2_eff * 100; pd_err("[%s] chg2_watt:%d ibat2:%d ibat1:%d ibat:%d\n", __func__, chg2_watt, chg2_ibat, chg1_ibat, ibat * 100); } #endif pd_hal_get_mivr_state(alg, CHG1, &chg1_mivr); pd_hal_get_mivr(alg, CHG1, &mivr1); chg_cnt = pd_hal_get_charger_cnt(alg); if (chg_cnt > 1 && alg->config == DUAL_CHARGERS_IN_SERIES) { for (i = CHG2; i < CHG_MAX; i++) { is_chip_enabled = pd_hal_is_chip_enable(alg, i); if (is_chip_enabled) { pd_hal_get_mivr_state(alg, CHG2, &chg2_mivr); pd_hal_get_mivr(alg, CHG2, &mivr2); } } } vbus = pd_hal_get_vbus(alg); ibus = ibus / 1000; if (ibus == 0) ibus = 1000; if ((chg1_mivr && (vbus < mivr1 / 1000 - 500)) || (chg2_mivr && (vbus < mivr2 / 1000 - 500))) goto reset; selected_idx = cap->selected_cap_idx; idx = selected_idx; if (idx < 0 || idx >= PD_CAP_MAX_NR) idx = selected_idx = 0; pd_err("idx:%d %d %d %d %d %d\n", idx, cap->max_mv[idx], cap->ma[idx], cap->maxwatt[idx], pd->ibus_err, ibus); pd_max_watt = cap->max_mv[idx] * (cap->ma[idx] / 100 * (100 - pd->ibus_err) - 100); pd_dbg("pd_max_watt:%d %d %d %d %d\n", idx, cap->max_mv[idx], cap->ma[idx], pd->ibus_err, pd_max_watt); now_max_watt = cap->max_mv[idx] * ibus + chg2_watt; pd_dbg("now_max_watt:%d %d %d %d %d\n", idx, cap->max_mv[idx], ibus, chg2_watt, now_max_watt); pd_min_watt = cap->max_mv[pd->pd_buck_idx] * cap->ma[pd->pd_buck_idx] / 100 * (100 - pd->ibus_err) - pd->vsys_watt; pd_dbg("pd_min_watt:%d %d %d %d %d\n", pd->pd_buck_idx, cap->max_mv[pd->pd_buck_idx], cap->ma[pd->pd_buck_idx], pd->ibus_err, pd->vsys_watt); if (pd_min_watt <= 5000000) pd_min_watt = 5000000; if ((now_max_watt >= pd_max_watt) || chg1_mivr || chg2_mivr) { *newidx = pd->pd_boost_idx; boost = true; } else if (now_max_watt <= pd_min_watt) { *newidx = pd->pd_buck_idx; buck = true; } else { *newidx = selected_idx; boost = false; buck = false; } *newvbus = cap->max_mv[*newidx]; *newcur = cap->ma[*newidx]; pd_err("[%s]watt:%d,%d,%d up:%d,%d vbus:%d ibus:%d, mivr:%d,%d\n", __func__, pd_max_watt, now_max_watt, pd_min_watt, boost, buck, vbus, ibus, chg1_mivr, chg2_mivr); pd_err("[%s]vbus:%d:%d:%d current:%d idx:%d default_idx:%d\n", __func__, pd->vbus_h, pd->vbus_l, *newvbus, *newcur, *newidx, selected_idx); return 0; reset: mtk_pdc_reset(alg); *newidx = pd->pd_reset_idx; *newvbus = cap->max_mv[*newidx]; *newcur = cap->ma[*newidx]; return 0; } static int pd_sc_set_charger(struct chg_alg_device *alg) { struct mtk_pd *pd; int ichg1_min = -1, aicr1_min = -1; int ret; pd = dev_get_drvdata(&alg->dev); if (pd->input_current_limit1 == 0 || pd->charging_current_limit1 == 0) { pr_notice("input/charging current is 0, end Pd\n"); return -1; } mutex_lock(&pd->data_lock); if (pd->charging_current_limit1 != -1) { if (pd->charging_current_limit1 < pd->sc_charger_current) pd->charging_current1 = pd->charging_current_limit1; ret = pd_hal_get_min_charging_current(alg, CHG1, &ichg1_min); if (ret != -ENOTSUPP && pd->charging_current_limit1 < ichg1_min) pd->charging_current1 = 0; } else pd->charging_current1 = pd->sc_charger_current; if (pd->input_current_limit1 != -1 && pd->input_current_limit1 < pd->sc_input_current) { pd->input_current1 = pd->input_current_limit1; ret = pd_hal_get_min_input_current(alg, CHG1, &aicr1_min); if (ret != -ENOTSUPP && pd->input_current_limit1 < aicr1_min) pd->input_current1 = 0; } else pd->input_current1 = pd->sc_input_current; mutex_unlock(&pd->data_lock); if (pd->input_current1 == 0 || pd->charging_current1 == 0) { pd_err("current is zero %d %d\n", pd->input_current1, pd->charging_current1); return -1; } pd_hal_set_charging_current(alg, CHG1, pd->charging_current1); pd_hal_set_input_current(alg, CHG1, pd->input_current1); pd_hal_set_cv(alg, CHG1, pd->cv); pd_dbg("%s m:%d s:%d cv:%d chg1:%d,%d min:%d:%d\n", __func__, alg->config, pd->state, pd->cv, pd->input_current1, pd->charging_current1, ichg1_min, aicr1_min); return 0; } static int pd_dcs_set_charger(struct chg_alg_device *alg) { struct mtk_pd *pd; bool chg2_enable = true; bool chg2_chip_enabled = false; int ret; int ichg1_min = -1, ichg2_min = -1; int aicr1_min = -1; pd = dev_get_drvdata(&alg->dev); if (pd->input_current_limit1 == 0 || pd->charging_current_limit1 == 0 || pd->charging_current_limit2 == 0) { pr_notice("input/charging current is 0, end PD\n"); return -1; } mutex_lock(&pd->data_lock); if (pd->input_current_limit1 != -1 && pd->input_current_limit1 < pd->dcs_input_current) { pd->input_current1 = pd->input_current_limit1; ret = pd_hal_get_min_input_current(alg, CHG1, &aicr1_min); if (ret != -ENOTSUPP && pd->input_current_limit1 < aicr1_min) pd->input_current1 = 0; } else pd->input_current1 = pd->dcs_input_current; if (pd->charging_current_limit1 != -1 && pd->charging_current_limit1 < pd->dcs_chg1_charger_current) { pd->charging_current1 = pd->charging_current_limit1; ret = pd_hal_get_min_charging_current(alg, CHG1, &ichg1_min); if (ret != -ENOTSUPP && pd->charging_current_limit1 < ichg1_min) pd->charging_current1 = 0; } else pd->charging_current1 = pd->dcs_chg1_charger_current; if (pd->state == PD_RUN) pd->charging_current2 = pd->dcs_chg2_charger_current; if (pd->charging_current_limit2 != -1 && pd->charging_current_limit2 < pd->charging_current2) { pd->charging_current2 = pd->charging_current_limit2; ret = pd_hal_get_min_charging_current(alg, CHG2, &ichg2_min); if (ret != -ENOTSUPP && pd->charging_current_limit2 < ichg2_min) pd->charging_current2 = 0; } mutex_unlock(&pd->data_lock); if (pd->input_current1 == 0 || pd->charging_current1 == 0 || pd->charging_current2 == 0) { pd_err("current is zero %d %d %d\n", pd->input_current1, pd->charging_current1, pd->charging_current2); pd_hal_enable_charger(alg, CHG2, false); pd_hal_charger_enable_chip(alg, CHG2, false); return -1; } chg2_chip_enabled = pd_hal_is_chip_enable(alg, CHG2); pd_err("chg2_en:%d %d %d\n", chg2_enable, chg2_chip_enabled, pd->state); if (pd->state == PD_RUN) { if (!chg2_chip_enabled) pd_hal_charger_enable_chip(alg, CHG2, true); pd_hal_enable_charger(alg, CHG2, true); pd_hal_set_input_current(alg, CHG2, pd->charging_current2); pd_hal_set_charging_current(alg, CHG2, pd->charging_current2); pd_hal_set_eoc_current(alg, CHG1, pd->dual_polling_ieoc); pd_hal_enable_termination(alg, CHG1, false); pd_hal_safety_check(alg, pd->dual_polling_ieoc); } else if (pd->state == PD_TUNING) { if (!chg2_chip_enabled) pd_hal_charger_enable_chip(alg, CHG2, true); pd_hal_enable_charger(alg, CHG2, true); pd_hal_set_eoc_current(alg, CHG1, pd->dual_polling_ieoc); pd_hal_enable_termination(alg, CHG1, false); pd_hal_safety_check(alg, pd->dual_polling_ieoc); } else if (pd->state == PD_POSTCC) { pd_hal_set_eoc_current(alg, CHG1, 150000); pd_hal_enable_termination(alg, CHG1, true); } else { pd_err("%s state error!", __func__); return -1; } pd_hal_set_charging_current(alg, CHG1, pd->charging_current1); pd_hal_set_input_current(alg, CHG1, pd->input_current1); pd_hal_set_cv(alg, CHG1, pd->cv); pd_dbg("%s m:%d s:%d cv:%d chg1:%d,%d chg2:%d,%d chg2en:%d min:%d,%d,%d\n", __func__, alg->config, pd->state, pd->cv, pd->input_current1, pd->charging_current1, pd->input_current2, pd->charging_current2, chg2_enable, ichg1_min, ichg2_min, aicr1_min); return 0; } static int __pd_run(struct chg_alg_device *alg) { struct mtk_pd *pd = dev_get_drvdata(&alg->dev); int vbus, cur, idx, ret, ret_value = ALG_RUNNING; ret = __mtk_pdc_get_setting(alg, &vbus, &cur, &idx); if (ret != -1 && idx != -1) { if ((pd->input_current_limit1 != -1 && pd->input_current_limit1 < cur * 1000) == false) pd->input_current_limit1 = cur * 1000; __mtk_pdc_setup(alg, idx); } else { pd->input_current_limit1 = PD_FAIL_CURRENT; pd->charging_current_limit1 = PD_FAIL_CURRENT; } if (alg->config == DUAL_CHARGERS_IN_SERIES) { if (pd_dcs_set_charger(alg) != 0) { ret_value = ALG_DONE; //goto out; } } else { if (pd_sc_set_charger(alg) != 0) { ret_value = ALG_DONE; //goto out; } } return ret_value; } static int _pd_start_algo(struct chg_alg_device *alg) { int ret_value = 0; struct mtk_pd *pd = dev_get_drvdata(&alg->dev); bool again = false; int uisoc; mutex_lock(&pd->access_lock); do { pd_info("%s state:%d %s %d\n", __func__, pd->state, pd_state_to_str(pd->state), again); again = false; switch (pd->state) { case PD_HW_UNINIT: case PD_HW_FAIL: ret_value = ALG_INIT_FAIL; break; case PD_HW_READY: ret_value = pd_hal_is_pd_adapter_ready(alg); if (ret_value == ALG_TA_NOT_SUPPORT) pd->state = PD_TA_NOT_SUPPORT; else if (ret_value == ALG_READY) { uisoc = pd_hal_get_uisoc(alg); if (pd->input_current_limit1 != -1 || pd->charging_current_limit1 != -1 || pd->input_current_limit2 != -1 || pd->charging_current_limit2 != -1 || uisoc >= pd->pd_stop_battery_soc) ret_value = ALG_NOT_READY; else { pd->state = PD_RUN; again = true; } } break; case PD_TA_NOT_SUPPORT: ret_value = ALG_TA_NOT_SUPPORT; break; case PD_RUN: case PD_TUNING: case PD_POSTCC: ret_value = __pd_run(alg); break; default: pd_err("PD unknown state:%d\n", pd->state); ret_value = ALG_INIT_FAIL; break; } } while (again == true); mutex_unlock(&pd->access_lock); return ret_value; } static bool _pd_is_algo_running(struct chg_alg_device *alg) { struct mtk_pd *pd; pd_dbg("%s\n", __func__); pd = dev_get_drvdata(&alg->dev); if (pd->state == PD_RUN || pd->state == PD_TUNING || pd->state == PD_POSTCC) return true; return false; } static int _pd_stop_algo(struct chg_alg_device *alg) { int ret_value = 0; struct mtk_pd *pd = dev_get_drvdata(&alg->dev); mutex_lock(&pd->access_lock); pd_info("%s state:%d %s\n", __func__, pd->state, pd_state_to_str(pd->state)); switch (pd->state) { case PD_HW_UNINIT: case PD_HW_FAIL: case PD_HW_READY: case PD_TA_NOT_SUPPORT: break; case PD_TUNING: case PD_POSTCC: case PD_RUN: mtk_pdc_reset(alg); pd_hal_set_charging_current(alg, CHG1, PD_FAIL_CURRENT); pd_hal_set_input_current(alg, CHG1, PD_FAIL_CURRENT); pd_hal_set_cv(alg, CHG1, pd->cv); pd->state = PD_HW_READY; if (alg->config == DUAL_CHARGERS_IN_SERIES) { pd_hal_enable_charger(alg, CHG2, false); pd_hal_charger_enable_chip(alg, CHG2, false); } break; default: pd_err("PD unknown state:%d\n", pd->state); ret_value = ALG_INIT_FAIL; break; } mutex_unlock(&pd->access_lock); return ret_value; } static int pd_full_evt(struct chg_alg_device *alg) { struct mtk_pd *pd; int ret = 0; bool chg_en = true, chg2_enabled = false; int ichg2 = 0, ichg2_min = 100000; int ret_value = 0; pd = dev_get_drvdata(&alg->dev); switch (pd->state) { case PD_HW_UNINIT: case PD_HW_FAIL: case PD_HW_READY: case PD_TA_NOT_SUPPORT: break; case PD_TUNING: case PD_POSTCC: case PD_RUN: if (alg->config == DUAL_CHARGERS_IN_SERIES) { pd_hal_is_charger_enable( alg, CHG2, &chg_en); chg2_enabled = pd_hal_is_chip_enable(alg, CHG2); if (!chg_en || !chg2_enabled) { /* notify eoc , fix me */ pd->state = PD_HW_READY; pd_err("%s: charging done:%d %d\n", __func__, chg_en, chg2_enabled); if (alg->is_polling_mode == false) ret_value = 1; } else { pd_hal_get_charging_current(alg, CHG2, &ichg2); ret = pd_hal_get_min_charging_current( alg, CHG2, &ichg2_min); if (ret == -ENOTSUPP) ichg2_min = 100000; pd_err("ichg2:%d, ichg2_min:%d state:%d\n", ichg2, ichg2_min, pd->state); if (ichg2 - 500000 <= ichg2_min) { pd->state = PD_POSTCC; pd_hal_enable_charger(alg, CHG2, false); pd_hal_set_eoc_current(alg, CHG1, 150000); pd_hal_enable_termination(alg, CHG1, true); } else { pd->state = PD_TUNING; mutex_lock(&pd->data_lock); if (pd->charging_current2 >= 500000) pd->charging_current2 = ichg2 - 500000; pd_hal_set_charging_current(alg, CHG2, pd->charging_current2); mutex_unlock(&pd->data_lock); } ret_value = 1; } } else { if (pd->state == PD_RUN) { pd_err("%s evt full\n", __func__); pd->state = PD_HW_READY; } } break; default: pd_err("PD unknown state:%d\n", pd->state); ret_value = ALG_INIT_FAIL; break; } return ret_value; } static int pd_plugout_reset(struct chg_alg_device *alg) { struct mtk_pd *pd; pd = dev_get_drvdata(&alg->dev); switch (pd->state) { case PD_HW_UNINIT: case PD_HW_FAIL: case PD_HW_READY: break; case PD_TA_NOT_SUPPORT: pd->state = PD_HW_READY; break; case PD_TUNING: case PD_POSTCC: case PD_RUN: pd->state = PD_HW_READY; if (alg->config == DUAL_CHARGERS_IN_SERIES) { pd_hal_enable_charger(alg, CHG2, false); pd_hal_charger_enable_chip(alg, CHG2, false); } pd->pd_cap_max_watt = -1; pd->pd_idx = -1; pd->pd_reset_idx = -1; pd->pd_boost_idx = 0; pd->pd_buck_idx = 0; break; default: pd_err("PD unknown state:%d\n", pd->state); break; } return 0; } static int _pd_notifier_call(struct chg_alg_device *alg, struct chg_alg_notify *notify) { struct mtk_pd *pd; int ret_value = 0; pd = dev_get_drvdata(&alg->dev); pd_err("%s evt:%d state:%s\n", __func__, notify->evt, pd_state_to_str(pd->state)); switch (notify->evt) { case EVT_PLUG_OUT: ret_value = pd_plugout_reset(alg); break; case EVT_FULL: ret_value = pd_full_evt(alg); break; default: ret_value = -EINVAL; } return ret_value; } static void mtk_pd_parse_dt(struct mtk_pd *pd, struct device *dev) { struct device_node *np = dev->of_node; u32 val; val = 0; if (of_property_read_u32(np, "min_charger_voltage", &val) >= 0) pd->min_charger_voltage = val; else { pd_err("use default V_CHARGER_MIN:%d\n", V_CHARGER_MIN); pd->min_charger_voltage = V_CHARGER_MIN; } /* PD */ val = 0; if (of_property_read_u32(np, "pd_vbus_upper_bound", &val) >= 0) { pd->vbus_h = val / 1000; } else { pd_err("use default pd_vbus_upper_bound:%d\n", PD_VBUS_UPPER_BOUND); pd->vbus_h = PD_VBUS_UPPER_BOUND / 1000; } if (of_property_read_u32(np, "pd_vbus_low_bound", &val) >= 0) { pd->vbus_l = val / 1000; } else { pd_err("use default pd_vbus_low_bound:%d\n", PD_VBUS_LOW_BOUND); pd->vbus_l = PD_VBUS_LOW_BOUND / 1000; } if (of_property_read_u32(np, "vsys_watt", &val) >= 0) { pd->vsys_watt = val; } else { pd_err("use default vsys_watt:%d\n", VSYS_WATT); pd->vsys_watt = VSYS_WATT; } if (of_property_read_u32(np, "ibus_err", &val) >= 0) { pd->ibus_err = val; } else { pd_err("use default ibus_err:%d\n", IBUS_ERR); pd->ibus_err = IBUS_ERR; } if (of_property_read_u32(np, "pd_stop_battery_soc", &val) >= 0) pd->pd_stop_battery_soc = val; else { pd_err("use default pd_stop_battery_soc:%d\n", PD_STOP_BATTERY_SOC); pd->pd_stop_battery_soc = PD_STOP_BATTERY_SOC; } /* single charger */ if (of_property_read_u32(np, "sc_input_current", &val) >= 0) { pd->sc_input_current = val; } else { pd_err("use default sc_input_current:%d\n", PD_SC_INPUT_CURRENT); pd->sc_input_current = PD_SC_INPUT_CURRENT; } if (of_property_read_u32(np, "sc_charger_current", &val) >= 0) { pd->sc_charger_current = val; } else { pd_err("use default sc_charger_current:%d\n", PD_SC_CHARGER_CURRENT); pd->sc_charger_current = PD_SC_CHARGER_CURRENT; } /* dual charger in series*/ if (of_property_read_u32(np, "dcs_input_current", &val) >= 0) { pd->dcs_input_current = val; } else { pd_err("use default dcs_input_current:%d\n", PD_DCS_INPUT_CURRENT); pd->dcs_input_current = PD_DCS_INPUT_CURRENT; } if (of_property_read_u32(np, "dcs_chg1_charger_current", &val) >= 0) { pd->dcs_chg1_charger_current = val; } else { pd_err("use default dcs_chg1_charger_current:%d\n", PD_DCS_CHG1_CHARGER_CURRENT); pd->dcs_chg1_charger_current = PD_DCS_CHG1_CHARGER_CURRENT; } if (of_property_read_u32(np, "dcs_chg2_charger_current", &val) >= 0) { pd->dcs_chg2_charger_current = val; } else { pd_err("use default dcs_chg2_charger_current:%d\n", PD_DCS_CHG2_CHARGER_CURRENT); pd->dcs_chg2_charger_current = PD_DCS_CHG2_CHARGER_CURRENT; } /* dual charger */ if (of_property_read_u32(np, "slave_mivr_diff", &val) >= 0) pd->slave_mivr_diff = val; else { pd_err("use default SLAVE_MIVR_DIFF:%d\n", SLAVE_MIVR_DIFF); pd->slave_mivr_diff = SLAVE_MIVR_DIFF; } if (of_property_read_u32(np, "dual_polling_ieoc", &val) >= 0) pd->dual_polling_ieoc = val; else { pd_err("use default dual_polling_ieoc :%d\n", 750000); pd->dual_polling_ieoc = 750000; } } int _pd_get_prop(struct chg_alg_device *alg, enum chg_alg_props s, int *value) { pr_notice("%s\n", __func__); if (s == ALG_MAX_VBUS) *value = 10000; else pr_notice("%s does not support prop:%d\n", __func__, s); return 0; } int _pd_set_setting(struct chg_alg_device *alg_dev, struct chg_limit_setting *setting) { struct mtk_pd *pd; pd_dbg("%s cv:%d icl:%d,%d cc:%d,%d\n", __func__, setting->cv, setting->input_current_limit1, setting->input_current_limit2, setting->charging_current_limit1, setting->charging_current_limit2); pd = dev_get_drvdata(&alg_dev->dev); mutex_lock(&pd->access_lock); pd->cv = setting->cv; pd->input_current_limit1 = setting->input_current_limit1; pd->charging_current_limit1 = setting->charging_current_limit1; pd->input_current_limit2 = setting->input_current_limit2; pd->charging_current_limit2 = setting->charging_current_limit2; mutex_unlock(&pd->access_lock); return 0; } int _pd_set_prop(struct chg_alg_device *alg, enum chg_alg_props s, int value) { pr_notice("%s %d %d\n", __func__, s, value); return 0; } static struct chg_alg_ops pd_alg_ops = { .init_algo = _pd_init_algo, .is_algo_ready = _pd_is_algo_ready, .start_algo = _pd_start_algo, .is_algo_running = _pd_is_algo_running, .stop_algo = _pd_stop_algo, .notifier_call = _pd_notifier_call, .get_prop = _pd_get_prop, .set_prop = _pd_set_prop, .set_current_limit = _pd_set_setting, }; static int mtk_pd_probe(struct platform_device *pdev) { struct mtk_pd *pd = NULL; pr_notice("%s: starts\n", __func__); pd = devm_kzalloc(&pdev->dev, sizeof(*pd), GFP_KERNEL); if (!pd) return -ENOMEM; platform_set_drvdata(pdev, pd); pd->pdev = pdev; mutex_init(&pd->access_lock); mutex_init(&pd->data_lock); mtk_pd_parse_dt(pd, &pdev->dev); pd->bat_psy = devm_power_supply_get_by_phandle(&pdev->dev, "gauge"); if (IS_ERR_OR_NULL(pd->bat_psy)) pd_err("%s: devm power fail to get bat_psy\n", __func__); pd->alg = chg_alg_device_register("pd", &pdev->dev, pd, &pd_alg_ops, NULL); return 0; } static int mtk_pd_remove(struct platform_device *dev) { return 0; } static void mtk_pd_shutdown(struct platform_device *dev) { } static const struct of_device_id mtk_pd_of_match[] = { {.compatible = "mediatek,charger,pd",}, {}, }; MODULE_DEVICE_TABLE(of, mtk_pd_of_match); struct platform_device pd_device = { .name = "pd", .id = -1, }; static struct platform_driver pd_driver = { .probe = mtk_pd_probe, .remove = mtk_pd_remove, .shutdown = mtk_pd_shutdown, .driver = { .name = "pd", .of_match_table = mtk_pd_of_match, }, }; static int __init mtk_pd_init(void) { return platform_driver_register(&pd_driver); } late_initcall(mtk_pd_init); static void __exit mtk_pd_exit(void) { platform_driver_unregister(&pd_driver); } module_exit(mtk_pd_exit); MODULE_AUTHOR("wy.chuang "); MODULE_DESCRIPTION("MTK PD algorithm Driver"); MODULE_LICENSE("GPL");