[coreboot] New patch to review for coreboot: 050c40f Add an option to enable PCIe root port coalescing

Stefan Reinauer (stefan.reinauer@coreboot.org) gerrit at coreboot.org
Tue May 1 01:50:35 CEST 2012


Stefan Reinauer (stefan.reinauer at coreboot.org) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/979

-gerrit

commit 050c40f1dcee3945709146abf0c8cfd47b4abca3
Author: Duncan Laurie <dlaurie at chromium.org>
Date:   Fri Apr 27 10:30:51 2012 -0700

    Add an option to enable PCIe root port coalescing
    
    Background: The PCI spec (3.0-3.2.2.3.4) requires that PCI devices
    implement function 0.  The Linux Kernel therefore will not enumerate
    a PCI device if it does not present a valid config space at function 0.
    
    If a board does not have anything connected to root port 0 and it is
    desired to disable the unused ports in order to save power then this
    will cause the other downstream PCIe devices to go missing as they
    will not be enumerated.
    
    Intel chipsets provide a way to map root port numbers to different PCI
    function numbers, thereby avoiding this issue and allowing root port 0
    to be turned off.
    
    This change adds a new chip config option 'pcie_port_coalesce' that
    will collapse the enabled root ports into a linear map starting at
    zero.  This option defaults to disabled as it can have a confusing
    effect on the system as the declared static devicetree may not match
    what is seen at runtime.  This option is also forced on if the static
    devicetree disables port 0.
    
    When each root port is processed in the early enable stage it looks
    for a lower numbered root port that has been disabled and then swaps
    the two assigned function numbers.
    
    However the mapping register is write-once so it has to keep track of
    the proposed mapping changes until all ports have been processed
    before writing out the final map value.  At this point it also updates
    the function numbers in the static device tree so they are consistent
    with the new layout.
    
    There are a few other closely related fixes in this change:
    
    1) There is a power savings opportunity if an entire bank of ports
    (0-3 or 4-7) are disabled.  This was checking the chipset revision to
    look for CougarPoint B1+ stepping and that was not passing on
    PantherPoint where this should always be applied.  To fix this I added
    a function to determine the chipset type based on comparing the upper
    byte of the device ID.
    
    2) Apply the same chipset type check fix to the IOBP programming.
    
    3) There is another power savings opportunity to enable dynamic clock
    gating on shared PCIe resources which only applies to ports 0 and 4.
    However if 0 or 4 is disabled then the later check to enable this
    would fail as that device is already hidden.
    
    LUMPY current:
    
      00:1c.0 PCI bridge: Intel Corporation Device 1c10 (rev b5)
      00:1c.3 PCI bridge: Intel Corporation Device 1c16 (rev b5)
      01:00.0 Network controller: Atheros Communications Inc. Device 0030 (rev 01)
      02:00.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL8111/8168B
    
    LUMPY with PCIe port coalesce enabled:
    
      00:1c.0 PCI bridge: Intel Corporation Device 1c10 (rev b5)
      00:1c.1 PCI bridge: Intel Corporation Device 1c16 (rev b5)
      01:00.0 Network controller: Atheros Communications Inc. Device 0030 (rev 01)
      02:00.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL8111/8168B
    
    Change-Id: I828aa407fdc9c156c1c42eda8e2d893c0aa66eef
    Signed-off-by: Duncan Laurie <dlaurie at chromium.org>
---
 src/southbridge/intel/bd82x6x/chip.h |    3 +
 src/southbridge/intel/bd82x6x/pch.c  |  196 ++++++++++++++++++++++++++++++++--
 src/southbridge/intel/bd82x6x/pch.h  |   21 +++-
 3 files changed, 206 insertions(+), 14 deletions(-)

diff --git a/src/southbridge/intel/bd82x6x/chip.h b/src/southbridge/intel/bd82x6x/chip.h
index 2786d72..880244b 100644
--- a/src/southbridge/intel/bd82x6x/chip.h
+++ b/src/southbridge/intel/bd82x6x/chip.h
@@ -72,6 +72,9 @@ struct southbridge_intel_bd82x6x_config {
 	uint32_t gen2_dec;
 	uint32_t gen3_dec;
 	uint32_t gen4_dec;
+
+	/* Enable linear PCIe Root Port function numbers starting at zero */
+	uint8_t pcie_port_coalesce;
 };
 
 extern struct chip_operations southbridge_intel_bd82x6x_ops;
diff --git a/src/southbridge/intel/bd82x6x/pch.c b/src/southbridge/intel/bd82x6x/pch.c
index e83c2e6..5f440e6 100644
--- a/src/southbridge/intel/bd82x6x/pch.c
+++ b/src/southbridge/intel/bd82x6x/pch.c
@@ -2,6 +2,7 @@
  * This file is part of the coreboot project.
  *
  * Copyright (C) 2008-2009 coresystems GmbH
+ * Copyright (C) 2012 The Chromium OS Authors.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
@@ -25,6 +26,7 @@
 #include "pch.h"
 
 static int pch_revision_id = -1;
+static int pch_type = -1;
 
 int pch_silicon_revision(void)
 {
@@ -35,6 +37,40 @@ int pch_silicon_revision(void)
 	return pch_revision_id;
 }
 
+int pch_silicon_type(void)
+{
+	if (pch_type < 0)
+		pch_type = pci_read_config8(
+			dev_find_slot(0, PCI_DEVFN(0x1f, 0)),
+			PCI_DEVICE_ID + 1);
+	return pch_type;
+}
+
+int pch_silicon_supported(int type, int rev)
+{
+	int cur_type = pch_silicon_type();
+	int cur_rev = pch_silicon_revision();
+
+	switch (type) {
+	case PCH_TYPE_CPT:
+		/* CougarPoint minimum revision */
+		if (cur_type == PCH_TYPE_CPT && cur_rev >= rev)
+			return 1;
+		/* PantherPoint any revision */
+		if (cur_type == PCH_TYPE_PPT)
+			return 1;
+		break;
+
+	case PCH_TYPE_PPT:
+		/* PantherPoint minimum revision */
+		if (cur_type == PCH_TYPE_PPT && cur_rev >= rev)
+			return 1;
+		break;
+	}
+
+	return 0;
+}
+
 /* Set bit in Function Disble register to hide this device */
 static void pch_hide_devfn(unsigned devfn)
 {
@@ -119,7 +155,7 @@ void pch_iobp_update(u32 address, u32 andvalue, u32 orvalue)
 	RCBA32(IOBPIRI) = address;
 
 	/* READ OPCODE */
-	if (pch_silicon_revision() >= PCH_STEP_B0)
+	if (pch_silicon_supported(PCH_TYPE_CPT, PCH_STEP_B0))
 		RCBA32(IOBPS) = IOBPS_RW_BX;
 	else
 		RCBA32(IOBPS) = IOBPS_READ_AX;
@@ -142,7 +178,7 @@ void pch_iobp_update(u32 address, u32 andvalue, u32 orvalue)
 	data |= orvalue;
 
 	/* WRITE OPCODE */
-	if (pch_silicon_revision() >= PCH_STEP_B0)
+	if (pch_silicon_supported(PCH_TYPE_CPT, PCH_STEP_B0))
 		RCBA32(IOBPS) = IOBPS_RW_BX;
 	else
 		RCBA32(IOBPS) = IOBPS_WRITE_AX;
@@ -184,15 +220,85 @@ static int pch_pcie_check_set_enabled(device_t dev)
 	return 0;
 }
 
-void pch_enable(device_t dev)
+/* RPFN is a write-once register so keep a copy until it is written */
+static u32 new_rpfn;
+
+/* Swap function numbers assigned to two PCIe Root Ports */
+static void pch_pcie_function_swap(u8 old_fn, u8 new_fn)
+{
+	u32 old_rpfn = new_rpfn;
+
+	printk(BIOS_DEBUG, "PCH: 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));
+}
+
+/* Update devicetree with new Root Port function number assignment */
+static void pch_pcie_devicetree_update(void)
+{
+	device_t dev;
+
+	/* Update the function numbers in the static devicetree */
+	for (dev = all_devices; dev; dev = dev->next) {
+		u8 new_devfn;
+
+		/* Only care about PCH PCIe root ports */
+		if (PCI_SLOT(dev->path.pci.devfn) !=
+		    PCH_PCIE_DEV_SLOT)
+			continue;
+
+		/* Determine the new devfn for this port */
+		new_devfn = PCI_DEVFN(PCH_PCIE_DEV_SLOT,
+			      RPFN_FNGET(new_rpfn,
+				 PCI_FUNC(dev->path.pci.devfn)));
+
+		if (dev->path.pci.devfn != new_devfn) {
+			printk(BIOS_DEBUG,
+			       "PCH: 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;
+		}
+	}
+}
+
+/* Special handling for PCIe Root Port devices */
+static void pch_pcie_enable(device_t dev)
 {
+	struct southbridge_intel_bd82x6x_config *config = dev->chip_info;
 	u32 reg32;
 
-	if (!dev->enabled) {
-		printk(BIOS_DEBUG, "%s: Disabling device\n",  dev_path(dev));
+	/*
+	 * 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);
 
 		/*
-		 * PCIE Power Savings for stepping B1+:
+		 * Enable Root Port coalescing if the first port is disabled
+		 * or the other devices will not be enumerated by the OS.
+		 */
+		if (!dev->enabled)
+			config->pcie_port_coalesce = 1;
+
+		if (config->pcie_port_coalesce)
+			printk(BIOS_INFO,
+			       "PCH: PCIe Root Port coalescing is enabled\n");
+	}
+
+	if (!dev->enabled) {
+		/*
+		 * PCIE Power Savings for PantherPoint and CougarPoint/B1+
 		 *
 		 * If PCIe 0-3 disabled set Function 0 0xE2[0] = 1
 		 * If PCIe 4-7 disabled set Function 4 0xE2[0] = 1
@@ -201,21 +307,89 @@ void pch_enable(device_t dev)
 		 * because the pcie driver enable() handler is not
 		 * called unless the device is enabled.
 		 */
-		if (pch_silicon_revision() >= PCH_STEP_B1 &&
-		    PCI_SLOT(dev->path.pci.devfn) == PCH_PCIE_DEV_SLOT &&
-		    (PCI_FUNC(dev->path.pci.devfn) == 0 ||
+		if ((PCI_FUNC(dev->path.pci.devfn) == 0 ||
 		     PCI_FUNC(dev->path.pci.devfn) == 4)) {
-			if (!pch_pcie_check_set_enabled(dev)) {
+			/* Handle workaround for PPT and CPT/B1+ */
+			if (pch_silicon_supported(PCH_TYPE_CPT, PCH_STEP_B1) &&
+			    !pch_pcie_check_set_enabled(dev)) {
 				u8 reg8 = pci_read_config8(dev, 0xe2);
 				reg8 |= 1;
 				pci_write_config8(dev, 0xe2, reg8);
 			}
+
+			/*
+			 * Enable Clock Gating for shared PCIe resources
+			 * before disabling this particular port.
+			 */
+			pci_write_config8(dev, 0xe1, 0x3c);
 		}
 
 		/* 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_COMMAND_MEMORY | PCI_COMMAND_IO);
+		pci_write_config32(dev, PCI_COMMAND, reg32);
+
+		/* Do not claim downstream transactions for PCIe ports */
+		new_rpfn |= RPFN_HIDE(PCI_FUNC(dev->path.pci.devfn));
+
+		/* Hide this device if possible */
+		pch_hide_devfn(dev->path.pci.devfn);
+	} else {
+		int fn;
+
+		/*
+		 * Check if there is a lower disabled port to swap with this
+		 * port in order to maintain linear order starting at zero.
+		 */
+		if (config->pcie_port_coalesce) {
+			for (fn=0; fn < PCI_FUNC(dev->path.pci.devfn); fn++) {
+				if (!(new_rpfn & RPFN_HIDE(fn)))
+					continue;
+
+				/* Swap places with this function */
+				pch_pcie_function_swap(
+					PCI_FUNC(dev->path.pci.devfn), fn);
+				break;
+			}
+		}
+
+		/* 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) == 7) {
+		printk(BIOS_SPEW, "PCH: RPFN 0x%08x -> 0x%08x\n",
+		       RCBA32(RPFN), new_rpfn);
+		RCBA32(RPFN) = new_rpfn;
+
+		/* Update static devictree with new function numbers */
+		if (config->pcie_port_coalesce)
+			pch_pcie_devicetree_update();
+	}
+}
+
+void pch_enable(device_t dev)
+{
+	u32 reg32;
+
+	/* PCH PCIe Root Ports get special handling */
+	if (PCI_SLOT(dev->path.pci.devfn) == PCH_PCIE_DEV_SLOT)
+		return pch_pcie_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 */
diff --git a/src/southbridge/intel/bd82x6x/pch.h b/src/southbridge/intel/bd82x6x/pch.h
index 80b09e2..7b646f3 100644
--- a/src/southbridge/intel/bd82x6x/pch.h
+++ b/src/southbridge/intel/bd82x6x/pch.h
@@ -2,7 +2,7 @@
  * This file is part of the coreboot project.
  *
  * Copyright (C) 2008-2009 coresystems GmbH
- * Copyright (C) 2011 Google Inc.
+ * Copyright (C) 2012 The Chromium OS Authors.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -21,6 +21,10 @@
 #ifndef SOUTHBRIDGE_INTEL_BD82X6X_PCH_H
 #define SOUTHBRIDGE_INTEL_BD82X6X_PCH_H
 
+/* PCH types */
+#define PCH_TYPE_CPT	0x1c /* CougarPoint */
+#define PCH_TYPE_PPT	0x1e /* IvyBridge */
+
 /* PCH stepping values for LPC device */
 #define PCH_STEP_A0	0
 #define PCH_STEP_A1	1
@@ -57,6 +61,8 @@ void intel_pch_finalize_smm(void);
 #if !defined(__PRE_RAM__) && !defined(__SMM__)
 #include "chip.h"
 int pch_silicon_revision(void);
+int pch_silicon_type(void);
+int pch_silicon_supported(int type, int rev);
 void pch_enable(device_t dev);
 void pch_iobp_update(u32 address, u32 andvalue, u32 orvalue);
 #else
@@ -270,8 +276,17 @@ int smbus_read_byte(unsigned device, unsigned address);
 #define RP6D		0x0180	/* 32bit */
 #define RP6BA		0x0188	/* 64bit */
 
-#define RPC		0x0224	/* 32bit */
-#define RPFN		0x0238	/* 32bit */
+#define RPC		0x0400	/* 32bit */
+#define RPFN		0x0404	/* 32bit */
+
+/* Root Port configuratinon space hide */
+#define RPFN_HIDE(port)         (1 << (((port) * 4) + 3))
+/* 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 */




More information about the coreboot mailing list