blob: a0c5cd18c192a4aef91634b58ca851576cd2b4c6 [file] [log] [blame]
Maxime Bizone7300d02009-08-18 13:23:37 +01001/*
2 * This file is subject to the terms and conditions of the GNU General Public
3 * License. See the file "COPYING" in the main directory of this archive
4 * for more details.
5 *
6 * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
7 * Copyright (C) 2008 Nicolas Schichan <nschichan@freebox.fr>
8 */
9
10#include <linux/kernel.h>
11#include <linux/init.h>
12#include <linux/interrupt.h>
13#include <linux/module.h>
14#include <asm/irq_cpu.h>
15#include <asm/mipsregs.h>
16#include <bcm63xx_cpu.h>
17#include <bcm63xx_regs.h>
18#include <bcm63xx_io.h>
19#include <bcm63xx_irq.h>
20
21/*
22 * dispatch internal devices IRQ (uart, enet, watchdog, ...). do not
23 * prioritize any interrupt relatively to another. the static counter
24 * will resume the loop where it ended the last time we left this
25 * function.
26 */
27static void bcm63xx_irq_dispatch_internal(void)
28{
29 u32 pending;
30 static int i;
31
32 pending = bcm_perf_readl(PERF_IRQMASK_REG) &
33 bcm_perf_readl(PERF_IRQSTAT_REG);
34
35 if (!pending)
36 return ;
37
38 while (1) {
39 int to_call = i;
40
41 i = (i + 1) & 0x1f;
42 if (pending & (1 << to_call)) {
43 do_IRQ(to_call + IRQ_INTERNAL_BASE);
44 break;
45 }
46 }
47}
48
49asmlinkage void plat_irq_dispatch(void)
50{
51 u32 cause;
52
53 do {
54 cause = read_c0_cause() & read_c0_status() & ST0_IM;
55
56 if (!cause)
57 break;
58
59 if (cause & CAUSEF_IP7)
60 do_IRQ(7);
61 if (cause & CAUSEF_IP2)
62 bcm63xx_irq_dispatch_internal();
63 if (cause & CAUSEF_IP3)
64 do_IRQ(IRQ_EXT_0);
65 if (cause & CAUSEF_IP4)
66 do_IRQ(IRQ_EXT_1);
67 if (cause & CAUSEF_IP5)
68 do_IRQ(IRQ_EXT_2);
69 if (cause & CAUSEF_IP6)
70 do_IRQ(IRQ_EXT_3);
71 } while (1);
72}
73
74/*
75 * internal IRQs operations: only mask/unmask on PERF irq mask
76 * register.
77 */
78static inline void bcm63xx_internal_irq_mask(unsigned int irq)
79{
80 u32 mask;
81
82 irq -= IRQ_INTERNAL_BASE;
83 mask = bcm_perf_readl(PERF_IRQMASK_REG);
84 mask &= ~(1 << irq);
85 bcm_perf_writel(mask, PERF_IRQMASK_REG);
86}
87
88static void bcm63xx_internal_irq_unmask(unsigned int irq)
89{
90 u32 mask;
91
92 irq -= IRQ_INTERNAL_BASE;
93 mask = bcm_perf_readl(PERF_IRQMASK_REG);
94 mask |= (1 << irq);
95 bcm_perf_writel(mask, PERF_IRQMASK_REG);
96}
97
98static unsigned int bcm63xx_internal_irq_startup(unsigned int irq)
99{
100 bcm63xx_internal_irq_unmask(irq);
101 return 0;
102}
103
104/*
105 * external IRQs operations: mask/unmask and clear on PERF external
106 * irq control register.
107 */
108static void bcm63xx_external_irq_mask(unsigned int irq)
109{
110 u32 reg;
111
112 irq -= IRQ_EXT_BASE;
113 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
114 reg &= ~EXTIRQ_CFG_MASK(irq);
115 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
116}
117
118static void bcm63xx_external_irq_unmask(unsigned int irq)
119{
120 u32 reg;
121
122 irq -= IRQ_EXT_BASE;
123 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
124 reg |= EXTIRQ_CFG_MASK(irq);
125 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
126}
127
128static void bcm63xx_external_irq_clear(unsigned int irq)
129{
130 u32 reg;
131
132 irq -= IRQ_EXT_BASE;
133 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
134 reg |= EXTIRQ_CFG_CLEAR(irq);
135 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
136}
137
138static unsigned int bcm63xx_external_irq_startup(unsigned int irq)
139{
140 set_c0_status(0x100 << (irq - IRQ_MIPS_BASE));
141 irq_enable_hazard();
142 bcm63xx_external_irq_unmask(irq);
143 return 0;
144}
145
146static void bcm63xx_external_irq_shutdown(unsigned int irq)
147{
148 bcm63xx_external_irq_mask(irq);
149 clear_c0_status(0x100 << (irq - IRQ_MIPS_BASE));
150 irq_disable_hazard();
151}
152
153static int bcm63xx_external_irq_set_type(unsigned int irq,
154 unsigned int flow_type)
155{
156 u32 reg;
157 struct irq_desc *desc = irq_desc + irq;
158
159 irq -= IRQ_EXT_BASE;
160
161 flow_type &= IRQ_TYPE_SENSE_MASK;
162
163 if (flow_type == IRQ_TYPE_NONE)
164 flow_type = IRQ_TYPE_LEVEL_LOW;
165
166 reg = bcm_perf_readl(PERF_EXTIRQ_CFG_REG);
167 switch (flow_type) {
168 case IRQ_TYPE_EDGE_BOTH:
169 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
170 reg |= EXTIRQ_CFG_BOTHEDGE(irq);
171 break;
172
173 case IRQ_TYPE_EDGE_RISING:
174 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
175 reg |= EXTIRQ_CFG_SENSE(irq);
176 reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
177 break;
178
179 case IRQ_TYPE_EDGE_FALLING:
180 reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
181 reg &= ~EXTIRQ_CFG_SENSE(irq);
182 reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
183 break;
184
185 case IRQ_TYPE_LEVEL_HIGH:
186 reg |= EXTIRQ_CFG_LEVELSENSE(irq);
187 reg |= EXTIRQ_CFG_SENSE(irq);
188 break;
189
190 case IRQ_TYPE_LEVEL_LOW:
191 reg |= EXTIRQ_CFG_LEVELSENSE(irq);
192 reg &= ~EXTIRQ_CFG_SENSE(irq);
193 break;
194
195 default:
196 printk(KERN_ERR "bogus flow type combination given !\n");
197 return -EINVAL;
198 }
199 bcm_perf_writel(reg, PERF_EXTIRQ_CFG_REG);
200
201 if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) {
202 desc->status |= IRQ_LEVEL;
203 desc->handle_irq = handle_level_irq;
204 } else {
205 desc->handle_irq = handle_edge_irq;
206 }
207
208 return 0;
209}
210
211static struct irq_chip bcm63xx_internal_irq_chip = {
212 .name = "bcm63xx_ipic",
213 .startup = bcm63xx_internal_irq_startup,
214 .shutdown = bcm63xx_internal_irq_mask,
215
216 .mask = bcm63xx_internal_irq_mask,
217 .mask_ack = bcm63xx_internal_irq_mask,
218 .unmask = bcm63xx_internal_irq_unmask,
219};
220
221static struct irq_chip bcm63xx_external_irq_chip = {
222 .name = "bcm63xx_epic",
223 .startup = bcm63xx_external_irq_startup,
224 .shutdown = bcm63xx_external_irq_shutdown,
225
226 .ack = bcm63xx_external_irq_clear,
227
228 .mask = bcm63xx_external_irq_mask,
229 .unmask = bcm63xx_external_irq_unmask,
230
231 .set_type = bcm63xx_external_irq_set_type,
232};
233
234static struct irqaction cpu_ip2_cascade_action = {
235 .handler = no_action,
236 .name = "cascade_ip2",
237};
238
239void __init arch_init_irq(void)
240{
241 int i;
242
243 mips_cpu_irq_init();
244 for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i)
245 set_irq_chip_and_handler(i, &bcm63xx_internal_irq_chip,
246 handle_level_irq);
247
248 for (i = IRQ_EXT_BASE; i < IRQ_EXT_BASE + 4; ++i)
249 set_irq_chip_and_handler(i, &bcm63xx_external_irq_chip,
250 handle_edge_irq);
251
252 setup_irq(IRQ_MIPS_BASE + 2, &cpu_ip2_cascade_action);
253}