kernel_samsung_a34x-permissive/drivers/power/supply/mediatek/charger/rt9759.c
2024-04-28 15:51:13 +02:00

2004 lines
54 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2021 MediaTek Inc.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/of.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/kthread.h>
#include <linux/gpio/consumer.h>
#include "mtk_charger_intf.h"
#ifdef CONFIG_RT_REGMAP
#include <mt-plat/rt-regmap.h>
#endif /* CONFIG_RT_REGMAP */
/* Information */
#define RT9759_DRV_VERSION "2.0.1_MTK"
#define RT9759_DEVID 0x08
/* Registers */
#define RT9759_REG_VBATOVP 0x00
#define RT9759_REG_VBATOVP_ALM 0x01
#define RT9759_REG_IBATOCP 0x02
#define RT9759_REG_IBATOCP_ALM 0x03
#define RT9759_REG_IBATUCP_ALM 0x04
#define RT9759_REG_ACPROTECT 0x05
#define RT9759_REG_VBUSOVP 0x06
#define RT9759_REG_VBUSOVP_ALM 0x07
#define RT9759_REG_IBUSOCUCP 0x08
#define RT9759_REG_IBUSOCP_ALM 0x09
#define RT9759_REG_CONVSTAT 0x0A
#define RT9759_REG_CHGCTRL0 0x0B
#define RT9759_REG_CHGCTRL1 0x0C
#define RT9759_REG_INTSTAT 0x0D
#define RT9759_REG_INTFLAG 0x0E
#define RT9759_REG_INTMASK 0x0F
#define RT9759_REG_FLTSTAT 0x10
#define RT9759_REG_FLTFLAG 0x11
#define RT9759_REG_FLTMASK 0x12
#define RT9759_REG_DEVINFO 0x13
#define RT9759_REG_ADCCTRL 0x14
#define RT9759_REG_ADCEN 0x15
#define RT9759_REG_IBUSADC1 0x16
#define RT9759_REG_IBUSADC0 0x17
#define RT9759_REG_VBUSADC1 0x18
#define RT9759_REG_VBUSADC0 0x19
#define RT9759_REG_VACADC1 0x1A
#define RT9759_REG_VACADC0 0x1B
#define RT9759_REG_VOUTADC1 0x1C
#define RT9759_REG_VOUTADC0 0x1D
#define RT9759_REG_VBATADC1 0x1E
#define RT9759_REG_VBATADC0 0x1F
#define RT9759_REG_IBATADC1 0x20
#define RT9759_REG_IBATADC0 0x21
#define RT9759_REG_TSBUSADC1 0x22
#define RT9759_REG_TSBUSADC0 0x23
#define RT9759_REG_TSBATADC1 0x24
#define RT9759_REG_TSBATADC0 0x25
#define RT9759_REG_TDIEADC1 0x26
#define RT9759_REG_TDIEADC0 0x27
#define RT9759_REG_TSBUSOTP 0x28
#define RT9759_REG_TSBATOTP 0x29
#define RT9759_REG_TDIEALM 0x2A
#define RT9759_REG_REGCTRL 0x2B
#define RT9759_REG_REGTHRES 0x2C
#define RT9759_REG_REGFLAGMASK 0x2D
#define RT9759_REG_BUSDEGLH 0x2E
#define RT9759_REG_OTHER1 0x30
#define RT9759_REG_SYSCTRL1 0x42
#define RT9759_REG_PASSWORD0 0x90
#define RT9759_REG_PASSWORD1 0x91
/* Control bits */
#define RT9759_CHGEN_MASK BIT(7)
#define RT9759_CHGEN_SHFT 7
#define RT9759_ADCEN_MASK BIT(7)
#define RT9759_WDTEN_MASK BIT(2)
#define RT9759_WDTMR_MASK 0x03
#define RT9759_REGRST_MASK BIT(7)
#define RT9759_DEVREV_MASK 0xF0
#define RT9759_DEVREV_SHFT 4
#define RT9759_DEVID_MASK 0x0F
#define RT9759_MS_MASK 0x60
#define RT9759_MS_SHFT 5
#define RT9759_VBUSOVP_MASK 0x7F
#define RT9759_IBUSOCP_MASK 0x0F
#define RT9759_VBATOVP_MASK 0x3F
#define RT9759_IBATOCP_MASK 0x7F
#define RT9759_VBATOVP_ALM_MASK 0x3F
#define RT9759_VBATOVP_ALMDIS_MASK BIT(7)
#define RT9759_VBUSOVP_ALM_MASK 0x7F
#define RT9759_VBUSOVP_ALMDIS_MASK BIT(7)
#define RT9759_VBUSLOWERR_FLAG_SHFT 2
#define RT9759_VBUSLOWERR_STAT_SHFT 5
enum rt9759_irqidx {
RT9759_IRQIDX_VACOVP = 0,
RT9759_IRQIDX_IBUSUCPF,
RT9759_IRQIDX_IBUSUCPR,
RT9759_IRQIDX_CFLYDIAG,
RT9759_IRQIDX_CONOCP,
RT9759_IRQIDX_SWITCHING,
RT9759_IRQIDX_IBUSUCPTOUT,
RT9759_IRQIDX_VBUSHERR,
RT9759_IRQIDX_VBUSLERR,
RT9759_IRQIDX_TDIEOTP,
RT9759_IRQIDX_WDT,
RT9759_IRQIDX_ADCDONE,
RT9759_IRQIDX_VOUTINSERT,
RT9759_IRQIDX_VACINSERT,
RT9759_IRQIDX_IBATUCPALM,
RT9759_IRQIDX_IBUSOCPALM,
RT9759_IRQIDX_VBUSOVPALM,
RT9759_IRQIDX_IBATOCPALM,
RT9759_IRQIDX_VBATOVPALM,
RT9759_IRQIDX_TDIEOTPALM,
RT9759_IRQIDX_TSBUSOTP,
RT9759_IRQIDX_TSBATOTP,
RT9759_IRQIDX_TSBUSBATOTPALM,
RT9759_IRQIDX_IBUSOCP,
RT9759_IRQIDX_VBUSOVP,
RT9759_IRQIDX_IBATOCP,
RT9759_IRQIDX_VBATOVP,
RT9759_IRQIDX_VOUTOVP,
RT9759_IRQIDX_VDROVP,
RT9759_IRQIDX_IBATREG,
RT9759_IRQIDX_VBATREG,
RT9759_IRQIDX_MAX,
};
enum rt9759_notify {
RT9759_NOTIFY_IBUSUCPF = 0,
RT9759_NOTIFY_VBUSOVPALM,
RT9759_NOTIFY_VBATOVPALM,
RT9759_NOTIFY_IBUSOCP,
RT9759_NOTIFY_VBUSOVP,
RT9759_NOTIFY_IBATOCP,
RT9759_NOTIFY_VBATOVP,
RT9759_NOTIFY_VOUTOVP,
RT9759_NOTIFY_VDROVP,
RT9759_NOTIFY_MAX,
};
enum rt9759_statflag_idx {
RT9759_SF_ACPROTECT = 0,
RT9759_SF_IBUSOCUCP,
RT9759_SF_CONVSTAT,
RT9759_SF_CHGCTRL0,
RT9759_SF_INTFLAG,
RT9759_SF_INTSTAT,
RT9759_SF_FLTFLAG,
RT9759_SF_FLTSTAT,
RT9759_SF_REGFLAGMASK,
RT9759_SF_REGTHRES,
RT9759_SF_OTHER1,
RT9759_SF_MAX,
};
enum rt9759_type {
RT9759_TYPE_STANDALONE = 0,
RT9759_TYPE_SLAVE,
RT9759_TYPE_MASTER,
RT9759_TYPE_MAX,
};
static const char *rt9759_type_name[RT9759_TYPE_MAX] = {
"standalone", "slave", "master",
};
static const u32 rt9759_chgdev_notify_map[RT9759_NOTIFY_MAX] = {
CHARGER_DEV_NOTIFY_IBUSUCP_FALL,
CHARGER_DEV_NOTIFY_VBUSOVP_ALARM,
CHARGER_DEV_NOTIFY_VBATOVP_ALARM,
CHARGER_DEV_NOTIFY_IBUSOCP,
CHARGER_DEV_NOTIFY_VBUS_OVP,
CHARGER_DEV_NOTIFY_IBATOCP,
CHARGER_DEV_NOTIFY_BAT_OVP,
CHARGER_DEV_NOTIFY_VOUTOVP,
CHARGER_DEV_NOTIFY_VDROVP,
};
static const u8 rt9759_reg_sf[RT9759_SF_MAX] = {
RT9759_REG_ACPROTECT,
RT9759_REG_IBUSOCUCP,
RT9759_REG_CONVSTAT,
RT9759_REG_CHGCTRL0,
RT9759_REG_INTFLAG,
RT9759_REG_INTSTAT,
RT9759_REG_FLTFLAG,
RT9759_REG_FLTSTAT,
RT9759_REG_REGFLAGMASK,
RT9759_REG_REGTHRES,
RT9759_REG_OTHER1,
};
struct rt9759_reg_defval {
u8 reg;
u8 value;
u8 mask;
};
static const struct rt9759_reg_defval rt9759_init_chip_check_reg[] = {
{
.reg = RT9759_REG_VBATOVP,
.value = 0x22,
.mask = RT9759_VBATOVP_MASK,
},
{
.reg = RT9759_REG_IBATOCP,
.value = 0x3D,
.mask = RT9759_IBATOCP_MASK,
},
{
.reg = RT9759_REG_CHGCTRL0,
.value = 0x00,
.mask = RT9759_WDTMR_MASK,
},
};
struct rt9759_desc {
const char *chg_name;
const char *rm_name;
u8 rm_slave_addr;
u32 vbatovp;
u32 vbatovp_alm;
u32 ibatocp;
u32 ibatocp_alm;
u32 ibatucp_alm;
u32 vbusovp;
u32 vbusovp_alm;
u32 ibusocp;
u32 ibusocp_alm;
u32 vacovp;
u32 wdt;
u32 ibat_rsense;
u32 ibusucpf_deglitch;
bool vbatovp_dis;
bool vbatovp_alm_dis;
bool ibatocp_dis;
bool ibatocp_alm_dis;
bool ibatucp_alm_dis;
bool vbusovp_alm_dis;
bool ibusocp_dis;
bool ibusocp_alm_dis;
bool wdt_dis;
bool tsbusotp_dis;
bool tsbatotp_dis;
bool tdieotp_dis;
bool reg_en;
bool voutovp_dis;
bool ibusadc_dis;
bool vbusadc_dis;
bool vacadc_dis;
bool voutadc_dis;
bool vbatadc_dis;
bool ibatadc_dis;
bool tsbusadc_dis;
bool tsbatadc_dis;
bool tdieadc_dis;
bool ibat_rsense_half;
};
static const struct rt9759_desc rt9759_desc_defval = {
.chg_name = "divider_charger",
.rm_name = "rt9759",
.rm_slave_addr = 0x66,
.vbatovp = 4350000,
.vbatovp_alm = 4200000,
.ibatocp = 8100000,
.ibatocp_alm = 8000000,
.ibatucp_alm = 2000000,
.vbusovp = 8900000,
.vbusovp_alm = 8800000,
.ibusocp = 4250000,
.ibusocp_alm = 4000000,
.vacovp = 11000000,
.wdt = 500000,
.ibat_rsense = 0, /* 2mohm */
.ibusucpf_deglitch = 0, /* 10us */
.vbatovp_dis = false,
.vbatovp_alm_dis = false,
.ibatocp_dis = false,
.ibatocp_alm_dis = false,
.ibatucp_alm_dis = false,
.vbusovp_alm_dis = false,
.ibusocp_dis = false,
.ibusocp_alm_dis = false,
.wdt_dis = false,
.tsbusotp_dis = false,
.tsbatotp_dis = false,
.tdieotp_dis = false,
.reg_en = false,
.voutovp_dis = false,
.ibat_rsense_half = false,
};
struct rt9759_chip {
struct device *dev;
struct i2c_client *client;
struct mutex io_lock;
struct mutex adc_lock;
struct mutex stat_lock;
struct mutex notify_lock;
struct charger_device *chg_dev;
struct charger_properties chg_prop;
struct rt9759_desc *desc;
struct gpio_desc *irq_gpio;
struct task_struct *notify_task;
int irq;
int notify;
u8 revision;
u32 flag;
u32 stat;
u32 hm_cnt;
enum rt9759_type type;
bool wdt_en;
bool force_adc_en;
bool stop_thread;
wait_queue_head_t wq;
#ifdef CONFIG_RT_REGMAP
struct rt_regmap_device *rm_dev;
struct rt_regmap_properties *rm_prop;
#endif /* CONFIG_RT_REGMAP */
};
enum rt9759_adc_channel {
RT9759_ADC_IBUS = 0,
RT9759_ADC_VBUS,
RT9759_ADC_VAC,
RT9759_ADC_VOUT,
RT9759_ADC_VBAT,
RT9759_ADC_IBAT,
RT9759_ADC_TSBUS,
RT9759_ADC_TSBAT,
RT9759_ADC_TDIE,
RT9759_ADC_MAX,
RT9759_ADC_NOTSUPP = RT9759_ADC_MAX,
};
static const u8 rt9759_adc_reg[RT9759_ADC_MAX] = {
RT9759_REG_IBUSADC1,
RT9759_REG_VBUSADC1,
RT9759_REG_VACADC1,
RT9759_REG_VOUTADC1,
RT9759_REG_VBATADC1,
RT9759_REG_IBATADC1,
RT9759_REG_TSBUSADC1,
RT9759_REG_TSBATADC1,
RT9759_REG_TDIEADC1,
};
static const char *rt9759_adc_name[RT9759_ADC_MAX] = {
"Ibus", "Vbus", "VAC", "Vout", "Vbat", "Ibat", "TSBus", "TSBat", "TDie",
};
static const u32 rt9759_adc_accuracy_tbl[RT9759_ADC_MAX] = {
150000, /* IBUS */
35000, /* VBUS */
35000, /* VAC */
20000, /* VOUT */
20000, /* VBAT */
200000, /* IBAT */
1, /* TSBUS */
1, /* TSBAT */
4, /* TDIE */
};
static int rt9759_read_device(void *client, u32 addr, int len, void *dst)
{
struct i2c_client *i2c = (struct i2c_client *)client;
return i2c_smbus_read_i2c_block_data(i2c, addr, len, dst);
}
static int rt9759_write_device(void *client, u32 addr, int len, const void *src)
{
struct i2c_client *i2c = (struct i2c_client *)client;
return i2c_smbus_write_i2c_block_data(i2c, addr, len, src);
}
#ifdef CONFIG_RT_REGMAP
RT_REG_DECL(RT9759_REG_VBATOVP, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_VBATOVP_ALM, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_IBATOCP, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_IBATOCP_ALM, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_IBATUCP_ALM, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_ACPROTECT, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_VBUSOVP, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_VBUSOVP_ALM, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_IBUSOCUCP, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_IBUSOCP_ALM, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_CONVSTAT, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_CHGCTRL0, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_CHGCTRL1, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_INTSTAT, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_INTFLAG, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_INTMASK, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_FLTSTAT, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_FLTFLAG, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_FLTMASK, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_DEVINFO, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_ADCCTRL, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_ADCEN, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_IBUSADC1, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_IBUSADC0, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_VBUSADC1, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_VBUSADC0, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_VACADC1, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_VACADC0, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_VOUTADC1, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_VOUTADC0, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_VBATADC1, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_VBATADC0, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_IBATADC1, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_IBATADC0, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_TSBUSADC1, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_TSBUSADC0, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_TSBATADC1, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_TSBATADC0, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_TDIEADC1, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_TDIEADC0, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_TSBUSOTP, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_TSBATOTP, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_TDIEALM, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_REGCTRL, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_REGTHRES, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_REGFLAGMASK, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_BUSDEGLH, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_OTHER1, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_SYSCTRL1, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_PASSWORD0, 1, RT_VOLATILE, {});
RT_REG_DECL(RT9759_REG_PASSWORD1, 1, RT_VOLATILE, {});
static const rt_register_map_t rt9759_regmap[] = {
RT_REG(RT9759_REG_VBATOVP),
RT_REG(RT9759_REG_VBATOVP_ALM),
RT_REG(RT9759_REG_IBATOCP),
RT_REG(RT9759_REG_IBATOCP_ALM),
RT_REG(RT9759_REG_IBATUCP_ALM),
RT_REG(RT9759_REG_ACPROTECT),
RT_REG(RT9759_REG_VBUSOVP),
RT_REG(RT9759_REG_VBUSOVP_ALM),
RT_REG(RT9759_REG_IBUSOCUCP),
RT_REG(RT9759_REG_IBUSOCP_ALM),
RT_REG(RT9759_REG_CONVSTAT),
RT_REG(RT9759_REG_CHGCTRL0),
RT_REG(RT9759_REG_CHGCTRL1),
RT_REG(RT9759_REG_INTSTAT),
RT_REG(RT9759_REG_INTFLAG),
RT_REG(RT9759_REG_INTMASK),
RT_REG(RT9759_REG_FLTSTAT),
RT_REG(RT9759_REG_FLTFLAG),
RT_REG(RT9759_REG_FLTMASK),
RT_REG(RT9759_REG_DEVINFO),
RT_REG(RT9759_REG_ADCCTRL),
RT_REG(RT9759_REG_ADCEN),
RT_REG(RT9759_REG_IBUSADC1),
RT_REG(RT9759_REG_IBUSADC0),
RT_REG(RT9759_REG_VBUSADC1),
RT_REG(RT9759_REG_VBUSADC0),
RT_REG(RT9759_REG_VACADC1),
RT_REG(RT9759_REG_VACADC0),
RT_REG(RT9759_REG_VOUTADC1),
RT_REG(RT9759_REG_VOUTADC0),
RT_REG(RT9759_REG_VBATADC1),
RT_REG(RT9759_REG_VBATADC0),
RT_REG(RT9759_REG_IBATADC1),
RT_REG(RT9759_REG_IBATADC0),
RT_REG(RT9759_REG_TSBUSADC1),
RT_REG(RT9759_REG_TSBUSADC0),
RT_REG(RT9759_REG_TSBATADC1),
RT_REG(RT9759_REG_TSBATADC0),
RT_REG(RT9759_REG_TDIEADC1),
RT_REG(RT9759_REG_TDIEADC0),
RT_REG(RT9759_REG_TSBUSOTP),
RT_REG(RT9759_REG_TSBATOTP),
RT_REG(RT9759_REG_TDIEALM),
RT_REG(RT9759_REG_REGCTRL),
RT_REG(RT9759_REG_REGTHRES),
RT_REG(RT9759_REG_REGFLAGMASK),
RT_REG(RT9759_REG_BUSDEGLH),
RT_REG(RT9759_REG_OTHER1),
RT_REG(RT9759_REG_SYSCTRL1),
RT_REG(RT9759_REG_PASSWORD0),
RT_REG(RT9759_REG_PASSWORD1),
};
static struct rt_regmap_fops rt9759_rm_fops = {
.read_device = rt9759_read_device,
.write_device = rt9759_write_device,
};
static int rt9759_register_regmap(struct rt9759_chip *chip)
{
struct i2c_client *client = chip->client;
struct rt_regmap_properties *prop = NULL;
dev_info(chip->dev, "%s\n", __func__);
prop = devm_kzalloc(&client->dev, sizeof(*prop), GFP_KERNEL);
if (!prop)
return -ENOMEM;
prop->name = chip->desc->rm_name;
prop->aliases = chip->desc->rm_name;
prop->register_num = ARRAY_SIZE(rt9759_regmap);
prop->rm = rt9759_regmap;
prop->rt_regmap_mode = RT_SINGLE_BYTE | RT_CACHE_DISABLE |
RT_IO_PASS_THROUGH;
prop->io_log_en = 0;
chip->rm_prop = prop;
chip->rm_dev = rt_regmap_device_register_ex(chip->rm_prop,
&rt9759_rm_fops, chip->dev,
client,
chip->desc->rm_slave_addr,
chip);
if (!chip->rm_dev) {
dev_notice(chip->dev, "%s register regmap dev fail\n", __func__);
return -EINVAL;
}
return 0;
}
#endif /* CONFIG_RT_REGMAP */
#define I2C_ACCESS_MAX_RETRY 5
static inline int __rt9759_i2c_write8(struct rt9759_chip *chip, u8 reg, u8 data)
{
int ret, retry = 0;
do {
#ifdef CONFIG_RT_REGMAP
ret = rt_regmap_block_write(chip->rm_dev, reg, 1, &data);
#else
ret = rt9759_write_device(chip->client, reg, 1, &data);
#endif /* CONFIG_RT_REGMAP */
retry++;
if (ret < 0)
usleep_range(10, 15);
} while (ret < 0 && retry < I2C_ACCESS_MAX_RETRY);
if (ret < 0) {
dev_notice(chip->dev, "%s I2CW[0x%02X] = 0x%02X fail\n", __func__,
reg, data);
return ret;
}
dev_dbg(chip->dev, "%s I2CW[0x%02X] = 0x%02X\n", __func__, reg, data);
return 0;
}
static inline int __rt9759_i2c_read8(struct rt9759_chip *chip, u8 reg, u8 *data)
{
int ret, retry = 0;
do {
#ifdef CONFIG_RT_REGMAP
ret = rt_regmap_block_read(chip->rm_dev, reg, 1, data);
#else
ret = rt9759_read_device(chip->client, reg, 1, data);
#endif /* CONFIG_RT_REGMAP */
retry++;
if (ret < 0)
usleep_range(10, 15);
} while (ret < 0 && retry < I2C_ACCESS_MAX_RETRY);
if (ret < 0) {
dev_notice(chip->dev, "%s I2CR[0x%02X] fail\n", __func__, reg);
return ret;
}
dev_dbg(chip->dev, "%s I2CR[0x%02X] = 0x%02X\n", __func__, reg, *data);
return 0;
}
static int rt9759_i2c_read8(struct rt9759_chip *chip, u8 reg, u8 *data)
{
int ret;
mutex_lock(&chip->io_lock);
ret = __rt9759_i2c_read8(chip, reg, data);
mutex_unlock(&chip->io_lock);
return ret;
}
static inline int __rt9759_i2c_write_block(struct rt9759_chip *chip, u8 reg,
u32 len, const u8 *data)
{
int ret;
#ifdef CONFIG_RT_REGMAP
ret = rt_regmap_block_write(chip->rm_dev, reg, len, data);
#else
ret = rt9759_write_device(chip->client, reg, len, data);
#endif /* CONFIG_RT_REGMAP */
return ret;
}
static inline int __rt9759_i2c_read_block(struct rt9759_chip *chip, u8 reg,
u32 len, u8 *data)
{
int ret;
#ifdef CONFIG_RT_REGMAP
ret = rt_regmap_block_read(chip->rm_dev, reg, len, data);
#else
ret = rt9759_read_device(chip->client, reg, len, data);
#endif /* CONFIG_RT_REGMAP */
return ret;
}
static int rt9759_i2c_read_block(struct rt9759_chip *chip, u8 reg, u32 len,
u8 *data)
{
int ret;
mutex_lock(&chip->io_lock);
ret = __rt9759_i2c_read_block(chip, reg, len, data);
mutex_unlock(&chip->io_lock);
return ret;
}
static int rt9759_i2c_test_bit(struct rt9759_chip *chip, u8 reg, u8 shft,
bool *one)
{
int ret;
u8 data;
ret = rt9759_i2c_read8(chip, reg, &data);
if (ret < 0) {
*one = false;
return ret;
}
*one = (data & BIT(shft)) ? true : false;
return 0;
}
static int rt9759_i2c_update_bits(struct rt9759_chip *chip, u8 reg, u8 data,
u8 mask)
{
int ret;
u8 _data;
mutex_lock(&chip->io_lock);
ret = __rt9759_i2c_read8(chip, reg, &_data);
if (ret < 0)
goto out;
_data &= ~mask;
_data |= (data & mask);
ret = __rt9759_i2c_write8(chip, reg, _data);
out:
mutex_unlock(&chip->io_lock);
return ret;
}
static inline int rt9759_set_bits(struct rt9759_chip *chip, u8 reg, u8 mask)
{
return rt9759_i2c_update_bits(chip, reg, mask, mask);
}
static inline int rt9759_clr_bits(struct rt9759_chip *chip, u8 reg, u8 mask)
{
return rt9759_i2c_update_bits(chip, reg, 0x00, mask);
}
static inline u8 rt9759_val_toreg(u32 min, u32 max, u32 step, u32 target,
bool ru)
{
if (target <= min)
return 0;
if (target >= max)
return (max - min) / step;
if (ru)
return (target - min + step - 1) / step;
return (target - min) / step;
}
static inline u8 rt9759_val_toreg_via_tbl(const u32 *tbl, int tbl_size,
u32 target)
{
int i;
if (target < tbl[0])
return 0;
for (i = 0; i < tbl_size - 1; i++) {
if (target >= tbl[i] && target < tbl[i + 1])
return i;
}
return tbl_size - 1;
}
static u8 rt9759_vbatovp_toreg(u32 uV)
{
return rt9759_val_toreg(3500000, 5075000, 25000, uV, false);
}
static u8 rt9759_ibatocp_toreg(u32 uA)
{
return rt9759_val_toreg(2000000, 10000000, 100000, uA, true);
}
static u8 rt9759_ibatucp_toreg(u32 uA)
{
return rt9759_val_toreg(0, 6350000, 50000, uA, false);
}
static u8 rt9759_vbusovp_toreg(u32 uV)
{
return rt9759_val_toreg(6000000, 12350000, 50000, uV, true);
}
static u8 rt9759_ibusocp_toreg(u32 uA)
{
return rt9759_val_toreg(1000000, 4750000, 250000, uA, true);
}
static u8 rt9759_ibusocp_alm_toreg(u32 uA)
{
return rt9759_val_toreg(0, 6350000, 50000, uA, true);
}
static const u32 rt9759_wdt[] = {
500000, 1000000, 5000000, 3000000,
};
static u8 rt9759_wdt_toreg(u32 uS)
{
return rt9759_val_toreg_via_tbl(rt9759_wdt, ARRAY_SIZE(rt9759_wdt), uS);
}
static u8 rt9759_vacovp_toreg(u32 uV)
{
if (uV < 11000000)
return 0x07;
return rt9759_val_toreg(11000000, 17000000, 1000000, uV, true);
}
static int __rt9759_update_status(struct rt9759_chip *chip);
static int __rt9759_init_chip(struct rt9759_chip *chip);
/* Must be called while holding a lock */
static int rt9759_enable_wdt(struct rt9759_chip *chip, bool en)
{
int ret;
if (chip->wdt_en == en)
return 0;
ret = (en ? rt9759_clr_bits : rt9759_set_bits)
(chip, RT9759_REG_CHGCTRL0, RT9759_WDTEN_MASK);
if (ret < 0)
return ret;
chip->wdt_en = en;
return 0;
}
static int __rt9759_get_adc(struct rt9759_chip *chip,
enum rt9759_adc_channel chan, int *val)
{
int ret;
u8 data[2];
ret = rt9759_set_bits(chip, RT9759_REG_ADCCTRL, RT9759_ADCEN_MASK);
if (ret < 0)
goto out;
usleep_range(12000, 15000);
ret = rt9759_i2c_read_block(chip, rt9759_adc_reg[chan], 2, data);
if (ret < 0)
goto out_dis;
switch (chan) {
case RT9759_ADC_IBUS:
case RT9759_ADC_VBUS:
case RT9759_ADC_VAC:
case RT9759_ADC_VOUT:
case RT9759_ADC_VBAT:
case RT9759_ADC_IBAT:
*val = ((data[0] << 8) + data[1]) * 1000;
if (chan == RT9759_ADC_IBAT && chip->desc->ibat_rsense_half)
*val *= 2;
break;
case RT9759_ADC_TDIE:
*val = (data[0] << 7) + (data[1] >> 1);
break;
case RT9759_ADC_TSBAT:
case RT9759_ADC_TSBUS:
default:
ret = -ENOTSUPP;
break;
}
if (ret < 0)
dev_notice(chip->dev, "%s %s fail(%d)\n", __func__,
rt9759_adc_name[chan], ret);
else
dev_info(chip->dev, "%s %s %d\n", __func__,
rt9759_adc_name[chan], *val);
out_dis:
if (!chip->force_adc_en)
ret = rt9759_clr_bits(chip, RT9759_REG_ADCCTRL,
RT9759_ADCEN_MASK);
out:
return ret;
}
static int rt9759_enable_chg(struct charger_device *chg_dev, bool en)
{
int ret;
struct rt9759_chip *chip = charger_get_data(chg_dev);
u32 err_check = BIT(RT9759_IRQIDX_VBUSOVP) |
BIT(RT9759_IRQIDX_VACOVP) |
BIT(RT9759_IRQIDX_VDROVP) |
BIT(RT9759_IRQIDX_VBUSOVP) |
BIT(RT9759_IRQIDX_TDIEOTP) |
BIT(RT9759_IRQIDX_VBUSLERR) |
BIT(RT9759_IRQIDX_VBUSHERR) |
BIT(RT9759_IRQIDX_VOUTOVP) |
BIT(RT9759_IRQIDX_TSBUSOTP) |
BIT(RT9759_IRQIDX_TSBATOTP);
u32 stat_check = BIT(RT9759_IRQIDX_VACINSERT) |
BIT(RT9759_IRQIDX_VOUTINSERT);
dev_info(chip->dev, "%s %d\n", __func__, en);
mutex_lock(&chip->adc_lock);
chip->force_adc_en = en;
if (!en) {
ret = rt9759_clr_bits(chip, RT9759_REG_CHGCTRL1,
RT9759_CHGEN_MASK);
if (ret < 0)
goto out_unlock;
ret = rt9759_clr_bits(chip, RT9759_REG_ADCCTRL,
RT9759_ADCEN_MASK);
if (ret < 0)
goto out_unlock;
ret = rt9759_enable_wdt(chip, false);
goto out_unlock;
}
/* Enable ADC to check status before enable charging */
ret = rt9759_set_bits(chip, RT9759_REG_ADCCTRL, RT9759_ADCEN_MASK);
if (ret < 0)
goto out_unlock;
mutex_unlock(&chip->adc_lock);
usleep_range(12000, 15000);
mutex_lock(&chip->stat_lock);
__rt9759_update_status(chip);
if ((chip->stat & err_check) ||
((chip->stat & stat_check) != stat_check)) {
dev_notice(chip->dev, "%s error(0x%08X,0x%08X,0x%08X)\n", __func__,
chip->stat, err_check, stat_check);
ret = -EINVAL;
mutex_unlock(&chip->stat_lock);
goto out;
}
mutex_unlock(&chip->stat_lock);
if (!chip->desc->wdt_dis) {
ret = rt9759_enable_wdt(chip, true);
if (ret < 0)
goto out;
}
ret = rt9759_set_bits(chip, RT9759_REG_CHGCTRL1, RT9759_CHGEN_MASK);
goto out;
out_unlock:
mutex_unlock(&chip->adc_lock);
out:
return ret;
}
static int rt9759_is_chg_enabled(struct charger_device *chg_dev, bool *en)
{
int ret;
struct rt9759_chip *chip = charger_get_data(chg_dev);
ret = rt9759_i2c_test_bit(chip, RT9759_REG_CHGCTRL1, RT9759_CHGEN_SHFT,
en);
if (ret < 0)
return ret;
dev_info(chip->dev, "%s %d\n", __func__, *en);
return 0;
}
static inline enum rt9759_adc_channel to_rt9759_adc(enum adc_channel chan)
{
switch (chan) {
case ADC_CHANNEL_VBUS:
return RT9759_ADC_VBUS;
case ADC_CHANNEL_VBAT:
return RT9759_ADC_VBAT;
case ADC_CHANNEL_IBUS:
return RT9759_ADC_IBUS;
case ADC_CHANNEL_IBAT:
return RT9759_ADC_IBAT;
case ADC_CHANNEL_TEMP_JC:
return RT9759_ADC_TDIE;
case ADC_CHANNEL_VOUT:
return RT9759_ADC_VOUT;
default:
break;
}
return RT9759_ADC_NOTSUPP;
}
static int rt9759_get_adc(struct charger_device *chg_dev, enum adc_channel chan,
int *min, int *max)
{
int ret;
struct rt9759_chip *chip = charger_get_data(chg_dev);
enum rt9759_adc_channel _chan = to_rt9759_adc(chan);
if (_chan == RT9759_ADC_NOTSUPP)
return -EINVAL;
mutex_lock(&chip->adc_lock);
ret = __rt9759_get_adc(chip, _chan, max);
if (ret < 0)
goto out;
if (min != max)
*min = *max;
out:
mutex_unlock(&chip->adc_lock);
return ret;
}
static int rt9759_get_adc_accuracy(struct charger_device *chg_dev,
enum adc_channel chan, int *min, int *max)
{
enum rt9759_adc_channel _chan = to_rt9759_adc(chan);
if (_chan == RT9759_ADC_NOTSUPP)
return -EINVAL;
*min = *max = rt9759_adc_accuracy_tbl[_chan];
return 0;
}
static int rt9759_set_vbusovp(struct charger_device *chg_dev, u32 uV)
{
struct rt9759_chip *chip = charger_get_data(chg_dev);
u8 reg = rt9759_vbusovp_toreg(uV);
dev_info(chip->dev, "%s %d(0x%02X)\n", __func__, uV, reg);
return rt9759_i2c_update_bits(chip, RT9759_REG_VBUSOVP, reg,
RT9759_VBUSOVP_MASK);
}
static int rt9759_set_ibusocp(struct charger_device *chg_dev, u32 uA)
{
struct rt9759_chip *chip = charger_get_data(chg_dev);
u8 reg = rt9759_ibusocp_toreg(uA);
dev_info(chip->dev, "%s %d(0x%02X)\n", __func__, uA, reg);
return rt9759_i2c_update_bits(chip, RT9759_REG_IBUSOCUCP, reg,
RT9759_IBUSOCP_MASK);
}
static int rt9759_set_vbatovp(struct charger_device *chg_dev, u32 uV)
{
struct rt9759_chip *chip = charger_get_data(chg_dev);
u8 reg = rt9759_vbatovp_toreg(uV);
dev_info(chip->dev, "%s %d(0x%02X)\n", __func__, uV, reg);
return rt9759_i2c_update_bits(chip, RT9759_REG_VBATOVP, reg,
RT9759_VBATOVP_MASK);
}
static int rt9759_set_vbatovp_alarm(struct charger_device *chg_dev, u32 uV)
{
struct rt9759_chip *chip = charger_get_data(chg_dev);
u8 reg = rt9759_vbatovp_toreg(uV);
dev_info(chip->dev, "%s %d\n", __func__, uV);
return rt9759_i2c_update_bits(chip, RT9759_REG_VBATOVP_ALM, reg,
RT9759_VBATOVP_ALM_MASK);
}
static int rt9759_reset_vbatovp_alarm(struct charger_device *chg_dev)
{
int ret;
struct rt9759_chip *chip = charger_get_data(chg_dev);
u8 data;
dev_info(chip->dev, "%s\n", __func__);
mutex_lock(&chip->io_lock);
ret = __rt9759_i2c_read8(chip, RT9759_REG_VBATOVP_ALM, &data);
if (ret < 0)
goto out;
data |= RT9759_VBATOVP_ALMDIS_MASK;
ret = __rt9759_i2c_write8(chip, RT9759_REG_VBATOVP_ALM, data);
if (ret < 0)
goto out;
data &= ~RT9759_VBATOVP_ALMDIS_MASK;
ret = __rt9759_i2c_write8(chip, RT9759_REG_VBATOVP_ALM, data);
out:
mutex_unlock(&chip->io_lock);
return ret;
}
static int rt9759_set_vbusovp_alarm(struct charger_device *chg_dev, u32 uV)
{
struct rt9759_chip *chip = charger_get_data(chg_dev);
u8 reg = rt9759_vbusovp_toreg(uV);
dev_info(chip->dev, "%s %d\n", __func__, uV);
return rt9759_i2c_update_bits(chip, RT9759_REG_VBUSOVP_ALM, reg,
RT9759_VBUSOVP_ALM_MASK);
}
static int rt9759_is_vbuslowerr(struct charger_device *chg_dev, bool *err)
{
int ret;
struct rt9759_chip *chip = charger_get_data(chg_dev);
mutex_lock(&chip->adc_lock);
ret = rt9759_set_bits(chip, RT9759_REG_ADCCTRL, RT9759_ADCEN_MASK);
if (ret < 0)
goto out;
usleep_range(12000, 15000);
ret = rt9759_i2c_test_bit(chip, RT9759_REG_OTHER1,
RT9759_VBUSLOWERR_FLAG_SHFT, err);
if (ret < 0 || *err)
goto out_dis;
ret = rt9759_i2c_test_bit(chip, RT9759_REG_CONVSTAT,
RT9759_VBUSLOWERR_STAT_SHFT, err);
out_dis:
if (!chip->force_adc_en)
rt9759_clr_bits(chip, RT9759_REG_ADCCTRL, RT9759_ADCEN_MASK);
out:
mutex_unlock(&chip->adc_lock);
return ret;
}
static int rt9759_reset_vbusovp_alarm(struct charger_device *chg_dev)
{
int ret;
struct rt9759_chip *chip = charger_get_data(chg_dev);
u8 data;
dev_info(chip->dev, "%s\n", __func__);
mutex_lock(&chip->io_lock);
ret = __rt9759_i2c_read8(chip, RT9759_REG_VBUSOVP_ALM, &data);
if (ret < 0)
goto out;
data |= RT9759_VBUSOVP_ALMDIS_MASK;
ret = __rt9759_i2c_write8(chip, RT9759_REG_VBUSOVP_ALM, data);
if (ret < 0)
goto out;
data &= ~RT9759_VBUSOVP_ALMDIS_MASK;
ret = __rt9759_i2c_write8(chip, RT9759_REG_VBUSOVP_ALM, data);
out:
mutex_unlock(&chip->io_lock);
return ret;
}
static int rt9759_set_ibatocp(struct charger_device *chg_dev, u32 uA)
{
struct rt9759_chip *chip = charger_get_data(chg_dev);
u8 reg = rt9759_ibatocp_toreg(uA);
dev_info(chip->dev, "%s %d(0x%02X)\n", __func__, uA, reg);
return rt9759_i2c_update_bits(chip, RT9759_REG_IBATOCP, reg,
RT9759_IBATOCP_MASK);
}
static int rt9759_init_chip(struct charger_device *chg_dev)
{
int i, ret;
struct rt9759_chip *chip = charger_get_data(chg_dev);
const struct rt9759_reg_defval *reg_defval;
u8 val;
for (i = 0; i < ARRAY_SIZE(rt9759_init_chip_check_reg); i++) {
reg_defval = &rt9759_init_chip_check_reg[i];
ret = rt9759_i2c_read8(chip, reg_defval->reg, &val);
if (ret < 0)
return ret;
if ((val & reg_defval->mask) == reg_defval->value) {
dev_notice(chip->dev,
"%s chip reset happened, reinit\n", __func__);
return __rt9759_init_chip(chip);
}
}
return 0;
}
static int rt9759_vacovp_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s\n", __func__);
return 0;
}
static inline void rt9759_set_notify(struct rt9759_chip *chip,
enum rt9759_notify notify)
{
mutex_lock(&chip->notify_lock);
chip->notify |= BIT(notify);
mutex_unlock(&chip->notify_lock);
}
static int rt9759_ibusucpf_irq_handler(struct rt9759_chip *chip)
{
bool ucpf = !!(chip->stat & BIT(RT9759_IRQIDX_IBUSUCPF));
dev_info(chip->dev, "%s %d\n", __func__, ucpf);
if (ucpf)
rt9759_set_notify(chip, RT9759_NOTIFY_IBUSUCPF);
return 0;
}
static int rt9759_ibusucpr_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s %d\n", __func__,
!!(chip->stat & BIT(RT9759_IRQIDX_IBUSUCPR)));
return 0;
}
static int rt9759_cflydiag_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s\n", __func__);
return 0;
}
static int rt9759_conocp_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s\n", __func__);
return 0;
}
static int rt9759_switching_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s %d\n", __func__,
!!(chip->stat & BIT(RT9759_IRQIDX_SWITCHING)));
return 0;
}
static int rt9759_ibusucptout_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s\n", __func__);
return 0;
}
static int rt9759_vbushigherr_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s %d\n", __func__,
!!(chip->stat & BIT(RT9759_IRQIDX_VBUSHERR)));
return 0;
}
static int rt9759_vbuslowerr_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s %d\n", __func__,
!!(chip->stat & BIT(RT9759_IRQIDX_VBUSLERR)));
return 0;
}
static int rt9759_tdieotp_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s\n", __func__);
return 0;
}
static int rt9759_wdt_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s\n", __func__);
return 0;
}
static int rt9759_adcdone_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s\n", __func__);
return 0;
}
static int rt9759_voutinsert_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s %d\n", __func__,
!!(chip->stat & BIT(RT9759_IRQIDX_VOUTINSERT)));
return 0;
}
static int rt9759_vacinsert_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s %d\n", __func__,
!!(chip->stat & BIT(RT9759_IRQIDX_VACINSERT)));
return 0;
}
static int rt9759_ibatucpalm_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s\n", __func__);
return 0;
}
static int rt9759_ibusocpalm_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s\n", __func__);
return 0;
}
static int rt9759_vbusovpalm_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s\n", __func__);
rt9759_set_notify(chip, RT9759_NOTIFY_VBUSOVPALM);
return 0;
}
static int rt9759_ibatocpalm_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s\n", __func__);
return 0;
}
static int rt9759_vbatovpalm_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s\n", __func__);
rt9759_set_notify(chip, RT9759_NOTIFY_VBATOVPALM);
return 0;
}
static int rt9759_tdieotpalm_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s\n", __func__);
return 0;
}
static int rt9759_tsbusotp_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s\n", __func__);
return 0;
}
static int rt9759_tsbatotp_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s\n", __func__);
return 0;
}
static int rt9759_tsbusbatotpalm_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s\n", __func__);
return 0;
}
static int rt9759_ibusocp_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s\n", __func__);
rt9759_set_notify(chip, RT9759_NOTIFY_IBUSOCP);
return 0;
}
static int rt9759_vbusovp_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s\n", __func__);
rt9759_set_notify(chip, RT9759_NOTIFY_VBUSOVP);
return 0;
}
static int rt9759_ibatocp_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s\n", __func__);
rt9759_set_notify(chip, RT9759_NOTIFY_IBATOCP);
return 0;
}
static int rt9759_vbatovp_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s\n", __func__);
rt9759_set_notify(chip, RT9759_NOTIFY_VBATOVP);
return 0;
}
static int rt9759_voutovp_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s\n", __func__);
rt9759_set_notify(chip, RT9759_NOTIFY_VOUTOVP);
return 0;
}
static int rt9759_vdrovp_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s\n", __func__);
rt9759_set_notify(chip, RT9759_NOTIFY_VDROVP);
return 0;
}
static int rt9759_ibatreg_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s\n", __func__);
return 0;
}
static int rt9759_vbatreg_irq_handler(struct rt9759_chip *chip)
{
dev_info(chip->dev, "%s\n", __func__);
return 0;
}
struct irq_map_desc {
const char *name;
int (*hdlr)(struct rt9759_chip *chip);
u8 flag_idx;
u8 stat_idx;
u8 flag_mask;
u8 stat_mask;
u32 irq_idx;
bool stat_only;
};
#define RT9759_IRQ_DESC(_name, _flag_i, _stat_i, _flag_s, _stat_s, _irq_idx, \
_stat_only) \
{.name = #_name, .hdlr = rt9759_##_name##_irq_handler, \
.flag_idx = _flag_i, .stat_idx = _stat_i, \
.flag_mask = (1 << _flag_s), .stat_mask = (1 << _stat_s), \
.irq_idx = _irq_idx, .stat_only = _stat_only}
/*
* RSS: Reister index of flag, Shift of flag, Shift of state
* RRS: Register index of flag, Register index of state, Shift of flag
* RS: Register index of flag, Shift of flag
* RSSO: Register index of state, Shift of state, State Only
*/
#define RT9759_IRQ_DESC_RSS(_name, _flag_i, _flag_s, _stat_s, _irq_idx) \
RT9759_IRQ_DESC(_name, _flag_i, _flag_i, _flag_s, _stat_s, _irq_idx, \
false)
#define RT9759_IRQ_DESC_RRS(_name, _flag_i, _stat_i, _flag_s, _irq_idx) \
RT9759_IRQ_DESC(_name, _flag_i, _stat_i, _flag_s, _flag_s, _irq_idx, \
false)
#define RT9759_IRQ_DESC_RS(_name, _flag_i, _flag_s, _irq_idx) \
RT9759_IRQ_DESC(_name, _flag_i, _flag_i, _flag_s, _flag_s, _irq_idx, \
false)
#define RT9759_IRQ_DESC_RSSO(_name, _flag_i, _flag_s, _irq_idx) \
RT9759_IRQ_DESC(_name, _flag_i, _flag_i, _flag_s, _flag_s, _irq_idx, \
true)
static const struct irq_map_desc rt9759_irq_map_tbl[RT9759_IRQIDX_MAX] = {
RT9759_IRQ_DESC_RSS(vacovp, RT9759_SF_ACPROTECT, 6, 7,
RT9759_IRQIDX_VACOVP),
RT9759_IRQ_DESC_RS(ibusucpf, RT9759_SF_IBUSOCUCP, 4,
RT9759_IRQIDX_IBUSUCPF),
RT9759_IRQ_DESC_RS(ibusucpr, RT9759_SF_IBUSOCUCP, 6,
RT9759_IRQIDX_IBUSUCPR),
RT9759_IRQ_DESC_RS(cflydiag, RT9759_SF_CONVSTAT, 0,
RT9759_IRQIDX_CFLYDIAG),
RT9759_IRQ_DESC_RS(conocp, RT9759_SF_CONVSTAT, 1, RT9759_IRQIDX_CONOCP),
RT9759_IRQ_DESC_RSSO(switching, RT9759_SF_CONVSTAT, 2,
RT9759_IRQIDX_SWITCHING),
RT9759_IRQ_DESC_RS(ibusucptout, RT9759_SF_CONVSTAT, 3,
RT9759_IRQIDX_IBUSUCPTOUT),
RT9759_IRQ_DESC_RSSO(vbushigherr, RT9759_SF_CONVSTAT, 4,
RT9759_IRQIDX_VBUSHERR),
RT9759_IRQ_DESC_RSSO(vbuslowerr, RT9759_SF_CONVSTAT, 5,
RT9759_IRQIDX_VBUSLERR),
RT9759_IRQ_DESC_RSS(tdieotp, RT9759_SF_CONVSTAT, 7, 6,
RT9759_IRQIDX_TDIEOTP),
RT9759_IRQ_DESC_RS(wdt, RT9759_SF_CHGCTRL0, 3, RT9759_IRQIDX_WDT),
RT9759_IRQ_DESC_RRS(adcdone, RT9759_SF_INTFLAG, RT9759_SF_INTSTAT, 0,
RT9759_IRQIDX_ADCDONE),
RT9759_IRQ_DESC_RRS(voutinsert, RT9759_SF_INTFLAG,
RT9759_SF_INTSTAT, 1, RT9759_IRQIDX_VOUTINSERT),
RT9759_IRQ_DESC_RRS(vacinsert, RT9759_SF_INTFLAG, RT9759_SF_INTSTAT, 2,
RT9759_IRQIDX_VACINSERT),
RT9759_IRQ_DESC_RRS(ibatucpalm, RT9759_SF_INTFLAG,
RT9759_SF_INTSTAT, 3, RT9759_IRQIDX_IBATUCPALM),
RT9759_IRQ_DESC_RRS(ibusocpalm, RT9759_SF_INTFLAG,
RT9759_SF_INTSTAT, 4, RT9759_IRQIDX_IBUSOCPALM),
RT9759_IRQ_DESC_RRS(vbusovpalm, RT9759_SF_INTFLAG,
RT9759_SF_INTSTAT, 5, RT9759_IRQIDX_VBUSOVPALM),
RT9759_IRQ_DESC_RRS(ibatocpalm, RT9759_SF_INTFLAG,
RT9759_SF_INTSTAT, 6, RT9759_IRQIDX_IBATOCPALM),
RT9759_IRQ_DESC_RRS(vbatovpalm, RT9759_SF_INTFLAG,
RT9759_SF_INTSTAT, 7, RT9759_IRQIDX_VBATOVPALM),
RT9759_IRQ_DESC_RRS(tdieotpalm, RT9759_SF_FLTFLAG,
RT9759_SF_FLTSTAT, 0, RT9759_IRQIDX_TDIEOTPALM),
RT9759_IRQ_DESC_RRS(tsbusotp, RT9759_SF_FLTFLAG, RT9759_SF_FLTSTAT, 1,
RT9759_IRQIDX_TSBUSOTP),
RT9759_IRQ_DESC_RRS(tsbatotp, RT9759_SF_FLTFLAG, RT9759_SF_FLTSTAT, 2,
RT9759_IRQIDX_TSBATOTP),
RT9759_IRQ_DESC_RRS(tsbusbatotpalm, RT9759_SF_FLTFLAG,
RT9759_SF_FLTSTAT, 3, RT9759_IRQIDX_TSBUSBATOTPALM),
RT9759_IRQ_DESC_RRS(ibusocp, RT9759_SF_FLTFLAG, RT9759_SF_FLTSTAT, 4,
RT9759_IRQIDX_IBUSOCP),
RT9759_IRQ_DESC_RRS(vbusovp, RT9759_SF_FLTFLAG, RT9759_SF_FLTSTAT, 5,
RT9759_IRQIDX_VBUSOVP),
RT9759_IRQ_DESC_RRS(ibatocp, RT9759_SF_FLTFLAG, RT9759_SF_FLTSTAT, 6,
RT9759_IRQIDX_IBATOCP),
RT9759_IRQ_DESC_RRS(vbatovp, RT9759_SF_FLTFLAG, RT9759_SF_FLTSTAT, 7,
RT9759_IRQIDX_VBATOVP),
RT9759_IRQ_DESC(voutovp, RT9759_SF_REGFLAGMASK, RT9759_SF_REGTHRES,
4, 0, RT9759_IRQIDX_VOUTOVP, false),
RT9759_IRQ_DESC(vdrovp, RT9759_SF_REGFLAGMASK, RT9759_SF_REGTHRES,
5, 1, RT9759_IRQIDX_VDROVP, false),
RT9759_IRQ_DESC(ibatreg, RT9759_SF_REGFLAGMASK, RT9759_SF_REGTHRES,
6, 2, RT9759_IRQIDX_IBATREG, false),
RT9759_IRQ_DESC(vbatreg, RT9759_SF_REGFLAGMASK, RT9759_SF_REGTHRES,
7, 3, RT9759_IRQIDX_VBATREG, false),
};
static int __rt9759_update_status(struct rt9759_chip *chip)
{
int i;
u8 sf[RT9759_SF_MAX] = {0};
const struct irq_map_desc *desc;
for (i = 0; i < RT9759_SF_MAX; i++)
rt9759_i2c_read8(chip, rt9759_reg_sf[i], &sf[i]);
for (i = 0; i < ARRAY_SIZE(rt9759_irq_map_tbl); i++) {
desc = &rt9759_irq_map_tbl[i];
if (sf[desc->flag_idx] & desc->flag_mask) {
if (!desc->stat_only)
chip->flag |= BIT(desc->irq_idx);
}
if (sf[desc->stat_idx] & desc->stat_mask) {
if (desc->stat_only &&
!(chip->stat & BIT(desc->irq_idx)))
chip->flag |= BIT(desc->irq_idx);
chip->stat |= BIT(desc->irq_idx);
} else {
if (desc->stat_only &&
(chip->stat & BIT(desc->irq_idx)))
chip->flag |= BIT(desc->irq_idx);
chip->stat &= ~BIT(desc->irq_idx);
}
}
return 0;
}
static int rt9759_notify_task_threadfn(void *data)
{
int i;
struct rt9759_chip *chip = data;
while (!kthread_should_stop()) {
wait_event_interruptible(chip->wq, chip->notify != 0 ||
kthread_should_stop());
if (kthread_should_stop())
goto out;
pm_stay_awake(chip->dev);
mutex_lock(&chip->notify_lock);
for (i = 0; i < RT9759_NOTIFY_MAX; i++) {
if (chip->notify & BIT(i)) {
chip->notify &= ~BIT(i);
mutex_unlock(&chip->notify_lock);
charger_dev_notify(chip->chg_dev,
rt9759_chgdev_notify_map[i]);
mutex_lock(&chip->notify_lock);
}
}
mutex_unlock(&chip->notify_lock);
pm_relax(chip->dev);
}
out:
return 0;
}
static irqreturn_t rt9759_irq_handler(int irq, void *data)
{
int i;
struct rt9759_chip *chip = data;
const struct irq_map_desc *desc;
pm_stay_awake(chip->dev);
mutex_lock(&chip->stat_lock);
__rt9759_update_status(chip);
for (i = 0; i < ARRAY_SIZE(rt9759_irq_map_tbl); i++) {
desc = &rt9759_irq_map_tbl[i];
if ((chip->flag & (1 << desc->irq_idx)) && desc->hdlr)
desc->hdlr(chip);
}
chip->flag = 0;
wake_up_interruptible(&chip->wq);
mutex_unlock(&chip->stat_lock);
pm_relax(chip->dev);
return IRQ_HANDLED;
}
static const struct charger_ops rt9759_chg_ops = {
.enable = rt9759_enable_chg,
.is_enabled = rt9759_is_chg_enabled,
.get_adc = rt9759_get_adc,
.set_vbusovp = rt9759_set_vbusovp,
.set_ibusocp = rt9759_set_ibusocp,
.set_vbatovp = rt9759_set_vbatovp,
.set_ibatocp = rt9759_set_ibatocp,
.init_chip = rt9759_init_chip,
.set_vbatovp_alarm = rt9759_set_vbatovp_alarm,
.reset_vbatovp_alarm = rt9759_reset_vbatovp_alarm,
.set_vbusovp_alarm = rt9759_set_vbusovp_alarm,
.reset_vbusovp_alarm = rt9759_reset_vbusovp_alarm,
.is_vbuslowerr = rt9759_is_vbuslowerr,
.get_adc_accuracy = rt9759_get_adc_accuracy,
};
static int rt9759_register_chgdev(struct rt9759_chip *chip)
{
chip->chg_prop.alias_name = chip->desc->chg_name;
chip->chg_dev = charger_device_register(chip->desc->chg_name, chip->dev,
chip, &rt9759_chg_ops,
&chip->chg_prop);
return chip->chg_dev ? 0 : -EINVAL;
}
static int rt9759_clearall_irq(struct rt9759_chip *chip)
{
int i, ret;
u8 data;
for (i = 0; i < RT9759_SF_MAX; i++) {
ret = rt9759_i2c_read8(chip, rt9759_reg_sf[i], &data);
if (ret < 0)
return ret;
}
return 0;
}
static int rt9759_init_irq(struct rt9759_chip *chip)
{
int ret = 0, len = 0;
char *name = NULL;
dev_info(chip->dev, "%s\n", __func__);
ret = rt9759_clearall_irq(chip);
if (ret < 0) {
dev_notice(chip->dev, "%s clr all irq fail(%d)\n", __func__, ret);
return ret;
}
// Fix Me
if (chip->type == RT9759_TYPE_SLAVE)
return 0;
chip->irq = gpiod_to_irq(chip->irq_gpio);
if (chip->irq < 0) {
dev_notice(chip->dev, "%s irq mapping fail(%d)\n", __func__,
chip->irq);
return ret;
}
dev_info(chip->dev, "%s irq = %d\n", __func__, chip->irq);
/* Request threaded IRQ */
len = strlen(chip->desc->chg_name);
name = devm_kzalloc(chip->dev, len + 5, GFP_KERNEL);
snprintf(name, len + 5, "%s_irq", chip->desc->chg_name);
ret = devm_request_threaded_irq(chip->dev, chip->irq, NULL,
rt9759_irq_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, name,
chip);
if (ret < 0) {
dev_notice(chip->dev, "%s request thread irq fail(%d)\n", __func__,
ret);
return ret;
}
device_init_wakeup(chip->dev, true);
return 0;
}
#define RT9759_DT_VALPROP(name, reg, shft, mask, func, base) \
{#name, offsetof(struct rt9759_desc, name), reg, shft, mask, func, base}
struct rt9759_dtprop {
const char *name;
size_t offset;
u8 reg;
u8 shft;
u8 mask;
u8 (*toreg)(u32 val);
u8 base;
};
static inline void rt9759_parse_dt_u32(struct device_node *np, void *desc,
const struct rt9759_dtprop *props,
int prop_cnt)
{
int i;
for (i = 0; i < prop_cnt; i++) {
if (unlikely(!props[i].name))
continue;
of_property_read_u32(np, props[i].name, desc + props[i].offset);
}
}
static inline void rt9759_parse_dt_bool(struct device_node *np, void *desc,
const struct rt9759_dtprop *props,
int prop_cnt)
{
int i;
for (i = 0; i < prop_cnt; i++) {
if (unlikely(!props[i].name))
continue;
*((bool *)(desc + props[i].offset)) =
of_property_read_bool(np, props[i].name);
}
}
static inline int rt9759_apply_dt(struct rt9759_chip *chip, void *desc,
const struct rt9759_dtprop *props,
int prop_cnt)
{
int i, ret;
u32 val;
for (i = 0; i < prop_cnt; i++) {
val = *(u32 *)(desc + props[i].offset);
if (props[i].toreg)
val = props[i].toreg(val);
val += props[i].base;
ret = rt9759_i2c_update_bits(chip, props[i].reg,
val << props[i].shft,
props[i].mask);
if (ret < 0)
return ret;
}
return 0;
}
static const struct rt9759_dtprop rt9759_dtprops_u32[] = {
RT9759_DT_VALPROP(vbatovp, RT9759_REG_VBATOVP, 0, 0x3f,
rt9759_vbatovp_toreg, 0),
RT9759_DT_VALPROP(vbatovp_alm, RT9759_REG_VBATOVP_ALM, 0, 0x3f,
rt9759_vbatovp_toreg, 0),
RT9759_DT_VALPROP(ibatocp, RT9759_REG_IBATOCP, 0, 0x7f,
rt9759_ibatocp_toreg, 0),
RT9759_DT_VALPROP(ibatocp_alm, RT9759_REG_IBATOCP_ALM, 0, 0x7f,
rt9759_ibatocp_toreg, 0),
RT9759_DT_VALPROP(ibatucp_alm, RT9759_REG_IBATUCP_ALM, 0, 0x7f,
rt9759_ibatucp_toreg, 0),
RT9759_DT_VALPROP(vbusovp, RT9759_REG_VBUSOVP, 0, 0x7f,
rt9759_vbusovp_toreg, 0),
RT9759_DT_VALPROP(vbusovp_alm, RT9759_REG_VBUSOVP_ALM, 0, 0x7f,
rt9759_vbusovp_toreg, 0),
RT9759_DT_VALPROP(ibusocp, RT9759_REG_IBUSOCUCP, 0, 0x0f,
rt9759_ibusocp_toreg, 0),
RT9759_DT_VALPROP(ibusocp_alm, RT9759_REG_IBUSOCP_ALM, 0, 0x7f,
rt9759_ibusocp_alm_toreg, 0),
RT9759_DT_VALPROP(wdt, RT9759_REG_CHGCTRL0, 0, 0x03,
rt9759_wdt_toreg, 0),
RT9759_DT_VALPROP(vacovp, RT9759_REG_ACPROTECT, 0, 0x07,
rt9759_vacovp_toreg, 0),
RT9759_DT_VALPROP(ibat_rsense, RT9759_REG_REGCTRL, 1, 0x02, NULL, 0),
RT9759_DT_VALPROP(ibusucpf_deglitch, RT9759_REG_BUSDEGLH, 3, 0x08, NULL,
0),
};
static const struct rt9759_dtprop rt9759_dtprops_bool[] = {
RT9759_DT_VALPROP(vbatovp_dis, RT9759_REG_VBATOVP, 7, 0x80, NULL, 0),
RT9759_DT_VALPROP(vbatovp_alm_dis, RT9759_REG_VBATOVP_ALM, 7, 0x80,
NULL, 0),
RT9759_DT_VALPROP(ibatocp_dis, RT9759_REG_IBATOCP, 7, 0x80, NULL, 0),
RT9759_DT_VALPROP(ibatocp_alm_dis, RT9759_REG_IBATOCP_ALM, 7, 0x80,
NULL, 0),
RT9759_DT_VALPROP(ibatucp_alm_dis, RT9759_REG_IBATUCP_ALM, 7, 0x80,
NULL, 0),
RT9759_DT_VALPROP(vbusovp_alm_dis, RT9759_REG_VBUSOVP_ALM, 7, 0x80,
NULL, 0),
RT9759_DT_VALPROP(ibusocp_dis, RT9759_REG_IBUSOCUCP, 7, 0x80, NULL, 0),
RT9759_DT_VALPROP(ibusocp_alm_dis, RT9759_REG_IBUSOCP_ALM, 7, 0x80,
NULL, 0),
RT9759_DT_VALPROP(wdt_dis, RT9759_REG_CHGCTRL0, 2, 0x04, NULL, 0),
RT9759_DT_VALPROP(tsbusotp_dis, RT9759_REG_CHGCTRL1, 2, 0x04, NULL, 0),
RT9759_DT_VALPROP(tsbatotp_dis, RT9759_REG_CHGCTRL1, 1, 0x02, NULL, 0),
RT9759_DT_VALPROP(tdieotp_dis, RT9759_REG_CHGCTRL1, 0, 0x01, NULL, 0),
RT9759_DT_VALPROP(reg_en, RT9759_REG_REGCTRL, 4, 0x10, NULL, 0),
RT9759_DT_VALPROP(voutovp_dis, RT9759_REG_REGCTRL, 3, 0x08, NULL, 0),
RT9759_DT_VALPROP(ibusadc_dis, RT9759_REG_ADCCTRL, 0, 0x01, NULL, 0),
RT9759_DT_VALPROP(tdieadc_dis, RT9759_REG_ADCEN, 0, 0x01, NULL, 0),
RT9759_DT_VALPROP(tsbatadc_dis, RT9759_REG_ADCEN, 1, 0x02, NULL, 0),
RT9759_DT_VALPROP(tsbusadc_dis, RT9759_REG_ADCEN, 2, 0x04, NULL, 0),
RT9759_DT_VALPROP(ibatadc_dis, RT9759_REG_ADCEN, 3, 0x08, NULL, 0),
RT9759_DT_VALPROP(vbatadc_dis, RT9759_REG_ADCEN, 4, 0x10, NULL, 0),
RT9759_DT_VALPROP(voutadc_dis, RT9759_REG_ADCEN, 5, 0x20, NULL, 0),
RT9759_DT_VALPROP(vacadc_dis, RT9759_REG_ADCEN, 6, 0x40, NULL, 0),
RT9759_DT_VALPROP(vbusadc_dis, RT9759_REG_ADCEN, 7, 0x80, NULL, 0),
};
static int rt9759_parse_dt(struct rt9759_chip *chip)
{
struct rt9759_desc *desc;
struct device_node *np = chip->dev->of_node;
struct device_node *child_np;
if (!np)
return -ENODEV;
//FIX ME
if (chip->type == RT9759_TYPE_SLAVE)
goto ignore_intr;
chip->irq_gpio = devm_gpiod_get(chip->dev, "rt9759,intr", GPIOD_IN);
if (IS_ERR(chip->irq_gpio))
return PTR_ERR(chip->irq_gpio);
//FIX ME
ignore_intr:
desc = devm_kzalloc(chip->dev, sizeof(*desc), GFP_KERNEL);
if (!desc)
return -ENOMEM;
memcpy(desc, &rt9759_desc_defval, sizeof(*desc));
if (of_property_read_string(np, "rm_name", &desc->rm_name) < 0)
dev_info(chip->dev, "%s no rm name\n", __func__);
if (of_property_read_u8(np, "rm_slave_addr", &desc->rm_slave_addr) < 0)
dev_info(chip->dev, "%s no regmap slave addr\n", __func__);
child_np = of_get_child_by_name(np, rt9759_type_name[chip->type]);
if (!child_np) {
dev_notice(chip->dev, "%s no node(%s) found\n", __func__,
rt9759_type_name[chip->type]);
return -ENODEV;
}
if (of_property_read_string(child_np, "chg_name", &desc->chg_name) < 0)
dev_info(chip->dev, "%s no chg name\n", __func__);
rt9759_parse_dt_u32(child_np, (void *)desc, rt9759_dtprops_u32,
ARRAY_SIZE(rt9759_dtprops_u32));
rt9759_parse_dt_bool(child_np, (void *)desc, rt9759_dtprops_bool,
ARRAY_SIZE(rt9759_dtprops_bool));
desc->ibat_rsense_half = of_property_read_bool(child_np,
"ibat_rsense_half");
chip->desc = desc;
return 0;
}
static int rt9759_reset_register(struct rt9759_chip *chip)
{
int ret;
ret = rt9759_set_bits(chip, RT9759_REG_CHGCTRL0, RT9759_REGRST_MASK);
dev_info(chip->dev, "%s ret(%d)\n", __func__, ret);
usleep_range(5, 10);
return ret;
}
static int __rt9759_init_chip(struct rt9759_chip *chip)
{
int ret;
dev_info(chip->dev, "%s\n", __func__);
ret = rt9759_reset_register(chip);
if (ret < 0)
return ret;
ret = rt9759_apply_dt(chip, (void *)chip->desc, rt9759_dtprops_u32,
ARRAY_SIZE(rt9759_dtprops_u32));
if (ret < 0)
return ret;
ret = rt9759_apply_dt(chip, (void *)chip->desc, rt9759_dtprops_bool,
ARRAY_SIZE(rt9759_dtprops_bool));
if (ret < 0)
return ret;
chip->wdt_en = !chip->desc->wdt_dis;
return chip->wdt_en ? rt9759_enable_wdt(chip, false) : 0;
}
static int rt9759_check_devinfo(struct i2c_client *client, u8 *chip_rev,
enum rt9759_type *type)
{
int ret;
ret = i2c_smbus_read_byte_data(client, RT9759_REG_DEVINFO);
if (ret < 0)
return ret;
if ((ret & RT9759_DEVID_MASK) != RT9759_DEVID)
return -ENODEV;
*chip_rev = (ret & RT9759_DEVREV_MASK) >> RT9759_DEVREV_SHFT;
ret = i2c_smbus_read_byte_data(client, RT9759_REG_CHGCTRL1);
if (ret < 0)
return ret;
*type = (ret & RT9759_MS_MASK) >> RT9759_MS_SHFT;
dev_info(&client->dev, "%s rev(0x%02X), type(%s)\n", __func__,
*chip_rev, rt9759_type_name[*type]);
return 0;
}
static int rt9759_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret;
struct rt9759_chip *chip;
u8 chip_rev;
enum rt9759_type type;
dev_info(&client->dev, "%s(%s)\n", __func__, RT9759_DRV_VERSION);
ret = rt9759_check_devinfo(client, &chip_rev, &type);
if (ret < 0)
return ret;
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
chip->dev = &client->dev;
chip->client = client;
chip->revision = chip_rev;
chip->type = type;
mutex_init(&chip->io_lock);
mutex_init(&chip->adc_lock);
mutex_init(&chip->stat_lock);
mutex_init(&chip->notify_lock);
init_waitqueue_head(&chip->wq);
i2c_set_clientdata(client, chip);
ret = rt9759_parse_dt(chip);
if (ret < 0) {
dev_notice(chip->dev, "%s parse dt fail(%d)\n", __func__, ret);
goto err;
}
#ifdef CONFIG_RT_REGMAP
ret = rt9759_register_regmap(chip);
if (ret < 0) {
dev_notice(chip->dev, "%s reg regmap fail(%d)\n", __func__, ret);
goto err;
}
#endif /* CONFIG_RT_REGMAP */
ret = __rt9759_init_chip(chip);
if (ret < 0) {
dev_notice(chip->dev, "%s init chip fail(%d)\n", __func__, ret);
goto err_unreg_regmap;
}
ret = rt9759_register_chgdev(chip);
if (ret < 0) {
dev_notice(chip->dev, "%s reg chgdev fail(%d)\n", __func__, ret);
goto err_unreg_regmap;
}
chip->notify_task = kthread_run(rt9759_notify_task_threadfn, chip,
"notify_thread");
if (IS_ERR(chip->notify_task)) {
dev_notice(chip->dev, "%s run notify thread fail(%d)\n", __func__,
ret);
ret = PTR_ERR(chip->notify_task);
goto err_unreg_chgdev;
}
ret = rt9759_init_irq(chip);
if (ret < 0) {
dev_notice(chip->dev, "%s init irq fail(%d)\n", __func__, ret);
goto err_unreg_chgdev;
}
dev_info(chip->dev, "%s successfully\n", __func__);
return 0;
err_unreg_chgdev:
charger_device_unregister(chip->chg_dev);
err_unreg_regmap:
#ifdef CONFIG_RT_REGMAP
rt_regmap_device_unregister(chip->rm_dev);
#endif /* CONFIG_RT_REGMAP */
err:
mutex_destroy(&chip->notify_lock);
mutex_destroy(&chip->stat_lock);
mutex_destroy(&chip->adc_lock);
mutex_destroy(&chip->io_lock);
return ret;
}
static void rt9759_i2c_shutdown(struct i2c_client *client)
{
struct rt9759_chip *chip = i2c_get_clientdata(client);
dev_info(&client->dev, "%s\n", __func__);
if (chip)
rt9759_reset_register(chip);
}
static int rt9759_i2c_remove(struct i2c_client *client)
{
struct rt9759_chip *chip = i2c_get_clientdata(client);
dev_info(&client->dev, "%s\n", __func__);
if (!chip)
return 0;
if (chip->notify_task)
kthread_stop(chip->notify_task);
charger_device_unregister(chip->chg_dev);
#ifdef CONFIG_RT_REGMAP
rt_regmap_device_unregister(chip->rm_dev);
#endif /* CONFIG_RT_REGMAP */
mutex_destroy(&chip->notify_lock);
mutex_destroy(&chip->stat_lock);
mutex_destroy(&chip->adc_lock);
mutex_destroy(&chip->io_lock);
return 0;
}
static int __maybe_unused rt9759_i2c_suspend(struct device *dev)
{
struct i2c_client *i2c = to_i2c_client(dev);
struct rt9759_chip *chip = i2c_get_clientdata(i2c);
dev_info(dev, "%s\n", __func__);
if (device_may_wakeup(dev))
enable_irq_wake(chip->irq);
disable_irq(chip->irq);
return 0;
}
static int __maybe_unused rt9759_i2c_resume(struct device *dev)
{
struct i2c_client *i2c = to_i2c_client(dev);
struct rt9759_chip *chip = i2c_get_clientdata(i2c);
dev_info(dev, "%s\n", __func__);
if (device_may_wakeup(dev))
disable_irq_wake(chip->irq);
enable_irq(chip->irq);
return 0;
}
static SIMPLE_DEV_PM_OPS(rt9759_pm_ops, rt9759_i2c_suspend, rt9759_i2c_resume);
static const struct of_device_id rt9759_of_id[] = {
{ .compatible = "richtek,rt9759" },
{},
};
MODULE_DEVICE_TABLE(of, rt9759_of_id);
static const struct i2c_device_id rt9759_i2c_id[] = {
{ "rt9759", 0},
{},
};
MODULE_DEVICE_TABLE(i2c, rt9759_i2c_id);
static struct i2c_driver rt9759_i2c_driver = {
.driver = {
.name = "rt9759",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(rt9759_of_id),
.pm = &rt9759_pm_ops,
},
.probe = rt9759_i2c_probe,
.shutdown = rt9759_i2c_shutdown,
.remove = rt9759_i2c_remove,
.id_table = rt9759_i2c_id,
};
module_i2c_driver(rt9759_i2c_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Richtek RT9759 Charger Driver");
MODULE_AUTHOR("ShuFan Lee<shufan_lee@richtek.com>");
MODULE_VERSION(RT9759_DRV_VERSION);
/*
* 2.0.1_MTK
* (1) Remove ignoring slave charger irq init
*
* 2.0.0_MTK
* (1) Adapt to new ops of charger_class
* (2) Arrange include files by alphabet
* (3) Remove suspend_lock, use enable/disable irq instead
* (4) Reset register before shutdown and init_chip
* (5) Add ibat_rsense_half to allow user to use rsense with only
* half of ibat rsense setting
*
* 1.0.8_MTK
* (1) Add init_chip ops, if register reset happened, init_chip again.
*
* 1.0.7_MTK
* (1) Add ibusucpf_deglitch in dtsi
*
* 1.0.6_MTK
* (1) Add ibat_rsense in dtsi
*
* 1.0.5_MTK
* (1) Modify IBUS ADC accuracy to 150mA
* (2) Move adc_lock from __rt9759_get_adc to rt9759_get_adc
* (3) Check force_adc_en before disabling adc in rt9759_is_vbuslowerr
*
* 1.0.4_MTK
* (1) Add get_adc_accuracy ops
*
* 1.0.3_MTK
* (1) Modify xxx_to_reg to support round up/down
* (2) Show register value when set protection
*
* 1.0.2_MTK
* (1) Notify ibusucpf/vbusovpalm/vbatovpalm/ibusocp/vbusovp/ibatocp/vbatovp/
* voutovp/vdrovp event
* (2) Add checking vbuslowerr ops
* (3) Create a thread to handle notification
*
* 1.0.1_MTK
* (1) Modify maximum IBUSOCP from 3750 to 4750mA
* (2) Remove operation of enabling sBase before enabling charging and ADC
* (3) Add Master/Slave/Standalone mode's operation
* (4) Add RSSO flag/state description and handle state only event
* only if state has changed
* (5) If WDT is enabled in dtsi, only enable it right before enabling CHG_EN
* and disable it right after disabling CHG_EN
*
* 1.0.0_MTK
* Initial release
*/