diff options
-rw-r--r-- | board/oak/board.c | 162 | ||||
-rw-r--r-- | board/oak/board.h | 9 | ||||
-rw-r--r-- | board/oak/gpio.inc | 2 | ||||
-rw-r--r-- | board/oak/usb_pd_policy.c | 30 |
4 files changed, 198 insertions, 5 deletions
diff --git a/board/oak/board.c b/board/oak/board.c index e129a30053..192def312e 100644 --- a/board/oak/board.c +++ b/board/oak/board.c @@ -29,6 +29,7 @@ #include "switch.h" #include "task.h" #include "timer.h" +#include "usb_pd.h" #include "usb_pd_tcpm.h" #include "util.h" @@ -41,6 +42,9 @@ /* Default input current limit when VBUS is present */ #define DEFAULT_CURR_LIMIT 500 /* mA */ +/* Dispaly port hardware can connect to port 0, 1 or neither. */ +#define PD_PORT_NONE -1 + static void ap_reset_deferred(void) { /* Warm reset AP */ @@ -131,6 +135,20 @@ BUILD_ASSERT(ARRAY_SIZE(pi3usb9281_chips) == static int discharging_on_ac; /** + * Store the state of our USB data switches so that they can be restored + * after pericom reset. + */ +static int usb_switch_state[CONFIG_USB_PD_PORT_COUNT]; +static struct mutex usb_switch_lock[CONFIG_USB_PD_PORT_COUNT]; +static uint8_t ss_mux_mode[CONFIG_USB_PD_PORT_COUNT]; + +/** + * Store the current DP hardware route. + */ +static int dp_hw_port = PD_PORT_NONE; +static struct mutex dp_hw_lock; + +/** * Discharge battery when on AC power for factory test. */ int board_discharge_on_ac(int enable) @@ -281,6 +299,150 @@ void board_charge_manager_override_timeout(void) } DECLARE_DEFERRED(board_charge_manager_override_timeout); +/** + * Set type-C port USB2.0 switch state. + * + * @param port the type-C port to change + * @param setting enum usb_switch + */ +void board_set_usb_switches(int port, enum usb_switch setting) +{ + /* If switch is not charging, then return */ + if (setting == usb_switch_state[port]) + return; + + mutex_lock(&usb_switch_lock[port]); + if (setting != USB_SWITCH_RESTORE) + usb_switch_state[port] = setting; + pi3usb9281_set_switches(port, usb_switch_state[port]); + mutex_unlock(&usb_switch_lock[port]); +} + +/** + * Set USB3.0/DP mux. + * + * @param port the type-C port to change + * @param mux mux setting in enum typec_mux + * @param usb USB2.0 switch + * @param polarity 0 or 1 + */ +void board_set_usb_mux(int port, enum typec_mux mux, + enum usb_switch usb, int polarity) +{ + const uint8_t modes[] = { + [TYPEC_MUX_NONE] = PI3USB30532_MODE_POWERON, + [TYPEC_MUX_USB] = PI3USB30532_MODE_USB, + [TYPEC_MUX_DP] = PI3USB30532_MODE_DP, + [TYPEC_MUX_DOCK] = PI3USB30532_MODE_DP_USB, + }; + + /* Configure USB2.0 */ + board_set_usb_switches(port, usb); + + /* Configure superspeed lanes */ + ss_mux_mode[port] = modes[mux] | (polarity ? PI3USB30532_BIT_SWAP : 0); + pi3usb30532_set_switch(port, ss_mux_mode[port]); + CPRINTS("usb/dp mux: port(%d) typec_mux(%d) usb2(%d) polarity(%d)", + port, mux, usb, polarity); +} + +/** + * Get USB/DP mux state. + * + * @param port the type-C port to check + * @param dp_str return DP mux status in "DP1", "DP2" or NULL + * @param usb_str return USB mux status in "USB1", "USB2" or NULL + * + * @return superspeed lane enable or not. + */ +int board_get_usb_mux(int port, const char **dp_str, const char **usb_str) +{ + const char *dp, *usb; + int has_ss, has_dp, has_usb, polarity; + int mode = ss_mux_mode[port]; + + polarity = mode & PI3USB30532_BIT_SWAP; + dp = polarity ? "DP2" : "DP1"; + usb = polarity ? "USB2" : "USB1"; + + has_ss = mode & (PI3USB30532_BIT_DP | PI3USB30532_BIT_USB); + has_dp = mode & PI3USB30532_BIT_DP; + has_usb = mode & PI3USB30532_BIT_USB; + *dp_str = has_dp ? dp : NULL; + *usb_str = has_usb ? usb : NULL; + + return has_ss ? 1 : 0; +} + +static void hpd_irq_deferred(void) +{ + gpio_set_level(GPIO_USB_DP_HPD, 1); +} +DECLARE_DEFERRED(hpd_irq_deferred); + +/** + * Turn on DP hardware on type-C port. + */ +void board_typec_dp_on(int port) +{ + mutex_lock(&dp_hw_lock); + + if (dp_hw_port != !port) { + /* Get control of DP hardware */ + dp_hw_port = port; + gpio_set_level(GPIO_DP_SWITCH_CTL, port); + + if (!gpio_get_level(GPIO_USB_DP_HPD)) { + gpio_set_level(GPIO_USB_DP_HPD, 1); + } else { + gpio_set_level(GPIO_USB_DP_HPD, 0); + hook_call_deferred(hpd_irq_deferred, + HPD_DSTREAM_DEBOUNCE_IRQ); + } + } + + mutex_unlock(&dp_hw_lock); +} + +/** + * Turn off a PD port's DP output. + */ +void board_typec_dp_off(int port, int *dp_flags) +{ + mutex_lock(&dp_hw_lock); + + if (dp_hw_port == !port) { + mutex_unlock(&dp_hw_lock); + return; + } + + dp_hw_port = PD_PORT_NONE; + gpio_set_level(GPIO_USB_DP_HPD, 0); + mutex_unlock(&dp_hw_lock); + + /* Enable the other port if its dp flag is on */ + if (dp_flags[!port] & DP_FLAGS_DP_ON) + board_typec_dp_on(!port); +} + +/** + * Set DP hotplug detect level. + */ +void board_typec_dp_set(int port, int level) +{ + mutex_lock(&dp_hw_lock); + + if (dp_hw_port == PD_PORT_NONE) { + dp_hw_port = port; + gpio_set_level(GPIO_DP_SWITCH_CTL, port); + } + + if (dp_hw_port == port) + gpio_set_level(GPIO_USB_DP_HPD, level); + + mutex_unlock(&dp_hw_lock); +} + #ifndef CONFIG_AP_WARM_RESET_INTERRUPT /* Using this hook if system doesn't have enough external line. */ static void check_ap_reset_second(void) diff --git a/board/oak/board.h b/board/oak/board.h index f10c6dabe7..5f9cc199b4 100644 --- a/board/oak/board.h +++ b/board/oak/board.h @@ -37,6 +37,7 @@ #define CONFIG_CHARGER_DISCHARGE_ON_AC #define CONFIG_CHARGER_V2 #define CONFIG_CHIPSET_MEDIATEK +#define CONFIG_CMD_TYPEC #define CONFIG_FORCE_CONSOLE_RESUME #undef CONFIG_HIBERNATE #define CONFIG_HOST_COMMAND_STATUS @@ -49,6 +50,7 @@ #define CONFIG_PMIC_FW_LONG_PRESS_TIMER #define CONFIG_POWER_BUTTON #define CONFIG_POWER_COMMON +#define CONFIG_USBC_SS_MUX #define CONFIG_USB_POWER_DELIVERY #define CONFIG_USB_PD_ALT_MODE #define CONFIG_USB_PD_ALT_MODE_DFP @@ -71,7 +73,7 @@ #undef CONFIG_UART_RX_DMA #undef DEFERRABLE_MAX_COUNT -#define DEFERRABLE_MAX_COUNT 10 +#define DEFERRABLE_MAX_COUNT 11 /* * Allow dangerous commands. @@ -161,6 +163,11 @@ int board_discharge_on_ac(int enable); /* Reset PD MCU */ void board_reset_pd_mcu(void); +/* Control type-C DP route and hotplug detect signal */ +void board_typec_dp_on(int port); +void board_typec_dp_off(int port, int *dp_flags); +void board_typec_dp_set(int port, int level); + #endif /* !__ASSEMBLER__ */ #endif /* __BOARD_H */ diff --git a/board/oak/gpio.inc b/board/oak/gpio.inc index 701e9c84cb..9b64561ec8 100644 --- a/board/oak/gpio.inc +++ b/board/oak/gpio.inc @@ -73,7 +73,7 @@ GPIO(USB_C1_5V_OUT, PIN(D, 10), GPIO_OUT_LOW) /* USBC port 1 5V */ GPIO(USB_C1_CHARGE_L, PIN(D, 11), GPIO_OUT_LOW) /* USBC port 1 charge */ GPIO(USB_PD_VBUS_WAKE, PIN(B, 15), GPIO_OUT_LOW) /* PD VBUS wake */ GPIO(USB_DP_HPD, PIN(F, 3), GPIO_OUT_LOW) -GPIO(DP_MUX_ENABLE, PIN(E, 6), GPIO_OUT_HIGH) +GPIO(DP_MUX_EN_L, PIN(E, 6), GPIO_OUT_LOW) GPIO(DP_SWITCH_CTL, PIN(E, 5), GPIO_OUT_LOW) #ifdef CONFIG_BOARD_OAK_REV_1 diff --git a/board/oak/usb_pd_policy.c b/board/oak/usb_pd_policy.c index cc7abc60d6..10665c08e9 100644 --- a/board/oak/usb_pd_policy.c +++ b/board/oak/usb_pd_policy.c @@ -234,7 +234,8 @@ static void svdm_safe_dp_mode(int port) { /* make DP interface safe until configure */ dp_flags[port] = 0; - /* board_set_usb_mux(port, TYPEC_MUX_NONE, pd_get_polarity(port)); */ + board_set_usb_mux(port, TYPEC_MUX_NONE, + USB_SWITCH_CONNECT, pd_get_polarity(port)); } static int svdm_enter_dp_mode(int port, uint32_t mode_caps) @@ -267,7 +268,8 @@ static int svdm_dp_status(int port, uint32_t *payload) static int svdm_dp_config(int port, uint32_t *payload) { int opos = pd_alt_mode(port, USB_SID_DISPLAYPORT); - /* board_set_usb_mux(port, TYPEC_MUX_DP, pd_get_polarity(port)); */ + board_set_usb_mux(port, TYPEC_MUX_DP, + USB_SWITCH_CONNECT, pd_get_polarity(port)); payload[0] = VDO(USB_SID_DISPLAYPORT, 1, CMD_DP_CONFIG | VDO_OPOS(opos)); payload[1] = VDO_DP_CFG(MODE_DP_PIN_E, /* pin mode */ @@ -281,10 +283,32 @@ static void svdm_dp_post_config(int port) dp_flags[port] |= DP_FLAGS_DP_ON; if (!(dp_flags[port] & DP_FLAGS_HPD_HI_PENDING)) return; + board_typec_dp_set(port, 1); } static int svdm_dp_attention(int port, uint32_t *payload) { + int cur_lvl; + int lvl = PD_VDO_DPSTS_HPD_LVL(payload[1]); + int irq = PD_VDO_DPSTS_HPD_IRQ(payload[1]); + + cur_lvl = gpio_get_level(GPIO_USB_DP_HPD); + + /* Its initial DP status message prior to config */ + if (!(dp_flags[port] & DP_FLAGS_DP_ON)) { + if (lvl) + dp_flags[port] |= DP_FLAGS_HPD_HI_PENDING; + return 1; + } + + if (irq & cur_lvl) { + board_typec_dp_on(port); + } else if (irq & !cur_lvl) { + CPRINTF("ERR:HPD:IRQ&LOW\n"); + return 0; /* nak */ + } else { + board_typec_dp_set(port, lvl); + } /* ack */ return 1; } @@ -292,7 +316,7 @@ static int svdm_dp_attention(int port, uint32_t *payload) static void svdm_exit_dp_mode(int port) { svdm_safe_dp_mode(port); - /* gpio_set_level(PORT_TO_HPD(port), 0); */ + board_typec_dp_off(port, dp_flags); } static int svdm_enter_gfu_mode(int port, uint32_t mode_caps) |