6db4831e98
Android 14
1362 lines
31 KiB
C
1362 lines
31 KiB
C
// 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
|
|
|
|
|
|
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 <wy.chuang@mediatek.com>");
|
|
MODULE_DESCRIPTION("MTK PD algorithm Driver");
|
|
MODULE_LICENSE("GPL");
|
|
|