summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--board/oak/board.c162
-rw-r--r--board/oak/board.h9
-rw-r--r--board/oak/gpio.inc2
-rw-r--r--board/oak/usb_pd_policy.c30
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)