aboutsummaryrefslogtreecommitdiff
path: root/arch/ppc/syslib/xilinx_pic.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/ppc/syslib/xilinx_pic.c')
-rw-r--r--arch/ppc/syslib/xilinx_pic.c157
1 files changed, 157 insertions, 0 deletions
diff --git a/arch/ppc/syslib/xilinx_pic.c b/arch/ppc/syslib/xilinx_pic.c
new file mode 100644
index 00000000000..e0bd66f0847
--- /dev/null
+++ b/arch/ppc/syslib/xilinx_pic.c
@@ -0,0 +1,157 @@
+/*
+ * arch/ppc/syslib/xilinx_pic.c
+ *
+ * Interrupt controller driver for Xilinx Virtex-II Pro.
+ *
+ * Author: MontaVista Software, Inc.
+ * source@mvista.com
+ *
+ * 2002-2004 (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/init.h>
+#include <linux/irq.h>
+#include <asm/io.h>
+#include <asm/xparameters.h>
+#include <asm/ibm4xx.h>
+
+/* No one else should require these constants, so define them locally here. */
+#define ISR 0 /* Interrupt Status Register */
+#define IPR 1 /* Interrupt Pending Register */
+#define IER 2 /* Interrupt Enable Register */
+#define IAR 3 /* Interrupt Acknowledge Register */
+#define SIE 4 /* Set Interrupt Enable bits */
+#define CIE 5 /* Clear Interrupt Enable bits */
+#define IVR 6 /* Interrupt Vector Register */
+#define MER 7 /* Master Enable Register */
+
+#if XPAR_XINTC_USE_DCR == 0
+static volatile u32 *intc;
+#define intc_out_be32(addr, mask) out_be32((addr), (mask))
+#define intc_in_be32(addr) in_be32((addr))
+#else
+#define intc XPAR_INTC_0_BASEADDR
+#define intc_out_be32(addr, mask) mtdcr((addr), (mask))
+#define intc_in_be32(addr) mfdcr((addr))
+#endif
+
+static void
+xilinx_intc_enable(unsigned int irq)
+{
+ unsigned long mask = (0x00000001 << (irq & 31));
+ pr_debug("enable: %d\n", irq);
+ intc_out_be32(intc + SIE, mask);
+}
+
+static void
+xilinx_intc_disable(unsigned int irq)
+{
+ unsigned long mask = (0x00000001 << (irq & 31));
+ pr_debug("disable: %d\n", irq);
+ intc_out_be32(intc + CIE, mask);
+}
+
+static void
+xilinx_intc_disable_and_ack(unsigned int irq)
+{
+ unsigned long mask = (0x00000001 << (irq & 31));
+ pr_debug("disable_and_ack: %d\n", irq);
+ intc_out_be32(intc + CIE, mask);
+ if (!(irq_desc[irq].status & IRQ_LEVEL))
+ intc_out_be32(intc + IAR, mask); /* ack edge triggered intr */
+}
+
+static void
+xilinx_intc_end(unsigned int irq)
+{
+ unsigned long mask = (0x00000001 << (irq & 31));
+
+ pr_debug("end: %d\n", irq);
+ if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
+ intc_out_be32(intc + SIE, mask);
+ /* ack level sensitive intr */
+ if (irq_desc[irq].status & IRQ_LEVEL)
+ intc_out_be32(intc + IAR, mask);
+ }
+}
+
+static struct hw_interrupt_type xilinx_intc = {
+ "Xilinx Interrupt Controller",
+ NULL,
+ NULL,
+ xilinx_intc_enable,
+ xilinx_intc_disable,
+ xilinx_intc_disable_and_ack,
+ xilinx_intc_end,
+ 0
+};
+
+int
+xilinx_pic_get_irq(struct pt_regs *regs)
+{
+ int irq;
+
+ /*
+ * NOTE: This function is the one that needs to be improved in
+ * order to handle multiple interrupt controllers. It currently
+ * is hardcoded to check for interrupts only on the first INTC.
+ */
+
+ irq = intc_in_be32(intc + IVR);
+ if (irq != -1)
+ irq = irq;
+
+ pr_debug("get_irq: %d\n", irq);
+
+ return (irq);
+}
+
+void __init
+ppc4xx_pic_init(void)
+{
+ int i;
+
+ /*
+ * NOTE: The assumption here is that NR_IRQS is 32 or less
+ * (NR_IRQS is 32 for PowerPC 405 cores by default).
+ */
+#if (NR_IRQS > 32)
+#error NR_IRQS > 32 not supported
+#endif
+
+#if XPAR_XINTC_USE_DCR == 0
+ intc = ioremap(XPAR_INTC_0_BASEADDR, 32);
+
+ printk(KERN_INFO "Xilinx INTC #0 at 0x%08lX mapped to 0x%08lX\n",
+ (unsigned long) XPAR_INTC_0_BASEADDR, (unsigned long) intc);
+#else
+ printk(KERN_INFO "Xilinx INTC #0 at 0x%08lX (DCR)\n",
+ (unsigned long) XPAR_INTC_0_BASEADDR);
+#endif
+
+ /*
+ * Disable all external interrupts until they are
+ * explicity requested.
+ */
+ intc_out_be32(intc + IER, 0);
+
+ /* Acknowledge any pending interrupts just in case. */
+ intc_out_be32(intc + IAR, ~(u32) 0);
+
+ /* Turn on the Master Enable. */
+ intc_out_be32(intc + MER, 0x3UL);
+
+ ppc_md.get_irq = xilinx_pic_get_irq;
+
+ for (i = 0; i < NR_IRQS; ++i) {
+ irq_desc[i].handler = &xilinx_intc;
+
+ if (XPAR_INTC_0_KIND_OF_INTR & (0x00000001 << i))
+ irq_desc[i].status &= ~IRQ_LEVEL;
+ else
+ irq_desc[i].status |= IRQ_LEVEL;
+ }
+}