diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-01 15:14:04 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-01 15:14:04 -0700 |
commit | 4b1779c2cf030c68aefe939d946475e4136c1895 (patch) | |
tree | 27e4bda2f6c8d269d02dec52a62dd1443880c6dc | |
parent | 62ff577fa2fec87edbf26f53e87210ba726d4d44 (diff) | |
parent | 30723cbf6f7aec2ab4810bdc4bf12c5749a09e33 (diff) |
Merge tag 'pci-v3.15-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci
Pull PCI changes from Bjorn Helgaas:
"Enumeration
- Increment max correctly in pci_scan_bridge() (Andreas Noever)
- Clarify the "scan anyway" comment in pci_scan_bridge() (Andreas Noever)
- Assign CardBus bus number only during the second pass (Andreas Noever)
- Use request_resource_conflict() instead of insert_ for bus numbers (Andreas Noever)
- Make sure bus number resources stay within their parents bounds (Andreas Noever)
- Remove pci_fixup_parent_subordinate_busnr() (Andreas Noever)
- Check for child busses which use more bus numbers than allocated (Andreas Noever)
- Don't scan random busses in pci_scan_bridge() (Andreas Noever)
- x86: Drop pcibios_scan_root() check for bus already scanned (Bjorn Helgaas)
- x86: Use pcibios_scan_root() instead of pci_scan_bus_with_sysdata() (Bjorn Helgaas)
- x86: Use pcibios_scan_root() instead of pci_scan_bus_on_node() (Bjorn Helgaas)
- x86: Merge pci_scan_bus_on_node() into pcibios_scan_root() (Bjorn Helgaas)
- x86: Drop return value of pcibios_scan_root() (Bjorn Helgaas)
NUMA
- x86: Add x86_pci_root_bus_node() to look up NUMA node from PCI bus (Bjorn Helgaas)
- x86: Use x86_pci_root_bus_node() instead of get_mp_bus_to_node() (Bjorn Helgaas)
- x86: Remove mp_bus_to_node[], set_mp_bus_to_node(), get_mp_bus_to_node() (Bjorn Helgaas)
- x86: Use NUMA_NO_NODE, not -1, for unknown node (Bjorn Helgaas)
- x86: Remove acpi_get_pxm() usage (Bjorn Helgaas)
- ia64: Use NUMA_NO_NODE, not MAX_NUMNODES, for unknown node (Bjorn Helgaas)
- ia64: Remove acpi_get_pxm() usage (Bjorn Helgaas)
- ACPI: Fix acpi_get_node() prototype (Bjorn Helgaas)
Resource management
- i2o: Fix and refactor PCI space allocation (Bjorn Helgaas)
- Add resource_contains() (Bjorn Helgaas)
- Add %pR support for IORESOURCE_UNSET (Bjorn Helgaas)
- Mark resources as IORESOURCE_UNSET if we can't assign them (Bjorn Helgaas)
- Don't clear IORESOURCE_UNSET when updating BAR (Bjorn Helgaas)
- Check IORESOURCE_UNSET before updating BAR (Bjorn Helgaas)
- Don't try to claim IORESOURCE_UNSET resources (Bjorn Helgaas)
- Mark 64-bit resource as IORESOURCE_UNSET if we only support 32-bit (Bjorn Helgaas)
- Don't enable decoding if BAR hasn't been assigned an address (Bjorn Helgaas)
- Add "weak" generic pcibios_enable_device() implementation (Bjorn Helgaas)
- alpha, microblaze, sh, sparc, tile: Use default pcibios_enable_device() (Bjorn Helgaas)
- s390: Use generic pci_enable_resources() (Bjorn Helgaas)
- Don't check resource_size() in pci_bus_alloc_resource() (Bjorn Helgaas)
- Set type in __request_region() (Bjorn Helgaas)
- Check all IORESOURCE_TYPE_BITS in pci_bus_alloc_from_region() (Bjorn Helgaas)
- Change pci_bus_alloc_resource() type_mask to unsigned long (Bjorn Helgaas)
- Log IDE resource quirk in dmesg (Bjorn Helgaas)
- Revert "[PATCH] Insert GART region into resource map" (Bjorn Helgaas)
PCI device hotplug
- Make check_link_active() non-static (Rajat Jain)
- Use link change notifications for hot-plug and removal (Rajat Jain)
- Enable link state change notifications (Rajat Jain)
- Don't disable the link permanently during removal (Rajat Jain)
- Don't check adapter or latch status while disabling (Rajat Jain)
- Disable link notification across slot reset (Rajat Jain)
- Ensure very fast hotplug events are also processed (Rajat Jain)
- Add hotplug_lock to serialize hotplug events (Rajat Jain)
- Remove a non-existent card, regardless of "surprise" capability (Rajat Jain)
- Don't turn slot off when hot-added device already exists (Yijing Wang)
MSI
- Keep pci_enable_msi() documentation (Alexander Gordeev)
- ahci: Fix broken single MSI fallback (Alexander Gordeev)
- ahci, vfio: Use pci_enable_msi_range() (Alexander Gordeev)
- Check kmalloc() return value, fix leak of name (Greg Kroah-Hartman)
- Fix leak of msi_attrs (Greg Kroah-Hartman)
- Fix pci_msix_vec_count() htmldocs failure (Masanari Iida)
Virtualization
- Device-specific ACS support (Alex Williamson)
Freescale i.MX6
- Wait for retraining (Marek Vasut)
Marvell MVEBU
- Use Device ID and revision from underlying endpoint (Andrew Lunn)
- Fix incorrect size for PCI aperture resources (Jason Gunthorpe)
- Call request_resource() on the apertures (Jason Gunthorpe)
- Fix potential issue in range parsing (Jean-Jacques Hiblot)
Renesas R-Car
- Check platform_get_irq() return code (Ben Dooks)
- Add error interrupt handling (Ben Dooks)
- Fix bridge logic configuration accesses (Ben Dooks)
- Register each instance independently (Magnus Damm)
- Break out window size handling (Magnus Damm)
- Make the Kconfig dependencies more generic (Magnus Damm)
Synopsys DesignWare
- Fix RC BAR to be single 64-bit non-prefetchable memory (Mohit Kumar)
Miscellaneous
- Remove unused SR-IOV VF Migration support (Bjorn Helgaas)
- Enable INTx if BIOS left them disabled (Bjorn Helgaas)
- Fix hex vs decimal typo in cpqhpc_probe() (Dan Carpenter)
- Clean up par-arch object file list (Liviu Dudau)
- Set IORESOURCE_ROM_SHADOW only for the default VGA device (Sander Eikelenboom)
- ACPI, ARM, drm, powerpc, pcmcia, PCI: Use list_for_each_entry() for bus traversal (Yijing Wang)
- Fix pci_bus_b() build failure (Paul Gortmaker)"
* tag 'pci-v3.15-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci: (108 commits)
Revert "[PATCH] Insert GART region into resource map"
PCI: Log IDE resource quirk in dmesg
PCI: Change pci_bus_alloc_resource() type_mask to unsigned long
PCI: Check all IORESOURCE_TYPE_BITS in pci_bus_alloc_from_region()
resources: Set type in __request_region()
PCI: Don't check resource_size() in pci_bus_alloc_resource()
s390/PCI: Use generic pci_enable_resources()
tile PCI RC: Use default pcibios_enable_device()
sparc/PCI: Use default pcibios_enable_device() (Leon only)
sh/PCI: Use default pcibios_enable_device()
microblaze/PCI: Use default pcibios_enable_device()
alpha/PCI: Use default pcibios_enable_device()
PCI: Add "weak" generic pcibios_enable_device() implementation
PCI: Don't enable decoding if BAR hasn't been assigned an address
PCI: Enable INTx in pci_reenable_device() only when MSI/MSI-X not enabled
PCI: Mark 64-bit resource as IORESOURCE_UNSET if we only support 32-bit
PCI: Don't try to claim IORESOURCE_UNSET resources
PCI: Check IORESOURCE_UNSET before updating BAR
PCI: Don't clear IORESOURCE_UNSET when updating BAR
PCI: Mark resources as IORESOURCE_UNSET if we can't assign them
...
Conflicts:
arch/x86/include/asm/topology.h
drivers/ata/ahci.c
68 files changed, 943 insertions, 863 deletions
diff --git a/Documentation/PCI/pci-iov-howto.txt b/Documentation/PCI/pci-iov-howto.txt index 86551cc72e0..2d91ae25198 100644 --- a/Documentation/PCI/pci-iov-howto.txt +++ b/Documentation/PCI/pci-iov-howto.txt @@ -68,10 +68,6 @@ To disable SR-IOV capability: echo 0 > \ /sys/bus/pci/devices/<DOMAIN:BUS:DEVICE.FUNCTION>/sriov_numvfs -To notify SR-IOV core of Virtual Function Migration: -(a) In the driver: - irqreturn_t pci_sriov_migration(struct pci_dev *dev); - 3.2 Usage example Following piece of code illustrates the usage of the SR-IOV API. diff --git a/arch/alpha/kernel/pci.c b/arch/alpha/kernel/pci.c index edb4e0097b7..076c35cd6cd 100644 --- a/arch/alpha/kernel/pci.c +++ b/arch/alpha/kernel/pci.c @@ -254,12 +254,6 @@ void pcibios_fixup_bus(struct pci_bus *bus) } } -int -pcibios_enable_device(struct pci_dev *dev, int mask) -{ - return pci_enable_resources(dev, mask); -} - /* * If we set up a device for bus mastering, we need to check the latency * timer as certain firmware forgets to set it properly, as seen diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index 317da88ae65..d0d46786892 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -19,7 +19,7 @@ static int debug_pci; /* - * We can't use pci_find_device() here since we are + * We can't use pci_get_device() here since we are * called from interrupt context. */ static void pcibios_bus_report_status(struct pci_bus *bus, u_int status_mask, int warn) @@ -57,13 +57,10 @@ static void pcibios_bus_report_status(struct pci_bus *bus, u_int status_mask, in void pcibios_report_status(u_int status_mask, int warn) { - struct list_head *l; - - list_for_each(l, &pci_root_buses) { - struct pci_bus *bus = pci_bus_b(l); + struct pci_bus *bus; + list_for_each_entry(bus, &pci_root_buses, node) pcibios_bus_report_status(bus, status_mask, warn); - } } /* diff --git a/arch/frv/mb93090-mb00/pci-frv.c b/arch/frv/mb93090-mb00/pci-frv.c index c2812176544..67b1d168575 100644 --- a/arch/frv/mb93090-mb00/pci-frv.c +++ b/arch/frv/mb93090-mb00/pci-frv.c @@ -88,7 +88,7 @@ static void __init pcibios_allocate_bus_resources(struct list_head *bus_list) /* Depth-First Search on bus tree */ for (ln=bus_list->next; ln != bus_list; ln=ln->next) { - bus = pci_bus_b(ln); + bus = list_entry(ln, struct pci_bus, node); if ((dev = bus->self)) { for (idx = PCI_BRIDGE_RESOURCES; idx < PCI_NUM_RESOURCES; idx++) { r = &dev->resource[idx]; diff --git a/arch/ia64/hp/common/sba_iommu.c b/arch/ia64/hp/common/sba_iommu.c index 30c43d39ded..1a871b78e57 100644 --- a/arch/ia64/hp/common/sba_iommu.c +++ b/arch/ia64/hp/common/sba_iommu.c @@ -1140,11 +1140,13 @@ sba_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, #ifdef CONFIG_NUMA { + int node = ioc->node; struct page *page; - page = alloc_pages_exact_node(ioc->node == MAX_NUMNODES ? - numa_node_id() : ioc->node, flags, - get_order(size)); + if (node == NUMA_NO_NODE) + node = numa_node_id(); + + page = alloc_pages_exact_node(node, flags, get_order(size)); if (unlikely(!page)) return NULL; @@ -1914,7 +1916,7 @@ ioc_show(struct seq_file *s, void *v) seq_printf(s, "Hewlett Packard %s IOC rev %d.%d\n", ioc->name, ((ioc->rev >> 4) & 0xF), (ioc->rev & 0xF)); #ifdef CONFIG_NUMA - if (ioc->node != MAX_NUMNODES) + if (ioc->node != NUMA_NO_NODE) seq_printf(s, "NUMA node : %d\n", ioc->node); #endif seq_printf(s, "IOVA size : %ld MB\n", ((ioc->pdir_size >> 3) * iovp_size)/(1024*1024)); @@ -2015,31 +2017,19 @@ sba_connect_bus(struct pci_bus *bus) printk(KERN_WARNING "No IOC for PCI Bus %04x:%02x in ACPI\n", pci_domain_nr(bus), bus->number); } -#ifdef CONFIG_NUMA static void __init sba_map_ioc_to_node(struct ioc *ioc, acpi_handle handle) { +#ifdef CONFIG_NUMA unsigned int node; - int pxm; - - ioc->node = MAX_NUMNODES; - - pxm = acpi_get_pxm(handle); - - if (pxm < 0) - return; - - node = pxm_to_node(pxm); - if (node >= MAX_NUMNODES || !node_online(node)) - return; + node = acpi_get_node(handle); + if (node != NUMA_NO_NODE && !node_online(node)) + node = NUMA_NO_NODE; ioc->node = node; - return; -} -#else -#define sba_map_ioc_to_node(ioc, handle) #endif +} static int acpi_sba_ioc_add(struct acpi_device *device, diff --git a/arch/ia64/include/asm/pci.h b/arch/ia64/include/asm/pci.h index 71fbaaa495c..7d41cc08982 100644 --- a/arch/ia64/include/asm/pci.h +++ b/arch/ia64/include/asm/pci.h @@ -98,7 +98,7 @@ struct pci_controller { struct acpi_device *companion; void *iommu; int segment; - int node; /* nearest node with memory or -1 for global allocation */ + int node; /* nearest node with memory or NUMA_NO_NODE for global allocation */ void *platform_data; }; diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c index 467497ade45..0d407b30076 100644 --- a/arch/ia64/kernel/acpi.c +++ b/arch/ia64/kernel/acpi.c @@ -799,14 +799,9 @@ int acpi_isa_irq_to_gsi(unsigned isa_irq, u32 *gsi) * ACPI based hotplug CPU support */ #ifdef CONFIG_ACPI_HOTPLUG_CPU -static -int acpi_map_cpu2node(acpi_handle handle, int cpu, int physid) +static int acpi_map_cpu2node(acpi_handle handle, int cpu, int physid) { #ifdef CONFIG_ACPI_NUMA - int pxm_id; - int nid; - - pxm_id = acpi_get_pxm(handle); /* * We don't have cpu-only-node hotadd. But if the system equips * SRAT table, pxm is already found and node is ready. @@ -814,11 +809,10 @@ int acpi_map_cpu2node(acpi_handle handle, int cpu, int physid) * This code here is for the system which doesn't have full SRAT * table for possible cpus. */ - nid = acpi_map_pxm_to_node(pxm_id); node_cpuid[cpu].phys_id = physid; - node_cpuid[cpu].nid = nid; + node_cpuid[cpu].nid = acpi_get_node(handle); #endif - return (0); + return 0; } int additional_cpus __initdata = -1; @@ -925,7 +919,7 @@ static acpi_status acpi_map_iosapic(acpi_handle handle, u32 depth, union acpi_object *obj; struct acpi_madt_io_sapic *iosapic; unsigned int gsi_base; - int pxm, node; + int node; /* Only care about objects w/ a method that returns the MADT */ if (ACPI_FAILURE(acpi_evaluate_object(handle, "_MAT", NULL, &buffer))) @@ -952,17 +946,9 @@ static acpi_status acpi_map_iosapic(acpi_handle handle, u32 depth, kfree(buffer.pointer); - /* - * OK, it's an IOSAPIC MADT entry, look for a _PXM value to tell - * us which node to associate this with. - */ - pxm = acpi_get_pxm(handle); - if (pxm < 0) - return AE_OK; - - node = pxm_to_node(pxm); - - if (node >= MAX_NUMNODES || !node_online(node) || + /* OK, it's an IOSAPIC MADT entry; associate it with a node */ + node = acpi_get_node(handle); + if (node == NUMA_NO_NODE || !node_online(node) || cpumask_empty(cpumask_of_node(node))) return AE_OK; diff --git a/arch/ia64/pci/fixup.c b/arch/ia64/pci/fixup.c index 5dc969dd4ac..eee069a0b53 100644 --- a/arch/ia64/pci/fixup.c +++ b/arch/ia64/pci/fixup.c @@ -5,6 +5,7 @@ #include <linux/pci.h> #include <linux/init.h> +#include <linux/vgaarb.h> #include <asm/machvec.h> @@ -19,9 +20,10 @@ * IORESOURCE_ROM_SHADOW is used to associate the boot video * card with this copy. On laptops this copy has to be used since * the main ROM may be compressed or combined with another image. - * See pci_map_rom() for use of this flag. IORESOURCE_ROM_SHADOW - * is marked here since the boot video device will be the only enabled - * video device at this point. + * See pci_map_rom() for use of this flag. Before marking the device + * with IORESOURCE_ROM_SHADOW check if a vga_default_device is already set + * by either arch cde or vga-arbitration, if so only apply the fixup to this + * already determined primary video card. */ static void pci_fixup_video(struct pci_dev *pdev) @@ -35,9 +37,6 @@ static void pci_fixup_video(struct pci_dev *pdev) return; /* Maybe, this machine supports legacy memory map. */ - if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA) - return; - /* Is VGA routed to us? */ bus = pdev->bus; while (bus) { @@ -60,10 +59,14 @@ static void pci_fixup_video(struct pci_dev *pdev) } bus = bus->parent; } - pci_read_config_word(pdev, PCI_COMMAND, &config); - if (config & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) { - pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW; - dev_printk(KERN_DEBUG, &pdev->dev, "Boot video device\n"); + if (!vga_default_device() || pdev == vga_default_device()) { + pci_read_config_word(pdev, PCI_COMMAND, &config); + if (config & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) { + pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW; + dev_printk(KERN_DEBUG, &pdev->dev, "Boot video device\n"); + vga_set_default_device(pdev); + } } } -DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_video); +DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_DISPLAY_VGA, 8, pci_fixup_video); diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index 9e4938d8ca4..291a582777c 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -126,7 +126,6 @@ static struct pci_controller *alloc_pci_controller(int seg) return NULL; controller->segment = seg; - controller->node = -1; return controller; } @@ -430,19 +429,14 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) struct pci_root_info *info = NULL; int busnum = root->secondary.start; struct pci_bus *pbus; - int pxm, ret; + int ret; controller = alloc_pci_controller(domain); if (!controller) return NULL; controller->companion = device; - - pxm = acpi_get_pxm(device->handle); -#ifdef CONFIG_NUMA - if (pxm >= 0) - controller->node = pxm_to_node(pxm); -#endif + controller->node = acpi_get_node(device->handle); info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) { diff --git a/arch/microblaze/pci/pci-common.c b/arch/microblaze/pci/pci-common.c index 66804adcacf..70996cc66aa 100644 --- a/arch/microblaze/pci/pci-common.c +++ b/arch/microblaze/pci/pci-common.c @@ -1294,11 +1294,6 @@ void pcibios_finish_adding_to_bus(struct pci_bus *bus) } EXPORT_SYMBOL_GPL(pcibios_finish_adding_to_bus); -int pcibios_enable_device(struct pci_dev *dev, int mask) -{ - return pci_enable_resources(dev, mask); -} - static void pcibios_setup_phb_resources(struct pci_controller *hose, struct list_head *resources) { diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c index a9e311f7a9d..2a4779091a5 100644 --- a/arch/powerpc/kernel/pci_64.c +++ b/arch/powerpc/kernel/pci_64.c @@ -208,7 +208,6 @@ long sys_pciconfig_iobase(long which, unsigned long in_bus, unsigned long in_devfn) { struct pci_controller* hose; - struct list_head *ln; struct pci_bus *bus = NULL; struct device_node *hose_node; @@ -230,8 +229,7 @@ long sys_pciconfig_iobase(long which, unsigned long in_bus, * used on pre-domains setup. We return the first match */ - for (ln = pci_root_buses.next; ln != &pci_root_buses; ln = ln->next) { - bus = pci_bus_b(ln); + list_for_each_entry(bus, &pci_root_buses, node) { if (in_bus >= bus->number && in_bus <= bus->busn_res.end) break; bus = NULL; diff --git a/arch/powerpc/platforms/pseries/pci_dlpar.c b/arch/powerpc/platforms/pseries/pci_dlpar.c index efe61374f6e..203cbf0dc10 100644 --- a/arch/powerpc/platforms/pseries/pci_dlpar.c +++ b/arch/powerpc/platforms/pseries/pci_dlpar.c @@ -37,15 +37,15 @@ find_bus_among_children(struct pci_bus *bus, struct device_node *dn) { struct pci_bus *child = NULL; - struct list_head *tmp; + struct pci_bus *tmp; struct device_node *busdn; busdn = pci_bus_to_OF_node(bus); if (busdn == dn) return bus; - list_for_each(tmp, &bus->children) { - child = find_bus_among_children(pci_bus_b(tmp), dn); + list_for_each_entry(tmp, &bus->children, node) { + child = find_bus_among_children(tmp, dn); if (child) break; }; diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index 66670ff262a..1df1d29ac81 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -686,27 +686,13 @@ int pcibios_add_device(struct pci_dev *pdev) int pcibios_enable_device(struct pci_dev *pdev, int mask) { struct zpci_dev *zdev = get_zdev(pdev); - struct resource *res; - u16 cmd; - int i; zdev->pdev = pdev; zpci_debug_init_device(zdev); zpci_fmb_enable_device(zdev); zpci_map_resources(zdev); - pci_read_config_word(pdev, PCI_COMMAND, &cmd); - for (i = 0; i < PCI_BAR_COUNT; i++) { - res = &pdev->resource[i]; - - if (res->flags & IORESOURCE_IO) - return -EINVAL; - - if (res->flags & IORESOURCE_MEM) - cmd |= PCI_COMMAND_MEMORY; - } - pci_write_config_word(pdev, PCI_COMMAND, cmd); - return 0; + return pci_enable_resources(pdev, mask); } void pcibios_disable_device(struct pci_dev *pdev) diff --git a/arch/sh/drivers/pci/pci.c b/arch/sh/drivers/pci/pci.c index 60ed3e1c4b7..1bc09ee7948 100644 --- a/arch/sh/drivers/pci/pci.c +++ b/arch/sh/drivers/pci/pci.c @@ -186,11 +186,6 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res, return start; } -int pcibios_enable_device(struct pci_dev *dev, int mask) -{ - return pci_enable_resources(dev, mask); -} - static void __init pcibios_bus_report_status_early(struct pci_channel *hose, int top_bus, int current_bus, diff --git a/arch/sparc/kernel/leon_pci.c b/arch/sparc/kernel/leon_pci.c index 88aaaa57bb6..e16c4157e1a 100644 --- a/arch/sparc/kernel/leon_pci.c +++ b/arch/sparc/kernel/leon_pci.c @@ -99,11 +99,6 @@ resource_size_t pcibios_align_resource(void *data, const struct resource *res, return res->start; } -int pcibios_enable_device(struct pci_dev *dev, int mask) -{ - return pci_enable_resources(dev, mask); -} - /* in/out routines taken from pcic.c * * This probably belongs here rather than ioport.c because diff --git a/arch/tile/kernel/pci_gx.c b/arch/tile/kernel/pci_gx.c index a97a6452b81..077b7bc437e 100644 --- a/arch/tile/kernel/pci_gx.c +++ b/arch/tile/kernel/pci_gx.c @@ -1065,18 +1065,6 @@ char *__init pcibios_setup(char *str) } /* - * Enable memory address decoding, as appropriate, for the - * device described by the 'dev' struct. - * - * This is called from the generic PCI layer, and can be called - * for bridges or endpoints. - */ -int pcibios_enable_device(struct pci_dev *dev, int mask) -{ - return pci_enable_resources(dev, mask); -} - -/* * Called for each device after PCI setup is done. * We initialize the PCI device capabilities conservatively, assuming that * all devices can only address the 32-bit DMA space. The exception here is diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h index 1ac6114c9ea..96ae4f4040b 100644 --- a/arch/x86/include/asm/pci.h +++ b/arch/x86/include/asm/pci.h @@ -26,11 +26,6 @@ extern int pci_routeirq; extern int noioapicquirk; extern int noioapicreroute; -/* scan a bus after allocating a pci_sysdata for it */ -extern struct pci_bus *pci_scan_bus_on_node(int busno, struct pci_ops *ops, - int node); -extern struct pci_bus *pci_scan_bus_with_sysdata(int busno); - #ifdef CONFIG_PCI #ifdef CONFIG_PCI_DOMAINS @@ -70,7 +65,7 @@ extern unsigned long pci_mem_start; extern int pcibios_enabled; void pcibios_config_init(void); -struct pci_bus *pcibios_scan_root(int bus); +void pcibios_scan_root(int bus); void pcibios_set_master(struct pci_dev *dev); void pcibios_penalize_isa_irq(int irq, int active); diff --git a/arch/x86/include/asm/topology.h b/arch/x86/include/asm/topology.h index b28097e4c8c..0e8f04f2c26 100644 --- a/arch/x86/include/asm/topology.h +++ b/arch/x86/include/asm/topology.h @@ -132,19 +132,7 @@ static inline void arch_fix_phys_package_id(int num, u32 slot) } struct pci_bus; +int x86_pci_root_bus_node(int bus); void x86_pci_root_bus_resources(int bus, struct list_head *resources); -#ifdef CONFIG_NUMA -extern int get_mp_bus_to_node(int busnum); -extern void set_mp_bus_to_node(int busnum, int node); -#else -static inline int get_mp_bus_to_node(int busnum) -{ - return 0; -} -static inline void set_mp_bus_to_node(int busnum, int node) -{ -} -#endif - #endif /* _ASM_X86_TOPOLOGY_H */ diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 4f25ec07755..01edac6c5e1 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -218,9 +218,8 @@ static void teardown_mcfg_map(struct pci_root_info *info) } #endif -static acpi_status -resource_to_addr(struct acpi_resource *resource, - struct acpi_resource_address64 *addr) +static acpi_status resource_to_addr(struct acpi_resource *resource, + struct acpi_resource_address64 *addr) { acpi_status status; struct acpi_resource_memory24 *memory24; @@ -265,8 +264,7 @@ resource_to_addr(struct acpi_resource *resource, return AE_ERROR; } -static acpi_status -count_resource(struct acpi_resource *acpi_res, void *data) +static acpi_status count_resource(struct acpi_resource *acpi_res, void *data) { struct pci_root_info *info = data; struct acpi_resource_address64 addr; @@ -278,8 +276,7 @@ count_resource(struct acpi_resource *acpi_res, void *data) return AE_OK; } -static acpi_status -setup_resource(struct acpi_resource *acpi_res, void *data) +static acpi_status setup_resource(struct acpi_resource *acpi_res, void *data) { struct pci_root_info *info = data; struct resource *res; @@ -435,9 +432,9 @@ static void release_pci_root_info(struct pci_host_bridge *bridge) __release_pci_root_info(info); } -static void -probe_pci_root_info(struct pci_root_info *info, struct acpi_device *device, - int busnum, int domain) +static void probe_pci_root_info(struct pci_root_info *info, + struct acpi_device *device, + int busnum, int domain) { size_t size; @@ -473,16 +470,13 @@ probe_pci_root_info(struct pci_root_info *info, struct acpi_device *device, struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) { struct acpi_device *device = root->device; - struct pci_root_info *info = NULL; + struct pci_root_info *info; int domain = root->segment; int busnum = root->secondary.start; LIST_HEAD(resources); - struct pci_bus *bus = NULL; + struct pci_bus *bus; struct pci_sysdata *sd; int node; -#ifdef CONFIG_ACPI_NUMA - int pxm; -#endif if (pci_ignore_seg) domain = 0; @@ -494,19 +488,12 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) return NULL; } - node = -1; -#ifdef CONFIG_ACPI_NUMA - pxm = acpi_get_pxm(device->handle); - if (pxm >= 0) - node = pxm_to_node(pxm); - if (node != -1) - set_mp_bus_to_node(busnum, node); - else -#endif - node = get_mp_bus_to_node(busnum); + node = acpi_get_node(device->handle); + if (node == NUMA_NO_NODE) + node = x86_pci_root_bus_node(busnum); - if (node != -1 && !node_online(node)) - node = -1; + if (node != NUMA_NO_NODE && !node_online(node)) + node = NUMA_NO_NODE; info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) { @@ -519,15 +506,12 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) sd->domain = domain; sd->node = node; sd->companion = device; - /* - * Maybe the desired pci bus has been already scanned. In such case - * it is unnecessary to scan the pci bus with the given domain,busnum. - */ + bus = pci_find_bus(domain, busnum); if (bus) { /* - * If the desired bus exits, the content of bus->sysdata will - * be replaced by sd. + * If the desired bus has been scanned already, replace + * its bus->sysdata. */ memcpy(bus->sysdata, sd, sizeof(*sd)); kfree(info); @@ -572,15 +556,8 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) pcie_bus_configure_settings(child); } - if (bus && node != -1) { -#ifdef CONFIG_ACPI_NUMA - if (pxm >= 0) - dev_printk(KERN_DEBUG, &bus->dev, - "on NUMA node %d (pxm %d)\n", node, pxm); -#else + if (bus && node != NUMA_NO_NODE) dev_printk(KERN_DEBUG, &bus->dev, "on NUMA node %d\n", node); -#endif - } return bus; } diff --git a/arch/x86/pci/amd_bus.c b/arch/x86/pci/amd_bus.c index a48be98e9de..a313a7fb6b8 100644 --- a/arch/x86/pci/amd_bus.c +++ b/arch/x86/pci/amd_bus.c @@ -44,15 +44,6 @@ static struct pci_root_info __init *find_pci_root_info(int node, int link) return NULL; } -static void __init set_mp_bus_range_to_node(int min_bus, int max_bus, int node) -{ -#ifdef CONFIG_NUMA - int j; - - for (j = min_bus; j <= max_bus; j++) - set_mp_bus_to_node(j, node); -#endif -} /** * early_fill_mp_bus_to_node() * called before pcibios_scan_root and pci_scan_bus @@ -117,7 +108,6 @@ static int __init early_fill_mp_bus_info(void) min_bus = (reg >> 16) & 0xff; max_bus = (reg >> 24) & 0xff; node = (reg >> 4) & 0x07; - set_mp_bus_range_to_node(min_bus, max_bus, node); link = (reg >> 8) & 0x03; info = alloc_pci_root_info(min_bus, max_bus, node, link); diff --git a/arch/x86/pci/bus_numa.c b/arch/x86/pci/bus_numa.c index c2735feb250..f3a2cfc1412 100644 --- a/arch/x86/pci/bus_numa.c +++ b/arch/x86/pci/bus_numa.c @@ -10,9 +10,6 @@ static struct pci_root_info *x86_find_pci_root_info(int bus) { struct pci_root_info *info; - if (list_empty(&pci_root_infos)) - return NULL; - list_for_each_entry(info, &pci_root_infos, list) if (info->busn.start == bus) return info; @@ -20,6 +17,16 @@ static struct pci_root_info *x86_find_pci_root_info(int bus) return NULL; } +int x86_pci_root_bus_node(int bus) +{ + struct pci_root_info *info = x86_find_pci_root_info(bus); + + if (!info) + return NUMA_NO_NODE; + + return info->node; +} + void x86_pci_root_bus_resources(int bus, struct list_head *resources) { struct pci_root_info *info = x86_find_pci_root_info(bus); diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c index 981c2dbd72c..d491deddeba 100644 --- a/arch/x86/pci/common.c +++ b/arch/x86/pci/common.c @@ -456,19 +456,25 @@ void __init dmi_check_pciprobe(void) dmi_check_system(pciprobe_dmi_table); } -struct pci_bus *pcibios_scan_root(int busnum) +void pcibios_scan_root(int busnum) { - struct pci_bus *bus = NULL; + struct pci_bus *bus; + struct pci_sysdata *sd; + LIST_HEAD(resources); - while ((bus = pci_find_next_bus(bus)) != NULL) { - if (bus->number == busnum) { - /* Already scanned */ - return bus; - } + sd = kzalloc(sizeof(*sd), GFP_KERNEL); + if (!sd) { + printk(KERN_ERR "PCI: OOM, skipping PCI bus %02x\n", busnum); + return; + } + sd->node = x86_pci_root_bus_node(busnum); + x86_pci_root_bus_resources(busnum, &resources); + printk(KERN_DEBUG "PCI: Probing PCI hardware (bus %02x)\n", busnum); + bus = pci_scan_root_bus(NULL, busnum, &pci_root_ops, sd, &resources); + if (!bus) { + pci_free_resource_list(&resources); + kfree(sd); } - - return pci_scan_bus_on_node(busnum, &pci_root_ops, - get_mp_bus_to_node(busnum)); } void __init pcibios_set_cache_line_size(void) @@ -677,105 +683,3 @@ int pci_ext_cfg_avail(void) else return 0; } - -struct pci_bus *pci_scan_bus_on_node(int busno, struct pci_ops *ops, int node) -{ - LIST_HEAD(resources); - struct pci_bus *bus = NULL; - struct pci_sysdata *sd; - - /* - * Allocate per-root-bus (not per bus) arch-specific data. - * TODO: leak; this memory is never freed. - * It's arguable whether it's worth the trouble to care. - */ - sd = kzalloc(sizeof(*sd), GFP_KERNEL); - if (!sd) { - printk(KERN_ERR "PCI: OOM, skipping PCI bus %02x\n", busno); - return NULL; - } - sd->node = node; - x86_pci_root_bus_resources(busno, &resources); - printk(KERN_DEBUG "PCI: Probing PCI hardware (bus %02x)\n", busno); - bus = pci_scan_root_bus(NULL, busno, ops, sd, &resources); - if (!bus) { - pci_free_resource_list(&resources); - kfree(sd); - } - - return bus; -} - -struct pci_bus *pci_scan_bus_with_sysdata(int busno) -{ - return pci_scan_bus_on_node(busno, &pci_root_ops, -1); -} - -/* - * NUMA info for PCI busses - * - * Early arch code is responsible for filling in reasonable values here. - * A node id of "-1" means "use current node". In other words, if a bus - * has a -1 node id, it's not tightly coupled to any particular chunk - * of memory (as is the case on some Nehalem systems). - */ -#ifdef CONFIG_NUMA - -#define BUS_NR 256 - -#ifdef CONFIG_X86_64 - -static int mp_bus_to_node[BUS_NR] = { - [0 ... BUS_NR - 1] = -1 -}; - -void set_mp_bus_to_node(int busnum, int node) -{ - if (busnum >= 0 && busnum < BUS_NR) - mp_bus_to_node[busnum] = node; -} - -int get_mp_bus_to_node(int busnum) -{ - int node = -1; - - if (busnum < 0 || busnum > (BUS_NR - 1)) - return node; - - node = mp_bus_to_node[busnum]; - - /* - * let numa_node_id to decide it later in dma_alloc_pages - * if there is no ram on that node - */ - if (node != -1 && !node_online(node)) - node = -1; - - return node; -} - -#else /* CONFIG_X86_32 */ - -static int mp_bus_to_node[BUS_NR] = { - [0 ... BUS_NR - 1] = -1 -}; - -void set_mp_bus_to_node(int busnum, int node) -{ - if (busnum >= 0 && busnum < BUS_NR) - mp_bus_to_node[busnum] = (unsigned char) node; -} - -int get_mp_bus_to_node(int busnum) -{ - int node; - - if (busnum < 0 || busnum > (BUS_NR - 1)) - return 0; - node = mp_bus_to_node[busnum]; - return node; -} - -#endif /* CONFIG_X86_32 */ - -#endif /* CONFIG_NUMA */ diff --git a/arch/x86/pci/fixup.c b/arch/x86/pci/fixup.c index bca9e85daaa..94ae9ae9574 100644 --- a/arch/x86/pci/fixup.c +++ b/arch/x86/pci/fixup.c @@ -25,9 +25,9 @@ static void pci_fixup_i450nx(struct pci_dev *d) dev_dbg(&d->dev, "i450NX PXB %d: %02x/%02x/%02x\n", pxb, busno, suba, subb); if (busno) - pci_scan_bus_with_sysdata(busno); /* Bus A */ + pcibios_scan_root(busno); /* Bus A */ if (suba < subb) - pci_scan_bus_with_sysdata(suba+1); /* Bus B */ + pcibios_scan_root(suba+1); /* Bus B */ } pcibios_last_bus = -1; } @@ -42,7 +42,7 @@ static void pci_fixup_i450gx(struct pci_dev *d) u8 busno; pci_read_config_byte(d, 0x4a, &busno); dev_info(&d->dev, "i440KX/GX host bridge; secondary bus %02x\n", busno); - pci_scan_bus_with_sysdata(busno); + pcibios_scan_root(busno); pcibios_last_bus = -1; } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454GX, pci_fixup_i450gx); @@ -313,9 +313,10 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_MCH_PC1, pcie_r * IORESOURCE_ROM_SHADOW is used to associate the boot video * card with this copy. On laptops this copy has to be used since * the main ROM may be compressed or combined with another image. - * See pci_map_rom() for use of this flag. IORESOURCE_ROM_SHADOW - * is marked here since the boot video device will be the only enabled - * video device at this point. + * See pci_map_rom() for use of this flag. Before marking the device + * with IORESOURCE_ROM_SHADOW check if a vga_default_device is already set + * by either arch cde or vga-arbitration, if so only apply the fixup to this + * already determined primary video card. */ static void pci_fixup_video(struct pci_dev *pdev) @@ -346,12 +347,13 @@ static void pci_fixup_video(struct pci_dev *pdev) } bus = bus->parent; } - pci_read_config_word(pdev, PCI_COMMAND, &config); - if (config & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) { - pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW; - dev_printk(KERN_DEBUG, &pdev->dev, "Boot video device\n"); - if (!vga_default_device()) + if (!vga_default_device() || pdev == vga_default_device()) { + pci_read_config_word(pdev, PCI_COMMAND, &config); + if (config & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) { + pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW; + dev_printk(KERN_DEBUG, &pdev->dev, "Boot video device\n"); vga_set_default_device(pdev); + } } } DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID, diff --git a/arch/x86/pci/irq.c b/arch/x86/pci/irq.c index 372e9b8989b..84112f55dd7 100644 --- a/arch/x86/pci/irq.c +++ b/arch/x86/pci/irq.c @@ -136,13 +136,9 @@ static void __init pirq_peer_trick(void) busmap[e->bus] = 1; } for (i = 1; i < 256; i++) { - int node; if (!busmap[i] || pci_find_bus(0, i)) continue; - node = get_mp_bus_to_node(i); - if (pci_scan_bus_on_node(i, &pci_root_ops, node)) - printk(KERN_INFO "PCI: Discovered primary peer " - "bus %02x [IRQ]\n", i); + pcibios_scan_root(i); } pcibios_last_bus = -1; } diff --git a/arch/x86/pci/legacy.c b/arch/x86/pci/legacy.c index 4db96fb1c23..5b662c0faf8 100644 --- a/arch/x86/pci/legacy.c +++ b/arch/x86/pci/legacy.c @@ -37,19 +37,17 @@ int __init pci_legacy_init(void) void pcibios_scan_specific_bus(int busn) { int devfn; - long node; u32 l; if (pci_find_bus(0, busn)) return; - node = get_mp_bus_to_node(busn); for (devfn = 0; devfn < 256; devfn += 8) { if (!raw_pci_read(0, busn, devfn, PCI_VENDOR_ID, 2, &l) && l != 0x0000 && l != 0xffff) { DBG("Found device at %02x:%02x [%04x]\n", busn, devfn, l); printk(KERN_INFO "PCI: Discovered peer bus %02x\n", busn); - pci_scan_bus_on_node(busn, &pci_root_ops, node); + pcibios_scan_root(busn); return; } } diff --git a/arch/x86/pci/numaq_32.c b/arch/x86/pci/numaq_32.c index 72c229f9ebc..080eb0374ff 100644 --- a/arch/x86/pci/numaq_32.c +++ b/arch/x86/pci/numaq_32.c @@ -135,11 +135,11 @@ static void pci_fixup_i450nx(struct pci_dev *d) pxb, busno, suba, subb); if (busno) { /* Bus A */ - pci_scan_bus_with_sysdata(QUADLOCAL2BUS(quad, busno)); + pcibios_scan_root(QUADLOCAL2BUS(quad, busno)); } if (suba < subb) { /* Bus B */ - pci_scan_bus_with_sysdata(QUADLOCAL2BUS(quad, suba+1)); + pcibios_scan_root(QUADLOCAL2BUS(quad, suba+1)); } } pcibios_last_bus = -1; @@ -159,7 +159,7 @@ int __init pci_numaq_init(void) continue; printk("Scanning PCI bus %d for quad %d\n", QUADLOCAL2BUS(quad,0), quad); - pci_scan_bus_with_sysdata(QUADLOCAL2BUS(quad, 0)); + pcibios_scan_root(QUADLOCAL2BUS(quad, 0)); } return 0; } diff --git a/arch/x86/pci/visws.c b/arch/x86/pci/visws.c index 3e6d2a6db86..cd9d4d1681d 100644 --- a/arch/x86/pci/visws.c +++ b/arch/x86/pci/visws.c @@ -78,8 +78,8 @@ int __init pci_visws_init(void) "bridge B (PIIX4) bus: %u\n", pci_bus1, pci_bus0); raw_pci_ops = &pci_direct_conf1; - pci_scan_bus_with_sysdata(pci_bus0); - pci_scan_bus_with_sysdata(pci_bus1); + pcibios_scan_root(pci_bus0); + pcibios_scan_root(pci_bus1); pci_fixup_irqs(pci_common_swizzle, visws_map_irq); pcibios_resource_survey(); /* Request bus scan */ diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index 9e6816ef280..24b5476449a 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -60,7 +60,7 @@ int node_to_pxm(int node) return node_to_pxm_map[node]; } -void __acpi_map_pxm_to_node(int pxm, int node) +static void __acpi_map_pxm_to_node(int pxm, int node) { if (pxm_to_node_map[pxm] == NUMA_NO_NODE || node < pxm_to_node_map[pxm]) pxm_to_node_map[pxm] = node; @@ -193,7 +193,7 @@ static int __init acpi_parse_slit(struct acpi_table_header *table) return 0; } -void __init __attribute__ ((weak)) +void __init __weak acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa) { printk(KERN_WARNING PREFIX @@ -314,7 +314,7 @@ int __init acpi_numa_init(void) return 0; } -int acpi_get_pxm(acpi_handle h) +static int acpi_get_pxm(acpi_handle h) { unsigned long long pxm; acpi_status status; @@ -331,14 +331,14 @@ int acpi_get_pxm(acpi_handle h) return -1; } -int acpi_get_node(acpi_handle *handle) +int acpi_get_node(acpi_handle handle) { - int pxm, node = NUMA_NO_NODE; + int pxm; pxm = acpi_get_pxm(handle); - if (pxm >= 0 && pxm < MAX_PXM_DOMAINS) - node = acpi_map_pxm_to_node(pxm); + if (pxm < 0 || pxm >= MAX_PXM_DOMAINS) + return NUMA_NO_NODE; - return node; + return acpi_map_pxm_to_node(pxm); } EXPORT_SYMBOL(acpi_get_node); diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index a52a5b662f3..5a0bf8ed649 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -1166,13 +1166,13 @@ static inline void ahci_gtf_filter_workaround(struct ata_host *host) static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports, struct ahci_host_priv *hpriv) { - int rc, nvec; + int nvec; if (hpriv->flags & AHCI_HFLAG_NO_MSI) goto intx; - rc = pci_msi_vec_count(pdev); - if (rc < 0) + nvec = pci_msi_vec_count(pdev); + if (nvec < 0) goto intx; /* @@ -1180,21 +1180,19 @@ static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports, * Message mode could be enforced. In this case assume that advantage * of multipe MSIs is negated and use single MSI mode instead. */ - if (rc < n_ports) + if (nvec < n_ports) goto single_msi; - nvec = rc; - rc = pci_enable_msi_block(pdev, nvec); - if (rc < 0) - goto intx; - else if (rc > 0) + nvec = pci_enable_msi_range(pdev, nvec, nvec); + if (nvec == -ENOSPC) goto single_msi; + else if (nvec < 0) + goto intx; return nvec; single_msi: - rc = pci_enable_msi(pdev); - if (rc) + if (pci_enable_msi(pdev)) goto intx; return 1; diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c index 725c46162bb..2ac754e18bc 100644 --- a/drivers/bus/mvebu-mbus.c +++ b/drivers/bus/mvebu-mbus.c @@ -870,14 +870,14 @@ static void __init mvebu_mbus_get_pcie_resources(struct device_node *np, ret = of_property_read_u32_array(np, "pcie-mem-aperture", reg, ARRAY_SIZE(reg)); if (!ret) { mem->start = reg[0]; - mem->end = mem->start + reg[1]; + mem->end = mem->start + reg[1] - 1; mem->flags = IORESOURCE_MEM; } ret = of_property_read_u32_array(np, "pcie-io-aperture", reg, ARRAY_SIZE(reg)); if (!ret) { io->start = reg[0]; - io->end = io->start + reg[1]; + io->end = io->start + reg[1] - 1; io->flags = IORESOURCE_IO; } } diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 7f2af9aca03..309023f12d7 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -319,7 +319,8 @@ static int drm_open_helper(struct inode *inode, struct file *filp, pci_dev_put(pci_dev); } if (!dev->hose) { - struct pci_bus *b = pci_bus_b(pci_root_buses.next); + struct pci_bus *b = list_entry(pci_root_buses.next, + struct pci_bus, node); if (b) dev->hose = b->sysdata; } diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h index e400fbe411d..cff039df056 100644 --- a/drivers/iommu/amd_iommu_types.h +++ b/drivers/iommu/amd_iommu_types.h @@ -25,6 +25,7 @@ #include <linux/list.h> #include <linux/spinlock.h> #include <linux/pci.h> +#include <linux/irqreturn.h> /* * Maximum number of IOMMUs supported diff --git a/drivers/message/i2o/iop.c b/drivers/message/i2o/iop.c index a8c08f332da..92752fb5b2d 100644 --- a/drivers/message/i2o/iop.c +++ b/drivers/message/i2o/iop.c @@ -652,6 +652,44 @@ static int i2o_iop_activate(struct i2o_controller *c) return i2o_hrt_get(c); }; +static void i2o_res_alloc(struct i2o_controller *c, unsigned long flags) +{ + i2o_status_block *sb = c->status_block.virt; + struct resource *res = &c->mem_resource; + resource_size_t size, align; + int err; + + res->name = c->pdev->bus->name; + res->flags = flags; + res->start = 0; + res->end = 0; + osm_info("%s: requires private memory resources.\n", c->name); + + if (flags & IORESOURCE_MEM) { + size = sb->desired_mem_size; + align = 1 << 20; /* unspecified, use 1Mb and play safe */ + } else { + size = sb->desired_io_size; + align = 1 << 12; /* unspecified, use 4Kb and play safe */ + } + + err = pci_bus_alloc_resource(c->pdev->bus, res, size, align, 0, 0, + NULL, NULL); + if (err < 0) + return; + + if (flags & IORESOURCE_MEM) { + c->mem_alloc = 1; + sb->current_mem_size = resource_size(res); + sb->current_mem_base = res->start; + } else if (flags & IORESOURCE_IO) { + c->io_alloc = 1; + sb->current_io_size = resource_size(res); + sb->current_io_base = res->start; + } + osm_info("%s: allocated PCI space %pR\n", c->name, res); +} + /** * i2o_iop_systab_set - Set the I2O System Table of the specified IOP * @c: I2O controller to which the system table should be send @@ -665,52 +703,13 @@ static int i2o_iop_systab_set(struct i2o_controller *c) struct i2o_message *msg; i2o_status_block *sb = c->status_block.virt; struct device *dev = &c->pdev->dev; - struct resource *root; int rc; - if (sb->current_mem_size < sb->desired_mem_size) { - struct resource *res = &c->mem_resource; - res->name = c->pdev->bus->name; - res->flags = IORESOURCE_MEM; - res->start = 0; - res->end = 0; - osm_info("%s: requires private memory resources.\n", c->name); - root = pci_find_parent_resource(c->pdev, res); - if (root == NULL) - osm_warn("%s: Can't find parent resource!\n", c->name); - if (root && allocate_resource(root, res, sb->desired_mem_size, sb->desired_mem_size, sb->desired_mem_size, 1 << 20, /* Unspecified, so use 1Mb and play safe */ - NULL, NULL) >= 0) { - c->mem_alloc = 1; - sb->current_mem_size = resource_size(res); - sb->current_mem_base = res->start; - osm_info("%s: allocated %llu bytes of PCI memory at " - "0x%016llX.\n", c->name, - (unsigned long long)resource_size(res), - (unsigned long long)res->start); - } - } + if (sb->current_mem_size < sb->desired_mem_size) + i2o_res_alloc(c, IORESOURCE_MEM); - if (sb->current_io_size < sb->desired_io_size) { - struct resource *res = &c->io_resource; - res->name = c->pdev->bus->name; - res->flags = IORESOURCE_IO; - res->start = 0; - res->end = 0; - osm_info("%s: requires private memory resources.\n", c->name); - root = pci_find_parent_resource(c->pdev, res); - if (root == NULL) - osm_warn("%s: Can't find parent resource!\n", c->name); - if (root && allocate_resource(root, res, sb->desired_io_size, sb->desired_io_size, sb->desired_io_size, 1 << 20, /* Unspecified, so use 1Mb and play safe */ - NULL, NULL) >= 0) { - c->io_alloc = 1; - sb->current_io_size = resource_size(res); - sb->current_mem_base = res->start; - osm_info("%s: allocated %llu bytes of PCI I/O at " - "0x%016llX.\n", c->name, - (unsigned long long)resource_size(res), - (unsigned long long)res->start); - } - } + if (sb->current_io_size < sb->desired_io_size) + i2o_res_alloc(c, IORESOURCE_IO); msg = i2o_msg_get_wait(c, I2O_TIMEOUT_MESSAGE_GET); if (IS_ERR(msg)) diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h index 80bd829fbd9..893d5119fa9 100644 --- a/drivers/misc/mei/hw-me.h +++ b/drivers/misc/mei/hw-me.h @@ -20,6 +20,7 @@ #define _MEI_INTERFACE_H_ #include <linux/mei.h> +#include <linux/irqreturn.h> #include "mei_dev.h" #include "client.h" diff --git a/drivers/misc/mic/card/mic_device.h b/drivers/misc/mic/card/mic_device.h index 347b9b3b791..306f502be95 100644 --- a/drivers/misc/mic/card/mic_device.h +++ b/drivers/misc/mic/card/mic_device.h @@ -29,6 +29,7 @@ #include <linux/workqueue.h> #include <linux/io.h> +#include <linux/irqreturn.h> /** * struct mic_intr_info - Contains h/w specific interrupt sources info diff --git a/drivers/misc/mic/host/mic_device.h b/drivers/misc/mic/host/mic_device.h index 1a6edce2ecd..0398c696d25 100644 --- a/drivers/misc/mic/host/mic_device.h +++ b/drivers/misc/mic/host/mic_device.h @@ -24,6 +24,7 @@ #include <linux/cdev.h> #include <linux/idr.h> #include <linux/notifier.h> +#include <linux/irqreturn.h> #include "mic_intr.h" diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 17d2b07ee67..e2501ac6fe8 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -33,21 +33,15 @@ obj-$(CONFIG_PCI_IOV) += iov.o # # Some architectures use the generic PCI setup functions # -obj-$(CONFIG_X86) += setup-bus.o -obj-$(CONFIG_ALPHA) += setup-bus.o setup-irq.o -obj-$(CONFIG_ARM) += setup-bus.o setup-irq.o -obj-$(CONFIG_UNICORE32) += setup-bus.o setup-irq.o -obj-$(CONFIG_PARISC) += setup-bus.o -obj-$(CONFIG_SUPERH) += setup-bus.o setup-irq.o -obj-$(CONFIG_PPC) += setup-bus.o -obj-$(CONFIG_FRV) += setup-bus.o -obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o +obj-$(CONFIG_ALPHA) += setup-irq.o +obj-$(CONFIG_ARM) += setup-irq.o +obj-$(CONFIG_UNICORE32) += setup-irq.o +obj-$(CONFIG_SUPERH) += setup-irq.o +obj-$(CONFIG_MIPS) += setup-irq.o obj-$(CONFIG_X86_VISWS) += setup-irq.o -obj-$(CONFIG_MN10300) += setup-bus.o -obj-$(CONFIG_MICROBLAZE) += setup-bus.o -obj-$(CONFIG_TILE) += setup-bus.o setup-irq.o -obj-$(CONFIG_SPARC_LEON) += setup-bus.o setup-irq.o -obj-$(CONFIG_M68K) += setup-bus.o setup-irq.o +obj-$(CONFIG_TILE) += setup-irq.o +obj-$(CONFIG_SPARC_LEON) += setup-irq.o +obj-$(CONFIG_M68K) += setup-irq.o # # ACPI Related PCI FW Functions diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 38901665c77..fb8aed307c2 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -132,7 +132,7 @@ static void pci_clip_resource_to_region(struct pci_bus *bus, static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res, resource_size_t size, resource_size_t align, - resource_size_t min, unsigned int type_mask, + resource_size_t min, unsigned long type_mask, resource_size_t (*alignf)(void *, const struct resource *, resource_size_t, @@ -144,7 +144,7 @@ static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res, struct resource *r, avail; resource_size_t max; - type_mask |= IORESOURCE_IO | IORESOURCE_MEM; + type_mask |= IORESOURCE_TYPE_BITS; pci_bus_for_each_resource(bus, r, i) { if (!r) @@ -200,7 +200,7 @@ static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res, */ int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, resource_size_t size, resource_size_t align, - resource_size_t min, unsigned int type_mask, + resource_size_t min, unsigned long type_mask, resource_size_t (*alignf)(void *, const struct resource *, resource_size_t, diff --git a/drivers/pci/host-bridge.c b/drivers/pci/host-bridge.c index 06ace6248c6..47aaf22d814 100644 --- a/drivers/pci/host-bridge.c +++ b/drivers/pci/host-bridge.c @@ -32,11 +32,6 @@ void pci_set_host_bridge_release(struct pci_host_bridge *bridge, bridge->release_data = release_data; } -static bool resource_contains(struct resource *res1, struct resource *res2) -{ - return res1->start <= res2->start && res1->end >= res2->end; -} - void pcibios_resource_to_bus(struct pci_bus *bus, struct pci_bus_region *region, struct resource *res) { @@ -45,9 +40,6 @@ void pcibios_resource_to_bus(struct pci_bus *bus, struct pci_bus_region *region, resource_size_t offset = 0; list_for_each_entry(window, &bridge->windows, list) { - if (resource_type(res) != resource_type(window->res)) - continue; - if (resource_contains(window->res, res)) { offset = window->offset; break; diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 47d46c6d846..a6f67ec8882 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -27,7 +27,7 @@ config PCI_TEGRA config PCI_RCAR_GEN2 bool "Renesas R-Car Gen2 Internal PCI controller" - depends on ARM && (ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST) + depends on ARCH_SHMOBILE || (ARM && COMPILE_TEST) help Say Y here if you want internal PCI support on R-Car Gen2 SoC. There are 3 internal PCI controllers available with a single diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c index e8663a8c340..ee082509b0b 100644 --- a/drivers/pci/host/pci-imx6.c +++ b/drivers/pci/host/pci-imx6.c @@ -424,20 +424,40 @@ static void imx6_pcie_reset_phy(struct pcie_port *pp) static int imx6_pcie_link_up(struct pcie_port *pp) { - u32 rc, ltssm, rx_valid; + u32 rc, debug_r0, rx_valid; + int count = 5; /* - * Test if the PHY reports that the link is up and also that - * the link training finished. It might happen that the PHY - * reports the link is already up, but the link training bit - * is still set, so make sure to check the training is done - * as well here. + * Test if the PHY reports that the link is up and also that the LTSSM + * training finished. There are three possible states of the link when + * this code is called: + * 1) The link is DOWN (unlikely) + * The link didn't come up yet for some reason. This usually means + * we have a real problem somewhere. Reset the PHY and exit. This + * state calls for inspection of the DEBUG registers. + * 2) The link is UP, but still in LTSSM training + * Wait for the training to finish, which should take a very short + * time. If the training does not finish, we have a problem and we + * need to inspect the DEBUG registers. If the training does finish, + * the link is up and operating correctly. + * 3) The link is UP and no longer in LTSSM training + * The link is up and operating correctly. */ - rc = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1); - if ((rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_UP) && - !(rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING)) - return 1; - + while (1) { + rc = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1); + if (!(rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_UP)) + break; + if (!(rc & PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING)) + return 1; + if (!count--) + break; + dev_dbg(pp->dev, "Link is up, but still in training\n"); + /* + * Wait a little bit, then re-check if the link finished + * the training. + */ + usleep_range(1000, 2000); + } /* * From L0, initiate MAC entry to gen2 if EP/RC supports gen2. * Wait 2ms (LTSSM timeout is 24ms, PHY lock is ~5us in gen2). @@ -446,15 +466,16 @@ static int imx6_pcie_link_up(struct pcie_port *pp) * to gen2 is stuck */ pcie_phy_read(pp->dbi_base, PCIE_PHY_RX_ASIC_OUT, &rx_valid); - ltssm = readl(pp->dbi_base + PCIE_PHY_DEBUG_R0) & 0x3F; + debug_r0 = readl(pp->dbi_base + PCIE_PHY_DEBUG_R0); if (rx_valid & 0x01) return 0; - if (ltssm != 0x0d) + if ((debug_r0 & 0x3f) != 0x0d) return 0; dev_err(pp->dev, "transition to gen2 is stuck, reset PHY!\n"); + dev_dbg(pp->dev, "debug_r0=%08x debug_r1=%08x\n", debug_r0, rc); imx6_pcie_reset_phy(pp); diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 0e79665afd4..d3d1cfd51e0 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -101,7 +101,9 @@ struct mvebu_pcie { struct mvebu_pcie_port *ports; struct msi_chip *msi; struct resource io; + char io_name[30]; struct resource realio; + char mem_name[30]; struct resource mem; struct resource busn; int nports; @@ -672,10 +674,30 @@ static int mvebu_pcie_setup(int nr, struct pci_sys_data *sys) { struct mvebu_pcie *pcie = sys_to_pcie(sys); int i; + int domain = 0; - if (resource_size(&pcie->realio) != 0) +#ifdef CONFIG_PCI_DOMAINS + domain = sys->domain; +#endif + + snprintf(pcie->mem_name, sizeof(pcie->mem_name), "PCI MEM %04x", + domain); + pcie->mem.name = pcie->mem_name; + + snprintf(pcie->io_name, sizeof(pcie->io_name), "PCI I/O %04x", domain); + pcie->realio.name = pcie->io_name; + + if (request_resource(&iomem_resource, &pcie->mem)) + return 0; + + if (resource_size(&pcie->realio) != 0) { + if (request_resource(&ioport_resource, &pcie->realio)) { + release_resource(&pcie->mem); + return 0; + } pci_add_resource_offset(&sys->resources, &pcie->realio, sys->io_offset); + } pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset); pci_add_resource(&sys->resources, &pcie->busn); @@ -797,7 +819,7 @@ static int mvebu_get_tgt_attr(struct device_node *np, int devfn, for (i = 0; i < nranges; i++) { u32 flags = of_read_number(range, 1); - u32 slot = of_read_number(range, 2); + u32 slot = of_read_number(range + 1, 1); u64 cpuaddr = of_read_number(range + na, pna); unsigned long rtype; diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c index ceec147baec..fd3e3ab5650 100644 --- a/drivers/pci/host/pci-rcar-gen2.c +++ b/drivers/pci/host/pci-rcar-gen2.c @@ -18,6 +18,7 @@ #include <linux/pci.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/sizes.h> #include <linux/slab.h> /* AHB-PCI Bridge PCI communication registers */ @@ -39,9 +40,26 @@ #define RCAR_PCI_INT_ENABLE_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x20) #define RCAR_PCI_INT_STATUS_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x24) +#define RCAR_PCI_INT_SIGTABORT (1 << 0) +#define RCAR_PCI_INT_SIGRETABORT (1 << 1) +#define RCAR_PCI_INT_REMABORT (1 << 2) +#define RCAR_PCI_INT_PERR (1 << 3) +#define RCAR_PCI_INT_SIGSERR (1 << 4) +#define RCAR_PCI_INT_RESERR (1 << 5) +#define RCAR_PCI_INT_WIN1ERR (1 << 12) +#define RCAR_PCI_INT_WIN2ERR (1 << 13) #define RCAR_PCI_INT_A (1 << 16) #define RCAR_PCI_INT_B (1 << 17) #define RCAR_PCI_INT_PME (1 << 19) +#define RCAR_PCI_INT_ALLERRORS (RCAR_PCI_INT_SIGTABORT | \ + RCAR_PCI_INT_SIGRETABORT | \ + RCAR_PCI_INT_SIGRETABORT | \ + RCAR_PCI_INT_REMABORT | \ + RCAR_PCI_INT_PERR | \ + RCAR_PCI_INT_SIGSERR | \ + RCAR_PCI_INT_RESERR | \ + RCAR_PCI_INT_WIN1ERR | \ + RCAR_PCI_INT_WIN2ERR) #define RCAR_AHB_BUS_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x30) #define RCAR_AHB_BUS_MMODE_HTRANS (1 << 0) @@ -74,9 +92,6 @@ #define RCAR_PCI_UNIT_REV_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x48) -/* Number of internal PCI controllers */ -#define RCAR_PCI_NR_CONTROLLERS 3 - struct rcar_pci_priv { struct device *dev; void __iomem *reg; @@ -84,6 +99,7 @@ struct rcar_pci_priv { struct resource mem_res; struct resource *cfg_res; int irq; + unsigned long window_size; }; /* PCI configuration space operations */ @@ -102,6 +118,10 @@ static void __iomem *rcar_pci_cfg_base(struct pci_bus *bus, unsigned int devfn, if (slot > 2) return NULL; + /* bridge logic only has registers to 0x40 */ + if (slot == 0x0 && where >= 0x40) + return NULL; + val = slot ? RCAR_AHBPCI_WIN1_DEVICE | RCAR_AHBPCI_WIN_CTR_CFG : RCAR_AHBPCI_WIN1_HOST | RCAR_AHBPCI_WIN_CTR_CFG; @@ -156,7 +176,7 @@ static int rcar_pci_write_config(struct pci_bus *bus, unsigned int devfn, } /* PCI interrupt mapping */ -static int __init rcar_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +static int rcar_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { struct pci_sys_data *sys = dev->bus->sysdata; struct rcar_pci_priv *priv = sys->private_data; @@ -164,8 +184,48 @@ static int __init rcar_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) return priv->irq; } +#ifdef CONFIG_PCI_DEBUG +/* if debug enabled, then attach an error handler irq to the bridge */ + +static irqreturn_t rcar_pci_err_irq(int irq, void *pw) +{ + struct rcar_pci_priv *priv = pw; + u32 status = ioread32(priv->reg + RCAR_PCI_INT_STATUS_REG); + + if (status & RCAR_PCI_INT_ALLERRORS) { + dev_err(priv->dev, "error irq: status %08x\n", status); + + /* clear the error(s) */ + iowrite32(status & RCAR_PCI_INT_ALLERRORS, + priv->reg + RCAR_PCI_INT_STATUS_REG); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static void rcar_pci_setup_errirq(struct rcar_pci_priv *priv) +{ + int ret; + u32 val; + + ret = devm_request_irq(priv->dev, priv->irq, rcar_pci_err_irq, + IRQF_SHARED, "error irq", priv); + if (ret) { + dev_err(priv->dev, "cannot claim IRQ for error handling\n"); + return; + } + + val = ioread32(priv->reg + RCAR_PCI_INT_ENABLE_REG); + val |= RCAR_PCI_INT_ALLERRORS; + iowrite32(val, priv->reg + RCAR_PCI_INT_ENABLE_REG); +} +#else +static inline void rcar_pci_setup_errirq(struct rcar_pci_priv *priv) { } +#endif + /* PCI host controller setup */ -static int __init rcar_pci_setup(int nr, struct pci_sys_data *sys) +static int rcar_pci_setup(int nr, struct pci_sys_data *sys) { struct rcar_pci_priv *priv = sys->private_data; void __iomem *reg = priv->reg; @@ -183,10 +243,31 @@ static int __init rcar_pci_setup(int nr, struct pci_sys_data *sys) iowrite32(val, reg + RCAR_USBCTR_REG); udelay(4); - /* De-assert reset and set PCIAHB window1 size to 1GB */ + /* De-assert reset and reset PCIAHB window1 size */ val &= ~(RCAR_USBCTR_PCIAHB_WIN1_MASK | RCAR_USBCTR_PCICLK_MASK | RCAR_USBCTR_USBH_RST | RCAR_USBCTR_PLL_RST); - iowrite32(val | RCAR_USBCTR_PCIAHB_WIN1_1G, reg + RCAR_USBCTR_REG); + + /* Setup PCIAHB window1 size */ + switch (priv->window_size) { + case SZ_2G: + val |= RCAR_USBCTR_PCIAHB_WIN1_2G; + break; + case SZ_1G: + val |= RCAR_USBCTR_PCIAHB_WIN1_1G; + break; + case SZ_512M: + val |= RCAR_USBCTR_PCIAHB_WIN1_512M; + break; + default: + pr_warn("unknown window size %ld - defaulting to 256M\n", + priv->window_size); + priv->window_size = SZ_256M; + /* fall-through */ + case SZ_256M: + val |= RCAR_USBCTR_PCIAHB_WIN1_256M; + break; + } + iowrite32(val, reg + RCAR_USBCTR_REG); /* Configure AHB master and slave modes */ iowrite32(RCAR_AHB_BUS_MODE, reg + RCAR_AHB_BUS_CTR_REG); @@ -197,7 +278,7 @@ static int __init rcar_pci_setup(int nr, struct pci_sys_data *sys) RCAR_PCI_ARBITER_PCIBP_MODE; iowrite32(val, reg + RCAR_PCI_ARBITER_CTR_REG); - /* PCI-AHB mapping: 0x40000000-0x80000000 */ + /* PCI-AHB mapping: 0x40000000 base */ iowrite32(0x40000000 | RCAR_PCIAHB_PREFETCH16, reg + RCAR_PCIAHB_WIN1_CTR_REG); @@ -224,10 +305,15 @@ static int __init rcar_pci_setup(int nr, struct pci_sys_data *sys) iowrite32(RCAR_PCI_INT_A | RCAR_PCI_INT_B | RCAR_PCI_INT_PME, reg + RCAR_PCI_INT_ENABLE_REG); + if (priv->irq > 0) + rcar_pci_setup_errirq(priv); + /* Add PCI resources */ pci_add_resource(&sys->resources, &priv->io_res); pci_add_resource(&sys->resources, &priv->mem_res); + /* Setup bus number based on platform device id */ + sys->busnr = to_platform_device(priv->dev)->id; return 1; } @@ -236,48 +322,13 @@ static struct pci_ops rcar_pci_ops = { .write = rcar_pci_write_config, }; -static struct hw_pci rcar_hw_pci __initdata = { - .map_irq = rcar_pci_map_irq, - .ops = &rcar_pci_ops, - .setup = rcar_pci_setup, -}; - -static int rcar_pci_count __initdata; - -static int __init rcar_pci_add_controller(struct rcar_pci_priv *priv) -{ - void **private_data; - int count; - - if (rcar_hw_pci.nr_controllers < rcar_pci_count) - goto add_priv; - - /* (Re)allocate private data pointer array if needed */ - count = rcar_pci_count + RCAR_PCI_NR_CONTROLLERS; - private_data = kzalloc(count * sizeof(void *), GFP_KERNEL); - if (!private_data) - return -ENOMEM; - - rcar_pci_count = count; - if (rcar_hw_pci.private_data) { - memcpy(private_data, rcar_hw_pci.private_data, - rcar_hw_pci.nr_controllers * sizeof(void *)); - kfree(rcar_hw_pci.private_data); - } - - rcar_hw_pci.private_data = private_data; - -add_priv: - /* Add private data pointer to the array */ - rcar_hw_pci.private_data[rcar_hw_pci.nr_controllers++] = priv; - return 0; -} - -static int __init rcar_pci_probe(struct platform_device *pdev) +static int rcar_pci_probe(struct platform_device *pdev) { struct resource *cfg_res, *mem_res; struct rcar_pci_priv *priv; void __iomem *reg; + struct hw_pci hw; + void *hw_private[1]; cfg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); reg = devm_ioremap_resource(&pdev->dev, cfg_res); @@ -308,31 +359,34 @@ static int __init rcar_pci_probe(struct platform_device *pdev) priv->reg = reg; priv->dev = &pdev->dev; - return rcar_pci_add_controller(priv); + if (priv->irq < 0) { + dev_err(&pdev->dev, "no valid irq found\n"); + return priv->irq; + } + + priv->window_size = SZ_1G; + + hw_private[0] = priv; + memset(&hw, 0, sizeof(hw)); + hw.nr_controllers = ARRAY_SIZE(hw_private); + hw.private_data = hw_private; + hw.map_irq = rcar_pci_map_irq; + hw.ops = &rcar_pci_ops; + hw.setup = rcar_pci_setup; + pci_common_init_dev(&pdev->dev, &hw); + return 0; } static struct platform_driver rcar_pci_driver = { .driver = { .name = "pci-rcar-gen2", + .owner = THIS_MODULE, + .suppress_bind_attrs = true, }, + .probe = rcar_pci_probe, }; -static int __init rcar_pci_init(void) -{ - int retval; - - retval = platform_driver_probe(&rcar_pci_driver, rcar_pci_probe); - if (!retval) - pci_common_init(&rcar_hw_pci); - - /* Private data pointer array is not needed any more */ - kfree(rcar_hw_pci.private_data); - rcar_hw_pci.private_data = NULL; - - return retval; -} - -subsys_initcall(rcar_pci_init); +module_platform_driver(rcar_pci_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Renesas R-Car Gen2 internal PCI"); diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 2e48ecf09e2..509a29d8450 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -798,7 +798,7 @@ void dw_pcie_setup_rc(struct pcie_port *pp) /* setup RC BARs */ dw_pcie_writel_rc(pp, 0x00000004, PCI_BASE_ADDRESS_0); - dw_pcie_writel_rc(pp, 0x00000004, PCI_BASE_ADDRESS_1); + dw_pcie_writel_rc(pp, 0x00000000, PCI_BASE_ADDRESS_1); /* setup interrupt pins */ dw_pcie_readl_rc(pp, PCI_INTERRUPT_LINE, &val); diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 828acf422c1..bccc27ee103 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -424,7 +424,7 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) */ static unsigned char acpiphp_max_busnr(struct pci_bus *bus) { - struct list_head *tmp; + struct pci_bus *tmp; unsigned char max, n; /* @@ -437,8 +437,8 @@ static unsigned char acpiphp_max_busnr(struct pci_bus *bus) */ max = bus->busn_res.start; - list_for_each(tmp, &bus->children) { - n = pci_bus_max_busnr(pci_bus_b(tmp)); + list_for_each_entry(tmp, &bus->children, node) { + n = pci_bus_max_busnr(tmp); if (n > max) max = n; } diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c index 31273e155e6..037e2612c5b 100644 --- a/drivers/pci/hotplug/cpqphp_core.c +++ b/drivers/pci/hotplug/cpqphp_core.c @@ -920,12 +920,12 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) bus->max_bus_speed = PCI_SPEED_100MHz_PCIX; break; } - if (bus_cap & 20) { + if (bus_cap & 0x20) { dbg("bus max supports 66MHz PCI-X\n"); bus->max_bus_speed = PCI_SPEED_66MHz_PCIX; break; } - if (bus_cap & 10) { + if (bus_cap & 0x10) { dbg("bus max supports 66MHz PCI\n"); bus->max_bus_speed = PCI_SPEED_66MHz; break; diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 88b37cad4b3..8a66866b8cf 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -76,6 +76,7 @@ struct slot { struct hotplug_slot *hotplug_slot; struct delayed_work work; /* work for button event */ struct mutex lock; + struct mutex hotplug_lock; struct workqueue_struct *wq; }; @@ -109,6 +110,8 @@ struct controller { #define INT_BUTTON_PRESS 7 #define INT_BUTTON_RELEASE 8 #define INT_BUTTON_CANCEL 9 +#define INT_LINK_UP 10 +#define INT_LINK_DOWN 11 #define STATIC_STATE 0 #define BLINKINGON_STATE 1 @@ -132,6 +135,7 @@ u8 pciehp_handle_attention_button(struct slot *p_slot); u8 pciehp_handle_switch_change(struct slot *p_slot); u8 pciehp_handle_presence_change(struct slot *p_slot); u8 pciehp_handle_power_fault(struct slot *p_slot); +void pciehp_handle_linkstate_change(struct slot *p_slot); int pciehp_configure_device(struct slot *p_slot); int pciehp_unconfigure_device(struct slot *p_slot); void pciehp_queue_pushbutton_work(struct work_struct *work); @@ -153,6 +157,7 @@ void pciehp_green_led_on(struct slot *slot); void pciehp_green_led_off(struct slot *slot); void pciehp_green_led_blink(struct slot *slot); int pciehp_check_link_status(struct controller *ctrl); +bool pciehp_check_link_active(struct controller *ctrl); void pciehp_release_ctrl(struct controller *ctrl); int pciehp_reset_slot(struct slot *slot, int probe); diff --git a/drivers/pci/hotplug/pciehp_acpi.c b/drivers/pci/hotplug/pciehp_acpi.c index eddddd447d0..20fea57d214 100644 --- a/drivers/pci/hotplug/pciehp_acpi.c +++ b/drivers/pci/hotplug/pciehp_acpi.c @@ -112,6 +112,7 @@ static struct pcie_port_service_driver __initdata dummy_driver = { static int __init select_detection_mode(void) { struct dummy_slot *slot, *tmp; + if (pcie_port_service_register(&dummy_driver)) return PCIEHP_DETECT_ACPI; pcie_port_service_unregister(&dummy_driver); diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 53b58debc28..0e0a2fff20a 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -108,6 +108,7 @@ static int init_slot(struct controller *ctrl) ops = kzalloc(sizeof(*ops), GFP_KERNEL); if (!ops) goto out; + ops->enable_slot = enable_slot; ops->disable_slot = disable_slot; ops->get_power_status = get_power_status; @@ -283,8 +284,11 @@ static int pciehp_probe(struct pcie_device *dev) slot = ctrl->slot; pciehp_get_adapter_status(slot, &occupied); pciehp_get_power_status(slot, &poweron); - if (occupied && pciehp_force) + if (occupied && pciehp_force) { + mutex_lock(&slot->hotplug_lock); pciehp_enable_slot(slot); + mutex_unlock(&slot->hotplug_lock); + } /* If empty slot's power status is on, turn power off */ if (!occupied && poweron && POWER_CTRL(ctrl)) pciehp_power_off_slot(slot); @@ -328,10 +332,12 @@ static int pciehp_resume (struct pcie_device *dev) /* Check if slot is occupied */ pciehp_get_adapter_status(slot, &status); + mutex_lock(&slot->hotplug_lock); if (status) pciehp_enable_slot(slot); else pciehp_disable_slot(slot); + mutex_unlock(&slot->hotplug_lock); return 0; } #endif /* PM */ diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 50628487597..c75e6a678dc 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -150,6 +150,27 @@ u8 pciehp_handle_power_fault(struct slot *p_slot) return 1; } +void pciehp_handle_linkstate_change(struct slot *p_slot) +{ + u32 event_type; + struct controller *ctrl = p_slot->ctrl; + + /* Link Status Change */ + ctrl_dbg(ctrl, "Data Link Layer State change\n"); + + if (pciehp_check_link_active(ctrl)) { + ctrl_info(ctrl, "slot(%s): Link Up event\n", + slot_name(p_slot)); + event_type = INT_LINK_UP; + } else { + ctrl_info(ctrl, "slot(%s): Link Down event\n", + slot_name(p_slot)); + event_type = INT_LINK_DOWN; + } + + queue_interrupt_event(p_slot, event_type); +} + /* The following routines constitute the bulk of the hotplug controller logic */ @@ -212,7 +233,8 @@ static int board_added(struct slot *p_slot) if (retval) { ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n", pci_domain_nr(parent), parent->number); - goto err_exit; + if (retval != -EEXIST) + goto err_exit; } pciehp_green_led_on(p_slot); @@ -255,6 +277,9 @@ static int remove_board(struct slot *p_slot) struct power_work_info { struct slot *p_slot; struct work_struct work; + unsigned int req; +#define DISABLE_REQ 0 +#define ENABLE_REQ 1 }; /** @@ -269,30 +294,38 @@ static void pciehp_power_thread(struct work_struct *work) struct power_work_info *info = container_of(work, struct power_work_info, work); struct slot *p_slot = info->p_slot; + int ret; - mutex_lock(&p_slot->lock); - switch (p_slot->state) { - case POWEROFF_STATE: - mutex_unlock(&p_slot->lock); + switch (info->req) { + case DISABLE_REQ: ctrl_dbg(p_slot->ctrl, "Disabling domain:bus:device=%04x:%02x:00\n", pci_domain_nr(p_slot->ctrl->pcie->port->subordinate), p_slot->ctrl->pcie->port->subordinate->number); + mutex_lock(&p_slot->hotplug_lock); pciehp_disable_slot(p_slot); + mutex_unlock(&p_slot->hotplug_lock); mutex_lock(&p_slot->lock); p_slot->state = STATIC_STATE; - break; - case POWERON_STATE: mutex_unlock(&p_slot->lock); - if (pciehp_enable_slot(p_slot)) + break; + case ENABLE_REQ: + ctrl_dbg(p_slot->ctrl, + "Enabling domain:bus:device=%04x:%02x:00\n", + pci_domain_nr(p_slot->ctrl->pcie->port->subordinate), + p_slot->ctrl->pcie->port->subordinate->number); + mutex_lock(&p_slot->hotplug_lock); + ret = pciehp_enable_slot(p_slot); + mutex_unlock(&p_slot->hotplug_lock); + if (ret) pciehp_green_led_off(p_slot); mutex_lock(&p_slot->lock); p_slot->state = STATIC_STATE; + mutex_unlock(&p_slot->lock); break; default: break; } - mutex_unlock(&p_slot->lock); kfree(info); } @@ -315,9 +348,11 @@ void pciehp_queue_pushbutton_work(struct work_struct *work) switch (p_slot->state) { case BLINKINGOFF_STATE: p_slot->state = POWEROFF_STATE; + info->req = DISABLE_REQ; break; case BLINKINGON_STATE: p_slot->state = POWERON_STATE; + info->req = ENABLE_REQ; break; default: kfree(info); @@ -364,11 +399,10 @@ static void handle_button_press_event(struct slot *p_slot) */ ctrl_info(ctrl, "Button cancel on Slot(%s)\n", slot_name(p_slot)); cancel_delayed_work(&p_slot->work); - if (p_slot->state == BLINKINGOFF_STATE) { + if (p_slot->state == BLINKINGOFF_STATE) pciehp_green_led_on(p_slot); - } else { + else pciehp_green_led_off(p_slot); - } pciehp_set_attention_status(p_slot, 0); ctrl_info(ctrl, "PCI slot #%s - action canceled " "due to button press\n", slot_name(p_slot)); @@ -407,14 +441,81 @@ static void handle_surprise_event(struct slot *p_slot) INIT_WORK(&info->work, pciehp_power_thread); pciehp_get_adapter_status(p_slot, &getstatus); - if (!getstatus) + if (!getstatus) { p_slot->state = POWEROFF_STATE; - else + info->req = DISABLE_REQ; + } else { p_slot->state = POWERON_STATE; + info->req = ENABLE_REQ; + } queue_work(p_slot->wq, &info->work); } +/* + * Note: This function must be called with slot->lock held + */ +static void handle_link_event(struct slot *p_slot, u32 event) +{ + struct controller *ctrl = p_slot->ctrl; + struct power_work_info *info; + + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n", + __func__); + return; + } + info->p_slot = p_slot; + info->req = event == INT_LINK_UP ? ENABLE_REQ : DISABLE_REQ; + INIT_WORK(&info->work, pciehp_power_thread); + + switch (p_slot->state) { + case BLINKINGON_STATE: + case BLINKINGOFF_STATE: + cancel_delayed_work(&p_slot->work); + /* Fall through */ + case STATIC_STATE: + p_slot->state = event == INT_LINK_UP ? + POWERON_STATE : POWEROFF_STATE; + queue_work(p_slot->wq, &info->work); + break; + case POWERON_STATE: + if (event == INT_LINK_UP) { + ctrl_info(ctrl, + "Link Up event ignored on slot(%s): already powering on\n", + slot_name(p_slot)); + kfree(info); + } else { + ctrl_info(ctrl, + "Link Down event queued on slot(%s): currently getting powered on\n", + slot_name(p_slot)); + p_slot->state = POWEROFF_STATE; + queue_work(p_slot->wq, &info->work); + } + break; + case POWEROFF_STATE: + if (event == INT_LINK_UP) { + ctrl_info(ctrl, + "Link Up event queued on slot(%s): currently getting powered off\n", + slot_name(p_slot)); + p_slot->state = POWERON_STATE; + queue_work(p_slot->wq, &info->work); + } else { + ctrl_info(ctrl, + "Link Down event ignored on slot(%s): already powering off\n", + slot_name(p_slot)); + kfree(info); + } + break; + default: + ctrl_err(ctrl, "Not a valid state on slot(%s)\n", + slot_name(p_slot)); + kfree(info); + break; + } +} + static void interrupt_event_handler(struct work_struct *work) { struct event_info *info = container_of(work, struct event_info, work); @@ -433,12 +534,23 @@ static void interrupt_event_handler(struct work_struct *work) pciehp_green_led_off(p_slot); break; case INT_PRESENCE_ON: - case INT_PRESENCE_OFF: if (!HP_SUPR_RM(ctrl)) break; + ctrl_dbg(ctrl, "Surprise Insertion\n"); + handle_surprise_event(p_slot); + break; + case INT_PRESENCE_OFF: + /* + * Regardless of surprise capability, we need to + * definitely remove a card that has been pulled out! + */ ctrl_dbg(ctrl, "Surprise Removal\n"); handle_surprise_event(p_slot); break; + case INT_LINK_UP: + case INT_LINK_DOWN: + handle_link_event(p_slot, info->event_type); + break; default: break; } @@ -447,6 +559,9 @@ static void interrupt_event_handler(struct work_struct *work) kfree(info); } +/* + * Note: This function must be called with slot->hotplug_lock held + */ int pciehp_enable_slot(struct slot *p_slot) { u8 getstatus = 0; @@ -479,13 +594,15 @@ int pciehp_enable_slot(struct slot *p_slot) pciehp_get_latch_status(p_slot, &getstatus); rc = board_added(p_slot); - if (rc) { + if (rc) pciehp_get_latch_status(p_slot, &getstatus); - } + return rc; } - +/* + * Note: This function must be called with slot->hotplug_lock held + */ int pciehp_disable_slot(struct slot *p_slot) { u8 getstatus = 0; @@ -494,24 +611,6 @@ int pciehp_disable_slot(struct slot *p_slot) if (!p_slot->ctrl) return 1; - if (!HP_SUPR_RM(p_slot->ctrl)) { - pciehp_get_adapter_status(p_slot, &getstatus); - if (!getstatus) { - ctrl_info(ctrl, "No adapter on slot(%s)\n", - slot_name(p_slot)); - return -ENODEV; - } - } - - if (MRL_SENS(p_slot->ctrl)) { - pciehp_get_latch_status(p_slot, &getstatus); - if (getstatus) { - ctrl_info(ctrl, "Latch open on slot(%s)\n", - slot_name(p_slot)); - return -ENODEV; - } - } - if (POWER_CTRL(p_slot->ctrl)) { pciehp_get_power_status(p_slot, &getstatus); if (!getstatus) { @@ -536,7 +635,9 @@ int pciehp_sysfs_enable_slot(struct slot *p_slot) case STATIC_STATE: p_slot->state = POWERON_STATE; mutex_unlock(&p_slot->lock); + mutex_lock(&p_slot->hotplug_lock); retval = pciehp_enable_slot(p_slot); + mutex_unlock(&p_slot->hotplug_lock); mutex_lock(&p_slot->lock); p_slot->state = STATIC_STATE; break; diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 14acfccb767..d7d058fa19a 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -206,7 +206,7 @@ static void pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) mutex_unlock(&ctrl->ctrl_lock); } -static bool check_link_active(struct controller *ctrl) +bool pciehp_check_link_active(struct controller *ctrl) { struct pci_dev *pdev = ctrl_dev(ctrl); u16 lnk_status; @@ -225,12 +225,12 @@ static void __pcie_wait_link_active(struct controller *ctrl, bool active) { int timeout = 1000; - if (check_link_active(ctrl) == active) + if (pciehp_check_link_active(ctrl) == active) return; while (timeout > 0) { msleep(10); timeout -= 10; - if (check_link_active(ctrl) == active) + if (pciehp_check_link_active(ctrl) == active) return; } ctrl_dbg(ctrl, "Data Link Layer Link Active not %s in 1000 msec\n", @@ -242,11 +242,6 @@ static void pcie_wait_link_active(struct controller *ctrl) __pcie_wait_link_active(ctrl, true); } -static void pcie_wait_link_not_active(struct controller *ctrl) -{ - __pcie_wait_link_active(ctrl, false); -} - static bool pci_bus_check_dev(struct pci_bus *bus, int devfn) { u32 l; @@ -332,11 +327,6 @@ static int pciehp_link_enable(struct controller *ctrl) return __pciehp_link_set(ctrl, true); } -static int pciehp_link_disable(struct controller *ctrl) -{ - return __pciehp_link_set(ctrl, false); -} - void pciehp_get_attention_status(struct slot *slot, u8 *status) { struct controller *ctrl = slot->ctrl; @@ -508,14 +498,6 @@ void pciehp_power_off_slot(struct slot * slot) { struct controller *ctrl = slot->ctrl; - /* Disable the link at first */ - pciehp_link_disable(ctrl); - /* wait the link is down */ - if (ctrl->link_active_reporting) - pcie_wait_link_not_active(ctrl); - else - msleep(1000); - pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_OFF, PCI_EXP_SLTCTL_PCC); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, @@ -540,7 +522,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) detected &= (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD | PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC | - PCI_EXP_SLTSTA_CC); + PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_DLLSC); detected &= ~intr_loc; intr_loc |= detected; if (!intr_loc) @@ -579,6 +561,10 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) ctrl->power_fault_detected = 1; pciehp_handle_power_fault(slot); } + + if (intr_loc & PCI_EXP_SLTSTA_DLLSC) + pciehp_handle_linkstate_change(slot); + return IRQ_HANDLED; } @@ -596,9 +582,17 @@ void pcie_enable_notification(struct controller *ctrl) * when it is cleared in the interrupt service routine, and * next power fault detected interrupt was notified again. */ - cmd = PCI_EXP_SLTCTL_PDCE; + + /* + * Always enable link events: thus link-up and link-down shall + * always be treated as hotplug and unplug respectively. Enable + * presence detect only if Attention Button is not present. + */ + cmd = PCI_EXP_SLTCTL_DLLSCE; if (ATTN_BUTTN(ctrl)) cmd |= PCI_EXP_SLTCTL_ABPE; + else + cmd |= PCI_EXP_SLTCTL_PDCE; if (MRL_SENS(ctrl)) cmd |= PCI_EXP_SLTCTL_MRLSCE; if (!pciehp_poll_mode) @@ -606,7 +600,8 @@ void pcie_enable_notification(struct controller *ctrl) mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE | PCI_EXP_SLTCTL_MRLSCE | PCI_EXP_SLTCTL_PFDE | - PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE); + PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE | + PCI_EXP_SLTCTL_DLLSCE); pcie_write_cmd(ctrl, cmd, mask); } @@ -624,33 +619,38 @@ static void pcie_disable_notification(struct controller *ctrl) /* * pciehp has a 1:1 bus:slot relationship so we ultimately want a secondary - * bus reset of the bridge, but if the slot supports surprise removal we need - * to disable presence detection around the bus reset and clear any spurious + * bus reset of the bridge, but at the same time we want to ensure that it is + * not seen as a hot-unplug, followed by the hot-plug of the device. Thus, + * disable link state notification and presence detection change notification + * momentarily, if we see that they could interfere. Also, clear any spurious * events after. */ int pciehp_reset_slot(struct slot *slot, int probe) { struct controller *ctrl = slot->ctrl; struct pci_dev *pdev = ctrl_dev(ctrl); + u16 stat_mask = 0, ctrl_mask = 0; if (probe) return 0; - if (HP_SUPR_RM(ctrl)) { - pcie_write_cmd(ctrl, 0, PCI_EXP_SLTCTL_PDCE); - if (pciehp_poll_mode) - del_timer_sync(&ctrl->poll_timer); + if (!ATTN_BUTTN(ctrl)) { + ctrl_mask |= PCI_EXP_SLTCTL_PDCE; + stat_mask |= PCI_EXP_SLTSTA_PDC; } + ctrl_mask |= PCI_EXP_SLTCTL_DLLSCE; + stat_mask |= PCI_EXP_SLTSTA_DLLSC; + + pcie_write_cmd(ctrl, 0, ctrl_mask); + if (pciehp_poll_mode) + del_timer_sync(&ctrl->poll_timer); pci_reset_bridge_secondary_bus(ctrl->pcie->port); - if (HP_SUPR_RM(ctrl)) { - pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, - PCI_EXP_SLTSTA_PDC); - pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PDCE, PCI_EXP_SLTCTL_PDCE); - if (pciehp_poll_mode) - int_poll_timeout(ctrl->poll_timer.data); - } + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, stat_mask); + pcie_write_cmd(ctrl, ctrl_mask, ctrl_mask); + if (pciehp_poll_mode) + int_poll_timeout(ctrl->poll_timer.data); return 0; } @@ -687,6 +687,7 @@ static int pcie_init_slot(struct controller *ctrl) slot->ctrl = ctrl; mutex_init(&slot->lock); + mutex_init(&slot->hotplug_lock); INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work); ctrl->slot = slot; return 0; diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index b07d7cc2d69..1b533060ce6 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -50,7 +50,7 @@ int pciehp_configure_device(struct slot *p_slot) "at %04x:%02x:00, cannot hot-add\n", pci_name(dev), pci_domain_nr(parent), parent->number); pci_dev_put(dev); - ret = -EINVAL; + ret = -EEXIST; goto out; } diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 9dce7c5e2a7..de7a74782f9 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -170,97 +170,6 @@ static void virtfn_remove(struct pci_dev *dev, int id, int reset) pci_dev_put(dev); } -static int sriov_migration(struct pci_dev *dev) -{ - u16 status; - struct pci_sriov *iov = dev->sriov; - - if (!iov->num_VFs) - return 0; - - if (!(iov->cap & PCI_SRIOV_CAP_VFM)) - return 0; - - pci_read_config_word(dev, iov->pos + PCI_SRIOV_STATUS, &status); - if (!(status & PCI_SRIOV_STATUS_VFM)) - return 0; - - schedule_work(&iov->mtask); - - return 1; -} - -static void sriov_migration_task(struct work_struct *work) -{ - int i; - u8 state; - u16 status; - struct pci_sriov *iov = container_of(work, struct pci_sriov, mtask); - - for (i = iov->initial_VFs; i < iov->num_VFs; i++) { - state = readb(iov->mstate + i); - if (state == PCI_SRIOV_VFM_MI) { - writeb(PCI_SRIOV_VFM_AV, iov->mstate + i); - state = readb(iov->mstate + i); - if (state == PCI_SRIOV_VFM_AV) - virtfn_add(iov->self, i, 1); - } else if (state == PCI_SRIOV_VFM_MO) { - virtfn_remove(iov->self, i, 1); - writeb(PCI_SRIOV_VFM_UA, iov->mstate + i); - state = readb(iov->mstate + i); - if (state == PCI_SRIOV_VFM_AV) - virtfn_add(iov->self, i, 0); - } - } - - pci_read_config_word(iov->self, iov->pos + PCI_SRIOV_STATUS, &status); - status &= ~PCI_SRIOV_STATUS_VFM; - pci_write_config_word(iov->self, iov->pos + PCI_SRIOV_STATUS, status); -} - -static int sriov_enable_migration(struct pci_dev *dev, int nr_virtfn) -{ - int bir; - u32 table; - resource_size_t pa; - struct pci_sriov *iov = dev->sriov; - - if (nr_virtfn <= iov->initial_VFs) - return 0; - - pci_read_config_dword(dev, iov->pos + PCI_SRIOV_VFM, &table); - bir = PCI_SRIOV_VFM_BIR(table); - if (bir > PCI_STD_RESOURCE_END) - return -EIO; - - table = PCI_SRIOV_VFM_OFFSET(table); - if (table + nr_virtfn > pci_resource_len(dev, bir)) - return -EIO; - - pa = pci_resource_start(dev, bir) + table; - iov->mstate = ioremap(pa, nr_virtfn); - if (!iov->mstate) - return -ENOMEM; - - INIT_WORK(&iov->mtask, sriov_migration_task); - - iov->ctrl |= PCI_SRIOV_CTRL_VFM | PCI_SRIOV_CTRL_INTR; - pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); - - return 0; -} - -static void sriov_disable_migration(struct pci_dev *dev) -{ - struct pci_sriov *iov = dev->sriov; - - iov->ctrl &= ~(PCI_SRIOV_CTRL_VFM | PCI_SRIOV_CTRL_INTR); - pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl); - - cancel_work_sync(&iov->mtask); - iounmap(iov->mstate); -} - static int sriov_enable(struct pci_dev *dev, int nr_virtfn) { int rc; @@ -351,12 +260,6 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) goto failed; } - if (iov->cap & PCI_SRIOV_CAP_VFM) { - rc = sriov_enable_migration(dev, nr_virtfn); - if (rc) - goto failed; - } - kobject_uevent(&dev->dev.kobj, KOBJ_CHANGE); iov->num_VFs = nr_virtfn; @@ -387,9 +290,6 @@ static void sriov_disable(struct pci_dev *dev) if (!iov->num_VFs) return; - if (iov->cap & PCI_SRIOV_CAP_VFM) - sriov_disable_migration(dev); - for (i = 0; i < iov->num_VFs; i++) virtfn_remove(dev, i, 0); @@ -688,25 +588,6 @@ void pci_disable_sriov(struct pci_dev *dev) EXPORT_SYMBOL_GPL(pci_disable_sriov); /** - * pci_sriov_migration - notify SR-IOV core of Virtual Function Migration - * @dev: the PCI device - * - * Returns IRQ_HANDLED if the IRQ is handled, or IRQ_NONE if not. - * - * Physical Function driver is responsible to register IRQ handler using - * VF Migration Interrupt Message Number, and call this function when the - * interrupt is generated by the hardware. - */ -irqreturn_t pci_sriov_migration(struct pci_dev *dev) -{ - if (!dev->is_physfn) - return IRQ_NONE; - - return sriov_migration(dev) ? IRQ_HANDLED : IRQ_NONE; -} -EXPORT_SYMBOL_GPL(pci_sriov_migration); - -/** * pci_num_vf - return number of VFs associated with a PF device_release_driver * @dev: the PCI device * diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index fdbc294821e..7325d43bf03 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -108,12 +108,12 @@ static bool pcie_ari_disabled; */ unsigned char pci_bus_max_busnr(struct pci_bus* bus) { - struct list_head *tmp; + struct pci_bus *tmp; unsigned char max, n; max = bus->busn_res.end; - list_for_each(tmp, &bus->children) { - n = pci_bus_max_busnr(pci_bus_b(tmp)); + list_for_each_entry(tmp, &bus->children, node) { + n = pci_bus_max_busnr(tmp); if(n > max) max = n; } @@ -401,33 +401,40 @@ EXPORT_SYMBOL_GPL(pci_find_ht_capability); * @res: child resource record for which parent is sought * * For given resource region of given device, return the resource - * region of parent bus the given region is contained in or where - * it should be allocated from. + * region of parent bus the given region is contained in. */ struct resource * pci_find_parent_resource(const struct pci_dev *dev, struct resource *res) { const struct pci_bus *bus = dev->bus; + struct resource *r; int i; - struct resource *best = NULL, *r; pci_bus_for_each_resource(bus, r, i) { if (!r) continue; - if (res->start && !(res->start >= r->start && res->end <= r->end)) - continue; /* Not contained */ - if ((res->flags ^ r->flags) & (IORESOURCE_IO | IORESOURCE_MEM)) - continue; /* Wrong type */ - if (!((res->flags ^ r->flags) & IORESOURCE_PREFETCH)) - return r; /* Exact match */ - /* We can't insert a non-prefetch resource inside a prefetchable parent .. */ - if (r->flags & IORESOURCE_PREFETCH) - continue; - /* .. but we can put a prefetchable resource inside a non-prefetchable one */ - if (!best) - best = r; + if (res->start && resource_contains(r, res)) { + + /* + * If the window is prefetchable but the BAR is + * not, the allocator made a mistake. + */ + if (r->flags & IORESOURCE_PREFETCH && + !(res->flags & IORESOURCE_PREFETCH)) + return NULL; + + /* + * If we're below a transparent bridge, there may + * be both a positively-decoded aperture and a + * subtractively-decoded region that contain the BAR. + * We want the positively-decoded one, so this depends + * on pci_bus_for_each_resource() giving us those + * first. + */ + return r; + } } - return best; + return NULL; } /** @@ -1178,6 +1185,11 @@ int pci_load_and_free_saved_state(struct pci_dev *dev, } EXPORT_SYMBOL_GPL(pci_load_and_free_saved_state); +int __weak pcibios_enable_device(struct pci_dev *dev, int bars) +{ + return pci_enable_resources(dev, bars); +} + static int do_pci_enable_device(struct pci_dev *dev, int bars) { int err; @@ -1624,29 +1636,27 @@ static void pci_pme_list_scan(struct work_struct *work) struct pci_pme_device *pme_dev, *n; mutex_lock(&pci_pme_list_mutex); - if (!list_empty(&pci_pme_list)) { - list_for_each_entry_safe(pme_dev, n, &pci_pme_list, list) { - if (pme_dev->dev->pme_poll) { - struct pci_dev *bridge; - - bridge = pme_dev->dev->bus->self; - /* - * If bridge is in low power state, the - * configuration space of subordinate devices - * may be not accessible - */ - if (bridge && bridge->current_state != PCI_D0) - continue; - pci_pme_wakeup(pme_dev->dev, NULL); - } else { - list_del(&pme_dev->list); - kfree(pme_dev); - } + list_for_each_entry_safe(pme_dev, n, &pci_pme_list, list) { + if (pme_dev->dev->pme_poll) { + struct pci_dev *bridge; + + bridge = pme_dev->dev->bus->self; + /* + * If bridge is in low power state, the + * configuration space of subordinate devices + * may be not accessible + */ + if (bridge && bridge->current_state != PCI_D0) + continue; + pci_pme_wakeup(pme_dev->dev, NULL); + } else { + list_del(&pme_dev->list); + kfree(pme_dev); } - if (!list_empty(&pci_pme_list)) - schedule_delayed_work(&pci_pme_work, - msecs_to_jiffies(PME_TIMEOUT)); } + if (!list_empty(&pci_pme_list)) + schedule_delayed_work(&pci_pme_work, + msecs_to_jiffies(PME_TIMEOUT)); mutex_unlock(&pci_pme_list_mutex); } @@ -2193,21 +2203,18 @@ void pci_request_acs(void) } /** - * pci_enable_acs - enable ACS if hardware support it + * pci_std_enable_acs - enable ACS on devices using standard ACS capabilites * @dev: the PCI device */ -void pci_enable_acs(struct pci_dev *dev) +static int pci_std_enable_acs(struct pci_dev *dev) { int pos; u16 cap; u16 ctrl; - if (!pci_acs_enable) - return; - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS); if (!pos) - return; + return -ENODEV; pci_read_config_word(dev, pos + PCI_ACS_CAP, &cap); pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl); @@ -2225,6 +2232,23 @@ void pci_enable_acs(struct pci_dev *dev) ctrl |= (cap & PCI_ACS_UF); pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl); + + return 0; +} + +/** + * pci_enable_acs - enable ACS if hardware support it + * @dev: the PCI device + */ +void pci_enable_acs(struct pci_dev *dev) +{ + if (!pci_acs_enable) + return; + + if (!pci_std_enable_acs(dev)) + return; + + pci_dev_specific_enable_acs(dev); } static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags) @@ -4250,6 +4274,7 @@ void pci_reassigndev_resource_alignment(struct pci_dev *dev) "Rounding up size of resource #%d to %#llx.\n", i, (unsigned long long)size); } + r->flags |= IORESOURCE_UNSET; r->end = size - 1; r->start = 0; } @@ -4263,6 +4288,7 @@ void pci_reassigndev_resource_alignment(struct pci_dev *dev) r = &dev->resource[i]; if (!(r->flags & IORESOURCE_MEM)) continue; + r->flags |= IORESOURCE_UNSET; r->end = resource_size(r) - 1; r->start = 0; } diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 4df38df224f..6bd082299e3 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -1,8 +1,6 @@ #ifndef DRIVERS_PCI_H #define DRIVERS_PCI_H -#include <linux/workqueue.h> - #define PCI_CFG_SPACE_SIZE 256 #define PCI_CFG_SPACE_EXP_SIZE 4096 @@ -240,8 +238,6 @@ struct pci_sriov { struct pci_dev *dev; /* lowest numbered PF */ struct pci_dev *self; /* this PF */ struct mutex lock; /* lock for VF bus */ - struct work_struct mtask; /* VF Migration task */ - u8 __iomem *mstate; /* VF Migration State Array */ }; #ifdef CONFIG_PCI_ATS diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 6e34498ec9f..ef09f5f2fe6 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -252,6 +252,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, /* Address above 32-bit boundary; disable the BAR */ pci_write_config_dword(dev, pos, 0); pci_write_config_dword(dev, pos + 4, 0); + res->flags |= IORESOURCE_UNSET; region.start = 0; region.end = sz64; bar_disabled = true; @@ -731,22 +732,6 @@ struct pci_bus *__ref pci_add_new_bus(struct pci_bus *parent, struct pci_dev *de return child; } -static void pci_fixup_parent_subordinate_busnr(struct pci_bus *child, int max) -{ - struct pci_bus *parent = child->parent; - - /* Attempts to fix that up are really dangerous unless - we're going to re-assign all bus numbers. */ - if (!pcibios_assign_all_busses()) - return; - - while (parent->parent && parent->busn_res.end < max) { - parent->busn_res.end = max; - pci_write_config_byte(parent->self, PCI_SUBORDINATE_BUS, max); - parent = parent->parent; - } -} - /* * If it's a bridge, configure it and scan the bus behind it. * For CardBus bridges, we don't scan behind as the devices will @@ -782,7 +767,7 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) /* Check if setup is sensible at all */ if (!pass && (primary != bus->number || secondary <= bus->number || - secondary > subordinate)) { + secondary > subordinate || subordinate > bus->busn_res.end)) { dev_info(&dev->dev, "bridge configuration invalid ([bus %02x-%02x]), reconfiguring\n", secondary, subordinate); broken = 1; @@ -805,11 +790,10 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) goto out; /* - * If we already got to this bus through a different bridge, - * don't re-add it. This can happen with the i450NX chipset. - * - * However, we continue to descend down the hierarchy and - * scan remaining child buses. + * The bus might already exist for two reasons: Either we are + * rescanning the bus or the bus is reachable through more than + * one bridge. The second case can happen with the i450NX + * chipset. */ child = pci_find_bus(pci_domain_nr(bus), secondary); if (!child) { @@ -822,17 +806,19 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) } cmax = pci_scan_child_bus(child); - if (cmax > max) - max = cmax; - if (child->busn_res.end > max) - max = child->busn_res.end; + if (cmax > subordinate) + dev_warn(&dev->dev, "bridge has subordinate %02x but max busn %02x\n", + subordinate, cmax); + /* subordinate should equal child->busn_res.end */ + if (subordinate > max) + max = subordinate; } else { /* * We need to assign a number to this bus which we always * do in the second pass. */ if (!pass) { - if (pcibios_assign_all_busses() || broken) + if (pcibios_assign_all_busses() || broken || is_cardbus) /* Temporarily disable forwarding of the configuration cycles on all bridges in this bus segment to avoid possible @@ -844,19 +830,25 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) goto out; } + if (max >= bus->busn_res.end) { + dev_warn(&dev->dev, "can't allocate child bus %02x from %pR\n", + max, &bus->busn_res); + goto out; + } + /* Clear errors */ pci_write_config_word(dev, PCI_STATUS, 0xffff); - /* Prevent assigning a bus number that already exists. - * This can happen when a bridge is hot-plugged, so in - * this case we only re-scan this bus. */ + /* The bus will already exist if we are rescanning */ child = pci_find_bus(pci_domain_nr(bus), max+1); if (!child) { - child = pci_add_new_bus(bus, dev, ++max); + child = pci_add_new_bus(bus, dev, max+1); if (!child) goto out; - pci_bus_insert_busn_res(child, max, 0xff); + pci_bus_insert_busn_res(child, max+1, + bus->busn_res.end); } + max++; buses = (buses & 0xff000000) | ((unsigned int)(child->primary) << 0) | ((unsigned int)(child->busn_res.start) << 8) @@ -878,20 +870,7 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) if (!is_cardbus) { child->bridge_ctl = bctl; - /* - * Adjust subordinate busnr in parent buses. - * We do this before scanning for children because - * some devices may not be detected if the bios - * was lazy. - */ - pci_fixup_parent_subordinate_busnr(child, max); - /* Now we can scan all subordinate buses... */ max = pci_scan_child_bus(child); - /* - * now fix it up again since we have found - * the real value of max. - */ - pci_fixup_parent_subordinate_busnr(child, max); } else { /* * For CardBus bridges, we leave 4 bus numbers @@ -922,11 +901,15 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass) } } max += i; - pci_fixup_parent_subordinate_busnr(child, max); } /* * Set the subordinate bus number to its real value. */ + if (max > bus->busn_res.end) { + dev_warn(&dev->dev, "max busn %02x is outside %pR\n", + max, &bus->busn_res); + max = bus->busn_res.end; + } pci_bus_update_busn_res_end(child, max); pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max); } @@ -1125,10 +1108,10 @@ int pci_setup_device(struct pci_dev *dev) pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &dev->subsystem_device); /* - * Do the ugly legacy mode stuff here rather than broken chip - * quirk code. Legacy mode ATA controllers have fixed - * addresses. These are not always echoed in BAR0-3, and - * BAR0-3 in a few cases contain junk! + * Do the ugly legacy mode stuff here rather than broken chip + * quirk code. Legacy mode ATA controllers have fixed + * addresses. These are not always echoed in BAR0-3, and + * BAR0-3 in a few cases contain junk! */ if (class == PCI_CLASS_STORAGE_IDE) { u8 progif; @@ -1139,11 +1122,15 @@ int pci_setup_device(struct pci_dev *dev) res = &dev->resource[0]; res->flags = LEGACY_IO_RESOURCE; pcibios_bus_to_resource(dev->bus, res, ®ion); + dev_info(&dev->dev, "legacy IDE quirk: reg 0x10: %pR\n", + res); region.start = 0x3F6; region.end = 0x3F6; res = &dev->resource[1]; res->flags = LEGACY_IO_RESOURCE; pcibios_bus_to_resource(dev->bus, res, ®ion); + dev_info(&dev->dev, "legacy IDE quirk: reg 0x14: %pR\n", + res); } if ((progif & 4) == 0) { region.start = 0x170; @@ -1151,11 +1138,15 @@ int pci_setup_device(struct pci_dev *dev) res = &dev->resource[2]; res->flags = LEGACY_IO_RESOURCE; pcibios_bus_to_resource(dev->bus, res, ®ion); + dev_info(&dev->dev, "legacy IDE quirk: reg 0x18: %pR\n", + res); region.start = 0x376; region.end = 0x376; res = &dev->resource[3]; res->flags = LEGACY_IO_RESOURCE; pcibios_bus_to_resource(dev->bus, res, ®ion); + dev_info(&dev->dev, "legacy IDE quirk: reg 0x1c: %pR\n", + res); } } break; @@ -1835,7 +1826,7 @@ int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int bus_max) res->flags |= IORESOURCE_PCI_FIXED; } - conflict = insert_resource_conflict(parent_res, res); + conflict = request_resource_conflict(parent_res, res); if (conflict) dev_printk(KERN_DEBUG, &b->dev, diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 5cb726c193d..e7292065a1b 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -296,6 +296,7 @@ static void quirk_s3_64M(struct pci_dev *dev) struct resource *r = &dev->resource[0]; if ((r->start & 0x3ffffff) || r->end != r->start + 0x3ffffff) { + r->flags |= IORESOURCE_UNSET; r->start = 0; r->end = 0x3ffffff; } @@ -937,6 +938,8 @@ DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C static void quirk_dunord(struct pci_dev *dev) { struct resource *r = &dev->resource [1]; + + r->flags |= IORESOURCE_UNSET; r->start = 0; r->end = 0xffffff; } @@ -1740,6 +1743,7 @@ static void quirk_tc86c001_ide(struct pci_dev *dev) struct resource *r = &dev->resource[0]; if (r->start & 0x8) { + r->flags |= IORESOURCE_UNSET; r->start = 0; r->end = 0xf; } @@ -1769,6 +1773,7 @@ static void quirk_plx_pci9050(struct pci_dev *dev) dev_info(&dev->dev, "Re-allocating PLX PCI 9050 BAR %u to length 256 to avoid bit 7 bug\n", bar); + r->flags |= IORESOURCE_UNSET; r->start = 0; r->end = 0xff; } @@ -3423,6 +3428,61 @@ static int pci_quirk_amd_sb_acs(struct pci_dev *dev, u16 acs_flags) #endif } +/* + * Many Intel PCH root ports do provide ACS-like features to disable peer + * transactions and validate bus numbers in requests, but do not provide an + * actual PCIe ACS capability. This is the list of device IDs known to fall + * into that category as provided by Intel in Red Hat bugzilla 1037684. + */ +static const u16 pci_quirk_intel_pch_acs_ids[] = { + /* Ibexpeak PCH */ + 0x3b42, 0x3b43, 0x3b44, 0x3b45, 0x3b46, 0x3b47, 0x3b48, 0x3b49, + 0x3b4a, 0x3b4b, 0x3b4c, 0x3b4d, 0x3b4e, 0x3b4f, 0x3b50, 0x3b51, + /* Cougarpoint PCH */ + 0x1c10, 0x1c11, 0x1c12, 0x1c13, 0x1c14, 0x1c15, 0x1c16, 0x1c17, + 0x1c18, 0x1c19, 0x1c1a, 0x1c1b, 0x1c1c, 0x1c1d, 0x1c1e, 0x1c1f, + /* Pantherpoint PCH */ + 0x1e10, 0x1e11, 0x1e12, 0x1e13, 0x1e14, 0x1e15, 0x1e16, 0x1e17, + 0x1e18, 0x1e19, 0x1e1a, 0x1e1b, 0x1e1c, 0x1e1d, 0x1e1e, 0x1e1f, + /* Lynxpoint-H PCH */ + 0x8c10, 0x8c11, 0x8c12, 0x8c13, 0x8c14, 0x8c15, 0x8c16, 0x8c17, + 0x8c18, 0x8c19, 0x8c1a, 0x8c1b, 0x8c1c, 0x8c1d, 0x8c1e, 0x8c1f, + /* Lynxpoint-LP PCH */ + 0x9c10, 0x9c11, 0x9c12, 0x9c13, 0x9c14, 0x9c15, 0x9c16, 0x9c17, + 0x9c18, 0x9c19, 0x9c1a, 0x9c1b, + /* Wildcat PCH */ + 0x9c90, 0x9c91, 0x9c92, 0x9c93, 0x9c94, 0x9c95, 0x9c96, 0x9c97, + 0x9c98, 0x9c99, 0x9c9a, 0x9c9b, +}; + +static bool pci_quirk_intel_pch_acs_match(struct pci_dev *dev) +{ + int i; + + /* Filter out a few obvious non-matches first */ + if (!pci_is_pcie(dev) || pci_pcie_type(dev) != PCI_EXP_TYPE_ROOT_PORT) + return false; + + for (i = 0; i < ARRAY_SIZE(pci_quirk_intel_pch_acs_ids); i++) + if (pci_quirk_intel_pch_acs_ids[i] == dev->device) + return true; + + return false; +} + +#define INTEL_PCH_ACS_FLAGS (PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF | PCI_ACS_SV) + +static int pci_quirk_intel_pch_acs(struct pci_dev *dev, u16 acs_flags) +{ + u16 flags = dev->dev_flags & PCI_DEV_FLAGS_ACS_ENABLED_QUIRK ? + INTEL_PCH_ACS_FLAGS : 0; + + if (!pci_quirk_intel_pch_acs_match(dev)) + return -ENOTTY; + + return acs_flags & ~flags ? 0 : 1; +} + static const struct pci_dev_acs_enabled { u16 vendor; u16 device; @@ -3434,6 +3494,7 @@ static const struct pci_dev_acs_enabled { { PCI_VENDOR_ID_ATI, 0x439d, pci_quirk_amd_sb_acs }, { PCI_VENDOR_ID_ATI, 0x4384, pci_quirk_amd_sb_acs }, { PCI_VENDOR_ID_ATI, 0x4399, pci_quirk_amd_sb_acs }, + { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs }, { 0 } }; @@ -3461,3 +3522,132 @@ int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags) return -ENOTTY; } + +/* Config space offset of Root Complex Base Address register */ +#define INTEL_LPC_RCBA_REG 0xf0 +/* 31:14 RCBA address */ +#define INTEL_LPC_RCBA_MASK 0xffffc000 +/* RCBA Enable */ +#define INTEL_LPC_RCBA_ENABLE (1 << 0) + +/* Backbone Scratch Pad Register */ +#define INTEL_BSPR_REG 0x1104 +/* Backbone Peer Non-Posted Disable */ +#define INTEL_BSPR_REG_BPNPD (1 << 8) +/* Backbone Peer Posted Disable */ +#define INTEL_BSPR_REG_BPPD (1 << 9) + +/* Upstream Peer Decode Configuration Register */ +#define INTEL_UPDCR_REG 0x1114 +/* 5:0 Peer Decode Enable bits */ +#define INTEL_UPDCR_REG_MASK 0x3f + +static int pci_quirk_enable_intel_lpc_acs(struct pci_dev *dev) +{ + u32 rcba, bspr, updcr; + void __iomem *rcba_mem; + + /* + * Read the RCBA register from the LPC (D31:F0). PCH root ports + * are D28:F* and therefore get probed before LPC, thus we can't + * use pci_get_slot/pci_read_config_dword here. + */ + pci_bus_read_config_dword(dev->bus, PCI_DEVFN(31, 0), + INTEL_LPC_RCBA_REG, &rcba); + if (!(rcba & INTEL_LPC_RCBA_ENABLE)) + return -EINVAL; + + rcba_mem = ioremap_nocache(rcba & INTEL_LPC_RCBA_MASK, + PAGE_ALIGN(INTEL_UPDCR_REG)); + if (!rcba_mem) + return -ENOMEM; + + /* + * The BSPR can disallow peer cycles, but it's set by soft strap and + * therefore read-only. If both posted and non-posted peer cycles are + * disallowed, we're ok. If either are allowed, then we need to use + * the UPDCR to disable peer decodes for each port. This provides the + * PCIe ACS equivalent of PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF + */ + bspr = readl(rcba_mem + INTEL_BSPR_REG); + bspr &= INTEL_BSPR_REG_BPNPD | INTEL_BSPR_REG_BPPD; + if (bspr != (INTEL_BSPR_REG_BPNPD | INTEL_BSPR_REG_BPPD)) { + updcr = readl(rcba_mem + INTEL_UPDCR_REG); + if (updcr & INTEL_UPDCR_REG_MASK) { + dev_info(&dev->dev, "Disabling UPDCR peer decodes\n"); + updcr &= ~INTEL_UPDCR_REG_MASK; + writel(updcr, rcba_mem + INTEL_UPDCR_REG); + } + } + + iounmap(rcba_mem); + return 0; +} + +/* Miscellaneous Port Configuration register */ +#define INTEL_MPC_REG 0xd8 +/* MPC: Invalid Receive Bus Number Check Enable */ +#define INTEL_MPC_REG_IRBNCE (1 << 26) + +static void pci_quirk_enable_intel_rp_mpc_acs(struct pci_dev *dev) +{ + u32 mpc; + + /* + * When enabled, the IRBNCE bit of the MPC register enables the + * equivalent of PCI ACS Source Validation (PCI_ACS_SV), which + * ensures that requester IDs fall within the bus number range + * of the bridge. Enable if not already. + */ + pci_read_config_dword(dev, INTEL_MPC_REG, &mpc); + if (!(mpc & INTEL_MPC_REG_IRBNCE)) { + dev_info(&dev->dev, "Enabling MPC IRBNCE\n"); + mpc |= INTEL_MPC_REG_IRBNCE; + pci_write_config_word(dev, INTEL_MPC_REG, mpc); + } +} + +static int pci_quirk_enable_intel_pch_acs(struct pci_dev *dev) +{ + if (!pci_quirk_intel_pch_acs_match(dev)) + return -ENOTTY; + + if (pci_quirk_enable_intel_lpc_acs(dev)) { + dev_warn(&dev->dev, "Failed to enable Intel PCH ACS quirk\n"); + return 0; + } + + pci_quirk_enable_intel_rp_mpc_acs(dev); + + dev->dev_flags |= PCI_DEV_FLAGS_ACS_ENABLED_QUIRK; + + dev_info(&dev->dev, "Intel PCH root port ACS workaround enabled\n"); + + return 0; +} + +static const struct pci_dev_enable_acs { + u16 vendor; + u16 device; + int (*enable_acs)(struct pci_dev *dev); +} pci_dev_enable_acs[] = { + { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_enable_intel_pch_acs }, + { 0 } +}; + +void pci_dev_specific_enable_acs(struct pci_dev *dev) +{ + const struct pci_dev_enable_acs *i; + int ret; + + for (i = pci_dev_enable_acs; i->enable_acs; i++) { + if ((i->vendor == dev->vendor || + i->vendor == (u16)PCI_ANY_ID) && + (i->device == dev->device || + i->device == (u16)PCI_ANY_ID)) { + ret = i->enable_acs(dev); + if (ret >= 0) + return; + } + } +} diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index 5d595724e5f..c1839450d4d 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -197,8 +197,10 @@ void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom) void pci_cleanup_rom(struct pci_dev *pdev) { struct resource *res = &pdev->resource[PCI_ROM_RESOURCE]; + if (res->flags & IORESOURCE_ROM_COPY) { kfree((void*)(unsigned long)res->start); + res->flags |= IORESOURCE_UNSET; res->flags &= ~IORESOURCE_ROM_COPY; res->start = 0; res->end = 0; diff --git a/drivers/pci/search.c b/drivers/pci/search.c index 3ff2ac7c14e..4a1b972efe7 100644 --- a/drivers/pci/search.c +++ b/drivers/pci/search.c @@ -54,14 +54,14 @@ pci_find_upstream_pcie_bridge(struct pci_dev *pdev) static struct pci_bus *pci_do_find_bus(struct pci_bus *bus, unsigned char busnr) { - struct pci_bus* child; - struct list_head *tmp; + struct pci_bus *child; + struct pci_bus *tmp; if(bus->number == busnr) return bus; - list_for_each(tmp, &bus->children) { - child = pci_do_find_bus(pci_bus_b(tmp), busnr); + list_for_each_entry(tmp, &bus->children, node) { + child = pci_do_find_bus(tmp, busnr); if(child) return child; } @@ -111,7 +111,7 @@ pci_find_next_bus(const struct pci_bus *from) down_read(&pci_bus_sem); n = from ? from->node.next : pci_root_buses.next; if (n != &pci_root_buses) - b = pci_bus_b(n); + b = list_entry(n, struct pci_bus, node); up_read(&pci_bus_sem); return b; } diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 5c060b152ce..7eed671d558 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -44,6 +44,9 @@ void pci_update_resource(struct pci_dev *dev, int resno) if (!res->flags) return; + if (res->flags & IORESOURCE_UNSET) + return; + /* * Ignore non-moveable resources. This might be legacy resources for * which no functional BAR register exists or another important @@ -101,11 +104,6 @@ void pci_update_resource(struct pci_dev *dev, int resno) if (disable) pci_write_config_word(dev, PCI_COMMAND, cmd); - - res->flags &= ~IORESOURCE_UNSET; - dev_dbg(&dev->dev, "BAR %d: set to %pR (PCI address [%#llx-%#llx])\n", - resno, res, (unsigned long long)region.start, - (unsigned long long)region.end); } int pci_claim_resource(struct pci_dev *dev, int resource) @@ -113,18 +111,23 @@ int pci_claim_resource(struct pci_dev *dev, int resource) struct resource *res = &dev->resource[resource]; struct resource *root, *conflict; + if (res->flags & IORESOURCE_UNSET) { + dev_info(&dev->dev, "can't claim BAR %d %pR: no address assigned\n", + resource, res); + return -EINVAL; + } + root = pci_find_parent_resource(dev, res); if (!root) { - dev_info(&dev->dev, "no compatible bridge window for %pR\n", - res); + dev_info(&dev->dev, "can't claim BAR %d %pR: no compatible bridge window\n", + resource, res); return -EINVAL; } conflict = request_resource_conflict(root, res); if (conflict) { - dev_info(&dev->dev, - "address space collision: %pR conflicts with %s %pR\n", - res, conflict->name, conflict); + dev_info(&dev->dev, "can't claim BAR %d %pR: address conflict with %s %pR\n", + resource, res, conflict->name, conflict); return -EBUSY; } @@ -263,6 +266,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno) resource_size_t align, size; int ret; + res->flags |= IORESOURCE_UNSET; align = pci_resource_alignment(dev, res); if (!align) { dev_info(&dev->dev, "BAR %d: can't assign %pR " @@ -282,6 +286,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno) ret = pci_revert_fw_address(res, dev, resno, size); if (!ret) { + res->flags &= ~IORESOURCE_UNSET; res->flags &= ~IORESOURCE_STARTALIGN; dev_info(&dev->dev, "BAR %d: assigned %pR\n", resno, res); if (resno < PCI_BRIDGE_RESOURCES) @@ -297,6 +302,7 @@ int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsiz resource_size_t new_size; int ret; + res->flags |= IORESOURCE_UNSET; if (!res->parent) { dev_info(&dev->dev, "BAR %d: can't reassign an unassigned resource %pR " "\n", resno, res); @@ -307,6 +313,7 @@ int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsiz new_size = resource_size(res) + addsize; ret = _pci_assign_resource(dev, resno, new_size, min_align); if (!ret) { + res->flags &= ~IORESOURCE_UNSET; res->flags &= ~IORESOURCE_STARTALIGN; dev_info(&dev->dev, "BAR %d: reassigned %pR\n", resno, res); if (resno < PCI_BRIDGE_RESOURCES) @@ -336,9 +343,15 @@ int pci_enable_resources(struct pci_dev *dev, int mask) (!(r->flags & IORESOURCE_ROM_ENABLE))) continue; + if (r->flags & IORESOURCE_UNSET) { + dev_err(&dev->dev, "can't enable device: BAR %d %pR not assigned\n", + i, r); + return -EINVAL; + } + if (!r->parent) { - dev_err(&dev->dev, "device not available " - "(can't reserve %pR)\n", r); + dev_err(&dev->dev, "can't enable device: BAR %d %pR not claimed\n", + i, r); return -EINVAL; } diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index 8485761e76a..946f90ef602 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -1076,7 +1076,7 @@ static void yenta_config_init(struct yenta_socket *socket) */ static void yenta_fixup_parent_bridge(struct pci_bus *cardbus_bridge) { - struct list_head *tmp; + struct pci_bus *sibling; unsigned char upper_limit; /* * We only check and fix the parent bridge: All systems which need @@ -1095,18 +1095,18 @@ static void yenta_fixup_parent_bridge(struct pci_bus *cardbus_bridge) /* stay within the limits of the bus range of the parent: */ upper_limit = bridge_to_fix->parent->busn_res.end; - /* check the bus ranges of all silbling bridges to prevent overlap */ - list_for_each(tmp, &bridge_to_fix->parent->children) { - struct pci_bus *silbling = pci_bus_b(tmp); + /* check the bus ranges of all sibling bridges to prevent overlap */ + list_for_each_entry(sibling, &bridge_to_fix->parent->children, + node) { /* - * If the silbling has a higher secondary bus number + * If the sibling has a higher secondary bus number * and it's secondary is equal or smaller than our * current upper limit, set the new upper limit to - * the bus number below the silbling's range: + * the bus number below the sibling's range: */ - if (silbling->busn_res.start > bridge_to_fix->busn_res.end - && silbling->busn_res.start <= upper_limit) - upper_limit = silbling->busn_res.start - 1; + if (sibling->busn_res.start > bridge_to_fix->busn_res.end + && sibling->busn_res.start <= upper_limit) + upper_limit = sibling->busn_res.start - 1; } /* Show that the wanted subordinate number is not possible: */ diff --git a/drivers/vfio/pci/vfio_pci_intrs.c b/drivers/vfio/pci/vfio_pci_intrs.c index 210357691dc..9dd49c9839a 100644 --- a/drivers/vfio/pci/vfio_pci_intrs.c +++ b/drivers/vfio/pci/vfio_pci_intrs.c @@ -482,15 +482,19 @@ static int vfio_msi_enable(struct vfio_pci_device *vdev, int nvec, bool msix) for (i = 0; i < nvec; i++) vdev->msix[i].entry = i; - ret = pci_enable_msix(pdev, vdev->msix, nvec); - if (ret) { + ret = pci_enable_msix_range(pdev, vdev->msix, 1, nvec); + if (ret < nvec) { + if (ret > 0) + pci_disable_msix(pdev); kfree(vdev->msix); kfree(vdev->ctx); return ret; } } else { - ret = pci_enable_msi_block(pdev, nvec); - if (ret) { + ret = pci_enable_msi_range(pdev, 1, nvec); + if (ret < nvec) { + if (ret > 0) + pci_disable_msi(pdev); kfree(vdev->ctx); return ret; } diff --git a/include/acpi/acpi_numa.h b/include/acpi/acpi_numa.h index 451823cb883..94a37cd7fbd 100644 --- a/include/acpi/acpi_numa.h +++ b/include/acpi/acpi_numa.h @@ -13,7 +13,6 @@ extern int pxm_to_node(int); extern int node_to_pxm(int); -extern void __acpi_map_pxm_to_node(int, int); extern int acpi_map_pxm_to_node(int); extern unsigned char acpi_srat_revision; diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 6a15dddbaa0..7a8f2cd66c8 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -263,14 +263,9 @@ extern void acpi_dmi_osi_linux(int enable, const struct dmi_system_id *d); extern void acpi_osi_setup(char *str); #ifdef CONFIG_ACPI_NUMA -int acpi_get_pxm(acpi_handle handle); -int acpi_get_node(acpi_handle *handle); +int acpi_get_node(acpi_handle handle); #else -static inline int acpi_get_pxm(acpi_handle handle) -{ - return 0; -} -static inline int acpi_get_node(acpi_handle *handle) +static inline int acpi_get_node(acpi_handle handle) { return 0; } diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 89b7c24a36e..5e3a906cc08 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -51,7 +51,7 @@ struct resource { #define IORESOURCE_EXCLUSIVE 0x08000000 /* Userland may not map this resource */ #define IORESOURCE_DISABLED 0x10000000 -#define IORESOURCE_UNSET 0x20000000 +#define IORESOURCE_UNSET 0x20000000 /* No address assigned yet */ #define IORESOURCE_AUTO 0x40000000 #define IORESOURCE_BUSY 0x80000000 /* Driver has marked this resource busy */ @@ -169,6 +169,16 @@ static inline unsigned long resource_type(const struct resource *res) { return res->flags & IORESOURCE_TYPE_BITS; } +/* True iff r1 completely contains r2 */ +static inline bool resource_contains(struct resource *r1, struct resource *r2) +{ + if (resource_type(r1) != resource_type(r2)) + return false; + if (r1->flags & IORESOURCE_UNSET || r2->flags & IORESOURCE_UNSET) + return false; + return r1->start <= r2->start && r1->end >= r2->end; +} + /* Convenience shorthand with allocation */ #define request_region(start,n,name) __request_region(&ioport_resource, (start), (n), (name), 0) diff --git a/include/linux/pci.h b/include/linux/pci.h index 33aa2caf0f0..aab57b4abe7 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -29,7 +29,6 @@ #include <linux/atomic.h> #include <linux/device.h> #include <linux/io.h> -#include <linux/irqreturn.h> #include <uapi/linux/pci.h> #include <linux/pci_ids.h> @@ -170,6 +169,8 @@ enum pci_dev_flags { PCI_DEV_FLAGS_NO_D3 = (__force pci_dev_flags_t) 2, /* Provide indication device is assigned by a Virtual Machine Manager */ PCI_DEV_FLAGS_ASSIGNED = (__force pci_dev_flags_t) 4, + /* Flag for quirk use to store if quirk-specific ACS is enabled */ + PCI_DEV_FLAGS_ACS_ENABLED_QUIRK = (__force pci_dev_flags_t) 8, }; enum pci_irq_reroute_variant { @@ -461,7 +462,6 @@ struct pci_bus { unsigned int is_added:1; }; -#define pci_bus_b(n) list_entry(n, struct pci_bus, node) #define to_pci_bus(n) container_of(n, struct pci_bus, dev) /* @@ -1066,7 +1066,7 @@ void pci_bus_remove_resources(struct pci_bus *bus); int __must_check pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, resource_size_t size, resource_size_t align, resource_size_t min, - unsigned int type_mask, + unsigned long type_mask, resource_size_t (*alignf)(void *, const struct resource *, resource_size_t, @@ -1530,6 +1530,7 @@ enum pci_fixup_pass { void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev); struct pci_dev *pci_get_dma_source(struct pci_dev *dev); int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags); +void pci_dev_specific_enable_acs(struct pci_dev *dev); #else static inline void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) { } @@ -1542,6 +1543,7 @@ static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev, { return -ENOTTY; } +static inline void pci_dev_specific_enable_acs(struct pci_dev *dev) { } #endif void __iomem *pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen); @@ -1597,7 +1599,6 @@ void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar); #ifdef CONFIG_PCI_IOV int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn); void pci_disable_sriov(struct pci_dev *dev); -irqreturn_t pci_sriov_migration(struct pci_dev *dev); int pci_num_vf(struct pci_dev *dev); int pci_vfs_assigned(struct pci_dev *dev); int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs); @@ -1606,8 +1607,6 @@ int pci_sriov_get_totalvfs(struct pci_dev *dev); static inline int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn) { return -ENODEV; } static inline void pci_disable_sriov(struct pci_dev *dev) { } -static inline irqreturn_t pci_sriov_migration(struct pci_dev *dev) -{ return IRQ_NONE; } static inline int pci_num_vf(struct pci_dev *dev) { return 0; } static inline int pci_vfs_assigned(struct pci_dev *dev) { return 0; } diff --git a/kernel/resource.c b/kernel/resource.c index 3f285dce934..673061c06da 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -432,11 +432,6 @@ static void resource_clip(struct resource *res, resource_size_t min, res->end = max; } -static bool resource_contains(struct resource *res1, struct resource *res2) -{ - return res1->start <= res2->start && res1->end >= res2->end; -} - /* * Find empty slot in the resource tree with the given range and * alignment constraints @@ -471,10 +466,11 @@ static int __find_resource(struct resource *root, struct resource *old, arch_remove_reservations(&tmp); /* Check for overflow after ALIGN() */ - avail = *new; avail.start = ALIGN(tmp.start, constraint->align); avail.end = tmp.end; + avail.flags = new->flags & ~IORESOURCE_UNSET; if (avail.start >= tmp.start) { + alloc.flags = avail.flags; alloc.start = constraint->alignf(constraint->alignf_data, &avail, size, constraint->align); alloc.end = alloc.start + size - 1; @@ -949,8 +945,8 @@ struct resource * __request_region(struct resource *parent, res->name = name; res->start = start; res->end = start + n - 1; - res->flags = IORESOURCE_BUSY; - res->flags |= flags; + res->flags = resource_type(parent); + res->flags |= IORESOURCE_BUSY | flags; write_lock(&resource_lock); diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 185b6d300eb..5e2cf6f342f 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -719,10 +719,15 @@ char *resource_string(char *buf, char *end, struct resource *res, specp = &mem_spec; decode = 0; } - p = number(p, pend, res->start, *specp); - if (res->start != res->end) { - *p++ = '-'; - p = number(p, pend, res->end, *specp); + if (decode && res->flags & IORESOURCE_UNSET) { + p = string(p, pend, "size ", str_spec); + p = number(p, pend, resource_size(res), *specp); + } else { + p = number(p, pend, res->start, *specp); + if (res->start != res->end) { + *p++ = '-'; + p = number(p, pend, res->end, *specp); + } } if (decode) { if (res->flags & IORESOURCE_MEM_64) |