diff options
Diffstat (limited to 'driver/charger/rt946x.c')
-rw-r--r-- | driver/charger/rt946x.c | 1927 |
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, ®_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, ®_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, ®); - - 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, ®); - 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, ®); - 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, ®)) - 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, ®)) - 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, ®_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, ®); - - /* 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 |