#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>
return mem;
}
-int __init ebus_intmap_match(struct linux_ebus *ebus,
- struct linux_prom_registers *reg,
- int *interrupt)
-{
- struct linux_prom_ebus_intmap *imap;
- struct linux_prom_ebus_intmask *imask;
- unsigned int hi, lo, irq;
- int i, len, n_imap;
-
- imap = of_get_property(ebus->prom_node, "interrupt-map", &len);
- if (!imap)
- return 0;
- n_imap = len / sizeof(imap[0]);
-
- imask = of_get_property(ebus->prom_node, "interrupt-map-mask", NULL);
- if (!imask)
- return 0;
-
- hi = reg->which_io & imask->phys_hi;
- lo = reg->phys_addr & imask->phys_lo;
- irq = *interrupt & imask->interrupt;
- for (i = 0; i < n_imap; i++) {
- if ((imap[i].phys_hi == hi) &&
- (imap[i].phys_lo == lo) &&
- (imap[i].interrupt == irq)) {
- *interrupt = imap[i].cinterrupt;
- return 0;
- }
- }
- return -1;
-}
-
-void __init fill_ebus_child(struct device_node *dp,
- struct linux_prom_registers *preg,
- struct linux_ebus_child *dev,
- int non_standard_regs)
+static void __init fill_ebus_child(struct device_node *dp,
+ struct linux_ebus_child *dev,
+ int non_standard_regs)
{
+ struct of_device *op;
int *regs;
- int *irqs;
int i, len;
dev->prom_node = dp;
}
}
- for (i = 0; i < PROMINTR_MAX; i++)
- dev->irqs[i] = PCI_IRQ_NONE;
-
- irqs = of_get_property(dp, "interrupts", &len);
- if (!irqs) {
+ 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];
+ }
+
+ if (!dev->num_irqs) {
/*
* Oh, well, some PROMs don't export interrupts
* property to children of EBus devices...
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];
- }
- }
}
}
return 0;
}
-void __init fill_ebus_device(struct device_node *dp, struct linux_ebus_device *dev)
+static void __init fill_ebus_device(struct device_node *dp, struct linux_ebus_device *dev)
{
- struct linux_prom_registers *regs;
struct linux_ebus_child *child;
- int *irqs;
- int i, n, len;
+ struct of_device *op;
+ int i, len;
dev->prom_node = dp;
printk(" [%s", dp->name);
- regs = of_get_property(dp, "reg", &len);
- if (!regs) {
+ op = of_find_device_by_node(dp);
+ if (!op) {
dev->num_addrs = 0;
- goto probe_interrupts;
- }
-
- if (len % sizeof(struct linux_prom_registers)) {
- prom_printf("UGH: proplen for %s was %d, need multiple of %d\n",
- dev->prom_node->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_node->name;
- request_resource(&dev->bus->self->resource[n],
- &dev->resource[i]);
- }
-
-probe_interrupts:
- for (i = 0; i < PROMINTR_MAX; i++)
- dev->irqs[i] = PCI_IRQ_NONE;
-
- irqs = of_get_property(dp, "interrupts", &len);
- if (!irqs) {
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, ®s[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];
- }
- }
+ (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));
+
+ dev->num_irqs = op->num_irqs;
+ for (i = 0; i < dev->num_irqs; i++)
+ dev->irqs[i] = op->irqs[i];
}
dev->ofdev.node = dp;
child->next = NULL;
child->parent = dev;
child->bus = dev->bus;
- fill_ebus_child(dp, regs, child,
+ fill_ebus_child(dp, child,
child_regs_nonstandard(dev));
while ((dp = dp->sibling) != NULL) {
child->next = NULL;
child->parent = dev;
child->bus = dev->bus;
- fill_ebus_child(dp, regs, child,
+ fill_ebus_child(dp, child,
child_regs_nonstandard(dev));
}
}
#include <linux/pci.h>
#include <linux/slab.h>
#include <asm/oplib.h>
+#include <asm/prom.h>
+#include <asm/of_device.h>
#include <asm/isa.h>
struct sparc_isa_bridge *isa_chain;
return pregs;
}
-/* I can't believe they didn't put a real INO in the isa device
- * interrupts property. The whole point of the OBP properties
- * is to shield the kernel from IRQ routing details.
- *
- * The P1275 standard for ISA devices seems to also have been
- * totally ignored.
- *
- * On later systems, an interrupt-map and interrupt-map-mask scheme
- * akin to EBUS is used.
- */
-static struct {
- int obp_irq;
- int pci_ino;
-} grover_irq_table[] = {
- { 1, 0x00 }, /* dma, unknown ino at this point */
- { 2, 0x27 }, /* floppy */
- { 3, 0x22 }, /* parallel */
- { 4, 0x2b }, /* serial */
- { 5, 0x25 }, /* acpi power management */
-
- { 0, 0x00 } /* end of table */
-};
-
-static int __init isa_dev_get_irq_using_imap(struct sparc_isa_device *isa_dev,
- struct sparc_isa_bridge *isa_br,
- int *interrupt,
- struct linux_prom_registers *reg)
-{
- struct linux_prom_ebus_intmap *imap;
- struct linux_prom_ebus_intmask *imask;
- unsigned int hi, lo, irq;
- int i, len, n_imap;
-
- imap = of_get_property(isa_br->prom_node, "interrupt-map", &len);
- if (!imap)
- return 0;
- n_imap = len / sizeof(imap[0]);
-
- imask = of_get_property(isa_br->prom_node, "interrupt-map-mask", NULL);
- if (!imask)
- return 0;
-
- hi = reg->which_io & imask->phys_hi;
- lo = reg->phys_addr & imask->phys_lo;
- irq = *interrupt & imask->interrupt;
- for (i = 0; i < n_imap; i++) {
- if ((imap[i].phys_hi == hi) &&
- (imap[i].phys_lo == lo) &&
- (imap[i].interrupt == irq)) {
- *interrupt = imap[i].cinterrupt;
- return 0;
- }
- }
- return -1;
-}
-
static void __init isa_dev_get_irq(struct sparc_isa_device *isa_dev,
struct linux_prom_registers *pregs)
{
- int irq_prop;
+ struct of_device *op = of_find_device_by_node(isa_dev->prom_node);
- irq_prop = of_getintprop_default(isa_dev->prom_node,
- "interrupts", -1);
- if (irq_prop <= 0) {
- goto no_irq;
+ if (!op || !op->num_irqs) {
+ isa_dev->irq = PCI_IRQ_NONE;
} else {
- struct pci_controller_info *pcic;
- struct pci_pbm_info *pbm;
- int i;
-
- if (of_find_property(isa_dev->bus->prom_node,
- "interrupt-map", NULL)) {
- if (!isa_dev_get_irq_using_imap(isa_dev,
- isa_dev->bus,
- &irq_prop,
- pregs))
- goto route_irq;
- }
-
- for (i = 0; grover_irq_table[i].obp_irq != 0; i++) {
- if (grover_irq_table[i].obp_irq == irq_prop) {
- int ino = grover_irq_table[i].pci_ino;
-
- if (ino == 0)
- goto no_irq;
-
- irq_prop = ino;
- goto route_irq;
- }
- }
- goto no_irq;
-
-route_irq:
- pbm = isa_dev->bus->parent;
- pcic = pbm->parent;
- isa_dev->irq = pcic->irq_build(pbm, NULL, irq_prop);
- return;
+ isa_dev->irq = op->irqs[0];
}
-
-no_irq:
- isa_dev->irq = PCI_IRQ_NONE;
}
static void __init isa_fill_children(struct sparc_isa_device *parent_isa_dev)
}
EXPORT_SYMBOL(of_iounmap);
+static int node_match(struct device *dev, void *data)
+{
+ struct of_device *op = to_of_device(dev);
+ struct device_node *dp = data;
+
+ return (op->node == dp);
+}
+
+struct of_device *of_find_device_by_node(struct device_node *dp)
+{
+ struct device *dev = bus_find_device(&of_bus_type, NULL,
+ dp, node_match);
+
+ if (dev)
+ return to_of_device(dev);
+
+ return NULL;
+}
+EXPORT_SYMBOL(of_find_device_by_node);
+
#ifdef CONFIG_PCI
struct bus_type isa_bus_type = {
.name = "isa",
return IORESOURCE_MEM;
}
-
/*
* PCI bus specific translator
*/
}
}
+static struct device_node * __init
+apply_interrupt_map(struct device_node *dp, struct device_node *pp,
+ u32 *imap, int imlen, u32 *imask,
+ unsigned int *irq_p)
+{
+ struct device_node *cp;
+ unsigned int irq = *irq_p;
+ struct of_bus *bus;
+ phandle handle;
+ u32 *reg;
+ int na, num_reg, i;
+
+ bus = of_match_bus(pp);
+ bus->count_cells(dp, &na, NULL);
+
+ reg = of_get_property(dp, "reg", &num_reg);
+ if (!reg || !num_reg)
+ return NULL;
+
+ imlen /= ((na + 3) * 4);
+ handle = 0;
+ for (i = 0; i < imlen; i++) {
+ int j;
+
+ for (j = 0; j < na; j++) {
+ if ((reg[j] & imask[j]) != imap[j])
+ goto next;
+ }
+ if (imap[na] == irq) {
+ handle = imap[na + 1];
+ irq = imap[na + 2];
+ break;
+ }
+
+ next:
+ imap += (na + 3);
+ }
+ if (i == imlen)
+ return NULL;
+
+ *irq_p = irq;
+ cp = of_find_node_by_phandle(handle);
+
+ return cp;
+}
+
+static unsigned int __init pci_irq_swizzle(struct device_node *dp,
+ struct device_node *pp,
+ unsigned int irq)
+{
+ struct linux_prom_pci_registers *regs;
+ unsigned int devfn, slot, ret;
+
+ if (irq < 1 || irq > 4)
+ return irq;
+
+ regs = of_get_property(dp, "reg", NULL);
+ if (!regs)
+ return irq;
+
+ devfn = (regs->phys_hi >> 8) & 0xff;
+ slot = (devfn >> 3) & 0x1f;
+
+ ret = ((irq - 1 + (slot & 3)) & 3) + 1;
+
+ return ret;
+}
+
+static unsigned int __init build_one_device_irq(struct of_device *op,
+ struct device *parent,
+ unsigned int irq)
+{
+ struct device_node *dp = op->node;
+ struct device_node *pp, *ip;
+ unsigned int orig_irq = irq;
+
+ if (irq == 0xffffffff)
+ return irq;
+
+ if (dp->irq_trans) {
+ irq = dp->irq_trans->irq_build(dp, irq,
+ dp->irq_trans->data);
+#if 1
+ printk("%s: direct translate %x --> %x\n",
+ dp->full_name, orig_irq, irq);
+#endif
+ return irq;
+ }
+
+ /* Something more complicated. Walk up to the root, applying
+ * interrupt-map or bus specific translations, until we hit
+ * an IRQ translator.
+ *
+ * If we hit a bus type or situation we cannot handle, we
+ * stop and assume that the original IRQ number was in a
+ * format which has special meaning to it's immediate parent.
+ */
+ pp = dp->parent;
+ ip = NULL;
+ while (pp) {
+ void *imap, *imsk;
+ int imlen;
+
+ imap = of_get_property(pp, "interrupt-map", &imlen);
+ imsk = of_get_property(pp, "interrupt-map-mask", NULL);
+ if (imap && imsk) {
+ struct device_node *iret;
+ int this_orig_irq = irq;
+
+ iret = apply_interrupt_map(dp, pp,
+ imap, imlen, imsk,
+ &irq);
+#if 1
+ printk("%s: Apply [%s:%x] imap --> [%s:%x]\n",
+ op->node->full_name,
+ pp->full_name, this_orig_irq,
+ (iret ? iret->full_name : "NULL"), irq);
+#endif
+ if (!iret)
+ break;
+
+ if (iret->irq_trans) {
+ ip = iret;
+ break;
+ }
+ } else {
+ if (!strcmp(pp->type, "pci") ||
+ !strcmp(pp->type, "pciex")) {
+ unsigned int this_orig_irq = irq;
+
+ irq = pci_irq_swizzle(dp, pp, irq);
+#if 1
+ printk("%s: PCI swizzle [%s] %x --> %x\n",
+ op->node->full_name,
+ pp->full_name, this_orig_irq, irq);
+#endif
+ }
+
+ if (pp->irq_trans) {
+ ip = pp;
+ break;
+ }
+ }
+ dp = pp;
+ pp = pp->parent;
+ }
+ if (!ip)
+ return orig_irq;
+
+ irq = ip->irq_trans->irq_build(op->node, irq,
+ ip->irq_trans->data);
+#if 1
+ printk("%s: Apply IRQ trans [%s] %x --> %x\n",
+ op->node->full_name, ip->full_name, orig_irq, irq);
+#endif
+
+ return irq;
+}
+
static struct of_device * __init scan_one_device(struct device_node *dp,
struct device *parent)
{
struct of_device *op = kzalloc(sizeof(*op), GFP_KERNEL);
unsigned int *irq;
- int len;
+ int len, i;
if (!op)
return NULL;
op->portid = of_getintprop_default(dp, "portid", -1);
irq = of_get_property(dp, "interrupts", &len);
- if (irq)
- op->irq = *irq;
- else
- op->irq = 0xffffffff;
+ if (irq) {
+ memcpy(op->irqs, irq, len);
+ op->num_irqs = len / 4;
+ } else {
+ op->num_irqs = 0;
+ }
build_device_resources(op, parent);
+ for (i = 0; i < op->num_irqs; i++)
+ op->irqs[i] = build_one_device_irq(op, parent, op->irqs[i]);
op->dev.parent = parent;
op->dev.bus = &of_bus_type;
}
EXPORT_SYMBOL(pcibios_bus_to_resource);
-extern int pci_irq_verbose;
-
char * __init pcibios_setup(char *str)
{
- if (!strcmp(str, "irq_verbose")) {
- pci_irq_verbose = 1;
- return NULL;
- }
return str;
}
#include <asm/pbm.h>
#include <asm/prom.h>
+#include <asm/of_device.h>
#include "pci_impl.h"
-/* Pass "pci=irq_verbose" on the kernel command line to enable this. */
-int pci_irq_verbose;
-
/* Fix self device of BUS and hook it into BUS->self.
* The pci_scan_bus does not do this for the host bridge.
*/
}
pcp->pbm = pbm;
pcp->prom_node = dp;
+ pcp->op = of_find_device_by_node(dp);
memcpy(pcp->prom_regs, pregs,
nregs * sizeof(struct linux_prom_pci_registers));
pcp->num_prom_regs = nregs;
pci_assign_unassigned(pbm, bus);
}
-static inline unsigned int pci_slot_swivel(struct pci_pbm_info *pbm,
- struct pci_dev *toplevel_pdev,
- struct pci_dev *pdev,
- unsigned int interrupt)
-{
- unsigned int ret;
-
- if (unlikely(interrupt < 1 || interrupt > 4)) {
- printk("%s: Device %s interrupt value of %u is strange.\n",
- pbm->name, pci_name(pdev), interrupt);
- return interrupt;
- }
-
- ret = ((interrupt - 1 + (PCI_SLOT(pdev->devfn) & 3)) & 3) + 1;
-
- if (pci_irq_verbose)
- printk("%s: %s IRQ Swivel %s [%x:%x] -> [%x]\n",
- pbm->name, pci_name(toplevel_pdev), pci_name(pdev),
- interrupt, PCI_SLOT(pdev->devfn), ret);
-
- return ret;
-}
-
-static inline unsigned int pci_apply_intmap(struct pci_pbm_info *pbm,
- struct pci_dev *toplevel_pdev,
- struct pci_dev *pbus,
- struct pci_dev *pdev,
- unsigned int interrupt,
- struct device_node **cnode)
-{
- struct linux_prom_pci_intmap *imap;
- struct linux_prom_pci_intmask *imask;
- struct pcidev_cookie *pbus_pcp = pbus->sysdata;
- struct pcidev_cookie *pdev_pcp = pdev->sysdata;
- struct linux_prom_pci_registers *pregs = pdev_pcp->prom_regs;
- struct property *prop;
- int plen, num_imap, i;
- unsigned int hi, mid, lo, irq, orig_interrupt;
-
- *cnode = pbus_pcp->prom_node;
-
- prop = of_find_property(pbus_pcp->prom_node, "interrupt-map", &plen);
- if (!prop ||
- (plen % sizeof(struct linux_prom_pci_intmap)) != 0) {
- printk("%s: Device %s interrupt-map has bad len %d\n",
- pbm->name, pci_name(pbus), plen);
- goto no_intmap;
- }
- imap = prop->value;
- num_imap = plen / sizeof(struct linux_prom_pci_intmap);
-
- prop = of_find_property(pbus_pcp->prom_node, "interrupt-map-mask", &plen);
- if (!prop ||
- (plen % sizeof(struct linux_prom_pci_intmask)) != 0) {
- printk("%s: Device %s interrupt-map-mask has bad len %d\n",
- pbm->name, pci_name(pbus), plen);
- goto no_intmap;
- }
- imask = prop->value;
-
- orig_interrupt = interrupt;
-
- hi = pregs->phys_hi & imask->phys_hi;
- mid = pregs->phys_mid & imask->phys_mid;
- lo = pregs->phys_lo & imask->phys_lo;
- irq = interrupt & imask->interrupt;
-
- for (i = 0; i < num_imap; i++) {
- if (imap[i].phys_hi == hi &&
- imap[i].phys_mid == mid &&
- imap[i].phys_lo == lo &&
- imap[i].interrupt == irq) {
- *cnode = of_find_node_by_phandle(imap[i].cnode);
- interrupt = imap[i].cinterrupt;
- }
- }
-
- if (pci_irq_verbose)
- printk("%s: %s MAP BUS %s DEV %s [%x] -> [%x]\n",
- pbm->name, pci_name(toplevel_pdev),
- pci_name(pbus), pci_name(pdev),
- orig_interrupt, interrupt);
-
-no_intmap:
- return interrupt;
-}
-
-/* For each PCI bus on the way to the root:
- * 1) If it has an interrupt-map property, apply it.
- * 2) Else, swivel the interrupt number based upon the PCI device number.
- *
- * Return the "IRQ controller" node. If this is the PBM's device node,
- * all interrupt translations are complete, else we should use that node's
- * "reg" property to apply the PBM's "interrupt-{map,mask}" to the interrupt.
- */
-static struct device_node * __init
-pci_intmap_match_to_root(struct pci_pbm_info *pbm,
- struct pci_dev *pdev,
- unsigned int *interrupt)
-{
- struct pci_dev *toplevel_pdev = pdev;
- struct pcidev_cookie *toplevel_pcp = toplevel_pdev->sysdata;
- struct device_node *cnode = toplevel_pcp->prom_node;
-
- while (pdev->bus->number != pbm->pci_first_busno) {
- struct pci_dev *pbus = pdev->bus->self;
- struct pcidev_cookie *pcp = pbus->sysdata;
- struct property *prop;
-
- prop = of_find_property(pcp->prom_node, "interrupt-map", NULL);
- if (!prop) {
- *interrupt = pci_slot_swivel(pbm, toplevel_pdev,
- pdev, *interrupt);
- cnode = pcp->prom_node;
- } else {
- *interrupt = pci_apply_intmap(pbm, toplevel_pdev,
- pbus, pdev,
- *interrupt, &cnode);
-
- while (pcp->prom_node != cnode &&
- pbus->bus->number != pbm->pci_first_busno) {
- pbus = pbus->bus->self;
- pcp = pbus->sysdata;
- }
- }
- pdev = pbus;
-
- if (cnode == pbm->prom_node)
- break;
- }
-
- return cnode;
-}
-
-static int __init pci_intmap_match(struct pci_dev *pdev, unsigned int *interrupt)
-{
- struct pcidev_cookie *dev_pcp = pdev->sysdata;
- struct pci_pbm_info *pbm = dev_pcp->pbm;
- struct linux_prom_pci_registers *reg;
- struct device_node *cnode;
- struct property *prop;
- unsigned int hi, mid, lo, irq;
- int i, plen;
-
- cnode = pci_intmap_match_to_root(pbm, pdev, interrupt);
- if (cnode == pbm->prom_node)
- goto success;
-
- prop = of_find_property(cnode, "reg", &plen);
- if (!prop ||
- (plen % sizeof(struct linux_prom_pci_registers)) != 0) {
- printk("%s: OBP node %s reg property has bad len %d\n",
- pbm->name, cnode->full_name, plen);
- goto fail;
- }
- reg = prop->value;
-
- hi = reg[0].phys_hi & pbm->pbm_intmask->phys_hi;
- mid = reg[0].phys_mid & pbm->pbm_intmask->phys_mid;
- lo = reg[0].phys_lo & pbm->pbm_intmask->phys_lo;
- irq = *interrupt & pbm->pbm_intmask->interrupt;
-
- for (i = 0; i < pbm->num_pbm_intmap; i++) {
- struct linux_prom_pci_intmap *intmap;
-
- intmap = &pbm->pbm_intmap[i];
-
- if (intmap->phys_hi == hi &&
- intmap->phys_mid == mid &&
- intmap->phys_lo == lo &&
- intmap->interrupt == irq) {
- *interrupt = intmap->cinterrupt;
- goto success;
- }
- }
-
-fail:
- return 0;
-
-success:
- if (pci_irq_verbose)
- printk("%s: Routing bus[%2x] slot[%2x] to INO[%02x]\n",
- pbm->name,
- pdev->bus->number, PCI_SLOT(pdev->devfn),
- *interrupt);
- return 1;
-}
-
static void __init pdev_fixup_irq(struct pci_dev *pdev)
{
struct pcidev_cookie *pcp = pdev->sysdata;
- struct pci_pbm_info *pbm = pcp->pbm;
- struct pci_controller_info *p = pbm->parent;
- unsigned int portid = pbm->portid;
- unsigned int prom_irq;
- struct device_node *dp = pcp->prom_node;
- struct property *prop;
-
- /* If this is an empty EBUS device, sometimes OBP fails to
- * give it a valid fully specified interrupts property.
- * The EBUS hooked up to SunHME on PCI I/O boards of
- * Ex000 systems is one such case.
- *
- * The interrupt is not important so just ignore it.
- */
- if (pdev->vendor == PCI_VENDOR_ID_SUN &&
- pdev->device == PCI_DEVICE_ID_SUN_EBUS &&
- !dp->child) {
- pdev->irq = 0;
- return;
- }
+ struct of_device *op = pcp->op;
- prop = of_find_property(dp, "interrupts", NULL);
- if (!prop) {
- pdev->irq = 0;
+ if (op->irqs[0] == 0xffffffff) {
+ pdev->irq = PCI_IRQ_NONE;
return;
}
- prom_irq = *(unsigned int *) prop->value;
-
- if (tlb_type != hypervisor) {
- /* Fully specified already? */
- if (((prom_irq & PCI_IRQ_IGN) >> 6) == portid) {
- pdev->irq = p->irq_build(pbm, pdev, prom_irq);
- goto have_irq;
- }
-
- /* An onboard device? (bit 5 set) */
- if ((prom_irq & PCI_IRQ_INO) & 0x20) {
- pdev->irq = p->irq_build(pbm, pdev, (portid << 6 | prom_irq));
- goto have_irq;
- }
- }
-
- /* Can we find a matching entry in the interrupt-map? */
- if (pci_intmap_match(pdev, &prom_irq)) {
- pdev->irq = p->irq_build(pbm, pdev, (portid << 6) | prom_irq);
- goto have_irq;
- }
-
- /* Ok, we have to do it the hard way. */
- {
- unsigned int bus, slot, line;
-
- bus = (pbm == &pbm->parent->pbm_B) ? (1 << 4) : 0;
-
- /* If we have a legal interrupt property, use it as
- * the IRQ line.
- */
- if (prom_irq > 0 && prom_irq < 5) {
- line = ((prom_irq - 1) & 3);
- } else {
- u8 pci_irq_line;
- /* Else just directly consult PCI config space. */
- pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pci_irq_line);
- line = ((pci_irq_line - 1) & 3);
- }
-
- /* Now figure out the slot.
- *
- * Basically, device number zero on the top-level bus is
- * always the PCI host controller. Slot 0 is then device 1.
- * PBM A supports two external slots (0 and 1), and PBM B
- * supports 4 external slots (0, 1, 2, and 3). On-board PCI
- * devices are wired to device numbers outside of these
- * ranges. -DaveM
- */
- if (pdev->bus->number == pbm->pci_first_busno) {
- slot = PCI_SLOT(pdev->devfn) - pbm->pci_first_slot;
- } else {
- struct pci_dev *bus_dev;
-
- /* Underneath a bridge, use slot number of parent
- * bridge which is closest to the PBM.
- */
- bus_dev = pdev->bus->self;
- while (bus_dev->bus &&
- bus_dev->bus->number != pbm->pci_first_busno)
- bus_dev = bus_dev->bus->self;
-
- slot = PCI_SLOT(bus_dev->devfn) - pbm->pci_first_slot;
- }
- slot = slot << 2;
-
- pdev->irq = p->irq_build(pbm, pdev,
- ((portid << 6) & PCI_IRQ_IGN) |
- (bus | slot | line));
- }
+ pdev->irq = op->irqs[0];
-have_irq:
pci_write_config_byte(pdev, PCI_INTERRUPT_LINE,
pdev->irq & PCI_IRQ_INO);
}
#include <asm/irq.h>
#include <asm/starfire.h>
#include <asm/prom.h>
+#include <asm/of_device.h>
#include "pci_impl.h"
#include "iommu_common.h"
.write = psycho_write_pci_cfg,
};
-/* PSYCHO interrupt mapping support. */
-#define PSYCHO_IMAP_A_SLOT0 0x0c00UL
-#define PSYCHO_IMAP_B_SLOT0 0x0c20UL
-static unsigned long psycho_pcislot_imap_offset(unsigned long ino)
-{
- unsigned int bus = (ino & 0x10) >> 4;
- unsigned int slot = (ino & 0x0c) >> 2;
-
- if (bus == 0)
- return PSYCHO_IMAP_A_SLOT0 + (slot * 8);
- else
- return PSYCHO_IMAP_B_SLOT0 + (slot * 8);
-}
-
-#define PSYCHO_IMAP_SCSI 0x1000UL
-#define PSYCHO_IMAP_ETH 0x1008UL
-#define PSYCHO_IMAP_BPP 0x1010UL
-#define PSYCHO_IMAP_AU_REC 0x1018UL
-#define PSYCHO_IMAP_AU_PLAY 0x1020UL
-#define PSYCHO_IMAP_PFAIL 0x1028UL
-#define PSYCHO_IMAP_KMS 0x1030UL
-#define PSYCHO_IMAP_FLPY 0x1038UL
-#define PSYCHO_IMAP_SHW 0x1040UL
-#define PSYCHO_IMAP_KBD 0x1048UL
-#define PSYCHO_IMAP_MS 0x1050UL
-#define PSYCHO_IMAP_SER 0x1058UL
-#define PSYCHO_IMAP_TIM0 0x1060UL
-#define PSYCHO_IMAP_TIM1 0x1068UL
-#define PSYCHO_IMAP_UE 0x1070UL
-#define PSYCHO_IMAP_CE 0x1078UL
-#define PSYCHO_IMAP_A_ERR 0x1080UL
-#define PSYCHO_IMAP_B_ERR 0x1088UL
-#define PSYCHO_IMAP_PMGMT 0x1090UL
-#define PSYCHO_IMAP_GFX 0x1098UL
-#define PSYCHO_IMAP_EUPA 0x10a0UL
-
-static unsigned long __onboard_imap_off[] = {
-/*0x20*/ PSYCHO_IMAP_SCSI,
-/*0x21*/ PSYCHO_IMAP_ETH,
-/*0x22*/ PSYCHO_IMAP_BPP,
-/*0x23*/ PSYCHO_IMAP_AU_REC,
-/*0x24*/ PSYCHO_IMAP_AU_PLAY,
-/*0x25*/ PSYCHO_IMAP_PFAIL,
-/*0x26*/ PSYCHO_IMAP_KMS,
-/*0x27*/ PSYCHO_IMAP_FLPY,
-/*0x28*/ PSYCHO_IMAP_SHW,
-/*0x29*/ PSYCHO_IMAP_KBD,
-/*0x2a*/ PSYCHO_IMAP_MS,
-/*0x2b*/ PSYCHO_IMAP_SER,
-/*0x2c*/ PSYCHO_IMAP_TIM0,
-/*0x2d*/ PSYCHO_IMAP_TIM1,
-/*0x2e*/ PSYCHO_IMAP_UE,
-/*0x2f*/ PSYCHO_IMAP_CE,
-/*0x30*/ PSYCHO_IMAP_A_ERR,
-/*0x31*/ PSYCHO_IMAP_B_ERR,
-/*0x32*/ PSYCHO_IMAP_PMGMT
-};
-#define PSYCHO_ONBOARD_IRQ_BASE 0x20
-#define PSYCHO_ONBOARD_IRQ_LAST 0x32
-#define psycho_onboard_imap_offset(__ino) \
- __onboard_imap_off[(__ino) - PSYCHO_ONBOARD_IRQ_BASE]
-
-#define PSYCHO_ICLR_A_SLOT0 0x1400UL
-#define PSYCHO_ICLR_SCSI 0x1800UL
-
-#define psycho_iclr_offset(ino) \
- ((ino & 0x20) ? (PSYCHO_ICLR_SCSI + (((ino) & 0x1f) << 3)) : \
- (PSYCHO_ICLR_A_SLOT0 + (((ino) & 0x1f)<<3)))
-
-static unsigned int psycho_irq_build(struct pci_pbm_info *pbm,
- struct pci_dev *pdev,
- unsigned int ino)
-{
- unsigned long imap, iclr;
- unsigned long imap_off, iclr_off;
- int inofixup = 0;
-
- ino &= PCI_IRQ_INO;
- if (ino < PSYCHO_ONBOARD_IRQ_BASE) {
- /* PCI slot */
- imap_off = psycho_pcislot_imap_offset(ino);
- } else {
- /* Onboard device */
- if (ino > PSYCHO_ONBOARD_IRQ_LAST) {
- prom_printf("psycho_irq_build: Wacky INO [%x]\n", ino);
- prom_halt();
- }
- imap_off = psycho_onboard_imap_offset(ino);
- }
-
- /* Now build the IRQ bucket. */
- imap = pbm->controller_regs + imap_off;
- imap += 4;
-
- iclr_off = psycho_iclr_offset(ino);
- iclr = pbm->controller_regs + iclr_off;
- iclr += 4;
-
- if ((ino & 0x20) == 0)
- inofixup = ino & 0x03;
-
- return build_irq(inofixup, iclr, imap);
-}
-
/* PSYCHO error handling support. */
enum psycho_error_type {
UE_ERR, CE_ERR, PCI_ERR
#define PSYCHO_ECCCTRL_EE 0x8000000000000000UL /* Enable ECC Checking */
#define PSYCHO_ECCCTRL_UE 0x4000000000000000UL /* Enable UE Interrupts */
#define PSYCHO_ECCCTRL_CE 0x2000000000000000UL /* Enable CE INterrupts */
-#define PSYCHO_UE_INO 0x2e
-#define PSYCHO_CE_INO 0x2f
-#define PSYCHO_PCIERR_A_INO 0x30
-#define PSYCHO_PCIERR_B_INO 0x31
static void psycho_register_error_handlers(struct pci_controller_info *p)
{
struct pci_pbm_info *pbm = &p->pbm_A; /* arbitrary */
+ struct of_device *op = of_find_device_by_node(pbm->prom_node);
unsigned long base = p->pbm_A.controller_regs;
- unsigned int irq, portid = pbm->portid;
u64 tmp;
- /* Build IRQs and register handlers. */
- irq = psycho_irq_build(pbm, NULL, (portid << 6) | PSYCHO_UE_INO);
- if (request_irq(irq, psycho_ue_intr,
- SA_SHIRQ, "PSYCHO UE", p) < 0) {
- prom_printf("PSYCHO%d: Cannot register UE interrupt.\n",
- p->index);
- prom_halt();
- }
+ if (!op)
+ return;
- irq = psycho_irq_build(pbm, NULL, (portid << 6) | PSYCHO_CE_INO);
- if (request_irq(irq, psycho_ce_intr,
- SA_SHIRQ, "PSYCHO CE", p) < 0) {
- prom_printf("PSYCHO%d: Cannot register CE interrupt.\n",
- p->index);
- prom_halt();
- }
+ /* Psycho interrupt property order is:
+ * 0: PCIERR PBM B INO
+ * 1: UE ERR
+ * 2: CE ERR
+ * 3: POWER FAIL
+ * 4: SPARE HARDWARE
+ * 5: PCIERR PBM A INO
+ */
- pbm = &p->pbm_A;
- irq = psycho_irq_build(pbm, NULL, (portid << 6) | PSYCHO_PCIERR_A_INO);
- if (request_irq(irq, psycho_pcierr_intr,
- SA_SHIRQ, "PSYCHO PCIERR", &p->pbm_A) < 0) {
- prom_printf("PSYCHO%d(PBMA): Cannot register PciERR interrupt.\n",
- p->index);
- prom_halt();
- }
+ if (op->num_irqs < 6)
+ return;
- pbm = &p->pbm_B;
- irq = psycho_irq_build(pbm, NULL, (portid << 6) | PSYCHO_PCIERR_B_INO);
- if (request_irq(irq, psycho_pcierr_intr,
- SA_SHIRQ, "PSYCHO PCIERR", &p->pbm_B) < 0) {
- prom_printf("PSYCHO%d(PBMB): Cannot register PciERR interrupt.\n",
- p->index);
- prom_halt();
- }
+ request_irq(op->irqs[1], psycho_ue_intr, SA_SHIRQ, "PSYCHO UE", p);
+ request_irq(op->irqs[2], psycho_ce_intr, SA_SHIRQ, "PSYCHO CE", p);
+ request_irq(op->irqs[5], psycho_pcierr_intr, SA_SHIRQ,
+ "PSYCHO PCIERR-A", &p->pbm_A);
+ request_irq(op->irqs[0], psycho_pcierr_intr, SA_SHIRQ,
+ "PSYCHO PCIERR-B", &p->pbm_B);
/* Enable UE and CE interrupts for controller. */
psycho_write(base + PSYCHO_ECC_CTRL,
p->index = pci_num_controllers++;
p->pbms_same_domain = 0;
p->scan_bus = psycho_scan_bus;
- p->irq_build = psycho_irq_build;
p->base_address_update = psycho_base_address_update;
p->resource_adjust = psycho_resource_adjust;
p->pci_ops = &psycho_ops;
.write = sabre_write_pci_cfg,
};
-static unsigned long sabre_pcislot_imap_offset(unsigned long ino)
-{
- unsigned int bus = (ino & 0x10) >> 4;
- unsigned int slot = (ino & 0x0c) >> 2;
-
- if (bus == 0)
- return SABRE_IMAP_A_SLOT0 + (slot * 8);
- else
- return SABRE_IMAP_B_SLOT0 + (slot * 8);
-}
-
-static unsigned long __onboard_imap_off[] = {
-/*0x20*/ SABRE_IMAP_SCSI,
-/*0x21*/ SABRE_IMAP_ETH,
-/*0x22*/ SABRE_IMAP_BPP,
-/*0x23*/ SABRE_IMAP_AU_REC,
-/*0x24*/ SABRE_IMAP_AU_PLAY,
-/*0x25*/ SABRE_IMAP_PFAIL,
-/*0x26*/ SABRE_IMAP_KMS,
-/*0x27*/ SABRE_IMAP_FLPY,
-/*0x28*/ SABRE_IMAP_SHW,
-/*0x29*/ SABRE_IMAP_KBD,
-/*0x2a*/ SABRE_IMAP_MS,
-/*0x2b*/ SABRE_IMAP_SER,
-/*0x2c*/ 0 /* reserved */,
-/*0x2d*/ 0 /* reserved */,
-/*0x2e*/ SABRE_IMAP_UE,
-/*0x2f*/ SABRE_IMAP_CE,
-/*0x30*/ SABRE_IMAP_PCIERR,
-};
-#define SABRE_ONBOARD_IRQ_BASE 0x20
-#define SABRE_ONBOARD_IRQ_LAST 0x30
-#define sabre_onboard_imap_offset(__ino) \
- __onboard_imap_off[(__ino) - SABRE_ONBOARD_IRQ_BASE]
-
-#define sabre_iclr_offset(ino) \
- ((ino & 0x20) ? (SABRE_ICLR_SCSI + (((ino) & 0x1f) << 3)) : \
- (SABRE_ICLR_A_SLOT0 + (((ino) & 0x1f)<<3)))
-
-/* When a device lives behind a bridge deeper in the PCI bus topology
- * than APB, a special sequence must run to make sure all pending DMA
- * transfers at the time of IRQ delivery are visible in the coherency
- * domain by the cpu. This sequence is to perform a read on the far
- * side of the non-APB bridge, then perform a read of Sabre's DMA
- * write-sync register.
- */
-static void sabre_wsync_handler(unsigned int ino, void *_arg1, void *_arg2)
-{
- struct pci_dev *pdev = _arg1;
- unsigned long sync_reg = (unsigned long) _arg2;
- u16 _unused;
-
- pci_read_config_word(pdev, PCI_VENDOR_ID, &_unused);
- sabre_read(sync_reg);
-}
-
-static unsigned int sabre_irq_build(struct pci_pbm_info *pbm,
- struct pci_dev *pdev,
- unsigned int ino)
-{
- unsigned long imap, iclr;
- unsigned long imap_off, iclr_off;
- int inofixup = 0;
- int virt_irq;
-
- ino &= PCI_IRQ_INO;
- if (ino < SABRE_ONBOARD_IRQ_BASE) {
- /* PCI slot */
- imap_off = sabre_pcislot_imap_offset(ino);
- } else {
- /* onboard device */
- if (ino > SABRE_ONBOARD_IRQ_LAST) {
- prom_printf("sabre_irq_build: Wacky INO [%x]\n", ino);
- prom_halt();
- }
- imap_off = sabre_onboard_imap_offset(ino);
- }
-
- /* Now build the IRQ bucket. */
- imap = pbm->controller_regs + imap_off;
- imap += 4;
-
- iclr_off = sabre_iclr_offset(ino);
- iclr = pbm->controller_regs + iclr_off;
- iclr += 4;
-
- if ((ino & 0x20) == 0)
- inofixup = ino & 0x03;
-
- virt_irq = build_irq(inofixup, iclr, imap);
-
- if (pdev) {
- struct pcidev_cookie *pcp = pdev->sysdata;
-
- if (pdev->bus->number != pcp->pbm->pci_first_busno) {
- struct pci_controller_info *p = pcp->pbm->parent;
-
- irq_install_pre_handler(virt_irq,
- sabre_wsync_handler,
- pdev,
- (void *)
- p->pbm_A.controller_regs +
- SABRE_WRSYNC);
- }
- }
- return virt_irq;
-}
-
/* SABRE error handling support. */
static void sabre_check_iommu_error(struct pci_controller_info *p,
unsigned long afsr,
return IRQ_HANDLED;
}
-/* XXX What about PowerFail/PowerManagement??? -DaveM */
-#define SABRE_UE_INO 0x2e
-#define SABRE_CE_INO 0x2f
-#define SABRE_PCIERR_INO 0x30
static void sabre_register_error_handlers(struct pci_controller_info *p)
{
struct pci_pbm_info *pbm = &p->pbm_A; /* arbitrary */
+ struct device_node *dp = pbm->prom_node;
+ struct of_device *op;
unsigned long base = pbm->controller_regs;
- unsigned long irq, portid = pbm->portid;
u64 tmp;
+ if (pbm->chip_type == PBM_CHIP_TYPE_SABRE)
+ dp = dp->parent;
+
+ op = of_find_device_by_node(dp);
+ if (!op)
+ return;
+
+ /* Sabre/Hummingbird IRQ property layout is:
+ * 0: PCI ERR
+ * 1: UE ERR
+ * 2: CE ERR
+ * 3: POWER FAIL
+ */
+ if (op->num_irqs < 4)
+ return;
+
/* We clear the error bits in the appropriate AFSR before
* registering the handler so that we don't get spurious
* interrupts.
(SABRE_UEAFSR_PDRD | SABRE_UEAFSR_PDWR |
SABRE_UEAFSR_SDRD | SABRE_UEAFSR_SDWR |
SABRE_UEAFSR_SDTE | SABRE_UEAFSR_PDTE));
- irq = sabre_irq_build(pbm, NULL, (portid << 6) | SABRE_UE_INO);
- if (request_irq(irq, sabre_ue_intr,
- SA_SHIRQ, "SABRE UE", p) < 0) {
- prom_printf("SABRE%d: Cannot register UE interrupt.\n",
- p->index);
- prom_halt();
- }
+
+ request_irq(op->irqs[1], sabre_ue_intr, SA_SHIRQ, "SABRE UE", p);
sabre_write(base + SABRE_CE_AFSR,
(SABRE_CEAFSR_PDRD | SABRE_CEAFSR_PDWR |
SABRE_CEAFSR_SDRD | SABRE_CEAFSR_SDWR));
- irq = sabre_irq_build(pbm, NULL, (portid << 6) | SABRE_CE_INO);
- if (request_irq(irq, sabre_ce_intr,
- SA_SHIRQ, "SABRE CE", p) < 0) {
- prom_printf("SABRE%d: Cannot register CE interrupt.\n",
- p->index);
- prom_halt();
- }
- irq = sabre_irq_build(pbm, NULL, (portid << 6) | SABRE_PCIERR_INO);
- if (request_irq(irq, sabre_pcierr_intr,
- SA_SHIRQ, "SABRE PCIERR", p) < 0) {
- prom_printf("SABRE%d: Cannot register PciERR interrupt.\n",
- p->index);
- prom_halt();
- }
+ request_irq(op->irqs[2], sabre_ce_intr, SA_SHIRQ, "SABRE CE", p);
+ request_irq(op->irqs[0], sabre_pcierr_intr, SA_SHIRQ,
+ "SABRE PCIERR", p);
tmp = sabre_read(base + SABRE_PCICTRL);
tmp |= SABRE_PCICTRL_ERREN;
p->index = pci_num_controllers++;
p->pbms_same_domain = 1;
p->scan_bus = sabre_scan_bus;
- p->irq_build = sabre_irq_build;
p->base_address_update = sabre_base_address_update;
p->resource_adjust = sabre_resource_adjust;
p->pci_ops = &sabre_ops;
.write = schizo_write_pci_cfg,
};
-/* SCHIZO interrupt mapping support. Unlike Psycho, for this controller the
- * imap/iclr registers are per-PBM.
- */
-#define SCHIZO_IMAP_BASE 0x1000UL
-#define SCHIZO_ICLR_BASE 0x1400UL
-
-static unsigned long schizo_imap_offset(unsigned long ino)
-{
- return SCHIZO_IMAP_BASE + (ino * 8UL);
-}
-
-static unsigned long schizo_iclr_offset(unsigned long ino)
-{
- return SCHIZO_ICLR_BASE + (ino * 8UL);
-}
-
-static void tomatillo_wsync_handler(unsigned int ino, void *_arg1, void *_arg2)
-{
- unsigned long sync_reg = (unsigned long) _arg2;
- u64 mask = 1UL << (ino & IMAP_INO);
- u64 val;
- int limit;
-
- schizo_write(sync_reg, mask);
-
- limit = 100000;
- val = 0;
- while (--limit) {
- val = schizo_read(sync_reg);
- if (!(val & mask))
- break;
- }
- if (limit <= 0) {
- printk("tomatillo_wsync_handler: DMA won't sync [%lx:%lx]\n",
- val, mask);
- }
-
- if (_arg1) {
- static unsigned char cacheline[64]
- __attribute__ ((aligned (64)));
-
- __asm__ __volatile__("rd %%fprs, %0\n\t"
- "or %0, %4, %1\n\t"
- "wr %1, 0x0, %%fprs\n\t"
- "stda %%f0, [%5] %6\n\t"
- "wr %0, 0x0, %%fprs\n\t"
- "membar #Sync"
- : "=&r" (mask), "=&r" (val)
- : "0" (mask), "1" (val),
- "i" (FPRS_FEF), "r" (&cacheline[0]),
- "i" (ASI_BLK_COMMIT_P));
- }
-}
-
-static unsigned long schizo_ino_to_iclr(struct pci_pbm_info *pbm,
- unsigned int ino)
-{
- ino &= PCI_IRQ_INO;
- return pbm->pbm_regs + schizo_iclr_offset(ino) + 4;
-}
-
-static unsigned long schizo_ino_to_imap(struct pci_pbm_info *pbm,
- unsigned int ino)
-{
- ino &= PCI_IRQ_INO;
- return pbm->pbm_regs + schizo_imap_offset(ino) + 4;
-}
-
-static unsigned int schizo_irq_build(struct pci_pbm_info *pbm,
- struct pci_dev *pdev,
- unsigned int ino)
-{
- unsigned long imap, iclr;
- int ign_fixup;
- int virt_irq;
-
- ino &= PCI_IRQ_INO;
-
- /* Now build the IRQ bucket. */
- imap = schizo_ino_to_imap(pbm, ino);
- iclr = schizo_ino_to_iclr(pbm, ino);
-
- /* On Schizo, no inofixup occurs. This is because each
- * INO has it's own IMAP register. On Psycho and Sabre
- * there is only one IMAP register for each PCI slot even
- * though four different INOs can be generated by each
- * PCI slot.
- *
- * But, for JBUS variants (essentially, Tomatillo), we have
- * to fixup the lowest bit of the interrupt group number.
- */
- ign_fixup = 0;
- if (pbm->chip_type == PBM_CHIP_TYPE_TOMATILLO) {
- if (pbm->portid & 1)
- ign_fixup = (1 << 6);
- }
-
- virt_irq = build_irq(ign_fixup, iclr, imap);
-
- if (pdev && pbm->chip_type == PBM_CHIP_TYPE_TOMATILLO) {
- irq_install_pre_handler(virt_irq,
- tomatillo_wsync_handler,
- ((pbm->chip_version <= 4) ?
- (void *) 1 : (void *) 0),
- (void *) pbm->sync_reg);
- }
-
- return virt_irq;
-}
-
/* SCHIZO error handling support. */
enum schizo_error_type {
UE_ERR, CE_ERR, PCI_ERR, SAFARI_ERR
return &p->pbm_A;
}
-static void schizo_clear_other_err_intr(struct pci_controller_info *p, int irq)
-{
- struct pci_pbm_info *pbm;
- unsigned long iclr;
-
- /* Do not clear the interrupt for the other PCI bus.
- *
- * This "ACK both PBM IRQs" only needs to be performed
- * for chip-wide error interrupts.
- */
- if ((irq & IMAP_INO) == SCHIZO_PCIERR_A_INO ||
- (irq & IMAP_INO) == SCHIZO_PCIERR_B_INO)
- return;
-
- pbm = pbm_for_ino(p, irq);
- if (pbm == &p->pbm_A)
- pbm = &p->pbm_B;
- else
- pbm = &p->pbm_A;
-
- schizo_irq_build(pbm, NULL,
- (pbm->portid << 6) | (irq & IMAP_INO));
-
- iclr = schizo_ino_to_iclr(pbm,
- (pbm->portid << 6) | (irq & IMAP_INO));
- upa_writel(ICLR_IDLE, iclr);
-}
-
#define SCHIZO_STC_ERR 0xb800UL /* --> 0xba00 */
#define SCHIZO_STC_TAG 0xba00UL /* --> 0xba80 */
#define SCHIZO_STC_LINE 0xbb00UL /* --> 0xbb80 */
/* Interrogate IOMMU for error status. */
schizo_check_iommu_error(p, UE_ERR);
- schizo_clear_other_err_intr(p, irq);
-
return IRQ_HANDLED;
}
printk("(none)");
printk("]\n");
- schizo_clear_other_err_intr(p, irq);
-
return IRQ_HANDLED;
}
if (error_bits & (SCHIZO_PCIAFSR_PPERR | SCHIZO_PCIAFSR_SPERR))
pci_scan_for_parity_error(p, pbm, pbm->pci_bus);
- schizo_clear_other_err_intr(p, irq);
-
return IRQ_HANDLED;
}
printk("PCI%d: Unexpected Safari/JBUS error interrupt, errlog[%016lx]\n",
p->index, errlog);
- schizo_clear_other_err_intr(p, irq);
return IRQ_HANDLED;
}
p->index);
schizo_check_iommu_error(p, SAFARI_ERR);
- schizo_clear_other_err_intr(p, irq);
return IRQ_HANDLED;
}
static void tomatillo_register_error_handlers(struct pci_controller_info *p)
{
struct pci_pbm_info *pbm;
- unsigned int irq;
+ struct of_device *op;
u64 tmp, err_mask, err_no_mask;
- /* Build IRQs and register handlers. */
+ /* Tomatillo IRQ property layout is:
+ * 0: PCIERR
+ * 1: UE ERR
+ * 2: CE ERR
+ * 3: SERR
+ * 4: POWER FAIL?
+ */
+
pbm = pbm_for_ino(p, SCHIZO_UE_INO);
- irq = schizo_irq_build(pbm, NULL, (pbm->portid << 6) | SCHIZO_UE_INO);
- if (request_irq(irq, schizo_ue_intr,
- SA_SHIRQ, "TOMATILLO UE", p) < 0) {
- prom_printf("%s: Cannot register UE interrupt.\n",
- pbm->name);
- prom_halt();
- }
- tmp = upa_readl(schizo_ino_to_imap(pbm, (pbm->portid << 6) | SCHIZO_UE_INO));
- upa_writel(tmp, (pbm->pbm_regs +
- schizo_imap_offset(SCHIZO_UE_INO) + 4));
+ op = of_find_device_by_node(pbm->prom_node);
+ if (op)
+ request_irq(op->irqs[1], schizo_ue_intr, SA_SHIRQ,
+ "TOMATILLO_UE", p);
pbm = pbm_for_ino(p, SCHIZO_CE_INO);
- irq = schizo_irq_build(pbm, NULL, (pbm->portid << 6) | SCHIZO_CE_INO);
- if (request_irq(irq, schizo_ce_intr,
- SA_SHIRQ, "TOMATILLO CE", p) < 0) {
- prom_printf("%s: Cannot register CE interrupt.\n",
- pbm->name);
- prom_halt();
- }
- tmp = upa_readl(schizo_ino_to_imap(pbm, (pbm->portid << 6) | SCHIZO_CE_INO));
- upa_writel(tmp, (pbm->pbm_regs +
- schizo_imap_offset(SCHIZO_CE_INO) + 4));
+ op = of_find_device_by_node(pbm->prom_node);
+ if (op)
+ request_irq(op->irqs[2], schizo_ce_intr, SA_SHIRQ,
+ "TOMATILLO CE", p);
pbm = pbm_for_ino(p, SCHIZO_PCIERR_A_INO);
- irq = schizo_irq_build(pbm, NULL, ((pbm->portid << 6) |
- SCHIZO_PCIERR_A_INO));
- if (request_irq(irq, schizo_pcierr_intr,
- SA_SHIRQ, "TOMATILLO PCIERR", pbm) < 0) {
- prom_printf("%s: Cannot register PBM A PciERR interrupt.\n",
- pbm->name);
- prom_halt();
- }
- tmp = upa_readl(schizo_ino_to_imap(pbm, ((pbm->portid << 6) |
- SCHIZO_PCIERR_A_INO)));
- upa_writel(tmp, (pbm->pbm_regs +
- schizo_imap_offset(SCHIZO_PCIERR_A_INO) + 4));
+ op = of_find_device_by_node(pbm->prom_node);
+ if (op)
+ request_irq(op->irqs[0], schizo_pcierr_intr, SA_SHIRQ,
+ "TOMATILLO PCIERR-A", pbm);
+
pbm = pbm_for_ino(p, SCHIZO_PCIERR_B_INO);
- irq = schizo_irq_build(pbm, NULL, ((pbm->portid << 6) |
- SCHIZO_PCIERR_B_INO));
- if (request_irq(irq, schizo_pcierr_intr,
- SA_SHIRQ, "TOMATILLO PCIERR", pbm) < 0) {
- prom_printf("%s: Cannot register PBM B PciERR interrupt.\n",
- pbm->name);
- prom_halt();
- }
- tmp = upa_readl(schizo_ino_to_imap(pbm, ((pbm->portid << 6) |
- SCHIZO_PCIERR_B_INO)));
- upa_writel(tmp, (pbm->pbm_regs +
- schizo_imap_offset(SCHIZO_PCIERR_B_INO) + 4));
+ op = of_find_device_by_node(pbm->prom_node);
+ if (op)
+ request_irq(op->irqs[0], schizo_pcierr_intr, SA_SHIRQ,
+ "TOMATILLO PCIERR-B", pbm);
pbm = pbm_for_ino(p, SCHIZO_SERR_INO);
- irq = schizo_irq_build(pbm, NULL, (pbm->portid << 6) | SCHIZO_SERR_INO);
- if (request_irq(irq, schizo_safarierr_intr,
- SA_SHIRQ, "TOMATILLO SERR", p) < 0) {
- prom_printf("%s: Cannot register SafariERR interrupt.\n",
- pbm->name);
- prom_halt();
- }
- tmp = upa_readl(schizo_ino_to_imap(pbm, ((pbm->portid << 6) |
- SCHIZO_SERR_INO)));
- upa_writel(tmp, (pbm->pbm_regs +
- schizo_imap_offset(SCHIZO_SERR_INO) + 4));
+ op = of_find_device_by_node(pbm->prom_node);
+ if (op)
+ request_irq(op->irqs[3], schizo_safarierr_intr, SA_SHIRQ,
+ "TOMATILLO SERR", p);
/* Enable UE and CE interrupts for controller. */
schizo_write(p->pbm_A.controller_regs + SCHIZO_ECC_CTRL,
static void schizo_register_error_handlers(struct pci_controller_info *p)
{
struct pci_pbm_info *pbm;
- unsigned int irq;
+ struct of_device *op;
u64 tmp, err_mask, err_no_mask;
- /* Build IRQs and register handlers. */
+ /* Schizo IRQ property layout is:
+ * 0: PCIERR
+ * 1: UE ERR
+ * 2: CE ERR
+ * 3: SERR
+ * 4: POWER FAIL?
+ */
+
pbm = pbm_for_ino(p, SCHIZO_UE_INO);
- irq = schizo_irq_build(pbm, NULL, (pbm->portid << 6) | SCHIZO_UE_INO);
- if (request_irq(irq, schizo_ue_intr,
- SA_SHIRQ, "SCHIZO UE", p) < 0) {
- prom_printf("%s: Cannot register UE interrupt.\n",
- pbm->name);
- prom_halt();
- }
- tmp = upa_readl(schizo_ino_to_imap(pbm, (pbm->portid << 6) | SCHIZO_UE_INO));
- upa_writel(tmp, (pbm->pbm_regs + schizo_imap_offset(SCHIZO_UE_INO) + 4));
+ op = of_find_device_by_node(pbm->prom_node);
+ if (op)
+ request_irq(op->irqs[1], schizo_ue_intr, SA_SHIRQ,
+ "SCHIZO_UE", p);
pbm = pbm_for_ino(p, SCHIZO_CE_INO);
- irq = schizo_irq_build(pbm, NULL, (pbm->portid << 6) | SCHIZO_CE_INO);
- if (request_irq(irq, schizo_ce_intr,
- SA_SHIRQ, "SCHIZO CE", p) < 0) {
- prom_printf("%s: Cannot register CE interrupt.\n",
- pbm->name);
- prom_halt();
- }
- tmp = upa_readl(schizo_ino_to_imap(pbm, (pbm->portid << 6) | SCHIZO_CE_INO));
- upa_writel(tmp, (pbm->pbm_regs + schizo_imap_offset(SCHIZO_CE_INO) + 4));
+ op = of_find_device_by_node(pbm->prom_node);
+ if (op)
+ request_irq(op->irqs[2], schizo_ce_intr, SA_SHIRQ,
+ "SCHIZO CE", p);
pbm = pbm_for_ino(p, SCHIZO_PCIERR_A_INO);
- irq = schizo_irq_build(pbm, NULL, (pbm->portid << 6) | SCHIZO_PCIERR_A_INO);
- if (request_irq(irq, schizo_pcierr_intr,
- SA_SHIRQ, "SCHIZO PCIERR", pbm) < 0) {
- prom_printf("%s: Cannot register PBM A PciERR interrupt.\n",
- pbm->name);
- prom_halt();
- }
- tmp = upa_readl(schizo_ino_to_imap(pbm, (pbm->portid << 6) | SCHIZO_PCIERR_A_INO));
- upa_writel(tmp, (pbm->pbm_regs + schizo_imap_offset(SCHIZO_PCIERR_A_INO) + 4));
+ op = of_find_device_by_node(pbm->prom_node);
+ if (op)
+ request_irq(op->irqs[0], schizo_pcierr_intr, SA_SHIRQ,
+ "SCHIZO PCIERR-A", pbm);
+
pbm = pbm_for_ino(p, SCHIZO_PCIERR_B_INO);
- irq = schizo_irq_build(pbm, NULL, (pbm->portid << 6) | SCHIZO_PCIERR_B_INO);
- if (request_irq(irq, schizo_pcierr_intr,
- SA_SHIRQ, "SCHIZO PCIERR", &p->pbm_B) < 0) {
- prom_printf("%s: Cannot register PBM B PciERR interrupt.\n",
- pbm->name);
- prom_halt();
- }
- tmp = upa_readl(schizo_ino_to_imap(pbm, (pbm->portid << 6) | SCHIZO_PCIERR_B_INO));
- upa_writel(tmp, (pbm->pbm_regs + schizo_imap_offset(SCHIZO_PCIERR_B_INO) + 4));
+ op = of_find_device_by_node(pbm->prom_node);
+ if (op)
+ request_irq(op->irqs[0], schizo_pcierr_intr, SA_SHIRQ,
+ "SCHIZO PCIERR-B", pbm);
pbm = pbm_for_ino(p, SCHIZO_SERR_INO);
- irq = schizo_irq_build(pbm, NULL, (pbm->portid << 6) | SCHIZO_SERR_INO);
- if (request_irq(irq, schizo_safarierr_intr,
- SA_SHIRQ, "SCHIZO SERR", p) < 0) {
- prom_printf("%s: Cannot register SafariERR interrupt.\n",
- pbm->name);
- prom_halt();
- }
- tmp = upa_readl(schizo_ino_to_imap(pbm, (pbm->portid << 6) | SCHIZO_SERR_INO));
- upa_writel(tmp, (pbm->pbm_regs + schizo_imap_offset(SCHIZO_SERR_INO) + 4));
+ op = of_find_device_by_node(pbm->prom_node);
+ if (op)
+ request_irq(op->irqs[3], schizo_safarierr_intr, SA_SHIRQ,
+ "SCHIZO SERR", p);
/* Enable UE and CE interrupts for controller. */
schizo_write(p->pbm_A.controller_regs + SCHIZO_ECC_CTRL,
p->scan_bus = (chip_type == PBM_CHIP_TYPE_TOMATILLO ?
tomatillo_scan_bus :
schizo_scan_bus);
- p->irq_build = schizo_irq_build;
p->base_address_update = schizo_base_address_update;
p->resource_adjust = schizo_resource_adjust;
p->pci_ops = &schizo_ops;
/* XXX register error interrupt handlers XXX */
}
-static unsigned int pci_sun4v_irq_build(struct pci_pbm_info *pbm,
- struct pci_dev *pdev,
- unsigned int devino)
-{
- u32 devhandle = pbm->devhandle;
-
- return sun4v_build_irq(devhandle, devino);
-}
-
static void pci_sun4v_base_address_update(struct pci_dev *pdev, int resource)
{
struct pcidev_cookie *pcp = pdev->sysdata;
p->pbms_same_domain = 0;
p->scan_bus = pci_sun4v_scan_bus;
- p->irq_build = pci_sun4v_irq_build;
p->base_address_update = pci_sun4v_base_address_update;
p->resource_adjust = pci_sun4v_resource_adjust;
p->pci_ops = &pci_sun4v_ops;
* 2 of the License, or (at your option) any later version.
*/
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/module.h>
#include <asm/prom.h>
+#include <asm/of_device.h>
#include <asm/oplib.h>
+#include <asm/irq.h>
+#include <asm/asi.h>
+#include <asm/upa.h>
static struct device_node *allnodes;
return ret;
}
+#ifdef CONFIG_PCI
+/* PSYCHO interrupt mapping support. */
+#define PSYCHO_IMAP_A_SLOT0 0x0c00UL
+#define PSYCHO_IMAP_B_SLOT0 0x0c20UL
+static unsigned long psycho_pcislot_imap_offset(unsigned long ino)
+{
+ unsigned int bus = (ino & 0x10) >> 4;
+ unsigned int slot = (ino & 0x0c) >> 2;
+
+ if (bus == 0)
+ return PSYCHO_IMAP_A_SLOT0 + (slot * 8);
+ else
+ return PSYCHO_IMAP_B_SLOT0 + (slot * 8);
+}
+
+#define PSYCHO_IMAP_SCSI 0x1000UL
+#define PSYCHO_IMAP_ETH 0x1008UL
+#define PSYCHO_IMAP_BPP 0x1010UL
+#define PSYCHO_IMAP_AU_REC 0x1018UL
+#define PSYCHO_IMAP_AU_PLAY 0x1020UL
+#define PSYCHO_IMAP_PFAIL 0x1028UL
+#define PSYCHO_IMAP_KMS 0x1030UL
+#define PSYCHO_IMAP_FLPY 0x1038UL
+#define PSYCHO_IMAP_SHW 0x1040UL
+#define PSYCHO_IMAP_KBD 0x1048UL
+#define PSYCHO_IMAP_MS 0x1050UL
+#define PSYCHO_IMAP_SER 0x1058UL
+#define PSYCHO_IMAP_TIM0 0x1060UL
+#define PSYCHO_IMAP_TIM1 0x1068UL
+#define PSYCHO_IMAP_UE 0x1070UL
+#define PSYCHO_IMAP_CE 0x1078UL
+#define PSYCHO_IMAP_A_ERR 0x1080UL
+#define PSYCHO_IMAP_B_ERR 0x1088UL
+#define PSYCHO_IMAP_PMGMT 0x1090UL
+#define PSYCHO_IMAP_GFX 0x1098UL
+#define PSYCHO_IMAP_EUPA 0x10a0UL
+
+static unsigned long __psycho_onboard_imap_off[] = {
+/*0x20*/ PSYCHO_IMAP_SCSI,
+/*0x21*/ PSYCHO_IMAP_ETH,
+/*0x22*/ PSYCHO_IMAP_BPP,
+/*0x23*/ PSYCHO_IMAP_AU_REC,
+/*0x24*/ PSYCHO_IMAP_AU_PLAY,
+/*0x25*/ PSYCHO_IMAP_PFAIL,
+/*0x26*/ PSYCHO_IMAP_KMS,
+/*0x27*/ PSYCHO_IMAP_FLPY,
+/*0x28*/ PSYCHO_IMAP_SHW,
+/*0x29*/ PSYCHO_IMAP_KBD,
+/*0x2a*/ PSYCHO_IMAP_MS,
+/*0x2b*/ PSYCHO_IMAP_SER,
+/*0x2c*/ PSYCHO_IMAP_TIM0,
+/*0x2d*/ PSYCHO_IMAP_TIM1,
+/*0x2e*/ PSYCHO_IMAP_UE,
+/*0x2f*/ PSYCHO_IMAP_CE,
+/*0x30*/ PSYCHO_IMAP_A_ERR,
+/*0x31*/ PSYCHO_IMAP_B_ERR,
+/*0x32*/ PSYCHO_IMAP_PMGMT
+};
+#define PSYCHO_ONBOARD_IRQ_BASE 0x20
+#define PSYCHO_ONBOARD_IRQ_LAST 0x32
+#define psycho_onboard_imap_offset(__ino) \
+ __psycho_onboard_imap_off[(__ino) - PSYCHO_ONBOARD_IRQ_BASE]
+
+#define PSYCHO_ICLR_A_SLOT0 0x1400UL
+#define PSYCHO_ICLR_SCSI 0x1800UL
+
+#define psycho_iclr_offset(ino) \
+ ((ino & 0x20) ? (PSYCHO_ICLR_SCSI + (((ino) & 0x1f) << 3)) : \
+ (PSYCHO_ICLR_A_SLOT0 + (((ino) & 0x1f)<<3)))
+
+static unsigned int psycho_irq_build(struct device_node *dp,
+ unsigned int ino,
+ void *_data)
+{
+ unsigned long controller_regs = (unsigned long) _data;
+ unsigned long imap, iclr;
+ unsigned long imap_off, iclr_off;
+ int inofixup = 0;
+
+ ino &= 0x3f;
+ if (ino < PSYCHO_ONBOARD_IRQ_BASE) {
+ /* PCI slot */
+ imap_off = psycho_pcislot_imap_offset(ino);
+ } else {
+ /* Onboard device */
+ if (ino > PSYCHO_ONBOARD_IRQ_LAST) {
+ prom_printf("psycho_irq_build: Wacky INO [%x]\n", ino);
+ prom_halt();
+ }
+ imap_off = psycho_onboard_imap_offset(ino);
+ }
+
+ /* Now build the IRQ bucket. */
+ imap = controller_regs + imap_off;
+ imap += 4;
+
+ iclr_off = psycho_iclr_offset(ino);
+ iclr = controller_regs + iclr_off;
+ iclr += 4;
+
+ if ((ino & 0x20) == 0)
+ inofixup = ino & 0x03;
+
+ return build_irq(inofixup, iclr, imap);
+}
+
+static void psycho_irq_trans_init(struct device_node *dp)
+{
+ struct linux_prom64_registers *regs;
+
+ dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
+ dp->irq_trans->irq_build = psycho_irq_build;
+
+ regs = of_get_property(dp, "reg", NULL);
+ dp->irq_trans->data = (void *) regs[2].phys_addr;
+}
+
+#define sabre_read(__reg) \
+({ u64 __ret; \
+ __asm__ __volatile__("ldxa [%1] %2, %0" \
+ : "=r" (__ret) \
+ : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \
+ : "memory"); \
+ __ret; \
+})
+
+struct sabre_irq_data {
+ unsigned long controller_regs;
+ unsigned int pci_first_busno;
+};
+#define SABRE_CONFIGSPACE 0x001000000UL
+#define SABRE_WRSYNC 0x1c20UL
+
+#define SABRE_CONFIG_BASE(CONFIG_SPACE) \
+ (CONFIG_SPACE | (1UL << 24))
+#define SABRE_CONFIG_ENCODE(BUS, DEVFN, REG) \
+ (((unsigned long)(BUS) << 16) | \
+ ((unsigned long)(DEVFN) << 8) | \
+ ((unsigned long)(REG)))
+
+/* When a device lives behind a bridge deeper in the PCI bus topology
+ * than APB, a special sequence must run to make sure all pending DMA
+ * transfers at the time of IRQ delivery are visible in the coherency
+ * domain by the cpu. This sequence is to perform a read on the far
+ * side of the non-APB bridge, then perform a read of Sabre's DMA
+ * write-sync register.
+ */
+static void sabre_wsync_handler(unsigned int ino, void *_arg1, void *_arg2)
+{
+ unsigned int phys_hi = (unsigned int) (unsigned long) _arg1;
+ struct sabre_irq_data *irq_data = _arg2;
+ unsigned long controller_regs = irq_data->controller_regs;
+ unsigned long sync_reg = controller_regs + SABRE_WRSYNC;
+ unsigned long config_space = controller_regs + SABRE_CONFIGSPACE;
+ unsigned int bus, devfn;
+ u16 _unused;
+
+ config_space = SABRE_CONFIG_BASE(config_space);
+
+ bus = (phys_hi >> 16) & 0xff;
+ devfn = (phys_hi >> 8) & 0xff;
+
+ config_space |= SABRE_CONFIG_ENCODE(bus, devfn, 0x00);
+
+ __asm__ __volatile__("membar #Sync\n\t"
+ "lduha [%1] %2, %0\n\t"
+ "membar #Sync"
+ : "=r" (_unused)
+ : "r" ((u16 *) config_space),
+ "i" (ASI_PHYS_BYPASS_EC_E_L)
+ : "memory");
+
+ sabre_read(sync_reg);
+}
+
+#define SABRE_IMAP_A_SLOT0 0x0c00UL
+#define SABRE_IMAP_B_SLOT0 0x0c20UL
+#define SABRE_IMAP_SCSI 0x1000UL
+#define SABRE_IMAP_ETH 0x1008UL
+#define SABRE_IMAP_BPP 0x1010UL
+#define SABRE_IMAP_AU_REC 0x1018UL
+#define SABRE_IMAP_AU_PLAY 0x1020UL
+#define SABRE_IMAP_PFAIL 0x1028UL
+#define SABRE_IMAP_KMS 0x1030UL
+#define SABRE_IMAP_FLPY 0x1038UL
+#define SABRE_IMAP_SHW 0x1040UL
+#define SABRE_IMAP_KBD 0x1048UL
+#define SABRE_IMAP_MS 0x1050UL
+#define SABRE_IMAP_SER 0x1058UL
+#define SABRE_IMAP_UE 0x1070UL
+#define SABRE_IMAP_CE 0x1078UL
+#define SABRE_IMAP_PCIERR 0x1080UL
+#define SABRE_IMAP_GFX 0x1098UL
+#define SABRE_IMAP_EUPA 0x10a0UL
+#define SABRE_ICLR_A_SLOT0 0x1400UL
+#define SABRE_ICLR_B_SLOT0 0x1480UL
+#define SABRE_ICLR_SCSI 0x1800UL
+#define SABRE_ICLR_ETH 0x1808UL
+#define SABRE_ICLR_BPP 0x1810UL
+#define SABRE_ICLR_AU_REC 0x1818UL
+#define SABRE_ICLR_AU_PLAY 0x1820UL
+#define SABRE_ICLR_PFAIL 0x1828UL
+#define SABRE_ICLR_KMS 0x1830UL
+#define SABRE_ICLR_FLPY 0x1838UL
+#define SABRE_ICLR_SHW 0x1840UL
+#define SABRE_ICLR_KBD 0x1848UL
+#define SABRE_ICLR_MS 0x1850UL
+#define SABRE_ICLR_SER 0x1858UL
+#define SABRE_ICLR_UE 0x1870UL
+#define SABRE_ICLR_CE 0x1878UL
+#define SABRE_ICLR_PCIERR 0x1880UL
+
+static unsigned long sabre_pcislot_imap_offset(unsigned long ino)
+{
+ unsigned int bus = (ino & 0x10) >> 4;
+ unsigned int slot = (ino & 0x0c) >> 2;
+
+ if (bus == 0)
+ return SABRE_IMAP_A_SLOT0 + (slot * 8);
+ else
+ return SABRE_IMAP_B_SLOT0 + (slot * 8);
+}
+
+static unsigned long __sabre_onboard_imap_off[] = {
+/*0x20*/ SABRE_IMAP_SCSI,
+/*0x21*/ SABRE_IMAP_ETH,
+/*0x22*/ SABRE_IMAP_BPP,
+/*0x23*/ SABRE_IMAP_AU_REC,
+/*0x24*/ SABRE_IMAP_AU_PLAY,
+/*0x25*/ SABRE_IMAP_PFAIL,
+/*0x26*/ SABRE_IMAP_KMS,
+/*0x27*/ SABRE_IMAP_FLPY,
+/*0x28*/ SABRE_IMAP_SHW,
+/*0x29*/ SABRE_IMAP_KBD,
+/*0x2a*/ SABRE_IMAP_MS,
+/*0x2b*/ SABRE_IMAP_SER,
+/*0x2c*/ 0 /* reserved */,
+/*0x2d*/ 0 /* reserved */,
+/*0x2e*/ SABRE_IMAP_UE,
+/*0x2f*/ SABRE_IMAP_CE,
+/*0x30*/ SABRE_IMAP_PCIERR,
+};
+#define SABRE_ONBOARD_IRQ_BASE 0x20
+#define SABRE_ONBOARD_IRQ_LAST 0x30
+#define sabre_onboard_imap_offset(__ino) \
+ __sabre_onboard_imap_off[(__ino) - SABRE_ONBOARD_IRQ_BASE]
+
+#define sabre_iclr_offset(ino) \
+ ((ino & 0x20) ? (SABRE_ICLR_SCSI + (((ino) & 0x1f) << 3)) : \
+ (SABRE_ICLR_A_SLOT0 + (((ino) & 0x1f)<<3)))
+
+static unsigned int sabre_irq_build(struct device_node *dp,
+ unsigned int ino,
+ void *_data)
+{
+ struct sabre_irq_data *irq_data = _data;
+ unsigned long controller_regs = irq_data->controller_regs;
+ struct linux_prom_pci_registers *regs;
+ unsigned long imap, iclr;
+ unsigned long imap_off, iclr_off;
+ int inofixup = 0;
+ int virt_irq;
+
+ ino &= 0x3f;
+ if (ino < SABRE_ONBOARD_IRQ_BASE) {
+ /* PCI slot */
+ imap_off = sabre_pcislot_imap_offset(ino);
+ } else {
+ /* onboard device */
+ if (ino > SABRE_ONBOARD_IRQ_LAST) {
+ prom_printf("sabre_irq_build: Wacky INO [%x]\n", ino);
+ prom_halt();
+ }
+ imap_off = sabre_onboard_imap_offset(ino);
+ }
+
+ /* Now build the IRQ bucket. */
+ imap = controller_regs + imap_off;
+ imap += 4;
+
+ iclr_off = sabre_iclr_offset(ino);
+ iclr = controller_regs + iclr_off;
+ iclr += 4;
+
+ if ((ino & 0x20) == 0)
+ inofixup = ino & 0x03;
+
+ virt_irq = build_irq(inofixup, iclr, imap);
+
+ regs = of_get_property(dp, "reg", NULL);
+ if (regs &&
+ ((regs->phys_hi >> 16) & 0xff) != irq_data->pci_first_busno) {
+ irq_install_pre_handler(virt_irq,
+ sabre_wsync_handler,
+ (void *) (long) regs->phys_hi,
+ (void *)
+ controller_regs +
+ SABRE_WRSYNC);
+ }
+
+ return virt_irq;
+}
+
+static void sabre_irq_trans_init(struct device_node *dp)
+{
+ struct linux_prom64_registers *regs;
+ struct sabre_irq_data *irq_data;
+ u32 *busrange;
+
+ dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
+ dp->irq_trans->irq_build = sabre_irq_build;
+
+ irq_data = prom_early_alloc(sizeof(struct sabre_irq_data));
+
+ regs = of_get_property(dp, "reg", NULL);
+ irq_data->controller_regs = regs[0].phys_addr;
+
+ busrange = of_get_property(dp, "bus-range", NULL);
+ irq_data->pci_first_busno = busrange[0];
+
+ dp->irq_trans->data = irq_data;
+}
+
+/* SCHIZO interrupt mapping support. Unlike Psycho, for this controller the
+ * imap/iclr registers are per-PBM.
+ */
+#define SCHIZO_IMAP_BASE 0x1000UL
+#define SCHIZO_ICLR_BASE 0x1400UL
+
+static unsigned long schizo_imap_offset(unsigned long ino)
+{
+ return SCHIZO_IMAP_BASE + (ino * 8UL);
+}
+
+static unsigned long schizo_iclr_offset(unsigned long ino)
+{
+ return SCHIZO_ICLR_BASE + (ino * 8UL);
+}
+
+static unsigned long schizo_ino_to_iclr(unsigned long pbm_regs,
+ unsigned int ino)
+{
+ return pbm_regs + schizo_iclr_offset(ino) + 4;
+}
+
+static unsigned long schizo_ino_to_imap(unsigned long pbm_regs,
+ unsigned int ino)
+{
+ return pbm_regs + schizo_imap_offset(ino) + 4;
+}
+
+#define schizo_read(__reg) \
+({ u64 __ret; \
+ __asm__ __volatile__("ldxa [%1] %2, %0" \
+ : "=r" (__ret) \
+ : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \
+ : "memory"); \
+ __ret; \
+})
+#define schizo_write(__reg, __val) \
+ __asm__ __volatile__("stxa %0, [%1] %2" \
+ : /* no outputs */ \
+ : "r" (__val), "r" (__reg), \
+ "i" (ASI_PHYS_BYPASS_EC_E) \
+ : "memory")
+
+static void tomatillo_wsync_handler(unsigned int ino, void *_arg1, void *_arg2)
+{
+ unsigned long sync_reg = (unsigned long) _arg2;
+ u64 mask = 1UL << (ino & IMAP_INO);
+ u64 val;
+ int limit;
+
+ schizo_write(sync_reg, mask);
+
+ limit = 100000;
+ val = 0;
+ while (--limit) {
+ val = schizo_read(sync_reg);
+ if (!(val & mask))
+ break;
+ }
+ if (limit <= 0) {
+ printk("tomatillo_wsync_handler: DMA won't sync [%lx:%lx]\n",
+ val, mask);
+ }
+
+ if (_arg1) {
+ static unsigned char cacheline[64]
+ __attribute__ ((aligned (64)));
+
+ __asm__ __volatile__("rd %%fprs, %0\n\t"
+ "or %0, %4, %1\n\t"
+ "wr %1, 0x0, %%fprs\n\t"
+ "stda %%f0, [%5] %6\n\t"
+ "wr %0, 0x0, %%fprs\n\t"
+ "membar #Sync"
+ : "=&r" (mask), "=&r" (val)
+ : "0" (mask), "1" (val),
+ "i" (FPRS_FEF), "r" (&cacheline[0]),
+ "i" (ASI_BLK_COMMIT_P));
+ }
+}
+
+struct schizo_irq_data {
+ unsigned long pbm_regs;
+ unsigned long sync_reg;
+ u32 portid;
+ int chip_version;
+};
+
+static unsigned int schizo_irq_build(struct device_node *dp,
+ unsigned int ino,
+ void *_data)
+{
+ struct schizo_irq_data *irq_data = _data;
+ unsigned long pbm_regs = irq_data->pbm_regs;
+ unsigned long imap, iclr;
+ int ign_fixup;
+ int virt_irq;
+ int is_tomatillo;
+
+ ino &= 0x3f;
+
+ /* Now build the IRQ bucket. */
+ imap = schizo_ino_to_imap(pbm_regs, ino);
+ iclr = schizo_ino_to_iclr(pbm_regs, ino);
+
+ /* On Schizo, no inofixup occurs. This is because each
+ * INO has it's own IMAP register. On Psycho and Sabre
+ * there is only one IMAP register for each PCI slot even
+ * though four different INOs can be generated by each
+ * PCI slot.
+ *
+ * But, for JBUS variants (essentially, Tomatillo), we have
+ * to fixup the lowest bit of the interrupt group number.
+ */
+ ign_fixup = 0;
+
+ is_tomatillo = (irq_data->sync_reg != 0UL);
+
+ if (is_tomatillo) {
+ if (irq_data->portid & 1)
+ ign_fixup = (1 << 6);
+ }
+
+ virt_irq = build_irq(ign_fixup, iclr, imap);
+
+ if (is_tomatillo) {
+ irq_install_pre_handler(virt_irq,
+ tomatillo_wsync_handler,
+ ((irq_data->chip_version <= 4) ?
+ (void *) 1 : (void *) 0),
+ (void *) irq_data->sync_reg);
+ }
+
+ return virt_irq;
+}
+
+static void schizo_irq_trans_init(struct device_node *dp)
+{
+ struct linux_prom64_registers *regs;
+ struct schizo_irq_data *irq_data;
+
+ dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
+ dp->irq_trans->irq_build = schizo_irq_build;
+
+ irq_data = prom_early_alloc(sizeof(struct schizo_irq_data));
+
+ regs = of_get_property(dp, "reg", NULL);
+ dp->irq_trans->data = irq_data;
+
+ irq_data->pbm_regs = regs[0].phys_addr;
+ irq_data->sync_reg = regs[3].phys_addr + 0x1a18UL;
+ irq_data->portid = of_getintprop_default(dp, "portid", 0);
+ irq_data->chip_version = of_getintprop_default(dp, "version#", 0);
+}
+
+static unsigned int pci_sun4v_irq_build(struct device_node *dp,
+ unsigned int devino,
+ void *_data)
+{
+ u32 devhandle = (u32) (unsigned long) _data;
+
+ return sun4v_build_irq(devhandle, devino);
+}
+
+static void pci_sun4v_irq_trans_init(struct device_node *dp)
+{
+ struct linux_prom64_registers *regs;
+
+ dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
+ dp->irq_trans->irq_build = pci_sun4v_irq_build;
+
+ regs = of_get_property(dp, "reg", NULL);
+ dp->irq_trans->data = (void *) (unsigned long)
+ ((regs->phys_addr >> 32UL) & 0x0fffffff);
+}
+#endif /* CONFIG_PCI */
+
+#ifdef CONFIG_SBUS
+/* INO number to IMAP register offset for SYSIO external IRQ's.
+ * This should conform to both Sunfire/Wildfire server and Fusion
+ * desktop designs.
+ */
+#define SYSIO_IMAP_SLOT0 0x2c04UL
+#define SYSIO_IMAP_SLOT1 0x2c0cUL
+#define SYSIO_IMAP_SLOT2 0x2c14UL
+#define SYSIO_IMAP_SLOT3 0x2c1cUL
+#define SYSIO_IMAP_SCSI 0x3004UL
+#define SYSIO_IMAP_ETH 0x300cUL
+#define SYSIO_IMAP_BPP 0x3014UL
+#define SYSIO_IMAP_AUDIO 0x301cUL
+#define SYSIO_IMAP_PFAIL 0x3024UL
+#define SYSIO_IMAP_KMS 0x302cUL
+#define SYSIO_IMAP_FLPY 0x3034UL
+#define SYSIO_IMAP_SHW 0x303cUL
+#define SYSIO_IMAP_KBD 0x3044UL
+#define SYSIO_IMAP_MS 0x304cUL
+#define SYSIO_IMAP_SER 0x3054UL
+#define SYSIO_IMAP_TIM0 0x3064UL
+#define SYSIO_IMAP_TIM1 0x306cUL
+#define SYSIO_IMAP_UE 0x3074UL
+#define SYSIO_IMAP_CE 0x307cUL
+#define SYSIO_IMAP_SBERR 0x3084UL
+#define SYSIO_IMAP_PMGMT 0x308cUL
+#define SYSIO_IMAP_GFX 0x3094UL
+#define SYSIO_IMAP_EUPA 0x309cUL
+
+#define bogon ((unsigned long) -1)
+static unsigned long sysio_irq_offsets[] = {
+ /* SBUS Slot 0 --> 3, level 1 --> 7 */
+ SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0,
+ SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0, SYSIO_IMAP_SLOT0,
+ SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1,
+ SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1, SYSIO_IMAP_SLOT1,
+ SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2,
+ SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2, SYSIO_IMAP_SLOT2,
+ SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3,
+ SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3, SYSIO_IMAP_SLOT3,
+
+ /* Onboard devices (not relevant/used on SunFire). */
+ SYSIO_IMAP_SCSI,
+ SYSIO_IMAP_ETH,
+ SYSIO_IMAP_BPP,
+ bogon,
+ SYSIO_IMAP_AUDIO,
+ SYSIO_IMAP_PFAIL,
+ bogon,
+ bogon,
+ SYSIO_IMAP_KMS,
+ SYSIO_IMAP_FLPY,
+ SYSIO_IMAP_SHW,
+ SYSIO_IMAP_KBD,
+ SYSIO_IMAP_MS,
+ SYSIO_IMAP_SER,
+ bogon,
+ bogon,
+ SYSIO_IMAP_TIM0,
+ SYSIO_IMAP_TIM1,
+ bogon,
+ bogon,
+ SYSIO_IMAP_UE,
+ SYSIO_IMAP_CE,
+ SYSIO_IMAP_SBERR,
+ SYSIO_IMAP_PMGMT,
+};
+
+#undef bogon
+
+#define NUM_SYSIO_OFFSETS ARRAY_SIZE(sysio_irq_offsets)
+
+/* Convert Interrupt Mapping register pointer to associated
+ * Interrupt Clear register pointer, SYSIO specific version.
+ */
+#define SYSIO_ICLR_UNUSED0 0x3400UL
+#define SYSIO_ICLR_SLOT0 0x340cUL
+#define SYSIO_ICLR_SLOT1 0x344cUL
+#define SYSIO_ICLR_SLOT2 0x348cUL
+#define SYSIO_ICLR_SLOT3 0x34ccUL
+static unsigned long sysio_imap_to_iclr(unsigned long imap)
+{
+ unsigned long diff = SYSIO_ICLR_UNUSED0 - SYSIO_IMAP_SLOT0;
+ return imap + diff;
+}
+
+static unsigned int sbus_of_build_irq(struct device_node *dp,
+ unsigned int ino,
+ void *_data)
+{
+ unsigned long reg_base = (unsigned long) _data;
+ struct linux_prom_registers *regs;
+ unsigned long imap, iclr;
+ int sbus_slot = 0;
+ int sbus_level = 0;
+
+ ino &= 0x3f;
+
+ regs = of_get_property(dp, "reg", NULL);
+ if (regs)
+ sbus_slot = regs->which_io;
+
+ if (ino < 0x20)
+ ino += (sbus_slot * 8);
+
+ imap = sysio_irq_offsets[ino];
+ if (imap == ((unsigned long)-1)) {
+ prom_printf("get_irq_translations: Bad SYSIO INO[%x]\n",
+ ino);
+ prom_halt();
+ }
+ imap += reg_base;
+
+ /* SYSIO inconsistency. For external SLOTS, we have to select
+ * the right ICLR register based upon the lower SBUS irq level
+ * bits.
+ */
+ if (ino >= 0x20) {
+ iclr = sysio_imap_to_iclr(imap);
+ } else {
+ sbus_level = ino & 0x7;
+
+ switch(sbus_slot) {
+ case 0:
+ iclr = reg_base + SYSIO_ICLR_SLOT0;
+ break;
+ case 1:
+ iclr = reg_base + SYSIO_ICLR_SLOT1;
+ break;
+ case 2:
+ iclr = reg_base + SYSIO_ICLR_SLOT2;
+ break;
+ default:
+ case 3:
+ iclr = reg_base + SYSIO_ICLR_SLOT3;
+ break;
+ };
+
+ iclr += ((unsigned long)sbus_level - 1UL) * 8UL;
+ }
+ return build_irq(sbus_level, iclr, imap);
+}
+
+static void sbus_irq_trans_init(struct device_node *dp)
+{
+ struct linux_prom64_registers *regs;
+
+ dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
+ dp->irq_trans->irq_build = sbus_of_build_irq;
+
+ regs = of_get_property(dp, "reg", NULL);
+ dp->irq_trans->data = (void *) (unsigned long) regs->phys_addr;
+}
+#endif /* CONFIG_SBUS */
+
+
+static unsigned int central_build_irq(struct device_node *dp,
+ unsigned int ino,
+ void *_data)
+{
+ struct device_node *central_dp = _data;
+ struct of_device *central_op = of_find_device_by_node(central_dp);
+ struct resource *res;
+ unsigned long imap, iclr;
+ u32 tmp;
+
+ if (!strcmp(dp->name, "eeprom")) {
+ res = ¢ral_op->resource[5];
+ } else if (!strcmp(dp->name, "zs")) {
+ res = ¢ral_op->resource[4];
+ } else if (!strcmp(dp->name, "clock-board")) {
+ res = ¢ral_op->resource[3];
+ } else {
+ return ino;
+ }
+
+ imap = res->start + 0x00UL;
+ iclr = res->start + 0x10UL;
+
+ /* Set the INO state to idle, and disable. */
+ upa_writel(0, iclr);
+ upa_readl(iclr);
+
+ tmp = upa_readl(imap);
+ tmp &= ~0x80000000;
+ upa_writel(tmp, imap);
+
+ return build_irq(0, iclr, imap);
+}
+
+static void central_irq_trans_init(struct device_node *dp)
+{
+ dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
+ dp->irq_trans->irq_build = central_build_irq;
+
+ dp->irq_trans->data = dp;
+}
+
+struct irq_trans {
+ const char *name;
+ void (*init)(struct device_node *);
+};
+
+#ifdef CONFIG_PCI
+static struct irq_trans pci_irq_trans_table[] = {
+ { "SUNW,sabre", sabre_irq_trans_init },
+ { "pci108e,a000", sabre_irq_trans_init },
+ { "pci108e,a001", sabre_irq_trans_init },
+ { "SUNW,psycho", psycho_irq_trans_init },
+ { "pci108e,8000", psycho_irq_trans_init },
+ { "SUNW,schizo", schizo_irq_trans_init },
+ { "pci108e,8001", schizo_irq_trans_init },
+ { "SUNW,schizo+", schizo_irq_trans_init },
+ { "pci108e,8002", schizo_irq_trans_init },
+ { "SUNW,tomatillo", schizo_irq_trans_init },
+ { "pci108e,a801", schizo_irq_trans_init },
+ { "SUNW,sun4v-pci", pci_sun4v_irq_trans_init },
+};
+#endif
+
+static void irq_trans_init(struct device_node *dp)
+{
+ const char *model;
+ int i;
+
+ model = of_get_property(dp, "model", NULL);
+ if (!model)
+ model = of_get_property(dp, "compatible", NULL);
+ if (!model)
+ return;
+
+#ifdef CONFIG_PCI
+ for (i = 0; i < ARRAY_SIZE(pci_irq_trans_table); i++) {
+ struct irq_trans *t = &pci_irq_trans_table[i];
+
+ if (!strcmp(model, t->name))
+ return t->init(dp);
+ }
+#endif
+#ifdef CONFIG_SBUS
+ if (!strcmp(dp->name, "sbus") ||
+ !strcmp(dp->name, "sbi"))
+ return sbus_irq_trans_init(dp);
+#endif
+ if (!strcmp(dp->name, "central"))
+ return central_irq_trans_init(dp->child);
+}
+
static int is_root_node(const struct device_node *dp)
{
if (!dp)
dp->type = get_one_property(node, "device_type");
dp->node = node;
- /* Build interrupts later... */
-
dp->properties = build_prop_list(node);
+ irq_trans_init(dp);
+
return dp;
}
struct device_node *node;
struct device dev;
struct resource resource[PROMREG_MAX];
- unsigned int irq;
+ unsigned int irqs[PROMINTR_MAX];
+ int num_irqs;
void *sysdata;
extern void __iomem *of_ioremap(struct resource *res, unsigned long offset, unsigned long size, char *name);
extern void of_iounmap(void __iomem *base, unsigned long size);
+extern struct of_device *of_find_device_by_node(struct device_node *);
+
extern const struct of_device_id *of_match_device(
const struct of_device_id *matches, const struct of_device *dev);
#include <asm/page.h>
#include <asm/oplib.h>
#include <asm/prom.h>
+#include <asm/of_device.h>
#include <asm/iommu.h>
/* The abstraction used here is that there are PCI controllers,
/* Operations which are controller specific. */
void (*scan_bus)(struct pci_controller_info *);
- unsigned int (*irq_build)(struct pci_pbm_info *, struct pci_dev *, unsigned int);
void (*base_address_update)(struct pci_dev *, int);
void (*resource_adjust)(struct pci_dev *, struct resource *, struct resource *);
struct pcidev_cookie {
struct pci_pbm_info *pbm;
struct device_node *prom_node;
+ struct of_device *op;
struct linux_prom_pci_registers prom_regs[PROMREG_MAX];
int num_prom_regs;
struct linux_prom_pci_registers prom_assignments[PROMREG_MAX];
unsigned int unique_id;
};
+struct of_irq_controller;
struct device_node {
char *name;
char *type;
unsigned long _flags;
void *data;
unsigned int unique_id;
+
+ struct of_irq_controller *irq_trans;
+};
+
+struct of_irq_controller {
+ unsigned int (*irq_build)(struct device_node *, unsigned int, void *);
+ void *data;
};
/* flag descriptions */