patch-2.4.22 linux-2.4.22/arch/ppc/kernel/ppc4xx_pic.c
Next file: linux-2.4.22/arch/ppc/kernel/ppc4xx_setup.c
Previous file: linux-2.4.22/arch/ppc/kernel/ppc405_pci.c
Back to the patch index
Back to the overall index
- Lines: 483
- Date:
2003-08-25 04:44:40.000000000 -0700
- Orig file:
linux-2.4.21/arch/ppc/kernel/ppc4xx_pic.c
- Orig date:
2003-06-13 07:51:31.000000000 -0700
diff -urN linux-2.4.21/arch/ppc/kernel/ppc4xx_pic.c linux-2.4.22/arch/ppc/kernel/ppc4xx_pic.c
@@ -15,10 +15,14 @@
* there are eight internal interrupts for the on-chip serial port
* (SPU), DMA controller, and JTAG controller.
*
- * The PowerPC 405 cores' Universal Interrupt Controller (UIC) has 32
- * possible interrupts as well. There are seven, configurable external
- * interrupt pins and there are 17 internal interrupts for the on-chip
- * serial port, DMA controller, on-chip Ethernet controller, PCI, etc.
+ * 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.
*
*/
@@ -33,19 +37,31 @@
#include <asm/ppc4xx_pic.h>
/* Global Variables */
-
struct hw_interrupt_type *ppc4xx_pic;
+/*
+ * We define 4xxIRQ_InitSenses table thusly:
+ * bit 0x1: sense, 0 for edge and 1 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 void ppc405_uic_enable(unsigned int irq);
-static void ppc405_uic_disable(unsigned int irq);
-static void ppc405_uic_disable_and_ack(unsigned int irq);
+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",
@@ -57,55 +73,11 @@
0
};
-static struct hw_interrupt_type ppc405_uic = {
- "405GP UIC",
- NULL,
- NULL,
- ppc405_uic_enable,
- ppc405_uic_disable,
- ppc405_uic_disable_and_ack,
- 0
-};
-
-/*
- * Document me.
- */
-void __init
-ppc4xx_pic_init(void)
-{
- unsigned long ver = PVR_VER(mfspr(SPRN_PVR));
-
- switch (ver) {
-
- case PVR_VER(PVR_403GC):
- /*
- * Disable all external interrupts until they are
- * explicity requested.
- */
- ppc_cached_irq_mask[0] = 0;
- mtdcr(DCRN_EXIER, 0);
-
- ppc4xx_pic = &ppc403_aic;
- break;
-
- case PVR_VER(PVR_405GP):
- ppc4xx_pic = &ppc405_uic;
- break;
- }
-
- return;
-}
-
-/*
- * XXX - Currently 403-specific!
- *
- * Document me.
- */
int
-ppc4xx_pic_get_irq(struct pt_regs *regs)
+ppc403_pic_get_irq(struct pt_regs *regs)
{
int irq;
- unsigned long bits, mask = (1 << 31);
+ unsigned long bits;
/*
* Only report the status of those interrupts that are actually
@@ -117,19 +89,17 @@
/*
* 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);
- for (irq = 0; irq < NR_IRQS; irq++, mask >>= 1) {
- if (bits & mask)
- break;
- }
+ if (irq == NR_AIC_IRQS)
+ irq = -1;
return (irq);
}
-/*
- * Document me.
- */
static void
ppc403_aic_enable(unsigned int irq)
{
@@ -142,9 +112,6 @@
mtdcr(DCRN_EXIER, ppc_cached_irq_mask[word]);
}
-/*
- * Document me.
- */
static void
ppc403_aic_disable(unsigned int irq)
{
@@ -157,9 +124,6 @@
mtdcr(DCRN_EXIER, ppc_cached_irq_mask[word]);
}
-/*
- * Document me.
- */
static void
ppc403_aic_disable_and_ack(unsigned int irq)
{
@@ -173,29 +137,308 @@
mtdcr(DCRN_EXISR, (1 << (31 - bit)));
}
-/*
- * Document me.
- */
+#else
+
+#ifndef UIC1
+#define UIC1 UIC0
+#endif
+
static void
ppc405_uic_enable(unsigned int irq)
{
- /* XXX - Implement me. */
+ int bit, word;
+ irq_desc_t *desc = irq_desc + irq;
+
+ bit = irq & 0x1f;
+ word = irq >> 5;
+
+#ifdef UIC_DEBUG
+ printk("ppc405_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
+ /* lets hope this works since in linux/irq.h
+ * there is no define for EDGE and it's assumed
+ * once you set status to LEVEL you would not
+ * want to change it - Armin
+ */
+ 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
+ /* lets hope this works since in linux/irq.h
+ * there is no define for EDGE and it's assumed
+ * once you set status to LEVEL you would not
+ * want to change it - Armin
+ */
+ desc->status = desc->status & ~IRQ_LEVEL;
+ break;
+ }
+
}
-/*
- * Document me.
- */
static void
ppc405_uic_disable(unsigned int irq)
{
- /* XXX - Implement me. */
+ int bit, word;
+
+ bit = irq & 0x1f;
+ word = irq >> 5;
+#ifdef UIC_DEBUG
+ printk("ppc405_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;
+ }
}
-/*
- * Document me.
- */
static void
ppc405_uic_disable_and_ack(unsigned int irq)
{
- /* XXX - Implement me. */
+ int bit, word;
+
+ bit = irq & 0x1f;
+ word = irq >> 5;
+
+#ifdef UIC_DEBUG
+ printk("ppc405_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)));
+ break;
+#if NR_UICS > 1
+ case 1:
+ mtdcr(DCRN_UIC_ER(UIC1), ppc_cached_irq_mask[word]);
+ mtdcr(DCRN_UIC_SR(UIC1), (1 << (31 - bit)));
+ /* ACK cascaded interrupt in UIC0 */
+ mtdcr(DCRN_UIC_SR(UIC0), (1 << (31 - UIC0_UIC1NC)));
+ break;
+#endif
+ }
+}
+
+static void
+ppc405_uic_end(unsigned int irq)
+{
+ int bit, word;
+ unsigned int tr_bits;
+
+ bit = irq & 0x1f;
+ word = irq >> 5;
+
+#ifdef UIC_DEBUG
+ printk("ppc405_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;
+ }
+
+ if ((tr_bits & (1 << (31 - bit))) == 0) {
+ /* level trigger */
+ switch (word) {
+ case 0:
+ mtdcr(DCRN_UIC_SR(UIC0), 1 << (31 - bit));
+ break;
+#if NR_UICS > 1
+ case 1:
+ mtdcr(DCRN_UIC_SR(UIC1), 1 << (31 - bit));
+ /* ACK cascaded interrupt in UIC0 */
+ mtdcr(DCRN_UIC_SR(UIC0), (1 << (31 - UIC0_UIC1NC)));
+ break;
+#endif
+ }
+ }
+
+ 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;
+ }
+ }
+}
+
+static struct hw_interrupt_type ppc405_uic = {
+#if (NR_UICS == 1)
+ "IBM UIC",
+#else
+ "IBM UIC Cascade",
+#endif
+ NULL,
+ NULL,
+ ppc405_uic_enable,
+ ppc405_uic_disable,
+ ppc405_uic_disable_and_ack,
+ ppc405_uic_end,
+ 0
+};
+
+int
+ppc405_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.
+ */
+
+ bits = mfdcr(DCRN_UIC_MSR(UIC0));
+
+#if (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("ppc405_pic_get_irq - irq %d bit 0x%x\n", irq, bits);
+#endif
+
+ return (irq);
+}
+#endif
+
+void __init
+ppc4xx_extpic_init(void)
+{
+ 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_pol_mask[0] = 0;
+ ppc_cached_pol_mask[1] = 0;
+
+ for (irq = 0; irq < NR_IRQS; irq++) {
+
+ bit = irq & 0x1f;
+ word = irq >> 5;
+
+ sense = (irq < ibm4xxPIC_NumInitSenses) ?
+ ibm4xxPIC_InitSenses[irq] :
+ IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE;
+#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;
+ }
+ }
+
+}
+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;
+
+#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 > 1)
+ ppc_cached_irq_mask[0] |= 1 << (31 - UIC0_UIC1NC); /* enable cascading interrupt */
+ 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 > 1)
+ mtdcr(DCRN_UIC_SR(UIC1), 0xffffffff);
+#endif
+ mtdcr(DCRN_UIC_SR(UIC0), 0xffffffff);
+
+ ppc4xx_pic = &ppc405_uic;
+ ppc_md.get_irq = ppc405_pic_get_irq;
+#endif
+
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)