unplugged-kernel/drivers/misc/mediatek/pmic/mt6370/mt6370_pmu_charger_gm20.c

3574 lines
89 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/jiffies.h>
#include <linux/switch.h>
#include <mtk_charger_intf.h>
#include <mt-plat/aee.h>
#include <mt-plat/charging.h>
#include <mt-plat/battery_common.h>
#include <mt-plat/mtk_boot_common.h>
#include <mach/mtk_pe.h>
#include "inc/mt6370_pmu_charger.h"
#include "inc/mt6370_pmu.h"
#define MT6370_PMU_CHARGER_DRV_VERSION "1.1.12_MTK"
/* ======================= */
/* MT6370 Charger Variable */
/* ======================= */
enum mt6370_pmu_charger_irqidx {
MT6370_CHG_IRQIDX_CHGIRQ1 = 0,
MT6370_CHG_IRQIDX_CHGIRQ2,
MT6370_CHG_IRQIDX_CHGIRQ3,
MT6370_CHG_IRQIDX_CHGIRQ4,
MT6370_CHG_IRQIDX_CHGIRQ5,
MT6370_CHG_IRQIDX_CHGIRQ6,
MT6370_CHG_IRQIDX_QCIRQ,
MT6370_CHG_IRQIDX_DICHGIRQ7,
MT6370_CHG_IRQIDX_OVPCTRLIRQ,
MT6370_CHG_IRQIDX_MAX,
};
enum mt6370_pmu_chg_type {
MT6370_CHG_TYPE_NOVBUS = 0,
MT6370_CHG_TYPE_UNDER_GOING,
MT6370_CHG_TYPE_SDP,
MT6370_CHG_TYPE_SDPNSTD,
MT6370_CHG_TYPE_DCP,
MT6370_CHG_TYPE_CDP,
MT6370_CHG_TYPE_MAX,
};
enum mt6370_usbsw_state {
MT6370_USBSW_CHG = 0,
MT6370_USBSW_USB,
};
struct mt6370_pmu_charger_desc {
u32 ichg;
u32 aicr;
u32 mivr;
u32 cv;
u32 ieoc;
u32 safety_timer;
u32 ircmp_resistor;
u32 ircmp_vclamp;
u32 dc_wdt;
bool en_te;
bool en_wdt;
bool en_otg_wdt;
bool en_polling;
const char *chg_dev_name;
const char *ls_dev_name;
};
struct mt6370_pmu_charger_data {
/* Inherited from mtk_charger_info */
struct mtk_charger_info mchr_info;
struct mt6370_pmu_charger_desc *chg_desc;
struct mt6370_pmu_chip *chip;
struct mutex adc_access_lock;
struct mutex irq_access_lock;
struct mutex aicr_access_lock;
struct mutex ichg_access_lock;
struct mutex bc12_access_lock;
struct device *dev;
wait_queue_head_t wait_queue;
bool err_state;
enum charger_type chg_type;
bool chg_online;
u8 irq_flag[MT6370_CHG_IRQIDX_MAX];
u32 zcv;
bool adc_hang;
struct switch_dev *usb_switch;
bool bc12_en;
#ifndef CONFIG_TCPC_CLASS
struct work_struct chgdet_work;
#endif
};
/* These default values will be used if there's no property in dts */
static struct mt6370_pmu_charger_desc mt6370_default_chg_desc = {
.ichg = 2000000, /* uA */
.aicr = 500000, /* uA */
.mivr = 4400000, /* uV */
.cv = 4350000, /* uA */
.ieoc = 250000, /* uA */
.safety_timer = 12, /* hour */
#ifdef CONFIG_MTK_BIF_SUPPORT
.ircmp_resistor = 0, /* uohm */
.ircmp_vclamp = 0, /* uV */
#else
.ircmp_resistor = 25000, /* uohm */
.ircmp_vclamp = 32000, /* uV */
#endif
.dc_wdt = 4000000, /* us */
.en_te = true,
.en_wdt = true,
};
static const u32 mt6370_otg_oc_threshold[] = {
500000, 700000, 1100000, 1300000, 1800000, 2100000, 2400000, 3000000,
}; /* uA */
static const u32 mt6370_dc_vbatov_lvl[] = {
104, 108, 119,
}; /* % * VOREG */
static const u32 mt6370_dc_wdt[] = {
0, 125000, 250000, 500000, 1000000, 2000000, 4000000, 8000000,
}; /* us */
enum mt6370_charging_status {
MT6370_CHG_STATUS_READY = 0,
MT6370_CHG_STATUS_PROGRESS,
MT6370_CHG_STATUS_DONE,
MT6370_CHG_STATUS_FAULT,
MT6370_CHG_STATUS_MAX,
};
/* Charging status name */
static const char *mt6370_chg_status_name[MT6370_CHG_STATUS_MAX] = {
"ready", "progress", "done", "fault",
};
static const unsigned char mt6370_reg_en_hidden_mode[] = {
MT6370_PMU_REG_HIDDENPASCODE1,
MT6370_PMU_REG_HIDDENPASCODE2,
MT6370_PMU_REG_HIDDENPASCODE3,
MT6370_PMU_REG_HIDDENPASCODE4,
};
static const unsigned char mt6370_val_en_hidden_mode[] = {
0x96, 0x69, 0xC3, 0x3C,
};
enum mt6370_iin_limit_sel {
MT6370_IIMLMTSEL_AICR_3250 = 0,
MT6370_IIMLMTSEL_CHG_TYPE,
MT6370_IINLMTSEL_AICR,
MT6370_IINLMTSEL_LOWER_LEVEL, /* lower of above three */
};
enum mt6370_adc_sel {
MT6370_ADC_VBUS_DIV5 = 1,
MT6370_ADC_VBUS_DIV2,
MT6370_ADC_VSYS,
MT6370_ADC_VBAT,
MT6370_ADC_TS_BAT = 6,
MT6370_ADC_IBUS = 8,
MT6370_ADC_IBAT,
MT6370_ADC_CHG_VDDP = 11,
MT6370_ADC_TEMP_JC,
MT6370_ADC_MAX,
};
/* Unit for each ADC parameter
* 0 stands for reserved
* For TS_BAT/TS_BUS, the real unit is 0.25.
* Here we use 25, please remember to divide 100 while showing the value
*/
static const int mt6370_adc_unit[MT6370_ADC_MAX] = {
0,
MT6370_ADC_UNIT_VBUS_DIV5,
MT6370_ADC_UNIT_VBUS_DIV2,
MT6370_ADC_UNIT_VSYS,
MT6370_ADC_UNIT_VBAT,
0,
MT6370_ADC_UNIT_TS_BAT,
0,
MT6370_ADC_UNIT_IBUS,
MT6370_ADC_UNIT_IBAT,
0,
MT6370_ADC_UNIT_CHG_VDDP,
MT6370_ADC_UNIT_TEMP_JC,
};
static const int mt6370_adc_offset[MT6370_ADC_MAX] = {
0,
MT6370_ADC_OFFSET_VBUS_DIV5,
MT6370_ADC_OFFSET_VBUS_DIV2,
MT6370_ADC_OFFSET_VSYS,
MT6370_ADC_OFFSET_VBAT,
0,
MT6370_ADC_OFFSET_TS_BAT,
0,
MT6370_ADC_OFFSET_IBUS,
MT6370_ADC_OFFSET_IBAT,
0,
MT6370_ADC_OFFSET_CHG_VDDP,
MT6370_ADC_OFFSET_TEMP_JC,
};
/* =============================== */
/* mt6370 Charger Register Address */
/* =============================== */
static const unsigned char mt6370_chg_reg_addr[] = {
MT6370_PMU_REG_CHGCTRL1,
MT6370_PMU_REG_CHGCTRL2,
MT6370_PMU_REG_CHGCTRL3,
MT6370_PMU_REG_CHGCTRL4,
MT6370_PMU_REG_CHGCTRL5,
MT6370_PMU_REG_CHGCTRL6,
MT6370_PMU_REG_CHGCTRL7,
MT6370_PMU_REG_CHGCTRL8,
MT6370_PMU_REG_CHGCTRL9,
MT6370_PMU_REG_CHGCTRL10,
MT6370_PMU_REG_CHGCTRL11,
MT6370_PMU_REG_CHGCTRL12,
MT6370_PMU_REG_CHGCTRL13,
MT6370_PMU_REG_CHGCTRL14,
MT6370_PMU_REG_CHGCTRL15,
MT6370_PMU_REG_CHGCTRL16,
MT6370_PMU_REG_CHGADC,
MT6370_PMU_REG_DEVICETYPE,
MT6370_PMU_REG_QCCTRL1,
MT6370_PMU_REG_QCCTRL2,
MT6370_PMU_REG_QC3P0CTRL1,
MT6370_PMU_REG_QC3P0CTRL2,
MT6370_PMU_REG_USBSTATUS1,
MT6370_PMU_REG_QCSTATUS1,
MT6370_PMU_REG_QCSTATUS2,
MT6370_PMU_REG_CHGPUMP,
MT6370_PMU_REG_CHGCTRL17,
MT6370_PMU_REG_CHGCTRL18,
MT6370_PMU_REG_CHGDIRCHG1,
MT6370_PMU_REG_CHGDIRCHG2,
MT6370_PMU_REG_CHGDIRCHG3,
MT6370_PMU_REG_CHGSTAT,
MT6370_PMU_REG_CHGNTC,
MT6370_PMU_REG_ADCDATAH,
MT6370_PMU_REG_ADCDATAL,
MT6370_PMU_REG_CHGCTRL19,
MT6370_PMU_REG_CHGSTAT1,
MT6370_PMU_REG_CHGSTAT2,
MT6370_PMU_REG_CHGSTAT3,
MT6370_PMU_REG_CHGSTAT4,
MT6370_PMU_REG_CHGSTAT5,
MT6370_PMU_REG_CHGSTAT6,
MT6370_PMU_REG_QCSTAT,
MT6370_PMU_REG_DICHGSTAT,
MT6370_PMU_REG_OVPCTRLSTAT,
};
/* ===================================================================== */
/* Internal Functions */
/* ===================================================================== */
static int mt6370_set_aicr(struct mtk_charger_info *mchr_info, void *data);
static int mt6370_get_aicr(struct mtk_charger_info *mchr_info, void *data);
static int mt6370_set_ichg(struct mtk_charger_info *mchr_info, void *data);
static int mt6370_get_ichg(struct mtk_charger_info *mchr_info, void *data);
static int mt6370_enable_charging(struct mtk_charger_info *mchr_info,
void *data);
static inline void mt6370_chg_irq_set_flag(
struct mt6370_pmu_charger_data *chg_data, u8 *irq, u8 mask)
{
mutex_lock(&chg_data->irq_access_lock);
*irq |= mask;
mutex_unlock(&chg_data->irq_access_lock);
}
static inline void mt6370_chg_irq_clr_flag(
struct mt6370_pmu_charger_data *chg_data, u8 *irq, u8 mask)
{
mutex_lock(&chg_data->irq_access_lock);
*irq &= ~mask;
mutex_unlock(&chg_data->irq_access_lock);
}
static inline int mt6370_pmu_reg_test_bit(
struct mt6370_pmu_chip *chip, u8 cmd, u8 shift, bool *is_one)
{
int ret = 0;
u8 data = 0;
ret = mt6370_pmu_reg_read(chip, cmd);
if (ret < 0) {
*is_one = false;
return ret;
}
data = ret & (1 << shift);
*is_one = (data == 0 ? false : true);
return ret;
}
static u8 mt6370_find_closest_reg_value(u32 min, u32 max, u32 step, u32 num,
u32 target)
{
u32 i = 0, cur_val = 0, next_val = 0;
/* Smaller than minimum supported value, use minimum one */
if (target < min)
return 0;
for (i = 0; i < num - 1; i++) {
cur_val = min + i * step;
next_val = cur_val + step;
if (cur_val > max)
cur_val = max;
if (next_val > max)
next_val = max;
if (target >= cur_val && target < next_val)
return i;
}
/* Greater than maximum supported value, use maximum one */
return num - 1;
}
static u8 mt6370_find_closest_reg_value_via_table(const u32 *value_table,
u32 table_size, u32 target_value)
{
u32 i = 0;
/* Smaller than minimum supported value, use minimum one */
if (target_value < value_table[0])
return 0;
for (i = 0; i < table_size - 1; i++) {
if (target_value >= value_table[i] &&
target_value < value_table[i + 1])
return i;
}
/* Greater than maximum supported value, use maximum one */
return table_size - 1;
}
static u32 mt6370_find_closest_real_value(u32 min, u32 max, u32 step,
u8 reg_val)
{
u32 ret_val = 0;
ret_val = min + reg_val * step;
if (ret_val > max)
ret_val = max;
return ret_val;
}
static int mt6370_set_fast_charge_timer(
struct mt6370_pmu_charger_data *chg_data, u32 hour)
{
int ret = 0;
u8 reg_fct = 0;
reg_fct = mt6370_find_closest_reg_value(
MT6370_WT_FC_MIN,
MT6370_WT_FC_MAX,
MT6370_WT_FC_STEP,
MT6370_WT_FC_NUM,
hour
);
dev_info(chg_data->dev, "%s: timer = %d (0x%02X)\n", __func__, hour,
reg_fct);
ret = mt6370_pmu_reg_update_bits(
chg_data->chip,
MT6370_PMU_REG_CHGCTRL12,
MT6370_MASK_WT_FC,
reg_fct << MT6370_SHIFT_WT_FC
);
return ret;
}
static int mt6370_set_usbsw_state(struct mt6370_pmu_charger_data *chg_data,
int state)
{
dev_info(chg_data->dev, "%s: state = %d\n", __func__, state);
if (chg_data->usb_switch)
switch_set_state(chg_data->usb_switch, state);
#ifdef CONFIG_PROJECT_PHY
else {
if (state == MT6370_USBSW_CHG)
Charger_Detect_Init();
else
Charger_Detect_Release();
}
#endif
return 0;
}
/* Hardware pin current limit */
static int mt6370_enable_ilim(struct mt6370_pmu_charger_data *chg_data, bool en)
{
int ret = 0;
dev_info(chg_data->dev, "%s: en = %d\n", __func__, en);
ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit)
(chg_data->chip, MT6370_PMU_REG_CHGCTRL3, MT6370_MASK_ILIM_EN);
return ret;
}
/* Select IINLMTSEL */
static int mt6370_select_input_current_limit(
struct mt6370_pmu_charger_data *chg_data, enum mt6370_iin_limit_sel sel)
{
int ret = 0;
dev_info(chg_data->dev, "%s: select input current limit = %d\n",
__func__, sel);
ret = mt6370_pmu_reg_update_bits(
chg_data->chip,
MT6370_PMU_REG_CHGCTRL2,
MT6370_MASK_IINLMTSEL,
sel << MT6370_SHIFT_IINLMTSEL
);
return ret;
}
static int mt6370_enable_hidden_mode(struct mt6370_pmu_charger_data *chg_data,
bool en)
{
int ret = 0;
dev_info(chg_data->dev, "%s: en = %d\n", __func__, en);
/* Disable hidden mode */
if (!en) {
ret = mt6370_pmu_reg_write(chg_data->chip,
mt6370_reg_en_hidden_mode[0], 0x00);
if (ret < 0)
goto err;
return ret;
}
ret = mt6370_pmu_reg_block_write(
chg_data->chip,
mt6370_reg_en_hidden_mode[0],
ARRAY_SIZE(mt6370_val_en_hidden_mode),
mt6370_val_en_hidden_mode
);
if (ret < 0)
goto err;
return ret;
err:
dev_err(chg_data->dev, "%s: en = %d failed, ret = %d\n", __func__,
en, ret);
return ret;
}
/* Software workaround */
static int mt6370_chg_sw_workaround(struct mt6370_pmu_charger_data *chg_data)
{
int ret = 0;
u8 zcv_data[2] = {0};
dev_info(chg_data->dev, "%s\n", __func__);
/* Enter hidden mode */
ret = mt6370_enable_hidden_mode(chg_data, true);
if (ret < 0)
goto out;
/* Read ZCV data */
ret = mt6370_pmu_reg_block_read(chg_data->chip,
MT6370_PMU_REG_ADCBATDATAH, 2, zcv_data);
if (ret < 0)
dev_err(chg_data->dev, "%s: read zcv data failed\n", __func__);
else {
chg_data->zcv = 5000 * (zcv_data[0] * 256 + zcv_data[1]);
dev_info(chg_data->dev, "%s: zcv = (0x%02X, 0x%02X, %dmV)\n",
__func__, zcv_data[0], zcv_data[1],
chg_data->zcv / 1000);
}
/* Trigger any ADC before disabling ZCV */
ret = mt6370_pmu_reg_write(chg_data->chip, MT6370_PMU_REG_CHGADC,
0x11);
if (ret < 0)
dev_err(chg_data->dev, "%s: trigger ADC failed\n", __func__);
/* Disable ZCV */
ret = mt6370_pmu_reg_set_bit(chg_data->chip, MT6370_PMU_REG_OSCCTRL,
0x04);
if (ret < 0)
dev_err(chg_data->dev, "%s: disable ZCV failed\n", __func__);
/* Disable TS auto sensing */
ret = mt6370_pmu_reg_clr_bit(chg_data->chip,
MT6370_PMU_REG_CHGHIDDENCTRL15, 0x01);
mdelay(200);
/* Disable SEN_DCP for charging mode */
ret = mt6370_pmu_reg_clr_bit(chg_data->chip,
MT6370_PMU_REG_QCCTRL2, MT6370_MASK_EN_DCP);
out:
/* Exit hidden mode */
ret = mt6370_enable_hidden_mode(chg_data, false);
if (ret < 0)
dev_err(chg_data->dev, "%s: exit hidden mode failed\n",
__func__);
return ret;
}
static int mt6370_get_adc(struct mt6370_pmu_charger_data *chg_data,
enum mt6370_adc_sel adc_sel, int *adc_val)
{
int ret = 0, i = 0;
u8 adc_data[6] = {0};
bool adc_start = false;
u32 aicr = 0, ichg = 0;
s64 adc_result = 0;
const int max_wait_times = 6;
mutex_lock(&chg_data->adc_access_lock);
mt6370_enable_hidden_mode(chg_data, true);
/* Select ADC to desired channel */
ret = mt6370_pmu_reg_update_bits(
chg_data->chip,
MT6370_PMU_REG_CHGADC,
MT6370_MASK_ADC_IN_SEL,
adc_sel << MT6370_SHIFT_ADC_IN_SEL
);
if (ret < 0) {
dev_err(chg_data->dev, "%s: select ch to %d failed, ret = %d\n",
__func__, adc_sel, ret);
goto out;
}
/* Workaround for IBUS & IBAT */
if (adc_sel == MT6370_ADC_IBUS) {
mutex_lock(&chg_data->aicr_access_lock);
ret = mt6370_get_aicr(&chg_data->mchr_info, &aicr);
if (ret < 0) {
dev_err(chg_data->dev, "%s: get aicr failed\n",
__func__);
goto out_unlock_all;
}
} else if (adc_sel == MT6370_ADC_IBAT) {
mutex_lock(&chg_data->ichg_access_lock);
ret = mt6370_get_ichg(&chg_data->mchr_info, &ichg);
if (ret < 0) {
dev_err(chg_data->dev, "%s: get ichg failed\n",
__func__);
goto out_unlock_all;
}
}
/* Start ADC conversation */
ret = mt6370_pmu_reg_set_bit(chg_data->chip, MT6370_PMU_REG_CHGADC,
MT6370_MASK_ADC_START);
if (ret < 0) {
dev_err(chg_data->dev,
"%s: start conversation failed, sel = %d, ret = %d\n",
__func__, adc_sel, ret);
goto out_unlock_all;
}
for (i = 0; i < max_wait_times; i++) {
msleep(35);
ret = mt6370_pmu_reg_test_bit(chg_data->chip,
MT6370_PMU_REG_CHGADC, MT6370_SHIFT_ADC_START,
&adc_start);
if (!adc_start && ret >= 0)
break;
}
if (i == max_wait_times) {
dev_err(chg_data->dev,
"%s: wait conversation failed, sel = %d, ret = %d\n",
__func__, adc_sel, ret);
/* AEE for debug */
if (!chg_data->adc_hang) {
for (i = 0; i < ARRAY_SIZE(mt6370_chg_reg_addr); i++) {
ret = mt6370_pmu_reg_read(chg_data->chip,
mt6370_chg_reg_addr[i]);
dev_err(chg_data->dev,
"%s: reg[0x%02X] = 0x%02X\n",
__func__, mt6370_chg_reg_addr[i], ret);
}
chg_data->adc_hang = true;
}
/* Add for debug */
/* ZCV, reg0x10 */
ret = mt6370_pmu_reg_read(chg_data->chip,
MT6370_PMU_REG_OSCCTRL);
if (ret < 0)
dev_err(chg_data->dev, "%s: read reg0x10 failed\n",
__func__);
else
dev_err(chg_data->dev, "%s: reg0x10 = 0x%02X\n",
__func__, ret);
/* TS auto sensing */
ret = mt6370_pmu_reg_read(chg_data->chip,
MT6370_PMU_REG_CHGHIDDENCTRL15);
if (ret < 0)
dev_err(chg_data->dev, "%s: read reg0x3E failed\n",
__func__);
else
dev_err(chg_data->dev, "%s: reg0x3E = 0x%02X\n",
__func__, ret);
}
mdelay(1);
/* Read ADC data */
ret = mt6370_pmu_reg_block_read(chg_data->chip, MT6370_PMU_REG_ADCDATAH,
6, adc_data);
if (ret < 0) {
dev_err(chg_data->dev,
"%s: read ADC data failed, ret = %d\n", __func__, ret);
goto out_unlock_all;
}
dev_dbg(chg_data->dev,
"%s: adc_sel = %d, adc_h = 0x%02X, adc_l = 0x%02X\n",
__func__, adc_sel, adc_data[0], adc_data[1]);
dev_dbg(chg_data->dev,
"%s: 0x4E~51 = (0x%02X, 0x%02X, 0x%02X, 0x%02X)\n", __func__,
adc_data[2], adc_data[3], adc_data[4], adc_data[5]);
/* Calculate ADC value */
adc_result = (adc_data[0] * 256
+ adc_data[1]) * mt6370_adc_unit[adc_sel]
+ mt6370_adc_offset[adc_sel];
out_unlock_all:
/* Coefficient of IBUS & IBAT */
if (adc_sel == MT6370_ADC_IBUS) {
if (aicr < 400000) /* 400mA */
adc_result = adc_result * 67 / 100;
mutex_unlock(&chg_data->aicr_access_lock);
} else if (adc_sel == MT6370_ADC_IBAT) {
if (ichg >= 100000 && ichg <= 450000) /* 100~450mA */
adc_result = adc_result * 475 / 1000;
else if (ichg >= 500000 && ichg <= 850000) /* 500~850mA */
adc_result = adc_result * 536 / 1000;
mutex_unlock(&chg_data->ichg_access_lock);
}
out:
*adc_val = adc_result;
mt6370_enable_hidden_mode(chg_data, false);
mutex_unlock(&chg_data->adc_access_lock);
return ret;
}
static int mt6370_enable_wdt(struct mt6370_pmu_charger_data *chg_data,
bool en)
{
int ret = 0;
dev_info(chg_data->dev, "%s: en = %d\n", __func__, en);
ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit)
(chg_data->chip, MT6370_PMU_REG_CHGCTRL13, MT6370_MASK_WDT_EN);
return ret;
}
static int mt6370_is_charging_enable(struct mt6370_pmu_charger_data *chg_data,
bool *en)
{
int ret = 0;
ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_CHGCTRL2,
MT6370_SHIFT_CHG_EN, en);
return ret;
}
static int mt6370_enable_te(struct mt6370_pmu_charger_data *chg_data, bool en)
{
int ret = 0;
dev_info(chg_data->dev, "%s: en = %d\n", __func__, en);
ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit)
(chg_data->chip, MT6370_PMU_REG_CHGCTRL2, MT6370_MASK_TE_EN);
return ret;
}
static int mt6370_enable_pump_express(struct mt6370_pmu_charger_data *chg_data,
bool en)
{
int ret = 0, i = 0;
const int max_wait_times = 5;
bool pumpx_en = false;
bool chg_en = true;
u32 aicr = 80000; /* 10uA */
u32 ichg = 200000; /* 10uA */
dev_info(chg_data->dev, "%s: en %d\n", __func__, en);
ret = mt6370_set_aicr(&chg_data->mchr_info, &aicr);
if (ret < 0)
return ret;
ret = mt6370_set_ichg(&chg_data->mchr_info, &ichg);
if (ret < 0)
return ret;
ret = mt6370_enable_charging(&chg_data->mchr_info, &chg_en);
if (ret < 0)
return ret;
ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit)
(chg_data->chip, MT6370_PMU_REG_CHGCTRL17, MT6370_MASK_PUMPX_EN);
if (ret < 0)
return ret;
for (i = 0; i < max_wait_times; i++) {
msleep(2500);
ret = mt6370_pmu_reg_test_bit(chg_data->chip,
MT6370_PMU_REG_CHGCTRL17, MT6370_SHIFT_PUMPX_EN,
&pumpx_en);
if (!pumpx_en && ret >= 0)
break;
}
if (i == max_wait_times) {
dev_err(chg_data->dev, "%s: wait failed, ret = %d\n", __func__,
ret);
ret = -EIO;
return ret;
}
return ret;
}
static int mt6370_get_ieoc(struct mt6370_pmu_charger_data *chg_data, u32 *ieoc)
{
int ret = 0;
u8 reg_ieoc = 0;
ret = mt6370_pmu_reg_read(chg_data->chip, MT6370_PMU_REG_CHGCTRL9);
if (ret < 0)
return ret;
reg_ieoc = (ret & MT6370_MASK_IEOC) >> MT6370_SHIFT_IEOC;
*ieoc = mt6370_find_closest_real_value(
MT6370_IEOC_MIN,
MT6370_IEOC_MAX,
MT6370_IEOC_STEP,
reg_ieoc
);
return ret;
}
static int mt6370_get_mivr(struct mt6370_pmu_charger_data *chg_data, u32 *mivr)
{
int ret = 0;
u8 reg_mivr = 0;
ret = mt6370_pmu_reg_read(chg_data->chip, MT6370_PMU_REG_CHGCTRL6);
if (ret < 0)
return ret;
reg_mivr = ((ret & MT6370_MASK_MIVR) >> MT6370_SHIFT_MIVR) & 0xFF;
*mivr = mt6370_find_closest_real_value(
MT6370_MIVR_MIN,
MT6370_MIVR_MAX,
MT6370_MIVR_STEP,
reg_mivr
);
return ret;
}
static int mt6370_set_ieoc(struct mt6370_pmu_charger_data *chg_data, u32 ieoc)
{
int ret = 0;
/* Find corresponding reg value */
u8 reg_ieoc = mt6370_find_closest_reg_value(
MT6370_IEOC_MIN,
MT6370_IEOC_MAX,
MT6370_IEOC_STEP,
MT6370_IEOC_NUM,
ieoc
);
dev_info(chg_data->dev, "%s: ieoc = %d (0x%02X)\n", __func__, ieoc,
reg_ieoc);
ret = mt6370_pmu_reg_update_bits(
chg_data->chip,
MT6370_PMU_REG_CHGCTRL9,
MT6370_MASK_IEOC,
reg_ieoc << MT6370_SHIFT_IEOC
);
return ret;
}
static int mt6370_get_charging_status(struct mt6370_pmu_charger_data *chg_data,
enum mt6370_charging_status *chg_stat)
{
int ret = 0;
ret = mt6370_pmu_reg_read(chg_data->chip, MT6370_PMU_REG_CHGSTAT);
if (ret < 0)
return ret;
*chg_stat = (ret & MT6370_MASK_CHG_STAT) >> MT6370_SHIFT_CHG_STAT;
return ret;
}
static int mt6370_set_dc_wdt(struct mt6370_pmu_charger_data *chg_data, u32 us)
{
int ret = 0;
u8 reg_wdt = 0;
reg_wdt = mt6370_find_closest_reg_value_via_table(
mt6370_dc_wdt,
ARRAY_SIZE(mt6370_dc_wdt),
us
);
dev_info(chg_data->dev, "%s: wdt = %dms(0x%02X)\n", __func__, us / 1000,
reg_wdt);
ret = mt6370_pmu_reg_update_bits(
chg_data->chip,
MT6370_PMU_REG_CHGDIRCHG2,
MT6370_MASK_DC_WDT,
reg_wdt << MT6370_SHIFT_DC_WDT
);
return ret;
}
static int mt6370_enable_jeita(struct mt6370_pmu_charger_data *chg_data,
bool en)
{
int ret = 0;
dev_info(chg_data->dev, "%s: en = %d\n", __func__, en);
ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit)
(chg_data->chip, MT6370_PMU_REG_CHGCTRL16, MT6370_MASK_JEITA_EN);
return ret;
}
static int mt6370_set_aicl_vth(struct mt6370_pmu_charger_data *chg_data,
u32 aicl_vth)
{
int ret = 0;
u8 reg_aicl_vth = 0;
reg_aicl_vth = mt6370_find_closest_reg_value(
MT6370_AICL_VTH_MIN,
MT6370_AICL_VTH_MAX,
MT6370_AICL_VTH_STEP,
MT6370_AICL_VTH_NUM,
aicl_vth
);
dev_info(chg_data->dev, "%s: vth = %d (0x%02X)\n", __func__, aicl_vth,
reg_aicl_vth);
ret = mt6370_pmu_reg_update_bits(
chg_data->chip,
MT6370_PMU_REG_CHGCTRL14,
MT6370_MASK_AICL_VTH,
reg_aicl_vth << MT6370_SHIFT_AICL_VTH
);
if (ret < 0)
dev_err(chg_data->dev, "%s: set aicl vth failed, ret = %d\n",
__func__, ret);
return ret;
}
static int mt6370_enable_chgdet_flow(struct mt6370_pmu_charger_data *chg_data,
bool en)
{
int ret = 0, i = 0;
#ifdef CONFIG_TCPC_CLASS
int vbus = 0;
#endif
const int max_wait_cnt = 200;
if (en) {
/* Workaround for CDP port */
for (i = 0; i < max_wait_cnt; i++) {
if (is_usb_rdy())
break;
dev_err(chg_data->dev, "%s: CDP block\n", __func__);
#ifdef CONFIG_TCPC_CLASS
ret = mt6370_get_adc(chg_data, MT6370_ADC_VBUS_DIV5,
&vbus);
if (ret >= 0 && vbus < 4300000) {
dev_info(chg_data->dev,
"%s: plug out, vbus = %dmV\n",
__func__, vbus / 1000);
return 0;
}
#endif
msleep(100);
}
if (i == max_wait_cnt)
dev_err(chg_data->dev, "%s: CDP timeout\n", __func__);
else
dev_info(chg_data->dev, "%s: CDP free\n", __func__);
}
dev_info(chg_data->dev, "%s: en = %d\n", __func__, en);
mutex_lock(&chg_data->bc12_access_lock);
ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit)
(chg_data->chip, MT6370_PMU_REG_DEVICETYPE, MT6370_MASK_USBCHGEN);
if (ret >= 0)
chg_data->bc12_en = en;
mutex_unlock(&chg_data->bc12_access_lock);
return ret;
}
static int _mt6370_set_mivr(struct mt6370_pmu_charger_data *chg_data, u32 uV)
{
int ret = 0;
u8 reg_mivr = 0;
/* Find corresponding reg value */
reg_mivr = mt6370_find_closest_reg_value(
MT6370_MIVR_MIN,
MT6370_MIVR_MAX,
MT6370_MIVR_STEP,
MT6370_MIVR_NUM,
uV
);
dev_info(chg_data->dev, "%s: mivr = %d (0x%02X)\n", __func__, uV,
reg_mivr);
ret = mt6370_pmu_reg_update_bits(
chg_data->chip,
MT6370_PMU_REG_CHGCTRL6,
MT6370_MASK_MIVR,
reg_mivr << MT6370_SHIFT_MIVR
);
return ret;
}
#ifndef CONFIG_TCPC_CLASS
static void mt6370_chgdet_work_handler(struct work_struct *work)
{
int ret = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)container_of(work,
struct mt6370_pmu_charger_data, chgdet_work);
mt6370_set_usbsw_state(chg_data, MT6370_USBSW_CHG);
/* Enable USB charger type detection */
ret = mt6370_enable_chgdet_flow(chg_data, true);
if (ret < 0)
dev_err(chg_data->dev,
"%s: enable usb chrdet failed\n", __func__);
}
#endif /* CONFIG_TCPC_CLASS */
static int _mt6370_set_ichg(struct mt6370_pmu_charger_data *chg_data, u32 uA)
{
int ret = 0;
u8 reg_ichg = 0;
/* For adc workaround */
mutex_lock(&chg_data->ichg_access_lock);
/* Find corresponding reg value */
reg_ichg = mt6370_find_closest_reg_value(
MT6370_ICHG_MIN,
MT6370_ICHG_MAX,
MT6370_ICHG_STEP,
MT6370_ICHG_NUM,
uA
);
dev_info(chg_data->dev, "%s: ichg = %d (0x%02X)\n", __func__, uA,
reg_ichg);
ret = mt6370_pmu_reg_update_bits(
chg_data->chip,
MT6370_PMU_REG_CHGCTRL7,
MT6370_MASK_ICHG,
reg_ichg << MT6370_SHIFT_ICHG
);
if (chg_data->chip->chip_vid == 0xF0)
goto bypass_ieoc_workaround;
/* Workaround to make IEOC accurate */
if (uA < 900000) /* 900mA */
ret = mt6370_set_ieoc(chg_data,
chg_data->chg_desc->ieoc + 100000);
else
ret = mt6370_set_ieoc(chg_data, chg_data->chg_desc->ieoc);
bypass_ieoc_workaround:
/* For adc workaround */
mutex_unlock(&chg_data->ichg_access_lock);
return ret;
}
static int _mt6370_set_aicr(struct mt6370_pmu_charger_data *chg_data, u32 uA)
{
int ret = 0;
u8 reg_aicr = 0;
mutex_lock(&chg_data->aicr_access_lock);
/* Find corresponding reg value */
reg_aicr = mt6370_find_closest_reg_value(
MT6370_AICR_MIN,
MT6370_AICR_MAX,
MT6370_AICR_STEP,
MT6370_AICR_NUM,
uA
);
dev_info(chg_data->dev, "%s: aicr = %d (0x%02X)\n", __func__, uA,
reg_aicr);
ret = mt6370_pmu_reg_update_bits(
chg_data->chip,
MT6370_PMU_REG_CHGCTRL3,
MT6370_MASK_AICR,
reg_aicr << MT6370_SHIFT_AICR
);
mutex_unlock(&chg_data->aicr_access_lock);
return ret;
}
static int _mt6370_get_cv(struct mt6370_pmu_charger_data *chg_data, u32 *cv)
{
int ret = 0;
u8 reg_cv = 0;
ret = mt6370_pmu_reg_read(chg_data->chip, MT6370_PMU_REG_CHGCTRL4);
if (ret < 0)
return ret;
reg_cv = (ret & MT6370_MASK_BAT_VOREG) >> MT6370_SHIFT_BAT_VOREG;
*cv = mt6370_find_closest_real_value(
MT6370_BAT_VOREG_MIN,
MT6370_BAT_VOREG_MAX,
MT6370_BAT_VOREG_STEP,
reg_cv
);
return ret;
}
static int mt6370_get_cv(struct charger_device *chg_dev, u32 *cv)
{
struct mt6370_pmu_charger_data *chg_data = dev_get_drvdata(&chg_dev->dev);
return _mt6370_get_cv(chg_data, cv);
}
static int _mt6370_set_cv(struct mt6370_pmu_charger_data *chg_data, u32 uV)
{
int ret = 0, reg_val = 0;
u8 reg_cv = 0;
u32 ori_cv;
/* Get the original cv to check if this step of setting cv is necessary */
ret = _mt6370_get_cv(chg_data, &ori_cv);
if (ret < 0)
return ret;
if (ori_cv == uV)
return 0;
/* Enable hidden mode */
ret = mt6370_enable_hidden_mode(chg_data, true);
if (ret < 0)
return ret;
/* Store BATOVP Level */
reg_val = mt6370_pmu_reg_read(chg_data->chip, MT6370_PMU_REG_CHGHIDDENCTRL22);
if (reg_val < 0)
goto out;
/* Disable BATOVP (set 0x45[6:5] = b'11) */
ret = mt6370_pmu_reg_write(chg_data->chip, MT6370_PMU_REG_CHGHIDDENCTRL22,
reg_val | MT6370_MASK_BATOVP_LVL);
if (ret < 0)
goto out;
/* Set CV */
reg_cv = mt6370_find_closest_reg_value(
MT6370_BAT_VOREG_MIN,
MT6370_BAT_VOREG_MAX,
MT6370_BAT_VOREG_STEP,
MT6370_BAT_VOREG_NUM,
uV
);
dev_info(chg_data->dev, "%s: bat voreg = %d (0x%02X)\n", __func__, uV,
reg_cv);
ret = mt6370_pmu_reg_update_bits(
chg_data->chip,
MT6370_PMU_REG_CHGCTRL4,
MT6370_MASK_BAT_VOREG,
reg_cv << MT6370_SHIFT_BAT_VOREG
);
if (ret < 0)
goto out;
/* Delay 5ms */
mdelay(5);
/* Enable BATOVP and restore BATOVP level */
ret = mt6370_pmu_reg_write(chg_data->chip, MT6370_PMU_REG_CHGHIDDENCTRL22, reg_val);
out:
/* Disable hidden mode */
return mt6370_enable_hidden_mode(chg_data, false);
}
static int mt6370_set_cv(struct charger_device *chg_dev, u32 uV)
{
int ret = 0;
struct mt6370_pmu_charger_data *chg_data =
dev_get_drvdata(&chg_dev->dev);
ret = _mt6370_set_cv(chg_data, uV);
return ret;
}
static int _mt6370_enable_safety_timer(struct mt6370_pmu_charger_data *chg_data,
bool en)
{
int ret = 0;
dev_info(chg_data->dev, "%s: en = %d\n", __func__, en);
ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit)
(chg_data->chip, MT6370_PMU_REG_CHGCTRL12, MT6370_MASK_TMR_EN);
return ret;
}
static int _mt6370_enable_hz(struct mt6370_pmu_charger_data *chg_data, bool en)
{
int ret = 0;
dev_info(chg_data->dev, "%s: en = %d\n", __func__, en);
ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit)
(chg_data->chip, MT6370_PMU_REG_CHGCTRL1, MT6370_MASK_HZ_EN);
return ret;
}
static int _mt6370_set_ircmp_resistor(struct mt6370_pmu_charger_data *chg_data,
u32 uohm)
{
int ret = 0;
u8 reg_resistor = 0;
reg_resistor = mt6370_find_closest_reg_value(
MT6370_IRCMP_RES_MIN,
MT6370_IRCMP_RES_MAX,
MT6370_IRCMP_RES_STEP,
MT6370_IRCMP_RES_NUM,
uohm
);
dev_info(chg_data->dev, "%s: resistor = %d (0x%02X)\n", __func__, uohm,
reg_resistor);
ret = mt6370_pmu_reg_update_bits(
chg_data->chip,
MT6370_PMU_REG_CHGCTRL18,
MT6370_MASK_IRCMP_RES,
reg_resistor << MT6370_SHIFT_IRCMP_RES
);
return ret;
}
static int _mt6370_set_ircmp_vclamp(struct mt6370_pmu_charger_data *chg_data,
u32 uV)
{
int ret = 0;
u8 reg_vclamp = 0;
reg_vclamp = mt6370_find_closest_reg_value(
MT6370_IRCMP_VCLAMP_MIN,
MT6370_IRCMP_VCLAMP_MAX,
MT6370_IRCMP_VCLAMP_STEP,
MT6370_IRCMP_VCLAMP_NUM,
uV
);
dev_info(chg_data->dev, "%s: vclamp = %d (0x%02X)\n", __func__, uV,
reg_vclamp);
ret = mt6370_pmu_reg_update_bits(
chg_data->chip,
MT6370_PMU_REG_CHGCTRL18,
MT6370_MASK_IRCMP_VCLAMP,
reg_vclamp << MT6370_SHIFT_IRCMP_VCLAMP
);
return ret;
}
/* =================== */
/* Released interfaces */
/* =================== */
/* This is for GM20's PE20 */
static int mt6370_hw_init(struct mtk_charger_info *mchr_info, void *data)
{
return 0;
}
static int mt6370_enable_hz(struct mtk_charger_info *mchr_info, void *data)
{
int ret = 0;
bool en = *((bool *)data);
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
ret = _mt6370_enable_hz(chg_data, en);
return ret;
}
static int mt6370_run_aicl(struct mtk_charger_info *mchr_info, void *data)
{
int ret = 0;
u32 mivr = 0, aicl_vth = 0, aicr = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
/* Check whether MIVR loop is active */
if (!(chg_data->irq_flag[MT6370_CHG_IRQIDX_CHGIRQ1]
& MT6370_MASK_CHG_MIVR)) {
dev_info(chg_data->dev,
"%s: mivr loop is not active\n", __func__);
return 0;
}
dev_info(chg_data->dev, "%s: mivr loop is active\n", __func__);
/* Clear chg mivr event */
mt6370_chg_irq_clr_flag(chg_data,
&chg_data->irq_flag[MT6370_CHG_IRQIDX_CHGIRQ1],
MT6370_MASK_CHG_MIVR);
ret = mt6370_get_mivr(chg_data, &mivr);
if (ret < 0)
goto out;
/* Check if there's a suitable AICL_VTH */
aicl_vth = mivr + 200;
if (aicl_vth > MT6370_AICL_VTH_MAX) {
pr_info("%s: no suitable VTH, vth = %d\n", __func__, aicl_vth);
ret = -EINVAL;
goto out;
}
ret = mt6370_set_aicl_vth(chg_data, aicl_vth);
if (ret < 0)
goto out;
/* Clear AICL measurement IRQ */
mt6370_chg_irq_clr_flag(chg_data,
&chg_data->irq_flag[MT6370_CHG_IRQIDX_CHGIRQ5],
MT6370_MASK_CHG_AICLMEASI);
ret = mt6370_pmu_reg_set_bit(chg_data->chip, MT6370_PMU_REG_CHGCTRL14,
MT6370_MASK_AICL_MEAS);
if (ret < 0)
goto out;
ret = wait_event_interruptible_timeout(chg_data->wait_queue,
chg_data->irq_flag[MT6370_CHG_IRQIDX_CHGIRQ5] &
MT6370_MASK_CHG_AICLMEASI,
msecs_to_jiffies(2500));
if (ret <= 0) {
pr_err("%s: wait AICL time out, ret = %d\n", __func__, ret);
ret = -EIO;
goto out;
}
ret = mt6370_get_aicr(mchr_info, &aicr);
if (ret < 0)
goto out;
*((u32 *)data) = aicr / 100;
dev_info(chg_data->dev, "%s: aicr upper bound = %dmA\n", __func__,
aicr / 100);
goto en_mivrirq;
out:
*((u32 *)data) = 0;
en_mivrirq:
ret = mt6370_pmu_reg_clr_bit(chg_data->chip,
MT6370_PMU_CHGMASK1, MT6370_MASK_CHG_MIVRM);
return ret;
}
static int mt6370_set_ircmp_resistor(struct mtk_charger_info *mchr_info,
void *data)
{
int ret = 0;
u32 uV = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
uV = *((u32 *)data) * 1000;
ret = _mt6370_set_ircmp_resistor(chg_data, uV);
return ret;
}
static int mt6370_set_ircmp_vclamp(struct mtk_charger_info *mchr_info,
void *data)
{
int ret = 0;
u32 uV = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
uV = *((u32 *)data) * 1000;
ret = _mt6370_set_ircmp_vclamp(chg_data, uV);
return ret;
}
static int mt6370_set_error_state(struct mtk_charger_info *mchr_info,
void *data)
{
int ret = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
chg_data->err_state = *((bool *)data);
mt6370_enable_hz(mchr_info, &chg_data->err_state);
return ret;
}
static int mt6370_get_charger_type(struct mtk_charger_info *mchr_info,
void *data)
{
int ret = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
dev_info(chg_data->dev, "%s\n", __func__);
#ifndef CONFIG_TCPC_CLASS
mt6370_set_usbsw_state(chg_data, MT6370_USBSW_CHG);
/* Clear attachi event */
mt6370_chg_irq_clr_flag(chg_data,
&chg_data->irq_flag[MT6370_CHG_IRQIDX_QCIRQ],
MT6370_MASK_ATTACHI);
/* Turn off/on USB charger detection to retrigger bc1.2 */
ret = mt6370_enable_chgdet_flow(chg_data, false);
if (ret < 0)
pr_err("%s: disable usb chrdet failed\n", __func__);
ret = mt6370_enable_chgdet_flow(chg_data, true);
if (ret < 0)
pr_err("%s: disable usb chrdet failed\n", __func__);
#endif
ret = wait_event_interruptible_timeout(chg_data->wait_queue,
chg_data->irq_flag[MT6370_CHG_IRQIDX_QCIRQ] & MT6370_MASK_ATTACHI,
msecs_to_jiffies(1000));
if (ret <= 0) {
pr_err("%s: wait attachi failed, ret = %d\n", __func__, ret);
chg_data->chg_type = CHARGER_UNKNOWN;
}
*(CHARGER_TYPE *)data = chg_data->chg_type;
pr_info("%s: chg_type = %d\n", __func__, chg_data->chg_type);
return ret;
}
static int mt6370_enable_charging(struct mtk_charger_info *mchr_info,
void *data)
{
int ret = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
bool en = *((bool *)data);
dev_info(chg_data->dev, "%s: en = %d\n", __func__, en);
ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit)
(chg_data->chip, MT6370_PMU_REG_CHGCTRL2, MT6370_MASK_CHG_EN);
return ret;
}
static int mt6370_enable_safety_timer(struct mtk_charger_info *mchr_info,
void *data)
{
int ret = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
bool en = *((bool *)data);
ret = _mt6370_enable_safety_timer(chg_data, en);
return ret;
}
static int mt6370_is_safety_timer_enable(struct mtk_charger_info *mchr_info,
void *data)
{
int ret = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_CHGCTRL12,
MT6370_SHIFT_TMR_EN, data);
if (ret < 0)
return ret;
return ret;
}
static int mt6370_enable_power_path(struct mtk_charger_info *mchr_info,
void *data)
{
int ret = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
bool en = *((bool *)data);
u32 mivr = en ? 4500000 : MT6370_MIVR_MAX;
dev_info(chg_data->dev, "%s: en = %d\n", __func__, en);
/*
* enable power path -> unmask mivr irq
* mask mivr irq -> disable power path
*/
if (!en)
ret = mt6370_pmu_reg_set_bit(chg_data->chip,
MT6370_PMU_CHGMASK1, MT6370_MASK_CHG_MIVRM);
ret = _mt6370_set_mivr(chg_data, mivr);
if (en)
ret = mt6370_pmu_reg_clr_bit(chg_data->chip,
MT6370_PMU_CHGMASK1, MT6370_MASK_CHG_MIVRM);
return ret;
}
static int mt6370_is_power_path_enable(struct mtk_charger_info *mchr_info,
void *data)
{
int ret = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
u32 mivr = 0;
ret = mt6370_get_mivr(chg_data, &mivr);
*((bool *)data) = (mivr == MT6370_MIVR_MAX ? false : true);
return ret;
}
static int mt6370_get_ichg(struct mtk_charger_info *mchr_info, void *data)
{
int ret = 0;
u8 reg_ichg = 0;
u32 ichg = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
ret = mt6370_pmu_reg_read(chg_data->chip, MT6370_PMU_REG_CHGCTRL7);
if (ret < 0)
return ret;
reg_ichg = (ret & MT6370_MASK_ICHG) >> MT6370_SHIFT_ICHG;
ichg = mt6370_find_closest_real_value(MT6370_ICHG_MIN, MT6370_ICHG_MAX,
MT6370_ICHG_STEP, reg_ichg);
/* MTK's current unit : 10uA */
/* Our current unit : uA */
ichg /= 10;
*((u32 *)data) = ichg;
return ret;
}
static int mt6370_set_ichg(struct mtk_charger_info *mchr_info, void *data)
{
int ret = 0;
u32 uA = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
/* MTK's current unit : 10uA */
/* Our current unit : uA */
uA = *((u32 *)data) * 10;
ret = _mt6370_set_ichg(chg_data, uA);
return ret;
}
static int mt6370_get_aicr(struct mtk_charger_info *mchr_info, void *data)
{
int ret = 0;
u8 reg_aicr = 0;
u32 aicr = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
ret = mt6370_pmu_reg_read(chg_data->chip, MT6370_PMU_REG_CHGCTRL3);
if (ret < 0)
return ret;
reg_aicr = (ret & MT6370_MASK_AICR) >> MT6370_SHIFT_AICR;
aicr = mt6370_find_closest_real_value(MT6370_AICR_MIN, MT6370_AICR_MAX,
MT6370_AICR_STEP, reg_aicr);
/* MTK's current unit : 10uA */
/* Our current unit : uA */
aicr /= 10;
*((u32 *)data) = aicr;
return ret;
}
static int mt6370_set_aicr(struct mtk_charger_info *mchr_info, void *data)
{
int ret = 0;
u32 uA = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
/* MTK's current unit : 10uA */
/* Our current unit : uA */
uA = *((u32 *)data) * 10;
ret = _mt6370_set_aicr(chg_data, uA);
return ret;
}
static int mt6370_set_mivr(struct mtk_charger_info *mchr_info, void *data)
{
int ret = 0;
u32 uV = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
bool en = true;
ret = mt6370_is_power_path_enable(mchr_info, &en);
if (!en) {
dev_err(chg_data->dev, "%s: power path is disabled\n",
__func__);
return -EINVAL;
}
/* MTK's current unit : mV */
/* Our current unit : uV */
uV = *((u32 *)data) * 1000;
ret = _mt6370_set_mivr(chg_data, uV);
return ret;
}
static int mt6370_set_otg_current_limit(struct mtk_charger_info *mchr_info,
void *data)
{
int ret = 0;
u8 reg_ilimit = 0;
u32 uA = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
/* MTK's current unit : mA */
/* Our current unit : uA */
uA = *((u32 *)data) * 1000;
reg_ilimit = mt6370_find_closest_reg_value_via_table(
mt6370_otg_oc_threshold,
ARRAY_SIZE(mt6370_otg_oc_threshold),
uA
);
dev_info(chg_data->dev, "%s: ilimit = %d (0x%02X)\n", __func__, uA,
reg_ilimit);
ret = mt6370_pmu_reg_update_bits(
chg_data->chip,
MT6370_PMU_REG_CHGCTRL10,
MT6370_MASK_BOOST_OC,
reg_ilimit << MT6370_SHIFT_BOOST_OC
);
return ret;
}
static int mt6370_enable_otg(struct mtk_charger_info *mchr_info, void *data)
{
int ret = 0;
bool en = *((bool *)data);
bool en_otg = false;
u32 current_limit = 500; /* mA */
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
u8 hidden_val = en ? 0x00 : 0x0F;
dev_info(chg_data->dev, "%s: en = %d\n", __func__, en);
/* Set OTG_OC to 500mA */
ret = mt6370_set_otg_current_limit(mchr_info, &current_limit);
if (ret < 0) {
dev_err(chg_data->dev, "%s: set otg oc failed\n", __func__);
return ret;
}
/* Turn off USB charger detection/Enable WDT */
if (en) {
ret = mt6370_enable_chgdet_flow(chg_data, false);
if (ret < 0)
dev_err(chg_data->dev,
"%s: disable usb chrdet failed\n", __func__);
if (chg_data->chg_desc->en_otg_wdt) {
ret = mt6370_enable_wdt(chg_data, true);
if (ret < 0)
dev_err(chg_data->dev, "%s: en wdt failed\n",
__func__);
}
}
/* Switch OPA mode to boost mode */
ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit)
(chg_data->chip, MT6370_PMU_REG_CHGCTRL1, MT6370_MASK_OPA_MODE);
msleep(20);
if (en) {
ret = mt6370_pmu_reg_test_bit(chg_data->chip,
MT6370_PMU_REG_CHGCTRL1,
MT6370_SHIFT_OPA_MODE, &en_otg);
if (ret < 0 || !en_otg) {
dev_err(chg_data->dev, "%s: failed, ret = %d\n",
__func__, ret);
/* Disable OTG */
mt6370_pmu_reg_clr_bit(chg_data->chip,
MT6370_PMU_REG_CHGCTRL1, MT6370_MASK_OPA_MODE);
/* Disable WDT */
ret = mt6370_enable_wdt(chg_data, false);
if (ret < 0)
dev_err(chg_data->dev,
"%s: disable wdt failed\n", __func__);
#ifndef CONFIG_TCPC_CLASS
ret = mt6370_enable_chgdet_flow(chg_data, true);
if (ret < 0)
dev_err(chg_data->dev,
"%s: en usb chrdet failed\n", __func__);
#endif
return -EIO;
}
#ifndef CONFIG_TCPC_CLASS
mt6370_set_usbsw_state(chg_data, MT6370_USBSW_USB);
#endif
}
/*
* Woraround reg[0x25] = 0x00 after entering OTG mode
* reg[0x25] = 0x0F after leaving OTG mode
*/
ret = mt6370_enable_hidden_mode(chg_data, true);
if (ret < 0)
dev_err(chg_data->dev, "%s: enter hidden mode failed\n",
__func__);
else {
ret = mt6370_pmu_reg_write(chg_data->chip,
MT6370_PMU_REG_CHGHIDDENCTRL6, hidden_val);
if (ret < 0)
dev_err(chg_data->dev,
"%s: workaroud failed, ret = %d\n",
__func__, ret);
ret = mt6370_enable_hidden_mode(chg_data, false);
if (ret < 0)
dev_err(chg_data->dev,
"%s: exist hidden mode failed\n", __func__);
}
/* Disable WDT */
if (!en) {
ret = mt6370_enable_wdt(chg_data, false);
if (ret < 0)
dev_err(chg_data->dev, "%s: disable wdt failed\n",
__func__);
}
return ret;
}
static int mt6370_set_pep_current_pattern(struct mtk_charger_info *mchr_info,
void *data)
{
int ret = 0;
bool is_increase = *((bool *)data);
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
dev_info(chg_data->dev, "%s: pe1.0 pump_up = %d\n", __func__,
is_increase);
/* Set to PE1.0 */
ret = mt6370_pmu_reg_clr_bit(chg_data->chip, MT6370_PMU_REG_CHGCTRL17,
MT6370_MASK_PUMPX_20_10);
/* Set Pump Up/Down */
ret = (is_increase ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit)
(chg_data->chip, MT6370_PMU_REG_CHGCTRL17,
MT6370_MASK_PUMPX_UP_DN);
if (ret < 0)
return ret;
/* Enable PumpX */
ret = mt6370_enable_pump_express(chg_data, true);
return ret;
}
static int mt6370_set_pep20_reset(struct mtk_charger_info *mchr_info,
void *data)
{
int ret = 0;
u32 mivr = 4500; /* mA */
u32 ichg = 51200; /* 10uA */
u32 aicr = 10000; /* 10uA */
ret = mt6370_set_mivr(mchr_info, &mivr);
if (ret < 0)
return ret;
ret = mt6370_set_ichg(mchr_info, &ichg);
if (ret < 0)
return ret;
ret = mt6370_set_aicr(mchr_info, &aicr);
if (ret < 0)
return ret;
msleep(250);
aicr = 70000; /* 10uA */
ret = mt6370_set_aicr(mchr_info, &aicr);
return ret;
}
static int mt6370_set_pep20_current_pattern(struct mtk_charger_info *mchr_info,
void *data)
{
int ret = 0;
u8 reg_volt = 0;
u32 uV = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
/* MTK's current unit : mA */
/* Our current unit : uA */
uV = *((u32 *)data) * 1000;
dev_info(chg_data->dev, "%s: pep2.0 = %d\n", __func__, uV);
/* Set to PEP2.0 */
ret = mt6370_pmu_reg_set_bit(chg_data->chip, MT6370_PMU_REG_CHGCTRL17,
MT6370_MASK_PUMPX_20_10);
if (ret < 0)
return ret;
/* Find register value of target voltage */
reg_volt = mt6370_find_closest_reg_value(
MT6370_PEP20_VOLT_MIN,
MT6370_PEP20_VOLT_MAX,
MT6370_PEP20_VOLT_STEP,
MT6370_PEP20_VOLT_NUM,
uV
);
/* Set Voltage */
ret = mt6370_pmu_reg_update_bits(
chg_data->chip,
MT6370_PMU_REG_CHGCTRL17,
MT6370_MASK_PUMPX_DEC,
reg_volt << MT6370_SHIFT_PUMPX_DEC
);
if (ret < 0)
return ret;
/* Enable PumpX */
ret = mt6370_enable_pump_express(chg_data, true);
ret = (ret >= 0) ? 0 : ret;
return ret;
}
static int mt6370_set_pep20_efficiency_table(struct mtk_charger_info *mchr_info,
void *data)
{
int ret = 0;
pep20_profile_t *profile = (pep20_profile_t *)data;
profile[0].vchr = 8000;
profile[1].vchr = 8000;
profile[2].vchr = 8000;
profile[3].vchr = 8500;
profile[4].vchr = 8500;
profile[5].vchr = 8500;
profile[6].vchr = 9000;
profile[7].vchr = 9000;
profile[8].vchr = 9500;
profile[9].vchr = 9500;
return ret;
}
static int mt6370_is_charging_done(struct mtk_charger_info *mchr_info,
void *data)
{
int ret = 0;
enum mt6370_charging_status chg_stat = MT6370_CHG_STATUS_READY;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
ret = mt6370_get_charging_status(chg_data, &chg_stat);
if (ret < 0)
return ret;
/* Return is charging done or not */
switch (chg_stat) {
case MT6370_CHG_STATUS_READY:
case MT6370_CHG_STATUS_PROGRESS:
case MT6370_CHG_STATUS_FAULT:
*((u32 *)data) = false;
break;
case MT6370_CHG_STATUS_DONE:
*((u32 *)data) = true;
break;
default:
*((u32 *)data) = false;
break;
}
return 0;
}
static int mt6370_kick_wdt(struct mtk_charger_info *mchr_info, void *data)
{
/* Any I2C communication can kick watchdog timer */
int ret = 0;
enum mt6370_charging_status chg_status;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
ret = mt6370_get_charging_status(chg_data, &chg_status);
return ret;
}
static int mt6370_enable_direct_charge(struct mtk_charger_info *mchr_info,
void *data)
{
int ret = 0;
bool en = *((bool *)data);
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
dev_info(chg_data->dev, "%s: en = %d\n", __func__, en);
if (en) {
ret = mt6370_pmu_reg_set_bit(chg_data->chip,
MT6370_PMU_CHGMASK1, MT6370_MASK_CHG_MIVRM);
if (ret < 0)
dev_err(chg_data->dev, "%s: mask MIVR IRQ failed\n",
__func__);
/* Enable bypass mode */
ret = mt6370_pmu_reg_set_bit(chg_data->chip,
MT6370_PMU_REG_CHGCTRL2, MT6370_MASK_BYPASS_MODE);
if (ret < 0) {
dev_err(chg_data->dev, "%s: en bypass mode failed\n",
__func__);
goto out;
}
/* VG_EN = 1 */
ret = mt6370_pmu_reg_set_bit(chg_data->chip,
MT6370_PMU_REG_CHGPUMP, MT6370_MASK_VG_EN);
if (ret < 0) {
dev_err(chg_data->dev, "%s: en VG_EN failed\n",
__func__);
goto disable_bypass;
}
return ret;
}
/* Disable direct charge */
/* VG_EN = 0 */
ret = mt6370_pmu_reg_clr_bit(chg_data->chip, MT6370_PMU_REG_CHGPUMP,
MT6370_MASK_VG_EN);
if (ret < 0)
dev_err(chg_data->dev, "%s: disable VG_EN failed\n", __func__);
disable_bypass:
/* Disable bypass mode */
ret = mt6370_pmu_reg_clr_bit(chg_data->chip, MT6370_PMU_REG_CHGCTRL2,
MT6370_MASK_BYPASS_MODE);
if (ret < 0)
dev_err(chg_data->dev, "%s: disable bypass mode failed\n",
__func__);
out:
/* Unmask MIVR IRQ */
ret = mt6370_pmu_reg_clr_bit(chg_data->chip,
MT6370_PMU_CHGMASK1, MT6370_MASK_CHG_MIVRM);
if (ret < 0)
dev_err(chg_data->dev, "%s: unmask MIVR IRQ failed\n",
__func__);
return ret;
}
static int mt6370_enable_dc_vbusov(struct mtk_charger_info *mchr_info,
void *data)
{
int ret = 0;
bool en = *((bool *)data);
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit)
(chg_data->chip, MT6370_PMU_REG_CHGDIRCHG3,
MT6370_MASK_DC_VBUSOV_EN);
return ret;
}
static int mt6370_set_dc_vbusov(struct mtk_charger_info *mchr_info, void *data)
{
int ret = 0;
u8 reg_vbusov = 0;
u32 uV = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
/* MTK's current unit : mV */
/* Our current unit : uV */
uV = *((u32 *)data) * 1000;
reg_vbusov = mt6370_find_closest_reg_value(
MT6370_DC_VBUSOV_LVL_MIN,
MT6370_DC_VBUSOV_LVL_MAX,
MT6370_DC_VBUSOV_LVL_STEP,
MT6370_DC_VBUSOV_LVL_NUM,
uV
);
dev_info(chg_data->dev, "%s: vbusov = %d (0x%02X)\n", __func__, uV,
reg_vbusov);
ret = mt6370_pmu_reg_update_bits(
chg_data->chip,
MT6370_PMU_REG_CHGDIRCHG3,
MT6370_MASK_DC_VBUSOV_LVL,
reg_vbusov << MT6370_SHIFT_DC_VBUSOV_LVL
);
return ret;
}
static int mt6370_enable_dc_ibusoc(struct mtk_charger_info *mchr_info,
void *data)
{
int ret = 0;
bool en = *((bool *)data);
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
dev_info(chg_data->dev, "%s: en = %d\n", __func__, en);
ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit)
(chg_data->chip, MT6370_PMU_REG_CHGDIRCHG1,
MT6370_MASK_DC_IBUSOC_EN);
return ret;
}
static int mt6370_set_dc_ibusoc(struct mtk_charger_info *mchr_info,
void *data)
{
int ret = 0;
u8 reg_ibusoc = 0;
u32 uA = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
/* MTK's current unit : mA */
/* Our current unit : uA */
uA = *((u32 *)data) * 1000;
reg_ibusoc = mt6370_find_closest_reg_value(
MT6370_DC_IBUSOC_LVL_MIN,
MT6370_DC_IBUSOC_LVL_MAX,
MT6370_DC_IBUSOC_LVL_STEP,
MT6370_DC_IBUSOC_LVL_NUM,
uA
);
dev_info(chg_data->dev, "%s: ibusoc = %d (0x%02X)\n", __func__, uA,
reg_ibusoc);
ret = mt6370_pmu_reg_update_bits(
chg_data->chip,
MT6370_PMU_REG_CHGDIRCHG1,
MT6370_MASK_DC_IBUSOC_LVL,
reg_ibusoc << MT6370_SHIFT_DC_IBUSOC_LVL
);
return ret;
}
static int mt6370_enable_dc_vbatov(struct mtk_charger_info *mchr_info,
void *data)
{
int ret = 0;
bool en = *((bool *)data);
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
dev_info(chg_data->dev, "%s: en = %d\n", __func__, en);
ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit)
(chg_data->chip, MT6370_PMU_REG_CHGDIRCHG1,
MT6370_MASK_DC_VBATOV_EN);
return ret;
}
static int mt6370_set_dc_vbatov(struct mtk_charger_info *mchr_info,
void *data)
{
int ret = 0, i = 0;
u8 reg_vbatov = 0;
u32 cv = 0, vbatov = 0;
u32 uV = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
/* MTK's current unit : mV */
/* Our current unit : uV */
uV = *((u32 *)data) * 1000;
ret = mt6370_get_cv(mchr_info, &cv);
if (ret < 0) {
dev_err(chg_data->dev, "%s: get voreg failed\n", __func__);
return ret;
}
for (i = 0; i < ARRAY_SIZE(mt6370_dc_vbatov_lvl); i++) {
vbatov = (mt6370_dc_vbatov_lvl[i] * cv) / 100;
/* Choose closest level */
if (uV <= vbatov) {
reg_vbatov = i;
break;
}
}
if (i == ARRAY_SIZE(mt6370_dc_vbatov_lvl))
reg_vbatov = i;
dev_info(chg_data->dev, "%s: vbatov = %dmV (0x%02X)\n", __func__,
uV / 1000, reg_vbatov);
ret = mt6370_pmu_reg_update_bits(
chg_data->chip,
MT6370_PMU_REG_CHGDIRCHG1,
MT6370_MASK_DC_VBATOV_LVL,
reg_vbatov << MT6370_SHIFT_DC_VBATOV_LVL
);
return ret;
}
static int mt6370_is_dc_enable(struct mtk_charger_info *mchr_info,
void *data)
{
int ret = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_CHGPUMP,
MT6370_SHIFT_VG_EN, data);
return ret;
}
static int mt6370_kick_dc_wdt(struct mtk_charger_info *mchr_info, void *data)
{
/* Any I2C communication can reset watchdog timer */
int ret = 0;
enum mt6370_charging_status chg_status;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
ret = mt6370_get_charging_status(chg_data, &chg_status);
return ret;
}
static int mt6370_get_tchg(struct mtk_charger_info *mchr_info,
void *data)
{
int ret = 0, adc_temp = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
/* Get value from ADC */
ret = mt6370_get_adc(chg_data, MT6370_ADC_TEMP_JC, &adc_temp);
if (ret < 0)
return ret;
((int *)data)[0] = adc_temp;
((int *)data)[1] = adc_temp;
dev_info(chg_data->dev, "%s: tchg = %d\n", __func__, adc_temp);
return ret;
}
static int mt6370_get_ibus(struct mtk_charger_info *mchr_info, void *data)
{
int ret = 0, adc_ibus = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
/* Get value from ADC */
ret = mt6370_get_adc(chg_data, MT6370_ADC_IBUS, &adc_ibus);
if (ret < 0)
return ret;
*((u32 *)data) = adc_ibus / 1000;
dev_info(chg_data->dev, "%s: ibus = %dmA\n", __func__, adc_ibus / 1000);
return ret;
}
static int mt6370_get_vbus(struct mtk_charger_info *mchr_info, void *data)
{
int ret = 0, adc_vbus = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
/* Get value from ADC */
ret = mt6370_get_adc(chg_data, MT6370_ADC_VBUS_DIV2, &adc_vbus);
if (ret < 0)
return ret;
*((u32 *)data) = adc_vbus / 1000;
dev_info(chg_data->dev, "%s: vbus = %dmV\n", __func__, adc_vbus / 1000);
return ret;
}
static int mt6370_dump_register(struct mtk_charger_info *mchr_info,
void *data)
{
int i = 0, ret = 0;
u32 ichg = 0, aicr = 0, mivr = 0, ieoc = 0, cv = 0;
bool chg_en = 0;
int adc_vsys = 0, adc_vbat = 0, adc_ibat = 0, adc_ibus = 0;
enum mt6370_charging_status chg_status = MT6370_CHG_STATUS_READY;
u8 chg_stat = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
ret = mt6370_get_ichg(mchr_info, &ichg); /* 10uA */
ret = mt6370_get_aicr(mchr_info, &aicr); /* 10uA */
ret = mt6370_get_charging_status(chg_data, &chg_status);
ret = mt6370_get_ieoc(chg_data, &ieoc);
ret = mt6370_get_mivr(chg_data, &mivr);
ret = mt6370_get_cv(mchr_info, &cv);
ret = mt6370_is_charging_enable(chg_data, &chg_en);
ret = mt6370_get_adc(chg_data, MT6370_ADC_VSYS, &adc_vsys);
ret = mt6370_get_adc(chg_data, MT6370_ADC_VBAT, &adc_vbat);
ret = mt6370_get_adc(chg_data, MT6370_ADC_IBAT, &adc_ibat);
ret = mt6370_get_adc(chg_data, MT6370_ADC_IBUS, &adc_ibus);
chg_stat = mt6370_pmu_reg_read(chg_data->chip, MT6370_PMU_REG_CHGSTAT1);
if (chg_status == MT6370_CHG_STATUS_FAULT) {
for (i = 0; i < ARRAY_SIZE(mt6370_chg_reg_addr); i++) {
ret = mt6370_pmu_reg_read(chg_data->chip,
mt6370_chg_reg_addr[i]);
if (ret < 0)
return ret;
dev_dbg(chg_data->dev, "%s: reg[0x%02X] = 0x%02X\n",
__func__, mt6370_chg_reg_addr[i], ret);
}
}
dev_info(chg_data->dev, "%s: ICHG = %dmA, AICR = %dmA, MIVR = %dmV,
IEOC = %dmA, CV = %dmV\n", __func__, ichg / 100, aicr / 100,
mivr / 1000, ieoc / 1000, cv / 1000);
dev_info(chg_data->dev,
"%s: VSYS = %dmV, VBAT = %dmV, IBAT = %dmA, IBUS = %dmA\n",
__func__, adc_vsys / 1000, adc_vbat / 1000, adc_ibat / 1000,
adc_ibus / 1000);
dev_info(chg_data->dev, "%s: CHG_EN = %d, CHG_STATUS = %s,
CHG_STAT = 0x%02X\n", __func__, chg_en,
mt6370_chg_status_name[chg_status], chg_stat);
ret = 0;
return ret;
}
static int mt6370_enable_chg_type_det(struct mtk_charger_info *mchr_info,
void *data)
{
int ret = 0;
#ifdef CONFIG_TCPC_CLASS
bool en = *(bool *)data;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)mchr_info;
dev_info(chg_data->dev, "%s: en = %d\n", __func__, en);
/* TypeC attach, clear attachi event */
if (en)
mt6370_chg_irq_clr_flag(chg_data,
&chg_data->irq_flag[MT6370_CHG_IRQIDX_QCIRQ],
MT6370_MASK_ATTACHI);
else { /* TypeC detach */
ret = mt6370_enable_ilim(chg_data, true);
if (ret < 0)
dev_err(chg_data->dev, "%s: en ilim failed\n",
__func__);
chg_data->chg_type = CHARGER_UNKNOWN;
}
/* No matter plug in/out, make usb switch to MT6370 */
mt6370_set_usbsw_state(chg_data, MT6370_USBSW_CHG);
ret = mt6370_enable_chgdet_flow(chg_data, en);
if (ret < 0)
dev_err(chg_data->dev, "%s: en = %d, failed\n", __func__, en);
#endif
return ret;
}
#ifdef MT6370_APPLE_SAMSUNG_TA_SUPPORT
static int mt6370_detect_apple_samsung_ta(
struct mt6370_pmu_charger_data *chg_data)
{
int ret = 0;
bool dcd_timeout = false;
bool dp_0_9v = false, dp_1_5v = false, dp_2_3v = false, dm_2_3v = false;
/* Only SDP/CDP/DCP could possibly be Apple/Samsung TA */
if (chg_data->chg_type != STANDARD_HOST &&
chg_data->chg_type != CHARGING_HOST &&
chg_data->chg_type != STANDARD_CHARGER)
return -EINVAL;
if (chg_data->chg_type == STANDARD_HOST ||
chg_data->chg_type == CHARGING_HOST) {
ret = mt6370_pmu_reg_test_bit(chg_data->chip,
MT6370_PMU_REG_QCSTAT, MT6370_SHIFT_DCDTI_STAT,
&dcd_timeout);
if (ret < 0) {
dev_err(chg_data->dev, "%s: read dcd timeout failed\n",
__func__);
return ret;
}
if (!dcd_timeout) {
dev_info(chg_data->dev, "%s: dcd is not timeout\n",
__func__);
return 0;
}
}
/* Check DP > 0.9V */
ret = mt6370_pmu_reg_update_bits(
chg_data->chip,
MT6370_PMU_REG_QCSTATUS2,
0x03,
0x03
);
ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_QCSTATUS2,
4, &dp_0_9v);
if (ret < 0)
return ret;
if (!dp_0_9v) {
dev_info(chg_data->dev, "%s: DP < 0.9V\n", __func__);
return ret;
}
ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_QCSTATUS2,
5, &dp_1_5v);
if (ret < 0)
return ret;
/* Samsung charger */
if (!dp_1_5v) {
dev_info(chg_data->dev, "%s: 0.9V < DP < 1.5V\n", __func__);
chg_data->chg_type = SAMSUNG_CHARGER;
return ret;
}
/* Check DP > 2.3 V */
ret = mt6370_pmu_reg_update_bits(
chg_data->chip,
MT6370_PMU_REG_QCSTATUS2,
0x0B,
0x0B
);
ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_QCSTATUS2,
5, &dp_2_3v);
if (ret < 0)
return ret;
/* Check DM > 2.3V */
ret = mt6370_pmu_reg_update_bits(
chg_data->chip,
MT6370_PMU_REG_QCSTATUS2,
0x0F,
0x0F
);
ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_QCSTATUS2,
5, &dm_2_3v);
if (ret < 0)
return ret;
/* Apple charger */
if (!dp_2_3v && !dm_2_3v) {
dev_info(chg_data->dev, "%s: 1.5V < DP < 2.3V && DM < 2.3V\n",
__func__);
chg_data->chg_type = APPLE_0_5A_CHARGER;
} else if (!dp_2_3v && dm_2_3v) {
dev_info(chg_data->dev, "%s: 1.5V < DP < 2.3V && 2.3V < DM\n",
__func__);
chg_data->chg_type = APPLE_1_0A_CHARGER;
} else if (dp_2_3v && !dm_2_3v) {
dev_info(chg_data->dev, "%s: 2.3V < DP && DM < 2.3V\n",
__func__);
chg_data->chg_type = APPLE_2_1A_CHARGER;
} else {
dev_info(chg_data->dev, "%s: 2.3V < DP && 2.3V < DM\n",
__func__);
chg_data->chg_type = APPLE_2_4A_CHARGER;
}
return 0;
}
#endif
static irqreturn_t mt6370_pmu_chg_treg_irq_handler(int irq, void *data)
{
int ret = 0;
bool treg_stat = false;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_err(chg_data->dev, "%s\n", __func__);
/* Read treg status */
ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_CHGSTAT1,
MT6370_SHIFT_CHG_TREG, &treg_stat);
if (ret < 0)
dev_err(chg_data->dev, "%s: read treg stat failed\n", __func__);
else
dev_err(chg_data->dev, "%s: treg stat = %d\n", __func__,
treg_stat);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_chg_aicr_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_notice(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_chg_mivr_irq_handler(int irq, void *data)
{
int ret = 0;
bool mivr_stat = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_info(chg_data->dev, "%s\n", __func__);
ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_CHGSTAT1,
MT6370_SHIFT_MIVR_STAT, &mivr_stat);
if (ret < 0) {
dev_err(chg_data->dev, "%s: read mivr stat failed\n", __func__);
goto out;
}
if (!mivr_stat) {
dev_info(chg_data->dev, "%s: mivr stat not act\n", __func__);
goto out;
}
/* Disable MIVR IRQ */
ret = mt6370_pmu_reg_set_bit(chg_data->chip,
MT6370_PMU_CHGMASK1, MT6370_MASK_CHG_MIVRM);
if (ret < 0) {
dev_err(chg_data->dev, "%s: disable mivr IRQ failed\n",
__func__);
goto out;
}
mt6370_chg_irq_set_flag(chg_data,
&chg_data->irq_flag[MT6370_CHG_IRQIDX_CHGIRQ1],
MT6370_MASK_CHG_MIVR);
out:
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_pwr_rdy_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_info(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_chg_vinovp_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_notice(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_chg_vsysuv_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_notice(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_chg_vsysov_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_notice(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_chg_vbatov_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_notice(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_chg_vbusov_irq_handler(int irq, void *data)
{
int ret = 0;
bool vbusov_stat = false;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_err(chg_data->dev, "%s\n", __func__);
ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_CHGSTAT2,
MT6370_SHIFT_CHG_VBUSOV_STAT, &vbusov_stat);
if (ret < 0)
return IRQ_HANDLED;
dev_info(chg_data->dev, "%s: stat = %d\n", __func__, vbusov_stat);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_ts_bat_cold_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_notice(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_ts_bat_cool_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_notice(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_ts_bat_warm_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_notice(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_ts_bat_hot_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_notice(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_chg_tmri_irq_handler(int irq, void *data)
{
int ret = 0;
bool tmr_stat = false;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_err(chg_data->dev, "%s\n", __func__);
ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_CHGSTAT4,
MT6370_SHIFT_CHG_TMRI_STAT, &tmr_stat);
if (ret < 0)
return IRQ_HANDLED;
dev_info(chg_data->dev, "%s: stat = %d\n", __func__, tmr_stat);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_chg_batabsi_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_notice(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_chg_adpbadi_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_notice(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_chg_rvpi_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_notice(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_otpi_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_notice(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_chg_aiclmeasi_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_info(chg_data->dev, "%s\n", __func__);
mt6370_chg_irq_set_flag(chg_data,
&chg_data->irq_flag[MT6370_CHG_IRQIDX_CHGIRQ5],
MT6370_MASK_CHG_AICLMEASI);
wake_up_interruptible(&chg_data->wait_queue);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_chg_ichgmeasi_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_info(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_chgdet_donei_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_info(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_chg_wdtmri_irq_handler(int irq, void *data)
{
int ret = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_notice(chg_data->dev, "%s\n", __func__);
ret = mt6370_kick_wdt(&chg_data->mchr_info, NULL);
if (ret < 0)
dev_err(chg_data->dev, "%s: kick wdt failed\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_ssfinishi_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_info(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_chg_rechgi_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_info(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_chg_termi_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_info(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_chg_ieoci_irq_handler(int irq, void *data)
{
int ret = 0;
bool ieoc_stat = false;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_info(chg_data->dev, "%s\n", __func__);
ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_CHGSTAT5,
MT6370_SHIFT_CHG_IEOCI_STAT, &ieoc_stat);
if (ret < 0)
return IRQ_HANDLED;
dev_info(chg_data->dev, "%s: stat = %d\n", __func__, ieoc_stat);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_adc_donei_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_info(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_pumpx_donei_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_info(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_bst_batuvi_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_notice(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_bst_vbusovi_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_notice(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_bst_olpi_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_notice(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_attachi_irq_handler(int irq, void *data)
{
int ret = 0;
u8 usb_status = 0;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_info(chg_data->dev, "%s\n", __func__);
/* Check bc12 enable flag */
mutex_lock(&chg_data->bc12_access_lock);
if (!chg_data->bc12_en) {
dev_err(chg_data->dev, "%s: bc12 disabled, ignore irq\n",
__func__);
mutex_unlock(&chg_data->bc12_access_lock);
return IRQ_HANDLED;
}
mutex_unlock(&chg_data->bc12_access_lock);
ret = mt6370_pmu_reg_read(chg_data->chip, MT6370_PMU_REG_USBSTATUS1);
if (ret < 0) {
dev_err(chg_data->dev, "%s: read charger type failed\n",
__func__);
return IRQ_HANDLED;
}
usb_status = (ret & MT6370_MASK_USB_STATUS) >> MT6370_SHIFT_USB_STATUS;
chg_data->chg_online = true;
switch (usb_status) {
case MT6370_CHG_TYPE_SDP:
chg_data->chg_type = STANDARD_HOST;
break;
case MT6370_CHG_TYPE_SDPNSTD:
chg_data->chg_type = NONSTANDARD_CHARGER;
break;
case MT6370_CHG_TYPE_CDP:
chg_data->chg_type = CHARGING_HOST;
break;
case MT6370_CHG_TYPE_DCP:
chg_data->chg_type = STANDARD_CHARGER;
break;
default:
chg_data->chg_type = CHARGER_UNKNOWN;
break;
}
#ifdef MT6370_APPLE_SAMSUNG_TA_SUPPORT
ret = mt6370_detect_apple_samsung_ta(chg_data);
if (ret < 0)
dev_err(chg_data->dev,
"%s: detect apple/samsung ta failed, ret = %d\n",
__func__, ret);
#endif
mt6370_chg_irq_set_flag(chg_data,
&chg_data->irq_flag[MT6370_CHG_IRQIDX_QCIRQ],
MT6370_MASK_ATTACHI);
wake_up_interruptible(&chg_data->wait_queue);
/* Turn off USB charger detection */
ret = mt6370_enable_chgdet_flow(chg_data, false);
if (ret < 0)
dev_err(chg_data->dev, "%s: disable usb chrdet failed\n",
__func__);
if (chg_data->chg_type == STANDARD_HOST ||
chg_data->chg_type == CHARGING_HOST)
mt6370_set_usbsw_state(chg_data, MT6370_USBSW_USB);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_detachi_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_info(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_qc30stpdone_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_info(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_qc_vbusdet_done_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_info(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_hvdcp_det_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_info(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_chgdeti_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_info(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_dcdti_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_info(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_dirchg_vgoki_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_info(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_dirchg_wdtmri_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_notice(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_dirchg_uci_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_notice(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_dirchg_oci_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_notice(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_dirchg_ovi_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_notice(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_ovpctrl_swon_evt_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_notice(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_ovpctrl_uvp_d_evt_irq_handler(int irq, void *data)
{
#ifndef CONFIG_TCPC_CLASS
int ret = 0;
bool uvp_d_stat = false;
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_err(chg_data->dev, "%s\n", __func__);
/* Check UVP_D_STAT */
ret = mt6370_pmu_reg_test_bit(
chg_data->chip,
MT6370_PMU_REG_OVPCTRLSTAT,
MT6370_SHIFT_OVPCTRL_UVP_D_STAT,
&uvp_d_stat
);
if (!uvp_d_stat) {
dev_info(chg_data->dev, "%s: no uvp_d_stat\n", __func__);
return IRQ_HANDLED;
}
/* Plug out */
chg_data->chg_online = false;
chg_data->chg_type = CHARGER_UNKNOWN;
/* Turn on USB charger detection */
ret = mt6370_enable_chgdet_flow(chg_data, true);
if (ret < 0)
dev_err(chg_data->dev, "%s: en usb chrdet failed\n", __func__);
/* Switch DPDM to MT6370 */
mt6370_set_usbsw_state(chg_data, MT6370_USBSW_CHG);
#endif /* CONFIG_TCPC_CLASS */
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_ovpctrl_uvp_evt_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_notice(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_ovpctrl_ovp_d_evt_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_notice(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t mt6370_pmu_ovpctrl_ovp_evt_irq_handler(int irq, void *data)
{
struct mt6370_pmu_charger_data *chg_data =
(struct mt6370_pmu_charger_data *)data;
dev_notice(chg_data->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static struct mt6370_pmu_irq_desc mt6370_chg_irq_desc[] = {
MT6370_PMU_IRQDESC(chg_treg),
MT6370_PMU_IRQDESC(chg_aicr),
MT6370_PMU_IRQDESC(chg_mivr),
MT6370_PMU_IRQDESC(pwr_rdy),
MT6370_PMU_IRQDESC(chg_vinovp),
MT6370_PMU_IRQDESC(chg_vsysuv),
MT6370_PMU_IRQDESC(chg_vsysov),
MT6370_PMU_IRQDESC(chg_vbatov),
MT6370_PMU_IRQDESC(chg_vbusov),
MT6370_PMU_IRQDESC(ts_bat_cold),
MT6370_PMU_IRQDESC(ts_bat_cool),
MT6370_PMU_IRQDESC(ts_bat_warm),
MT6370_PMU_IRQDESC(ts_bat_hot),
MT6370_PMU_IRQDESC(chg_tmri),
MT6370_PMU_IRQDESC(chg_batabsi),
MT6370_PMU_IRQDESC(chg_adpbadi),
MT6370_PMU_IRQDESC(chg_rvpi),
MT6370_PMU_IRQDESC(otpi),
MT6370_PMU_IRQDESC(chg_aiclmeasi),
MT6370_PMU_IRQDESC(chg_ichgmeasi),
MT6370_PMU_IRQDESC(chgdet_donei),
MT6370_PMU_IRQDESC(chg_wdtmri),
MT6370_PMU_IRQDESC(ssfinishi),
MT6370_PMU_IRQDESC(chg_rechgi),
MT6370_PMU_IRQDESC(chg_termi),
MT6370_PMU_IRQDESC(chg_ieoci),
MT6370_PMU_IRQDESC(adc_donei),
MT6370_PMU_IRQDESC(pumpx_donei),
MT6370_PMU_IRQDESC(bst_batuvi),
MT6370_PMU_IRQDESC(bst_vbusovi),
MT6370_PMU_IRQDESC(bst_olpi),
MT6370_PMU_IRQDESC(attachi),
MT6370_PMU_IRQDESC(detachi),
MT6370_PMU_IRQDESC(qc30stpdone),
MT6370_PMU_IRQDESC(qc_vbusdet_done),
MT6370_PMU_IRQDESC(hvdcp_det),
MT6370_PMU_IRQDESC(chgdeti),
MT6370_PMU_IRQDESC(dcdti),
MT6370_PMU_IRQDESC(dirchg_vgoki),
MT6370_PMU_IRQDESC(dirchg_wdtmri),
MT6370_PMU_IRQDESC(dirchg_uci),
MT6370_PMU_IRQDESC(dirchg_oci),
MT6370_PMU_IRQDESC(dirchg_ovi),
MT6370_PMU_IRQDESC(ovpctrl_swon_evt),
MT6370_PMU_IRQDESC(ovpctrl_uvp_d_evt),
MT6370_PMU_IRQDESC(ovpctrl_uvp_evt),
MT6370_PMU_IRQDESC(ovpctrl_ovp_d_evt),
MT6370_PMU_IRQDESC(ovpctrl_ovp_evt),
};
static void mt6370_pmu_charger_irq_register(struct platform_device *pdev)
{
struct resource *res;
int i, ret = 0;
for (i = 0; i < ARRAY_SIZE(mt6370_chg_irq_desc); i++) {
if (!mt6370_chg_irq_desc[i].name)
continue;
res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
mt6370_chg_irq_desc[i].name);
if (!res)
continue;
ret = devm_request_threaded_irq(&pdev->dev, res->start, NULL,
mt6370_chg_irq_desc[i].irq_handler,
IRQF_TRIGGER_FALLING,
mt6370_chg_irq_desc[i].name,
platform_get_drvdata(pdev));
if (ret < 0) {
dev_err(&pdev->dev, "request %s irq fail\n", res->name);
continue;
}
mt6370_chg_irq_desc[i].irq = res->start;
}
}
static inline int mt_parse_dt(struct device *dev,
struct mt6370_pmu_charger_data *chg_data)
{
struct mt6370_pmu_charger_desc *chg_desc = NULL;
struct device_node *np = dev->of_node;
struct i2c_client *i2c = chg_data->chip->i2c;
dev_info(chg_data->dev, "%s\n", __func__);
chg_data->chg_desc = &mt6370_default_chg_desc;
chg_desc = devm_kzalloc(&i2c->dev,
sizeof(struct mt6370_pmu_charger_desc), GFP_KERNEL);
if (!chg_desc)
return -ENOMEM;
memcpy(chg_desc, &mt6370_default_chg_desc,
sizeof(struct mt6370_pmu_charger_desc));
if (of_property_read_string(np, "charger_name",
&(chg_data->mchr_info.name)) < 0) {
dev_err(chg_data->dev, "%s: no charger name\n", __func__);
chg_data->mchr_info.name = "mt6370_charger";
}
if (of_property_read_u32(np, "ichg", &chg_desc->ichg) < 0)
dev_err(chg_data->dev, "%s: no ichg\n", __func__);
if (of_property_read_u32(np, "aicr", &chg_desc->aicr) < 0)
dev_err(chg_data->dev, "%s: no aicr\n", __func__);
if (of_property_read_u32(np, "mivr", &chg_desc->mivr) < 0)
dev_err(chg_data->dev, "%s: no mivr\n", __func__);
if (of_property_read_u32(np, "cv", &chg_desc->cv) < 0)
dev_err(chg_data->dev, "%s: no cv\n", __func__);
if (of_property_read_u32(np, "ieoc", &chg_desc->ieoc) < 0)
dev_err(chg_data->dev, "%s: no ieoc\n", __func__);
if (of_property_read_u32(np, "safety_timer",
&chg_desc->safety_timer) < 0)
dev_err(chg_data->dev, "%s: no safety timer\n", __func__);
if (of_property_read_u32(np, "dc_wdt", &chg_desc->dc_wdt) < 0)
dev_err(chg_data->dev, "%s: no dc wdt\n", __func__);
if (of_property_read_u32(np, "ircmp_resistor",
&chg_desc->ircmp_resistor) < 0)
dev_err(chg_data->dev, "%s: no ircmp resistor\n", __func__);
if (of_property_read_u32(np, "ircmp_vclamp",
&chg_desc->ircmp_vclamp) < 0)
dev_err(chg_data->dev, "%s: no ircmp vclamp\n", __func__);
chg_desc->en_te = of_property_read_bool(np, "enable_te");
chg_desc->en_wdt = of_property_read_bool(np, "enable_wdt");
chg_desc->en_otg_wdt = of_property_read_bool(np, "enable_otg_wdt");
chg_data->chg_desc = chg_desc;
return 0;
}
static int mt6370_chg_init_setting(struct mt6370_pmu_charger_data *chg_data)
{
int ret = 0;
struct mt6370_pmu_charger_desc *chg_desc = chg_data->chg_desc;
dev_info(chg_data->dev, "%s\n", __func__);
/* Select IINLMTSEL to use AICR */
ret = mt6370_select_input_current_limit(chg_data,
MT6370_IINLMTSEL_AICR);
if (ret < 0)
dev_err(chg_data->dev, "%s: select iinlmtsel failed\n",
__func__);
mdelay(5);
/* Disable hardware ILIM */
ret = mt6370_enable_ilim(chg_data, false);
if (ret < 0)
dev_err(chg_data->dev, "%s: disable ilim failed\n", __func__);
ret = _mt6370_set_ichg(chg_data, chg_desc->ichg);
if (ret < 0)
dev_err(chg_data->dev, "%s: set ichg failed\n", __func__);
ret = _mt6370_set_aicr(chg_data, chg_desc->aicr);
if (ret < 0)
dev_err(chg_data->dev, "%s: set aicr failed\n", __func__);
ret = _mt6370_set_mivr(chg_data, chg_desc->mivr);
if (ret < 0)
dev_err(chg_data->dev, "%s: set mivr failed\n", __func__);
ret = _mt6370_set_cv(chg_data, chg_desc->cv);
if (ret < 0)
dev_err(chg_data->dev, "%s: set voreg failed\n", __func__);
ret = mt6370_set_ieoc(chg_data, chg_desc->ieoc);
if (ret < 0)
dev_err(chg_data->dev, "%s: set ieoc failed\n", __func__);
ret = mt6370_enable_te(chg_data, chg_desc->en_te);
if (ret < 0)
dev_err(chg_data->dev, "%s: set te failed\n", __func__);
ret = mt6370_set_fast_charge_timer(chg_data, chg_desc->safety_timer);
if (ret < 0)
dev_err(chg_data->dev, "%s: set fast timer failed\n", __func__);
ret = mt6370_set_dc_wdt(chg_data, chg_desc->dc_wdt);
if (ret < 0)
dev_err(chg_data->dev, "%s: set dc watch dog timer failed\n",
__func__);
ret = _mt6370_enable_safety_timer(chg_data, true);
if (ret < 0)
dev_err(chg_data->dev, "%s: enable charger timer failed\n",
__func__);
/* Initially disable WDT to prevent 1mA power consumption */
ret = mt6370_enable_wdt(chg_data, false);
if (ret < 0)
dev_err(chg_data->dev, "%s: disable watchdog timer failed\n",
__func__);
/* Disable JEITA */
ret = mt6370_enable_jeita(chg_data, false);
if (ret < 0)
dev_err(chg_data->dev, "%s: disable jeita failed\n", __func__);
/* Disable HZ */
ret = _mt6370_enable_hz(chg_data, false);
if (ret < 0)
dev_err(chg_data->dev, "%s: disable hz failed\n", __func__);
ret = _mt6370_set_ircmp_resistor(chg_data, chg_desc->ircmp_resistor);
if (ret < 0)
dev_err(chg_data->dev,
"%s: set IR compensation resistor failed\n", __func__);
ret = _mt6370_set_ircmp_vclamp(chg_data, chg_desc->ircmp_vclamp);
if (ret < 0)
dev_err(chg_data->dev,
"%s: set IR compensation vclamp failed\n", __func__);
/* Disable USB charger type detection */
ret = mt6370_enable_chgdet_flow(chg_data, false);
if (ret < 0)
dev_err(chg_data->dev,
"%s: disable usb chrdet failed\n", __func__);
return ret;
}
static const mtk_charger_intf mt6370_mchr_intf[CHARGING_CMD_NUMBER] = {
[CHARGING_CMD_INIT] = mt6370_hw_init,
[CHARGING_CMD_DUMP_REGISTER] = mt6370_dump_register,
[CHARGING_CMD_ENABLE] = mt6370_enable_charging,
[CHARGING_CMD_SET_HIZ_SWCHR] = mt6370_enable_hz,
[CHARGING_CMD_ENABLE_SAFETY_TIMER] = mt6370_enable_safety_timer,
[CHARGING_CMD_ENABLE_OTG] = mt6370_enable_otg,
[CHARGING_CMD_ENABLE_POWER_PATH] = mt6370_enable_power_path,
[CHARGING_CMD_ENABLE_DIRECT_CHARGE] = mt6370_enable_direct_charge,
[CHARGING_CMD_SET_CURRENT] = mt6370_set_ichg,
[CHARGING_CMD_SET_INPUT_CURRENT] = mt6370_set_aicr,
[CHARGING_CMD_SET_VINDPM] = mt6370_set_mivr,
[CHARGING_CMD_SET_CV_VOLTAGE] = mt6370_set_cv,
[CHARGING_CMD_SET_BOOST_CURRENT_LIMIT] = mt6370_set_otg_current_limit,
[CHARGING_CMD_SET_TA_CURRENT_PATTERN] = mt6370_set_pep_current_pattern,
[CHARGING_CMD_SET_TA20_RESET] = mt6370_set_pep20_reset,
[CHARGING_CMD_SET_TA20_CURRENT_PATTERN] =
mt6370_set_pep20_current_pattern,
[CHARGING_CMD_SET_ERROR_STATE] = mt6370_set_error_state,
[CHARGING_CMD_GET_CURRENT] = mt6370_get_ichg,
[CHARGING_CMD_GET_INPUT_CURRENT] = mt6370_get_aicr,
[CHARGING_CMD_GET_CHARGER_TEMPERATURE] = mt6370_get_tchg,
[CHARGING_CMD_GET_CHARGING_STATUS] = mt6370_is_charging_done,
[CHARGING_CMD_GET_IS_POWER_PATH_ENABLE] = mt6370_is_power_path_enable,
[CHARGING_CMD_GET_IS_SAFETY_TIMER_ENABLE] =
mt6370_is_safety_timer_enable,
[CHARGING_CMD_RESET_WATCH_DOG_TIMER] = mt6370_kick_wdt,
[CHARGING_CMD_GET_IBUS] = mt6370_get_ibus,
[CHARGING_CMD_GET_VBUS] = mt6370_get_vbus,
[CHARGING_CMD_RUN_AICL] = mt6370_run_aicl,
[CHARGING_CMD_RESET_DC_WATCH_DOG_TIMER] = mt6370_kick_dc_wdt,
[CHARGING_CMD_ENABLE_DC_VBUSOV] = mt6370_enable_dc_vbusov,
[CHARGING_CMD_SET_DC_VBUSOV] = mt6370_set_dc_vbusov,
[CHARGING_CMD_ENABLE_DC_VBUSOC] = mt6370_enable_dc_ibusoc,
[CHARGING_CMD_SET_DC_VBUSOC] = mt6370_set_dc_ibusoc,
[CHARGING_CMD_ENABLE_DC_VBATOV] = mt6370_enable_dc_vbatov,
[CHARGING_CMD_SET_DC_VBATOV] = mt6370_set_dc_vbatov,
[CHARGING_CMD_GET_IS_DC_ENABLE] = mt6370_is_dc_enable,
[CHARGING_CMD_SET_IRCMP_RESISTOR] = mt6370_set_ircmp_resistor,
[CHARGING_CMD_SET_IRCMP_VOLT_CLAMP] = mt6370_set_ircmp_vclamp,
[CHARGING_CMD_SET_PEP20_EFFICIENCY_TABLE] =
mt6370_set_pep20_efficiency_table,
[CHARGING_CMD_GET_CHARGER_TYPE] = mt6370_get_charger_type,
[CHARGING_CMD_ENABLE_CHR_TYPE_DET] = mt6370_enable_chg_type_det,
/*
* The following interfaces are not related to charger
* Define in mtk_charger_intf.c
*/
[CHARGING_CMD_SW_INIT] = mtk_charger_sw_init,
[CHARGING_CMD_SET_HV_THRESHOLD] = mtk_charger_set_hv_threshold,
[CHARGING_CMD_GET_HV_STATUS] = mtk_charger_get_hv_status,
[CHARGING_CMD_GET_BATTERY_STATUS] = mtk_charger_get_battery_status,
[CHARGING_CMD_GET_CHARGER_DET_STATUS] =
mtk_charger_get_charger_det_status,
[CHARGING_CMD_GET_IS_PCM_TIMER_TRIGGER] =
mtk_charger_get_is_pcm_timer_trigger,
[CHARGING_CMD_SET_PLATFORM_RESET] = mtk_charger_set_platform_reset,
[CHARGING_CMD_GET_PLATFORM_BOOT_MODE] =
mtk_charger_get_platform_boot_mode,
[CHARGING_CMD_SET_POWER_OFF] = mtk_charger_set_power_off,
[CHARGING_CMD_GET_POWER_SOURCE] = mtk_charger_get_power_source,
[CHARGING_CMD_GET_CSDAC_FALL_FLAG] = mtk_charger_get_csdac_full_flag,
[CHARGING_CMD_DISO_INIT] = mtk_charger_diso_init,
[CHARGING_CMD_GET_DISO_STATE] = mtk_charger_get_diso_state,
[CHARGING_CMD_SET_VBUS_OVP_EN] = mtk_charger_set_vbus_ovp_en,
[CHARGING_CMD_GET_BIF_VBAT] = mtk_charger_get_bif_vbat,
[CHARGING_CMD_SET_CHRIND_CK_PDN] = mtk_charger_set_chrind_ck_pdn,
[CHARGING_CMD_GET_BIF_TBAT] = mtk_charger_get_bif_tbat,
[CHARGING_CMD_SET_DP] = mtk_charger_set_dp,
[CHARGING_CMD_GET_BIF_IS_EXIST] = mtk_charger_get_bif_is_exist,
};
static int mt6370_pmu_charger_probe(struct platform_device *pdev)
{
int ret = 0;
struct mt6370_pmu_charger_data *chg_data;
bool use_dt = pdev->dev.of_node;
pr_info("%s: (%s)\n", __func__, MT6370_PMU_CHARGER_DRV_VERSION);
chg_data = devm_kzalloc(&pdev->dev, sizeof(*chg_data), GFP_KERNEL);
if (!chg_data)
return -ENOMEM;
mutex_init(&chg_data->adc_access_lock);
mutex_init(&chg_data->irq_access_lock);
mutex_init(&chg_data->aicr_access_lock);
mutex_init(&chg_data->ichg_access_lock);
mutex_init(&chg_data->bc12_access_lock);
chg_data->chip = dev_get_drvdata(pdev->dev.parent);
chg_data->dev = &pdev->dev;
chg_data->chg_type = CHARGER_UNKNOWN;
if (use_dt) {
ret = mt_parse_dt(&pdev->dev, chg_data);
if (ret < 0)
dev_err(chg_data->dev, "%s: parse dts failed\n",
__func__);
}
platform_set_drvdata(pdev, chg_data);
/* Init wait queue head */
init_waitqueue_head(&chg_data->wait_queue);
#ifndef CONFIG_TCPC_CLASS
INIT_WORK(&chg_data->chgdet_work, mt6370_chgdet_work_handler);
#endif
/* Do initial setting */
ret = mt6370_chg_init_setting(chg_data);
if (ret < 0) {
dev_err(chg_data->dev, "%s: sw init failed\n", __func__);
goto err_chg_init_setting;
}
/* SW workaround */
ret = mt6370_chg_sw_workaround(chg_data);
if (ret < 0) {
dev_err(chg_data->dev, "%s: software workaround failed\n",
__func__);
goto err_chg_sw_workaround;
}
mt6370_pmu_charger_irq_register(pdev);
chg_data->mchr_info.mchr_intf = mt6370_mchr_intf;
mtk_charger_set_info(&chg_data->mchr_info);
mt6370_dump_register(&chg_data->mchr_info, NULL);
/* Schedule work for microB's BC1.2 */
#ifndef CONFIG_TCPC_CLASS
schedule_work(&chg_data->chgdet_work);
#endif
dev_info(&pdev->dev, "%s successfully\n", __func__);
return ret;
err_chg_sw_workaround:
err_chg_init_setting:
mutex_destroy(&chg_data->ichg_access_lock);
mutex_destroy(&chg_data->adc_access_lock);
mutex_destroy(&chg_data->irq_access_lock);
mutex_destroy(&chg_data->aicr_access_lock);
mutex_destroy(&chg_data->bc12_access_lock);
return ret;
}
static int mt6370_pmu_charger_remove(struct platform_device *pdev)
{
struct mt6370_pmu_charger_data *chg_data = platform_get_drvdata(pdev);
if (chg_data) {
mutex_destroy(&chg_data->ichg_access_lock);
mutex_destroy(&chg_data->adc_access_lock);
mutex_destroy(&chg_data->irq_access_lock);
mutex_destroy(&chg_data->aicr_access_lock);
mutex_destroy(&chg_data->bc12_access_lock);
dev_info(chg_data->dev, "%s successfully\n", __func__);
}
return 0;
}
static const struct of_device_id mt_ofid_table[] = {
{ .compatible = "mediatek,mt6370_pmu_charger", },
{ },
};
MODULE_DEVICE_TABLE(of, mt_ofid_table);
static const struct platform_device_id mt_id_table[] = {
{ "mt6370_pmu_charger", 0},
{ },
};
MODULE_DEVICE_TABLE(platform, mt_id_table);
static struct platform_driver mt6370_pmu_charger = {
.driver = {
.name = "mt6370_pmu_charger",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(mt_ofid_table),
},
.probe = mt6370_pmu_charger_probe,
.remove = mt6370_pmu_charger_remove,
.id_table = mt_id_table,
};
module_platform_driver(mt6370_pmu_charger);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MediaTek MT6370 PMU Charger");
MODULE_VERSION(MT6370_PMU_CHARGER_DRV_VERSION);
/*
* Release Note
* 1.1.12_MTK
* (1) Enable charger before sending PE+/PE+20 pattern
* (2) Select to use reg AICR as input limit -> disable HW limit
*
* 1.1.11_MTK
* (1) Fix get_adc lock unbalance issue
* (2) Add a flag to check enable status of bc12
*
* 1.1.10_MTK
* (1) Add ext usb switch control
* (2) Add MT6370_APPLE_SAMSUNG_TA_SUPPORT config
* (3) Remove some debug log and unncessary code
*
* 1.1.9_MTK
* (1) Use polling mode for ADC done and PE+
* (2) Return immediately if usb is plugged out while waiting CDP
*
* 1.1.8_MTK
* (1) Read CHG_STAT in dump register
* (2) Read ZCV before disabling it and add ZCV ops
* (3) Add AEE log for ADC hang issue
*
* 1.1.7_MTK
* (1) Add a adc flag for adc_done IRQ
* (2) Not waitting ssfinish IRQ after enabling OTG
* (3) Add debug information for ADC
* (4) For degug, read ADC data even if conversation failed
* (5) Trigger any ADC before disabling ZCV
*
* 1.1.6_MTK
* (1) Read USB STATUS(0x27) instead of device type(0x22)
* to check charger type
* (2) Show CV value in dump_register
* (3) If PE20/30 is connected, do not run AICL
* (4) Disable MIVR IRQ -> enable direct charge
* Enable MIVR IRQ -> disable direct charge
*
* 1.1.5_MTK
* (1) Modify probe sequence
* (2) Change ADC log from ratelimited to normal one for debug
* (3) Polling ADC_START after receiving ADC_DONE irq
* (4) Disable ZCV in probe function
* (5) Plug out -> enable ILIM_EN
* Plug in -> sleep 1200ms -> disable ILIM_EN
*
* 1.1.4_MTK
* (1) Add IEOC workaround
*
* 1.1.3_MTK
* (1) Enable safety timer, rechg, vbusov, eoc IRQs
* and notify charger manager if event happens
* (2) Modify IBAT/IBUS ADC's coefficient
* (3) Change dev_dbg to dev_dbg_ratelimited
*
* 1.1.2_MTK
* (1) Enable charger in plug in callback
* (2) Enable power path -> Enable MIVR IRQ,
* Disable MIVR IRQ -> Disable power path
* (3) Change "PrimarySWCHG" to "primary_chg"
* Change "load_switch" to "primary_load_switch"
* (4) Check ADC_START bit if ADC_DONE IRQ timeout
*
* 1.1.1_MTK
* (1) Shorten enable to en
* (2) Enable WDT for charging/OTG mode, disable WDT when no cable plugged in
* (3) Use dev_ series instead pr_ series to dump log
* (4) Do AICL in a workqueue after receiving MIVR IRQ
*
* 1.1.0_MTK
* (1) Add a load switch device to support PE30
*
* 1.0.9_MTK
* (1) Report charger online to charger type detection
* (2) Use MIVR to enable/disable power path
* (3) Release PE+20 efficiency table interface
* (4) For ovpctrl_uvp, read uvp status to decide it is plug-in or plug-out
*
* 1.0.8_MTK
* (1) Release mt6370_enable_discharge interface
* discharge/OTG is controlled by PD
*
* 1.0.7_MTK
* (1) Adapt to GM30
*
* 1.0.6_MTK
* (1) Use wait queue instead of mdelay for waiting interrupt event
*
* 1.0.5_MTK
* (1) Modify USB charger type detecion flow
* Follow notifier from TypeC if CONFIG_TCPC_CLASS is defined
* (2) Add WDT timeout interrupt and kick watchdog in irq handler
*
* 1.0.4_MTK
* (1) Add USB charger type detection
*
* 1.0.3_MTK
* (1) Remove reset chip operation in probe function
*
* 1.0.2_MTK
* (1) For normal boot, set IPREC to 850mA to prevent Isys drop
* (2) Modify naming rules of functions
*
* 1.0.0_MTK
* (1) Initial Release
*/