summaryrefslogtreecommitdiff
path: root/resources/coreboot/haswell/patches/0005-sb-intel-lynxpoint-Add-native-USB-init.patch
diff options
context:
space:
mode:
authorLeah Rowe <leah@libreboot.org>2023-03-18 00:36:27 +0000
committerLeah Rowe <leah@libreboot.org>2023-03-18 00:55:10 +0000
commit548872ce8e84fe10d52417acab9b3cf886821386 (patch)
treeb549ad51c9c693a323f0f8edc9a11eab92220013 /resources/coreboot/haswell/patches/0005-sb-intel-lynxpoint-Add-native-USB-init.patch
parenta942bd6590dd450140fc0af8549ca470a065adf5 (diff)
haswell boards: use libre mrc.bin replacement
courtesy of Angel Pons from the coreboot project this uses the following patch set from gerrit, as yet unmerged (in coreboot master) on this date: https://review.coreboot.org/c/coreboot/+/64198/5 logic for downloading mrc blobs has been deleted from lbmk, as this is now completely obsolete (for haswell boards) if other platforms are added later that need mrc.bin, then logic will be re-added again for that
Diffstat (limited to 'resources/coreboot/haswell/patches/0005-sb-intel-lynxpoint-Add-native-USB-init.patch')
-rw-r--r--resources/coreboot/haswell/patches/0005-sb-intel-lynxpoint-Add-native-USB-init.patch783
1 files changed, 783 insertions, 0 deletions
diff --git a/resources/coreboot/haswell/patches/0005-sb-intel-lynxpoint-Add-native-USB-init.patch b/resources/coreboot/haswell/patches/0005-sb-intel-lynxpoint-Add-native-USB-init.patch
new file mode 100644
index 00000000..d9c2570b
--- /dev/null
+++ b/resources/coreboot/haswell/patches/0005-sb-intel-lynxpoint-Add-native-USB-init.patch
@@ -0,0 +1,783 @@
+From 9bfb8614dbf1d9800ef8251cb3d839bcdbe5577f Mon Sep 17 00:00:00 2001
+From: Angel Pons <th3fanbus@gmail.com>
+Date: Fri, 6 May 2022 23:17:39 +0200
+Subject: [PATCH 05/26] sb/intel/lynxpoint: Add native USB init
+
+Implement native USB initialisation for Lynx Point. This is only needed
+when MRC.bin is not used.
+
+TO DO: Figure out how to deal with the FIXME's and TODO's lying around.
+
+Change-Id: Ie0fbeeca7b1ca1557173772d733fd2fa27703373
+Signed-off-by: Angel Pons <th3fanbus@gmail.com>
+---
+ .../haswell/native_raminit/raminit_native.c | 3 +
+ src/southbridge/intel/lynxpoint/Makefile.inc | 2 +-
+ src/southbridge/intel/lynxpoint/early_usb.c | 11 -
+ .../intel/lynxpoint/early_usb_native.c | 584 ++++++++++++++++++
+ src/southbridge/intel/lynxpoint/pch.h | 49 ++
+ 5 files changed, 637 insertions(+), 12 deletions(-)
+ create mode 100644 src/southbridge/intel/lynxpoint/early_usb_native.c
+
+diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.c b/src/northbridge/intel/haswell/native_raminit/raminit_native.c
+index 6a002548c1..ef61d4ee09 100644
+--- a/src/northbridge/intel/haswell/native_raminit/raminit_native.c
++++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.c
+@@ -5,6 +5,7 @@
+ #include <northbridge/intel/haswell/haswell.h>
+ #include <northbridge/intel/haswell/raminit.h>
+ #include <southbridge/intel/lynxpoint/me.h>
++#include <southbridge/intel/lynxpoint/pch.h>
+ #include <types.h>
+
+ static bool early_init_native(int s3resume)
+@@ -15,6 +16,8 @@ static bool early_init_native(int s3resume)
+ /** TODO: CPU replacement check must be skipped in warm boots and S3 resumes **/
+ const bool cpu_replaced = !s3resume && intel_early_me_cpu_replacement_check();
+
++ early_usb_init();
++
+ if (!CONFIG(INTEL_LYNXPOINT_LP))
+ dmi_early_init();
+
+diff --git a/src/southbridge/intel/lynxpoint/Makefile.inc b/src/southbridge/intel/lynxpoint/Makefile.inc
+index b8503ac8bc..0e1f2fe4eb 100644
+--- a/src/southbridge/intel/lynxpoint/Makefile.inc
++++ b/src/southbridge/intel/lynxpoint/Makefile.inc
+@@ -37,7 +37,7 @@ bootblock-y += early_pch.c
+ romstage-y += early_usb.c early_me.c me_status.c early_pch.c
+ romstage-y += pmutil.c
+
+-romstage-$(CONFIG_USE_NATIVE_RAMINIT) += early_pch_native.c
++romstage-$(CONFIG_USE_NATIVE_RAMINIT) += early_pch_native.c early_usb_native.c iobp.c
+
+ ifeq ($(CONFIG_INTEL_LYNXPOINT_LP),y)
+ romstage-y += lp_gpio.c
+diff --git a/src/southbridge/intel/lynxpoint/early_usb.c b/src/southbridge/intel/lynxpoint/early_usb.c
+index a753681ce0..52e8ac17f8 100644
+--- a/src/southbridge/intel/lynxpoint/early_usb.c
++++ b/src/southbridge/intel/lynxpoint/early_usb.c
+@@ -4,17 +4,6 @@
+ #include <device/pci_def.h>
+ #include "pch.h"
+
+-/* HCD_INDEX == 2 selects 0:1a.0 (PCH_EHCI2), any other index
+- * selects 0:1d.0 (PCH_EHCI1) for usbdebug use.
+- */
+-#if CONFIG_USBDEBUG_HCD_INDEX != 2
+-#define PCH_EHCI1_TEMP_BAR0 CONFIG_EHCI_BAR
+-#define PCH_EHCI2_TEMP_BAR0 (PCH_EHCI1_TEMP_BAR0 + 0x400)
+-#else
+-#define PCH_EHCI2_TEMP_BAR0 CONFIG_EHCI_BAR
+-#define PCH_EHCI1_TEMP_BAR0 (PCH_EHCI2_TEMP_BAR0 + 0x400)
+-#endif
+-
+ /*
+ * Setup USB controller MMIO BAR to prevent the
+ * reference code from resetting the controller.
+diff --git a/src/southbridge/intel/lynxpoint/early_usb_native.c b/src/southbridge/intel/lynxpoint/early_usb_native.c
+new file mode 100644
+index 0000000000..cb6f6ee8e6
+--- /dev/null
++++ b/src/southbridge/intel/lynxpoint/early_usb_native.c
+@@ -0,0 +1,584 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++
++#include <console/console.h>
++#include <delay.h>
++#include <device/mmio.h>
++#include <device/pci_def.h>
++#include <device/pci_ops.h>
++#include <northbridge/intel/haswell/haswell.h>
++#include <northbridge/intel/haswell/raminit.h>
++#include <southbridge/intel/lynxpoint/iobp.h>
++#include <southbridge/intel/lynxpoint/pch.h>
++#include <timer.h>
++#include <types.h>
++
++static unsigned int is_usbr_enabled(void)
++{
++ return !!(pci_read_config32(PCH_XHCI_DEV, XHCI_USB3FUS) & BIT(5));
++}
++
++static char *const xhci_bar = (char *)PCH_XHCI_TEMP_BAR0;
++
++static void ehci_hcs_init(const pci_devfn_t dev, const uintptr_t ehci_bar)
++{
++ pci_write_config32(dev, PCI_BASE_ADDRESS_0, ehci_bar);
++
++ /** FIXME: Determine whether Bus Master is required (or clean it up afterwards) **/
++ pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
++
++ char *const mem_bar = (char *)ehci_bar;
++
++ /**
++ * Shared EHCI/XHCI ports w/a.
++ * This step is required when some of the ports are routed to EHCI
++ * and other ports are routed XHCI at the same time.
++ *
++ * FIXME: Under which conditions should this be done?
++ */
++ pci_and_config16(dev, 0x78, ~0x03);
++
++ /* Skip reset if usbdebug is enabled */
++ if (!CONFIG(USBDEBUG_IN_PRE_RAM))
++ setbits32(mem_bar + EHCI_USB_CMD, EHCI_USB_CMD_HCRESET);
++
++ /* 2: Configure number of controllers and ports */
++ pci_or_config16(dev, EHCI_ACCESS_CNTL, ACCESS_CNTL_ENABLE);
++ clrsetbits32(mem_bar + EHCI_HCS_PARAMS, 0xf << 12, 0);
++ clrsetbits32(mem_bar + EHCI_HCS_PARAMS, 0xf << 0, 2 + is_usbr_enabled());
++ pci_and_config16(dev, EHCI_ACCESS_CNTL, ~ACCESS_CNTL_ENABLE);
++
++ pci_or_config16(dev, 0x78, BIT(2));
++ pci_or_config16(dev, 0x7c, BIT(14) | BIT(7));
++ pci_update_config32(dev, 0x8c, ~(0xf << 8), (4 << 8));
++ pci_update_config32(dev, 0x8c, ~BIT(26), BIT(17));
++}
++
++static inline unsigned int physical_port_count(void)
++{
++ return MAX_USB2_PORTS;
++}
++
++static unsigned int hs_port_count(void)
++{
++ /** TODO: Apparently, WPT-LP has 10 USB2 ports **/
++ if (CONFIG(INTEL_LYNXPOINT_LP))
++ return 8;
++
++ switch ((pci_read_config32(PCH_XHCI_DEV, XHCI_USB3FUS) >> 1) & 3) {
++ case 3:
++ return 8;
++ case 2:
++ return 10;
++ case 1:
++ return 12;
++ case 0:
++ default:
++ return 14;
++ }
++}
++
++static unsigned int ss_port_count(void)
++{
++ if (CONFIG(INTEL_LYNXPOINT_LP))
++ return 4;
++
++ switch ((pci_read_config32(PCH_XHCI_DEV, XHCI_USB3FUS) >> 3) & 3) {
++ case 3:
++ return 0;
++ case 2:
++ return 2;
++ case 1:
++ return 4;
++ case 0:
++ default:
++ return 6;
++ }
++}
++
++static void common_ehci_hcs_init(void)
++{
++ const bool is_lp = CONFIG(INTEL_LYNXPOINT_LP);
++
++ ehci_hcs_init(PCH_EHCI1_DEV, PCH_EHCI1_TEMP_BAR0);
++ if (!is_lp)
++ ehci_hcs_init(PCH_EHCI2_DEV, PCH_EHCI2_TEMP_BAR0);
++
++ pch_iobp_update(0xe5007f04, 0, 0x00004481);
++
++ for (unsigned int port = 0; port < physical_port_count(); port++)
++ pch_iobp_update(0xe500400f + port * 0x100, ~(1 << 0), 0 << 0);
++
++ pch_iobp_update(0xe5007f14, ~(3 << 19), (3 << 19));
++
++ if (is_lp)
++ pch_iobp_update(0xe5007f02, ~(3 << 22), (0 << 22));
++}
++
++static void xhci_open_memory_space(void)
++{
++ /** FIXME: Determine whether Bus Master is required (or clean it up afterwards) **/
++ pci_write_config32(PCH_XHCI_DEV, PCI_BASE_ADDRESS_0, (uintptr_t)xhci_bar);
++ pci_or_config16(PCH_XHCI_DEV, PCI_COMMAND, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
++}
++
++static void xhci_close_memory_space(void)
++{
++ pci_and_config16(PCH_XHCI_DEV, PCI_COMMAND, ~(PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY));
++ pci_write_config32(PCH_XHCI_DEV, PCI_BASE_ADDRESS_0, 0);
++}
++
++static void common_xhci_hc_init(void)
++{
++ const bool is_lp = CONFIG(INTEL_LYNXPOINT_LP);
++
++ if (!is_lp) {
++ const unsigned int max_ports = 15 + ss_port_count();
++ clrsetbits32(xhci_bar + XHCI_HCS_PARAMS_1, 0xf << 28, max_ports << 28);
++ }
++
++ clrsetbits32(xhci_bar + XHCI_HCS_PARAMS_3, 0xffff << 16 | 0xff, 0x200 << 16 | 0x0a);
++ clrsetbits32(xhci_bar + XHCI_HCC_PARAMS, BIT(5), BIT(10) | BIT(9));
++
++ if (!is_lp)
++ clrsetbits32(xhci_bar + 0x8008, BIT(19), 0);
++
++ if (is_lp)
++ clrsetbits32(xhci_bar + 0x8058, BIT(8), BIT(16));
++ else
++ clrsetbits32(xhci_bar + 0x8058, BIT(8), BIT(16) | BIT(20));
++
++ clrsetbits32(xhci_bar + 0x8060, 0, BIT(25) | BIT(18));
++ clrsetbits32(xhci_bar + 0x8090, 0, BIT(14) | BIT(8));
++ clrsetbits32(xhci_bar + 0x8094, 0, BIT(23) | BIT(21) | BIT(14));
++ clrsetbits32(xhci_bar + 0x80e0, BIT(16), BIT(6));
++ clrsetbits32(xhci_bar + 0x80ec, (7 << 12) | (7 << 9), (0 << 12) | (6 << 9));
++ clrsetbits32(xhci_bar + 0x80f0, BIT(20), 0);
++
++ if (is_lp)
++ clrsetbits32(xhci_bar + 0x80fc, 0, BIT(25));
++
++ if (is_lp)
++ clrsetbits32(xhci_bar + 0x8110, BIT(8) | BIT(2), BIT(20) | BIT(11));
++ else
++ clrsetbits32(xhci_bar + 0x8110, BIT(2), BIT(20) | BIT(11));
++
++ if (is_lp)
++ write32(xhci_bar + 0x8140, 0xff00f03c);
++ else
++ write32(xhci_bar + 0x8140, 0xff03c132);
++
++ if (is_lp)
++ clrsetbits32(xhci_bar + 0x8154, BIT(21), BIT(13));
++ else
++ clrsetbits32(xhci_bar + 0x8154, BIT(21) | BIT(13), 0);
++
++ clrsetbits32(xhci_bar + 0x8154, BIT(3), 0);
++
++ if (is_lp) {
++ clrsetbits32(xhci_bar + 0x8164, 0, BIT(1) | BIT(0));
++ write32(xhci_bar + 0x8174, 0x01400c0a);
++ write32(xhci_bar + 0x817c, 0x033200a3);
++ write32(xhci_bar + 0x8180, 0x00cb0028);
++ write32(xhci_bar + 0x8184, 0x0064001e);
++ }
++
++ /*
++ * Note: Register at offset 0x44 is 32-bit, but bit 31 is write-once.
++ * We use these weird partial accesses here to avoid locking bit 31.
++ */
++ pci_or_config16(PCH_XHCI_DEV, 0x44, BIT(15) | BIT(14) | BIT(10) | BIT(0));
++ pci_or_config8(PCH_XHCI_DEV, 0x44 + 2, 0x0f);
++
++ /* LPT-LP >= B0 */
++ if (is_lp)
++ clrsetbits32(xhci_bar + 0x8188, 0, BIT(26) | BIT(24));
++
++ /* LPT-H >= C0 */
++ if (!is_lp)
++ clrsetbits32(xhci_bar + 0x8188, 0, BIT(24));
++}
++
++static inline bool is_mem_sr(void)
++{
++ return pci_read_config16(PCH_LPC_DEV, GEN_PMCON_2) & GEN_PMCON_2_MEM_SR;
++}
++
++static bool should_restore_xhci_smart_auto(void)
++{
++ if (!is_mem_sr())
++ return false;
++
++ return pci_read_config32(PCH_LPC_DEV, PMIR) & PMIR_XHCI_SMART_AUTO;
++}
++
++enum usb_port_route {
++ ROUTE_TO_EHCI,
++ ROUTE_TO_XHCI,
++};
++
++/* Returns whether port reset was successful */
++static bool reset_usb2_ports(const unsigned int ehci_ports)
++{
++ for (unsigned int port = 0; port < ehci_ports; port++) {
++ /* Initiate port reset for all USB2 ports */
++ clrsetbits32(
++ xhci_bar + XHCI_USB2_PORTSC(port),
++ XHCI_USB2_PORTSC_PED,
++ XHCI_USB2_PORTSC_PR);
++ }
++ /* Poll for port reset bit to be cleared or time out at 100ms */
++ struct stopwatch timer;
++ stopwatch_init_msecs_expire(&timer, 100);
++ uint32_t reg32;
++ do {
++ reg32 = 0;
++ for (unsigned int port = 0; port < ehci_ports; port++)
++ reg32 |= read32(xhci_bar + XHCI_USB2_PORTSC(port));
++
++ reg32 &= XHCI_USB2_PORTSC_PR;
++ if (!reg32) {
++ const long elapsed_time = stopwatch_duration_usecs(&timer);
++ printk(BIOS_DEBUG, "%s: took %lu usecs\n", __func__, elapsed_time);
++ return true;
++ }
++ /* Reference code has a 10 ms delay here, but a smaller delay works too */
++ udelay(100);
++ } while (!stopwatch_expired(&timer));
++ printk(BIOS_ERR, "%s: timed out\n", __func__);
++ return !reg32;
++}
++
++/* Returns whether warm reset was successful */
++static bool warm_reset_usb3_ports(const unsigned int xhci_ports)
++{
++ for (unsigned int port = 0; port < xhci_ports; port++) {
++ /* Initiate warm reset for all USB3 ports */
++ clrsetbits32(
++ xhci_bar + XHCI_USB3_PORTSC(port),
++ XHCI_USB3_PORTSC_PED,
++ XHCI_USB3_PORTSC_WPR);
++ }
++ /* Poll for port reset bit to be cleared or time out at 100ms */
++ struct stopwatch timer;
++ stopwatch_init_msecs_expire(&timer, 100);
++ uint32_t reg32;
++ do {
++ reg32 = 0;
++ for (unsigned int port = 0; port < xhci_ports; port++)
++ reg32 |= read32(xhci_bar + XHCI_USB3_PORTSC(port));
++
++ reg32 &= XHCI_USB3_PORTSC_PR;
++ if (!reg32) {
++ const long elapsed_time = stopwatch_duration_usecs(&timer);
++ printk(BIOS_DEBUG, "%s: took %lu usecs\n", __func__, elapsed_time);
++ return true;
++ }
++ /* Reference code has a 10 ms delay here, but a smaller delay works too */
++ udelay(100);
++ } while (!stopwatch_expired(&timer));
++ printk(BIOS_ERR, "%s: timed out\n", __func__);
++ return !reg32;
++}
++
++static void perform_xhci_ehci_switching_flow(const enum usb_port_route usb_route)
++{
++ const pci_devfn_t dev = PCH_XHCI_DEV;
++
++ const unsigned int ehci_ports = hs_port_count() + is_usbr_enabled();
++ const unsigned int xhci_ports = ss_port_count();
++
++ const uint32_t ehci_mask = BIT(ehci_ports) - 1;
++ const uint32_t xhci_mask = BIT(xhci_ports) - 1;
++
++ /** TODO: Handle USBr port? How, though? **/
++ pci_update_config32(dev, XHCI_USB2PRM, ~XHCI_USB2PR_HCSEL, ehci_mask);
++ pci_update_config32(dev, XHCI_USB3PRM, ~XHCI_USB3PR_SSEN, xhci_mask);
++
++ /*
++ * Workaround for USB2PR / USB3PR value not surviving warm reset.
++ * Restore USB Port Routing registers if OS HC Switch driver has been executed.
++ */
++ if (should_restore_xhci_smart_auto()) {
++ /** FIXME: Derive values from mainboard code instead? **/
++ pci_update_config32(dev, XHCI_USB2PR, ~XHCI_USB2PR_HCSEL, ehci_mask);
++ pci_update_config32(dev, XHCI_USB3PR, ~XHCI_USB3PR_SSEN, xhci_mask);
++ }
++
++ /* Later stages shouldn't need the value of this bit */
++ pci_and_config32(PCH_LPC_DEV, PMIR, ~PMIR_XHCI_SMART_AUTO);
++
++ /**
++ * FIXME: Things here depend on the chosen routing mode.
++ * For now, implement both functions.
++ */
++
++ /* Route to EHCI if xHCI disabled or auto mode */
++ if (usb_route == ROUTE_TO_EHCI) {
++ if (!reset_usb2_ports(ehci_ports))
++ printk(BIOS_ERR, "USB2 port reset timed out\n");
++
++ pci_and_config32(dev, XHCI_USB2PR, ~XHCI_USB2PR_HCSEL);
++
++ for (unsigned int port = 0; port < ehci_ports; port++) {
++ clrsetbits32(
++ xhci_bar + XHCI_USB2_PORTSC(port),
++ XHCI_USB2_PORTSC_PED,
++ XHCI_USB2_PORTSC_CHST);
++ }
++
++ if (!warm_reset_usb3_ports(xhci_ports))
++ printk(BIOS_ERR, "USB3 warm reset timed out\n");
++
++ /* FIXME: BWG says this should be inside the warm reset function */
++ pci_and_config32(dev, XHCI_USB3PR, ~XHCI_USB3PR_SSEN);
++
++ for (unsigned int port = 0; port < ehci_ports; port++) {
++ clrsetbits32(
++ xhci_bar + XHCI_USB3_PORTSC(port),
++ XHCI_USB3_PORTSC_PED,
++ XHCI_USB3_PORTSC_CHST);
++ }
++
++ setbits32(xhci_bar + XHCI_USBCMD, BIT(0));
++ clrbits32(xhci_bar + XHCI_USBCMD, BIT(0));
++ }
++
++ /* Route to xHCI if xHCI enabled */
++ if (usb_route == ROUTE_TO_XHCI) {
++ if (is_mem_sr()) {
++ if (!warm_reset_usb3_ports(xhci_ports))
++ printk(BIOS_ERR, "USB3 warm reset timed out\n");
++ }
++
++ const uint32_t xhci_port_mask = pci_read_config32(dev, XHCI_USB3PRM) & 0x3f;
++ pci_update_config32(dev, XHCI_USB3PR, ~XHCI_USB3PR_SSEN, xhci_port_mask);
++
++ const uint32_t ehci_port_mask = pci_read_config32(dev, XHCI_USB2PRM) & 0x7fff;
++ pci_update_config32(dev, XHCI_USB2PR, ~XHCI_USB2PR_HCSEL, ehci_port_mask);
++ }
++}
++
++/* Do not shift in this macro, as it can cause undefined behaviour for bad port/oc values */
++#define PORT_TO_OC_SHIFT(port, oc) ((oc) * 8 + (port))
++
++/* Avoid shifting into undefined behaviour */
++static inline bool shift_ok(const int shift)
++{
++ return shift >= 0 && shift < 32;
++}
++
++static void usb_overcurrent_mapping(void)
++{
++ const bool is_lp = CONFIG(INTEL_LYNXPOINT_LP);
++
++ uint32_t ehci_1_ocmap = 0;
++ uint32_t ehci_2_ocmap = 0;
++ uint32_t xhci_1_ocmap = 0;
++ uint32_t xhci_2_ocmap = 0;
++
++ /*
++ * EHCI
++ */
++ for (unsigned int idx = 0; idx < physical_port_count(); idx++) {
++ const struct usb2_port_config *const port = &mainboard_usb2_ports[idx];
++ printk(BIOS_DEBUG, "USB2 port %u => ", idx);
++ if (!port->enable) {
++ printk(BIOS_DEBUG, "disabled\n");
++ continue;
++ }
++ const unsigned short oc_pin = port->oc_pin;
++ if (oc_pin == USB_OC_PIN_SKIP) {
++ printk(BIOS_DEBUG, "not mapped to OC pin\n");
++ continue;
++ }
++ /* Ports 0 .. 7 => OC 0 .. 3 */
++ if (idx < 8 && oc_pin <= 3) {
++ const int shift = PORT_TO_OC_SHIFT(idx, oc_pin);
++ if (shift_ok(shift)) {
++ printk(BIOS_DEBUG, "mapped to OC pin %u\n", oc_pin);
++ ehci_1_ocmap |= 1 << shift;
++ continue;
++ }
++ }
++ /* Ports 8 .. 13 => OC 4 .. 7 (LPT-H only) */
++ if (!is_lp && idx >= 8 && oc_pin >= 4) {
++ const int shift = PORT_TO_OC_SHIFT(idx, oc_pin - 4);
++ if (shift_ok(shift)) {
++ printk(BIOS_DEBUG, "mapped to OC pin %u\n", oc_pin);
++ ehci_2_ocmap |= 1 << shift;
++ continue;
++ }
++ }
++ printk(BIOS_ERR, "Invalid OC pin %u for USB2 port %u\n", oc_pin, idx);
++ }
++ printk(BIOS_DEBUG, "\n");
++ pci_write_config32(PCH_EHCI1_DEV, EHCI_OCMAP, ehci_1_ocmap);
++ if (!is_lp)
++ pci_write_config32(PCH_EHCI2_DEV, EHCI_OCMAP, ehci_2_ocmap);
++
++ /*
++ * xHCI
++ */
++ for (unsigned int idx = 0; idx < ss_port_count(); idx++) {
++ const struct usb3_port_config *const port = &mainboard_usb3_ports[idx];
++ printk(BIOS_DEBUG, "USB3 port %u => ", idx);
++ if (!port->enable) {
++ printk(BIOS_DEBUG, "disabled\n");
++ continue;
++ }
++ const unsigned short oc_pin = port->oc_pin;
++ if (oc_pin == USB_OC_PIN_SKIP) {
++ printk(BIOS_DEBUG, "not mapped to OC pin\n");
++ continue;
++ }
++ /* Ports 0 .. 5 => OC 0 .. 3 */
++ if (oc_pin <= 3) {
++ const int shift = PORT_TO_OC_SHIFT(idx, oc_pin);
++ if (shift_ok(shift)) {
++ printk(BIOS_DEBUG, "mapped to OC pin %u\n", oc_pin);
++ xhci_1_ocmap |= 1 << shift;
++ continue;
++ }
++ }
++ /* Ports 0 .. 5 => OC 4 .. 7 (LPT-H only) */
++ if (!is_lp && oc_pin >= 4) {
++ const int shift = PORT_TO_OC_SHIFT(idx, oc_pin - 4);
++ if (shift_ok(shift)) {
++ printk(BIOS_DEBUG, "mapped to OC pin %u\n", oc_pin);
++ xhci_2_ocmap |= 1 << shift;
++ continue;
++ }
++ }
++ printk(BIOS_ERR, "Invalid OC pin %u for USB3 port %u\n", oc_pin, idx);
++ }
++ printk(BIOS_DEBUG, "\n");
++ pci_write_config32(PCH_XHCI_DEV, XHCI_U2OCM1, ehci_1_ocmap);
++ pci_write_config32(PCH_XHCI_DEV, XHCI_U3OCM1, xhci_1_ocmap);
++ if (!is_lp) {
++ pci_write_config32(PCH_XHCI_DEV, XHCI_U2OCM2, ehci_2_ocmap);
++ pci_write_config32(PCH_XHCI_DEV, XHCI_U3OCM2, xhci_2_ocmap);
++ }
++}
++
++static uint8_t get_ehci_tune_param_1(const struct usb2_port_config *const port)
++{
++ const bool is_lp = CONFIG(INTEL_LYNXPOINT_LP);
++
++ const enum pch_platform_type plat_type = get_pch_platform_type();
++ const enum usb2_port_location location = port->location;
++ const uint16_t length = port->length;
++ if (!is_lp) {
++ if (plat_type == PCH_TYPE_DESKTOP) {
++ if (location == USB_PORT_BACK_PANEL)
++ return 4; /* Back Panel */
++ else
++ return 3; /* Front Panel */
++
++ } else if (plat_type == PCH_TYPE_MOBILE) {
++ if (location == USB_PORT_INTERNAL)
++ return 5; /* Internal Topology */
++ else if (location == USB_PORT_DOCK)
++ return 4; /* Dock */
++ else if (length < 0x70)
++ return 5; /* Back Panel, less than 7" */
++ else
++ return 6; /* Back Panel, 7" or more */
++ }
++ } else {
++ if (location == USB_PORT_BACK_PANEL || location == USB_PORT_MINI_PCIE) {
++ if (length < 0x70)
++ return 5; /* Back Panel, less than 7" */
++ else
++ return 6; /* Back Panel, 7" or more */
++ } else if (location == USB_PORT_DOCK) {
++ return 4; /* Dock */
++ } else {
++ return 5; /* Internal Topology */
++ }
++ }
++ printk(BIOS_ERR, "%s: Unhandled case\n", __func__);
++ return 0;
++}
++
++static uint8_t get_ehci_tune_param_2(const struct usb2_port_config *const port)
++{
++ const bool is_lp = CONFIG(INTEL_LYNXPOINT_LP);
++
++ const enum pch_platform_type plat_type = get_pch_platform_type();
++ const enum usb2_port_location location = port->location;
++ const uint16_t length = port->length;
++ if (!is_lp) {
++ if (plat_type == PCH_TYPE_DESKTOP) {
++ if (location == USB_PORT_BACK_PANEL) {
++ if (length < 0x80)
++ return 2; /* Back Panel, less than 8" */
++ else if (length < 0x130)
++ return 3; /* Back Panel, 8"-13" */
++ else
++ return 4; /* Back Panel, 13" or more */
++ } else {
++ return 2; /* Front Panel */
++ }
++
++ } else if (plat_type == PCH_TYPE_MOBILE) {
++ if (location == USB_PORT_INTERNAL) {
++ return 2; /* Internal Topology */
++ } else if (location == USB_PORT_DOCK) {
++ if (length < 0x50)
++ return 1; /* Dock, less than 5" */
++ else
++ return 2; /* Dock, 5" or more */
++ } else {
++ if (length < 0x100)
++ return 2; /* Back Panel, less than 10" */
++ else
++ return 3; /* Back Panel, 10" or more */
++ }
++ }
++ } else {
++ if (location == USB_PORT_BACK_PANEL || location == USB_PORT_MINI_PCIE) {
++ if (length < 0x100)
++ return 2; /* Back Panel, less than 10" */
++ else
++ return 3; /* Back Panel, 10" or more */
++ } else if (location == USB_PORT_DOCK) {
++ if (length < 0x50)
++ return 1; /* Dock, less than 5" */
++ else
++ return 2; /* Dock, 5" or more */
++ } else {
++ return 2; /* Internal Topology */
++ }
++ }
++ printk(BIOS_ERR, "%s: Unhandled case\n", __func__);
++ return 0;
++}
++
++static void program_ehci_port_length(void)
++{
++ for (unsigned int port = 0; port < physical_port_count(); port++) {
++ if (!mainboard_usb2_ports[port].enable)
++ continue;
++ const uint32_t addr = 0xe5004000 + (port + 1) * 0x100;
++ const uint8_t param_1 = get_ehci_tune_param_1(&mainboard_usb2_ports[port]);
++ const uint8_t param_2 = get_ehci_tune_param_2(&mainboard_usb2_ports[port]);
++ pch_iobp_update(addr, ~0x7f00, param_2 << 11 | param_1 << 8);
++ }
++}
++
++void early_usb_init(void)
++{
++ /** TODO: Make this configurable? How do the modes affect usbdebug? **/
++ const enum usb_port_route usb_route = ROUTE_TO_XHCI;
++ ///(pd->boot_mode == 2 && pd->usb_xhci_on_resume) ? ROUTE_TO_XHCI : ROUTE_TO_EHCI;
++
++ common_ehci_hcs_init();
++ xhci_open_memory_space();
++ common_xhci_hc_init();
++ perform_xhci_ehci_switching_flow(usb_route);
++ usb_overcurrent_mapping();
++ program_ehci_port_length();
++ /** FIXME: USB per port control is missing, is it needed? **/
++ xhci_close_memory_space();
++ /** TODO: Close EHCI memory space? **/
++}
+diff --git a/src/southbridge/intel/lynxpoint/pch.h b/src/southbridge/intel/lynxpoint/pch.h
+index b5e0c2a830..ad983d86cf 100644
+--- a/src/southbridge/intel/lynxpoint/pch.h
++++ b/src/southbridge/intel/lynxpoint/pch.h
+@@ -115,6 +115,7 @@ enum pch_platform_type {
+
+ void pch_dmi_setup_physical_layer(void);
+ void pch_dmi_tc_vc_mapping(u32 vc0, u32 vc1, u32 vcp, u32 vcm);
++void early_usb_init(void);
+
+ void usb_ehci_sleep_prepare(pci_devfn_t dev, u8 slp_typ);
+ void usb_ehci_disable(pci_devfn_t dev);
+@@ -202,6 +203,8 @@ void mainboard_config_rcba(void);
+ #define GEN_PMCON_1 0xa0
+ #define SMI_LOCK (1 << 4)
+ #define GEN_PMCON_2 0xa2
++#define GEN_PMCON_2_DISB (1 << 7)
++#define GEN_PMCON_2_MEM_SR (1 << 5)
+ #define SYSTEM_RESET_STS (1 << 4)
+ #define THERMTRIP_STS (1 << 3)
+ #define SYSPWR_FLR (1 << 1)
+@@ -215,6 +218,7 @@ void mainboard_config_rcba(void);
+ #define PMIR 0xac
+ #define PMIR_CF9LOCK (1 << 31)
+ #define PMIR_CF9GR (1 << 20)
++#define PMIR_XHCI_SMART_AUTO (1 << 16) /* c.f. LPT BWG or WPT-LP BIOS spec */
+
+ /* GEN_PMCON_3 bits */
+ #define RTC_BATTERY_DEAD (1 << 2)
+@@ -282,6 +286,20 @@ void mainboard_config_rcba(void);
+ #define SATA_DTLE_DATA_SHIFT 24
+ #define SATA_DTLE_EDGE_SHIFT 16
+
++/*
++ * HCD_INDEX == 2 selects 0:1a.0 (PCH_EHCI2), any other index
++ * selects 0:1d.0 (PCH_EHCI1) for usbdebug use.
++ */
++#if CONFIG_USBDEBUG_HCD_INDEX != 2
++#define PCH_EHCI1_TEMP_BAR0 CONFIG_EHCI_BAR
++#define PCH_EHCI2_TEMP_BAR0 (PCH_EHCI1_TEMP_BAR0 + 0x400)
++#else
++#define PCH_EHCI2_TEMP_BAR0 CONFIG_EHCI_BAR
++#define PCH_EHCI1_TEMP_BAR0 (PCH_EHCI2_TEMP_BAR0 + 0x400)
++#endif
++
++#define PCH_XHCI_TEMP_BAR0 0xe8100000
++
+ /* EHCI PCI Registers */
+ #define EHCI_PWR_CTL_STS 0x54
+ #define PWR_CTL_SET_MASK 0x3
+@@ -289,10 +307,15 @@ void mainboard_config_rcba(void);
+ #define PWR_CTL_SET_D3 0x3
+ #define PWR_CTL_ENABLE_PME (1 << 8)
+ #define PWR_CTL_STATUS_PME (1 << 15)
++#define EHCI_OCMAP 0x74
++#define EHCI_ACCESS_CNTL 0x80
++#define ACCESS_CNTL_ENABLE (1 << 0)
+
+ /* EHCI Memory Registers */
++#define EHCI_HCS_PARAMS 0x04
+ #define EHCI_USB_CMD 0x20
+ #define EHCI_USB_CMD_RUN (1 << 0)
++#define EHCI_USB_CMD_HCRESET (1 << 1)
+ #define EHCI_USB_CMD_PSE (1 << 4)
+ #define EHCI_USB_CMD_ASE (1 << 5)
+ #define EHCI_PORTSC(port) (0x64 + (port) * 4)
+@@ -301,6 +324,10 @@ void mainboard_config_rcba(void);
+
+ /* XHCI PCI Registers */
+ #define XHCI_PWR_CTL_STS 0x74
++#define XHCI_U2OCM1 0xc0
++#define XHCI_U2OCM2 0xc4
++#define XHCI_U3OCM1 0xc8
++#define XHCI_U3OCM2 0xcc
+ #define XHCI_USB2PR 0xd0
+ #define XHCI_USB2PRM 0xd4
+ #define XHCI_USB2PR_HCSEL 0x7fff
+@@ -313,6 +340,27 @@ void mainboard_config_rcba(void);
+ #define XHCI_USB3PDO 0xe8
+
+ /* XHCI Memory Registers */
++#define XHCI_HCS_PARAMS_1 0x04
++#define XHCI_HCS_PARAMS_2 0x08
++#define XHCI_HCS_PARAMS_3 0x0c
++#define XHCI_HCC_PARAMS 0x10
++#define XHCI_USBCMD 0x80
++#define XHCI_USB2_PORTSC(port) (0x480 + ((port) * 0x10))
++#define XHCI_USB2_PORTSC_WPR (1 << 31) /* Warm Port Reset */
++#define XHCI_USB2_PORTSC_CEC (1 << 23) /* Port Config Error Change */
++#define XHCI_USB2_PORTSC_PLC (1 << 22) /* Port Link State Change */
++#define XHCI_USB2_PORTSC_PRC (1 << 21) /* Port Reset Change */
++#define XHCI_USB2_PORTSC_OCC (1 << 20) /* Over-current Change */
++#define XHCI_USB2_PORTSC_WRC (1 << 19) /* Warm Port Reset Change */
++#define XHCI_USB2_PORTSC_PEC (1 << 18) /* Port Enabled Disabled Change */
++#define XHCI_USB2_PORTSC_CSC (1 << 17) /* Connect Status Change */
++#define XHCI_USB2_PORTSC_CHST (0x7f << 17)
++#define XHCI_USB2_PORTSC_LWS (1 << 16) /* Port Link State Write Strobe */
++#define XHCI_USB2_PORTSC_PP (1 << 9)
++#define XHCI_USB2_PORTSC_PR (1 << 4) /* Port Reset */
++#define XHCI_USB2_PORTSC_PED (1 << 1) /* Port Enable/Disabled */
++#define XHCI_USB2_PORTSC_CCS (1 << 0) /* Current Connect Status */
++
+ #define XHCI_USB3_PORTSC(port) ((pch_is_lp() ? 0x510 : 0x570) + ((port) * 0x10))
+ #define XHCI_USB3_PORTSC_CHST (0x7f << 17)
+ #define XHCI_USB3_PORTSC_WCE (1 << 25) /* Wake on Connect */
+@@ -320,6 +368,7 @@ void mainboard_config_rcba(void);
+ #define XHCI_USB3_PORTSC_WOE (1 << 27) /* Wake on Overcurrent */
+ #define XHCI_USB3_PORTSC_WRC (1 << 19) /* Warm Reset Complete */
+ #define XHCI_USB3_PORTSC_LWS (1 << 16) /* Link Write Strobe */
++#define XHCI_USB3_PORTSC_PR (1 << 4) /* Port Reset */
+ #define XHCI_USB3_PORTSC_PED (1 << 1) /* Port Enabled/Disabled */
+ #define XHCI_USB3_PORTSC_WPR (1 << 31) /* Warm Port Reset */
+ #define XHCI_USB3_PORTSC_PLS (0xf << 5) /* Port Link State */
+--
+2.39.2
+