Commit a975072b authored by Matthew Wilcox's avatar Matthew Wilcox Committed by Greg Kroah-Hartman

[PATCH] PCI Hotplug: Rewrite acpiphp detect_used_resource

There are two unrelated problems in acpiphp that are fixed by this patch.
First, acpiphp can be a module, so it is unsafe to probe the BARs of each
device while it initialises -- the device may be active at the time.
Second, it does not know about PCI-PCI bridge registers and so it reads
garbage for the last 4 registers of the PCI-PCI bridge card and doesn't
take into account the ranges that are forwarded by the bridge.

This patch avoids all that by using the struct resources embedded in
the pci_dev.  Note that we no longer need to recurse as all the devices
on the other side of a PCI-PCI bridge have their resources entirely
contained within the PCI-PCI bridge's ranges.
parent 4e3b369f
...@@ -198,106 +198,42 @@ static int init_config_space (struct acpiphp_func *func) ...@@ -198,106 +198,42 @@ static int init_config_space (struct acpiphp_func *func)
/* detect_used_resource - subtract resource under dev from bridge */ /* detect_used_resource - subtract resource under dev from bridge */
static int detect_used_resource (struct acpiphp_bridge *bridge, struct pci_dev *dev) static int detect_used_resource (struct acpiphp_bridge *bridge, struct pci_dev *dev)
{ {
u32 bar, len;
u64 base;
u32 address[] = {
PCI_BASE_ADDRESS_0,
PCI_BASE_ADDRESS_1,
PCI_BASE_ADDRESS_2,
PCI_BASE_ADDRESS_3,
PCI_BASE_ADDRESS_4,
PCI_BASE_ADDRESS_5,
0
};
int count; int count;
struct pci_resource *res;
dbg("Device %s\n", pci_name(dev)); dbg("Device %s\n", pci_name(dev));
for (count = 0; address[count]; count++) { /* for 6 BARs */ for (count = 0; count < DEVICE_COUNT_RESOURCE; count++) {
pci_read_config_dword(dev, address[count], &bar); struct pci_resource *res;
struct pci_resource **head;
unsigned long base = dev->resource[count].start;
unsigned long len = dev->resource[count].end - base + 1;
unsigned long flags = dev->resource[count].flags;
if (!bar) /* This BAR is not implemented */ if (!flags)
continue; continue;
pci_write_config_dword(dev, address[count], 0xFFFFFFFF); dbg("BAR[%d] 0x%lx - 0x%lx (0x%lx)\n", count, base,
pci_read_config_dword(dev, address[count], &len); base + len - 1, flags);
if (len & PCI_BASE_ADDRESS_SPACE_IO) { if (flags & IORESOURCE_IO) {
/* This is IO */ head = &bridge->io_head;
base = bar & 0xFFFFFFFC; } else if (flags & IORESOURCE_PREFETCH) {
len = len & (PCI_BASE_ADDRESS_IO_MASK & 0xFFFF); head = &bridge->p_mem_head;
len = len & ~(len - 1);
dbg("BAR[%d] %08x - %08x (IO)\n", count, (u32)base, (u32)base + len - 1);
spin_lock(&bridge->res_lock);
res = acpiphp_get_resource_with_base(&bridge->io_head, base, len);
spin_unlock(&bridge->res_lock);
if (res)
kfree(res);
} else { } else {
/* This is Memory */ head = &bridge->mem_head;
base = bar & 0xFFFFFFF0;
if (len & PCI_BASE_ADDRESS_MEM_PREFETCH) {
/* pfmem */
len &= 0xFFFFFFF0;
len = ~len + 1;
if (len & PCI_BASE_ADDRESS_MEM_TYPE_64) { /* takes up another dword */
dbg("prefetch mem 64\n");
count += 1;
} }
dbg("BAR[%d] %08x - %08x (PMEM)\n", count, (u32)base, (u32)base + len - 1);
spin_lock(&bridge->res_lock);
res = acpiphp_get_resource_with_base(&bridge->p_mem_head, base, len);
spin_unlock(&bridge->res_lock);
if (res)
kfree(res);
} else {
/* regular memory */
len &= 0xFFFFFFF0;
len = ~len + 1;
if (len & PCI_BASE_ADDRESS_MEM_TYPE_64) {
/* takes up another dword */
dbg("mem 64\n");
count += 1;
}
dbg("BAR[%d] %08x - %08x (MEM)\n", count, (u32)base, (u32)base + len - 1);
spin_lock(&bridge->res_lock); spin_lock(&bridge->res_lock);
res = acpiphp_get_resource_with_base(&bridge->mem_head, base, len); res = acpiphp_get_resource_with_base(head, base, len);
spin_unlock(&bridge->res_lock); spin_unlock(&bridge->res_lock);
if (res) if (res)
kfree(res); kfree(res);
} }
}
pci_write_config_dword(dev, address[count], bar);
}
return 0; return 0;
} }
/* detect_pci_resource_bus - subtract resource under pci_bus */
static void detect_used_resource_bus(struct acpiphp_bridge *bridge, struct pci_bus *bus)
{
struct list_head *l;
struct pci_dev *dev;
list_for_each (l, &bus->devices) {
dev = pci_dev_b(l);
detect_used_resource(bridge, dev);
/* XXX recursive call */
if (dev->subordinate)
detect_used_resource_bus(bridge, dev->subordinate);
}
}
/** /**
* acpiphp_detect_pci_resource - detect resources under bridge * acpiphp_detect_pci_resource - detect resources under bridge
* @bridge: detect all resources already used under this bridge * @bridge: detect all resources already used under this bridge
...@@ -306,7 +242,13 @@ static void detect_used_resource_bus(struct acpiphp_bridge *bridge, struct pci_b ...@@ -306,7 +242,13 @@ static void detect_used_resource_bus(struct acpiphp_bridge *bridge, struct pci_b
*/ */
int acpiphp_detect_pci_resource (struct acpiphp_bridge *bridge) int acpiphp_detect_pci_resource (struct acpiphp_bridge *bridge)
{ {
detect_used_resource_bus(bridge, bridge->pci_bus); struct list_head *l;
struct pci_dev *dev;
list_for_each (l, &bridge->pci_bus->devices) {
dev = pci_dev_b(l);
detect_used_resource(bridge, dev);
}
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment