diff --git a/src/southbridge/intel/lynxpoint/pch.c b/src/southbridge/intel/lynxpoint/pch.c index f13efb0f7f..b4605f5c02 100644 --- a/src/southbridge/intel/lynxpoint/pch.c +++ b/src/southbridge/intel/lynxpoint/pch.c @@ -289,198 +289,13 @@ void pch_iobp_update(u32 address, u32 andvalue, u32 orvalue) pch_iobp_write(address, data); } -/* Check if any port in set X to X+3 is enabled */ -static int pch_pcie_check_set_enabled(device_t dev) -{ - device_t port; - int port_func; - int dev_func = PCI_FUNC(dev->path.pci.devfn); - - printk(BIOS_DEBUG, "%s: check set enabled\n", dev_path(dev)); - - /* Go through static device tree list of devices - * because enumeration is still in progress */ - for (port = all_devices; port; port = port->next) { - /* Only care about PCIe root ports */ - if (PCI_SLOT(port->path.pci.devfn) != - PCI_SLOT(dev->path.pci.devfn)) - continue; - - /* Check if port is in range and enabled */ - port_func = PCI_FUNC(port->path.pci.devfn); - if (port_func >= dev_func && - port_func < (dev_func + 4) && - port->enabled) - return 1; - } - - /* None of the ports in this set are enabled */ - return 0; -} - -/* 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_lynxpoint_config *config = dev->chip_info; - u32 reg32; - - /* - * 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) - config->pcie_port_coalesce = 1; - - if (config->pcie_port_coalesce) - printk(BIOS_INFO, - "PCH: PCIe Root Port coalescing is enabled\n"); - } - - if (!dev->enabled) { - printk(BIOS_DEBUG, "%s: Disabling device\n", dev_path(dev)); - - /* - * 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 - * - * This check is done here instead of pcie driver - * because the pcie driver enable() handler is not - * called unless the device is enabled. - */ - if ((PCI_FUNC(dev->path.pci.devfn) == 0 || - PCI_FUNC(dev->path.pci.devfn) == 4)) { - /* Handle workaround for PPT and CPT/B1+ */ - if (!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_write_config32(dev, PCI_COMMAND, reg32); - - /* Do not claim downstream transactions for PCIe ports */ - new_rpfn |= RPFN_HIDE(PCI_FUNC(dev->path.pci.devfn)); - - /* Disable this device if possible */ - pch_disable_devfn(dev); - } 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); + return pch_pcie_enable_dev(dev); if (!dev->enabled) { printk(BIOS_DEBUG, "%s: Disabling device\n", dev_path(dev)); diff --git a/src/southbridge/intel/lynxpoint/pch.h b/src/southbridge/intel/lynxpoint/pch.h index 7c187054cc..9acdd1f79c 100644 --- a/src/southbridge/intel/lynxpoint/pch.h +++ b/src/southbridge/intel/lynxpoint/pch.h @@ -166,6 +166,7 @@ void pch_disable_devfn(device_t dev); u32 pch_iobp_read(u32 address); void pch_iobp_write(u32 address, u32 data); void pch_iobp_update(u32 address, u32 andvalue, u32 orvalue); +void pch_pcie_enable_dev(device_t dev); #if CONFIG_ELOG void pch_log_state(void); #endif diff --git a/src/southbridge/intel/lynxpoint/pcie.c b/src/southbridge/intel/lynxpoint/pcie.c index a3b84e3c3f..28f4b6ad15 100644 --- a/src/southbridge/intel/lynxpoint/pcie.c +++ b/src/southbridge/intel/lynxpoint/pcie.c @@ -25,6 +25,191 @@ #include #include "pch.h" +/* Check if any port in set X to X+3 is enabled */ +static int pch_pcie_check_set_enabled(device_t dev) +{ + device_t port; + int port_func; + int dev_func = PCI_FUNC(dev->path.pci.devfn); + + printk(BIOS_DEBUG, "%s: check set enabled\n", dev_path(dev)); + + /* Go through static device tree list of devices + * because enumeration is still in progress */ + for (port = all_devices; port; port = port->next) { + /* Only care about PCIe root ports */ + if (PCI_SLOT(port->path.pci.devfn) != + PCI_SLOT(dev->path.pci.devfn)) + continue; + + /* Check if port is in range and enabled */ + port_func = PCI_FUNC(port->path.pci.devfn); + if (port_func >= dev_func && + port_func < (dev_func + 4) && + port->enabled) + return 1; + } + + /* None of the ports in this set are enabled */ + return 0; +} + +/* 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 */ +void pch_pcie_enable_dev(device_t dev) +{ + struct southbridge_intel_lynxpoint_config *config = dev->chip_info; + u32 reg32; + + /* + * 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) + config->pcie_port_coalesce = 1; + + if (config->pcie_port_coalesce) + printk(BIOS_INFO, + "PCH: PCIe Root Port coalescing is enabled\n"); + } + + if (!dev->enabled) { + printk(BIOS_DEBUG, "%s: Disabling device\n", dev_path(dev)); + + /* + * 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 + * + * This check is done here instead of pcie driver + * because the pcie driver enable() handler is not + * called unless the device is enabled. + */ + if ((PCI_FUNC(dev->path.pci.devfn) == 0 || + PCI_FUNC(dev->path.pci.devfn) == 4)) { + /* Handle workaround for PPT and CPT/B1+ */ + if (!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_write_config32(dev, PCI_COMMAND, reg32); + + /* Do not claim downstream transactions for PCIe ports */ + new_rpfn |= RPFN_HIDE(PCI_FUNC(dev->path.pci.devfn)); + + /* Disable this device if possible */ + pch_disable_devfn(dev); + } 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(); + } +} + static void pch_pcie_pm_early(struct device *dev) { /* RPC has been moved. It is in PCI config space now. */