104 lines
3.7 KiB
Diff
104 lines
3.7 KiB
Diff
|
From: Bjorn Helgaas <bjorn.helgaas@hp.com>
|
||
|
Date: Thu, 15 Jul 2010 15:41:42 +0000 (-0600)
|
||
|
Subject: PCI: fall back to original BIOS BAR addresses
|
||
|
X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=58c84eda07560a6b75b03e8d3b26d6eddfc14011
|
||
|
|
||
|
PCI: fall back to original BIOS BAR addresses
|
||
|
|
||
|
If we fail to assign resources to a PCI BAR, this patch makes us try the
|
||
|
original address from BIOS rather than leaving it disabled.
|
||
|
|
||
|
Linux tries to make sure all PCI device BARs are inside the upstream
|
||
|
PCI host bridge or P2P bridge apertures, reassigning BARs if necessary.
|
||
|
Windows does similar reassignment.
|
||
|
|
||
|
Before this patch, if we could not move a BAR into an aperture, we left
|
||
|
the resource unassigned, i.e., at address zero. Windows leaves such BARs
|
||
|
at the original BIOS addresses, and this patch makes Linux do the same.
|
||
|
|
||
|
This is a bit ugly because we disable the resource long before we try to
|
||
|
reassign it, so we have to keep track of the BIOS BAR address somewhere.
|
||
|
For lack of a better place, I put it in the struct pci_dev.
|
||
|
|
||
|
I think it would be cleaner to attempt the assignment immediately when the
|
||
|
claim fails, so we could easily remember the original address. But we
|
||
|
currently claim motherboard resources in the middle, after attempting to
|
||
|
claim PCI resources and before assigning new PCI resources, and changing
|
||
|
that is a fairly big job.
|
||
|
|
||
|
Addresses https://bugzilla.kernel.org/show_bug.cgi?id=16263
|
||
|
|
||
|
Reported-by: Andrew <nitr0@seti.kr.ua>
|
||
|
Tested-by: Andrew <nitr0@seti.kr.ua>
|
||
|
Signed-off-by: Bjorn Helgaas <bjorn.helgaas@hp.com>
|
||
|
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
|
||
|
---
|
||
|
|
||
|
diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c
|
||
|
index 6fdb3ec..5525309 100644
|
||
|
--- a/arch/x86/pci/i386.c
|
||
|
+++ b/arch/x86/pci/i386.c
|
||
|
@@ -184,6 +184,7 @@ static void __init pcibios_allocate_resources(int pass)
|
||
|
idx, r, disabled, pass);
|
||
|
if (pci_claim_resource(dev, idx) < 0) {
|
||
|
/* We'll assign a new address later */
|
||
|
+ dev->fw_addr[idx] = r->start;
|
||
|
r->end -= r->start;
|
||
|
r->start = 0;
|
||
|
}
|
||
|
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
|
||
|
index 92379e2..2aaa131 100644
|
||
|
--- a/drivers/pci/setup-res.c
|
||
|
+++ b/drivers/pci/setup-res.c
|
||
|
@@ -156,6 +156,38 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
|
||
|
pcibios_align_resource, dev);
|
||
|
}
|
||
|
|
||
|
+ if (ret < 0 && dev->fw_addr[resno]) {
|
||
|
+ struct resource *root, *conflict;
|
||
|
+ resource_size_t start, end;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * If we failed to assign anything, let's try the address
|
||
|
+ * where firmware left it. That at least has a chance of
|
||
|
+ * working, which is better than just leaving it disabled.
|
||
|
+ */
|
||
|
+
|
||
|
+ if (res->flags & IORESOURCE_IO)
|
||
|
+ root = &ioport_resource;
|
||
|
+ else
|
||
|
+ root = &iomem_resource;
|
||
|
+
|
||
|
+ start = res->start;
|
||
|
+ end = res->end;
|
||
|
+ res->start = dev->fw_addr[resno];
|
||
|
+ res->end = res->start + size - 1;
|
||
|
+ dev_info(&dev->dev, "BAR %d: trying firmware assignment %pR\n",
|
||
|
+ resno, res);
|
||
|
+ conflict = request_resource_conflict(root, res);
|
||
|
+ if (conflict) {
|
||
|
+ dev_info(&dev->dev,
|
||
|
+ "BAR %d: %pR conflicts with %s %pR\n", resno,
|
||
|
+ res, conflict->name, conflict);
|
||
|
+ res->start = start;
|
||
|
+ res->end = end;
|
||
|
+ } else
|
||
|
+ ret = 0;
|
||
|
+ }
|
||
|
+
|
||
|
if (!ret) {
|
||
|
res->flags &= ~IORESOURCE_STARTALIGN;
|
||
|
dev_info(&dev->dev, "BAR %d: assigned %pR\n", resno, res);
|
||
|
diff --git a/include/linux/pci.h b/include/linux/pci.h
|
||
|
index 7cb0084..f26fda7 100644
|
||
|
--- a/include/linux/pci.h
|
||
|
+++ b/include/linux/pci.h
|
||
|
@@ -288,6 +288,7 @@ struct pci_dev {
|
||
|
*/
|
||
|
unsigned int irq;
|
||
|
struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */
|
||
|
+ resource_size_t fw_addr[DEVICE_COUNT_RESOURCE]; /* FW-assigned addr */
|
||
|
|
||
|
/* These fields are used by common fixups */
|
||
|
unsigned int transparent:1; /* Transparent PCI bridge */
|