diff options
author | David S. Miller <davem@davemloft.net> | 2006-06-29 15:07:37 -0700 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-06-29 16:37:38 -0700 |
commit | 2b1e59787198e75fb2ffb3bb4fb247da1c55ac12 (patch) | |
tree | 96d74048849b310135e0c79f663b16c52186caa5 /arch | |
parent | c3a8b85f5ac2c21f4ef75e87bfe55ee7a753ffcf (diff) |
[SPARC64]: of_device layer IRQ resolution
Do IRQ determination generically by parsing the PROM properties,
and using IRQ controller drivers for final resolution.
One immediate positive effect is that all of the IRQ frobbing
in the EBUS, ISA, and PCI controller layers has been eliminated.
We just look up the of_device and use the properly computed
value.
The PCI controller irq_build() routines are gone and no longer
used. Unfortunately sbus_build_irq() has to remain as there is
a direct reference to this in the sunzilog driver. That can be
killed off once the sparc32 side of this is written and the
sunzilog driver is transformed into an "of" bus driver.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/sparc64/kernel/ebus.c | 150 | ||||
-rw-r--r-- | arch/sparc64/kernel/isa.c | 101 | ||||
-rw-r--r-- | arch/sparc64/kernel/of_device.c | 194 | ||||
-rw-r--r-- | arch/sparc64/kernel/pci.c | 6 | ||||
-rw-r--r-- | arch/sparc64/kernel/pci_common.c | 291 | ||||
-rw-r--r-- | arch/sparc64/kernel/pci_psycho.c | 161 | ||||
-rw-r--r-- | arch/sparc64/kernel/pci_sabre.c | 158 | ||||
-rw-r--r-- | arch/sparc64/kernel/pci_schizo.c | 311 | ||||
-rw-r--r-- | arch/sparc64/kernel/pci_sun4v.c | 10 | ||||
-rw-r--r-- | arch/sparc64/kernel/prom.c | 757 |
10 files changed, 1091 insertions, 1048 deletions
diff --git a/arch/sparc64/kernel/ebus.c b/arch/sparc64/kernel/ebus.c index 98e0a8cbeec..aac014d15ad 100644 --- a/arch/sparc64/kernel/ebus.c +++ b/arch/sparc64/kernel/ebus.c @@ -20,6 +20,8 @@ #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> @@ -279,45 +281,12 @@ static inline void *ebus_alloc(size_t size) 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; @@ -354,12 +323,16 @@ void __init fill_ebus_child(struct device_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... @@ -375,23 +348,6 @@ 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]; - } - } } } @@ -403,72 +359,32 @@ static int __init child_regs_nonstandard(struct linux_ebus_device *dev) 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; @@ -490,7 +406,7 @@ probe_interrupts: 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) { @@ -500,7 +416,7 @@ probe_interrupts: 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)); } } diff --git a/arch/sparc64/kernel/isa.c b/arch/sparc64/kernel/isa.c index 24c0dc34be3..0f3aec72ef5 100644 --- a/arch/sparc64/kernel/isa.c +++ b/arch/sparc64/kernel/isa.c @@ -3,6 +3,8 @@ #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; @@ -46,107 +48,16 @@ isa_dev_get_resource(struct sparc_isa_device *isa_dev) 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) diff --git a/arch/sparc64/kernel/of_device.c b/arch/sparc64/kernel/of_device.c index 9812cfa6dd3..3670dc8a7d5 100644 --- a/arch/sparc64/kernel/of_device.c +++ b/arch/sparc64/kernel/of_device.c @@ -146,6 +146,26 @@ void of_iounmap(void __iomem *base, unsigned long size) } 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", @@ -261,7 +281,6 @@ static unsigned int of_bus_default_get_flags(u32 *addr) return IORESOURCE_MEM; } - /* * PCI bus specific translator */ @@ -594,12 +613,171 @@ static void __init build_device_resources(struct of_device *op, } } +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; @@ -613,12 +791,16 @@ static struct of_device * __init scan_one_device(struct device_node *dp, 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; diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c index 7b962582860..04ea6c2eb7a 100644 --- a/arch/sparc64/kernel/pci.c +++ b/arch/sparc64/kernel/pci.c @@ -404,14 +404,8 @@ void pcibios_bus_to_resource(struct pci_dev *pdev, struct resource *res, } 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; } diff --git a/arch/sparc64/kernel/pci_common.c b/arch/sparc64/kernel/pci_common.c index b06a2955bf5..7a59cc72c84 100644 --- a/arch/sparc64/kernel/pci_common.c +++ b/arch/sparc64/kernel/pci_common.c @@ -10,12 +10,10 @@ #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. */ @@ -169,6 +167,7 @@ static void __init pdev_cookie_fillin(struct pci_pbm_info *pbm, } 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; @@ -549,296 +548,18 @@ void __init pci_assign_unassigned(struct pci_pbm_info *pbm, 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); } diff --git a/arch/sparc64/kernel/pci_psycho.c b/arch/sparc64/kernel/pci_psycho.c index 1cb63346f09..bf7b32b3670 100644 --- a/arch/sparc64/kernel/pci_psycho.c +++ b/arch/sparc64/kernel/pci_psycho.c @@ -18,6 +18,7 @@ #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" @@ -208,110 +209,6 @@ static struct pci_ops psycho_ops = { .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 @@ -944,51 +841,34 @@ static irqreturn_t psycho_pcierr_intr(int irq, void *dev_id, struct pt_regs *reg #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;< |