diff options
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; 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; |