kernel_samsung_a34x-permissive/drivers/power/supply/mediatek/charger/rt9458.c

1260 lines
33 KiB
C
Raw Permalink Normal View History

/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#ifdef CONFIG_RT_REGMAP
#include <mt-plat/rt-regmap.h>
#endif /* #ifdef CONFIG_RT_REGMAP */
#if defined(CONFIG_MTK_GAUGE_VERSION) && (CONFIG_MTK_GAUGE_VERSION == 30)
#include <mt-plat/v1/charger_class.h>
#include "mtk_charger_intf.h"
#endif /* #if (CONFIG_MTK_GAUGE_VERSION == 30) */
#include "rt9458.h"
#define RT9458_DRV_VERSION "1.0.1_MTK"
static bool dbg_log_en;
module_param(dbg_log_en, bool, 0644);
struct rt9458_info {
struct device *dev;
struct i2c_client *i2c;
#ifdef CONFIG_RT_REGMAP
struct rt_regmap_device *regmap_dev;
struct rt_regmap_properties *regmap_props;
#endif /* #ifdef CONFIG_RT_REGMAP */
#if defined(CONFIG_MTK_GAUGE_VERSION) && (CONFIG_MTK_GAUGE_VERSION == 30)
struct charger_device *chg_dev;
#endif /* #if (CONFIG_MTK_GAUGE_VERSION == 30) */
struct mutex io_lock;
int irq;
u8 irq_mask[RT9458_IRQ_REGNUM];
u8 chip_rev;
u8 bst_sel;
};
static const struct rt9458_platform_data rt9458_def_platform_data = {
.chg_name = "primary_chg",
.ichg = 1550000, /* unit: uA */
.aicr = 500000, /* unit: uA */
.mivr = 4500000, /* unit: uV */
.ieoc = 150000, /* unit: uA */
.voreg = 4350000, /* unit : uV */
.vmreg = 4350000, /* unit : uV */
.intr_gpio = -1,
};
#ifdef CONFIG_RT_REGMAP
RT_REG_DECL(RT9458_REG_CTRL1, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9458_REG_CTRL2, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9458_REG_CTRL3, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9458_REG_DEVID, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9458_REG_CTRL4, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9458_REG_CTRL5, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9458_REG_CTRL6, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9458_REG_CTRL7, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9458_REG_IRQ1, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9458_REG_IRQ2, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9458_REG_IRQ3, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9458_REG_MASK1, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9458_REG_MASK2, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9458_REG_MASK3, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9458_REG_CTRL8, 1, RT_VOLATILE, {});
static const rt_register_map_t rt9458_register_map[] = {
RT_REG(RT9458_REG_CTRL1),
RT_REG(RT9458_REG_CTRL2),
RT_REG(RT9458_REG_CTRL3),
RT_REG(RT9458_REG_DEVID),
RT_REG(RT9458_REG_CTRL4),
RT_REG(RT9458_REG_CTRL5),
RT_REG(RT9458_REG_CTRL6),
RT_REG(RT9458_REG_CTRL7),
RT_REG(RT9458_REG_IRQ1),
RT_REG(RT9458_REG_IRQ2),
RT_REG(RT9458_REG_IRQ3),
RT_REG(RT9458_REG_MASK1),
RT_REG(RT9458_REG_MASK2),
RT_REG(RT9458_REG_MASK3),
RT_REG(RT9458_REG_CTRL8),
};
#define RT9458_REGISTER_NUM ARRAY_SIZE(rt9458_register_map)
static const struct rt_regmap_properties rt9458_regmap_props = {
.aliases = "rt9458",
.register_num = RT9458_REGISTER_NUM,
.rm = rt9458_register_map,
.rt_regmap_mode = RT_SINGLE_BYTE,
};
static int rt9458_io_write(void *i2c, u32 addr, int len, const void *src)
{
return i2c_smbus_write_i2c_block_data(i2c, addr, len, src);
}
static int rt9458_io_read(void *i2c, u32 addr, int len, void *dst)
{
return i2c_smbus_read_i2c_block_data(i2c, addr, len, dst);
}
static struct rt_regmap_fops rt9458_regmap_fops = {
.read_device = rt9458_io_read,
.write_device = rt9458_io_write,
};
#else
#define rt9458_io_write(i2c, addr, len, src) \
i2c_smbus_write_i2c_block_data(i2c, addr, len, src)
#define rt9458_io_read(i2c, addr, len, dst) \
i2c_smbus_read_i2c_block_data(i2c, addr, len, dst)
#endif /* #ifdef CONFIG_RT_REGMAP */
/* common i2c transfer function ++ */
#if 0
static int rt9458_reg_write(struct rt9458_info *ri, u8 reg, u8 data)
{
#ifdef CONFIG_RT_REGMAP
struct rt_reg_data rrd = {0};
return rt_regmap_reg_write(ri->regmap_dev, &rrd, reg, data);
#else
int ret = 0;
mutex_lock(&ri->io_lock);
ret = rt9458_io_write(ri->i2c, reg, 1, &data);
mutex_unlock(&ri->io_lock);
return 0;
#endif /* #ifdef CONFIG_RT_REGMAP */
}
#endif
static int rt9458_reg_block_write(struct rt9458_info *ri, u8 reg,
int len, const void *src)
{
#ifdef CONFIG_RT_REGMAP
return rt_regmap_block_write(ri->regmap_dev, reg, len, src);
#else
int ret = 0;
mutex_lock(&ri->io_lock);
ret = rt9458_io_write(ri->i2c, reg, len, src);
mutex_unlock(&ri->io_lock);
return ret;
#endif /* #ifdef CONFIG_RT_REGMAP */
}
static int rt9458_reg_read(struct rt9458_info *ri, u8 reg)
{
#ifdef CONFIG_RT_REGMAP
struct rt_reg_data rrd = {0};
int ret = 0;
ret = rt_regmap_reg_read(ri->regmap_dev, &rrd, reg);
return (ret < 0 ? ret : rrd.rt_data.data_u32);
#else
u8 data = 0;
int ret = 0;
mutex_lock(&ri->io_lock);
ret = rt9458_io_read(ri->i2c, reg, 1, &data);
mutex_unlock(&ri->io_lock);
return (ret < 0) ? ret : data;
#endif /* #ifdef CONFIG_RT_REGMAP */
}
static int rt9458_reg_block_read(struct rt9458_info *ri, u8 reg,
int len, void *dst)
{
#ifdef CONFIG_RT_REGMAP
return rt_regmap_block_read(ri->regmap_dev, reg, len, dst);
#else
int ret = 0;
mutex_lock(&ri->io_lock);
ret = rt9458_io_read(ri->i2c, reg, len, dst);
mutex_unlock(&ri->io_lock);
return ret;
#endif /* #ifdef CONFIG_RT_REGMAP */
}
static int rt9458_reg_assign_bits(struct rt9458_info *ri,
u8 reg, u8 mask, u8 data)
{
#ifdef CONFIG_RT_REGMAP
struct rt_reg_data rrd = {0};
return rt_regmap_update_bits(ri->regmap_dev, &rrd, reg, mask, data);
#else
u8 orig_data = 0;
int ret = 0;
mutex_lock(&ri->io_lock);
ret = rt9458_io_read(ri->i2c, reg, 1, &orig_data);
if (ret < 0)
goto out_unlock;
orig_data &= (~mask);
orig_data |= (data & mask);
ret = rt9458_io_write(ri->i2c, reg, 1, &orig_data);
out_unlock:
mutex_unlock(&ri->io_lock);
return ret;
#endif /* #ifdef CONFIG_RT_REGMAP */
}
#define rt9458_reg_set_bits(ri, reg, mask) \
rt9458_reg_assign_bits(ri, reg, mask, mask)
#define rt9458_reg_clr_bits(ri, reg, mask) \
rt9458_reg_assign_bits(ri, reg, mask, 0)
/* common i2c transfer function -- */
/* ================== */
/* Internal Functions */
/* ================== */
static const u32 rt9458_support_iaicr[] = {100, 500, 700, 1000};
static inline int rt9458_set_aicr(struct rt9458_info *ri, u32 uA)
{
u8 iaicr_sel, iaicr;
u32 mA = uA / 1000;
int i, ret = 0;
/* change by sw control */
for (i = 1; i < ARRAY_SIZE(rt9458_support_iaicr); i++) {
if (mA < rt9458_support_iaicr[i])
break;
}
if (i == ARRAY_SIZE(rt9458_support_iaicr) &&
mA > rt9458_support_iaicr[i - 1])
dev_dbg(ri->dev, "will config to no limit\n");
else
i--;
switch (i) {
case 0:
iaicr_sel = 0;
iaicr = 0;
break;
case 1:
iaicr_sel = 0;
iaicr = 1;
break;
case 2:
iaicr_sel = 1;
iaicr = 1;
break;
case 3:
iaicr_sel = 0;
iaicr = 2;
break;
case 4:
iaicr_sel = 0;
iaicr = 3;
break;
default:
dev_warn(ri->dev, "illegal selection\n");
return -EINVAL;
}
ret = rt9458_reg_assign_bits(ri, RT9458_REG_CTRL2, RT9458_IAICR_MASK,
iaicr << RT9458_IAICR_SHFT);
if (ret < 0) {
dev_info(ri->dev, "config iaicr fail\n");
return ret;
}
ret = rt9458_reg_assign_bits(ri, RT9458_REG_CTRL6,
RT9458_IAICRSEL_MASK,
(iaicr_sel ? 0xff : 0));
if (ret < 0) {
dev_info(ri->dev, "config iaicr_sel fail\n");
return ret;
}
/* config aicr to internal aicr register */
ret = rt9458_reg_set_bits(ri, RT9458_REG_CTRL2, RT9458_IAICRINT_MASK);
if (ret < 0) {
dev_info(ri->dev, "config iaicr_int fail\n");
return ret;
}
return 0;
}
static inline int rt9458_set_mivr(struct rt9458_info *ri, u32 uV)
{
u8 data = 0;
if (uV >= 4100000) {
data = DIV_ROUND_UP(uV - 4100000, 100000);
if (data > RT9458_MIVR_MAXVAL)
data = RT9458_MIVR_MAXVAL;
} else {
dev_warn(ri->dev, "value is too small, disable mivr\n");
data = 7;
}
return rt9458_reg_assign_bits(ri, RT9458_REG_CTRL8, RT9458_MIVR_MASK,
data << RT9458_MIVR_SHFT);
}
static inline int rt9458_set_ichg(struct rt9458_info *ri, u32 uA)
{
u8 data = 0;
if (uA >= 500000) {
data = (uA - 500000) / 150000;
if (data > RT9458_ICHG_MAXVAL)
data = RT9458_ICHG_MAXVAL;
}
return rt9458_reg_assign_bits(ri, RT9458_REG_CTRL6, RT9458_ICHG_MASK,
data << RT9458_ICHG_SHFT);
}
static inline int rt9458_set_ieoc(struct rt9458_info *ri, u32 uA)
{
u8 data = 0;
if (uA >= 50000) {
data = DIV_ROUND_UP(uA - 50000, 50000);
if (data > RT9458_IEOC_MAXVAL)
data = RT9458_IEOC_MAXVAL;
}
return rt9458_reg_assign_bits(ri, RT9458_REG_CTRL5, RT9458_IEOC_MASK,
data << RT9458_IEOC_SHFT);
}
static inline int rt9458_set_voreg(struct rt9458_info *ri, u32 uV)
{
u8 data = 0;
if (uV >= 4330000) {
data = DIV_ROUND_UP(uV - 4330000, 20000);
data += 0x29;
if (data > RT9458_VOREG_MAXVAL)
data = RT9458_VOREG_MAXVAL;
} else if (uV > 4300000)
data = 0x29;
else if (uV >= 3500000)
data = DIV_ROUND_UP(uV - 3500000, 20000);
return rt9458_reg_assign_bits(ri, RT9458_REG_CTRL3, RT9458_VOREG_MASK,
data << RT9458_VOREG_SHFT);
}
static inline int rt9458_set_vmreg(struct rt9458_info *ri, u32 uV)
{
u8 data = 0;
if (uV >= 4200000) {
data = DIV_ROUND_UP(uV - 4200000, 20000);
if (data > RT9458_VMREG_MAXVAL)
data = RT9458_VOREG_MAXVAL;
}
return rt9458_reg_assign_bits(ri, RT9458_REG_CTRL7, RT9458_VMREG_MASK,
data << RT9458_VMREG_SHFT);
}
static inline int rt9458_set_eoc_shdn(struct rt9458_info *ri, bool en)
{
return rt9458_reg_assign_bits(ri, RT9458_REG_CTRL2, RT9458_TESHDN_MASK,
en ? 0xff : 0);
}
static inline int rt9458_set_chg_enable(struct rt9458_info *ri, bool en)
{
return rt9458_reg_assign_bits(ri, RT9458_REG_CTRL7, RT9458_CHGEN_MASK,
en ? 0xff : 0);
}
#if defined(CONFIG_MTK_GAUGE_VERSION) && (CONFIG_MTK_GAUGE_VERSION == 30)
static int rt9458_charger_is_charging_done(struct charger_device *chg_dev,
bool *done)
{
struct rt9458_info *ri = charger_get_data(chg_dev);
int ret = 0;
ret = rt9458_reg_read(ri, RT9458_REG_CTRL1);
if (ret < 0)
return ret;
ret = (ret & RT9458_STAT_MASK) >> RT9458_STAT_SHFT;
if (ret == 0x02)
*done = true;
else
*done = false;
return 0;
}
static int rt9458_charger_get_charging_stat(struct charger_device *chg_dev,
enum rt9458_chg_stat *chg_stat)
{
struct rt9458_info *ri = charger_get_data(chg_dev);
int ret = 0;
ret = rt9458_reg_read(ri, RT9458_REG_CTRL1);
if (ret < 0)
return ret;
*chg_stat = (ret & RT9458_STAT_MASK) >> RT9458_STAT_SHFT;
return ret;
}
static int rt9458_charger_enable_otg(struct charger_device *chg_dev, bool en)
{
struct rt9458_info *ri = charger_get_data(chg_dev);
int ret = 0;
/* clear HZ mode */
dev_info(ri->dev, "%s: clear HZ mode\n", __func__);
rt9458_reg_clr_bits(ri, RT9458_REG_CTRL2, RT9458_HZ_MASK);
if (en) {
/* store VOREG selector, and switch Vbst to 5V */
ret = rt9458_reg_read(ri, RT9458_REG_CTRL3);
ri->bst_sel = (ret & RT9458_VOREG_MASK) >> RT9458_VOREG_SHFT;
ret = rt9458_reg_assign_bits(ri,
RT9458_REG_CTRL3,
RT9458_VOREG_MASK,
0x17 << RT9458_VOREG_SHFT);
} else /* recover original VOREG */
ret = rt9458_reg_assign_bits(ri,
RT9458_REG_CTRL3,
RT9458_VOREG_MASK,
ri->bst_sel << RT9458_VOREG_SHFT);
return rt9458_reg_assign_bits(ri, RT9458_REG_CTRL2, RT9458_OPA_MASK,
en ? 0xff : 0x00);
}
static int rt9458_charger_enable_te(struct charger_device *chg_dev, bool en)
{
struct rt9458_info *ri = charger_get_data(chg_dev);
struct rt9458_platform_data *pdata = dev_get_platdata(ri->dev);
/* if not specified to enable te function, bypass it */
if (!pdata->enable_te)
return 0;
return rt9458_reg_assign_bits(ri, RT9458_REG_CTRL2, RT9458_TERM_MASK,
en ? 0xff : 0x00);
}
static int rt9458_charger_enable_timer(struct charger_device *chg_dev, bool en)
{
struct rt9458_info *ri = charger_get_data(chg_dev);
return rt9458_reg_assign_bits(ri, RT9458_REG_CTRL5, RT9458_TMREN_MASK,
en ? 0xff : 0);
}
static int rt9458_charger_is_timer_enabled(struct charger_device *chg_dev,
bool *en)
{
struct rt9458_info *ri = charger_get_data(chg_dev);
int ret = 0;
ret = rt9458_reg_read(ri, RT9458_REG_CTRL5);
if (ret < 0)
return ret;
*en = (ret & RT9458_TMREN_MASK) ? true : false;
return 0;
}
static int rt9458_charger_set_mivr(struct charger_device *chg_dev, u32 uV)
{
struct rt9458_info *ri = charger_get_data(chg_dev);
return rt9458_set_mivr(ri, uV);
}
static int rt9458_charger_get_min_aicr(struct charger_device *chg_dev, u32 *uA)
{
*uA = 100000;
return 0;
}
static int rt9458_charger_set_aicr(struct charger_device *chg_dev, u32 uA)
{
struct rt9458_info *ri = charger_get_data(chg_dev);
return rt9458_set_aicr(ri, uA);
}
static int rt9458_charger_get_aicr(struct charger_device *chg_dev, u32 *uA)
{
struct rt9458_info *ri = charger_get_data(chg_dev);
u8 iaicr = 0;
u8 iaicr_sel = 0;
int ret = 0;
ret = rt9458_reg_read(ri, RT9458_REG_CTRL2);
if (ret < 0)
return ret;
iaicr = (ret & RT9458_IAICR_MASK) >> RT9458_IAICR_SHFT;
ret = rt9458_reg_read(ri, RT9458_REG_CTRL6);
if (ret < 0)
return ret;
iaicr_sel = ret & RT9458_IAICRSEL_MASK;
switch (iaicr) {
case 0:
*uA = 100000;
break;
case 1:
if (iaicr_sel)
*uA = 700000;
else
*uA = 500000;
break;
case 2:
if (iaicr_sel)
*uA = 700000;
else
*uA = 1000000;
break;
case 3:
/* no limit */
*uA = U32_MAX;
break;
default:
return -EINVAL;
}
return 0;
}
static int rt9458_charger_get_cv(struct charger_device *chg_dev, u32 *uV)
{
struct rt9458_info *ri = charger_get_data(chg_dev);
int ret = 0;
ret = rt9458_reg_read(ri, RT9458_REG_CTRL3);
if (ret < 0)
return ret;
ret = (ret & RT9458_VOREG_MASK) >> RT9458_VOREG_SHFT;
if (ret > RT9458_VOREG_MAXVAL)
ret = RT9458_VOREG_MAXVAL;
if (ret > 0x28)
*uV = 4330000 + 20000 * (ret - 0x29);
else
*uV = 3500000 + 20000 * ret;
return 0;
}
static int rt9458_charger_set_cv(struct charger_device *chg_dev, u32 uV)
{
struct rt9458_info *ri = charger_get_data(chg_dev);
return rt9458_set_voreg(ri, uV);
}
static int rt9458_charger_get_min_ichg(struct charger_device *chg_dev, u32 *uA)
{
*uA = 500000;
return 0;
}
static int rt9458_charger_set_ichg(struct charger_device *chg_dev, u32 uA)
{
struct rt9458_info *ri = charger_get_data(chg_dev);
return rt9458_set_ichg(ri, uA);
}
static int rt9458_charger_get_ichg(struct charger_device *chg_dev, u32 *uA)
{
struct rt9458_info *ri = charger_get_data(chg_dev);
int ret = 0;
ret = rt9458_reg_read(ri, RT9458_REG_CTRL6);
if (ret < 0)
return ret;
ret = (ret & RT9458_ICHG_MASK) >> RT9458_ICHG_SHFT;
if (ret > RT9458_ICHG_MAXVAL)
ret = RT9458_ICHG_MAXVAL;
*uA = 500000 + (ret * 150000);
return 0;
}
static int rt9458_charger_is_enabled(struct charger_device *chg_dev, bool *en)
{
struct rt9458_info *ri = charger_get_data(chg_dev);
int ret = 0;
ret = rt9458_reg_read(ri, RT9458_REG_CTRL7);
if (ret < 0)
return ret;
*en = (ret & RT9458_CHGEN_MASK) ? true : false;
return 0;
}
static int rt9458_charger_enable(struct charger_device *chg_dev, bool en)
{
struct rt9458_info *ri = charger_get_data(chg_dev);
return rt9458_set_chg_enable(ri, en);
}
static int rt9458_charger_plug_out(struct charger_device *chg_dev)
{
struct rt9458_info *ri = charger_get_data(chg_dev);
struct rt9458_platform_data *pdata = dev_get_platdata(ri->dev);
return pdata->enable_te ? rt9458_charger_enable_te(chg_dev, 0) : 0;
}
static int rt9458_charger_plug_in(struct charger_device *chg_dev)
{
struct rt9458_info *ri = charger_get_data(chg_dev);
struct rt9458_platform_data *pdata = dev_get_platdata(ri->dev);
return pdata->enable_te ? rt9458_charger_enable_te(chg_dev, 1) : 0;
}
static inline int rt9458_get_mivr(struct rt9458_info *ri, u32 *uV)
{
u8 reg_mivr;
int ret = 0;
ret = rt9458_reg_read(ri, RT9458_REG_CTRL8);
if (ret < 0)
return ret;
reg_mivr = (ret & RT9458_MIVR_MASK) >> RT9458_MIVR_SHFT;
/* disable mivr */
if (reg_mivr == 0x07)
*uV = 0;
else
*uV = 4100000 + 100000 * reg_mivr;
return 0;
}
static inline int rt9458_get_ieoc(struct rt9458_info *ri, u32 *uA)
{
int ret = 0;
u8 reg_ieoc = 0;
ret = rt9458_reg_read(ri, RT9458_REG_CTRL5);
if (ret < 0)
return ret;
reg_ieoc = (ret & RT9458_IEOC_MASK) >> RT9458_IEOC_SHFT;
*uA = 50000 + 50000 * reg_ieoc;
return 0;
}
static int rt9458_charger_dump_registers(struct charger_device *chg_dev)
{
struct rt9458_info *ri = charger_get_data(chg_dev);
struct rt9458_platform_data *pdata = dev_get_platdata(ri->dev);
int i = 0, ret = 0;
u32 ichg = 0, aicr = 0, mivr = 0, ieoc = 0, voreg = 0;
enum rt9458_chg_stat chg_stat = RT9458_CHG_STAT_READY;
bool chg_en = 0;
ret = rt9458_charger_get_ichg(chg_dev, &ichg);
if (ret < 0)
dev_info(ri->dev, "get ichg fail\n");
ret = rt9458_charger_get_aicr(chg_dev, &aicr);
if (ret < 0)
dev_info(ri->dev, "get aicr fail\n");
ret = rt9458_get_mivr(ri, &mivr);
if (ret < 0)
dev_info(ri->dev, "get mivr fail\n");
ret = rt9458_charger_is_enabled(chg_dev, &chg_en);
if (ret < 0)
dev_info(ri->dev, "get charger enabled fail\n");
ret = rt9458_get_ieoc(ri, &ieoc);
if (ret < 0)
dev_info(ri->dev, "get ieoc fail\n");
ret = rt9458_charger_get_cv(chg_dev, &voreg);
if (ret < 0)
dev_info(ri->dev, "get cv fail\n");
ret = rt9458_charger_get_charging_stat(chg_dev, &chg_stat);
if (ret < 0 || chg_stat >= RT9458_CHG_STAT_MAX || chg_stat < 0) {
dev_info(ri->dev, "get charger status fail\n");
chg_stat = RT9458_CHG_STAT_READY;
}
if (dbg_log_en) {
for (i = RT9458_REG_CTRL1; i <= RT9458_REG_CTRL8; i++) {
/* byapss ire event register */
if (i >= RT9458_REG_IRQ1 && i <= RT9458_REG_IRQ3)
continue;
/* bypass not existed register */
if (i > RT9458_REG_MASK3 && i < RT9458_REG_CTRL8)
continue;
ret = rt9458_reg_read(ri, i);
if (ret < 0)
continue;
dev_info(ri->dev, "[0x%02X] : 0x%02x\n", i, ret);
}
}
dev_info(ri->dev,
"%s: ICHG = %dmA, AICR = %dmA%s, MIVR = %dmV, IEOC = %dmA\n",
__func__, ichg / 1000, aicr / 1000, (aicr == U32_MAX) ?
" (no limit)" : "", mivr / 1000, ieoc / 1000);
dev_info(ri->dev,
"%s: CV = %dmV, vmreg = %dmV, CHG_EN = %d, CHG_STATUS = %s\n",
__func__, voreg / 1000, pdata->vmreg / 1000, chg_en,
rt9458_chg_stat_name[chg_stat]);
return 0;
}
static int rt9458_charger_do_event(struct charger_device *chg_dev, u32 event,
u32 args)
{
struct rt9458_info *ri = charger_get_data(chg_dev);
switch (event) {
case EVENT_EOC:
dev_info(ri->dev, "do eoc event\n");
charger_dev_notify(ri->chg_dev, CHARGER_DEV_NOTIFY_EOC);
break;
case EVENT_RECHARGE:
dev_info(ri->dev, "do recharge event\n");
charger_dev_notify(ri->chg_dev, CHARGER_DEV_NOTIFY_RECHG);
break;
default:
break;
}
return 0;
}
static const struct charger_ops rt9458_chg_ops = {
/* cable plug in/out */
.plug_in = rt9458_charger_plug_in,
.plug_out = rt9458_charger_plug_out,
/* enable */
.enable = rt9458_charger_enable,
.is_enabled = rt9458_charger_is_enabled,
/* charging current */
.get_charging_current = rt9458_charger_get_ichg,
.set_charging_current = rt9458_charger_set_ichg,
.get_min_charging_current = rt9458_charger_get_min_ichg,
/* charging voltage */
.set_constant_voltage = rt9458_charger_set_cv,
.get_constant_voltage = rt9458_charger_get_cv,
/* charging input current */
.get_input_current = rt9458_charger_get_aicr,
.set_input_current = rt9458_charger_set_aicr,
.get_min_input_current = rt9458_charger_get_min_aicr,
/* charging mivr */
.set_mivr = rt9458_charger_set_mivr,
/* safety timer */
.is_safety_timer_enabled = rt9458_charger_is_timer_enabled,
.enable_safety_timer = rt9458_charger_enable_timer,
/* charing termination */
.enable_termination = rt9458_charger_enable_te,
/* OTG */
.enable_otg = rt9458_charger_enable_otg,
/* misc */
.is_charging_done = rt9458_charger_is_charging_done,
.dump_registers = rt9458_charger_dump_registers,
/* event */
.event = rt9458_charger_do_event,
};
static const struct charger_properties rt9458_chg_props = {
.alias_name = "rt9458",
};
#endif /* #if (CONFIG_MTK_GAUGE_VERSION == 30) */
static irqreturn_t rt9458_irq_BATAB_handler(int irq, void *dev_id)
{
struct rt9458_info *ri = (struct rt9458_info *)dev_id;
dev_info(ri->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t rt9458_irq_VINOVPI_handler(int irq, void *dev_id)
{
struct rt9458_info *ri = (struct rt9458_info *)dev_id;
dev_info(ri->dev, "%s\n", __func__);
#if defined(CONFIG_MTK_GAUGE_VERSION) && (CONFIG_MTK_GAUGE_VERSION == 30)
ri->chg_dev->noti.vbusov_stat = true;
charger_dev_notify(ri->chg_dev, CHARGER_DEV_NOTIFY_VBUS_OVP);
#endif /* #if (CONFIG_MTK_GAUGE_VERSION == 30) */
return IRQ_HANDLED;
}
static irqreturn_t rt9458_irq_TSDI_handler(int irq, void *dev_id)
{
struct rt9458_info *ri = (struct rt9458_info *)dev_id;
dev_info(ri->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t rt9458_irq_CHMIVRI_handler(int irq, void *dev_id)
{
struct rt9458_info *ri = (struct rt9458_info *)dev_id;
dev_info(ri->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t rt9458_irq_CHTREGI_handler(int irq, void *dev_id)
{
struct rt9458_info *ri = (struct rt9458_info *)dev_id;
dev_dbg(ri->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t rt9458_irq_CH32MI_handler(int irq, void *dev_id)
{
struct rt9458_info *ri = (struct rt9458_info *)dev_id;
dev_dbg(ri->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t rt9458_irq_CHRCHGI_handler(int irq, void *dev_id)
{
struct rt9458_info *ri = (struct rt9458_info *)dev_id;
dev_info(ri->dev, "%s\n", __func__);
#if defined(CONFIG_MTK_GAUGE_VERSION) && (CONFIG_MTK_GAUGE_VERSION == 30)
charger_dev_notify(ri->chg_dev, CHARGER_DEV_NOTIFY_RECHG);
#endif /* #if (CONFIG_MTK_GAUGE_VERSION == 30) */
return IRQ_HANDLED;
}
static irqreturn_t rt9458_irq_CHTERMI_handler(int irq, void *dev_id)
{
struct rt9458_info *ri = (struct rt9458_info *)dev_id;
dev_info(ri->dev, "%s\n", __func__);
#if defined(CONFIG_MTK_GAUGE_VERSION) && (CONFIG_MTK_GAUGE_VERSION == 30)
charger_dev_notify(ri->chg_dev, CHARGER_DEV_NOTIFY_EOC);
#endif /* #if (CONFIG_MTK_GAUGE_VERSION == 30) */
return IRQ_HANDLED;
}
static irqreturn_t rt9458_irq_CHBATOVI_handler(int irq, void *dev_id)
{
struct rt9458_info *ri = (struct rt9458_info *)dev_id;
dev_info(ri->dev, "%s\n", __func__);
#if defined(CONFIG_MTK_GAUGE_VERSION) && (CONFIG_MTK_GAUGE_VERSION == 30)
charger_dev_notify(ri->chg_dev, CHARGER_DEV_NOTIFY_BAT_OVP);
#endif /* #if (CONFIG_MTK_GAUGE_VERSION == 30) */
return IRQ_HANDLED;
}
static irqreturn_t rt9458_irq_CHRVPI_handler(int irq, void *dev_id)
{
struct rt9458_info *ri = (struct rt9458_info *)dev_id;
dev_info(ri->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t rt9458_irq_BST32SI_handler(int irq, void *dev_id)
{
struct rt9458_info *ri = (struct rt9458_info *)dev_id;
dev_info(ri->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t rt9458_irq_BSTLOWVI_handler(int irq, void *dev_id)
{
struct rt9458_info *ri = (struct rt9458_info *)dev_id;
dev_dbg(ri->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t rt9458_irq_BSTOLI_handler(int irq, void *dev_id)
{
struct rt9458_info *ri = (struct rt9458_info *)dev_id;
dev_dbg(ri->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static irqreturn_t rt9458_irq_BSTVINOVI_handler(int irq, void *dev_id)
{
struct rt9458_info *ri = (struct rt9458_info *)dev_id;
dev_dbg(ri->dev, "%s\n", __func__);
return IRQ_HANDLED;
}
static const irq_handler_t rt9458_irq_desc[RT9458_IRQ_MAX] = {
[RT9458_IRQ_BATAB] = rt9458_irq_BATAB_handler,
[RT9458_IRQ_VINOVPI] = rt9458_irq_VINOVPI_handler,
[RT9458_IRQ_TSDI] = rt9458_irq_TSDI_handler,
[RT9458_IRQ_CHMIVRI] = rt9458_irq_CHMIVRI_handler,
[RT9458_IRQ_CHTREGI] = rt9458_irq_CHTREGI_handler,
[RT9458_IRQ_CH32MI] = rt9458_irq_CH32MI_handler,
[RT9458_IRQ_CHRCHGI] = rt9458_irq_CHRCHGI_handler,
[RT9458_IRQ_CHTERMI] = rt9458_irq_CHTERMI_handler,
[RT9458_IRQ_CHBATOVI] = rt9458_irq_CHBATOVI_handler,
[RT9458_IRQ_CHRVPI] = rt9458_irq_CHRVPI_handler,
[RT9458_IRQ_BST32SI] = rt9458_irq_BST32SI_handler,
[RT9458_IRQ_BSTLOWVI] = rt9458_irq_BSTLOWVI_handler,
[RT9458_IRQ_BSTOLI] = rt9458_irq_BSTOLI_handler,
[RT9458_IRQ_BSTVINOVI] = rt9458_irq_BSTVINOVI_handler,
};
static irqreturn_t rt9458_intr_handler(int irq, void *dev_id)
{
struct rt9458_info *ri = (struct rt9458_info *)dev_id;
u8 event[RT9458_IRQ_REGNUM] = {0};
int i, j, id, ret = 0;
dev_dbg(ri->dev, "%s triggered\n", __func__);
ret = rt9458_reg_block_read(ri, RT9458_REG_IRQ1,
RT9458_IRQ_REGNUM, event);
if (ret < 0) {
dev_info(ri->dev, "read irq event fail\n");
goto out_intr_handler;
}
for (i = 0; i < RT9458_IRQ_REGNUM; i++) {
event[i] &= ~(ri->irq_mask[i]);
if (!event[i])
continue;
for (j = 0; j < 8; j++) {
id = i * 8 + j;
if (!(event[i] & (1 << j)) ||
(id >= ARRAY_SIZE(rt9458_irq_desc)))
continue;
if (!rt9458_irq_desc[id]) {
dev_warn(ri->dev, "no %d irq_handler", id);
continue;
}
rt9458_irq_desc[id](id, ri);
}
}
out_intr_handler:
return IRQ_HANDLED;
}
static int rt9458_chip_irq_init(struct rt9458_info *ri)
{
struct rt9458_platform_data *pdata = dev_get_platdata(ri->dev);
int ret = 0;
ret = devm_gpio_request_one(ri->dev, pdata->intr_gpio, GPIOF_IN,
"rt9458_intr_gpio");
if (ret < 0) {
dev_info(ri->dev, "request gpio fail\n");
return ret;
}
ri->irq = gpio_to_irq(pdata->intr_gpio);
ret = devm_request_threaded_irq(ri->dev, ri->irq, NULL,
rt9458_intr_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
dev_name(ri->dev), ri);
if (ret < 0) {
dev_info(ri->dev, "request interrupt fail\n");
return ret;
}
device_init_wakeup(ri->dev, true);
ri->irq_mask[0] = 0x00;
ri->irq_mask[1] = 0x19;
ri->irq_mask[2] = 0x00;
return rt9458_reg_block_write(ri, RT9458_REG_MASK1, 3, ri->irq_mask);
}
static int rt9458_register_rt_regmap(struct rt9458_info *ri)
{
#ifdef CONFIG_RT_REGMAP
ri->regmap_props = devm_kzalloc(ri->dev, sizeof(*ri->regmap_props),
GFP_KERNEL);
if (!ri->regmap_props)
return -ENOMEM;
memcpy(ri->regmap_props, &rt9458_regmap_props,
sizeof(*ri->regmap_props));
ri->regmap_props->name = kasprintf(GFP_KERNEL, "rt9458.%s",
dev_name(ri->dev));
ri->regmap_dev = rt_regmap_device_register(ri->regmap_props,
&rt9458_regmap_fops, ri->dev, ri->i2c, ri);
if (!ri->regmap_dev)
return -EINVAL;
return 0;
#else
return 0;
#endif /* #ifdef CONFIG_RT_REGMAP */
}
static int rt9458_chip_pdata_init(struct rt9458_info *ri)
{
struct rt9458_platform_data *pdata = dev_get_platdata(ri->dev);
int ret = 0;
ret = rt9458_set_mivr(ri, pdata->mivr);
if (ret < 0) {
dev_info(ri->dev, "set mivr fail\n");
return ret;
}
ret = rt9458_set_aicr(ri, pdata->aicr);
if (ret < 0) {
dev_info(ri->dev, "set aicr fail\n");
return ret;
}
ret = rt9458_set_vmreg(ri, pdata->vmreg);
if (ret < 0) {
dev_info(ri->dev, "set vmreg fail\n");
return ret;
}
ret = rt9458_set_voreg(ri, pdata->voreg);
if (ret < 0) {
dev_info(ri->dev, "set voreg fail\n");
return ret;
}
ret = rt9458_set_ichg(ri, pdata->ichg);
if (ret < 0) {
dev_info(ri->dev, "set ichg fail\n");
return ret;
}
ret = rt9458_set_ieoc(ri, pdata->ieoc);
if (ret < 0) {
dev_info(ri->dev, "set ieoc fail\n");
return ret;
}
ret = rt9458_set_eoc_shdn(ri, pdata->enable_eoc_shdn);
if (ret < 0)
dev_info(ri->dev, "set eoc shutdown fail\n");
if (!strcmp(pdata->chg_name, "secondary_chg"))
rt9458_set_chg_enable(ri, false);
return ret;
}
static int rt9458_chip_reset(struct i2c_client *i2c)
{
u8 tmp[RT9458_IRQ_REGNUM] = {0};
int ret = 0;
ret = i2c_smbus_write_byte_data(i2c, RT9458_REG_CTRL4, 0x80);
if (ret < 0) {
dev_info(&i2c->dev, "chip reset fail\n");
return ret;
}
msleep(20);
/* default disable safety timer */
ret = i2c_smbus_write_byte_data(i2c, RT9458_REG_CTRL5, 0x02);
if (ret < 0) {
dev_info(&i2c->dev, "default disable timer fail\n");
return ret;
}
memset(tmp, 0xff, RT9458_IRQ_REGNUM);
ret = i2c_smbus_write_i2c_block_data(i2c, RT9458_REG_MASK1,
RT9458_IRQ_REGNUM, tmp);
if (ret < 0) {
dev_info(&i2c->dev, "set all masked fail\n");
return ret;
}
ret = i2c_smbus_read_i2c_block_data(i2c, RT9458_REG_IRQ1,
RT9458_IRQ_REGNUM, tmp);
if (ret < 0) {
dev_info(&i2c->dev, "read all irqevents fail\n");
return ret;
}
return 0;
}
static inline int rt9458_i2c_detect_devid(struct i2c_client *i2c)
{
int ret = 0;
ret = i2c_smbus_read_byte_data(i2c, RT9458_REG_DEVID);
if (ret < 0) {
dev_info(&i2c->dev, "%s: chip io may bail\n", __func__);
return ret;
}
dev_dbg(&i2c->dev, "%s: dev_id 0x%02x\n", __func__, ret);
if ((ret & 0xf0) != 0x00) {
dev_info(&i2c->dev, "%s: vendor id not correct\n", __func__);
return -ENODEV;
}
/* finally return the rev id */
return (ret & 0x0f);
}
static void rt_parse_dt(struct device *dev, struct rt9458_platform_data *pdata)
{
/* just used to prevent the null parameter */
if (!dev || !pdata)
return;
if (of_property_read_string(dev->of_node, "charger_name",
&pdata->chg_name) < 0)
dev_warn(dev, "not specified chg_name\n");
if (of_property_read_u32(dev->of_node, "ichg", &pdata->ichg) < 0)
dev_warn(dev, "not specified ichg value\n");
if (of_property_read_u32(dev->of_node, "aicr", &pdata->aicr) < 0)
dev_warn(dev, "not specified aicr value\n");
if (of_property_read_u32(dev->of_node, "mivr", &pdata->mivr) < 0)
dev_warn(dev, "not specified mivr value\n");
if (of_property_read_u32(dev->of_node, "ieoc", &pdata->ieoc) < 0)
dev_warn(dev, "not specified ieoc_value\n");
if (of_property_read_u32(dev->of_node, "cv", &pdata->voreg) < 0)
dev_warn(dev, "not specified cv value\n");
if (of_property_read_u32(dev->of_node, "vmreg", &pdata->vmreg) < 0)
dev_warn(dev, "not specified vmreg value\n");
pdata->enable_te = of_property_read_bool(dev->of_node, "enable_te");
pdata->enable_eoc_shdn = of_property_read_bool(dev->of_node,
"enable_eoc_shdn");
#if (!defined(CONFIG_MTK_GPIO) || defined(CONFIG_MTK_GPIOLIB_STAND))
pdata->intr_gpio = of_get_named_gpio(dev->of_node, "rt,intr_gpio", 0);
#else
if (of_property_read_u32(np, "rt,intr_gpio_num", &pdata->intr_gpio) < 0)
dev_warn(dev, "not specified irq gpio number\n");
#endif /* #if (!defined(CONFIG_MTK_GPIO) || defined(CONFIG_MTK_GPIOLIB_STAND) */
}
static int rt9458_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct rt9458_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt9458_info *ri = NULL;
u8 rev_id = 0;
bool use_dt = i2c->dev.of_node;
int ret = 0;
dev_info(&i2c->dev, "%s start(%s)\n", __func__, RT9458_DRV_VERSION);
ret = rt9458_i2c_detect_devid(i2c);
if (ret < 0)
return ret;
/* if success, return value is revision id */
rev_id = (u8)ret;
/* driver data */
ri = devm_kzalloc(&i2c->dev, sizeof(*ri), GFP_KERNEL);
if (!ri)
return -ENOMEM;
/* platform data */
if (use_dt) {
pdata = devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
memcpy(pdata, &rt9458_def_platform_data, sizeof(*pdata));
i2c->dev.platform_data = pdata;
rt_parse_dt(&i2c->dev, pdata);
} else {
if (!pdata) {
dev_info(&i2c->dev, "no pdata specify\n");
return -EINVAL;
}
}
ri->dev = &i2c->dev;
ri->i2c = i2c;
mutex_init(&ri->io_lock);
ri->chip_rev = rev_id;
memset(ri->irq_mask, 0xff, RT9458_IRQ_REGNUM);
i2c_set_clientdata(i2c, ri);
/* do whole chip reset */
ret = rt9458_chip_reset(i2c);
if (ret < 0)
return ret;
/* rt-regmap register */
ret = rt9458_register_rt_regmap(ri);
if (ret < 0)
return ret;
ret = rt9458_chip_pdata_init(ri);
if (ret < 0)
return ret;
#if defined(CONFIG_MTK_GAUGE_VERSION) && (CONFIG_MTK_GAUGE_VERSION == 30)
/* charger class register */
ri->chg_dev = charger_device_register(pdata->chg_name, ri->dev, ri,
&rt9458_chg_ops,
&rt9458_chg_props);
if (IS_ERR(ri->chg_dev)) {
dev_info(ri->dev, "charger device register fail\n");
return PTR_ERR(ri->chg_dev);
}
#endif /* #if (CONFIG_MTK_GAUGE_VERSION == 30) */
ret = rt9458_chip_irq_init(ri);
if (ret < 0)
return ret;
dev_info(ri->dev, "%s end\n", __func__);
return 0;
}
static int rt9458_i2c_remove(struct i2c_client *i2c)
{
struct rt9458_info *ri = i2c_get_clientdata(i2c);
dev_info(ri->dev, "%s start\n", __func__);
#if defined(CONFIG_MTK_GAUGE_VERSION) && (CONFIG_MTK_GAUGE_VERSION == 30)
charger_device_unregister(ri->chg_dev);
#endif /* #if (CONFIG_MTK_GAUGE_VERSION == 30) */
#ifdef CONFIG_RT_REGMAP
rt_regmap_device_unregister(ri->regmap_dev);
#endif /* #ifdef CONFIG_RT_REGMAP */
dev_info(ri->dev, "%s end\n", __func__);
return 0;
}
static void rt9458_i2c_shutdown(struct i2c_client *i2c)
{
struct rt9458_info *ri = i2c_get_clientdata(i2c);
disable_irq(ri->irq);
rt9458_chip_reset(i2c);
}
static int rt9458_i2c_suspend(struct device *dev)
{
struct rt9458_info *ri = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
enable_irq_wake(ri->irq);
return 0;
}
static int rt9458_i2c_resume(struct device *dev)
{
struct rt9458_info *ri = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
disable_irq_wake(ri->irq);
return 0;
}
static SIMPLE_DEV_PM_OPS(rt9458_pm_ops, rt9458_i2c_suspend, rt9458_i2c_resume);
static const struct of_device_id of_id_table[] = {
{ .compatible = "richtek,rt9458"},
{},
};
MODULE_DEVICE_TABLE(of, of_id_table);
static const struct i2c_device_id i2c_id_table[] = {
{ "rt9458", 0},
{},
};
MODULE_DEVICE_TABLE(i2c, i2c_id_table);
static struct i2c_driver rt9458_i2c_driver = {
.driver = {
.name = "rt9458",
.owner = THIS_MODULE,
.pm = &rt9458_pm_ops,
.of_match_table = of_match_ptr(of_id_table),
},
.probe = rt9458_i2c_probe,
.remove = rt9458_i2c_remove,
.shutdown = rt9458_i2c_shutdown,
.id_table = i2c_id_table,
};
module_i2c_driver(rt9458_i2c_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Richtek RT9458 Charger driver");
MODULE_AUTHOR("CY Huang <cy_huang@richtek.com>");
MODULE_VERSION(RT9458_DRV_VERSION);
/*
* Release Note
* 1.0.1
* (1) Replace unsigned long/char with u32/u8
* (2) Fix setting AICR to 1A ends in no limit
* (3) Show (no limit) for unlimited AICR in rt9458_charger_dump_registers
*
* 1.0.0
* (1) Initial released
*/