unplugged-kernel/drivers/power/supply/mediatek/charger/mtk_pdc_intf.c

580 lines
14 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include "mtk_charger_intf.h"
#define PD_VBUS_IR_DROP_THRESHOLD 1200
void mtk_pdc_plugout(struct charger_manager *info)
{
info->pdc.check_impedance = true;
info->pdc.pd_cap_max_watt = -1;
info->pdc.pd_idx = -1;
info->pdc.pd_reset_idx = -1;
info->pdc.pd_boost_idx = 0;
info->pdc.pd_buck_idx = 0;
}
int mtk_pdc_set_mivr(struct charger_manager *info, int uV)
{
int ret = 0;
bool chg2_chip_enabled = false;
ret = charger_dev_set_mivr(info->chg1_dev, uV);
if (ret < 0)
chr_err("%s: failed, ret = %d\n", __func__, ret);
if (info->chg2_dev) {
charger_dev_is_chip_enabled(info->chg2_dev,
&chg2_chip_enabled);
if (chg2_chip_enabled) {
ret = charger_dev_set_mivr(info->chg2_dev,
uV + info->data.slave_mivr_diff);
if (ret < 0)
pr_info("%s: chg2 failed, ret = %d\n", __func__,
ret);
}
}
return ret;
}
void mtk_pdc_check_cable_impedance(struct charger_manager *pinfo)
{
int ret = 0;
int vchr1, vchr2, cable_imp;
unsigned int aicr_value;
bool mivr_state = false;
struct timespec ptime[2], diff;
if (pinfo->pdc.check_impedance == false)
return;
pinfo->pdc.check_impedance = false;
pr_debug("%s: starts\n", __func__);
get_monotonic_boottime(&ptime[0]);
/* Set ichg = 2500mA, set MIVR */
charger_dev_set_charging_current(pinfo->chg1_dev, 2500000);
mdelay(240);
ret = mtk_pdc_set_mivr(pinfo, pinfo->data.min_charger_voltage);
if (ret < 0)
chr_err("%s: failed, ret = %d\n", __func__, ret);
get_monotonic_boottime(&ptime[1]);
diff = timespec_sub(ptime[1], ptime[0]);
aicr_value = 800000;
charger_dev_set_input_current(pinfo->chg1_dev, aicr_value);
/* To wait for soft-start */
msleep(150);
ret = charger_dev_get_mivr_state(pinfo->chg1_dev, &mivr_state);
if (ret != -ENOTSUPP && mivr_state) {
pr_debug("%s: fail ret:%d mivr_state:%d\n", __func__,
ret, mivr_state);
goto end;
}
vchr1 = battery_get_vbus() * 1000;
aicr_value = 500000;
charger_dev_set_input_current(pinfo->chg1_dev, aicr_value);
msleep(20);
vchr2 = battery_get_vbus() * 1000;
/*
* Calculate cable impedance (|V1 - V2|) / (|I2 - I1|)
* m_ohm = (mv * 10 * 1000) / (mA * 10)
* m_ohm = (uV * 10) / (mA * 10)
*/
cable_imp = (abs(vchr1 - vchr2) * 10) / (7400 - 4625);
chr_err("%s: cable_imp:%d mohm, vchr1:%d, vchr2:%d, time:%ld\n",
__func__, cable_imp, vchr1 / 1000, vchr2 / 1000, diff.tv_nsec);
chr_err("cable_imp:%d threshold:%d s:%d\n", cable_imp,
pinfo->data.cable_imp_threshold, mivr_state);
return;
end:
chr_err("%s fail\n",
__func__);
}
static bool mtk_is_pdc_ready(struct charger_manager *info)
{
if (info->pd_type == MTK_PD_CONNECT_PE_READY_SNK ||
info->pd_type == MTK_PD_CONNECT_PE_READY_SNK_PD30)
return true;
if (info->pd_type == MTK_PD_CONNECT_PE_READY_SNK_APDO &&
info->enable_pe_4 == false &&
info->enable_pe_5 == false)
return true;
return false;
}
bool mtk_pdc_check_charger(struct charger_manager *info)
{
if (mtk_is_pdc_ready(info) == false)
return false;
return true;
}
void mtk_pdc_plugout_reset(struct charger_manager *info)
{
info->pdc.cap.nr = 0;
}
void mtk_pdc_set_max_watt(struct charger_manager *info, int watt)
{
info->pdc.vbus_h = 10000;
info->pdc.pdc_max_watt_setting = watt;
}
int mtk_pdc_get_max_watt(struct charger_manager *info)
{
int charging_current = info->data.pd_charger_current / 1000;
int vbat = pmic_get_battery_voltage();
if (info->pdc.pdc_max_watt_setting != -1)
info->pdc.pdc_max_watt = info->pdc.pdc_max_watt_setting;
else {
if (info->chg1_data.thermal_charging_current_limit != -1)
charging_current =
info->chg1_data.thermal_charging_current_limit / 1000;
info->pdc.pdc_max_watt = vbat * charging_current;
}
chr_err("[%s]watt:%d:%d vbat:%d c:%d=>\n", __func__,
info->pdc.pdc_max_watt_setting,
info->pdc.pdc_max_watt, vbat, charging_current);
return info->pdc.pdc_max_watt;
}
int mtk_pdc_get_idx(struct charger_manager *info, int selected_idx,
int *boost_idx, int *buck_idx)
{
struct mtk_pdc *pd = &info->pdc;
struct adapter_power_cap *cap;
int i = 0;
int idx = 0;
cap = &pd->cap;
idx = selected_idx;
if (idx < 0) {
chr_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) {
chr_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) {
chr_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) {
chr_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) {
chr_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 charger_manager *info, int idx)
{
int ret = -100;
unsigned int mivr;
unsigned int oldmivr = 4600000;
unsigned int oldmA = 3000000;
bool chg2_chip_enabled = false;
bool force_update = false;
struct mtk_pdc *pd = &info->pdc;
if (pd->pd_idx == idx) {
charger_dev_get_mivr(info->chg1_dev, &oldmivr);
if (pd->cap.max_mv[idx] - oldmivr / 1000 >
PD_VBUS_IR_DROP_THRESHOLD)
force_update = true;
if (info->chg2_dev) {
charger_dev_is_chip_enabled(info->chg2_dev,
&chg2_chip_enabled);
if (chg2_chip_enabled) {
charger_dev_get_mivr(info->chg2_dev, &oldmivr);
if (pd->cap.max_mv[idx] - oldmivr / 1000 >
PD_VBUS_IR_DROP_THRESHOLD -
info->data.slave_mivr_diff / 1000)
force_update = true;
}
}
}
if (pd->pd_idx != idx || force_update) {
if (pd->cap.max_mv[idx] > 5000)
charger_enable_vbus_ovp(info, false);
else
charger_enable_vbus_ovp(info, true);
charger_dev_get_mivr(info->chg1_dev, &oldmivr);
mivr = info->data.min_charger_voltage / 1000;
mtk_pdc_set_mivr(info, info->data.min_charger_voltage);
charger_dev_get_input_current(info->chg1_dev, &oldmA);
oldmA = oldmA / 1000;
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);
ret = adapter_dev_set_cap(info->pd_adapter, MTK_PD,
pd->cap.max_mv[idx], pd->cap.ma[idx]);
if (ret == MTK_ADAPTER_OK) {
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);
if ((pd->cap.max_mv[idx] - PD_VBUS_IR_DROP_THRESHOLD)
> mivr)
mivr = pd->cap.max_mv[idx] -
PD_VBUS_IR_DROP_THRESHOLD;
mtk_pdc_set_mivr(info, mivr * 1000);
} else {
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);
mtk_pdc_set_mivr(info, oldmivr);
}
mtk_pdc_get_idx(info, idx, &pd->pd_boost_idx, &pd->pd_buck_idx);
}
chr_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_get_cap_max_watt(struct charger_manager *info)
{
struct mtk_pdc *pd = &info->pdc;
struct adapter_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->max_mv[i] <= pd->vbus_h) {
if (cap->maxwatt[i] > pd->pd_cap_max_watt) {
pd->pd_cap_max_watt = cap->maxwatt[i];
idx = i;
}
continue;
}
}
chr_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);
}
}
void mtk_pdc_get_reset_idx(struct charger_manager *info)
{
struct mtk_pdc *pd = &info->pdc;
struct adapter_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;
chr_err("[%s]reset idx:%d vbus:%d %d\n", __func__,
idx, cap->min_mv[idx], cap->max_mv[idx]);
}
}
void mtk_pdc_reset(struct charger_manager *info)
{
struct mtk_pdc *pd = &info->pdc;
info->is_pdc_run = false;
chr_err("%s: reset to default profile\n", __func__);
mtk_pdc_init_table(info);
mtk_pdc_get_reset_idx(info);
mtk_pdc_setup(info, pd->pd_reset_idx);
}
int mtk_pdc_get_setting(struct charger_manager *info, 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_pdc *pd = &info->pdc;
int ibus = 0, vbus;
int ibat = 0, chg1_ibat = 0, chg2_ibat = 0;
int chg2_watt = 0;
bool boost = false, buck = false;
struct adapter_power_cap *cap = NULL;
unsigned int mivr1 = 0;
unsigned int mivr2 = 0;
bool chg1_mivr = false;
bool chg2_mivr = false;
bool chg2_enable = false;
mtk_pdc_init_table(info);
mtk_pdc_get_reset_idx(info);
mtk_pdc_get_cap_max_watt(info);
cap = &pd->cap;
if (cap->nr == 0)
return -1;
if (info->enable_hv_charging == false)
goto reset;
ret = charger_dev_get_ibus(info->chg1_dev, &ibus);
if (ret < 0) {
chr_err("[%s] get ibus fail, keep default voltage\n", __func__);
return -1;
}
if (info->data.parallel_vbus) {
ret = charger_dev_get_ibat(info->chg1_dev, &chg1_ibat);
if (ret < 0)
chr_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;
chr_err("[%s] chg2_watt:%d ibat2:%d ibat1:%d ibat:%d\n",
__func__, chg2_watt, chg2_ibat, chg1_ibat, ibat * 100);
}
charger_dev_get_mivr_state(info->chg1_dev, &chg1_mivr);
charger_dev_get_mivr(info->chg1_dev, &mivr1);
if (is_dual_charger_supported(info)) {
charger_dev_is_enabled(info->chg2_dev, &chg2_enable);
if (chg2_enable) {
charger_dev_get_mivr_state(info->chg2_dev, &chg2_mivr);
charger_dev_get_mivr(info->chg2_dev, &mivr2);
}
}
vbus = battery_get_vbus();
ibus = 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 >= ADAPTER_CAP_MAX_NR)
idx = selected_idx = 0;
pd_max_watt = cap->max_mv[idx] * (cap->ma[idx]
/ 100 * (100 - info->data.ibus_err) - 100);
now_max_watt = cap->max_mv[idx] * ibus + chg2_watt;
pd_min_watt = cap->max_mv[pd->pd_buck_idx] * cap->ma[pd->pd_buck_idx]
/ 100 * (100 - info->data.ibus_err)
- info->data.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];
chr_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);
chr_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(info);
*newidx = pd->pd_reset_idx;
*newvbus = cap->max_mv[*newidx];
*newcur = cap->ma[*newidx];
return 0;
}
void mtk_pdc_init_table(struct charger_manager *info)
{
struct mtk_pdc *pd = &info->pdc;
pd->cap.nr = 0;
pd->cap.selected_cap_idx = -1;
if (mtk_is_pdc_ready(info))
adapter_dev_get_cap(info->pd_adapter, MTK_PD, &pd->cap);
else
chr_err("mtk_is_pdc_ready is fail\n");
chr_err("[%s] nr:%d default:%d\n", __func__, pd->cap.nr,
pd->cap.selected_cap_idx);
}
bool mtk_pdc_init(struct charger_manager *info)
{
info->pdc.pdc_max_watt_setting = -1;
info->pdc.check_impedance = true;
info->pdc.pd_cap_max_watt = -1;
info->pdc.pd_idx = -1;
info->pdc.pd_reset_idx = -1;
info->pdc.pd_boost_idx = 0;
info->pdc.pd_buck_idx = 0;
info->pdc.vbus_l = info->data.pd_vbus_low_bound / 1000;
info->pdc.vbus_h = info->data.pd_vbus_upper_bound / 1000;
return true;
}