| // 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 <linux/init.h> /* For init/exit macros */ |
| #include <linux/module.h> /* For MODULE_ marcros */ |
| #include <linux/fs.h> |
| #include <linux/device.h> |
| #include <linux/interrupt.h> |
| #include <linux/spinlock.h> |
| #include <linux/platform_device.h> |
| #include <linux/device.h> |
| #include <linux/kdev_t.h> |
| #include <linux/fs.h> |
| #include <linux/cdev.h> |
| #include <linux/delay.h> |
| #include <linux/kernel.h> |
| #include <linux/init.h> |
| #include <linux/types.h> |
| #include <linux/wait.h> |
| #include <linux/slab.h> |
| #include <linux/fs.h> |
| #include <linux/sched.h> |
| #include <linux/poll.h> |
| #include <linux/power_supply.h> |
| #include <linux/pm_wakeup.h> |
| #include <linux/time.h> |
| #include <linux/mutex.h> |
| #include <linux/kthread.h> |
| #include <linux/proc_fs.h> |
| #include <linux/platform_device.h> |
| #include <linux/seq_file.h> |
| #include <linux/scatterlist.h> |
| #include <linux/suspend.h> |
| #include <linux/of.h> |
| #include <linux/of_irq.h> |
| #include <linux/of_address.h> |
| #include <linux/reboot.h> |
| |
| #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 |
| |
| static bool algo_waiver_test; |
| module_param(algo_waiver_test, bool, 0644); |
| |
| 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, log_level; |
| |
| 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; |
| |
| pd_hal_vbat_mon_en(alg, CHG1, false); |
| pd->old_cv = 0; |
| pd->stop_6pin_re_en = 0; |
| |
| 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; |
| |
| log_level = pd_hal_get_log_level(alg); |
| pr_notice("%s: log_level=%d", __func__, log_level); |
| if (log_level > 0) |
| pd_dbg_level = log_level; |
| |
| 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; |
| |
| if (algo_waiver_test) { |
| ret_value = ALG_WAIVER; |
| goto skip; |
| } |
| |
| pd_dbg("%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) |
| ret_value = ALG_NOT_READY; |
| else if (uisoc >= pd->pd_stop_battery_soc || |
| (uisoc == -1 && pd->ref_vbat > pd->vbat_threshold)) |
| ret_value = ALG_WAIVER; |
| } 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; |
| } |
| skip: |
| 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_dbg("[%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_dbg("[%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_dbg("%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_dbg("[%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_dbg("[%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_dbg("%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); |
| pd_hal_vbat_mon_en(alg, CHG1, false); |
| pd->old_cv = 0; |
| } |
| |
| |
| 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_dbg("[%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_dbg("[%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_dbg("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_dbg("[%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_dbg("[%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 != -EOPNOTSUPP && |
| 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 != -EOPNOTSUPP && |
| 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); |
| |
| if (pd->old_cv == 0 || (pd->old_cv != pd->cv) || pd->pd_6pin_en == 0) { |
| pd_hal_vbat_mon_en(alg, CHG1, false); |
| pd_hal_set_cv(alg, CHG1, pd->cv); |
| if (pd->pd_6pin_en && pd->stop_6pin_re_en != 1) |
| pd_hal_vbat_mon_en(alg, CHG1, true); |
| |
| pd->old_cv = pd->cv; |
| } else { |
| if (pd->pd_6pin_en && pd->stop_6pin_re_en != 1) { |
| pd->stop_6pin_re_en = 1; |
| pd_hal_vbat_mon_en(alg, CHG1, true); |
| } |
| } |
| |
| pd_dbg("%s old_cv=%d, new_cv=%d, pd_6pin_en=%d 6pin_re_en=%d\n", __func__, |
| pd->old_cv, pd->cv, pd->pd_6pin_en, pd->stop_6pin_re_en); |
| |
| 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 != -EOPNOTSUPP && |
| 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 != -EOPNOTSUPP && |
| 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 != -EOPNOTSUPP && |
| 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_dbg("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); |
| |
| if (algo_waiver_test) { |
| ret_value = ALG_WAIVER; |
| goto skip; |
| } |
| |
| 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) |
| ret_value = ALG_NOT_READY; |
| else if (uisoc >= pd->pd_stop_battery_soc || |
| (uisoc == -1 && pd->ref_vbat > pd->vbat_threshold)) |
| ret_value = ALG_WAIVER; |
| 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); |
| skip: |
| 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 == -EOPNOTSUPP) |
| 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_dbg("%s evt:%d state:%s\n", __func__, notify->evt, |
| pd_state_to_str(pd->state)); |
| |
| switch (notify->evt) { |
| case EVT_PLUG_OUT: |
| pd->stop_6pin_re_en = 0; |
| ret_value = pd_plugout_reset(alg); |
| break; |
| case EVT_FULL: |
| pd->stop_6pin_re_en = 1; |
| ret_value = pd_full_evt(alg); |
| break; |
| case EVT_BATPRO_DONE: |
| pd->pd_6pin_en = 0; |
| ret_value = 0; |
| 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; |
| |
| 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 */ |
| 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; |
| } |
| |
| if (of_property_read_u32(np, "vbat_threshold", &val) >= 0) |
| pd->vbat_threshold = val; |
| else { |
| pr_notice("turn off vbat_threshold checking:%d\n", |
| DISABLE_VBAT_THRESHOLD); |
| pd->vbat_threshold = DISABLE_VBAT_THRESHOLD; |
| } |
| |
| } |
| |
| 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->pd_6pin_en = setting->vbat_mon_en; |
| 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) |
| { |
| struct mtk_pd *pd; |
| |
| pr_notice("%s %d %d\n", __func__, s, value); |
| |
| pd = dev_get_drvdata(&alg->dev); |
| |
| switch (s) { |
| case ALG_LOG_LEVEL: |
| pd_dbg_level = value; |
| break; |
| case ALG_REF_VBAT: |
| pd->ref_vbat = value; |
| break; |
| default: |
| break; |
| } |
| |
| 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); |
| } |
| module_init(mtk_pd_init); |
| |
| static void __exit mtk_pd_exit(void) |
| { |
| platform_driver_unregister(&pd_driver); |
| } |
| module_exit(mtk_pd_exit); |
| |
| |
| MODULE_AUTHOR("wy.chuang <wy.chuang@mediatek.com>"); |
| MODULE_DESCRIPTION("MTK PD algorithm Driver"); |
| MODULE_LICENSE("GPL"); |