Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / drivers / parisc / lba_pci.c
index dc83880..3fe4a77 100644 (file)
 
 /* non-postable I/O port space, densely packed */
 #define LBA_PORT_BASE  (PCI_F_EXTEND | 0xfee00000UL)
-static void __iomem *astro_iop_base;
+static void __iomem *astro_iop_base __read_mostly;
 
 #define ELROY_HVERS    0x782
 #define MERCURY_HVERS  0x783
@@ -695,11 +695,71 @@ lba_claim_dev_resources(struct pci_dev *dev)
                }
        }
 }
+
+
+/*
+ * 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)
+#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) {
@@ -1146,7 +1213,7 @@ lba_pat_resources(struct parisc_device *pa_dev, struct lba_device *lba_dev)
                        ** Postable I/O port space is per PCI host adapter.
                        ** base of 64MB PIOP region
                        */
-                       lba_dev->iop_base = ioremap(p->start, 64 * 1024 * 1024);
+                       lba_dev->iop_base = ioremap_nocache(p->start, 64 * 1024 * 1024);
 
                        sprintf(lba_dev->hba.io_name, "PCI%02lx Ports",
                                        lba_dev->hba.bus_num.start);
@@ -1288,7 +1355,7 @@ lba_legacy_resources(struct parisc_device *pa_dev, struct lba_device *lba_dev)
                ** Adjust "window" for this rope.
                */
                rsize /= ROPES_PER_IOC;
-               r->start += (rsize + 1) * LBA_NUM(pa_dev->hpa);
+               r->start += (rsize + 1) * LBA_NUM(pa_dev->hpa.start);
                r->end = r->start + rsize;
        } else {
                r->end = r->start = 0;  /* Not enabled. */
@@ -1458,7 +1525,7 @@ lba_driver_probe(struct parisc_device *dev)
        u32 func_class;
        void *tmp_obj;
        char *version;
-       void __iomem *addr = ioremap(dev->hpa, 4096);
+       void __iomem *addr = ioremap_nocache(dev->hpa.start, 4096);
 
        /* Read HW Rev First */
        func_class = READ_REG32(addr + LBA_FCLASS);
@@ -1476,7 +1543,7 @@ lba_driver_probe(struct parisc_device *dev)
                }
 
                printk(KERN_INFO "%s version %s (0x%x) found at 0x%lx\n",
-                       MODULE_NAME, version, func_class & 0xf, dev->hpa);
+                       MODULE_NAME, version, func_class & 0xf, dev->hpa.start);
 
                if (func_class < 2) {
                        printk(KERN_WARNING "Can't support LBA older than "
@@ -1498,35 +1565,33 @@ lba_driver_probe(struct parisc_device *dev)
        } else if (IS_MERCURY(dev) || IS_QUICKSILVER(dev)) {
                func_class &= 0xff;
                version = kmalloc(6, GFP_KERNEL);
-               sprintf(version,"TR%d.%d",(func_class >> 4),(func_class & 0xf));
+               snprintf(version, 6, "TR%d.%d",(func_class >> 4),(func_class & 0xf));
                /* We could use one printk for both Elroy and Mercury,
                  * but for the mask for func_class.
                  */ 
                printk(KERN_INFO "%s version %s (0x%x) found at 0x%lx\n",
-                       MODULE_NAME, version, func_class & 0xff, dev->hpa);
+                      MODULE_NAME, version, func_class & 0xff, dev->hpa.start);
                cfg_ops = &mercury_cfg_ops;
        } else {
-               printk(KERN_ERR "Unknown LBA found at 0x%lx\n", dev->hpa);
+               printk(KERN_ERR "Unknown LBA found at 0x%lx\n", dev->hpa.start);
                return -ENODEV;
        }
 
        /*
        ** Tell I/O SAPIC driver we have a IRQ handler/region.
        */
-       tmp_obj = iosapic_register(dev->hpa + LBA_IOSAPIC_BASE);
+       tmp_obj = iosapic_register(dev->hpa.start + LBA_IOSAPIC_BASE);
 
        /* NOTE: PCI devices (e.g. 103c:1005 graphics card) which don't
        **      have an IRT entry will get NULL back from iosapic code.
        */
        
-       lba_dev = kmalloc(sizeof(struct lba_device), GFP_KERNEL);
+       lba_dev = kzalloc(sizeof(struct lba_device), GFP_KERNEL);
        if (!lba_dev) {
                printk(KERN_ERR "lba_init_chip - couldn't alloc lba_device\n");
                return(1);
        }
 
-       memset(lba_dev, 0, sizeof(struct lba_device));
-
 
        /* ---------- First : initialize data we already have --------- */
 
@@ -1554,7 +1619,7 @@ lba_driver_probe(struct parisc_device *dev)
        } else {
                if (!astro_iop_base) {
                        /* Sprockets PDC uses NPIOP region */
-                       astro_iop_base = ioremap(LBA_PORT_BASE, 64 * 1024);
+                       astro_iop_base = ioremap_nocache(LBA_PORT_BASE, 64 * 1024);
                        pci_port = &lba_astro_port_ops;
                }
 
@@ -1570,6 +1635,8 @@ lba_driver_probe(struct parisc_device *dev)
        lba_bus = lba_dev->hba.hba_bus =
                pci_scan_bus_parented(&dev->dev, lba_dev->hba.bus_num.start,
                                cfg_ops, NULL);
+       if (lba_bus)
+               pci_bus_add_devices(lba_bus);
 
        /* This is in lieu of calling pci_assign_unassigned_resources() */
        if (is_pdc_pat()) {
@@ -1633,7 +1700,7 @@ void __init lba_init(void)
 */
 void lba_set_iregs(struct parisc_device *lba, u32 ibase, u32 imask)
 {
-       void __iomem * base_addr = ioremap(lba->hpa, 4096);
+       void __iomem * base_addr = ioremap_nocache(lba->hpa.start, 4096);
 
        imask <<= 2;    /* adjust for hints - 2 more bits */