6db4831e98
Android 14
1696 lines
40 KiB
C
1696 lines
40 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* Copyright (c) 2021 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>
|
|
#include <linux/kthread.h>
|
|
|
|
#include "mtk_charger_intf.h"
|
|
#include "rt9750.h"
|
|
|
|
#ifdef CONFIG_RT_REGMAP
|
|
#include <mt-plat/rt-regmap.h>
|
|
#endif
|
|
|
|
#define RT9750_DRV_VERSION "1.0.10_MTK"
|
|
|
|
#define I2C_ACCESS_MAX_RETRY 5
|
|
|
|
struct rt9750_desc {
|
|
int regmap_represent_slave_addr;
|
|
const char *regmap_name;
|
|
const char *chg_dev_name;
|
|
const char *alias_name;
|
|
u32 vbat_reg;
|
|
u32 vout_reg;
|
|
u32 iococp;
|
|
u32 wdt;
|
|
};
|
|
|
|
static struct rt9750_desc rt9750_default_desc = {
|
|
.regmap_represent_slave_addr = RT9750_SLAVE_ADDR,
|
|
.regmap_name = "rt9750",
|
|
.alias_name = "rt9750",
|
|
.chg_dev_name = "primary_load_switch",
|
|
.vbat_reg = 4400000, /* uV */
|
|
.vout_reg = 5000000, /* uV */
|
|
.iococp = 5000000, /* uA */
|
|
.wdt = 2000000, /* us */
|
|
};
|
|
|
|
struct rt9750_info {
|
|
struct i2c_client *i2c;
|
|
struct device *dev;
|
|
struct rt9750_desc *desc;
|
|
struct charger_device *chg_dev;
|
|
struct charger_properties chg_props;
|
|
struct mutex i2c_access_lock;
|
|
struct mutex adc_access_lock;
|
|
struct mutex gpio_access_lock;
|
|
u32 intr_gpio;
|
|
u32 en_gpio;
|
|
int irq;
|
|
u8 chip_rev;
|
|
bool is_chip_en;
|
|
#if 0
|
|
struct task_struct *task;
|
|
#endif
|
|
#ifdef CONFIG_RT_REGMAP
|
|
struct rt_regmap_device *regmap_dev;
|
|
struct rt_regmap_properties *regmap_prop;
|
|
#endif
|
|
};
|
|
|
|
static u32 rt9750_wdt[] = {
|
|
0, 500000, 1000000, 2000000,
|
|
}; /* us */
|
|
|
|
enum rt9750_irq_idx {
|
|
RT9750_IRQIDX_EVT1 = 0,
|
|
RT9750_IRQIDX_EVT2,
|
|
RT9750_IRQIDX_MAX,
|
|
};
|
|
|
|
static u8 rt9750_irqmask[RT9750_IRQIDX_MAX] = {
|
|
0xEF, 0xFF,
|
|
};
|
|
|
|
static const u8 rt9750_irq_maskall[RT9750_IRQIDX_MAX] = {
|
|
0xEF, 0xFF,
|
|
};
|
|
|
|
struct irq_mapping_tbl {
|
|
const char *name;
|
|
const int id;
|
|
};
|
|
|
|
#define RT9750_IRQ_MAPPING(_name, _id) {.name = #_name, .id = _id}
|
|
static const struct irq_mapping_tbl rt9750_irq_mapping_tbl[] = {
|
|
RT9750_IRQ_MAPPING(ibus_irev_flt, 0),
|
|
RT9750_IRQ_MAPPING(tbat_otp_flt, 1),
|
|
RT9750_IRQ_MAPPING(tbus_otp_flt, 2),
|
|
RT9750_IRQ_MAPPING(vout_reg_ldo, 3),
|
|
RT9750_IRQ_MAPPING(vbat_reg_ldo, 5),
|
|
RT9750_IRQ_MAPPING(ibus_reg_ldo, 6),
|
|
RT9750_IRQ_MAPPING(vbus_ovp_flt, 7),
|
|
RT9750_IRQ_MAPPING(ioc_flt, 8),
|
|
RT9750_IRQ_MAPPING(tshut_flt, 9),
|
|
RT9750_IRQ_MAPPING(bat_insert, 10),
|
|
RT9750_IRQ_MAPPING(vbus_insert, 11),
|
|
RT9750_IRQ_MAPPING(vdrop_ovp_flt, 12),
|
|
RT9750_IRQ_MAPPING(vdrop_alm_flt, 13),
|
|
RT9750_IRQ_MAPPING(adc_done, 14),
|
|
RT9750_IRQ_MAPPING(lowchg_alm_flt, 15),
|
|
};
|
|
|
|
/* ========= */
|
|
/* RT Regmap */
|
|
/* ========= */
|
|
|
|
#ifdef CONFIG_RT_REGMAP
|
|
RT_REG_DECL(RT9750_REG_CORE_CTRL0, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_EVENT1_MASK, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_EVENT2_MASK, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_EVENT1, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_EVENT2, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_EVENT1_EN, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_CONTROL, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_ADC_CTRL, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_SAMPLE_EN, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_PROT_DLYOCP, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_VBUS_OVP, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_VOUT_REG, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_VDROP_OVP, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_VDROP_ALM, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_VBAT_REG, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_IBUS_OCP, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_TBUS_OTP, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_TBAT_OTP, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_VBUS_ADC2, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_VBUS_ADC1, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_IBUS_ADC2, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_IBUS_ADC1, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_VOUT_ADC2, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_VOUT_ADC1, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_VDROP_ADC2, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_VDROP_ADC1, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_VBAT_ADC2, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_VBAT_ADC1, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_TBUS_ADC2, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_TBUS_ADC1, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_TBAT_ADC2, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_TBAT_ADC1, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_TDIE_ADC1, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_EVENT_STATUS1, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_EVENT_STATUS2, 1, RT_VOLATILE, {});
|
|
RT_REG_DECL(RT9750_REG_EVENT_STATUS, 1, RT_VOLATILE, {});
|
|
|
|
static const rt_register_map_t rt9750_regmap_map[] = {
|
|
RT_REG(RT9750_REG_CORE_CTRL0),
|
|
RT_REG(RT9750_REG_EVENT1_MASK),
|
|
RT_REG(RT9750_REG_EVENT2_MASK),
|
|
RT_REG(RT9750_REG_EVENT1),
|
|
RT_REG(RT9750_REG_EVENT2),
|
|
RT_REG(RT9750_REG_EVENT1_EN),
|
|
RT_REG(RT9750_REG_CONTROL),
|
|
RT_REG(RT9750_REG_ADC_CTRL),
|
|
RT_REG(RT9750_REG_SAMPLE_EN),
|
|
RT_REG(RT9750_REG_PROT_DLYOCP),
|
|
RT_REG(RT9750_REG_VBUS_OVP),
|
|
RT_REG(RT9750_REG_VOUT_REG),
|
|
RT_REG(RT9750_REG_VDROP_OVP),
|
|
RT_REG(RT9750_REG_VDROP_ALM),
|
|
RT_REG(RT9750_REG_VBAT_REG),
|
|
RT_REG(RT9750_REG_IBUS_OCP),
|
|
RT_REG(RT9750_REG_TBUS_OTP),
|
|
RT_REG(RT9750_REG_TBAT_OTP),
|
|
RT_REG(RT9750_REG_VBUS_ADC2),
|
|
RT_REG(RT9750_REG_VBUS_ADC1),
|
|
RT_REG(RT9750_REG_IBUS_ADC2),
|
|
RT_REG(RT9750_REG_IBUS_ADC1),
|
|
RT_REG(RT9750_REG_VOUT_ADC2),
|
|
RT_REG(RT9750_REG_VOUT_ADC1),
|
|
RT_REG(RT9750_REG_VDROP_ADC2),
|
|
RT_REG(RT9750_REG_VDROP_ADC1),
|
|
RT_REG(RT9750_REG_VBAT_ADC2),
|
|
RT_REG(RT9750_REG_VBAT_ADC1),
|
|
RT_REG(RT9750_REG_TBUS_ADC2),
|
|
RT_REG(RT9750_REG_TBUS_ADC1),
|
|
RT_REG(RT9750_REG_TBAT_ADC2),
|
|
RT_REG(RT9750_REG_TBAT_ADC1),
|
|
RT_REG(RT9750_REG_TDIE_ADC1),
|
|
RT_REG(RT9750_REG_EVENT_STATUS1),
|
|
RT_REG(RT9750_REG_EVENT_STATUS2),
|
|
RT_REG(RT9750_REG_EVENT_STATUS),
|
|
};
|
|
#endif /* CONFIG_RT_REGMAP */
|
|
|
|
static const unsigned char rt9750_reg_addr[] = {
|
|
RT9750_REG_CORE_CTRL0,
|
|
RT9750_REG_EVENT1_MASK,
|
|
RT9750_REG_EVENT2_MASK,
|
|
RT9750_REG_EVENT1,
|
|
RT9750_REG_EVENT2,
|
|
RT9750_REG_EVENT1_EN,
|
|
RT9750_REG_CONTROL,
|
|
RT9750_REG_ADC_CTRL,
|
|
RT9750_REG_SAMPLE_EN,
|
|
RT9750_REG_PROT_DLYOCP,
|
|
RT9750_REG_VBUS_OVP,
|
|
RT9750_REG_VOUT_REG,
|
|
RT9750_REG_VDROP_OVP,
|
|
RT9750_REG_VDROP_ALM,
|
|
RT9750_REG_VBAT_REG,
|
|
RT9750_REG_IBUS_OCP,
|
|
RT9750_REG_TBUS_OTP,
|
|
RT9750_REG_TBAT_OTP,
|
|
RT9750_REG_VBUS_ADC2,
|
|
RT9750_REG_VBUS_ADC1,
|
|
RT9750_REG_IBUS_ADC2,
|
|
RT9750_REG_IBUS_ADC1,
|
|
RT9750_REG_VOUT_ADC2,
|
|
RT9750_REG_VOUT_ADC1,
|
|
RT9750_REG_VDROP_ADC2,
|
|
RT9750_REG_VDROP_ADC1,
|
|
RT9750_REG_VBAT_ADC2,
|
|
RT9750_REG_VBAT_ADC1,
|
|
RT9750_REG_TBUS_ADC2,
|
|
RT9750_REG_TBUS_ADC1,
|
|
RT9750_REG_TBAT_ADC2,
|
|
RT9750_REG_TBAT_ADC1,
|
|
RT9750_REG_TDIE_ADC1,
|
|
RT9750_REG_EVENT_STATUS1,
|
|
RT9750_REG_EVENT_STATUS2,
|
|
RT9750_REG_EVENT_STATUS,
|
|
};
|
|
|
|
/* ========================= */
|
|
/* I2C operations */
|
|
/* ========================= */
|
|
|
|
static inline bool rt9750_is_chip_en(struct rt9750_info *info)
|
|
{
|
|
int en = 0;
|
|
|
|
en = gpio_get_value(info->en_gpio);
|
|
if ((en && !info->is_chip_en) || (!en && info->is_chip_en))
|
|
dev_notice(info->dev, "%s: en not sync(%d, %d)\n", __func__, en,
|
|
info->is_chip_en);
|
|
|
|
return en ? true : false;
|
|
}
|
|
|
|
static int rt9750_device_read(void *client, u32 addr, int leng, void *dst)
|
|
{
|
|
int ret = 0;
|
|
struct i2c_client *i2c = NULL;
|
|
|
|
i2c = (struct i2c_client *)client;
|
|
ret = i2c_smbus_read_i2c_block_data(i2c, addr, leng, dst);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rt9750_device_write(void *client, u32 addr, int leng,
|
|
const void *src)
|
|
{
|
|
int ret = 0;
|
|
struct i2c_client *i2c = NULL;
|
|
|
|
i2c = (struct i2c_client *)client;
|
|
ret = i2c_smbus_write_i2c_block_data(i2c, addr, leng, src);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CONFIG_RT_REGMAP
|
|
static struct rt_regmap_fops rt9750_regmap_fops = {
|
|
.read_device = rt9750_device_read,
|
|
.write_device = rt9750_device_write,
|
|
};
|
|
|
|
static int rt9750_register_rt_regmap(struct rt9750_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(rt9750_regmap_map);
|
|
prop->rm = rt9750_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,
|
|
&rt9750_regmap_fops,
|
|
&i2c->dev,
|
|
i2c,
|
|
info->desc->regmap_represent_slave_addr,
|
|
info
|
|
);
|
|
|
|
if (!info->regmap_dev) {
|
|
dev_notice(&i2c->dev, "register regmap device failed\n");
|
|
return -EIO;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* CONFIG_RT_REGMAP */
|
|
|
|
static inline int _rt9750_i2c_write_byte(struct rt9750_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 = rt9750_device_write(info->i2c, cmd, 1, &data);
|
|
#endif
|
|
retry++;
|
|
if (ret < 0)
|
|
mdelay(20);
|
|
} 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_ratelimited(info->dev, "%s: I2CW[0x%02X] = 0x%02X\n",
|
|
__func__, cmd, data);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#if 0
|
|
static int rt9750_i2c_write_byte(struct rt9750_info *info, u8 cmd, u8 data)
|
|
{
|
|
int ret = 0, en = 0;
|
|
|
|
mutex_lock(&info->i2c_access_lock);
|
|
mutex_lock(&info->gpio_access_lock);
|
|
if (rt9750_is_chip_en(info))
|
|
ret = _rt9750_i2c_write_byte(info, cmd, data);
|
|
mutex_unlock(&info->gpio_access_lock);
|
|
mutex_unlock(&info->i2c_access_lock);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static inline int _rt9750_i2c_read_byte(struct rt9750_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 = rt9750_device_read(info->i2c, cmd, 1, &ret_val);
|
|
#endif
|
|
retry++;
|
|
if (ret < 0)
|
|
msleep(20);
|
|
} 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_ratelimited(info->dev, "%s: I2CR[0x%02X] = 0x%02X\n", __func__,
|
|
cmd, ret_val);
|
|
|
|
return ret_val;
|
|
}
|
|
|
|
static int rt9750_i2c_read_byte(struct rt9750_info *info, u8 cmd)
|
|
{
|
|
int ret = 0;
|
|
|
|
mutex_lock(&info->i2c_access_lock);
|
|
mutex_lock(&info->gpio_access_lock);
|
|
if (rt9750_is_chip_en(info))
|
|
ret = _rt9750_i2c_read_byte(info, cmd);
|
|
mutex_unlock(&info->gpio_access_lock);
|
|
mutex_unlock(&info->i2c_access_lock);
|
|
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return (ret & 0xFF);
|
|
}
|
|
|
|
static inline int _rt9750_i2c_block_write(struct rt9750_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 = rt9750_device_write(info->i2c, cmd, leng, data);
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int rt9750_i2c_block_write(struct rt9750_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 (rt9750_is_chip_en(info))
|
|
ret = _rt9750_i2c_block_write(info, cmd, leng, data);
|
|
mutex_unlock(&info->gpio_access_lock);
|
|
mutex_unlock(&info->i2c_access_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static inline int _rt9750_i2c_block_read(struct rt9750_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 = rt9750_device_read(info->i2c, cmd, leng, data);
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int rt9750_i2c_block_read(struct rt9750_info *info, u8 cmd, u32 leng,
|
|
u8 *data)
|
|
{
|
|
int ret = 0;
|
|
|
|
mutex_lock(&info->i2c_access_lock);
|
|
mutex_lock(&info->gpio_access_lock);
|
|
if (rt9750_is_chip_en(info))
|
|
ret = _rt9750_i2c_block_read(info, cmd, leng, data);
|
|
mutex_unlock(&info->gpio_access_lock);
|
|
mutex_unlock(&info->i2c_access_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rt9750_i2c_test_bit(struct rt9750_info *info, u8 cmd, u8 shift)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = rt9750_i2c_read_byte(info, cmd);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = ret & (1 << shift);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rt9750_i2c_update_bits(struct rt9750_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 (rt9750_is_chip_en(info)) {
|
|
ret = _rt9750_i2c_read_byte(info, cmd);
|
|
if (ret < 0)
|
|
goto out;
|
|
|
|
reg_data = ret & 0xFF;
|
|
reg_data &= ~mask;
|
|
reg_data |= (data & mask);
|
|
|
|
ret = _rt9750_i2c_write_byte(info, cmd, reg_data);
|
|
}
|
|
|
|
out:
|
|
mutex_unlock(&info->gpio_access_lock);
|
|
mutex_unlock(&info->i2c_access_lock);
|
|
return ret;
|
|
}
|
|
|
|
static inline int rt9750_set_bit(struct rt9750_info *info, u8 reg, u8 mask)
|
|
{
|
|
return rt9750_i2c_update_bits(info, reg, mask, mask);
|
|
}
|
|
|
|
static inline int rt9750_clr_bit(struct rt9750_info *info, u8 reg, u8 mask)
|
|
{
|
|
return rt9750_i2c_update_bits(info, reg, 0x00, mask);
|
|
}
|
|
|
|
/* ========================= */
|
|
/* Internal function */
|
|
/* ========================= */
|
|
|
|
static u8 rt9750_find_closest_reg_value(const u32 min, const u32 max,
|
|
const u32 step, const u32 num, const u32 target)
|
|
{
|
|
u32 i = 0, cur_val = 0, next_val = 0;
|
|
|
|
/* Smaller than minimum supported value, use minimum one */
|
|
if (target < min)
|
|
return 0;
|
|
|
|
for (i = 0; i < num - 1; i++) {
|
|
cur_val = min + i * step;
|
|
next_val = cur_val + step;
|
|
|
|
if (cur_val > max)
|
|
cur_val = max;
|
|
|
|
if (next_val > max)
|
|
next_val = max;
|
|
|
|
if (target >= cur_val && target < next_val)
|
|
return i;
|
|
}
|
|
|
|
/* Greater than maximum supported value, use maximum one */
|
|
return num - 1;
|
|
}
|
|
|
|
static u8 rt9750_find_closest_reg_value_via_table(const u32 *value_table,
|
|
const u32 table_size, const u32 target_value)
|
|
{
|
|
u32 i = 0;
|
|
|
|
/* Smaller than minimum supported value, use minimum one */
|
|
if (target_value < value_table[0])
|
|
return 0;
|
|
|
|
for (i = 0; i < table_size - 1; i++) {
|
|
if (target_value >= value_table[i] &&
|
|
target_value < value_table[i + 1])
|
|
return i;
|
|
}
|
|
|
|
/* Greater than maximum supported value, use maximum one */
|
|
return table_size - 1;
|
|
}
|
|
|
|
static u32 rt9750_find_closest_real_value(const u32 min, const u32 max,
|
|
const u32 step, const 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 rt9750_is_hw_exist(struct rt9750_info *info)
|
|
{
|
|
int ret = 0;
|
|
u8 dev_id = 0, chip_rev = 0;
|
|
|
|
ret = i2c_smbus_read_byte_data(info->i2c, RT9750_REG_CORE_CTRL0);
|
|
if (ret < 0)
|
|
return false;
|
|
|
|
dev_id = ret & 0x07;
|
|
chip_rev = ret & 0x38;
|
|
if (dev_id != RT9750_DEVICE_ID) {
|
|
dev_notice(info->dev, "%s: device id is incorrect\n", __func__);
|
|
return false;
|
|
}
|
|
|
|
dev_info(info->dev, "%s: E%d(0x%02X)\n",
|
|
__func__, chip_rev + 1, chip_rev);
|
|
|
|
info->chip_rev = chip_rev;
|
|
return true;
|
|
}
|
|
|
|
static inline const char *rt9750_get_irq_name(struct rt9750_info *info,
|
|
int irqnum)
|
|
{
|
|
int i = 0;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(rt9750_irq_mapping_tbl); i++) {
|
|
if (rt9750_irq_mapping_tbl[i].id == irqnum)
|
|
return rt9750_irq_mapping_tbl[i].name;
|
|
}
|
|
|
|
return "not found";
|
|
}
|
|
|
|
static inline void rt9750_irq_mask(struct rt9750_info *info, int irqnum)
|
|
{
|
|
dev_dbg(info->dev, "%s: irq = %d, %s\n", __func__, irqnum,
|
|
rt9750_get_irq_name(info, irqnum));
|
|
rt9750_irqmask[irqnum / 8] |= (1 << (irqnum % 8));
|
|
}
|
|
|
|
static inline void rt9750_irq_unmask(struct rt9750_info *info, int irqnum)
|
|
{
|
|
dev_dbg(info->dev, "%s: irq = %d, %s\n", __func__, irqnum,
|
|
rt9750_get_irq_name(info, irqnum));
|
|
rt9750_irqmask[irqnum / 8] &= ~(1 << (irqnum % 8));
|
|
}
|
|
|
|
static int rt9750_ibus_irev_flt_irq_handler(struct rt9750_info *info)
|
|
{
|
|
dev_info(info->dev, "%s\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int rt9750_tbat_otp_flt_irq_handler(struct rt9750_info *info)
|
|
{
|
|
dev_info(info->dev, "%s\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int rt9750_tbus_otp_flt_irq_handler(struct rt9750_info *info)
|
|
{
|
|
dev_info(info->dev, "%s\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int rt9750_vout_reg_ldo_irq_handler(struct rt9750_info *info)
|
|
{
|
|
dev_info(info->dev, "%s\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int rt9750_vbat_reg_ldo_irq_handler(struct rt9750_info *info)
|
|
{
|
|
dev_info(info->dev, "%s\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int rt9750_ibus_reg_ldo_irq_handler(struct rt9750_info *info)
|
|
{
|
|
dev_info(info->dev, "%s\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int rt9750_vbus_ovp_flt_irq_handler(struct rt9750_info *info)
|
|
{
|
|
dev_info(info->dev, "%s\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int rt9750_ioc_flt_irq_handler(struct rt9750_info *info)
|
|
{
|
|
dev_info(info->dev, "%s\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int rt9750_tshut_flt_irq_handler(struct rt9750_info *info)
|
|
{
|
|
dev_info(info->dev, "%s\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int rt9750_bat_insert_irq_handler(struct rt9750_info *info)
|
|
{
|
|
dev_info(info->dev, "%s\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int rt9750_vbus_insert_irq_handler(struct rt9750_info *info)
|
|
{
|
|
dev_info(info->dev, "%s\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int rt9750_vdrop_ovp_flt_irq_handler(struct rt9750_info *info)
|
|
{
|
|
dev_info(info->dev, "%s\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int rt9750_vdrop_alm_flt_irq_handler(struct rt9750_info *info)
|
|
{
|
|
dev_info(info->dev, "%s\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int rt9750_adc_done_irq_handler(struct rt9750_info *info)
|
|
{
|
|
dev_info(info->dev, "%s\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int rt9750_lowchg_alm_flt_irq_handler(struct rt9750_info *info)
|
|
{
|
|
dev_info(info->dev, "%s\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
typedef int (*rt9750_irq_fptr)(struct rt9750_info *);
|
|
static rt9750_irq_fptr rt9750_irq_handler_tbl[16] = {
|
|
rt9750_ibus_irev_flt_irq_handler,
|
|
rt9750_tbat_otp_flt_irq_handler,
|
|
rt9750_tbus_otp_flt_irq_handler,
|
|
rt9750_vout_reg_ldo_irq_handler,
|
|
NULL,
|
|
rt9750_vbat_reg_ldo_irq_handler,
|
|
rt9750_ibus_reg_ldo_irq_handler,
|
|
rt9750_vbus_ovp_flt_irq_handler,
|
|
rt9750_ioc_flt_irq_handler,
|
|
rt9750_tshut_flt_irq_handler,
|
|
rt9750_bat_insert_irq_handler,
|
|
rt9750_vbus_insert_irq_handler,
|
|
rt9750_vdrop_ovp_flt_irq_handler,
|
|
rt9750_vdrop_alm_flt_irq_handler,
|
|
rt9750_adc_done_irq_handler,
|
|
rt9750_lowchg_alm_flt_irq_handler,
|
|
};
|
|
|
|
static irqreturn_t rt9750_irq_handler(int irq, void *data)
|
|
{
|
|
int ret = 0, i = 0, j = 0;
|
|
struct rt9750_info *info = (struct rt9750_info *)data;
|
|
u8 evt[RT9750_IRQIDX_MAX] = {0};
|
|
u8 mask[RT9750_IRQIDX_MAX] = {0};
|
|
|
|
dev_info(info->dev, "%s\n", __func__);
|
|
|
|
ret = rt9750_i2c_block_read(info, RT9750_REG_EVENT1, RT9750_IRQIDX_MAX,
|
|
evt);
|
|
if (ret < 0) {
|
|
dev_notice(info->dev, "%s: read irq data failed\n", __func__);
|
|
goto err_read_irq;
|
|
}
|
|
|
|
ret = rt9750_i2c_block_read(info, RT9750_REG_EVENT1_MASK,
|
|
RT9750_IRQIDX_MAX, mask);
|
|
if (ret < 0) {
|
|
dev_notice(info->dev, "%s: read irq mask failed\n", __func__);
|
|
goto err_read_irq;
|
|
}
|
|
|
|
for (i = 0; i < RT9750_IRQIDX_MAX; i++) {
|
|
evt[i] &= ~mask[i];
|
|
for (j = 0; j < 8; j++) {
|
|
if (!(evt[i] & (1 << j)))
|
|
continue;
|
|
if (rt9750_irq_handler_tbl[i * 8 + j])
|
|
rt9750_irq_handler_tbl[i * 8 + j](info);
|
|
}
|
|
}
|
|
|
|
err_read_irq:
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int rt9750_register_irq(struct rt9750_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);
|
|
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__);
|
|
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 */
|
|
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,
|
|
rt9750_irq_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, name,
|
|
info);
|
|
if (ret < 0) {
|
|
dev_notice(info->dev, "%s: request thread irq fail\n",
|
|
__func__);
|
|
goto err;
|
|
}
|
|
|
|
return 0;
|
|
err:
|
|
return ret;
|
|
}
|
|
|
|
static inline int rt9750_get_irq_number(struct rt9750_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(rt9750_irq_mapping_tbl); i++) {
|
|
if (!strcmp(name, rt9750_irq_mapping_tbl[i].name))
|
|
return rt9750_irq_mapping_tbl[i].id;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int rt9750_parse_dt(struct rt9750_info *info, struct device *dev)
|
|
{
|
|
int ret = 0, irqcnt = 0, irqnum = 0, len = 0;
|
|
struct rt9750_desc *desc = NULL;
|
|
struct device_node *np = dev->of_node;
|
|
char *en_name = NULL;
|
|
const char *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 = &rt9750_default_desc;
|
|
desc = devm_kzalloc(dev, sizeof(struct rt9750_desc), GFP_KERNEL);
|
|
if (!desc)
|
|
return -ENOMEM;
|
|
memcpy(desc, &rt9750_default_desc, sizeof(struct rt9750_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;
|
|
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,intr_gpio_num", &info->intr_gpio);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = of_property_read_u32(np, "rt,en_gpio_num", &info->en_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);
|
|
|
|
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);
|
|
|
|
/* request en gpio */
|
|
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 (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 (of_property_read_u32(np, "vout_reg", &desc->vout_reg) < 0)
|
|
dev_notice(info->dev, "%s: no vout regulation\n", __func__);
|
|
|
|
if (of_property_read_u32(np, "vbat_reg", &desc->vbat_reg) < 0)
|
|
dev_notice(info->dev, "%s: no vbat regulation\n", __func__);
|
|
|
|
if (of_property_read_u32(np, "iococp", &desc->iococp) < 0)
|
|
dev_notice(info->dev, "%s: no iococp\n", __func__);
|
|
|
|
if (of_property_read_u32(np, "wdt", &desc->wdt) < 0)
|
|
dev_notice(info->dev, "%s: no wdt\n", __func__);
|
|
|
|
while (true) {
|
|
ret = of_property_read_string_index(np, "interrupt-names",
|
|
irqcnt, &name);
|
|
if (ret < 0)
|
|
break;
|
|
irqcnt++;
|
|
irqnum = rt9750_get_irq_number(info, name);
|
|
if (irqnum >= 0)
|
|
rt9750_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 rt9750_set_wdt(struct rt9750_info *info, const u32 us)
|
|
{
|
|
int ret = 0;
|
|
u8 wdt_reg = 0;
|
|
|
|
wdt_reg = rt9750_find_closest_reg_value_via_table(rt9750_wdt,
|
|
ARRAY_SIZE(rt9750_wdt), us);
|
|
|
|
dev_info(info->dev, "%s: set wdt = %dms(0x%02X)\n",
|
|
__func__, us / 1000, wdt_reg);
|
|
|
|
ret = rt9750_i2c_update_bits(
|
|
info,
|
|
RT9750_REG_CONTROL,
|
|
wdt_reg << RT9750_SHIFT_WDT,
|
|
RT9750_MASK_WDT
|
|
);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rt9750_get_vout(struct rt9750_info *info, u32 *vout)
|
|
{
|
|
int ret = 0;
|
|
u8 reg_vout = 0;
|
|
|
|
ret = rt9750_i2c_read_byte(info, RT9750_REG_VOUT_REG);
|
|
if (ret < 0)
|
|
return ret;
|
|
reg_vout = ((ret & RT9750_MASK_VOUT) >> RT9750_SHIFT_VOUT) & 0xFF;
|
|
|
|
*vout = rt9750_find_closest_real_value(RT9750_VOUT_MIN, RT9750_VOUT_MAX,
|
|
RT9750_VOUT_STEP, reg_vout);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rt9750_set_vout(struct rt9750_info *info, u32 uV)
|
|
{
|
|
int ret = 0;
|
|
u8 reg_vout = 0;
|
|
|
|
reg_vout = rt9750_find_closest_reg_value(
|
|
RT9750_VOUT_MIN,
|
|
RT9750_VOUT_MAX,
|
|
RT9750_VOUT_STEP,
|
|
RT9750_VOUT_NUM,
|
|
uV
|
|
);
|
|
|
|
dev_info(info->dev, "%s: vout = %d (0x%02X)\n", __func__, uV, reg_vout);
|
|
|
|
ret = rt9750_i2c_update_bits(
|
|
info,
|
|
RT9750_REG_VOUT_REG,
|
|
reg_vout << RT9750_SHIFT_VOUT,
|
|
RT9750_MASK_VOUT
|
|
);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rt9750_get_vbat(struct rt9750_info *info, u32 *vbat)
|
|
{
|
|
int ret = 0;
|
|
u8 reg_vbat = 0;
|
|
|
|
ret = rt9750_i2c_read_byte(info, RT9750_REG_VBAT_REG);
|
|
if (ret < 0)
|
|
return ret;
|
|
reg_vbat = ((ret & RT9750_MASK_VBAT) >> RT9750_SHIFT_VBAT) & 0xFF;
|
|
|
|
*vbat = rt9750_find_closest_real_value(RT9750_VBAT_MIN, RT9750_VBAT_MAX,
|
|
RT9750_VBAT_STEP, reg_vbat);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rt9750_set_vbat(struct rt9750_info *info, u32 uV)
|
|
{
|
|
int ret = 0;
|
|
u8 reg_vbat = 0;
|
|
|
|
reg_vbat = rt9750_find_closest_reg_value(
|
|
RT9750_VBAT_MIN,
|
|
RT9750_VBAT_MAX,
|
|
RT9750_VBAT_STEP,
|
|
RT9750_VBAT_NUM,
|
|
uV
|
|
);
|
|
|
|
dev_info(info->dev, "%s: vbat = %d (0x%02X)\n", __func__, uV, reg_vbat);
|
|
|
|
ret = rt9750_i2c_update_bits(
|
|
info,
|
|
RT9750_REG_VBAT_REG,
|
|
reg_vbat << RT9750_SHIFT_VBAT,
|
|
RT9750_MASK_VBAT
|
|
);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rt9750_set_iococp(struct rt9750_info *info, u32 uA)
|
|
{
|
|
int ret = 0;
|
|
u8 reg_iococp = 0;
|
|
|
|
reg_iococp = rt9750_find_closest_reg_value(RT9750_IOCOCP_MIN,
|
|
RT9750_IOCOCP_MAX, RT9750_IOCOCP_STEP, RT9750_IOCOCP_NUM, uA);
|
|
|
|
dev_info(info->dev, "%s: iococp = %d (0x%02X)\n",
|
|
__func__, uA, reg_iococp);
|
|
|
|
ret = rt9750_i2c_update_bits(
|
|
info,
|
|
RT9750_REG_PROT_DLYOCP,
|
|
reg_iococp << RT9750_SHIFT_IOCOCP,
|
|
RT9750_MASK_IOCOCP
|
|
);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rt9750_maskall_irq(struct rt9750_info *info)
|
|
{
|
|
int ret = 0;
|
|
|
|
dev_info(info->dev, "%s\n", __func__);
|
|
ret = rt9750_i2c_block_write(info, RT9750_REG_EVENT1_MASK,
|
|
ARRAY_SIZE(rt9750_irq_maskall), rt9750_irq_maskall);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rt9750_init_irq(struct rt9750_info *info)
|
|
{
|
|
int ret = 0;
|
|
|
|
dev_info(info->dev, "%s\n", __func__);
|
|
|
|
ret = rt9750_i2c_block_write(info, RT9750_REG_EVENT1_MASK,
|
|
ARRAY_SIZE(rt9750_irqmask), rt9750_irqmask);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rt9750_init_setting(struct rt9750_info *info)
|
|
{
|
|
int ret = 0;
|
|
u8 evt[RT9750_IRQIDX_MAX] = {0};
|
|
|
|
ret = rt9750_maskall_irq(info);
|
|
if (ret < 0)
|
|
dev_notice(info->dev, "%s: mask all irq fail\n", __func__);
|
|
|
|
/* clear evt */
|
|
ret = rt9750_i2c_block_read(info, RT9750_REG_EVENT1, RT9750_IRQIDX_MAX,
|
|
evt);
|
|
if (ret < 0)
|
|
dev_notice(info->dev, "%s: read irq data fail\n", __func__);
|
|
|
|
ret = rt9750_set_wdt(info, info->desc->wdt);
|
|
if (ret < 0)
|
|
dev_notice(info->dev, "%s: set wdt failed\n", __func__);
|
|
|
|
ret = rt9750_set_vout(info, info->desc->vout_reg);
|
|
if (ret < 0)
|
|
dev_notice(info->dev, "%s: set vout failed\n", __func__);
|
|
|
|
ret = rt9750_set_vbat(info, info->desc->vbat_reg);
|
|
if (ret < 0)
|
|
dev_notice(info->dev, "%s: set vbat failed\n", __func__);
|
|
|
|
ret = rt9750_set_iococp(info, info->desc->iococp);
|
|
if (ret < 0)
|
|
dev_notice(info->dev, "%s: set iococp failed\n", __func__);
|
|
|
|
ret = rt9750_init_irq(info);
|
|
if (ret < 0)
|
|
dev_notice(info->dev, "%s: init irq fail\n", __func__);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rt9750_is_switch_enable(struct rt9750_info *info, bool *en)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = rt9750_i2c_test_bit(info, RT9750_REG_CONTROL,
|
|
RT9750_SHIFT_CHG_EN);
|
|
if (ret < 0) {
|
|
*en = false;
|
|
return ret;
|
|
}
|
|
|
|
*en = (ret == 0 ? false : true);
|
|
dev_info(info->dev, "%s: enable = %d\n", __func__, *en);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#if 0
|
|
static int rt9750_get_vbus_adc(struct rt9750_info *info, u32 *vbus_adc)
|
|
{
|
|
int ret = 0;
|
|
u8 data[2] = {0};
|
|
|
|
ret = rt9750_i2c_block_read(info, RT9750_REG_VBUS_ADC2, 2, data);
|
|
if (ret < 0) {
|
|
dev_notice(info->dev, "%s: get vbus adc failed\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
*vbus_adc = ((data[0] & RT9750_MASK_VBUS_ADC2) << 8) + data[1];
|
|
|
|
dev_info(info->dev, "%s: vbus_adc = %dmV\n", __func__, *vbus_adc);
|
|
return ret;
|
|
}
|
|
static int rt9750_get_vout_adc(struct rt9750_info *info, u32 *vout_adc)
|
|
{
|
|
int ret = 0;
|
|
u8 data[2] = {0};
|
|
|
|
ret = rt9750_i2c_block_read(info, RT9750_REG_VOUT_ADC2, 2, data);
|
|
if (ret < 0) {
|
|
dev_notice(info->dev, "%s: get vout adc failed\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
*vout_adc = ((data[0] & RT9750_MASK_VOUT_ADC2) << 8) + data[1];
|
|
|
|
dev_info(info->dev, "%s: vout_adc = %dmV\n", __func__, *vout_adc);
|
|
return ret;
|
|
}
|
|
|
|
static int rt9750_get_vdrop_adc(struct rt9750_info *info, u32 *vdrop_adc)
|
|
{
|
|
int ret = 0;
|
|
u8 data[2] = {0};
|
|
|
|
ret = rt9750_i2c_block_read(info, RT9750_REG_VDROP_ADC2, 2, data);
|
|
if (ret < 0) {
|
|
dev_notice(info->dev, "%s: get vdrop adc failed\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
*vdrop_adc = ((data[0] & RT9750_MASK_VDROP_ADC2) << 8) + data[1];
|
|
|
|
dev_info(info->dev, "%s: vdrop_adc = %dmV\n", __func__, *vdrop_adc);
|
|
return ret;
|
|
}
|
|
|
|
static int rt9750_get_vbat_adc(struct rt9750_info *info, u32 *vbat_adc)
|
|
{
|
|
int ret = 0;
|
|
u8 data[2] = {0};
|
|
|
|
ret = rt9750_i2c_block_read(info, RT9750_REG_VBAT_ADC2, 2, data);
|
|
if (ret < 0) {
|
|
dev_notice(info->dev, "%s: get vbat adc failed\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
*vbat_adc = ((data[0] & RT9750_MASK_VBAT_ADC2) << 8) + data[1];
|
|
|
|
dev_info(info->dev, "%s: vbat_adc = %dmV\n", __func__, *vbat_adc);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int rt9750_get_tbat_adc(struct rt9750_info *info, u32 *tbat_adc)
|
|
{
|
|
int ret = 0;
|
|
u8 data[2] = {0};
|
|
|
|
ret = rt9750_i2c_block_read(info, RT9750_REG_TBAT_ADC2, 2, data);
|
|
if (ret < 0) {
|
|
dev_notice(info->dev, "%s: get tbat adc failed\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
*tbat_adc = ((data[0] & RT9750_MASK_TBAT_ADC2) << 8) + data[1];
|
|
|
|
dev_info(info->dev, "%s: tbat_adc = %ddegree\n", __func__, *tbat_adc);
|
|
return ret;
|
|
}
|
|
|
|
static int rt9750_get_tbus_adc(struct rt9750_info *info, u32 *tbus_adc)
|
|
{
|
|
int ret = 0;
|
|
u8 data[2] = {0};
|
|
|
|
ret = rt9750_i2c_block_read(info, RT9750_REG_TBUS_ADC2, 2, data);
|
|
if (ret < 0) {
|
|
dev_notice(info->dev, "%s: get tbus adc failed\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
*tbus_adc = ((data[0] & RT9750_MASK_TBUS_ADC2) << 8) + data[1];
|
|
|
|
dev_info(info->dev, "%s: tbus_adc = %ddegree\n", __func__, *tbus_adc);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
/* ========================= */
|
|
/* Released function */
|
|
/* ========================= */
|
|
|
|
static int rt9750_dump_register(struct charger_device *chg_dev)
|
|
{
|
|
int i = 0, ret = 0;
|
|
u32 vout = 0, vbat = 0;
|
|
bool en = false;
|
|
struct rt9750_info *info = dev_get_drvdata(&chg_dev->dev);
|
|
|
|
ret = rt9750_get_vout(info, &vout);
|
|
ret = rt9750_get_vbat(info, &vbat);
|
|
ret = rt9750_is_switch_enable(info, &en);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(rt9750_reg_addr); i++) {
|
|
ret = rt9750_i2c_read_byte(info, rt9750_reg_addr[i]);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
|
|
dev_info(info->dev, "%s: VOUT = %dmV, VBAT = %dmV, SWITCH_EN = %d\n",
|
|
__func__, vout / 1000, vbat / 1000, en);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int _rt9750_enable_chip(struct rt9750_info *info, bool en)
|
|
{
|
|
dev_info(info->dev, "%s\n", __func__);
|
|
|
|
mutex_lock(&info->gpio_access_lock);
|
|
if (en) {
|
|
/* Lock I2C to solve I2C SDA drop problem */
|
|
i2c_lock_adapter(info->i2c->adapter);
|
|
gpio_set_value(info->en_gpio, 1);
|
|
dev_info(info->dev, "%s: set gpio high\n", __func__);
|
|
udelay(10);
|
|
i2c_unlock_adapter(info->i2c->adapter);
|
|
|
|
/* wait rt9750 enable, at least 200us */
|
|
mdelay(1);
|
|
} else {
|
|
gpio_set_value(info->en_gpio, 0);
|
|
dev_info(info->dev, "%s: set gpio low\n", __func__);
|
|
}
|
|
|
|
info->is_chip_en = en;
|
|
mutex_unlock(&info->gpio_access_lock);
|
|
return 0;
|
|
}
|
|
|
|
static int rt9750_enable_chip(struct charger_device *chg_dev, bool en)
|
|
{
|
|
int ret = 0;
|
|
struct rt9750_info *info = dev_get_drvdata(&chg_dev->dev);
|
|
|
|
ret = _rt9750_enable_chip(info, en);
|
|
if (ret < 0) {
|
|
dev_notice(info->dev, "%s: enable chip failed\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
if (en) {
|
|
ret = rt9750_init_setting(info);
|
|
if (ret < 0)
|
|
dev_notice(info->dev, "%s: init setting failed\n",
|
|
__func__);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rt9750_enable_switch(struct charger_device *chg_dev, bool en)
|
|
{
|
|
int ret = 0;
|
|
struct rt9750_info *info = dev_get_drvdata(&chg_dev->dev);
|
|
|
|
dev_info(info->dev, "%s, enable = %d\n", __func__, en);
|
|
ret = (en ? rt9750_set_bit : rt9750_clr_bit)
|
|
(info, RT9750_REG_CONTROL, RT9750_MASK_CHG_EN);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rt9750_set_ibusoc(struct charger_device *chg_dev, u32 uA)
|
|
{
|
|
int ret = 0;
|
|
struct rt9750_info *info = dev_get_drvdata(&chg_dev->dev);
|
|
u8 reg_ibusoc = 0;
|
|
|
|
reg_ibusoc = rt9750_find_closest_reg_value(RT9750_IBUSOC_MIN,
|
|
RT9750_IBUSOC_MAX, RT9750_IBUSOC_STEP, RT9750_IBUSOC_NUM, uA);
|
|
|
|
dev_info(info->dev, "%s: ibusoc = %d (0x%02X)\n", __func__, uA,
|
|
reg_ibusoc);
|
|
|
|
ret = rt9750_i2c_update_bits(
|
|
info,
|
|
RT9750_REG_IBUS_OCP,
|
|
reg_ibusoc << RT9750_SHIFT_IBUS_OCP,
|
|
RT9750_MASK_IBUS_OCP
|
|
);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int rt9750_set_vbusov(struct charger_device *chg_dev, u32 uV)
|
|
{
|
|
int ret = 0;
|
|
struct rt9750_info *info = dev_get_drvdata(&chg_dev->dev);
|
|
u8 reg_vbusov = 0;
|
|
|
|
reg_vbusov = rt9750_find_closest_reg_value(RT9750_VBUSOV_MIN,
|
|
RT9750_VBUSOV_MAX, RT9750_VBUSOV_STEP, RT9750_VBUSOV_NUM,
|
|
uV);
|
|
|
|
dev_info(info->dev, "%s: vbusov = %d (0x%02X)\n",
|
|
__func__, uV, reg_vbusov);
|
|
|
|
ret = rt9750_i2c_update_bits(
|
|
info,
|
|
RT9750_REG_VBUS_OVP,
|
|
reg_vbusov << RT9750_SHIFT_VBUSOVP,
|
|
RT9750_MASK_VBUSOVP
|
|
);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int rt9750_kick_wdt(struct charger_device *chg_dev)
|
|
{
|
|
int ret = 0;
|
|
u32 vout = 0;
|
|
struct rt9750_info *info = dev_get_drvdata(&chg_dev->dev);
|
|
|
|
/* Any I2C operation can kick wdt */
|
|
dev_info(info->dev, "%s\n", __func__);
|
|
ret = rt9750_get_vout(info, &vout);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rt9750_get_ibus_adc(struct charger_device *chg_dev, u32 *ibus_adc)
|
|
{
|
|
int ret = 0;
|
|
u8 data[2] = {0};
|
|
struct rt9750_info *info = dev_get_drvdata(&chg_dev->dev);
|
|
|
|
ret = rt9750_i2c_block_read(info, RT9750_REG_IBUS_ADC2, 2, data);
|
|
if (ret < 0) {
|
|
dev_notice(info->dev, "%s: get ibus adc failed\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
*ibus_adc = ((data[0] & RT9750_MASK_IBUS_ADC2) << 8) + data[1];
|
|
*ibus_adc *= 1000; /* uA */
|
|
|
|
dev_info(info->dev, "%s: ibus_adc = %dmA\n", __func__, *ibus_adc);
|
|
return ret;
|
|
}
|
|
|
|
static int rt9750_get_tdie_adc(struct charger_device *chg_dev,
|
|
int *tdie_adc_min, int *tdie_adc_max)
|
|
{
|
|
int ret = 0;
|
|
struct rt9750_info *info = dev_get_drvdata(&chg_dev->dev);
|
|
|
|
ret = rt9750_i2c_read_byte(info, RT9750_REG_TDIE_ADC1);
|
|
if (ret < 0) {
|
|
dev_notice(info->dev, "%s: get vbus adc failed\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
*tdie_adc_min = ret;
|
|
*tdie_adc_max = ret;
|
|
|
|
dev_info(info->dev, "%s: tdie_adc = %ddegree\n",
|
|
__func__, *tdie_adc_min);
|
|
return ret;
|
|
}
|
|
|
|
static struct charger_ops rt9750_chg_ops = {
|
|
.enable_chip = rt9750_enable_chip,
|
|
.enable_direct_charging = rt9750_enable_switch,
|
|
.dump_registers = rt9750_dump_register,
|
|
.kick_direct_charging_wdt = rt9750_kick_wdt,
|
|
.set_direct_charging_ibusoc = rt9750_set_ibusoc,
|
|
.set_direct_charging_vbusov = rt9750_set_vbusov,
|
|
.get_ibus_adc = rt9750_get_ibus_adc,
|
|
.get_tchg_adc = rt9750_get_tdie_adc,
|
|
};
|
|
|
|
|
|
/* ========================= */
|
|
/* I2C driver function */
|
|
/* ========================= */
|
|
|
|
#if 0
|
|
static int rt9750_dbg_thread(void *data)
|
|
{
|
|
int ret = 0;
|
|
u32 ibus_adc = 0;
|
|
int tdie_min = 0, tdie_max = 0;
|
|
bool en = false;
|
|
struct rt9750_info *info = (struct rt9750_info *)data;
|
|
|
|
dev_info(info->dev, "%s\n", __func__);
|
|
ret = rt9750_enable_chip(info->chg_dev, true);
|
|
while (1) {
|
|
ret = rt9750_get_ibus_adc(info->chg_dev, &ibus_adc);
|
|
ret = rt9750_get_tdie_adc(info->chg_dev, &tdie_min, &tdie_max);
|
|
ret = rt9750_set_ibusoc(info->chg_dev, 6000000);
|
|
ret = rt9750_set_vbusov(info->chg_dev, 6000000);
|
|
ret = rt9750_is_switch_enable(info, &en);
|
|
msleep(2000);
|
|
};
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static int rt9750_probe(struct i2c_client *i2c,
|
|
const struct i2c_device_id *dev_id)
|
|
{
|
|
int ret = 0;
|
|
struct rt9750_info *info = NULL;
|
|
|
|
pr_info("%s: %s\n", __func__, RT9750_DRV_VERSION);
|
|
|
|
info = devm_kzalloc(&i2c->dev, sizeof(struct rt9750_info), GFP_KERNEL);
|
|
if (!info)
|
|
return -ENOMEM;
|
|
|
|
info->i2c = i2c;
|
|
info->dev = &i2c->dev;
|
|
info->is_chip_en = false;
|
|
mutex_init(&info->i2c_access_lock);
|
|
mutex_init(&info->adc_access_lock);
|
|
mutex_init(&info->gpio_access_lock);
|
|
|
|
ret = rt9750_parse_dt(info, &i2c->dev);
|
|
if (ret < 0) {
|
|
dev_notice(info->dev, "%s: parse dt failed\n", __func__);
|
|
goto err_parse_dt;
|
|
}
|
|
|
|
/* Enable Chip */
|
|
ret = _rt9750_enable_chip(info, true);
|
|
if (ret < 0) {
|
|
dev_notice(info->dev, "%s: enable chip failed\n", __func__);
|
|
goto err_enable_chip;
|
|
}
|
|
|
|
/* Is HW exist */
|
|
if (!rt9750_is_hw_exist(info)) {
|
|
dev_notice(info->dev, "%s: no rt9750 exists\n", __func__);
|
|
ret = -ENODEV;
|
|
goto err_no_dev;
|
|
}
|
|
i2c_set_clientdata(i2c, info);
|
|
|
|
#ifdef CONFIG_RT_REGMAP
|
|
ret = rt9750_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, &rt9750_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 = rt9750_register_irq(info);
|
|
if (ret < 0) {
|
|
dev_notice(info->dev, "%s: register irq failed\n", __func__);
|
|
goto err_register_irq;
|
|
}
|
|
|
|
rt9750_dump_register(info->chg_dev);
|
|
|
|
/* Disable Chip */
|
|
ret = _rt9750_enable_chip(info, false);
|
|
if (ret < 0) {
|
|
dev_notice(info->dev, "%s: disable chip failed\n", __func__);
|
|
goto err_disable_chip;
|
|
}
|
|
|
|
|
|
#if 0
|
|
info->task = kthread_create(rt9750_dbg_thread, (void *)info,
|
|
"dbg_thread");
|
|
if (IS_ERR(info->task))
|
|
dev_notice(info->dev, "%s: create dbg thread failed\n",
|
|
__func__);
|
|
wake_up_process(info->task);
|
|
#endif
|
|
|
|
dev_info(info->dev, "%s: ends\n", __func__);
|
|
return ret;
|
|
|
|
err_disable_chip:
|
|
err_register_irq:
|
|
err_register_chg_dev:
|
|
#ifdef CONFIG_RT_REGMAP
|
|
rt_regmap_device_unregister(info->regmap_dev);
|
|
err_register_regmap:
|
|
#endif
|
|
err_no_dev:
|
|
_rt9750_enable_chip(info, false);
|
|
err_enable_chip:
|
|
err_parse_dt:
|
|
mutex_destroy(&info->i2c_access_lock);
|
|
mutex_destroy(&info->adc_access_lock);
|
|
mutex_destroy(&info->gpio_access_lock);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int rt9750_remove(struct i2c_client *i2c)
|
|
{
|
|
int ret = 0;
|
|
struct rt9750_info *info = i2c_get_clientdata(i2c);
|
|
|
|
pr_info("%s\n", __func__);
|
|
|
|
if (info) {
|
|
#ifdef CONFIG_RT_REGMAP
|
|
rt_regmap_device_unregister(info->regmap_dev);
|
|
#endif
|
|
charger_device_unregister(info->chg_dev);
|
|
mutex_destroy(&info->i2c_access_lock);
|
|
mutex_destroy(&info->adc_access_lock);
|
|
mutex_destroy(&info->gpio_access_lock);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void rt9750_shutdown(struct i2c_client *i2c)
|
|
{
|
|
pr_info("%s\n", __func__);
|
|
}
|
|
|
|
static const struct i2c_device_id rt9750_i2c_id[] = {
|
|
{"rt9750", 0},
|
|
{}
|
|
};
|
|
|
|
#ifdef CONFIG_OF
|
|
static const struct of_device_id rt9750_of_match[] = {
|
|
{ .compatible = "richtek,rt9750", },
|
|
{},
|
|
};
|
|
#else /* Not define CONFIG_OF */
|
|
|
|
#define RT9750_BUSNUM 0
|
|
|
|
static struct i2c_board_info rt9750_i2c_board_info __initdata = {
|
|
I2C_BOARD_INFO("rt9750", rt9750_SALVE_ADDR)
|
|
};
|
|
#endif /* CONFIG_OF */
|
|
|
|
|
|
static struct i2c_driver rt9750_i2c_driver = {
|
|
.driver = {
|
|
.name = "rt9750",
|
|
.owner = THIS_MODULE,
|
|
#ifdef CONFIG_OF
|
|
.of_match_table = rt9750_of_match,
|
|
#endif
|
|
},
|
|
.probe = rt9750_probe,
|
|
.remove = rt9750_remove,
|
|
.shutdown = rt9750_shutdown,
|
|
.id_table = rt9750_i2c_id,
|
|
};
|
|
|
|
static int __init rt9750_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(RT9750_BUSNUM, &rt9750_i2c_board_info, 1);
|
|
#endif
|
|
|
|
ret = i2c_add_driver(&rt9750_i2c_driver);
|
|
if (ret < 0)
|
|
pr_notice("%s: register i2c driver failed\n", __func__);
|
|
|
|
return ret;
|
|
}
|
|
module_init(rt9750_init);
|
|
|
|
|
|
static void __exit rt9750_exit(void)
|
|
{
|
|
i2c_del_driver(&rt9750_i2c_driver);
|
|
}
|
|
module_exit(rt9750_exit);
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("ShuFanLee <shufan_lee@richtek.com>");
|
|
MODULE_DESCRIPTION("RT9750 Load Switch Driver");
|
|
MODULE_VERSION(RT9750_DRV_VERSION);
|
|
|
|
/*
|
|
* Version Note
|
|
* 1.0.10
|
|
* (1) Use GPIO API instead of pinctrl
|
|
* (2) Add IRQ handlers for each IRQ
|
|
*
|
|
* 1.0.9
|
|
* (1) Modify init sequence in probe function
|
|
* (2) Change pr_xxx to dev_xxx
|
|
*
|
|
* 1.0.8
|
|
* (1) Modify unit of get_ibus to uA
|
|
* (2) Modify load switch name to primary_load_switch
|
|
*
|
|
* 1.0.7
|
|
* (1) Add 1ms delay after enabling chip
|
|
*
|
|
* 1.0.6
|
|
* (1) Disable chip if probed failed
|
|
*
|
|
* 1.0.5
|
|
* (1) Unregister charger device if probe failed
|
|
* (2) Mask all irqs for now
|
|
*
|
|
* 1.0.4
|
|
* (1) Add set_iococp
|
|
* (2) Init iococp in init_setting
|
|
*
|
|
* 1.0.3
|
|
* (1) Remove registering power supply class
|
|
*
|
|
* 1.0.2
|
|
* (1) Add interface for setting ibusoc/vbusov
|
|
* (2) Write initial setting every time after enabling chip
|
|
* (3) Init vout_reg/vbat_reg in init_setting
|
|
*
|
|
* 1.0.1
|
|
* (1) Add gpio lock for rt9750_enable_chip
|
|
*
|
|
* 1.0.0
|
|
* First Release
|
|
*/
|