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