summaryrefslogtreecommitdiffstats
path: root/arch/ppc/syslib/cpc700_pic.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/ppc/syslib/cpc700_pic.c')
-rw-r--r--arch/ppc/syslib/cpc700_pic.c187
1 files changed, 187 insertions, 0 deletions
diff --git a/arch/ppc/syslib/cpc700_pic.c b/arch/ppc/syslib/cpc700_pic.c
new file mode 100644
index 00000000000..77470980753
--- /dev/null
+++ b/arch/ppc/syslib/cpc700_pic.c
@@ -0,0 +1,187 @@
+/*
+ * arch/ppc/syslib/cpc700_pic.c
+ *
+ * Interrupt controller support for IBM Spruce
+ *
+ * Authors: Mark Greer, Matt Porter, and Johnnie Peters
+ * mgreer@mvista.com
+ * mporter@mvista.com
+ * jpeters@mvista.com
+ *
+ * 2001-2002 (c) 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 <linux/stddef.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/irq.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+
+#include "cpc700.h"
+
+static void
+cpc700_unmask_irq(unsigned int irq)
+{
+ unsigned int tr_bits;
+
+ /*
+ * IRQ 31 is largest IRQ supported.
+ * IRQs 17-19 are reserved.
+ */
+ if ((irq <= 31) && ((irq < 17) || (irq > 19))) {
+ tr_bits = CPC700_IN_32(CPC700_UIC_UICTR);
+
+ if ((tr_bits & (1 << (31 - irq))) == 0) {
+ /* level trigger interrupt, clear bit in status
+ * register */
+ CPC700_OUT_32(CPC700_UIC_UICSR, 1 << (31 - irq));
+ }
+
+ /* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */
+ ppc_cached_irq_mask[0] |= CPC700_UIC_IRQ_BIT(irq);
+
+ CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]);
+ }
+ return;
+}
+
+static void
+cpc700_mask_irq(unsigned int irq)
+{
+ /*
+ * IRQ 31 is largest IRQ supported.
+ * IRQs 17-19 are reserved.
+ */
+ if ((irq <= 31) && ((irq < 17) || (irq > 19))) {
+ /* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */
+ ppc_cached_irq_mask[0] &=
+ ~CPC700_UIC_IRQ_BIT(irq);
+
+ CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]);
+ }
+ return;
+}
+
+static void
+cpc700_mask_and_ack_irq(unsigned int irq)
+{
+ u_int bit;
+
+ /*
+ * IRQ 31 is largest IRQ supported.
+ * IRQs 17-19 are reserved.
+ */
+ if ((irq <= 31) && ((irq < 17) || (irq > 19))) {
+ /* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */
+ bit = CPC700_UIC_IRQ_BIT(irq);
+
+ ppc_cached_irq_mask[0] &= ~bit;
+ CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]);
+ CPC700_OUT_32(CPC700_UIC_UICSR, bit); /* Write 1 clears IRQ */
+ }
+ return;
+}
+
+static struct hw_interrupt_type cpc700_pic = {
+ "CPC700 PIC",
+ NULL,
+ NULL,
+ cpc700_unmask_irq,
+ cpc700_mask_irq,
+ cpc700_mask_and_ack_irq,
+ NULL,
+ NULL
+};
+
+__init static void
+cpc700_pic_init_irq(unsigned int irq)
+{
+ unsigned int tmp;
+
+ /* Set interrupt sense */
+ tmp = CPC700_IN_32(CPC700_UIC_UICTR);
+ if (cpc700_irq_assigns[irq][0] == 0) {
+ tmp &= ~CPC700_UIC_IRQ_BIT(irq);
+ } else {
+ tmp |= CPC700_UIC_IRQ_BIT(irq);
+ }
+ CPC700_OUT_32(CPC700_UIC_UICTR, tmp);
+
+ /* Set interrupt polarity */
+ tmp = CPC700_IN_32(CPC700_UIC_UICPR);
+ if (cpc700_irq_assigns[irq][1]) {
+ tmp |= CPC700_UIC_IRQ_BIT(irq);
+ } else {
+ tmp &= ~CPC700_UIC_IRQ_BIT(irq);
+ }
+ CPC700_OUT_32(CPC700_UIC_UICPR, tmp);
+
+ /* Set interrupt critical */
+ tmp = CPC700_IN_32(CPC700_UIC_UICCR);
+ tmp |= CPC700_UIC_IRQ_BIT(irq);
+ CPC700_OUT_32(CPC700_UIC_UICCR, tmp);
+
+ return;
+}
+
+__init void
+cpc700_init_IRQ(void)
+{
+ int i;
+
+ ppc_cached_irq_mask[0] = 0;
+ CPC700_OUT_32(CPC700_UIC_UICER, 0x00000000); /* Disable all irq's */
+ CPC700_OUT_32(CPC700_UIC_UICSR, 0xffffffff); /* Clear cur intrs */
+ CPC700_OUT_32(CPC700_UIC_UICCR, 0xffffffff); /* Gen INT not MCP */
+ CPC700_OUT_32(CPC700_UIC_UICPR, 0x00000000); /* Active low */
+ CPC700_OUT_32(CPC700_UIC_UICTR, 0x00000000); /* Level Sensitive */
+ CPC700_OUT_32(CPC700_UIC_UICVR, CPC700_UIC_UICVCR_0_HI);
+ /* IRQ 0 is highest */
+
+ for (i = 0; i < 17; i++) {
+ irq_desc[i].handler = &cpc700_pic;
+ cpc700_pic_init_irq(i);
+ }
+
+ for (i = 20; i < 32; i++) {
+ irq_desc[i].handler = &cpc700_pic;
+ cpc700_pic_init_irq(i);
+ }
+
+ return;
+}
+
+
+
+/*
+ * Find the highest IRQ that generating an interrupt, if any.
+ */
+int
+cpc700_get_irq(struct pt_regs *regs)
+{
+ int irq = 0;
+ u_int irq_status, irq_test = 1;
+
+ irq_status = CPC700_IN_32(CPC700_UIC_UICMSR);
+
+ do
+ {
+ if (irq_status & irq_test)
+ break;
+ irq++;
+ irq_test <<= 1;
+ } while (irq < NR_IRQS);
+
+
+ if (irq == NR_IRQS)
+ irq = 33;
+
+ return (31 - irq);
+}