kernel-brax3-ubuntu-touch/drivers/power/supply/mtk_pe5p_hal.c
erascape f319b992b1 kernel-5.15: Initial import brax3 UT kernel
* halium configs enabled

Signed-off-by: erascape <erascape@proton.me>
2025-09-23 15:17:10 +00:00

675 lines
16 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020 MediaTek Inc.
*/
#include "charger_class.h"
#include "mtk_charger.h"
#include "mtk_pe5p.h"
enum mtk_chg_type {
MTK_CHGTYP_SWCHG = 0,
MTK_CHGTYP_DVCHG,
MTK_CHGTYP_DVCHG_SLAVE,
MTK_CHGTYP_BUCKBOOSTCHG,
MTK_CHGTYP_HV_DVCHG,
MTK_CHGTYP_HV_DVCHG_SLAVE,
MTK_CHGTYP_MAX,
};
struct mtk_chgdev_desc {
enum mtk_chg_type type;
const char *name;
bool must_exist;
};
static struct mtk_chgdev_desc mtk_chgdev_desc_tbl[MTK_CHGTYP_MAX] = {
{
.type = MTK_CHGTYP_SWCHG,
.name = "primary_chg",
.must_exist = true,
},
{
.type = MTK_CHGTYP_DVCHG,
.name = "primary_dvchg",
.must_exist = false,
},
{
.type = MTK_CHGTYP_DVCHG_SLAVE,
.name = "secondary_dvchg",
.must_exist = false,
},
{
.type = MTK_CHGTYP_BUCKBOOSTCHG,
.name = "buck_boost_chg",
.must_exist = false,
},
{
.type = MTK_CHGTYP_HV_DVCHG,
.name = "hvdiv2_chg1",
.must_exist = true,
},
{
.type = MTK_CHGTYP_HV_DVCHG_SLAVE,
.name = "hvdiv2_chg2",
.must_exist = false,
},
};
struct pe5p_hal {
struct device *dev;
struct charger_device *chgdevs[MTK_CHGTYP_MAX];
struct adapter_device **adapters;
struct adapter_device *adapter;
const char **support_ta;
int support_ta_cnt;
};
static inline int to_chgtyp(enum chg_idx idx)
{
switch (idx) {
case CHG1:
return MTK_CHGTYP_SWCHG;
case DVCHG1:
return MTK_CHGTYP_DVCHG;
case DVCHG2:
return MTK_CHGTYP_DVCHG_SLAVE;
case HVDVCHG1:
return MTK_CHGTYP_HV_DVCHG;
case HVDVCHG2:
return MTK_CHGTYP_HV_DVCHG_SLAVE;
case BUCKBSTCHG:
return MTK_CHGTYP_BUCKBOOSTCHG;
default:
return -EOPNOTSUPP;
}
}
static inline int to_chgclass_adc(enum pe5p_adc_channel chan)
{
switch (chan) {
case PE5P_ADCCHAN_VBUS:
return ADC_CHANNEL_VBUS;
case PE5P_ADCCHAN_IBUS:
return ADC_CHANNEL_IBUS;
case PE5P_ADCCHAN_VBAT:
return ADC_CHANNEL_VBAT;
case PE5P_ADCCHAN_IBAT:
return ADC_CHANNEL_IBAT;
case PE5P_ADCCHAN_TBAT:
return ADC_CHANNEL_TBAT;
case PE5P_ADCCHAN_TCHG:
return ADC_CHANNEL_TEMP_JC;
case PE5P_ADCCHAN_VOUT:
return ADC_CHANNEL_VOUT;
case PE5P_ADCCHAN_VSYS:
return ADC_CHANNEL_VSYS;
default:
break;
}
return -EOPNOTSUPP;
}
int pe5p_hal_get_ta_output(struct chg_alg_device *alg, int *mV, int *mA)
{
int ret;
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
ret = adapter_dev_get_output(hal->adapter, mV, mA);
if (ret < 0)
return ret;
return (ret == MTK_ADAPTER_OK) ? 0 : -ret;
}
int pe5p_hal_get_ta_status(struct chg_alg_device *alg,
struct pe5p_ta_status *status)
{
int ret;
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
struct adapter_status _status;
_status.temperature = 0;
_status.ocp = 0;
_status.ovp = 0;
_status.otp = 0;
ret = adapter_dev_get_status(hal->adapter, &_status);
if (ret < 0)
return ret;
if (ret != MTK_ADAPTER_OK)
return -ret;
/* 0 means NOT SUPPORT */
status->temperature = (_status.temperature == 0) ? 25 :
_status.temperature;
status->ocp = _status.ocp;
status->ovp = _status.ovp;
status->otp = _status.otp;
return 0;
}
int pe5p_hal_set_ta_cap(struct chg_alg_device *alg, int mV, int mA)
{
int ret;
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
ret = adapter_dev_set_cap(hal->adapter, MTK_PD_APDO, mV, mA);
return (ret <= MTK_ADAPTER_OK) ? ret : -ret;
}
int pe5p_hal_is_ta_cc(struct chg_alg_device *alg, bool *is_cc)
{
int ret;
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
ret = adapter_dev_is_cc(hal->adapter, is_cc);
return (ret <= MTK_ADAPTER_OK) ? ret : -ret;
}
int pe5p_hal_set_ta_wdt(struct chg_alg_device *alg, u32 ms)
{
int ret;
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
ret = adapter_dev_set_wdt(hal->adapter, ms);
return (ret <= MTK_ADAPTER_OK) ? ret : -ret;
}
int pe5p_hal_enable_ta_wdt(struct chg_alg_device *alg, bool en)
{
int ret;
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
ret = adapter_dev_enable_wdt(hal->adapter, en);
return (ret <= MTK_ADAPTER_OK) ? ret : -ret;
}
int pe5p_hal_enable_ta_charging(struct chg_alg_device *alg, bool en,
int mV, int mA)
{
int ret;
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
enum adapter_cap_type type = en ? MTK_PD_APDO_START : MTK_PD_APDO_END;
ret = adapter_dev_set_cap(hal->adapter, type, mV, mA);
return (ret <= MTK_ADAPTER_OK) ? ret : -ret;
}
int pe5p_hal_sync_ta_volt(struct chg_alg_device *alg, u32 mV)
{
int ret;
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
ret = adapter_dev_sync_volt(hal->adapter, mV);
return (ret <= MTK_ADAPTER_OK) ? ret : -ret;
}
int pe5p_hal_authenticate_ta(struct chg_alg_device *alg,
struct pe5p_ta_auth_data *data)
{
int ret, i;
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
struct adapter_auth_data _data = {
.vcap_min = data->vcap_min,
.vcap_max = data->vcap_max,
.icap_min = data->icap_min,
};
for (i = 0; i < hal->support_ta_cnt; i++) {
if (!hal->adapters[i])
continue;
ret = adapter_dev_authentication(hal->adapters[i], &_data);
if (ret < 0 || ret != MTK_ADAPTER_OK) {
PE5P_DBG("authenticate fail(%d)\n", ret);
continue;
}
hal->adapter = hal->adapters[i];
data->vta_min = _data.vta_min;
data->vta_max = _data.vta_max;
data->ita_min = _data.ita_min;
data->ita_max = _data.ita_max;
data->pwr_lmt = _data.pwr_lmt;
data->pdp = _data.pdp;
data->support_meas_cap = _data.support_meas_cap;
data->support_status = _data.support_status;
data->support_cc = _data.support_cc;
data->vta_step = _data.vta_step;
data->ita_step = _data.ita_step;
data->ita_gap_per_vstep = _data.ita_gap_per_vstep;
PE5P_INFO("lmt(%d,%dW),step(%d,%d),cc=%d,cap=%d,status=%d\n",
data->pwr_lmt, data->pdp, data->vta_step,
data->ita_step, data->support_cc,
data->support_meas_cap,
data->support_status);
return 0;
}
return -EINVAL;
}
int pe5p_hal_send_ta_hardreset(struct chg_alg_device *alg)
{
int ret;
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
ret = adapter_dev_send_hardreset(hal->adapter);
return (ret <= MTK_ADAPTER_OK) ? ret : -ret;
}
int pe5p_hal_init_hardware(struct chg_alg_device *alg, const char **support_ta,
int support_ta_cnt)
{
struct pe5p_algo_info *info = chg_alg_dev_get_drvdata(alg);
struct pe5p_algo_data *data = info->data;
struct pe5p_hal *hal;
int i, ret;
bool has_ta = false;
if (support_ta_cnt <= 0)
return -EINVAL;
hal = devm_kzalloc(info->dev, sizeof(*hal), GFP_KERNEL);
if (!hal)
return -ENOMEM;
hal->adapters = devm_kzalloc(info->dev,
sizeof(struct adapter_device *) *
support_ta_cnt, GFP_KERNEL);
if (!hal->adapters)
return -ENOMEM;
hal->support_ta = support_ta;
hal->support_ta_cnt = support_ta_cnt;
/* get TA */
for (i = 0; i < hal->support_ta_cnt; i++) {
hal->adapters[i] = get_adapter_by_name(support_ta[i]);
if (hal->adapters[i]) {
has_ta = true;
continue;
}
PE5P_ERR("no %s\n", hal->support_ta[i]);
}
if (!has_ta) {
ret = -ENODEV;
goto err;
}
/* get charger device */
for (i = 0; i < MTK_CHGTYP_MAX; i++) {
hal->chgdevs[i] =
get_charger_by_name(mtk_chgdev_desc_tbl[i].name);
if (!hal->chgdevs[i]) {
PE5P_ERR("get %s fail\n", mtk_chgdev_desc_tbl[i].name);
if (mtk_chgdev_desc_tbl[i].must_exist) {
ret = -ENODEV;
goto err;
}
}
}
data->is_dvchg_exist[PE5P_BUCK_BSTCHG] = true;
data->is_dvchg_exist[PE5P_HVDVCHG_MASTER] = true;
if (hal->chgdevs[MTK_CHGTYP_HV_DVCHG_SLAVE])
data->is_dvchg_exist[PE5P_HVDVCHG_SLAVE] = true;
chg_alg_dev_set_drv_hal_data(alg, hal);
hal->dev = info->dev;
PE5P_INFO("successfully\n");
return 0;
err:
return ret;
}
int pe5p_hal_enable_sw_vbusovp(struct chg_alg_device *alg, bool en)
{
mtk_chg_enable_vbus_ovp(en);
return 0;
}
int pe5p_set_operating_mode(struct chg_alg_device *alg, enum chg_idx chgidx,
bool en)
{
int chgtyp = to_chgtyp(chgidx);
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
if (chgtyp < 0)
return chgtyp;
return charger_dev_set_operation_mode(hal->chgdevs[chgtyp], en);
}
int pe5p_hal_enable_charging(struct chg_alg_device *alg,
enum chg_idx chgidx, bool en)
{
int chgtyp = to_chgtyp(chgidx);
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
if (chgtyp < 0)
return chgtyp;
return charger_dev_enable(hal->chgdevs[chgtyp], en);
}
int pe5p_hal_dump_registers(struct chg_alg_device *alg, enum chg_idx chgidx)
{
int chgtyp = to_chgtyp(chgidx);
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
if (chgtyp < 0)
return chgtyp;
return charger_dev_dump_registers(hal->chgdevs[chgtyp]);
}
int pe5p_hal_enable_hz(struct chg_alg_device *alg,
enum chg_idx chgidx, bool en)
{
int chgtyp = to_chgtyp(chgidx);
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
u32 mV = en ? 24000 : 4500;
if (chgtyp < 0)
return chgtyp;
return charger_dev_set_mivr(hal->chgdevs[chgtyp], milli_to_micro(mV));
}
int pe5p_hal_set_vacovp(struct chg_alg_device *alg,
enum chg_idx chgidx, u32 mV)
{
int chgtyp = to_chgtyp(chgidx);
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
if (chgtyp < 0)
return chgtyp;
return charger_dev_set_vac_ovp(hal->chgdevs[chgtyp],
milli_to_micro(mV));
}
int pe5p_hal_set_vbusovp(struct chg_alg_device *alg,
enum chg_idx chgidx, u32 mV)
{
int chgtyp = to_chgtyp(chgidx);
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
if (chgtyp < 0)
return chgtyp;
return charger_dev_set_vbusovp(hal->chgdevs[chgtyp],
milli_to_micro(mV));
}
int pe5p_hal_set_ibusocp(struct chg_alg_device *alg,
enum chg_idx chgidx, u32 mA)
{
int chgtyp = to_chgtyp(chgidx);
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
if (chgtyp < 0)
return chgtyp;
return charger_dev_set_ibusocp(hal->chgdevs[chgtyp],
milli_to_micro(mA));
}
int pe5p_hal_set_vbatovp(struct chg_alg_device *alg,
enum chg_idx chgidx, u32 mV)
{
int chgtyp = to_chgtyp(chgidx);
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
if (chgtyp < 0)
return chgtyp;
return charger_dev_set_vbatovp(hal->chgdevs[chgtyp],
milli_to_micro(mV));
}
int pe5p_hal_set_ibatocp(struct chg_alg_device *alg,
enum chg_idx chgidx, u32 mA)
{
int chgtyp = to_chgtyp(chgidx);
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
if (chgtyp < 0)
return chgtyp;
return charger_dev_set_ibatocp(hal->chgdevs[chgtyp],
milli_to_micro(mA));
}
int pe5p_hal_set_vbatovp_alarm(struct chg_alg_device *alg,
enum chg_idx chgidx, u32 mV)
{
int chgtyp = to_chgtyp(chgidx);
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
if (chgtyp < 0)
return chgtyp;
return charger_dev_set_vbatovp_alarm(hal->chgdevs[chgtyp],
milli_to_micro(mV));
}
int pe5p_hal_reset_vbatovp_alarm(struct chg_alg_device *alg,
enum chg_idx chgidx)
{
int chgtyp = to_chgtyp(chgidx);
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
if (chgtyp < 0)
return chgtyp;
return charger_dev_reset_vbatovp_alarm(hal->chgdevs[chgtyp]);
}
int pe5p_hal_set_vbusovp_alarm(struct chg_alg_device *alg,
enum chg_idx chgidx, u32 mV)
{
int chgtyp = to_chgtyp(chgidx);
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
if (chgtyp < 0)
return chgtyp;
return charger_dev_set_vbusovp_alarm(hal->chgdevs[chgtyp],
milli_to_micro(mV));
}
int pe5p_hal_reset_vbusovp_alarm(struct chg_alg_device *alg,
enum chg_idx chgidx)
{
int chgtyp = to_chgtyp(chgidx);
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
if (chgtyp < 0)
return chgtyp;
return charger_dev_reset_vbusovp_alarm(hal->chgdevs[chgtyp]);
}
static int pe5p_get_tbat(struct pe5p_hal *hal)
{
int ret = 0;
int tmp_ret;
union power_supply_propval prop;
struct power_supply *bat_psy;
bat_psy = devm_power_supply_get_by_phandle(hal->dev, "gauge");
if (IS_ERR_OR_NULL(bat_psy)) {
PE5P_ERR("%s Couldn't get bat_psy\n", __func__);
ret = 27;
} else {
tmp_ret = power_supply_get_property(bat_psy,
POWER_SUPPLY_PROP_TEMP, &prop);
ret = prop.intval / 10;
}
ret = 25;
PE5P_DBG("%d\n", ret);
return ret;
}
int pe5p_hal_get_adc(struct chg_alg_device *alg, enum chg_idx chgidx,
enum pe5p_adc_channel chan, int *val)
{
int ret;
int chgtyp = to_chgtyp(chgidx);
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
int _chan = to_chgclass_adc(chan);
if (chgtyp < 0)
return chgtyp;
if (_chan < 0)
return _chan;
if (_chan == ADC_CHANNEL_TBAT) {
*val = pe5p_get_tbat(hal);
return 0;
}
ret = charger_dev_get_adc(hal->chgdevs[chgtyp], _chan, val, val);
if (ret < 0)
return ret;
if (_chan == ADC_CHANNEL_VBAT || _chan == ADC_CHANNEL_IBAT ||
_chan == ADC_CHANNEL_VBUS || _chan == ADC_CHANNEL_IBUS ||
_chan == ADC_CHANNEL_VOUT || _chan == ADC_CHANNEL_VSYS)
*val = micro_to_milli(*val);
return 0;
}
int pe5p_hal_get_soc(struct chg_alg_device *alg, u32 *soc)
{
int ret;
int ret_tmp;
struct power_supply *bat_psy;
union power_supply_propval prop;
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
bat_psy = devm_power_supply_get_by_phandle(hal->dev, "gauge");
if (IS_ERR_OR_NULL(bat_psy)) {
PE5P_ERR("%s Couldn't get bat_psy\n", __func__);
ret = 50;
} else {
ret_tmp = power_supply_get_property(bat_psy,
POWER_SUPPLY_PROP_CAPACITY, &prop);
ret = prop.intval;
}
if (ret < 0)
return ret;
*soc = ret;
PE5P_DBG("%d\n", *soc);
return 0;
}
int pe5p_hal_is_pd_adapter_ready(struct chg_alg_device *alg)
{
struct pe5p_hal *hal;
int type = 0;
int i;
if (alg == NULL) {
pr_notice("%s: alg is null\n", __func__);
return -EINVAL;
}
hal = chg_alg_dev_get_drv_hal_data(alg);
for (i = 0; i < hal->support_ta_cnt; i++) {
if (!hal->adapters[i])
continue;
type = adapter_dev_get_property(hal->adapters[i], PD_TYPE);
if (type < 0)
continue;
}
pr_notice("%s type:%d\n", __func__, type);
if (type == MTK_PD_CONNECT_PE_READY_SNK_APDO)
return ALG_READY;
else if (type == MTK_PD_CONNECT_TYPEC_ONLY_SNK ||
type == MTK_PD_CONNECT_PE_READY_SNK ||
type == MTK_PD_CONNECT_PE_READY_SNK_PD30)
return ALG_TA_NOT_SUPPORT;
return ALG_TA_CHECKING;
}
int pe5p_hal_set_ichg(struct chg_alg_device *alg, enum chg_idx chgidx, u32 mA)
{
int chgtyp = to_chgtyp(chgidx);
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
if (chgtyp < 0)
return chgtyp;
return charger_dev_set_charging_current(hal->chgdevs[chgtyp],
milli_to_micro(mA));
}
int pe5p_hal_set_aicr(struct chg_alg_device *alg, enum chg_idx chgidx, u32 mA)
{
int chgtyp = to_chgtyp(chgidx);
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
if (chgtyp < 0)
return chgtyp;
return charger_dev_set_input_current(hal->chgdevs[chgtyp],
milli_to_micro(mA));
}
int pe5p_hal_get_ichg(struct chg_alg_device *alg, enum chg_idx chgidx, u32 *mA)
{
int chgtyp = to_chgtyp(chgidx);
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
int ret, uA = 0;
if (chgtyp < 0)
return chgtyp;
ret = charger_dev_get_charging_current(hal->chgdevs[chgtyp], &uA);
*mA = micro_to_milli(uA);
return ret;
}
int pe5p_hal_get_aicr(struct chg_alg_device *alg, enum chg_idx chgidx, u32 *mA)
{
int chgtyp = to_chgtyp(chgidx);
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
int ret, uA = 0;
if (chgtyp < 0)
return chgtyp;
ret = charger_dev_get_input_current(hal->chgdevs[chgtyp], &uA);
*mA = micro_to_milli(uA);
return ret;
}
int pe5p_hal_is_vbuslowerr(struct chg_alg_device *alg,
enum chg_idx chgidx, bool *err)
{
int chgtyp = to_chgtyp(chgidx);
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
if (chgtyp < 0)
return chgtyp;
return charger_dev_is_vbuslowerr(hal->chgdevs[chgtyp], err);
}
int pe5p_hal_get_adc_accuracy(struct chg_alg_device *alg,
enum chg_idx chgidx,
enum pe5p_adc_channel chan, int *val)
{
int ret;
int chgtyp = to_chgtyp(chgidx);
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
int _chan = to_chgclass_adc(chan);
if (chgtyp < 0)
return chgtyp;
if (_chan < 0)
return _chan;
ret = charger_dev_get_adc_accuracy(hal->chgdevs[chgtyp], _chan, val,
val);
if (ret < 0)
return ret;
if (_chan == ADC_CHANNEL_VBAT || _chan == ADC_CHANNEL_IBAT ||
_chan == ADC_CHANNEL_VBUS || _chan == ADC_CHANNEL_IBUS ||
_chan == ADC_CHANNEL_VOUT)
*val = micro_to_milli(*val);
return 0;
}
int pe5p_hal_init_chip(struct chg_alg_device *alg, enum chg_idx chgidx)
{
int chgtyp = to_chgtyp(chgidx);
struct pe5p_hal *hal = chg_alg_dev_get_drv_hal_data(alg);
if (chgtyp < 0)
return chgtyp;
return charger_dev_init_chip(hal->chgdevs[chgtyp]);
}