nb/sandybridge,sb/bd82x6x: Configure USB from southbridge devicetree

Transfer all USB responsibilities to southbridge/intel/bd82x6x,
using one set of USB port configuration supplied by mainboards
in the southbridge section of their devicetree.

For MRC raminit, export southbridge_fill_pei_data() as a hook for
southbridge code to implement. With new code via this hook, bd82x6x
fills pei_data based on this one set of USB port config.

For native raminit, early_usb_init() now goes directly to the devicetree
and no longer get passed an address to it.

TEST=abuild passes for all affected boards. All USB ports still work
on asus/p8x7x-series/v/p8z77-m.

Change-Id: I38378c7ee0701abc434b030dd97873f2af63e6b0
Signed-off-by: Keith Hui <buurin@gmail.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/81881
Reviewed-by: Patrick Rudolph <patrick.rudolph@9elements.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
diff --git a/src/northbridge/intel/sandybridge/pei_data.h b/src/northbridge/intel/sandybridge/pei_data.h
index 0309cf3..db6347c 100644
--- a/src/northbridge/intel/sandybridge/pei_data.h
+++ b/src/northbridge/intel/sandybridge/pei_data.h
@@ -116,4 +116,5 @@
 	int ddr_refresh_rate_config;
 } __packed;
 
+void southbridge_fill_pei_data(struct pei_data *pei_data);
 #endif
diff --git a/src/northbridge/intel/sandybridge/raminit_mrc.c b/src/northbridge/intel/sandybridge/raminit_mrc.c
index b9279f3..98b3028 100644
--- a/src/northbridge/intel/sandybridge/raminit_mrc.c
+++ b/src/northbridge/intel/sandybridge/raminit_mrc.c
@@ -252,11 +252,6 @@
 	}
 }
 
-static void southbridge_fill_pei_data(struct pei_data *pei_data)
-{
-	/* This will move to southbridge later. */
-}
-
 static void devicetree_fill_pei_data(struct pei_data *pei_data)
 {
 	const struct northbridge_intel_sandybridge_config *cfg = config_of_soc();
@@ -290,9 +285,6 @@
 		}
 	}
 	memcpy(pei_data->ts_addresses,  cfg->ts_addresses,  sizeof(pei_data->ts_addresses));
-
-	memcpy(pei_data->usb_port_config, cfg->usb_port_config,
-	       sizeof(pei_data->usb_port_config));
 }
 
 static void spd_fill_pei_data(struct pei_data *pei_data)
@@ -374,7 +366,7 @@
 		.nmode          = cfg->nmode,
 		.ddr_refresh_rate_config  = cfg->ddr_refresh_rate_config,
 		.usb3.mode                = cfg->usb3.mode,
-		.usb3.hs_port_switch_mask = cfg->usb3.hs_port_switch_mask,
+		/* .usb3.hs_port_switch_mask = native config->xhci_switchable_ports */
 		.usb3.preboot_support     = cfg->usb3.preboot_support,
 		.usb3.xhci_streams        = cfg->usb3.xhci_streams,
 	};
diff --git a/src/northbridge/intel/sandybridge/romstage.c b/src/northbridge/intel/sandybridge/romstage.c
index 56dc677..86569c1 100644
--- a/src/northbridge/intel/sandybridge/romstage.c
+++ b/src/northbridge/intel/sandybridge/romstage.c
@@ -58,7 +58,7 @@
 
 	/* When using MRC, USB is initialized by MRC */
 	if (CONFIG(USE_NATIVE_RAMINIT)) {
-		early_usb_init(mainboard_usb_ports);
+		early_usb_init();
 	}
 
 	/* Perform some early chipset init needed before RAM initialization can work */
diff --git a/src/southbridge/intel/bd82x6x/early_usb.c b/src/southbridge/intel/bd82x6x/early_usb.c
index 6c21cf7..7bca0bb 100644
--- a/src/southbridge/intel/bd82x6x/early_usb.c
+++ b/src/southbridge/intel/bd82x6x/early_usb.c
@@ -8,8 +8,9 @@
 #include <southbridge/intel/common/pmbase.h>
 
 #include "pch.h"
+#include "chip.h"
 
-void early_usb_init(const struct southbridge_usb_port *portmap)
+void early_usb_init(void)
 {
 	u32 reg32;
 	const u32 rcba_dump[8] = {
@@ -23,6 +24,9 @@
 				 USBIR_TXRX_GAIN_DESKTOP6_LOW, USBIR_TXRX_GAIN_DESKTOP6_HIGH,
 				 USBIR_TXRX_GAIN_DESKTOP7_LOW, USBIR_TXRX_GAIN_DESKTOP7_MED,
 				 0x20000053, 0x2000055f, 0x20000f5f};
+	const struct device *dev = pcidev_on_root(0x1d, 0);
+	const struct southbridge_intel_bd82x6x_config *config = dev->chip_info;
+	const struct southbridge_usb_port *portmap = config->usb_port_config;
 	int i;
 
 	/* Unlock registers.  */
diff --git a/src/southbridge/intel/bd82x6x/early_usb_mrc.c b/src/southbridge/intel/bd82x6x/early_usb_mrc.c
index f60cc0b..bf43e26 100644
--- a/src/southbridge/intel/bd82x6x/early_usb_mrc.c
+++ b/src/southbridge/intel/bd82x6x/early_usb_mrc.c
@@ -3,6 +3,8 @@
 #include <device/pci_ops.h>
 #include <device/pci_def.h>
 #include "pch.h"
+#include "chip.h"
+#include <northbridge/intel/sandybridge/pei_data.h>
 
 #define PCH_EHCI1_TEMP_BAR0 0xe8000000
 #define PCH_EHCI2_TEMP_BAR0 0xe8000400
@@ -29,3 +31,63 @@
 			   PCH_EHCI2_TEMP_BAR0);
 	pci_or_config16(usb1, PCI_COMMAND, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
 }
+
+/*
+ * Translate coreboot native USB port configuration in devicetree
+ * into a format reference code expects:
+ *
+ * [MRC index] = .native_field // what for
+ *         [0] = .enabled // enable
+ *         [1] = .oc_pin // overcurrent pin
+ *         [2] = .current // length
+ *
+ * For .current, use these native values for MRC settings 1-3, corresponding
+ * to values of 0x40/0x80/0x130, which should produce the correct values
+ * across all supported PCHs.
+ *
+ * PCH type    | 1 | 2 | 3
+ * ------------+---+---+---
+ * Mobile      | 0 | 1 | 2
+ * Desktop x6x | 6 | 1 | 7
+ * Desktop x7x | 8 | 9 | 2
+ *
+ * See also:
+ * northbridge/intel/sandybridge/pei_data.h
+ * pch.h
+ * early_usb.c
+ */
+void southbridge_fill_pei_data(struct pei_data *pei_data)
+{
+	const struct device *dev = pcidev_on_root(0x1d, 0);
+	const struct southbridge_intel_bd82x6x_config *config = dev->chip_info;
+	/* Native current -> MRC length map to get the same USBIRx register value */
+	const uint16_t currents[] = { 0x40, 0x80, 0x130,
+				      0, 0, 0, /* 3-5 not seen in MRC */
+				      0x40, 0x130, 0x40, 0x80};
+	for (unsigned int port = 0; port < ARRAY_SIZE(config->usb_port_config); port++) {
+		uint16_t current = 0;
+		int ocp = config->usb_port_config[port].oc_pin;
+		if (ocp == -1)
+			ocp = (port < 8) ? 0 : 4;
+
+		if (config->usb_port_config[port].current < ARRAY_SIZE(currents))
+			current = currents[config->usb_port_config[port].current];
+
+		/*
+		 * Note for developers: If this message shows, your board uses a
+		 * current setting MRC.bin cannot produce. Choose a value as close
+		 * as possible and test all USB ports, or consider using native raminit.
+		 */
+		if (current == 0) {
+			printk(BIOS_NOTICE,
+			       "%s: USB%02d: %d is an invalid setting for MRC.bin!\n",
+			       __func__, port, config->usb_port_config[port].current);
+		}
+
+		pei_data->usb_port_config[port][0] = config->usb_port_config[port].enabled;
+		pei_data->usb_port_config[port][1] = ocp;
+		pei_data->usb_port_config[port][2] = current;
+	}
+
+	pei_data->usb3.hs_port_switch_mask = config->xhci_switchable_ports;
+}
diff --git a/src/southbridge/intel/bd82x6x/pch.h b/src/southbridge/intel/bd82x6x/pch.h
index f2f02b3..8924dca 100644
--- a/src/southbridge/intel/bd82x6x/pch.h
+++ b/src/southbridge/intel/bd82x6x/pch.h
@@ -64,7 +64,7 @@
 void pch_enable(struct device *dev);
 extern const struct southbridge_usb_port mainboard_usb_ports[14];
 
-void early_usb_init(const struct southbridge_usb_port *portmap);
+void early_usb_init(void);
 
 /* PCI Configuration Space (D30:F0): PCI2PCI */
 #define PSTS	0x06