summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRong Chang <rongchang@chromium.org>2015-06-11 20:38:21 +0800
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2015-06-17 20:24:18 +0000
commitdd1987051b473e74dab3490921d0cc77c76fcac3 (patch)
tree0e4f28d2302d3dfc33b67082139ef814068a43c3
parent4982391dc27960c5af4e1814d627154a8aac3e29 (diff)
downloadchrome-ec-dd1987051b473e74dab3490921d0cc77c76fcac3.tar.gz
oak: enable USBC superspeed mux
This change enables USB3/DP superspeed mux. Oak's two type-C ports share one DP hardware. When both ports connect to DP output device, only the first DP signal will be routed to SoC. On exit dp mode, oak sends HPD again if the other port's DP flag is on. BRANCH=none BUG=chrome-os-partner:41404 TEST=none Change-Id: I7eebc0b2354f93d7421bf83796294a6b2acf4c3b Signed-off-by: Rong Chang <rongchang@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/277000 Reviewed-by: Alec Berg <alecaberg@chromium.org>
-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)