--- a/arch/powerpc/sysdev/Makefile 2006-10-25 19:07:24.000000000 +0200 +++ b/arch/powerpc/sysdev/Makefile 2006-10-26 11:38:02.000000000 +0200 @@ -13,6 +13,7 @@ obj-$(CONFIG_FSL_SOC) += fsl_soc.o obj-$(CONFIG_PPC_TODC) += todc.o obj-$(CONFIG_TSI108_BRIDGE) += tsi108_pci.o tsi108_dev.o obj-$(CONFIG_QUICC_ENGINE) += qe_lib/ +obj-$(CONFIG_PPC_MPC52xx_PIC) += mpc52xx_pic.o ifeq ($(CONFIG_PPC_MERGE),y) obj-$(CONFIG_PPC_I8259) += i8259.o --- a/arch/powerpc/sysdev/mpc52xx_pic.c 2006-10-25 19:07:24.000000000 +0200 +++ b/arch/powerpc/sysdev/mpc52xx_pic.c 2006-10-26 10:55:44.000000000 +0200 @@ -0,0 +1,371 @@ +/* + * arch/powerpc/sysdev/mpc52xx_pic.c + * + * Programmable Interrupt Controller functions for the Freescale MPC52xx + * embedded CPU. + * Modified for CHRP Efika 5K2 + * + * Maintainer : Sylvain Munaut + * + * Based on (well, mostly copied from) the code from the 2.4 kernel by + * Dale Farnsworth and Kent Borg. + * + * Copyright (C) 2004 Sylvain Munaut + * Copyright (C) 2003 Montavista Software, Inc + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +//#define DEBUG + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +static struct mpc52xx_intr __iomem *intr; +static struct mpc52xx_sdma __iomem *sdma; + +static struct irq_host *mpc52xx_irqhost = NULL; + +static void +mpc52xx_ic_disable(unsigned int virq) +{ + u32 val; + int irq; + + irq = irq_map[virq].hwirq; + + pr_debug("%s: irq=%d\n", __func__, irq); + + if (irq == MPC52xx_IRQ0) { + val = in_be32(&intr->ctrl); + val &= ~(1 << 11); + out_be32(&intr->ctrl, val); + } else if (irq < MPC52xx_IRQ1) { + BUG(); + } else if (irq <= MPC52xx_IRQ3) { + val = in_be32(&intr->ctrl); + val &= ~(1 << (10 - (irq - MPC52xx_IRQ1))); + out_be32(&intr->ctrl, val); + } else if (irq < MPC52xx_SDMA_IRQ_BASE) { + val = in_be32(&intr->main_mask); + val |= 1 << (16 - (irq - MPC52xx_MAIN_IRQ_BASE)); + out_be32(&intr->main_mask, val); + } else if (irq < MPC52xx_PERP_IRQ_BASE) { + val = in_be32(&sdma->IntMask); + val |= 1 << (irq - MPC52xx_SDMA_IRQ_BASE); + out_be32(&sdma->IntMask, val); + } else { + val = in_be32(&intr->per_mask); + val |= 1 << (31 - (irq - MPC52xx_PERP_IRQ_BASE)); + out_be32(&intr->per_mask, val); + } +} + +static void +mpc52xx_ic_enable(unsigned int virq) +{ + u32 val; + int irq; + + irq = irq_map[virq].hwirq; + + pr_debug("%s: irq=%d\n", __func__, irq); + + if (irq == MPC52xx_IRQ0) { + val = in_be32(&intr->ctrl); + val |= 1 << 11; + out_be32(&intr->ctrl, val); + } else if (irq < MPC52xx_IRQ1) { + BUG(); + } else if (irq <= MPC52xx_IRQ3) { + val = in_be32(&intr->ctrl); + val |= 1 << (10 - (irq - MPC52xx_IRQ1)); + out_be32(&intr->ctrl, val); + } else if (irq < MPC52xx_SDMA_IRQ_BASE) { + val = in_be32(&intr->main_mask); + val &= ~(1 << (16 - (irq - MPC52xx_MAIN_IRQ_BASE))); + out_be32(&intr->main_mask, val); + } else if (irq < MPC52xx_PERP_IRQ_BASE) { + val = in_be32(&sdma->IntMask); + val &= ~(1 << (irq - MPC52xx_SDMA_IRQ_BASE)); + out_be32(&sdma->IntMask, val); + } else { + val = in_be32(&intr->per_mask); + val &= ~(1 << (31 - (irq - MPC52xx_PERP_IRQ_BASE))); + out_be32(&intr->per_mask, val); + } +} + +static void +mpc52xx_ic_ack(unsigned int virq) +{ + u32 val; + int irq; + + irq = irq_map[virq].hwirq; + + pr_debug("%s: irq=%d\n", __func__, irq); + + /* + * Only some irqs are reset here, others in interrupting hardware. + */ + + switch (irq) { + case MPC52xx_IRQ0: + val = in_be32(&intr->ctrl); + val |= 0x08000000; + out_be32(&intr->ctrl, val); + break; + case MPC52xx_CCS_IRQ: + val = in_be32(&intr->enc_status); + val |= 0x00000400; + out_be32(&intr->enc_status, val); + break; + case MPC52xx_IRQ1: + val = in_be32(&intr->ctrl); + val |= 0x04000000; + out_be32(&intr->ctrl, val); + break; + case MPC52xx_IRQ2: + val = in_be32(&intr->ctrl); + val |= 0x02000000; + out_be32(&intr->ctrl, val); + break; + case MPC52xx_IRQ3: + val = in_be32(&intr->ctrl); + val |= 0x01000000; + out_be32(&intr->ctrl, val); + break; + default: + if (irq >= MPC52xx_SDMA_IRQ_BASE + && irq < (MPC52xx_SDMA_IRQ_BASE + MPC52xx_SDMA_IRQ_NUM)) { + out_be32(&sdma->IntPend, + 1 << (irq - MPC52xx_SDMA_IRQ_BASE)); + } + + break; + } + +} + +static void +mpc52xx_ic_disable_and_ack(unsigned int irq) +{ + mpc52xx_ic_disable(irq); + mpc52xx_ic_ack(irq); +} + +static void +mpc52xx_ic_end(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) + mpc52xx_ic_enable(irq); +} + +static struct irq_chip mpc52xx_irqchip = { + .name = " MPC52xx ", + .enable = mpc52xx_ic_enable, + .disable = mpc52xx_ic_disable, + .ack = mpc52xx_ic_disable_and_ack, + .end = mpc52xx_ic_end, +}; + + +extern struct device_node *find_mpc52xx_pic(void); +static int mpc52xx_irqhost_match(struct irq_host *h, struct device_node *node) +{ + pr_debug("%s: %p vs %p\n", __func__, find_mpc52xx_pic(), node); + return find_mpc52xx_pic() == node; +} + +static int mpc52xx_irqhost_xlate(struct irq_host *h, struct device_node *ct, + u32 *intspec, unsigned int intsize, + irq_hw_number_t *out_hwirq, unsigned int *out_flags) +{ + static unsigned char map_senses[4] = { + IRQ_TYPE_LEVEL_HIGH, + IRQ_TYPE_EDGE_FALLING, + IRQ_TYPE_EDGE_RISING, + IRQ_TYPE_LEVEL_LOW, + }; + + int intrvect_l1; + int intrvect_l2; + int intrvect_type; + int intrvect_linux; + + pr_debug("%s:\n", __func__); + + if (intsize!=3) + return -1; + + intrvect_l1 = (int) intspec[0]; + intrvect_l2 = (int) intspec[1]; + intrvect_type = (int) intspec[2]; + + pr_debug("l1=%d, l2=%d, type=%d\n", intrvect_l1, intrvect_l2, intrvect_type ); + + switch(intrvect_l1) { + case 0: /* Critical */ + intrvect_linux = MPC52xx_CRIT_IRQ_BASE; + break; + + case 1: /* Main */ + intrvect_linux = MPC52xx_MAIN_IRQ_BASE; + break; + + case 2: /* Periph */ + intrvect_linux = MPC52xx_PERP_IRQ_BASE; + break; + + case 3: /* Bestcomm */ + intrvect_linux = MPC52xx_SDMA_IRQ_BASE; + break; + + default: + if ( printk_ratelimit() ) + printk(KERN_ERR "Wrong L1 interrupt vector (%d)\n", intrvect_l1); + + return -1; + } + + intrvect_linux += intrvect_l2; + + pr_debug("return %d\n", intrvect_linux); + + *out_hwirq = intrvect_linux; + *out_flags = map_senses[intrvect_type]; + + return 0; + +} + +int mpc52xx_irqhost_map(struct irq_host *h, unsigned int virq, irq_hw_number_t hw) +{ + pr_debug("%s: v=%d, hw=%d\n", __func__, virq, (int) hw); + + return 0; +} + +void mpc52xx_irqhost_unmap(struct irq_host *h, unsigned int virq) +{ + pr_debug("%s: v=%d\n", __func__, virq); +} + +static struct irq_host_ops mpc52xx_irqhost_ops = { + .match = mpc52xx_irqhost_match, + .xlate = mpc52xx_irqhost_xlate, + .map = mpc52xx_irqhost_map, + .unmap = mpc52xx_irqhost_unmap, +}; + +void __init +mpc52xx_init_irq(void) +{ + int i; + u32 intr_ctrl; + + + /* Remap the necessary zones */ + intr = ioremap(MPC52xx_PA(MPC52xx_INTR_OFFSET), MPC52xx_INTR_SIZE); + sdma = ioremap(MPC52xx_PA(MPC52xx_SDMA_OFFSET), MPC52xx_SDMA_SIZE); + + if ((intr==NULL) || (sdma==NULL)) + panic("Can't ioremap PIC/SDMA register or init_irq !"); + + /* Disable all interrupt sources. */ + out_be32(&sdma->IntPend, 0xffffffff); /* 1 means clear pending */ + out_be32(&sdma->IntMask, 0xffffffff); /* 1 means disabled */ + out_be32(&intr->per_mask, 0x7ffffc00); /* 1 means disabled */ + out_be32(&intr->main_mask, 0x00010fff); /* 1 means disabled */ + intr_ctrl = in_be32(&intr->ctrl); + intr_ctrl &= 0x00ff0000; /* Keeps IRQ[0-3] config */ + intr_ctrl |= 0x0f000000 | /* clear IRQ 0-3 */ + 0x00001000 | /* MEE master external enable */ + 0x00000000 | /* 0 means disable IRQ 0-3 */ + 0x00000001; /* CEb route critical normally */ + out_be32(&intr->ctrl, intr_ctrl); + + /* Zero a bunch of the priority settings. */ + out_be32(&intr->per_pri1, 0); + out_be32(&intr->per_pri2, 0); + out_be32(&intr->per_pri3, 0); + out_be32(&intr->main_pri1, 0); + out_be32(&intr->main_pri2, 0); + /* Initialize irq_desc[i].handler's with mpc52xx_ic. */ + for (i = 0; i < NR_IRQS; i++) { + irq_desc[i].chip = &mpc52xx_irqchip; + irq_desc[i].status = IRQ_LEVEL; + + } + +#define IRQn_MODE(intr_ctrl,irq) (((intr_ctrl) >> (22-(i<<1))) & 0x03) + for (i=0 ; i<4 ; i++) { + int mode; + mode = IRQn_MODE(intr_ctrl,i); + if ((mode == 0x1) || (mode == 0x2)) + irq_desc[i?MPC52xx_IRQ1+i-1:MPC52xx_IRQ0].status = 0; + } + + /* + * As last step, add an irq host to translate the real + * hw irq information provided by the ofw to linux virq + */ + + mpc52xx_irqhost = irq_alloc_host(IRQ_HOST_MAP_LINEAR, MPC52xx_BDLC_IRQ+1, &mpc52xx_irqhost_ops, -1); + pr_debug("%s: mpc52xx_irqhost =%p\n", __func__, mpc52xx_irqhost ); +} + +unsigned int +mpc52xx_get_irq(void) +{ + u32 status; + int virq; + int irq = NO_IRQ_IGNORE; + + status = in_be32(&intr->enc_status); + if (status & 0x00000400) + { /* critical */ + irq = (status >> 8) & 0x3; + if (irq == 2) /* high priority peripheral */ + goto peripheral; + irq += MPC52xx_CRIT_IRQ_BASE; + } else if (status & 0x00200000) + { /* main */ + irq = (status >> 16) & 0x1f; + if (irq == 4) /* low priority peripheral */ + goto peripheral; + irq += MPC52xx_MAIN_IRQ_BASE; + } else if (status & 0x20000000) + { /* peripheral */ +peripheral: + irq = (status >> 24) & 0x1f; + if (irq == 0) { /* bestcomm */ + status = in_be32(&sdma->IntPend); + irq = ffs(status) + MPC52xx_SDMA_IRQ_BASE-1; + } else + irq += MPC52xx_PERP_IRQ_BASE; + + } + + virq = irq_linear_revmap(mpc52xx_irqhost, irq); + pr_debug("%s: irq=%d -> %d\n", __func__, irq, virq); + + return virq; +} + --- a/arch/powerpc/Kconfig 2006-10-25 19:07:23.000000000 +0200 +++ b/arch/powerpc/Kconfig 2006-10-26 11:32:54.000000000 +0200 @@ -384,6 +384,12 @@ config PPC_CHRP select PPC_RTAS select PPC_MPC106 select PPC_UDBG_16550 + select PPC_MPC52xx_PIC + default y + +config PPC_MPC52xx_PIC + bool + depends on PPC_CHRP default y config PPC_PMAC --- a/arch/powerpc/platforms/chrp/setup.c 2006-10-25 19:07:23.000000000 +0200 +++ b/arch/powerpc/platforms/chrp/setup.c 2006-10-26 10:59:39.000000000 +0200 @@ -51,6 +51,7 @@ #include #include #include +#include #include "chrp.h" @@ -435,6 +436,51 @@ static struct irqaction xmon_irqaction = }; #endif + +struct device_node *find_mpc52xx_pic(void) +{ + struct device_node *dev; + const char *piccompatible_list[] = + { + "mpc5200-interrupt-controller", + "mpc52xx-interrupt-controller", + "mpc52xx-pic", + "mpc5200-pic", + "5200-interrupt-controller", + "52xx-interrupt-controller", + "52xx-pic", + "5200-pic", + NULL + }; + + /* Look for an MPC52xx interrupt controller */ + for_each_node_by_type(dev, "interrupt-controller") + { + const char **piccompatible_entry = piccompatible_list; + + for(piccompatible_entry = piccompatible_list; *piccompatible_entry; piccompatible_entry++ ) + { + if (device_is_compatible(dev, *piccompatible_entry )) + return dev; + } + } + + return NULL; +} + +static int __init chrp_find_mpc52xx_pic(void) +{ + if (find_mpc52xx_pic()) + { + printk(KERN_INFO "Found MPC52xx Interrupt Controller\n"); + ppc_md.get_irq = mpc52xx_get_irq; + mpc52xx_init_irq(); + return 0; + } + + return -ENODEV; +} + static void __init chrp_find_8259(void) { struct device_node *np, *pic = NULL; @@ -494,8 +540,12 @@ void __init chrp_init_IRQ(void) #if defined(CONFIG_VT) && defined(CONFIG_INPUT_ADBHID) && defined(XMON) struct device_node *kbd; #endif - chrp_find_openpic(); - chrp_find_8259(); + + if ( chrp_find_mpc52xx_pic() < 0) + { + chrp_find_openpic(); + chrp_find_8259(); + } #ifdef CONFIG_SMP /* Pegasos has no MPIC, those ops would make it crash. It might be an diff -uprN a/include/asm-ppc/mpc52xx.h b/include/asm-ppc/mpc52xx.h --- a/include/asm-ppc/mpc52xx.h 2006-10-25 19:07:48.000000000 +0200 +++ b/include/asm-ppc/mpc52xx.h 2006-10-25 19:11:55.000000000 +0200 @@ -119,7 +119,7 @@ enum ppc_sys_devices { #define MPC52xx_SDMA_IRQ_NUM 17 #define MPC52xx_PERP_IRQ_NUM 23 -#define MPC52xx_CRIT_IRQ_BASE 1 +#define MPC52xx_CRIT_IRQ_BASE 0 #define MPC52xx_MAIN_IRQ_BASE (MPC52xx_CRIT_IRQ_BASE + MPC52xx_CRIT_IRQ_NUM) #define MPC52xx_SDMA_IRQ_BASE (MPC52xx_MAIN_IRQ_BASE + MPC52xx_MAIN_IRQ_NUM) #define MPC52xx_PERP_IRQ_BASE (MPC52xx_SDMA_IRQ_BASE + MPC52xx_SDMA_IRQ_NUM) @@ -415,7 +415,7 @@ struct mpc52xx_cdm { #ifndef __ASSEMBLY__ extern void mpc52xx_init_irq(void); -extern int mpc52xx_get_irq(void); +extern unsigned int mpc52xx_get_irq(void); extern unsigned long mpc52xx_find_end_of_memory(void); extern void mpc52xx_set_bat(void);