soc/intel/skylake: Swap PCI devfn resides in same PCI device

After FSP-S, a device on PCI function n will be function swapped
to function 0 if there is no device presnet on function 0.
It needs some modification for DT and causes mismatches between
software configuration and hardware schematic. This patch is
from d779605, which swaps the devfn of the first enabled device
in DT and function 0 resides in a PCI device.

BUG=b:80105785
BRANCH=None
TEST=Make sure the device is still enabled after coalescence with
     device on bus 0 and w/o device on bus 0. Test with suspend
     and resume and ensure it's consistent.

Change-Id: Ibbc5d6e979977011f5904c8bd4b2f1be16bd23dc
Signed-off-by: Gaggery Tsai <gaggery.tsai@intel.com>
Reviewed-on: https://review.coreboot.org/26479
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Furquan Shaikh <furquan@google.com>
diff --git a/src/soc/intel/skylake/chip_fsp20.c b/src/soc/intel/skylake/chip_fsp20.c
index bc78586..0ba70e0 100644
--- a/src/soc/intel/skylake/chip_fsp20.c
+++ b/src/soc/intel/skylake/chip_fsp20.c
@@ -19,12 +19,14 @@
 #include <device/pci.h>
 #include <fsp/api.h>
 #include <arch/acpi.h>
+#include <arch/io.h>
 #include <chip.h>
 #include <compiler.h>
 #include <bootstate.h>
 #include <console/console.h>
 #include <device/device.h>
 #include <device/pci.h>
+#include <device/pci_ids.h>
 #include <fsp/api.h>
 #include <fsp/util.h>
 #include <intelblocks/xdci.h>
@@ -39,10 +41,135 @@
 #include <soc/systemagent.h>
 #include <string.h>
 
+struct pcie_entry {
+	unsigned int devfn;
+	unsigned int func_count;
+};
+
+/*
+ * According to table 2-2 in doc#546717:
+ * PCI bus[function]	ID
+ * D28:[F0 - F7]		0xA110 - 0xA117
+ * D29:[F0 - F7]		0xA118 - 0xA11F
+ * D27:[F0 - F3]		0xA167 - 0xA16A
+ */
+static const struct pcie_entry pcie_table_skl_pch_h[] = {
+	{PCH_DEVFN_PCIE1, 8},
+	{PCH_DEVFN_PCIE9, 8},
+	{PCH_DEVFN_PCIE17, 4},
+};
+
+/*
+ * According to table 2-2 in doc#564464:
+ * PCI bus[function]	ID
+ * D28:[F0 - F7]		0xA290 - 0xA297
+ * D29:[F0 - F7]		0xA298 - 0xA29F
+ * D27:[F0 - F7]		0xA2E7 - 0xA2EE
+ */
+static const struct pcie_entry pcie_table_kbl_pch_h[] = {
+	{PCH_DEVFN_PCIE1, 8},
+	{PCH_DEVFN_PCIE9, 8},
+	{PCH_DEVFN_PCIE17, 8},
+};
+
+/*
+ * According to table 2-2 in doc#567995/545659:
+ * PCI bus[function]	ID
+ * D28:[F0 - F7]		0x9D10 - 0x9D17
+ * D29:[F0 - F3]		0x9D18 - 0x9D1B
+ */
+static const struct pcie_entry pcie_table_skl_pch_lp[] = {
+	{PCH_DEVFN_PCIE1, 8},
+	{PCH_DEVFN_PCIE9, 4},
+};
+
+/*
+ * If the PCIe root port at function 0 is disabled,
+ * the PCIe root ports might be coalesced after FSP silicon init.
+ * The below function will swap the devfn of the first enabled device
+ * in devicetree and function 0 resides a pci device
+ * so that it won't confuse coreboot.
+ */
+static void pcie_update_device_tree(const struct pcie_entry *pcie_rp_group,
+		size_t pci_groups)
+{
+	struct device *func0;
+	unsigned int devfn, devfn0;
+	int i, group;
+	unsigned int inc = PCI_DEVFN(0, 1);
+
+	for (group = 0; group < pci_groups; group++) {
+		devfn0 = pcie_rp_group[group].devfn;
+		func0 = dev_find_slot(0, devfn0);
+		if (func0 == NULL)
+			continue;
+
+		/* No more functions if function 0 is disabled. */
+		if (pci_read_config32(func0, PCI_VENDOR_ID) == 0xffffffff)
+			continue;
+
+		devfn = devfn0 + inc;
+
+		/*
+		 * Increase function by 1.
+		 * Then find first enabled device to replace func0
+		 * as that port was move to func0.
+		 */
+		for (i = 1; i < pcie_rp_group[group].func_count;
+				i++, devfn += inc) {
+			struct device *dev = dev_find_slot(0, devfn);
+			if (dev == NULL || !dev->enabled)
+				continue;
+
+			/*
+			 * Found the first enabled device in
+			 * a given dev number.
+			 */
+			printk(BIOS_INFO, "PCI func %d was swapped"
+				" to func 0.\n", i);
+			func0->path.pci.devfn = dev->path.pci.devfn;
+			dev->path.pci.devfn = devfn0;
+			break;
+		}
+	}
+}
+
+static void pcie_override_devicetree_after_silicon_init(void)
+{
+	uint16_t id, id_mask;
+
+	id = pci_read_config16(PCH_DEV_PCIE1, PCI_DEVICE_ID);
+	/*
+	 * We may read an ID other than func 0 after FSP-S.
+	 * Strip out 4 least significant bits.
+	 */
+	id_mask = id & ~0xf;
+	printk(BIOS_INFO, "Override DT after FSP-S, PCH is ");
+	if (id_mask == (PCI_DEVICE_ID_INTEL_SPT_LP_PCIE_RP1 & ~0xf)) {
+		printk(BIOS_INFO, "KBL/SKL PCH-LP SKU\n");
+		pcie_update_device_tree(&pcie_table_skl_pch_lp[0],
+			ARRAY_SIZE(pcie_table_skl_pch_lp));
+	} else if (id_mask == (PCI_DEVICE_ID_INTEL_KBP_H_PCIE_RP1 & ~0xf)) {
+		printk(BIOS_INFO, "KBL PCH-H SKU\n");
+		pcie_update_device_tree(&pcie_table_kbl_pch_h[0],
+			ARRAY_SIZE(pcie_table_kbl_pch_h));
+	} else if (id_mask == (PCI_DEVICE_ID_INTEL_SPT_H_PCIE_RP1 & ~0xf)) {
+		printk(BIOS_INFO, "SKL PCH-H SKU\n");
+		pcie_update_device_tree(&pcie_table_skl_pch_h[0],
+			ARRAY_SIZE(pcie_table_skl_pch_h));
+	} else {
+		printk(BIOS_ERR, "[BUG] PCIE Root Port id 0x%x"
+			" is not found\n", id);
+		return;
+	}
+}
+
 void soc_init_pre_device(void *chip_info)
 {
 	/* Perform silicon specific init. */
 	fsp_silicon_init(romstage_handoff_is_resume());
+	/* swap enabled PCI ports in device tree if needed */
+	pcie_override_devicetree_after_silicon_init();
 }
 
 void soc_fsp_load(void)
diff --git a/src/soc/intel/skylake/include/soc/pci_devs.h b/src/soc/intel/skylake/include/soc/pci_devs.h
index 4fc8807..bc6c9cb 100644
--- a/src/soc/intel/skylake/include/soc/pci_devs.h
+++ b/src/soc/intel/skylake/include/soc/pci_devs.h
@@ -94,6 +94,24 @@
 #define  PCH_DEV_I2C5		_PCH_DEV(SIO2, 1)
 #define  PCH_DEV_I2C4		_PCH_DEV(SIO2, 2)
 
+#define PCH_DEV_SLOT_PCIE_2	0x1b
+#define  PCH_DEVFN_PCIE17	_PCH_DEVFN(PCIE_2, 0)
+#define  PCH_DEVFN_PCIE18	_PCH_DEVFN(PCIE_2, 1)
+#define  PCH_DEVFN_PCIE19	_PCH_DEVFN(PCIE_2, 2)
+#define  PCH_DEVFN_PCIE20	_PCH_DEVFN(PCIE_2, 3)
+#define  PCH_DEVFN_PCIE21	_PCH_DEVFN(PCIE_2, 4)
+#define  PCH_DEVFN_PCIE22	_PCH_DEVFN(PCIE_2, 5)
+#define  PCH_DEVFN_PCIE23	_PCH_DEVFN(PCIE_2, 6)
+#define  PCH_DEVFN_PCIE24	_PCH_DEVFN(PCIE_2, 7)
+#define  PCH_DEV_PCIE17		_PCH_DEV(PCIE_2, 0)
+#define  PCH_DEV_PCIE18		_PCH_DEV(PCIE_2, 1)
+#define  PCH_DEV_PCIE19		_PCH_DEV(PCIE_2, 2)
+#define  PCH_DEV_PCIE20		_PCH_DEV(PCIE_2, 3)
+#define  PCH_DEV_PCIE21		_PCH_DEV(PCIE_2, 4)
+#define  PCH_DEV_PCIE22		_PCH_DEV(PCIE_2, 5)
+#define  PCH_DEV_PCIE23		_PCH_DEV(PCIE_2, 6)
+#define  PCH_DEV_PCIE24		_PCH_DEV(PCIE_2, 7)
+
 #define PCH_DEV_SLOT_PCIE	0x1c
 #define  PCH_DEVFN_PCIE1	_PCH_DEVFN(PCIE, 0)
 #define  PCH_DEVFN_PCIE2	_PCH_DEVFN(PCIE, 1)
@@ -117,10 +135,18 @@
 #define  PCH_DEVFN_PCIE10	_PCH_DEVFN(PCIE_1, 1)
 #define  PCH_DEVFN_PCIE11	_PCH_DEVFN(PCIE_1, 2)
 #define  PCH_DEVFN_PCIE12	_PCH_DEVFN(PCIE_1, 3)
+#define  PCH_DEVFN_PCIE13	_PCH_DEVFN(PCIE_1, 4)
+#define  PCH_DEVFN_PCIE14	_PCH_DEVFN(PCIE_1, 5)
+#define  PCH_DEVFN_PCIE15	_PCH_DEVFN(PCIE_1, 6)
+#define  PCH_DEVFN_PCIE16	_PCH_DEVFN(PCIE_1, 7)
 #define  PCH_DEV_PCIE9		_PCH_DEV(PCIE_1, 0)
 #define  PCH_DEV_PCIE10		_PCH_DEV(PCIE_1, 1)
 #define  PCH_DEV_PCIE11		_PCH_DEV(PCIE_1, 2)
 #define  PCH_DEV_PCIE12		_PCH_DEV(PCIE_1, 3)
+#define  PCH_DEV_PCIE13		_PCH_DEV(PCIE_1, 4)
+#define  PCH_DEV_PCIE14		_PCH_DEV(PCIE_1, 5)
+#define  PCH_DEV_PCIE15		_PCH_DEV(PCIE_1, 6)
+#define  PCH_DEV_PCIE16		_PCH_DEV(PCIE_1, 7)
 
 #define PCH_DEV_SLOT_STORAGE	0x1e
 #define  PCH_DEVFN_UART0	_PCH_DEVFN(STORAGE, 0)