linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / arch / sparc64 / kernel / ebus.c
index 8a9b470..7991e91 100644 (file)
@@ -5,6 +5,7 @@
  * Copyright (C) 1999  David S. Miller (davem@redhat.com)
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/types.h>
@@ -19,8 +20,6 @@
 #include <asm/pbm.h>
 #include <asm/ebus.h>
 #include <asm/oplib.h>
-#include <asm/prom.h>
-#include <asm/of_device.h>
 #include <asm/bpp.h>
 #include <asm/irq.h>
 
@@ -140,7 +139,7 @@ int ebus_dma_irq_enable(struct ebus_dma_info *p, int on)
 
        if (on) {
                if (p->flags & EBUS_DMA_FLAG_USE_EBDMA_HANDLER) {
-                       if (request_irq(p->irq, ebus_dma_irq, IRQF_SHARED, p->name, p))
+                       if (request_irq(p->irq, ebus_dma_irq, SA_SHIRQ, p->name, p))
                                return -EBUSY;
                }
 
@@ -270,32 +269,92 @@ EXPORT_SYMBOL(ebus_dma_enable);
 
 struct linux_ebus *ebus_chain = NULL;
 
+#ifdef CONFIG_SUN_AUXIO
+extern void auxio_probe(void);
+#endif
+
 static inline void *ebus_alloc(size_t size)
 {
        void *mem;
 
-       mem = kzalloc(size, GFP_ATOMIC);
+       mem = kmalloc(size, GFP_ATOMIC);
        if (!mem)
                panic("ebus_alloc: out of memory");
+       memset((char *)mem, 0, size);
        return mem;
 }
 
-static void __init fill_ebus_child(struct device_node *dp,
-                                  struct linux_ebus_child *dev,
-                                  int non_standard_regs)
+static void __init ebus_ranges_init(struct linux_ebus *ebus)
+{
+       int success;
+
+       ebus->num_ebus_ranges = 0;
+       success = prom_getproperty(ebus->prom_node, "ranges",
+                                  (char *)ebus->ebus_ranges,
+                                  sizeof(ebus->ebus_ranges));
+       if (success != -1)
+               ebus->num_ebus_ranges = (success/sizeof(struct linux_prom_ebus_ranges));
+}
+
+static void __init ebus_intmap_init(struct linux_ebus *ebus)
+{
+       int success;
+
+       ebus->num_ebus_intmap = 0;
+       success = prom_getproperty(ebus->prom_node, "interrupt-map",
+                                  (char *)ebus->ebus_intmap,
+                                  sizeof(ebus->ebus_intmap));
+       if (success == -1)
+               return;
+
+       ebus->num_ebus_intmap = (success/sizeof(struct linux_prom_ebus_intmap));
+
+       success = prom_getproperty(ebus->prom_node, "interrupt-map-mask",
+                                  (char *)&ebus->ebus_intmask,
+                                  sizeof(ebus->ebus_intmask));
+       if (success == -1) {
+               prom_printf("%s: can't get interrupt-map-mask\n", __FUNCTION__);
+               prom_halt();
+       }
+}
+
+int __init ebus_intmap_match(struct linux_ebus *ebus,
+                            struct linux_prom_registers *reg,
+                            int *interrupt)
 {
-       struct of_device *op;
-       int *regs;
+       unsigned int hi, lo, irq;
+       int i;
+
+       if (!ebus->num_ebus_intmap)
+               return 0;
+
+       hi = reg->which_io & ebus->ebus_intmask.phys_hi;
+       lo = reg->phys_addr & ebus->ebus_intmask.phys_lo;
+       irq = *interrupt & ebus->ebus_intmask.interrupt;
+       for (i = 0; i < ebus->num_ebus_intmap; i++) {
+               if ((ebus->ebus_intmap[i].phys_hi == hi) &&
+                   (ebus->ebus_intmap[i].phys_lo == lo) &&
+                   (ebus->ebus_intmap[i].interrupt == irq)) {
+                       *interrupt = ebus->ebus_intmap[i].cinterrupt;
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+void __init fill_ebus_child(int node, struct linux_prom_registers *preg,
+                           struct linux_ebus_child *dev, int non_standard_regs)
+{
+       int regs[PROMREG_MAX];
+       int irqs[PROMREG_MAX];
        int i, len;
 
-       dev->prom_node = dp;
-       printk(" (%s)", dp->name);
+       dev->prom_node = node;
+       prom_getstring(node, "name", dev->prom_name, sizeof(dev->prom_name));
+       printk(" (%s)", dev->prom_name);
 
-       regs = of_get_property(dp, "reg", &len);
-       if (!regs)
-               dev->num_addrs = 0;
-       else
-               dev->num_addrs = len / sizeof(regs[0]);
+       len = prom_getproperty(node, "reg", (void *)regs, sizeof(regs));
+       dev->num_addrs = len / sizeof(regs[0]);
 
        if (non_standard_regs) {
                /* This is to handle reg properties which are not
@@ -312,34 +371,30 @@ static void __init fill_ebus_child(struct device_node *dp,
                        int rnum = regs[i];
                        if (rnum >= dev->parent->num_addrs) {
                                prom_printf("UGH: property for %s was %d, need < %d\n",
-                                           dp->name, len, dev->parent->num_addrs);
-                               prom_halt();
+                                           dev->prom_name, len, dev->parent->num_addrs);
+                               panic(__FUNCTION__);
                        }
                        dev->resource[i].start = dev->parent->resource[i].start;
                        dev->resource[i].end = dev->parent->resource[i].end;
                        dev->resource[i].flags = IORESOURCE_MEM;
-                       dev->resource[i].name = dp->name;
+                       dev->resource[i].name = dev->prom_name;
                }
        }
 
-       op = of_find_device_by_node(dp);
-       if (!op) {
-               dev->num_irqs = 0;
-       } else {
-               dev->num_irqs = op->num_irqs;
-               for (i = 0; i < dev->num_irqs; i++)
-                       dev->irqs[i] = op->irqs[i];
-       }
+       for (i = 0; i < PROMINTR_MAX; i++)
+               dev->irqs[i] = PCI_IRQ_NONE;
 
-       if (!dev->num_irqs) {
+       len = prom_getproperty(node, "interrupts", (char *)&irqs, sizeof(irqs));
+       if ((len == -1) || (len == 0)) {
+               dev->num_irqs = 0;
                /*
                 * Oh, well, some PROMs don't export interrupts
                 * property to children of EBus devices...
                 *
                 * Be smart about PS/2 keyboard and mouse.
                 */
-               if (!strcmp(dev->parent->prom_node->name, "8042")) {
-                       if (!strcmp(dev->prom_node->name, "kb_ps2")) {
+               if (!strcmp(dev->parent->prom_name, "8042")) {
+                       if (!strcmp(dev->prom_name, "kb_ps2")) {
                                dev->num_irqs = 1;
                                dev->irqs[0] = dev->parent->irqs[0];
                        } else {
@@ -347,57 +402,103 @@ static void __init fill_ebus_child(struct device_node *dp,
                                dev->irqs[0] = dev->parent->irqs[1];
                        }
                }
+       } else {
+               dev->num_irqs = len / sizeof(irqs[0]);
+               for (i = 0; i < dev->num_irqs; i++) {
+                       struct pci_pbm_info *pbm = dev->bus->parent;
+                       struct pci_controller_info *p = pbm->parent;
+
+                       if (ebus_intmap_match(dev->bus, preg, &irqs[i]) != -1) {
+                               dev->irqs[i] = p->irq_build(pbm,
+                                                           dev->bus->self,
+                                                           irqs[i]);
+                       } else {
+                               /* If we get a bogus interrupt property, just
+                                * record the raw value instead of punting.
+                                */
+                               dev->irqs[i] = irqs[i];
+                       }
+               }
        }
 }
 
 static int __init child_regs_nonstandard(struct linux_ebus_device *dev)
 {
-       if (!strcmp(dev->prom_node->name, "i2c") ||
-           !strcmp(dev->prom_node->name, "SUNW,lombus"))
+       if (!strcmp(dev->prom_name, "i2c") ||
+           !strcmp(dev->prom_name, "SUNW,lombus"))
                return 1;
        return 0;
 }
 
-static void __init fill_ebus_device(struct device_node *dp, struct linux_ebus_device *dev)
+void __init fill_ebus_device(int node, struct linux_ebus_device *dev)
 {
+       struct linux_prom_registers regs[PROMREG_MAX];
        struct linux_ebus_child *child;
-       struct of_device *op;
-       int i, len;
+       int irqs[PROMINTR_MAX];
+       int i, n, len;
 
-       dev->prom_node = dp;
+       dev->prom_node = node;
+       prom_getstring(node, "name", dev->prom_name, sizeof(dev->prom_name));
+       printk(" [%s", dev->prom_name);
 
-       printk(" [%s", dp->name);
-
-       op = of_find_device_by_node(dp);
-       if (!op) {
+       len = prom_getproperty(node, "reg", (void *)regs, sizeof(regs));
+       if (len == -1) {
                dev->num_addrs = 0;
-               dev->num_irqs = 0;
-       } else {
-               (void) of_get_property(dp, "reg", &len);
-               dev->num_addrs = len / sizeof(struct linux_prom_registers);
-
-               for (i = 0; i < dev->num_addrs; i++)
-                       memcpy(&dev->resource[i],
-                              &op->resource[i],
-                              sizeof(struct resource));
+               goto probe_interrupts;
+       }
 
-               dev->num_irqs = op->num_irqs;
-               for (i = 0; i < dev->num_irqs; i++)
-                       dev->irqs[i] = op->irqs[i];
+       if (len % sizeof(struct linux_prom_registers)) {
+               prom_printf("UGH: proplen for %s was %d, need multiple of %d\n",
+                           dev->prom_name, len,
+                           (int)sizeof(struct linux_prom_registers));
+               prom_halt();
+       }
+       dev->num_addrs = len / sizeof(struct linux_prom_registers);
+
+       for (i = 0; i < dev->num_addrs; i++) {
+               /* XXX Learn how to interpret ebus ranges... -DaveM */
+               if (regs[i].which_io >= 0x10)
+                       n = (regs[i].which_io - 0x10) >> 2;
+               else
+                       n = regs[i].which_io;
+
+               dev->resource[i].start  = dev->bus->self->resource[n].start;
+               dev->resource[i].start += (unsigned long)regs[i].phys_addr;
+               dev->resource[i].end    =
+                       (dev->resource[i].start + (unsigned long)regs[i].reg_size - 1UL);
+               dev->resource[i].flags  = IORESOURCE_MEM;
+               dev->resource[i].name   = dev->prom_name;
+               request_resource(&dev->bus->self->resource[n],
+                                &dev->resource[i]);
        }
 
-       dev->ofdev.node = dp;
-       dev->ofdev.dev.parent = &dev->bus->ofdev.dev;
-       dev->ofdev.dev.bus = &ebus_bus_type;
-       strcpy(dev->ofdev.dev.bus_id, dp->path_component_name);
+probe_interrupts:
+       for (i = 0; i < PROMINTR_MAX; i++)
+               dev->irqs[i] = PCI_IRQ_NONE;
 
-       /* Register with core */
-       if (of_device_register(&dev->ofdev) != 0)
-               printk(KERN_DEBUG "ebus: device registration error for %s!\n",
-                      dev->ofdev.dev.bus_id);
+       len = prom_getproperty(node, "interrupts", (char *)&irqs, sizeof(irqs));
+       if ((len == -1) || (len == 0)) {
+               dev->num_irqs = 0;
+       } else {
+               dev->num_irqs = len / sizeof(irqs[0]);
+               for (i = 0; i < dev->num_irqs; i++) {
+                       struct pci_pbm_info *pbm = dev->bus->parent;
+                       struct pci_controller_info *p = pbm->parent;
+
+                       if (ebus_intmap_match(dev->bus, &regs[0], &irqs[i]) != -1) {
+                               dev->irqs[i] = p->irq_build(pbm,
+                                                           dev->bus->self,
+                                                           irqs[i]);
+                       } else {
+                               /* If we get a bogus interrupt property, just
+                                * record the raw value instead of punting.
+                                */
+                               dev->irqs[i] = irqs[i];
+                       }
+               }
+       }
 
-       dp = dp->child;
-       if (dp) {
+       if ((node = prom_getchild(node))) {
                printk(" ->");
                dev->children = ebus_alloc(sizeof(struct linux_ebus_child));
 
@@ -405,18 +506,18 @@ static void __init fill_ebus_device(struct device_node *dp, struct linux_ebus_de
                child->next = NULL;
                child->parent = dev;
                child->bus = dev->bus;
-               fill_ebus_child(dp, child,
-                               child_regs_nonstandard(dev));
+               fill_ebus_child(node, &regs[0],
+                               child, child_regs_nonstandard(dev));
 
-               while ((dp = dp->sibling) != NULL) {
+               while ((node = prom_getsibling(node)) != 0) {
                        child->next = ebus_alloc(sizeof(struct linux_ebus_child));
 
                        child = child->next;
                        child->next = NULL;
                        child->parent = dev;
                        child->bus = dev->bus;
-                       fill_ebus_child(dp, child,
-                                       child_regs_nonstandard(dev));
+                       fill_ebus_child(node, &regs[0],
+                                       child, child_regs_nonstandard(dev));
                }
        }
        printk("]");
@@ -443,8 +544,7 @@ void __init ebus_init(void)
        struct linux_ebus *ebus;
        struct pci_dev *pdev;
        struct pcidev_cookie *cookie;
-       struct device_node *dp;
-       int is_rio;
+       int nd, ebusnd, is_rio;
        int num_ebus = 0;
 
        pdev = find_next_ebus(NULL, &is_rio);
@@ -454,22 +554,20 @@ void __init ebus_init(void)
        }
 
        cookie = pdev->sysdata;
-       dp = cookie->prom_node;
+       ebusnd = cookie->prom_node;
 
        ebus_chain = ebus = ebus_alloc(sizeof(struct linux_ebus));
        ebus->next = NULL;
        ebus->is_rio = is_rio;
 
-       while (dp) {
-               struct device_node *child;
-
+       while (ebusnd) {
                /* SUNW,pci-qfe uses four empty ebuses on it.
                   I think we should not consider them here,
                   as they have half of the properties this
                   code expects and once we do PCI hot-plug,
                   we'd have to tweak with the ebus_chain
                   in the runtime after initialization. -jj */
-               if (!dp->child) {
+               if (!prom_getchild (ebusnd)) {
                        pdev = find_next_ebus(pdev, &is_rio);
                        if (!pdev) {
                                if (ebus == ebus_chain) {
@@ -481,29 +579,22 @@ void __init ebus_init(void)
                        }
                        ebus->is_rio = is_rio;
                        cookie = pdev->sysdata;
-                       dp = cookie->prom_node;
+                       ebusnd = cookie->prom_node;
                        continue;
                }
                printk("ebus%d:", num_ebus);
 
+               prom_getstring(ebusnd, "name", ebus->prom_name, sizeof(ebus->prom_name));
                ebus->index = num_ebus;
-               ebus->prom_node = dp;
+               ebus->prom_node = ebusnd;
                ebus->self = pdev;
                ebus->parent = pbm = cookie->pbm;
 
-               ebus->ofdev.node = dp;
-               ebus->ofdev.dev.parent = &pdev->dev;
-               ebus->ofdev.dev.bus = &ebus_bus_type;
-               strcpy(ebus->ofdev.dev.bus_id, dp->path_component_name);
-
-               /* Register with core */
-               if (of_device_register(&ebus->ofdev) != 0)
-                       printk(KERN_DEBUG "ebus: device registration error for %s!\n",
-                              ebus->ofdev.dev.bus_id);
+               ebus_ranges_init(ebus);
+               ebus_intmap_init(ebus);
 
-
-               child = dp->child;
-               if (!child)
+               nd = prom_getchild(ebusnd);
+               if (!nd)
                        goto next_ebus;
 
                ebus->devices = ebus_alloc(sizeof(struct linux_ebus_device));
@@ -512,16 +603,16 @@ void __init ebus_init(void)
                dev->next = NULL;
                dev->children = NULL;
                dev->bus = ebus;
-               fill_ebus_device(child, dev);
+               fill_ebus_device(nd, dev);
 
-               while ((child = child->sibling) != NULL) {
+               while ((nd = prom_getsibling(nd)) != 0) {
                        dev->next = ebus_alloc(sizeof(struct linux_ebus_device));
 
                        dev = dev->next;
                        dev->next = NULL;
                        dev->children = NULL;
                        dev->bus = ebus;
-                       fill_ebus_device(child, dev);
+                       fill_ebus_device(nd, dev);
                }
 
        next_ebus:
@@ -532,7 +623,7 @@ void __init ebus_init(void)
                        break;
 
                cookie = pdev->sysdata;
-               dp = cookie->prom_node;
+               ebusnd = cookie->prom_node;
 
                ebus->next = ebus_alloc(sizeof(struct linux_ebus));
                ebus = ebus->next;
@@ -541,4 +632,8 @@ void __init ebus_init(void)
                ++num_ebus;
        }
        pci_dev_put(pdev); /* XXX for the case, when ebusnd is 0, is it OK? */
+
+#ifdef CONFIG_SUN_AUXIO
+       auxio_probe();
+#endif
 }