kernel_samsung_a34x-permissive/drivers/power/supply/mediatek/mtk_pep_intf.c
2024-04-28 15:51:13 +02:00

620 lines
14 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include "mtk_pep_intf.h"
#include "mtk_pep20_intf.h"
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/wakelock.h>
#include <mt-plat/battery_common.h>
#include <mt-plat/battery_meter.h>
#if !defined(TA_AC_CHARGING_CURRENT)
#include <mach/mt_pe.h>
#endif
#ifdef CONFIG_MTK_PUMP_EXPRESS_PLUS_SUPPORT
static struct mutex pep_access_lock;
static struct mutex pep_pmic_sync_lock;
static struct wake_lock pep_suspend_lock;
static int pep_ta_vchr_org = 5000; /* mA */
static bool pep_to_check_chr_type = true;
static bool pep_to_tune_ta_vchr = true;
static bool pep_is_cable_out_occur; /* Plug out happened while detect PE+ */
static bool pep_is_connect;
static bool pep_is_enabled = true;
static int pep_enable_hw_vbus_ovp(bool enable)
{
int ret = 0;
u32 data;
data = (enable ? 1 : 0); /* Compatible with charging_hw_bq25896.c */
ret = battery_charging_control(CHARGING_CMD_SET_VBUS_OVP_EN, &data);
if (ret < 0)
battery_log(BAT_LOG_CRTI, "%s: failed, ret = %d\n", __func__,
ret);
return ret;
}
/* Enable/Disable HW & SW VBUS OVP */
static int pep_enable_vbus_ovp(bool enable)
{
int ret = 0;
u32 sw_ovp = (enable ? V_CHARGER_MAX : 15000);
/* Enable/Disable HW(PMIC) OVP */
ret = pep_enable_hw_vbus_ovp(enable);
if (ret < 0) {
battery_log(BAT_LOG_CRTI, "%s: failed, ret = %d\n", __func__,
ret);
return ret;
}
/* Enable/Disable SW OVP status */
batt_cust_data.v_charger_max = sw_ovp;
return ret;
}
static int pep_enable_charging(bool enable)
{
int ret = 0;
u32 data;
data = (enable ? 1 : 0); /* Compatible with charging_hw_bq25896.c */
ret = battery_charging_control(CHARGING_CMD_ENABLE, &enable);
if (ret < 0)
battery_log(BAT_LOG_CRTI, "%s: failed, ret = %d\n", __func__,
ret);
return ret;
}
static int pep_set_mivr(u32 mivr)
{
int ret = 0;
ret = battery_charging_control(CHARGING_CMD_SET_VINDPM, &mivr);
if (ret < 0)
battery_log(BAT_LOG_CRTI, "%s: failed, ret = %d\n", __func__,
ret);
return ret;
}
static int pep_leave(bool disable_charging)
{
int ret = 0;
battery_log(BAT_LOG_FULL, "%s: starts\n", __func__);
/* CV point reached, disable charger */
ret = pep_enable_charging(disable_charging);
if (ret < 0)
goto _err;
/* Decrease TA voltage to 5V */
ret = mtk_pep_reset_ta_vchr();
if (ret < 0 || pep_is_connect)
goto _err;
battery_log(BAT_LOG_CRTI, "%s: OK\n", __func__);
return ret;
_err:
battery_log(BAT_LOG_CRTI, "%s: failed, is_connect = %d, ret = %d\n",
__func__, pep_is_connect, ret);
return ret;
}
static int pep_check_leave_status(void)
{
int ret = 0;
u32 ichg = 0, vchr = 0;
kal_bool current_sign;
battery_log(BAT_LOG_FULL, "%s: starts\n", __func__);
/* PE+ leaves unexpectedly */
vchr = battery_meter_get_charger_voltage();
if (abs(vchr - pep_ta_vchr_org) < 1000) {
battery_log(BAT_LOG_CRTI,
"%s: PE+ leave unexpectedly, recheck TA\n",
__func__);
pep_to_check_chr_type = true;
ret = pep_leave(true);
if (ret < 0 || pep_is_connect)
goto _err;
return ret;
}
ichg = battery_meter_get_battery_current(); /* 0.1 mA */
ichg /= 10; /* mA */
current_sign = battery_meter_get_battery_current_sign();
/* Check SOC & Ichg */
if (BMT_status.SOC > batt_cust_data.ta_stop_battery_soc &&
current_sign && ichg < 1000) {
ret = pep_leave(true);
if (ret < 0 || pep_is_connect)
goto _err;
battery_log(BAT_LOG_CRTI,
"%s: OK, SOC = (%d,%d), Ichg = %dmA, stop PE+\n",
__func__, BMT_status.SOC,
batt_cust_data.ta_stop_battery_soc, ichg);
}
return ret;
_err:
battery_log(BAT_LOG_CRTI, "%s: failed, is_connect = %d, ret = %d\n",
__func__, pep_is_connect, ret);
return ret;
}
static int __pep_increase_ta_vchr(void)
{
int ret = 0;
bool increase = true; /* Increase */
kal_bool data; /* Use kal_bool, compatible with charging_hw_bq25896.c */
data = (increase ? KAL_TRUE : KAL_FALSE);
if (BMT_status.charger_exist) {
ret = battery_charging_control(
CHARGING_CMD_SET_TA_CURRENT_PATTERN, &data);
if (ret < 0)
battery_log(BAT_LOG_CRTI, "%s: failed, ret = %d\n",
__func__, ret);
else
battery_log(BAT_LOG_CRTI, "%s: OK\n", __func__);
return ret;
}
/* TA is not exist */
ret = -EIO;
battery_log(BAT_LOG_CRTI, "%s: failed, cable out\n", __func__);
return ret;
}
static int pep_increase_ta_vchr(u32 vchr_target)
{
int ret = 0;
int vchr_before, vchr_after;
u32 retry_cnt = 0;
do {
vchr_before = battery_meter_get_charger_voltage();
__pep_increase_ta_vchr();
vchr_after = battery_meter_get_charger_voltage();
if (abs(vchr_after - vchr_target) <= 1000) {
battery_log(BAT_LOG_CRTI, "%s: OK\n", __func__);
return ret;
}
battery_log(
BAT_LOG_CRTI,
"%s: retry, cnt = %d, vchr = (%d, %d), vchr_target = %d\n",
__func__, retry_cnt, vchr_before, vchr_after,
vchr_target);
retry_cnt++;
} while (BMT_status.charger_exist && retry_cnt < 3 &&
mtk_chr_is_hv_charging_enable());
ret = -EIO;
battery_log(BAT_LOG_CRTI,
"%s: failed, vchr = (%d, %d), vchr_target = %d\n", __func__,
vchr_before, vchr_after, vchr_target);
return ret;
}
static int pep_detect_ta(void)
{
int ret = 0;
battery_log(BAT_LOG_FULL, "%s: starts\n", __func__);
/* Disable OVP */
ret = pep_enable_vbus_ovp(false);
if (ret < 0)
goto _err;
pep_ta_vchr_org = battery_meter_get_charger_voltage();
ret = pep_increase_ta_vchr(7000); /* mA */
if (ret == 0) {
pep_is_connect = true;
battery_log(BAT_LOG_CRTI, "%s: OK, is_connect = %d\n", __func__,
pep_is_connect);
return ret;
}
/* Detect PE+ TA failed */
pep_is_connect = false;
pep_to_check_chr_type = false;
/* Enable OVP */
pep_enable_vbus_ovp(true);
/* Set MIVR to 4.5V for vbus 5V */
pep_set_mivr(4500);
_err:
battery_log(BAT_LOG_CRTI, "%s: failed, is_connect = %d\n", __func__,
pep_is_connect);
return ret;
}
static int pep_init_ta(void)
{
int ret = 0;
battery_log(BAT_LOG_FULL, "%s: starts\n", __func__);
if (batt_cust_data.ta_9v_support || batt_cust_data.ta_12v_support)
pep_to_tune_ta_vchr = true;
ret = battery_charging_control(CHARGING_CMD_INIT, NULL);
if (ret < 0)
battery_log(BAT_LOG_CRTI, "%s failed, ret = %d\n", __func__,
ret);
else
battery_log(BAT_LOG_CRTI, "%s OK\n", __func__);
return ret;
}
static int pep_plugout_reset(void)
{
int ret = 0;
battery_log(BAT_LOG_FULL, "%s: starts\n", __func__);
pep_to_check_chr_type = true;
ret = mtk_pep_reset_ta_vchr();
if (ret < 0)
goto _err;
/* Set cable out occur to false */
mtk_pep_set_is_cable_out_occur(false);
battery_log(BAT_LOG_CRTI, "%s: OK\n", __func__);
return ret;
_err:
battery_log(BAT_LOG_CRTI, "%s: failed, ret = %d\n", __func__, ret);
return ret;
}
int mtk_pep_init(void)
{
wake_lock_init(&pep_suspend_lock, WAKE_LOCK_SUSPEND,
"PE+ TA charger suspend wakelock");
mutex_init(&pep_access_lock);
mutex_init(&pep_pmic_sync_lock);
return 0;
}
int mtk_pep_reset_ta_vchr(void)
{
int ret = 0, chr_volt = 0;
u32 retry_cnt = 0;
CHR_CURRENT_ENUM aicr;
battery_log(BAT_LOG_FULL, "%s: starts\n", __func__);
/* Set aicr to 70 mA */
aicr = CHARGE_CURRENT_70_00_MA;
do {
ret = battery_charging_control(CHARGING_CMD_SET_INPUT_CURRENT,
&aicr);
msleep(500);
/* Check charger's voltage */
chr_volt = battery_meter_get_charger_voltage();
if (abs(chr_volt - pep_ta_vchr_org) <= 1000) {
pep_is_connect = false;
break;
}
retry_cnt++;
} while (retry_cnt < 3);
if (pep_is_connect) {
battery_log(BAT_LOG_CRTI, "%s: failed, ret = %d\n", __func__,
ret);
/*
* SET_INPUT_CURRENT success but chr_volt does not reset to 5V
* set ret = -EIO to represent the case
*/
ret = -EIO;
return ret;
}
/* Enable OVP */
ret = pep_enable_vbus_ovp(true);
pep_set_mivr(4500);
battery_log(BAT_LOG_CRTI, "%s: OK\n", __func__);
return ret;
}
int mtk_pep_check_charger(void)
{
int ret = 0;
if (!mtk_chr_is_hv_charging_enable()) {
pr_info("%s: hv charging is disabled\n", __func__);
if (pep_is_connect) {
pep_leave(true);
pep_to_check_chr_type = true;
}
return ret;
}
if (mtk_pep20_get_is_connect()) {
battery_log(BAT_LOG_CRTI, "%s: stop, PE+20 is connected\n",
__func__);
return ret;
}
if (!pep_is_enabled) {
battery_log(BAT_LOG_CRTI, "%s: stop, PE+ is disabled\n",
__func__);
return ret;
}
/* Lock */
mutex_lock(&pep_access_lock);
wake_lock(&pep_suspend_lock);
battery_log(BAT_LOG_FULL, "%s: starts\n", __func__);
if (!BMT_status.charger_exist || pep_is_cable_out_occur)
pep_plugout_reset();
/* Not to check charger type or
* Not standard charger or
* SOC is not in range
*/
if (!pep_to_check_chr_type ||
BMT_status.charger_type != STANDARD_CHARGER ||
BMT_status.SOC < batt_cust_data.ta_start_battery_soc ||
BMT_status.SOC >= batt_cust_data.ta_stop_battery_soc)
goto _err;
/* Reset/Init/Detect TA */
ret = mtk_pep_reset_ta_vchr();
if (ret < 0)
goto _err;
ret = pep_init_ta();
if (ret < 0)
goto _err;
ret = pep_detect_ta();
if (ret < 0)
goto _err;
pep_to_check_chr_type = false;
/* Unlock */
wake_unlock(&pep_suspend_lock);
mutex_unlock(&pep_access_lock);
battery_log(BAT_LOG_CRTI, "%s: OK, to_check_chr_type = %d\n", __func__,
pep_to_check_chr_type);
return ret;
_err:
/* Unlock */
wake_unlock(&pep_suspend_lock);
mutex_unlock(&pep_access_lock);
battery_log(
BAT_LOG_CRTI,
"%s: stop, SOC = %d, to_check_chr_type = %d, chr_type = %d, ret = %d\n",
__func__, BMT_status.SOC, pep_to_check_chr_type,
BMT_status.charger_type, ret);
return ret;
}
int mtk_pep_start_algorithm(void)
{
int ret = 0, chr_volt;
if (!mtk_chr_is_hv_charging_enable()) {
pr_info("%s: hv charging is disabled\n", __func__);
if (pep_is_connect) {
pep_leave(true);
pep_to_check_chr_type = true;
}
return ret;
}
if (mtk_pep20_get_is_connect()) {
battery_log(BAT_LOG_CRTI, "%s: stop, PE+20 is connected\n",
__func__);
return ret;
}
if (!pep_is_enabled) {
battery_log(BAT_LOG_CRTI, "%s: stop, PE+ is disabled\n",
__func__);
return ret;
}
/* Lock */
mutex_lock(&pep_access_lock);
wake_lock(&pep_suspend_lock);
battery_log(BAT_LOG_FULL, "%s: starts\n", __func__);
if (!BMT_status.charger_exist || pep_is_cable_out_occur)
pep_plugout_reset();
/* TA is not connected */
if (!pep_is_connect) {
ret = -EIO;
battery_log(BAT_LOG_CRTI, "%s: stop, PE+ is not connected\n",
__func__);
goto _out;
}
/* No need to tune TA */
if (!pep_to_tune_ta_vchr) {
ret = pep_check_leave_status();
battery_log(BAT_LOG_CRTI, "%s: stop, not to tune TA vchr\n",
__func__);
goto _out;
}
pep_to_tune_ta_vchr = false;
/* Increase TA voltage to 9V */
if (batt_cust_data.ta_9v_support || batt_cust_data.ta_12v_support) {
ret = pep_increase_ta_vchr(9000); /* mA */
if (ret < 0) {
battery_log(BAT_LOG_CRTI,
"%s: failed, cannot increase to 9V\n",
__func__);
goto _err;
}
/* Successfully, increase to 9V */
battery_log(BAT_LOG_CRTI, "%s: output 9V ok\n", __func__);
}
/* Increase TA voltage to 12V */
if (batt_cust_data.ta_12v_support) {
ret = pep_increase_ta_vchr(12000); /* mA */
if (ret < 0) {
battery_log(BAT_LOG_CRTI,
"%s: failed, cannot increase to 12V\n",
__func__);
goto _err;
}
/* Successfully, increase to 12V */
battery_log(BAT_LOG_CRTI, "%s: output 12V ok\n", __func__);
}
chr_volt = battery_meter_get_charger_voltage();
ret = pep_set_mivr(chr_volt - 1000);
if (ret < 0)
goto _err;
battery_log(BAT_LOG_CRTI,
"%s: vchr_org = %d, vchr_after = %d, delta = %d\n",
__func__, pep_ta_vchr_org, chr_volt,
chr_volt - pep_ta_vchr_org);
battery_log(BAT_LOG_CRTI, "%s: OK\n", __func__);
wake_unlock(&pep_suspend_lock);
mutex_unlock(&pep_access_lock);
return ret;
_err:
pep_leave(false);
_out:
chr_volt = battery_meter_get_charger_voltage();
battery_log(BAT_LOG_CRTI,
"%s: vchr_org = %d, vchr_after = %d, delta = %d\n",
__func__, pep_ta_vchr_org, chr_volt,
chr_volt - pep_ta_vchr_org);
wake_unlock(&pep_suspend_lock);
mutex_unlock(&pep_access_lock);
return ret;
}
int mtk_pep_set_charging_current(CHR_CURRENT_ENUM *ichg, CHR_CURRENT_ENUM *aicr)
{
int ret = 0, chr_volt = 0;
if (!pep_is_connect)
return -ENOTSUPP;
chr_volt = battery_meter_get_charger_voltage();
if ((chr_volt - pep_ta_vchr_org) > 6000) { /* TA = 12V */
*aicr = batt_cust_data.ta_ac_12v_input_current;
*ichg = batt_cust_data.ta_ac_charging_current;
} else if ((chr_volt - pep_ta_vchr_org) > 3000) { /* TA = 9V */
*aicr = batt_cust_data.ta_ac_9v_input_current;
*ichg = batt_cust_data.ta_ac_charging_current;
} else if ((chr_volt - pep_ta_vchr_org) > 1000) { /* TA = 7V */
*aicr = batt_cust_data.ta_ac_7v_input_current;
*ichg = batt_cust_data.ta_ac_charging_current;
}
battery_log(
BAT_LOG_CRTI,
"%s: Ichg= %dmA, AICR = %dmA, chr_org = %d, chr_after = %d\n",
__func__, *ichg / 100, *aicr / 100, pep_ta_vchr_org, chr_volt);
return ret;
}
/* PE+ set functions */
void mtk_pep_set_to_check_chr_type(bool check)
{
mutex_lock(&pep_access_lock);
wake_lock(&pep_suspend_lock);
battery_log(BAT_LOG_CRTI, "%s: check = %d\n", __func__, check);
pep_to_check_chr_type = check;
wake_unlock(&pep_suspend_lock);
mutex_unlock(&pep_access_lock);
}
void mtk_pep_set_is_enable(bool enable)
{
mutex_lock(&pep_access_lock);
wake_lock(&pep_suspend_lock);
battery_log(BAT_LOG_CRTI, "%s: enable = %d\n", __func__, enable);
pep_is_enabled = enable;
wake_unlock(&pep_suspend_lock);
mutex_unlock(&pep_access_lock);
}
void mtk_pep_set_is_cable_out_occur(bool out)
{
battery_log(BAT_LOG_CRTI, "%s: out = %d\n", __func__, out);
mutex_lock(&pep_pmic_sync_lock);
pep_is_cable_out_occur = out;
mutex_unlock(&pep_pmic_sync_lock);
}
/* PE+ get functions */
bool mtk_pep_get_to_check_chr_type(void)
{
return pep_to_check_chr_type;
}
bool mtk_pep_get_is_connect(void)
{
/*
* Cable out is occurred,
* but not execute plugout_reset yet
*/
if (pep_is_cable_out_occur)
return false;
return pep_is_connect;
}
bool mtk_pep_get_is_enable(void)
{
return pep_is_enabled;
}
#endif