aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/kernel-parameters.txt6
-rw-r--r--arch/x86/include/asm/pci.h1
-rw-r--r--arch/x86/include/asm/pci_x86.h1
-rw-r--r--arch/x86/pci/common.c6
-rw-r--r--arch/x86/pci/i386.c66
-rw-r--r--arch/x86/pci/mrst.c7
-rw-r--r--drivers/char/agp/intel-gtt.c14
-rw-r--r--drivers/pci/bus.c74
-rw-r--r--drivers/pci/host-bridge.c48
-rw-r--r--drivers/pci/hotplug-pci.c12
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c4
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c5
-rw-r--r--drivers/pci/iov.c31
-rw-r--r--drivers/pci/pci-driver.c6
-rw-r--r--drivers/pci/pci.c32
-rw-r--r--drivers/pci/pci.h5
-rw-r--r--drivers/pci/probe.c491
-rw-r--r--drivers/pci/quirks.c256
-rw-r--r--drivers/pci/remove.c5
-rw-r--r--drivers/pci/setup-bus.c106
-rw-r--r--drivers/pci/setup-res.c66
-rw-r--r--drivers/pci/xen-pcifront.c4
-rw-r--r--drivers/pcmcia/yenta_socket.c75
-rw-r--r--include/linux/ioport.h15
-rw-r--r--include/linux/pci.h98
-rw-r--r--kernel/resource.c311
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, &region, 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, &region);
+
+ 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, &region);
+ 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, &region);
+ 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, &region);
+ 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, &region);
- quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES, "ali7101 ACPI");
- pci_read_config_word(dev, 0xE2, &region);
- 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, &region);
- quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES, "PIIX4 ACPI");
- pci_read_config_dword(dev, 0x90, &region);
- 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, &region);
- 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, &region);
- 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, &region);
- 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, &region);
- 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, &region);
- 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, &region, 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
*/