[coreboot-gerrit] Change in ...coreboot[master]: [WIP]sb/intel/i82801gx: Autodisable functions based on devicetree

Arthur Heymans (Code Review) gerrit at coreboot.org
Sat Dec 15 23:48:50 CET 2018


Arthur Heymans has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/30244


Change subject: [WIP]sb/intel/i82801gx: Autodisable functions based on devicetree
......................................................................

[WIP]sb/intel/i82801gx: Autodisable functions based on devicetree

This mostly reimplements what sandybridge does albeit a bit simpler

TODO:
- remove romstage FD
- What about INTLAN?

TESTED with coalescing (root port 0 disabled in devicetree)

Change-Id: I83576599538a02d295fe00b35826f98d8c97d1cf
Signed-off-by: Arthur Heymans <arthur at aheymans.xyz>
---
M src/southbridge/intel/i82801gx/i82801gx.c
M src/southbridge/intel/i82801gx/i82801gx.h
2 files changed, 205 insertions(+), 7 deletions(-)



  git pull ssh://review.coreboot.org:29418/coreboot refs/changes/44/30244/1

diff --git a/src/southbridge/intel/i82801gx/i82801gx.c b/src/southbridge/intel/i82801gx/i82801gx.c
index eb0583f..2541fc6 100644
--- a/src/southbridge/intel/i82801gx/i82801gx.c
+++ b/src/southbridge/intel/i82801gx/i82801gx.c
@@ -20,18 +20,204 @@
 #include "i82801gx.h"
 #include "sata.h"
 
+static void ich_hide_devfn(unsigned int devfn)
+{
+	switch (devfn) {
+	case PCI_DEVFN(27, 0): /* HD Audio Controller */
+		RCBA32_OR(FD, FD_HDAUD);
+		break;
+	case PCI_DEVFN(28, 0): /* PCI Express Root Port 1 */
+	case PCI_DEVFN(28, 1): /* PCI Express Root Port 2 */
+	case PCI_DEVFN(28, 2): /* PCI Express Root Port 3 */
+	case PCI_DEVFN(28, 3): /* PCI Express Root Port 4 */
+	case PCI_DEVFN(28, 4): /* PCI Express Root Port 5 */
+	case PCI_DEVFN(28, 5): /* PCI Express Root Port 6 */
+		RCBA32_OR(FD, ICH_DISABLE_PCIE(PCI_FUNC(devfn)));
+		break;
+	case PCI_DEVFN(29, 0): /* UHCI #1 */
+	case PCI_DEVFN(29, 1): /* UHCI #2 */
+	case PCI_DEVFN(29, 2): /* UHCI #3 */
+	case PCI_DEVFN(29, 3): /* UHCI #4 */
+		RCBA32_OR(FD, ICH_DISABLE_UHCI(PCI_FUNC(devfn)));
+		break;
+	case PCI_DEVFN(29, 7): /* EHCI #1 */
+		RCBA32_OR(FD, FD_EHCI);
+		break;
+	case PCI_DEVFN(30, 2): /* AC Audio */
+		RCBA32_OR(FD, FD_ACMOD);
+		break;
+	case PCI_DEVFN(30, 3): /* AC Modem */
+		RCBA32_OR(FD, FD_ACMOD);
+		break;
+	case PCI_DEVFN(31, 0): /* LPC */
+		RCBA32_OR(FD, FD_LPCB);
+		break;
+	case PCI_DEVFN(31, 1): /* PATA #1 */
+		RCBA32_OR(FD, FD_PATA);
+		break;
+	case PCI_DEVFN(31, 2): /* SATA #1 */
+		RCBA32_OR(FD, FD_PATA);
+		break;
+	case PCI_DEVFN(31, 3): /* SMBUS */
+		RCBA32_OR(FD, FD_SMBUS);
+		break;
+	}
+}
+
+/* RPFN is a write-once register so keep a copy until it is written */
+static u32 new_rpfn;
+
+/* Update devicetree with new Root Port function number assignment */
+static void ich_pcie_devicetree_update(
+		struct southbridge_intel_i82801gx_config *config)
+{
+	struct device *dev;
+
+	/* Update the function numbers in the static devicetree */
+	for (dev = all_devices; dev; dev = dev->next) {
+		u8 new_devfn;
+
+		/* Only care about ICH PCIe root ports */
+		if (PCI_SLOT(dev->path.pci.devfn) != ICH_PCIE_DEV_SLOT)
+			continue;
+
+		/* Determine the new devfn for this port */
+		new_devfn = PCI_DEVFN(ICH_PCIE_DEV_SLOT,
+			      RPFN_FNGET(new_rpfn,
+				 PCI_FUNC(dev->path.pci.devfn)));
+
+		if (dev->path.pci.devfn != new_devfn) {
+			printk(BIOS_DEBUG,
+			       "ICH: PCIe map %02x.%1x -> %02x.%1x\n",
+			       PCI_SLOT(dev->path.pci.devfn),
+			       PCI_FUNC(dev->path.pci.devfn),
+			       PCI_SLOT(new_devfn), PCI_FUNC(new_devfn));
+
+			dev->path.pci.devfn = new_devfn;
+		}
+	}
+}
+
+/* Swap function numbers assigned to two PCIe Root Ports */
+static void ich_pcie_function_swap(u8 old_fn, u8 new_fn)
+{
+	u32 old_rpfn = new_rpfn;
+
+	printk(BIOS_DEBUG, "ICH: Remap PCIe function %d to %d\n",
+	       old_fn, new_fn);
+
+	new_rpfn &= ~(RPFN_FNMASK(old_fn) | RPFN_FNMASK(new_fn));
+
+	/* Old function set to new function and disabled */
+	new_rpfn |= RPFN_FNSET(old_fn, RPFN_FNGET(old_rpfn, new_fn));
+	new_rpfn |= RPFN_FNSET(new_fn, RPFN_FNGET(old_rpfn, old_fn));
+}
+
+/* Special handling for PCIe Root Port devices */
+static void ich_pcie_enable(struct device *dev)
+{
+	struct southbridge_intel_i82801gx_config *config = dev->chip_info;
+	u32 reg32;
+	static int pcie_port_coalesce = 0;
+	static int port_coalescence_done = 0;
+
+	if (!config)
+		return;
+
+	/*
+	 * Save a copy of the Root Port Function Number map when
+	 * starting to walk the list of PCIe Root Ports so it can
+	 * be updated locally and written out when the last port
+	 * has been processed.
+	 */
+	if (PCI_FUNC(dev->path.pci.devfn) == 0) {
+		new_rpfn = RCBA32(RPFN);
+
+		/*
+		 * Enable Root Port coalescing if the first port is disabled
+		 * or the other devices will not be enumerated by the OS.
+		 */
+		if (!dev->enabled)
+			pcie_port_coalesce = 1;
+
+		if (pcie_port_coalesce)
+			printk(BIOS_INFO,
+			       "ICH: PCIe Root Port coalescing is enabled\n");
+	}
+
+	if (!dev->enabled) {
+		printk(BIOS_DEBUG, "%s: Disabling device\n",  dev_path(dev));
+
+		/* Ensure memory, io, and bus master are all disabled */
+		reg32 = pci_read_config32(dev, PCI_COMMAND);
+		reg32 &= ~(PCI_COMMAND_MASTER |
+			   PCI_COMMAND_MEMORY | PCI_COMMAND_IO);
+		pci_write_config32(dev, PCI_COMMAND, reg32);
+
+		/* Hide this device if possible */
+		ich_hide_devfn(dev->path.pci.devfn);
+	} else {
+		/*
+		 * Check if there is a lower disabled port to swap with this
+		 * port in order to maintain linear order starting at zero.
+		 */
+		if (pcie_port_coalesce && !port_coalescence_done) {
+			/* Swap places with this function */
+			ich_pcie_function_swap(PCI_FUNC(dev->path.pci.devfn),
+					       0);
+			port_coalescence_done = 1;
+		}
+
+		/* Enable SERR */
+		reg32 = pci_read_config32(dev, PCI_COMMAND);
+		reg32 |= PCI_COMMAND_SERR;
+		pci_write_config32(dev, PCI_COMMAND, reg32);
+	}
+
+	/*
+	 * When processing the last PCIe root port we can now
+	 * update the Root Port Function Number and Hide register.
+	 */
+	if (PCI_FUNC(dev->path.pci.devfn) == 5) {
+		printk(BIOS_SPEW, "ICH: RPFN 0x%08x -> 0x%08x\n",
+		       RCBA32(RPFN), new_rpfn);
+		RCBA32(RPFN) = new_rpfn;
+
+		/* Update static devictree with new function numbers */
+		if (pcie_port_coalesce)
+			ich_pcie_devicetree_update(config);
+	}
+}
+
 void i82801gx_enable(struct device *dev)
 {
 	u32 reg32;
 
-	/* Enable SERR */
-	reg32 = pci_read_config32(dev, PCI_COMMAND);
-	reg32 |= PCI_COMMAND_SERR;
-	pci_write_config32(dev, PCI_COMMAND, reg32);
+	/* ICH PCIe Root Ports get special handling */
+	if (PCI_SLOT(dev->path.pci.devfn) == ICH_PCIE_DEV_SLOT)
+		return ich_pcie_enable(dev);
 
-	if (dev->path.pci.devfn == PCI_DEVFN(31, 2)) {
-		printk(BIOS_DEBUG, "Set SATA mode early\n");
-		sata_enable(dev);
+	if (!dev->enabled) {
+		printk(BIOS_DEBUG, "%s: Disabling device\n",  dev_path(dev));
+
+		/* Ensure memory, io, and bus master are all disabled */
+		reg32 = pci_read_config32(dev, PCI_COMMAND);
+		reg32 &= ~(PCI_COMMAND_MASTER |
+			   PCI_COMMAND_MEMORY | PCI_COMMAND_IO);
+		pci_write_config32(dev, PCI_COMMAND, reg32);
+
+		/* Hide this device if possible */
+		ich_hide_devfn(dev->path.pci.devfn);
+	} else {
+		/* Enable SERR */
+		reg32 = pci_read_config32(dev, PCI_COMMAND);
+		reg32 |= PCI_COMMAND_SERR;
+		pci_write_config32(dev, PCI_COMMAND, reg32);
+
+		if (dev->path.pci.devfn == PCI_DEVFN(31, 2)) {
+			printk(BIOS_DEBUG, "Set SATA mode early\n");
+			sata_enable(dev);
+		}
 	}
 }
 
diff --git a/src/southbridge/intel/i82801gx/i82801gx.h b/src/southbridge/intel/i82801gx/i82801gx.h
index 40c2bb7..c1235ed 100644
--- a/src/southbridge/intel/i82801gx/i82801gx.h
+++ b/src/southbridge/intel/i82801gx/i82801gx.h
@@ -72,6 +72,8 @@
 #define   SEE	(1 << 1)
 #define   PERE	(1 << 0)
 
+#define ICH_PCIE_DEV_SLOT	28
+
 /* PCI Configuration Space (D31:F0): LPC */
 
 #define SERIRQ_CNTL		0x64
@@ -231,6 +233,13 @@
 #define RPC		0x0224	/* 32bit */
 #define RPFN		0x0238	/* 32bit */
 
+/* Get the function number assigned to a Root Port */
+#define RPFN_FNGET(reg, port)    (((reg) >> ((port) * 4)) & 7)
+/* Set the function number for a Root Port */
+#define RPFN_FNSET(port, func)   (((func) & 7) << ((port) * 4))
+/* Root Port function number mask */
+#define RPFN_FNMASK(port)       (7 << ((port) * 4))
+
 #define TRSR		0x1e00	/*  8bit */
 #define TRCR		0x1e10	/* 64bit */
 #define TWDR		0x1e18	/* 64bit */
@@ -273,6 +282,7 @@
 #define FD_PCIE3	(1 << 18)
 #define FD_PCIE2	(1 << 17)
 #define FD_PCIE1	(1 << 16)
+#define ICH_DISABLE_PCIE(x)	(1 << (16 + x))
 #define FD_EHCI		(1 << 15)
 #define FD_LPCB		(1 << 14)
 
@@ -283,6 +293,8 @@
 #define FD_UHCI34	((1 << 10) | FD_UHCI4)
 #define FD_UHCI234	((1 <<  9) | FD_UHCI3)
 #define FD_UHCI1234	((1 <<  8) | FD_UHCI2)
+#define ICH_DISABLE_UHCI(x)	(1 << (8 + x))
+
 
 #define FD_INTLAN	(1 <<  7)
 #define FD_ACMOD	(1 <<  6)

-- 
To view, visit https://review.coreboot.org/c/coreboot/+/30244
To unsubscribe, or for help writing mail filters, visit https://review.coreboot.org/settings

Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-Change-Id: I83576599538a02d295fe00b35826f98d8c97d1cf
Gerrit-Change-Number: 30244
Gerrit-PatchSet: 1
Gerrit-Owner: Arthur Heymans <arthur at aheymans.xyz>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.coreboot.org/pipermail/coreboot-gerrit/attachments/20181215/61a5deef/attachment-0001.html>


More information about the coreboot-gerrit mailing list