PCI: read memory ranges out of Broadcom CNB20LE host bridge
authorIra W. Snyder <iws@ovro.caltech.edu>
Thu, 1 Apr 2010 18:43:30 +0000 (11:43 -0700)
committerJesse Barnes <jbarnes@virtuousgeek.org>
Fri, 21 May 2010 21:43:46 +0000 (14:43 -0700)
Read the memory ranges behind the Broadcom CNB20LE host bridge out of the
hardware. This allows PCI hotplugging to work, since we know which memory
range to allocate PCI BAR's from.

The x86 PCI code automatically prefers the ACPI _CRS information when it is
available. In that case, this information is not used.

Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
arch/x86/Kconfig
arch/x86/pci/Makefile
arch/x86/pci/broadcom_bus.c [new file with mode: 0644]

index 9458685..677b87d 100644 (file)
@@ -1931,6 +1931,14 @@ config PCI_MMCONFIG
        bool "Support mmconfig PCI config space access"
        depends on X86_64 && PCI && ACPI
 
+config PCI_CNB20LE_QUIRK
+       bool "Read CNB20LE Host Bridge Windows"
+       depends on PCI
+       help
+         Read the PCI windows out of the CNB20LE host bridge. This allows
+         PCI hotplug to work on systems with the CNB20LE chipset which do
+         not have ACPI.
+
 config DMAR
        bool "Support for DMA Remapping Devices (EXPERIMENTAL)"
        depends on PCI_MSI && ACPI && EXPERIMENTAL
index b110d97..a0207a7 100644 (file)
@@ -18,6 +18,8 @@ obj-$(CONFIG_X86_MRST)                += mrst.o
 obj-y                          += common.o early.o
 obj-y                          += amd_bus.o bus_numa.o
 
+obj-$(CONFIG_PCI_CNB20LE_QUIRK)        += broadcom_bus.o
+
 ifeq ($(CONFIG_PCI_DEBUG),y)
 EXTRA_CFLAGS += -DDEBUG
 endif
diff --git a/arch/x86/pci/broadcom_bus.c b/arch/x86/pci/broadcom_bus.c
new file mode 100644 (file)
index 0000000..0846a5b
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Read address ranges from a Broadcom CNB20LE Host Bridge
+ *
+ * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu>
+ *
+ * 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 the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/dmi.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <asm/pci_x86.h>
+
+#include "bus_numa.h"
+
+static void __devinit cnb20le_res(struct pci_dev *dev)
+{
+       struct pci_root_info *info;
+       struct resource res;
+       u16 word1, word2;
+       u8 fbus, lbus;
+       int i;
+
+       /*
+        * The x86_pci_root_bus_res_quirks() function already refuses to use
+        * this information if ACPI _CRS was used. Therefore, we don't bother
+        * checking if ACPI is enabled, and just generate the information
+        * for both the ACPI _CRS and no ACPI cases.
+        */
+
+       info = &pci_root_info[pci_root_num];
+       pci_root_num++;
+
+       /* read the PCI bus numbers */
+       pci_read_config_byte(dev, 0x44, &fbus);
+       pci_read_config_byte(dev, 0x45, &lbus);
+       info->bus_min = fbus;
+       info->bus_max = lbus;
+
+       /*
+        * Add the legacy IDE ports on bus 0
+        *
+        * These do not exist anywhere in the bridge registers, AFAICT. I do
+        * not have the datasheet, so this is the best I can do.
+        */
+       if (fbus == 0) {
+               update_res(info, 0x01f0, 0x01f7, IORESOURCE_IO, 0);
+               update_res(info, 0x03f6, 0x03f6, IORESOURCE_IO, 0);
+               update_res(info, 0x0170, 0x0177, IORESOURCE_IO, 0);
+               update_res(info, 0x0376, 0x0376, IORESOURCE_IO, 0);
+               update_res(info, 0xffa0, 0xffaf, IORESOURCE_IO, 0);
+       }
+
+       /* read the non-prefetchable memory window */
+       pci_read_config_word(dev, 0xc0, &word1);
+       pci_read_config_word(dev, 0xc2, &word2);
+       if (word1 != word2) {
+               res.start = (word1 << 16) | 0x0000;
+               res.end   = (word2 << 16) | 0xffff;
+               res.flags = IORESOURCE_MEM;
+               update_res(info, res.start, res.end, res.flags, 0);
+       }
+
+       /* read the prefetchable memory window */
+       pci_read_config_word(dev, 0xc4, &word1);
+       pci_read_config_word(dev, 0xc6, &word2);
+       if (word1 != word2) {
+               res.start = (word1 << 16) | 0x0000;
+               res.end   = (word2 << 16) | 0xffff;
+               res.flags = IORESOURCE_MEM | IORESOURCE_PREFETCH;
+               update_res(info, res.start, res.end, res.flags, 0);
+       }
+
+       /* read the IO port window */
+       pci_read_config_word(dev, 0xd0, &word1);
+       pci_read_config_word(dev, 0xd2, &word2);
+       if (word1 != word2) {
+               res.start = word1;
+               res.end   = word2;
+               res.flags = IORESOURCE_IO;
+               update_res(info, res.start, res.end, res.flags, 0);
+       }
+
+       /* print information about this host bridge */
+       res.start = fbus;
+       res.end   = lbus;
+       res.flags = IORESOURCE_BUS;
+       dev_info(&dev->dev, "CNB20LE PCI Host Bridge (domain %04x %pR)\n",
+                           pci_domain_nr(dev->bus), &res);
+
+       for (i = 0; i < info->res_num; i++)
+               dev_info(&dev->dev, "host bridge window %pR\n", &info->res[i]);
+}
+
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_LE,
+                       cnb20le_res);
+