This patch is to add support for the MPIC inside the Axon South Bridge. Benjamin Herrenschmidt apparently has plans to generalize nested IRQ controllers on powerpc, so this may be done in different ways in the future. Signed-off-by: Shaun Wetzstein Signed-off-by: Murali Iyer Signed-off-by: Arnd Bergmann Index: linus-2.6/arch/powerpc/Kconfig =================================================================== --- linus-2.6.orig/arch/powerpc/Kconfig 2006-04-21 20:32:20.000000000 +0200 +++ linus-2.6/arch/powerpc/Kconfig 2006-04-21 20:32:25.000000000 +0200 @@ -395,7 +395,7 @@ default n config MPIC - depends on PPC_PSERIES || PPC_PMAC || PPC_MAPLE || PPC_CHRP + depends on PPC_PSERIES || PPC_PMAC || PPC_MAPLE || PPC_CHRP || PPC_CELL bool default y Index: linus-2.6/arch/powerpc/platforms/cell/interrupt.c =================================================================== --- linus-2.6.orig/arch/powerpc/platforms/cell/interrupt.c 2006-04-21 20:32:20.000000000 +0200 +++ linus-2.6/arch/powerpc/platforms/cell/interrupt.c 2006-04-21 20:32:25.000000000 +0200 @@ -31,36 +31,24 @@ #include #include #include +#include #include "interrupt.h" -struct iic_pending_bits { - u32 data; - u8 flags; - u8 class; - u8 source; - u8 prio; -}; +extern void mpic_enable_irq(unsigned int irq); +extern void mpic_disable_irq(unsigned int irq); +extern void mpic_end_irq(unsigned int irq); + +DEFINE_PER_CPU(struct iic, iic); + +struct eic cell_eic[NR_CPUS]; +struct mpic *cell_mpic; enum iic_pending_flags { IIC_VALID = 0x80, IIC_IPI = 0x40, }; -struct iic_regs { - struct iic_pending_bits pending; - struct iic_pending_bits pending_destr; - u64 generate; - u64 prio; -}; - -struct iic { - struct iic_regs __iomem *regs; - u8 target_id; -}; - -static DEFINE_PER_CPU(struct iic, iic); - void iic_local_enable(void) { out_be64(&__get_cpu_var(iic).regs->prio, 0xff); @@ -102,6 +90,9 @@ { int irq; unsigned char node, unit; + struct iic *iic; + + iic = &__get_cpu_var(iic); node = pending.source >> 4; unit = pending.source & 0xf; @@ -121,8 +112,9 @@ */ if (pending.class != 2) break; + BUG_ON(!cell_eic[node].get_irq); irq = IIC_EXT_OFFSET - + spider_get_irq(node) + + cell_eic[node].get_irq(node) + node * IIC_NODE_STRIDE; break; case 0x01 ... 0x04: @@ -220,7 +212,7 @@ struct device_node *dn; unsigned long *regs; char *compatible; - unsigned *np, found = 0; + unsigned *np, found = 0; struct iic *iic = NULL; for (dn = NULL; (dn = of_find_node_by_name(dn, "interrupt-controller"));) { @@ -259,7 +251,7 @@ printk("IIC for CPU %d at %lx mapped to %p\n", np[1], regs[2], iic->regs); found++; - } + } if (found) return 0; @@ -267,6 +259,62 @@ return -ENODEV; } +static int cell_mpic_get_irq(int node) +{ + return mpic_get_one_irq(cell_mpic, NULL); +} + +static void cell_mpic_end_irq(unsigned int irq) +{ + mpic_end_irq(irq); + iic_end(irq); +} + +void __init cell_setup_mpic(void) +{ + unsigned long * mpic_reg; + unsigned char senses[NR_IRQS]; + char *compatible; + struct device_node *dn; + int node = 0; + + for (dn = NULL; (dn = of_find_node_by_name(dn, "interrupt-controller"));) { + compatible = (char *)get_property(dn, "compatible", NULL); + + if (strstr(compatible, "CBEA,platform-open-pic")) + mpic_reg = (unsigned long *)get_property(dn,"reg", NULL); + else + continue; + + if(!mpic_reg) { + printk("Could not find mpic !\n"); + continue; + } + + /* XXX used size field */ + printk("%s:%d mpic found at %lx !\n", __FILE__, __LINE__, *mpic_reg); + + cell_eic[node].get_irq = cell_mpic_get_irq; + + prom_get_irq_senses(senses, 0, NR_IRQS); + + cell_mpic = mpic_alloc(*mpic_reg, MPIC_PRIMARY | MPIC_BIG_ENDIAN | MPIC_AXON, + 0, 0, 128+4, + 128, senses, NR_IRQS, + " MPIC "); + + mpic_init(cell_mpic); + + /* + * Axon MPIC is not the top level PIC so we need to wrap + * mpic_end_irq to unmask the IIC + */ + cell_mpic->hc_irq.end = cell_mpic_end_irq; + + node++; + } +} + #ifdef CONFIG_SMP /* Use the highest interrupt priorities for IPI */ @@ -351,5 +399,6 @@ if (iic->regs) out_be64(&iic->regs->prio, 0xff); } + iic_setup_spe_handlers(); } Index: linus-2.6/arch/powerpc/platforms/cell/interrupt.h =================================================================== --- linus-2.6.orig/arch/powerpc/platforms/cell/interrupt.h 2006-04-21 20:32:20.000000000 +0200 +++ linus-2.6/arch/powerpc/platforms/cell/interrupt.h 2006-04-21 20:32:25.000000000 +0200 @@ -46,6 +46,30 @@ IIC_NODE_STRIDE = 0x80, /* Total IRQs per node */ }; +struct iic_pending_bits { + u32 data; + u8 flags; + u8 class; + u8 source; + u8 prio; +}; + +struct iic_regs { + struct iic_pending_bits pending; + struct iic_pending_bits pending_destr; + u64 generate; + u64 prio; +}; + +struct iic { + struct iic_regs __iomem *regs; + u8 target_id; +}; + +struct eic { + int (*get_irq)(int node); +}; + extern void iic_init_IRQ(void); extern int iic_get_irq(struct pt_regs *regs); extern void iic_cause_IPI(int cpu, int mesg); @@ -59,5 +83,7 @@ extern void spider_init_IRQ(void); extern int spider_get_irq(int node); +extern struct eic cell_eic[NR_CPUS]; + #endif #endif /* ASM_CELL_PIC_H */ Index: linus-2.6/arch/powerpc/platforms/cell/setup.c =================================================================== --- linus-2.6.orig/arch/powerpc/platforms/cell/setup.c 2006-04-21 20:32:20.000000000 +0200 +++ linus-2.6/arch/powerpc/platforms/cell/setup.c 2006-04-21 20:32:25.000000000 +0200 @@ -60,6 +60,7 @@ #define DBG(fmt...) #endif extern void cell_final_fixup(void); +extern void cell_setup_mpic(void); static void cell_show_cpuinfo(struct seq_file *m) { @@ -107,6 +108,22 @@ WARN_ON(fixup); } +static void cell_init_pic(void) +{ + struct device_node *root; + + root=of_find_node_by_path("/"); + + if (get_property(root, "platform-spider-pic", NULL)) { + spider_init_IRQ(); + } else if (get_property(root, "platform-open-pic", NULL)) { + cell_setup_mpic(); + } else { + printk(KERN_ERR "Could not find external interrupt controller !\n"); + } + +} + static void __init cell_setup_arch(void) { ppc_md.init_IRQ = iic_init_IRQ; @@ -129,7 +146,10 @@ /* Find and initialize PCI host bridges */ init_pci_config_tokens(); find_and_init_phbs(); - spider_init_IRQ(); + + /* setup external interrupt controller(s) */ + cell_init_pic(); + cell_pervasive_init(); #ifdef CONFIG_DUMMY_CONSOLE conswitchp = &dummy_con; Index: linus-2.6/arch/powerpc/platforms/cell/spider-pic.c =================================================================== --- linus-2.6.orig/arch/powerpc/platforms/cell/spider-pic.c 2006-04-21 20:32:24.000000000 +0200 +++ linus-2.6/arch/powerpc/platforms/cell/spider-pic.c 2006-04-21 20:32:25.000000000 +0200 @@ -22,6 +22,8 @@ #include #include +#include +#include #include #include @@ -56,7 +58,8 @@ REISWAITEN = 0x508, /* Reissue Wait Control*/ }; -static void __iomem *spider_pics[4]; +static void __iomem *spider_pics[NR_CPUS]; +extern struct eic cell_eic[NR_CPUS]; static void __iomem *spider_get_pic(int irq) { @@ -163,6 +166,8 @@ get_irq_desc(irq)->handler = &spider_pic; } + cell_eic[node].get_irq = spider_get_irq; + /* do not mask any interrupts because of level */ out_be32(spider_pics[node] + TIR_MSK, 0x0); @@ -218,6 +223,8 @@ get_irq_desc(irq)->handler = &spider_pic; } + cell_eic[node].get_irq = spider_get_irq; + /* do not mask any interrupts because of level */ out_be32(spider_pics[node] + TIR_MSK, 0x0); Index: linus-2.6/arch/powerpc/sysdev/mpic.c =================================================================== --- linus-2.6.orig/arch/powerpc/sysdev/mpic.c 2006-04-21 20:32:20.000000000 +0200 +++ linus-2.6/arch/powerpc/sysdev/mpic.c 2006-04-21 20:32:25.000000000 +0200 @@ -61,8 +61,9 @@ static inline u32 _mpic_read(unsigned int be, volatile u32 __iomem *base, - unsigned int reg) + unsigned int reg, unsigned int stride) { + reg = reg * stride; if (be) return in_be32(base + (reg >> 2)); else @@ -70,8 +71,10 @@ } static inline void _mpic_write(unsigned int be, volatile u32 __iomem *base, - unsigned int reg, u32 value) + unsigned int reg, u32 value, unsigned int stride) { + reg = reg * stride; + if (be) out_be32(base + (reg >> 2), value); else @@ -85,14 +88,14 @@ if (mpic->flags & MPIC_BROKEN_IPI) be = !be; - return _mpic_read(be, mpic->gregs, offset); + return _mpic_read(be, mpic->gregs, offset, mpic->stride); } static inline void _mpic_ipi_write(struct mpic *mpic, unsigned int ipi, u32 value) { unsigned int offset = MPIC_GREG_IPI_VECTOR_PRI_0 + (ipi * 0x10); - _mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->gregs, offset, value); + _mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->gregs, offset, value, mpic->stride); } static inline u32 _mpic_cpu_read(struct mpic *mpic, unsigned int reg) @@ -102,7 +105,7 @@ if (mpic->flags & MPIC_PRIMARY) cpu = hard_smp_processor_id(); - return _mpic_read(mpic->flags & MPIC_BIG_ENDIAN, mpic->cpuregs[cpu], reg); + return _mpic_read(mpic->flags & MPIC_BIG_ENDIAN, mpic->cpuregs[cpu], reg, mpic->stride); } static inline void _mpic_cpu_write(struct mpic *mpic, unsigned int reg, u32 value) @@ -112,7 +115,7 @@ if (mpic->flags & MPIC_PRIMARY) cpu = hard_smp_processor_id(); - _mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->cpuregs[cpu], reg, value); + _mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->cpuregs[cpu], reg, value, mpic->stride); } static inline u32 _mpic_irq_read(struct mpic *mpic, unsigned int src_no, unsigned int reg) @@ -121,7 +124,7 @@ unsigned int idx = src_no & mpic->isu_mask; return _mpic_read(mpic->flags & MPIC_BIG_ENDIAN, mpic->isus[isu], - reg + (idx * MPIC_IRQ_STRIDE)); + reg + (idx * MPIC_IRQ_STRIDE), mpic->stride); } static inline void _mpic_irq_write(struct mpic *mpic, unsigned int src_no, @@ -131,11 +134,11 @@ unsigned int idx = src_no & mpic->isu_mask; _mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->isus[isu], - reg + (idx * MPIC_IRQ_STRIDE), value); + reg + (idx * MPIC_IRQ_STRIDE), value, mpic->stride); } -#define mpic_read(b,r) _mpic_read(mpic->flags & MPIC_BIG_ENDIAN,(b),(r)) -#define mpic_write(b,r,v) _mpic_write(mpic->flags & MPIC_BIG_ENDIAN,(b),(r),(v)) +#define mpic_read(b,r) _mpic_read(mpic->flags & MPIC_BIG_ENDIAN,(b),(r), mpic->stride) +#define mpic_write(b,r,v) _mpic_write(mpic->flags & MPIC_BIG_ENDIAN,(b),(r),(v), mpic->stride) #define mpic_ipi_read(i) _mpic_ipi_read(mpic,(i)) #define mpic_ipi_write(i,v) _mpic_ipi_write(mpic,(i),(v)) #define mpic_cpu_read(i) _mpic_cpu_read(mpic,(i)) @@ -411,7 +414,7 @@ */ -static void mpic_enable_irq(unsigned int irq) +void mpic_enable_irq(unsigned int irq) { unsigned int loops = 100000; struct mpic *mpic = mpic_from_irq(irq); @@ -458,7 +461,7 @@ return 0; } -static void mpic_disable_irq(unsigned int irq) +void mpic_disable_irq(unsigned int irq) { unsigned int loops = 100000; struct mpic *mpic = mpic_from_irq(irq); @@ -493,7 +496,7 @@ mpic_disable_irq(irq); } -static void mpic_end_irq(unsigned int irq) +void mpic_end_irq(unsigned int irq) { struct mpic *mpic = mpic_from_irq(irq); @@ -585,7 +588,6 @@ mpic = alloc_bootmem(sizeof(struct mpic)); if (mpic == NULL) return NULL; - memset(mpic, 0, sizeof(struct mpic)); mpic->name = name; @@ -614,9 +616,16 @@ mpic->senses = senses; mpic->senses_count = senses_count; + if (flags & MPIC_AXON) { + mpic->stride = 16; + } else { + mpic->stride = 1; + } + /* Map the global registers */ - mpic->gregs = ioremap(phys_addr + MPIC_GREG_BASE, 0x1000); - mpic->tmregs = mpic->gregs + ((MPIC_TIMER_BASE - MPIC_GREG_BASE) >> 2); + mpic->gregs = ioremap(phys_addr + MPIC_GREG_BASE + * mpic->stride, 0x1000 * mpic->stride); + mpic->tmregs = mpic->gregs + (((MPIC_TIMER_BASE - MPIC_GREG_BASE) >> 2) * mpic->stride); BUG_ON(mpic->gregs == NULL); /* Reset */ @@ -642,16 +651,19 @@ /* Map the per-CPU registers */ for (i = 0; i < mpic->num_cpus; i++) { - mpic->cpuregs[i] = ioremap(phys_addr + MPIC_CPU_BASE + - i * MPIC_CPU_STRIDE, 0x1000); + mpic->cpuregs[i] = ioremap(phys_addr + (MPIC_CPU_BASE + + i * MPIC_CPU_STRIDE) * + mpic->stride, + 0x1000 * mpic->stride); BUG_ON(mpic->cpuregs[i] == NULL); } /* Initialize main ISU if none provided */ if (mpic->isu_size == 0) { mpic->isu_size = mpic->num_sources; - mpic->isus[0] = ioremap(phys_addr + MPIC_IRQ_BASE, - MPIC_IRQ_STRIDE * mpic->isu_size); + mpic->isus[0] = ioremap(phys_addr + MPIC_IRQ_BASE * + mpic->stride, MPIC_IRQ_STRIDE * + mpic->stride * mpic->isu_size); BUG_ON(mpic->isus[0] == NULL); } mpic->isu_shift = 1 + __ilog2(mpic->isu_size - 1); @@ -693,7 +705,8 @@ BUG_ON(isu_num >= MPIC_MAX_ISU); - mpic->isus[isu_num] = ioremap(phys_addr, MPIC_IRQ_STRIDE * mpic->isu_size); + mpic->isus[isu_num] = ioremap(phys_addr, MPIC_IRQ_STRIDE * + mpic->isu_size * mpic->stride); if ((isu_first + mpic->isu_size) > mpic->num_sources) mpic->num_sources = isu_first + mpic->isu_size; } Index: linus-2.6/include/asm-powerpc/mpic.h =================================================================== --- linus-2.6.orig/include/asm-powerpc/mpic.h 2006-04-21 20:32:20.000000000 +0200 +++ linus-2.6/include/asm-powerpc/mpic.h 2006-04-21 20:32:25.000000000 +0200 @@ -156,6 +156,7 @@ /* senses array */ unsigned char *senses; unsigned int senses_count; + unsigned int stride; #ifdef CONFIG_MPIC_BROKEN_U3 /* The fixup table */ @@ -186,6 +187,8 @@ #define MPIC_BROKEN_IPI 0x00000008 /* MPIC wants a reset */ #define MPIC_WANTS_RESET 0x00000010 +/* AXON strided MPIC */ +#define MPIC_AXON 0x00000020 /* Allocate the controller structure and setup the linux irq descs * for the range if interrupts passed in. No HW initialization is --