diff options
| -rw-r--r-- | arch/x86/pci/i386.c | 1 | ||||
| -rw-r--r-- | drivers/pci/setup-res.c | 32 | ||||
| -rw-r--r-- | include/linux/pci.h | 1 |
3 files changed, 34 insertions, 0 deletions
diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index 6fdb3ec30c31..55253095be84 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) | |||
| 184 | idx, r, disabled, pass); | 184 | idx, r, disabled, pass); |
| 185 | if (pci_claim_resource(dev, idx) < 0) { | 185 | if (pci_claim_resource(dev, idx) < 0) { |
| 186 | /* We'll assign a new address later */ | 186 | /* We'll assign a new address later */ |
| 187 | dev->fw_addr[idx] = r->start; | ||
| 187 | r->end -= r->start; | 188 | r->end -= r->start; |
| 188 | r->start = 0; | 189 | r->start = 0; |
| 189 | } | 190 | } |
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 92379e2d37e7..2aaa13150de3 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, | |||
| 156 | pcibios_align_resource, dev); | 156 | pcibios_align_resource, dev); |
| 157 | } | 157 | } |
| 158 | 158 | ||
| 159 | if (ret < 0 && dev->fw_addr[resno]) { | ||
| 160 | struct resource *root, *conflict; | ||
| 161 | resource_size_t start, end; | ||
| 162 | |||
| 163 | /* | ||
| 164 | * If we failed to assign anything, let's try the address | ||
| 165 | * where firmware left it. That at least has a chance of | ||
| 166 | * working, which is better than just leaving it disabled. | ||
| 167 | */ | ||
| 168 | |||
| 169 | if (res->flags & IORESOURCE_IO) | ||
| 170 | root = &ioport_resource; | ||
| 171 | else | ||
| 172 | root = &iomem_resource; | ||
| 173 | |||
| 174 | start = res->start; | ||
| 175 | end = res->end; | ||
| 176 | res->start = dev->fw_addr[resno]; | ||
| 177 | res->end = res->start + size - 1; | ||
| 178 | dev_info(&dev->dev, "BAR %d: trying firmware assignment %pR\n", | ||
| 179 | resno, res); | ||
| 180 | conflict = request_resource_conflict(root, res); | ||
| 181 | if (conflict) { | ||
| 182 | dev_info(&dev->dev, | ||
| 183 | "BAR %d: %pR conflicts with %s %pR\n", resno, | ||
| 184 | res, conflict->name, conflict); | ||
| 185 | res->start = start; | ||
| 186 | res->end = end; | ||
| 187 | } else | ||
| 188 | ret = 0; | ||
| 189 | } | ||
| 190 | |||
| 159 | if (!ret) { | 191 | if (!ret) { |
| 160 | res->flags &= ~IORESOURCE_STARTALIGN; | 192 | res->flags &= ~IORESOURCE_STARTALIGN; |
| 161 | dev_info(&dev->dev, "BAR %d: assigned %pR\n", resno, res); | 193 | dev_info(&dev->dev, "BAR %d: assigned %pR\n", resno, res); |
diff --git a/include/linux/pci.h b/include/linux/pci.h index 7cb00845f150..f26fda76b87f 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h | |||
| @@ -288,6 +288,7 @@ struct pci_dev { | |||
| 288 | */ | 288 | */ |
| 289 | unsigned int irq; | 289 | unsigned int irq; |
| 290 | struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */ | 290 | struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */ |
| 291 | resource_size_t fw_addr[DEVICE_COUNT_RESOURCE]; /* FW-assigned addr */ | ||
| 291 | 292 | ||
| 292 | /* These fields are used by common fixups */ | 293 | /* These fields are used by common fixups */ |
| 293 | unsigned int transparent:1; /* Transparent PCI bridge */ | 294 | unsigned int transparent:1; /* Transparent PCI bridge */ |
