/* * * Copyright (c) 1999 Grant Erickson * * Module name: ppc4xx_pic.c * * Description: * Interrupt controller driver for PowerPC 4xx-based processors. */ /* * The PowerPC 403 cores' Asynchronous Interrupt Controller (AIC) has * 32 possible interrupts, a majority of which are not implemented on * all cores. There are six configurable, external interrupt pins and * there are eight internal interrupts for the on-chip serial port * (SPU), DMA controller, and JTAG controller. * * The PowerPC 405/440 cores' Universal Interrupt Controller (UIC) has * 32 possible interrupts as well. Depending on the core and SoC * implementation, a portion of the interrrupts are used for on-chip * peripherals and a portion of the interrupts are available to be * configured for external devices generating interrupts. * * The PowerNP and 440GP (and most likely future implementations) have * cascaded UICs. * */ #include #include #include #include #include #include #include #include /* Global Variables */ struct hw_interrupt_type *ppc4xx_pic; /* * We define 4xxIRQ_InitSenses table thusly: * bit 0x1: sense, 1 for edge and 0 for level. * bit 0x2: polarity, 0 for negative, 1 for positive. */ unsigned int ibm4xxPIC_NumInitSenses __initdata = 0; unsigned char *ibm4xxPIC_InitSenses __initdata = NULL; /* Six of one, half dozen of the other....#ifdefs, separate files, * other tricks..... * * There are basically two types of interrupt controllers, the 403 AIC * and the "others" with UIC. I just kept them both here separated * with #ifdefs, but it seems to change depending upon how supporting * files (like ppc4xx.h) change. -- Dan. */ #ifdef CONFIG_403 /* Function Prototypes */ static void ppc403_aic_enable(unsigned int irq); static void ppc403_aic_disable(unsigned int irq); static void ppc403_aic_disable_and_ack(unsigned int irq); static struct hw_interrupt_type ppc403_aic = { "403GC AIC", NULL, NULL, ppc403_aic_enable, ppc403_aic_disable, ppc403_aic_disable_and_ack, 0 }; int ppc403_pic_get_irq(struct pt_regs *regs) { int irq; unsigned long bits; /* * Only report the status of those interrupts that are actually * enabled. */ bits = mfdcr(DCRN_EXISR) & mfdcr(DCRN_EXIER); /* * Walk through the interrupts from highest priority to lowest, and * report the first pending interrupt found. * We want PPC, not C bit numbering, so just subtract the ffs() * result from 32. */ irq = 32 - ffs(bits); if (irq == NR_AIC_IRQS) irq = -1; return (irq); } static void ppc403_aic_enable(unsigned int irq) { int bit, word; bit = irq & 0x1f; word = irq >> 5; ppc_cached_irq_mask[word] |= (1 << (31 - bit)); mtdcr(DCRN_EXIER, ppc_cached_irq_mask[word]); } static void ppc403_aic_disable(unsigned int irq) { int bit, word; bit = irq & 0x1f; word = irq >> 5; ppc_cached_irq_mask[word] &= ~(1 << (31 - bit)); mtdcr(DCRN_EXIER, ppc_cached_irq_mask[word]); } static void ppc403_aic_disable_and_ack(unsigned int irq) { int bit, word; bit = irq & 0x1f; word = irq >> 5; ppc_cached_irq_mask[word] &= ~(1 << (31 - bit)); mtdcr(DCRN_EXIER, ppc_cached_irq_mask[word]); mtdcr(DCRN_EXISR, (1 << (31 - bit))); } #else #ifndef UIC1 #define UIC1 UIC0 #endif #ifndef UIC2 #define UIC2 UIC1 #endif static void ppc4xx_uic_enable(unsigned int irq) { int bit, word; irq_desc_t *desc = irq_desc + irq; bit = irq & 0x1f; word = irq >> 5; #ifdef UIC_DEBUG printk("ppc4xx_uic_enable - irq %d word %d bit 0x%x\n", irq, word, bit); #endif ppc_cached_irq_mask[word] |= 1 << (31 - bit); switch (word) { case 0: mtdcr(DCRN_UIC_ER(UIC0), ppc_cached_irq_mask[word]); if ((mfdcr(DCRN_UIC_TR(UIC0)) & (1 << (31 - bit))) == 0) desc->status |= IRQ_LEVEL; else desc->status = desc->status & ~IRQ_LEVEL; break; case 1: mtdcr(DCRN_UIC_ER(UIC1), ppc_cached_irq_mask[word]); if ((mfdcr(DCRN_UIC_TR(UIC1)) & (1 << (31 - bit))) == 0) desc->status |= IRQ_LEVEL; else desc->status = desc->status & ~IRQ_LEVEL; break; case 2: mtdcr(DCRN_UIC_ER(UIC2), ppc_cached_irq_mask[word]); if ((mfdcr(DCRN_UIC_TR(UIC2)) & (1 << (31 - bit))) == 0) desc->status |= IRQ_LEVEL; else desc->status = desc->status & ~IRQ_LEVEL; break; } } static void ppc4xx_uic_disable(unsigned int irq) { int bit, word; bit = irq & 0x1f; word = irq >> 5; #ifdef UIC_DEBUG printk("ppc4xx_uic_disable - irq %d word %d bit 0x%x\n", irq, word, bit); #endif ppc_cached_irq_mask[word] &= ~(1 << (31 - bit)); switch (word) { case 0: mtdcr(DCRN_UIC_ER(UIC0), ppc_cached_irq_mask[word]); break; case 1: mtdcr(DCRN_UIC_ER(UIC1), ppc_cached_irq_mask[word]); break; case 2: mtdcr(DCRN_UIC_ER(UIC2), ppc_cached_irq_mask[word]); break; } } static void ppc4xx_uic_disable_and_ack(unsigned int irq) { int bit, word; bit = irq & 0x1f; word = irq >> 5; #ifdef UIC_DEBUG printk("ppc4xx_uic_disable_and_ack - irq %d word %d bit 0x%x\n", irq, word, bit); #endif ppc_cached_irq_mask[word] &= ~(1 << (31 - bit)); switch (word) { case 0: mtdcr(DCRN_UIC_ER(UIC0), ppc_cached_irq_mask[word]); mtdcr(DCRN_UIC_SR(UIC0), (1 << (31 - bit))); #if (NR_UICS > 2) mtdcr(DCRN_UIC_SR(UICB), UICB_UIC0NC); #endif break; case 1: mtdcr(DCRN_UIC_ER(UIC1), ppc_cached_irq_mask[word]); mtdcr(DCRN_UIC_SR(UIC1), (1 << (31 - bit))); #if (NR_UICS == 2) mtdcr(DCRN_UIC_SR(UIC0), (1 << (31 - UIC0_UIC1NC))); #endif #if (NR_UICS > 2) mtdcr(DCRN_UIC_SR(UICB), UICB_UIC1NC); #endif break; case 2: mtdcr(DCRN_UIC_ER(UIC2), ppc_cached_irq_mask[word]); mtdcr(DCRN_UIC_SR(UIC2), (1 << (31 - bit))); #if (NR_UICS > 2) mtdcr(DCRN_UIC_SR(UICB), UICB_UIC2NC); #endif break; } } static void ppc4xx_uic_end(unsigned int irq) { int bit, word; unsigned int tr_bits = 0; bit = irq & 0x1f; word = irq >> 5; #ifdef UIC_DEBUG printk("ppc4xx_uic_end - irq %d word %d bit 0x%x\n", irq, word, bit); #endif switch (word) { case 0: tr_bits = mfdcr(DCRN_UIC_TR(UIC0)); break; case 1: tr_bits = mfdcr(DCRN_UIC_TR(UIC1)); break; case 2: tr_bits = mfdcr(DCRN_UIC_TR(UIC2)); break; } if ((tr_bits & (1 << (31 - bit))) == 0) { /* level trigger */ switch (word) { case 0: mtdcr(DCRN_UIC_SR(UIC0), 1 << (31 - bit)); #if (NR_UICS > 2) mtdcr(DCRN_UIC_SR(UICB), UICB_UIC0NC); #endif break; case 1: mtdcr(DCRN_UIC_SR(UIC1), 1 << (31 - bit)); #if (NR_UICS == 2) mtdcr(DCRN_UIC_SR(UIC0), (1 << (31 - UIC0_UIC1NC))); #endif #if (NR_UICS > 2) mtdcr(DCRN_UIC_SR(UICB), UICB_UIC1NC); #endif break; case 2: mtdcr(DCRN_UIC_SR(UIC2), 1 << (31 - bit)); #if (NR_UICS > 2) mtdcr(DCRN_UIC_SR(UICB), UICB_UIC2NC); #endif break; } } if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) { ppc_cached_irq_mask[word] |= 1 << (31 - bit); switch (word) { case 0: mtdcr(DCRN_UIC_ER(UIC0), ppc_cached_irq_mask[word]); break; case 1: mtdcr(DCRN_UIC_ER(UIC1), ppc_cached_irq_mask[word]); break; case 2: mtdcr(DCRN_UIC_ER(UIC2), ppc_cached_irq_mask[word]); break; } } } static struct hw_interrupt_type ppc4xx_uic = { #if (NR_UICS == 1) "IBM UIC", #else "IBM UIC Cascade", #endif NULL, NULL, ppc4xx_uic_enable, ppc4xx_uic_disable, ppc4xx_uic_disable_and_ack, ppc4xx_uic_end, 0 }; int ppc4xx_pic_get_irq(struct pt_regs *regs) { int irq, cas_irq; unsigned long bits; cas_irq = 0; /* * Only report the status of those interrupts that are actually * enabled. */ #if (NR_UICS > 2) bits = mfdcr(DCRN_UIC_MSR(UICB)); #else bits = mfdcr(DCRN_UIC_MSR(UIC0)); #endif #if (NR_UICS > 2) if (bits & UICB_UIC0NC) { bits = mfdcr(DCRN_UIC_MSR(UIC0)); irq = 32 - ffs(bits); } else if (bits & UICB_UIC1NC) { bits = mfdcr(DCRN_UIC_MSR(UIC1)); irq = 64 - ffs(bits); } else if (bits & UICB_UIC2NC) { bits = mfdcr(DCRN_UIC_MSR(UIC2)); irq = 96 - ffs(bits); } else { irq = -1; } #elif (NR_UICS > 1) if (bits & UIC_CASCADE_MASK) { bits = mfdcr(DCRN_UIC_MSR(UIC1)); cas_irq = 32 - ffs(bits); irq = 32 + cas_irq; } else { irq = 32 - ffs(bits); if (irq == 32) irq = -1; } #else /* * Walk through the interrupts from highest priority to lowest, and * report the first pending interrupt found. * We want PPC, not C bit numbering, so just subtract the ffs() * result from 32. */ irq = 32 - ffs(bits); #endif if (irq == (NR_UIC_IRQS * NR_UICS)) irq = -1; #ifdef UIC_DEBUG printk("ppc4xx_pic_get_irq - irq %d bit 0x%x\n", irq, bits); #endif return (irq); } #endif void __init ppc4xx_extpic_init(void) { /* set polarity * 1 = default/pos/rising , 0= neg/falling internal * 1 = neg/falling , 0= pos/rising external * Sense * 0 = default level internal * 0 = level, 1 = edge external */ unsigned int sense, irq; int bit, word; unsigned long ppc_cached_sense_mask[NR_MASK_WORDS]; unsigned long ppc_cached_pol_mask[NR_MASK_WORDS]; ppc_cached_sense_mask[0] = 0; ppc_cached_sense_mask[1] = 0; ppc_cached_sense_mask[2] = 0; ppc_cached_pol_mask[0] = 0; ppc_cached_pol_mask[1] = 0; ppc_cached_pol_mask[2] = 0; for (irq = 0; irq < NR_IRQS; irq++) { bit = irq & 0x1f; word = irq >> 5; sense = (irq < ibm4xxPIC_NumInitSenses) ? ibm4xxPIC_InitSenses[irq] : 3; #ifdef PPC4xx_PIC_DEBUG printk("PPC4xx_picext %d word:%x bit:%x sense:%x", irq, word, bit, sense); #endif ppc_cached_sense_mask[word] |= (~sense & IRQ_SENSE_MASK) << (31 - bit); ppc_cached_pol_mask[word] |= ((sense & IRQ_POLARITY_MASK) >> 1) << (31 - bit); switch (word) { case 0: #ifdef PPC4xx_PIC_DEBUG printk("Pol %x ", mfdcr(DCRN_UIC_PR(UIC0))); printk("Level %x\n", mfdcr(DCRN_UIC_TR(UIC0))); #endif /* polarity setting */ mtdcr(DCRN_UIC_PR(UIC0), ppc_cached_pol_mask[word]); /* Level setting */ mtdcr(DCRN_UIC_TR(UIC0), ppc_cached_sense_mask[word]); break; case 1: #ifdef PPC4xx_PIC_DEBUG printk("Pol %x ", mfdcr(DCRN_UIC_PR(UIC1))); printk("Level %x\n", mfdcr(DCRN_UIC_TR(UIC1))); #endif /* polarity setting */ mtdcr(DCRN_UIC_PR(UIC1), ppc_cached_pol_mask[word]); /* Level setting */ mtdcr(DCRN_UIC_TR(UIC1), ppc_cached_sense_mask[word]); break; case 2: #ifdef PPC4xx_PIC_DEBUG printk("Pol %x ", mfdcr(DCRN_UIC_PR(UIC2))); printk("Level %x\n", mfdcr(DCRN_UIC_TR(UIC2))); #endif /* polarity setting */ mtdcr(DCRN_UIC_PR(UIC2), ppc_cached_pol_mask[word]); /* Level setting */ mtdcr(DCRN_UIC_TR(UIC2), ppc_cached_sense_mask[word]); break; } } } void __init ppc4xx_pic_init(void) { /* * Disable all external interrupts until they are * explicity requested. */ ppc_cached_irq_mask[0] = 0; ppc_cached_irq_mask[1] = 0; ppc_cached_irq_mask[2] = 0; #if defined CONFIG_403 mtdcr(DCRN_EXIER, ppc_cached_irq_mask[0]); ppc4xx_pic = &ppc403_aic; ppc_md.get_irq = ppc403_pic_get_irq; #else #if (NR_UICS > 2) mtdcr(DCRN_UIC_ER(UICB), UICB_UIC0NC | UICB_UIC1NC | UICB_UIC2NC); mtdcr(DCRN_UIC_CR(UICB), 0); mtdcr(DCRN_UIC_ER(UIC2), ppc_cached_irq_mask[2]); mtdcr(DCRN_UIC_CR(UIC2), 0); #endif #if (NR_UICS > 1) #if (NR_UICS == 2) /* enable cascading interrupt */ ppc_cached_irq_mask[0] |= 1 << (31 - UIC0_UIC1NC); #endif mtdcr(DCRN_UIC_ER(UIC1), ppc_cached_irq_mask[1]); mtdcr(DCRN_UIC_CR(UIC1), 0); #endif mtdcr(DCRN_UIC_ER(UIC0), ppc_cached_irq_mask[0]); mtdcr(DCRN_UIC_CR(UIC0), 0); if (ibm4xxPIC_InitSenses != NULL) ppc4xx_extpic_init(); /* Clear any pending interrupts */ #if (NR_UICS > 2) mtdcr(DCRN_UIC_SR(UICB), 0xffffffff); mtdcr(DCRN_UIC_SR(UIC2), 0xffffffff); #endif #if (NR_UICS > 1) mtdcr(DCRN_UIC_SR(UIC1), 0xffffffff); #endif mtdcr(DCRN_UIC_SR(UIC0), 0xffffffff); ppc4xx_pic = &ppc4xx_uic; ppc_md.get_irq = ppc4xx_pic_get_irq; #endif }