/* * Programmable Interrupt Controller functions for the Freescale MPC52xx * embedded CPU. * * * 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. */ #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 void mpc52xx_ic_disable(unsigned int irq) { u32 val; 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 irq) { u32 val; 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 irq) { u32 val; /* * 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 hw_interrupt_type mpc52xx_ic = { .typename = " MPC52xx ", .enable = mpc52xx_ic_enable, .disable = mpc52xx_ic_disable, .ack = mpc52xx_ic_disable_and_ack, .end = mpc52xx_ic_end, }; 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 for 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].handler = &mpc52xx_ic; 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; } } int mpc52xx_get_irq(struct pt_regs *regs) { u32 status; int irq = -1; 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; } return irq; }