RT-AC56 3.0.0.4.374.37 core
[tomato.git] / release / src-rt-6.x.4708 / cfe / cfe / arch / mips / cpu / sb1250 / src / sb1250_irq.c
blob437ca21d99080aace6287685b75b35211e7560c0
1 /* *********************************************************************
2 * Broadcom Common Firmware Environment (CFE)
3 *
4 * Polled interrupt dispatch routines File: sb1250_irq.c
5 *
6 *********************************************************************
8 * Copyright 2000,2001,2002,2003
9 * Broadcom Corporation. All rights reserved.
11 * This software is furnished under license and may be used and
12 * copied only in accordance with the following terms and
13 * conditions. Subject to these conditions, you may download,
14 * copy, install, use, modify and distribute modified or unmodified
15 * copies of this software in source and/or binary form. No title
16 * or ownership is transferred hereby.
18 * 1) Any source code used, modified or distributed must reproduce
19 * and retain this copyright notice and list of conditions
20 * as they appear in the source file.
22 * 2) No right is granted to use any trade name, trademark, or
23 * logo of Broadcom Corporation. The "Broadcom Corporation"
24 * name may not be used to endorse or promote products derived
25 * from this software without the prior written permission of
26 * Broadcom Corporation.
28 * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
29 * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
30 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
31 * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
32 * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
33 * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
34 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
35 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
36 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
37 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
38 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
39 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
40 * THE POSSIBILITY OF SUCH DAMAGE.
41 ********************************************************************* */
43 /* *********************************************************************
44 * This module provides an interface for associating service
45 * routines with SB-1250 system and LDT interrupt sources. The
46 * various interrupt mapper registers are periodically polled
47 * and the requested service routine is invoked when a
48 * corresponding interrupt request is active and enabled.
50 * The interface is loosely based on irq.c from Linux.
52 * This is not a full-fledged interrupt handler.
54 * If CFG_INTERRUPTS == 0, it operates synchronously with the
55 * main polling loop and is never invoked directly by the
56 * hardware exception handler. If CFG_INTERRUPTS == 1, certain
57 * interrupt sources can be handled asynchronously as exceptions.
59 * For now, only CPU0 polls interrupts, via its interrupt mapper.
61 ********************************************************************* */
63 #include "lib_types.h"
64 #include "lib_printf.h"
65 #include "lib_malloc.h"
67 #include "sbmips.h"
68 #include "sb1250_regs.h"
69 #include "sb1250_int.h"
70 extern void sb1250_update_sr(uint32_t clear, uint32_t set);
72 #include "exception.h"
73 #include "cfe_irq.h"
74 extern void sb1250_irq_install(void);
75 extern void sb1250_irq_arm(void);
78 /* First level dispatching (MIPS IP level). */
80 #define IP_LEVELS 8
82 /* Shared variables that must be protected in non-interrupt code. */
83 static ip_handler_t ip_handler[IP_LEVELS] = {NULL};
85 void
86 cfe_irq_setvector(int index, ip_handler_t handler)
88 if (index >= 0 && index < IP_LEVELS) {
89 uint32_t set, clear;
91 ip_handler[index] = NULL; /* disable: see demux */
92 if (handler == NULL) {
93 clear = _MM_MAKEMASK1(S_SR_IMMASK + index);
94 set = 0;
96 else {
97 clear = 0;
98 set = _MM_MAKEMASK1(S_SR_IMMASK + index);
100 sb1250_update_sr(clear, set);
101 ip_handler[index] = handler;
107 * Dispatch function called from the exception handler for
108 * asynchronous (non-polled) interrupts.
109 * info is a pointer to the saved register block.
111 * At entry, interrupts will be masked.
114 static void
115 sb1250_irq_demux(int code, uint64_t *info)
117 uint32_t pending;
119 pending = info[XCP0_CAUSE] & info[XCP0_SR];
121 /* For now, we handle IP7 (internal timers) and IP2 (mapper) only */
123 if (pending & M_CAUSE_IP7) {
124 if (ip_handler[7] != NULL) {
125 (*(ip_handler[7]))(7);
127 else {
128 /* mask off IP7, else we're caught here forever */
129 sb1250_update_sr(M_SR_IM7, 0);
133 if (pending & M_CAUSE_IP2) {
134 if (ip_handler[2] != NULL) {
135 (*(ip_handler[2]))(2);
137 else {
138 /* mask off IP2, else we're caught here forever */
139 sb1250_update_sr(M_SR_IM2, 0);
146 * Initialize the MIPS level dispatch vector.
147 * This function should be called with interrupts disabled.
150 static void
151 sb1250_irq_vectorinit(void)
153 int i;
155 for (i = 0; i < IP_LEVELS; i++) {
156 ip_handler[i] = NULL;
158 _exc_setvector(XTYPE_INTERRUPT, (void *) sb1250_irq_demux);
159 sb1250_irq_arm();
163 #define IMR_POINTER(cpu,reg) \
164 ((volatile uint64_t *)(PHYS_TO_K1(A_IMR_REGISTER(cpu,reg))))
166 typedef struct irq_action_s irq_action_t;
167 struct irq_action_s {
168 void (*handler)(void *arg);
169 void *arg;
170 unsigned long flags;
171 int device; /* to distinguish actions for shared interrupts */
172 irq_action_t *next;
175 typedef struct irq_desc_s {
176 irq_action_t *actions;
177 int depth;
178 int status;
179 } irq_desc_t;
181 /* status flags */
182 #define IRQ_DISABLED 0x0001
184 /* Shared variables that must be protected in non-interrupt code. */
185 static irq_desc_t irq_desc[NR_IRQS];
188 * cfe_irq_init is called early in the boot sequence. It is
189 * responsible for setting up the interrupt mapper and initializing
190 * the handler table that will be used for dispatching pending
191 * interrupts. If hard interrupts are used, it then enables hardware
192 * interrupts (initially all masked).
194 * This function should be called before interrupts are enabled.
197 void
198 cfe_irq_init(void)
200 int i;
201 irq_desc_t *p;
203 for (i = 0, p = irq_desc; i < NR_IRQS; i++, p++) {
204 p->actions = NULL;
205 p->depth = 0;
206 p->status = IRQ_DISABLED;
209 /* initially, all interrupts are masked */
210 *IMR_POINTER(0, R_IMR_INTERRUPT_MASK) = ~((uint64_t)0);
211 *IMR_POINTER(1, R_IMR_INTERRUPT_MASK) = ~((uint64_t)0);
213 *IMR_POINTER(0, R_IMR_INTERRUPT_DIAG) = 0;
214 *IMR_POINTER(1, R_IMR_INTERRUPT_DIAG) = 0;
216 for (i = 0; i < R_IMR_INTERRUPT_MAP_COUNT; i++) {
217 *IMR_POINTER(0, R_IMR_INTERRUPT_MAP_BASE + 8*i) = 0;
218 *IMR_POINTER(1, R_IMR_INTERRUPT_MAP_BASE + 8*i) = 0;
221 sb1250_irq_install();
223 sb1250_irq_vectorinit();
225 #if CFG_INTERRUPTS
226 cfe_irq_setvector(2, (ip_handler_t)cfe_irq_poll);
227 #endif
231 /* cfe_mask_irq() is called to mask an interrupt at the hw level */
232 void
233 cfe_mask_irq(int cpu, unsigned int irq)
235 int sr;
237 if (irq >= NR_IRQS)
238 return;
240 sr = cfe_irq_disable();
241 *IMR_POINTER(cpu, R_IMR_INTERRUPT_MASK) |= ((uint64_t)1 << irq);
242 __asm__ __volatile__ ("sync" : : : "memory");
243 cfe_irq_enable(sr);
247 /* cfe_unmask_irq() is called to unmask an interrupt at the hw level */
248 void
249 cfe_unmask_irq(int cpu, unsigned int irq)
251 int sr;
253 if (irq >= NR_IRQS)
254 return;
256 sr = cfe_irq_disable();
257 *IMR_POINTER(cpu, R_IMR_INTERRUPT_MASK) &=~ ((uint64_t)1 << irq);
258 cfe_irq_enable(sr);
262 /* If depth is 0, unmask the interrupt. Increment depth. */
263 void
264 cfe_enable_irq(unsigned int irq)
266 int sr;
267 irq_desc_t *desc;
269 if (irq >= NR_IRQS)
270 return;
272 desc = &irq_desc[irq];
273 /* The following code must be atomic */
274 sr = cfe_irq_disable();
275 if (desc->depth == 0) {
276 *IMR_POINTER(0, R_IMR_INTERRUPT_MASK) &=~ ((uint64_t)1 << irq);
277 desc->status &=~ IRQ_DISABLED;
279 desc->depth++;
280 cfe_irq_enable(sr);
284 /* Decrement depth. If depth is 0, mask the interrupt. */
285 void
286 cfe_disable_irq(unsigned int irq)
288 int sr;
289 irq_desc_t *desc;
291 if (irq >= NR_IRQS)
292 return;
294 desc = &irq_desc[irq];
295 /* The following code must be atomic */
296 sr = cfe_irq_disable();
297 desc->depth--;
298 if (desc->depth == 0) {
299 *IMR_POINTER(0, R_IMR_INTERRUPT_MASK) |= ((uint64_t)1 << irq);
300 desc->status |= IRQ_DISABLED;
301 __asm__ __volatile__ ("sync" : : : "memory");
303 cfe_irq_enable(sr);
308 * cfe_request_irq() is called by drivers to request addition to the
309 * chain of handlers called for a given interrupt.
312 cfe_request_irq(unsigned int irq,
313 void (*handler)(void *), void *arg,
314 unsigned long irqflags, int device)
316 int sr;
317 irq_action_t *action;
318 irq_desc_t *desc;
319 irq_action_t *p;
321 if (irq >= NR_IRQS) {
322 return -1;
324 if (handler == NULL) {
325 return -1;
328 action = (irq_action_t *) KMALLOC(sizeof(irq_action_t), 0);
329 if (action == NULL) {
330 return -1;
333 action->handler = handler;
334 action->arg = arg;
335 action->flags = irqflags;
336 action->device = device;
337 action->next = NULL;
339 desc = &irq_desc[irq];
341 /* The following block of code has to be executed atomically */
342 sr = cfe_irq_disable();
343 p = desc->actions;
344 if (p == NULL) {
345 desc->actions = action;
346 desc->depth = 0;
347 desc->status |= IRQ_DISABLED;
349 /* always direct to IP[2] for now */
350 *IMR_POINTER(0, R_IMR_INTERRUPT_MAP_BASE + 8*irq) = 0;
351 __asm__ __volatile__ ("sync" : : : "memory");
353 else {
354 /* Can't share interrupts unless both agree to */
355 if ((p->flags & action->flags & CFE_IRQ_FLAGS_SHARED) == 0) {
356 cfe_enable_irq(irq);
357 KFREE(action);
358 xprintf("cfe_request_irq: conflicting unsharable interrupts.\n");
359 return -1;
361 while (p->next != NULL) {
362 p = p->next;
364 p->next = action;
367 cfe_enable_irq(irq);
368 cfe_irq_enable(sr);
369 return 0;
374 * free_irq() releases a handler set up by request_irq()
376 void
377 cfe_free_irq(unsigned int irq, int device)
379 int sr;
380 irq_desc_t *desc;
381 irq_action_t *p, *q;
383 if (irq >= NR_IRQS)
384 return;
386 desc = &irq_desc[irq];
388 /* The following block of code has to be executed atomically */
389 sr = cfe_irq_disable();
390 p = desc->actions;
391 q = NULL;
392 while (p != NULL) {
393 if (p->device == device) {
394 cfe_disable_irq(irq);
395 if (q == NULL) {
396 desc->actions = p->next;
397 if (desc->actions == NULL)
398 desc->status |= IRQ_DISABLED;
400 else
401 q->next = p->next;
402 break;
404 else {
405 q = p;
406 p = p->next;
409 cfe_irq_enable(sr);
410 if (p != NULL)
411 KFREE(p);
415 /* The interrupt polling code calls this routine to dispatch each
416 pending, unmasked interrupt.
418 For asynchronous interrupt processing, it is entered with
419 interrupts disabled. */
421 void sb1250_dispatch_irq(unsigned int irq);
422 void
423 sb1250_dispatch_irq(unsigned int irq)
425 irq_action_t *action;
426 irq_desc_t *desc = &irq_desc[irq];
428 for (action = desc->actions; action != NULL; action = action->next) {
429 if (action->handler != NULL) {
430 (*action->handler)(action->arg);