[PARISC] Truncate overlapping PAT PDC reported ranges

Deal with overlapping LBA MMIO resources,

rp3440 PDC BUG: PDC reports lmmio range for the last rope that overlaps
with the CPU HPA. Console output was:

...
Found devices:
1. Storm Peak Fast at 0xfffffffffe798000 [152] { 0, 0x0, 0x889, 0x00004 }
2. Storm Peak Fast at 0xfffffffffe799000 [153] { 0, 0x0, 0x889, 0x00004 }
...
FAILED: lba_fixup_bus() request for lmmio_space
[fffffffff0000000/fffffffffecffffe]

Output is now:

LBA: Truncating lmmio_space [fffffffff0000000/fffffffffecffffe] to
[fffffffff0000000,fffffffffe797fff]

My only concern with this patch is how C8000 (PAT PDC) will report
elmmio ranges when a gfx card is installed. I'll have to test this
another day.

Signed-off-by: Grant Grundler <grundler@parisc-linux.org>
Signed-off-by: James Bottomley <jejb@parisc-linux.org>
Signed-off-by: Matthew Wilcox <willy@parisc-linux.org>
Signed-off-by: Kyle McMartin <kyle@parisc-linux.org>
This commit is contained in:
Grant Grundler 2006-01-10 20:47:56 -05:00 committed by Kyle McMartin
parent 110957f0e5
commit 6ca45a24cc

View file

@ -695,11 +695,71 @@ lba_claim_dev_resources(struct pci_dev *dev)
}
}
}
#else
#define lba_claim_dev_resources(dev)
#endif
/*
* truncate_pat_collision: Deal with overlaps or outright collisions
* between PAT PDC reported ranges.
*
* Broken PA8800 firmware will report lmmio range that
* overlaps with CPU HPA. Just truncate the lmmio range.
*
* BEWARE: conflicts with this lmmio range may be an
* elmmio range which is pointing down another rope.
*
* FIXME: only deals with one collision per range...theoretically we
* could have several. Supporting more than one collision will get messy.
*/
static unsigned long
truncate_pat_collision(struct resource *root, struct resource *new)
{
unsigned long start = new->start;
unsigned long end = new->end;
struct resource *tmp = root->child;
if (end <= start || start < root->start || !tmp)
return 0;
/* find first overlap */
while (tmp && tmp->end < start)
tmp = tmp->sibling;
/* no entries overlap */
if (!tmp) return 0;
/* found one that starts behind the new one
** Don't need to do anything.
*/
if (tmp->start >= end) return 0;
if (tmp->start <= start) {
/* "front" of new one overlaps */
new->start = tmp->end + 1;
if (tmp->end >= end) {
/* AACCKK! totally overlaps! drop this range. */
return 1;
}
}
if (tmp->end < end ) {
/* "end" of new one overlaps */
new->end = tmp->start - 1;
}
printk(KERN_WARNING "LBA: Truncating lmmio_space [%lx/%lx] "
"to [%lx,%lx]\n",
start, end,
new->start, new->end );
return 0; /* truncation successful */
}
#else
#define lba_claim_dev_resources(dev) do { } while (0)
#define truncate_pat_collision(r,n) (0)
#endif
/*
** The algorithm is generic code.
** But it needs to access local data structures to get the IRQ base.
@ -747,6 +807,9 @@ lba_fixup_bus(struct pci_bus *bus)
lba_dump_res(&ioport_resource, 2);
BUG();
}
/* advertize Host bridge resources to PCI bus */
bus->resource[0] = &(ldev->hba.io_space);
i = 1;
if (ldev->hba.elmmio_space.start) {
err = request_resource(&iomem_resource,
@ -760,23 +823,35 @@ lba_fixup_bus(struct pci_bus *bus)
/* lba_dump_res(&iomem_resource, 2); */
/* BUG(); */
}
} else
bus->resource[i++] = &(ldev->hba.elmmio_space);
}
err = request_resource(&iomem_resource, &(ldev->hba.lmmio_space));
if (err < 0) {
/* FIXME overlaps with elmmio will fail here.
* Need to prune (or disable) the distributed range.
*
* BEWARE: conflicts with this lmmio range may be
* elmmio range which is pointing down another rope.
*/
printk("FAILED: lba_fixup_bus() request for "
/* Overlaps with elmmio can (and should) fail here.
* We will prune (or ignore) the distributed range.
*
* FIXME: SBA code should register all elmmio ranges first.
* that would take care of elmmio ranges routed
* to a different rope (already discovered) from
* getting registered *after* LBA code has already
* registered it's distributed lmmio range.
*/
if (truncate_pat_collision(&iomem_resource,
&(ldev->hba.lmmio_space))) {
printk(KERN_WARNING "LBA: lmmio_space [%lx/%lx] duplicate!\n",
ldev->hba.lmmio_space.start,
ldev->hba.lmmio_space.end);
} else {
err = request_resource(&iomem_resource, &(ldev->hba.lmmio_space));
if (err < 0) {
printk(KERN_ERR "FAILED: lba_fixup_bus() request for "
"lmmio_space [%lx/%lx]\n",
ldev->hba.lmmio_space.start,
ldev->hba.lmmio_space.end);
/* lba_dump_res(&iomem_resource, 2); */
} else
bus->resource[i++] = &(ldev->hba.lmmio_space);
}
#ifdef CONFIG_64BIT
@ -791,18 +866,10 @@ lba_fixup_bus(struct pci_bus *bus)
lba_dump_res(&iomem_resource, 2);
BUG();
}
bus->resource[i++] = &(ldev->hba.gmmio_space);
}
#endif
/* advertize Host bridge resources to PCI bus */
bus->resource[0] = &(ldev->hba.io_space);
bus->resource[1] = &(ldev->hba.lmmio_space);
i=2;
if (ldev->hba.elmmio_space.start)
bus->resource[i++] = &(ldev->hba.elmmio_space);
if (ldev->hba.gmmio_space.start)
bus->resource[i++] = &(ldev->hba.gmmio_space);
}
list_for_each(ln, &bus->devices) {