davinci: INTC: add support for TI cp_intc
authorSergei Shtylyov <sshtylyov@ru.mvista.com>
Wed, 11 Mar 2009 15:49:05 +0000 (19:49 +0400)
committerKevin Hilman <khilman@deeprootsystems.com>
Tue, 26 May 2009 14:18:14 +0000 (07:18 -0700)
Add support for Texas Instuments Common Platform Interrupt Controller
(cp_intc) used on DA830/OMAP-L137.

Signed-off-by: Steve Chen <schen@mvista.com>
Signed-off-by: Mark Greer <mgreer@mvista.com>
Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com>
Signed-off-by: Kevin Hilman <khilman@deeprootsystems.com>
arch/arm/mach-davinci/Kconfig
arch/arm/mach-davinci/Makefile
arch/arm/mach-davinci/cp_intc.c [new file with mode: 0644]
arch/arm/mach-davinci/include/mach/cp_intc.h [new file with mode: 0644]

index a9c78bc..56cd03f 100644 (file)
@@ -1,5 +1,8 @@
 if ARCH_DAVINCI
 
+config CP_INTC
+       bool
+
 menu "TI DaVinci Implementations"
 
 comment "DaVinci Core Type"
index 1674661..9a696ae 100644 (file)
@@ -8,6 +8,7 @@ obj-y                   := time.o irq.o clock.o serial.o io.o id.o psc.o \
                           gpio.o devices.o dma.o usb.o
 
 obj-$(CONFIG_DAVINCI_MUX)              += mux.o
+obj-$(CONFIG_CP_INTC)                  += cp_intc.o
 
 # Chip specific
 obj-$(CONFIG_ARCH_DAVINCI_DM644x)       += dm644x.o
diff --git a/arch/arm/mach-davinci/cp_intc.c b/arch/arm/mach-davinci/cp_intc.c
new file mode 100644 (file)
index 0000000..96c8e97
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * TI Common Platform Interrupt Controller (cp_intc) driver
+ *
+ * Author: Steve Chen <schen@mvista.com>
+ * Copyright (C) 2008-2009, MontaVista Software, Inc. <source@mvista.com>
+ *
+ * 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/sched.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <mach/cp_intc.h>
+
+static void __iomem *cp_intc_base;
+
+static inline unsigned int cp_intc_read(unsigned offset)
+{
+       return __raw_readl(cp_intc_base + offset);
+}
+
+static inline void cp_intc_write(unsigned long value, unsigned offset)
+{
+       __raw_writel(value, cp_intc_base + offset);
+}
+
+static void cp_intc_ack_irq(unsigned int irq)
+{
+       cp_intc_write(irq, CP_INTC_SYS_STAT_IDX_CLR);
+}
+
+/* Disable interrupt */
+static void cp_intc_mask_irq(unsigned int irq)
+{
+       /* XXX don't know why we need to disable nIRQ here... */
+       cp_intc_write(1, CP_INTC_HOST_ENABLE_IDX_CLR);
+       cp_intc_write(irq, CP_INTC_SYS_ENABLE_IDX_CLR);
+       cp_intc_write(1, CP_INTC_HOST_ENABLE_IDX_SET);
+}
+
+/* Enable interrupt */
+static void cp_intc_unmask_irq(unsigned int irq)
+{
+       cp_intc_write(irq, CP_INTC_SYS_ENABLE_IDX_SET);
+}
+
+static int cp_intc_set_irq_type(unsigned int irq, unsigned int flow_type)
+{
+       unsigned reg            = BIT_WORD(irq);
+       unsigned mask           = BIT_MASK(irq);
+       unsigned polarity       = cp_intc_read(CP_INTC_SYS_POLARITY(reg));
+       unsigned type           = cp_intc_read(CP_INTC_SYS_TYPE(reg));
+
+       switch (flow_type) {
+       case IRQ_TYPE_EDGE_RISING:
+               polarity |= mask;
+               type |= mask;
+               break;
+       case IRQ_TYPE_EDGE_FALLING:
+               polarity &= ~mask;
+               type |= mask;
+               break;
+       case IRQ_TYPE_LEVEL_HIGH:
+               polarity |= mask;
+               type &= ~mask;
+               break;
+       case IRQ_TYPE_LEVEL_LOW:
+               polarity &= ~mask;
+               type &= ~mask;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       cp_intc_write(polarity, CP_INTC_SYS_POLARITY(reg));
+       cp_intc_write(type, CP_INTC_SYS_TYPE(reg));
+
+       return 0;
+}
+
+static struct irq_chip cp_intc_irq_chip = {
+       .name           = "cp_intc",
+       .ack            = cp_intc_ack_irq,
+       .mask           = cp_intc_mask_irq,
+       .unmask         = cp_intc_unmask_irq,
+       .set_type       = cp_intc_set_irq_type,
+};
+
+void __init cp_intc_init(void __iomem *base, unsigned short num_irq,
+                        u8 *irq_prio)
+{
+       unsigned num_reg        = BITS_TO_LONGS(num_irq);
+       int i;
+
+       cp_intc_base = base;
+
+       cp_intc_write(0, CP_INTC_GLOBAL_ENABLE);
+
+       /* Disable all host interrupts */
+       cp_intc_write(0, CP_INTC_HOST_ENABLE(0));
+
+       /* Disable system interrupts */
+       for (i = 0; i < num_reg; i++)
+               cp_intc_write(~0, CP_INTC_SYS_ENABLE_CLR(i));
+
+       /* Set to normal mode, no nesting, no priority hold */
+       cp_intc_write(0, CP_INTC_CTRL);
+       cp_intc_write(0, CP_INTC_HOST_CTRL);
+
+       /* Clear system interrupt status */
+       for (i = 0; i < num_reg; i++)
+               cp_intc_write(~0, CP_INTC_SYS_STAT_CLR(i));
+
+       /* Enable nIRQ (what about nFIQ?) */
+       cp_intc_write(1, CP_INTC_HOST_ENABLE_IDX_SET);
+
+       /*
+        * Priority is determined by host channel: lower channel number has
+        * higher priority i.e. channel 0 has highest priority and channel 31
+        * had the lowest priority.
+        */
+       num_reg = (num_irq + 3) >> 2;   /* 4 channels per register */
+       if (irq_prio) {
+               unsigned j, k;
+               u32 val;
+
+               for (k = i = 0; i < num_reg; i++) {
+                       for (val = j = 0; j < 4; j++, k++) {
+                               val >>= 8;
+                               if (k < num_irq)
+                                       val |= irq_prio[k] << 24;
+                       }
+
+                       cp_intc_write(val, CP_INTC_CHAN_MAP(i));
+               }
+       } else  {
+               /*
+                * Default everything to channel 15 if priority not specified.
+                * Note that channel 0-1 are mapped to nFIQ and channels 2-31
+                * are mapped to nIRQ.
+                */
+               for (i = 0; i < num_reg; i++)
+                       cp_intc_write(0x0f0f0f0f, CP_INTC_CHAN_MAP(i));
+       }
+
+       /* Set up genirq dispatching for cp_intc */
+       for (i = 0; i < num_irq; i++) {
+               set_irq_chip(i, &cp_intc_irq_chip);
+               set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
+               set_irq_handler(i, handle_edge_irq);
+       }
+
+       /* Enable global interrupt */
+       cp_intc_write(1, CP_INTC_GLOBAL_ENABLE);
+}
diff --git a/arch/arm/mach-davinci/include/mach/cp_intc.h b/arch/arm/mach-davinci/include/mach/cp_intc.h
new file mode 100644 (file)
index 0000000..c4d27ee
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * TI Common Platform Interrupt Controller (cp_intc) definitions
+ *
+ * Author: Steve Chen <schen@mvista.com>
+ * Copyright (C) 2008-2009, MontaVista Software, Inc. <source@mvista.com>
+ *
+ * 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.
+ */
+#ifndef __ASM_HARDWARE_CP_INTC_H
+#define __ASM_HARDWARE_CP_INTC_H
+
+#define CP_INTC_REV                    0x00
+#define CP_INTC_CTRL                   0x04
+#define CP_INTC_HOST_CTRL              0x0C
+#define CP_INTC_GLOBAL_ENABLE          0x10
+#define CP_INTC_GLOBAL_NESTING_LEVEL   0x1C
+#define CP_INTC_SYS_STAT_IDX_SET       0x20
+#define CP_INTC_SYS_STAT_IDX_CLR       0x24
+#define CP_INTC_SYS_ENABLE_IDX_SET     0x28
+#define CP_INTC_SYS_ENABLE_IDX_CLR     0x2C
+#define CP_INTC_GLOBAL_WAKEUP_ENABLE   0x30
+#define CP_INTC_HOST_ENABLE_IDX_SET    0x34
+#define CP_INTC_HOST_ENABLE_IDX_CLR    0x38
+#define CP_INTC_PACING_PRESCALE        0x40
+#define CP_INTC_VECTOR_BASE            0x50
+#define CP_INTC_VECTOR_SIZE            0x54
+#define CP_INTC_VECTOR_NULL            0x58
+#define CP_INTC_PRIO_IDX               0x80
+#define CP_INTC_PRIO_VECTOR            0x84
+#define CP_INTC_SECURE_ENABLE          0x90
+#define CP_INTC_SECURE_PRIO_IDX        0x94
+#define CP_INTC_PACING_PARAM(n)        (0x0100 + (n << 4))
+#define CP_INTC_PACING_DEC(n)          (0x0104 + (n << 4))
+#define CP_INTC_PACING_MAP(n)          (0x0108 + (n << 4))
+#define CP_INTC_SYS_RAW_STAT(n)        (0x0200 + (n << 2))
+#define CP_INTC_SYS_STAT_CLR(n)        (0x0280 + (n << 2))
+#define CP_INTC_SYS_ENABLE_SET(n)      (0x0300 + (n << 2))
+#define CP_INTC_SYS_ENABLE_CLR(n)      (0x0380 + (n << 2))
+#define CP_INTC_CHAN_MAP(n)            (0x0400 + (n << 2))
+#define CP_INTC_HOST_MAP(n)            (0x0800 + (n << 2))
+#define CP_INTC_HOST_PRIO_IDX(n)       (0x0900 + (n << 2))
+#define CP_INTC_SYS_POLARITY(n)        (0x0D00 + (n << 2))
+#define CP_INTC_SYS_TYPE(n)            (0x0D80 + (n << 2))
+#define CP_INTC_WAKEUP_ENABLE(n)       (0x0E00 + (n << 2))
+#define CP_INTC_DEBUG_SELECT(n)        (0x0F00 + (n << 2))
+#define CP_INTC_SYS_SECURE_ENABLE(n)   (0x1000 + (n << 2))
+#define CP_INTC_HOST_NESTING_LEVEL(n)  (0x1100 + (n << 2))
+#define CP_INTC_HOST_ENABLE(n)         (0x1500 + (n << 2))
+#define CP_INTC_HOST_PRIO_VECTOR(n)    (0x1600 + (n << 2))
+#define CP_INTC_VECTOR_ADDR(n)         (0x2000 + (n << 2))
+
+void __init cp_intc_init(void __iomem *base, unsigned short num_irq,
+                        u8 *irq_prio);
+
+#endif /* __ASM_HARDWARE_CP_INTC_H */