4522 lines
114 KiB
C
4522 lines
114 KiB
C
|
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||
|
|
/*
|
||
|
|
* Copyright (c) 2021 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/power_supply.h>
|
||
|
|
#include <linux/workqueue.h>
|
||
|
|
#include <linux/math64.h>
|
||
|
|
|
||
|
|
#include <mt-plat/upmu_common.h>
|
||
|
|
#include <mt-plat/v1/charger_type.h>
|
||
|
|
#include <mt-plat/mtk_boot.h>
|
||
|
|
#include <mtk_charger_intf.h>
|
||
|
|
|
||
|
|
#include "inc/mt6370_pmu_fled.h"
|
||
|
|
#include "inc/mt6370_pmu_charger.h"
|
||
|
|
#include "inc/mt6370_pmu.h"
|
||
|
|
|
||
|
|
#define MT6370_PMU_CHARGER_DRV_VERSION "1.1.30_MTK"
|
||
|
|
|
||
|
|
struct tag_bootmode {
|
||
|
|
u32 size;
|
||
|
|
u32 tag;
|
||
|
|
u32 bootmode;
|
||
|
|
u32 boottype;
|
||
|
|
};
|
||
|
|
|
||
|
|
static bool dbg_log_en;
|
||
|
|
module_param(dbg_log_en, bool, 0644);
|
||
|
|
|
||
|
|
/* ======================= */
|
||
|
|
/* 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;
|
||
|
|
u32 lbp_hys_sel;
|
||
|
|
u32 lbp_dt;
|
||
|
|
bool en_te;
|
||
|
|
bool en_wdt;
|
||
|
|
bool en_otg_wdt;
|
||
|
|
bool en_polling;
|
||
|
|
bool disable_vlgc;
|
||
|
|
bool fast_unknown_ta_dect;
|
||
|
|
bool post_aicl;
|
||
|
|
const char *chg_dev_name;
|
||
|
|
const char *ls_dev_name;
|
||
|
|
};
|
||
|
|
|
||
|
|
struct mt6370_pmu_charger_data {
|
||
|
|
struct mt6370_pmu_charger_desc *chg_desc;
|
||
|
|
struct mt6370_pmu_chip *chip;
|
||
|
|
struct charger_device *chg_dev;
|
||
|
|
struct charger_device *ls_dev;
|
||
|
|
struct charger_properties chg_props;
|
||
|
|
struct charger_properties ls_props;
|
||
|
|
struct mutex adc_access_lock;
|
||
|
|
struct mutex irq_access_lock;
|
||
|
|
struct mutex aicr_access_lock;
|
||
|
|
struct mutex ichg_access_lock;
|
||
|
|
struct mutex pe_access_lock;
|
||
|
|
struct mutex bc12_access_lock;
|
||
|
|
struct mutex hidden_mode_lock;
|
||
|
|
struct mutex ieoc_lock;
|
||
|
|
struct mutex tchg_lock;
|
||
|
|
struct mutex pp_lock;
|
||
|
|
struct device *dev;
|
||
|
|
struct power_supply *psy;
|
||
|
|
wait_queue_head_t wait_queue;
|
||
|
|
enum charger_type chg_type;
|
||
|
|
bool pwr_rdy;
|
||
|
|
u8 irq_flag[MT6370_CHG_IRQIDX_MAX];
|
||
|
|
int aicr_limit;
|
||
|
|
u32 zcv;
|
||
|
|
bool adc_hang;
|
||
|
|
bool bc12_en;
|
||
|
|
u32 hidden_mode_cnt;
|
||
|
|
u32 ieoc;
|
||
|
|
u32 ichg;
|
||
|
|
u32 ichg_dis_chg;
|
||
|
|
u32 mivr;
|
||
|
|
bool ieoc_wkard;
|
||
|
|
bool dcd_timeout;
|
||
|
|
atomic_t bc12_cnt;
|
||
|
|
atomic_t bc12_wkard;
|
||
|
|
int tchg;
|
||
|
|
#ifdef CONFIG_TCPC_CLASS
|
||
|
|
atomic_t tcpc_usb_connected;
|
||
|
|
#else
|
||
|
|
struct work_struct chgdet_work;
|
||
|
|
#endif /* CONFIG_TCPC_CLASS */
|
||
|
|
struct delayed_work mivr_dwork;
|
||
|
|
|
||
|
|
bool pp_en;
|
||
|
|
};
|
||
|
|
|
||
|
|
/* 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,
|
||
|
|
.en_polling = false,
|
||
|
|
.post_aicl = true,
|
||
|
|
.chg_dev_name = "primary_chg",
|
||
|
|
.ls_dev_name = "primary_load_switch",
|
||
|
|
};
|
||
|
|
|
||
|
|
|
||
|
|
static const u32 mt6370_otg_oc_threshold[] = {
|
||
|
|
500000, 700000, 1100000, 1300000, 1800000, 2100000, 2400000, 3000000,
|
||
|
|
}; /* uA */
|
||
|
|
|
||
|
|
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_IINLMTSEL_AICR_3250 = 0,
|
||
|
|
MT6370_IINLMTSEL_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 charger_device *chg_dev, u32 uA);
|
||
|
|
static int mt6370_get_aicr(struct charger_device *chg_dev, u32 *uA);
|
||
|
|
static int mt6370_set_ichg(struct charger_device *chg_dev, u32 uA);
|
||
|
|
static int mt6370_get_ichg(struct charger_device *chg_dev, u32 *uA);
|
||
|
|
static int mt6370_enable_charging(struct charger_device *chg_dev, bool en);
|
||
|
|
#ifdef CONFIG_MT6370_PMU_CHARGER_TYPE_DETECT
|
||
|
|
static int mt6370_inform_psy_changed(struct mt6370_pmu_charger_data *chg_data);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
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 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 inline void mt6370_enable_irq(struct mt6370_pmu_charger_data *chg_data,
|
||
|
|
const char *name, bool en)
|
||
|
|
{
|
||
|
|
struct resource *res = NULL;
|
||
|
|
struct platform_device *pdev = to_platform_device(chg_data->dev);
|
||
|
|
|
||
|
|
dev_info(chg_data->dev, "%s: (%s) en = %d", __func__, name, en);
|
||
|
|
|
||
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, name);
|
||
|
|
if (res)
|
||
|
|
(en ? enable_irq : disable_irq_nosync)(res->start);
|
||
|
|
else
|
||
|
|
dev_err(chg_data->dev, "%s: get plat res fail\n", __func__);
|
||
|
|
}
|
||
|
|
|
||
|
|
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_enable_hidden_mode(struct mt6370_pmu_charger_data *chg_data,
|
||
|
|
bool en)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
mutex_lock(&chg_data->hidden_mode_lock);
|
||
|
|
|
||
|
|
if (en) {
|
||
|
|
if (chg_data->hidden_mode_cnt == 0) {
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
chg_data->hidden_mode_cnt++;
|
||
|
|
} else {
|
||
|
|
if (chg_data->hidden_mode_cnt == 1) /* last one */
|
||
|
|
ret = mt6370_pmu_reg_write(chg_data->chip,
|
||
|
|
mt6370_reg_en_hidden_mode[0], 0x00);
|
||
|
|
chg_data->hidden_mode_cnt--;
|
||
|
|
if (ret < 0)
|
||
|
|
goto err;
|
||
|
|
}
|
||
|
|
mt_dbg(chg_data->dev, "%s: en = %d\n", __func__, en);
|
||
|
|
goto out;
|
||
|
|
|
||
|
|
err:
|
||
|
|
dev_err(chg_data->dev, "%s: en = %d fail(%d)\n", __func__, en, ret);
|
||
|
|
out:
|
||
|
|
mutex_unlock(&chg_data->hidden_mode_lock);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void diff(struct mt6370_pmu_charger_data *chg_data, int index,
|
||
|
|
struct timespec start, struct timespec end)
|
||
|
|
{
|
||
|
|
struct timespec temp;
|
||
|
|
|
||
|
|
temp = timespec_sub(end, start);
|
||
|
|
if (temp.tv_sec > 0) {
|
||
|
|
/* BUG_ON(1); */
|
||
|
|
dev_info(chg_data->dev, "%s: duration[%d] %d %ld\n", __func__,
|
||
|
|
index, (int)temp.tv_sec, temp.tv_nsec);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
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;
|
||
|
|
struct timespec time0, time1, time2;
|
||
|
|
|
||
|
|
time0.tv_sec = 0; time0.tv_nsec = 0;
|
||
|
|
time1.tv_sec = 0; time1.tv_nsec = 0;
|
||
|
|
time2.tv_sec = 0; time2.tv_nsec = 0;
|
||
|
|
|
||
|
|
get_monotonic_boottime(&time0);
|
||
|
|
mutex_lock(&chg_data->adc_access_lock);
|
||
|
|
get_monotonic_boottime(&time1);
|
||
|
|
diff(chg_data, 1, time0, time1);
|
||
|
|
|
||
|
|
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
|
||
|
|
);
|
||
|
|
|
||
|
|
get_monotonic_boottime(&time2);
|
||
|
|
diff(chg_data, 2, time1, time2);
|
||
|
|
|
||
|
|
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->chg_dev, &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->chg_dev, &ichg);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_err(chg_data->dev, "%s: get ichg failed\n",
|
||
|
|
__func__);
|
||
|
|
goto out_unlock_all;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
get_monotonic_boottime(&time1);
|
||
|
|
diff(chg_data, 3, time2, time1);
|
||
|
|
|
||
|
|
/* 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;
|
||
|
|
}
|
||
|
|
|
||
|
|
get_monotonic_boottime(&time2);
|
||
|
|
diff(chg_data, 4, time1, time2);
|
||
|
|
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
|
||
|
|
get_monotonic_boottime(&time1);
|
||
|
|
diff(chg_data, 5, time2, time1);
|
||
|
|
|
||
|
|
if (i == max_wait_times) {
|
||
|
|
dev_err(chg_data->dev,
|
||
|
|
"%s: wait conversation failed, sel = %d, ret = %d\n",
|
||
|
|
__func__, adc_sel, ret);
|
||
|
|
|
||
|
|
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);
|
||
|
|
|
||
|
|
get_monotonic_boottime(&time2);
|
||
|
|
diff(chg_data, 6, time1, time2);
|
||
|
|
|
||
|
|
/* 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;
|
||
|
|
}
|
||
|
|
|
||
|
|
get_monotonic_boottime(&time1);
|
||
|
|
diff(chg_data, 7, time2, time1);
|
||
|
|
|
||
|
|
mt_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]);
|
||
|
|
|
||
|
|
mt_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 = ((s64)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 defined(__LP64__) || defined(_LP64)
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
#else
|
||
|
|
if (adc_sel == MT6370_ADC_IBUS) {
|
||
|
|
if (aicr < 400000) /* 400mA */
|
||
|
|
adc_result = div_s64(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 = div_s64(adc_result * 475, 1000);
|
||
|
|
else if (ichg >= 500000 && ichg <= 850000) /* 500~850mA */
|
||
|
|
adc_result = div_s64(adc_result * 536, 1000);
|
||
|
|
mutex_unlock(&chg_data->ichg_access_lock);
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
out:
|
||
|
|
*adc_val = adc_result;
|
||
|
|
mt6370_enable_hidden_mode(chg_data, false);
|
||
|
|
mutex_unlock(&chg_data->adc_access_lock);
|
||
|
|
|
||
|
|
get_monotonic_boottime(&time2);
|
||
|
|
diff(chg_data, 8, time0, time2);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifndef CONFIG_MT6370_DCDTOUT_SUPPORT
|
||
|
|
static int __maybe_unused mt6370_enable_dcd_tout(
|
||
|
|
struct mt6370_pmu_charger_data *chg_data, bool en)
|
||
|
|
{
|
||
|
|
dev_info(chg_data->dev, "%s en = %d\n", __func__, en);
|
||
|
|
return (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit)
|
||
|
|
(chg_data->chip, MT6370_PMU_REG_DEVICETYPE,
|
||
|
|
MT6370_MASK_DCDTOUTEN);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int __maybe_unused mt6370_is_dcd_tout_enable(
|
||
|
|
struct mt6370_pmu_charger_data *chg_data, bool *en)
|
||
|
|
{
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
ret = mt6370_pmu_reg_read(chg_data->chip, MT6370_PMU_REG_DEVICETYPE);
|
||
|
|
if (ret < 0) {
|
||
|
|
*en = false;
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
*en = (ret & MT6370_MASK_DCDTOUTEN ? true : false);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
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 (state == MT6370_USBSW_CHG)
|
||
|
|
Charger_Detect_Init();
|
||
|
|
else
|
||
|
|
Charger_Detect_Release();
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int __maybe_unused __mt6370_enable_chgdet_flow(
|
||
|
|
struct mt6370_pmu_charger_data *chg_data, bool en)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
enum mt6370_usbsw_state usbsw =
|
||
|
|
en ? MT6370_USBSW_CHG : MT6370_USBSW_USB;
|
||
|
|
|
||
|
|
dev_info(chg_data->dev, "%s: en = %d\n", __func__, en);
|
||
|
|
mt6370_set_usbsw_state(chg_data, usbsw);
|
||
|
|
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;
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef CONFIG_MT6370_PMU_CHARGER_TYPE_DETECT
|
||
|
|
static int mt6370_inform_psy_changed(struct mt6370_pmu_charger_data *chg_data);
|
||
|
|
|
||
|
|
static int mt6370_enable_chgdet_flow(struct mt6370_pmu_charger_data *chg_data,
|
||
|
|
bool en)
|
||
|
|
{
|
||
|
|
int i, ret = 0;
|
||
|
|
#ifndef CONFIG_TCPC_CLASS
|
||
|
|
int vbus = 0;
|
||
|
|
#endif /* !CONFIG_TCPC_CLASS */
|
||
|
|
const int max_wait_cnt = 250;
|
||
|
|
#ifndef CONFIG_MT6370_DCDTOUT_SUPPORT
|
||
|
|
bool dcd_en = false;
|
||
|
|
#endif /* CONFIG_MT6370_DCDTOUT_SUPPORT */
|
||
|
|
|
||
|
|
struct device *dev = NULL;
|
||
|
|
struct device_node *boot_node = NULL;
|
||
|
|
struct tag_bootmode *tag = NULL;
|
||
|
|
int boot_mode = 11;//UNKNOWN_BOOT
|
||
|
|
// workaround for mt6768
|
||
|
|
dev = chg_data->dev;
|
||
|
|
if (dev != NULL){
|
||
|
|
boot_node = of_parse_phandle(dev->of_node, "bootmode", 0);
|
||
|
|
if (!boot_node){
|
||
|
|
chr_err("%s: failed to get boot mode phandle\n", __func__);
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
tag = (struct tag_bootmode *)of_get_property(boot_node,
|
||
|
|
"atag,boot", NULL);
|
||
|
|
if (!tag){
|
||
|
|
chr_err("%s: failed to get atag,boot\n", __func__);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
boot_mode = tag->bootmode;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (en && (boot_mode == 1)) {
|
||
|
|
/* Skip charger type detection to speed up meta boot.*/
|
||
|
|
dev_notice(chg_data->dev, "force Standard USB Host in meta\n");
|
||
|
|
chg_data->pwr_rdy = true;
|
||
|
|
chg_data->chg_type = STANDARD_HOST;
|
||
|
|
mt6370_inform_psy_changed(chg_data);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (en) {
|
||
|
|
#ifndef CONFIG_MT6370_DCDTOUT_SUPPORT
|
||
|
|
ret = mt6370_is_dcd_tout_enable(chg_data, &dcd_en);
|
||
|
|
if (!dcd_en)
|
||
|
|
msleep(180);
|
||
|
|
#endif /* CONFIG_MT6370_DCDTOUT_SUPPORT */
|
||
|
|
/* Workaround for CDP port */
|
||
|
|
for (i = 0; i < max_wait_cnt; i++) {
|
||
|
|
if (is_usb_rdy())
|
||
|
|
break;
|
||
|
|
dev_info(chg_data->dev, "%s: CDP block\n", __func__);
|
||
|
|
#ifndef 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;
|
||
|
|
}
|
||
|
|
#else
|
||
|
|
if (!atomic_read(&chg_data->tcpc_usb_connected)) {
|
||
|
|
dev_info(chg_data->dev,
|
||
|
|
"%s: plug out\n", __func__);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
#endif /* !CONFIG_TCPC_CLASS */
|
||
|
|
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__);
|
||
|
|
}
|
||
|
|
|
||
|
|
mutex_lock(&chg_data->bc12_access_lock);
|
||
|
|
ret = __mt6370_enable_chgdet_flow(chg_data, en);
|
||
|
|
mutex_unlock(&chg_data->bc12_access_lock);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_inform_psy_changed(struct mt6370_pmu_charger_data *chg_data)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
union power_supply_propval propval;
|
||
|
|
|
||
|
|
dev_info(chg_data->dev, "%s: pwr_rdy = %d, type = %d\n", __func__,
|
||
|
|
chg_data->pwr_rdy, chg_data->chg_type);
|
||
|
|
|
||
|
|
/* Get chg type det power supply */
|
||
|
|
if (!chg_data->psy)
|
||
|
|
chg_data->psy = power_supply_get_by_name("charger");
|
||
|
|
if (!chg_data->psy) {
|
||
|
|
dev_notice(chg_data->dev, "%s: get power supply failed\n",
|
||
|
|
__func__);
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Inform chg det power supply */
|
||
|
|
propval.intval = chg_data->pwr_rdy;
|
||
|
|
ret = power_supply_set_property(chg_data->psy, POWER_SUPPLY_PROP_ONLINE,
|
||
|
|
&propval);
|
||
|
|
if (ret < 0)
|
||
|
|
dev_err(chg_data->dev, "%s: psy online failed, ret = %d\n",
|
||
|
|
__func__, ret);
|
||
|
|
|
||
|
|
propval.intval = chg_data->chg_type;
|
||
|
|
ret = power_supply_set_property(chg_data->psy,
|
||
|
|
POWER_SUPPLY_PROP_CHARGE_TYPE, &propval);
|
||
|
|
if (ret < 0)
|
||
|
|
dev_err(chg_data->dev, "%s: psy type failed, ret = %d\n",
|
||
|
|
__func__, ret);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline int mt6370_toggle_chgdet_flow(
|
||
|
|
struct mt6370_pmu_charger_data *chg_data)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
u8 data = 0;
|
||
|
|
|
||
|
|
/* read data */
|
||
|
|
ret = i2c_smbus_read_i2c_block_data(chg_data->chip->i2c,
|
||
|
|
MT6370_PMU_REG_DEVICETYPE, 1, &data);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_err(chg_data->dev, "%s: read usbd fail\n", __func__);
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* usbd off */
|
||
|
|
data &= ~MT6370_MASK_USBCHGEN;
|
||
|
|
ret = i2c_smbus_write_i2c_block_data(chg_data->chip->i2c,
|
||
|
|
MT6370_PMU_REG_DEVICETYPE, 1, &data);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_err(chg_data->dev, "%s: usbd off fail\n", __func__);
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
udelay(40);
|
||
|
|
|
||
|
|
/* usbd on */
|
||
|
|
data |= MT6370_MASK_USBCHGEN;
|
||
|
|
ret = i2c_smbus_write_i2c_block_data(chg_data->chip->i2c,
|
||
|
|
MT6370_PMU_REG_DEVICETYPE, 1, &data);
|
||
|
|
if (ret < 0)
|
||
|
|
dev_err(chg_data->dev, "%s: usbd on fail\n", __func__);
|
||
|
|
out:
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_bc12_workaround(struct mt6370_pmu_charger_data *chg_data)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
dev_info(chg_data->dev, "%s\n", __func__);
|
||
|
|
|
||
|
|
mutex_lock(&chg_data->chip->io_lock);
|
||
|
|
|
||
|
|
ret = mt6370_toggle_chgdet_flow(chg_data);
|
||
|
|
if (ret < 0)
|
||
|
|
goto err;
|
||
|
|
|
||
|
|
mdelay(10);
|
||
|
|
|
||
|
|
ret = mt6370_toggle_chgdet_flow(chg_data);
|
||
|
|
if (ret < 0)
|
||
|
|
goto err;
|
||
|
|
|
||
|
|
goto out;
|
||
|
|
err:
|
||
|
|
dev_err(chg_data->dev, "%s: fail\n", __func__);
|
||
|
|
out:
|
||
|
|
mutex_unlock(&chg_data->chip->io_lock);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int __mt6370_chgdet_handler(struct mt6370_pmu_charger_data *chg_data)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
bool pwr_rdy = false, inform_psy = true;
|
||
|
|
u8 usb_status = 0, chip_vid = chg_data->chip->chip_vid;
|
||
|
|
|
||
|
|
dev_info(chg_data->dev, "%s\n", __func__);
|
||
|
|
|
||
|
|
#ifdef CONFIG_TCPC_CLASS
|
||
|
|
pwr_rdy = atomic_read(&chg_data->tcpc_usb_connected);
|
||
|
|
#else
|
||
|
|
/* Check UVP_D_STAT & OTG mode */
|
||
|
|
ret = mt6370_pmu_reg_test_bit(chg_data->chip,
|
||
|
|
MT6370_PMU_REG_OVPCTRLSTAT,
|
||
|
|
MT6370_SHIFT_OVPCTRL_UVP_D_STAT, &pwr_rdy);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_err(chg_data->dev, "%s: read uvp_d_stat fail\n", __func__);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
pwr_rdy = !pwr_rdy;
|
||
|
|
#endif
|
||
|
|
if (chg_data->pwr_rdy == pwr_rdy &&
|
||
|
|
atomic_read(&chg_data->bc12_wkard) == 0) {
|
||
|
|
dev_info(chg_data->dev, "%s: pwr rdy(%d) is the same\n",
|
||
|
|
__func__, pwr_rdy);
|
||
|
|
if (!pwr_rdy) {
|
||
|
|
inform_psy = false;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
chg_data->pwr_rdy = pwr_rdy;
|
||
|
|
|
||
|
|
/* plug out */
|
||
|
|
if (!pwr_rdy) {
|
||
|
|
chg_data->chg_type = CHARGER_UNKNOWN;
|
||
|
|
atomic_set(&chg_data->bc12_cnt, 0);
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
atomic_inc(&chg_data->bc12_cnt);
|
||
|
|
|
||
|
|
/* plug in */
|
||
|
|
if (chg_data->dcd_timeout) {
|
||
|
|
chg_data->chg_type = NONSTANDARD_CHARGER;
|
||
|
|
chg_data->dcd_timeout = false;
|
||
|
|
goto dcd_timeout;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = mt6370_pmu_reg_read(chg_data->chip, MT6370_PMU_REG_USBSTATUS1);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_err(chg_data->dev, "%s: read chg type fail\n", __func__);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
usb_status = (ret & MT6370_MASK_USB_STATUS) >> MT6370_SHIFT_USB_STATUS;
|
||
|
|
|
||
|
|
switch (usb_status) {
|
||
|
|
case MT6370_CHG_TYPE_UNDER_GOING:
|
||
|
|
dev_info(chg_data->dev, "%s: under going...\n", __func__);
|
||
|
|
return ret;
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* BC12 workaround (NONSTD -> STD) */
|
||
|
|
if (atomic_read(&chg_data->bc12_cnt) < 3 &&
|
||
|
|
chg_data->chg_type == STANDARD_HOST &&
|
||
|
|
(chip_vid == RT5081_VENDOR_ID ||
|
||
|
|
chip_vid == MT6370_VENDOR_ID)) {
|
||
|
|
ret = mt6370_bc12_workaround(chg_data);
|
||
|
|
/* Workaround success, wait for next event */
|
||
|
|
if (ret >= 0) {
|
||
|
|
atomic_set(&chg_data->bc12_wkard, 1);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
#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 fail(%d)\n",
|
||
|
|
__func__, ret);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
out:
|
||
|
|
atomic_set(&chg_data->bc12_wkard, 0);
|
||
|
|
|
||
|
|
dcd_timeout:
|
||
|
|
/* Turn off USB charger detection */
|
||
|
|
if (chg_data->chg_type != STANDARD_CHARGER) {
|
||
|
|
ret = __mt6370_enable_chgdet_flow(chg_data, false);
|
||
|
|
if (ret < 0)
|
||
|
|
dev_notice(chg_data->dev, "%s: disable chgdet fail\n",
|
||
|
|
__func__);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (inform_psy)
|
||
|
|
mt6370_inform_psy_changed(chg_data);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_chgdet_handler(struct mt6370_pmu_charger_data *chg_data)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
mutex_lock(&chg_data->bc12_access_lock);
|
||
|
|
ret = __mt6370_chgdet_handler(chg_data);
|
||
|
|
mutex_unlock(&chg_data->bc12_access_lock);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
#endif /* CONFIG_MT6370_PMU_CHARGER_TYPE_DETECT */
|
||
|
|
|
||
|
|
/* 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;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 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;
|
||
|
|
}
|
||
|
|
|
||
|
|
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__);
|
||
|
|
|
||
|
|
mt6370_enable_hidden_mode(chg_data, true);
|
||
|
|
|
||
|
|
/* 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);
|
||
|
|
|
||
|
|
/* Disable SEN_DCP for charging mode */
|
||
|
|
ret = mt6370_pmu_reg_clr_bit(chg_data->chip,
|
||
|
|
MT6370_PMU_REG_QCCTRL2, MT6370_MASK_EN_DCP);
|
||
|
|
|
||
|
|
mt6370_enable_hidden_mode(chg_data, false);
|
||
|
|
|
||
|
|
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;
|
||
|
|
|
||
|
|
dev_info(chg_data->dev, "%s: en = %d\n", __func__, en);
|
||
|
|
|
||
|
|
ret = mt6370_set_aicr(chg_data->chg_dev, 800000);
|
||
|
|
if (ret < 0)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
ret = mt6370_set_ichg(chg_data->chg_dev, 2000000);
|
||
|
|
if (ret < 0)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
ret = mt6370_enable_charging(chg_data->chg_dev, true);
|
||
|
|
if (ret < 0)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
mt6370_enable_hidden_mode(chg_data, true);
|
||
|
|
|
||
|
|
ret = mt6370_pmu_reg_clr_bit(chg_data->chip,
|
||
|
|
MT6370_PMU_REG_CHGHIDDENCTRL9, 0x80);
|
||
|
|
if (ret < 0)
|
||
|
|
dev_err(chg_data->dev, "%s: disable psk mode fail\n", __func__);
|
||
|
|
|
||
|
|
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)
|
||
|
|
goto out;
|
||
|
|
|
||
|
|
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;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
ret = 0;
|
||
|
|
out:
|
||
|
|
mt6370_pmu_reg_set_bit(chg_data->chip, MT6370_PMU_REG_CHGHIDDENCTRL9,
|
||
|
|
0x80);
|
||
|
|
mt6370_enable_hidden_mode(chg_data, false);
|
||
|
|
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;
|
||
|
|
*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;
|
||
|
|
u8 reg_ieoc = 0;
|
||
|
|
|
||
|
|
/* IEOC workaround */
|
||
|
|
if (chg_data->ieoc_wkard)
|
||
|
|
ieoc += 100000; /* 100mA */
|
||
|
|
|
||
|
|
/* Find corresponding reg value */
|
||
|
|
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
|
||
|
|
);
|
||
|
|
if (ret < 0)
|
||
|
|
dev_err(chg_data->dev, "%s: set ieoc fail\n", __func__);
|
||
|
|
|
||
|
|
/* Store IEOC */
|
||
|
|
ret = mt6370_get_ieoc(chg_data, &chg_data->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_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;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
static int __mt6370_set_aicr(struct mt6370_pmu_charger_data *chg_data, u32 uA)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
u8 reg_aicr = 0;
|
||
|
|
|
||
|
|
/* Find corresponding reg value */
|
||
|
|
reg_aicr = mt6370_find_closest_reg_value(
|
||
|
|
MT6370_AICR_MIN,
|
||
|
|
MT6370_AICR_MAX,
|
||
|
|
MT6370_AICR_STEP,
|
||
|
|
MT6370_AICR_NUM,
|
||
|
|
uA
|
||
|
|
);
|
||
|
|
|
||
|
|
mt_dbg(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
|
||
|
|
);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline int mt6370_post_aicl_measure(struct charger_device *chg_dev,
|
||
|
|
u32 start, u32 stop, u32 step,
|
||
|
|
u32 *measure)
|
||
|
|
{
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
int cur, ret;
|
||
|
|
|
||
|
|
mt_dbg(chg_data->dev,
|
||
|
|
"%s: post_aicc = (%d, %d, %d)\n", __func__, start, stop, step);
|
||
|
|
for (cur = start; cur < stop; cur += step) {
|
||
|
|
/* set_aicr to cur */
|
||
|
|
ret = __mt6370_set_aicr(chg_data, cur + step);
|
||
|
|
if (ret < 0)
|
||
|
|
return ret;
|
||
|
|
usleep_range(150, 200);
|
||
|
|
ret = mt6370_pmu_reg_read(chg_data->chip,
|
||
|
|
MT6370_PMU_REG_CHGSTAT1);
|
||
|
|
if (ret < 0)
|
||
|
|
return ret;
|
||
|
|
/* read mivr stat */
|
||
|
|
if (ret & MT6370_MASK_CHG_MIVR)
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
if (cur > stop)
|
||
|
|
cur = stop;
|
||
|
|
*measure = cur;
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int __mt6370_run_aicl(struct mt6370_pmu_charger_data *chg_data)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
u32 mivr = 0, aicl_vth = 0, aicr = 0;
|
||
|
|
bool mivr_stat = false;
|
||
|
|
|
||
|
|
mt_dbg(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) {
|
||
|
|
mt_dbg(chg_data->dev, "%s: mivr stat not act\n", __func__);
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = __mt6370_get_mivr(chg_data, &mivr);
|
||
|
|
if (ret < 0)
|
||
|
|
goto out;
|
||
|
|
|
||
|
|
/* Check if there's a suitable AICL_VTH */
|
||
|
|
aicl_vth = mivr + 200000;
|
||
|
|
if (aicl_vth > MT6370_AICL_VTH_MAX) {
|
||
|
|
dev_info(chg_data->dev, "%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);
|
||
|
|
|
||
|
|
mutex_lock(&chg_data->pe_access_lock);
|
||
|
|
mutex_lock(&chg_data->aicr_access_lock);
|
||
|
|
|
||
|
|
ret = mt6370_pmu_reg_set_bit(chg_data->chip, MT6370_PMU_REG_CHGCTRL14,
|
||
|
|
MT6370_MASK_AICL_MEAS);
|
||
|
|
if (ret < 0)
|
||
|
|
goto unlock_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) {
|
||
|
|
dev_err(chg_data->dev, "%s: wait AICL time out, ret = %d\n",
|
||
|
|
__func__, ret);
|
||
|
|
ret = -EIO;
|
||
|
|
goto unlock_out;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = mt6370_get_aicr(chg_data->chg_dev, &aicr);
|
||
|
|
if (ret < 0)
|
||
|
|
goto unlock_out;
|
||
|
|
|
||
|
|
if (chg_data->chg_desc->post_aicl == false)
|
||
|
|
goto skip_post_aicl;
|
||
|
|
|
||
|
|
dev_info(chg_data->dev, "%s: aicc pre val = %d\n", __func__, aicr);
|
||
|
|
/* always start/end aicc_val/aicc_val+200mA */
|
||
|
|
ret = mt6370_post_aicl_measure(chg_data->chg_dev, aicr,
|
||
|
|
aicr + 200000, 50000, &aicr);
|
||
|
|
if (ret < 0)
|
||
|
|
goto out;
|
||
|
|
dev_info(chg_data->dev, "%s: aicc post val = %d\n", __func__, aicr);
|
||
|
|
|
||
|
|
skip_post_aicl:
|
||
|
|
chg_data->aicr_limit = aicr;
|
||
|
|
dev_info(chg_data->dev, "%s: OK, aicr upper bound = %dmA\n", __func__,
|
||
|
|
aicr / 1000);
|
||
|
|
|
||
|
|
unlock_out:
|
||
|
|
mutex_unlock(&chg_data->aicr_access_lock);
|
||
|
|
mutex_unlock(&chg_data->pe_access_lock);
|
||
|
|
out:
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
#if defined(CONFIG_MT6370_PMU_CHARGER_TYPE_DETECT)\
|
||
|
|
&& !defined(CONFIG_TCPC_CLASS)
|
||
|
|
static void mt6370_chgdet_work_handler(struct work_struct *work)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
bool uvp_d = false, otg_mode = false;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
(struct mt6370_pmu_charger_data *)container_of(work,
|
||
|
|
struct mt6370_pmu_charger_data, chgdet_work);
|
||
|
|
|
||
|
|
/* Check UVP_D_STAT & OTG mode */
|
||
|
|
ret = mt6370_pmu_reg_test_bit(chg_data->chip,
|
||
|
|
MT6370_PMU_REG_OVPCTRLSTAT, MT6370_SHIFT_OVPCTRL_UVP_D_STAT,
|
||
|
|
&uvp_d);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_err(chg_data->dev, "%s: read uvp_d_stat fail\n", __func__);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* power not good */
|
||
|
|
if (uvp_d)
|
||
|
|
return;
|
||
|
|
|
||
|
|
/* power good */
|
||
|
|
ret = mt6370_pmu_reg_test_bit(chg_data->chip,
|
||
|
|
MT6370_PMU_REG_CHGCTRL1, MT6370_SHIFT_OPA_MODE,
|
||
|
|
&otg_mode);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_err(chg_data->dev, "%s: read otg mode fail\n", __func__);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* In OTG mode skip this event */
|
||
|
|
if (otg_mode) {
|
||
|
|
dev_info(chg_data->dev, "%s: triggered by OTG\n", __func__);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Turn on USB charger detection */
|
||
|
|
ret = mt6370_enable_chgdet_flow(chg_data, true);
|
||
|
|
if (ret < 0)
|
||
|
|
dev_err(chg_data->dev, "%s: en bc12 fail\n", __func__);
|
||
|
|
}
|
||
|
|
#endif /* CONFIG_MT6370_PMU_CHARGER_TYPE_DETECT && !CONFIG_TCPC_CLASS */
|
||
|
|
|
||
|
|
static int __mt6370_get_ichg(struct mt6370_pmu_charger_data *chg_data,
|
||
|
|
u32 *ichg)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
u8 reg_ichg = 0;
|
||
|
|
|
||
|
|
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);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static inline int mt6370_ichg_workaround(
|
||
|
|
struct mt6370_pmu_charger_data *chg_data, u32 uA)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
/* Vsys short protection */
|
||
|
|
mt6370_enable_hidden_mode(chg_data, true);
|
||
|
|
|
||
|
|
if (chg_data->ichg >= 900000 && uA < 900000)
|
||
|
|
ret = mt6370_pmu_reg_update_bits(chg_data->chip,
|
||
|
|
MT6370_PMU_REG_CHGHIDDENCTRL7, 0x60, 0x00);
|
||
|
|
else if (uA >= 900000 && chg_data->ichg < 900000)
|
||
|
|
ret = mt6370_pmu_reg_update_bits(chg_data->chip,
|
||
|
|
MT6370_PMU_REG_CHGHIDDENCTRL7, 0x60, 0x40);
|
||
|
|
|
||
|
|
mt6370_enable_hidden_mode(chg_data, false);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int __mt6370_set_ichg(struct mt6370_pmu_charger_data *chg_data, u32 uA)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
u8 reg_ichg = 0;
|
||
|
|
u8 chip_vid = chg_data->chip->chip_vid;
|
||
|
|
|
||
|
|
uA = (uA < 500000) ? 500000 : uA;
|
||
|
|
|
||
|
|
if (chip_vid == RT5081_VENDOR_ID || chip_vid == MT6370_VENDOR_ID) {
|
||
|
|
ret = mt6370_ichg_workaround(chg_data, uA);
|
||
|
|
if (ret < 0)
|
||
|
|
dev_info(chg_data->dev, "%s: workaround fail\n",
|
||
|
|
__func__);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Find corresponding reg value */
|
||
|
|
reg_ichg = mt6370_find_closest_reg_value(
|
||
|
|
MT6370_ICHG_MIN,
|
||
|
|
MT6370_ICHG_MAX,
|
||
|
|
MT6370_ICHG_STEP,
|
||
|
|
MT6370_ICHG_NUM,
|
||
|
|
uA
|
||
|
|
);
|
||
|
|
|
||
|
|
mt_dbg(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 (ret < 0)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* Store Ichg setting */
|
||
|
|
__mt6370_get_ichg(chg_data, &chg_data->ichg);
|
||
|
|
|
||
|
|
if (chip_vid != RT5081_VENDOR_ID && chip_vid != MT6370_VENDOR_ID)
|
||
|
|
goto bypass_ieoc_workaround;
|
||
|
|
/* Workaround to make IEOC accurate */
|
||
|
|
if (uA < 900000 && !chg_data->ieoc_wkard) { /* 900mA */
|
||
|
|
ret = __mt6370_set_ieoc(chg_data, chg_data->ieoc + 100000);
|
||
|
|
chg_data->ieoc_wkard = true;
|
||
|
|
} else if (uA >= 900000 && chg_data->ieoc_wkard) {
|
||
|
|
chg_data->ieoc_wkard = false;
|
||
|
|
ret = __mt6370_set_ieoc(chg_data, chg_data->ieoc - 100000);
|
||
|
|
}
|
||
|
|
|
||
|
|
bypass_ieoc_workaround:
|
||
|
|
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 */
|
||
|
|
/* =================== */
|
||
|
|
|
||
|
|
static int mt6370_enable_charging(struct charger_device *chg_dev, bool en)
|
||
|
|
{
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
int ret = 0;
|
||
|
|
u32 ichg_ramp_t = 0;
|
||
|
|
|
||
|
|
mt_dbg(chg_data->dev, "%s: en = %d\n", __func__, en);
|
||
|
|
|
||
|
|
/* Workaround for avoiding vsys overshoot when charge disable */
|
||
|
|
mutex_lock(&chg_data->ichg_access_lock);
|
||
|
|
if (!en) {
|
||
|
|
if (chg_data->ichg <= 500000)
|
||
|
|
goto out;
|
||
|
|
chg_data->ichg_dis_chg = chg_data->ichg;
|
||
|
|
ichg_ramp_t = (chg_data->ichg - 500000) / 50000 * 2;
|
||
|
|
ret = mt6370_pmu_reg_update_bits(chg_data->chip,
|
||
|
|
MT6370_PMU_REG_CHGCTRL7,
|
||
|
|
MT6370_MASK_ICHG,
|
||
|
|
0x04 << MT6370_SHIFT_ICHG);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_notice(chg_data->dev,
|
||
|
|
"%s: set ichg fail\n", __func__);
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
mdelay(ichg_ramp_t);
|
||
|
|
} else {
|
||
|
|
if (chg_data->ichg == chg_data->ichg_dis_chg) {
|
||
|
|
ret = __mt6370_set_ichg(chg_data, chg_data->ichg);
|
||
|
|
if (ret < 0)
|
||
|
|
dev_notice(chg_data->dev,
|
||
|
|
"%s: set ichg fail\n", __func__);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
out:
|
||
|
|
ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit)
|
||
|
|
(chg_data->chip, MT6370_PMU_REG_CHGCTRL2, MT6370_MASK_CHG_EN);
|
||
|
|
if (ret < 0)
|
||
|
|
dev_notice(chg_data->dev, "%s: fail, en = %d\n", __func__, en);
|
||
|
|
mutex_unlock(&chg_data->ichg_access_lock);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_enable_safety_timer(struct charger_device *chg_dev, bool en)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
ret = __mt6370_enable_safety_timer(chg_data, en);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_enable_te(struct charger_device *chg_dev, bool en)
|
||
|
|
{
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
return __mt6370_enable_te(chg_data, en);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_reset_eoc_state(struct charger_device *chg_dev)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
dev_info(chg_data->dev, "%s\n", __func__);
|
||
|
|
|
||
|
|
mt6370_enable_hidden_mode(chg_data, true);
|
||
|
|
|
||
|
|
ret = mt6370_pmu_reg_set_bit(chg_data->chip,
|
||
|
|
MT6370_PMU_REG_CHGHIDDENCTRL0, 0x80);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_err(chg_data->dev, "%s: set failed, ret = %d\n",
|
||
|
|
__func__, ret);
|
||
|
|
goto err;
|
||
|
|
}
|
||
|
|
|
||
|
|
udelay(100);
|
||
|
|
ret = mt6370_pmu_reg_clr_bit(chg_data->chip,
|
||
|
|
MT6370_PMU_REG_CHGHIDDENCTRL0, 0x80);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_err(chg_data->dev, "%s: clear failed, ret = %d\n",
|
||
|
|
__func__, ret);
|
||
|
|
goto err;
|
||
|
|
}
|
||
|
|
|
||
|
|
err:
|
||
|
|
mt6370_enable_hidden_mode(chg_data, false);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_safety_check(struct charger_device *chg_dev, u32 polling_ieoc)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
int adc_ibat = 0;
|
||
|
|
static int counter;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
ret = mt6370_get_adc(chg_data, MT6370_ADC_IBAT, &adc_ibat);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_info(chg_data->dev, "%s: get adc failed\n", __func__);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (adc_ibat <= polling_ieoc)
|
||
|
|
counter++;
|
||
|
|
else
|
||
|
|
counter = 0;
|
||
|
|
|
||
|
|
/* If IBAT is less than polling_ieoc for 3 times, trigger EOC event */
|
||
|
|
if (counter == 3) {
|
||
|
|
dev_info(chg_data->dev, "%s: polling_ieoc = %d, ibat = %d\n",
|
||
|
|
__func__, polling_ieoc, adc_ibat);
|
||
|
|
charger_dev_notify(chg_data->chg_dev, CHARGER_DEV_NOTIFY_EOC);
|
||
|
|
counter = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_is_safety_timer_enable(struct charger_device *chg_dev,
|
||
|
|
bool *en)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_CHGCTRL12,
|
||
|
|
MT6370_SHIFT_TMR_EN, en);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_enable_power_path(struct charger_device *chg_dev, bool en)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
mutex_lock(&chg_data->pp_lock);
|
||
|
|
|
||
|
|
dev_info(chg_data->dev, "%s: en = %d, pp_en = %d\n",
|
||
|
|
__func__, en, chg_data->pp_en);
|
||
|
|
if (en == chg_data->pp_en)
|
||
|
|
goto out;
|
||
|
|
|
||
|
|
ret = (en ? mt6370_pmu_reg_clr_bit : mt6370_pmu_reg_set_bit)
|
||
|
|
(chg_data->chip, MT6370_PMU_REG_CHGCTRL1,
|
||
|
|
MT6370_MASK_FORCE_SLEEP);
|
||
|
|
/*
|
||
|
|
* enable power path -> unmask mivr irq
|
||
|
|
* mask mivr irq -> disable power path
|
||
|
|
*/
|
||
|
|
if (!en)
|
||
|
|
mt6370_enable_irq(chg_data, "chg_mivr", false);
|
||
|
|
ret = __mt6370_set_mivr(chg_data, en ? chg_data->mivr :
|
||
|
|
MT6370_MIVR_MAX);
|
||
|
|
if (en)
|
||
|
|
mt6370_enable_irq(chg_data, "chg_mivr", true);
|
||
|
|
chg_data->pp_en = en;
|
||
|
|
out:
|
||
|
|
mutex_unlock(&chg_data->pp_lock);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_is_power_path_enable(struct charger_device *chg_dev, bool *en)
|
||
|
|
{
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
mutex_lock(&chg_data->pp_lock);
|
||
|
|
*en = chg_data->pp_en;
|
||
|
|
mutex_unlock(&chg_data->pp_lock);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_get_ichg(struct charger_device *chg_dev, u32 *ichg)
|
||
|
|
{
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
return __mt6370_get_ichg(chg_data, ichg);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_set_ichg(struct charger_device *chg_dev, u32 uA)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
mutex_lock(&chg_data->ichg_access_lock);
|
||
|
|
mutex_lock(&chg_data->ieoc_lock);
|
||
|
|
ret = __mt6370_set_ichg(chg_data, uA);
|
||
|
|
mutex_unlock(&chg_data->ieoc_lock);
|
||
|
|
mutex_unlock(&chg_data->ichg_access_lock);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_set_ieoc(struct charger_device *chg_dev, u32 uA)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
mutex_lock(&chg_data->ichg_access_lock);
|
||
|
|
mutex_lock(&chg_data->ieoc_lock);
|
||
|
|
ret = __mt6370_set_ieoc(chg_data, uA);
|
||
|
|
mutex_unlock(&chg_data->ieoc_lock);
|
||
|
|
mutex_unlock(&chg_data->ichg_access_lock);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_get_aicr(struct charger_device *chg_dev, u32 *aicr)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
u8 reg_aicr = 0;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
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);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_set_aicr(struct charger_device *chg_dev, u32 uA)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
mutex_lock(&chg_data->aicr_access_lock);
|
||
|
|
ret = __mt6370_set_aicr(chg_data, uA);
|
||
|
|
mutex_unlock(&chg_data->aicr_access_lock);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_get_mivr_state(struct charger_device *chg_dev, bool *in_loop)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
ret = mt6370_pmu_reg_read(chg_data->chip, MT6370_PMU_REG_CHGSTAT1);
|
||
|
|
if (ret < 0)
|
||
|
|
return ret;
|
||
|
|
*in_loop = (ret & MT6370_MASK_MIVR_STAT) >> MT6370_SHIFT_MIVR_STAT;
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_get_mivr(struct charger_device *chg_dev, u32 *mivr)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
ret = __mt6370_get_mivr(chg_data, mivr);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_set_mivr(struct charger_device *chg_dev, u32 uV)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
mutex_lock(&chg_data->pp_lock);
|
||
|
|
|
||
|
|
if (!chg_data->pp_en) {
|
||
|
|
dev_err(chg_data->dev, "%s: power path is disabled\n",
|
||
|
|
__func__);
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = __mt6370_set_mivr(chg_data, uV);
|
||
|
|
out:
|
||
|
|
if (ret >= 0)
|
||
|
|
chg_data->mivr = uV;
|
||
|
|
mutex_unlock(&chg_data->pp_lock);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_set_otg_current_limit(struct charger_device *chg_dev, u32 uA)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
u8 reg_ilimit = 0;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
/* Set higher OC threshold */
|
||
|
|
for (reg_ilimit = 0;
|
||
|
|
reg_ilimit < ARRAY_SIZE(mt6370_otg_oc_threshold) - 1; reg_ilimit++)
|
||
|
|
if (uA <= mt6370_otg_oc_threshold[reg_ilimit])
|
||
|
|
break;
|
||
|
|
|
||
|
|
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 charger_device *chg_dev, bool en)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
bool en_otg = false;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
u8 hidden_val = en ? 0x00 : 0x0F;
|
||
|
|
u8 lg_slew_rate = en ? 0x7C : 0x73;
|
||
|
|
|
||
|
|
dev_info(chg_data->dev, "%s: en = %d\n", __func__, en);
|
||
|
|
|
||
|
|
mt6370_enable_hidden_mode(chg_data, true);
|
||
|
|
|
||
|
|
/* Set OTG_OC to 500mA */
|
||
|
|
ret = mt6370_set_otg_current_limit(chg_dev, 500000);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_err(chg_data->dev, "%s: set otg oc failed\n", __func__);
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* Woraround :
|
||
|
|
* slow Low side mos Gate driver slew rate for decline VBUS noise
|
||
|
|
* reg[0x33] = 0x7C after entering OTG mode
|
||
|
|
* reg[0x33] = 0x73 after leaving OTG mode
|
||
|
|
*/
|
||
|
|
ret = mt6370_pmu_reg_write(chg_data->chip, MT6370_PMU_REG_LG_CONTROL,
|
||
|
|
lg_slew_rate);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_err(chg_data->dev,
|
||
|
|
"%s: recover Low side mos Gate drive speed fail(%d)\n",
|
||
|
|
__func__, ret);
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = mt6370_pmu_reg_read(chg_data->chip, MT6370_PMU_REG_LG_CONTROL);
|
||
|
|
if (ret < 0)
|
||
|
|
dev_info(chg_data->dev, "%s: read reg0x33 failed\n", __func__);
|
||
|
|
else
|
||
|
|
dev_info(chg_data->dev, "%s: reg0x33 = 0x%02X\n", __func__,
|
||
|
|
ret);
|
||
|
|
|
||
|
|
/* Turn off USB charger detection/Enable WDT */
|
||
|
|
if (en) {
|
||
|
|
#if 0
|
||
|
|
ret = mt6370_enable_chgdet_flow(chg_data, false);
|
||
|
|
if (ret < 0)
|
||
|
|
dev_err(chg_data->dev, "%s: disable usb chrdet fail\n",
|
||
|
|
__func__);
|
||
|
|
#endif /* CONFIG_MT6370_PMU_CHARGER_TYPE_DETECT */
|
||
|
|
|
||
|
|
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 fail\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: fail(%d)\n", __func__, ret);
|
||
|
|
goto err_en_otg;
|
||
|
|
}
|
||
|
|
#if 0
|
||
|
|
mt6370_set_usbsw_state(chg_data, MT6370_USBSW_USB);
|
||
|
|
#endif /* CONFIG_MT6370_PMU_CHARGER_TYPE_DETECT && CONFIG_TCPC_CLASS */
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* Woraround reg[0x35] = 0x00 after entering OTG mode
|
||
|
|
* reg[0x35] = 0x0F after leaving OTG mode
|
||
|
|
*/
|
||
|
|
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);
|
||
|
|
|
||
|
|
/* 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__);
|
||
|
|
}
|
||
|
|
goto out;
|
||
|
|
|
||
|
|
err_en_otg:
|
||
|
|
/* 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__);
|
||
|
|
|
||
|
|
/* Recover Low side mos Gate slew rate */
|
||
|
|
ret = mt6370_pmu_reg_write(chg_data->chip,
|
||
|
|
MT6370_PMU_REG_LG_CONTROL, 0x73);
|
||
|
|
if (ret < 0)
|
||
|
|
dev_err(chg_data->dev,
|
||
|
|
"%s: recover Low side mos Gate drive speed fail(%d)\n",
|
||
|
|
__func__, ret);
|
||
|
|
ret = -EIO;
|
||
|
|
out:
|
||
|
|
mt6370_enable_hidden_mode(chg_data, false);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_enable_discharge(struct charger_device *chg_dev, bool en)
|
||
|
|
{
|
||
|
|
int ret = 0, i = 0;
|
||
|
|
const u32 check_dischg_max = 3;
|
||
|
|
bool is_dischg = true;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
dev_info(chg_data->dev, "%s: en = %d\n", __func__, en);
|
||
|
|
|
||
|
|
ret = mt6370_enable_hidden_mode(chg_data, true);
|
||
|
|
if (ret < 0)
|
||
|
|
goto out;
|
||
|
|
|
||
|
|
/* Set bit2 of reg[0x31] to 1/0 to enable/disable discharging */
|
||
|
|
ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit)
|
||
|
|
(chg_data->chip, MT6370_PMU_REG_CHGHIDDENCTRL1, 0x04);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_err(chg_data->dev, "%s: en = %d failed, ret = %d\n",
|
||
|
|
__func__, en, ret);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!en) {
|
||
|
|
for (i = 0; i < check_dischg_max; i++) {
|
||
|
|
ret = mt6370_pmu_reg_test_bit(chg_data->chip,
|
||
|
|
MT6370_PMU_REG_CHGHIDDENCTRL1, 2, &is_dischg);
|
||
|
|
if (!is_dischg)
|
||
|
|
break;
|
||
|
|
ret = mt6370_pmu_reg_clr_bit(chg_data->chip,
|
||
|
|
MT6370_PMU_REG_CHGHIDDENCTRL1, 0x04);
|
||
|
|
}
|
||
|
|
if (i == check_dischg_max)
|
||
|
|
dev_err(chg_data->dev,
|
||
|
|
"%s: disable discharg failed, ret = %d\n",
|
||
|
|
__func__, ret);
|
||
|
|
}
|
||
|
|
|
||
|
|
out:
|
||
|
|
mt6370_enable_hidden_mode(chg_data, false);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_set_pep_current_pattern(struct charger_device *chg_dev,
|
||
|
|
bool is_increase)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
dev_info(chg_data->dev, "%s: pe1.0 pump_up = %d\n", __func__,
|
||
|
|
is_increase);
|
||
|
|
|
||
|
|
mutex_lock(&chg_data->pe_access_lock);
|
||
|
|
|
||
|
|
/* 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)
|
||
|
|
goto out;
|
||
|
|
|
||
|
|
/* Enable PumpX */
|
||
|
|
ret = mt6370_enable_pump_express(chg_data, true);
|
||
|
|
|
||
|
|
out:
|
||
|
|
mutex_unlock(&chg_data->pe_access_lock);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_set_pep20_reset(struct charger_device *chg_dev)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
mutex_lock(&chg_data->pe_access_lock);
|
||
|
|
/* disable skip mode */
|
||
|
|
mt6370_enable_hidden_mode(chg_data, true);
|
||
|
|
|
||
|
|
ret = mt6370_pmu_reg_clr_bit(chg_data->chip,
|
||
|
|
MT6370_PMU_REG_CHGHIDDENCTRL9, 0x80);
|
||
|
|
if (ret < 0)
|
||
|
|
dev_err(chg_data->dev, "%s: disable psk mode fail\n", __func__);
|
||
|
|
|
||
|
|
/* Select IINLMTSEL to use AICR */
|
||
|
|
ret = mt6370_select_input_current_limit(chg_data,
|
||
|
|
MT6370_IINLMTSEL_AICR);
|
||
|
|
if (ret < 0)
|
||
|
|
goto out;
|
||
|
|
|
||
|
|
ret = mt6370_set_aicr(chg_dev, 100000);
|
||
|
|
if (ret < 0)
|
||
|
|
goto out;
|
||
|
|
|
||
|
|
msleep(250);
|
||
|
|
|
||
|
|
ret = mt6370_set_aicr(chg_dev, 700000);
|
||
|
|
|
||
|
|
out:
|
||
|
|
mt6370_pmu_reg_set_bit(chg_data->chip, MT6370_PMU_REG_CHGHIDDENCTRL9,
|
||
|
|
0x80);
|
||
|
|
mt6370_enable_hidden_mode(chg_data, false);
|
||
|
|
mutex_unlock(&chg_data->pe_access_lock);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_set_pep20_current_pattern(struct charger_device *chg_dev,
|
||
|
|
u32 uV)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
u8 reg_volt = 0;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
dev_info(chg_data->dev, "%s: pep2.0 = %d\n", __func__, uV);
|
||
|
|
|
||
|
|
mutex_lock(&chg_data->pe_access_lock);
|
||
|
|
/* 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)
|
||
|
|
goto out;
|
||
|
|
|
||
|
|
/* 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)
|
||
|
|
goto out;
|
||
|
|
|
||
|
|
/* Enable PumpX */
|
||
|
|
ret = mt6370_enable_pump_express(chg_data, true);
|
||
|
|
ret = (ret >= 0) ? 0 : ret;
|
||
|
|
|
||
|
|
out:
|
||
|
|
mutex_unlock(&chg_data->pe_access_lock);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_set_pep20_efficiency_table(struct charger_device *chg_dev)
|
||
|
|
{
|
||
|
|
struct charger_manager *chg_mgr = NULL;
|
||
|
|
|
||
|
|
chg_mgr = charger_dev_get_drvdata(chg_dev);
|
||
|
|
if (!chg_mgr)
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
chg_mgr->pe2.profile[0].vchr = 8000000;
|
||
|
|
chg_mgr->pe2.profile[1].vchr = 8000000;
|
||
|
|
chg_mgr->pe2.profile[2].vchr = 8000000;
|
||
|
|
chg_mgr->pe2.profile[3].vchr = 8500000;
|
||
|
|
chg_mgr->pe2.profile[4].vchr = 8500000;
|
||
|
|
chg_mgr->pe2.profile[5].vchr = 8500000;
|
||
|
|
chg_mgr->pe2.profile[6].vchr = 9000000;
|
||
|
|
chg_mgr->pe2.profile[7].vchr = 9000000;
|
||
|
|
chg_mgr->pe2.profile[8].vchr = 9500000;
|
||
|
|
chg_mgr->pe2.profile[9].vchr = 9500000;
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_enable_cable_drop_comp(struct charger_device *chg_dev,
|
||
|
|
bool en)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
dev_info(chg_data->dev, "%s: en = %d\n", __func__, en);
|
||
|
|
|
||
|
|
mutex_lock(&chg_data->pe_access_lock);
|
||
|
|
/* 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)
|
||
|
|
goto out;
|
||
|
|
|
||
|
|
/* Set Voltage */
|
||
|
|
ret = mt6370_pmu_reg_update_bits(
|
||
|
|
chg_data->chip,
|
||
|
|
MT6370_PMU_REG_CHGCTRL17,
|
||
|
|
MT6370_MASK_PUMPX_DEC,
|
||
|
|
0x1F << MT6370_SHIFT_PUMPX_DEC
|
||
|
|
);
|
||
|
|
if (ret < 0)
|
||
|
|
goto out;
|
||
|
|
|
||
|
|
/* Enable PumpX */
|
||
|
|
ret = mt6370_enable_pump_express(chg_data, true);
|
||
|
|
|
||
|
|
out:
|
||
|
|
mutex_unlock(&chg_data->pe_access_lock);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_is_charging_done(struct charger_device *chg_dev, bool *done)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
enum mt6370_charging_status chg_stat = MT6370_CHG_STATUS_READY;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
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:
|
||
|
|
*done = false;
|
||
|
|
break;
|
||
|
|
case MT6370_CHG_STATUS_DONE:
|
||
|
|
*done = true;
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
*done = false;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_kick_wdt(struct charger_device *chg_dev)
|
||
|
|
{
|
||
|
|
/* Any I2C communication can kick watchdog timer */
|
||
|
|
int ret = 0;
|
||
|
|
enum mt6370_charging_status chg_status;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
ret = mt6370_get_charging_status(chg_data, &chg_status);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_enable_direct_charge(struct charger_device *chg_dev, bool en)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
dev_info(chg_data->dev, "%s: en = %d\n", __func__, en);
|
||
|
|
|
||
|
|
if (en) {
|
||
|
|
mt6370_enable_irq(chg_data, "chg_mivr", false);
|
||
|
|
|
||
|
|
/* 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:
|
||
|
|
mt6370_enable_irq(chg_data, "chg_mivr", true);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_set_dc_vbusov(struct charger_device *chg_dev, u32 uV)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
u8 reg_vbusov = 0;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
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_set_dc_ibusoc(struct charger_device *chg_dev, u32 uA)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
u8 reg_ibusoc = 0;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
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_kick_dc_wdt(struct charger_device *chg_dev)
|
||
|
|
{
|
||
|
|
/* Any I2C communication can reset watchdog timer */
|
||
|
|
int ret = 0;
|
||
|
|
enum mt6370_charging_status chg_status;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
ret = mt6370_get_charging_status(chg_data, &chg_status);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_get_tchg(struct charger_device *chg_dev, int *tchg_min,
|
||
|
|
int *tchg_max)
|
||
|
|
{
|
||
|
|
int ret = 0, adc_temp = 0;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
u32 retry_cnt = 3;
|
||
|
|
|
||
|
|
/* Get value from ADC */
|
||
|
|
ret = mt6370_get_adc(chg_data, MT6370_ADC_TEMP_JC, &adc_temp);
|
||
|
|
if (ret < 0)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* Check unusual temperature */
|
||
|
|
while (adc_temp >= 120 && retry_cnt > 0) {
|
||
|
|
dev_err(chg_data->dev, "%s: [WARNING] t = %d\n",
|
||
|
|
__func__, adc_temp);
|
||
|
|
mt6370_get_adc(chg_data, MT6370_ADC_VBAT, &adc_temp);
|
||
|
|
ret = mt6370_get_adc(chg_data, MT6370_ADC_TEMP_JC, &adc_temp);
|
||
|
|
retry_cnt--;
|
||
|
|
}
|
||
|
|
if (ret < 0)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
mutex_lock(&chg_data->tchg_lock);
|
||
|
|
if (adc_temp >= 120)
|
||
|
|
adc_temp = chg_data->tchg;
|
||
|
|
else
|
||
|
|
chg_data->tchg = adc_temp;
|
||
|
|
mutex_unlock(&chg_data->tchg_lock);
|
||
|
|
|
||
|
|
*tchg_min = adc_temp;
|
||
|
|
*tchg_max = adc_temp;
|
||
|
|
|
||
|
|
dev_info(chg_data->dev, "%s: tchg = %d\n", __func__, adc_temp);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_get_ibus(struct charger_device *chg_dev, u32 *ibus)
|
||
|
|
{
|
||
|
|
int ret = 0, adc_ibus = 0;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
/* Get value from ADC */
|
||
|
|
ret = mt6370_get_adc(chg_data, MT6370_ADC_IBUS, &adc_ibus);
|
||
|
|
if (ret < 0)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
*ibus = adc_ibus;
|
||
|
|
|
||
|
|
dev_info(chg_data->dev, "%s: ibus = %dmA\n", __func__, adc_ibus / 1000);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_plug_out(struct charger_device *chg_dev)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
dev_info(chg_data->dev, "%s\n", __func__);
|
||
|
|
|
||
|
|
/* Reset AICR limit */
|
||
|
|
chg_data->aicr_limit = -1;
|
||
|
|
|
||
|
|
/* Enable charger */
|
||
|
|
ret = mt6370_enable_charging(chg_dev, true);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_notice(chg_data->dev, "%s: en chg failed\n", __func__);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Disable WDT */
|
||
|
|
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_plug_in(struct charger_device *chg_dev)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
dev_info(chg_data->dev, "%s\n", __func__);
|
||
|
|
|
||
|
|
/* Enable WDT */
|
||
|
|
if (chg_data->chg_desc->en_wdt) {
|
||
|
|
ret = mt6370_enable_wdt(chg_data, true);
|
||
|
|
if (ret < 0)
|
||
|
|
dev_err(chg_data->dev, "%s: en wdt failed\n", __func__);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Enable charger */
|
||
|
|
ret = mt6370_enable_charging(chg_dev, true);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_err(chg_data->dev, "%s: en chg failed\n", __func__);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_run_aicl(struct charger_device *chg_dev, u32 *uA)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
ret = __mt6370_run_aicl(chg_data);
|
||
|
|
if (ret >= 0)
|
||
|
|
*uA = chg_data->aicr_limit;
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_get_min_ichg(struct charger_device *chg_dev, u32 *uA)
|
||
|
|
{
|
||
|
|
*uA = 500000;
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_get_min_aicr(struct charger_device *chg_dev, u32 *uA)
|
||
|
|
{
|
||
|
|
*uA = 100000;
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_dump_register(struct charger_device *chg_dev)
|
||
|
|
{
|
||
|
|
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;
|
||
|
|
int adc_vbus = 0;
|
||
|
|
enum mt6370_charging_status chg_status = MT6370_CHG_STATUS_READY;
|
||
|
|
u8 chg_stat = 0, chg_ctrl[2] = {0};
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
ret = mt6370_get_ichg(chg_dev, &ichg);
|
||
|
|
ret = mt6370_get_aicr(chg_dev, &aicr);
|
||
|
|
ret = mt6370_get_charging_status(chg_data, &chg_status);
|
||
|
|
ret = mt6370_get_ieoc(chg_data, &ieoc);
|
||
|
|
ret = mt6370_get_mivr(chg_dev, &mivr);
|
||
|
|
ret = mt6370_get_cv(chg_dev, &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);
|
||
|
|
ret = mt6370_get_adc(chg_data, MT6370_ADC_VBUS_DIV5, &adc_vbus);
|
||
|
|
|
||
|
|
chg_stat = mt6370_pmu_reg_read(chg_data->chip, MT6370_PMU_REG_CHGSTAT1);
|
||
|
|
ret = mt6370_pmu_reg_block_read(chg_data->chip, MT6370_PMU_REG_CHGCTRL1,
|
||
|
|
2, chg_ctrl);
|
||
|
|
|
||
|
|
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 / 1000, aicr / 1000, mivr / 1000,
|
||
|
|
ieoc / 1000, cv / 1000);
|
||
|
|
|
||
|
|
dev_info(chg_data->dev,
|
||
|
|
"%s: VSYS = %dmV, VBAT = %dmV, IBAT = %dmA, IBUS = %dmA, VBUS = %dmV\n",
|
||
|
|
__func__, adc_vsys / 1000, adc_vbat / 1000,
|
||
|
|
adc_ibat / 1000, adc_ibus / 1000, adc_vbus / 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);
|
||
|
|
|
||
|
|
dev_info(chg_data->dev, "%s: CHG_CTRL1 = 0x%02X, CHG_CTRL2 = 0x%02X\n",
|
||
|
|
__func__, chg_ctrl[0], chg_ctrl[1]);
|
||
|
|
|
||
|
|
ret = 0;
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_enable_chg_type_det(struct charger_device *chg_dev, bool en)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
#if defined(CONFIG_MT6370_PMU_CHARGER_TYPE_DETECT) && defined(CONFIG_TCPC_CLASS)
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
dev_info(chg_data->dev, "%s: en = %d\n", __func__, en);
|
||
|
|
|
||
|
|
atomic_set(&chg_data->tcpc_usb_connected, en);
|
||
|
|
|
||
|
|
/* TypeC detach */
|
||
|
|
if (!en) {
|
||
|
|
ret = mt6370_chgdet_handler(chg_data);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* TypeC attach */
|
||
|
|
ret = mt6370_enable_chgdet_flow(chg_data, true);
|
||
|
|
if (ret < 0)
|
||
|
|
dev_err(chg_data->dev, "%s: en bc12 fail(%d)\n", __func__, ret);
|
||
|
|
#endif /* CONFIG_MT6370_PMU_CHARGER_TYPE_DETECT && CONFIG_TCPC_CLASS */
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_get_zcv(struct charger_device *chg_dev, u32 *uV)
|
||
|
|
{
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
dev_get_drvdata(&chg_dev->dev);
|
||
|
|
|
||
|
|
dev_info(chg_data->dev, "%s: zcv = %dmV\n", __func__,
|
||
|
|
chg_data->zcv / 1000);
|
||
|
|
*uV = chg_data->zcv;
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_do_event(struct charger_device *chg_dev, u32 event, u32 args)
|
||
|
|
{
|
||
|
|
switch (event) {
|
||
|
|
case EVENT_EOC:
|
||
|
|
charger_dev_notify(chg_dev, CHARGER_DEV_NOTIFY_EOC);
|
||
|
|
break;
|
||
|
|
case EVENT_RECHARGE:
|
||
|
|
charger_dev_notify(chg_dev, CHARGER_DEV_NOTIFY_RECHG);
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
#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,
|
||
|
|
0x0F,
|
||
|
|
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,
|
||
|
|
0x0F,
|
||
|
|
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 int mt6370_toggle_cfo(struct mt6370_pmu_charger_data *chg_data)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
u8 data = 0;
|
||
|
|
|
||
|
|
mutex_lock(&chg_data->chip->io_lock);
|
||
|
|
|
||
|
|
/* check if strobe mode */
|
||
|
|
ret = i2c_smbus_read_i2c_block_data(chg_data->chip->i2c,
|
||
|
|
MT6370_PMU_REG_FLEDEN, 1, &data);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_err(chg_data->dev, "%s: check strobe fail\n", __func__);
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
if (data & MT6370_STROBE_EN_MASK) {
|
||
|
|
dev_err(chg_data->dev, "%s: in strobe mode\n", __func__);
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* read data */
|
||
|
|
ret = i2c_smbus_read_i2c_block_data(chg_data->chip->i2c,
|
||
|
|
MT6370_PMU_REG_CHGCTRL2, 1, &data);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_err(chg_data->dev, "%s: read cfo fail\n", __func__);
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* cfo off */
|
||
|
|
data &= ~MT6370_MASK_CFO_EN;
|
||
|
|
ret = i2c_smbus_write_i2c_block_data(chg_data->chip->i2c,
|
||
|
|
MT6370_PMU_REG_CHGCTRL2, 1, &data);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_err(chg_data->dev, "%s: cfo off fail\n", __func__);
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* cfo on */
|
||
|
|
data |= MT6370_MASK_CFO_EN;
|
||
|
|
ret = i2c_smbus_write_i2c_block_data(chg_data->chip->i2c,
|
||
|
|
MT6370_PMU_REG_CHGCTRL2, 1, &data);
|
||
|
|
if (ret < 0)
|
||
|
|
dev_err(chg_data->dev, "%s: cfo on fail\n", __func__);
|
||
|
|
|
||
|
|
out:
|
||
|
|
mutex_unlock(&chg_data->chip->io_lock);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
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 void mt6370_pmu_chg_mivr_dwork_handler(struct work_struct *work)
|
||
|
|
{
|
||
|
|
struct mt6370_pmu_charger_data *chg_data = container_of(work,
|
||
|
|
struct mt6370_pmu_charger_data, mivr_dwork.work);
|
||
|
|
|
||
|
|
mt6370_enable_irq(chg_data, "chg_mivr", true);
|
||
|
|
}
|
||
|
|
|
||
|
|
static irqreturn_t mt6370_pmu_chg_mivr_irq_handler(int irq, void *data)
|
||
|
|
{
|
||
|
|
int ret = 0, ibus = 0;
|
||
|
|
bool mivr_stat = false;
|
||
|
|
struct mt6370_pmu_charger_data *chg_data = data;
|
||
|
|
|
||
|
|
mt_dbg(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) {
|
||
|
|
mt_dbg(chg_data->dev, "%s: mivr stat not act\n", __func__);
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = mt6370_get_adc(chg_data, MT6370_ADC_IBUS, &ibus);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_err(chg_data->dev, "%s: get ibus fail\n", __func__);
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (ibus < 100000) { /* 100mA */
|
||
|
|
ret = mt6370_toggle_cfo(chg_data);
|
||
|
|
if (ret < 0)
|
||
|
|
dev_err(chg_data->dev, "%s: toggle cfo fail\n",
|
||
|
|
__func__);
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
out:
|
||
|
|
mt6370_enable_irq(chg_data, "chg_mivr", false);
|
||
|
|
schedule_delayed_work(&chg_data->mivr_dwork, msecs_to_jiffies(500));
|
||
|
|
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;
|
||
|
|
struct chgdev_notify *noti = &(chg_data->chg_dev->noti);
|
||
|
|
|
||
|
|
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;
|
||
|
|
|
||
|
|
noti->vbusov_stat = vbusov_stat;
|
||
|
|
dev_info(chg_data->dev, "%s: stat = %d\n", __func__, vbusov_stat);
|
||
|
|
|
||
|
|
charger_dev_notify(chg_data->chg_dev, CHARGER_DEV_NOTIFY_VBUS_OVP);
|
||
|
|
|
||
|
|
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);
|
||
|
|
if (!tmr_stat)
|
||
|
|
return IRQ_HANDLED;
|
||
|
|
|
||
|
|
charger_dev_notify(chg_data->chg_dev,
|
||
|
|
CHARGER_DEV_NOTIFY_SAFETY_TIMEOUT);
|
||
|
|
|
||
|
|
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->chg_dev);
|
||
|
|
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__);
|
||
|
|
charger_dev_notify(chg_data->chg_dev, CHARGER_DEV_NOTIFY_RECHG);
|
||
|
|
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);
|
||
|
|
if (!ieoc_stat)
|
||
|
|
return IRQ_HANDLED;
|
||
|
|
|
||
|
|
charger_dev_notify(chg_data->chg_dev, CHARGER_DEV_NOTIFY_EOC);
|
||
|
|
|
||
|
|
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)
|
||
|
|
{
|
||
|
|
#ifdef CONFIG_MT6370_PMU_CHARGER_TYPE_DETECT
|
||
|
|
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__);
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
__mt6370_chgdet_handler(chg_data);
|
||
|
|
out:
|
||
|
|
mutex_unlock(&chg_data->bc12_access_lock);
|
||
|
|
#endif /* CONFIG_MT6370_PMU_CHARGER_TYPE_DETECT */
|
||
|
|
|
||
|
|
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)
|
||
|
|
{
|
||
|
|
#ifdef CONFIG_MT6370_PMU_CHARGER_TYPE_DETECT
|
||
|
|
struct mt6370_pmu_charger_data *chg_data =
|
||
|
|
(struct mt6370_pmu_charger_data *)data;
|
||
|
|
int ret = 0;
|
||
|
|
bool dcdt = false;
|
||
|
|
|
||
|
|
dev_info(chg_data->dev, "%s\n", __func__);
|
||
|
|
if (chg_data->chg_desc->fast_unknown_ta_dect) {
|
||
|
|
ret = mt6370_pmu_reg_test_bit(chg_data->chip,
|
||
|
|
MT6370_PMU_REG_USBSTATUS1,
|
||
|
|
MT6370_SHIFT_DCDT, &dcdt);
|
||
|
|
if (ret < 0 || !dcdt)
|
||
|
|
return IRQ_HANDLED;
|
||
|
|
dev_info(chg_data->dev, "%s: unknown TA Detected\n", __func__);
|
||
|
|
mutex_lock(&chg_data->bc12_access_lock);
|
||
|
|
chg_data->dcd_timeout = true;
|
||
|
|
__mt6370_chgdet_handler(chg_data);
|
||
|
|
mutex_unlock(&chg_data->bc12_access_lock);
|
||
|
|
}
|
||
|
|
#endif /* CONFIG_MT6370_PMU_CHARGER_TYPE_DETECT */
|
||
|
|
|
||
|
|
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)
|
||
|
|
{
|
||
|
|
#if defined(CONFIG_MT6370_PMU_CHARGER_TYPE_DETECT) \
|
||
|
|
&& !defined(CONFIG_TCPC_CLASS)
|
||
|
|
int ret = 0;
|
||
|
|
bool uvp_d = false, otg_mode = 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 & OTG mode */
|
||
|
|
ret = mt6370_pmu_reg_test_bit(chg_data->chip,
|
||
|
|
MT6370_PMU_REG_OVPCTRLSTAT, MT6370_SHIFT_OVPCTRL_UVP_D_STAT,
|
||
|
|
&uvp_d);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_err(chg_data->dev, "%s: read uvp_d_stat fail\n", __func__);
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* power good */
|
||
|
|
if (!uvp_d) {
|
||
|
|
ret = mt6370_pmu_reg_test_bit(chg_data->chip,
|
||
|
|
MT6370_PMU_REG_CHGCTRL1, MT6370_SHIFT_OPA_MODE,
|
||
|
|
&otg_mode);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_err(chg_data->dev, "%s: read otg mode fail\n",
|
||
|
|
__func__);
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* In OTG mode skip this event */
|
||
|
|
if (otg_mode) {
|
||
|
|
dev_info(chg_data->dev, "%s: triggered by OTG\n",
|
||
|
|
__func__);
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Turn on USB charger detection */
|
||
|
|
ret = mt6370_enable_chgdet_flow(chg_data, true);
|
||
|
|
if (ret < 0)
|
||
|
|
dev_err(chg_data->dev, "%s: en bc12 fail\n", __func__);
|
||
|
|
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* not power good */
|
||
|
|
ret = mt6370_chgdet_handler(chg_data);
|
||
|
|
|
||
|
|
out:
|
||
|
|
#endif /* CONFIG_MT6370_PMU_CHARGER_TYPE_DETECT && !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));
|
||
|
|
|
||
|
|
/* Alias name is in charger properties but not in desc */
|
||
|
|
if (of_property_read_string(np, "chg_alias_name",
|
||
|
|
&(chg_data->chg_props.alias_name)) < 0) {
|
||
|
|
dev_err(chg_data->dev, "%s: no chg alias name\n", __func__);
|
||
|
|
chg_data->chg_props.alias_name = "mt6370_chg";
|
||
|
|
}
|
||
|
|
|
||
|
|
if (of_property_read_string(np, "ls_alias_name",
|
||
|
|
&(chg_data->ls_props.alias_name)) < 0) {
|
||
|
|
dev_err(chg_data->dev, "%s: no ls alias name\n", __func__);
|
||
|
|
chg_data->ls_props.alias_name = "mt6370_ls";
|
||
|
|
}
|
||
|
|
|
||
|
|
if (of_property_read_string(np, "charger_name",
|
||
|
|
&(chg_desc->chg_dev_name)) < 0)
|
||
|
|
dev_err(chg_data->dev, "%s: no charger name\n", __func__);
|
||
|
|
|
||
|
|
if (of_property_read_string(np, "load_switch_name",
|
||
|
|
&(chg_desc->ls_dev_name)) < 0)
|
||
|
|
dev_err(chg_data->dev, "%s: no load switch name\n", __func__);
|
||
|
|
|
||
|
|
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__);
|
||
|
|
|
||
|
|
if (of_property_read_u32(np, "lbp_hys_sel", &chg_desc->lbp_hys_sel) < 0)
|
||
|
|
dev_err(chg_data->dev, "%s: no lbp_hys_sel\n", __func__);
|
||
|
|
|
||
|
|
if (of_property_read_u32(np, "lbp_dt", &chg_desc->lbp_dt) < 0)
|
||
|
|
dev_err(chg_data->dev, "%s: no lbp_dt\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_desc->en_polling = of_property_read_bool(np, "enable_polling");
|
||
|
|
chg_desc->disable_vlgc = of_property_read_bool(np, "disable_vlgc");
|
||
|
|
chg_desc->fast_unknown_ta_dect =
|
||
|
|
of_property_read_bool(np, "fast_unknown_ta_dect");
|
||
|
|
chg_desc->post_aicl = of_property_read_bool(np, "post_aicl");
|
||
|
|
|
||
|
|
chg_data->chg_desc = chg_desc;
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_set_otglbp(
|
||
|
|
struct mt6370_pmu_charger_data *chg_data, u32 lbp_hys_sel, u32 lbp_dt)
|
||
|
|
{
|
||
|
|
u8 reg_data = (lbp_hys_sel << MT6370_SHIFT_LBPHYS_SEL)
|
||
|
|
| (lbp_dt << MT6370_SHIFT_LBP_DT);
|
||
|
|
|
||
|
|
dev_info(chg_data->dev, "%s: otglbp(%d), dt(%d)\n",
|
||
|
|
__func__, lbp_hys_sel, lbp_dt);
|
||
|
|
|
||
|
|
return mt6370_pmu_reg_update_bits(
|
||
|
|
chg_data->chip, MT6370_PMU_REG_VDDASUPPLY,
|
||
|
|
MT6370_MASK_LBP, reg_data);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_disable_vlgc(
|
||
|
|
struct mt6370_pmu_charger_data *chg_data, bool dis)
|
||
|
|
{
|
||
|
|
return (dis ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit)
|
||
|
|
(chg_data->chip, MT6370_PMU_REG_QCSTATUS1,
|
||
|
|
MT6370_MASK_VLGC_DISABLE);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mt6370_enable_fast_unknown_ta_dect(
|
||
|
|
struct mt6370_pmu_charger_data *chg_data, bool en)
|
||
|
|
{
|
||
|
|
return (en ? mt6370_pmu_reg_clr_bit : mt6370_pmu_reg_set_bit)
|
||
|
|
(chg_data->chip, MT6370_PMU_REG_USBSTATUS1,
|
||
|
|
MT6370_MASK_FAST_UNKNOWN_TA_DECT);
|
||
|
|
}
|
||
|
|
|
||
|
|
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;
|
||
|
|
|
||
|
|
struct device *dev = NULL;
|
||
|
|
struct device_node *boot_node = NULL;
|
||
|
|
struct tag_bootmode *tag = NULL;
|
||
|
|
u32 boot_mode = 11;//UNKNOWN_BOOT
|
||
|
|
// workaround for mt6768
|
||
|
|
dev = chg_data->dev;
|
||
|
|
if (dev != NULL){
|
||
|
|
boot_node = of_parse_phandle(dev->of_node, "bootmode", 0);
|
||
|
|
if (!boot_node){
|
||
|
|
chr_err("%s: failed to get boot mode phandle\n", __func__);
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
tag = (struct tag_bootmode *)of_get_property(boot_node,
|
||
|
|
"atag,boot", NULL);
|
||
|
|
if (!tag){
|
||
|
|
chr_err("%s: failed to get atag,boot\n", __func__);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
boot_mode = tag->bootmode;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
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__);
|
||
|
|
|
||
|
|
if (boot_mode == META_BOOT || boot_mode == ADVMETA_BOOT) {
|
||
|
|
ret = __mt6370_set_aicr(chg_data, 200000);
|
||
|
|
dev_info(chg_data->dev, "%s: set aicr to 200mA in meta mode\n",
|
||
|
|
__func__);
|
||
|
|
} else
|
||
|
|
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__);
|
||
|
|
chg_data->mivr = chg_desc->mivr;
|
||
|
|
|
||
|
|
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 first, no matter use it or not */
|
||
|
|
ret = __mt6370_enable_chgdet_flow(chg_data, false);
|
||
|
|
if (ret < 0)
|
||
|
|
dev_err(chg_data->dev,
|
||
|
|
"%s: disable usb chrdet failed\n", __func__);
|
||
|
|
|
||
|
|
ret = mt6370_set_otglbp(
|
||
|
|
chg_data, chg_desc->lbp_hys_sel, chg_desc->lbp_dt);
|
||
|
|
if (ret < 0)
|
||
|
|
dev_err(chg_data->dev, "%s: set otg lbp fail\n", __func__);
|
||
|
|
|
||
|
|
ret = mt6370_disable_vlgc(chg_data, chg_desc->disable_vlgc);
|
||
|
|
if (ret < 0)
|
||
|
|
dev_err(chg_data->dev, "%s: set vlgc fail\n", __func__);
|
||
|
|
|
||
|
|
ret = mt6370_enable_fast_unknown_ta_dect(
|
||
|
|
chg_data, chg_desc->fast_unknown_ta_dect);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_err(chg_data->dev,
|
||
|
|
"%s: set fast unknown ta dect fail\n", __func__);
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifndef CONFIG_MT6370_DCDTOUT_SUPPORT
|
||
|
|
/* Disable DCD */
|
||
|
|
ret = mt6370_enable_dcd_tout(chg_data, false);
|
||
|
|
if (ret < 0)
|
||
|
|
dev_notice(chg_data->dev, "%s disable dcd fail\n", __func__);
|
||
|
|
#endif
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
static struct charger_ops mt6370_chg_ops = {
|
||
|
|
/* Normal charging */
|
||
|
|
.plug_out = mt6370_plug_out,
|
||
|
|
.plug_in = mt6370_plug_in,
|
||
|
|
.dump_registers = mt6370_dump_register,
|
||
|
|
.enable = mt6370_enable_charging,
|
||
|
|
.get_charging_current = mt6370_get_ichg,
|
||
|
|
.set_charging_current = mt6370_set_ichg,
|
||
|
|
.get_input_current = mt6370_get_aicr,
|
||
|
|
.set_input_current = mt6370_set_aicr,
|
||
|
|
.get_constant_voltage = mt6370_get_cv,
|
||
|
|
.set_constant_voltage = mt6370_set_cv,
|
||
|
|
.kick_wdt = mt6370_kick_wdt,
|
||
|
|
.set_mivr = mt6370_set_mivr,
|
||
|
|
.get_mivr = mt6370_get_mivr,
|
||
|
|
.get_mivr_state = mt6370_get_mivr_state,
|
||
|
|
.is_charging_done = mt6370_is_charging_done,
|
||
|
|
.get_zcv = mt6370_get_zcv,
|
||
|
|
.run_aicl = mt6370_run_aicl,
|
||
|
|
.set_eoc_current = mt6370_set_ieoc,
|
||
|
|
.enable_termination = mt6370_enable_te,
|
||
|
|
.reset_eoc_state = mt6370_reset_eoc_state,
|
||
|
|
.safety_check = mt6370_safety_check,
|
||
|
|
.get_min_charging_current = mt6370_get_min_ichg,
|
||
|
|
.get_min_input_current = mt6370_get_min_aicr,
|
||
|
|
|
||
|
|
/* Safety timer */
|
||
|
|
.enable_safety_timer = mt6370_enable_safety_timer,
|
||
|
|
.is_safety_timer_enabled = mt6370_is_safety_timer_enable,
|
||
|
|
|
||
|
|
/* Power path */
|
||
|
|
.enable_powerpath = mt6370_enable_power_path,
|
||
|
|
.is_powerpath_enabled = mt6370_is_power_path_enable,
|
||
|
|
|
||
|
|
/* Charger type detection */
|
||
|
|
.enable_chg_type_det = mt6370_enable_chg_type_det,
|
||
|
|
|
||
|
|
/* OTG */
|
||
|
|
.enable_otg = mt6370_enable_otg,
|
||
|
|
.set_boost_current_limit = mt6370_set_otg_current_limit,
|
||
|
|
.enable_discharge = mt6370_enable_discharge,
|
||
|
|
|
||
|
|
/* PE+/PE+20 */
|
||
|
|
.send_ta_current_pattern = mt6370_set_pep_current_pattern,
|
||
|
|
.set_pe20_efficiency_table = mt6370_set_pep20_efficiency_table,
|
||
|
|
.send_ta20_current_pattern = mt6370_set_pep20_current_pattern,
|
||
|
|
.reset_ta = mt6370_set_pep20_reset,
|
||
|
|
.enable_cable_drop_comp = mt6370_enable_cable_drop_comp,
|
||
|
|
|
||
|
|
/* ADC */
|
||
|
|
.get_tchg_adc = mt6370_get_tchg,
|
||
|
|
.get_ibus_adc = mt6370_get_ibus,
|
||
|
|
|
||
|
|
/* Event */
|
||
|
|
.event = mt6370_do_event,
|
||
|
|
};
|
||
|
|
|
||
|
|
|
||
|
|
static struct charger_ops mt6370_ls_ops = {
|
||
|
|
/* Direct charging */
|
||
|
|
.enable_direct_charging = mt6370_enable_direct_charge,
|
||
|
|
.set_direct_charging_vbusov = mt6370_set_dc_vbusov,
|
||
|
|
.set_direct_charging_ibusoc = mt6370_set_dc_ibusoc,
|
||
|
|
.kick_direct_charging_wdt = mt6370_kick_dc_wdt,
|
||
|
|
.get_tchg_adc = mt6370_get_tchg,
|
||
|
|
.get_ibus_adc = mt6370_get_ibus,
|
||
|
|
};
|
||
|
|
|
||
|
|
static ssize_t shipping_mode_store(struct device *dev,
|
||
|
|
struct device_attribute *attr,
|
||
|
|
const char *buf, size_t count)
|
||
|
|
{
|
||
|
|
struct mt6370_pmu_charger_data *chg_data = dev_get_drvdata(dev);
|
||
|
|
int32_t tmp = 0;
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
if (kstrtoint(buf, 10, &tmp) < 0) {
|
||
|
|
dev_notice(dev, "parsing number fail\n");
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
if (tmp != 5526789)
|
||
|
|
return -EINVAL;
|
||
|
|
mutex_lock(&chg_data->adc_access_lock);
|
||
|
|
ret = mt6370_pmu_reg_write(chg_data->chip,
|
||
|
|
MT6370_PMU_REG_RSTPASCODE1, 0xA9);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_notice(dev, "set passcode1 fail\n");
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
ret = mt6370_pmu_reg_write(chg_data->chip,
|
||
|
|
MT6370_PMU_REG_RSTPASCODE2, 0x96);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_notice(dev, "set passcode2 fail\n");
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
/* reset all chg/fled/ldo/rgb/bl/db reg and logic */
|
||
|
|
ret = mt6370_pmu_reg_write(chg_data->chip,
|
||
|
|
MT6370_PMU_REG_CORECTRL2, 0x7F);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_notice(dev, "set reset bits fail\n");
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
/* disable chg auto sensing */
|
||
|
|
mt6370_enable_hidden_mode(chg_data, true);
|
||
|
|
ret = mt6370_pmu_reg_clr_bit(chg_data->chip,
|
||
|
|
MT6370_PMU_REG_CHGHIDDENCTRL15, 0x01);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_notice(dev, "set auto sensing disable\n");
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
mt6370_enable_hidden_mode(chg_data, false);
|
||
|
|
mdelay(50);
|
||
|
|
/* enter shipping mode */
|
||
|
|
ret = mt6370_pmu_reg_set_bit(chg_data->chip,
|
||
|
|
MT6370_PMU_REG_CHGCTRL2, 0x80);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_notice(dev, "enter shipping mode\n");
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
static const DEVICE_ATTR_WO(shipping_mode);
|
||
|
|
|
||
|
|
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->pe_access_lock);
|
||
|
|
mutex_init(&chg_data->bc12_access_lock);
|
||
|
|
mutex_init(&chg_data->hidden_mode_lock);
|
||
|
|
mutex_init(&chg_data->ieoc_lock);
|
||
|
|
mutex_init(&chg_data->tchg_lock);
|
||
|
|
mutex_init(&chg_data->pp_lock);
|
||
|
|
chg_data->chip = dev_get_drvdata(pdev->dev.parent);
|
||
|
|
chg_data->dev = &pdev->dev;
|
||
|
|
chg_data->chg_type = CHARGER_UNKNOWN;
|
||
|
|
chg_data->aicr_limit = -1;
|
||
|
|
chg_data->adc_hang = false;
|
||
|
|
chg_data->bc12_en = true;
|
||
|
|
chg_data->hidden_mode_cnt = 0;
|
||
|
|
chg_data->ieoc_wkard = false;
|
||
|
|
chg_data->ieoc = 250000; /* register default value 250mA */
|
||
|
|
chg_data->ichg = 2000000;
|
||
|
|
chg_data->ichg_dis_chg = 2000000;
|
||
|
|
atomic_set(&chg_data->bc12_cnt, 0);
|
||
|
|
atomic_set(&chg_data->bc12_wkard, 0);
|
||
|
|
#ifdef CONFIG_TCPC_CLASS
|
||
|
|
atomic_set(&chg_data->tcpc_usb_connected, 0);
|
||
|
|
#endif
|
||
|
|
chg_data->pp_en = true;
|
||
|
|
|
||
|
|
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);
|
||
|
|
|
||
|
|
#if defined(CONFIG_MT6370_PMU_CHARGER_TYPE_DETECT)\
|
||
|
|
&& !defined(CONFIG_TCPC_CLASS)
|
||
|
|
INIT_WORK(&chg_data->chgdet_work, mt6370_chgdet_work_handler);
|
||
|
|
#endif /* CONFIG_MT6370_PMU_CHARGER_TYPE_DETECT && !CONFIG_TCPC_CLASS */
|
||
|
|
INIT_DELAYED_WORK(&chg_data->mivr_dwork,
|
||
|
|
mt6370_pmu_chg_mivr_dwork_handler);
|
||
|
|
|
||
|
|
/* 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;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Register charger device */
|
||
|
|
chg_data->chg_dev = charger_device_register(
|
||
|
|
chg_data->chg_desc->chg_dev_name,
|
||
|
|
chg_data->dev, chg_data, &mt6370_chg_ops, &chg_data->chg_props);
|
||
|
|
if (IS_ERR_OR_NULL(chg_data->chg_dev)) {
|
||
|
|
ret = PTR_ERR(chg_data->chg_dev);
|
||
|
|
goto err_register_chg_dev;
|
||
|
|
}
|
||
|
|
chg_data->chg_dev->is_polling_mode = chg_data->chg_desc->en_polling;
|
||
|
|
|
||
|
|
/* Register load switch charger device */
|
||
|
|
chg_data->ls_dev = charger_device_register(
|
||
|
|
chg_data->chg_desc->ls_dev_name,
|
||
|
|
chg_data->dev, chg_data, &mt6370_ls_ops, &chg_data->ls_props);
|
||
|
|
if (IS_ERR_OR_NULL(chg_data->ls_dev)) {
|
||
|
|
ret = PTR_ERR(chg_data->ls_dev);
|
||
|
|
goto err_register_ls_dev;
|
||
|
|
}
|
||
|
|
chg_data->ls_dev->is_polling_mode = chg_data->chg_desc->en_polling;
|
||
|
|
|
||
|
|
mt6370_pmu_charger_irq_register(pdev);
|
||
|
|
|
||
|
|
ret = device_create_file(chg_data->dev, &dev_attr_shipping_mode);
|
||
|
|
if (ret < 0) {
|
||
|
|
dev_notice(&pdev->dev, "create shipping attr fail\n");
|
||
|
|
goto err_register_ls_dev;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Schedule work for microB's BC1.2 */
|
||
|
|
#if defined(CONFIG_MT6370_PMU_CHARGER_TYPE_DETECT)\
|
||
|
|
&& !defined(CONFIG_TCPC_CLASS)
|
||
|
|
schedule_work(&chg_data->chgdet_work);
|
||
|
|
#endif /* CONFIG_MT6370_PMU_CHARGER_TYPE_DETECT && !CONFIG_TCPC_CLASS */
|
||
|
|
|
||
|
|
dev_info(&pdev->dev, "%s successfully\n", __func__);
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
err_register_ls_dev:
|
||
|
|
charger_device_unregister(chg_data->chg_dev);
|
||
|
|
err_register_chg_dev:
|
||
|
|
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->pe_access_lock);
|
||
|
|
mutex_destroy(&chg_data->bc12_access_lock);
|
||
|
|
mutex_destroy(&chg_data->hidden_mode_lock);
|
||
|
|
mutex_destroy(&chg_data->ieoc_lock);
|
||
|
|
mutex_destroy(&chg_data->tchg_lock);
|
||
|
|
mutex_destroy(&chg_data->pp_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) {
|
||
|
|
device_remove_file(chg_data->dev, &dev_attr_shipping_mode);
|
||
|
|
charger_device_unregister(chg_data->ls_dev);
|
||
|
|
charger_device_unregister(chg_data->chg_dev);
|
||
|
|
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->pe_access_lock);
|
||
|
|
mutex_destroy(&chg_data->bc12_access_lock);
|
||
|
|
mutex_destroy(&chg_data->hidden_mode_lock);
|
||
|
|
mutex_destroy(&chg_data->ieoc_lock);
|
||
|
|
mutex_destroy(&chg_data->tchg_lock);
|
||
|
|
mutex_destroy(&chg_data->pp_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.30_MTK
|
||
|
|
* (1) Reduce IBUS Iq when pp is off for MT6371 and MT6372
|
||
|
|
*
|
||
|
|
* 1.1.29_MTK
|
||
|
|
* (1) Masks mivr irq for 500ms after mivr irq gets handled
|
||
|
|
*
|
||
|
|
* 1.1.28_MTK
|
||
|
|
* (1) mutex_unlock() once in mt6370_pmu_attachi_irq_handler()
|
||
|
|
* (2) Do not do the workaround for VSYS overshoot if ichg <= 500mA
|
||
|
|
*
|
||
|
|
* 1.1.27_MTK
|
||
|
|
* (1) Only RT5081/MT6370 need BC12 workaround
|
||
|
|
* (2) Fix mt6370_post_aicl_measure() to return measure between start and stop
|
||
|
|
*
|
||
|
|
* 1.1.26_MTK
|
||
|
|
* (1) Add support for MT6372
|
||
|
|
* (2) Add workaround for VSYS overshoot
|
||
|
|
*
|
||
|
|
* 1.1.25_MTK
|
||
|
|
* (1) Keep mivr via chg_ops
|
||
|
|
*
|
||
|
|
* 1.1.24_MTK
|
||
|
|
* (1) Add debug information for ADC
|
||
|
|
*
|
||
|
|
* 1.1.23_MTK
|
||
|
|
* (1) Use bc12_access_lock instead of chgdet_lock
|
||
|
|
* (2) Add junction ADC workaround
|
||
|
|
*
|
||
|
|
* 1.1.22_MTK
|
||
|
|
* (1) If ichg < 900mA -> disable vsys sp
|
||
|
|
* ichg >= 900mA -> enable vsys sp
|
||
|
|
* Always keep ichg >= 500mA
|
||
|
|
*
|
||
|
|
* 1.1.21_MTK
|
||
|
|
* (1) Remove keeping ichg >= 900mA
|
||
|
|
* (2) Add BC12 SDP workaround
|
||
|
|
*
|
||
|
|
* 1.1.20_MTK
|
||
|
|
* (1) Always keep setting of ichg >= 900mA
|
||
|
|
* (2) Remove setting ichg to 512mA in pep20_reset
|
||
|
|
*
|
||
|
|
* 1.1.19_MTK
|
||
|
|
* (1) Always disable charger detection first no matter use it or not
|
||
|
|
*
|
||
|
|
* 1.1.18_MTK
|
||
|
|
* (1) Move AICL work from MIVR IRQ handler to charger thread
|
||
|
|
* (2) Keep IEOC setting which is modified in set_ichg
|
||
|
|
* (3) Add enable_te & set_ieoc ops
|
||
|
|
* (4) Add enable_irq function to enable/disable irq usage
|
||
|
|
* (5) Use atomic to represent tcpc usb connected
|
||
|
|
* (6) Move definition of mt6370 charger type detect to Kconfig
|
||
|
|
*
|
||
|
|
* 1.1.17_MTK
|
||
|
|
* (1) Remove delay after Disable TS auto sensing for speed up
|
||
|
|
* (2) Remove dump register in probe for speed up
|
||
|
|
*
|
||
|
|
* 1.1.16_MTK
|
||
|
|
* (1) Modify microB BC1.2 flow to fix AP usb switch issue
|
||
|
|
* (2) Remove unlock battery protection workaround
|
||
|
|
* (3) Add enable/disable psk mode for pep20_reset
|
||
|
|
*
|
||
|
|
* 1.1.15_MTK
|
||
|
|
* (1) Prevent backboost
|
||
|
|
* (2) After PE pattern -> Enable psk mode
|
||
|
|
* Disable psk mode -> Start PE pattern
|
||
|
|
*
|
||
|
|
* 1.1.14_MTK
|
||
|
|
* (1) Add workaround to adjust slow rate for OTG
|
||
|
|
*
|
||
|
|
* 1.1.13_MTK
|
||
|
|
* (1) Add polling mode for EOC/Rechg
|
||
|
|
* (2) Use charger_dev_notify instead of srcu_notifier_call_chain
|
||
|
|
*
|
||
|
|
* 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 pe_access_lock
|
||
|
|
* (3) 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 PPS
|
||
|
|
*
|
||
|
|
* 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
|
||
|
|
*/
|