summaryrefslogtreecommitdiff
path: root/driver/charger/rt946x.c
diff options
context:
space:
mode:
Diffstat (limited to 'driver/charger/rt946x.c')
-rw-r--r--driver/charger/rt946x.c1927
1 files changed, 0 insertions, 1927 deletions
diff --git a/driver/charger/rt946x.c b/driver/charger/rt946x.c
deleted file mode 100644
index a4cfa5d2eb..0000000000
--- a/driver/charger/rt946x.c
+++ /dev/null
@@ -1,1927 +0,0 @@
-/* Copyright 2017 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- *
- * Richtek rt946x, Mediatek mt6370 battery charger driver.
- */
-
-#include "battery.h"
-#include "battery_smart.h"
-#include "charger.h"
-#include "charge_manager.h"
-#include "common.h"
-#include "compile_time_macros.h"
-#include "config.h"
-#include "console.h"
-#include "extpower.h"
-#include "hooks.h"
-#include "i2c.h"
-#include "printf.h"
-#include "driver/wpc/p9221.h"
-#include "rt946x.h"
-#include "task.h"
-#include "tcpm/tcpm.h"
-#include "timer.h"
-#include "usb_charge.h"
-#include "usb_pd.h"
-#include "util.h"
-
-/* Console output macros */
-#define CPRINTF(format, args...) cprintf(CC_CHARGER, format, ## args)
-#define CPRINTS(format, args...) \
- cprints(CC_CHARGER, "%s " format, "RT946X", ## args)
-
-/* Charger parameters */
-#define CHARGER_NAME RT946X_CHARGER_NAME
-#define CHARGE_V_MAX 4710
-#define CHARGE_V_MIN 3900
-#define CHARGE_V_STEP 10
-#define CHARGE_I_MAX 5000
-#define CHARGE_I_MIN 100
-#define CHARGE_I_OFF 0
-#define CHARGE_I_STEP 100
-#define INPUT_I_MAX 3250
-#define INPUT_I_MIN 100
-#define INPUT_I_STEP 50
-
-/* Charger parameters */
-static const struct charger_info rt946x_charger_info = {
- .name = CHARGER_NAME,
- .voltage_max = CHARGE_V_MAX,
- .voltage_min = CHARGE_V_MIN,
- .voltage_step = CHARGE_V_STEP,
- .current_max = CHARGE_I_MAX,
- .current_min = CHARGE_I_MIN,
- .current_step = CHARGE_I_STEP,
- .input_current_max = INPUT_I_MAX,
- .input_current_min = INPUT_I_MIN,
- .input_current_step = INPUT_I_STEP,
-};
-
-static const struct rt946x_init_setting default_init_setting = {
- .eoc_current = 400,
- .mivr = 4000,
- .ircmp_vclamp = 32,
- .ircmp_res = 25,
- .boost_voltage = 5050,
- .boost_current = 1500,
-};
-
-__attribute__((weak))
-const struct rt946x_init_setting *board_rt946x_init_setting(void)
-{
- return &default_init_setting;
-}
-
-enum rt946x_ilmtsel {
- RT946X_ILMTSEL_PSEL_OTG,
- RT946X_ILMTSEL_AICR = 2,
- RT946X_ILMTSEL_LOWER_LEVEL, /* lower of above two */
-};
-
-enum rt946x_chg_stat {
- RT946X_CHGSTAT_READY = 0,
- RT946X_CHGSTAT_IN_PROGRESS,
- RT946X_CHGSTAT_DONE,
- RT946X_CHGSTAT_FAULT,
-};
-
-static struct mutex adc_access_lock;
-
-#ifdef CONFIG_CHARGER_MT6370
-/*
- * Unit for each ADC parameter
- * 0 stands for reserved
- */
-static const int mt6370_adc_unit[MT6370_ADC_MAX] = {
- 0,
- MT6370_ADC_UNIT_VBUS_DIV5,
- MT6370_ADC_UNIT_VBUS_DIV2,
- MT6370_ADC_UNIT_VSYS,
- MT6370_ADC_UNIT_VBAT,
- 0,
- MT6370_ADC_UNIT_TS_BAT,
- 0,
- MT6370_ADC_UNIT_IBUS,
- MT6370_ADC_UNIT_IBAT,
- 0,
- MT6370_ADC_UNIT_CHG_VDDP,
- MT6370_ADC_UNIT_TEMP_JC,
-};
-
-static const int mt6370_adc_offset[MT6370_ADC_MAX] = {
- 0,
- MT6370_ADC_OFFSET_VBUS_DIV5,
- MT6370_ADC_OFFSET_VBUS_DIV2,
- MT6370_ADC_OFFSET_VSYS,
- MT6370_ADC_OFFSET_VBAT,
- 0,
- MT6370_ADC_OFFSET_TS_BAT,
- 0,
- MT6370_ADC_OFFSET_IBUS,
- MT6370_ADC_OFFSET_IBAT,
- 0,
- MT6370_ADC_OFFSET_CHG_VDDP,
- MT6370_ADC_OFFSET_TEMP_JC,
-};
-
-static int hidden_mode_cnt = 0;
-static struct mutex hidden_mode_lock;
-static const unsigned char mt6370_reg_en_hidden_mode[] = {
- MT6370_REG_HIDDENPASCODE1,
- MT6370_REG_HIDDENPASCODE2,
- MT6370_REG_HIDDENPASCODE3,
- MT6370_REG_HIDDENPASCODE4,
-};
-
-static const unsigned char mt6370_val_en_hidden_mode[] = {
- 0x96, 0x69, 0xC3, 0x3C,
-};
-
-static const unsigned char mt6370_val_en_test_mode[] = {
- 0x69, 0x96, 0x63, 0x70,
-};
-#endif /* CONFIG_CHARGER_MT6370 */
-
-#if defined(CONFIG_CHARGER_RT9466) || defined(CONFIG_CHARGER_RT9467)
-enum rt946x_irq {
- RT946X_IRQ_CHGSTATC = 0,
- RT946X_IRQ_CHGFAULT,
- RT946X_IRQ_TSSTATC,
- RT946X_IRQ_CHGIRQ1,
- RT946X_IRQ_CHGIRQ2,
- RT946X_IRQ_CHGIRQ3,
-#ifdef CONFIG_CHARGER_RT9467
- RT946X_IRQ_DPDMIRQ,
-#endif
- RT946X_IRQ_COUNT,
-};
-
-static uint8_t rt946x_irqmask[RT946X_IRQ_COUNT] = {
- 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF,
-#ifdef CONFIG_CHARGER_RT9467
- 0xFC,
-#endif
-};
-
-static const uint8_t rt946x_irq_maskall[RT946X_IRQ_COUNT] = {
- 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF,
-#ifdef CONFIG_CHARGER_RT9467
- 0xFF,
-#endif
-};
-#elif defined(CONFIG_CHARGER_MT6370)
-enum rt946x_irq {
- MT6370_IRQ_CHGSTAT1 = 0,
- MT6370_IRQ_CHGSTAT2,
- MT6370_IRQ_CHGSTAT3,
- MT6370_IRQ_CHGSTAT4,
- MT6370_IRQ_CHGSTAT5,
- MT6370_IRQ_CHGSTAT6,
- MT6370_IRQ_DPDMSTAT,
- MT6370_IRQ_DICHGSTAT,
- MT6370_IRQ_OVPCTRLSTAT,
- MT6370_IRQ_FLEDSTAT1,
- MT6370_IRQ_FLEDSTAT2,
- MT6370_IRQ_BASESTAT,
- MT6370_IRQ_LDOSTAT,
- MT6370_IRQ_RGBSTAT,
- MT6370_IRQ_BLSTAT,
- MT6370_IRQ_DBSTAT,
- RT946X_IRQ_COUNT,
-};
-
-static uint8_t rt946x_irqmask[RT946X_IRQ_COUNT] = {
- 0xBF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFC, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF,
-};
-
-static const uint8_t rt946x_irq_maskall[RT946X_IRQ_COUNT] = {
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF,
-};
-#endif
-
-static enum ec_error_list rt946x_set_current(int chgnum, int current);
-static enum ec_error_list rt946x_get_current(int chgnum, int *current);
-static enum ec_error_list rt946x_set_voltage(int chgnum, int voltage);
-static enum ec_error_list rt946x_enable_otg_power(int chgnum, int enabled);
-static const struct charger_info *rt946x_get_info(int chgnum);
-
-/* Must be in ascending order */
-static const uint16_t rt946x_boost_current[] = {
- 500, 700, 1100, 1300, 1800, 2100, 2400,
-};
-
-static enum ec_error_list rt946x_read8(int chgnum, int reg, int *val)
-{
- return i2c_read8(chg_chips[chgnum].i2c_port,
- chg_chips[chgnum].i2c_addr_flags, reg, val);
-}
-
-static enum ec_error_list rt946x_write8(int chgnum, int reg, int val)
-{
- return i2c_write8(chg_chips[chgnum].i2c_port,
- chg_chips[chgnum].i2c_addr_flags, reg, val);
-}
-
-static enum ec_error_list rt946x_block_write(int chgnum, int reg,
- const uint8_t *val, int len)
-{
- return i2c_write_block(chg_chips[chgnum].i2c_port,
- chg_chips[chgnum].i2c_addr_flags,
- reg, val, len);
-}
-
-static int rt946x_update_bits(int chgnum, int reg, int mask, int val)
-{
- int rv;
- int reg_val = 0;
-
- rv = rt946x_read8(chgnum, reg, &reg_val);
- if (rv)
- return rv;
- reg_val &= ~mask;
- reg_val |= (mask & val);
- rv = rt946x_write8(chgnum, reg, reg_val);
- return rv;
-}
-
-static inline int rt946x_set_bit(int chgnum, int reg, int mask)
-{
- return rt946x_update_bits(chgnum, reg, mask, mask);
-}
-
-static inline int rt946x_clr_bit(int chgnum, int reg, int mask)
-{
- return rt946x_update_bits(chgnum, reg, mask, 0x00);
-}
-
-static inline int mt6370_pmu_reg_test_bit(int chgnum, int cmd, int shift,
- int *is_one)
-{
- int rv, data;
-
- rv = rt946x_read8(chgnum, cmd, &data);
- if (rv) {
- *is_one = 0;
- return rv;
- }
-
- *is_one = !!(data & BIT(shift));
- return rv;
-}
-
-static inline uint8_t rt946x_closest_reg(uint16_t min, uint16_t max,
- uint16_t step, uint16_t target)
-{
- if (target < min)
- return 0;
- if (target >= max)
- return ((max - min) / step);
- return (target - min) / step;
-}
-
-static int rt946x_get_ieoc(int chgnum, uint32_t *ieoc)
-{
- int ret, reg_ieoc;
-
- ret = rt946x_read8(chgnum, RT946X_REG_CHGCTRL9, &reg_ieoc);
- if (ret)
- return ret;
-
- *ieoc = RT946X_IEOC_MIN +
- RT946X_IEOC_STEP *
- ((reg_ieoc & RT946X_MASK_IEOC) >> RT946X_SHIFT_IEOC);
-
- return EC_SUCCESS;
-}
-
-#ifdef CONFIG_CHARGER_MT6370
-static int mt6370_enable_hidden_mode(int chgnum, int en)
-{
- int rv = 0;
-
- if (in_interrupt_context()) {
- CPRINTS("Err: use hidden mode in IRQ");
- return EC_ERROR_INVAL;
- }
-
- mutex_lock(&hidden_mode_lock);
- if (en) {
- if (hidden_mode_cnt == 0) {
- rv = rt946x_block_write(chgnum,
- mt6370_reg_en_hidden_mode[0],
- mt6370_val_en_hidden_mode,
- ARRAY_SIZE(mt6370_val_en_hidden_mode));
- if (rv)
- goto out;
- }
- hidden_mode_cnt++;
- } else {
- if (hidden_mode_cnt == 1) /* last one */
- rv = rt946x_write8(chgnum, mt6370_reg_en_hidden_mode[0],
- 0x00);
- hidden_mode_cnt--;
- if (rv)
- goto out;
- }
-
-out:
- mutex_unlock(&hidden_mode_lock);
- return rv;
-}
-
-/*
- * Vsys short protection:
- * When the system is charging at 500mA, and if Isys > 3600mA, the
- * power path will be turned off and cause the system shutdown.
- * When Ichg < 400mA, then power path is roughly 1/8 of the original.
- * When Isys > 3600mA, this cause the voltage between Vbat and Vsys too
- * huge (Vbat - Vsys > Vsys short portection) and turns off the power
- * path.
- * To workaround this,
- * 1. disable Vsys short protection when Ichg is set below 900mA
- * 2. forbids Ichg <= 400mA (this is done natually on mt6370, since mt6370's
- * minimum current is 512)
- */
-static int mt6370_ichg_workaround(int chgnum, int new_ichg)
-{
- int rv = EC_SUCCESS;
- int curr_ichg;
-
- /*
- * TODO(b:144532905): The workaround should be applied to rt9466 as
- * well. But this needs rt9466's hidden register datasheet. Enable
- * this if we need it in the future.
- */
- if (!IS_ENABLED(CONFIG_CHARGER_MT6370))
- return EC_SUCCESS;
-
- rv = rt946x_get_current(chgnum, &curr_ichg);
- if (rv)
- return rv;
-
- mt6370_enable_hidden_mode(chgnum, 1);
-
- /* disable Vsys protect if if the new ichg is below 900mA */
- if (curr_ichg >= 900 && new_ichg < 900)
- rv = rt946x_update_bits(chgnum, RT946X_REG_CHGHIDDENCTRL7,
- RT946X_MASK_HIDDENCTRL7_VSYS_PROTECT,
- 0);
- /* enable Vsys protect if the new ichg is above 900mA */
- else if (new_ichg >= 900 && curr_ichg < 900)
- rv = rt946x_update_bits(chgnum, RT946X_REG_CHGHIDDENCTRL7,
- RT946X_MASK_HIDDENCTRL7_VSYS_PROTECT,
- RT946X_ENABLE_VSYS_PROTECT);
-
- mt6370_enable_hidden_mode(chgnum, 0);
- return rv;
-}
-#endif /* CONFIG_CHARGER_MT6370 */
-
-static inline int rt946x_enable_wdt(int chgnum, int en)
-{
- return (en ? rt946x_set_bit : rt946x_clr_bit)
- (chgnum, RT946X_REG_CHGCTRL13, RT946X_MASK_WDT_EN);
-}
-
-/* Enable high-impedance mode */
-static inline int rt946x_enable_hz(int chgnum, int en)
-{
- return (en ? rt946x_set_bit : rt946x_clr_bit)
- (chgnum, RT946X_REG_CHGCTRL1, RT946X_MASK_HZ_EN);
-}
-
-int rt946x_por_reset(void)
-{
- int rv, val;
-
-#ifdef CONFIG_CHARGER_MT6370
- /* Soft reset. It takes only 1ns for resetting. b/116682788 */
- val = RT946X_MASK_SOFT_RST;
- /*
- * MT6370 has to set passcodes before resetting all the registers and
- * logics.
- */
- rv = rt946x_write8(CHARGER_SOLO, MT6370_REG_RSTPASCODE1,
- MT6370_MASK_RSTPASCODE1);
- rv |= rt946x_write8(CHARGER_SOLO, MT6370_REG_RSTPASCODE2,
- MT6370_MASK_RSTPASCODE2);
-#else
- /* Hard reset, may take several milliseconds. */
- val = RT946X_MASK_RST;
- rv = rt946x_enable_hz(CHARGER_SOLO, 0);
-#endif
- if (rv)
- return rv;
-
- return rt946x_set_bit(CHARGER_SOLO, RT946X_REG_CORECTRL_RST, val);
-}
-
-static int rt946x_reset_to_zero(int chgnum)
-{
- int rv;
-
- rv = rt946x_set_current(chgnum, 0);
- if (rv)
- return rv;
-
- rv = rt946x_set_voltage(chgnum, 0);
- if (rv)
- return rv;
-
- return rt946x_enable_hz(chgnum, 1);
-}
-
-static int rt946x_enable_bc12_detection(int chgnum, int en)
-{
-#if defined(CONFIG_CHARGER_RT9467) || defined(CONFIG_CHARGER_MT6370)
- int rv;
-
- if (en) {
-#ifdef CONFIG_CHARGER_MT6370_BC12_GPIO
- gpio_set_level(GPIO_BC12_DET_EN, 1);
-#endif /* CONFIG_CHARGER_MT6370_BC12_GPIO */
- return rt946x_set_bit(chgnum, RT946X_REG_DPDM1,
- RT946X_MASK_USBCHGEN);
- }
-
- rv = rt946x_clr_bit(chgnum, RT946X_REG_DPDM1, RT946X_MASK_USBCHGEN);
-#ifdef CONFIG_CHARGER_MT6370_BC12_GPIO
- gpio_set_level(GPIO_BC12_DET_EN, 0);
-#endif /* CONFIG_CHARGER_MT6370_BC12_GPIO */
- return rv;
-#endif
- return 0;
-}
-
-static int rt946x_set_ieoc(int chgnum, unsigned int ieoc)
-{
- uint8_t reg_ieoc;
-
- reg_ieoc = rt946x_closest_reg(RT946X_IEOC_MIN, RT946X_IEOC_MAX,
- RT946X_IEOC_STEP, ieoc);
-
- CPRINTS("ieoc=%d", ieoc);
-
- return rt946x_update_bits(chgnum, RT946X_REG_CHGCTRL9, RT946X_MASK_IEOC,
- reg_ieoc << RT946X_SHIFT_IEOC);
-}
-
-static int rt946x_set_mivr(int chgnum, unsigned int mivr)
-{
- uint8_t reg_mivr = 0;
-
- reg_mivr = rt946x_closest_reg(RT946X_MIVR_MIN, RT946X_MIVR_MAX,
- RT946X_MIVR_STEP, mivr);
-
- CPRINTS("mivr=%d", mivr);
-
- return rt946x_update_bits(chgnum, RT946X_REG_CHGCTRL6, RT946X_MASK_MIVR,
- reg_mivr << RT946X_SHIFT_MIVR);
-}
-
-static int rt946x_set_boost_voltage(int chgnum, unsigned int voltage)
-{
- uint8_t reg_voltage = 0;
-
- reg_voltage = rt946x_closest_reg(RT946X_BOOST_VOLTAGE_MIN,
- RT946X_BOOST_VOLTAGE_MAX, RT946X_BOOST_VOLTAGE_STEP, voltage);
-
- CPRINTS("voltage=%d", voltage);
-
- return rt946x_update_bits(chgnum, RT946X_REG_CHGCTRL5,
- RT946X_MASK_BOOST_VOLTAGE,
- reg_voltage << RT946X_SHIFT_BOOST_VOLTAGE);
-}
-
-static int rt946x_set_boost_current(int chgnum, unsigned int current)
-{
- int i;
-
- /*
- * Find the smallest output current threshold which can support
- * our requested output current. Use the greatest achievable
- * boost current (2.4A) if requested current is too large.
- */
- for (i = 0; i < ARRAY_SIZE(rt946x_boost_current) - 1; i++) {
- if (current < rt946x_boost_current[i])
- break;
- }
-
- CPRINTS("current=%d", current);
-
- return rt946x_update_bits(chgnum, RT946X_REG_CHGCTRL10,
- RT946X_MASK_BOOST_CURRENT,
- i << RT946X_SHIFT_BOOST_CURRENT);
-}
-
-static int rt946x_set_ircmp_vclamp(int chgnum, unsigned int vclamp)
-{
- uint8_t reg_vclamp = 0;
-
- reg_vclamp = rt946x_closest_reg(RT946X_IRCMP_VCLAMP_MIN,
- RT946X_IRCMP_VCLAMP_MAX, RT946X_IRCMP_VCLAMP_STEP, vclamp);
-
- CPRINTS("vclamp=%d", vclamp);
-
- return rt946x_update_bits(chgnum, RT946X_REG_CHGCTRL18,
- RT946X_MASK_IRCMP_VCLAMP,
- reg_vclamp << RT946X_SHIFT_IRCMP_VCLAMP);
-}
-
-static int rt946x_set_ircmp_res(int chgnum, unsigned int res)
-{
- uint8_t reg_res = 0;
-
- reg_res = rt946x_closest_reg(RT946X_IRCMP_RES_MIN, RT946X_IRCMP_RES_MAX,
- RT946X_IRCMP_RES_STEP, res);
-
- CPRINTS("res=%d", res);
-
- return rt946x_update_bits(chgnum, RT946X_REG_CHGCTRL18,
- RT946X_MASK_IRCMP_RES,
- reg_res << RT946X_SHIFT_IRCMP_RES);
-}
-
-static int rt946x_set_vprec(int chgnum, unsigned int vprec)
-{
- uint8_t reg_vprec = 0;
-
- reg_vprec = rt946x_closest_reg(RT946X_VPREC_MIN, RT946X_VPREC_MAX,
- RT946X_VPREC_STEP, vprec);
-
- CPRINTS("vprec=%d", vprec);
-
- return rt946x_update_bits(chgnum, RT946X_REG_CHGCTRL8,
- RT946X_MASK_VPREC,
- reg_vprec << RT946X_SHIFT_VPREC);
-}
-
-static int rt946x_set_iprec(int chgnum, unsigned int iprec)
-{
- uint8_t reg_iprec = 0;
-
- reg_iprec = rt946x_closest_reg(RT946X_IPREC_MIN, RT946X_IPREC_MAX,
- RT946X_IPREC_STEP, iprec);
-
- CPRINTS("iprec=%d", iprec);
-
- return rt946x_update_bits(chgnum, RT946X_REG_CHGCTRL8,
- RT946X_MASK_IPREC,
- reg_iprec << RT946X_SHIFT_IPREC);
-}
-
-static int rt946x_init_irq(int chgnum)
-{
- int rv = 0;
- int unused;
- int i;
-
- /* Mask all interrupts */
- rv = rt946x_block_write(chgnum, RT946X_REG_CHGSTATCCTRL,
- rt946x_irq_maskall, RT946X_IRQ_COUNT);
- if (rv)
- return rv;
-
- /* Clear all interrupt flags */
- for (i = 0; i < RT946X_IRQ_COUNT; i++) {
- rv = rt946x_read8(chgnum, RT946X_REG_CHGSTATC + i, &unused);
- if (rv)
- return rv;
- }
-
- /* Init interrupt */
- return rt946x_block_write(chgnum, RT946X_REG_CHGSTATCCTRL,
- rt946x_irqmask, ARRAY_SIZE(rt946x_irqmask));
-}
-
-static int rt946x_init_setting(int chgnum)
-{
- int rv = 0;
- const struct battery_info *batt_info = battery_get_info();
- const struct rt946x_init_setting *setting = board_rt946x_init_setting();
-
-#ifdef CONFIG_BATTERY_SMART
- /* Disable EOC */
- rv = rt946x_enable_charge_eoc(0);
- if (rv)
- return rv;
-#endif
-
-#ifdef CONFIG_CHARGER_OTG
- /* Disable boost-mode output voltage */
- rv = rt946x_enable_otg_power(chgnum, 0);
- if (rv)
- return rv;
-#endif
- /* Disable BC 1.2 detection by default. It will be enabled on demand */
- rv = rt946x_enable_bc12_detection(chgnum, 0);
- if (rv)
- return rv;
- /* Disable WDT */
- rv = rt946x_enable_wdt(chgnum, 0);
- if (rv)
- return rv;
- /* Disable battery thermal protection */
- rv = rt946x_clr_bit(chgnum, RT946X_REG_CHGCTRL16, RT946X_MASK_JEITA_EN);
- if (rv)
- return rv;
- /* Disable charge timer */
- rv = rt946x_clr_bit(chgnum, RT946X_REG_CHGCTRL12, RT946X_MASK_TMR_EN);
- if (rv)
- return rv;
- rv = rt946x_set_mivr(chgnum, setting->mivr);
- if (rv)
- return rv;
- rv = rt946x_set_ieoc(chgnum, setting->eoc_current);
- if (rv)
- return rv;
- rv = rt946x_set_boost_voltage(chgnum,
- setting->boost_voltage);
- if (rv)
- return rv;
- rv = rt946x_set_boost_current(chgnum,
- setting->boost_current);
- if (rv)
- return rv;
- rv = rt946x_set_ircmp_vclamp(chgnum, setting->ircmp_vclamp);
- if (rv)
- return rv;
- rv = rt946x_set_ircmp_res(chgnum, setting->ircmp_res);
- if (rv)
- return rv;
- rv = rt946x_set_vprec(chgnum, batt_info->precharge_voltage ?
- batt_info->precharge_voltage : batt_info->voltage_min);
- if (rv)
- return rv;
- rv = rt946x_set_iprec(chgnum, batt_info->precharge_current);
- if (rv)
- return rv;
-
-#ifdef CONFIG_CHARGER_MT6370_BACKLIGHT
- rt946x_write8(chgnum, MT6370_BACKLIGHT_BLEN,
- MT6370_MASK_BLED_EXT_EN | MT6370_MASK_BLED_EN |
- MT6370_MASK_BLED_1CH_EN | MT6370_MASK_BLED_2CH_EN |
- MT6370_MASK_BLED_3CH_EN | MT6370_MASK_BLED_4CH_EN |
- MT6370_BLED_CODE_LINEAR);
- rt946x_update_bits(chgnum, MT6370_BACKLIGHT_BLPWM,
- MT6370_MASK_BLPWM_BLED_PWM,
- BIT(MT6370_SHIFT_BLPWM_BLED_PWM));
-#endif
-
- return rt946x_init_irq(chgnum);
-}
-
-#ifdef CONFIG_CHARGER_OTG
-static enum ec_error_list rt946x_enable_otg_power(int chgnum, int enabled)
-{
- return (enabled ? rt946x_set_bit : rt946x_clr_bit)
- (chgnum, RT946X_REG_CHGCTRL1, RT946X_MASK_OPA_MODE);
-}
-
-static int rt946x_is_sourcing_otg_power(int chgnum, int port)
-{
- int val;
-
- if (rt946x_read8(CHARGER_SOLO, RT946X_REG_CHGCTRL1, &val))
- return 0;
-
- return !!(val & RT946X_MASK_OPA_MODE);
-}
-#endif
-
-static enum ec_error_list rt946x_set_input_current_limit(int chgnum,
- int input_current)
-{
- uint8_t reg_iin = 0;
- const struct charger_info * const info = rt946x_get_info(chgnum);
-
- reg_iin = rt946x_closest_reg(info->input_current_min,
- info->input_current_max, info->input_current_step,
- input_current);
-
- CPRINTS("iin=%d", input_current);
-
- return rt946x_update_bits(chgnum, RT946X_REG_CHGCTRL3, RT946X_MASK_AICR,
- reg_iin << RT946X_SHIFT_AICR);
-}
-
-static enum ec_error_list rt946x_get_input_current_limit(int chgnum,
- int *input_current)
-{
- int rv;
- int val = 0;
- const struct charger_info * const info = rt946x_get_info(chgnum);
-
- rv = rt946x_read8(chgnum, RT946X_REG_CHGCTRL3, &val);
- if (rv)
- return rv;
-
- val = (val & RT946X_MASK_AICR) >> RT946X_SHIFT_AICR;
- *input_current = val * info->input_current_step
- + info->input_current_min;
-
- return EC_SUCCESS;
-}
-
-static enum ec_error_list rt946x_manufacturer_id(int chgnum, int *id)
-{
- return EC_ERROR_UNIMPLEMENTED;
-}
-
-static enum ec_error_list rt946x_device_id(int chgnum, int *id)
-{
- int rv;
-
- rv = rt946x_read8(chgnum, RT946X_REG_DEVICEID, id);
- if (rv == EC_SUCCESS)
- *id &= RT946X_MASK_VENDOR_ID;
- return rv;
-}
-
-static enum ec_error_list rt946x_get_option(int chgnum, int *option)
-{
- /* Ignored: does not exist */
- *option = 0;
- return EC_SUCCESS;
-}
-
-static enum ec_error_list rt946x_set_option(int chgnum, int option)
-{
- /* Ignored: does not exist */
- return EC_SUCCESS;
-}
-
-static const struct charger_info *rt946x_get_info(int chgnum)
-{
- return &rt946x_charger_info;
-}
-
-static enum ec_error_list rt946x_get_status(int chgnum, int *status)
-{
- int rv;
- int val = 0;
-
- rv = rt946x_read8(chgnum, RT946X_REG_CHGCTRL2, &val);
- if (rv)
- return rv;
- val = (val & RT946X_MASK_CHG_EN) >> RT946X_SHIFT_CHG_EN;
- if (!val)
- *status |= CHARGER_CHARGE_INHIBITED;
-
- rv = rt946x_read8(chgnum, RT946X_REG_CHGFAULT, &val);
- if (rv)
- return rv;
- if (val & RT946X_MASK_CHG_VBATOV)
- *status |= CHARGER_VOLTAGE_OR;
-
-
- rv = rt946x_read8(chgnum, RT946X_REG_CHGNTC, &val);
- if (rv)
- return rv;
- val = (val & RT946X_MASK_BATNTC_FAULT) >> RT946X_SHIFT_BATNTC_FAULT;
-
- switch (val) {
- case RT946X_BATTEMP_WARM:
- *status |= CHARGER_RES_HOT;
- break;
- case RT946X_BATTEMP_COOL:
- *status |= CHARGER_RES_COLD;
- break;
- case RT946X_BATTEMP_COLD:
- *status |= CHARGER_RES_COLD;
- *status |= CHARGER_RES_UR;
- break;
- case RT946X_BATTEMP_HOT:
- *status |= CHARGER_RES_HOT;
- *status |= CHARGER_RES_OR;
- break;
- default:
- break;
- }
-
- return EC_SUCCESS;
-}
-
-static enum ec_error_list rt946x_set_mode(int chgnum, int mode)
-{
- int rv;
-
- if (mode & CHARGE_FLAG_POR_RESET) {
- rv = rt946x_por_reset();
- if (rv)
- return rv;
- }
-
- if (mode & CHARGE_FLAG_RESET_TO_ZERO) {
- rv = rt946x_reset_to_zero(chgnum);
- if (rv)
- return rv;
- }
-
- return EC_SUCCESS;
-}
-
-static enum ec_error_list rt946x_get_current(int chgnum, int *current)
-{
- int rv;
- int val = 0;
- const struct charger_info * const info = rt946x_get_info(chgnum);
-
- rv = rt946x_read8(chgnum, RT946X_REG_CHGCTRL7, &val);
- if (rv)
- return rv;
-
- val = (val & RT946X_MASK_ICHG) >> RT946X_SHIFT_ICHG;
- *current = val * info->current_step + info->current_min;
-
- return EC_SUCCESS;
-}
-
-static enum ec_error_list rt946x_set_current(int chgnum, int current)
-{
- int rv;
- uint8_t reg_icc;
- static int workaround;
- const struct charger_info *const info = rt946x_get_info(chgnum);
-
- /*
- * mt6370's minimum regulated current is 500mA REG17[7:2] 0b100,
- * values below 0b100 are preserved.
- */
- if (IS_ENABLED(CONFIG_CHARGER_MT6370))
- current = MAX(500, current);
-
-#ifdef CONFIG_CHARGER_MT6370
- rv = mt6370_ichg_workaround(chgnum, current);
- if (rv)
- return rv;
-#endif
-
- reg_icc = rt946x_closest_reg(info->current_min, info->current_max,
- info->current_step, current);
-
- rv = rt946x_update_bits(chgnum, RT946X_REG_CHGCTRL7, RT946X_MASK_ICHG,
- reg_icc << RT946X_SHIFT_ICHG);
- if (rv)
- return rv;
-
- if (IS_ENABLED(CONFIG_CHARGER_RT9466) ||
- IS_ENABLED(CONFIG_CHARGER_MT6370)) {
- uint32_t curr_ieoc;
-
- /*
- * workaround to make IEOC accurate:
- * witht normal charging (ICC >= 900mA), the power path is fully
- * turned on. But at low charging current state (ICC < 900mA),
- * the power path will only be partially turned on. So under
- * such situation, the IEOC is inaccurate.
- */
- rv = rt946x_get_ieoc(chgnum, &curr_ieoc);
- if (rv)
- return rv;
-
- if (current < 900 && !workaround) {
- /* raise IEOC if charge current is under 900 */
- rv = rt946x_set_ieoc(chgnum, curr_ieoc + 100);
- workaround = 1;
- } else if (current >= 900 && workaround) {
- /* reset IEOC if charge current is above 900 */
- workaround = 0;
- rv = rt946x_set_ieoc(chgnum, curr_ieoc - 100);
- }
- }
-
- return rv;
-}
-
-static enum ec_error_list rt946x_get_voltage(int chgnum, int *voltage)
-{
- int rv;
- int val = 0;
- const struct charger_info * const info = rt946x_get_info(chgnum);
-
- rv = rt946x_read8(chgnum, RT946X_REG_CHGCTRL4, &val);
- if (rv)
- return rv;
-
- val = (val & RT946X_MASK_CV) >> RT946X_SHIFT_CV;
- *voltage = val * info->voltage_step + info->voltage_min;
-
- return EC_SUCCESS;
-}
-
-static enum ec_error_list rt946x_set_voltage(int chgnum, int voltage)
-{
- uint8_t reg_cv = 0;
- const struct charger_info * const info = rt946x_get_info(chgnum);
-
- reg_cv = rt946x_closest_reg(info->voltage_min, info->voltage_max,
- info->voltage_step, voltage);
-
- return rt946x_update_bits(chgnum, RT946X_REG_CHGCTRL4, RT946X_MASK_CV,
- reg_cv << RT946X_SHIFT_CV);
-}
-
-static enum ec_error_list rt946x_discharge_on_ac(int chgnum, int enable)
-{
- return rt946x_enable_hz(chgnum, enable);
-}
-
-/* Setup sourcing current to prevent overload */
-#ifdef CONFIG_CHARGER_ILIM_PIN_DISABLED
-static int rt946x_enable_ilim_pin(int chgnum, int en)
-{
- int ret;
-
- ret = (en ? rt946x_set_bit : rt946x_clr_bit)
- (chgnum, RT946X_REG_CHGCTRL3, RT946X_MASK_ILIMEN);
-
- return ret;
-}
-
-static int rt946x_select_ilmt(int chgnum, enum rt946x_ilmtsel sel)
-{
- int ret;
-
- ret = rt946x_update_bits(chgnum, RT946X_REG_CHGCTRL2,
- RT946X_MASK_ILMTSEL,
- sel << RT946X_SHIFT_ILMTSEL);
-
- return ret;
-}
-#endif /* CONFIG_CHARGER_ILIM_PIN_DISABLED */
-
-/* Charging power state initialization */
-static enum ec_error_list rt946x_post_init(int chgnum)
-{
-#ifdef CONFIG_CHARGER_ILIM_PIN_DISABLED
- int rv;
-
- rv = rt946x_select_ilmt(chgnum, RT946X_ILMTSEL_AICR);
- if (rv)
- return rv;
-
- /* Need 5ms to ramp after choose current limit source */
- msleep(5);
-
- /* Disable ILIM pin */
- rv = rt946x_enable_ilim_pin(chgnum, 0);
- if (rv)
- return rv;
-#endif
- return EC_SUCCESS;
-}
-
-/* Hardware current ramping (aka AICL: Average Input Current Level) */
-#ifdef CONFIG_CHARGE_RAMP_HW
-static int rt946x_get_mivr(int chgnum, int *mivr)
-{
- int rv;
- int val = 0;
-
- rv = rt946x_read8(chgnum, RT946X_REG_CHGCTRL6, &val);
- if (rv)
- return rv;
-
- val = (val & RT946X_MASK_MIVR) >> RT946X_SHIFT_MIVR;
- *mivr = val * RT946X_MIVR_STEP + RT946X_MIVR_MIN;
-
- return EC_SUCCESS;
-}
-
-static int rt946x_set_aicl_vth(int chgnum, uint8_t aicl_vth)
-{
- uint8_t reg_aicl_vth = 0;
-
- reg_aicl_vth = rt946x_closest_reg(RT946X_AICLVTH_MIN,
- RT946X_AICLVTH_MAX, RT946X_AICLVTH_STEP, aicl_vth);
-
- return rt946x_update_bits(chgnum, RT946X_REG_CHGCTRL14,
- RT946X_MASK_AICLVTH,
- reg_aicl_vth << RT946X_SHIFT_AICLVTH);
-}
-
-static enum ec_error_list rt946x_set_hw_ramp(int chgnum, int enable)
-{
- int rv;
- unsigned int mivr = 0;
-
- if (!enable) {
- rv = rt946x_clr_bit(chgnum, RT946X_REG_CHGCTRL14,
- RT946X_MASK_AICLMEAS);
- return rv;
- }
-
- rv = rt946x_get_mivr(chgnum, &mivr);
- if (rv < 0)
- return rv;
-
- /*
- * Check if there's a suitable AICL_VTH.
- * The vendor suggests setting AICL_VTH as (MIVR + 200mV).
- */
- if ((mivr + 200) > RT946X_AICLVTH_MAX) {
- CPRINTS("mivr(%d) too high", mivr);
- return EC_ERROR_INVAL;
- }
-
- rv = rt946x_set_aicl_vth(chgnum, mivr + 200);
- if (rv < 0)
- return rv;
-
- return rt946x_set_bit(chgnum, RT946X_REG_CHGCTRL14,
- RT946X_MASK_AICLMEAS);
-}
-
-static int rt946x_ramp_is_stable(int chgnum)
-{
- int rv;
- int val = 0;
-
- rv = rt946x_read8(chgnum, RT946X_REG_CHGCTRL14, &val);
- val = (val & RT946X_MASK_AICLMEAS) >> RT946X_SHIFT_AICLMEAS;
-
- return (!rv && !val);
-}
-
-static int rt946x_ramp_is_detected(int chgnum)
-{
- return 1;
-}
-
-static int rt946x_ramp_get_current_limit(int chgnum)
-{
- int rv;
- int input_current = 0;
-
- rv = rt946x_get_input_current_limit(chgnum, &input_current);
-
- return rv ? -1 : input_current;
-}
-#endif /* CONFIG_CHARGE_RAMP_HW */
-
-static void rt946x_init(int chgnum)
-{
- int ret = rt946x_init_setting(chgnum);
-
- CPRINTS("init%d %s(%d)", chgnum, ret ? "fail" : "good", ret);
-}
-
-#ifdef HAS_TASK_USB_CHG
-#ifdef CONFIG_CHARGER_MT6370
-static int mt6370_detect_apple_samsung_ta(int chgnum, int usb_stat)
-{
- int ret, reg;
- int chg_type =
- (usb_stat & MT6370_MASK_USB_STATUS) >> MT6370_SHIFT_USB_STATUS;
- int dp_2_3v, dm_2_3v;
-
- /* Only SDP/CDP/DCP could possibly be Apple/Samsung TA */
- if (chg_type != MT6370_CHG_TYPE_SDPNSTD &&
- chg_type != MT6370_CHG_TYPE_CDP &&
- chg_type != MT6370_CHG_TYPE_DCP)
- return chg_type;
-
- if (chg_type == MT6370_CHG_TYPE_SDPNSTD ||
- chg_type == MT6370_CHG_TYPE_CDP)
- if (!(usb_stat & MT6370_MASK_DCD_TIMEOUT))
- return chg_type;
-
- /* Check D+ > 0.9V */
- ret = rt946x_update_bits(chgnum, MT6370_REG_QCSTATUS2,
- MT6360_MASK_CHECK_DPDM,
- MT6370_MASK_APP_SS_EN | MT6370_MASK_APP_SS_PL);
- ret |= rt946x_read8(chgnum, MT6370_REG_QCSTATUS2, &reg);
-
- if (ret)
- return chg_type;
-
- /* Normal port (D+ < 0.9V) */
- if (!(reg & MT6370_MASK_SS_OUT))
- return chg_type;
-
- /* Samsung charger (D+ < 1.5V) */
- if (!(reg & MT6370_MASK_APP_OUT))
- return MT6370_CHG_TYPE_SAMSUNG_CHARGER;
-
- /* Check D+ > 2.3 V */
- ret = rt946x_update_bits(chgnum, MT6370_REG_QCSTATUS2,
- MT6360_MASK_CHECK_DPDM,
- MT6370_MASK_APP_REF | MT6370_MASK_APP_SS_PL |
- MT6370_MASK_APP_SS_EN);
- ret |= rt946x_read8(chgnum, MT6370_REG_QCSTATUS2, &reg);
- dp_2_3v = reg & MT6370_MASK_APP_OUT;
-
- /* Check D- > 2.3 V */
- ret |= rt946x_update_bits(chgnum,
- MT6370_REG_QCSTATUS2, MT6360_MASK_CHECK_DPDM,
- MT6370_MASK_APP_REF | MT6370_MASK_APP_DPDM_IN |
- MT6370_MASK_APP_SS_PL | MT6370_MASK_APP_SS_EN);
- ret |= rt946x_read8(chgnum, MT6370_REG_QCSTATUS2, &reg);
- dm_2_3v = reg & MT6370_MASK_APP_OUT;
-
- if (ret)
- return chg_type;
-
- /* Apple charger */
- if (!dp_2_3v && !dm_2_3v)
- /* Apple 2.5W charger */
- return MT6370_CHG_TYPE_APPLE_0_5A_CHARGER;
- else if (!dp_2_3v && dm_2_3v)
- /* Apple 5W charger */
- return MT6370_CHG_TYPE_APPLE_1_0A_CHARGER;
- else if (dp_2_3v && !dm_2_3v)
- /* Apple 10W charger */
- return MT6370_CHG_TYPE_APPLE_2_1A_CHARGER;
- else
- /* Apple 12W charger */
- return MT6370_CHG_TYPE_APPLE_2_4A_CHARGER;
-}
-#endif
-
-static int mt6370_get_bc12_device_type(int charger_type)
-{
- switch (charger_type) {
- case MT6370_CHG_TYPE_SDP:
- case MT6370_CHG_TYPE_SDPNSTD:
- return CHARGE_SUPPLIER_BC12_SDP;
- case MT6370_CHG_TYPE_CDP:
- return CHARGE_SUPPLIER_BC12_CDP;
- case MT6370_CHG_TYPE_DCP:
- case MT6370_CHG_TYPE_SAMSUNG_CHARGER:
- case MT6370_CHG_TYPE_APPLE_0_5A_CHARGER:
- case MT6370_CHG_TYPE_APPLE_1_0A_CHARGER:
- case MT6370_CHG_TYPE_APPLE_2_1A_CHARGER:
- case MT6370_CHG_TYPE_APPLE_2_4A_CHARGER:
- return CHARGE_SUPPLIER_BC12_DCP;
- default:
- return CHARGE_SUPPLIER_NONE;
- }
-}
-
-/* Returns a mt6370 charger_type. */
-static int mt6370_get_charger_type(int chgnum)
-{
-#ifdef CONFIG_CHARGER_MT6370
- int reg;
-
- if (rt946x_read8(chgnum, MT6370_REG_USBSTATUS1, &reg))
- return CHARGE_SUPPLIER_NONE;
- return mt6370_detect_apple_samsung_ta(chgnum, reg);
-#else
- return CHARGE_SUPPLIER_NONE;
-#endif
-}
-
-/*
- * The USB Type-C specification limits the maximum amount of current from BC 1.2
- * suppliers to 1.5A. Technically, proprietary methods are not allowed, but we
- * will continue to allow those.
- */
-static int mt6370_get_bc12_ilim(int charge_supplier)
-{
- switch (charge_supplier) {
- case MT6370_CHG_TYPE_APPLE_0_5A_CHARGER:
- return 500;
- case MT6370_CHG_TYPE_APPLE_1_0A_CHARGER:
- return 1000;
- case MT6370_CHG_TYPE_APPLE_2_1A_CHARGER:
- case MT6370_CHG_TYPE_APPLE_2_4A_CHARGER:
- case MT6370_CHG_TYPE_DCP:
- case MT6370_CHG_TYPE_CDP:
- case MT6370_CHG_TYPE_SAMSUNG_CHARGER:
- return USB_CHARGER_MAX_CURR_MA;
- case MT6370_CHG_TYPE_SDP:
- default:
- return USB_CHARGER_MIN_CURR_MA;
- }
-}
-
-static int rt946x_get_bc12_device_type(int chgnum, int charger_type)
-{
- int reg;
-
- if (rt946x_read8(chgnum, RT946X_REG_DPDM1, &reg))
- return CHARGE_SUPPLIER_NONE;
-
- switch (reg & RT946X_MASK_BC12_TYPE) {
- case RT946X_MASK_SDP:
- return CHARGE_SUPPLIER_BC12_SDP;
- case RT946X_MASK_CDP:
- return CHARGE_SUPPLIER_BC12_CDP;
- case RT946X_MASK_DCP:
- return CHARGE_SUPPLIER_BC12_DCP;
- default:
- return CHARGE_SUPPLIER_NONE;
- }
-}
-
-static int rt946x_get_bc12_ilim(int charge_supplier)
-{
- switch (charge_supplier) {
- case CHARGE_SUPPLIER_BC12_DCP:
- if (IS_ENABLED(CONFIG_CHARGE_RAMP_SW) ||
- IS_ENABLED(CONFIG_CHARGE_RAMP_HW))
- /* A conservative value to prevent a bad charger. */
- return RT946X_AICR_TYP2MAX(USB_CHARGER_MAX_CURR_MA);
- /* fallback */
- case CHARGE_SUPPLIER_BC12_CDP:
- return USB_CHARGER_MAX_CURR_MA;
- case CHARGE_SUPPLIER_BC12_SDP:
- default:
- return USB_CHARGER_MIN_CURR_MA;
- }
-}
-
-static void check_ac_state(void)
-{
- static uint8_t ac;
-
- if (ac != extpower_is_present()) {
- ac = !ac;
- hook_notify(HOOK_AC_CHANGE);
- }
-}
-DECLARE_DEFERRED(check_ac_state);
-
-void rt946x_interrupt(enum gpio_signal signal)
-{
- task_wake(TASK_ID_USB_CHG);
- /*
- * Generally, VBUS detection can be done immediately when the port
- * plug/unplug happens. But if it's a PD plug(and will generate an
- * interrupt), then it will take a few milliseconds to raise VBUS
- * by PD negotiation.
- */
- hook_call_deferred(&check_ac_state_data, 100 * MSEC);
-}
-
-int rt946x_toggle_bc12_detection(void)
-{
- int rv;
- rv = rt946x_enable_bc12_detection(CHARGER_SOLO, 0);
- if (rv)
- return rv;
- /* mt6370 requires 40us delay to toggle RT946X_MASK_USBCHGEN */
- udelay(40);
- return rt946x_enable_bc12_detection(CHARGER_SOLO, 1);
-}
-
-static void check_pd_capable(void)
-{
- const int port = TASK_ID_TO_USB_CHG_PORT(TASK_ID_USB_CHG);
-
- if (!pd_capable(port)) {
- enum tcpc_cc_voltage_status cc1, cc2;
-
- tcpm_get_cc(port, &cc1, &cc2);
- /* if CC is not changed. */
- if (cc_is_rp(cc1) || cc_is_rp(cc2))
- rt946x_toggle_bc12_detection();
- }
-}
-DECLARE_DEFERRED(check_pd_capable);
-
-static void rt946x_usb_connect(void)
-{
- const int port = TASK_ID_TO_USB_CHG_PORT(TASK_ID_USB_CHG);
- enum tcpc_cc_voltage_status cc1, cc2;
-
- tcpm_get_cc(port, &cc1, &cc2);
-
- /*
- * Only detect BC1.2 device when USB-C device recognition is
- * finished to prevent a potential race condition with USB enumeration.
- * If CC exists RP, then it might be a BC12 or a PD capable device.
- * Check this later to ensure it's not PD capable.
- */
- if (cc_is_rp(cc1) || cc_is_rp(cc2))
- /* delay extra 50 ms to ensure SrcCap received */
- hook_call_deferred(&check_pd_capable_data,
- PD_T_SINK_WAIT_CAP + 50 * MSEC);
- hook_call_deferred(&check_ac_state_data, 0);
-}
-DECLARE_HOOK(HOOK_USB_PD_CONNECT, rt946x_usb_connect, HOOK_PRIO_DEFAULT);
-
-static void rt946x_pd_disconnect(void)
-{
- /* Type-C disconnected, disable deferred check. */
- hook_call_deferred(&check_pd_capable_data, -1);
- hook_call_deferred(&check_ac_state_data, 0);
-}
-DECLARE_HOOK(HOOK_USB_PD_DISCONNECT, rt946x_pd_disconnect, HOOK_PRIO_DEFAULT);
-
-int rt946x_get_adc(enum rt946x_adc_in_sel adc_sel, int *adc_val)
-{
- int rv, i, adc_start, adc_result = 0;
- int adc_data_h, adc_data_l, aicr;
- const int max_wait_times = 6;
-
- if (in_interrupt_context()) {
- CPRINTS("Err: use ADC in IRQ");
- return EC_ERROR_INVAL;
- }
- mutex_lock(&adc_access_lock);
-#ifdef CONFIG_CHARGER_MT6370
- mt6370_enable_hidden_mode(CHARGER_SOLO, 1);
-#endif
-
- /* Select ADC to desired channel */
- rv = rt946x_update_bits(CHARGER_SOLO, RT946X_REG_CHGADC,
- RT946X_MASK_ADC_IN_SEL,
- adc_sel << RT946X_SHIFT_ADC_IN_SEL);
- if (rv)
- goto out;
-
- if (adc_sel == MT6370_ADC_IBUS) {
- rv = charger_get_input_current_limit(CHARGER_SOLO, &aicr);
- if (rv)
- goto out;
- }
-
- /* Start ADC conversation */
- rv = rt946x_set_bit(CHARGER_SOLO, RT946X_REG_CHGADC,
- RT946X_MASK_ADC_START);
- if (rv)
- goto out;
-
- for (i = 0; i < max_wait_times; i++) {
- msleep(35);
- rv = mt6370_pmu_reg_test_bit(CHARGER_SOLO, RT946X_REG_CHGADC,
- RT946X_SHIFT_ADC_START,
- &adc_start);
- if (!adc_start && rv == 0)
- break;
- }
- if (i == max_wait_times)
- CPRINTS("conversion fail sel=%d", adc_sel);
-
- /* Read ADC data */
- rv = rt946x_read8(CHARGER_SOLO, RT946X_REG_ADCDATAH, &adc_data_h);
- rv = rt946x_read8(CHARGER_SOLO, RT946X_REG_ADCDATAL, &adc_data_l);
- if (rv)
- goto out;
-
-#if defined(CONFIG_CHARGER_RT9466) || defined(CONFIG_CHARGER_RT9467)
- if (adc_sel == RT946X_ADC_VBUS_DIV5)
- adc_result = ((adc_data_h << 8) | adc_data_l) * 25;
- else
- CPRINTS("unsupported channel %d", adc_sel);
- *adc_val = adc_result;
-#elif defined(CONFIG_CHARGER_MT6370)
- /* Calculate ADC value */
- adc_result = (adc_data_h * 256 + adc_data_l)
- * mt6370_adc_unit[adc_sel] + mt6370_adc_offset[adc_sel];
-
- /* For TS_BAT/TS_BUS, the real unit is 0.25, here we use 25(unit) */
- if (adc_sel == MT6370_ADC_TS_BAT)
- adc_result /= 100;
-#endif
-
-out:
-#ifdef CONFIG_CHARGER_MT6370
- if (adc_sel == MT6370_ADC_IBUS) {
- if (aicr < 400) /* 400mA */
- adc_result = adc_result * 67 / 100;
- }
-
- if (adc_sel != MT6370_ADC_TS_BAT && adc_sel != MT6370_ADC_TEMP_JC)
- *adc_val = adc_result / 1000;
- else
- *adc_val = adc_result;
- mt6370_enable_hidden_mode(CHARGER_SOLO, 0);
-#endif
- mutex_unlock(&adc_access_lock);
- return rv;
-}
-
-static enum ec_error_list rt946x_get_vbus_voltage(int chgnum, int port,
- int *voltage)
-{
- int vbus_mv;
- int rv;
-
- rv = rt946x_get_adc(RT946X_ADC_VBUS_DIV5, &vbus_mv);
- *voltage = vbus_mv;
-
- return rv;
-}
-
-#ifdef CONFIG_CHARGER_MT6370
-static int mt6370_toggle_cfo(void)
-{
- int rv, data;
-
- rv = rt946x_read8(CHARGER_SOLO, MT6370_REG_FLEDEN, &data);
- if (rv)
- return rv;
-
- if (data & MT6370_STROBE_EN_MASK)
- return rv;
-
- /* read data */
- rv = rt946x_read8(CHARGER_SOLO, RT946X_REG_CHGCTRL2, &data);
- if (rv)
- return rv;
-
- /* cfo off */
- data &= ~RT946X_MASK_CFO_EN;
- rv = rt946x_write8(CHARGER_SOLO, RT946X_REG_CHGCTRL2, data);
- if (rv)
- return rv;
-
- /* cfo on */
- data |= RT946X_MASK_CFO_EN;
- return rt946x_write8(CHARGER_SOLO, RT946X_REG_CHGCTRL2, data);
-}
-
-static int mt6370_pmu_chg_mivr_irq_handler(int chgnum)
-{
- int rv, ibus = 0, mivr_stat;
-
- rv = mt6370_pmu_reg_test_bit(chgnum, MT6370_REG_CHGSTAT1,
- MT6370_SHIFT_MIVR_STAT, &mivr_stat);
- if (rv)
- return rv;
-
- if (!mivr_stat) {
- CPRINTS("no mivr stat");
- return rv;
- }
-
- rv = rt946x_get_adc(MT6370_ADC_IBUS, &ibus);
- if (rv)
- return rv;
-
- if (ibus < 100) /* 100mA */
- rv = mt6370_toggle_cfo();
-
- return rv;
-}
-
-static int mt6370_irq_handler(int chgnum)
-{
- int data, mask, ret, reg_val;
- int stat_chg, valid_chg, stat_old, stat_new;
-
- ret = rt946x_write8(chgnum, MT6370_REG_IRQMASK, MT6370_IRQ_MASK_ALL);
- if (ret)
- return ret;
-
- ret = rt946x_read8(chgnum, MT6370_REG_IRQIND, &reg_val);
- if (ret)
- return ret;
-
- /* read stat before reading irq evt */
- ret = rt946x_read8(chgnum, MT6370_REG_CHGSTAT1, &stat_old);
- if (ret)
- return ret;
-
- /* workaround for irq, divided irq event into upper and lower */
- ret = rt946x_read8(chgnum, MT6370_REG_CHGIRQ1, &data);
- if (ret)
- return ret;
-
- /* read stat after reading irq evt */
- ret = rt946x_read8(chgnum, MT6370_REG_CHGSTAT1, &stat_new);
- if (ret)
- return ret;
-
- ret = rt946x_read8(chgnum, MT6370_REG_CHGMASK1, &mask);
- if (ret)
- return ret;
-
- ret = rt946x_write8(chgnum, MT6370_REG_IRQMASK, 0x00);
- if (ret)
- return ret;
-
- stat_chg = stat_old ^ stat_new;
- valid_chg = (stat_new & 0xF1) | (~stat_new & 0xF1);
- data |= (stat_chg & valid_chg);
- data &= ~mask;
- if (data)
- ret = mt6370_pmu_chg_mivr_irq_handler(chgnum);
- return ret;
-}
-#endif /* CONFIG_CHARGER_MT6370 */
-
-static void rt946x_bc12_workaround(void)
-{
- /*
- * There is a parasitic capacitance on D+,
- * which results in pulling D+ up too slow while detecting BC1.2.
- * So we try to fix this in two steps:
- * 1. Pull D+ up to a voltage under 0.6V
- * 2. re-toggling and pull D+ up to 0.6V (again)
- * and then detect the voltage of D-.
- */
- rt946x_toggle_bc12_detection();
- msleep(10);
- rt946x_toggle_bc12_detection();
-}
-DECLARE_DEFERRED(rt946x_bc12_workaround);
-
-static void rt946x_usb_charger_task(const int unused)
-{
- struct charge_port_info chg;
- int bc12_type = CHARGE_SUPPLIER_NONE;
- int chg_type;
- int reg = 0;
- int bc12_cnt = 0;
- const int max_bc12_cnt = 3;
- int voltage;
-
- chg.voltage = USB_CHARGER_VOLTAGE_MV;
- while (1) {
-#ifdef CONFIG_CHARGER_MT6370
- mt6370_irq_handler(CHARGER_SOLO);
-#endif /* CONFIG_CHARGER_MT6370 */
-
- rt946x_read8(CHARGER_SOLO, RT946X_REG_DPDMIRQ, &reg);
-
- /* VBUS attach event */
- if (reg & RT946X_MASK_DPDMIRQ_ATTACH) {
- charger_get_vbus_voltage(0, &voltage);
- CPRINTS("VBUS attached: %dmV", voltage);
- if (IS_ENABLED(CONFIG_CHARGER_MT6370)) {
- chg_type =
- mt6370_get_charger_type(CHARGER_SOLO);
- bc12_type =
- mt6370_get_bc12_device_type(chg_type);
- chg.current = mt6370_get_bc12_ilim(bc12_type);
- } else {
- bc12_type =
- rt946x_get_bc12_device_type(CHARGER_SOLO,
- chg_type);
- chg.current = rt946x_get_bc12_ilim(bc12_type);
- }
- CPRINTS("BC12 type %d", bc12_type);
- if (bc12_type == CHARGE_SUPPLIER_NONE)
- goto bc12_none;
- if (IS_ENABLED(CONFIG_WIRELESS_CHARGER_P9221_R7) &&
- bc12_type == CHARGE_SUPPLIER_BC12_SDP &&
- wpc_chip_is_online()) {
- p9221_notify_vbus_change(1);
- CPRINTS("WPC ON");
- }
- if (bc12_type == CHARGE_SUPPLIER_BC12_SDP &&
- ++bc12_cnt < max_bc12_cnt) {
- /*
- * defer the workaround and awaiting for
- * waken up by the interrupt.
- */
- hook_call_deferred(
- &rt946x_bc12_workaround_data, 5);
- goto wait_event;
- }
-
- charge_manager_update_charge(bc12_type, 0, &chg);
-bc12_none:
- rt946x_enable_bc12_detection(CHARGER_SOLO, 0);
- }
-
- /* VBUS detach event */
- if (reg & RT946X_MASK_DPDMIRQ_DETACH &&
- bc12_type != CHARGE_SUPPLIER_NONE) {
- CPRINTS("VBUS detached");
- bc12_cnt = 0;
-#ifdef CONFIG_WIRELESS_CHARGER_P9221_R7
- p9221_notify_vbus_change(0);
-#endif
- charge_manager_update_charge(bc12_type, 0, NULL);
- }
-
-wait_event:
- task_wait_event(-1);
- }
-}
-
-static int rt946x_ramp_allowed(int supplier)
-{
- return supplier == CHARGE_SUPPLIER_BC12_DCP;
-}
-
-static int rt946x_ramp_max(int supplier, int sup_curr)
-{
- return rt946x_get_bc12_ilim(supplier);
-}
-#endif /* HAS_TASK_USB_CHG */
-
-/* Non-standard interface functions */
-
-int rt946x_enable_charger_boost(int en)
-{
- return (en ? rt946x_set_bit : rt946x_clr_bit)
- (CHARGER_SOLO, RT946X_REG_CHGCTRL2, RT946X_MASK_CHG_EN);
-}
-
-/*
- * rt946x reports VBUS ready after VBUS is up for ~500ms.
- * Check if this works for the use case before calling this function.
- */
-int rt946x_is_vbus_ready(void)
-{
- int val = 0;
-
- return rt946x_read8(CHARGER_SOLO, RT946X_REG_CHGSTATC, &val) ?
- 0 : !!(val & RT946X_MASK_PWR_RDY);
-}
-
-int rt946x_is_charge_done(void)
-{
- int val = 0;
-
- if (rt946x_read8(CHARGER_SOLO, RT946X_REG_CHGSTAT, &val))
- return 0;
-
- val = (val & RT946X_MASK_CHG_STAT) >> RT946X_SHIFT_CHG_STAT;
-
- return val == RT946X_CHGSTAT_DONE;
-}
-
-int rt946x_cutoff_battery(void)
-{
-#ifdef CONFIG_CHARGER_MT6370
-/*
- * We should lock ADC usage to prevent from using ADC while
- * cut-off. Or this might cause the ADC power not turning off.
- */
-
- int rv;
-
- mutex_lock(&adc_access_lock);
- rv = rt946x_write8(CHARGER_SOLO, MT6370_REG_RSTPASCODE1,
- MT6370_MASK_RSTPASCODE1);
- if (rv)
- goto out;
-
- rv = rt946x_write8(CHARGER_SOLO, MT6370_REG_RSTPASCODE2,
- MT6370_MASK_RSTPASCODE2);
- if (rv)
- goto out;
-
- /* reset all chg/fled/ldo/rgb/bl/db reg and logic */
- rv = rt946x_write8(CHARGER_SOLO, RT946X_REG_CORECTRL2, 0x7F);
- if (rv)
- goto out;
-
- /* disable chg auto sensing */
- mt6370_enable_hidden_mode(CHARGER_SOLO, 1);
- rv = rt946x_clr_bit(CHARGER_SOLO, MT6370_REG_CHGHIDDENCTRL15,
- MT6370_MASK_ADC_TS_AUTO);
- mt6370_enable_hidden_mode(CHARGER_SOLO, 0);
- if (rv)
- goto out;
- msleep(50);
- /* enter shipping mode */
- rv = rt946x_set_bit(CHARGER_SOLO, RT946X_REG_CHGCTRL2,
- RT946X_MASK_SHIP_MODE);
-
-out:
- mutex_unlock(&adc_access_lock);
- return rv;
-#endif
- /* enter shipping mode */
- return rt946x_set_bit(CHARGER_SOLO, RT946X_REG_CHGCTRL2,
- RT946X_MASK_SHIP_MODE);
-}
-
-int rt946x_enable_charge_termination(int en)
-{
- return (en ? rt946x_set_bit : rt946x_clr_bit)
- (CHARGER_SOLO, RT946X_REG_CHGCTRL2, RT946X_MASK_TE);
-}
-
-int rt946x_enable_charge_eoc(int en)
-{
- return (en ? rt946x_set_bit : rt946x_clr_bit)
- (CHARGER_SOLO, RT946X_REG_CHGCTRL9, RT946X_MASK_EOC);
-}
-
-#ifdef CONFIG_CHARGER_MT6370
-/* MT6370 LDO */
-
-int mt6370_set_ldo_voltage(int mv)
-{
- int rv;
- int vout_val;
- const int vout_mask = MT6370_MASK_LDOVOUT_EN | MT6370_MASK_LDOVOUT_VOUT;
-
- /* LDO output-off mode to floating. */
- rv = rt946x_update_bits(CHARGER_SOLO, MT6370_REG_LDOCFG,
- MT6370_MASK_LDOCFG_OMS, 0);
- if (rv)
- return rv;
-
- /* Disable LDO if voltage is zero. */
- if (mv == 0)
- return rt946x_clr_bit(CHARGER_SOLO, MT6370_REG_LDOVOUT,
- MT6370_MASK_LDOVOUT_EN);
-
- vout_val = 1 << MT6370_SHIFT_LDOVOUT_EN;
- vout_val |= rt946x_closest_reg(MT6370_LDO_MIN, MT6370_LDO_MAX,
- MT6370_LDO_STEP, mv);
- return rt946x_update_bits(CHARGER_SOLO, MT6370_REG_LDOVOUT, vout_mask,
- vout_val);
-}
-
-/* MT6370 Display bias */
-int mt6370_db_external_control(int en)
-{
- return rt946x_update_bits(CHARGER_SOLO, MT6370_REG_DBCTRL1,
- MT6370_MASK_DB_EXT_EN,
- en << MT6370_SHIFT_DB_EXT_EN);
-}
-
-int mt6370_db_set_voltages(int vbst, int vpos, int vneg)
-{
- int rv;
-
- /* set display bias VBST */
- rv = rt946x_update_bits(CHARGER_SOLO, MT6370_REG_DBVBST,
- MT6370_MASK_DB_VBST,
- rt946x_closest_reg(MT6370_DB_VBST_MIN,
- MT6370_DB_VBST_MAX,
- MT6370_DB_VBST_STEP, vbst));
-
- /* set display bias VPOS */
- rv |= rt946x_update_bits(CHARGER_SOLO, MT6370_REG_DBVPOS,
- MT6370_MASK_DB_VPOS,
- rt946x_closest_reg(MT6370_DB_VPOS_MIN,
- MT6370_DB_VPOS_MAX,
- MT6370_DB_VPOS_STEP, vpos));
-
- /* set display bias VNEG */
- rv |= rt946x_update_bits(CHARGER_SOLO, MT6370_REG_DBVNEG,
- MT6370_MASK_DB_VNEG,
- rt946x_closest_reg(MT6370_DB_VNEG_MIN,
- MT6370_DB_VNEG_MAX,
- MT6370_DB_VNEG_STEP, vneg));
-
- /* Enable VNEG/VPOS discharge when VNEG/VPOS rails disabled. */
- rv |= rt946x_update_bits(CHARGER_SOLO,
- MT6370_REG_DBCTRL2,
- MT6370_MASK_DB_VNEG_DISC | MT6370_MASK_DB_VPOS_DISC,
- MT6370_MASK_DB_VNEG_DISC | MT6370_MASK_DB_VPOS_DISC);
-
- return rv;
-}
-
-/* MT6370 BACKLIGHT LED */
-
-int mt6370_backlight_set_dim(uint16_t dim)
-{
- int rv;
-
- /* datasheet suggests that update BLDIM2 first then BLDIM */
- rv = rt946x_write8(CHARGER_SOLO, MT6370_BACKLIGHT_BLDIM2,
- dim & MT6370_MASK_BLDIM2);
-
- if (rv)
- return rv;
-
- rv = rt946x_write8(CHARGER_SOLO, MT6370_BACKLIGHT_BLDIM,
- (dim >> MT6370_SHIFT_BLDIM_MSB) & MT6370_MASK_BLDIM);
-
- return rv;
-}
-
-/* MT6370 RGB LED */
-
-int mt6370_led_set_dim_mode(enum mt6370_led_index index,
- enum mt6370_led_dim_mode mode)
-{
- if (index <= MT6370_LED_ID_OFF || index >= MT6370_LED_ID_COUNT)
- return EC_ERROR_INVAL;
-
- rt946x_update_bits(CHARGER_SOLO, MT6370_REG_RGBDIM_BASE + index,
- MT6370_MASK_RGB_DIMMODE,
- mode << MT6370_SHIFT_RGB_DIMMODE);
- return EC_SUCCESS;
-}
-
-int mt6370_led_set_color(uint8_t mask)
-{
- return rt946x_update_bits(CHARGER_SOLO, MT6370_REG_RGBEN,
- MT6370_MASK_RGB_ISNK_ALL_EN, mask);
-}
-
-int mt6370_led_set_brightness(enum mt6370_led_index index, uint8_t brightness)
-{
- if (index >= MT6370_LED_ID_COUNT || index <= MT6370_LED_ID_OFF)
- return EC_ERROR_INVAL;
-
- rt946x_update_bits(CHARGER_SOLO, MT6370_REG_RGBISNK_BASE + index,
- MT6370_MASK_RGBISNK_CURSEL,
- brightness << MT6370_SHIFT_RGBISNK_CURSEL);
- return EC_SUCCESS;
-}
-
-int mt6370_led_set_pwm_dim_duty(enum mt6370_led_index index, uint8_t dim_duty)
-{
- if (index >= MT6370_LED_ID_COUNT || index <= MT6370_LED_ID_OFF)
- return EC_ERROR_INVAL;
-
- rt946x_update_bits(CHARGER_SOLO, MT6370_REG_RGBDIM_BASE + index,
- MT6370_MASK_RGB_DIMDUTY,
- dim_duty << MT6370_SHIFT_RGB_DIMDUTY);
- return EC_SUCCESS;
-}
-
-int mt6370_led_set_pwm_frequency(enum mt6370_led_index index,
- enum mt6370_led_pwm_freq freq)
-{
- if (index >= MT6370_LED_ID_COUNT || index <= MT6370_LED_ID_OFF)
- return EC_ERROR_INVAL;
-
- rt946x_update_bits(CHARGER_SOLO, MT6370_REG_RGBISNK_BASE + index,
- MT6370_MASK_RGBISNK_DIMFSEL,
- freq << MT6370_SHIFT_RGBISNK_DIMFSEL);
- return EC_SUCCESS;
-}
-
-int mt6370_reduce_db_bl_driving(void)
-{
- int rv;
-
- /* Enter test mode */
- rv = rt946x_block_write(CHARGER_SOLO, MT6370_REG_TM_PAS_CODE1,
- mt6370_val_en_test_mode,
- ARRAY_SIZE(mt6370_val_en_test_mode));
- if (rv)
- return rv;
- msleep(1);
- rv = rt946x_write8(CHARGER_SOLO, MT6370_REG_BANK, MT6370_MASK_REG_TM);
- if (rv)
- return rv;
- msleep(1);
- /* reduce bl driving */
- rv = rt946x_update_bits(CHARGER_SOLO, MT6370_TM_REG_BL3,
- MT6370_TM_MASK_BL3_SL, MT6370_TM_REDUCE_BL3_SL);
- if (rv)
- return rv;
- msleep(1);
- /* reduce db driving */
- rv = rt946x_update_bits(CHARGER_SOLO, MT6370_TM_REG_DSV1,
- MT6370_TM_MASK_DSV1_SL,
- MT6370_TM_REDUCE_DSV1_SL);
- if (rv)
- return rv;
- msleep(1);
- /* Leave test mode */
- return rt946x_write8(CHARGER_SOLO, MT6370_REG_TM_PAS_CODE1,
- MT6370_LEAVE_TM);
-}
-#endif /* CONFIG_CHARGER_MT6370 */
-
-const struct charger_drv rt946x_drv = {
- .init = &rt946x_init,
- .post_init = &rt946x_post_init,
- .get_info = &rt946x_get_info,
- .get_status = &rt946x_get_status,
- .set_mode = &rt946x_set_mode,
- .enable_otg_power = &rt946x_enable_otg_power,
- .is_sourcing_otg_power = &rt946x_is_sourcing_otg_power,
- .get_current = &rt946x_get_current,
- .set_current = &rt946x_set_current,
- .get_voltage = &rt946x_get_voltage,
- .set_voltage = &rt946x_set_voltage,
- .discharge_on_ac = &rt946x_discharge_on_ac,
- .get_vbus_voltage = &rt946x_get_vbus_voltage,
- .set_input_current_limit = &rt946x_set_input_current_limit,
- .get_input_current_limit = &rt946x_get_input_current_limit,
- .manufacturer_id = &rt946x_manufacturer_id,
- .device_id = &rt946x_device_id,
- .get_option = &rt946x_get_option,
- .set_option = &rt946x_set_option,
-#ifdef CONFIG_CHARGE_RAMP_HW
- .set_hw_ramp = &rt946x_set_hw_ramp,
- .ramp_is_stable = &rt946x_ramp_is_stable,
- .ramp_is_detected = &rt946x_ramp_is_detected,
- .ramp_get_current_limit = &rt946x_ramp_get_current_limit,
-#endif
-};
-
-#ifdef HAS_TASK_USB_CHG
-const struct bc12_drv rt946x_bc12_drv = {
- .usb_charger_task = rt946x_usb_charger_task,
- .ramp_allowed = rt946x_ramp_allowed,
- .ramp_max = rt946x_ramp_max,
-};
-
-#ifdef CONFIG_BC12_SINGLE_DRIVER
-/* provide a default bc12_ports[] for backward compatibility */
-struct bc12_config bc12_ports[CHARGE_PORT_COUNT] = {
- [0 ... (CHARGE_PORT_COUNT - 1)] = {
- .drv = &rt946x_bc12_drv,
- },
-};
-#endif /* CONFIG_BC12_SINGLE_DRIVER */
-#endif