1/*  *********************************************************************
2    *  Broadcom Common Firmware Environment (CFE)
3    *
4    *  Polled interrupt dispatch routines	File: sb1250_irq.c
5    *
6    *********************************************************************
7    *
8    *  Copyright 2000,2001,2002,2003
9    *  Broadcom Corporation. All rights reserved.
10    *
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.
17    *
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.
21    *
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.
27    *
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    ********************************************************************* */
42
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.
49    *
50    *  The interface is loosely based on irq.c from Linux.
51    *
52    *  This is not a full-fledged interrupt handler.
53    *
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.
58    *
59    *  For now, only CPU0 polls interrupts, via its interrupt mapper.
60    *
61    ********************************************************************* */
62
63#include "lib_types.h"
64#include "lib_printf.h"
65#include "lib_malloc.h"
66
67#include "sbmips.h"
68#include "sb1250_regs.h"
69#include "sb1250_int.h"
70extern void sb1250_update_sr(uint32_t clear, uint32_t set);
71
72#include "exception.h"
73#include "cfe_irq.h"
74extern void sb1250_irq_arm(void);
75
76
77/* First level dispatching (MIPS IP level). */
78
79#define IP_LEVELS 8
80
81/* Shared variables that must be protected in non-interrupt code. */
82static ip_handler_t ip_handler[IP_LEVELS] = {NULL};
83
84void
85cfe_irq_setvector(int index, ip_handler_t handler)
86{
87    if (index >= 0 && index < IP_LEVELS) {
88	uint32_t set, clear;
89
90	ip_handler[index] = NULL;   /* disable: see demux */
91	if (handler == NULL) {
92	    clear = _MM_MAKEMASK1(S_SR_IMMASK + index);
93	    set = 0;
94	    }
95	else {
96	    clear = 0;
97	    set = _MM_MAKEMASK1(S_SR_IMMASK + index);
98	    }
99	sb1250_update_sr(clear, set);
100	ip_handler[index] = handler;
101    }
102}
103
104
105/*
106 * Dispatch function called from the exception handler for
107 * asynchronous (non-polled) interrupts.
108 * info is a pointer to the saved register block.
109 *
110 * At entry, interrupts will be masked.
111 */
112
113static void
114sb1250_irq_demux(int code, mips_reg_t *info)
115{
116    uint32_t pending;
117
118    pending = (uint32_t)(info[XCP0_CAUSE] & info[XCP0_SR]);
119
120    /* For now, we handle IP7 (internal timers) and IP2 (mapper) only */
121
122    if (pending & M_CAUSE_IP7) {
123	if (ip_handler[7] != NULL) {
124	    (*(ip_handler[7]))(7);
125	    }
126	else {
127	    /* mask off IP7, else we're caught here forever */
128	    sb1250_update_sr(M_SR_IM7, 0);
129	    }
130	}
131
132    if (pending & M_CAUSE_IP2) {
133	if (ip_handler[2] != NULL) {
134	    (*(ip_handler[2]))(2);
135	    }
136	else {
137	    /* mask off IP2, else we're caught here forever */
138	    sb1250_update_sr(M_SR_IM2, 0);
139	    }
140	}
141}
142
143
144/*
145 * Initialize the MIPS level dispatch vector.
146 * This function should be called with interrupts disabled.
147 */
148
149static void
150sb1250_irq_vectorinit(void)
151{
152    int  i;
153
154    for (i = 0; i < IP_LEVELS; i++) {
155	ip_handler[i] = NULL;
156	}
157    _exc_setvector(XTYPE_INTERRUPT, (void *) sb1250_irq_demux);
158    sb1250_irq_arm();
159}
160
161
162/* Second level dispatching (sb1250 interrupt source level). */
163
164#define NR_IRQS K_INT_SOURCES
165
166#define IMR_POINTER(cpu,reg) \
167    ((volatile uint64_t *)(PHYS_TO_K1(A_IMR_REGISTER(cpu,reg))))
168
169typedef struct irq_action_s irq_action_t;
170struct irq_action_s {
171    void (*handler)(void *arg);
172    void *arg;
173    unsigned long flags;
174    int device;              /* to distinguish actions for shared interrupts */
175    irq_action_t *next;
176};
177
178typedef struct irq_desc_s {
179    irq_action_t *actions;
180    int depth;
181    int status;
182} irq_desc_t;
183
184/* status flags */
185#define IRQ_DISABLED  0x0001
186
187/* Shared variables that must be protected in non-interrupt code. */
188static irq_desc_t irq_desc[NR_IRQS];
189
190/*
191 *  cfe_irq_init is called early in the boot sequence.  It is
192 *  responsible for setting up the interrupt mapper and initializing
193 *  the handler table that will be used for dispatching pending
194 *  interrupts.  If hard interrupts are used, it then enables hardware
195 *  interrupts (initially all masked).
196 *
197 *  This function should be called before interrupts are enabled.
198 */
199
200void
201cfe_irq_init(void)
202{
203    int i;
204    irq_desc_t *p;
205
206    for (i = 0, p = irq_desc; i < NR_IRQS; i++, p++) {
207        p->actions = NULL;
208	p->depth = 0;
209	p->status = IRQ_DISABLED;
210	}
211
212    /* initially, all interrupts are masked */
213    *IMR_POINTER(0, R_IMR_INTERRUPT_MASK) = ~((uint64_t)0);
214    *IMR_POINTER(1, R_IMR_INTERRUPT_MASK) = ~((uint64_t)0);
215
216    *IMR_POINTER(0, R_IMR_INTERRUPT_DIAG) = 0;
217    *IMR_POINTER(1, R_IMR_INTERRUPT_DIAG) = 0;
218
219    for (i = 0; i < R_IMR_INTERRUPT_MAP_COUNT; i++) {
220        *IMR_POINTER(0, R_IMR_INTERRUPT_MAP_BASE + 8*i) = 0;
221        *IMR_POINTER(1, R_IMR_INTERRUPT_MAP_BASE + 8*i) = 0;
222	}
223
224    sb1250_irq_vectorinit();
225
226#if CFG_INTERRUPTS
227    cfe_irq_setvector(2, (ip_handler_t)cfe_irq_poll);
228#endif
229}
230
231
232/* cfe_mask_irq() is called to mask an interrupt at the hw level */
233void
234cfe_mask_irq(int cpu, unsigned int irq)
235{
236    int sr;
237
238    if (irq >= NR_IRQS)
239	return;
240
241    sr = cfe_irq_disable();
242    *IMR_POINTER(cpu, R_IMR_INTERRUPT_MASK) |= ((uint64_t)1 << irq);
243    __asm__ __volatile__ ("sync" : : : "memory");
244    cfe_irq_enable(sr);
245}
246
247
248/* cfe_unmask_irq() is called to unmask an interrupt at the hw level */
249void
250cfe_unmask_irq(int cpu, unsigned int irq)
251{
252    int sr;
253
254    if (irq >= NR_IRQS)
255	return;
256
257    sr = cfe_irq_disable();
258    *IMR_POINTER(cpu, R_IMR_INTERRUPT_MASK) &=~ ((uint64_t)1 << irq);
259    cfe_irq_enable(sr);
260}
261
262
263/* If depth is 0, unmask the interrupt. Increment depth. */
264void
265cfe_enable_irq(unsigned int irq)
266{
267    int sr;
268    irq_desc_t *desc;
269
270    if (irq >= NR_IRQS)
271	return;
272
273    desc = &irq_desc[irq];
274    /* The following code must be atomic */
275    sr = cfe_irq_disable();
276    if (desc->depth == 0) {
277	*IMR_POINTER(0, R_IMR_INTERRUPT_MASK) &=~ ((uint64_t)1 << irq);
278	desc->status &=~ IRQ_DISABLED;
279	}
280    desc->depth++;
281    cfe_irq_enable(sr);
282}
283
284
285/* Decrement depth. If depth is 0, mask the interrupt. */
286void
287cfe_disable_irq(unsigned int irq)
288{
289    int sr;
290    irq_desc_t *desc;
291
292    if (irq >= NR_IRQS)
293	return;
294
295    desc = &irq_desc[irq];
296    /* The following code must be atomic */
297    sr = cfe_irq_disable();
298    desc->depth--;
299    if (desc->depth == 0) {
300	*IMR_POINTER(0, R_IMR_INTERRUPT_MASK) |= ((uint64_t)1 << irq);
301	desc->status |= IRQ_DISABLED;
302	__asm__ __volatile__ ("sync" : : : "memory");
303	}
304    cfe_irq_enable(sr);
305}
306
307
308/*
309 *  cfe_request_irq() is called by drivers to request addition to the
310 *  chain of handlers called for a given interrupt.
311 */
312int
313cfe_request_irq(unsigned int irq,
314		void (*handler)(void *), void *arg,
315		unsigned long irqflags, int device)
316{
317    int sr;
318    irq_action_t *action;
319    irq_desc_t *desc;
320    irq_action_t *p;
321
322    if (irq >= NR_IRQS) {
323        return -1;
324	}
325    if (handler == NULL) {
326        return -1;
327	}
328
329    action = (irq_action_t *) KMALLOC(sizeof(irq_action_t), 0);
330    if (action == NULL) {
331        return -1;
332	}
333
334    action->handler = handler;
335    action->arg = arg;
336    action->flags = irqflags;
337    action->device = device;
338    action->next = NULL;
339
340    desc = &irq_desc[irq];
341
342    /* The following block of code has to be executed atomically */
343    sr = cfe_irq_disable();
344    p = desc->actions;
345    if (p == NULL) {
346        desc->actions = action;
347        desc->depth = 0;
348        desc->status |= IRQ_DISABLED;
349
350	 /* always direct to IP[2] for now */
351	*IMR_POINTER(0, R_IMR_INTERRUPT_MAP_BASE + 8*irq) = 0;
352	__asm__ __volatile__ ("sync" : : : "memory");
353	}
354    else {
355        /* Can't share interrupts unless both agree to */
356        if ((p->flags & action->flags & CFE_IRQ_FLAGS_SHARED) == 0) {
357	    cfe_enable_irq(irq);
358	    KFREE(action);
359	    xprintf("cfe_request_irq: conflicting unsharable interrupts.\n");
360            return -1;
361	    }
362	while (p->next != NULL) {
363	    p = p->next;
364	    }
365	p->next = action;
366	}
367
368    cfe_enable_irq(irq);
369    cfe_irq_enable(sr);
370    return 0;
371}
372
373
374/*
375 *  free_irq() releases a handler set up by request_irq()
376 */
377void
378cfe_free_irq(unsigned int irq, int device)
379{
380    int sr;
381    irq_desc_t *desc;
382    irq_action_t *p, *q;
383
384    if (irq >= NR_IRQS)
385        return;
386
387    desc = &irq_desc[irq];
388
389    /* The following block of code has to be executed atomically */
390    sr = cfe_irq_disable();
391    p = desc->actions;
392    q = NULL;
393    while (p != NULL) {
394	if (p->device == device) {
395	    cfe_disable_irq(irq);
396	    if (q == NULL) {
397		desc->actions = p->next;
398		if (desc->actions == NULL)
399		    desc->status |= IRQ_DISABLED;
400		}
401	    else
402		q->next = p->next;
403	    break;
404	    }
405	else {
406	    q = p;
407	    p = p->next;
408	    }
409	}
410    cfe_irq_enable(sr);
411    if (p != NULL)
412	KFREE(p);
413}
414
415
416/* The interrupt polling code calls this routine to dispatch each
417   pending, unmasked interrupt.
418
419   For asynchronous interrupt processing, it is entered with
420   interrupts disabled. */
421
422void sb1250_dispatch_irq(unsigned int irq);
423void
424sb1250_dispatch_irq(unsigned int irq)
425{
426    irq_action_t *action;
427    irq_desc_t *desc = &irq_desc[irq];
428
429    for (action = desc->actions; action != NULL; action = action->next) {
430	if (action->handler != NULL) {
431	    (*action->handler)(action->arg);
432	    }
433	}
434}
435