[Feature] add GA346 baseline version

Change-Id: Ic62933698569507dcf98240cdf5d9931ae34348f
diff --git a/src/kernel/linux/v4.19/drivers/power/supply/rt9467.c b/src/kernel/linux/v4.19/drivers/power/supply/rt9467.c
new file mode 100644
index 0000000..9914071
--- /dev/null
+++ b/src/kernel/linux/v4.19/drivers/power/supply/rt9467.c
@@ -0,0 +1,2259 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 MediaTek Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/pm_runtime.h>
+#include <linux/power_supply.h>
+
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#ifdef CONFIG_REGMAP
+#include <linux/regmap.h>
+#endif
+
+#include "rt9467.h"
+#define I2C_ACCESS_MAX_RETRY	5
+#define RT9467_DRV_VERSION	"1.0.19_MTK"
+
+/* ======================= */
+/* RT9467 Parameter        */
+/* ======================= */
+
+
+static const u32 rt9467_boost_oc_threshold[] = {
+	500000, 700000, 1100000, 1300000, 1800000, 2100000, 2400000,
+}; /* uA */
+
+enum rt9467_irq_idx {
+	RT9467_IRQIDX_CHG_STATC = 0,
+	RT9467_IRQIDX_CHG_FAULT,
+	RT9467_IRQIDX_TS_STATC,
+	RT9467_IRQIDX_CHG_IRQ1,
+	RT9467_IRQIDX_CHG_IRQ2,
+	RT9467_IRQIDX_CHG_IRQ3,
+	RT9467_IRQIDX_DPDM_IRQ,
+	RT9467_IRQIDX_MAX,
+};
+
+enum rt9467_irq_stat {
+	RT9467_IRQSTAT_CHG_STATC = 0,
+	RT9467_IRQSTAT_CHG_FAULT,
+	RT9467_IRQSTAT_TS_STATC,
+	RT9467_IRQSTAT_MAX,
+};
+
+enum rt9467_chg_type {
+	RT9467_CHG_TYPE_NOVBUS = 0,
+	RT9467_CHG_TYPE_UNDER_GOING,
+	RT9467_CHG_TYPE_SDP,
+	RT9467_CHG_TYPE_SDPNSTD,
+	RT9467_CHG_TYPE_DCP,
+	RT9467_CHG_TYPE_CDP,
+	RT9467_CHG_TYPE_MAX,
+};
+
+static const u8 rt9467_irq_maskall[RT9467_IRQIDX_MAX] = {
+	0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+};
+
+struct irq_mapping_tbl {
+	const char *name;
+	const int id;
+};
+
+#define RT9467_IRQ_MAPPING(_name, _id) {.name = #_name, .id = _id}
+static const struct irq_mapping_tbl rt9467_irq_mapping_tbl[] = {
+	RT9467_IRQ_MAPPING(chg_treg, 4),
+	RT9467_IRQ_MAPPING(chg_aicr, 5),
+	RT9467_IRQ_MAPPING(chg_mivr, 6),
+	RT9467_IRQ_MAPPING(pwr_rdy, 7),
+	RT9467_IRQ_MAPPING(chg_vsysuv, 12),
+	RT9467_IRQ_MAPPING(chg_vsysov, 13),
+	RT9467_IRQ_MAPPING(chg_vbatov, 14),
+	RT9467_IRQ_MAPPING(chg_vbusov, 15),
+	RT9467_IRQ_MAPPING(ts_batcold, 20),
+	RT9467_IRQ_MAPPING(ts_batcool, 21),
+	RT9467_IRQ_MAPPING(ts_batwarm, 22),
+	RT9467_IRQ_MAPPING(ts_bathot, 23),
+	RT9467_IRQ_MAPPING(ts_statci, 24),
+	RT9467_IRQ_MAPPING(chg_faulti, 25),
+	RT9467_IRQ_MAPPING(chg_statci, 26),
+	RT9467_IRQ_MAPPING(chg_tmri, 27),
+	RT9467_IRQ_MAPPING(chg_batabsi, 28),
+	RT9467_IRQ_MAPPING(chg_adpbadi, 29),
+	RT9467_IRQ_MAPPING(chg_rvpi, 30),
+	RT9467_IRQ_MAPPING(otpi, 31),
+	RT9467_IRQ_MAPPING(chg_aiclmeasi, 32),
+	RT9467_IRQ_MAPPING(chg_ichgmeasi, 33),
+	RT9467_IRQ_MAPPING(chgdet_donei, 34),
+	RT9467_IRQ_MAPPING(wdtmri, 35),
+	RT9467_IRQ_MAPPING(ssfinishi, 36),
+	RT9467_IRQ_MAPPING(chg_rechgi, 37),
+	RT9467_IRQ_MAPPING(chg_termi, 38),
+	RT9467_IRQ_MAPPING(chg_ieoci, 39),
+	RT9467_IRQ_MAPPING(adc_donei, 40),
+	RT9467_IRQ_MAPPING(pumpx_donei, 41),
+	RT9467_IRQ_MAPPING(bst_batuvi, 45),
+	RT9467_IRQ_MAPPING(bst_midovi, 46),
+	RT9467_IRQ_MAPPING(bst_olpi, 47),
+	RT9467_IRQ_MAPPING(attachi, 48),
+	RT9467_IRQ_MAPPING(detachi, 49),
+	RT9467_IRQ_MAPPING(chgdeti, 54),
+	RT9467_IRQ_MAPPING(dcdti, 55),
+};
+
+enum rt9467_charging_status {
+	RT9467_CHG_STATUS_READY = 0,
+	RT9467_CHG_STATUS_PROGRESS,
+	RT9467_CHG_STATUS_DONE,
+	RT9467_CHG_STATUS_FAULT,
+	RT9467_CHG_STATUS_MAX,
+};
+
+static const u8 rt9467_val_en_hidden_mode[] = {
+	0x49, 0x32, 0xB6, 0x27, 0x48, 0x18, 0x03, 0xE2,
+};
+
+enum rt9467_adc_sel {
+	RT9467_ADC_VBUS_DIV5 = 1,
+	RT9467_ADC_VBUS_DIV2,
+	RT9467_ADC_VSYS,
+	RT9467_ADC_VBAT,
+	RT9467_ADC_TS_BAT = 6,
+	RT9467_ADC_IBUS = 8,
+	RT9467_ADC_IBAT,
+	RT9467_ADC_REGN = 11,
+	RT9467_ADC_TEMP_JC,
+	RT9467_ADC_MAX,
+};
+
+/*
+ * Unit for each ADC parameter
+ * 0 stands for reserved
+ * For TS_BAT, the real unit is 0.25.
+ * Here we use 25, please remember to divide 100 while showing the value
+ */
+static const int rt9467_adc_unit[RT9467_ADC_MAX] = {
+	0,
+	RT9467_ADC_UNIT_VBUS_DIV5,
+	RT9467_ADC_UNIT_VBUS_DIV2,
+	RT9467_ADC_UNIT_VSYS,
+	RT9467_ADC_UNIT_VBAT,
+	0,
+	RT9467_ADC_UNIT_TS_BAT,
+	0,
+	RT9467_ADC_UNIT_IBUS,
+	RT9467_ADC_UNIT_IBAT,
+	0,
+	RT9467_ADC_UNIT_REGN,
+	RT9467_ADC_UNIT_TEMP_JC,
+};
+
+static const int rt9467_adc_offset[RT9467_ADC_MAX] = {
+	0,
+	RT9467_ADC_OFFSET_VBUS_DIV5,
+	RT9467_ADC_OFFSET_VBUS_DIV2,
+	RT9467_ADC_OFFSET_VSYS,
+	RT9467_ADC_OFFSET_VBAT,
+	0,
+	RT9467_ADC_OFFSET_TS_BAT,
+	0,
+	RT9467_ADC_OFFSET_IBUS,
+	RT9467_ADC_OFFSET_IBAT,
+	0,
+	RT9467_ADC_OFFSET_REGN,
+	RT9467_ADC_OFFSET_TEMP_JC,
+};
+
+struct rt9467_desc {
+	bool en_wdt;
+	bool en_irq_pulse;
+	const char *chg_dev_name;
+};
+
+/* These default values will be applied if there's no property in dts */
+static struct rt9467_desc rt9467_default_desc = {
+	.en_wdt = true,
+	.en_irq_pulse = false,
+	.chg_dev_name = "primary_chg",
+};
+
+struct rt9467_info {
+	struct i2c_client *client;
+	struct mutex i2c_access_lock;
+	struct mutex adc_access_lock;
+	struct mutex irq_access_lock;
+	struct mutex aicr_access_lock;
+	struct mutex ichg_access_lock;
+	struct mutex hidden_mode_lock;
+	struct device *dev;
+	struct rt9467_desc *desc;
+	int irq;
+	u32 intr_gpio;
+	u8 chip_rev;
+	u8 irq_flag[RT9467_IRQIDX_MAX];
+	u8 irq_stat[RT9467_IRQSTAT_MAX];
+	u8 irq_mask[RT9467_IRQIDX_MAX];
+	u32 hidden_mode_cnt;
+	struct regulator_dev *otg_rdev;
+};
+
+/* ======================= */
+/* Register Address        */
+/* ======================= */
+
+static const unsigned char rt9467_reg_addr[] = {
+	RT9467_REG_CORE_CTRL0,
+	RT9467_REG_CHG_CTRL1,
+	RT9467_REG_CHG_CTRL2,
+	RT9467_REG_CHG_CTRL3,
+	RT9467_REG_CHG_CTRL4,
+	RT9467_REG_CHG_CTRL5,
+	RT9467_REG_CHG_CTRL6,
+	RT9467_REG_CHG_CTRL7,
+	RT9467_REG_CHG_CTRL8,
+	RT9467_REG_CHG_CTRL9,
+	RT9467_REG_CHG_CTRL10,
+	RT9467_REG_CHG_CTRL11,
+	RT9467_REG_CHG_CTRL12,
+	RT9467_REG_CHG_CTRL13,
+	RT9467_REG_CHG_CTRL14,
+	RT9467_REG_CHG_CTRL15,
+	RT9467_REG_CHG_CTRL16,
+	RT9467_REG_CHG_ADC,
+	RT9467_REG_CHG_DPDM1,
+	RT9467_REG_CHG_DPDM2,
+	RT9467_REG_CHG_DPDM3,
+	RT9467_REG_CHG_CTRL19,
+	RT9467_REG_CHG_CTRL17,
+	RT9467_REG_CHG_CTRL18,
+	RT9467_REG_DEVICE_ID,
+	RT9467_REG_CHG_STAT,
+	RT9467_REG_CHG_NTC,
+	RT9467_REG_ADC_DATA_H,
+	RT9467_REG_ADC_DATA_L,
+	RT9467_REG_ADC_DATA_TUNE_H,
+	RT9467_REG_ADC_DATA_TUNE_L,
+	RT9467_REG_ADC_DATA_ORG_H,
+	RT9467_REG_ADC_DATA_ORG_L,
+	RT9467_REG_CHG_STATC,
+	RT9467_REG_CHG_FAULT,
+	RT9467_REG_TS_STATC,
+	/* Skip IRQ evt to prevent reading clear while dumping registers */
+	RT9467_REG_CHG_STATC_CTRL,
+	RT9467_REG_CHG_FAULT_CTRL,
+	RT9467_REG_TS_STATC_CTRL,
+	RT9467_REG_CHG_IRQ1_CTRL,
+	RT9467_REG_CHG_IRQ2_CTRL,
+	RT9467_REG_CHG_IRQ3_CTRL,
+	RT9467_REG_DPDM_IRQ_CTRL,
+};
+
+
+/* ========================= */
+/* I2C operations            */
+/* ========================= */
+
+#ifdef CONFIG_REGMAP
+static const struct regmap_config rt9467_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = RT9467_REG_MAX,
+	.cache_type = REGCACHE_NONE,
+};
+#endif /* CONFIG_REGMAP */
+
+static int rt9467_device_read(void *client, u32 addr, int leng, void *dst)
+{
+	struct i2c_client *i2c = (struct i2c_client *)client;
+
+	return i2c_smbus_read_i2c_block_data(i2c, addr, leng, dst);
+}
+
+static int rt9467_device_write(void *client, u32 addr, int leng,
+	const void *src)
+{
+	struct i2c_client *i2c = (struct i2c_client *)client;
+
+	return i2c_smbus_write_i2c_block_data(i2c, addr, leng, src);
+}
+
+static inline int __rt9467_i2c_write_byte(struct rt9467_info *info, u8 cmd,
+	u8 data)
+{
+	int ret = 0, retry = 0;
+
+	do {
+		ret = rt9467_device_write(info->client, cmd, 1, &data);
+		retry++;
+		if (ret < 0)
+			udelay(10);
+	} while (ret < 0 && retry < I2C_ACCESS_MAX_RETRY);
+
+	if (ret < 0)
+		dev_notice(info->dev, "%s: I2CW[0x%02X] = 0x%02X fail\n",
+			__func__, cmd, data);
+	else
+		dev_dbg(info->dev, "%s: I2CW[0x%02X] = 0x%02X\n", __func__,
+			cmd, data);
+
+	return ret;
+}
+
+static int rt9467_i2c_write_byte(struct rt9467_info *info, u8 cmd, u8 data)
+{
+	int ret = 0;
+
+	mutex_lock(&info->i2c_access_lock);
+	ret = __rt9467_i2c_write_byte(info, cmd, data);
+	mutex_unlock(&info->i2c_access_lock);
+
+	return ret;
+}
+
+static inline int __rt9467_i2c_read_byte(struct rt9467_info *info, u8 cmd)
+{
+	int ret = 0, ret_val = 0, retry = 0;
+
+	do {
+		ret = rt9467_device_read(info->client, cmd, 1, &ret_val);
+		retry++;
+		if (ret < 0)
+			udelay(10);
+	} while (ret < 0 && retry < I2C_ACCESS_MAX_RETRY);
+
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: I2CR[0x%02X] fail\n", __func__, cmd);
+		return ret;
+	}
+
+	ret_val = ret_val & 0xFF;
+
+	dev_dbg(info->dev, "%s: I2CR[0x%02X] = 0x%02X\n", __func__, cmd,
+		ret_val);
+
+	return ret_val;
+}
+
+static int rt9467_i2c_read_byte(struct rt9467_info *info, u8 cmd)
+{
+	int ret = 0;
+
+	mutex_lock(&info->i2c_access_lock);
+	ret = __rt9467_i2c_read_byte(info, cmd);
+	mutex_unlock(&info->i2c_access_lock);
+
+	if (ret < 0)
+		return ret;
+
+	return (ret & 0xFF);
+}
+
+static inline int __rt9467_i2c_block_write(struct rt9467_info *info, u8 cmd,
+	u32 leng, const u8 *data)
+{
+	int ret = 0;
+
+	ret = rt9467_device_write(info->client, cmd, leng, data);
+
+	return ret;
+}
+
+
+static int rt9467_i2c_block_write(struct rt9467_info *info, u8 cmd, u32 leng,
+	const u8 *data)
+{
+	int ret = 0;
+
+	mutex_lock(&info->i2c_access_lock);
+	ret = __rt9467_i2c_block_write(info, cmd, leng, data);
+	mutex_unlock(&info->i2c_access_lock);
+
+	return ret;
+}
+
+static inline int __rt9467_i2c_block_read(struct rt9467_info *info, u8 cmd,
+	u32 leng, u8 *data)
+{
+	int ret = 0;
+
+	ret = rt9467_device_read(info->client, cmd, leng, data);
+
+	return ret;
+}
+
+
+static int rt9467_i2c_block_read(struct rt9467_info *info, u8 cmd, u32 leng,
+	u8 *data)
+{
+	int ret = 0;
+
+	mutex_lock(&info->i2c_access_lock);
+	ret = __rt9467_i2c_block_read(info, cmd, leng, data);
+	mutex_unlock(&info->i2c_access_lock);
+
+	return ret;
+}
+
+
+static int rt9467_i2c_test_bit(struct rt9467_info *info, u8 cmd, u8 shift,
+	bool *is_one)
+{
+	int ret = 0;
+	u8 data = 0;
+
+	ret = rt9467_i2c_read_byte(info, cmd);
+	if (ret < 0) {
+		*is_one = false;
+		return ret;
+	}
+
+	data = ret & (1 << shift);
+	*is_one = (data == 0 ? false : true);
+
+	return ret;
+}
+
+static int rt9467_i2c_update_bits(struct rt9467_info *info, u8 cmd, u8 data,
+	u8 mask)
+{
+	int ret = 0;
+	u8 reg_data = 0;
+
+	mutex_lock(&info->i2c_access_lock);
+	ret = __rt9467_i2c_read_byte(info, cmd);
+	if (ret < 0) {
+		mutex_unlock(&info->i2c_access_lock);
+		return ret;
+	}
+
+	reg_data = ret & 0xFF;
+	reg_data &= ~mask;
+	reg_data |= (data & mask);
+
+	ret = __rt9467_i2c_write_byte(info, cmd, reg_data);
+	mutex_unlock(&info->i2c_access_lock);
+
+	return ret;
+}
+
+static inline int rt9467_set_bit(struct rt9467_info *info, u8 reg, u8 mask)
+{
+	return rt9467_i2c_update_bits(info, reg, mask, mask);
+}
+
+static inline int rt9467_clr_bit(struct rt9467_info *info, u8 reg, u8 mask)
+{
+	return rt9467_i2c_update_bits(info, reg, 0x00, mask);
+}
+
+/* ================== */
+/* Internal Functions */
+/* ================== */
+static int rt9467_kick_wdt(struct rt9467_info *info);
+static int rt9467_enable_hidden_mode(struct rt9467_info *info, bool en);
+
+static inline void rt9467_irq_set_flag(struct rt9467_info *info, u8 *irq,
+	u8 mask)
+{
+	mutex_lock(&info->irq_access_lock);
+	*irq |= mask;
+	mutex_unlock(&info->irq_access_lock);
+}
+
+static inline void rt9467_irq_clr_flag(struct rt9467_info *info, u8 *irq,
+	u8 mask)
+{
+	mutex_lock(&info->irq_access_lock);
+	*irq &= ~mask;
+	mutex_unlock(&info->irq_access_lock);
+}
+
+static inline const char *rt9467_get_irq_name(struct rt9467_info *info,
+	int irqnum)
+{
+	int i = 0;
+
+	for (i = 0; i < ARRAY_SIZE(rt9467_irq_mapping_tbl); i++) {
+		if (rt9467_irq_mapping_tbl[i].id == irqnum)
+			return rt9467_irq_mapping_tbl[i].name;
+	}
+
+	return "not found";
+}
+
+static inline void rt9467_irq_mask(struct rt9467_info *info, int irqnum)
+{
+	dev_dbg(info->dev, "%s: irq = %d, %s\n", __func__, irqnum,
+		rt9467_get_irq_name(info, irqnum));
+	info->irq_mask[irqnum / 8] |= (1 << (irqnum % 8));
+}
+
+static inline void rt9467_irq_unmask(struct rt9467_info *info, int irqnum)
+{
+	dev_dbg(info->dev, "%s: irq = %d, %s\n", __func__, irqnum,
+		rt9467_get_irq_name(info, irqnum));
+	info->irq_mask[irqnum / 8] &= ~(1 << (irqnum % 8));
+}
+
+static inline u8 rt9467_closest_reg(u32 min, u32 max, u32 step, u32 target)
+{
+	/* Smaller than minimum supported value, use minimum one */
+	if (target < min)
+		return 0;
+
+	/* Greater than maximum supported value, use maximum one */
+	if (target >= max)
+		return (max - min) / step;
+
+	return (target - min) / step;
+}
+
+static inline u8 rt9467_closest_reg_via_tbl(const u32 *tbl, u32 tbl_size,
+	u32 target)
+{
+	u32 i = 0;
+
+	/* Smaller than minimum supported value, use minimum one */
+	if (target < tbl[0])
+		return 0;
+
+	for (i = 0; i < tbl_size - 1; i++) {
+		if (target >= tbl[i] && target < tbl[i + 1])
+			return i;
+	}
+
+	/* Greater than maximum supported value, use maximum one */
+	return tbl_size - 1;
+}
+
+static inline u32 rt9467_closest_value(u32 min, u32 max, u32 step, u8 reg_val)
+{
+	u32 ret_val = 0;
+
+	ret_val = min + reg_val * step;
+	if (ret_val > max)
+		ret_val = max;
+
+	return ret_val;
+}
+
+static int rt9467_get_aicr(struct rt9467_info *info, u32 *aicr);
+static int rt9467_get_ichg(struct rt9467_info *info, u32 *ichg);
+static int rt9467_get_adc(struct rt9467_info *info,
+	enum rt9467_adc_sel adc_sel, int *adc_val)
+{
+	int ret = 0, i = 0;
+	const int max_wait_times = 6;
+	u8 adc_data[6] = {0};
+	u32 aicr = 0, ichg = 0;
+	bool adc_start = false;
+
+	mutex_lock(&info->adc_access_lock);
+
+	rt9467_enable_hidden_mode(info, true);
+
+	/* Select ADC to desired channel */
+	ret = rt9467_i2c_update_bits(info, RT9467_REG_CHG_ADC,
+		adc_sel << RT9467_SHIFT_ADC_IN_SEL, RT9467_MASK_ADC_IN_SEL);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: select ch to %d fail(%d)\n",
+			__func__, adc_sel, ret);
+		goto out;
+	}
+
+	/* Workaround for IBUS & IBAT */
+	if (adc_sel == RT9467_ADC_IBUS) {
+		mutex_lock(&info->aicr_access_lock);
+		ret = rt9467_get_aicr(info, &aicr);
+		if (ret < 0) {
+			dev_notice(info->dev, "%s: get aicr fail\n", __func__);
+			goto out_unlock_all;
+		}
+	} else if (adc_sel == RT9467_ADC_IBAT) {
+		mutex_lock(&info->ichg_access_lock);
+		ret = rt9467_get_ichg(info, &ichg);
+		if (ret < 0) {
+			dev_notice(info->dev, "%s: get ichg fail\n", __func__);
+			goto out_unlock_all;
+		}
+	}
+
+	/* Start ADC conversation */
+	ret = rt9467_set_bit(info, RT9467_REG_CHG_ADC, RT9467_MASK_ADC_START);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: start con fail(%d), sel = %d\n",
+			__func__, ret, adc_sel);
+		goto out_unlock_all;
+	}
+
+	for (i = 0; i < max_wait_times; i++) {
+		msleep(35);
+		ret = rt9467_i2c_test_bit(info, RT9467_REG_CHG_ADC,
+			RT9467_SHIFT_ADC_START, &adc_start);
+		if (ret >= 0 && !adc_start)
+			break;
+	}
+	if (i == max_wait_times) {
+		dev_notice(info->dev, "%s: wait con fail(%d), sel = %d\n",
+			__func__, ret, adc_sel);
+		ret = -EINVAL;
+		goto out_unlock_all;
+	}
+
+	mdelay(1);
+
+	/* Read ADC data high/low byte */
+	ret = rt9467_i2c_block_read(info, RT9467_REG_ADC_DATA_H, 6, adc_data);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: read ADC data fail\n", __func__);
+		goto out_unlock_all;
+	}
+	dev_dbg(info->dev,
+		"%s: adc_tune = (0x%02X, 0x%02X), adc_org = (0x%02X, 0x%02X)\n",
+		__func__, adc_data[2], adc_data[3], adc_data[4], adc_data[5]);
+
+	/* Calculate ADC value */
+	*adc_val = ((adc_data[0] << 8) + adc_data[1]) * rt9467_adc_unit[adc_sel]
+		+ rt9467_adc_offset[adc_sel];
+
+	dev_dbg(info->dev,
+		"%s: adc_sel = %d, adc_h = 0x%02X, adc_l = 0x%02X, val = %d\n",
+		__func__, adc_sel, adc_data[0], adc_data[1], *adc_val);
+
+	ret = 0;
+
+out_unlock_all:
+	/* Coefficient of IBUS & IBAT */
+	if (adc_sel == RT9467_ADC_IBUS) {
+		if (aicr < 400000) /* 400mA */
+			*adc_val = *adc_val * 67 / 100;
+		mutex_unlock(&info->aicr_access_lock);
+	} else if (adc_sel == RT9467_ADC_IBAT) {
+		if (ichg >= 100000 && ichg <= 450000) /* 100~450mA */
+			*adc_val = *adc_val * 57 / 100;
+		else if (ichg >= 500000 && ichg <= 850000) /* 500~850mA */
+			*adc_val = *adc_val * 63 / 100;
+		mutex_unlock(&info->ichg_access_lock);
+	}
+
+out:
+	rt9467_enable_hidden_mode(info, false);
+	mutex_unlock(&info->adc_access_lock);
+	return ret;
+}
+
+static inline int __rt9467_enable_chgdet_flow(struct rt9467_info *info, bool en)
+{
+	int ret = 0;
+
+	dev_info(info->dev, "%s: en = %d\n", __func__, en);
+	ret = (en ? rt9467_set_bit : rt9467_clr_bit)
+		(info, RT9467_REG_CHG_DPDM1, RT9467_MASK_USBCHGEN);
+	return ret;
+}
+
+/* Prevent back boost */
+static int rt9467_toggle_cfo(struct rt9467_info *info)
+{
+	int ret = 0;
+	u8 data = 0;
+
+	dev_info(info->dev, "%s\n", __func__);
+	mutex_lock(&info->i2c_access_lock);
+	ret = rt9467_device_read(info->client, RT9467_REG_CHG_CTRL2, 1, &data);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s read cfo fail(%d)\n", __func__, ret);
+		goto out;
+	}
+
+	/* CFO off */
+	data &= ~RT9467_MASK_CFO_EN;
+	ret = rt9467_device_write(info->client, RT9467_REG_CHG_CTRL2, 1, &data);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s cfo off fail(%d)\n", __func__, ret);
+		goto out;
+	}
+
+	/* CFO on */
+	data |= RT9467_MASK_CFO_EN;
+	ret = rt9467_device_write(info->client, RT9467_REG_CHG_CTRL2, 1, &data);
+	if (ret < 0)
+		dev_notice(info->dev, "%s cfo on fail(%d)\n", __func__, ret);
+
+out:
+	mutex_unlock(&info->i2c_access_lock);
+	return ret;
+}
+
+/* IRQ handlers */
+static int rt9467_pwr_rdy_irq_handler(struct rt9467_info *info)
+{
+#ifndef CONFIG_TCPC_CLASS
+	int ret = 0;
+	bool pwr_rdy = false;
+#endif /* CONFIG_TCPC_CLASS */
+
+	dev_notice(info->dev, "%s\n", __func__);
+
+#ifndef CONFIG_TCPC_CLASS
+	ret = rt9467_i2c_test_bit(info, RT9467_REG_CHG_STATC,
+		RT9467_SHIFT_PWR_RDY, &pwr_rdy);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: read pwr rdy fail\n", __func__);
+		goto out;
+	}
+
+	if (!pwr_rdy) {
+		dev_info(info->dev, "%s: pwr rdy = 0\n", __func__);
+		goto out;
+	}
+
+out:
+#endif /* CONFIG_TCPC_CLASS */
+
+	return 0;
+}
+
+static int rt9467_chg_mivr_irq_handler(struct rt9467_info *info)
+{
+	int ret = 0;
+	bool mivr_act = false;
+	int adc_ibus = 0;
+
+	dev_notice(info->dev, "%s\n", __func__);
+
+	/* Check whether MIVR loop is active */
+	ret = rt9467_i2c_test_bit(info, RT9467_REG_CHG_STATC,
+		RT9467_SHIFT_CHG_MIVR, &mivr_act);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: read mivr stat fail\n", __func__);
+		goto out;
+	}
+
+	if (!mivr_act) {
+		dev_info(info->dev, "%s: mivr loop is not active\n", __func__);
+		goto out;
+	}
+
+	if (strcmp(info->desc->chg_dev_name, "primary_chg") == 0) {
+		/* Check IBUS ADC */
+		ret = rt9467_get_adc(info, RT9467_ADC_IBUS, &adc_ibus);
+		if (ret < 0) {
+			dev_notice(info->dev, "%s: get ibus fail\n", __func__);
+			return ret;
+		}
+		if (adc_ibus < 100000) { /* 100mA */
+			ret = rt9467_toggle_cfo(info);
+			return ret;
+		}
+	}
+out:
+	return 0;
+}
+
+static int rt9467_chg_aicr_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_chg_treg_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_chg_vsysuv_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_chg_vsysov_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_chg_vbatov_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_chg_vbusov_irq_handler(struct rt9467_info *info)
+{
+	int ret = 0;
+	bool vbusov = false;
+
+	dev_notice(info->dev, "%s\n", __func__);
+	ret = rt9467_i2c_test_bit(info, RT9467_REG_CHG_FAULT,
+		RT9467_SHIFT_VBUSOV, &vbusov);
+	if (ret < 0)
+		return ret;
+
+	dev_info(info->dev, "%s: vbusov = %d\n", __func__, vbusov);
+
+	return 0;
+}
+
+static int rt9467_ts_bat_cold_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_ts_bat_cool_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_ts_bat_warm_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_ts_bat_hot_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_ts_statci_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_chg_faulti_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_chg_statci_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_chg_tmri_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_chg_batabsi_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_chg_adpbadi_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_chg_rvpi_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_chg_otpi_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_chg_aiclmeasi_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_chg_ichgmeasi_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_chgdet_donei_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_wdtmri_irq_handler(struct rt9467_info *info)
+{
+	int ret = 0;
+
+	dev_notice(info->dev, "%s\n", __func__);
+	ret = rt9467_kick_wdt(info);
+	if (ret < 0)
+		dev_notice(info->dev, "%s: kick wdt fail\n", __func__);
+
+	return ret;
+}
+
+static int rt9467_ssfinishi_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_chg_rechgi_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_chg_termi_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_chg_ieoci_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_adc_donei_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_pumpx_donei_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_bst_batuvi_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_bst_midovi_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_bst_olpi_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_attachi_irq_handler(struct rt9467_info *info)
+{
+	int ret = 0;
+
+	dev_notice(info->dev, "%s\n", __func__);
+	return ret;
+}
+
+static int rt9467_detachi_irq_handler(struct rt9467_info *info)
+{
+	int ret = 0;
+
+	dev_notice(info->dev, "%s\n", __func__);
+	return ret;
+}
+
+static int rt9467_chgdeti_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int rt9467_dcdti_irq_handler(struct rt9467_info *info)
+{
+	dev_notice(info->dev, "%s\n", __func__);
+	return 0;
+}
+
+typedef int (*rt9467_irq_fptr)(struct rt9467_info *);
+static rt9467_irq_fptr rt9467_irq_handler_tbl[56] = {
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	rt9467_chg_treg_irq_handler,
+	rt9467_chg_aicr_irq_handler,
+	rt9467_chg_mivr_irq_handler,
+	rt9467_pwr_rdy_irq_handler,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	rt9467_chg_vsysuv_irq_handler,
+	rt9467_chg_vsysov_irq_handler,
+	rt9467_chg_vbatov_irq_handler,
+	rt9467_chg_vbusov_irq_handler,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	rt9467_ts_bat_cold_irq_handler,
+	rt9467_ts_bat_cool_irq_handler,
+	rt9467_ts_bat_warm_irq_handler,
+	rt9467_ts_bat_hot_irq_handler,
+	rt9467_ts_statci_irq_handler,
+	rt9467_chg_faulti_irq_handler,
+	rt9467_chg_statci_irq_handler,
+	rt9467_chg_tmri_irq_handler,
+	rt9467_chg_batabsi_irq_handler,
+	rt9467_chg_adpbadi_irq_handler,
+	rt9467_chg_rvpi_irq_handler,
+	rt9467_chg_otpi_irq_handler,
+	rt9467_chg_aiclmeasi_irq_handler,
+	rt9467_chg_ichgmeasi_irq_handler,
+	rt9467_chgdet_donei_irq_handler,
+	rt9467_wdtmri_irq_handler,
+	rt9467_ssfinishi_irq_handler,
+	rt9467_chg_rechgi_irq_handler,
+	rt9467_chg_termi_irq_handler,
+	rt9467_chg_ieoci_irq_handler,
+	rt9467_adc_donei_irq_handler,
+	rt9467_pumpx_donei_irq_handler,
+	NULL,
+	NULL,
+	NULL,
+	rt9467_bst_batuvi_irq_handler,
+	rt9467_bst_midovi_irq_handler,
+	rt9467_bst_olpi_irq_handler,
+	rt9467_attachi_irq_handler,
+	rt9467_detachi_irq_handler,
+	NULL,
+	NULL,
+	NULL,
+	rt9467_chgdeti_irq_handler,
+	rt9467_dcdti_irq_handler,
+};
+
+static inline int rt9467_enable_irqrez(struct rt9467_info *info, bool en)
+{
+	dev_info(info->dev, "%s: en = %d\n", __func__, en);
+	return (en ? rt9467_set_bit : rt9467_clr_bit)
+		(info, RT9467_REG_CHG_CTRL13, RT9467_MASK_IRQ_REZ);
+}
+
+static int __rt9467_irq_handler(struct rt9467_info *info)
+{
+	int ret = 0, i = 0, j = 0;
+	u8 evt[RT9467_IRQIDX_MAX] = {0};
+	u8 mask[RT9467_IRQIDX_MAX] = {0};
+	u8 stat[RT9467_IRQSTAT_MAX] = {0};
+	u8 usb_status_old = 0, usb_status_new = 0;
+
+	dev_info(info->dev, "%s\n", __func__);
+
+	/* Read DPDM status before reading evts */
+	ret = rt9467_i2c_read_byte(info, RT9467_REG_CHG_DPDM2);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: read type fail\n", __func__);
+		goto err_read_irq;
+	}
+	usb_status_old = (ret & RT9467_MASK_USB_STATUS) >>
+		RT9467_SHIFT_USB_STATUS;
+
+	/* Read event and skip CHG_IRQ3 */
+	ret = rt9467_i2c_block_read(info, RT9467_REG_CHG_IRQ1, 2, &evt[3]);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: read evt1 fail(%d)\n", __func__,
+			ret);
+		goto err_read_irq;
+	}
+
+	ret = rt9467_i2c_block_read(info, RT9467_REG_DPDM_IRQ, 1, &evt[6]);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: read evt2 fail(%d)\n", __func__,
+			ret);
+		goto err_read_irq;
+	}
+
+	ret = rt9467_i2c_block_read(info, RT9467_REG_CHG_STATC, 3, evt);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: read stat fail(%d)\n", __func__,
+			ret);
+		goto err_read_irq;
+	}
+
+	/* Read DPDM status after reading evts */
+	ret = rt9467_i2c_read_byte(info, RT9467_REG_CHG_DPDM2);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: read type fail\n", __func__);
+		goto err_read_irq;
+	}
+	usb_status_new = (ret & RT9467_MASK_USB_STATUS) >>
+		RT9467_SHIFT_USB_STATUS;
+
+	/* Read mask */
+	ret = rt9467_i2c_block_read(info, RT9467_REG_CHG_STATC_CTRL,
+		ARRAY_SIZE(mask), mask);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: read mask fail(%d)\n", __func__,
+			ret);
+		goto err_read_irq;
+	}
+
+	/* Detach */
+	if (usb_status_old != RT9467_CHG_TYPE_NOVBUS &&
+		usb_status_new == RT9467_CHG_TYPE_NOVBUS)
+		evt[RT9467_IRQIDX_DPDM_IRQ] |= 0x02;
+
+	/* Attach */
+	if (usb_status_new >= RT9467_CHG_TYPE_SDP &&
+		usb_status_new <= RT9467_CHG_TYPE_CDP &&
+		usb_status_old != usb_status_new)
+		evt[RT9467_IRQIDX_DPDM_IRQ] |= 0x01;
+
+	/* Store/Update stat */
+	memcpy(stat, info->irq_stat, RT9467_IRQSTAT_MAX);
+
+	for (i = 0; i < RT9467_IRQIDX_MAX; i++) {
+		evt[i] &= ~mask[i];
+		if (i < RT9467_IRQSTAT_MAX) {
+			info->irq_stat[i] = evt[i];
+			evt[i] ^= stat[i];
+		}
+		for (j = 0; j < 8; j++) {
+			if (!(evt[i] & (1 << j)))
+				continue;
+			if (rt9467_irq_handler_tbl[i * 8 + j])
+				rt9467_irq_handler_tbl[i * 8 + j](info);
+		}
+	}
+
+err_read_irq:
+	return ret;
+}
+
+static irqreturn_t rt9467_irq_handler(int irq, void *data)
+{
+	int ret = 0;
+	struct rt9467_info *info = (struct rt9467_info *)data;
+
+	dev_info(info->dev, "%s\n", __func__);
+
+	ret = __rt9467_irq_handler(info);
+	ret = rt9467_enable_irqrez(info, true);
+	if (ret < 0)
+		dev_notice(info->dev, "%s: en irqrez fail\n", __func__);
+
+	return IRQ_HANDLED;
+}
+
+static int rt9467_irq_register(struct rt9467_info *info)
+{
+	int ret = 0, len = 0;
+	char *name = NULL;
+
+	if (strcmp(info->desc->chg_dev_name, "secondary_chg") == 0)
+		return 0;
+
+	dev_info(info->dev, "%s\n", __func__);
+
+	/* request gpio */
+	len = strlen(info->desc->chg_dev_name);
+	name = devm_kzalloc(info->dev, len + 10, GFP_KERNEL);
+	snprintf(name,  len + 10, "%s_irq_gpio", info->desc->chg_dev_name);
+	ret = devm_gpio_request_one(info->dev, info->intr_gpio, GPIOF_IN, name);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: gpio request fail\n", __func__);
+		return ret;
+	}
+
+	ret = gpio_to_irq(info->intr_gpio);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: irq mapping fail\n", __func__);
+		return ret;
+	}
+	info->irq = ret;
+	dev_info(info->dev, "%s: irq = %d\n", __func__, info->irq);
+
+	/* Request threaded IRQ */
+	name = devm_kzalloc(info->dev, len + 5, GFP_KERNEL);
+	snprintf(name, len + 5, "%s_irq", info->desc->chg_dev_name);
+	ret = devm_request_threaded_irq(info->dev, info->irq, NULL,
+		rt9467_irq_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, name,
+		info);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: request thread irq fail\n",
+			__func__);
+		return ret;
+	}
+	device_init_wakeup(info->dev, true);
+
+	return 0;
+}
+
+static inline int rt9467_maskall_irq(struct rt9467_info *info)
+{
+	dev_info(info->dev, "%s\n", __func__);
+	return rt9467_i2c_block_write(info, RT9467_REG_CHG_STATC_CTRL,
+		ARRAY_SIZE(rt9467_irq_maskall), rt9467_irq_maskall);
+}
+
+static inline int rt9467_irq_init(struct rt9467_info *info)
+{
+	dev_info(info->dev, "%s\n", __func__);
+	return rt9467_i2c_block_write(info, RT9467_REG_CHG_STATC_CTRL,
+		ARRAY_SIZE(info->irq_mask), info->irq_mask);
+}
+
+static bool rt9467_is_hw_exist(struct rt9467_info *info)
+{
+	int ret = 0;
+	u8 vendor_id = 0, chip_rev = 0;
+
+	ret = i2c_smbus_read_byte_data(info->client, RT9467_REG_DEVICE_ID);
+	if (ret < 0)
+		return false;
+
+	vendor_id = ret & 0xF0;
+	chip_rev = ret & 0x0F;
+	if (vendor_id != RT9467_VENDOR_ID) {
+		dev_notice(info->dev, "%s: vendor id is incorrect (0x%02X)\n",
+			__func__, vendor_id);
+		return false;
+	}
+
+	dev_info(info->dev, "%s: 0x%02X\n", __func__, chip_rev);
+	info->chip_rev = chip_rev;
+
+	return true;
+}
+
+static inline int rt9467_enable_wdt(struct rt9467_info *info, bool en)
+{
+	dev_info(info->dev, "%s: en = %d\n", __func__, en);
+	return (en ? rt9467_set_bit : rt9467_clr_bit)
+		(info, RT9467_REG_CHG_CTRL13, RT9467_MASK_WDT_EN);
+}
+
+static int rt9467_enable_hidden_mode(struct rt9467_info *info, bool en)
+{
+	int ret = 0;
+
+	mutex_lock(&info->hidden_mode_lock);
+
+	if (en) {
+		if (info->hidden_mode_cnt == 0) {
+			ret = rt9467_i2c_block_write(info, 0x70,
+				ARRAY_SIZE(rt9467_val_en_hidden_mode),
+				rt9467_val_en_hidden_mode);
+			if (ret < 0)
+				goto err;
+		}
+		info->hidden_mode_cnt++;
+	} else {
+		if (info->hidden_mode_cnt == 1) /* last one */
+			ret = rt9467_i2c_write_byte(info, 0x70, 0x00);
+		info->hidden_mode_cnt--;
+		if (ret < 0)
+			goto err;
+	}
+	dev_dbg(info->dev, "%s: en = %d\n", __func__, en);
+	goto out;
+
+err:
+	dev_notice(info->dev, "%s: en = %d fail(%d)\n", __func__, en, ret);
+out:
+	mutex_unlock(&info->hidden_mode_lock);
+	return ret;
+}
+
+static int rt9467_sw_workaround(struct rt9467_info *info)
+{
+	int ret = 0;
+
+	dev_info(info->dev, "%s\n", __func__);
+
+	rt9467_enable_hidden_mode(info, true);
+
+	/* Modify UG driver */
+	ret = rt9467_i2c_update_bits(info, RT9467_REG_CHG_HIDDEN_CTRL4, 0xC0,
+		0xF0);
+	if (ret < 0)
+		dev_notice(info->dev, "%s: set UG driver fail\n", __func__);
+
+	ret = rt9467_i2c_read_byte(info, RT9467_REG_CHG_HIDDEN_CTRL4);
+	dev_info(info->dev, "%s: reg0x23 = 0x%02X\n", __func__, ret);
+
+	/* Disable TS auto sensing */
+	ret = rt9467_clr_bit(info, RT9467_REG_CHG_HIDDEN_CTRL15, 0x01);
+	if (ret < 0)
+		goto out;
+
+	/* Only revision <= E1 needs the following workaround */
+	if (info->chip_rev > RT9467_CHIP_REV_E1)
+		goto out;
+
+	/* ICC: modify sensing node, make it more accurate */
+	ret = rt9467_i2c_write_byte(info, RT9467_REG_CHG_HIDDEN_CTRL8, 0x00);
+	if (ret < 0)
+		goto out;
+
+	/* DIMIN level */
+	ret = rt9467_i2c_write_byte(info, RT9467_REG_CHG_HIDDEN_CTRL9, 0x86);
+
+out:
+	rt9467_enable_hidden_mode(info, false);
+	return ret;
+}
+
+static inline int rt9467_enable_hz(struct rt9467_info *info, bool en)
+{
+	dev_info(info->dev, "%s: en = %d\n", __func__, en);
+	return (en ? rt9467_set_bit : rt9467_clr_bit)
+		(info, RT9467_REG_CHG_CTRL1, RT9467_MASK_HZ_EN);
+}
+
+/* Reset all registers' value to default */
+static int rt9467_reset_chip(struct rt9467_info *info)
+{
+	int ret = 0;
+
+	dev_info(info->dev, "%s\n", __func__);
+
+	/* disable hz before reset chip */
+	ret = rt9467_enable_hz(info, false);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: disable hz fail\n", __func__);
+		return ret;
+	}
+
+	return rt9467_set_bit(info, RT9467_REG_CORE_CTRL0, RT9467_MASK_RST);
+}
+
+static inline int rt9467_enable_jeita(struct rt9467_info *info, bool en)
+{
+	dev_info(info->dev, "%s: en = %d\n", __func__, en);
+	return (en ? rt9467_set_bit : rt9467_clr_bit)
+		(info, RT9467_REG_CHG_CTRL16, RT9467_MASK_JEITA_EN);
+}
+
+
+static int rt9467_get_charging_status(struct rt9467_info *info,
+	enum rt9467_charging_status *chg_stat)
+{
+	int ret = 0;
+
+	ret = rt9467_i2c_read_byte(info, RT9467_REG_CHG_STAT);
+	if (ret < 0)
+		return ret;
+
+	*chg_stat = (ret & RT9467_MASK_CHG_STAT) >> RT9467_SHIFT_CHG_STAT;
+
+	return ret;
+}
+
+static inline int __rt9467_is_charging_enable(struct rt9467_info *info,
+	bool *en)
+{
+	return rt9467_i2c_test_bit(info, RT9467_REG_CHG_CTRL2,
+		RT9467_SHIFT_CHG_EN, en);
+}
+
+static int __rt9467_get_ichg(struct rt9467_info *info, u32 *ichg)
+{
+	int ret = 0;
+	u8 reg_ichg = 0;
+
+	ret = rt9467_i2c_read_byte(info, RT9467_REG_CHG_CTRL7);
+	if (ret < 0)
+		return ret;
+
+	reg_ichg = (ret & RT9467_MASK_ICHG) >> RT9467_SHIFT_ICHG;
+	*ichg = rt9467_closest_value(RT9467_ICHG_MIN, RT9467_ICHG_MAX,
+		RT9467_ICHG_STEP, reg_ichg);
+
+	return ret;
+}
+
+static inline int rt9467_enable_irq_pulse(struct rt9467_info *info, bool en)
+{
+	dev_info(info->dev, "%s: en = %d\n", __func__, en);
+	return (en ? rt9467_set_bit : rt9467_clr_bit)
+		(info, RT9467_REG_CHG_CTRL1, RT9467_MASK_IRQ_PULSE);
+}
+
+static inline int rt9467_get_irq_number(struct rt9467_info *info,
+	const char *name)
+{
+	int i = 0;
+
+	if (!name) {
+		dev_notice(info->dev, "%s: null name\n", __func__);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(rt9467_irq_mapping_tbl); i++) {
+		if (!strcmp(name, rt9467_irq_mapping_tbl[i].name))
+			return rt9467_irq_mapping_tbl[i].id;
+	}
+
+	return -EINVAL;
+}
+
+static int rt9467_parse_dt(struct rt9467_info *info, struct device *dev)
+{
+	int ret = 0, irq_cnt = 0;
+	struct rt9467_desc *desc = NULL;
+	struct device_node *np = dev->of_node;
+	const char *name = NULL;
+	int irqnum = 0;
+
+	dev_info(info->dev, "%s\n", __func__);
+
+	if (!np) {
+		dev_notice(info->dev, "%s: no device node\n", __func__);
+		return -EINVAL;
+	}
+
+	info->desc = &rt9467_default_desc;
+
+	desc = devm_kzalloc(dev, sizeof(struct rt9467_desc), GFP_KERNEL);
+	if (!desc)
+		return -ENOMEM;
+	memcpy(desc, &rt9467_default_desc, sizeof(struct rt9467_desc));
+
+	if (of_property_read_string(np, "charger_name",
+		&desc->chg_dev_name) < 0)
+		dev_notice(info->dev, "%s: no charger name\n", __func__);
+
+#if (!defined(CONFIG_MTK_GPIO) || defined(CONFIG_MTK_GPIOLIB_STAND))
+	ret = of_get_named_gpio(np, "rt,intr_gpio", 0);
+	if (ret < 0)
+		return ret;
+	info->intr_gpio = ret;
+#else
+	ret = of_property_read_u32(np, "rt,intr_gpio_num", &info->intr_gpio);
+	if (ret < 0)
+		return ret;
+#endif
+
+	dev_info(info->dev, "%s: intr gpio = %d\n", __func__,
+		info->intr_gpio);
+
+	desc->en_wdt = of_property_read_bool(np, "en_wdt");
+	desc->en_irq_pulse = of_property_read_bool(np, "en_irq_pulse");
+
+	while (true) {
+		ret = of_property_read_string_index(np, "interrupt-names",
+			irq_cnt, &name);
+		if (ret < 0)
+			break;
+		irq_cnt++;
+		irqnum = rt9467_get_irq_number(info, name);
+		if (irqnum >= 0)
+			rt9467_irq_unmask(info, irqnum);
+	}
+
+	info->desc = desc;
+
+	return 0;
+}
+
+
+/* =========================================================== */
+/* Released interfaces                                         */
+/* =========================================================== */
+
+static int rt9467_set_boost_current_limit(struct rt9467_info *info,
+	u32 current_limit)
+{
+	u8 reg_ilimit = 0;
+	int ret = 0;
+
+	reg_ilimit = rt9467_closest_reg_via_tbl(rt9467_boost_oc_threshold,
+		ARRAY_SIZE(rt9467_boost_oc_threshold), current_limit);
+
+	dev_info(info->dev, "%s: boost ilimit = %d(0x%02X)\n", __func__,
+		current_limit, reg_ilimit);
+
+	/*adjust inducer ocp level with different current limit*/
+	ret = ((current_limit >= 2100000) ? rt9467_set_bit :rt9467_clr_bit)
+		(info, RT9467_REG_CHG_CTRL13, RT9467_MASK_OCP);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: set ocp fail\n", __func__);
+		return ret;
+	}
+
+	return rt9467_i2c_update_bits(info, RT9467_REG_CHG_CTRL10,
+		reg_ilimit << RT9467_SHIFT_BOOST_OC, RT9467_MASK_BOOST_OC);
+}
+
+static int rt9467_enable_otg(struct rt9467_info *info, bool en)
+{
+	int ret = 0;
+	bool en_otg = false;
+	u8 hidden_val = en ? 0x00 : 0x0F;
+	u8 lg_slew_rate = en ? 0xCC : 0xC3;
+
+	dev_info(info->dev, "%s: en = %d\n", __func__, en);
+
+	rt9467_enable_hidden_mode(info, true);
+
+	/* Set OTG_OC to 1300mA */
+	ret = rt9467_set_boost_current_limit(info, 1300000);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: set current limit fail\n", __func__);
+		return ret;
+	}
+
+	/*
+	 * Woraround : slow Low side mos Gate driver slew rate
+	 * for decline VBUS noise
+	 * reg[0x23] = 0xCC after entering OTG mode
+	 * reg[0x23] = 0xC3 after leaving OTG mode
+	 */
+	ret = rt9467_i2c_write_byte(info, RT9467_REG_CHG_HIDDEN_CTRL4,
+		lg_slew_rate);
+	if (ret < 0) {
+		dev_notice(info->dev,
+			"%s: set Low side mos Gate drive speed fail(%d)\n",
+			__func__, ret);
+		goto out;
+	}
+
+	/* Enable WDT */
+	if (en && info->desc->en_wdt) {
+		ret = rt9467_enable_wdt(info, true);
+		if (ret < 0) {
+			dev_notice(info->dev, "%s: en wdt fail\n", __func__);
+			goto err_en_otg;
+		}
+	}
+
+	/* Switch OPA mode */
+	ret = (en ? rt9467_set_bit : rt9467_clr_bit)
+		(info, RT9467_REG_CHG_CTRL1, RT9467_MASK_OPA_MODE);
+
+	msleep(20);
+
+	if (en) {
+		ret = rt9467_i2c_test_bit(info, RT9467_REG_CHG_CTRL1,
+			RT9467_SHIFT_OPA_MODE, &en_otg);
+		if (ret < 0 || !en_otg) {
+			dev_notice(info->dev, "%s: otg fail(%d)\n", __func__,
+				ret);
+			goto err_en_otg;
+		}
+	}
+
+	/*
+	 * Woraround reg[0x25] = 0x00 after entering OTG mode
+	 * reg[0x25] = 0x0F after leaving OTG mode
+	 */
+	ret = rt9467_i2c_write_byte(info, RT9467_REG_CHG_HIDDEN_CTRL6,
+		hidden_val);
+	if (ret < 0)
+		dev_notice(info->dev, "%s: workaroud fail(%d)\n", __func__,
+			ret);
+
+	/* Disable WDT */
+	if (!en) {
+		ret = rt9467_enable_wdt(info, false);
+		if (ret < 0)
+			dev_notice(info->dev, "%s: disable wdt fail\n",
+				__func__);
+	}
+	goto out;
+
+err_en_otg:
+	/* Disable WDT */
+	ret = rt9467_enable_wdt(info, false);
+	if (ret < 0)
+		dev_notice(info->dev, "%s: disable wdt fail\n", __func__);
+
+	/* Recover Low side mos Gate slew rate */
+	ret = rt9467_i2c_write_byte(info, RT9467_REG_CHG_HIDDEN_CTRL4, 0x73);
+	if (ret < 0)
+		dev_notice(info->dev,
+			"%s: recover Low side mos Gate drive speed fail(%d)\n",
+			__func__, ret);
+	ret = -EIO;
+out:
+	rt9467_enable_hidden_mode(info, false);
+	return ret;
+}
+
+static int rt9467_enable_discharge(struct rt9467_info *info, bool en)
+{
+	int ret = 0, i = 0;
+	const int check_dischg_max = 3;
+	bool is_dischg = true;
+
+	dev_info(info->dev, "%s: en = %d\n", __func__, en);
+
+	ret = rt9467_enable_hidden_mode(info, true);
+	if (ret < 0)
+		return ret;
+
+	/* Set bit2 of reg[0x21] to 1 to enable discharging */
+	ret = (en ? rt9467_set_bit : rt9467_clr_bit)(info,
+		RT9467_REG_CHG_HIDDEN_CTRL2, 0x04);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: en = %d, fail\n", __func__, en);
+		return ret;
+	}
+
+	if (!en) {
+		for (i = 0; i < check_dischg_max; i++) {
+			ret = rt9467_i2c_test_bit(info,
+				RT9467_REG_CHG_HIDDEN_CTRL2, 2, &is_dischg);
+			if (ret >= 0 && !is_dischg)
+				break;
+			/* Disable discharging */
+			ret = rt9467_clr_bit(info, RT9467_REG_CHG_HIDDEN_CTRL2,
+				0x04);
+		}
+		if (i == check_dischg_max)
+			dev_notice(info->dev, "%s: disable dischg fail(%d)\n",
+				__func__, ret);
+	}
+
+	rt9467_enable_hidden_mode(info, false);
+	return ret;
+}
+
+static int rt9467_get_ichg(struct rt9467_info *info, u32 *ichg)
+{
+	return __rt9467_get_ichg(info, ichg);
+}
+
+static int rt9467_get_aicr(struct rt9467_info *info, u32 *aicr)
+{
+	int ret = 0;
+	u8 reg_aicr = 0;
+
+	ret = rt9467_i2c_read_byte(info, RT9467_REG_CHG_CTRL3);
+	if (ret < 0)
+		return ret;
+
+	reg_aicr = (ret & RT9467_MASK_AICR) >> RT9467_SHIFT_AICR;
+	*aicr = rt9467_closest_value(RT9467_AICR_MIN, RT9467_AICR_MAX,
+		RT9467_AICR_STEP, reg_aicr);
+
+	return ret;
+}
+
+
+#if 0
+static int rt9467_get_ibat(struct charger_device *chg_dev, u32 *ibat)
+{
+	int ret = 0, adc_ibat = 0;
+	struct rt9467_info *info = dev_get_drvdata(&chg_dev->dev);
+
+	/* Get value from ADC */
+	ret = rt9467_get_adc(info, RT9467_ADC_IBAT, &adc_ibat);
+	if (ret < 0)
+		return ret;
+
+	*ibat = adc_ibat;
+
+	dev_info(info->dev, "%s: ibat = %dmA\n", __func__, adc_ibat);
+	return ret;
+}
+#endif
+
+#if 0 /* Uncomment if you need this API */
+static int rt9467_get_vbus(struct charger_device *chg_dev, u32 *vbus)
+{
+	int ret = 0, adc_vbus = 0;
+	struct rt9467_info *info = dev_get_drvdata(&chg_dev->dev);
+
+	/* Get value from ADC */
+	ret = rt9467_get_adc(info, RT9467_ADC_VBUS_DIV2, &adc_vbus);
+	if (ret < 0)
+		return ret;
+
+	*vbus = adc_vbus;
+
+	dev_info(info->dev, "%s: vbus = %dmA\n", __func__, adc_vbus);
+	return ret;
+}
+#endif
+
+static int rt9467_kick_wdt(struct rt9467_info *info)
+{
+	enum rt9467_charging_status chg_status = RT9467_CHG_STATUS_READY;
+
+	/* Any I2C communication can reset watchdog timer */
+	return rt9467_get_charging_status(info, &chg_status);
+}
+
+static int __rt9467_enable_auto_sensing(struct rt9467_info *info, bool en)
+{
+	int ret = 0;
+	u8 auto_sense = 0;
+	u8 *data = 0x00;
+
+	/* enter hidden mode */
+	ret = rt9467_device_write(info->client, 0x70,
+		ARRAY_SIZE(rt9467_val_en_hidden_mode),
+		rt9467_val_en_hidden_mode);
+	if (ret < 0)
+		return ret;
+
+	ret = rt9467_device_read(info->client, RT9467_REG_CHG_HIDDEN_CTRL15, 1,
+		&auto_sense);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: read auto sense fail\n", __func__);
+		goto out;
+	}
+
+	if (en)
+		auto_sense &= 0xFE; /* clear bit0 */
+	else
+		auto_sense |= 0x01; /* set bit0 */
+	ret = rt9467_device_write(info->client, RT9467_REG_CHG_HIDDEN_CTRL15, 1,
+		&auto_sense);
+	if (ret < 0)
+		dev_notice(info->dev, "%s: en = %d fail\n", __func__, en);
+
+out:
+	return rt9467_device_write(info->client, 0x70, 1, &data);
+}
+
+/*
+ * This function is used in shutdown function
+ * Use i2c smbus directly
+ */
+static int rt9467_sw_reset(struct rt9467_info *info)
+{
+	int ret = 0;
+	u8 evt[RT9467_IRQIDX_MAX] = {0};
+
+	/* Register 0x01 ~ 0x10 */
+	u8 reg_data[] = {
+		0x10, 0x03, 0x23, 0x3C, 0x67, 0x0B, 0x4C, 0xA1,
+		0x3C, 0x58, 0x2C, 0x02, 0x52, 0x05, 0x00, 0x10
+	};
+
+	dev_info(info->dev, "%s\n", __func__);
+
+	/* Disable auto sensing/Enable HZ,ship mode of secondary charger */
+	if (strcmp(info->desc->chg_dev_name, "secondary_chg") == 0) {
+		mutex_lock(&info->hidden_mode_lock);
+		mutex_lock(&info->i2c_access_lock);
+		__rt9467_enable_auto_sensing(info, false);
+		mutex_unlock(&info->i2c_access_lock);
+		mutex_unlock(&info->hidden_mode_lock);
+
+		reg_data[0] = 0x14; /* HZ */
+		reg_data[1] = 0x83; /* Shipping mode */
+	}
+
+	/* Mask all irq */
+	mutex_lock(&info->i2c_access_lock);
+	ret = rt9467_device_write(info->client, RT9467_REG_CHG_STATC_CTRL,
+		ARRAY_SIZE(rt9467_irq_maskall), rt9467_irq_maskall);
+	if (ret < 0)
+		dev_notice(info->dev, "%s: mask all irq fail\n", __func__);
+
+	/* Read all irq */
+	ret = rt9467_device_read(info->client, RT9467_REG_CHG_STATC, 5, evt);
+	if (ret < 0)
+		dev_notice(info->dev, "%s: read evt1 fail(%d)\n", __func__,
+			ret);
+
+	ret = rt9467_device_read(info->client, RT9467_REG_DPDM_IRQ, 1, &evt[6]);
+	if (ret < 0)
+		dev_notice(info->dev, "%s: read evt2 fail(%d)\n", __func__,
+			ret);
+
+	/* Reset necessary registers */
+	ret = rt9467_device_write(info->client, RT9467_REG_CHG_CTRL1,
+		ARRAY_SIZE(reg_data), reg_data);
+	if (ret < 0)
+		dev_notice(info->dev, "%s: reset registers fail\n", __func__);
+	mutex_unlock(&info->i2c_access_lock);
+
+	return ret;
+}
+
+static int rt9467_init_setting(struct rt9467_info *info)
+{
+	int ret = 0;
+	struct rt9467_desc *desc = info->desc;
+	u8 evt[RT9467_IRQIDX_MAX] = {0};
+
+	dev_info(info->dev, "%s\n", __func__);
+
+	ret = rt9467_clr_bit(info, RT9467_REG_CHG_CTRL2, RT9467_MASK_CHG_EN);
+	if (ret < 0) {	
+		dev_notice(info->dev, "%s: disable chg_en fail\n",
+			__func__);
+		goto err;
+	}
+
+	/* disable USB charger type detection before reset IRQ */
+	ret = __rt9467_enable_chgdet_flow(info, false);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: disable usb chrdet fail\n",
+			__func__);
+		goto err;
+	}
+
+	ret = rt9467_clr_bit(info, RT9467_REG_CHG_DPDM1, 0x40);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: disable attach delay fail\n",
+			__func__);
+		goto err;
+	}
+
+	/* mask all irq */
+	ret = rt9467_maskall_irq(info);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: mask all irq fail\n", __func__);
+		goto err;
+	}
+
+	/* clear event */
+	ret = rt9467_i2c_block_read(info, RT9467_REG_CHG_STATC, ARRAY_SIZE(evt),
+		evt);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: clr evt fail(%d)\n", __func__, ret);
+		goto err;
+	}
+
+	ret = rt9467_enable_irq_pulse(info, desc->en_irq_pulse);
+	if (ret < 0)
+		dev_notice(info->dev, "%s: set irq pulse fail\n", __func__);
+
+	ret = rt9467_sw_workaround(info);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: workaround fail\n", __func__);
+		return ret;
+	}
+
+err:
+	return ret;
+}
+
+int rt9467_enable_regulator_otg(struct regulator_dev *rdev)
+{
+	int ret = 0;
+	struct rt9467_info *info = rdev_get_drvdata(rdev);
+
+	dev_info(info->dev, "%s\n", __func__);
+
+	ret = rt9467_enable_discharge(info, false);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: disable discharge fail\n", __func__);
+		return ret;
+	}
+
+	return rt9467_enable_otg(info, true);
+}
+
+int rt9467_disable_regulator_otg(struct regulator_dev *rdev)
+{
+	int ret = 0;
+	struct rt9467_info *info = rdev_get_drvdata(rdev);
+
+	dev_info(info->dev, "%s\n", __func__);
+
+	ret = rt9467_enable_otg(info, false);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: disable otg fail\n", __func__);
+		return ret;
+	}
+
+	return rt9467_enable_discharge(info, true);
+}
+
+static int rt9467_set_current_limit(struct regulator_dev *rdev,
+						int min_uA, int max_uA)
+{
+	struct rt9467_info *info = rdev_get_drvdata(rdev);
+	int num = ARRAY_SIZE(rt9467_boost_oc_threshold);
+
+	if (min_uA < rt9467_boost_oc_threshold[0])
+		min_uA = rt9467_boost_oc_threshold[0];
+	else if (min_uA > rt9467_boost_oc_threshold[num - 1])
+		min_uA = rt9467_boost_oc_threshold[num - 1];
+
+	return rt9467_set_boost_current_limit(info, min_uA);
+}
+
+static int rt9467_get_current_limit(struct regulator_dev *rdev)
+{
+	int ret = 0, val = 0;
+	struct rt9467_info *info = rdev_get_drvdata(rdev);
+
+	ret = rt9467_i2c_read_byte(info, RT9467_REG_CHG_CTRL10);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: read otg_cc fail\n", __func__);
+		return ret;
+	}
+	val = (ret & RT9467_MASK_BOOST_OC) >> RT9467_SHIFT_BOOST_OC;
+	return rt9467_boost_oc_threshold[val];
+}
+
+static const struct regulator_ops rt9467_chg_otg_ops = {
+	.enable = rt9467_enable_regulator_otg,
+	.disable = rt9467_disable_regulator_otg,
+	.is_enabled = regulator_is_enabled_regmap,
+	.list_voltage = regulator_list_voltage_linear, 
+	.set_voltage_sel = regulator_set_voltage_sel_regmap,
+	.get_voltage_sel = regulator_get_voltage_sel_regmap,
+	.set_current_limit = rt9467_set_current_limit,
+	.get_current_limit = rt9467_get_current_limit,
+};
+
+static const struct regulator_desc rt9467_otg_rdesc = {
+	.of_match = "usb-otg-vbus",
+	.name = "usb-otg-vbus",
+	.ops = &rt9467_chg_otg_ops,
+	.owner = THIS_MODULE,
+	.type = REGULATOR_VOLTAGE,
+	.min_uV = 4425000,
+	.uV_step = 25000, /* step  25mV */
+	.n_voltages = 57, /* 4425mV to 5825mV */
+	.vsel_reg = RT9467_REG_CHG_CTRL5,
+	.vsel_mask = RT9467_MASK_BOOST_VOREG,
+	.enable_reg = RT9467_REG_CHG_CTRL1,
+	.enable_mask = RT9467_MASK_OPA_MODE,
+};
+/* ========================= */
+/* I2C driver function       */
+/* ========================= */
+
+static int rt9467_probe(struct i2c_client *client,
+	const struct i2c_device_id *dev_id)
+{
+	int ret = 0;
+	struct rt9467_info *info = NULL;
+	struct regulator_config config = { };
+
+	pr_info("%s(%s)\n", __func__, RT9467_DRV_VERSION);
+
+	info = devm_kzalloc(&client->dev, sizeof(struct rt9467_info),
+		GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	mutex_init(&info->i2c_access_lock);
+	mutex_init(&info->adc_access_lock);
+	mutex_init(&info->irq_access_lock);
+	mutex_init(&info->aicr_access_lock);
+	mutex_init(&info->ichg_access_lock);
+	mutex_init(&info->hidden_mode_lock);
+
+	info->client = client;
+	info->dev = &client->dev;
+	info->hidden_mode_cnt = 0;
+	memcpy(info->irq_mask, rt9467_irq_maskall, RT9467_IRQIDX_MAX);
+
+	/* Is HW exist */
+	if (!rt9467_is_hw_exist(info)) {
+		dev_notice(info->dev, "%s: no rt9467 exists\n", __func__);
+		ret = -ENODEV;
+		goto err_no_dev;
+	}
+	i2c_set_clientdata(client, info);
+
+	ret = rt9467_parse_dt(info, &client->dev);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: parse dt fail\n", __func__);
+		goto err_parse_dt;
+	}
+
+	ret = rt9467_reset_chip(info);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: reset chip fail\n", __func__);
+		goto err_reset_chip;
+	}
+
+	ret = rt9467_init_setting(info);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: init setting fail\n", __func__);
+		goto err_init_setting;
+	}
+
+	ret = rt9467_irq_register(info);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: irq register fail\n", __func__);
+		goto err_irq_register;
+	}
+
+	ret = rt9467_irq_init(info);
+	if (ret < 0) {
+		dev_notice(info->dev, "%s: irq init fail\n", __func__);
+		goto err_irq_init;
+	}
+
+	/* otg regulator */
+	config.dev = info->dev;
+	config.driver_data = info;
+	config.regmap = devm_regmap_init_i2c(info->client,
+					    &rt9467_regmap_config);
+
+	info->otg_rdev = devm_regulator_register(info->dev, &rt9467_otg_rdesc,
+						&config);
+	if (IS_ERR(info->otg_rdev)) {
+		dev_notice(info->dev, "%s : regulator register fail\n", __func__);
+		goto err_regulator_dev;
+	}
+
+	dev_info(info->dev, "%s: successfully\n", __func__);
+	return ret;
+
+err_regulator_dev:
+err_irq_init:
+err_irq_register:
+err_init_setting:
+err_reset_chip:
+err_parse_dt:
+err_no_dev:
+	mutex_destroy(&info->i2c_access_lock);
+	mutex_destroy(&info->adc_access_lock);
+	mutex_destroy(&info->irq_access_lock);
+	mutex_destroy(&info->aicr_access_lock);
+	mutex_destroy(&info->ichg_access_lock);
+	mutex_destroy(&info->hidden_mode_lock);
+	return ret;
+}
+
+
+static int rt9467_remove(struct i2c_client *client)
+{
+	int ret = 0;
+	struct rt9467_info *info = i2c_get_clientdata(client);
+
+	pr_info("%s\n", __func__);
+
+	if (info) {
+		mutex_destroy(&info->i2c_access_lock);
+		mutex_destroy(&info->adc_access_lock);
+		mutex_destroy(&info->irq_access_lock);
+		mutex_destroy(&info->aicr_access_lock);
+		mutex_destroy(&info->ichg_access_lock);
+		mutex_destroy(&info->hidden_mode_lock);
+	}
+
+	return ret;
+}
+
+static void rt9467_shutdown(struct i2c_client *client)
+{
+	int ret = 0;
+	struct rt9467_info *info = i2c_get_clientdata(client);
+
+	pr_info("%s\n", __func__);
+	if (info) {
+		ret = rt9467_sw_reset(info);
+		if (ret < 0)
+			pr_notice("%s: sw reset fail\n", __func__);
+	}
+}
+
+static int rt9467_suspend(struct device *dev)
+{
+	struct rt9467_info *info = dev_get_drvdata(dev);
+
+	dev_info(dev, "%s\n", __func__);
+	if (device_may_wakeup(dev))
+		enable_irq_wake(info->irq);
+
+	return 0;
+}
+
+static int rt9467_resume(struct device *dev)
+{
+	struct rt9467_info *info = dev_get_drvdata(dev);
+
+	dev_info(dev, "%s\n", __func__);
+	if (device_may_wakeup(dev))
+		disable_irq_wake(info->irq);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(rt9467_pm_ops, rt9467_suspend, rt9467_resume);
+
+static const struct i2c_device_id rt9467_i2c_id[] = {
+	{"rt9467", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, rt9467_i2c_id);
+
+static const struct of_device_id rt9467_of_match[] = {
+	{ .compatible = "richtek,rt9467", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, rt9467_of_match);
+
+#ifndef CONFIG_OF
+#define RT9467_BUSNUM 1
+
+static struct i2c_board_info rt9467_i2c_board_info __initdata = {
+	I2C_BOARD_INFO("rt9467", RT9467_SALVE_ADDR)
+};
+#endif /* CONFIG_OF */
+
+
+static struct i2c_driver rt9467_i2c_driver = {
+	.driver = {
+		.name = "rt9467",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(rt9467_of_match),
+		.pm = &rt9467_pm_ops,
+	},
+	.probe = rt9467_probe,
+	.remove = rt9467_remove,
+	.shutdown = rt9467_shutdown,
+	.id_table = rt9467_i2c_id,
+};
+
+static int __init rt9467_init(void)
+{
+	int ret = 0;
+
+#ifdef CONFIG_OF
+	pr_info("%s: with dts\n", __func__);
+#else
+	pr_info("%s: without dts\n", __func__);
+	i2c_register_board_info(RT9467_BUSNUM, &rt9467_i2c_board_info, 1);
+#endif
+
+	ret = i2c_add_driver(&rt9467_i2c_driver);
+	if (ret < 0)
+		pr_notice("%s: register i2c driver fail\n", __func__);
+
+	return ret;
+}
+module_init(rt9467_init);
+
+
+static void __exit rt9467_exit(void)
+{
+	i2c_del_driver(&rt9467_i2c_driver);
+}
+module_exit(rt9467_exit);
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("ShuFanLee <shufan_lee@richtek.com>");
+MODULE_DESCRIPTION("RT9467 Charger Driver");
+MODULE_VERSION(RT9467_DRV_VERSION);
+
+/*
+ * Release Note
+ * 1.0.19
+ * (1) Fast toggle chgdet flow
+ * (2) When chg_type is DCP, enable chgdet for D+ 0.6V
+ * (3) mutex_unlock() once in rt9467_attachi_irq_handler()
+ * (4) Lower UG driver slew rate
+ * (5) Show VBUS, CV in rt9467_dump_register()
+ *
+ * 1.0.18
+ * (1) Check tchg 3 times if it >= 120 degree
+ *
+ * 1.0.17
+ * (1) Add ichg workaround
+ *
+ * 1.0.16
+ * (1) Fix type error of enable_auto_sensing in sw_reset
+ * (2) Move irq_mask to info structure
+ * (3) Remove config of Charger_Detect_Init/Release
+ *
+ * 1.0.15
+ * (1) Do ilim select in WQ and register charger class in probe
+ *
+ * 1.0.14
+ * (1) Disable attach delay
+ * (2) Enable IRQ_RZE at the end of irq handler
+ * (3) Remove IRQ related registers from reg_addr
+ * (4) Recheck status in irq handler
+ * (5) Use bc12_access_lock instead of chgdet_lock
+ *
+ * 1.0.13
+ * (1) Add do event interface for polling mode
+ * (2) Check INT pin after reading evt
+ *
+ * 1.0.12
+ * (1) Add MTK_SSUSB config for Charger_Detect_Init/Release
+ *
+ * 1.0.11
+ * (1) Disable psk mode in pep20_reest
+ * (2) For secondary chg, enter shipping mode before shdn
+ * (3) Add BC12 sdp workaround
+ * (4) Remove enabling/disabling ILIM in chgdet_handler
+ *
+ * 1.0.10
+ * (1) Add IEOC workaround
+ * (2) Release set_ieoc/enable_te interface
+ * (3) Fix type errors
+ *
+ * 1.0.9
+ * (1) Add USB workaround for CDP port
+ * (2) Plug in -> usbsw to charger, after chgdet usbsw to AP
+ *     Plug out -> usbsw to AP
+ * (3) Filter out not changed irq state
+ * (4) Not to use CHG_IRQ3
+ *
+ * 1.0.8
+ * (1) Set irq to wake up system
+ * (2) Refine I2C driver related table
+ *
+ * 1.0.7
+ * (1) Enable/Disable ILIM in chgdet_handler
+ *
+ * 1.0.6
+ * (1) Prevent backboot
+ * (2) Add CEB pin control for secondary charger
+ * (3) After PE pattern -> Enable skip mode
+ *     Disable skip mode -> Start PE pattern
+ * (4) Disable BC12 detection before reset IRQ in init_setting
+ *
+ * 1.0.5
+ * (1) Remove wait CDP flow
+ * (2) Add rt9467_chgdet_handler for attachi/detachi
+ * (3) Set secondary chg to HZ if it is not in charging mode
+ * (4) Add is_charging_enabled, get_min_ichg OPS
+ *
+ * 1.0.4
+ * (1) Set ichg&aicr, enable chg before sending PE+ series pattern
+ * (2) Add enable_cable_drop_com OPS
+ *
+ * 1.0.3
+ * (1) IRQs are default unmasked before E4, need to mask them manually
+ *
+ * 1.0.2
+ * (1) Fix AICL workqueue lock issue
+ *
+ * 1.0.1
+ * (1) Fix IRQ init sequence
+ *
+ * 1.0.0
+ * (1) Initial released
+ */