fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / parisc / lba_pci.c
index dc83880..ba67699 100644 (file)
@@ -46,9 +46,9 @@
 #include <asm/page.h>
 #include <asm/system.h>
 
+#include <asm/ropes.h>
 #include <asm/hardware.h>      /* for register_parisc_driver() stuff */
 #include <asm/parisc-device.h>
-#include <asm/iosapic.h>       /* for iosapic_register() */
 #include <asm/io.h>            /* read/write stuff */
 
 #undef DEBUG_LBA       /* general stuff */
 
 #define MODULE_NAME "LBA"
 
-#define LBA_FUNC_ID    0x0000  /* function id */
-#define LBA_FCLASS     0x0008  /* function class, bist, header, rev... */
-#define LBA_CAPABLE    0x0030  /* capabilities register */
-
-#define LBA_PCI_CFG_ADDR       0x0040  /* poke CFG address here */
-#define LBA_PCI_CFG_DATA       0x0048  /* read or write data here */
-
-#define LBA_PMC_MTLT   0x0050  /* Firmware sets this - read only. */
-#define LBA_FW_SCRATCH 0x0058  /* Firmware writes the PCI bus number here. */
-#define LBA_ERROR_ADDR 0x0070  /* On error, address gets logged here */
-
-#define LBA_ARB_MASK   0x0080  /* bit 0 enable arbitration. PAT/PDC enables */
-#define LBA_ARB_PRI    0x0088  /* firmware sets this. */
-#define LBA_ARB_MODE   0x0090  /* firmware sets this. */
-#define LBA_ARB_MTLT   0x0098  /* firmware sets this. */
-
-#define LBA_MOD_ID     0x0100  /* Module ID. PDC_PAT_CELL reports 4 */
-
-#define LBA_STAT_CTL   0x0108  /* Status & Control */
-#define   LBA_BUS_RESET                0x01    /*  Deassert PCI Bus Reset Signal */
-#define   CLEAR_ERRLOG         0x10    /*  "Clear Error Log" cmd */
-#define   CLEAR_ERRLOG_ENABLE  0x20    /*  "Clear Error Log" Enable */
-#define   HF_ENABLE    0x40    /*    enable HF mode (default is -1 mode) */
-
-#define LBA_LMMIO_BASE 0x0200  /* < 4GB I/O address range */
-#define LBA_LMMIO_MASK 0x0208
-
-#define LBA_GMMIO_BASE 0x0210  /* > 4GB I/O address range */
-#define LBA_GMMIO_MASK 0x0218
-
-#define LBA_WLMMIO_BASE        0x0220  /* All < 4GB ranges under the same *SBA* */
-#define LBA_WLMMIO_MASK        0x0228
-
-#define LBA_WGMMIO_BASE        0x0230  /* All > 4GB ranges under the same *SBA* */
-#define LBA_WGMMIO_MASK        0x0238
-
-#define LBA_IOS_BASE   0x0240  /* I/O port space for this LBA */
-#define LBA_IOS_MASK   0x0248
-
-#define LBA_ELMMIO_BASE        0x0250  /* Extra LMMIO range */
-#define LBA_ELMMIO_MASK        0x0258
-
-#define LBA_EIOS_BASE  0x0260  /* Extra I/O port space */
-#define LBA_EIOS_MASK  0x0268
-
-#define LBA_GLOBAL_MASK        0x0270  /* Mercury only: Global Address Mask */
-#define LBA_DMA_CTL    0x0278  /* firmware sets this */
-
-#define LBA_IBASE      0x0300  /* SBA DMA support */
-#define LBA_IMASK      0x0308
-
-/* FIXME: ignore DMA Hint stuff until we can measure performance */
-#define LBA_HINT_CFG   0x0310
-#define LBA_HINT_BASE  0x0380  /* 14 registers at every 8 bytes. */
-
-#define LBA_BUS_MODE   0x0620
-
-/* ERROR regs are needed for config cycle kluges */
-#define LBA_ERROR_CONFIG 0x0680
-#define     LBA_SMART_MODE 0x20
-#define LBA_ERROR_STATUS 0x0688
-#define LBA_ROPE_CTL     0x06A0
-
-#define LBA_IOSAPIC_BASE       0x800 /* Offset of IRQ logic */
-
 /* non-postable I/O port space, densely packed */
 #define LBA_PORT_BASE  (PCI_F_EXTEND | 0xfee00000UL)
-static void __iomem *astro_iop_base;
-
-#define ELROY_HVERS    0x782
-#define MERCURY_HVERS  0x783
-#define QUICKSILVER_HVERS      0x784
-
-static inline int IS_ELROY(struct parisc_device *d)
-{
-       return (d->id.hversion == ELROY_HVERS);
-}
-
-static inline int IS_MERCURY(struct parisc_device *d)
-{
-       return (d->id.hversion == MERCURY_HVERS);
-}
-
-static inline int IS_QUICKSILVER(struct parisc_device *d)
-{
-       return (d->id.hversion == QUICKSILVER_HVERS);
-}
-
-
-/*
-** lba_device: Per instance Elroy data structure
-*/
-struct lba_device {
-       struct pci_hba_data hba;
-
-       spinlock_t      lba_lock;
-       void            *iosapic_obj;
-
-#ifdef CONFIG_64BIT
-       void __iomem *  iop_base;    /* PA_VIEW - for IO port accessor funcs */
-#endif
-
-       int             flags;       /* state/functionality enabled */
-       int             hw_rev;      /* HW revision of chip */
-};
-
+static void __iomem *astro_iop_base __read_mostly;
 
 static u32 lba_t32;
 
@@ -695,11 +592,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 +704,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 +720,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 +763,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 +1110,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 +1252,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 +1422,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);
@@ -1475,8 +1439,8 @@ lba_driver_probe(struct parisc_device *dev)
                default: version = "TR4+";
                }
 
-               printk(KERN_INFO "%s version %s (0x%x) found at 0x%lx\n",
-                       MODULE_NAME, version, func_class & 0xf, dev->hpa);
+               printk(KERN_INFO "Elroy version %s (0x%x) found at 0x%lx\n",
+                      version, func_class & 0xf, dev->hpa.start);
 
                if (func_class < 2) {
                        printk(KERN_WARNING "Can't support LBA older than "
@@ -1496,37 +1460,39 @@ lba_driver_probe(struct parisc_device *dev)
                }
 
        } else if (IS_MERCURY(dev) || IS_QUICKSILVER(dev)) {
+               int major, minor;
+
                func_class &= 0xff;
-               version = kmalloc(6, GFP_KERNEL);
-               sprintf(version,"TR%d.%d",(func_class >> 4),(func_class & 0xf));
+               major = func_class >> 4, minor = 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);
+               printk(KERN_INFO "%s version TR%d.%d (0x%x) found at 0x%lx\n",
+                      IS_MERCURY(dev) ? "Mercury" : "Quicksilver", major,
+                      minor, func_class, 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 --------- */
 
@@ -1535,6 +1501,7 @@ lba_driver_probe(struct parisc_device *dev)
        lba_dev->hba.dev = dev;
        lba_dev->iosapic_obj = tmp_obj;  /* save interrupt handle */
        lba_dev->hba.iommu = sba_get_iommu(dev);  /* get iommu data */
+       parisc_set_drvdata(dev, lba_dev);
 
        /* ------------ Second : initialize common stuff ---------- */
        pci_bios = &lba_bios_ops;
@@ -1554,7 +1521,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 +1537,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 +1602,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 */