1 /* *********************************************************************
2 * Broadcom Common Firmware Environment (CFE)
4 * Polled interrupt dispatch routines File: sb1250_irq.c
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"
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"
74 extern void sb1250_irq_install(void);
75 extern void sb1250_irq_arm(void);
78 /* First level dispatching (MIPS IP level). */
82 /* Shared variables that must be protected in non-interrupt code. */
83 static ip_handler_t ip_handler
[IP_LEVELS
] = {NULL
};
86 cfe_irq_setvector(int index
, ip_handler_t handler
)
88 if (index
>= 0 && index
< IP_LEVELS
) {
91 ip_handler
[index
] = NULL
; /* disable: see demux */
92 if (handler
== NULL
) {
93 clear
= _MM_MAKEMASK1(S_SR_IMMASK
+ index
);
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.
115 sb1250_irq_demux(int code
, uint64_t *info
)
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);
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);
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.
151 sb1250_irq_vectorinit(void)
155 for (i
= 0; i
< IP_LEVELS
; i
++) {
156 ip_handler
[i
] = NULL
;
158 _exc_setvector(XTYPE_INTERRUPT
, (void *) sb1250_irq_demux
);
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
);
171 int device
; /* to distinguish actions for shared interrupts */
175 typedef struct irq_desc_s
{
176 irq_action_t
*actions
;
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.
203 for (i
= 0, p
= irq_desc
; i
< NR_IRQS
; i
++, p
++) {
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();
226 cfe_irq_setvector(2, (ip_handler_t
)cfe_irq_poll
);
231 /* cfe_mask_irq() is called to mask an interrupt at the hw level */
233 cfe_mask_irq(int cpu
, unsigned int irq
)
240 sr
= cfe_irq_disable();
241 *IMR_POINTER(cpu
, R_IMR_INTERRUPT_MASK
) |= ((uint64_t)1 << irq
);
242 __asm__
__volatile__ ("sync" : : : "memory");
247 /* cfe_unmask_irq() is called to unmask an interrupt at the hw level */
249 cfe_unmask_irq(int cpu
, unsigned int irq
)
256 sr
= cfe_irq_disable();
257 *IMR_POINTER(cpu
, R_IMR_INTERRUPT_MASK
) &=~ ((uint64_t)1 << irq
);
262 /* If depth is 0, unmask the interrupt. Increment depth. */
264 cfe_enable_irq(unsigned int irq
)
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
;
284 /* Decrement depth. If depth is 0, mask the interrupt. */
286 cfe_disable_irq(unsigned int irq
)
294 desc
= &irq_desc
[irq
];
295 /* The following code must be atomic */
296 sr
= cfe_irq_disable();
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");
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
)
317 irq_action_t
*action
;
321 if (irq
>= NR_IRQS
) {
324 if (handler
== NULL
) {
328 action
= (irq_action_t
*) KMALLOC(sizeof(irq_action_t
), 0);
329 if (action
== NULL
) {
333 action
->handler
= handler
;
335 action
->flags
= irqflags
;
336 action
->device
= device
;
339 desc
= &irq_desc
[irq
];
341 /* The following block of code has to be executed atomically */
342 sr
= cfe_irq_disable();
345 desc
->actions
= action
;
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");
354 /* Can't share interrupts unless both agree to */
355 if ((p
->flags
& action
->flags
& CFE_IRQ_FLAGS_SHARED
) == 0) {
358 xprintf("cfe_request_irq: conflicting unsharable interrupts.\n");
361 while (p
->next
!= NULL
) {
374 * free_irq() releases a handler set up by request_irq()
377 cfe_free_irq(unsigned int irq
, int device
)
386 desc
= &irq_desc
[irq
];
388 /* The following block of code has to be executed atomically */
389 sr
= cfe_irq_disable();
393 if (p
->device
== device
) {
394 cfe_disable_irq(irq
);
396 desc
->actions
= p
->next
;
397 if (desc
->actions
== NULL
)
398 desc
->status
|= IRQ_DISABLED
;
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
);
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
);