diff options
Diffstat (limited to 'driver/tcpm/rt1718s.c')
-rw-r--r-- | driver/tcpm/rt1718s.c | 111 |
1 files changed, 104 insertions, 7 deletions
diff --git a/driver/tcpm/rt1718s.c b/driver/tcpm/rt1718s.c index 3211bacb03..175f471fe5 100644 --- a/driver/tcpm/rt1718s.c +++ b/driver/tcpm/rt1718s.c @@ -12,18 +12,27 @@ #include "driver/tcpm/tcpci.h" #include "driver/tcpm/tcpm.h" #include "gpio.h" +#include "hooks.h" #include "stdint.h" #include "system.h" #include "task.h" #include "timer.h" #include "usb_mux.h" #include "usb_pd.h" +#include "usb_pe_sm.h" #include "util.h" #define CPRINTS(format, args...) cprints(CC_USBCHARGE, format, ## args) #define CPRINTF(format, args...) cprintf(CC_USBCHARGE, format, ## args) -#define RT1718S_SW_RESET_DELAY_MS 2 +#define RT1718S_SW_RESET_DELAY_MS 2 +/* Time for delay deasserting EN_FRS after FRS VBUS drop. */ +#define RT1718S_FRS_DIS_DELAY (5 * MSEC) + +#define FLAG_FRS_ENABLED BIT(0) +#define FLAG_FRS_RX_SIGNALLED BIT(1) +#define FLAG_FRS_VBUS_VALID_FALL BIT(2) +static atomic_t frs_flag[CONFIG_USB_PD_PORT_MAX_COUNT]; /* i2c_write function which won't wake TCPC from low power mode. */ static int rt1718s_write(int port, int reg, int val, int len) @@ -257,10 +266,16 @@ static int rt1718s_init(int port) TCPC_REG_ALERT_MASK_VENDOR_DEF, MASK_SET)); - if (IS_ENABLED(CONFIG_USB_PD_FRS)) - /* Set Rx frs unmasked */ - RETURN_ERROR(rt1718s_update_bits8(port, RT1718S_RT_MASK1, - RT1718S_RT_MASK1_M_RX_FRS, 0xFF)); + if (IS_ENABLED(CONFIG_USB_PD_FRS)) { + memset(frs_flag, 0, + sizeof(atomic_t) * CONFIG_USB_PD_PORT_MAX_COUNT); + /* Set Rx frs and valid vbus fall unmasked */ + RETURN_ERROR(rt1718s_update_bits8( + port, RT1718S_RT_MASK1, + RT1718S_RT_MASK1_M_RX_FRS | + RT1718S_RT_MASK1_M_VBUS_FRS_LOW, + 0xFF)); + } RETURN_ERROR(board_rt1718s_init(port)); @@ -363,6 +378,23 @@ static void rt1718s_bc12_usb_charger_task(const int port) } } +static void frs_gpio_disable_deferred(void) +{ + int i; + + for (i = 0; i < board_get_usb_pd_port_count(); ++i) { + if (frs_flag[i] & FLAG_FRS_VBUS_VALID_FALL) { + atomic_clear_bits(&frs_flag[i], + FLAG_FRS_RX_SIGNALLED | + FLAG_FRS_VBUS_VALID_FALL); + /* If the FRS gets enabled again, do not disable it. */ + if (!(frs_flag[i] & FLAG_FRS_ENABLED)) + board_rt1718s_set_frs_enable(i, 0); + } + } +} +DECLARE_DEFERRED(frs_gpio_disable_deferred); + void rt1718s_vendor_defined_alert(int port) { int rv, value; @@ -378,11 +410,47 @@ void rt1718s_vendor_defined_alert(int port) return; if ((int1 & RT1718S_RT_INT1_INT_RX_FRS)) { + atomic_or(&frs_flag[port], FLAG_FRS_RX_SIGNALLED); + /* notify TCPM we got FRS signal */ pd_got_frs_signal(port); + } + + if ((int1 & RT1718S_RT_INT1_INT_VBUS_FRS_LOW)) { + /* + * VBUS_FRS_LOW alert could be sent multiple times, + * filter it here. + */ + if (!(frs_flag[port] & FLAG_FRS_VBUS_VALID_FALL)) { + atomic_or(&frs_flag[port], + FLAG_FRS_VBUS_VALID_FALL); + /* + * b/223086905:comment8&comment17 + * We deferred the FRS disable + * (called to rt1718s_set_frs_enable()), now we + * can disable it after the VBUS fell. + */ + rt1718s_set_frs_enable(port, 0); + /* + * b/228422539:comment4 + * PPC HL5099 (pin-compatible to NX20P3483) + * suggested FRS gpio should be disabled after + * the SRC gpio enabled for 5ms to prevent the + * PPC from stopping sourcing the VBUS. + * Thought this is a workaround for HL5099, but + * it shouldn't affect other PPC chips since + * the DUT started sourcing the partner already. + */ + hook_call_deferred( + &frs_gpio_disable_deferred_data, + RT1718S_FRS_DIS_DELAY); + } + } + /* ignore other interrupts for faster frs handling */ + if (int1 & (RT1718S_RT_INT1_INT_RX_FRS | + RT1718S_RT_INT1_INT_VBUS_FRS_LOW)) { tcpc_write16(port, TCPC_REG_ALERT, - TCPC_REG_ALERT_VENDOR_DEF); - /* ignore other interrupts for faster frs handling */ + TCPC_REG_ALERT_VENDOR_DEF); return; } } @@ -523,6 +591,12 @@ out: } #ifdef CONFIG_USB_PD_FRS + +__overridable int board_rt1718s_set_frs_enable(int port, int enable) +{ + return EC_SUCCESS; +} + int rt1718s_set_frs_enable(int port, int enable) { /* @@ -532,15 +606,38 @@ int rt1718s_set_frs_enable(int port, int enable) int frs_ctrl2 = 0x10, vbus_ctrl_en = 0x3F; if (enable) { + atomic_or(&frs_flag[port], FLAG_FRS_ENABLED); frs_ctrl2 |= RT1718S_FRS_CTRL2_RX_FRS_EN; frs_ctrl2 |= RT1718S_FRS_CTRL2_VBUS_FRS_EN; vbus_ctrl_en |= RT1718S_VBUS_CTRL_EN_GPIO2_VBUS_PATH_EN; vbus_ctrl_en |= RT1718S_VBUS_CTRL_EN_GPIO1_VBUS_PATH_EN; + } else { + atomic_clear_bits(&frs_flag[port], FLAG_FRS_ENABLED); + if (FLAG_FRS_RX_SIGNALLED == + (frs_flag[port] & + (FLAG_FRS_RX_SIGNALLED | FLAG_FRS_VBUS_VALID_FALL))) { + /* + * Skip disable if we had only FRS_RX_SIGNALLED, and + * deferred the FRS register disable process in + * rt1718s_vendor_defined_alert. + */ + return EC_SUCCESS; + } } RETURN_ERROR(rt1718s_write8(port, RT1718S_FRS_CTRL2, frs_ctrl2)); RETURN_ERROR(rt1718s_write8(port, RT1718S_VBUS_CTRL_EN, vbus_ctrl_en)); + + /* + * b/223086905#comment13, b/228422539:comment4 + * If this function gets called when FRS RX signalled, then + * we'll deferred the GPIO disabled until the VBUS valid drop. So + * don't disable it here. + */ + if (enable || !(frs_flag[port] & FLAG_FRS_RX_SIGNALLED)) + RETURN_ERROR(board_rt1718s_set_frs_enable(port, enable)); + return EC_SUCCESS; } #endif |