| 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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 |
| |
| |
| @@ -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 */ |