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

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)