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