diff options
Diffstat (limited to 'arch/arm/mach-davinci/cp_intc.c')
-rw-r--r-- | arch/arm/mach-davinci/cp_intc.c | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/arch/arm/mach-davinci/cp_intc.c b/arch/arm/mach-davinci/cp_intc.c new file mode 100644 index 000000000000..96c8e97a7deb --- /dev/null +++ b/arch/arm/mach-davinci/cp_intc.c | |||
@@ -0,0 +1,161 @@ | |||
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 | } | ||