diff options
Diffstat (limited to 'arch/powerpc/platforms')
26 files changed, 1396 insertions, 1098 deletions
diff --git a/arch/powerpc/platforms/cell/interrupt.c b/arch/powerpc/platforms/cell/interrupt.c index 97936f547f1..9d5da789689 100644 --- a/arch/powerpc/platforms/cell/interrupt.c +++ b/arch/powerpc/platforms/cell/interrupt.c @@ -1,6 +1,9 @@ /* * Cell Internal Interrupt Controller * + * Copyright (C) 2006 Benjamin Herrenschmidt (benh@kernel.crashing.org) + * IBM, Corp. + * * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 * * Author: Arnd Bergmann <arndb@de.ibm.com> @@ -25,11 +28,13 @@ #include <linux/module.h> #include <linux/percpu.h> #include <linux/types.h> +#include <linux/ioport.h> #include <asm/io.h> #include <asm/pgtable.h> #include <asm/prom.h> #include <asm/ptrace.h> +#include <asm/machdep.h> #include "interrupt.h" #include "cbe_regs.h" @@ -39,9 +44,25 @@ struct iic { u8 target_id; u8 eoi_stack[16]; int eoi_ptr; + struct irq_host *host; }; static DEFINE_PER_CPU(struct iic, iic); +#define IIC_NODE_COUNT 2 +static struct irq_host *iic_hosts[IIC_NODE_COUNT]; + +/* Convert between "pending" bits and hw irq number */ +static irq_hw_number_t iic_pending_to_hwnum(struct cbe_iic_pending_bits bits) +{ + unsigned char unit = bits.source & 0xf; + + if (bits.flags & CBE_IIC_IRQ_IPI) + return IIC_IRQ_IPI0 | (bits.prio >> 4); + else if (bits.class <= 3) + return (bits.class << 4) | unit; + else + return IIC_IRQ_INVALID; +} static void iic_mask(unsigned int irq) { @@ -65,197 +86,21 @@ static struct irq_chip iic_chip = { .eoi = iic_eoi, }; -/* XXX All of this has to be reworked completely. We need to assign a real - * interrupt numbers to the external interrupts and remove all the hard coded - * interrupt maps (rely on the device-tree whenever possible). - * - * Basically, my scheme is to define the "pendings" bits to be the HW interrupt - * number (ignoring the data and flags here). That means we can sort-of split - * external sources based on priority, and we can use request_irq() on pretty - * much anything. - * - * For spider or axon, they have their own interrupt space. spider will just have - * local "hardward" interrupts 0...xx * node stride. The node stride is not - * necessary (separate interrupt chips will have separate HW number space), but - * will allow to be compatible with existing device-trees. - * - * All of thise little world will get a standard remapping scheme to map those HW - * numbers into the linux flat irq number space. -*/ -static int iic_external_get_irq(struct cbe_iic_pending_bits pending) -{ - int irq; - unsigned char node, unit; - - node = pending.source >> 4; - unit = pending.source & 0xf; - irq = -1; - - /* - * This mapping is specific to the Cell Broadband - * Engine. We might need to get the numbers - * from the device tree to support future CPUs. - */ - switch (unit) { - case 0x00: - case 0x0b: - /* - * One of these units can be connected - * to an external interrupt controller. - */ - if (pending.class != 2) - break; - /* TODO: We might want to silently ignore cascade interrupts - * when no cascade handler exist yet - */ - irq = IIC_EXT_CASCADE + node * IIC_NODE_STRIDE; - break; - case 0x01 ... 0x04: - case 0x07 ... 0x0a: - /* - * These units are connected to the SPEs - */ - if (pending.class > 2) - break; - irq = IIC_SPE_OFFSET - + pending.class * IIC_CLASS_STRIDE - + node * IIC_NODE_STRIDE - + unit; - break; - } - if (irq == -1) - printk(KERN_WARNING "Unexpected interrupt class %02x, " - "source %02x, prio %02x, cpu %02x\n", pending.class, - pending.source, pending.prio, smp_processor_id()); - return irq; -} - /* Get an IRQ number from the pending state register of the IIC */ -int iic_get_irq(struct pt_regs *regs) -{ - struct iic *iic; - int irq; - struct cbe_iic_pending_bits pending; - - iic = &__get_cpu_var(iic); - *(unsigned long *) &pending = - in_be64((unsigned long __iomem *) &iic->regs->pending_destr); - iic->eoi_stack[++iic->eoi_ptr] = pending.prio; - BUG_ON(iic->eoi_ptr > 15); - - irq = -1; - if (pending.flags & CBE_IIC_IRQ_VALID) { - if (pending.flags & CBE_IIC_IRQ_IPI) { - irq = IIC_IPI_OFFSET + (pending.prio >> 4); -/* - if (irq > 0x80) - printk(KERN_WARNING "Unexpected IPI prio %02x" - "on CPU %02x\n", pending.prio, - smp_processor_id()); -*/ - } else { - irq = iic_external_get_irq(pending); - } - } - return irq; -} - -/* hardcoded part to be compatible with older firmware */ - -static int __init setup_iic_hardcoded(void) -{ - struct device_node *np; - int nodeid, cpu; - unsigned long regs; - struct iic *iic; - - for_each_possible_cpu(cpu) { - iic = &per_cpu(iic, cpu); - nodeid = cpu/2; - - for (np = of_find_node_by_type(NULL, "cpu"); - np; - np = of_find_node_by_type(np, "cpu")) { - if (nodeid == *(int *)get_property(np, "node-id", NULL)) - break; - } - - if (!np) { - printk(KERN_WARNING "IIC: CPU %d not found\n", cpu); - iic->regs = NULL; - iic->target_id = 0xff; - return -ENODEV; - } - - regs = *(long *)get_property(np, "iic", NULL); - - /* hack until we have decided on the devtree info */ - regs += 0x400; - if (cpu & 1) - regs += 0x20; - - printk(KERN_INFO "IIC for CPU %d at %lx\n", cpu, regs); - iic->regs = ioremap(regs, sizeof(struct cbe_iic_thread_regs)); - iic->target_id = (nodeid << 4) + ((cpu & 1) ? 0xf : 0xe); - iic->eoi_stack[0] = 0xff; - } - - return 0; -} - -static int __init setup_iic(void) +static unsigned int iic_get_irq(struct pt_regs *regs) { - struct device_node *dn; - unsigned long *regs; - char *compatible; - unsigned *np, found = 0; - struct iic *iic = NULL; - - for (dn = NULL; (dn = of_find_node_by_name(dn, "interrupt-controller"));) { - compatible = (char *)get_property(dn, "compatible", NULL); - - if (!compatible) { - printk(KERN_WARNING "no compatible property found !\n"); - continue; - } - - if (strstr(compatible, "IBM,CBEA-Internal-Interrupt-Controller")) - regs = (unsigned long *)get_property(dn,"reg", NULL); - else - continue; - - if (!regs) - printk(KERN_WARNING "IIC: no reg property\n"); - - np = (unsigned int *)get_property(dn, "ibm,interrupt-server-ranges", NULL); - - if (!np) { - printk(KERN_WARNING "IIC: CPU association not found\n"); - iic->regs = NULL; - iic->target_id = 0xff; - return -ENODEV; - } - - iic = &per_cpu(iic, np[0]); - iic->regs = ioremap(regs[0], sizeof(struct cbe_iic_thread_regs)); - iic->target_id = ((np[0] & 2) << 3) + ((np[0] & 1) ? 0xf : 0xe); - iic->eoi_stack[0] = 0xff; - printk("IIC for CPU %d at %lx mapped to %p\n", np[0], regs[0], iic->regs); - - iic = &per_cpu(iic, np[1]); - iic->regs = ioremap(regs[2], sizeof(struct cbe_iic_thread_regs)); - iic->target_id = ((np[1] & 2) << 3) + ((np[1] & 1) ? 0xf : 0xe); - iic->eoi_stack[0] = 0xff; - - printk("IIC for CPU %d at %lx mapped to %p\n", np[1], regs[2], iic->regs); - - found++; - } - - if (found) - return 0; - else - return -ENODEV; + struct cbe_iic_pending_bits pending; + struct iic *iic; + + iic = &__get_cpu_var(iic); + *(unsigned long *) &pending = + in_be64((unsigned long __iomem *) &iic->regs->pending_destr); + iic->eoi_stack[++iic->eoi_ptr] = pending.prio; + BUG_ON(iic->eoi_ptr > 15); + if (pending.flags & CBE_IIC_IRQ_VALID) + return irq_linear_revmap(iic->host, + iic_pending_to_hwnum(pending)); + return NO_IRQ; } #ifdef CONFIG_SMP @@ -263,12 +108,12 @@ static int __init setup_iic(void) /* Use the highest interrupt priorities for IPI */ static inline int iic_ipi_to_irq(int ipi) { - return IIC_IPI_OFFSET + IIC_NUM_IPIS - 1 - ipi; + return IIC_IRQ_IPI0 + IIC_NUM_IPIS - 1 - ipi; } static inline int iic_irq_to_ipi(int irq) { - return IIC_NUM_IPIS - 1 - (irq - IIC_IPI_OFFSET); + return IIC_NUM_IPIS - 1 - (irq - IIC_IRQ_IPI0); } void iic_setup_cpu(void) @@ -287,22 +132,51 @@ u8 iic_get_target_id(int cpu) } EXPORT_SYMBOL_GPL(iic_get_target_id); +struct irq_host *iic_get_irq_host(int node) +{ + if (node < 0 || node >= IIC_NODE_COUNT) + return NULL; + return iic_hosts[node]; +} +EXPORT_SYMBOL_GPL(iic_get_irq_host); + + static irqreturn_t iic_ipi_action(int irq, void *dev_id, struct pt_regs *regs) { - smp_message_recv(iic_irq_to_ipi(irq), regs); + int ipi = (int)(long)dev_id; + + smp_message_recv(ipi, regs); + return IRQ_HANDLED; } static void iic_request_ipi(int ipi, const char *name) { - int irq; + int node, virq; - irq = iic_ipi_to_irq(ipi); - - /* IPIs are marked IRQF_DISABLED as they must run with irqs - * disabled */ - set_irq_chip_and_handler(irq, &iic_chip, handle_percpu_irq); - request_irq(irq, iic_ipi_action, IRQF_DISABLED, name, NULL); + for (node = 0; node < IIC_NODE_COUNT; node++) { + char *rname; + if (iic_hosts[node] == NULL) + continue; + virq = irq_create_mapping(iic_hosts[node], + iic_ipi_to_irq(ipi), 0); + if (virq == NO_IRQ) { + printk(KERN_ERR + "iic: failed to map IPI %s on node %d\n", + name, node); + continue; + } + rname = kzalloc(strlen(name) + 16, GFP_KERNEL); + if (rname) + sprintf(rname, "%s node %d", name, node); + else + rname = (char *)name; + if (request_irq(virq, iic_ipi_action, IRQF_DISABLED, + rname, (void *)(long)ipi)) + printk(KERN_ERR + "iic: failed to request IPI %s on node %d\n", + name, node); + } } void iic_request_IPIs(void) @@ -313,41 +187,119 @@ void iic_request_IPIs(void) iic_request_ipi(PPC_MSG_DEBUGGER_BREAK, "IPI-debug"); #endif /* CONFIG_DEBUGGER */ } + #endif /* CONFIG_SMP */ -static void __init iic_setup_builtin_handlers(void) + +static int iic_host_match(struct irq_host *h, struct device_node *node) +{ + return h->host_data != NULL && node == h->host_data; +} + +static int iic_host_map(struct irq_host *h, unsigned int virq, + irq_hw_number_t hw, unsigned int flags) +{ + if (hw < IIC_IRQ_IPI0) + set_irq_chip_and_handler(virq, &iic_chip, handle_fasteoi_irq); + else + set_irq_chip_and_handler(virq, &iic_chip, handle_percpu_irq); + return 0; +} + +static int iic_host_xlate(struct irq_host *h, struct device_node *ct, + u32 *intspec, unsigned int intsize, + irq_hw_number_t *out_hwirq, unsigned int *out_flags) + { - int be, isrc; + /* Currently, we don't translate anything. That needs to be fixed as + * we get better defined device-trees. iic interrupts have to be + * explicitely mapped by whoever needs them + */ + return -ENODEV; +} + +static struct irq_host_ops iic_host_ops = { + .match = iic_host_match, + .map = iic_host_map, + .xlate = iic_host_xlate, +}; + +static void __init init_one_iic(unsigned int hw_cpu, unsigned long addr, + struct irq_host *host) +{ + /* XXX FIXME: should locate the linux CPU number from the HW cpu + * number properly. We are lucky for now + */ + struct iic *iic = &per_cpu(iic, hw_cpu); - /* XXX FIXME: Assume two threads per BE are present */ - for (be=0; be < num_present_cpus() / 2; be++) { - int irq; + iic->regs = ioremap(addr, sizeof(struct cbe_iic_thread_regs)); + BUG_ON(iic->regs == NULL); - /* setup SPE chip and handlers */ - for (isrc = 0; isrc < IIC_CLASS_STRIDE * 3; isrc++) { - irq = IIC_NODE_STRIDE * be + IIC_SPE_OFFSET + isrc; - set_irq_chip_and_handler(irq, &iic_chip, handle_fasteoi_irq); + iic->target_id = ((hw_cpu & 2) << 3) | ((hw_cpu & 1) ? 0xf : 0xe); + iic->eoi_stack[0] = 0xff; + iic->host = host; + out_be64(&iic->regs->prio, 0); + + printk(KERN_INFO "IIC for CPU %d at %lx mapped to %p, target id 0x%x\n", + hw_cpu, addr, iic->regs, iic->target_id); +} + +static int __init setup_iic(void) +{ + struct device_node *dn; + struct resource r0, r1; + struct irq_host *host; + int found = 0; + u32 *np; + + for (dn = NULL; + (dn = of_find_node_by_name(dn,"interrupt-controller")) != NULL;) { + if (!device_is_compatible(dn, + "IBM,CBEA-Internal-Interrupt-Controller")) + continue; + np = (u32 *)get_property(dn, "ibm,interrupt-server-ranges", + NULL); + if (np == NULL) { + printk(KERN_WARNING "IIC: CPU association not found\n"); + of_node_put(dn); + return -ENODEV; } - /* setup cascade chip */ - irq = IIC_EXT_CASCADE + be * IIC_NODE_STRIDE; - set_irq_chip_and_handler(irq, &iic_chip, handle_fasteoi_irq); + if (of_address_to_resource(dn, 0, &r0) || + of_address_to_resource(dn, 1, &r1)) { + printk(KERN_WARNING "IIC: Can't resolve addresses\n"); + of_node_put(dn); + return -ENODEV; + } + host = NULL; + if (found < IIC_NODE_COUNT) { + host = irq_alloc_host(IRQ_HOST_MAP_LINEAR, + IIC_SOURCE_COUNT, + &iic_host_ops, + IIC_IRQ_INVALID); + iic_hosts[found] = host; + BUG_ON(iic_hosts[found] == NULL); + iic_hosts[found]->host_data = of_node_get(dn); + found++; + } + init_one_iic(np[0], r0.start, host); + init_one_iic(np[1], r1.start, host); } + + if (found) + return 0; + else + return -ENODEV; } void __init iic_init_IRQ(void) { - int cpu, irq_offset; - struct iic *iic; - + /* Discover and initialize iics */ if (setup_iic() < 0) - setup_iic_hardcoded(); + panic("IIC: Failed to initialize !\n"); - irq_offset = 0; - for_each_possible_cpu(cpu) { - iic = &per_cpu(iic, cpu); - if (iic->regs) - out_be64(&iic->regs->prio, 0xff); - } - iic_setup_builtin_handlers(); + /* Set master interrupt handling function */ + ppc_md.get_irq = iic_get_irq; + /* Enable on current CPU */ + iic_setup_cpu(); } diff --git a/arch/powerpc/platforms/cell/interrupt.h b/arch/powerpc/platforms/cell/interrupt.h index c74515aeb63..5560a92ec3a 100644 --- a/arch/powerpc/platforms/cell/interrupt.h +++ b/arch/powerpc/platforms/cell/interrupt.h @@ -37,23 +37,22 @@ */ enum { - IIC_EXT_OFFSET = 0x00, /* Start of south bridge IRQs */ - IIC_EXT_CASCADE = 0x20, /* There is no interrupt 32 on spider */ - IIC_NUM_EXT = 0x40, /* Number of south bridge IRQs */ - IIC_SPE_OFFSET = 0x40, /* Start of SPE interrupts */ - IIC_CLASS_STRIDE = 0x10, /* SPE IRQs per class */ - IIC_IPI_OFFSET = 0x70, /* Start of IPI IRQs */ - IIC_NUM_IPIS = 0x10, /* IRQs reserved for IPI */ - IIC_NODE_STRIDE = 0x80, /* Total IRQs per node */ + IIC_IRQ_INVALID = 0xff, + IIC_IRQ_MAX = 0x3f, + IIC_IRQ_EXT_IOIF0 = 0x20, + IIC_IRQ_EXT_IOIF1 = 0x2b, + IIC_IRQ_IPI0 = 0x40, + IIC_NUM_IPIS = 0x10, /* IRQs reserved for IPI */ + IIC_SOURCE_COUNT = 0x50, }; extern void iic_init_IRQ(void); -extern int iic_get_irq(struct pt_regs *regs); extern void iic_cause_IPI(int cpu, int mesg); extern void iic_request_IPIs(void); extern void iic_setup_cpu(void); extern u8 iic_get_target_id(int cpu); +extern struct irq_host *iic_get_irq_host(int node); extern void spider_init_IRQ(void); diff --git a/arch/powerpc/platforms/cell/setup.c b/arch/powerpc/platforms/cell/setup.c index 70a4e903f7e..282987d6d4a 100644 --- a/arch/powerpc/platforms/cell/setup.c +++ b/arch/powerpc/platforms/cell/setup.c @@ -80,6 +80,14 @@ static void cell_progress(char *s, unsigned short hex) printk("*** %04x : %s\n", hex, s ? s : ""); } +static void __init cell_pcibios_fixup(void) +{ + struct pci_dev *dev = NULL; + + for_each_pci_dev(dev) + pci_read_irq_line(dev); +} + static void __init cell_init_irq(void) { iic_init_IRQ(); @@ -130,8 +138,6 @@ static void __init cell_init_early(void) cell_init_iommu(); - ppc64_interrupt_controller = IC_CELL_PIC; - DBG(" <- cell_init_early()\n"); } @@ -178,8 +184,7 @@ define_machine(cell) { .check_legacy_ioport = cell_check_legacy_ioport, .progress = cell_progress, .init_IRQ = cell_init_irq, - .get_irq = iic_get_irq, - + .pcibios_fixup = cell_pcibios_fixup, #ifdef CONFIG_KEXEC .machine_kexec = default_machine_kexec, .machine_kexec_prepare = default_machine_kexec_prepare, diff --git a/arch/powerpc/platforms/cell/spider-pic.c b/arch/powerpc/platforms/cell/spider-pic.c index 98425acb6cd..ae7ef88f1a3 100644 --- a/arch/powerpc/platforms/cell/spider-pic.c +++ b/arch/powerpc/platforms/cell/spider-pic.c @@ -22,6 +22,7 @@ #include <linux/interrupt.h> #include <linux/irq.h> +#include <linux/ioport.h> #include <asm/pgtable.h> #include <asm/prom.h> @@ -56,58 +57,67 @@ enum { REISWAITEN = 0x508, /* Reissue Wait Control*/ }; -static void __iomem *spider_pics[4]; +#define SPIDER_CHIP_COUNT 4 +#define SPIDER_SRC_COUNT 64 +#define SPIDER_IRQ_INVALID 63 -static void __iomem *spider_get_pic(int irq) -{ - int node = irq / IIC_NODE_STRIDE; - irq %= IIC_NODE_STRIDE; - - if (irq >= IIC_EXT_OFFSET && - irq < IIC_EXT_OFFSET + IIC_NUM_EXT && - spider_pics) - return spider_pics[node]; - return NULL; -} +struct spider_pic { + struct irq_host *host; + struct device_node *of_node; + void __iomem *regs; + unsigned int node_id; +}; +static struct spider_pic spider_pics[SPIDER_CHIP_COUNT]; -static int spider_get_nr(unsigned int irq) +static struct spider_pic *spider_virq_to_pic(unsigned int virq) { - return (irq % IIC_NODE_STRIDE) - IIC_EXT_OFFSET; + return irq_map[virq].host->host_data; } -static void __iomem *spider_get_irq_config(int irq) +static void __iomem *spider_get_irq_config(struct spider_pic *pic, + unsigned int src) { - void __iomem *pic; - pic = spider_get_pic(irq); - return pic + TIR_CFGA + 8 * spider_get_nr(irq); + return pic->regs + TIR_CFGA + 8 * src; } -static void spider_unmask_irq(unsigned int irq) +static void spider_unmask_irq(unsigned int virq) { - int nodeid = (irq / IIC_NODE_STRIDE) * 0x10; - void __iomem *cfg = spider_get_irq_config(irq); - irq = spider_get_nr(irq); + struct spider_pic *pic = spider_virq_to_pic(virq); + void __iomem *cfg = spider_get_irq_config(pic, irq_map[virq].hwirq); - /* FIXME: Most of that is configuration and has nothing to do with enabling/disable, - * besides, it's also partially bogus. + /* We use no locking as we should be covered by the descriptor lock + * for access to invidual source configuration registers */ - out_be32(cfg, (in_be32(cfg) & ~0xf0)| 0x3107000eu | nodeid); - out_be32(cfg + 4, in_be32(cfg + 4) | 0x00020000u | irq); + out_be32(cfg, in_be32(cfg) | 0x30000000u); } -static void spider_mask_irq(unsigned int irq) +static void spider_mask_irq(unsigned int virq) { - void __iomem *cfg = spider_get_irq_config(irq); - irq = spider_get_nr(irq); + struct spider_pic *pic = spider_virq_to_pic(virq); + void __iomem *cfg = spider_get_irq_config(pic, irq_map[virq].hwirq); + /* We use no locking as we should be covered by the descriptor lock + * for access to invidual source configuration registers + */ out_be32(cfg, in_be32(cfg) & ~0x30000000u); } -static void spider_ack_irq(unsigned int irq) +static void spider_ack_irq(unsigned int virq) { - /* Should reset edge detection logic but we don't configure any edge interrupt - * at the moment. + struct spider_pic *pic = spider_virq_to_pic(virq); + unsigned int src = irq_map[virq].hwirq; + + /* Reset edge detection logic if necessary */ + if (get_irq_desc(virq)->status & IRQ_LEVEL) + return; + + /* Only interrupts 47 to 50 can be set to edge */ + if (src < 47 || src > 50) + return; + + /* Perform the clear of the edge logic */ + out_be32(pic->regs + TIR_EDC, 0x100 | (src & 0xf)); } static struct irq_chip spider_pic = { @@ -117,102 +127,243 @@ static struct irq_chip spider_pic = { .ack = spider_ack_irq, }; -static int spider_get_irq(int node) +static int spider_host_match(struct irq_host *h, struct device_node *node) +{ + struct spider_pic *pic = h->host_data; + return node == pic->of_node; +} + +static int spider_host_map(struct irq_host *h, unsigned int virq, + irq_hw_number_t hw, unsigned int flags) { - unsigned long cs; - void __iomem *regs = spider_pics[node]; + unsigned int sense = flags & IRQ_TYPE_SENSE_MASK; + struct spider_pic *pic = h->host_data; + void __iomem *cfg = spider_get_irq_config(pic, hw); + int level = 0; + u32 ic; + + /* Note that only level high is supported for most interrupts */ + if (sense != IRQ_TYPE_NONE && sense != IRQ_TYPE_LEVEL_HIGH && + (hw < 47 || hw > 50)) + return -EINVAL; + + /* Decode sense type */ + switch(sense) { + case IRQ_TYPE_EDGE_RISING: + ic = 0x3; + break; + case IRQ_TYPE_EDGE_FALLING: + ic = 0x2; + break; + case IRQ_TYPE_LEVEL_LOW: + ic = 0x0; + level = 1; + break; + case IRQ_TYPE_LEVEL_HIGH: + case IRQ_TYPE_NONE: + ic = 0x1; + level = 1; + break; + default: + return -EINVAL; + } - cs = in_be32(regs + TIR_CS) >> 24; + /* Configure the source. One gross hack that was there before and + * that I've kept around is the priority to the BE which I set to + * be the same as the interrupt source number. I don't know wether + * that's supposed to make any kind of sense however, we'll have to + * decide that, but for now, I'm not changing the behaviour. + */ + out_be32(cfg, (ic << 24) | (0x7 << 16) | (pic->node_id << 4) | 0xe); + out_be32(cfg + 4, (0x2 << 16) | (hw & 0xff)); - if (cs == 63) - return -1; - else - return cs; + if (level) + get_irq_desc(virq)->status |= IRQ_LEVEL; + set_irq_chip_and_handler(virq, &spider_pic, handle_level_irq); + return 0; +} + +static int spider_host_xlate(struct irq_host *h, struct device_node *ct, + u32 *intspec, unsigned int intsize, + irq_hw_number_t *out_hwirq, unsigned int *out_flags) + +{ + /* Spider interrupts have 2 cells, first is the interrupt source, + * second, well, I don't know for sure yet ... We mask the top bits + * because old device-trees encode a node number in there + */ + *out_hwirq = intspec[0] & 0x3f; + *out_flags = IRQ_TYPE_LEVEL_HIGH; + return 0; } +static struct irq_host_ops spider_host_ops = { + .match = spider_host_match, + .map = spider_host_map, + .xlate = spider_host_xlate, +}; + static void spider_irq_cascade(unsigned int irq, struct irq_desc *desc, struct pt_regs *regs) { - int node = (int)(long)desc->handler_data; - int cascade_irq; + struct spider_pic *pic = desc->handler_data; + unsigned int cs, virq; - cascade_irq = spider_get_irq(node); - generic_handle_irq(cascade_irq, regs); + cs = in_be32(pic->regs + TIR_CS) >> 24; + if (cs == SPIDER_IRQ_INVALID) + virq = NO_IRQ; + else + virq = irq_linear_revmap(pic->host, cs); + if (virq != NO_IRQ) + generic_handle_irq(virq, regs); desc->chip->eoi(irq); } -/* hardcoded part to be compatible with older firmware */ +/* For hooking up the cascace we have a problem. Our device-tree is + * crap and we don't know on which BE iic interrupt we are hooked on at + * least not the "standard" way. We can reconstitute it based on two + * informations though: which BE node we are connected to and wether + * we are connected to IOIF0 or IOIF1. Right now, we really only care + * about the IBM cell blade and we know that its firmware gives us an + * interrupt-map property which is pretty strange. + */ +static unsigned int __init spider_find_cascade_and_node(struct spider_pic *pic) +{ + unsigned int virq; + u32 *imap, *tmp; + int imaplen, intsize, unit; + struct device_node *iic; + struct irq_host *iic_host; + +#if 0 /* Enable that when we have a way to retreive the node as well */ + /* First, we check wether we have a real "interrupts" in the device + * tree in case the device-tree is ever fixed + */ + struct of_irq oirq; + if (of_irq_map_one(pic->of_node, 0, &oirq) == 0) { + virq = irq_create_of_mapping(oirq.controller, oirq.specifier, + oirq.size); + goto bail; + } +#endif + + /* Now do the horrible hacks */ + tmp = (u32 *)get_property(pic->of_node, "#interrupt-cells", NULL); + if (tmp == NULL) + return NO_IRQ; + intsize = *tmp; + imap = (u32 *)get_property(pic->of_node, "interrupt-map", &imaplen); + if (imap == NULL || imaplen < (intsize + 1)) + return NO_IRQ; + iic = of_find_node_by_phandle(imap[intsize]); + if (iic == NULL) + return NO_IRQ; + imap += intsize + 1; + tmp = (u32 *)get_property(iic, "#interrupt-cells", NULL); + if (tmp == NULL) + return NO_IRQ; + intsize = *tmp; + /* Assume unit is last entry of interrupt specifier */ + unit = imap[intsize - 1]; + /* Ok, we have a unit, now let's try to get the node */ + tmp = (u32 *)get_property(iic, "ibm,interrupt-server-ranges", NULL); + if (tmp == NULL) { + of_node_put(iic); + return NO_IRQ; + } + /* ugly as hell but works for now */ + pic->node_id = (*tmp) >> 1; + of_node_put(iic); + + /* Ok, now let's get cracking. You may ask me why I just didn't match + * the iic host from the iic OF node, but that way I'm still compatible + * with really really old old firmwares for which we don't have a node + */ + iic_host = iic_get_irq_host(pic->node_id); + if (iic_host == NULL) + return NO_IRQ; + /* Manufacture an IIC interrupt number of class 2 */ + virq = irq_create_mapping(iic_host, 0x20 | unit, 0); + if (virq == NO_IRQ) + printk(KERN_ERR "spider_pic: failed to map cascade !"); + return virq; +} + -static void __init spider_init_one(int node, unsigned long addr) +static void __init spider_init_one(struct device_node *of_node, int chip, + unsigned long addr) { - int n, irq; + struct spider_pic *pic = &spider_pics[chip]; + int i, virq; - spider_pics[node] = ioremap(addr, 0x800); - if (spider_pics[node] == NULL) + /* Map registers */ + pic->regs = ioremap(addr, 0x1000); + if (pic->regs == NULL) panic("spider_pic: can't map registers !"); - printk(KERN_INFO "spider_pic: mapped for node %d, addr: 0x%lx mapped to %p\n", - node, addr, spider_pics[node]); + /* Allocate a host */ + pic->host = irq_alloc_host(IRQ_HOST_MAP_LINEAR, SPIDER_SRC_COUNT, + &spider_host_ops, SPIDER_IRQ_INVALID); + if (pic->host == NULL) + panic("spider_pic: can't allocate irq host !"); + pic->host->host_data = pic; - for (n = 0; n < IIC_NUM_EXT; n++) { - if (n == IIC_EXT_CASCADE) - continue; - irq = n + IIC_EXT_OFFSET + node * IIC_NODE_STRIDE; - set_irq_chip_and_handler(irq, &spider_pic, handle_level_irq); - get_irq_desc(irq)->status |= IRQ_LEVEL; + /* Fill out other bits */ + pic->of_node = of_node_get(of_node); + + /* Go through all sources and disable them */ + for (i = 0; i < SPIDER_SRC_COUNT; i++) { + void __iomem *cfg = pic->regs + TIR_CFGA + 8 * i; + out_be32(cfg, in_be32(cfg) & ~0x30000000u); } /* do not mask any interrupts because of level */ - out_be32(spider_pics[node] + TIR_MSK, 0x0); - - /* disable edge detection clear */ - /* out_be32(spider_pics[node] + TIR_EDC, 0x0); */ + out_be32(pic->regs + TIR_MSK, 0x0); /* enable interrupt packets to be output */ - out_be32(spider_pics[node] + TIR_PIEN, - in_be32(spider_pics[node] + TIR_PIEN) | 0x1); + out_be32(pic->regs + TIR_PIEN, in_be32(pic->regs + TIR_PIEN) | 0x1); - /* Hook up cascade */ - irq = IIC_EXT_CASCADE + node * IIC_NODE_STRIDE; - set_irq_data(irq, (void *)(long)node); - set_irq_chained_handler(irq, spider_irq_cascade); + /* Hook up the cascade interrupt to the iic and nodeid */ + virq = spider_find_cascade_and_node(pic); + if (virq == NO_IRQ) + return; + set_irq_data(virq, pic); + set_irq_chained_handler(virq, spider_irq_cascade); + + printk(KERN_INFO "spider_pic: node %d, addr: 0x%lx %s\n", + pic->node_id, addr, of_node->full_name); /* Enable the interrupt detection enable bit. Do this last! */ - out_be32(spider_pics[node] + TIR_DEN, - in_be32(spider_pics[node] + TIR_DEN) | 0x1); + out_be32(pic->regs + TIR_DEN, in_be32(pic->regs + TIR_DEN) | 0x1); } void __init spider_init_IRQ(void) { - unsigned long *spider_reg; + struct resource r; struct device_node *dn; - char *compatible; - int node = 0; - - /* XXX node numbers are totally bogus. We _hope_ we get the device nodes in the right - * order here but that's definitely not guaranteed, we need to get the node from the - * device tree instead. There is currently no proper property for it (but our whole - * device-tree is bogus anyway) so all we can do is pray or maybe test the address - * and deduce the node-id + int chip = 0; + + /* XXX node numbers are totally bogus. We _hope_ we get the device + * nodes in the right order here but that's definitely not guaranteed, + * we need to get the node from the device tree instead. + * There is currently no proper property for it (but our whole + * device-tree is bogus anyway) so all we can do is pray or maybe test + * the address and deduce the node-id */ - for (dn = NULL; (dn = of_find_node_by_name(dn, "interrupt-controller"));) { - compatible = (char *)get_property(dn, "compatible", NULL); - - if (!compatible) - continue; - - if (strstr(compatible, "CBEA,platform-spider-pic")) - spider_reg = (unsigned long *)get_property(dn, "reg", NULL); - else if (strstr(compatible, "sti,platform-spider-pic") && (node < 2)) { - static long hard_coded_pics[] = { 0x24000008000, 0x34000008000 }; - spider_reg = &hard_coded_pics[node]; + for (dn = NULL; + (dn = of_find_node_by_name(dn, "interrupt-controller"));) { + if (device_is_compatible(dn, "CBEA,platform-spider-pic")) { + if (of_address_to_resource(dn, 0, &r)) { + printk(KERN_WARNING "spider-pic: Failed\n"); + continue; + } + } else if (device_is_compatible(dn, "sti,platform-spider-pic") + && (chip < 2)) { + static long hard_coded_pics[] = + { 0x24000008000, 0x34000008000 }; + r.start = hard_coded_pics[chip]; } else continue; - - if (spider_reg == NULL) - printk(KERN_ERR "spider_pic: No address for node %d\n", node); - - spider_init_one(node, *spider_reg); - node++; + spider_init_one(dn, chip++, r.start); } } diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c index 656c1ef5f4a..5d2313a6c82 100644 --- a/arch/powerpc/platforms/cell/spu_base.c +++ b/arch/powerpc/platforms/cell/spu_base.c @@ -264,51 +264,57 @@ spu_irq_class_2(int irq, void *data, struct pt_regs *regs) return stat ? IRQ_HANDLED : IRQ_NONE; } -static int -spu_request_irqs(struct spu *spu) +static int spu_request_irqs(struct spu *spu) { - int ret; - int irq_base; - - irq_base = IIC_NODE_STRIDE * spu->node + IIC_SPE_OFFSET; - - snprintf(spu->irq_c0, sizeof (spu->irq_c0), "spe%02d.0", spu->number); - ret = request_irq(irq_base + spu->isrc, - spu_irq_class_0, IRQF_DISABLED, spu->irq_c0, spu); - if (ret) - goto out; - - snprintf(spu->irq_c1, sizeof (spu->irq_c1), "spe%02d.1", spu->number); - ret = request_irq(irq_base + IIC_CLASS_STRIDE + spu->isrc, - spu_irq_class_1, IRQF_DISABLED, spu->irq_c1, spu); - if (ret) - goto out1; + int ret = 0; - snprintf(spu->irq_c2, sizeof (spu->irq_c2), "spe%02d.2", spu->number); - ret = request_irq(irq_base + 2*IIC_CLASS_STRIDE + spu->isrc, - spu_irq_class_2, IRQF_DISABLED, spu->irq_c2, spu); - if (ret) - goto out2; - goto out; + if (spu->irqs[0] != NO_IRQ) { + snprintf(spu->irq_c0, sizeof (spu->irq_c0), "spe%02d.0", + spu->number); + ret = request_irq(spu->irqs[0], spu_irq_class_0, + IRQF_DISABLED, + spu->irq_c0, spu); + if (ret) + goto bail0; + } + if (spu->irqs[1] != NO_IRQ) { + snprintf(spu->irq_c1, sizeof (spu->irq_c1), "spe%02d.1", + spu->number); + ret = request_irq(spu->irqs[1], spu_irq_class_1, + IRQF_DISABLED, + spu->irq_c1, spu); + if (ret) + goto bail1; + } + if (spu->irqs[2] != NO_IRQ) { + snprintf(spu->irq_c2, sizeof (spu->irq_c2), "spe%02d.2", + spu->number); + ret = request_irq(spu->irqs[2], spu_irq_class_2, + IRQF_DISABLED, + spu->irq_c2, spu); + if (ret) + goto bail2; + } + return 0; -out2: - free_irq(irq_base + IIC_CLASS_STRIDE + spu->isrc, spu); -out1: - free_irq(irq_base + spu->isrc, spu); -out: +bail2: + if (spu->irqs[1] != NO_IRQ) + free_irq(spu->irqs[1], spu); +bail1: + if (spu->irqs[0] != NO_IRQ) + free_irq(spu->irqs[0], spu); +bail0: return ret; } -static void -spu_free_irqs(struct spu *spu) +static void spu_free_irqs(struct spu *spu) { - int irq_base; - - irq_base = IIC_NODE_STRIDE * spu->node + IIC_SPE_OFFSET; - - free_irq(irq_base + spu->isrc, spu); - free_irq(irq_base + IIC_CLASS_STRIDE + spu->isrc, spu); - free_irq(irq_base + 2*IIC_CLASS_STRIDE + spu->isrc, spu); + if (spu->irqs[0] != NO_IRQ) + free_irq(spu->irqs[0], spu); + if (spu->irqs[1] != NO_IRQ) + free_irq(spu->irqs[1], spu); + if (spu->irqs[2] != NO_IRQ) + free_irq(spu->irqs[2], spu); } static LIST_HEAD(spu_list); @@ -559,17 +565,38 @@ static void spu_unmap(struct spu *spu) iounmap((u8 __iomem *)spu->local_store); } +/* This function shall be abstracted for HV platforms */ +static int __init spu_map_interrupts(struct spu *spu, struct device_node *np) +{ + struct irq_host *host; + unsigned int isrc; + u32 *tmp; + + host = iic_get_irq_host(spu->node); + if (host == NULL) + return -ENODEV; + + /* Get the interrupt source from the device-tree */ + tmp = (u32 *)get_property(np, "isrc", NULL); + if (!tmp) + return -ENODEV; + spu->isrc = isrc = tmp[0]; + + /* Now map interrupts of all 3 classes */ + spu->irqs[0] = irq_create_mapping(host, 0x00 | isrc, 0); + spu->irqs[1] = irq_create_mapping(host, 0x10 | isrc, 0); + spu->irqs[2] = irq_create_mapping(host, 0x20 | isrc, 0); + + /* Right now, we only fail if class 2 failed */ + return spu->irqs[2] == NO_IRQ ? -EINVAL : 0; +} + static int __init spu_map_device(struct spu *spu, struct device_node *node) { char *prop; int ret; ret = -ENODEV; - prop = get_property(node, "isrc", NULL); - if (!prop) - goto out; - spu->isrc = *(unsigned int *)prop; - spu->name = get_property(node, "name", NULL); if (!spu->name) goto out; @@ -636,7 +663,8 @@ static int spu_create_sysdev(struct spu *spu) return ret; } - sysdev_create_file(&spu->sysdev, &attr_isrc); + if (spu->isrc != 0) + sysdev_create_file(&spu->sysdev, &attr_isrc); sysfs_add_device_to_node(&spu->sysdev, spu->nid); return 0; @@ -668,6 +696,9 @@ static int __init create_spu(struct device_node *spe) spu->nid = of_node_to_nid(spe); if (spu->nid == -1) spu->nid = 0; + ret = spu_map_interrupts(spu, spe); + if (ret) + goto out_unmap; spin_lock_init(&spu->register_lock); spu_mfc_sdr_set(spu, mfspr(SPRN_SDR1)); spu_mfc_sr1_set(spu, 0x33); diff --git a/arch/powerpc/platforms/chrp/pci.c b/arch/powerpc/platforms/chrp/pci.c index 66c25349880..6802cdc3168 100644 --- a/arch/powerpc/platforms/chrp/pci.c +++ b/arch/powerpc/platforms/chrp/pci.c @@ -18,7 +18,6 @@ #include <asm/machdep.h> #include <asm/sections.h> #include <asm/pci-bridge.h> -#include <asm/open_pic.h> #include <asm/grackle.h> #include <asm/rtas.h> @@ -161,15 +160,9 @@ void __init chrp_pcibios_fixup(void) { struct pci_dev *dev = NULL; - struct device_node *np; - /* PCI interrupts are controlled by the OpenPIC */ - for_each_pci_dev(dev) { - np = pci_device_to_OF_node(dev); - if ((np != 0) && (np->n_intrs > 0) && (np->intrs[0].line != 0)) - dev->irq = np->intrs[0].line; - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); - } + for_each_pci_dev(dev) + pci_read_irq_line(dev); } #define PRG_CL_RESET_VALID 0x00010000 diff --git a/arch/powerpc/platforms/chrp/setup.c b/arch/powerpc/platforms/chrp/setup.c index a5dffc8d93a..bb10171132f 100644 --- a/arch/powerpc/platforms/chrp/setup.c +++ b/arch/powerpc/platforms/chrp/setup.c @@ -59,7 +59,7 @@ void rtas_indicator_progress(char *, unsigned short); int _chrp_type; EXPORT_SYMBOL(_chrp_type); -struct mpic *chrp_mpic; +static struct mpic *chrp_mpic; /* Used for doing CHRP event-scans */ DEFINE_PER_CPU(struct timer_list, heartbeat_timer); @@ -315,19 +315,13 @@ chrp_event_scan(unsigned long unused) jiffies + event_scan_interval); } -void chrp_8259_cascade(unsigned int irq, struct irq_desc *desc, - struct pt_regs *regs) +static void chrp_8259_cascade(unsigned int irq, struct irq_desc *desc, + struct pt_regs *regs) { - unsigned int max = 100; - - while(max--) { - int irq = i8259_irq(regs); - if (max == 99) - desc->chip->eoi(irq); - if (irq < 0) - break; - generic_handle_irq(irq, regs); - }; + unsigned int cascade_irq = i8259_irq(regs); + if (cascade_irq != NO_IRQ) + generic_handle_irq(cascade_irq, regs); + desc->chip->eoi(irq); } /* @@ -336,18 +330,17 @@ void chrp_8259_cascade(unsigned int irq, struct irq_desc *desc, static void __init chrp_find_openpic(void) { struct device_node *np, *root; - int len, i, j, irq_count; + int len, i, j; int isu_size, idu_size; unsigned int *iranges, *opprop = NULL; int oplen = 0; unsigned long opaddr; int na = 1; - unsigned char init_senses[NR_IRQS - NUM_8259_INTERRUPTS]; - np = find_type_devices("open-pic"); + np = of_find_node_by_type(NULL, "open-pic"); if (np == NULL) return; - root = find_path_device("/"); + root = of_find_node_by_path("/"); if (root) { opprop = (unsigned int *) get_property (root, "platform-open-pic", &oplen); @@ -358,19 +351,15 @@ static void __init chrp_find_openpic(void) oplen /= na * sizeof(unsigned int); } else { struct resource r; - if (of_address_to_resource(np, 0, &r)) - return; + if (of_address_to_resource(np, 0, &r)) { + goto bail; + } opaddr = r.start; oplen = 0; } printk(KERN_INFO "OpenPIC at %lx\n", opaddr); - irq_count = NR_IRQS - NUM_ISA_INTERRUPTS - 4; /* leave room for IPIs */ - prom_get_irq_senses(init_senses, NUM_ISA_INTERRUPTS, NR_IRQS - 4); - /* i8259 cascade is always positive level */ - init_senses[0] = IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE; - iranges = (unsigned int *) get_property(np, "interrupt-ranges", &len); if (iranges == NULL) len = 0; /* non-distributed mpic */ @@ -397,15 +386,12 @@ static void __init chrp_find_openpic(void) if (len > 1) isu_size = iranges[3]; - chrp_mpic = mpic_alloc(opaddr, MPIC_PRIMARY, - isu_size, NUM_ISA_INTERRUPTS, irq_count, - NR_IRQS - 4, init_senses, irq_count, - " MPIC "); + chrp_mpic = mpic_alloc(np, opaddr, MPIC_PRIMARY, + isu_size, 0, " MPIC "); if (chrp_mpic == NULL) { printk(KERN_ERR "Failed to allocate MPIC structure\n"); - return; + goto bail; } - j = na - 1; for (i = 1; i < len; ++i) { iranges += 2; @@ -417,7 +403,10 @@ static void __init chrp_find_openpic(void) } mpic_init(chrp_mpic); - set_irq_chained_handler(NUM_ISA_INTERRUPTS, chrp_8259_cascade); + ppc_md.get_irq = mpic_get_irq; + bail: + of_node_put(root); + of_node_put(np); } #if defined(CONFIG_VT) && defined(CONFIG_INPUT_ADBHID) && defined(XMON) @@ -428,14 +417,34 @@ static struct irqaction xmon_irqaction = { }; #endif -void __init chrp_init_IRQ(void) +static void __init chrp_find_8259(void) { - struct device_node *np; + struct device_node *np, *pic = NULL; unsigned long chrp_int_ack = 0; -#if defined(CONFIG_VT) && defined(CONFIG_INPUT_ADBHID) && defined(XMON) - struct device_node *kbd; -#endif + unsigned int cascade_irq; + /* Look for cascade */ + for_each_node_by_type(np, "interrupt-controller") + if (device_is_compatible(np, "chrp,iic")) { + pic = np; + break; + } + /* Ok, 8259 wasn't found. We need to handle the case where + * we have a pegasos that claims to be chrp but doesn't have + * a proper interrupt tree + */ + if (pic == NULL && chrp_mpic != NULL) { + printk(KERN_ERR "i8259: Not found in device-tree" + " assuming no legacy interrupts\n"); + return; + } + + /* Look for intack. In a perfect world, we would look for it on + * the ISA bus that holds the 8259 but heh... Works that way. If + * we ever see a problem, we can try to re-use the pSeries code here. + * Also, Pegasos-type platforms don't have a proper node to start + * from anyway + */ for (np = find_devices("pci"); np != NULL; np = np->next) { unsigned int *addrp = (unsigned int *) get_property(np, "8259-interrupt-acknowledge", NULL); @@ -446,11 +455,29 @@ void __init chrp_init_IRQ(void) break; } if (np == NULL) - printk(KERN_ERR "Cannot find PCI interrupt acknowledge address\n"); + printk(KERN_WARNING "Cannot find PCI interrupt acknowledge" + " address, polling\n"); + + i8259_init(pic, chrp_int_ack); + if (ppc_md.get_irq == NULL) + ppc_md.get_irq = i8259_irq; + if (chrp_mpic != NULL) { + cascade_irq = irq_of_parse_and_map(pic, 0); + if (cascade_irq == NO_IRQ) + printk(KERN_ERR "i8259: failed to map cascade irq\n"); + else + set_irq_chained_handler(cascade_irq, + chrp_8259_cascade); + } +} +void __init chrp_init_IRQ(void) +{ +#if defined(CONFIG_VT) && defined(CONFIG_INPUT_ADBHID) && defined(XMON) + struct device_node *kbd; +#endif chrp_find_openpic(); - - i8259_init(chrp_int_ack, 0); + chrp_find_8259(); if (_chrp_type == _CHRP_Pegasos) ppc_md.get_irq = i8259_irq; @@ -535,10 +562,6 @@ static int __init chrp_probe(void) DMA_MODE_READ = 0x44; DMA_MODE_WRITE = 0x48; isa_io_base = CHRP_ISA_IO_BASE; /* default value */ - ppc_do_canonicalize_irqs = 1; - - /* Assume we have an 8259... */ - __irq_offset_value = NUM_ISA_INTERRUPTS; return 1; } @@ -550,7 +573,6 @@ define_machine(chrp) { .init = chrp_init2, .show_cpuinfo = chrp_show_cpuinfo, .init_IRQ = chrp_init_IRQ, - .get_irq = mpic_get_irq, .pcibios_fixup = chrp_pcibios_fixup, .restart = rtas_restart, .power_off = rtas_power_off, diff --git a/arch/powerpc/platforms/chrp/smp.c b/arch/powerpc/platforms/chrp/smp.c index c298ca1ea68..1d2307e87c3 100644 --- a/arch/powerpc/platforms/chrp/smp.c +++ b/arch/powerpc/platforms/chrp/smp.c @@ -29,7 +29,6 @@ #include <asm/smp.h> #include <asm/residual.h> #include <asm/time.h> -#include <asm/open_pic.h> #include <asm/machdep.h> #include <asm/smp.h> #include <asm/mpic.h> diff --git a/arch/powerpc/platforms/iseries/irq.c b/arch/powerpc/platforms/iseries/irq.c index 7fb6a08786c..2275e64f315 100644 --- a/arch/powerpc/platforms/iseries/irq.c +++ b/arch/powerpc/platforms/iseries/irq.c @@ -162,27 +162,6 @@ static void pci_event_handler(struct HvLpEvent *event, struct pt_regs *regs) printk(KERN_ERR "pci_event_handler: NULL event received\n"); } -/* - * This is called by init_IRQ. set in ppc_md.init_IRQ by iSeries_setup.c - * It must be called before the bus walk. - */ -void __init iSeries_init_IRQ(void) -{ - /* Register PCI event handler and open an event path */ - int ret; - - ret = HvLpEvent_registerHandler(HvLpEvent_Type_PciIo, - &pci_event_handler); - if (ret == 0) { - ret = HvLpEvent_openPath(HvLpEvent_Type_PciIo, 0); - if (ret != 0) - printk(KERN_ERR "iseries_init_IRQ: open event path " - "failed with rc 0x%x\n", ret); - } else - printk(KERN_ERR "iseries_init_IRQ: register handler " - "failed with rc 0x%x\n", ret); -} - #define REAL_IRQ_TO_SUBBUS(irq) (((irq) >> 14) & 0xff) #define REAL_IRQ_TO_BUS(irq) ((((irq) >> 6) & 0xff) + 1) #define REAL_IRQ_TO_IDSEL(irq) ((((irq) >> 3) & 7) + 1) @@ -196,7 +175,7 @@ static void iseries_enable_IRQ(unsigned int irq) { u32 bus, dev_id, function, mask; const u32 sub_bus = 0; - unsigned int rirq = virt_irq_to_real_map[irq]; + unsigned int rirq = (unsigned int)irq_map[irq].hwirq; /* The IRQ has already been locked by the caller */ bus = REAL_IRQ_TO_BUS(rirq); @@ -213,7 +192,7 @@ static unsigned int iseries_startup_IRQ(unsigned int irq) { u32 bus, dev_id, function, mask; const u32 sub_bus = 0; - unsigned int rirq = virt_irq_to_real_map[irq]; + unsigned int rirq = (unsigned int)irq_map[irq].hwirq; bus = REAL_IRQ_TO_BUS(rirq); function = REAL_IRQ_TO_FUNC(rirq); @@ -254,7 +233,7 @@ static void iseries_shutdown_IRQ(unsigned int irq) { u32 bus, dev_id, function, mask; const u32 sub_bus = 0; - unsigned int rirq = virt_irq_to_real_map[irq]; + unsigned int rirq = (unsigned int)irq_map[irq].hwirq; /* irq should be locked by the caller */ bus = REAL_IRQ_TO_BUS(rirq); @@ -277,7 +256,7 @@ static void iseries_disable_IRQ(unsigned int irq) { u32 bus, dev_id, function, mask; const u32 sub_bus = 0; - unsigned int rirq = virt_irq_to_real_map[irq]; + unsigned int rirq = (unsigned int)irq_map[irq].hwirq; /* The IRQ has already been locked by the caller */ bus = REAL_IRQ_TO_BUS(rirq); @@ -291,7 +270,7 @@ static void iseries_disable_IRQ(unsigned int irq) static void iseries_end_IRQ(unsigned int irq) { - unsigned int rirq = virt_irq_to_real_map[irq]; + unsigned int rirq = (unsigned int)irq_map[irq].hwirq; HvCallPci_eoi(REAL_IRQ_TO_BUS(rirq), REAL_IRQ_TO_SUBBUS(rirq), (REAL_IRQ_TO_IDSEL(rirq) << 4) + REAL_IRQ_TO_FUNC(rirq)); @@ -314,16 +293,14 @@ static struct irq_chip iseries_pic = { int __init iSeries_allocate_IRQ(HvBusNumber bus, HvSubBusNumber sub_bus, u32 bsubbus) { - int virtirq; unsigned int realirq; u8 idsel = ISERIES_GET_DEVICE_FROM_SUBBUS(bsubbus); u8 function = ISERIES_GET_FUNCTION_FROM_SUBBUS(bsubbus); realirq = (((((sub_bus << 8) + (bus - 1)) << 3) + (idsel - 1)) << 3) + function; - virtirq = virt_irq_create_mapping(realirq); - set_irq_chip_and_handler(virtirq, &iseries_pic, handle_fasteoi_irq); - return virtirq; + + return irq_create_mapping(NULL, realirq, IRQ_TYPE_NONE); } #endif /* CONFIG_PCI */ @@ -331,10 +308,9 @@ int __init iSeries_allocate_IRQ(HvBusNumber bus, /* * Get the next pending IRQ. */ -int iSeries_get_irq(struct pt_regs *regs) +unsigned int iSeries_get_irq(struct pt_regs *regs) { - /* -2 means ignore this interrupt */ - int irq = -2; + int irq = NO_IRQ_IGNORE; #ifdef CONFIG_SMP if (get_lppaca()->int_dword.fields.ipi_cnt) { @@ -357,9 +333,57 @@ int iSeries_get_irq(struct pt_regs *regs) } spin_unlock(&pending_irqs_lock); if (irq >= NR_IRQS) - irq = -2; + irq = NO_IRQ_IGNORE; } #endif return irq; } + +static int iseries_irq_host_map(struct irq_host *h, unsigned int virq, + irq_hw_number_t hw, unsigned int flags) +{ + set_irq_chip_and_handler(virq, &iseries_pic, handle_fasteoi_irq); + + return 0; +} + +static struct irq_host_ops iseries_irq_host_ops = { + .map = iseries_irq_host_map, +}; + +/* + * This is called by init_IRQ. set in ppc_md.init_IRQ by iSeries_setup.c + * It must be called before the bus walk. + */ +void __init iSeries_init_IRQ(void) +{ + /* Register PCI event handler and open an event path */ + struct irq_host *host; + int ret; + + /* + * The Hypervisor only allows us up to 256 interrupt + * sources (the irq number is passed in a u8). + */ + irq_set_virq_count(256); + + /* Create irq host. No need for a revmap since HV will give us + * back our virtual irq number + */ + host = irq_alloc_host(IRQ_HOST_MAP_NOMAP, 0, &iseries_irq_host_ops, 0); + BUG_ON(host == NULL); + irq_set_default_host(host); + + ret = HvLpEvent_registerHandler(HvLpEvent_Type_PciIo, + &pci_event_handler); + if (ret == 0) { + ret = HvLpEvent_openPath(HvLpEvent_Type_PciIo, 0); + if (ret != 0) + printk(KERN_ERR "iseries_init_IRQ: open event path " + "failed with rc 0x%x\n", ret); + } else + printk(KERN_ERR "iseries_init_IRQ: register handler " + "failed with rc 0x%x\n", ret); +} + diff --git a/arch/powerpc/platforms/iseries/irq.h b/arch/powerpc/platforms/iseries/irq.h index 188aa808abd..1ee8985140e 100644 --- a/arch/powerpc/platforms/iseries/irq.h +++ b/arch/powerpc/platforms/iseries/irq.h @@ -4,6 +4,6 @@ extern void iSeries_init_IRQ(void); extern int iSeries_allocate_IRQ(HvBusNumber, HvSubBusNumber, u32); extern void iSeries_activate_IRQs(void); -extern int iSeries_get_irq(struct pt_regs *); +extern unsigned int iSeries_get_irq(struct pt_regs *); #endif /* _ISERIES_IRQ_H */ diff --git a/arch/powerpc/platforms/iseries/setup.c b/arch/powerpc/platforms/iseries/setup.c index c877074745b..c9605d773a7 100644 --- a/arch/powerpc/platforms/iseries/setup.c +++ b/arch/powerpc/platforms/iseries/setup.c @@ -294,8 +294,6 @@ static void __init iSeries_init_early(void) { DBG(" -> iSeries_init_early()\n"); - ppc64_interrupt_controller = IC_ISERIES; - #if defined(CONFIG_BLK_DEV_INITRD) /* * If the init RAM disk has been configured and there is @@ -659,12 +657,6 @@ static int __init iseries_probe(void) powerpc_firmware_features |= FW_FEATURE_ISERIES; powerpc_firmware_features |= FW_FEATURE_LPAR; - /* - * The Hypervisor only allows us up to 256 interrupt - * sources (the irq number is passed in a u8). - */ - virt_irq_max = 255; - hpte_init_iSeries(); return 1; diff --git a/arch/powerpc/platforms/maple/pci.c b/arch/powerpc/platforms/maple/pci.c index f7170ff86da..63a1670d3bf 100644 --- a/arch/powerpc/platforms/maple/pci.c +++ b/arch/powerpc/platforms/maple/pci.c @@ -443,18 +443,23 @@ void __init maple_pci_init(void) int maple_pci_get_legacy_ide_irq(struct pci_dev *pdev, int channel) { struct device_node *np; - int irq = channel ? 15 : 14; + unsigned int defirq = channel ? 15 : 14; + unsigned int irq; if (pdev->vendor != PCI_VENDOR_ID_AMD || pdev->device != PCI_DEVICE_ID_AMD_8111_IDE) - return irq; + return defirq; np = pci_device_to_OF_node(pdev); if (np == NULL) - return irq; - if (np->n_intrs < 2) - return irq; - return np->intrs[channel & 0x1].line; + return defirq; + irq = irq_of_parse_and_map(np, channel & 0x1); + if (irq == NO_IRQ) { + printk("Failed to map onboard IDE interrupt for channel %d\n", + channel); + return defirq; + } + return irq; } /* XXX: To remove once all firmwares are ok */ diff --git a/arch/powerpc/platforms/maple/setup.c b/arch/powerpc/platforms/maple/setup.c index 611ca8e979e..cb528c9de4c 100644 --- a/arch/powerpc/platforms/maple/setup.c +++ b/arch/powerpc/platforms/maple/setup.c @@ -198,50 +198,81 @@ static void __init maple_init_early(void) { DBG(" -> maple_init_early\n"); - /* Setup interrupt mapping options */ - ppc64_interrupt_controller = IC_OPEN_PIC; - iommu_init_early_dart(); DBG(" <- maple_init_early\n"); } - -static __init void maple_init_IRQ(void) +/* + * This is almost identical to pSeries and CHRP. We need to make that + * code generic at one point, with appropriate bits in the device-tree to + * identify the presence of an HT APIC + */ +static void __init maple_init_IRQ(void) { - struct device_node *root; + struct device_node *root, *np, *mpic_node = NULL; unsigned int *opprop; - unsigned long opic_addr; + unsigned long openpic_addr = 0; + int naddr, n, i, opplen, has_isus = 0; struct mpic *mpic; - unsigned char senses[128]; - int n; + unsigned int flags = MPIC_PRIMARY; - DBG(" -> maple_init_IRQ\n"); + /* Locate MPIC in the device-tree. Note that there is a bug + * in Maple device-tree where the type of the controller is + * open-pic and not interrupt-controller + */ + for_each_node_by_type(np, "open-pic") { + mpic_node = np; + break; + } + if (mpic_node == NULL) { + printk(KERN_ERR + "Failed to locate the MPIC interrupt controller\n"); + return; + } - /* XXX: Non standard, replace that with a proper openpic/mpic node - * in the device-tree. Find the Open PIC if present */ + /* Find address list in /platform-open-pic */ root = of_find_node_by_path("/"); - opprop = (unsigned int *) get_property(root, - "platform-open-pic", NULL); - if (opprop == 0) - panic("OpenPIC not found !\n"); - - n = prom_n_addr_cells(root); - for (opic_addr = 0; n > 0; --n) - opic_addr = (opic_addr << 32) + *opprop++; + naddr = prom_n_addr_cells(root); + opprop = (unsigned int *) get_property(root, "platform-open-pic", + &opplen); + if (opprop != 0) { + openpic_addr = of_read_number(opprop, naddr); + has_isus = (opplen > naddr); + printk(KERN_DEBUG "OpenPIC addr: %lx, has ISUs: %d\n", + openpic_addr, has_isus); + } of_node_put(root); - /* Obtain sense values from device-tree */ - prom_get_irq_senses(senses, 0, 128); + BUG_ON(openpic_addr == 0); - mpic = mpic_alloc(opic_addr, - MPIC_PRIMARY | MPIC_BIG_ENDIAN | - MPIC_BROKEN_U3 | MPIC_WANTS_RESET, - 0, 0, 128, 128, senses, 128, "U3-MPIC"); + /* Check for a big endian MPIC */ + if (get_property(np, "big-endian", NULL) != NULL) + flags |= MPIC_BIG_ENDIAN; + + /* XXX Maple specific bits */ + flags |= MPIC_BROKEN_U3 | MPIC_WANTS_RESET; + + /* Setup the openpic driver. More device-tree junks, we hard code no + * ISUs for now. I'll have to revisit some stuffs with the folks doing + * the firmware for those + */ + mpic = mpic_alloc(mpic_node, openpic_addr, flags, + /*has_isus ? 16 :*/ 0, 0, " MPIC "); BUG_ON(mpic == NULL); - mpic_init(mpic); - DBG(" <- maple_init_IRQ\n"); + /* Add ISUs */ + opplen /= sizeof(u32); + for (n = 0, i = naddr; i < opplen; i += naddr, n++) { + unsigned long isuaddr = of_read_number(opprop + i, naddr); + mpic_assign_isu(mpic, n, isuaddr); + } + + /* All ISUs are setup, complete initialization */ + mpic_init(mpic); + ppc_md.get_irq = mpic_get_irq; + of_node_put(mpic_node); + of_node_put(root); } static void __init maple_progress(char *s, unsigned short hex) @@ -279,7 +310,6 @@ define_machine(maple_md) { .setup_arch = maple_setup_arch, .init_early = maple_init_early, .init_IRQ = maple_init_IRQ, - .get_irq = mpic_get_irq, .pcibios_fixup = maple_pcibios_fixup, .pci_get_legacy_ide_irq = maple_pci_get_legacy_ide_irq, .restart = maple_restart, diff --git a/arch/powerpc/platforms/powermac/bootx_init.c b/arch/powerpc/platforms/powermac/bootx_init.c index 5685ad9e88e..e63d52f227e 100644 --- a/arch/powerpc/platforms/powermac/bootx_init.c +++ b/arch/powerpc/platforms/powermac/bootx_init.c @@ -162,6 +162,8 @@ static void __init bootx_add_chosen_props(unsigned long base, { u32 val; + bootx_dt_add_prop("linux,bootx", NULL, 0, mem_end); + if (bootx_info->kernelParamsOffset) { char *args = (char *)((unsigned long)bootx_info) + bootx_info->kernelParamsOffset; @@ -228,7 +230,7 @@ static void __init bootx_scan_dt_build_strings(unsigned long base, if (!strcmp(namep, "/chosen")) { DBG(" detected /chosen ! adding properties names !\n"); - bootx_dt_add_string("linux,platform", mem_end); + bootx_dt_add_string("linux,bootx", mem_end); bootx_dt_add_string("linux,stdout-path", mem_end); bootx_dt_add_string("linux,initrd-start", mem_end); bootx_dt_add_string("linux,initrd-end", mem_end); diff --git a/arch/powerpc/platforms/powermac/low_i2c.c b/arch/powerpc/platforms/powermac/low_i2c.c index ceafaf52a66..8677f50c258 100644 --- a/arch/powerpc/platforms/powermac/low_i2c.c +++ b/arch/powerpc/platforms/powermac/low_i2c.c @@ -522,10 +522,11 @@ static struct pmac_i2c_host_kw *__init kw_i2c_host_init(struct device_node *np) host->speed = KW_I2C_MODE_25KHZ; break; } - if (np->n_intrs > 0) - host->irq = np->intrs[0].line; - else - host->irq = NO_IRQ; + host->irq = irq_of_parse_and_map(np, 0); + if (host->irq == NO_IRQ) + printk(KERN_WARNING + "low_i2c: Failed to map interrupt for %s\n", + np->full_name); host->base = ioremap((*addrp), 0x1000); if (host->base == NULL) { diff --git a/arch/powerpc/platforms/powermac/nvram.c b/arch/powerpc/platforms/powermac/nvram.c index 41fa2409482..6a36ea9bf67 100644 --- a/arch/powerpc/platforms/powermac/nvram.c +++ b/arch/powerpc/platforms/powermac/nvram.c @@ -29,6 +29,8 @@ #include <asm/machdep.h> #include <asm/nvram.h> +#include "pmac.h" + #define DEBUG #ifdef DEBUG @@ -80,9 +82,6 @@ static int nvram_partitions[3]; // XXX Turn that into a sem static DEFINE_SPINLOCK(nv_lock); -extern int pmac_newworld; -extern int system_running; - static int (*core99_write_bank)(int bank, u8* datas); static int (*core99_erase_bank)(int bank); diff --git a/arch/powerpc/platforms/powermac/pci.c b/arch/powerpc/platforms/powermac/pci.c index d524a915aa8..556b349797e 100644 --- a/arch/powerpc/platforms/powermac/pci.c +++ b/arch/powerpc/platforms/powermac/pci.c @@ -46,6 +46,9 @@ static int has_uninorth; static struct pci_controller *u3_agp; static struct pci_controller *u4_pcie; static struct pci_controller *u3_ht; +#define has_second_ohare 0 +#else +static int has_second_ohare; #endif /* CONFIG_PPC64 */ extern u8 pci_cache_line_size; @@ -647,6 +650,33 @@ static void __init init_p2pbridge(void) early_write_config_word(hose, bus, devfn, PCI_BRIDGE_CONTROL, val); } +static void __init init_second_ohare(void) +{ + struct device_node *np = of_find_node_by_name(NULL, "pci106b,7"); + unsigned char bus, devfn; + unsigned short cmd; + + if (np == NULL) + return; + + /* This must run before we initialize the PICs since the second + * ohare hosts a PIC that will be accessed there. + */ + if (pci_device_from_OF_node(np, &bus, &devfn) == 0) { + struct pci_controller* hose = + pci_find_hose_for_OF_device(np); + if (!hose) { + printk(KERN_ERR "Can't find PCI hose for OHare2 !\n"); + return; + } + early_read_config_word(hose, bus, devfn, PCI_COMMAND, &cmd); + cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; + cmd &= ~PCI_COMMAND_IO; + early_write_config_word(hose, bus, devfn, PCI_COMMAND, cmd); + } + has_second_ohare = 1; +} + /* * Some Apple desktop machines have a NEC PD720100A USB2 controller * on the motherboard. Open Firmware, on these, will disable the @@ -688,9 +718,6 @@ static void __init fixup_nec_usb2(void) " EHCI, fixing up...\n"); data &= ~1UL; early_write_config_dword(hose, bus, devfn, 0xe4, data); - early_write_config_byte(hose, bus, - devfn | 2, PCI_INTERRUPT_LINE, - nec->intrs[0].line); } } } @@ -958,32 +985,28 @@ static int __init add_bridge(struct device_node *dev) return 0; } -static void __init pcibios_fixup_OF_interrupts(void) +void __init pmac_pcibios_fixup(void) { struct pci_dev* dev = NULL; - /* - * Open Firmware often doesn't initialize the - * PCI_INTERRUPT_LINE config register properly, so we - * should find the device node and apply the interrupt - * obtained from the OF device-tree - */ for_each_pci_dev(dev) { - struct device_node *node; - node = pci_device_to_OF_node(dev); - /* this is the node, see if it has interrupts */ - if (node && node->n_intrs > 0) - dev->irq = node->intrs[0].line; - pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); + /* Read interrupt from the device-tree */ + pci_read_irq_line(dev); + + /* Fixup interrupt for the modem/ethernet combo controller. + * on machines with a second ohare chip. + * The number in the device tree (27) is bogus (correct for + * the ethernet-only board but not the combo ethernet/modem + * board). The real interrupt is 28 on the second controller + * -> 28+32 = 60. + */ + if (has_second_ohare && + dev->vendor == PCI_VENDOR_ID_DEC && + dev->device == PCI_DEVICE_ID_DEC_TULIP_PLUS) + dev->irq = irq_create_mapping(NULL, 60, 0); } } -void __init pmac_pcibios_fixup(void) -{ - /* Fixup interrupts according to OF tree */ - pcibios_fixup_OF_interrupts(); -} - #ifdef CONFIG_PPC64 static void __init pmac_fixup_phb_resources(void) { @@ -1071,6 +1094,7 @@ void __init pmac_pci_init(void) #else /* CONFIG_PPC64 */ init_p2pbridge(); + init_second_ohare(); fixup_nec_usb2(); /* We are still having some issues with the Xserve G4, enabling diff --git a/arch/powerpc/platforms/powermac/pfunc_base.c b/arch/powerpc/platforms/powermac/pfunc_base.c index d6eab8b3f7d..6d66359ec8c 100644 --- a/arch/powerpc/platforms/powermac/pfunc_base.c +++ b/arch/powerpc/platforms/powermac/pfunc_base.c @@ -24,19 +24,18 @@ static irqreturn_t macio_gpio_irq(int irq, void *data, struct pt_regs *regs) static int macio_do_gpio_irq_enable(struct pmf_function *func) { - if (func->node->n_intrs < 1) + unsigned int irq = irq_of_parse_and_map(func->node, 0); + if (irq == NO_IRQ) return -EINVAL; - - return request_irq(func->node->intrs[0].line, macio_gpio_irq, 0, - func->node->name, func); + return request_irq(irq, macio_gpio_irq, 0, func->node->name, func); } static int macio_do_gpio_irq_disable(struct pmf_function *func) { - if (func->node->n_intrs < 1) + unsigned int irq = irq_of_parse_and_map(func->node, 0); + if (irq == NO_IRQ) return -EINVAL; - - free_irq(func->node->intrs[0].line, func); + free_irq(irq, func); return 0; } diff --git a/arch/powerpc/platforms/powermac/pic.c b/arch/powerpc/platforms/powermac/pic.c index 58a4c7b90b8..3d328bc1f7e 100644 --- a/arch/powerpc/platforms/powermac/pic.c +++ b/arch/powerpc/platforms/powermac/pic.c @@ -65,13 +65,11 @@ static u32 level_mask[4]; static DEFINE_SPINLOCK(pmac_pic_lock); -#define GATWICK_IRQ_POOL_SIZE 10 -static struct interrupt_info gatwick_int_pool[GATWICK_IRQ_POOL_SIZE]; - #define NR_MASK_WORDS ((NR_IRQS + 31) / 32) static unsigned long ppc_lost_interrupts[NR_MASK_WORDS]; static unsigned long ppc_cached_irq_mask[NR_MASK_WORDS]; static int pmac_irq_cascade = -1; +static struct irq_host *pmac_pic_host; static void __pmac_retrigger(unsigned int irq_nr) { @@ -86,18 +84,16 @@ static void __pmac_retrigger(unsigned int irq_nr) } } -static void pmac_mask_and_ack_irq(unsigned int irq_nr) +static void pmac_mask_and_ack_irq(unsigned int virq) { - unsigned long bit = 1UL << (irq_nr & 0x1f); - int i = irq_nr >> 5; + unsigned int src = irq_map[virq].hwirq; + unsigned long bit = 1UL << (virq & 0x1f); + int i = virq >> 5; unsigned long flags; - if ((unsigned)irq_nr >= max_irqs) - return; - spin_lock_irqsave(&pmac_pic_lock, flags); - __clear_bit(irq_nr, ppc_cached_irq_mask); - if (__test_and_clear_bit(irq_nr, ppc_lost_interrupts)) + __clear_bit(src, ppc_cached_irq_mask); + if (__test_and_clear_bit(src, ppc_lost_interrupts)) atomic_dec(&ppc_n_lost_interrupts); out_le32(&pmac_irq_hw[i]->enable, ppc_cached_irq_mask[i]); out_le32(&pmac_irq_hw[i]->ack, bit); @@ -110,17 +106,15 @@ static void pmac_mask_and_ack_irq(unsigned int irq_nr) spin_unlock_irqrestore(&pmac_pic_lock, flags); } -static void pmac_ack_irq(unsigned int irq_nr) +static void pmac_ack_irq(unsigned int virq) { - unsigned long bit = 1UL << (irq_nr & 0x1f); - int i = irq_nr >> 5; + unsigned int src = irq_map[virq].hwirq; + unsigned long bit = 1UL << (src & 0x1f); + int i = src >> 5; unsigned long flags; - if ((unsigned)irq_nr >= max_irqs) - return; - spin_lock_irqsave(&pmac_pic_lock, flags); - if (__test_and_clear_bit(irq_nr, ppc_lost_interrupts)) + if (__test_and_clear_bit(src, ppc_lost_interrupts)) atomic_dec(&ppc_n_lost_interrupts); out_le32(&pmac_irq_hw[i]->ack, bit); (void)in_le32(&pmac_irq_hw[i]->ack); @@ -157,48 +151,51 @@ static void __pmac_set_irq_mask(unsigned int irq_nr, int nokicklost) /* When an irq gets requested for the first client, if it's an * edge interrupt, we clear any previous one on the controller */ -static unsigned int pmac_startup_irq(unsigned int irq_nr) +static unsigned int pmac_startup_irq(unsigned int virq) { unsigned long flags; - unsigned long bit = 1UL << (irq_nr & 0x1f); - int i = irq_nr >> 5; + unsigned int src = irq_map[virq].hwirq; + unsigned long bit = 1UL << (src & 0x1f); + int i = src >> 5; spin_lock_irqsave(&pmac_pic_lock, flags); - if ((irq_desc[irq_nr].status & IRQ_LEVEL) == 0) + if ((irq_desc[virq].status & IRQ_LEVEL) == 0) out_le32(&pmac_irq_hw[i]->ack, bit); - __set_bit(irq_nr, ppc_cached_irq_mask); - __pmac_set_irq_mask(irq_nr, 0); + __set_bit(src, ppc_cached_irq_mask); + __pmac_set_irq_mask(src, 0); spin_unlock_irqrestore(&pmac_pic_lock, flags); return 0; } -static void pmac_mask_irq(unsigned int irq_nr) +static void pmac_mask_irq(unsigned int virq) { unsigned long flags; + unsigned int src = irq_map[virq].hwirq; spin_lock_irqsave(&pmac_pic_lock, flags); - __clear_bit(irq_nr, ppc_cached_irq_mask); - __pmac_set_irq_mask(irq_nr, 0); + __clear_bit(src, ppc_cached_irq_mask); + __pmac_set_irq_mask(src, 0); spin_unlock_irqrestore(&pmac_pic_lock, flags); } -static void pmac_unmask_irq(unsigned int irq_nr) +static void pmac_unmask_irq(unsigned int virq) { unsigned long flags; + unsigned int src = irq_map[virq].hwirq; spin_lock_irqsave(&pmac_pic_lock, flags); - __set_bit(irq_nr, ppc_cached_irq_mask); - __pmac_set_irq_mask(irq_nr, 0); + __set_bit(src, ppc_cached_irq_mask); + __pmac_set_irq_mask(src, 0); spin_unlock_irqrestore(&pmac_pic_lock, flags); } -static int pmac_retrigger(unsigned int irq_nr) +static int pmac_retrigger(unsigned int virq) { unsigned long flags; spin_lock_irqsave(&pmac_pic_lock, flags); - __pmac_retrigger(irq_nr); + __pmac_retrigger(irq_map[virq].hwirq); spin_unlock_irqrestore(&pmac_pic_lock, flags); return 1; } @@ -238,7 +235,7 @@ static irqreturn_t gatwick_action(int cpl, void *dev_id, struct pt_regs *regs) return rc; } -static int pmac_get_irq(struct pt_regs *regs) +static unsigned int pmac_pic_get_irq(struct pt_regs *regs) { int irq; unsigned long bits = 0; @@ -250,7 +247,7 @@ static int pmac_get_irq(struct pt_regs *regs) /* IPI's are a hack on the powersurge -- Cort */ if ( smp_processor_id() != 0 ) { psurge_smp_message_recv(regs); - return -2; /* ignore, already handled */ + return NO_IRQ_IGNORE; /* ignore, already handled */ } #endif /* CONFIG_SMP */ spin_lock_irqsave(&pmac_pic_lock, flags); @@ -266,133 +263,9 @@ static int pmac_get_irq(struct pt_regs *regs) break; } spin_unlock_irqrestore(&pmac_pic_lock, flags); - - return irq; -} - -/* This routine will fix some missing interrupt values in the device tree - * on the gatwick mac-io controller used by some PowerBooks - * - * Walking of OF nodes could use a bit more fixing up here, but it's not - * very important as this is all boot time code on static portions of the - * device-tree. - * - * However, the modifications done to "intrs" will have to be removed and - * replaced with proper updates of the "interrupts" properties or - * AAPL,interrupts, yet to be decided, once the dynamic parsing is there. - */ -static void __init pmac_fix_gatwick_interrupts(struct device_node *gw, - int irq_base) -{ - struct device_node *node; - int count; - - memset(gatwick_int_pool, 0, sizeof(gatwick_int_pool)); - count = 0; - for (node = NULL; (node = of_get_next_child(gw, node)) != NULL;) { - /* Fix SCC */ - if ((strcasecmp(node->name, "escc") == 0) && node->child) { - if (node->child->n_intrs < 3) { - node->child->intrs = &gatwick_int_pool[count]; - count += 3; - } - node->child->n_intrs = 3; - node->child->intrs[0].line = 15+irq_base; - node->child->intrs[1].line = 4+irq_base; - node->child->intrs[2].line = 5+irq_base; - printk(KERN_INFO "irq: fixed SCC on gatwick" - " (%d,%d,%d)\n", - node->child->intrs[0].line, - node->child->intrs[1].line, - node->child->intrs[2].line); - } - /* Fix media-bay & left SWIM */ - if (strcasecmp(node->name, "media-bay") == 0) { - struct device_node* ya_node; - - if (node->n_intrs == 0) - node->intrs = &gatwick_int_pool[count++]; - node->n_intrs = 1; - node->intrs[0].line = 29+irq_base; - printk(KERN_INFO "irq: fixed media-bay on gatwick" - " (%d)\n", node->intrs[0].line); - - ya_node = node->child; - while(ya_node) { - if (strcasecmp(ya_node->name, "floppy") == 0) { - if (ya_node->n_intrs < 2) { - ya_node->intrs = &gatwick_int_pool[count]; - count += 2; - } - ya_node->n_intrs = 2; - ya_node->intrs[0].line = 19+irq_base; - ya_node->intrs[1].line = 1+irq_base; - printk(KERN_INFO "irq: fixed floppy on second controller (%d,%d)\n", - ya_node->intrs[0].line, ya_node->intrs[1].line); - } - if (strcasecmp(ya_node->name, "ata4") == 0) { - if (ya_node->n_intrs < 2) { - ya_node->intrs = &gatwick_int_pool[count]; - count += 2; - } - ya_node->n_intrs = 2; - ya_node->intrs[0].line = 14+irq_base; - ya_node->intrs[1].line = 3+irq_base; - printk(KERN_INFO "irq: fixed ide on second controller (%d,%d)\n", - ya_node->intrs[0].line, ya_node->intrs[1].line); - } - ya_node = ya_node->sibling; - } - } - } - if (count > 10) { - printk("WARNING !! Gatwick interrupt pool overflow\n"); - printk(" GATWICK_IRQ_POOL_SIZE = %d\n", GATWICK_IRQ_POOL_SIZE); - printk(" requested = %d\n", count); - } -} - -/* - * The PowerBook 3400/2400/3500 can have a combo ethernet/modem - * card which includes an ohare chip that acts as a second interrupt - * controller. If we find this second ohare, set it up and fix the - * interrupt value in the device tree for the ethernet chip. - */ -static void __init enable_second_ohare(struct device_node *np) -{ - unsigned char bus, devfn; - unsigned short cmd; - struct device_node *ether; - - /* This code doesn't strictly belong here, it could be part of - * either the PCI initialisation or the feature code. It's kept - * here for historical reasons. - */ - if (pci_device_from_OF_node(np, &bus, &devfn) == 0) { - struct pci_controller* hose = - pci_find_hose_for_OF_device(np); - if (!hose) { - printk(KERN_ERR "Can't find PCI hose for OHare2 !\n"); - return; - } - early_read_config_word(hose, bus, devfn, PCI_COMMAND, &cmd); - cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; - cmd &= ~PCI_COMMAND_IO; - early_write_config_word(hose, bus, devfn, PCI_COMMAND, cmd); - } - - /* Fix interrupt for the modem/ethernet combo controller. The number - * in the device tree (27) is bogus (correct for the ethernet-only - * board but not the combo ethernet/modem board). - * The real interrupt is 28 on the second controller -> 28+32 = 60. - */ - ether = of_find_node_by_name(NULL, "pci1011,14"); - if (ether && ether->n_intrs > 0) { - ether->intrs[0].line = 60; - printk(KERN_INFO "irq: Fixed ethernet IRQ to %d\n", - ether->intrs[0].line); - } - of_node_put(ether); + if (unlikely(irq < 0)) + return NO_IRQ; + return irq_linear_revmap(pmac_pic_host, irq); } #ifdef CONFIG_XMON @@ -411,6 +284,50 @@ static struct irqaction gatwick_cascade_action = { .name = "cascade", }; +static int pmac_pic_host_match(struct irq_host *h, struct device_node *node) +{ + /* We match all, we don't always have a node anyway */ + return 1; +} + +static int pmac_pic_host_map(struct irq_host *h, unsigned int virq, + irq_hw_number_t hw, unsigned int flags) +{ + struct irq_desc *desc = get_irq_desc(virq); + int level; + + if (hw >= max_irqs) + return -EINVAL; + + /* Mark level interrupts, set delayed disable for edge ones and set + * handlers + */ + level = !!(level_mask[hw >> 5] & (1UL << (hw & 0x1f))); + if (level) + desc->status |= IRQ_LEVEL; + else + desc->status |= IRQ_DELAYED_DISABLE; + set_irq_chip_and_handler(virq, &pmac_pic, level ? + handle_level_irq : handle_edge_irq); + return 0; +} + +static int pmac_pic_host_xlate(struct irq_host *h, struct device_node *ct, + u32 *intspec, unsigned int intsize, + irq_hw_number_t *out_hwirq, + unsigned int *out_flags) + +{ + *out_hwirq = *intspec; + return 0; +} + +static struct irq_host_ops pmac_pic_host_ops = { + .match = pmac_pic_host_match, + .map = pmac_pic_host_map, + .xlate = pmac_pic_host_xlate, +}; + static void __init pmac_pic_probe_oldstyle(void) { int i; @@ -420,7 +337,7 @@ static void __init pmac_pic_probe_oldstyle(void) struct resource r; /* Set our get_irq function */ - ppc_md.get_irq = pmac_get_irq; + ppc_md.get_irq = pmac_pic_get_irq; /* * Find the interrupt controller type & node @@ -438,7 +355,6 @@ static void __init pmac_pic_probe_oldstyle(void) if (slave) { max_irqs = 64; level_mask[1] = OHARE_LEVEL_MASK; - enable_second_ohare(slave); } } else if ((master = of_find_node_by_name(NULL, "mac-io")) != NULL) { max_irqs = max_real_irqs = 64; @@ -462,21 +378,18 @@ static void __init pmac_pic_probe_oldstyle(void) max_irqs = 128; level_mask[2] = HEATHROW_LEVEL_MASK; level_mask[3] = 0; - pmac_fix_gatwick_interrupts(slave, max_real_irqs); } } BUG_ON(master == NULL); - /* Mark level interrupts and set handlers */ - for (i = 0; i < max_irqs; i++) { - int level = !!(level_mask[i >> 5] & (1UL << (i & 0x1f))); - if (level) - irq_desc[i].status |= IRQ_LEVEL; - else - irq_desc[i].status |= IRQ_DELAYED_DISABLE; - set_irq_chip_and_handler(i, &pmac_pic, level ? - handle_level_irq : handle_edge_irq); - } + /* + * Allocate an irq host + */ + pmac_pic_host = irq_alloc_host(IRQ_HOST_MAP_LINEAR, max_irqs, + &pmac_pic_host_ops, + max_irqs); + BUG_ON(pmac_pic_host == NULL); + irq_set_default_host(pmac_pic_host); /* Get addresses of first controller if we have a node for it */ BUG_ON(of_address_to_resource(master, 0, &r)); @@ -503,7 +416,7 @@ static void __init pmac_pic_probe_oldstyle(void) pmac_irq_hw[i++] = (volatile struct pmac_irq_hw __iomem *) (addr + 0x10); - pmac_irq_cascade = slave->intrs[0].line; + pmac_irq_cascade = irq_of_parse_and_map(slave, 0); printk(KERN_INFO "irq: Found slave Apple PIC %s for %d irqs" " cascade: %d\n", slave->full_name, @@ -516,12 +429,12 @@ static void __init pmac_pic_probe_oldstyle(void) out_le32(&pmac_irq_hw[i]->enable, 0); /* Hookup cascade irq */ - if (slave) + if (slave && pmac_irq_cascade != NO_IRQ) setup_irq(pmac_irq_cascade, &gatwick_cascade_action); printk(KERN_INFO "irq: System has %d possible interrupts\n", max_irqs); #ifdef CONFIG_XMON - setup_irq(20, &xmon_action); + setup_irq(irq_create_mapping(NULL, 20, 0), &xmon_action); #endif } #endif /* CONFIG_PPC32 */ @@ -530,16 +443,11 @@ static void pmac_u3_cascade(unsigned int irq, struct irq_desc *desc, struct pt_regs *regs) { struct mpic *mpic = desc->handler_data; - unsigned int max = 100; - while(max--) { - int cascade_irq = mpic_get_one_irq(mpic, regs); - if (max == 99) - desc->chip->eoi(irq); - if (irq < 0) - break; + unsigned int cascade_irq = mpic_get_one_irq(mpic, regs); + if (cascade_irq != NO_IRQ) generic_handle_irq(cascade_irq, regs); - }; + desc->chip->eoi(irq); } static void __init pmac_pic_setup_mpic_nmi(struct mpic *mpic) @@ -549,21 +457,20 @@ static void __init pmac_pic_setup_mpic_nmi(struct mpic *mpic) int nmi_irq; pswitch = of_find_node_by_name(NULL, "programmer-switch"); - if (pswitch && pswitch->n_intrs) { - nmi_irq = pswitch->intrs[0].line; - mpic_irq_set_priority(nmi_irq, 9); - setup_irq(nmi_irq, &xmon_action); + if (pswitch) { + nmi_irq = irq_of_parse_and_map(pswitch, 0); + if (nmi_irq != NO_IRQ) { + mpic_irq_set_priority(nmi_irq, 9); + setup_irq(nmi_irq, &xmon_action); + } + of_node_put(pswitch); } - of_node_put(pswitch); #endif /* defined(CONFIG_XMON) && defined(CONFIG_PPC32) */ } static struct mpic * __init pmac_setup_one_mpic(struct device_node *np, int master) { - unsigned char senses[128]; - int offset = master ? 0 : 128; - int count = master ? 128 : 124; const char *name = master ? " MPIC 1 " : " MPIC 2 "; struct resource r; struct mpic *mpic; @@ -576,8 +483,6 @@ static struct mpic * __init pmac_setup_one_mpic(struct device_node *np, pmac_call_feature(PMAC_FTR_ENABLE_MPIC, np, 0, 0); - prom_get_irq_senses(senses, offset, offset + count); - flags |= MPIC_WANTS_RESET; if (get_property(np, "big-endian", NULL)) flags |= MPIC_BIG_ENDIAN; @@ -588,8 +493,7 @@ static struct mpic * __init pmac_setup_one_mpic(struct device_node *np, if (master && (flags & MPIC_BIG_ENDIAN)) flags |= MPIC_BROKEN_U3; - mpic = mpic_alloc(r.start, flags, 0, offset, count, master ? 252 : 0, - senses, count, name); + mpic = mpic_alloc(np, r.start, flags, 0, 0, name); if (mpic == NULL) return NULL; @@ -602,6 +506,7 @@ static int __init pmac_pic_probe_mpic(void) { struct mpic *mpic1, *mpic2; struct device_node *np, *master = NULL, *slave = NULL; + unsigned int cascade; /* We can have up to 2 MPICs cascaded */ for (np = NULL; (np = of_find_node_by_type(np, "open-pic")) @@ -638,8 +543,15 @@ static int __init pmac_pic_probe_mpic(void) of_node_put(master); /* No slave, let's go out */ - if (slave == NULL || slave->n_intrs < 1) + if (slave == NULL) + return 0; + + /* Get/Map slave interrupt */ + cascade = irq_of_parse_and_map(slave, 0); + if (cascade == NO_IRQ) { + printk(KERN_ERR "Failed to map cascade IRQ\n"); return 0; + } mpic2 = pmac_setup_one_mpic(slave, 0); if (mpic2 == NULL) { @@ -647,8 +559,8 @@ static int __init pmac_pic_probe_mpic(void) of_node_put(slave); return 0; } - set_irq_data(slave->intrs[0].line, mpic2); - set_irq_chained_handler(slave->intrs[0].line, pmac_u3_cascade); + set_irq_data(cascade, mpic2); + set_irq_chained_handler(cascade, pmac_u3_cascade); of_node_put(slave); return 0; @@ -657,6 +569,19 @@ static int __init pmac_pic_probe_mpic(void) void __init pmac_pic_init(void) { + unsigned int flags = 0; + + /* We configure the OF parsing based on our oldworld vs. newworld + * platform type and wether we were booted by BootX. + */ +#ifdef CONFIG_PPC32 + if (!pmac_newworld) + flags |= OF_IMAP_OLDWORLD_MAC; + if (get_property(of_chosen, "linux,bootx", NULL) != NULL) + flags |= OF_IMAP_NO_PHANDLE; + of_irq_map_init(flags); +#endif /* CONFIG_PPC_32 */ + /* We first try to detect Apple's new Core99 chipset, since mac-io * is quite different on those machines and contains an IBM MPIC2. */ @@ -679,6 +604,7 @@ unsigned long sleep_save_mask[2]; /* This used to be passed by the PMU driver but that link got * broken with the new driver model. We use this tweak for now... + * We really want to do things differently though... */ static int pmacpic_find_viaint(void) { @@ -692,7 +618,7 @@ static int pmacpic_find_viaint(void) np = of_find_node_by_name(NULL, "via-pmu"); if (np == NULL) goto not_found; - viaint = np->intrs[0].line; + viaint = irq_of_parse_and_map(np, 0);; #endif /* CONFIG_ADB_PMU */ not_found: diff --git a/arch/powerpc/platforms/powermac/pmac.h b/arch/powerpc/platforms/powermac/pmac.h index 21c7b0f8f32..94e7b24b840 100644 --- a/arch/powerpc/platforms/powermac/pmac.h +++ b/arch/powerpc/platforms/powermac/pmac.h @@ -12,6 +12,8 @@ struct rtc_time; +extern int pmac_newworld; + extern long pmac_time_init(void); extern unsigned long pmac_get_boot_time(void); extern void pmac_get_rtc_time(struct rtc_time *); diff --git a/arch/powerpc/platforms/powermac/setup.c b/arch/powerpc/platforms/powermac/setup.c index 8654b5f0783..31a9da769fa 100644 --- a/arch/powerpc/platforms/powermac/setup.c +++ b/arch/powerpc/platforms/powermac/setup.c @@ -613,9 +613,6 @@ static void __init pmac_init_early(void) udbg_adb_init(!!strstr(cmd_line, "btextdbg")); #ifdef CONFIG_PPC64 - /* Setup interrupt mapping options */ - ppc64_interrupt_controller = IC_OPEN_PIC; - iommu_init_early_dart(); #endif } diff --git a/arch/powerpc/platforms/pseries/ras.c b/arch/powerpc/platforms/pseries/ras.c index 9639c66b453..9df783088b6 100644 --- a/arch/powerpc/platforms/pseries/ras.c +++ b/arch/powerpc/platforms/pseries/ras.c @@ -72,32 +72,62 @@ static irqreturn_t ras_error_interrupt(int irq, void *dev_id, /* #define DEBUG */ -static void request_ras_irqs(struct device_node *np, char *propname, + +static void request_ras_irqs(struct device_node *np, irqreturn_t (*handler)(int, void *, struct pt_regs *), const char *name) { - unsigned int *ireg, len, i; - int virq, n_intr; - - ireg = (unsigned int *)get_property(np, propname, &len); - if (ireg == NULL) - return; - n_intr = prom_n_intr_cells(np); - len /= n_intr * sizeof(*ireg); - - for (i = 0; i < len; i++) { - virq = virt_irq_create_mapping(*ireg); - if (virq == NO_IRQ) { - printk(KERN_ERR "Unable to allocate interrupt " - "number for %s\n", np->full_name); - return; + int i, index, count = 0; + struct of_irq oirq; + u32 *opicprop; + unsigned int opicplen; + unsigned int virqs[16]; + + /* Check for obsolete "open-pic-interrupt" property. If present, then + * map those interrupts using the default interrupt host and default + * trigger + */ + opicprop = (u32 *)get_property(np, "open-pic-interrupt", &opicplen); + if (opicprop) { + opicplen /= sizeof(u32); + for (i = 0; i < opicplen; i++) { + if (count > 15) + break; + virqs[count] = irq_create_mapping(NULL, *(opicprop++), + IRQ_TYPE_NONE); + if (virqs[count] == NO_IRQ) + printk(KERN_ERR "Unable to allocate interrupt " + "number for %s\n", np->full_name); + else + count++; + } - if (request_irq(irq_offset_up(virq), handler, 0, name, NULL)) { + } + /* Else use normal interrupt tree parsing */ + else { + /* First try to do a proper OF tree parsing */ + for (index = 0; of_irq_map_one(np, index, &oirq) == 0; + index++) { + if (count > 15) + break; + virqs[count] = irq_create_of_mapping(oirq.controller, + oirq.specifier, + oirq.size); + if (virqs[count] == NO_IRQ) + printk(KERN_ERR "Unable to allocate interrupt " + "number for %s\n", np->full_name); + else + count++; + } + } + + /* Now request them */ + for (i = 0; i < count; i++) { + if (request_irq(virqs[i], handler, 0, name, NULL)) { printk(KERN_ERR "Unable to request interrupt %d for " - "%s\n", irq_offset_up(virq), np->full_name); + "%s\n", virqs[i], np->full_name); return; } - ireg += n_intr; } } @@ -115,20 +145,14 @@ static int __init init_ras_IRQ(void) /* Internal Errors */ np = of_find_node_by_path("/event-sources/internal-errors"); if (np != NULL) { - request_ras_irqs(np, "open-pic-interrupt", ras_error_interrupt, - "RAS_ERROR"); - request_ras_irqs(np, "interrupts", ras_error_interrupt, - "RAS_ERROR"); + request_ras_irqs(np, ras_error_interrupt, "RAS_ERROR"); of_node_put(np); } /* EPOW Events */ np = of_find_node_by_path("/event-sources/epow-events"); if (np != NULL) { - request_ras_irqs(np, "open-pic-interrupt", ras_epow_interrupt, - "RAS_EPOW"); - request_ras_irqs(np, "interrupts", ras_epow_interrupt, - "RAS_EPOW"); + request_ras_irqs(np, ras_epow_interrupt, "RAS_EPOW"); of_node_put(np); } @@ -162,7 +186,7 @@ ras_epow_interrupt(int irq, void *dev_id, struct pt_regs * regs) status = rtas_call(ras_check_exception_token, 6, 1, NULL, RAS_VECTOR_OFFSET, - virt_irq_to_real(irq_offset_down(irq)), + irq_map[irq].hwirq, RTAS_EPOW_WARNING | RTAS_POWERMGM_EVENTS, critical, __pa(&ras_log_buf), rtas_get_error_log_max()); @@ -198,7 +222,7 @@ ras_error_interrupt(int irq, void *dev_id, struct pt_regs * regs) status = rtas_call(ras_check_exception_token, 6, 1, NULL, RAS_VECTOR_OFFSET, - virt_irq_to_real(irq_offset_down(irq)), + irq_map[irq].hwirq, RTAS_INTERNAL_ERROR, 1 /*Time Critical */, __pa(&ras_log_buf), rtas_get_error_log_max()); diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 476b564a208..54a52437265 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -76,6 +76,9 @@ #define DBG(fmt...) #endif +/* move those away to a .h */ +extern void smp_init_pseries_mpic(void); +extern void smp_init_pseries_xics(void); extern void find_udbg_vterm(void); int fwnmi_active; /* TRUE if an FWNMI handler is present */ @@ -83,7 +86,7 @@ int fwnmi_active; /* TRUE if an FWNMI handler is present */ static void pseries_shared_idle_sleep(void); static void pseries_dedicated_idle_sleep(void); -struct mpic *pSeries_mpic; +static struct device_node *pSeries_mpic_node; static void pSeries_show_cpuinfo(struct seq_file *m) { @@ -118,78 +121,92 @@ static void __init fwnmi_init(void) fwnmi_active = 1; } -void pSeries_8259_cascade(unsigned int irq, struct irq_desc *desc, +void pseries_8259_cascade(unsigned int irq, struct irq_desc *desc, struct pt_regs *regs) { - unsigned int max = 100; - - while(max--) { - int cascade_irq = i8259_irq(regs); - if (max == 99) - desc->chip->eoi(irq); - if (cascade_irq < 0) - break; + unsigned int cascade_irq = i8259_irq(regs); + if (cascade_irq != NO_IRQ) generic_handle_irq(cascade_irq, regs); - }; + desc->chip->eoi(irq); } -static void __init pSeries_init_mpic(void) +static void __init pseries_mpic_init_IRQ(void) { + struct device_node *np, *old, *cascade = NULL; unsigned int *addrp; - struct device_node *np; unsigned long intack = 0; - - /* All ISUs are setup, complete initialization */ - mpic_init(pSeries_mpic); - - /* Check what kind of cascade ACK we have */ - if (!(np = of_find_node_by_name(NULL, "pci")) - || !(addrp = (unsigned int *) - get_property(np, "8259-interrupt-acknowledge", NULL))) - printk(KERN_ERR "Cannot find pci to get ack address\n"); - else - intack = addrp[prom_n_addr_cells(np)-1]; - of_node_put(np); - - /* Setup the legacy interrupts & controller */ - i8259_init(intack, 0); - - /* Hook cascade to mpic */ - set_irq_chained_handler(NUM_ISA_INTERRUPTS, pSeries_8259_cascade); -} - -static void __init pSeries_setup_mpic(void) -{ unsigned int *opprop; unsigned long openpic_addr = 0; - unsigned char senses[NR_IRQS - NUM_ISA_INTERRUPTS]; - struct device_node *root; - int irq_count; + unsigned int cascade_irq; + int naddr, n, i, opplen; + struct mpic *mpic; - /* Find the Open PIC if present */ - root = of_find_node_by_path("/"); - opprop = (unsigned int *) get_property(root, "platform-open-pic", NULL); + np = of_find_node_by_path("/"); + naddr = prom_n_addr_cells(np); + opprop = (unsigned int *) get_property(np, "platform-open-pic", &opplen); if (opprop != 0) { - int n = prom_n_addr_cells(root); - - for (openpic_addr = 0; n > 0; --n) - openpic_addr = (openpic_addr << 32) + *opprop++; + openpic_addr = of_read_number(opprop, naddr); printk(KERN_DEBUG "OpenPIC addr: %lx\n", openpic_addr); } - of_node_put(root); + of_node_put(np); BUG_ON(openpic_addr == 0); - /* Get the sense values from OF */ - prom_get_irq_senses(senses, NUM_ISA_INTERRUPTS, NR_IRQS); - /* Setup the openpic driver */ - irq_count = NR_IRQS - NUM_ISA_INTERRUPTS - 4; /* leave room for IPIs */ - pSeries_mpic = mpic_alloc(openpic_addr, MPIC_PRIMARY, - 16, 16, irq_count, /* isu size, irq offset, irq count */ - NR_IRQS - 4, /* ipi offset */ - senses, irq_count, /* sense & sense size */ - " MPIC "); + mpic = mpic_alloc(pSeries_mpic_node, openpic_addr, + MPIC_PRIMARY, + 16, 250, /* isu size, irq count */ + " MPIC "); + BUG_ON(mpic == NULL); + + /* Add ISUs */ + opplen /= sizeof(u32); + for (n = 0, i = naddr; i < opplen; i += naddr, n++) { + unsigned long isuaddr = of_read_number(opprop + i, naddr); + mpic_assign_isu(mpic, n, isuaddr); + } + + /* All ISUs are setup, complete initialization */ + mpic_init(mpic); + + /* Look for cascade */ + for_each_node_by_type(np, "interrupt-controller") + if (device_is_compatible(np, "chrp,iic")) { + cascade = np; + break; + } + if (cascade == NULL) + return; + + cascade_irq = irq_of_parse_and_map(cascade, 0); + if (cascade == NO_IRQ) { + printk(KERN_ERR "xics: failed to map cascade interrupt"); + return; + } + + /* Check ACK type */ + for (old = of_node_get(cascade); old != NULL ; old = np) { + np = of_get_parent(old); + of_node_put(old); + if (np == NULL) + break; + if (strcmp(np->name, "pci") != 0) + continue; + addrp = (u32 *)get_property(np, "8259-interrupt-acknowledge", + NULL); + if (addrp == NULL) + continue; + naddr = prom_n_addr_cells(np); + intack = addrp[naddr-1]; + if (naddr > 1) + intack |= ((unsigned long)addrp[naddr-2]) << 32; + } + if (intack) + printk(KERN_DEBUG "mpic: PCI 8259 intack at 0x%016lx\n", + intack); + i8259_init(cascade, intack); + of_node_put(cascade); + set_irq_chained_handler(cascade_irq, pseries_8259_cascade); } static void pseries_lpar_enable_pmcs(void) @@ -207,21 +224,67 @@ static void pseries_lpar_enable_pmcs(void) get_lppaca()->pmcregs_in_use = 1; } -static void __init pSeries_setup_arch(void) +#ifdef CONFIG_KEXEC +static void pseries_kexec_cpu_down_mpic(int crash_shutdown, int secondary) { - /* Fixup ppc_md depending on the type of interrupt controller */ - if (ppc64_interrupt_controller == IC_OPEN_PIC) { - ppc_md.init_IRQ = pSeries_init_mpic; - ppc_md.get_irq = mpic_get_irq; - /* Allocate the mpic now, so that find_and_init_phbs() can - * fill the ISUs */ - pSeries_setup_mpic(); - } else - ppc_md.init_IRQ = xics_init_IRQ; + mpic_teardown_this_cpu(secondary); +} +static void pseries_kexec_cpu_down_xics(int crash_shutdown, int secondary) +{ + /* Don't risk a hypervisor call if we're crashing */ + if (firmware_has_feature(FW_FEATURE_SPLPAR) && !crash_shutdown) { + unsigned long vpa = __pa(get_lppaca()); + + if (unregister_vpa(hard_smp_processor_id(), vpa)) { + printk("VPA deregistration of cpu %u (hw_cpu_id %d) " + "failed\n", smp_processor_id(), + hard_smp_processor_id()); + } + } + xics_teardown_cpu(secondary); +} +#endif /* CONFIG_KEXEC */ + +static void __init pseries_discover_pic(void) +{ + struct device_node *np; + char *typep; + + for (np = NULL; (np = of_find_node_by_name(np, + "interrupt-controller"));) { + typep = (char *)get_property(np, "compatible", NULL); + if (strstr(typep, "open-pic")) { + pSeries_mpic_node = of_node_get(np); + ppc_md.init_IRQ = pseries_mpic_init_IRQ; + ppc_md.get_irq = mpic_get_irq; +#ifdef CONFIG_KEXEC + ppc_md.kexec_cpu_down = pseries_kexec_cpu_down_mpic; +#endif #ifdef CONFIG_SMP - smp_init_pSeries(); + smp_init_pseries_mpic(); #endif + return; + } else if (strstr(typep, "ppc-xicp")) { + ppc_md.init_IRQ = xics_init_IRQ; +#ifdef CONFIG_KEXEC + ppc_md.kexec_cpu_down = pseries_kexec_cpu_down_xics; +#endif +#ifdef CONFIG_SMP + smp_init_pseries_xics(); +#endif + return; + } + } + printk(KERN_ERR "pSeries_discover_pic: failed to recognize" + " interrupt-controller\n"); +} + +static void __init pSeries_setup_arch(void) +{ + /* Discover PIC type and setup ppc_md accordingly */ + pseries_discover_pic(); + /* openpic global configuration register (64-bit format). */ /* openpic Interrupt Source Unit pointer (64-bit format). */ /* python0 facility area (mmio) (64-bit format) REAL address. */ @@ -273,33 +336,6 @@ static int __init pSeries_init_panel(void) } arch_initcall(pSeries_init_panel); -static void __init pSeries_discover_pic(void) -{ - struct device_node *np; - char *typep; - - /* - * Setup interrupt mapping options that are needed for finish_device_tree - * to properly parse the OF interrupt tree & do the virtual irq mapping - */ - __irq_offset_value = NUM_ISA_INTERRUPTS; - ppc64_interrupt_controller = IC_INVALID; - for (np = NULL; (np = of_find_node_by_name(np, "interrupt-controller"));) { - typep = (char *)get_property(np, "compatible", NULL); - if (strstr(typep, "open-pic")) { - ppc64_interrupt_controller = IC_OPEN_PIC; - break; - } else if (strstr(typep, "ppc-xicp")) { - ppc64_interrupt_controller = IC_PPC_XIC; - break; - } - } - if (ppc64_interrupt_controller == IC_INVALID) - printk("pSeries_discover_pic: failed to recognize" - " interrupt-controller\n"); - -} - static void pSeries_mach_cpu_die(void) { local_irq_disable(); @@ -342,8 +378,6 @@ static void __init pSeries_init_early(void) iommu_init_early_pSeries(); - pSeries_discover_pic(); - DBG(" <- pSeries_init_early()\n"); } @@ -515,27 +549,6 @@ static int pSeries_pci_probe_mode(struct pci_bus *bus) return PCI_PROBE_NORMAL; } -#ifdef CONFIG_KEXEC -static void pseries_kexec_cpu_down(int crash_shutdown, int secondary) -{ - /* Don't risk a hypervisor call if we're crashing */ - if (firmware_has_feature(FW_FEATURE_SPLPAR) && !crash_shutdown) { - unsigned long vpa = __pa(get_lppaca()); - - if (unregister_vpa(hard_smp_processor_id(), vpa)) { - printk("VPA deregistration of cpu %u (hw_cpu_id %d) " - "failed\n", smp_processor_id(), - hard_smp_processor_id()); - } - } - - if (ppc64_interrupt_controller == IC_OPEN_PIC) - mpic_teardown_this_cpu(secondary); - else - xics_teardown_cpu(secondary); -} -#endif - define_machine(pseries) { .name = "pSeries", .probe = pSeries_probe, @@ -560,7 +573,6 @@ define_machine(pseries) { .system_reset_exception = pSeries_system_reset_exception, .machine_check_exception = pSeries_machine_check_exception, #ifdef CONFIG_KEXEC - .kexec_cpu_down = pseries_kexec_cpu_down, .machine_kexec = default_machine_kexec, .machine_kexec_prepare = default_machine_kexec_prepare, .machine_crash_shutdown = default_machine_crash_shutdown, diff --git a/arch/powerpc/platforms/pseries/smp.c b/arch/powerpc/platforms/pseries/smp.c index 4ad144df49c..ac61098ff40 100644 --- a/arch/powerpc/platforms/pseries/smp.c +++ b/arch/powerpc/platforms/pseries/smp.c @@ -416,27 +416,12 @@ static struct smp_ops_t pSeries_xics_smp_ops = { #endif /* This is called very early */ -void __init smp_init_pSeries(void) +static void __init smp_init_pseries(void) { int i; DBG(" -> smp_init_pSeries()\n"); - switch (ppc64_interrupt_controller) { -#ifdef CONFIG_MPIC - case IC_OPEN_PIC: - smp_ops = &pSeries_mpic_smp_ops; - break; -#endif -#ifdef CONFIG_XICS - case IC_PPC_XIC: - smp_ops = &pSeries_xics_smp_ops; - break; -#endif - default: - panic("Invalid interrupt controller"); - } - #ifdef CONFIG_HOTPLUG_CPU smp_ops->cpu_disable = pSeries_cpu_disable; smp_ops->cpu_die = pSeries_cpu_die; @@ -471,3 +456,18 @@ void __init smp_init_pSeries(void) DBG(" <- smp_init_pSeries()\n"); } +#ifdef CONFIG_MPIC +void __init smp_init_pseries_mpic(void) +{ + smp_ops = &pSeries_mpic_smp_ops; + + smp_init_pseries(); +} +#endif + +void __init smp_init_pseries_xics(void) +{ + smp_ops = &pSeries_xics_smp_ops; + + smp_init_pseries(); +} diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index c7f04420066..716972aa977 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -8,6 +8,9 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ + +#undef DEBUG + #include <linux/types.h> #include <linux/threads.h> #include <linux/kernel.h> @@ -19,6 +22,7 @@ #include <linux/gfp.h> #include <linux/radix-tree.h> #include <linux/cpu.h> + #include <asm/firmware.h> #include <asm/prom.h> #include <asm/io.h> @@ -31,9 +35,6 @@ #include "xics.h" -/* This is used to map real irq numbers to virtual */ -static struct radix_tree_root irq_map = RADIX_TREE_INIT(GFP_ATOMIC); - #define XICS_IPI 2 #define XICS_IRQ_SPURIOUS 0 @@ -64,12 +65,12 @@ struct xics_ipl { static struct xics_ipl __iomem *xics_per_cpu[NR_CPUS]; -static int xics_irq_8259_cascade = 0; -static int xics_irq_8259_cascade_real = 0; static unsigned int default_server = 0xFF; static unsigned int default_distrib_server = 0; static unsigned int interrupt_server_size = 8; +static struct irq_host *xics_host; + /* * XICS only has a single IPI, so encode the messages per CPU */ @@ -85,7 +86,7 @@ static int ibm_int_off; /* Direct HW low level accessors */ -static inline int direct_xirr_info_get(int n_cpu) +static inline unsigned int direct_xirr_info_get(int n_cpu) { return in_be32(&xics_per_cpu[n_cpu]->xirr.word); } @@ -130,7 +131,7 @@ static inline long plpar_xirr(unsigned long *xirr_ret) return plpar_hcall(H_XIRR, 0, 0, 0, 0, xirr_ret, &dummy, &dummy); } -static inline int lpar_xirr_info_get(int n_cpu) +static inline unsigned int lpar_xirr_info_get(int n_cpu) { unsigned long lpar_rc; unsigned long return_value; @@ -138,7 +139,7 @@ static inline int lpar_xirr_info_get(int n_cpu) lpar_rc = plpar_xirr(&return_value); if (lpar_rc != H_SUCCESS) panic(" bad return code xirr - rc = %lx \n", lpar_rc); - return (int)return_value; + return (unsigned int)return_value; } static inline void lpar_xirr_info_set(int n_cpu, int value) @@ -175,11 +176,11 @@ static inline void lpar_qirr_info(int n_cpu , u8 value) #ifdef CONFIG_SMP -static int get_irq_server(unsigned int irq) +static int get_irq_server(unsigned int virq) { unsigned int server; /* For the moment only implement delivery to all cpus or one cpu */ - cpumask_t cpumask = irq_desc[irq].affinity; + cpumask_t cpumask = irq_desc[virq].affinity; cpumask_t tmp = CPU_MASK_NONE; if (!distribute_irqs) @@ -200,7 +201,7 @@ static int get_irq_server(unsigned int irq) } #else -static int get_irq_server(unsigned int irq) +static int get_irq_server(unsigned int virq) { return default_server; } @@ -213,9 +214,11 @@ static void xics_unmask_irq(unsigned int virq) int call_status; unsigned int server; - irq = virt_irq_to_real(irq_offset_down(virq)); - WARN_ON(irq == NO_IRQ); - if (irq == XICS_IPI || irq == NO_IRQ) + pr_debug("xics: unmask virq %d\n", virq); + + irq = (unsigned int)irq_map[virq].hwirq; + pr_debug(" -> map to hwirq 0x%x\n", irq); + if (irq == XICS_IPI || irq == XICS_IRQ_SPURIOUS) return; server = get_irq_server(virq); @@ -267,75 +270,57 @@ static void xics_mask_irq(unsigned int virq) { unsigned int irq; - irq = virt_irq_to_real(irq_offset_down(virq)); - WARN_ON(irq == NO_IRQ); - if (irq != NO_IRQ) - xics_mask_real_irq(irq); + pr_debug("xics: mask virq %d\n", virq); + + irq = (unsigned int)irq_map[virq].hwirq; + if (irq == XICS_IPI || irq == XICS_IRQ_SPURIOUS) + return; + xics_mask_real_irq(irq); } -static void xics_set_irq_revmap(unsigned int virq) +static unsigned int xics_startup(unsigned int virq) { unsigned int irq; - irq = irq_offset_down(virq); - if (radix_tree_insert(&irq_map, virt_irq_to_real(irq), - &virt_irq_to_real_map[irq]) == -ENOMEM) - printk(KERN_CRIT "Out of memory creating real -> virtual" - " IRQ mapping for irq %u (real 0x%x)\n", - virq, virt_irq_to_real(irq)); -} + /* force a reverse mapping of the interrupt so it gets in the cache */ + irq = (unsigned int)irq_map[virq].hwirq; + irq_radix_revmap(xics_host, irq); -static unsigned int xics_startup(unsigned int virq) -{ - xics_set_irq_revmap(virq); + /* unmask it */ xics_unmask_irq(virq); return 0; } -static unsigned int real_irq_to_virt(unsigned int real_irq) -{ - unsigned int *ptr; - - ptr = radix_tree_lookup(&irq_map, real_irq); - if (ptr == NULL) - return NO_IRQ; - return ptr - virt_irq_to_real_map; -} - -static void xics_eoi_direct(unsigned int irq) +static void xics_eoi_direct(unsigned int virq) { int cpu = smp_processor_id(); + unsigned int irq = (unsigned int)irq_map[virq].hwirq; iosync(); - direct_xirr_info_set(cpu, ((0xff << 24) | - (virt_irq_to_real(irq_offset_down(irq))))); + direct_xirr_info_set(cpu, (0xff << 24) | irq); } -static void xics_eoi_lpar(unsigned int irq) +static void xics_eoi_lpar(unsigned int virq) { int cpu = smp_processor_id(); + unsigned int irq = (unsigned int)irq_map[virq].hwirq; iosync(); - lpar_xirr_info_set(cpu, ((0xff << 24) | - (virt_irq_to_real(irq_offset_down(irq))))); - + lpar_xirr_info_set(cpu, (0xff << 24) | irq); } -static inline int xics_remap_irq(int vec) +static inline unsigned int xics_remap_irq(unsigned int vec) { - int irq; + unsigned int irq; vec &= 0x00ffffff; if (vec == XICS_IRQ_SPURIOUS) return NO_IRQ; - - irq = real_irq_to_virt(vec); - if (irq == NO_IRQ) - irq = real_irq_to_virt_slowpath(vec); + irq = irq_radix_revmap(xics_host, vec); if (likely(irq != NO_IRQ)) - return irq_offset_up(irq); + return irq; printk(KERN_ERR "Interrupt %u (real) is invalid," " disabling it.\n", vec); @@ -343,14 +328,14 @@ static inline int xics_remap_irq(int vec) return NO_IRQ; } -static int xics_get_irq_direct(struct pt_regs *regs) +static unsigned int xics_get_irq_direct(struct pt_regs *regs) { unsigned int cpu = smp_processor_id(); return xics_remap_irq(direct_xirr_info_get(cpu)); } -static int xics_get_irq_lpar(struct pt_regs *regs) +static unsigned int xics_get_irq_lpar(struct pt_regs *regs) { unsigned int cpu = smp_processor_id(); @@ -437,8 +422,8 @@ static void xics_set_affinity(unsigned int virq, cpumask_t cpumask) unsigned long newmask; cpumask_t tmp = CPU_MASK_NONE; - irq = virt_irq_to_real(irq_offset_down(virq)); - if (irq == XICS_IPI || irq == NO_IRQ) + irq = (unsigned int)irq_map[virq].hwirq; + if (irq == XICS_IPI || irq == XICS_IRQ_SPURIOUS) return; status = rtas_call(ibm_get_xive, 1, 3, xics_status, irq); @@ -469,6 +454,24 @@ static void xics_set_affinity(unsigned int virq, cpumask_t cpumask) } } +void xics_setup_cpu(void) +{ + int cpu = smp_processor_id(); + + xics_set_cpu_priority(cpu, 0xff); + + /* + * Put the calling processor into the GIQ. This is really only + * necessary from a secondary thread as the OF start-cpu interface + * performs this function for us on primary threads. + * + * XXX: undo of teardown on kexec needs this too, as may hotplug + */ + rtas_set_indicator(GLOBAL_INTERRUPT_QUEUE, + (1UL << interrupt_server_size) - 1 - default_distrib_server, 1); +} + + static struct irq_chip xics_pic_direct = { .typename = " XICS ", .startup = xics_startup, @@ -489,90 +492,245 @@ static struct irq_chip xics_pic_lpar = { }; -void xics_setup_cpu(void) +static int xics_host_match(struct irq_host *h, struct device_node *node) { - int cpu = smp_processor_id(); + /* IBM machines have interrupt parents of various funky types for things + * like vdevices, events, etc... The trick we use here is to match + * everything here except the legacy 8259 which is compatible "chrp,iic" + */ + return !device_is_compatible(node, "chrp,iic"); +} - xics_set_cpu_priority(cpu, 0xff); +static int xics_host_map_direct(struct irq_host *h, unsigned int virq, + irq_hw_number_t hw, unsigned int flags) +{ + unsigned int sense = flags & IRQ_TYPE_SENSE_MASK; - /* - * Put the calling processor into the GIQ. This is really only - * necessary from a secondary thread as the OF start-cpu interface - * performs this function for us on primary threads. - * - * XXX: undo of teardown on kexec needs this too, as may hotplug + pr_debug("xics: map_direct virq %d, hwirq 0x%lx, flags: 0x%x\n", + virq, hw, flags); + + if (sense && sense != IRQ_TYPE_LEVEL_LOW) + printk(KERN_WARNING "xics: using unsupported sense 0x%x" + " for irq %d (h: 0x%lx)\n", flags, virq, hw); + + get_irq_desc(virq)->status |= IRQ_LEVEL; + set_irq_chip_and_handler(virq, &xics_pic_direct, handle_fasteoi_irq); + return 0; +} + +static int xics_host_map_lpar(struct irq_host *h, unsigned int virq, + irq_hw_number_t hw, unsigned int flags) +{ + unsigned int sense = flags & IRQ_TYPE_SENSE_MASK; + + pr_debug("xics: map_lpar virq %d, hwirq 0x%lx, flags: 0x%x\n", + virq, hw, flags); + + if (sense && sense != IRQ_TYPE_LEVEL_LOW) + printk(KERN_WARNING "xics: using unsupported sense 0x%x" + " for irq %d (h: 0x%lx)\n", flags, virq, hw); + + get_irq_desc(virq)->status |= IRQ_LEVEL; + set_irq_chip_and_handler(virq, &xics_pic_lpar, handle_fasteoi_irq); + return 0; +} + +static int xics_host_xlate(struct irq_host *h, struct device_node *ct, + u32 *intspec, unsigned int intsize, + irq_hw_number_t *out_hwirq, unsigned int *out_flags) + +{ + /* Current xics implementation translates everything + * to level. It is not technically right for MSIs but this + * is irrelevant at this point. We might get smarter in the future */ - rtas_set_indicator(GLOBAL_INTERRUPT_QUEUE, - (1UL << interrupt_server_size) - 1 - default_distrib_server, 1); + *out_hwirq = intspec[0]; + *out_flags = IRQ_TYPE_LEVEL_LOW; + + return 0; +} + +static struct irq_host_ops xics_host_direct_ops = { + .match = xics_host_match, + .map = xics_host_map_direct, + .xlate = xics_host_xlate, +}; + +static struct irq_host_ops xics_host_lpar_ops = { + .match = xics_host_match, + .map = xics_host_map_lpar, + .xlate = xics_host_xlate, +}; + +static void __init xics_init_host(void) +{ + struct irq_host_ops *ops; + + if (firmware_has_feature(FW_FEATURE_LPAR)) + ops = &xics_host_lpar_ops; + else + ops = &xics_host_direct_ops; + xics_host = irq_alloc_host(IRQ_HOST_MAP_TREE, 0, ops, + XICS_IRQ_SPURIOUS); + BUG_ON(xics_host == NULL); + irq_set_default_host(xics_host); } -void xics_init_IRQ(void) +static void __init xics_map_one_cpu(int hw_id, unsigned long addr, + unsigned long size) { +#ifdef CONFIG_SMP int i; - unsigned long intr_size = 0; - struct device_node *np; - uint *ireg, ilen, indx = 0; - unsigned long intr_base = 0; - struct xics_interrupt_node { - unsigned long addr; - unsigned long size; - } intnodes[NR_CPUS]; - struct irq_chip *chip; - ppc64_boot_msg(0x20, "XICS Init"); + /* This may look gross but it's good enough for now, we don't quite + * have a hard -> linux processor id matching. + */ + for_each_possible_cpu(i) { + if (!cpu_present(i)) + continue; + if (hw_id == get_hard_smp_processor_id(i)) { + xics_per_cpu[i] = ioremap(addr, size); + return; + } + } +#else + if (hw_id != 0) + return; + xics_per_cpu[0] = ioremap(addr, size); +#endif /* CONFIG_SMP */ +} - ibm_get_xive = rtas_token("ibm,get-xive"); - ibm_set_xive = rtas_token("ibm,set-xive"); - ibm_int_on = rtas_token("ibm,int-on"); - ibm_int_off = rtas_token("ibm,int-off"); +static void __init xics_init_one_node(struct device_node *np, + unsigned int *indx) +{ + unsigned int ilen; + u32 *ireg; - np = of_find_node_by_type(NULL, "PowerPC-External-Interrupt-Presentation"); - if (!np) - panic("xics_init_IRQ: can't find interrupt presentation"); + /* This code does the theorically broken assumption that the interrupt + * server numbers are the same as the hard CPU numbers. + * This happens to be the case so far but we are playing with fire... + * should be fixed one of these days. -BenH. + */ + ireg = (u32 *)get_property(np, "ibm,interrupt-server-ranges", NULL); -nextnode: - ireg = (uint *)get_property(np, "ibm,interrupt-server-ranges", NULL); + /* Do that ever happen ? we'll know soon enough... but even good'old + * f80 does have that property .. + */ + WARN_ON(ireg == NULL); if (ireg) { /* * set node starting index for this node */ - indx = *ireg; + *indx = *ireg; } - - ireg = (uint *)get_property(np, "reg", &ilen); + ireg = (u32 *)get_property(np, "reg", &ilen); if (!ireg) panic("xics_init_IRQ: can't find interrupt reg property"); - while (ilen) { - intnodes[indx].addr = (unsigned long)*ireg++ << 32; - ilen -= sizeof(uint); - intnodes[indx].addr |= *ireg++; - ilen -= sizeof(uint); - intnodes[indx].size = (unsigned long)*ireg++ << 32; - ilen -= sizeof(uint); - intnodes[indx].size |= *ireg++; - ilen -= sizeof(uint); - indx++; - if (indx >= NR_CPUS) break; + while (ilen >= (4 * sizeof(u32))) { + unsigned long addr, size; + + /* XXX Use proper OF parsing code here !!! */ + addr = (unsigned long)*ireg++ << 32; + ilen -= sizeof(u32); + addr |= *ireg++; + ilen -= sizeof(u32); + size = (unsigned long)*ireg++ << 32; + ilen -= sizeof(u32); + size |= *ireg++; + ilen -= sizeof(u32); + xics_map_one_cpu(*indx, addr, size); + (*indx)++; + } +} + + +static void __init xics_setup_8259_cascade(void) +{ + struct device_node *np, *old, *found = NULL; + int cascade, naddr; + u32 *addrp; + unsigned long intack = 0; + + for_each_node_by_type(np, "interrupt-controller") + if (device_is_compatible(np, "chrp,iic")) { + found = np; + break; + } + if (found == NULL) { + printk(KERN_DEBUG "xics: no ISA interrupt controller\n"); + return; } + cascade = irq_of_parse_and_map(found, 0); + if (cascade == NO_IRQ) { + printk(KERN_ERR "xics: failed to map cascade interrupt"); + return; + } + pr_debug("xics: cascade mapped to irq %d\n", cascade); + + for (old = of_node_get(found); old != NULL ; old = np) { + np = of_get_parent(old); + of_node_put(old); + if (np == NULL) + break; + if (strcmp(np->name, "pci") != 0) + continue; + addrp = (u32 *)get_property(np, "8259-interrupt-acknowledge", NULL); + if (addrp == NULL) + continue; + naddr = prom_n_addr_cells(np); + intack = addrp[naddr-1]; + if (naddr > 1) + intack |= ((unsigned long)addrp[naddr-2]) << 32; + } + if (intack) + printk(KERN_DEBUG "xics: PCI 8259 intack at 0x%016lx\n", intack); + i8259_init(found, intack); + of_node_put(found); + set_irq_chained_handler(cascade, pseries_8259_cascade); +} - np = of_find_node_by_type(np, "PowerPC-External-Interrupt-Presentation"); - if ((indx < NR_CPUS) && np) goto nextnode; +void __init xics_init_IRQ(void) +{ + int i; + struct device_node *np; + u32 *ireg, ilen, indx = 0; + int found = 0; + + ppc64_boot_msg(0x20, "XICS Init"); + + ibm_get_xive = rtas_token("ibm,get-xive"); + ibm_set_xive = rtas_token("ibm,set-xive"); + ibm_int_on = rtas_token("ibm,int-on"); + ibm_int_off = rtas_token("ibm,int-off"); + + for_each_node_by_type(np, "PowerPC-External-Interrupt-Presentation") { + found = 1; + if (firmware_has_feature(FW_FEATURE_LPAR)) + break; + xics_init_one_node(np, &indx); + } + if (found == 0) + return; + + xics_init_host(); /* Find the server numbers for the boot cpu. */ for (np = of_find_node_by_type(NULL, "cpu"); np; np = of_find_node_by_type(np, "cpu")) { - ireg = (uint *)get_property(np, "reg", &ilen); + ireg = (u32 *)get_property(np, "reg", &ilen); if (ireg && ireg[0] == get_hard_smp_processor_id(boot_cpuid)) { - ireg = (uint *)get_property(np, "ibm,ppc-interrupt-gserver#s", - &ilen); + ireg = (u32 *)get_property(np, + "ibm,ppc-interrupt-gserver#s", + &ilen); i = ilen / sizeof(int); if (ireg && i > 0) { default_server = ireg[0]; - default_distrib_server = ireg[i-1]; /* take last element */ + /* take last element */ + default_distrib_server = ireg[i-1]; } - ireg = (uint *)get_property(np, + ireg = (u32 *)get_property(np, "ibm,interrupt-server#-size", NULL); if (ireg) interrupt_server_size = *ireg; @@ -581,102 +739,48 @@ nextnode: } of_node_put(np); - intr_base = intnodes[0].addr; - intr_size = intnodes[0].size; - - if (firmware_has_feature(FW_FEATURE_LPAR)) { - ppc_md.get_irq = xics_get_irq_lpar; - chip = &xics_pic_lpar; - } else { -#ifdef CONFIG_SMP - for_each_possible_cpu(i) { - int hard_id; - - /* FIXME: Do this dynamically! --RR */ - if (!cpu_present(i)) - continue; - - hard_id = get_hard_smp_processor_id(i); - xics_per_cpu[i] = ioremap(intnodes[hard_id].addr, - intnodes[hard_id].size); - } -#else - xics_per_cpu[0] = ioremap(intr_base, intr_size); -#endif /* CONFIG_SMP */ + if (firmware_has_feature(FW_FEATURE_LPAR)) + ppc_md.get_irq = xics_get_irq_lpar; + else ppc_md.get_irq = xics_get_irq_direct; - chip = &xics_pic_direct; - - } - - for (i = irq_offset_value(); i < NR_IRQS; ++i) { - /* All IRQs on XICS are level for now. MSI code may want to modify - * that for reporting purposes - */ - get_irq_desc(i)->status |= IRQ_LEVEL; - set_irq_chip_and_handler(i, chip, handle_fasteoi_irq); - } xics_setup_cpu(); - ppc64_boot_msg(0x21, "XICS Done"); -} + xics_setup_8259_cascade(); -static int xics_setup_8259_cascade(void) -{ - struct device_node *np; - uint *ireg; - - np = of_find_node_by_type(NULL, "interrupt-controller"); - if (np == NULL) { - printk(KERN_WARNING "xics: no ISA interrupt controller\n"); - xics_irq_8259_cascade_real = -1; - xics_irq_8259_cascade = -1; - return 0; - } - - ireg = (uint *) get_property(np, "interrupts", NULL); - if (!ireg) - panic("xics_init_IRQ: can't find ISA interrupts property"); - - xics_irq_8259_cascade_real = *ireg; - xics_irq_8259_cascade = irq_offset_up - (virt_irq_create_mapping(xics_irq_8259_cascade_real)); - i8259_init(0, 0); - of_node_put(np); - - xics_set_irq_revmap(xics_irq_8259_cascade); - set_irq_chained_handler(xics_irq_8259_cascade, pSeries_8259_cascade); - - return 0; + ppc64_boot_msg(0x21, "XICS Done"); } -arch_initcall(xics_setup_8259_cascade); #ifdef CONFIG_SMP void xics_request_IPIs(void) { - virt_irq_to_real_map[XICS_IPI] = XICS_IPI; + unsigned int ipi; + + ipi = irq_create_mapping(xics_host, XICS_IPI, 0); + BUG_ON(ipi == NO_IRQ); /* * IPIs are marked IRQF_DISABLED as they must run with irqs * disabled */ - set_irq_handler(irq_offset_up(XICS_IPI), handle_percpu_irq); + set_irq_handler(ipi, handle_percpu_irq); if (firmware_has_feature(FW_FEATURE_LPAR)) - request_irq(irq_offset_up(XICS_IPI), xics_ipi_action_lpar, - SA_INTERRUPT, "IPI", NULL); + request_irq(ipi, xics_ipi_action_lpar, IRQF_DISABLED, + "IPI", NULL); else - request_irq(irq_offset_up(XICS_IPI), xics_ipi_action_direct, - SA_INTERRUPT, "IPI", NULL); + request_irq(ipi, xics_ipi_action_direct, IRQF_DISABLED, + "IPI", NULL); } #endif /* CONFIG_SMP */ void xics_teardown_cpu(int secondary) { - struct irq_desc *desc = get_irq_desc(irq_offset_up(XICS_IPI)); int cpu = smp_processor_id(); + unsigned int ipi; + struct irq_desc *desc; - xics_set_cpu_priority(cpu, 0); + xics_set_cpu_priority(cpu, 0); /* * we need to EOI the IPI if we got here from kexec down IPI @@ -685,6 +789,11 @@ void xics_teardown_cpu(int secondary) * should we be flagging idle loop instead? * or creating some task to be scheduled? */ + + ipi = irq_find_mapping(xics_host, XICS_IPI); + if (ipi == XICS_IRQ_SPURIOUS) + return; + desc = get_irq_desc(ipi); if (desc->chip && desc->chip->eoi) desc->chip->eoi(XICS_IPI); @@ -694,8 +803,8 @@ void xics_teardown_cpu(int secondary) */ if (secondary) rtas_set_indicator(GLOBAL_INTERRUPT_QUEUE, - (1UL << interrupt_server_size) - 1 - - default_distrib_server, 0); + (1UL << interrupt_server_size) - 1 - + default_distrib_server, 0); } #ifdef CONFIG_HOTPLUG_CPU @@ -723,15 +832,15 @@ void xics_migrate_irqs_away(void) unsigned long flags; /* We cant set affinity on ISA interrupts */ - if (virq < irq_offset_value()) + if (virq < NUM_ISA_INTERRUPTS) continue; - - desc = get_irq_desc(virq); - irq = virt_irq_to_real(irq_offset_down(virq)); - + if (irq_map[virq].host != xics_host) + continue; + irq = (unsigned int)irq_map[virq].hwirq; /* We need to get IPIs still. */ - if (irq == XICS_IPI || irq == NO_IRQ) + if (irq == XICS_IPI || irq == XICS_IRQ_SPURIOUS) continue; + desc = get_irq_desc(virq); /* We only need to migrate enabled IRQS */ if (desc == NULL || desc->chip == NULL diff --git a/arch/powerpc/platforms/pseries/xics.h b/arch/powerpc/platforms/pseries/xics.h index 67dedf3514e..6ee1055b0ff 100644 --- a/arch/powerpc/platforms/pseries/xics.h +++ b/arch/powerpc/platforms/pseries/xics.h @@ -31,7 +31,7 @@ struct xics_ipi_struct { extern struct xics_ipi_struct xics_ipi_message[NR_CPUS] __cacheline_aligned; struct irq_desc; -extern void pSeries_8259_cascade(unsigned int irq, struct irq_desc *desc, +extern void pseries_8259_cascade(unsigned int irq, struct irq_desc *desc, struct pt_regs *regs); #endif /* _POWERPC_KERNEL_XICS_H */ |