Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6
[linux-2.6] / arch / arm / mach-davinci / cp_intc.c
1 /*
2  * TI Common Platform Interrupt Controller (cp_intc) driver
3  *
4  * Author: Steve Chen <schen@mvista.com>
5  * Copyright (C) 2008-2009, MontaVista Software, Inc. <source@mvista.com>
6  *
7  * This file is licensed under the terms of the GNU General Public License
8  * version 2. This program is licensed "as is" without any warranty of any
9  * kind, whether express or implied.
10  */
11
12 #include <linux/init.h>
13 #include <linux/sched.h>
14 #include <linux/interrupt.h>
15 #include <linux/kernel.h>
16 #include <linux/irq.h>
17 #include <linux/io.h>
18
19 #include <mach/cp_intc.h>
20
21 static void __iomem *cp_intc_base;
22
23 static inline unsigned int cp_intc_read(unsigned offset)
24 {
25         return __raw_readl(cp_intc_base + offset);
26 }
27
28 static inline void cp_intc_write(unsigned long value, unsigned offset)
29 {
30         __raw_writel(value, cp_intc_base + offset);
31 }
32
33 static void cp_intc_ack_irq(unsigned int irq)
34 {
35         cp_intc_write(irq, CP_INTC_SYS_STAT_IDX_CLR);
36 }
37
38 /* Disable interrupt */
39 static void cp_intc_mask_irq(unsigned int irq)
40 {
41         /* XXX don't know why we need to disable nIRQ here... */
42         cp_intc_write(1, CP_INTC_HOST_ENABLE_IDX_CLR);
43         cp_intc_write(irq, CP_INTC_SYS_ENABLE_IDX_CLR);
44         cp_intc_write(1, CP_INTC_HOST_ENABLE_IDX_SET);
45 }
46
47 /* Enable interrupt */
48 static void cp_intc_unmask_irq(unsigned int irq)
49 {
50         cp_intc_write(irq, CP_INTC_SYS_ENABLE_IDX_SET);
51 }
52
53 static int cp_intc_set_irq_type(unsigned int irq, unsigned int flow_type)
54 {
55         unsigned reg            = BIT_WORD(irq);
56         unsigned mask           = BIT_MASK(irq);
57         unsigned polarity       = cp_intc_read(CP_INTC_SYS_POLARITY(reg));
58         unsigned type           = cp_intc_read(CP_INTC_SYS_TYPE(reg));
59
60         switch (flow_type) {
61         case IRQ_TYPE_EDGE_RISING:
62                 polarity |= mask;
63                 type |= mask;
64                 break;
65         case IRQ_TYPE_EDGE_FALLING:
66                 polarity &= ~mask;
67                 type |= mask;
68                 break;
69         case IRQ_TYPE_LEVEL_HIGH:
70                 polarity |= mask;
71                 type &= ~mask;
72                 break;
73         case IRQ_TYPE_LEVEL_LOW:
74                 polarity &= ~mask;
75                 type &= ~mask;
76                 break;
77         default:
78                 return -EINVAL;
79         }
80
81         cp_intc_write(polarity, CP_INTC_SYS_POLARITY(reg));
82         cp_intc_write(type, CP_INTC_SYS_TYPE(reg));
83
84         return 0;
85 }
86
87 static struct irq_chip cp_intc_irq_chip = {
88         .name           = "cp_intc",
89         .ack            = cp_intc_ack_irq,
90         .mask           = cp_intc_mask_irq,
91         .unmask         = cp_intc_unmask_irq,
92         .set_type       = cp_intc_set_irq_type,
93 };
94
95 void __init cp_intc_init(void __iomem *base, unsigned short num_irq,
96                          u8 *irq_prio)
97 {
98         unsigned num_reg        = BITS_TO_LONGS(num_irq);
99         int i;
100
101         cp_intc_base = base;
102
103         cp_intc_write(0, CP_INTC_GLOBAL_ENABLE);
104
105         /* Disable all host interrupts */
106         cp_intc_write(0, CP_INTC_HOST_ENABLE(0));
107
108         /* Disable system interrupts */
109         for (i = 0; i < num_reg; i++)
110                 cp_intc_write(~0, CP_INTC_SYS_ENABLE_CLR(i));
111
112         /* Set to normal mode, no nesting, no priority hold */
113         cp_intc_write(0, CP_INTC_CTRL);
114         cp_intc_write(0, CP_INTC_HOST_CTRL);
115
116         /* Clear system interrupt status */
117         for (i = 0; i < num_reg; i++)
118                 cp_intc_write(~0, CP_INTC_SYS_STAT_CLR(i));
119
120         /* Enable nIRQ (what about nFIQ?) */
121         cp_intc_write(1, CP_INTC_HOST_ENABLE_IDX_SET);
122
123         /*
124          * Priority is determined by host channel: lower channel number has
125          * higher priority i.e. channel 0 has highest priority and channel 31
126          * had the lowest priority.
127          */
128         num_reg = (num_irq + 3) >> 2;   /* 4 channels per register */
129         if (irq_prio) {
130                 unsigned j, k;
131                 u32 val;
132
133                 for (k = i = 0; i < num_reg; i++) {
134                         for (val = j = 0; j < 4; j++, k++) {
135                                 val >>= 8;
136                                 if (k < num_irq)
137                                         val |= irq_prio[k] << 24;
138                         }
139
140                         cp_intc_write(val, CP_INTC_CHAN_MAP(i));
141                 }
142         } else  {
143                 /*
144                  * Default everything to channel 15 if priority not specified.
145                  * Note that channel 0-1 are mapped to nFIQ and channels 2-31
146                  * are mapped to nIRQ.
147                  */
148                 for (i = 0; i < num_reg; i++)
149                         cp_intc_write(0x0f0f0f0f, CP_INTC_CHAN_MAP(i));
150         }
151
152         /* Set up genirq dispatching for cp_intc */
153         for (i = 0; i < num_irq; i++) {
154                 set_irq_chip(i, &cp_intc_irq_chip);
155                 set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
156                 set_irq_handler(i, handle_edge_irq);
157         }
158
159         /* Enable global interrupt */
160         cp_intc_write(1, CP_INTC_GLOBAL_ENABLE);
161 }