aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2009-04-01 09:47:12 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2009-04-01 09:47:12 -0700
commite76e5b2c663ac74ae6a542ac20795c625e36a5cd (patch)
tree2e7271be1f3a26832f4b121839fc4044fbbf27a6 /arch
parentMerge branch 'for-linus' of git://git390.marist.edu/pub/scm/linux-2.6 (diff)
parentPCI: fix HT MSI mapping fix (diff)
downloadlinux-dev-e76e5b2c663ac74ae6a542ac20795c625e36a5cd.tar.xz
linux-dev-e76e5b2c663ac74ae6a542ac20795c625e36a5cd.zip
Merge branch 'linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci-2.6
* 'linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci-2.6: (88 commits) PCI: fix HT MSI mapping fix PCI: don't enable too much HT MSI mapping x86/PCI: make pci=lastbus=255 work when acpi is on PCI: save and restore PCIe 2.0 registers PCI: update fakephp for bus_id removal PCI: fix kernel oops on bridge removal PCI: fix conflict between SR-IOV and config space sizing powerpc/PCI: include pci.h in powerpc MSI implementation PCI Hotplug: schedule fakephp for feature removal PCI Hotplug: rename legacy_fakephp to fakephp PCI Hotplug: restore fakephp interface with complete reimplementation PCI: Introduce /sys/bus/pci/devices/.../rescan PCI: Introduce /sys/bus/pci/devices/.../remove PCI: Introduce /sys/bus/pci/rescan PCI: Introduce pci_rescan_bus() PCI: do not enable bridges more than once PCI: do not initialize bridges more than once PCI: always scan child buses PCI: pci_scan_slot() returns newly found devices PCI: don't scan existing devices ... Fix trivial append-only conflict in Documentation/feature-removal-schedule.txt
Diffstat (limited to 'arch')
-rw-r--r--arch/alpha/include/asm/pci.h14
-rw-r--r--arch/alpha/kernel/Makefile2
-rw-r--r--arch/alpha/kernel/pci-sysfs.c366
-rw-r--r--arch/powerpc/include/asm/pci.h4
-rw-r--r--arch/powerpc/kernel/msi.c5
-rw-r--r--arch/x86/include/asm/pci.h3
-rw-r--r--arch/x86/kernel/apic/io_apic.c4
-rw-r--r--arch/x86/kernel/pci-dma.c3
-rw-r--r--arch/x86/pci/early.c19
-rw-r--r--arch/x86/pci/fixup.c20
-rw-r--r--arch/x86/pci/legacy.c3
-rw-r--r--arch/x86/pci/mmconfig-shared.c227
-rw-r--r--arch/x86/pci/mmconfig_64.c17
13 files changed, 594 insertions, 93 deletions
diff --git a/arch/alpha/include/asm/pci.h b/arch/alpha/include/asm/pci.h
index 2a14302c17a3..cb04eaa6ba33 100644
--- a/arch/alpha/include/asm/pci.h
+++ b/arch/alpha/include/asm/pci.h
@@ -273,4 +273,18 @@ struct pci_dev *alpha_gendev_to_pci(struct device *dev);
extern struct pci_dev *isa_bridge;
+extern int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val,
+ size_t count);
+extern int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val,
+ size_t count);
+extern int pci_mmap_legacy_page_range(struct pci_bus *bus,
+ struct vm_area_struct *vma,
+ enum pci_mmap_state mmap_state);
+extern void pci_adjust_legacy_attr(struct pci_bus *bus,
+ enum pci_mmap_state mmap_type);
+#define HAVE_PCI_LEGACY 1
+
+extern int pci_create_resource_files(struct pci_dev *dev);
+extern void pci_remove_resource_files(struct pci_dev *dev);
+
#endif /* __ALPHA_PCI_H */
diff --git a/arch/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile
index b4697759a123..a427538252f8 100644
--- a/arch/alpha/kernel/Makefile
+++ b/arch/alpha/kernel/Makefile
@@ -12,7 +12,7 @@ obj-y := entry.o traps.o process.o init_task.o osf_sys.o irq.o \
obj-$(CONFIG_VGA_HOSE) += console.o
obj-$(CONFIG_SMP) += smp.o
-obj-$(CONFIG_PCI) += pci.o pci_iommu.o
+obj-$(CONFIG_PCI) += pci.o pci_iommu.o pci-sysfs.o
obj-$(CONFIG_SRM_ENV) += srm_env.o
obj-$(CONFIG_MODULES) += module.o
diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c
new file mode 100644
index 000000000000..6ea822e7f724
--- /dev/null
+++ b/arch/alpha/kernel/pci-sysfs.c
@@ -0,0 +1,366 @@
+/*
+ * arch/alpha/kernel/pci-sysfs.c
+ *
+ * Copyright (C) 2009 Ivan Kokshaysky
+ *
+ * Alpha PCI resource files.
+ *
+ * Loosely based on generic HAVE_PCI_MMAP implementation in
+ * drivers/pci/pci-sysfs.c
+ */
+
+#include <linux/sched.h>
+#include <linux/pci.h>
+
+static int hose_mmap_page_range(struct pci_controller *hose,
+ struct vm_area_struct *vma,
+ enum pci_mmap_state mmap_type, int sparse)
+{
+ unsigned long base;
+
+ if (mmap_type == pci_mmap_mem)
+ base = sparse ? hose->sparse_mem_base : hose->dense_mem_base;
+ else
+ base = sparse ? hose->sparse_io_base : hose->dense_io_base;
+
+ vma->vm_pgoff += base >> PAGE_SHIFT;
+ vma->vm_flags |= (VM_IO | VM_RESERVED);
+
+ return io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot);
+}
+
+static int __pci_mmap_fits(struct pci_dev *pdev, int num,
+ struct vm_area_struct *vma, int sparse)
+{
+ unsigned long nr, start, size;
+ int shift = sparse ? 5 : 0;
+
+ nr = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+ start = vma->vm_pgoff;
+ size = ((pci_resource_len(pdev, num) - 1) >> (PAGE_SHIFT - shift)) + 1;
+
+ if (start < size && size - start >= nr)
+ return 1;
+ WARN(1, "process \"%s\" tried to map%s 0x%08lx-0x%08lx on %s BAR %d "
+ "(size 0x%08lx)\n",
+ current->comm, sparse ? " sparse" : "", start, start + nr,
+ pci_name(pdev), num, size);
+ return 0;
+}
+
+/**
+ * pci_mmap_resource - map a PCI resource into user memory space
+ * @kobj: kobject for mapping
+ * @attr: struct bin_attribute for the file being mapped
+ * @vma: struct vm_area_struct passed into the mmap
+ * @sparse: address space type
+ *
+ * Use the bus mapping routines to map a PCI resource into userspace.
+ */
+static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
+ struct vm_area_struct *vma, int sparse)
+{
+ struct pci_dev *pdev = to_pci_dev(container_of(kobj,
+ struct device, kobj));
+ struct resource *res = (struct resource *)attr->private;
+ enum pci_mmap_state mmap_type;
+ struct pci_bus_region bar;
+ int i;
+
+ for (i = 0; i < PCI_ROM_RESOURCE; i++)
+ if (res == &pdev->resource[i])
+ break;
+ if (i >= PCI_ROM_RESOURCE)
+ return -ENODEV;
+
+ if (!__pci_mmap_fits(pdev, i, vma, sparse))
+ return -EINVAL;
+
+ if (iomem_is_exclusive(res->start))
+ return -EINVAL;
+
+ pcibios_resource_to_bus(pdev, &bar, res);
+ vma->vm_pgoff += bar.start >> (PAGE_SHIFT - (sparse ? 5 : 0));
+ mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io;
+
+ return hose_mmap_page_range(pdev->sysdata, vma, mmap_type, sparse);
+}
+
+static int pci_mmap_resource_sparse(struct kobject *kobj,
+ struct bin_attribute *attr,
+ struct vm_area_struct *vma)
+{
+ return pci_mmap_resource(kobj, attr, vma, 1);
+}
+
+static int pci_mmap_resource_dense(struct kobject *kobj,
+ struct bin_attribute *attr,
+ struct vm_area_struct *vma)
+{
+ return pci_mmap_resource(kobj, attr, vma, 0);
+}
+
+/**
+ * pci_remove_resource_files - cleanup resource files
+ * @dev: dev to cleanup
+ *
+ * If we created resource files for @dev, remove them from sysfs and
+ * free their resources.
+ */
+void pci_remove_resource_files(struct pci_dev *pdev)
+{
+ int i;
+
+ for (i = 0; i < PCI_ROM_RESOURCE; i++) {
+ struct bin_attribute *res_attr;
+
+ res_attr = pdev->res_attr[i];
+ if (res_attr) {
+ sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
+ kfree(res_attr);
+ }
+
+ res_attr = pdev->res_attr_wc[i];
+ if (res_attr) {
+ sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
+ kfree(res_attr);
+ }
+ }
+}
+
+static int sparse_mem_mmap_fits(struct pci_dev *pdev, int num)
+{
+ struct pci_bus_region bar;
+ struct pci_controller *hose = pdev->sysdata;
+ long dense_offset;
+ unsigned long sparse_size;
+
+ pcibios_resource_to_bus(pdev, &bar, &pdev->resource[num]);
+
+ /* All core logic chips have 4G sparse address space, except
+ CIA which has 16G (see xxx_SPARSE_MEM and xxx_DENSE_MEM
+ definitions in asm/core_xxx.h files). This corresponds
+ to 128M or 512M of the bus space. */
+ dense_offset = (long)(hose->dense_mem_base - hose->sparse_mem_base);
+ sparse_size = dense_offset >= 0x400000000UL ? 0x20000000 : 0x8000000;
+
+ return bar.end < sparse_size;
+}
+
+static int pci_create_one_attr(struct pci_dev *pdev, int num, char *name,
+ char *suffix, struct bin_attribute *res_attr,
+ unsigned long sparse)
+{
+ size_t size = pci_resource_len(pdev, num);
+
+ sprintf(name, "resource%d%s", num, suffix);
+ res_attr->mmap = sparse ? pci_mmap_resource_sparse :
+ pci_mmap_resource_dense;
+ res_attr->attr.name = name;
+ res_attr->attr.mode = S_IRUSR | S_IWUSR;
+ res_attr->size = sparse ? size << 5 : size;
+ res_attr->private = &pdev->resource[num];
+ return sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
+}
+
+static int pci_create_attr(struct pci_dev *pdev, int num)
+{
+ /* allocate attribute structure, piggyback attribute name */
+ int retval, nlen1, nlen2 = 0, res_count = 1;
+ unsigned long sparse_base, dense_base;
+ struct bin_attribute *attr;
+ struct pci_controller *hose = pdev->sysdata;
+ char *suffix, *attr_name;
+
+ suffix = ""; /* Assume bwx machine, normal resourceN files. */
+ nlen1 = 10;
+
+ if (pdev->resource[num].flags & IORESOURCE_MEM) {
+ sparse_base = hose->sparse_mem_base;
+ dense_base = hose->dense_mem_base;
+ if (sparse_base && !sparse_mem_mmap_fits(pdev, num)) {
+ sparse_base = 0;
+ suffix = "_dense";
+ nlen1 = 16; /* resourceN_dense */
+ }
+ } else {
+ sparse_base = hose->sparse_io_base;
+ dense_base = hose->dense_io_base;
+ }
+
+ if (sparse_base) {
+ suffix = "_sparse";
+ nlen1 = 17;
+ if (dense_base) {
+ nlen2 = 16; /* resourceN_dense */
+ res_count = 2;
+ }
+ }
+
+ attr = kzalloc(sizeof(*attr) * res_count + nlen1 + nlen2, GFP_ATOMIC);
+ if (!attr)
+ return -ENOMEM;
+
+ /* Create bwx, sparse or single dense file */
+ attr_name = (char *)(attr + res_count);
+ pdev->res_attr[num] = attr;
+ retval = pci_create_one_attr(pdev, num, attr_name, suffix, attr,
+ sparse_base);
+ if (retval || res_count == 1)
+ return retval;
+
+ /* Create dense file */
+ attr_name += nlen1;
+ attr++;
+ pdev->res_attr_wc[num] = attr;
+ return pci_create_one_attr(pdev, num, attr_name, "_dense", attr, 0);
+}
+
+/**
+ * pci_create_resource_files - create resource files in sysfs for @dev
+ * @dev: dev in question
+ *
+ * Walk the resources in @dev creating files for each resource available.
+ */
+int pci_create_resource_files(struct pci_dev *pdev)
+{
+ int i;
+ int retval;
+
+ /* Expose the PCI resources from this device as files */
+ for (i = 0; i < PCI_ROM_RESOURCE; i++) {
+
+ /* skip empty resources */
+ if (!pci_resource_len(pdev, i))
+ continue;
+
+ retval = pci_create_attr(pdev, i);
+ if (retval) {
+ pci_remove_resource_files(pdev);
+ return retval;
+ }
+ }
+ return 0;
+}
+
+/* Legacy I/O bus mapping stuff. */
+
+static int __legacy_mmap_fits(struct pci_controller *hose,
+ struct vm_area_struct *vma,
+ unsigned long res_size, int sparse)
+{
+ unsigned long nr, start, size;
+
+ nr = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+ start = vma->vm_pgoff;
+ size = ((res_size - 1) >> PAGE_SHIFT) + 1;
+
+ if (start < size && size - start >= nr)
+ return 1;
+ WARN(1, "process \"%s\" tried to map%s 0x%08lx-0x%08lx on hose %d "
+ "(size 0x%08lx)\n",
+ current->comm, sparse ? " sparse" : "", start, start + nr,
+ hose->index, size);
+ return 0;
+}
+
+static inline int has_sparse(struct pci_controller *hose,
+ enum pci_mmap_state mmap_type)
+{
+ unsigned long base;
+
+ base = (mmap_type == pci_mmap_mem) ? hose->sparse_mem_base :
+ hose->sparse_io_base;
+
+ return base != 0;
+}
+
+int pci_mmap_legacy_page_range(struct pci_bus *bus, struct vm_area_struct *vma,
+ enum pci_mmap_state mmap_type)
+{
+ struct pci_controller *hose = bus->sysdata;
+ int sparse = has_sparse(hose, mmap_type);
+ unsigned long res_size;
+
+ res_size = (mmap_type == pci_mmap_mem) ? bus->legacy_mem->size :
+ bus->legacy_io->size;
+ if (!__legacy_mmap_fits(hose, vma, res_size, sparse))
+ return -EINVAL;
+
+ return hose_mmap_page_range(hose, vma, mmap_type, sparse);
+}
+
+/**
+ * pci_adjust_legacy_attr - adjustment of legacy file attributes
+ * @b: bus to create files under
+ * @mmap_type: I/O port or memory
+ *
+ * Adjust file name and size for sparse mappings.
+ */
+void pci_adjust_legacy_attr(struct pci_bus *bus, enum pci_mmap_state mmap_type)
+{
+ struct pci_controller *hose = bus->sysdata;
+
+ if (!has_sparse(hose, mmap_type))
+ return;
+
+ if (mmap_type == pci_mmap_mem) {
+ bus->legacy_mem->attr.name = "legacy_mem_sparse";
+ bus->legacy_mem->size <<= 5;
+ } else {
+ bus->legacy_io->attr.name = "legacy_io_sparse";
+ bus->legacy_io->size <<= 5;
+ }
+ return;
+}
+
+/* Legacy I/O bus read/write functions */
+int pci_legacy_read(struct pci_bus *bus, loff_t port, u32 *val, size_t size)
+{
+ struct pci_controller *hose = bus->sysdata;
+
+ port += hose->io_space->start;
+
+ switch(size) {
+ case 1:
+ *((u8 *)val) = inb(port);
+ return 1;
+ case 2:
+ if (port & 1)
+ return -EINVAL;
+ *((u16 *)val) = inw(port);
+ return 2;
+ case 4:
+ if (port & 3)
+ return -EINVAL;
+ *((u32 *)val) = inl(port);
+ return 4;
+ }
+ return -EINVAL;
+}
+
+int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, size_t size)
+{
+ struct pci_controller *hose = bus->sysdata;
+
+ port += hose->io_space->start;
+
+ switch(size) {
+ case 1:
+ outb(port, val);
+ return 1;
+ case 2:
+ if (port & 1)
+ return -EINVAL;
+ outw(port, val);
+ return 2;
+ case 4:
+ if (port & 3)
+ return -EINVAL;
+ outl(port, val);
+ return 4;
+ }
+ return -EINVAL;
+}
diff --git a/arch/powerpc/include/asm/pci.h b/arch/powerpc/include/asm/pci.h
index 3548159a1beb..ba17d5d90a49 100644
--- a/arch/powerpc/include/asm/pci.h
+++ b/arch/powerpc/include/asm/pci.h
@@ -114,6 +114,10 @@ extern int pci_domain_nr(struct pci_bus *bus);
/* Decide whether to display the domain number in /proc */
extern int pci_proc_domain(struct pci_bus *bus);
+/* MSI arch hooks */
+#define arch_setup_msi_irqs arch_setup_msi_irqs
+#define arch_teardown_msi_irqs arch_teardown_msi_irqs
+#define arch_msi_check_device arch_msi_check_device
struct vm_area_struct;
/* Map a range of PCI memory or I/O space for a device into user space */
diff --git a/arch/powerpc/kernel/msi.c b/arch/powerpc/kernel/msi.c
index 3bb7d3dd28be..8bbc12d20f5c 100644
--- a/arch/powerpc/kernel/msi.c
+++ b/arch/powerpc/kernel/msi.c
@@ -9,6 +9,7 @@
#include <linux/kernel.h>
#include <linux/msi.h>
+#include <linux/pci.h>
#include <asm/machdep.h>
@@ -19,6 +20,10 @@ int arch_msi_check_device(struct pci_dev* dev, int nvec, int type)
return -ENOSYS;
}
+ /* PowerPC doesn't support multiple MSI yet */
+ if (type == PCI_CAP_ID_MSI && nvec > 1)
+ return 1;
+
if (ppc_md.msi_check_device) {
pr_debug("msi: Using platform check routine.\n");
return ppc_md.msi_check_device(dev, nvec, type);
diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h
index a977de23cb4d..a0301bfeb954 100644
--- a/arch/x86/include/asm/pci.h
+++ b/arch/x86/include/asm/pci.h
@@ -86,6 +86,9 @@ static inline void early_quirks(void) { }
extern void pci_iommu_alloc(void);
+/* MSI arch hook */
+#define arch_setup_msi_irqs arch_setup_msi_irqs
+
#endif /* __KERNEL__ */
#ifdef CONFIG_X86_32
diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c
index da99ffcdfde6..1bb5c6cee3eb 100644
--- a/arch/x86/kernel/apic/io_apic.c
+++ b/arch/x86/kernel/apic/io_apic.c
@@ -3468,6 +3468,10 @@ int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
struct intel_iommu *iommu = NULL;
int index = 0;
+ /* x86 doesn't support multiple MSI yet */
+ if (type == PCI_CAP_ID_MSI && nvec > 1)
+ return 1;
+
irq_want = nr_irqs_gsi;
sub_handle = 0;
list_for_each_entry(msidesc, &dev->msi_list, list) {
diff --git a/arch/x86/kernel/pci-dma.c b/arch/x86/kernel/pci-dma.c
index c7c4776ff630..90f5b9ef5def 100644
--- a/arch/x86/kernel/pci-dma.c
+++ b/arch/x86/kernel/pci-dma.c
@@ -300,8 +300,7 @@ fs_initcall(pci_iommu_init);
static __devinit void via_no_dac(struct pci_dev *dev)
{
if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI && forbid_dac == 0) {
- printk(KERN_INFO
- "PCI: VIA PCI bridge detected. Disabling DAC.\n");
+ dev_info(&dev->dev, "disabling DAC on VIA PCI bridge\n");
forbid_dac = 1;
}
}
diff --git a/arch/x86/pci/early.c b/arch/x86/pci/early.c
index f6adf2c6d751..aaf26ae58cd5 100644
--- a/arch/x86/pci/early.c
+++ b/arch/x86/pci/early.c
@@ -69,11 +69,12 @@ void early_dump_pci_device(u8 bus, u8 slot, u8 func)
int j;
u32 val;
- printk(KERN_INFO "PCI: %02x:%02x:%02x", bus, slot, func);
+ printk(KERN_INFO "pci 0000:%02x:%02x.%d config space:",
+ bus, slot, func);
for (i = 0; i < 256; i += 4) {
if (!(i & 0x0f))
- printk("\n%04x:",i);
+ printk("\n %02x:",i);
val = read_pci_config(bus, slot, func, i);
for (j = 0; j < 4; j++) {
@@ -96,20 +97,22 @@ void early_dump_pci_devices(void)
for (func = 0; func < 8; func++) {
u32 class;
u8 type;
+
class = read_pci_config(bus, slot, func,
PCI_CLASS_REVISION);
if (class == 0xffffffff)
- break;
+ continue;
early_dump_pci_device(bus, slot, func);
- /* No multi-function device? */
- type = read_pci_config_byte(bus, slot, func,
+ if (func == 0) {
+ type = read_pci_config_byte(bus, slot,
+ func,
PCI_HEADER_TYPE);
- if (!(type & 0x80))
- break;
+ if (!(type & 0x80))
+ break;
+ }
}
}
}
}
-
diff --git a/arch/x86/pci/fixup.c b/arch/x86/pci/fixup.c
index 9c49919e4d1c..6dd89555fbfa 100644
--- a/arch/x86/pci/fixup.c
+++ b/arch/x86/pci/fixup.c
@@ -495,26 +495,6 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SIEMENS, 0x0015,
pci_siemens_interrupt_controller);
/*
- * Regular PCI devices have 256 bytes, but AMD Family 10h/11h CPUs have
- * 4096 bytes configuration space for each function of their processor
- * configuration space.
- */
-static void amd_cpu_pci_cfg_space_size(struct pci_dev *dev)
-{
- dev->cfg_size = pci_cfg_space_size_ext(dev);
-}
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1200, amd_cpu_pci_cfg_space_size);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1201, amd_cpu_pci_cfg_space_size);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1202, amd_cpu_pci_cfg_space_size);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1203, amd_cpu_pci_cfg_space_size);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1204, amd_cpu_pci_cfg_space_size);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1300, amd_cpu_pci_cfg_space_size);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1301, amd_cpu_pci_cfg_space_size);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1302, amd_cpu_pci_cfg_space_size);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1303, amd_cpu_pci_cfg_space_size);
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1304, amd_cpu_pci_cfg_space_size);
-
-/*
* SB600: Disable BAR1 on device 14.0 to avoid HPET resources from
* confusing the PCI engine:
*/
diff --git a/arch/x86/pci/legacy.c b/arch/x86/pci/legacy.c
index f1065b129e9c..4061bb0f267d 100644
--- a/arch/x86/pci/legacy.c
+++ b/arch/x86/pci/legacy.c
@@ -50,8 +50,6 @@ static int __init pci_legacy_init(void)
if (pci_root_bus)
pci_bus_add_devices(pci_root_bus);
- pcibios_fixup_peer_bridges();
-
return 0;
}
@@ -67,6 +65,7 @@ int __init pci_subsys_init(void)
pci_visws_init();
#endif
pci_legacy_init();
+ pcibios_fixup_peer_bridges();
pcibios_irq_init();
pcibios_init();
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c
index 89bf9242c80a..905bb526b133 100644
--- a/arch/x86/pci/mmconfig-shared.c
+++ b/arch/x86/pci/mmconfig-shared.c
@@ -14,6 +14,7 @@
#include <linux/init.h>
#include <linux/acpi.h>
#include <linux/bitmap.h>
+#include <linux/sort.h>
#include <asm/e820.h>
#include <asm/pci_x86.h>
@@ -24,24 +25,49 @@
/* Indicate if the mmcfg resources have been placed into the resource table. */
static int __initdata pci_mmcfg_resources_inserted;
+static __init int extend_mmcfg(int num)
+{
+ struct acpi_mcfg_allocation *new;
+ int new_num = pci_mmcfg_config_num + num;
+
+ new = kzalloc(sizeof(pci_mmcfg_config[0]) * new_num, GFP_KERNEL);
+ if (!new)
+ return -1;
+
+ if (pci_mmcfg_config) {
+ memcpy(new, pci_mmcfg_config,
+ sizeof(pci_mmcfg_config[0]) * new_num);
+ kfree(pci_mmcfg_config);
+ }
+ pci_mmcfg_config = new;
+
+ return 0;
+}
+
+static __init void fill_one_mmcfg(u64 addr, int segment, int start, int end)
+{
+ int i = pci_mmcfg_config_num;
+
+ pci_mmcfg_config_num++;
+ pci_mmcfg_config[i].address = addr;
+ pci_mmcfg_config[i].pci_segment = segment;
+ pci_mmcfg_config[i].start_bus_number = start;
+ pci_mmcfg_config[i].end_bus_number = end;
+}
+
static const char __init *pci_mmcfg_e7520(void)
{
u32 win;
raw_pci_ops->read(0, 0, PCI_DEVFN(0, 0), 0xce, 2, &win);
win = win & 0xf000;
- if(win == 0x0000 || win == 0xf000)
- pci_mmcfg_config_num = 0;
- else {
- pci_mmcfg_config_num = 1;
- pci_mmcfg_config = kzalloc(sizeof(pci_mmcfg_config[0]), GFP_KERNEL);
- if (!pci_mmcfg_config)
- return NULL;
- pci_mmcfg_config[0].address = win << 16;
- pci_mmcfg_config[0].pci_segment = 0;
- pci_mmcfg_config[0].start_bus_number = 0;
- pci_mmcfg_config[0].end_bus_number = 255;
- }
+ if (win == 0x0000 || win == 0xf000)
+ return NULL;
+
+ if (extend_mmcfg(1) == -1)
+ return NULL;
+
+ fill_one_mmcfg(win << 16, 0, 0, 255);
return "Intel Corporation E7520 Memory Controller Hub";
}
@@ -50,13 +76,11 @@ static const char __init *pci_mmcfg_intel_945(void)
{
u32 pciexbar, mask = 0, len = 0;
- pci_mmcfg_config_num = 1;
-
raw_pci_ops->read(0, 0, PCI_DEVFN(0, 0), 0x48, 4, &pciexbar);
/* Enable bit */
if (!(pciexbar & 1))
- pci_mmcfg_config_num = 0;
+ return NULL;
/* Size bits */
switch ((pciexbar >> 1) & 3) {
@@ -73,28 +97,23 @@ static const char __init *pci_mmcfg_intel_945(void)
len = 0x04000000U;
break;
default:
- pci_mmcfg_config_num = 0;
+ return NULL;
}
/* Errata #2, things break when not aligned on a 256Mb boundary */
/* Can only happen in 64M/128M mode */
if ((pciexbar & mask) & 0x0fffffffU)
- pci_mmcfg_config_num = 0;
+ return NULL;
/* Don't hit the APIC registers and their friends */
if ((pciexbar & mask) >= 0xf0000000U)
- pci_mmcfg_config_num = 0;
-
- if (pci_mmcfg_config_num) {
- pci_mmcfg_config = kzalloc(sizeof(pci_mmcfg_config[0]), GFP_KERNEL);
- if (!pci_mmcfg_config)
- return NULL;
- pci_mmcfg_config[0].address = pciexbar & mask;
- pci_mmcfg_config[0].pci_segment = 0;
- pci_mmcfg_config[0].start_bus_number = 0;
- pci_mmcfg_config[0].end_bus_number = (len >> 20) - 1;
- }
+ return NULL;
+
+ if (extend_mmcfg(1) == -1)
+ return NULL;
+
+ fill_one_mmcfg(pciexbar & mask, 0, 0, (len >> 20) - 1);
return "Intel Corporation 945G/GZ/P/PL Express Memory Controller Hub";
}
@@ -138,22 +157,77 @@ static const char __init *pci_mmcfg_amd_fam10h(void)
busnbits = 8;
}
- pci_mmcfg_config_num = (1 << segnbits);
- pci_mmcfg_config = kzalloc(sizeof(pci_mmcfg_config[0]) *
- pci_mmcfg_config_num, GFP_KERNEL);
- if (!pci_mmcfg_config)
+ if (extend_mmcfg(1 << segnbits) == -1)
return NULL;
- for (i = 0; i < (1 << segnbits); i++) {
- pci_mmcfg_config[i].address = base + (1<<28) * i;
- pci_mmcfg_config[i].pci_segment = i;
- pci_mmcfg_config[i].start_bus_number = 0;
- pci_mmcfg_config[i].end_bus_number = (1 << busnbits) - 1;
- }
+ for (i = 0; i < (1 << segnbits); i++)
+ fill_one_mmcfg(base + (1<<28) * i, i, 0, (1 << busnbits) - 1);
return "AMD Family 10h NB";
}
+static bool __initdata mcp55_checked;
+static const char __init *pci_mmcfg_nvidia_mcp55(void)
+{
+ int bus;
+ int mcp55_mmconf_found = 0;
+
+ static const u32 extcfg_regnum = 0x90;
+ static const u32 extcfg_regsize = 4;
+ static const u32 extcfg_enable_mask = 1<<31;
+ static const u32 extcfg_start_mask = 0xff<<16;
+ static const int extcfg_start_shift = 16;
+ static const u32 extcfg_size_mask = 0x3<<28;
+ static const int extcfg_size_shift = 28;
+ static const int extcfg_sizebus[] = {0x100, 0x80, 0x40, 0x20};
+ static const u32 extcfg_base_mask[] = {0x7ff8, 0x7ffc, 0x7ffe, 0x7fff};
+ static const int extcfg_base_lshift = 25;
+
+ /*
+ * do check if amd fam10h already took over
+ */
+ if (!acpi_disabled || pci_mmcfg_config_num || mcp55_checked)
+ return NULL;
+
+ mcp55_checked = true;
+ for (bus = 0; bus < 256; bus++) {
+ u64 base;
+ u32 l, extcfg;
+ u16 vendor, device;
+ int start, size_index, end;
+
+ raw_pci_ops->read(0, bus, PCI_DEVFN(0, 0), 0, 4, &l);
+ vendor = l & 0xffff;
+ device = (l >> 16) & 0xffff;
+
+ if (PCI_VENDOR_ID_NVIDIA != vendor || 0x0369 != device)
+ continue;
+
+ raw_pci_ops->read(0, bus, PCI_DEVFN(0, 0), extcfg_regnum,
+ extcfg_regsize, &extcfg);
+
+ if (!(extcfg & extcfg_enable_mask))
+ continue;
+
+ if (extend_mmcfg(1) == -1)
+ continue;
+
+ size_index = (extcfg & extcfg_size_mask) >> extcfg_size_shift;
+ base = extcfg & extcfg_base_mask[size_index];
+ /* base could > 4G */
+ base <<= extcfg_base_lshift;
+ start = (extcfg & extcfg_start_mask) >> extcfg_start_shift;
+ end = start + extcfg_sizebus[size_index] - 1;
+ fill_one_mmcfg(base, 0, start, end);
+ mcp55_mmconf_found++;
+ }
+
+ if (!mcp55_mmconf_found)
+ return NULL;
+
+ return "nVidia MCP55";
+}
+
struct pci_mmcfg_hostbridge_probe {
u32 bus;
u32 devfn;
@@ -171,8 +245,52 @@ static struct pci_mmcfg_hostbridge_probe pci_mmcfg_probes[] __initdata = {
0x1200, pci_mmcfg_amd_fam10h },
{ 0xff, PCI_DEVFN(0, 0), PCI_VENDOR_ID_AMD,
0x1200, pci_mmcfg_amd_fam10h },
+ { 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_NVIDIA,
+ 0x0369, pci_mmcfg_nvidia_mcp55 },
};
+static int __init cmp_mmcfg(const void *x1, const void *x2)
+{
+ const typeof(pci_mmcfg_config[0]) *m1 = x1;
+ const typeof(pci_mmcfg_config[0]) *m2 = x2;
+ int start1, start2;
+
+ start1 = m1->start_bus_number;
+ start2 = m2->start_bus_number;
+
+ return start1 - start2;
+}
+
+static void __init pci_mmcfg_check_end_bus_number(void)
+{
+ int i;
+ typeof(pci_mmcfg_config[0]) *cfg, *cfgx;
+
+ /* sort them at first */
+ sort(pci_mmcfg_config, pci_mmcfg_config_num,
+ sizeof(pci_mmcfg_config[0]), cmp_mmcfg, NULL);
+
+ /* last one*/
+ if (pci_mmcfg_config_num > 0) {
+ i = pci_mmcfg_config_num - 1;
+ cfg = &pci_mmcfg_config[i];
+ if (cfg->end_bus_number < cfg->start_bus_number)
+ cfg->end_bus_number = 255;
+ }
+
+ /* don't overlap please */
+ for (i = 0; i < pci_mmcfg_config_num - 1; i++) {
+ cfg = &pci_mmcfg_config[i];
+ cfgx = &pci_mmcfg_config[i+1];
+
+ if (cfg->end_bus_number < cfg->start_bus_number)
+ cfg->end_bus_number = 255;
+
+ if (cfg->end_bus_number >= cfgx->start_bus_number)
+ cfg->end_bus_number = cfgx->start_bus_number - 1;
+ }
+}
+
static int __init pci_mmcfg_check_hostbridge(void)
{
u32 l;
@@ -186,31 +304,33 @@ static int __init pci_mmcfg_check_hostbridge(void)
pci_mmcfg_config_num = 0;
pci_mmcfg_config = NULL;
- name = NULL;
- for (i = 0; !name && i < ARRAY_SIZE(pci_mmcfg_probes); i++) {
+ for (i = 0; i < ARRAY_SIZE(pci_mmcfg_probes); i++) {
bus = pci_mmcfg_probes[i].bus;
devfn = pci_mmcfg_probes[i].devfn;
raw_pci_ops->read(0, bus, devfn, 0, 4, &l);
vendor = l & 0xffff;
device = (l >> 16) & 0xffff;
+ name = NULL;
if (pci_mmcfg_probes[i].vendor == vendor &&
pci_mmcfg_probes[i].device == device)
name = pci_mmcfg_probes[i].probe();
- }
- if (name) {
- printk(KERN_INFO "PCI: Found %s %s MMCONFIG support.\n",
- name, pci_mmcfg_config_num ? "with" : "without");
+ if (name)
+ printk(KERN_INFO "PCI: Found %s with MMCONFIG support.\n",
+ name);
}
- return name != NULL;
+ /* some end_bus_number is crazy, fix it */
+ pci_mmcfg_check_end_bus_number();
+
+ return pci_mmcfg_config_num != 0;
}
static void __init pci_mmcfg_insert_resources(void)
{
-#define PCI_MMCFG_RESOURCE_NAME_LEN 19
+#define PCI_MMCFG_RESOURCE_NAME_LEN 24
int i;
struct resource *res;
char *names;
@@ -228,9 +348,10 @@ static void __init pci_mmcfg_insert_resources(void)
struct acpi_mcfg_allocation *cfg = &pci_mmcfg_config[i];
num_buses = cfg->end_bus_number - cfg->start_bus_number + 1;
res->name = names;
- snprintf(names, PCI_MMCFG_RESOURCE_NAME_LEN, "PCI MMCONFIG %u",
- cfg->pci_segment);
- res->start = cfg->address;
+ snprintf(names, PCI_MMCFG_RESOURCE_NAME_LEN,
+ "PCI MMCONFIG %u [%02x-%02x]", cfg->pci_segment,
+ cfg->start_bus_number, cfg->end_bus_number);
+ res->start = cfg->address + (cfg->start_bus_number << 20);
res->end = res->start + (num_buses << 20) - 1;
res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
insert_resource(&iomem_resource, res);
@@ -354,8 +475,6 @@ static void __init pci_mmcfg_reject_broken(int early)
(pci_mmcfg_config[0].address == 0))
return;
- cfg = &pci_mmcfg_config[0];
-
for (i = 0; i < pci_mmcfg_config_num; i++) {
int valid = 0;
u64 addr, size;
@@ -423,10 +542,10 @@ static void __init __pci_mmcfg_init(int early)
known_bridge = 1;
}
- if (!known_bridge) {
+ if (!known_bridge)
acpi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg);
- pci_mmcfg_reject_broken(early);
- }
+
+ pci_mmcfg_reject_broken(early);
if ((pci_mmcfg_config_num == 0) ||
(pci_mmcfg_config == NULL) ||
diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c
index 30007ffc8e11..94349f8b2f96 100644
--- a/arch/x86/pci/mmconfig_64.c
+++ b/arch/x86/pci/mmconfig_64.c
@@ -112,13 +112,18 @@ static struct pci_raw_ops pci_mmcfg = {
static void __iomem * __init mcfg_ioremap(struct acpi_mcfg_allocation *cfg)
{
void __iomem *addr;
- u32 size;
-
- size = (cfg->end_bus_number + 1) << 20;
- addr = ioremap_nocache(cfg->address, size);
+ u64 start, size;
+
+ start = cfg->start_bus_number;
+ start <<= 20;
+ start += cfg->address;
+ size = cfg->end_bus_number + 1 - cfg->start_bus_number;
+ size <<= 20;
+ addr = ioremap_nocache(start, size);
if (addr) {
printk(KERN_INFO "PCI: Using MMCONFIG at %Lx - %Lx\n",
- cfg->address, cfg->address + size - 1);
+ start, start + size - 1);
+ addr -= cfg->start_bus_number << 20;
}
return addr;
}
@@ -157,7 +162,7 @@ void __init pci_mmcfg_arch_free(void)
for (i = 0; i < pci_mmcfg_config_num; ++i) {
if (pci_mmcfg_virt[i].virt) {
- iounmap(pci_mmcfg_virt[i].virt);
+ iounmap(pci_mmcfg_virt[i].virt + (pci_mmcfg_virt[i].cfg->start_bus_number << 20));
pci_mmcfg_virt[i].virt = NULL;
pci_mmcfg_virt[i].cfg = NULL;
}