kernel_samsung_a34x-permissive/drivers/power/supply/rt9465.c
2024-04-28 15:49:01 +02:00

1810 lines
44 KiB
C
Executable file

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 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/interrupt.h>
#include <linux/irq.h>
#ifdef CONFIG_RT_REGMAP
#include <mt-plat/rt-regmap.h>
#endif
#include "charger_class.h"
#include "rt9465.h"
#define I2C_ACCESS_MAX_RETRY 5
#define RT9465_DRV_VERSION "1.0.11_MTK"
/* ======================= */
/* RT9465 Parameter */
/* ======================= */
enum rt9465_charging_status {
RT9465_CHG_STATUS_READY = 0,
RT9465_CHG_STATUS_PROGRESS,
RT9465_CHG_STATUS_DONE,
RT9465_CHG_STATUS_FAULT,
RT9465_CHG_STATUS_MAX,
};
/* Charging status name */
static const char *rt9465_chg_status_name[RT9465_CHG_STATUS_MAX] = {
"ready", "progress", "done", "fault",
};
static const u8 rt9465_hidden_mode_val[] = {
0x30, 0x26, 0xC7, 0xA4, 0x33, 0x06, 0x5F, 0xE1,
};
static const u32 rt9465_safety_timer[] = {
4, 6, 8, 10, 12, 14, 16, 20,
};
enum rt9465_irq_idx {
RT9465_IRQIDX_STATC = 0,
RT9465_IRQIDX_FAULT,
RT9465_IRQSTAT_MAX,
RT9465_IRQIDX_IRQ1 = RT9465_IRQSTAT_MAX,
RT9465_IRQIDX_IRQ2,
RT9465_IRQIDX_MAX,
};
static u8 rt9465_irqmask[RT9465_IRQIDX_MAX] = {
0x00, 0xE0, 0xFF, 0xFF
};
static const u8 rt9465_irq_maskall[RT9465_IRQIDX_MAX] = {
0xE0, 0xE0, 0xFF, 0xFF
};
struct rt9465_desc {
u32 ichg; /* uA */
u32 mivr; /* uV */
u32 cv; /* uV */
u32 ieoc; /* uA */
u32 safety_timer; /* hour */
bool en_te;
bool en_wdt;
bool en_st;
int regmap_represent_slave_addr;
const char *regmap_name;
const char *chg_dev_name;
const char *alias_name;
};
/* These default values will be used if there's no property in dts */
static struct rt9465_desc rt9465_default_desc = {
.ichg = 2000000, /* uA */
.mivr = 4400000, /* uV */
.cv = 4350000, /* uV */
.ieoc = 250000, /* uA */
.safety_timer = 12, /* hour */
.en_te = true,
.en_wdt = true,
.en_st = true,
.regmap_represent_slave_addr = RT9465_SLAVE_ADDR,
.regmap_name = "rt9465",
.chg_dev_name = "secondary_chg",
.alias_name = "rt9465",
};
struct rt9465_info {
struct i2c_client *i2c;
struct rt9465_desc *desc;
struct charger_device *chg_dev;
struct charger_properties chg_props;
struct device *dev;
struct mutex i2c_access_lock;
struct mutex adc_access_lock;
struct mutex gpio_access_lock;
struct mutex irq_access_lock;
struct mutex hidden_mode_lock;
u32 intr_gpio;
u32 en_gpio;
int irq;
u8 chip_rev;
u32 hidden_mode_cnt;
atomic_t is_chip_en;
u8 irq_flag[RT9465_IRQIDX_MAX];
u8 irq_stat[RT9465_IRQSTAT_MAX];
#ifdef CONFIG_RT_REGMAP
struct rt_regmap_device *regmap_dev;
struct rt_regmap_properties *regmap_prop;
#endif /* CONFIG_RT_REGMAP */
};
static int rt9465_kick_wdt(struct charger_device *chg_dev);
static int rt9465_reserved_handler(struct rt9465_info *info)
{
dev_info(info->dev, "%s\n", __func__);
return 0;
}
static int rt9465_chg_treg_handler(struct rt9465_info *info)
{
dev_info(info->dev, "%s\n", __func__);
return 0;
}
static int rt9465_chg_mivr_handler(struct rt9465_info *info)
{
dev_info(info->dev, "%s\n", __func__);
return 0;
}
static int rt9465_pwr_rdy_handler(struct rt9465_info *info)
{
dev_info(info->dev, "%s\n", __func__);
return 0;
}
static int rt9465_chg_vbatsuv_handler(struct rt9465_info *info)
{
dev_info(info->dev, "%s\n", __func__);
return 0;
}
static int rt9465_chg_vbatov_handler(struct rt9465_info *info)
{
dev_info(info->dev, "%s\n", __func__);
return 0;
}
static int rt9465_chg_vbusov_handler(struct rt9465_info *info)
{
dev_info(info->dev, "%s\n", __func__);
return 0;
}
static int rt9465_chg_faulti_handler(struct rt9465_info *info)
{
dev_info(info->dev, "%s\n", __func__);
return 0;
}
static int rt9465_chg_statci_handler(struct rt9465_info *info)
{
dev_info(info->dev, "%s\n", __func__);
return 0;
}
static int rt9465_temp_l_handler(struct rt9465_info *info)
{
dev_info(info->dev, "%s\n", __func__);
return 0;
}
static int rt9465_temp_h_handler(struct rt9465_info *info)
{
dev_info(info->dev, "%s\n", __func__);
return 0;
}
static int rt9465_chg_tmri_handler(struct rt9465_info *info)
{
dev_info(info->dev, "%s\n", __func__);
return 0;
}
static int rt9465_chg_adpbadi_handler(struct rt9465_info *info)
{
dev_info(info->dev, "%s\n", __func__);
return 0;
}
static int rt9465_chg_otpi_handler(struct rt9465_info *info)
{
dev_info(info->dev, "%s\n", __func__);
return 0;
}
static int rt9465_wdtmri_handler(struct rt9465_info *info)
{
int ret = 0;
dev_info(info->dev, "%s\n", __func__);
ret = rt9465_kick_wdt(info->chg_dev);
if (ret < 0)
dev_notice(info->dev, "%s: kick wdt fail\n", __func__);
return ret;
}
static int rt9465_ssfinishi_handler(struct rt9465_info *info)
{
dev_info(info->dev, "%s\n", __func__);
return 0;
}
static int rt9465_chg_termi_handler(struct rt9465_info *info)
{
dev_info(info->dev, "%s\n", __func__);
return 0;
}
static int rt9465_chg_ieoci_handler(struct rt9465_info *info)
{
dev_info(info->dev, "%s\n", __func__);
return 0;
}
struct irq_mapping_tbl {
const char *name;
int (*hdlr)(struct rt9465_info *info);
};
#define RT9465_IRQ_MAPPING(_name) \
{.name = #_name, .hdlr = rt9465_ ## _name ## _handler}
static const struct irq_mapping_tbl rt9465_irq_mapping_tbl[] = {
RT9465_IRQ_MAPPING(reserved),
RT9465_IRQ_MAPPING(reserved),
RT9465_IRQ_MAPPING(reserved),
RT9465_IRQ_MAPPING(reserved),
RT9465_IRQ_MAPPING(reserved),
RT9465_IRQ_MAPPING(chg_treg),
RT9465_IRQ_MAPPING(chg_mivr),
RT9465_IRQ_MAPPING(pwr_rdy),
RT9465_IRQ_MAPPING(reserved),
RT9465_IRQ_MAPPING(reserved),
RT9465_IRQ_MAPPING(reserved),
RT9465_IRQ_MAPPING(reserved),
RT9465_IRQ_MAPPING(reserved),
RT9465_IRQ_MAPPING(chg_vbatsuv),
RT9465_IRQ_MAPPING(chg_vbatov),
RT9465_IRQ_MAPPING(chg_vbusov),
RT9465_IRQ_MAPPING(chg_faulti),
RT9465_IRQ_MAPPING(chg_statci),
RT9465_IRQ_MAPPING(reserved),
RT9465_IRQ_MAPPING(temp_l),
RT9465_IRQ_MAPPING(temp_h),
RT9465_IRQ_MAPPING(chg_tmri),
RT9465_IRQ_MAPPING(chg_adpbadi),
RT9465_IRQ_MAPPING(chg_otpi),
RT9465_IRQ_MAPPING(reserved),
RT9465_IRQ_MAPPING(reserved),
RT9465_IRQ_MAPPING(reserved),
RT9465_IRQ_MAPPING(reserved),
RT9465_IRQ_MAPPING(wdtmri),
RT9465_IRQ_MAPPING(ssfinishi),
RT9465_IRQ_MAPPING(chg_termi),
RT9465_IRQ_MAPPING(chg_ieoci),
};
/* ======================= */
/* Address & Default value */
/* ======================= */
static const unsigned char rt9465_reg_addr[] = {
RT9465_REG_CHG_CTRL0,
RT9465_REG_CHG_CTRL1,
RT9465_REG_CHG_CTRL2,
RT9465_REG_CHG_CTRL3,
RT9465_REG_CHG_CTRL4,
RT9465_REG_CHG_CTRL5,
RT9465_REG_CHG_CTRL6,
RT9465_REG_CHG_CTRL7,
RT9465_REG_CHG_CTRL8,
RT9465_REG_CHG_CTRL9,
RT9465_REG_CHG_CTRL10,
RT9465_REG_CHG_CTRL12,
RT9465_REG_CHG_CTRL13,
RT9465_REG_HIDDEN_CTRL2,
RT9465_REG_HIDDEN_CTRL6,
RT9465_REG_SYSTEM1,
RT9465_REG_CHG_STATC,
RT9465_REG_CHG_FAULT,
RT9465_REG_CHG_IRQ1,
RT9465_REG_CHG_IRQ2,
RT9465_REG_CHG_STATC_MASK,
RT9465_REG_CHG_FAULT_MASK,
RT9465_REG_CHG_IRQ1_MASK,
RT9465_REG_CHG_IRQ2_MASK,
};
/* ========= */
/* RT Regmap */
/* ========= */
#ifdef CONFIG_RT_REGMAP
RT_REG_DECL(RT9465_REG_CHG_CTRL0, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9465_REG_CHG_CTRL1, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9465_REG_CHG_CTRL2, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9465_REG_CHG_CTRL3, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9465_REG_CHG_CTRL4, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9465_REG_CHG_CTRL5, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9465_REG_CHG_CTRL6, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9465_REG_CHG_CTRL7, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9465_REG_CHG_CTRL8, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9465_REG_CHG_CTRL9, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9465_REG_CHG_CTRL10, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9465_REG_CHG_CTRL12, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9465_REG_CHG_CTRL13, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9465_REG_HIDDEN_CTRL2, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9465_REG_HIDDEN_CTRL6, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9465_REG_SYSTEM1, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9465_REG_CHG_STATC, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9465_REG_CHG_FAULT, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9465_REG_CHG_IRQ1, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9465_REG_CHG_IRQ2, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9465_REG_CHG_STATC_MASK, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9465_REG_CHG_FAULT_MASK, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9465_REG_CHG_IRQ1_MASK, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9465_REG_CHG_IRQ2_MASK, 1, RT_VOLATILE, {});
static const rt_register_map_t rt9465_regmap_map[] = {
RT_REG(RT9465_REG_CHG_CTRL0),
RT_REG(RT9465_REG_CHG_CTRL1),
RT_REG(RT9465_REG_CHG_CTRL2),
RT_REG(RT9465_REG_CHG_CTRL3),
RT_REG(RT9465_REG_CHG_CTRL4),
RT_REG(RT9465_REG_CHG_CTRL5),
RT_REG(RT9465_REG_CHG_CTRL6),
RT_REG(RT9465_REG_CHG_CTRL7),
RT_REG(RT9465_REG_CHG_CTRL8),
RT_REG(RT9465_REG_CHG_CTRL9),
RT_REG(RT9465_REG_CHG_CTRL10),
RT_REG(RT9465_REG_CHG_CTRL12),
RT_REG(RT9465_REG_CHG_CTRL13),
RT_REG(RT9465_REG_HIDDEN_CTRL2),
RT_REG(RT9465_REG_HIDDEN_CTRL6),
RT_REG(RT9465_REG_SYSTEM1),
RT_REG(RT9465_REG_CHG_STATC),
RT_REG(RT9465_REG_CHG_FAULT),
RT_REG(RT9465_REG_CHG_IRQ1),
RT_REG(RT9465_REG_CHG_IRQ2),
RT_REG(RT9465_REG_CHG_STATC_MASK),
RT_REG(RT9465_REG_CHG_FAULT_MASK),
RT_REG(RT9465_REG_CHG_IRQ1_MASK),
RT_REG(RT9465_REG_CHG_IRQ2_MASK),
};
#endif /* CONFIG_RT_REGMAP */
/* ========================= */
/* I2C operations */
/* ========================= */
static inline bool __rt9465_is_chip_en(struct rt9465_info *info)
{
int en = 0;
en = gpio_get_value(info->en_gpio);
if ((en && !atomic_read(&info->is_chip_en)) ||
(!en && atomic_read(&info->is_chip_en)))
dev_notice(info->dev, "%s: en not sync(%d, %d)\n", __func__, en,
atomic_read(&info->is_chip_en));
return en;
}
static int rt9465_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 rt9465_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);
}
#ifdef CONFIG_RT_REGMAP
static struct rt_regmap_fops rt9465_regmap_fops = {
.read_device = rt9465_device_read,
.write_device = rt9465_device_write,
};
static int rt9465_register_rt_regmap(struct rt9465_info *info)
{
int ret = 0;
struct i2c_client *i2c = info->i2c;
struct rt_regmap_properties *prop = NULL;
dev_info(info->dev, "%s\n", __func__);
prop = devm_kzalloc(&i2c->dev, sizeof(struct rt_regmap_properties),
GFP_KERNEL);
if (!prop)
return -ENOMEM;
prop->name = info->desc->regmap_name;
prop->aliases = info->desc->regmap_name;
prop->register_num = ARRAY_SIZE(rt9465_regmap_map);
prop->rm = rt9465_regmap_map;
prop->rt_regmap_mode = RT_SINGLE_BYTE | RT_CACHE_DISABLE |
RT_IO_PASS_THROUGH;
prop->io_log_en = 0;
info->regmap_prop = prop;
info->regmap_dev = rt_regmap_device_register_ex(info->regmap_prop,
&rt9465_regmap_fops, &i2c->dev, i2c,
info->desc->regmap_represent_slave_addr, info);
if (!info->regmap_dev) {
dev_notice(info->dev, "register regmap device failed\n");
return -EIO;
}
return ret;
}
#endif /* CONFIG_RT_REGMAP */
static inline int __rt9465_i2c_write_byte(struct rt9465_info *info, u8 cmd,
u8 data)
{
int ret = 0, retry = 0;
do {
#ifdef CONFIG_RT_REGMAP
ret = rt_regmap_block_write(info->regmap_dev, cmd, 1, &data);
#else
ret = rt9465_device_write(info->i2c, cmd, 1, &data);
#endif
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 failed\n",
__func__, cmd, data);
else
dev_dbg_ratelimited(info->dev, "%s: I2CW[0x%02X] = 0x%02X\n",
__func__, cmd, data);
return ret;
}
static int rt9465_i2c_write_byte(struct rt9465_info *info, u8 cmd, u8 data)
{
int ret = 0;
mutex_lock(&info->i2c_access_lock);
mutex_lock(&info->gpio_access_lock);
if (__rt9465_is_chip_en(info))
ret = __rt9465_i2c_write_byte(info, cmd, data);
else
ret = -EINVAL;
mutex_unlock(&info->gpio_access_lock);
mutex_unlock(&info->i2c_access_lock);
return ret;
}
static inline int __rt9465_i2c_read_byte(struct rt9465_info *info, u8 cmd)
{
int ret = 0, ret_val = 0, retry = 0;
do {
#ifdef CONFIG_RT_REGMAP
ret = rt_regmap_block_read(info->regmap_dev, cmd, 1, &ret_val);
#else
ret = rt9465_device_read(info->i2c, cmd, 1, &ret_val);
#endif
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] failed\n",
__func__, cmd);
return ret;
}
ret_val = ret_val & 0xFF;
dev_dbg_ratelimited(info->dev, "%s: I2CR[0x%02X] = 0x%02X\n", __func__,
cmd, ret_val);
return ret_val;
}
static int rt9465_i2c_read_byte(struct rt9465_info *info, u8 cmd)
{
int ret = 0;
mutex_lock(&info->i2c_access_lock);
mutex_lock(&info->gpio_access_lock);
if (__rt9465_is_chip_en(info))
ret = __rt9465_i2c_read_byte(info, cmd);
else
ret = -EINVAL;
mutex_unlock(&info->gpio_access_lock);
mutex_unlock(&info->i2c_access_lock);
if (ret < 0)
return ret;
return (ret & 0xFF);
}
static inline int __rt9465_i2c_block_write(struct rt9465_info *info, u8 cmd,
u32 leng, const u8 *data)
{
int ret = 0;
#ifdef CONFIG_RT_REGMAP
ret = rt_regmap_block_write(info->regmap_dev, cmd, leng, data);
#else
ret = rt9465_device_write(info->i2c, cmd, leng, data);
#endif
return ret;
}
static int rt9465_i2c_block_write(struct rt9465_info *info, u8 cmd, u32 leng,
const u8 *data)
{
int ret = 0;
mutex_lock(&info->i2c_access_lock);
mutex_lock(&info->gpio_access_lock);
if (__rt9465_is_chip_en(info))
ret = __rt9465_i2c_block_write(info, cmd, leng, data);
else
ret = -EINVAL;
mutex_unlock(&info->gpio_access_lock);
mutex_unlock(&info->i2c_access_lock);
return ret;
}
static inline int __rt9465_i2c_block_read(struct rt9465_info *info, u8 cmd,
u32 leng, u8 *data)
{
int ret = 0;
#ifdef CONFIG_RT_REGMAP
ret = rt_regmap_block_read(info->regmap_dev, cmd, leng, data);
#else
ret = rt9465_device_read(info->i2c, cmd, leng, data);
#endif
return ret;
}
static int rt9465_i2c_block_read(struct rt9465_info *info, u8 cmd, u32 leng,
u8 *data)
{
int ret = 0;
mutex_lock(&info->i2c_access_lock);
mutex_lock(&info->gpio_access_lock);
if (__rt9465_is_chip_en(info))
ret = __rt9465_i2c_block_read(info, cmd, leng, data);
else
ret = -EINVAL;
mutex_unlock(&info->gpio_access_lock);
mutex_unlock(&info->i2c_access_lock);
return ret;
}
static int rt9465_i2c_test_bit(struct rt9465_info *info, u8 cmd, u8 shift,
bool *is_one)
{
int ret = 0;
u8 data = 0;
ret = rt9465_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 rt9465_i2c_update_bits(struct rt9465_info *info, u8 cmd, u8 data,
u8 mask)
{
int ret = 0;
u8 reg_data = 0;
mutex_lock(&info->i2c_access_lock);
mutex_lock(&info->gpio_access_lock);
if (__rt9465_is_chip_en(info)) {
ret = __rt9465_i2c_read_byte(info, cmd);
if (ret < 0)
goto out;
reg_data = ret & 0xFF;
reg_data &= ~mask;
reg_data |= (data & mask);
ret = __rt9465_i2c_write_byte(info, cmd, reg_data);
} else
ret = -EINVAL;
out:
mutex_unlock(&info->gpio_access_lock);
mutex_unlock(&info->i2c_access_lock);
return ret;
}
static inline int rt9465_set_bit(struct rt9465_info *info, u8 reg, u8 mask)
{
return rt9465_i2c_update_bits(info, reg, mask, mask);
}
static inline int rt9465_clr_bit(struct rt9465_info *info, u8 reg, u8 mask)
{
return rt9465_i2c_update_bits(info, reg, 0x00, mask);
}
/* ================== */
/* Internal Functions */
/* ================== */
/* The following APIs will be reference in internal functions */
static int rt9465_get_ichg(struct charger_device *chg_dev, u32 *uA);
static int rt9465_dump_register(struct charger_device *chg_dev);
static inline u8 rt9465_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 u32 rt9465_closest_value(u32 min, u32 max, u32 step, u8 reg)
{
u32 ret_val = 0;
ret_val = min + reg * step;
return ret_val > max ? max : ret_val;
}
static inline u8 rt9465_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 void rt9465_irq_set_flag(struct rt9465_info *info, u8 *irq,
u8 mask)
{
mutex_lock(&info->irq_access_lock);
*irq |= mask;
mutex_unlock(&info->irq_access_lock);
}
static inline void rt9465_irq_clr_flag(struct rt9465_info *info, u8 *irq,
u8 mask)
{
mutex_lock(&info->irq_access_lock);
*irq &= ~mask;
mutex_unlock(&info->irq_access_lock);
}
static inline const char *rt9465_get_irq_name(struct rt9465_info *info,
int irqnum)
{
if (irqnum >= 0 && irqnum < ARRAY_SIZE(rt9465_irq_mapping_tbl))
return rt9465_irq_mapping_tbl[irqnum].name;
return "not found";
}
static inline void rt9465_irq_unmask(struct rt9465_info *info,
unsigned int irqnum)
{
dev_dbg(info->dev, "%s: irq = %d, %s\n", __func__, irqnum,
rt9465_get_irq_name(info, irqnum));
rt9465_irqmask[irqnum / 8] &= ~(1 << (irqnum % 8));
}
static int rt9465_enable_hidden_mode(struct rt9465_info *info, bool en)
{
int ret = 0;
mutex_lock(&info->hidden_mode_lock);
if (en) {
if (info->hidden_mode_cnt == 0) {
ret = rt9465_i2c_block_write(info, 0x50,
ARRAY_SIZE(rt9465_hidden_mode_val),
rt9465_hidden_mode_val);
if (ret < 0)
goto err;
}
info->hidden_mode_cnt++;
} else {
if (info->hidden_mode_cnt == 1) /* last one */
ret = rt9465_i2c_write_byte(info, 0x50, 0x00);
info->hidden_mode_cnt--;
if (ret < 0)
goto err;
}
dev_info(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 rt9465_sw_workaround(struct rt9465_info *info)
{
int ret = 0;
dev_info(info->dev, "%s\n", __func__);
/* Enter hidden mode */
rt9465_enable_hidden_mode(info, true);
/* For chip rev == E2, set Hidden code to make ICHG accurate */
if (info->chip_rev == RT9465_VERSION_E2) {
ret = rt9465_i2c_write_byte(info, RT9465_REG_HIDDEN_CTRL2,
0x68);
if (ret < 0)
goto out;
ret = rt9465_i2c_write_byte(info, RT9465_REG_HIDDEN_CTRL6,
0x3A);
if (ret < 0)
goto out;
}
out:
rt9465_enable_hidden_mode(info, false);
return ret;
}
static irqreturn_t rt9465_irq_handler(int irq, void *data)
{
int ret = 0, i = 0, j = 0;
u8 evt[RT9465_IRQIDX_MAX] = {0};
u8 mask[RT9465_IRQIDX_MAX] = {0};
u8 stat[RT9465_IRQSTAT_MAX] = {0};
struct rt9465_info *info = (struct rt9465_info *)data;
dev_info(info->dev, "%s\n", __func__);
/* read event */
ret = rt9465_i2c_block_read(info, RT9465_REG_CHG_STATC, ARRAY_SIZE(evt),
evt);
if (ret < 0) {
dev_notice(info->dev, "%s: read evt fail\n", __func__);
goto err;
}
/* read mask */
ret = rt9465_i2c_block_read(info, RT9465_REG_CHG_STATC_MASK,
ARRAY_SIZE(mask), mask);
if (ret < 0) {
dev_notice(info->dev, "%s: read mask fail\n", __func__);
goto err;
}
/* Store stat */
memcpy(stat, info->irq_stat, RT9465_IRQSTAT_MAX);
for (i = 0; i < RT9465_IRQIDX_MAX; i++) {
evt[i] &= ~mask[i];
if (i < RT9465_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 (rt9465_irq_mapping_tbl[i * 8 + j].hdlr)
(rt9465_irq_mapping_tbl[i * 8 + j].hdlr)(info);
}
}
err:
return IRQ_HANDLED;
}
static int rt9465_register_irq(struct rt9465_info *info)
{
int ret = 0, len = 0;
char *name = NULL;
/* request gpio */
len = strlen(info->desc->chg_dev_name);
name = devm_kzalloc(info->dev, len + 10, GFP_KERNEL);
ret = snprintf(name, len + 10, "%s_irq_gpio", info->desc->chg_dev_name);
if (ret >= (len + 10))
dev_info(info->dev, "%s: name truncated\n", __func__);
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__);
goto err;
}
ret = gpio_to_irq(info->intr_gpio);
if (ret < 0) {
dev_notice(info->dev, "%s: irq mapping fail\n", __func__);
goto err;
}
info->irq = ret;
dev_info(info->dev, "%s: irq = %d\n", __func__, info->irq);
/* Request threaded IRQ */
ret = devm_request_threaded_irq(info->dev, info->irq, NULL,
rt9465_irq_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, name,
info);
if (ret < 0) {
dev_notice(info->dev, "%s: request thread irq failed\n",
__func__);
goto err;
}
device_init_wakeup(info->dev, true);
return 0;
err:
return ret;
}
static int rt9465_maskall_irq(struct rt9465_info *info)
{
dev_info(info->dev, "%s\n", __func__);
return rt9465_i2c_block_write(info, RT9465_REG_CHG_STATC_MASK,
ARRAY_SIZE(rt9465_irq_maskall), rt9465_irq_maskall);
}
static int rt9465_init_irq(struct rt9465_info *info)
{
dev_info(info->dev, "%s\n", __func__);
return rt9465_i2c_block_write(info, RT9465_REG_CHG_STATC,
ARRAY_SIZE(rt9465_irqmask), rt9465_irqmask);
}
static bool rt9465_is_hw_exist(struct rt9465_info *info)
{
int ret = 0;
u8 version = 0;
ret = i2c_smbus_read_byte_data(info->i2c, RT9465_REG_SYSTEM1);
version = (ret & RT9465_MASK_VERSION) >> RT9465_SHIFT_VERSION;
dev_info(info->dev, "%s: E%d(0x%02X)\n", __func__, version + 1,
version);
if (version < RT9465_VERSION_E5) {
dev_notice(info->dev, "%s: chip version is incorrect\n",
__func__);
return false;
}
info->chip_rev = version;
return true;
}
static int rt9465_set_safety_timer(struct rt9465_info *info, u32 hr)
{
u8 reg_st = 0;
reg_st = rt9465_closest_reg_via_tbl(rt9465_safety_timer,
ARRAY_SIZE(rt9465_safety_timer), hr);
dev_info(info->dev, "%s: st = %d(0x%02X)\n", __func__, hr, reg_st);
return rt9465_i2c_update_bits(info, RT9465_REG_CHG_CTRL9,
reg_st << RT9465_SHIFT_WT_FC, RT9465_MASK_WT_FC);
}
static inline int rt9465_enable_wdt(struct rt9465_info *info, bool en)
{
dev_info(info->dev, "%s: en = %d\n", __func__, en);
return (en ? rt9465_set_bit : rt9465_clr_bit)
(info, RT9465_REG_CHG_CTRL10, RT9465_MASK_WDT_EN);
}
static inline int rt9465_reset_chip(struct rt9465_info *info)
{
dev_info(info->dev, "%s\n", __func__);
return rt9465_i2c_write_byte(info, RT9465_REG_CHG_CTRL0, 0x80);
}
static inline int rt9465_enable_te(struct rt9465_info *info, bool en)
{
dev_info(info->dev, "%s: en = %d\n", __func__, en);
return (en ? rt9465_set_bit : rt9465_clr_bit)
(info, RT9465_REG_CHG_CTRL8, RT9465_MASK_TE_EN);
}
static int rt9465_set_ieoc(struct rt9465_info *info, u32 ieoc)
{
u8 reg_ieoc = 0;
/* Workaround for E1, IEOC must >= 700mA */
if (ieoc < 700000 && info->chip_rev == RT9465_VERSION_E1)
ieoc = 700000;
/* Find corresponding reg value */
reg_ieoc = rt9465_closest_reg(RT9465_IEOC_MIN, RT9465_IEOC_MAX,
RT9465_IEOC_STEP, ieoc);
/* ieoc starts from 600mA and its register value is 0x05 */
reg_ieoc += 0x05;
dev_info(info->dev, "%s: ieoc = %d(0x%02X)\n", __func__, ieoc,
reg_ieoc);
return rt9465_i2c_update_bits(info, RT9465_REG_CHG_CTRL7,
reg_ieoc << RT9465_SHIFT_IEOC, RT9465_MASK_IEOC);
}
static int __rt9465_get_mivr(struct rt9465_info *info, u32 *mivr)
{
int ret = 0;
u8 reg_mivr = 0;
ret = rt9465_i2c_read_byte(info, RT9465_REG_CHG_CTRL5);
if (ret < 0)
return ret;
reg_mivr = ((ret & RT9465_MASK_MIVR) >> RT9465_SHIFT_MIVR) & 0xFF;
*mivr = rt9465_closest_value(RT9465_MIVR_MIN, RT9465_MIVR_MAX,
RT9465_MIVR_STEP, reg_mivr);
return ret;
}
static int rt9465_get_charging_status(struct rt9465_info *info,
enum rt9465_charging_status *chg_stat)
{
int ret = 0;
ret = rt9465_i2c_read_byte(info, RT9465_REG_SYSTEM1);
if (ret < 0)
return ret;
*chg_stat = (ret & RT9465_MASK_CHG_STAT) >> RT9465_SHIFT_CHG_STAT;
return ret;
}
static int rt9465_get_ieoc(struct rt9465_info *info, u32 *ieoc)
{
int ret = 0;
u8 reg_ieoc = 0;
ret = rt9465_i2c_read_byte(info, RT9465_REG_CHG_CTRL7);
if (ret < 0)
return ret;
reg_ieoc = (ret & RT9465_MASK_IEOC) >> RT9465_SHIFT_IEOC;
/* ieoc starts from 600mA and its register value is 0x05 */
reg_ieoc -= 0x05;
*ieoc = rt9465_closest_value(RT9465_IEOC_MIN, RT9465_IEOC_MAX,
RT9465_IEOC_STEP, reg_ieoc);
return ret;
}
static inline int rt9465_get_irq_number(struct rt9465_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(rt9465_irq_mapping_tbl); i++) {
if (!strcmp(name, rt9465_irq_mapping_tbl[i].name))
return i;
}
return -EINVAL;
}
static int rt9465_parse_dt(struct rt9465_info *info, struct device *dev)
{
int ret = 0, irqcnt = 0, irqnum = 0;
struct rt9465_desc *desc = NULL;
struct device_node *np = dev->of_node;
const char *name = NULL;
int len = 0;
char *en_name = NULL;
dev_info(info->dev, "%s\n", __func__);
if (!np) {
dev_notice(info->dev, "%s: no device node\n", __func__);
return -EINVAL;
}
info->desc = &rt9465_default_desc;
desc = devm_kzalloc(dev, sizeof(struct rt9465_desc), GFP_KERNEL);
if (!desc)
return -ENOMEM;
memcpy(desc, &rt9465_default_desc, sizeof(struct rt9465_desc));
/*
* For dual charger, one is primary_chg;
* another one will be secondary_chg
*/
if (of_property_read_string(np, "charger_name",
&desc->chg_dev_name) < 0)
dev_notice(info->dev, "%s: no charger name\n", __func__);
if (of_property_read_u32(np, "regmap_represent_slave_addr",
&(desc->regmap_represent_slave_addr)) < 0)
dev_notice(info->dev, "%s: no regmap represent slave addr\n",
__func__);
if (of_property_read_string(np, "regmap_name", &desc->regmap_name) < 0)
dev_notice(info->dev, "%s: no regmap name\n", __func__);
if (of_property_read_string(np, "alias_name", &desc->alias_name) < 0)
dev_notice(info->dev, "%s: no alias name\n", __func__);
#if (!defined(CONFIG_MTK_GPIO) || defined(CONFIG_MTK_GPIOLIB_STAND))
ret = of_get_named_gpio(np, "rt,en_gpio", 0);
if (ret < 0)
return ret;
info->en_gpio = ret;
#else
ret = of_property_read_u32(np, "rt,en_gpio_num", &info->en_gpio);
if (ret < 0)
return ret;
#endif /* !CONFIG_MTK_GPIO || CONFIG_MTK_GPIOLIB_STAND */
dev_info(info->dev, "%s: intr/en gpio = %d, %d\n", __func__,
info->intr_gpio, info->en_gpio);
/* request en gpio */
len = strlen(desc->chg_dev_name);
en_name = devm_kzalloc(info->dev, len + 9, GFP_KERNEL);
snprintf(en_name, len + 9, "%s_en_gpio", desc->chg_dev_name);
ret = devm_gpio_request_one(info->dev, info->en_gpio, GPIOF_DIR_OUT,
en_name);
if (ret < 0) {
dev_notice(info->dev, "%s: en gpio request fail\n", __func__);
return ret;
}
#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/en gpio = %d, %d\n", __func__,
info->intr_gpio, info->en_gpio);
if (of_property_read_u32(np, "ichg", &desc->ichg) < 0)
dev_notice(info->dev, "%s: no ichg\n", __func__);
if (of_property_read_u32(np, "mivr", &desc->mivr) < 0)
dev_notice(info->dev, "%s: no mivr\n", __func__);
if (of_property_read_u32(np, "cv", &desc->cv) < 0)
dev_notice(info->dev, "%s: no cv\n", __func__);
if (of_property_read_u32(np, "ieoc", &desc->ieoc) < 0)
dev_notice(info->dev, "%s: no ieoc\n", __func__);
if (of_property_read_u32(np, "safety_timer", &desc->safety_timer) < 0)
dev_notice(info->dev, "%s: no safety timer\n", __func__);
desc->en_te = of_property_read_bool(np, "en_te");
desc->en_wdt = of_property_read_bool(np, "en_wdt");
desc->en_st = of_property_read_bool(np, "en_st");
while (true) {
ret = of_property_read_string_index(np, "interrupt-names",
irqcnt, &name);
if (ret < 0)
break;
irqcnt++;
irqnum = rt9465_get_irq_number(info, name);
if (irqnum >= 0)
rt9465_irq_unmask(info, irqnum);
}
info->desc = desc;
info->chg_props.alias_name = info->desc->alias_name;
dev_info(info->dev, "%s: chg_name:%s alias:%s\n", __func__,
info->desc->chg_dev_name, info->chg_props.alias_name);
return 0;
}
static int __rt9465_enable_chip(struct rt9465_info *info, bool en)
{
bool is_chip_en = false;
dev_info(info->dev, "%s: en = %d\n", __func__, en);
mutex_lock(&info->gpio_access_lock);
is_chip_en = __rt9465_is_chip_en(info);
if (en && !is_chip_en) {
gpio_set_value(info->en_gpio, 1);
dev_info(info->dev, "%s: set gpio high\n", __func__);
} else if (!en && is_chip_en) {
gpio_set_value(info->en_gpio, 0);
dev_info(info->dev, "%s: set gpio low\n", __func__);
}
/* Wait for chip's enable/disable */
mdelay(1);
atomic_set(&info->is_chip_en, en);
mutex_unlock(&info->gpio_access_lock);
return 0;
}
static int __rt9465_set_ichg(struct rt9465_info *info, u32 uA)
{
u8 reg_ichg = 0;
/* Workaround for E1, Ichg must >= 1000mA */
if (uA < 1000000 && info->chip_rev == RT9465_VERSION_E1)
uA = 1000000;
/* Find corresponding reg value */
reg_ichg = rt9465_closest_reg(RT9465_ICHG_MIN, RT9465_ICHG_MAX,
RT9465_ICHG_STEP, uA);
/* ichg starts from 600mA and its register value is 0x06 */
reg_ichg += 0x06;
dev_info(info->dev, "%s: ichg = %d(0x%02X)\n", __func__, uA, reg_ichg);
return rt9465_i2c_update_bits(info, RT9465_REG_CHG_CTRL6,
reg_ichg << RT9465_SHIFT_ICHG, RT9465_MASK_ICHG);
}
static int __rt9465_set_mivr(struct rt9465_info *info, u32 uV)
{
u8 reg_mivr = 0;
/* Find corresponding reg value */
reg_mivr = rt9465_closest_reg(RT9465_MIVR_MIN, RT9465_MIVR_MAX,
RT9465_MIVR_STEP, uV);
dev_info(info->dev, "%s: mivr = %d(0x%02X)\n", __func__, uV, reg_mivr);
return rt9465_i2c_update_bits(info, RT9465_REG_CHG_CTRL5,
reg_mivr << RT9465_SHIFT_MIVR, RT9465_MASK_MIVR);
}
static int __rt9465_set_cv(struct rt9465_info *info, u32 uV)
{
u8 reg_cv = 0;
reg_cv = rt9465_closest_reg(RT9465_BAT_VOREG_MIN, RT9465_BAT_VOREG_MAX,
RT9465_BAT_VOREG_STEP, uV);
dev_info(info->dev, "%s: cv = %d(0x%02X)\n", __func__, uV, reg_cv);
return rt9465_i2c_update_bits(info, RT9465_REG_CHG_CTRL3,
reg_cv << RT9465_SHIFT_BAT_VOREG, RT9465_MASK_BAT_VOREG);
}
static int __rt9465_enable_safety_timer(struct rt9465_info *info, bool en)
{
dev_info(info->dev, "%s: en = %d\n", __func__, en);
return (en ? rt9465_set_bit : rt9465_clr_bit)
(info, RT9465_REG_CHG_CTRL9, RT9465_MASK_TMR_EN);
}
static int rt9465_init_setting(struct rt9465_info *info)
{
int ret = 0;
u8 evt[RT9465_IRQIDX_MAX] = {0};
struct rt9465_desc *desc = info->desc;
dev_info(info->dev, "%s\n", __func__);
ret = rt9465_maskall_irq(info);
if (ret < 0) {
dev_notice(info->dev, "%s: mask all irq fail\n", __func__);
goto err;
}
/* clear evt */
ret = rt9465_i2c_block_read(info, RT9465_REG_CHG_STATC, ARRAY_SIZE(evt),
evt);
if (ret < 0) {
dev_notice(info->dev, "%s: read evt fail\n", __func__);
goto err;
}
ret = __rt9465_set_ichg(info, desc->ichg);
if (ret < 0)
dev_notice(info->dev, "%s: set ichg fail\n", __func__);
ret = __rt9465_set_mivr(info, desc->mivr);
if (ret < 0)
dev_notice(info->dev, "%s: set mivr fail\n", __func__);
ret = rt9465_set_ieoc(info, desc->ieoc);
if (ret < 0)
dev_notice(info->dev, "%s: set ieoc fail\n", __func__);
ret = __rt9465_set_cv(info, desc->cv);
if (ret < 0)
dev_notice(info->dev, "%s: set cv fail\n", __func__);
ret = rt9465_enable_te(info, desc->en_te);
if (ret < 0)
dev_notice(info->dev, "%s: set te fail\n", __func__);
ret = rt9465_set_safety_timer(info, desc->safety_timer);
if (ret < 0)
dev_notice(info->dev, "%s: set safety timer fail\n", __func__);
ret = __rt9465_enable_safety_timer(info, desc->en_st);
if (ret < 0)
dev_notice(info->dev, "%s: enable charger timer fail\n",
__func__);
ret = rt9465_enable_wdt(info, desc->en_wdt);
if (ret < 0)
dev_notice(info->dev, "%s: enable watchdog fail\n", __func__);
err:
return ret;
}
/* =========================================================== */
/* The following is released interfaces */
/* =========================================================== */
static int rt9465_enable_chip(struct charger_device *chg_dev, bool en)
{
int ret = 0;
struct rt9465_info *info = dev_get_drvdata(&chg_dev->dev);
ret = __rt9465_enable_chip(info, en);
if (ret < 0)
return ret;
if (!en)
return 0;
/* Do the following flow for enabling chip */
if (!rt9465_is_hw_exist(info)) {
dev_info(info->dev, "%s: no rt9465 exists\n", __func__);
return -ENODEV;
}
ret = rt9465_init_setting(info);
if (ret < 0)
dev_info(info->dev, "%s: init fail(%d)\n", __func__, ret);
ret = rt9465_sw_workaround(info);
if (ret < 0)
dev_info(info->dev, "%s: sw wkard fail(%d)\n", __func__, ret);
ret = rt9465_init_irq(info);
if (ret < 0)
dev_info(info->dev, "%s: init irq fail(%d)\n", __func__, ret);
rt9465_dump_register(info->chg_dev);
return ret;
}
static int rt9465_is_chip_enabled(struct charger_device *chg_dev, bool *en)
{
struct rt9465_info *info = dev_get_drvdata(&chg_dev->dev);
mutex_lock(&info->gpio_access_lock);
*en = __rt9465_is_chip_en(info);
mutex_unlock(&info->gpio_access_lock);
return 0;
}
static int rt9465_is_charging_enabled(struct charger_device *chg_dev, bool *en)
{
struct rt9465_info *info = dev_get_drvdata(&chg_dev->dev);
return rt9465_i2c_test_bit(info, RT9465_REG_CHG_CTRL1,
RT9465_SHIFT_CHG_EN, en);
}
static int rt9465_dump_register(struct charger_device *chg_dev)
{
int i = 0, ret = 0;
int ichg = 0;
u32 mivr = 0, ieoc = 0;
bool chg_enable = 0;
enum rt9465_charging_status chg_status = RT9465_CHG_STATUS_READY;
struct rt9465_info *info = dev_get_drvdata(&chg_dev->dev);
u8 chg_stat = 0;
ret = rt9465_get_ichg(chg_dev, &ichg);
ret = __rt9465_get_mivr(info, &mivr);
ret = rt9465_is_charging_enabled(chg_dev, &chg_enable);
ret = rt9465_get_ieoc(info, &ieoc);
ret = rt9465_get_charging_status(info, &chg_status);
chg_stat = rt9465_i2c_read_byte(info, RT9465_REG_CHG_STATC);
/* Dump register if in fault status */
if (chg_status == RT9465_CHG_STATUS_FAULT) {
for (i = 0; i < ARRAY_SIZE(rt9465_reg_addr); i++)
ret = rt9465_i2c_read_byte(info, rt9465_reg_addr[i]);
}
dev_info(info->dev, "%s: ICHG = %dmA, MIVR = %dmV, IEOC = %dmA\n",
__func__, ichg / 1000, mivr / 1000, ieoc / 1000);
dev_info(info->dev, "%s: CHG_EN = %d, CHG_STATUS = %s, CHG_STAT = 0x%02X\n",
__func__, chg_enable, rt9465_chg_status_name[chg_status],
chg_stat);
return ret;
}
static int rt9465_enable_charging(struct charger_device *chg_dev, bool en)
{
struct rt9465_info *info = dev_get_drvdata(&chg_dev->dev);
return (en ? rt9465_set_bit : rt9465_clr_bit)
(info, RT9465_REG_CHG_CTRL1, RT9465_MASK_CHG_EN);
}
static int rt9465_enable_safety_timer(struct charger_device *chg_dev, bool en)
{
struct rt9465_info *info = dev_get_drvdata(&chg_dev->dev);
return __rt9465_enable_safety_timer(info, en);
}
static int rt9465_set_ichg(struct charger_device *chg_dev, u32 uA)
{
struct rt9465_info *info = dev_get_drvdata(&chg_dev->dev);
return __rt9465_set_ichg(info, uA);
}
static int rt9465_set_mivr(struct charger_device *chg_dev, u32 uV)
{
struct rt9465_info *info = dev_get_drvdata(&chg_dev->dev);
return __rt9465_set_mivr(info, uV);
}
static int rt9465_get_mivr_state(struct charger_device *chg_dev, bool *in_loop)
{
struct rt9465_info *info = dev_get_drvdata(&chg_dev->dev);
return rt9465_i2c_test_bit(info, RT9465_REG_CHG_STATC,
RT9465_SHIFT_CHG_MIVR, in_loop);
}
static int rt9465_set_cv(struct charger_device *chg_dev, u32 uV)
{
struct rt9465_info *info = dev_get_drvdata(&chg_dev->dev);
return __rt9465_set_cv(info, uV);
}
static int rt9465_get_ichg(struct charger_device *chg_dev, u32 *uA)
{
int ret = 0;
u8 reg_ichg = 0;
struct rt9465_info *info = dev_get_drvdata(&chg_dev->dev);
ret = rt9465_i2c_read_byte(info, RT9465_REG_CHG_CTRL6);
if (ret < 0)
return ret;
reg_ichg = (ret & RT9465_MASK_ICHG) >> RT9465_SHIFT_ICHG;
reg_ichg -= 0x06;
*uA = rt9465_closest_value(RT9465_ICHG_MIN, RT9465_ICHG_MAX,
RT9465_ICHG_STEP, reg_ichg);
return ret;
}
static int rt9465_get_min_ichg(struct charger_device *chg_dev, u32 *uA)
{
*uA = rt9465_closest_value(RT9465_ICHG_MIN, RT9465_ICHG_MAX,
RT9465_ICHG_STEP, 0);
return 0;
}
static int rt9465_get_mivr(struct charger_device *chg_dev, u32 *uV)
{
struct rt9465_info *info = dev_get_drvdata(&chg_dev->dev);
return __rt9465_get_mivr(info, uV);
}
static int rt9465_get_tchg(struct charger_device *chg_dev,
int *tchg_min, int *tchg_max)
{
int ret = 0, reg_adc_temp = 0, adc_temp = 0;
struct rt9465_info *info = dev_get_drvdata(&chg_dev->dev);
mutex_lock(&info->adc_access_lock);
/* Get value from ADC */
ret = rt9465_i2c_read_byte(info, RT9465_REG_CHG_CTRL12);
if (ret < 0)
goto out;
reg_adc_temp = (ret & RT9465_MASK_ADC_RPT) >> RT9465_SHIFT_ADC_RPT;
if (reg_adc_temp == 0x00) {
*tchg_min = 0;
*tchg_max = 60;
} else {
reg_adc_temp -= 0x01;
adc_temp = rt9465_closest_value(RT9465_ADC_RPT_MIN,
RT9465_ADC_RPT_MAX, RT9465_ADC_RPT_STEP, reg_adc_temp);
*tchg_min = adc_temp + 1;
*tchg_max = adc_temp + RT9465_ADC_RPT_STEP;
}
dev_info(info->dev, "%s: %d < temperature <= %d\n", __func__, *tchg_min,
*tchg_max);
out:
mutex_unlock(&info->adc_access_lock);
return ret;
}
static int rt9465_is_charging_done(struct charger_device *chg_dev, bool *done)
{
int ret = 0;
enum rt9465_charging_status chg_stat = RT9465_CHG_STATUS_READY;
struct rt9465_info *info = dev_get_drvdata(&chg_dev->dev);
ret = rt9465_get_charging_status(info, &chg_stat);
/* Return is charging done or not */
switch (chg_stat) {
case RT9465_CHG_STATUS_READY:
case RT9465_CHG_STATUS_PROGRESS:
case RT9465_CHG_STATUS_FAULT:
*done = false;
break;
case RT9465_CHG_STATUS_DONE:
*done = true;
break;
default:
*done = false;
break;
}
return ret;
}
static int rt9465_kick_wdt(struct charger_device *chg_dev)
{
int ret = 0;
struct rt9465_info *info = dev_get_drvdata(&chg_dev->dev);
enum rt9465_charging_status chg_status = RT9465_CHG_STATUS_READY;
/* Workaround: enable/disable watchdog to kick it */
if (info->chip_rev <= RT9465_VERSION_E2) {
ret = rt9465_enable_wdt(info, false);
if (ret < 0)
dev_notice(info->dev, "%s: disable wdt failed\n",
__func__);
ret = rt9465_enable_wdt(info, true);
if (ret < 0)
dev_notice(info->dev, "%s: enable wdt failed\n",
__func__);
return ret;
}
/* Any I2C communication can kick wdt */
return rt9465_get_charging_status(info, &chg_status);
}
static struct charger_ops rt9465_chg_ops = {
.enable = rt9465_enable_charging,
.is_enabled = rt9465_is_charging_enabled,
.is_chip_enabled = rt9465_is_chip_enabled,
.enable_safety_timer = rt9465_enable_safety_timer,
.enable_chip = rt9465_enable_chip,
.dump_registers = rt9465_dump_register,
.is_charging_done = rt9465_is_charging_done,
.get_charging_current = rt9465_get_ichg,
.set_charging_current = rt9465_set_ichg,
.get_min_charging_current = rt9465_get_min_ichg,
.set_constant_voltage = rt9465_set_cv,
.kick_wdt = rt9465_kick_wdt,
.get_tchg_adc = rt9465_get_tchg,
.get_mivr = rt9465_get_mivr,
.set_mivr = rt9465_set_mivr,
.get_mivr_state = rt9465_get_mivr_state,
};
/* ========================= */
/* I2C driver function */
/* ========================= */
static int rt9465_probe(struct i2c_client *i2c,
const struct i2c_device_id *dev_id)
{
int ret = 0;
struct rt9465_info *info = NULL;
pr_info("%s (%s)\n", __func__, RT9465_DRV_VERSION);
info = devm_kzalloc(&i2c->dev, sizeof(struct rt9465_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
info->i2c = i2c;
info->dev = &i2c->dev;
mutex_init(&info->i2c_access_lock);
mutex_init(&info->adc_access_lock);
mutex_init(&info->gpio_access_lock);
mutex_init(&info->irq_access_lock);
mutex_init(&info->hidden_mode_lock);
atomic_set(&info->is_chip_en, 0);
/* Must parse en gpio */
ret = rt9465_parse_dt(info, &i2c->dev);
if (ret < 0) {
dev_notice(info->dev, "%s: parse dt failed\n", __func__);
goto err_parse_dt;
}
i2c_set_clientdata(i2c, info);
#ifdef CONFIG_RT_REGMAP
ret = rt9465_register_rt_regmap(info);
if (ret < 0)
goto err_register_regmap;
#endif
/* Register charger device */
info->chg_dev = charger_device_register(info->desc->chg_dev_name,
&i2c->dev, info, &rt9465_chg_ops, &info->chg_props);
if (IS_ERR_OR_NULL(info->chg_dev)) {
ret = PTR_ERR(info->chg_dev);
goto err_register_chg_dev;
}
ret = rt9465_register_irq(info);
if (ret < 0) {
dev_notice(info->dev, "%s: reg irq fail(%d)\n", __func__, ret);
goto err_register_irq;
}
dev_info(info->dev, "%s: successfully\n", __func__);
return ret;
err_register_irq:
err_register_chg_dev:
#ifdef CONFIG_RT_REGMAP
rt_regmap_device_unregister(info->regmap_dev);
err_register_regmap:
#endif
err_parse_dt:
mutex_destroy(&info->adc_access_lock);
mutex_destroy(&info->i2c_access_lock);
mutex_destroy(&info->gpio_access_lock);
mutex_destroy(&info->irq_access_lock);
mutex_destroy(&info->hidden_mode_lock);
return ret;
}
static int rt9465_remove(struct i2c_client *i2c)
{
int ret = 0;
struct rt9465_info *info = i2c_get_clientdata(i2c);
pr_info("%s\n", __func__);
if (info) {
if (info->chg_dev)
charger_device_unregister(info->chg_dev);
#ifdef CONFIG_RT_REGMAP
rt_regmap_device_unregister(info->regmap_dev);
#endif
mutex_destroy(&info->adc_access_lock);
mutex_destroy(&info->i2c_access_lock);
mutex_destroy(&info->gpio_access_lock);
mutex_destroy(&info->irq_access_lock);
mutex_destroy(&info->hidden_mode_lock);
}
return ret;
}
static void rt9465_shutdown(struct i2c_client *i2c)
{
int ret = 0;
struct rt9465_info *info = i2c_get_clientdata(i2c);
pr_info("%s\n", __func__);
if (info) {
ret = rt9465_reset_chip(info);
if (ret < 0)
dev_notice(info->dev, "%s: sw reset failed\n",
__func__);
}
}
static int rt9465_suspend(struct device *dev)
{
struct rt9465_info *info = dev_get_drvdata(dev);
dev_info(info->dev, "%s\n", __func__);
if (device_may_wakeup(dev))
enable_irq_wake(info->irq);
return 0;
}
static int rt9465_resume(struct device *dev)
{
struct rt9465_info *info = dev_get_drvdata(dev);
dev_info(info->dev, "%s\n", __func__);
if (device_may_wakeup(dev))
disable_irq_wake(info->irq);
return 0;
}
static SIMPLE_DEV_PM_OPS(rt9465_pm_ops, rt9465_suspend, rt9465_resume);
static const struct i2c_device_id rt9465_i2c_id[] = {
{"rt9465", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, rt9465_i2c_id);
static const struct of_device_id rt9465_of_match[] = {
{ .compatible = "richtek,rt9465", },
{},
};
MODULE_DEVICE_TABLE(of, rt9465_of_match);
#ifndef CONFIG_OF
#define RT9465_BUSNUM 1
static struct i2c_board_info rt9465_i2c_board_info __initdata = {
I2C_BOARD_INFO("rt9465", RT9465_SALVE_ADDR)
};
#endif /* CONFIG_OF */
static struct i2c_driver rt9465_i2c_driver = {
.driver = {
.name = "rt9465",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(rt9465_of_match),
.pm = &rt9465_pm_ops,
},
.probe = rt9465_probe,
.remove = rt9465_remove,
.shutdown = rt9465_shutdown,
.id_table = rt9465_i2c_id,
};
static int __init rt9465_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(RT9465_BUSNUM, &rt9465_i2c_board_info, 1);
#endif
ret = i2c_add_driver(&rt9465_i2c_driver);
if (ret < 0)
pr_notice("%s: register i2c driver fail\n", __func__);
return ret;
}
module_init(rt9465_init);
static void __exit rt9465_exit(void)
{
i2c_del_driver(&rt9465_i2c_driver);
}
module_exit(rt9465_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ShuFanLee <shufan_lee@richtek.com>");
MODULE_DESCRIPTION("RT9465 Charger Driver");
MODULE_VERSION(RT9465_DRV_VERSION);
/*
* Version Note
* 1.0.11
* (1) Remove mt6336 and mt6306 related code
*
* 1.0.10
* (1) Remove retries for chip id check
* (2) Move chip check from probe to enale_chip
*
* 1.0.9
* (1) Add more retries for chip id check
*
* 1.0.8
* (1) Use standard GPIO API instead of pinctrl
* (2) Remove EN pin pull high, lock i2c adapter workaround
* (3) Add mt6306 gpio expander control
*
* 1.0.7
* (1) Modify init sequence in probe function
*
* 1.0.6
* (1) Modify the way to kick WDT and the name of enable_watchdog_timer to
* enable_wdt
* (2) Change pr_xxx to dev_xxx
*
* 1.0.5
* (1) Modify charger name to secondary_chg
*
* 1.0.4
* (1) Modify some pr_debug to pr_debug_ratelimited
* (2) Modify the way to parse dt
*
* 1.0.3
* (1) Modify rt9465_is_hw_exist to support all version
* (2) Correct chip version
* (3) Release rt9465_is_charging_enabled/rt9465_get_min_ichg
*
* 1.0.2
* (1) Add ICHG accuracy workaround for E2
* (2) Support E3 chip
* (3) Add config to separate EN pin from MT6336 or AP
*
* 1.0.1
* (1) Remove registering power supply class
*
* 1.0.0
* Initial Release
*/