[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>
diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c
index 4f6bdf0..cbae8c8 100644
--- a/drivers/parisc/lba_pci.c
+++ b/drivers/parisc/lba_pci.c
@@ -695,12 +695,72 @@
}
}
}
-#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.
** Could make this a "pci_fixup_irq(bus, region)" but not sure
@@ -747,6 +807,9 @@
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_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_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) {