Brandon Breitenstein has uploaded this change for review.

View Change

soc/intel/tigerlake: Add code for early tcss

In order for USB Type-C to be detected prior to loading Kernel
PMC IPC driver is needed to communicate with PMC in order to
correctly set the USB Mux settings. This patch is adding in
support for early detection of both USB and Display Port.

BUG=b:141608957
BRANCH=NONE
TEST: built and booted TGL U RVP

Change-Id: I58e66f21210d565fb8145d140d2fc7febecdd21a
Signed-off-by: Brandon Breitenstein <brandon.breitenstein@intel.com>
---
M src/soc/intel/tigerlake/Kconfig
M src/soc/intel/tigerlake/Makefile.inc
M src/soc/intel/tigerlake/chip.c
A src/soc/intel/tigerlake/early_tcss.c
A src/soc/intel/tigerlake/include/soc/early_tcss.h
5 files changed, 266 insertions(+), 0 deletions(-)

git pull ssh://review.coreboot.org:29418/coreboot refs/changes/79/42079/1
diff --git a/src/soc/intel/tigerlake/Kconfig b/src/soc/intel/tigerlake/Kconfig
index fbf56b4..55c06b1 100644
--- a/src/soc/intel/tigerlake/Kconfig
+++ b/src/soc/intel/tigerlake/Kconfig
@@ -208,4 +208,10 @@
config PRERAM_CBMEM_CONSOLE_SIZE
hex
default 0xe00
+
+config EARLY_TCSS
+ bool "Enable early TCSS"
+ help
+ Enable devices to be detected over Type-C ports during boot.
+
endif
diff --git a/src/soc/intel/tigerlake/Makefile.inc b/src/soc/intel/tigerlake/Makefile.inc
index c4f71c7..5e60114 100644
--- a/src/soc/intel/tigerlake/Makefile.inc
+++ b/src/soc/intel/tigerlake/Makefile.inc
@@ -31,6 +31,7 @@
ramstage-y += acpi.c
ramstage-y += chip.c
ramstage-y += cpu.c
+ramstage-$(CONFIG_EARLY_TCSS) += early_tcss.c
ramstage-y += elog.c
ramstage-y += espi.c
ramstage-y += finalize.c
diff --git a/src/soc/intel/tigerlake/chip.c b/src/soc/intel/tigerlake/chip.c
index 00db2a4..3da5d9a 100644
--- a/src/soc/intel/tigerlake/chip.c
+++ b/src/soc/intel/tigerlake/chip.c
@@ -10,6 +10,7 @@
#include <intelblocks/itss.h>
#include <intelblocks/xdci.h>
#include <romstage_handoff.h>
+#include <soc/early_tcss.h>
#include <soc/intel/common/vbt.h>
#include <soc/itss.h>
#include <soc/pci_devs.h>
@@ -129,6 +130,10 @@
* default policy that doesn't honor boards' requirements. */
itss_snapshot_irq_polarities(GPIO_IRQ_START, GPIO_IRQ_END);

+ /* Check for early TCSS and connect devices */
+ if (CONFIG(EARLY_TCSS))
+ early_tcss_enable();
+
/* Perform silicon specific init. */
fsp_silicon_init(romstage_handoff_is_resume());

diff --git a/src/soc/intel/tigerlake/early_tcss.c b/src/soc/intel/tigerlake/early_tcss.c
new file mode 100644
index 0000000..942d490
--- /dev/null
+++ b/src/soc/intel/tigerlake/early_tcss.c
@@ -0,0 +1,206 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2020 The coreboot project Authors.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <bootstate.h>
+#include <console/console.h>
+#include <device/pci.h>
+#include <ec/google/chromeec/ec.h>
+#include <intelblocks/pmclib.h>
+#include <soc/early_tcss.h>
+#include <soc/pci_devs.h>
+#include <stdlib.h>
+
+/* send the ipc_command */
+static int send_ipc_command(uint32_t cmd, uint8_t *tcss_req, int req_size,
+ struct pmc_ipc_buffer *rbuf)
+{
+ struct pmc_ipc_buffer *wbuf = malloc(sizeof(*tcss_req));
+
+ /* copy the tcss_req into a buffer for ipc command */
+ memcpy(wbuf, tcss_req, req_size);
+
+ return pmc_send_ipc_cmd(cmd, wbuf, rbuf);
+}
+
+static int send_pmc_connect_request(struct tcss_mux mux_data,
+ struct pmc_ipc_buffer *rbuf)
+{
+ union pmc_ipc_cmd tcss_cmd = { 0 };
+ uint8_t tcss_req[PMC_IPC_CONN_REQ_SIZE] = { 0 };
+
+ tcss_cmd.cmd = PMC_IPC_USBC_CMD_ID;
+ tcss_cmd.subcmd = PMC_IPC_USBC_SUBCMD_ID;
+
+ tcss_req[0] = PMC_IPC_TCSS_CONN_REQ_RES | /* Usage */
+ mux_data.usb3_port << 4;
+ tcss_req[1] = mux_data.usb2_port |
+ mux_data.ufp << 4 | /* 1=UFP/0=DFP */
+ mux_data.polarity << 5 | /* ORI-HSL */
+ mux_data.polarity << 6 | /* ORI-SBU */
+ mux_data.acc << 7;
+ printk(BIOS_DEBUG, "tcss_req[0]-> 0x%x\n"
+ "tcss_req[1]-> 0x%x\n\t", tcss_req[0], tcss_req[1]);
+
+ tcss_cmd.len = PMC_IPC_CONN_REQ_SIZE;
+
+ return send_ipc_command(tcss_cmd.cmd_reg, tcss_req, PMC_IPC_CONN_REQ_SIZE, rbuf);
+}
+
+static int send_pmc_safe_mode_request(struct tcss_mux mux_data,
+ struct pmc_ipc_buffer *rbuf)
+{
+ union pmc_ipc_cmd tcss_cmd = { 0 };
+ uint8_t tcss_req[PMC_IPC_SAFE_REQ_SIZE] = { 0 };
+
+ tcss_cmd.cmd = PMC_IPC_USBC_CMD_ID;
+ tcss_cmd.subcmd = PMC_IPC_USBC_SUBCMD_ID;
+
+ tcss_req[0] = PMC_IPC_TCSS_SAFE_MODE_REQ_RES |
+ mux_data.usb3_port << 4;
+ printk(BIOS_DEBUG, "tcss_req[0]-> 0x%x\n", tcss_req[0]);
+
+ tcss_cmd.cmd.len = PMC_IPC_SAFE_REQ_SIZE;
+
+ return send_ipc_command(tcss_cmd.cmd_reg, tcss_req, PMC_IPC_SAFE_REQ_SIZE, rbuf);
+}
+
+static int send_pmc_alt_mode_request(struct tcss_mux mux_data,
+ struct pmc_ipc_buffer *rbuf)
+{
+ union pmc_ipc_cmd tcss_cmd = { 0 };
+ uint8_t tcss_req[PMC_IPC_ALT_REQ_SIZE] = { 0 };
+
+ tcss_cmd.cmd = PMC_IPC_USBC_CMD_ID;
+ tcss_cmd.subcmd = PMC_IPC_USBC_SUBCMD_ID;
+
+ tcss_req[0] = PMC_IPC_TCSS_ALTMODE_REQ_RES | mux_data.usb3_port << 4;
+ tcss_req[1] |= PMC_IPC_DP_MODE;
+
+ tcss_req[4] = mux_data.polarity << 1 |
+ mux_data.cable << 2 |
+ mux_data.ufp << 3 |
+ mux_data.polarity << 4 |
+ mux_data.polarity << 5;
+
+ if (mux_data.dp_mode <= MODE_DP_PIN_F) {
+ switch (mux_data.dp_mode) {
+ case MODE_DP_PIN_A:
+ tcss_req[5] = 0;
+ break;
+ case MODE_DP_PIN_B:
+ tcss_req[5] = 1;
+ break;
+ case MODE_DP_PIN_C:
+ tcss_req[5] = 2;
+ break;
+ case MODE_DP_PIN_D:
+ tcss_req[5] = 3;
+ break;
+ case MODE_DP_PIN_E:
+ tcss_req[5] = 4;
+ break;
+ case MODE_DP_PIN_F:
+ tcss_req[5] = 5;
+ break;
+ }
+ }
+
+ tcss_req[5] |= mux_data.hpd_lvl << 6;
+ printk(BIOS_DEBUG, "tcss_req[0]-> 0x%x\n"
+ "tcss_req[1]-> 0x%x\n"
+ "tcss_req[4]-> 0x%x\n"
+ "tcss_req[5]-> 0x%x\n\t",
+ tcss_req[0], tcss_req[1], tcss_req[4], tcss_req[5]);
+
+ tcss_cmd.len = PMC_IPC_ALT_REQ_SIZE;
+
+ return send_ipc_command(tcss_cmd.cmd_reg, tcss_req, PMC_IPC_ALT_REQ_SIZE, rbuf);
+}
+
+static void update_tcss_mux(int port, struct tcss_mux mux_data)
+{
+ struct pmc_ipc_buffer *rbuf = malloc(sizeof(*rbuf));
+ int ret = 0;
+
+ /* Check if the mux has a USB device */
+ if (mux_data.usb) {
+ ret = send_pmc_connect_request(mux_data, rbuf);
+ if (ret) {
+ printk(BIOS_ERR, "Port %d connect request failed\n", port);
+ return;
+ }
+ }
+
+ /* check if mux has a DP device */
+ if (mux_data.dp) {
+
+ if (!mux_data.usb) {
+ ret = send_pmc_connect_request(mux_data, rbuf);
+ if (ret) {
+ printk(BIOS_ERR, "Port %d connect request failed\n", port);
+ return;
+ }
+ }
+ ret = send_pmc_safe_mode_request(mux_data, rbuf);
+ if (ret) {
+ printk(BIOS_ERR, "Port %d safe mode request failed\n", port);
+ return;
+ }
+
+ ret = send_pmc_alt_mode_request(mux_data, rbuf);
+ }
+
+ if (ret)
+ printk(BIOS_ERR, "Port %d mux set failed with error %d\n", port, ret);
+}
+
+void early_tcss_enable(void)
+{
+ uint8_t num_ports;
+ int ret, i;
+
+ ret = google_chromeec_get_num_pd_ports(&num_ports);
+ if (ret < 0)
+ return;
+
+ for (i = 0; i < num_ports; i++) {
+ uint8_t port_map, mux_flags;
+ struct tcss_mux mux_data;
+
+ ret = google_chromeec_usb_get_pd_mux_info(i, &mux_flags);
+ if (ret < 0)
+ continue;
+
+ ret = google_chromeec_pd_get_port_info(i, &port_map);
+ if (ret < 0)
+ continue;
+
+ ret = google_chromeec_usb_pd_control(i, &mux_data.ufp, &mux_data.acc,
+ &mux_data.dp_mode);
+ if (ret < 0)
+ continue;
+
+ mux_data.usb = !!(mux_flags & USB_PD_CTRL_USB_ENABLED);
+ mux_data.dp = !!(mux_flags & USB_PD_CTRL_DP_ENABLED);
+ mux_data.cable = !!(mux_flags & USB_PD_MUX_TBT_ACTIVE_CABLE);
+ mux_data.polarity = !!(mux_flags & USB_PD_CTRL_POLARITY_INVERTED);
+ mux_data.hpd_irq = !!(mux_flags & USB_PD_CTRL_HPD_IRQ);
+ mux_data.hpd_lvl = !!(mux_flags & USB_PD_CTRL_HPD_LVL);
+ mux_data.usb2_port = port_map & 0x0F;
+ mux_data.usb3_port = (port_map & 0xF0) >> 4;
+
+ printk(BIOS_DEBUG, "Port %d mux=0x%x\n"
+ "USB2 port = %x\n"
+ "USB3 port = %x\n"
+ "ufp = %d dbg_acc=%d\n",
+ i, (unsigned int)mux_flags, mux_data.usb2_port,
+ mux_data.usb3_port, mux_data.ufp, mux_data.acc);
+
+ update_tcss_mux(i, mux_data);
+ }
+}
diff --git a/src/soc/intel/tigerlake/include/soc/early_tcss.h b/src/soc/intel/tigerlake/include/soc/early_tcss.h
new file mode 100644
index 0000000..29b7cdd
--- /dev/null
+++ b/src/soc/intel/tigerlake/include/soc/early_tcss.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/* PMC IPC related offsets and commands */
+#define PMC_IPC_USBC_CMD_ID 0xA7
+#define PMC_IPC_USBC_SUBCMD_ID 0x0
+#define PMC_IPC_CMD 0x0
+#define PMC_IPC_TCSS_CONN_REQ_RES 0x0
+#define PMC_IPC_TCSS_SAFE_MODE_REQ_RES 0x2
+#define PMC_IPC_TCSS_ALTMODE_REQ_RES 0x3
+#define PMC_IPC_CONN_REQ_SIZE 2
+#define PMC_IPC_ALT_REQ_SIZE 8
+#define PMC_IPC_SAFE_REQ_SIZE 1
+#define PMC_IPC_DP_MODE BIT(4)
+
+/* connection modes for pmc */
+enum pmc_ipc_conn_mode {
+ PMC_IPC_TCSS_DISCONNECT_MODE,
+ PMC_IPC_TCSS_USB_MODE,
+ PMC_IPC_TCSS_ALTERNATE_MODE,
+ PMC_IPC_TCSS_SAFE_MODE,
+ PMC_IPC_TCSS_HPD_MODE,
+ PMC_IPC_TCSS_TOTAL_MODES,
+};
+
+/* DP Mode pin definitions */
+#define MODE_DP_PIN_A BIT(0)
+#define MODE_DP_PIN_B BIT(1)
+#define MODE_DP_PIN_C BIT(2)
+#define MODE_DP_PIN_D BIT(3)
+#define MODE_DP_PIN_E BIT(4)
+#define MODE_DP_PIN_F BIT(5)
+
+/* struct to hold all tcss_mux related variables */
+struct tcss_mux {
+ bool dp; /* DP connected */
+ bool usb; /* USB connected */
+ bool cable; /* Activ/Passive Cable */
+ bool polarity; /* polarity of connected device */
+ bool hpd_lvl; /* HPD Level assert */
+ bool hpd_irq; /* HPD IRQ assert */
+ bool ufp;
+ bool acc;
+ uint8_t dp_mode; /* DP Operation Mode */
+ uint8_t usb3_port; /* USB2 Port Number */
+ uint8_t usb2_port; /* USB3 Port Number */
+};
+
+void early_tcss_enable(void);

To view, visit change 42079. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-Change-Id: I58e66f21210d565fb8145d140d2fc7febecdd21a
Gerrit-Change-Number: 42079
Gerrit-PatchSet: 1
Gerrit-Owner: Brandon Breitenstein <brandon.breitenstein@intel.com>
Gerrit-Reviewer: Martin Roth <martinroth@google.com>
Gerrit-Reviewer: Patrick Georgi <pgeorgi@google.com>
Gerrit-Reviewer: Patrick Rudolph <siro@das-labor.org>
Gerrit-MessageType: newchange