diff options
-rw-r--r-- | Documentation/kernel-parameters.txt | 6 | ||||
-rw-r--r-- | arch/x86/include/asm/pci.h | 1 | ||||
-rw-r--r-- | arch/x86/include/asm/pci_x86.h | 1 | ||||
-rw-r--r-- | arch/x86/pci/common.c | 6 | ||||
-rw-r--r-- | arch/x86/pci/i386.c | 66 | ||||
-rw-r--r-- | arch/x86/pci/mrst.c | 7 | ||||
-rw-r--r-- | drivers/char/agp/intel-gtt.c | 14 | ||||
-rw-r--r-- | drivers/pci/bus.c | 74 | ||||
-rw-r--r-- | drivers/pci/host-bridge.c | 48 | ||||
-rw-r--r-- | drivers/pci/hotplug-pci.c | 12 | ||||
-rw-r--r-- | drivers/pci/hotplug/acpiphp_glue.c | 4 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_hpc.c | 5 | ||||
-rw-r--r-- | drivers/pci/iov.c | 31 | ||||
-rw-r--r-- | drivers/pci/pci-driver.c | 6 | ||||
-rw-r--r-- | drivers/pci/pci.c | 32 | ||||
-rw-r--r-- | drivers/pci/pci.h | 5 | ||||
-rw-r--r-- | drivers/pci/probe.c | 491 | ||||
-rw-r--r-- | drivers/pci/quirks.c | 256 | ||||
-rw-r--r-- | drivers/pci/remove.c | 5 | ||||
-rw-r--r-- | drivers/pci/setup-bus.c | 106 | ||||
-rw-r--r-- | drivers/pci/setup-res.c | 66 | ||||
-rw-r--r-- | drivers/pci/xen-pcifront.c | 4 | ||||
-rw-r--r-- | drivers/pcmcia/yenta_socket.c | 75 | ||||
-rw-r--r-- | include/linux/ioport.h | 15 | ||||
-rw-r--r-- | include/linux/pci.h | 98 | ||||
-rw-r--r-- | kernel/resource.c | 311 |
26 files changed, 1265 insertions, 480 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 7aef3345f7396e..e31d4fb0e8aaa0 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2189,6 +2189,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted. off: Turn realloc off on: Turn realloc on realloc same as realloc=on + alloc_high= Enable/disable allocating PCI resources above + 4G at first when allocating resource to PCI + devices that does not get assigned by BIOS. + off: Turn alloc_high off + on: Turn alloc_high on + alloc_high same as alloc_high=on noari do not use PCIe ARI. pcie_scan_all Scan all possible PCIe devices. Otherwise we only look for one device below a PCIe downstream diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h index df75d07571ceac..27aa0d58ffe034 100644 --- a/arch/x86/include/asm/pci.h +++ b/arch/x86/include/asm/pci.h @@ -149,7 +149,6 @@ void default_restore_msi_irqs(struct pci_dev *dev, int irq); /* generic pci stuff */ #include <asm-generic/pci.h> -#define PCIBIOS_MAX_MEM_32 0xffffffff #ifdef CONFIG_NUMA /* Returns the node based on pci bus */ diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h index 73e8eeff22ee03..6edcf80d2708a0 100644 --- a/arch/x86/include/asm/pci_x86.h +++ b/arch/x86/include/asm/pci_x86.h @@ -35,6 +35,7 @@ do { \ #define PCI_NOASSIGN_ROMS 0x80000 #define PCI_ROOT_NO_CRS 0x100000 #define PCI_NOASSIGN_BARS 0x200000 +#define PCI_ASSIGN_PREF_BARS 0x400000 extern unsigned int pci_probe; extern unsigned long pirq_table_addr; diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c index 720e973fc34a31..409033e2763f61 100644 --- a/arch/x86/pci/common.c +++ b/arch/x86/pci/common.c @@ -136,8 +136,7 @@ static void __devinit pcibios_fixup_device_resources(struct pci_dev *dev) * resource so the kernel doesn't attmept to assign * it later on in pci_assign_unassigned_resources */ - for (bar = 0; bar <= PCI_STD_RESOURCE_END; bar++) { - bar_r = &dev->resource[bar]; + for_each_pci_resource(dev, bar_r, bar, PCI_NOIOV_RES & ~PCI_BRIDGE_RES & ~PCI_ROM_RES) { if (bar_r->start == 0 && bar_r->end != 0) { bar_r->flags = 0; bar_r->end = 0; @@ -573,6 +572,9 @@ char * __init pcibios_setup(char *str) } else if (!strcmp(str, "assign-busses")) { pci_probe |= PCI_ASSIGN_ALL_BUSSES; return NULL; + } else if (!strcmp(str, "pref_bar")) { + pci_probe |= PCI_ASSIGN_PREF_BARS; + return NULL; } else if (!strcmp(str, "use_crs")) { pci_probe |= PCI_USE__CRS; return NULL; diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index e0b7f306b383c7..11b4e3b6e1275c 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -206,11 +206,12 @@ static void pcibios_allocate_bridge_resources(struct pci_dev *dev) int idx; struct resource *r; - for (idx = PCI_BRIDGE_RESOURCES; idx < PCI_NUM_RESOURCES; idx++) { - r = &dev->resource[idx]; + for_each_pci_resource(dev, r, idx, PCI_BRIDGE_RES) { if (!r->flags) continue; - if (!r->start || pci_claim_resource(dev, idx) < 0) { + if (((r->flags & IORESOURCE_PREFETCH) && + (pci_probe & PCI_ASSIGN_PREF_BARS)) || + !r->start || pci_claim_resource(dev, idx) < 0) { /* * Something is wrong with the region. * Invalidate the resource to prevent @@ -234,49 +235,38 @@ static void pcibios_allocate_bus_resources(struct pci_bus *bus) pcibios_allocate_bus_resources(child); } -struct pci_check_idx_range { - int start; - int end; -}; - static void pcibios_allocate_dev_resources(struct pci_dev *dev, int pass) { - int idx, disabled, i; + int idx, disabled; u16 command; struct resource *r; - struct pci_check_idx_range idx_range[] = { - { PCI_STD_RESOURCES, PCI_STD_RESOURCE_END }, -#ifdef CONFIG_PCI_IOV - { PCI_IOV_RESOURCES, PCI_IOV_RESOURCE_END }, -#endif - }; - pci_read_config_word(dev, PCI_COMMAND, &command); - for (i = 0; i < ARRAY_SIZE(idx_range); i++) - for (idx = idx_range[i].start; idx <= idx_range[i].end; idx++) { - r = &dev->resource[idx]; - if (r->parent) /* Already allocated */ - continue; - if (!r->start) /* Address not assigned at all */ + for_each_pci_resource(dev, r, idx, PCI_NOROM_RES & ~PCI_BRIDGE_RES) { + if (r->parent) /* Already allocated */ + continue; + if (!r->start) /* Address not assigned at all */ + continue; + if (r->flags & IORESOURCE_IO) + disabled = !(command & PCI_COMMAND_IO); + else + disabled = !(command & PCI_COMMAND_MEMORY); + if (pass == disabled) { + if ((r->flags & IORESOURCE_PREFETCH) && + (pci_probe & PCI_ASSIGN_PREF_BARS)) + goto clear_start; + dev_dbg(&dev->dev, + "BAR %d: reserving %pr (d=%d, p=%d)\n", + idx, r, disabled, pass); + if (!pci_claim_resource(dev, idx)) continue; - if (r->flags & IORESOURCE_IO) - disabled = !(command & PCI_COMMAND_IO); - else - disabled = !(command & PCI_COMMAND_MEMORY); - if (pass == disabled) { - dev_dbg(&dev->dev, - "BAR %d: reserving %pr (d=%d, p=%d)\n", - idx, r, disabled, pass); - if (pci_claim_resource(dev, idx) < 0) { - /* We'll assign a new address later */ - pcibios_save_fw_addr(dev, - idx, r->start); - r->end -= r->start; - r->start = 0; - } - } +clear_start: + /* We'll assign a new address later */ + pcibios_save_fw_addr(dev, idx, r->start); + r->end -= r->start; + r->start = 0; } + } if (!pass) { r = &dev->resource[PCI_ROM_RESOURCE]; if (r->flags & IORESOURCE_ROM_ENABLE) { diff --git a/arch/x86/pci/mrst.c b/arch/x86/pci/mrst.c index e14a2ff708b5b3..506a4bd2cbb5c2 100644 --- a/arch/x86/pci/mrst.c +++ b/arch/x86/pci/mrst.c @@ -280,6 +280,7 @@ static void __devinit pci_fixed_bar_fixup(struct pci_dev *dev) unsigned long offset; u32 size; int i; + struct resource *res; if (!pci_soc_mode) return; @@ -294,10 +295,10 @@ static void __devinit pci_fixed_bar_fixup(struct pci_dev *dev) PCI_DEVFN(2, 2) == dev->devfn) return; - for (i = 0; i < PCI_ROM_RESOURCE; i++) { + for_each_pci_resource(dev, res, i, PCI_STD_RES) { pci_read_config_dword(dev, offset + 8 + (i * 4), &size); - dev->resource[i].end = dev->resource[i].start + size - 1; - dev->resource[i].flags |= IORESOURCE_PCI_FIXED; + res->end = res->start + size - 1; + res->flags |= IORESOURCE_PCI_FIXED; } } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_fixed_bar_fixup); diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 58e32f7c322956..87beca71562c9f 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -649,8 +649,10 @@ static void intel_gtt_cleanup(void) static int intel_gtt_init(void) { u32 gma_addr; + u32 addr_hi = 0; u32 gtt_map_size; int ret; + int pos; ret = intel_private.driver->setup(); if (ret != 0) @@ -696,13 +698,17 @@ static int intel_gtt_init(void) } if (INTEL_GTT_GEN <= 2) - pci_read_config_dword(intel_private.pcidev, I810_GMADDR, - &gma_addr); + pos = I810_GMADDR; else - pci_read_config_dword(intel_private.pcidev, I915_GMADDR, - &gma_addr); + pos = I915_GMADDR; + + pci_read_config_dword(intel_private.pcidev, pos, &gma_addr); + + if (gma_addr & PCI_BASE_ADDRESS_MEM_TYPE_64) + pci_read_config_dword(intel_private.pcidev, pos + 4, &addr_hi); intel_private.base.gma_bus_addr = (gma_addr & PCI_BASE_ADDRESS_MEM_MASK); + intel_private.base.gma_bus_addr |= (u64)addr_hi << 32; return 0; } diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index a85247d05efd6f..197b5d968ae248 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -98,6 +98,14 @@ void pci_bus_remove_resources(struct pci_bus *bus) } } +static bool pci_alloc_high_enable = false; +void __init pci_alloc_high_get_opt(char *str) +{ + if (!strncmp(str, "off", 3)) + pci_alloc_high_enable = false; + else if (!strncmp(str, "on", 2)) + pci_alloc_high_enable = true; +} /** * pci_bus_alloc_resource - allocate a resource from a parent bus * @bus: PCI bus @@ -114,26 +122,24 @@ void pci_bus_remove_resources(struct pci_bus *bus) * for a specific device resource. */ int -pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, +pci_bus_alloc_resource_fit(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 (*alignf)(void *, const struct resource *, resource_size_t, resource_size_t), - void *alignf_data) + void *alignf_data, bool fit) { int i, ret = -ENOMEM; struct resource *r; - resource_size_t max = -1; type_mask |= IORESOURCE_IO | IORESOURCE_MEM; - /* don't allocate too high if the pref mem doesn't support 64bit*/ - if (!(res->flags & IORESOURCE_MEM_64)) - max = PCIBIOS_MAX_MEM_32; - pci_bus_for_each_resource(bus, r, i) { + resource_size_t start, end, middle; + struct pci_bus_region region; + if (!r) continue; @@ -147,19 +153,63 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, !(res->flags & IORESOURCE_PREFETCH)) continue; + start = 0; + end = MAX_RESOURCE; + if (!pci_alloc_high_enable) + goto again; + /* + * don't allocate too high if the pref mem doesn't + * support 64bit, also if this is a 64-bit mem + * resource, try above 4GB first + */ + __pcibios_resource_to_bus(bus, ®ion, r); + if (region.start <= PCI_MAX_ADDR_32 && + region.end > PCI_MAX_ADDR_32) { + middle = pcibios_bus_addr_to_res(bus, res->flags, + PCI_MAX_ADDR_32); + if (res->flags & IORESOURCE_MEM_64) + start = middle + 1; + else + end = middle; + } else if (region.start > PCI_MAX_ADDR_32 && + !(res->flags & IORESOURCE_MEM_64)) + continue; + +again: /* Ok, try it out.. */ - ret = allocate_resource(r, res, size, - r->start ? : min, - max, align, - alignf, alignf_data); + ret = allocate_resource_fit(r, res, size, + max(start, r->start ? : min), + end, align, + alignf, alignf_data, fit); if (ret == 0) - break; + return 0; + + if (start != 0) { + start = 0; + goto again; + } } + + return ret; } void __weak pcibios_resource_survey_bus(struct pci_bus *bus) { } +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 (*alignf)(void *, + const struct resource *, + resource_size_t, + resource_size_t), + void *alignf_data) +{ + return pci_bus_alloc_resource_fit(bus, res, size, align, min, type_mask, + alignf, alignf_data, false); +} + /** * pci_bus_add_device - add a single device * @dev: device to add diff --git a/drivers/pci/host-bridge.c b/drivers/pci/host-bridge.c index a68dc613a5be6c..c911adb902cc44 100644 --- a/drivers/pci/host-bridge.c +++ b/drivers/pci/host-bridge.c @@ -9,22 +9,19 @@ #include "pci.h" -static struct pci_bus *find_pci_root_bus(struct pci_dev *dev) +static struct pci_bus *find_pci_root_bus(struct pci_bus *bus) { - struct pci_bus *bus; - - bus = dev->bus; while (bus->parent) bus = bus->parent; return bus; } -static struct pci_host_bridge *find_pci_host_bridge(struct pci_dev *dev) +static struct pci_host_bridge *find_pci_host_bridge(struct pci_bus *bus) { - struct pci_bus *bus = find_pci_root_bus(dev); + struct pci_bus *root_bus = find_pci_root_bus(bus); - return to_pci_host_bridge(bus->bridge); + return to_pci_host_bridge(root_bus->bridge); } void pci_set_host_bridge_release(struct pci_host_bridge *bridge, @@ -40,10 +37,11 @@ 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_dev *dev, struct pci_bus_region *region, - struct resource *res) +void __pcibios_resource_to_bus(struct pci_bus *bus, + struct pci_bus_region *region, + struct resource *res) { - struct pci_host_bridge *bridge = find_pci_host_bridge(dev); + struct pci_host_bridge *bridge = find_pci_host_bridge(bus); struct pci_host_bridge_window *window; resource_size_t offset = 0; @@ -60,6 +58,11 @@ void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, region->start = res->start - offset; region->end = res->end - offset; } +void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res) +{ + __pcibios_resource_to_bus(dev->bus, region, res); +} EXPORT_SYMBOL(pcibios_resource_to_bus); static bool region_contains(struct pci_bus_region *region1, @@ -68,10 +71,10 @@ static bool region_contains(struct pci_bus_region *region1, return region1->start <= region2->start && region1->end >= region2->end; } -void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, - struct pci_bus_region *region) +static void __pcibios_bus_to_resource(struct pci_bus *bus, struct resource *res, + struct pci_bus_region *region) { - struct pci_host_bridge *bridge = find_pci_host_bridge(dev); + struct pci_host_bridge *bridge = find_pci_host_bridge(bus); struct pci_host_bridge_window *window; resource_size_t offset = 0; @@ -93,4 +96,23 @@ void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, res->start = region->start + offset; res->end = region->end + offset; } +void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, + struct pci_bus_region *region) +{ + __pcibios_bus_to_resource(dev->bus, res, region); +} EXPORT_SYMBOL(pcibios_bus_to_resource); + +resource_size_t pcibios_bus_addr_to_res(struct pci_bus *bus, int flags, + resource_size_t addr) +{ + struct pci_bus_region region; + struct resource r; + + r.flags = flags; + region.start = addr; + region.end = addr; + __pcibios_bus_to_resource(bus, &r, ®ion); + + return r.end; +} diff --git a/drivers/pci/hotplug-pci.c b/drivers/pci/hotplug-pci.c index 6258dc260d9f54..2221a279f6f405 100644 --- a/drivers/pci/hotplug-pci.c +++ b/drivers/pci/hotplug-pci.c @@ -7,18 +7,8 @@ int __ref pci_hp_add_bridge(struct pci_dev *dev) { struct pci_bus *parent = dev->bus; - int pass, busnr, start = parent->busn_res.start; - int end = parent->busn_res.end; + int pass, busnr = parent->busn_res.start; - for (busnr = start; busnr <= end; busnr++) { - if (!pci_find_bus(pci_domain_nr(parent), busnr)) - break; - } - if (busnr-- > end) { - printk(KERN_ERR "No bus number available for hot-added bridge %s\n", - pci_name(dev)); - return -1; - } for (pass = 0; pass < 2; pass++) busnr = pci_scan_bridge(parent, dev, busnr, pass); if (!dev->subordinate) diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 1f26706827cbe4..df5e4dbf6cceff 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -1096,10 +1096,10 @@ static void acpiphp_sanitize_bus(struct pci_bus *bus) struct pci_dev *dev; int i; unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM; + struct resource *res; list_for_each_entry(dev, &bus->devices, bus_list) { - for (i=0; i<PCI_BRIDGE_RESOURCES; i++) { - struct resource *res = &dev->resource[i]; + for_each_pci_resource(dev, res, i, PCI_NOBRIDGE_RES) { if ((res->flags & type_mask) && !res->start && res->end) { /* Could not assign a required resources diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 13b2eaf7ba434f..204d61f998d029 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -798,6 +798,7 @@ static inline void dbg_ctrl(struct controller *ctrl) int i; u16 reg16; struct pci_dev *pdev = ctrl->pcie->port; + struct resource *res; if (!pciehp_debug) return; @@ -813,11 +814,11 @@ static inline void dbg_ctrl(struct controller *ctrl) pdev->subsystem_vendor); ctrl_info(ctrl, " PCIe Cap offset : 0x%02x\n", pci_pcie_cap(pdev)); - for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { + for_each_pci_resource(pdev, res, i, PCI_ALL_RES) { if (!pci_resource_len(pdev, i)) continue; ctrl_info(ctrl, " PCI resource [%d] : %pR\n", - i, &pdev->resource[i]); + i, res); } ctrl_info(ctrl, "Slot Capabilities : 0x%08x\n", ctrl->slot_cap); ctrl_info(ctrl, " Physical Slot Number : %d\n", PSN(ctrl)); diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index aeccc911abb829..a05326bbe11ad9 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -99,17 +99,20 @@ static int virtfn_add(struct pci_dev *dev, int id, int reset) pci_setup_device(virtfn); virtfn->dev.parent = dev->dev.parent; - for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { - res = dev->resource + PCI_IOV_RESOURCES + i; + for_each_pci_resource(dev, res, i, PCI_IOV_RES) { + struct resource *virtfn_res; + if (!res->parent) continue; - virtfn->resource[i].name = pci_name(virtfn); - virtfn->resource[i].flags = res->flags; + + virtfn_res = pci_dev_resource_n(virtfn, i - PCI_IOV_RESOURCES); + virtfn_res->name = pci_name(virtfn); + virtfn_res->flags = res->flags; size = resource_size(res); do_div(size, iov->total); - virtfn->resource[i].start = res->start + size * id; - virtfn->resource[i].end = virtfn->resource[i].start + size - 1; - rc = request_resource(res, &virtfn->resource[i]); + virtfn_res->start = res->start + size * id; + virtfn_res->end = virtfn_res->start + size - 1; + rc = request_resource(res, virtfn_res); BUG_ON(rc); } @@ -313,9 +316,8 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) return -EIO; nres = 0; - for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { - bars |= (1 << (i + PCI_IOV_RESOURCES)); - res = dev->resource + PCI_IOV_RESOURCES + i; + for_each_pci_resource(dev, res, i, PCI_IOV_RES) { + bars |= 1 << i; if (res->parent) nres++; } @@ -473,10 +475,9 @@ found: pci_write_config_dword(dev, pos + PCI_SRIOV_SYS_PGSIZE, pgsz); nres = 0; - for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { - res = dev->resource + PCI_IOV_RESOURCES + i; + for_each_pci_resource(dev, res, i, PCI_IOV_RES) { i += __pci_read_base(dev, pci_bar_unknown, res, - pos + PCI_SRIOV_BAR + i * 4); + pos + PCI_SRIOV_BAR + (i - PCI_IOV_RESOURCES) * 4); if (!res->flags) continue; if (resource_size(res) & (PAGE_SIZE - 1)) { @@ -519,10 +520,8 @@ found: return 0; failed: - for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { - res = dev->resource + PCI_IOV_RESOURCES + i; + for_each_pci_resource(dev, res, i, PCI_IOV_RES) res->flags = 0; - } return rc; } diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 8f87ea085a9f93..dae1fbae866338 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -1232,9 +1232,11 @@ pci_dev_driver(const struct pci_dev *dev) if (dev->driver) return dev->driver; else { + struct resource *res; int i; - for(i=0; i<=PCI_ROM_RESOURCE; i++) - if (dev->resource[i].flags & IORESOURCE_BUSY) + + for_each_pci_resource((struct pci_dev *)dev, res, i, PCI_NOIOV_RES & ~PCI_BRIDGE_RES) + if (res->flags & IORESOURCE_BUSY) return &pci_compat_driver; } return NULL; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 54858838f09867..34d6da045f5c14 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -440,8 +440,9 @@ static void pci_restore_bars(struct pci_dev *dev) { int i; + struct resource *res; - for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) + for_each_pci_resource(dev, res, i, PCI_NOBRIDGE_RES) pci_update_resource(dev, i); } @@ -1161,6 +1162,7 @@ static int __pci_enable_device_flags(struct pci_dev *dev, { int err; int i, bars = 0; + struct resource *res; /* * Power state could be unknown at this point, either due to a fresh @@ -1178,12 +1180,11 @@ static int __pci_enable_device_flags(struct pci_dev *dev, return 0; /* already enabled */ /* only skip sriov related */ - for (i = 0; i <= PCI_ROM_RESOURCE; i++) - if (dev->resource[i].flags & flags) - bars |= (1 << i); - for (i = PCI_BRIDGE_RESOURCES; i < DEVICE_COUNT_RESOURCE; i++) - if (dev->resource[i].flags & flags) + for_each_pci_resource(dev, res, i, PCI_NOIOV_RES) { + /* TODO: check i with bits of bars */ + if (res->flags & flags) bars |= (1 << i); + } err = do_pci_enable_device(dev, bars); if (err < 0) @@ -2517,7 +2518,7 @@ static int __pci_request_region(struct pci_dev *pdev, int bar, const char *res_n err_out: dev_warn(&pdev->dev, "BAR %d: can't reserve %pR\n", bar, - &pdev->resource[bar]); + pci_dev_resource_n(pdev, bar)); return -EBUSY; } @@ -3572,6 +3573,13 @@ int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type) reg = pci_iov_resource_bar(dev, resno, type); if (reg) return reg; + } else if (resno >= PCI_NUM_RESOURCES) { + struct resource *res = pci_dev_resource_n(dev, resno); + + if (res) { + *type = pci_bar_unknown; + return to_pci_dev_addon_resource(res)->reg_addr; + } } dev_err(&dev->dev, "BAR %d: invalid resource\n", resno); @@ -3752,8 +3760,7 @@ void pci_reassigndev_resource_alignment(struct pci_dev *dev) pci_write_config_word(dev, PCI_COMMAND, command); align = pci_specified_resource_alignment(dev); - for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) { - r = &dev->resource[i]; + for_each_pci_resource(dev, r, i, PCI_NOBRIDGE_RES) { if (!(r->flags & IORESOURCE_MEM)) continue; size = resource_size(r); @@ -3772,8 +3779,7 @@ void pci_reassigndev_resource_alignment(struct pci_dev *dev) */ if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { - for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) { - r = &dev->resource[i]; + for_each_pci_resource(dev, r, i, PCI_BRIDGE_RES) { if (!(r->flags & IORESOURCE_MEM)) continue; r->end = resource_size(r) - 1; @@ -3865,6 +3871,10 @@ static int __init pci_setup(char *str) pci_realloc_get_opt(str + 8); } else if (!strncmp(str, "realloc", 7)) { pci_realloc_get_opt("on"); + } else if (!strncmp(str, "alloc_high=", 11)) { + pci_alloc_high_get_opt(str + 11); + } else if (!strncmp(str, "alloc_high", 10)) { + pci_alloc_high_get_opt("on"); } else if (!strcmp(str, "nodomains")) { pci_no_domains(); } else if (!strncmp(str, "noari", 5)) { diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index bacbcba69cf386..d4e05b735370af 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -145,6 +145,7 @@ static inline void pci_msi_init_pci_dev(struct pci_dev *dev) { } #endif void pci_realloc_get_opt(char *); +void pci_alloc_high_get_opt(char *); static inline int pci_no_d1d2(struct pci_dev *dev) { @@ -203,6 +204,10 @@ enum pci_bar_type { pci_bar_mem64, /* A 64-bit memory BAR */ }; +#define PCI_MAX_ADDR_32 ((resource_size_t)0xffffffff) + +#define PCI_MAX_ADDR_32 ((resource_size_t)0xffffffff) + bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl, int crs_timeout); extern int pci_setup_device(struct pci_dev *dev); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 3cc58244f953ca..e0f43537e59592 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -105,6 +105,173 @@ static int __init pcibus_class_init(void) } postcore_initcall(pcibus_class_init); +struct resource *pci_dev_resource_n(struct pci_dev *dev, int n) +{ + struct pci_dev_addon_resource *addon_res; + + if (n < 0) + return NULL; + + if (n < PCI_NUM_RESOURCES) + return &dev->resource[n]; + + n -= PCI_NUM_RESOURCES; + list_for_each_entry(addon_res, &dev->addon_resources, list) { + if (n-- == 0) + return &addon_res->res; + } + + return NULL; +} +EXPORT_SYMBOL(pci_dev_resource_n); + +int pci_dev_resource_idx(struct pci_dev *dev, struct resource *res) +{ + struct pci_dev_addon_resource *addon_res; + int n; + + if (res >= dev->resource && + res <= dev->resource + (PCI_NUM_RESOURCES - 1)) + return res - dev->resource; + + n = PCI_NUM_RESOURCES; + list_for_each_entry(addon_res, &dev->addon_resources, list) { + if (res == &addon_res->res) + return n; + n++; + } + + return -1; +} + +struct pci_dev_addon_resource *__add_pci_dev_addon_resource( + struct pci_dev *dev, int addr, char *name) +{ + struct pci_dev_addon_resource *addon_res; + + addon_res = kzalloc(sizeof(*addon_res), GFP_KERNEL); + + if (!addon_res) + return NULL; + + addon_res->reg_addr = addr; + + if (name) + addon_res->res.name = name; + else + addon_res->res.name = pci_name(dev); + + list_add_tail(&addon_res->list, &dev->addon_resources); + + return addon_res; +} + +struct pci_dev_addon_resource *add_pci_dev_addon_fixed_resource( + struct pci_dev *dev, int start, int size, int flags, + int addr, char *name) +{ + struct pci_dev_addon_resource *addon_res; + struct resource *res; + + addon_res = __add_pci_dev_addon_resource(dev, addr, name); + if (addon_res) + return NULL; + + res = &addon_res->res; + res->start = start; + res->end = start + size - 1; + res->flags = flags | IORESOURCE_PCI_FIXED; + + dev_printk(KERN_DEBUG, &dev->dev, + "addon fixed resource %s %pR added\n", res->name, res); + + return addon_res; +} + +struct pci_dev_addon_resource *add_pci_dev_addon_resource(struct pci_dev *dev, + int addr, int size, struct resource_ops *ops, char *name) +{ + struct pci_dev_addon_resource *addon_res; + struct resource *res; + + addon_res = __add_pci_dev_addon_resource(dev, addr, name); + if (!addon_res) + return NULL; + + res = &addon_res->res; + if (ops) { + addon_res->ops = ops; + addon_res->size = size; + ops->read(dev, res, addr); + } else + __pci_read_base(dev, pci_bar_unknown, res, addr); + + dev_printk(KERN_DEBUG, &dev->dev, + "addon resource %s %pR added\n", res->name, res); + + return addon_res; +} + +static void pci_release_dev_addon_res(struct pci_dev *dev) +{ + struct pci_dev_addon_resource *addon_res, *tmp; + + list_for_each_entry_safe(addon_res, tmp, &dev->addon_resources, list) { + list_del(&addon_res->list); + kfree(addon_res); + } +} + +static void __init_res_idx_mask(unsigned long *mask, int flag) +{ + bitmap_zero(mask, PCI_NUM_RESOURCES); + if (flag & PCI_STD_RES) + bitmap_set(mask, PCI_STD_RESOURCES, + PCI_STD_RESOURCE_END - PCI_STD_RESOURCES + 1); + if (flag & PCI_ROM_RES) + bitmap_set(mask, PCI_ROM_RESOURCE, 1); +#ifdef CONFIG_PCI_IOV + if (flag & PCI_IOV_RES) + bitmap_set(mask, PCI_IOV_RESOURCES, + PCI_IOV_RESOURCE_END - PCI_IOV_RESOURCES + 1); +#endif + if (flag & PCI_BRIDGE_RES) + bitmap_set(mask, PCI_BRIDGE_RESOURCES, + PCI_BRIDGE_RESOURCE_END - PCI_BRIDGE_RESOURCES + 1); +} + +static DECLARE_BITMAP(res_idx_mask[1 << PCI_RES_BLOCK_NUM], PCI_NUM_RESOURCES); +static int __init pci_res_idx_mask_init(void) +{ + int i; + + for (i = 0; i < (1 << PCI_RES_BLOCK_NUM); i++) + __init_res_idx_mask(res_idx_mask[i], i); + + return 0; +} +postcore_initcall(pci_res_idx_mask_init); + +static inline unsigned long *get_res_idx_mask(int flag) +{ + return res_idx_mask[flag & ((1 << PCI_RES_BLOCK_NUM) - 1)]; +} + +int pci_next_resource_idx(int i, int flag) +{ + i++; + if (i < PCI_NUM_RESOURCES) + i = find_next_bit(get_res_idx_mask(flag), PCI_NUM_RESOURCES, i); + + if (i < PCI_NUM_RESOURCES) + return i; + + if (flag & PCI_ADDON_RES) + return i; + + return -1; +} + static u64 pci_size(u64 base, u64 maxbase, u64 mask) { u64 size = mask & maxbase; /* Find the significant bits */ @@ -288,9 +455,11 @@ out: static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) { unsigned int pos, reg; + struct resource *res; - for (pos = 0; pos < howmany; pos++) { - struct resource *res = &dev->resource[pos]; + for_each_pci_resource(dev, res, pos, PCI_STD_RES) { + if (pos >= howmany) + break; reg = PCI_BASE_ADDRESS_0 + (pos << 2); pos += __pci_read_base(dev, pci_bar_unknown, res, reg); } @@ -644,9 +813,8 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, * Set up the primary, secondary and subordinate * bus numbers. */ - child->number = child->busn_res.start = busnr; + child->number = busnr; child->primary = parent->busn_res.start; - child->busn_res.end = 0xff; if (!bridge) return child; @@ -666,6 +834,21 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, return child; } +static void pci_bus_replace_busn_res(struct pci_bus *b, + struct resource *busn_res) +{ + struct resource *res = &b->busn_res; + + /* busn_res must be registered already*/ + if (!busn_res->parent) + return; + + replace_resource(busn_res, res); + + dev_printk(KERN_DEBUG, &b->dev, "busn_res: %pR is updated under %pR\n", + res, res->parent); +} + struct pci_bus *__ref pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev, int busnr) { struct pci_bus *child; @@ -679,22 +862,136 @@ 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) +static void __devinit pci_bus_extend_top(struct pci_bus *parent, + long size, struct resource *parent_res) { - struct pci_bus *parent = child->parent; + struct resource *res; - /* Attempts to fix that up are really dangerous unless - we're going to re-assign all bus numbers. */ - if (!pcibios_assign_all_busses()) + if (!size) return; - while (parent->parent && parent->busn_res.end < max) { - parent->busn_res.end = max; - pci_write_config_byte(parent->self, PCI_SUBORDINATE_BUS, max); + while (parent) { + res = &parent->busn_res; + if (res == parent_res) + break; + pci_write_config_byte(parent->self, PCI_SUBORDINATE_BUS, + parent->busn_res.end); + dev_printk(KERN_DEBUG, &parent->dev, + "busn_res: %s %02lx to %pR\n", + (size > 0) ? "extended" : "shrunk", + abs(size), res); parent = parent->parent; } } +static void __devinit pci_bus_shrink_top(struct pci_bus *parent, + long size, struct resource *parent_res) +{ + pci_bus_extend_top(parent, -size, parent_res); +} + +static int __devinit pci_bridge_probe_busn_res(struct pci_bus *bus, + struct pci_dev *dev, struct resource *busn_res, + resource_size_t needed_size, struct resource **p) +{ + int ret = -ENOMEM; + int old_size = resource_size(&bus->busn_res); + int skip_nr = 1; + int stop_flags = IORESOURCE_PCI_FIXED; + int i; + + for (i = needed_size; i > 0; i--) { + ret = probe_resource(&bus->busn_res, busn_res, i, + p, skip_nr, stop_flags); + + if (!ret) + break; + } + + if (!i) + return ret; + + busn_res->flags = IORESOURCE_BUS; + + if (*p) { + /* extend parent bus top*/ + int new_size = resource_size(&bus->busn_res); + + pci_bus_extend_top(bus, new_size - old_size, *p); + } + + return ret; +} + +static int __devinit pci_bridge_check_busn_broken_with_unscaned( + struct pci_bus *bus, + struct pci_dev *dev, + int secondary, int subordinate) +{ + u32 buses2; + int broken = 0; + struct pci_dev *dev2; + int secondary2, subordinate2; + int common_start, common_end; + + /* need to check with not scaned sibling bridges */ + list_for_each_entry(dev2, &bus->devices, bus_list) { + if (dev2->hdr_type != PCI_HEADER_TYPE_BRIDGE && + dev2->hdr_type != PCI_HEADER_TYPE_CARDBUS) + continue; + if (dev2->subordinate) + continue; + if (dev2 == dev) + continue; + + pci_read_config_dword(dev2, PCI_PRIMARY_BUS, &buses2); + secondary2 = (buses2 >> 8) & 0xFF; + subordinate2 = (buses2 >> 16) & 0xFF; + + /* overlapping between them ? */ + common_start = max(secondary, secondary2); + common_end = min(subordinate, subordinate2); + if (common_start <= common_end) { + /* + * Temporarily disable forwarding of the + * configuration cycles on this sibling bridge + */ + pci_write_config_dword(dev2, PCI_PRIMARY_BUS, + buses2 & ~0xffffff); + broken = 1; + } + } + + return broken; +} + +static int __devinit pci_bridge_check_busn_broken(struct pci_bus *bus, + struct pci_dev *dev, + int secondary, int subordinate) +{ + int ret; + struct resource busn_res; + + memset(&busn_res, 0, sizeof(struct resource)); + dev_printk(KERN_DEBUG, &dev->dev, + "check if busn %02x-%02x is in busn_res: %pR\n", + secondary, subordinate, &bus->busn_res); + ret = allocate_resource(&bus->busn_res, &busn_res, + (subordinate - secondary + 1), + secondary, subordinate, + 1, NULL, NULL); + if (ret) + return 1; + + release_resource(&busn_res); + + return pci_bridge_check_busn_broken_with_unscaned(bus, dev, + secondary, subordinate); +} + +static unsigned int __devinit __pci_scan_child_bus(struct pci_bus *bus, + int pass); +#define HOTPLUG_BRIDGE_RESERVE_BUSNR 8 /* * 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 @@ -709,10 +1006,11 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, { struct pci_bus *child; int is_cardbus = (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS); - u32 buses, i, j = 0; + u32 buses; u16 bctl; u8 primary, secondary, subordinate; int broken = 0; + struct resource *parent_res = NULL; pci_read_config_dword(dev, PCI_PRIMARY_BUS, &buses); primary = buses & 0xFF; @@ -729,10 +1027,16 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, /* Check if setup is sensible at all */ if (!pass && - (primary != bus->number || secondary <= bus->number)) { - dev_dbg(&dev->dev, "bus configuration invalid, reconfiguring\n"); + (primary != bus->number || secondary <= bus->number)) broken = 1; - } + + /* more strict checking */ + if (!pass && !broken && !dev->subordinate) + broken = pci_bridge_check_busn_broken(bus, dev, + secondary, subordinate); + + if (broken) + dev_dbg(&dev->dev, "bus configuration invalid, reconfiguring\n"); /* Disable MasterAbortMode during probing to avoid reporting of bus errors (in some architectures) */ @@ -744,11 +1048,10 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, !is_cardbus && !broken) { unsigned int cmax; /* - * Bus already configured by firmware, process it in the first - * pass and just note the configuration. + * Bus already configured by firmware, still process it in two + * passes in extreme case like two adjaced bridges have children + * bridges that are not setup properly. */ - if (pass) - goto out; /* * If we already got to this bus through a different bridge, @@ -767,7 +1070,7 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, child->bridge_ctl = bctl; } - cmax = pci_scan_child_bus(child); + cmax = __pci_scan_child_bus(child, pass); if (cmax > max) max = cmax; if (child->busn_res.end > max) @@ -777,6 +1080,11 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, * We need to assign a number to this bus which we always * do in the second pass. */ + long shrink_size; + struct resource busn_res; + int ret = -ENOMEM; + int old_max = max; + if (!pass) { if (pcibios_assign_all_busses() || broken) /* Temporarily disable forwarding of the @@ -793,21 +1101,42 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, /* 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. */ - child = pci_find_bus(pci_domain_nr(bus), max+1); - if (!child) { - child = pci_add_new_bus(bus, dev, ++max); - if (!child) - goto out; - pci_bus_insert_busn_res(child, max, 0xff); + if (dev->subordinate) { + /* We get here only for cardbus */ + child = dev->subordinate; + if (!is_cardbus) + dev_warn(&dev->dev, + "rescan scaned bridge as broken one again ?"); + + goto out; } + /* + * For CardBus bridges, we leave 4 bus numbers + * as cards with a PCI-to-PCI bridge can be + * inserted later. + * other just allocate 8 bus to avoid we fall into + * small hole in the middle. + */ + ret = pci_bridge_probe_busn_res(bus, dev, &busn_res, + is_cardbus ? (CARDBUS_RESERVE_BUSNR + 1) : 8, + &parent_res); + + if (ret != 0) + goto out; + + child = pci_add_new_bus(bus, dev, busn_res.start); + if (!child) + goto out; + + pci_bus_replace_busn_res(child, &busn_res); + buses = (buses & 0xff000000) | ((unsigned int)(child->primary) << 0) | ((unsigned int)(child->busn_res.start) << 8) | ((unsigned int)(child->busn_res.end) << 16); + max = child->busn_res.end; + /* * yenta.c forces a secondary latency timer of 176. * Copy that behaviour here. @@ -824,57 +1153,29 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, 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 - * as cards with a PCI-to-PCI bridge can be - * inserted later. - */ - for (i=0; i<CARDBUS_RESERVE_BUSNR; i++) { - struct pci_bus *parent = bus; - if (pci_find_bus(pci_domain_nr(bus), - max+i+1)) - break; - while (parent->parent) { - if ((!pcibios_assign_all_busses()) && - (parent->busn_res.end > max) && - (parent->busn_res.end <= max+i)) { - j = 1; - } - parent = parent->parent; - } - if (j) { - /* - * Often, there are two cardbus bridges - * -- try to leave one valid bus number - * for each one. - */ - i /= 2; - break; - } - } - max += i; - pci_fixup_parent_subordinate_busnr(child, max); } /* * Set the subordinate bus number to its real value. */ - pci_bus_update_busn_res_end(child, max); + if (dev->is_hotplug_bridge && child->busn_res.end > max && + (max - child->busn_res.start) < HOTPLUG_BRIDGE_RESERVE_BUSNR) + max = min_t(int, child->busn_res.start + + HOTPLUG_BRIDGE_RESERVE_BUSNR, + child->busn_res.end); + shrink_size = (int)child->busn_res.end - max; pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max); + pci_bus_update_busn_res_end(child, max); + + /* shrink some back, if we extend top before */ + if (!is_cardbus && (shrink_size > 0) && parent_res) { + resource_shrink_parents_top(&bus->busn_res, shrink_size, + parent_res); + pci_bus_shrink_top(bus, shrink_size, parent_res); + } + + if (old_max > max) + max = old_max; } sprintf(child->name, @@ -1115,6 +1416,7 @@ static void pci_release_dev(struct device *dev) pci_dev = to_pci_dev(dev); pci_release_capabilities(pci_dev); pci_release_of_node(pci_dev); + pci_release_dev_addon_res(pci_dev); kfree(pci_dev); } @@ -1192,6 +1494,7 @@ struct pci_dev *alloc_pci_dev(void) return NULL; INIT_LIST_HEAD(&dev->bus_list); + INIT_LIST_HEAD(&dev->addon_resources); return dev; } @@ -1585,12 +1888,13 @@ void pcie_bus_configure_settings(struct pci_bus *bus, u8 mpss) } EXPORT_SYMBOL_GPL(pcie_bus_configure_settings); -unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus) +static unsigned int __devinit __pci_scan_child_bus(struct pci_bus *bus, + int pass) { - unsigned int devfn, pass, max = bus->busn_res.start; + unsigned int devfn, max = bus->busn_res.start; struct pci_dev *dev; - dev_dbg(&bus->dev, "scanning bus\n"); + dev_dbg(&bus->dev, "scanning bus pass %d\n", pass); /* Go find them, Rover! */ for (devfn = 0; devfn < 0x100; devfn += 8) @@ -1604,18 +1908,16 @@ unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus) * all PCI-to-PCI bridges on this bus. */ if (!bus->is_added) { - dev_dbg(&bus->dev, "fixups for bus\n"); + dev_dbg(&bus->dev, "fixups for bus pass %d\n", pass); pcibios_fixup_bus(bus); if (pci_is_root_bus(bus)) bus->is_added = 1; } - for (pass=0; pass < 2; pass++) - list_for_each_entry(dev, &bus->devices, bus_list) { - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || - dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) - max = pci_scan_bridge(bus, dev, max, pass); - } + list_for_each_entry(dev, &bus->devices, bus_list) + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || + dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) + max = pci_scan_bridge(bus, dev, max, pass); /* * We've scanned the bus and so we know all about what's on @@ -1624,7 +1926,24 @@ unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus) * * Return how far we've got finding sub-buses. */ - dev_dbg(&bus->dev, "bus scan returning with max=%02x\n", max); + dev_dbg(&bus->dev, "bus scan returning with max=%02x pass %d\n", + max, pass); + + return max; +} + +unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus) +{ + int pass; + unsigned int max = 0, tmp; + + for (pass = 0; pass < 2; pass++) { + tmp = __pci_scan_child_bus(bus, pass); + + if (tmp > max) + max = tmp; + } + return max; } diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 7a451ff56ecc05..2c628e55182aa3 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -324,29 +324,61 @@ static void __devinit quirk_cs5536_vsa(struct pci_dev *dev) } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA, quirk_cs5536_vsa); -static void __devinit quirk_io_region(struct pci_dev *dev, unsigned region, - unsigned size, int nr, const char *name) +static int quirk_read_io(struct pci_dev *dev, struct resource *res, int port) { - region &= ~(size-1); - if (region) { - struct pci_bus_region bus_region; - struct resource *res = dev->resource + nr; + struct pci_bus_region bus_region; + u16 region; + unsigned size = to_pci_dev_addon_resource(res)->size; - res->name = pci_name(dev); - res->start = region; - res->end = region + size - 1; - res->flags = IORESOURCE_IO; + pci_read_config_word(dev, port, ®ion); + region &= ~(size - 1); - /* Convert from PCI bus to resource space. */ - bus_region.start = res->start; - bus_region.end = res->end; - pcibios_bus_to_resource(dev, res, &bus_region); + /* Convert from PCI bus to resource space. */ + bus_region.start = region; + bus_region.end = region + size - 1; - if (pci_claim_resource(dev, nr) == 0) - dev_info(&dev->dev, "quirk: %pR claimed by %s\n", - res, name); - } -} + pcibios_bus_to_resource(dev, res, &bus_region); + + res->flags |= IORESOURCE_IO; + dev_info(&dev->dev, "PIO at %pR\n", res); + + return 0; +} +static int quirk_write_io(struct pci_dev *dev, struct resource *res, int port) +{ + struct pci_bus_region bus_region; + u16 region; + unsigned size = to_pci_dev_addon_resource(res)->size; + + pci_read_config_word(dev, port, ®ion); + region &= size - 1; + + /* Convert to PCI bus space. */ + pcibios_resource_to_bus(dev, &bus_region, res); + region |= bus_region.start & (~(size - 1)); + + pci_write_config_word(dev, port, region); + + return 0; +} +static struct resource_ops quirk_io_ops = { + .read = quirk_read_io, + .write = quirk_write_io, +}; + +static void __devinit quirk_io_region(struct pci_dev *dev, int port, + unsigned size, char *name) +{ + u16 region; + + pci_read_config_word(dev, port, ®ion); + region &= size - 1; + + if (!region) + return; + + add_pci_dev_addon_resource(dev, port, size, &quirk_io_ops, name); +} /* * ATI Northbridge setups MCE the processor if you even @@ -354,10 +386,12 @@ static void __devinit quirk_io_region(struct pci_dev *dev, unsigned region, */ static void __devinit quirk_ati_exploding_mce(struct pci_dev *dev) { - dev_info(&dev->dev, "ATI Northbridge, reserving I/O ports 0x3b0 to 0x3bb\n"); + dev_info(&dev->dev, "ATI Northbridge, will reserve I/O ports 0x3b0 to 0x3bb\n"); /* Mae rhaid i ni beidio ag edrych ar y lleoliadiau I/O hyn */ - request_region(0x3b0, 0x0C, "RadeonIGP"); - request_region(0x3d3, 0x01, "RadeonIGP"); + add_pci_dev_addon_fixed_resource(dev, 0x3B0, 0x0C, IORESOURCE_IO, 0, + "RadeonIGP"); + add_pci_dev_addon_fixed_resource(dev, 0x3d3, 0x01, IORESOURCE_IO, 0, + "RadeonIGP"); } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RS100, quirk_ati_exploding_mce); @@ -374,23 +408,19 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RS100, quirk_ati_ */ static void __devinit quirk_ali7101_acpi(struct pci_dev *dev) { - u16 region; - - pci_read_config_word(dev, 0xE0, ®ion); - quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES, "ali7101 ACPI"); - pci_read_config_word(dev, 0xE2, ®ion); - quirk_io_region(dev, region, 32, PCI_BRIDGE_RESOURCES+1, "ali7101 SMB"); + quirk_io_region(dev, 0xE0, 64, "ali7101 ACPI"); + quirk_io_region(dev, 0xE2, 32, "ali7101 SMB"); } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, quirk_ali7101_acpi); -static void piix4_io_quirk(struct pci_dev *dev, const char *name, unsigned int port, unsigned int enable) +static int piix4_read_io(struct pci_dev *dev, struct resource *res, int port) { u32 devres; u32 mask, size, base; + struct pci_bus_region bus_region; pci_read_config_dword(dev, port, &devres); - if ((devres & enable) != enable) - return; + mask = (devres >> 16) & 15; base = devres & 0xffff; size = 16; @@ -400,23 +430,53 @@ static void piix4_io_quirk(struct pci_dev *dev, const char *name, unsigned int p break; size = bit; } - /* - * For now we only print it out. Eventually we'll want to - * reserve it (at least if it's in the 0x1000+ range), but - * let's get enough confirmation reports first. - */ base &= -size; - dev_info(&dev->dev, "%s PIO at %04x-%04x\n", name, base, base + size - 1); + + bus_region.start = base; + bus_region.end = base + size - 1; + res->flags |= IORESOURCE_IO; + pcibios_bus_to_resource(dev, res, &bus_region); + dev_info(&dev->dev, "PIO at %pR\n", res); + + return 0; } +static int piix4_write_io(struct pci_dev *dev, struct resource *res, int port) +{ + u32 devres; + struct pci_bus_region bus_region; -static void piix4_mem_quirk(struct pci_dev *dev, const char *name, unsigned int port, unsigned int enable) + pcibios_resource_to_bus(dev, &bus_region, res); + + pci_read_config_dword(dev, port, &devres); + devres &= 0xffff0000; + devres |= bus_region.start & 0xffff; + pci_write_config_dword(dev, port, devres); + + return 0; +} +static struct resource_ops piix4_io_ops = { + .read = piix4_read_io, + .write = piix4_write_io, +}; +static void piix4_io_quirk(struct pci_dev *dev, char *name, unsigned int port, + unsigned int enable) { u32 devres; - u32 mask, size, base; pci_read_config_dword(dev, port, &devres); if ((devres & enable) != enable) return; + + add_pci_dev_addon_resource(dev, port, 0, &piix4_io_ops, name); +} + +static int piix4_read_mem(struct pci_dev *dev, struct resource *res, int port) +{ + u32 devres; + u32 mask, size, base; + struct pci_bus_region bus_region; + + pci_read_config_dword(dev, port, &devres); base = devres & 0xffff0000; mask = (devres & 0x3f) << 16; size = 128 << 16; @@ -426,12 +486,43 @@ static void piix4_mem_quirk(struct pci_dev *dev, const char *name, unsigned int break; size = bit; } - /* - * For now we only print it out. Eventually we'll want to - * reserve it, but let's get enough confirmation reports first. - */ base &= -size; - dev_info(&dev->dev, "%s MMIO at %04x-%04x\n", name, base, base + size - 1); + bus_region.start = base; + bus_region.end = base + size - 1; + res->flags |= IORESOURCE_MEM; + pcibios_bus_to_resource(dev, res, &bus_region); + dev_info(&dev->dev, "MMIO at %pR\n", res); + + return 0; +} +static int piix4_write_mem(struct pci_dev *dev, struct resource *res, int port) +{ + u32 devres; + struct pci_bus_region bus_region; + + pcibios_resource_to_bus(dev, &bus_region, res); + + pci_read_config_dword(dev, port, &devres); + devres &= 0x0000ffff; + devres |= bus_region.start & 0xffff0000; + pci_write_config_dword(dev, port, devres); + + return 0; +} +static struct resource_ops piix4_mem_ops = { + .read = piix4_read_mem, + .write = piix4_write_mem, +}; +static void piix4_mem_quirk(struct pci_dev *dev, char *name, unsigned int port, + unsigned int enable) +{ + u32 devres; + + pci_read_config_dword(dev, port, &devres); + if ((devres & enable) != enable) + return; + + add_pci_dev_addon_resource(dev, port, 0, &piix4_mem_ops, name); } /* @@ -442,12 +533,10 @@ static void piix4_mem_quirk(struct pci_dev *dev, const char *name, unsigned int */ static void __devinit quirk_piix4_acpi(struct pci_dev *dev) { - u32 region, res_a; + u32 res_a; - pci_read_config_dword(dev, 0x40, ®ion); - quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES, "PIIX4 ACPI"); - pci_read_config_dword(dev, 0x90, ®ion); - quirk_io_region(dev, region, 16, PCI_BRIDGE_RESOURCES+1, "PIIX4 SMB"); + quirk_io_region(dev, 0x40, 64, "PIIX4 ACPI"); + quirk_io_region(dev, 0x90, 16, "PIIX4 SMB"); /* Device resource A has enables for some of the other ones */ pci_read_config_dword(dev, 0x5c, &res_a); @@ -491,7 +580,6 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3, qui */ static void __devinit quirk_ich4_lpc_acpi(struct pci_dev *dev) { - u32 region; u8 enable; /* @@ -503,22 +591,12 @@ static void __devinit quirk_ich4_lpc_acpi(struct pci_dev *dev) */ pci_read_config_byte(dev, ICH_ACPI_CNTL, &enable); - if (enable & ICH4_ACPI_EN) { - pci_read_config_dword(dev, ICH_PMBASE, ®ion); - region &= PCI_BASE_ADDRESS_IO_MASK; - if (region >= PCIBIOS_MIN_IO) - quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, - "ICH4 ACPI/GPIO/TCO"); - } + if (enable & ICH4_ACPI_EN) + quirk_io_region(dev, ICH_PMBASE, 128, "ICH4 ACPI/GPIO/TCO"); pci_read_config_byte(dev, ICH4_GPIO_CNTL, &enable); - if (enable & ICH4_GPIO_EN) { - pci_read_config_dword(dev, ICH4_GPIOBASE, ®ion); - region &= PCI_BASE_ADDRESS_IO_MASK; - if (region >= PCIBIOS_MIN_IO) - quirk_io_region(dev, region, 64, - PCI_BRIDGE_RESOURCES + 1, "ICH4 GPIO"); - } + if (enable & ICH4_GPIO_EN) + quirk_io_region(dev, ICH4_GPIOBASE, 64, "ICH4 GPIO"); } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, quirk_ich4_lpc_acpi); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_0, quirk_ich4_lpc_acpi); @@ -533,26 +611,15 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1, qui static void __devinit ich6_lpc_acpi_gpio(struct pci_dev *dev) { - u32 region; u8 enable; pci_read_config_byte(dev, ICH_ACPI_CNTL, &enable); - if (enable & ICH6_ACPI_EN) { - pci_read_config_dword(dev, ICH_PMBASE, ®ion); - region &= PCI_BASE_ADDRESS_IO_MASK; - if (region >= PCIBIOS_MIN_IO) - quirk_io_region(dev, region, 128, PCI_BRIDGE_RESOURCES, - "ICH6 ACPI/GPIO/TCO"); - } + if (enable & ICH6_ACPI_EN) + quirk_io_region(dev, ICH_PMBASE, 128, "ICH6 ACPI/GPIO/TCO"); pci_read_config_byte(dev, ICH6_GPIO_CNTL, &enable); - if (enable & ICH6_GPIO_EN) { - pci_read_config_dword(dev, ICH6_GPIOBASE, ®ion); - region &= PCI_BASE_ADDRESS_IO_MASK; - if (region >= PCIBIOS_MIN_IO) - quirk_io_region(dev, region, 64, - PCI_BRIDGE_RESOURCES + 1, "ICH6 GPIO"); - } + if (enable & ICH6_GPIO_EN) + quirk_io_region(dev, ICH6_GPIOBASE, 64, "ICH6 GPIO"); } static void __devinit ich6_lpc_generic_decode(struct pci_dev *dev, unsigned reg, const char *name, int dynsize) @@ -650,13 +717,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH10_1, qui */ static void __devinit quirk_vt82c586_acpi(struct pci_dev *dev) { - u32 region; - - if (dev->revision & 0x10) { - pci_read_config_dword(dev, 0x48, ®ion); - region &= PCI_BASE_ADDRESS_IO_MASK; - quirk_io_region(dev, region, 256, PCI_BRIDGE_RESOURCES, "vt82c586 ACPI"); - } + if (dev->revision & 0x10) + quirk_io_region(dev, 0x48, 256, "vt82c586 ACPI"); } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3, quirk_vt82c586_acpi); @@ -668,18 +730,11 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3, quirk_vt */ static void __devinit quirk_vt82c686_acpi(struct pci_dev *dev) { - u16 hm; - u32 smb; - quirk_vt82c586_acpi(dev); - pci_read_config_word(dev, 0x70, &hm); - hm &= PCI_BASE_ADDRESS_IO_MASK; - quirk_io_region(dev, hm, 128, PCI_BRIDGE_RESOURCES + 1, "vt82c686 HW-mon"); + quirk_io_region(dev, 0x70, 128, "vt82c686 HW-mon"); - pci_read_config_dword(dev, 0x90, &smb); - smb &= PCI_BASE_ADDRESS_IO_MASK; - quirk_io_region(dev, smb, 16, PCI_BRIDGE_RESOURCES + 2, "vt82c686 SMB"); + quirk_io_region(dev, 0x90, 16, "vt82c686 SMB"); } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4, quirk_vt82c686_acpi); @@ -690,15 +745,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4, quirk_vt */ static void __devinit quirk_vt8235_acpi(struct pci_dev *dev) { - u16 pm, smb; - - pci_read_config_word(dev, 0x88, &pm); - pm &= PCI_BASE_ADDRESS_IO_MASK; - quirk_io_region(dev, pm, 128, PCI_BRIDGE_RESOURCES, "vt8235 PM"); - - pci_read_config_word(dev, 0xd0, &smb); - smb &= PCI_BASE_ADDRESS_IO_MASK; - quirk_io_region(dev, smb, 16, PCI_BRIDGE_RESOURCES + 1, "vt8235 SMB"); + quirk_io_region(dev, 0x88, 128, "vt8235 PM"); + quirk_io_region(dev, 0xd0, 16, "vt8235 SMB"); } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8235, quirk_vt8235_acpi); diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 4ee528af97fe10..3f9b5dbc8543dd 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -6,15 +6,14 @@ static void pci_free_resources(struct pci_dev *dev) { int i; + struct resource *res; msi_remove_pci_irq_vectors(dev); pci_cleanup_rom(dev); - for (i = 0; i < PCI_NUM_RESOURCES; i++) { - struct resource *res = dev->resource + i; + for_each_pci_resource(dev, res, i, PCI_ALL_RES) if (res->parent) release_resource(res); - } } static void pci_stop_dev(struct pci_dev *dev) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 63cb3baac74a0a..7341e4af6c0b36 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -106,7 +106,7 @@ static resource_size_t get_res_add_size(struct list_head *head, list_for_each_entry(dev_res, head, list) { if (dev_res->res == res) { - int idx = res - &dev_res->dev->resource[0]; + int idx = pci_dev_resource_idx(dev_res->dev, res); dev_printk(KERN_DEBUG, &dev_res->dev->dev, "res[%d]=%pR get_res_add_size add_size %llx\n", @@ -120,31 +120,47 @@ static resource_size_t get_res_add_size(struct list_head *head, return 0; } +static resource_size_t __pci_resource_alignment( + struct pci_dev *dev, + struct resource *r, + struct list_head *realloc_head) +{ + resource_size_t r_align, add_size = 0; + + if ((r->flags & IORESOURCE_SIZEALIGN) && realloc_head) + add_size = get_res_add_size(realloc_head, r); + r->end += add_size; + r_align = pci_resource_alignment(dev, r); + r->end -= add_size; + + return r_align; +} /* Sort resources by alignment */ -static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head) +static void pdev_sort_resources(struct pci_dev *dev, + struct list_head *realloc_head, + struct list_head *head) { int i; + struct resource *r; - for (i = 0; i < PCI_NUM_RESOURCES; i++) { - struct resource *r; + for_each_pci_resource(dev, r, i, PCI_ALL_RES) { struct pci_dev_resource *dev_res, *tmp; - resource_size_t r_align; + resource_size_t r_align, r_size; struct list_head *n; - r = &dev->resource[i]; - if (r->flags & IORESOURCE_PCI_FIXED) continue; if (!(r->flags) || r->parent) continue; - r_align = pci_resource_alignment(dev, r); + r_align = __pci_resource_alignment(dev, r, realloc_head); if (!r_align) { dev_warn(&dev->dev, "BAR %d: %pR has bogus alignment\n", i, r); continue; } + r_size = resource_size(r); tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); if (!tmp) @@ -158,10 +174,13 @@ static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head) list_for_each_entry(dev_res, head, list) { resource_size_t align; - align = pci_resource_alignment(dev_res->dev, - dev_res->res); + align = __pci_resource_alignment(dev_res->dev, + dev_res->res, + realloc_head); - if (r_align > align) { + if (r_align > align || + (r_align == align && + r_size > resource_size(dev_res->res))) { n = &dev_res->list; break; } @@ -172,6 +191,7 @@ static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head) } static void __dev_sort_resources(struct pci_dev *dev, + struct list_head *realloc_head, struct list_head *head) { u16 class = dev->class >> 8; @@ -188,7 +208,7 @@ static void __dev_sort_resources(struct pci_dev *dev, return; } - pdev_sort_resources(dev, head); + pdev_sort_resources(dev, realloc_head, head); } static inline void reset_resource(struct resource *res) @@ -237,7 +257,7 @@ static void reassign_resources_sorted(struct list_head *realloc_head, if (!found_match)/* just skip */ continue; - idx = res - &add_res->dev->resource[0]; + idx = pci_dev_resource_idx(add_res->dev, res); add_size = add_res->add_size; if (!resource_size(res)) { res->start = add_res->start; @@ -272,7 +292,7 @@ out: * requests that could not satisfied to the failed_list. */ static void assign_requested_resources_sorted(struct list_head *head, - struct list_head *fail_head) + struct list_head *fail_head, bool fit) { struct resource *res; struct pci_dev_resource *dev_res; @@ -280,21 +300,13 @@ static void assign_requested_resources_sorted(struct list_head *head, list_for_each_entry(dev_res, head, list) { res = dev_res->res; - idx = res - &dev_res->dev->resource[0]; + idx = pci_dev_resource_idx(dev_res->dev, res); if (resource_size(res) && - pci_assign_resource(dev_res->dev, idx)) { - if (fail_head) { - /* - * if the failed res is for ROM BAR, and it will - * be enabled later, don't add it to the list - */ - if (!((idx == PCI_ROM_RESOURCE) && - (!(res->flags & IORESOURCE_ROM_ENABLE)))) - add_to_list(fail_head, - dev_res->dev, res, - 0 /* dont care */, - 0 /* dont care */); - } + pci_assign_resource_fit(dev_res->dev, idx, fit)) { + if (fail_head) + add_to_list(fail_head, dev_res->dev, res, + 0 /* dont care */, + 0 /* dont care */); reset_resource(res); } } @@ -317,6 +329,7 @@ static void __assign_resources_sorted(struct list_head *head, LIST_HEAD(local_fail_head); struct pci_dev_resource *save_res; struct pci_dev_resource *dev_res; + bool fit = true; /* Check if optional add_size is there */ if (!realloc_head || list_empty(realloc_head)) @@ -336,7 +349,7 @@ static void __assign_resources_sorted(struct list_head *head, dev_res->res); /* Try updated head list with add_size added */ - assign_requested_resources_sorted(head, &local_fail_head); + assign_requested_resources_sorted(head, &local_fail_head, fit); /* all assigned with add_size ? */ if (list_empty(&local_fail_head)) { @@ -363,9 +376,12 @@ static void __assign_resources_sorted(struct list_head *head, } free_list(&save_head); + /* will need to expand later, so not use fit */ + fit = false; + requested_and_reassign: /* Satisfy the must-have resource requests */ - assign_requested_resources_sorted(head, fail_head); + assign_requested_resources_sorted(head, fail_head, fit); /* Try to satisfy any additional optional resource requests */ @@ -380,7 +396,7 @@ static void pdev_assign_resources_sorted(struct pci_dev *dev, { LIST_HEAD(head); - __dev_sort_resources(dev, &head); + __dev_sort_resources(dev, add_head, &head); __assign_resources_sorted(&head, add_head, fail_head); } @@ -393,7 +409,7 @@ static void pbus_assign_resources_sorted(const struct pci_bus *bus, LIST_HEAD(head); list_for_each_entry(dev, &bus->devices, bus_list) - __dev_sort_resources(dev, &head); + __dev_sort_resources(dev, realloc_head, &head); __assign_resources_sorted(&head, realloc_head, fail_head); } @@ -757,9 +773,9 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, io_align = min_align = window_alignment(bus, IORESOURCE_IO); list_for_each_entry(dev, &bus->devices, bus_list) { int i; + struct resource *r; - for (i = 0; i < PCI_NUM_RESOURCES; i++) { - struct resource *r = &dev->resource[i]; + for_each_pci_resource(dev, r, i, PCI_ALL_RES) { unsigned long r_size; if (r->parent || !(r->flags & IORESOURCE_IO)) @@ -870,24 +886,24 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, list_for_each_entry(dev, &bus->devices, bus_list) { int i; + struct resource *r; - for (i = 0; i < PCI_NUM_RESOURCES; i++) { - struct resource *r = &dev->resource[i]; + for_each_pci_resource(dev, r, i, PCI_ALL_RES) { resource_size_t r_size; if (r->parent || (r->flags & mask) != type) continue; r_size = resource_size(r); -#ifdef CONFIG_PCI_IOV - /* put SRIOV requested res to the optional list */ - if (realloc_head && i >= PCI_IOV_RESOURCES && - i <= PCI_IOV_RESOURCE_END) { + + /* put SRIOV/ROM requested res to the optional list */ + if (realloc_head && (is_pci_iov_resource_idx(i) || + is_pci_rom_resource_idx(i))) { r->end = r->start - 1; add_to_list(realloc_head, dev, r, r_size, 0/* dont' care */); children_add_size += r_size; continue; } -#endif + /* For bridges size != alignment */ align = pci_resource_alignment(dev, r); order = __ffs(align) - 20; @@ -1202,9 +1218,7 @@ static void pci_bridge_release_resources(struct pci_bus *bus, IORESOURCE_PREFETCH; dev = bus->self; - for (idx = PCI_BRIDGE_RESOURCES; idx <= PCI_BRIDGE_RESOURCE_END; - idx++) { - r = &dev->resource[idx]; + for_each_pci_resource(dev, r, idx, PCI_BRIDGE_RES) { if ((r->flags & type_mask) != type) continue; if (!r->parent) @@ -1375,9 +1389,9 @@ static void __init pci_realloc_detect(void) for_each_pci_dev(dev) { int i; + struct resource *r; - for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++) { - struct resource *r = &dev->resource[i]; + for_each_pci_resource(dev, r, i, PCI_IOV_RES) { /* Not assigned, or rejected by kernel ? */ if (r->flags && !r->start) { diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 81b88bda79307b..1a004ef51c8c71 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -35,7 +35,7 @@ void pci_update_resource(struct pci_dev *dev, int resno) u32 new, check, mask; int reg; enum pci_bar_type type; - struct resource *res = dev->resource + resno; + struct resource *res = pci_dev_resource_n(dev, resno); /* * Ignore resources for unimplemented BARs and unused resource slots @@ -52,6 +52,21 @@ void pci_update_resource(struct pci_dev *dev, int resno) if (res->flags & IORESOURCE_PCI_FIXED) return; + if (resno >= PCI_NUM_RESOURCES) { + struct pci_dev_addon_resource *addon_res; + + addon_res = to_pci_dev_addon_resource(res); + reg = addon_res->reg_addr; + if (addon_res->ops) { + addon_res->ops->write(dev, res, reg); + return; + } + } else + reg = pci_resource_bar(dev, resno, &type); + + if (!reg) + return; + pcibios_resource_to_bus(dev, ®ion, res); new = region.start | (res->flags & PCI_REGION_FLAG_MASK); @@ -60,9 +75,6 @@ void pci_update_resource(struct pci_dev *dev, int resno) else mask = (u32)PCI_BASE_ADDRESS_MEM_MASK; - reg = pci_resource_bar(dev, resno, &type); - if (!reg) - return; if (type != pci_bar_unknown) { if (!(res->flags & IORESOURCE_ROM_ENABLE)) return; @@ -110,7 +122,7 @@ void pci_update_resource(struct pci_dev *dev, int resno) int pci_claim_resource(struct pci_dev *dev, int resource) { - struct resource *res = &dev->resource[resource]; + struct resource *res = pci_dev_resource_n(dev, resource); struct resource *root, *conflict; root = pci_find_parent_resource(dev, res); @@ -198,18 +210,19 @@ static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev, } static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev, - int resno, resource_size_t size, resource_size_t align) + int resno, resource_size_t size, resource_size_t align, + bool fit) { - struct resource *res = dev->resource + resno; + struct resource *res = pci_dev_resource_n(dev, resno); resource_size_t min; int ret; min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM; /* First, try exact prefetching match.. */ - ret = pci_bus_alloc_resource(bus, res, size, align, min, + ret = pci_bus_alloc_resource_fit(bus, res, size, align, min, IORESOURCE_PREFETCH, - pcibios_align_resource, dev); + pcibios_align_resource, dev, fit); if (ret < 0 && (res->flags & IORESOURCE_PREFETCH)) { /* @@ -218,22 +231,24 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev, * But a prefetching area can handle a non-prefetching * window (it will just not perform as well). */ - ret = pci_bus_alloc_resource(bus, res, size, align, min, 0, - pcibios_align_resource, dev); + ret = pci_bus_alloc_resource_fit(bus, res, size, align, min, 0, + pcibios_align_resource, dev, fit); } return ret; } static int _pci_assign_resource(struct pci_dev *dev, int resno, - resource_size_t size, resource_size_t min_align) + resource_size_t size, resource_size_t min_align, + bool fit) { - struct resource *res = dev->resource + resno; + struct resource *res = pci_dev_resource_n(dev, resno); struct pci_bus *bus; int ret; char *type; bus = dev->bus; - while ((ret = __pci_assign_resource(bus, dev, resno, size, min_align))) { + while ((ret = __pci_assign_resource(bus, dev, resno, size, + min_align, fit))) { if (!bus->parent || !bus->self->transparent) break; bus = bus->parent; @@ -257,9 +272,9 @@ static int _pci_assign_resource(struct pci_dev *dev, int resno, return ret; } -int pci_assign_resource(struct pci_dev *dev, int resno) +int pci_assign_resource_fit(struct pci_dev *dev, int resno, bool fit) { - struct resource *res = dev->resource + resno; + struct resource *res = pci_dev_resource_n(dev, resno); resource_size_t align, size; struct pci_bus *bus; int ret; @@ -273,7 +288,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno) bus = dev->bus; size = resource_size(res); - ret = _pci_assign_resource(dev, resno, size, align); + ret = _pci_assign_resource(dev, resno, size, align, fit); /* * If we failed to assign anything, let's try the address @@ -286,7 +301,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno) if (!ret) { res->flags &= ~IORESOURCE_STARTALIGN; dev_info(&dev->dev, "BAR %d: assigned %pR\n", resno, res); - if (resno < PCI_BRIDGE_RESOURCES) + if (!is_pci_bridge_resource_idx(resno)) pci_update_resource(dev, resno); } return ret; @@ -295,7 +310,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno) int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsize, resource_size_t min_align) { - struct resource *res = dev->resource + resno; + struct resource *res = pci_dev_resource_n(dev, resno); resource_size_t new_size; int ret; @@ -307,16 +322,21 @@ int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsiz /* already aligned with min_align */ new_size = resource_size(res) + addsize; - ret = _pci_assign_resource(dev, resno, new_size, min_align); + ret = _pci_assign_resource(dev, resno, new_size, min_align, false); if (!ret) { res->flags &= ~IORESOURCE_STARTALIGN; dev_info(&dev->dev, "BAR %d: reassigned %pR\n", resno, res); - if (resno < PCI_BRIDGE_RESOURCES) + if (!is_pci_bridge_resource_idx(resno)) pci_update_resource(dev, resno); } return ret; } +int pci_assign_resource(struct pci_dev *dev, int resno) +{ + return pci_assign_resource_fit(dev, resno, false); +} + int pci_enable_resources(struct pci_dev *dev, int mask) { u16 cmd, old_cmd; @@ -326,12 +346,10 @@ int pci_enable_resources(struct pci_dev *dev, int mask) pci_read_config_word(dev, PCI_COMMAND, &cmd); old_cmd = cmd; - for (i = 0; i < PCI_NUM_RESOURCES; i++) { + for_each_pci_resource(dev, r, i, PCI_ALL_RES) { if (!(mask & (1 << i))) continue; - r = &dev->resource[i]; - if (!(r->flags & (IORESOURCE_IO | IORESOURCE_MEM))) continue; if ((i == PCI_ROM_RESOURCE) && diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c index d6cc62cb4cf746..09707dcaa2080a 100644 --- a/drivers/pci/xen-pcifront.c +++ b/drivers/pci/xen-pcifront.c @@ -394,9 +394,7 @@ static int pcifront_claim_resource(struct pci_dev *dev, void *data) int i; struct resource *r; - for (i = 0; i < PCI_NUM_RESOURCES; i++) { - r = &dev->resource[i]; - + for_each_pci_resource(dev, r, i, PCI_ALL_RES) { if (!r->parent && r->start && r->flags) { dev_info(&pdev->xdev->dev, "claiming resource %s/%d\n", pci_name(dev), i); diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index 667678db115311..7e42bb4132e4c0 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -1064,79 +1064,6 @@ static void yenta_config_init(struct yenta_socket *socket) config_writew(socket, CB_BRIDGE_CONTROL, bridge); } -/** - * yenta_fixup_parent_bridge - Fix subordinate bus# of the parent bridge - * @cardbus_bridge: The PCI bus which the CardBus bridge bridges to - * - * Checks if devices on the bus which the CardBus bridge bridges to would be - * invisible during PCI scans because of a misconfigured subordinate number - * of the parent brige - some BIOSes seem to be too lazy to set it right. - * Does the fixup carefully by checking how far it can go without conflicts. - * See http://bugzilla.kernel.org/show_bug.cgi?id=2944 for more information. - */ -static void yenta_fixup_parent_bridge(struct pci_bus *cardbus_bridge) -{ - struct list_head *tmp; - unsigned char upper_limit; - /* - * We only check and fix the parent bridge: All systems which need - * this fixup that have been reviewed are laptops and the only bridge - * which needed fixing was the parent bridge of the CardBus bridge: - */ - struct pci_bus *bridge_to_fix = cardbus_bridge->parent; - - /* Check bus numbers are already set up correctly: */ - if (bridge_to_fix->busn_res.end >= cardbus_bridge->busn_res.end) - return; /* The subordinate number is ok, nothing to do */ - - if (!bridge_to_fix->parent) - return; /* Root bridges are ok */ - - /* 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); - /* - * If the silbling 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: - */ - if (silbling->busn_res.start > bridge_to_fix->busn_res.end - && silbling->busn_res.start <= upper_limit) - upper_limit = silbling->busn_res.start - 1; - } - - /* Show that the wanted subordinate number is not possible: */ - if (cardbus_bridge->busn_res.end > upper_limit) - dev_printk(KERN_WARNING, &cardbus_bridge->dev, - "Upper limit for fixing this " - "bridge's parent bridge: #%02x\n", upper_limit); - - /* If we have room to increase the bridge's subordinate number, */ - if (bridge_to_fix->busn_res.end < upper_limit) { - - /* use the highest number of the hidden bus, within limits */ - unsigned char subordinate_to_assign = - min_t(int, cardbus_bridge->busn_res.end, upper_limit); - - dev_printk(KERN_INFO, &bridge_to_fix->dev, - "Raising subordinate bus# of parent " - "bus (#%02x) from #%02x to #%02x\n", - bridge_to_fix->number, - (int)bridge_to_fix->busn_res.end, subordinate_to_assign); - - /* Save the new subordinate in the bus struct of the bridge */ - bridge_to_fix->busn_res.end = subordinate_to_assign; - - /* and update the PCI config space with the new subordinate */ - pci_write_config_byte(bridge_to_fix->self, - PCI_SUBORDINATE_BUS, bridge_to_fix->busn_res.end); - } -} - /* * Initialize a cardbus controller. Make sure we have a usable * interrupt, and that we can map the cardbus area. Fill in the @@ -1257,8 +1184,6 @@ static int __devinit yenta_probe(struct pci_dev *dev, const struct pci_device_id dev_printk(KERN_INFO, &dev->dev, "Socket status: %08x\n", cb_readl(socket, CB_SOCKET_STATE)); - yenta_fixup_parent_bridge(dev->subordinate); - /* Register it with the pcmcia layer.. */ ret = pcmcia_register_socket(&socket->socket); if (ret == 0) { diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 589e0e75efae2d..7eb3224291de8c 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -148,6 +148,14 @@ extern struct resource *insert_resource_conflict(struct resource *parent, struct extern int insert_resource(struct resource *parent, struct resource *new); extern void insert_resource_expand_to_fit(struct resource *root, struct resource *new); extern void arch_remove_reservations(struct resource *avail); +int allocate_resource_fit(struct resource *root, struct resource *new, + resource_size_t size, resource_size_t min, + resource_size_t max, resource_size_t align, + resource_size_t (*alignf)(void *, + const struct resource *, + resource_size_t, + resource_size_t), + void *alignf_data, bool fit); extern int allocate_resource(struct resource *root, struct resource *new, resource_size_t size, resource_size_t min, resource_size_t max, resource_size_t align, @@ -156,6 +164,13 @@ extern int allocate_resource(struct resource *root, struct resource *new, resource_size_t, resource_size_t), void *alignf_data); +int resource_shrink_parents_top(struct resource *b_res, + long size, struct resource *parent_res); +int probe_resource(struct resource *b_res, + struct resource *busn_res, + resource_size_t needed_size, struct resource **p, + int skip_nr, int flags); +void replace_resource(struct resource *old_res, struct resource *new_res); struct resource *lookup_resource(struct resource *root, resource_size_t start); int adjust_resource(struct resource *res, resource_size_t start, resource_size_t size); diff --git a/include/linux/pci.h b/include/linux/pci.h index 88ae2373add38e..4e52a45579a1cb 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -114,6 +114,29 @@ enum { DEVICE_COUNT_RESOURCE = PCI_NUM_RESOURCES, }; +static inline bool is_pci_std_resource_idx(int i) +{ + return i >= PCI_STD_RESOURCES && i <= PCI_STD_RESOURCE_END; +} + +static inline bool is_pci_rom_resource_idx(int i) +{ + return i == PCI_ROM_RESOURCE; +} + +static inline bool is_pci_iov_resource_idx(int i) +{ +#ifdef CONFIG_PCI_IOV + return i >= PCI_IOV_RESOURCES && i <= PCI_IOV_RESOURCE_END; +#endif + return false; +} + +static inline bool is_pci_bridge_resource_idx(int i) +{ + return i >= PCI_BRIDGE_RESOURCES && i <= PCI_BRIDGE_RESOURCE_END; +} + typedef int __bitwise pci_power_t; #define PCI_D0 ((pci_power_t __force) 0) @@ -307,6 +330,7 @@ struct pci_dev { */ unsigned int irq; struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */ + struct list_head addon_resources; /* addon I/O and memory resource */ /* These fields are used by common fixups */ unsigned int transparent:1; /* Transparent PCI bridge */ @@ -357,6 +381,55 @@ struct pci_dev { #endif }; +struct resource_ops { + int (*read)(struct pci_dev *dev, struct resource *res, int addr); + int (*write)(struct pci_dev *dev, struct resource *res, int addr); +}; + +struct pci_dev_addon_resource { + struct list_head list; + int reg_addr; + int size; + struct resource res; + struct resource_ops *ops; +}; +#define to_pci_dev_addon_resource(n) \ + container_of(n, struct pci_dev_addon_resource, res) + +struct resource *pci_dev_resource_n(struct pci_dev *dev, int n); +int pci_dev_resource_idx(struct pci_dev *dev, struct resource *res); +struct pci_dev_addon_resource *add_pci_dev_addon_fixed_resource( + struct pci_dev *dev, int start, int size, int flags, + int addr, char *name); +struct pci_dev_addon_resource *add_pci_dev_addon_resource(struct pci_dev *dev, + int addr, int size, struct resource_ops *ops, char *name); + +#define PCI_STD_RES (1<<0) +#define PCI_ROM_RES (1<<1) +#define PCI_IOV_RES (1<<2) +#define PCI_BRIDGE_RES (1<<3) +#define PCI_RES_BLOCK_NUM 4 +/* addon does not need use bitmap ...*/ +#define PCI_ADDON_RES (1<<4) + +#define PCI_ALL_RES (PCI_STD_RES | PCI_ROM_RES | PCI_BRIDGE_RES | PCI_IOV_RES | PCI_ADDON_RES) +#define PCI_NOSTD_RES (PCI_ALL_RES & ~PCI_STD_RES) +#define PCI_NOIOV_RES (PCI_ALL_RES & ~PCI_IOV_RES) +#define PCI_NOROM_RES (PCI_ALL_RES & ~PCI_ROM_RES) +#define PCI_NOBRIDGE_RES (PCI_ALL_RES & ~PCI_BRIDGE_RES) +#define PCI_STD_ROM_RES (PCI_STD_RES | PCI_ROM_RES) +#define PCI_STD_IOV_RES (PCI_STD_RES | PCI_IOV_RES) +#define PCI_STD_ROM_IOV_RES (PCI_STD_RES | PCI_ROM_RES | PCI_IOV_RES) + +int pci_next_resource_idx(int i, int flag); + +#define for_each_pci_resource(dev, res, i, flag) \ + for (i = pci_next_resource_idx(-1, flag), \ + res = pci_dev_resource_n(dev, i); \ + res; \ + i = pci_next_resource_idx(i, flag), \ + res = pci_dev_resource_n(dev, i)) + static inline struct pci_dev *pci_physfn(struct pci_dev *dev) { #ifdef CONFIG_PCI_IOV @@ -693,10 +766,15 @@ void pci_fixup_cardbus(struct pci_bus *); /* Generic PCI functions used internally */ +void __pcibios_resource_to_bus(struct pci_bus *bus, + struct pci_bus_region *region, + struct resource *res); void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, struct resource *res); void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, struct pci_bus_region *region); +resource_size_t pcibios_bus_addr_to_res(struct pci_bus *bus, int flags, + resource_size_t addr); void pcibios_scan_specific_bus(int busn); extern struct pci_bus *pci_find_bus(int domain, int busnr); void pci_bus_add_devices(const struct pci_bus *bus); @@ -906,6 +984,7 @@ int __pci_reset_function_locked(struct pci_dev *dev); int pci_reset_function(struct pci_dev *dev); void pci_update_resource(struct pci_dev *dev, int resno); int __must_check pci_assign_resource(struct pci_dev *dev, int i); +int __must_check pci_assign_resource_fit(struct pci_dev *dev, int i, bool fit); int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align); int pci_select_bars(struct pci_dev *dev, unsigned long flags); @@ -1023,6 +1102,15 @@ int __must_check pci_bus_alloc_resource(struct pci_bus *bus, resource_size_t, resource_size_t), void *alignf_data); +int __must_check pci_bus_alloc_resource_fit(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 (*alignf)(void *, + const struct resource *, + resource_size_t, + resource_size_t), + void *alignf_data, bool fit); void pci_enable_bridges(struct pci_bus *bus); /* Proper probing supporting hot-pluggable devices */ @@ -1427,15 +1515,11 @@ static inline struct pci_dev *pci_dev_get(struct pci_dev *dev) #include <asm/pci.h> -#ifndef PCIBIOS_MAX_MEM_32 -#define PCIBIOS_MAX_MEM_32 (-1) -#endif - /* these helpers provide future and backwards compatibility * for accessing popular PCI BAR info */ -#define pci_resource_start(dev, bar) ((dev)->resource[(bar)].start) -#define pci_resource_end(dev, bar) ((dev)->resource[(bar)].end) -#define pci_resource_flags(dev, bar) ((dev)->resource[(bar)].flags) +#define pci_resource_start(dev, bar) (pci_dev_resource_n(dev, (bar))->start) +#define pci_resource_end(dev, bar) (pci_dev_resource_n(dev, (bar))->end) +#define pci_resource_flags(dev, bar) (pci_dev_resource_n(dev, (bar))->flags) #define pci_resource_len(dev,bar) \ ((pci_resource_start((dev), (bar)) == 0 && \ pci_resource_end((dev), (bar)) == \ diff --git a/kernel/resource.c b/kernel/resource.c index 34d45886ee8429..9084e34c837a1c 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -46,6 +46,7 @@ struct resource_constraint { resource_size_t (*alignf)(void *, const struct resource *, resource_size_t, resource_size_t); void *alignf_data; + bool fit; }; static DEFINE_RWLOCK(resource_lock); @@ -437,7 +438,7 @@ static int __find_resource(struct resource *root, struct resource *old, alloc.end = alloc.start + size - 1; if (resource_contains(&avail, &alloc)) { new->start = alloc.start; - new->end = alloc.end; + new->end = !old ? avail.end : alloc.end; return 0; } } @@ -452,6 +453,10 @@ next: if (!this || this->end == root->end) return -EBUSY; } +struct avail_resource { + struct list_head list; + struct resource res; +}; /* * Find empty slot in the resource tree given range and alignment. */ @@ -459,7 +464,67 @@ static int find_resource(struct resource *root, struct resource *new, resource_size_t size, struct resource_constraint *constraint) { - return __find_resource(root, NULL, new, size, constraint); + int ret = -1; + LIST_HEAD(head); + struct avail_resource *avail, *tmp; + resource_size_t avail_start = 0, avail_size = -1ULL; + + if (!constraint->fit) { + ret = __find_resource(root, NULL, new, size, constraint); + if (!ret) + new->end = new->start + size - 1; + return ret; + } + +again: + /* find all suitable ones */ + avail = kzalloc(sizeof(*avail), GFP_KERNEL); + if (!avail) + goto out; + + avail->res.start = new->start; + avail->res.end = new->end; + avail->res.flags = new->flags; + ret = __find_resource(root, NULL, &avail->res, size, constraint); + if (ret || __request_resource(root, &avail->res)) { + ret = -EBUSY; + kfree(avail); + goto out; + } + /* add to the list */ + list_add(&avail->list, &head); + goto again; + +out: + /* pick up the smallest one and delete the list */ + list_for_each_entry_safe(avail, tmp, &head, list) { + if (resource_size(&avail->res) < avail_size) { + avail_size = resource_size(&avail->res); + avail_start = avail->res.start; + ret = 0; + } + list_del(&avail->list); + __release_resource(&avail->res); + kfree(avail); + } + + if (!ret) { + /* compare which one have max order */ + new->start = round_down(avail_start + avail_size - size, + constraint->align); + new->end = avail_start + avail_size - 1; + new->start = constraint->alignf(constraint->alignf_data, new, + size, constraint->align); + new->end = new->start + size - 1; + + if (new->start < avail_start || + new->end > (avail_start + avail_size - 1) || + __ffs64(new->start) >= __ffs64(avail_start)) { + new->start = avail_start; + new->end = new->start + size - 1; + } + } + return ret; } /** @@ -472,7 +537,7 @@ static int find_resource(struct resource *root, struct resource *new, * @newsize: new size of the resource descriptor * @constraint: the size and alignment constraints to be met. */ -int reallocate_resource(struct resource *root, struct resource *old, +static int __reallocate_resource(struct resource *root, struct resource *old, resource_size_t newsize, struct resource_constraint *constraint) { @@ -480,7 +545,6 @@ int reallocate_resource(struct resource *root, struct resource *old, struct resource new = *old; struct resource *conflict; - write_lock(&resource_lock); if ((err = __find_resource(root, old, &new, newsize, constraint))) goto out; @@ -506,11 +570,20 @@ int reallocate_resource(struct resource *root, struct resource *old, BUG_ON(conflict); } out: - write_unlock(&resource_lock); return err; } +int reallocate_resource(struct resource *root, struct resource *old, + resource_size_t newsize, + struct resource_constraint *constraint) +{ + int ret; + write_lock(&resource_lock); + ret = __reallocate_resource(root, old, newsize, constraint); + write_unlock(&resource_lock); + return ret; +} /** * allocate_resource - allocate empty slot in the resource tree given range & alignment. * The resource will be reallocated with a new size if it was already allocated @@ -523,14 +596,14 @@ out: * @alignf: alignment function, optional, called if not NULL * @alignf_data: arbitrary data to pass to the @alignf function */ -int allocate_resource(struct resource *root, struct resource *new, +static int __allocate_resource(struct resource *root, struct resource *new, resource_size_t size, resource_size_t min, resource_size_t max, resource_size_t align, resource_size_t (*alignf)(void *, const struct resource *, resource_size_t, resource_size_t), - void *alignf_data) + void *alignf_data, bool fit) { int err; struct resource_constraint constraint; @@ -543,21 +616,50 @@ int allocate_resource(struct resource *root, struct resource *new, constraint.align = align; constraint.alignf = alignf; constraint.alignf_data = alignf_data; + constraint.fit = fit; - if ( new->parent ) { + if (new->parent) { /* resource is already allocated, try reallocating with the new constraints */ - return reallocate_resource(root, new, size, &constraint); + return __reallocate_resource(root, new, size, &constraint); } - write_lock(&resource_lock); err = find_resource(root, new, size, &constraint); if (err >= 0 && __request_resource(root, new)) err = -EBUSY; - write_unlock(&resource_lock); + return err; } +int allocate_resource_fit(struct resource *root, struct resource *new, + resource_size_t size, resource_size_t min, + resource_size_t max, resource_size_t align, + resource_size_t (*alignf)(void *, + const struct resource *, + resource_size_t, + resource_size_t), + void *alignf_data, bool fit) +{ + int ret; + + write_lock(&resource_lock); + ret = __allocate_resource(root, new, size, min, max, align, + alignf, alignf_data, fit); + write_unlock(&resource_lock); + return ret; +} +int allocate_resource(struct resource *root, struct resource *new, + resource_size_t size, resource_size_t min, + resource_size_t max, resource_size_t align, + resource_size_t (*alignf)(void *, + const struct resource *, + resource_size_t, + resource_size_t), + void *alignf_data) +{ + return allocate_resource_fit(root, new, size, min, max, + align, alignf, alignf_data, false); +} EXPORT_SYMBOL(allocate_resource); /** @@ -716,14 +818,13 @@ void insert_resource_expand_to_fit(struct resource *root, struct resource *new) * arguments. Returns 0 on success, -EBUSY if it can't fit. * Existing children of the resource are assumed to be immutable. */ -int adjust_resource(struct resource *res, resource_size_t start, resource_size_t size) +static int __adjust_resource(struct resource *res, resource_size_t start, + resource_size_t size) { struct resource *tmp, *parent = res->parent; resource_size_t end = start + size - 1; int result = -EBUSY; - write_lock(&resource_lock); - if (!parent) goto skip; @@ -751,9 +852,19 @@ skip: result = 0; out: - write_unlock(&resource_lock); return result; } +int adjust_resource(struct resource *res, resource_size_t start, + resource_size_t size) +{ + int ret; + + write_lock(&resource_lock); + ret = __adjust_resource(res, start, size); + write_unlock(&resource_lock); + + return ret; +} EXPORT_SYMBOL(adjust_resource); static void __init __reserve_region_with_split(struct resource *root, @@ -986,6 +1097,176 @@ void __release_region(struct resource *parent, resource_size_t start, } EXPORT_SYMBOL(__release_region); +static int __resource_shrink_parents_top(struct resource *b_res, + long size, struct resource *parent_res) +{ + struct resource *res = b_res; + + if (size <= 0) + return 0; + + while (res && res != parent_res) { + if (__adjust_resource(res, res->start, + resource_size(res) - size)) { + struct resource *tmp = b_res; + + /* roll back */ + while (tmp != res) { + __adjust_resource(tmp, tmp->start, + resource_size(tmp) + size); + tmp = tmp->parent; + } + + return -EBUSY; + + } + res = res->parent; + } + + return 0; +} + +int resource_shrink_parents_top(struct resource *b_res, + long size, struct resource *parent_res) +{ + int ret; + + write_lock(&resource_lock); + ret = __resource_shrink_parents_top(b_res, size, parent_res); + write_unlock(&resource_lock); + + return ret; +} + +static resource_size_t __find_res_under_top_free_size(struct resource *res, + int skip_nr) +{ + resource_size_t n_size; + + /* + * find out free number below res->end that we can use. + * res->start to res->start + skip_nr - 1 can not be used. + */ + if (res->child) { + struct resource *child = res->child; + + while (child->sibling) + child = child->sibling; + + /* check if children cover skip_nr */ + if (child->end - res->start + 1 >= skip_nr) + return res->end - child->end; + } + + n_size = resource_size(res); + if (n_size <= skip_nr) + return 0; + return n_size - skip_nr; +} + +/** + * probe_resource - Probe resource in parent resource. + * @b_res: parent resource descriptor + * @busn_res: return probed resource + * @needed_size: target size + * @p: pointer to farest parent that we extend the top + * @skip_nr: number in b_res start that we need to skip. + * @stop_flags: flags for stopping extend parent res + * + * will try to allocate resource in b_res, if can not find the range + * will try to extend parent resources' top. + */ +int probe_resource(struct resource *b_res, + struct resource *busn_res, + resource_size_t needed_size, struct resource **p, + int skip_nr, int stop_flags) +{ + int ret; + resource_size_t n_size; + struct resource *parent_res; + + write_lock(&resource_lock); + /* + * We first try to allocate range in b_res that + * we can use in b_res directly. + * we also need to skip skip_nr from start of b_res. + */ + memset(busn_res, 0, sizeof(struct resource)); + ret = __allocate_resource(b_res, busn_res, needed_size, + b_res->start + skip_nr, b_res->end, + 1, NULL, NULL, false); + if (!ret) { + *p = NULL; + goto out; + } + + /* Try to extend the top of parent resources to meet needed_size */ + + /* b_res could be root bus resource and can not be extended */ + if (b_res->flags & stop_flags) + goto out; + + /* find out free range under top at first */ + n_size = __find_res_under_top_free_size(b_res, skip_nr); + + /* Probe extended range above top */ + memset(busn_res, 0, sizeof(struct resource)); + parent_res = b_res; + while (parent_res && !(parent_res->flags & stop_flags)) { + ret = __adjust_resource(parent_res, parent_res->start, + resource_size(parent_res) + (needed_size - n_size)); + if (!ret) { + struct resource *res = b_res; + + /* save parent_res, we need it as stopper later */ + *p = parent_res->parent; + + /* extend parent resources top */ + while (res && res != parent_res) { + res->end += needed_size - n_size; + res = res->parent; + } + + ret = __allocate_resource(b_res, busn_res, needed_size, + b_res->start + skip_nr, b_res->end, + 1, NULL, NULL, false); + /* ret must be 0 here*/ + goto out; + } + parent_res = parent_res->parent; + } + +out: + write_unlock(&resource_lock); + + return ret; +} + +/* replace old with new in the resource tree */ +void replace_resource(struct resource *old, struct resource *new) +{ + struct resource *parent, *tmp, *p; + + write_lock(&resource_lock); + new->start = old->start; + new->end = old->end; + new->flags = old->flags; + + p = old->child; + while (p) { + tmp = p; + p = p->sibling; + __release_resource(tmp); + __request_resource(new, tmp); + } + parent = old->parent; + if (parent) { + __release_resource(old); + __request_resource(parent, new); + } + write_unlock(&resource_lock); +} + /* * Managed region resource */ |