aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/southbridge/intel/bd82x6x/chip.h3
-rw-r--r--src/southbridge/intel/bd82x6x/pch.c196
-rw-r--r--src/southbridge/intel/bd82x6x/pch.h21
3 files changed, 206 insertions, 14 deletions
diff --git a/src/southbridge/intel/bd82x6x/chip.h b/src/southbridge/intel/bd82x6x/chip.h
index 2786d72359..880244b6a7 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 e83c2e6478..5f440e61cd 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 80b09e2592..7b646f3f5d 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 */