diff options
| -rw-r--r-- | Documentation/kernel-parameters.txt | 9 | ||||
| -rw-r--r-- | drivers/pci/pci.c | 120 | ||||
| -rw-r--r-- | drivers/pci/pci.h | 6 | ||||
| -rw-r--r-- | drivers/pci/quirks.c | 60 | ||||
| -rw-r--r-- | drivers/pci/setup-res.c | 15 |
5 files changed, 210 insertions, 0 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index c7c441e7930e..1754fedc531c 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt | |||
| @@ -1760,6 +1760,15 @@ and is between 256 and 4096 characters. It is defined in the file | |||
| 1760 | cbmemsize=nn[KMG] The fixed amount of bus space which is | 1760 | cbmemsize=nn[KMG] The fixed amount of bus space which is |
| 1761 | reserved for the CardBus bridge's memory | 1761 | reserved for the CardBus bridge's memory |
| 1762 | window. The default value is 64 megabytes. | 1762 | window. The default value is 64 megabytes. |
| 1763 | resource_alignment= | ||
| 1764 | Format: | ||
| 1765 | [<order of align>@][<domain>:]<bus>:<slot>.<func>[; ...] | ||
| 1766 | Specifies alignment and device to reassign | ||
| 1767 | aligned memory resources. | ||
| 1768 | If <order of align> is not specified, | ||
| 1769 | PAGE_SIZE is used as alignment. | ||
| 1770 | PCI-PCI bridge can be specified, if resource | ||
| 1771 | windows need to be expanded. | ||
| 1763 | 1772 | ||
| 1764 | pcie_aspm= [PCIE] Forcibly enable or disable PCIe Active State Power | 1773 | pcie_aspm= [PCIE] Forcibly enable or disable PCIe Active State Power |
| 1765 | Management. | 1774 | Management. |
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8310dc2f943b..a35a8b2ba631 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
| @@ -20,6 +20,8 @@ | |||
| 20 | #include <linux/pm_wakeup.h> | 20 | #include <linux/pm_wakeup.h> |
| 21 | #include <linux/interrupt.h> | 21 | #include <linux/interrupt.h> |
| 22 | #include <asm/dma.h> /* isa_dma_bridge_buggy */ | 22 | #include <asm/dma.h> /* isa_dma_bridge_buggy */ |
| 23 | #include <linux/device.h> | ||
| 24 | #include <asm/setup.h> | ||
| 23 | #include "pci.h" | 25 | #include "pci.h" |
| 24 | 26 | ||
| 25 | unsigned int pci_pm_d3_delay = PCI_PM_D3_WAIT; | 27 | unsigned int pci_pm_d3_delay = PCI_PM_D3_WAIT; |
| @@ -2370,6 +2372,121 @@ int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type) | |||
| 2370 | return 0; | 2372 | return 0; |
| 2371 | } | 2373 | } |
| 2372 | 2374 | ||
| 2375 | #define RESOURCE_ALIGNMENT_PARAM_SIZE COMMAND_LINE_SIZE | ||
| 2376 | static char resource_alignment_param[RESOURCE_ALIGNMENT_PARAM_SIZE] = {0}; | ||
| 2377 | spinlock_t resource_alignment_lock = SPIN_LOCK_UNLOCKED; | ||
| 2378 | |||
| 2379 | /** | ||
| 2380 | * pci_specified_resource_alignment - get resource alignment specified by user. | ||
| 2381 | * @dev: the PCI device to get | ||
| 2382 | * | ||
| 2383 | * RETURNS: Resource alignment if it is specified. | ||
| 2384 | * Zero if it is not specified. | ||
| 2385 | */ | ||
| 2386 | resource_size_t pci_specified_resource_alignment(struct pci_dev *dev) | ||
| 2387 | { | ||
| 2388 | int seg, bus, slot, func, align_order, count; | ||
| 2389 | resource_size_t align = 0; | ||
| 2390 | char *p; | ||
| 2391 | |||
| 2392 | spin_lock(&resource_alignment_lock); | ||
| 2393 | p = resource_alignment_param; | ||
| 2394 | while (*p) { | ||
| 2395 | count = 0; | ||
| 2396 | if (sscanf(p, "%d%n", &align_order, &count) == 1 && | ||
| 2397 | p[count] == '@') { | ||
| 2398 | p += count + 1; | ||
| 2399 | } else { | ||
| 2400 | align_order = -1; | ||
| 2401 | } | ||
| 2402 | if (sscanf(p, "%x:%x:%x.%x%n", | ||
| 2403 | &seg, &bus, &slot, &func, &count) != 4) { | ||
| 2404 | seg = 0; | ||
| 2405 | if (sscanf(p, "%x:%x.%x%n", | ||
| 2406 | &bus, &slot, &func, &count) != 3) { | ||
| 2407 | /* Invalid format */ | ||
| 2408 | printk(KERN_ERR "PCI: Can't parse resource_alignment parameter: %s\n", | ||
| 2409 | p); | ||
| 2410 | break; | ||
| 2411 | } | ||
| 2412 | } | ||
| 2413 | p += count; | ||
| 2414 | if (seg == pci_domain_nr(dev->bus) && | ||
| 2415 | bus == dev->bus->number && | ||
| 2416 | slot == PCI_SLOT(dev->devfn) && | ||
| 2417 | func == PCI_FUNC(dev->devfn)) { | ||
| 2418 | if (align_order == -1) { | ||
| 2419 | align = PAGE_SIZE; | ||
| 2420 | } else { | ||
| 2421 | align = 1 << align_order; | ||
| 2422 | } | ||
| 2423 | /* Found */ | ||
| 2424 | break; | ||
| 2425 | } | ||
| 2426 | if (*p != ';' && *p != ',') { | ||
| 2427 | /* End of param or invalid format */ | ||
| 2428 | break; | ||
| 2429 | } | ||
| 2430 | p++; | ||
| 2431 | } | ||
| 2432 | spin_unlock(&resource_alignment_lock); | ||
| 2433 | return align; | ||
| 2434 | } | ||
| 2435 | |||
| 2436 | /** | ||
| 2437 | * pci_is_reassigndev - check if specified PCI is target device to reassign | ||
| 2438 | * @dev: the PCI device to check | ||
| 2439 | * | ||
| 2440 | * RETURNS: non-zero for PCI device is a target device to reassign, | ||
| 2441 | * or zero is not. | ||
| 2442 | */ | ||
| 2443 | int pci_is_reassigndev(struct pci_dev *dev) | ||
| 2444 | { | ||
| 2445 | return (pci_specified_resource_alignment(dev) != 0); | ||
| 2446 | } | ||
| 2447 | |||
| 2448 | ssize_t pci_set_resource_alignment_param(const char *buf, size_t count) | ||
| 2449 | { | ||
| 2450 | if (count > RESOURCE_ALIGNMENT_PARAM_SIZE - 1) | ||
| 2451 | count = RESOURCE_ALIGNMENT_PARAM_SIZE - 1; | ||
| 2452 | spin_lock(&resource_alignment_lock); | ||
| 2453 | strncpy(resource_alignment_param, buf, count); | ||
| 2454 | resource_alignment_param[count] = '\0'; | ||
| 2455 | spin_unlock(&resource_alignment_lock); | ||
| 2456 | return count; | ||
| 2457 | } | ||
| 2458 | |||
| 2459 | ssize_t pci_get_resource_alignment_param(char *buf, size_t size) | ||
| 2460 | { | ||
| 2461 | size_t count; | ||
| 2462 | spin_lock(&resource_alignment_lock); | ||
| 2463 | count = snprintf(buf, size, "%s", resource_alignment_param); | ||
| 2464 | spin_unlock(&resource_alignment_lock); | ||
| 2465 | return count; | ||
| 2466 | } | ||
| 2467 | |||
| 2468 | static ssize_t pci_resource_alignment_show(struct bus_type *bus, char *buf) | ||
| 2469 | { | ||
| 2470 | return pci_get_resource_alignment_param(buf, PAGE_SIZE); | ||
| 2471 | } | ||
| 2472 | |||
| 2473 | static ssize_t pci_resource_alignment_store(struct bus_type *bus, | ||
| 2474 | const char *buf, size_t count) | ||
| 2475 | { | ||
| 2476 | return pci_set_resource_alignment_param(buf, count); | ||
| 2477 | } | ||
| 2478 | |||
| 2479 | BUS_ATTR(resource_alignment, 0644, pci_resource_alignment_show, | ||
| 2480 | pci_resource_alignment_store); | ||
| 2481 | |||
| 2482 | static int __init pci_resource_alignment_sysfs_init(void) | ||
| 2483 | { | ||
| 2484 | return bus_create_file(&pci_bus_type, | ||
| 2485 | &bus_attr_resource_alignment); | ||
| 2486 | } | ||
| 2487 | |||
| 2488 | late_initcall(pci_resource_alignment_sysfs_init); | ||
| 2489 | |||
| 2373 | static void __devinit pci_no_domains(void) | 2490 | static void __devinit pci_no_domains(void) |
| 2374 | { | 2491 | { |
| 2375 | #ifdef CONFIG_PCI_DOMAINS | 2492 | #ifdef CONFIG_PCI_DOMAINS |
| @@ -2418,6 +2535,9 @@ static int __init pci_setup(char *str) | |||
| 2418 | pci_cardbus_io_size = memparse(str + 9, &str); | 2535 | pci_cardbus_io_size = memparse(str + 9, &str); |
| 2419 | } else if (!strncmp(str, "cbmemsize=", 10)) { | 2536 | } else if (!strncmp(str, "cbmemsize=", 10)) { |
| 2420 | pci_cardbus_mem_size = memparse(str + 10, &str); | 2537 | pci_cardbus_mem_size = memparse(str + 10, &str); |
| 2538 | } else if (!strncmp(str, "resource_alignment=", 19)) { | ||
| 2539 | pci_set_resource_alignment_param(str + 19, | ||
| 2540 | strlen(str + 19)); | ||
| 2421 | } else { | 2541 | } else { |
| 2422 | printk(KERN_ERR "PCI: Unknown option `%s'\n", | 2542 | printk(KERN_ERR "PCI: Unknown option `%s'\n", |
| 2423 | str); | 2543 | str); |
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 07c0aa5275e6..2cd1cba7236f 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h | |||
| @@ -195,4 +195,10 @@ static inline int pci_ari_enabled(struct pci_bus *bus) | |||
| 195 | return bus->self && bus->self->ari_enabled; | 195 | return bus->self && bus->self->ari_enabled; |
| 196 | } | 196 | } |
| 197 | 197 | ||
| 198 | #ifdef CONFIG_PCI_QUIRKS | ||
| 199 | extern int pci_is_reassigndev(struct pci_dev *dev); | ||
| 200 | resource_size_t pci_specified_resource_alignment(struct pci_dev *dev); | ||
| 201 | extern void pci_disable_bridge_window(struct pci_dev *dev); | ||
| 202 | #endif | ||
| 203 | |||
| 198 | #endif /* DRIVERS_PCI_H */ | 204 | #endif /* DRIVERS_PCI_H */ |
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 5aa2afb23ef9..50233818a763 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c | |||
| @@ -24,6 +24,7 @@ | |||
| 24 | #include <linux/kallsyms.h> | 24 | #include <linux/kallsyms.h> |
| 25 | #include <linux/dmi.h> | 25 | #include <linux/dmi.h> |
| 26 | #include <linux/pci-aspm.h> | 26 | #include <linux/pci-aspm.h> |
| 27 | #include <linux/ioport.h> | ||
| 27 | #include "pci.h" | 28 | #include "pci.h" |
| 28 | 29 | ||
| 29 | int isa_dma_bridge_buggy; | 30 | int isa_dma_bridge_buggy; |
| @@ -34,6 +35,65 @@ int pcie_mch_quirk; | |||
| 34 | EXPORT_SYMBOL(pcie_mch_quirk); | 35 | EXPORT_SYMBOL(pcie_mch_quirk); |
| 35 | 36 | ||
| 36 | #ifdef CONFIG_PCI_QUIRKS | 37 | #ifdef CONFIG_PCI_QUIRKS |
| 38 | /* | ||
| 39 | * This quirk function disables the device and releases resources | ||
| 40 | * which is specified by kernel's boot parameter 'pci=resource_alignment='. | ||
| 41 | * It also rounds up size to specified alignment. | ||
| 42 | * Later on, the kernel will assign page-aligned memory resource back | ||
| 43 | * to that device. | ||
| 44 | */ | ||
| 45 | static void __devinit quirk_resource_alignment(struct pci_dev *dev) | ||
| 46 | { | ||
| 47 | int i; | ||
| 48 | struct resource *r; | ||
| 49 | resource_size_t align, size; | ||
| 50 | |||
| 51 | if (!pci_is_reassigndev(dev)) | ||
| 52 | return; | ||
| 53 | |||
| 54 | if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL && | ||
| 55 | (dev->class >> 8) == PCI_CLASS_BRIDGE_HOST) { | ||
| 56 | dev_warn(&dev->dev, | ||
| 57 | "Can't reassign resources to host bridge.\n"); | ||
| 58 | return; | ||
| 59 | } | ||
| 60 | |||
| 61 | dev_info(&dev->dev, "Disabling device and release resources.\n"); | ||
| 62 | pci_disable_device(dev); | ||
| 63 | |||
| 64 | align = pci_specified_resource_alignment(dev); | ||
| 65 | for (i=0; i < PCI_BRIDGE_RESOURCES; i++) { | ||
| 66 | r = &dev->resource[i]; | ||
| 67 | if (!(r->flags & IORESOURCE_MEM)) | ||
| 68 | continue; | ||
| 69 | size = resource_size(r); | ||
| 70 | if (size < align) { | ||
| 71 | size = align; | ||
| 72 | dev_info(&dev->dev, | ||
| 73 | "Rounding up size of resource #%d to %#llx.\n", | ||
| 74 | i, (unsigned long long)size); | ||
| 75 | } | ||
| 76 | r->end = size - 1; | ||
| 77 | r->start = 0; | ||
| 78 | } | ||
| 79 | /* Need to disable bridge's resource window, | ||
| 80 | * to enable the kernel to reassign new resource | ||
| 81 | * window later on. | ||
| 82 | */ | ||
| 83 | if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && | ||
| 84 | (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { | ||
| 85 | for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) { | ||
| 86 | r = &dev->resource[i]; | ||
| 87 | if (!(r->flags & IORESOURCE_MEM)) | ||
| 88 | continue; | ||
| 89 | r->end = resource_size(r) - 1; | ||
| 90 | r->start = 0; | ||
| 91 | } | ||
| 92 | pci_disable_bridge_window(dev); | ||
| 93 | } | ||
| 94 | } | ||
| 95 | DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, quirk_resource_alignment); | ||
| 96 | |||
| 37 | /* The Mellanox Tavor device gives false positive parity errors | 97 | /* The Mellanox Tavor device gives false positive parity errors |
| 38 | * Mark this device with a broken_parity_status, to allow | 98 | * Mark this device with a broken_parity_status, to allow |
| 39 | * PCI scanning code to "skip" this now blacklisted device. | 99 | * PCI scanning code to "skip" this now blacklisted device. |
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 32e8d88a4619..3039fcb86afc 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c | |||
| @@ -120,6 +120,21 @@ int pci_claim_resource(struct pci_dev *dev, int resource) | |||
| 120 | return err; | 120 | return err; |
| 121 | } | 121 | } |
| 122 | 122 | ||
| 123 | #ifdef CONFIG_PCI_QUIRKS | ||
| 124 | void pci_disable_bridge_window(struct pci_dev *dev) | ||
| 125 | { | ||
| 126 | dev_dbg(&dev->dev, "Disabling bridge window.\n"); | ||
| 127 | |||
| 128 | /* MMIO Base/Limit */ | ||
| 129 | pci_write_config_dword(dev, PCI_MEMORY_BASE, 0x0000fff0); | ||
| 130 | |||
| 131 | /* Prefetchable MMIO Base/Limit */ | ||
| 132 | pci_write_config_dword(dev, PCI_PREF_LIMIT_UPPER32, 0); | ||
| 133 | pci_write_config_dword(dev, PCI_PREF_MEMORY_BASE, 0x0000fff0); | ||
| 134 | pci_write_config_dword(dev, PCI_PREF_BASE_UPPER32, 0xffffffff); | ||
| 135 | } | ||
| 136 | #endif /* CONFIG_PCI_QUIRKS */ | ||
| 137 | |||
| 123 | int pci_assign_resource(struct pci_dev *dev, int resno) | 138 | int pci_assign_resource(struct pci_dev *dev, int resno) |
| 124 | { | 139 | { |
| 125 | struct pci_bus *bus = dev->bus; | 140 | struct pci_bus *bus = dev->bus; |
