4 * Copyright (c) 2007 Ruslan Ermilov and Vsevolod Lobko.
7 * Redistribution and use in source and binary forms, with or
8 * without modification, are permitted provided that the following
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials provided
15 * with the distribution.
16 * 3. The names of the authors may not be used to endorse or promote
17 * products derived from this software without specific prior
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY
21 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
25 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
27 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
29 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
30 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
34 * Copyright (c) 2001 The NetBSD Foundation, Inc.
35 * All rights reserved.
37 * This code is derived from software contributed to The NetBSD Foundation
40 * Redistribution and use in source and binary forms, with or without
41 * modification, are permitted provided that the following conditions
43 * 1. Redistributions of source code must retain the above copyright
44 * notice, this list of conditions and the following disclaimer.
45 * 2. Redistributions in binary form must reproduce the above copyright
46 * notice, this list of conditions and the following disclaimer in the
47 * documentation and/or other materials provided with the distribution.
49 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
50 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
51 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
52 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
53 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
54 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
55 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
56 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
57 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
58 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
59 * POSSIBILITY OF SUCH DAMAGE.
63 * Platform-specific interrupt support for the RMI XLP, XLR, XLS
66 #include <sys/cdefs.h>
67 __KERNEL_RCSID(0, "$NetBSD$");
71 #include <sys/param.h>
72 #include <sys/queue.h>
73 #include <sys/malloc.h>
74 #include <sys/systm.h>
75 #include <sys/device.h>
76 #include <sys/kernel.h>
78 #include <machine/bus.h>
79 #include <machine/intr.h>
82 #include <mips/locore.h>
84 #include <mips/rmi/rmixlreg.h>
85 #include <mips/rmi/rmixlvar.h>
87 #include <dev/pci/pcireg.h>
88 #include <dev/pci/pcivar.h>
91 int iointr_debug
= IOINTR_DEBUG
;
92 # define DPRINTF(x) do { if (iointr_debug) printf x ; } while(0)
97 #define RMIXL_PICREG_READ(off) \
98 RMIXL_IOREG_READ(RMIXL_IO_DEV_PIC + (off))
99 #define RMIXL_PICREG_WRITE(off, val) \
100 RMIXL_IOREG_WRITE(RMIXL_IO_DEV_PIC + (off), (val))
102 * This is a mask of bits to clear in the SR when we go to a
103 * given hardware interrupt priority level.
104 * _SR_BITS_DFLT bits are to be always clear (disabled)
106 #define _SR_BITS_DFLT (MIPS_INT_MASK_2|MIPS_INT_MASK_3|MIPS_INT_MASK_4)
107 const uint32_t ipl_sr_bits
[_IPL_N
] = {
108 [IPL_NONE
] = _SR_BITS_DFLT
,
111 | MIPS_SOFT_INT_MASK_0
,
114 | MIPS_SOFT_INT_MASK_0
115 | MIPS_SOFT_INT_MASK_1
,
118 | MIPS_SOFT_INT_MASK_0
119 | MIPS_SOFT_INT_MASK_1
126 * 'IRQs' here are indiividual interrupt sources
127 * each has a slot in the Interrupt Redirection Table (IRT)
128 * in the order listed
130 * NOTE: many irq sources depend on the chip family
131 * XLS1xx vs. XLS2xx vs. XLS3xx vs. XLS6xx
132 * use the right table for the CPU that's running.
136 * rmixl_irqnames_xls1xx
137 * - use for XLS1xx, XLS2xx, XLS4xx-Lite
140 static const char *rmixl_irqnames_xls1xx
[NIRQS
] = {
141 "int 0 (watchdog)", /* 0 */
142 "int 1 (timer0)", /* 1 */
143 "int 2 (timer1)", /* 2 */
144 "int 3 (timer2)", /* 3 */
145 "int 4 (timer3)", /* 4 */
146 "int 5 (timer4)", /* 5 */
147 "int 6 (timer5)", /* 6 */
148 "int 7 (timer6)", /* 7 */
149 "int 8 (timer7)", /* 8 */
150 "int 9 (uart0)", /* 9 */
151 "int 10 (uart1)", /* 10 */
152 "int 11 (i2c0)", /* 11 */
153 "int 12 (i2c1)", /* 12 */
154 "int 13 (pcmcia)", /* 13 */
155 "int 14 (gpio_a)", /* 14 */
156 "int 15 (irq15)", /* 15 */
157 "int 16 (bridge_tb)", /* 16 */
158 "int 17 (gmac0)", /* 17 */
159 "int 18 (gmac1)", /* 18 */
160 "int 19 (gmac2)", /* 19 */
161 "int 20 (gmac3)", /* 20 */
162 "int 21 (irq21)", /* 21 */
163 "int 22 (irq22)", /* 22 */
164 "int 23 (irq23)", /* 23 */
165 "int 24 (irq24)", /* 24 */
166 "int 25 (bridge_err)", /* 25 */
167 "int 26 (pcie_link0)", /* 26 */
168 "int 27 (pcie_link1)", /* 27 */
169 "int 28 (irq28)", /* 28 */
170 "int 29 (irq29)", /* 29 */
171 "int 30 (gpio_b)", /* 30 */
172 "int 31 (usb)", /* 31 */
176 * rmixl_irqnames_xls4xx:
177 * - use for XLS4xx, XLS6xx
179 static const char *rmixl_irqnames_xls4xx
[NIRQS
] = {
180 "int 0 (watchdog)", /* 0 */
181 "int 1 (timer0)", /* 1 */
182 "int 2 (timer1)", /* 2 */
183 "int 3 (timer2)", /* 3 */
184 "int 4 (timer3)", /* 4 */
185 "int 5 (timer4)", /* 5 */
186 "int 6 (timer5)", /* 6 */
187 "int 7 (timer6)", /* 7 */
188 "int 8 (timer7)", /* 8 */
189 "int 9 (uart0)", /* 9 */
190 "int 10 (uart1)", /* 10 */
191 "int 11 (i2c0)", /* 11 */
192 "int 12 (i2c1)", /* 12 */
193 "int 13 (pcmcia)", /* 13 */
194 "int 14 (gpio_a)", /* 14 */
195 "int 15 (irq15)", /* 15 */
196 "int 16 (bridge_tb)", /* 16 */
197 "int 17 (gmac0)", /* 17 */
198 "int 18 (gmac1)", /* 18 */
199 "int 19 (gmac2)", /* 19 */
200 "int 20 (gmac3)", /* 20 */
201 "int 21 (irq21)", /* 21 */
202 "int 22 (irq22)", /* 22 */
203 "int 23 (irq23)", /* 23 */
204 "int 24 (irq24)", /* 24 */
205 "int 25 (bridge_err)", /* 25 */
206 "int 26 (pcie_link0)", /* 26 */
207 "int 27 (pcie_link1)", /* 27 */
208 "int 28 (pcie_link2)", /* 28 */
209 "int 29 (pcie_link3)", /* 29 */
210 "int 30 (gpio_b)", /* 30 */
211 "int 31 (usb)", /* 31 */
215 * rmixl_irqnames_xls4xx:
216 * - use for unknown cpu implementation
218 static const char *rmixl_irqnames_generic
[NIRQS
] = {
254 * per-IRQ event stats
256 struct rmixl_irqtab
{
257 struct evcnt irq_count
;
260 static struct rmixl_irqtab rmixl_irqtab
[NIRQS
];
264 * 'vectors' here correspond to IRT Entry vector numbers
265 * - IRT Entry vector# is bit# in EIRR
266 * - note that EIRR[7:0] == CAUSE[15:8]
267 * - we actually only use the first _IPL_N bits
270 * each IRT entry gets routed to a vector
271 * (if and when that interrupt is established)
272 * the vectors are shared on a per-IPL basis
273 * which simplifies dispatch
275 * XXX use of mips64 extended IRQs is TBD
277 #define NINTRVECS _IPL_N
280 * translate IPL to vector number
282 static const int rmixl_iplvec
[_IPL_N
] = {
283 [IPL_NONE
] = -1, /* XXX */
291 * list and ref count manage sharing of each vector
293 struct rmixl_intrvec
{
294 LIST_HEAD(, evbmips_intrhand
) iv_list
;
296 rmixl_intr_trigger_t iv_trigger
;
297 rmixl_intr_polarity_t iv_polarity
;
300 static struct rmixl_intrvec rmixl_intrvec
[NINTRVECS
];
303 static int evbmips_intr_init_done
;
307 static void rmixl_intr_irt_init(int);
308 static void rmixl_intr_irt_disestablish(int);
309 static void rmixl_intr_irt_establish(int, int, rmixl_intr_trigger_t
,
310 rmixl_intr_polarity_t
, int);
314 pic_irt_print(const char *s
, const int n
, u_int irq
)
319 c0
= RMIXL_PICREG_READ(RMIXL_PIC_IRTENTRYC0(irq
));
320 c1
= RMIXL_PICREG_READ(RMIXL_PIC_IRTENTRYC1(irq
));
321 printf("%s:%d: irq %d: c0 %#x, c1 %#x\n", s
, n
, irq
, c0
, c1
);
326 evbmips_intr_init(void)
331 KASSERT(cpu_rmixls(mycpu
));
334 if (evbmips_intr_init_done
!= 0)
335 panic("%s: evbmips_intr_init_done %d",
336 __func__
, evbmips_intr_init_done
);
339 for (i
=0; i
< NIRQS
; i
++) {
340 evcnt_attach_dynamic(&rmixl_irqtab
[i
].irq_count
,
341 EVCNT_TYPE_INTR
, NULL
, "rmixl", rmixl_intr_string(i
));
342 rmixl_irqtab
[i
].irq_ih
= NULL
;
345 for (i
=0; i
< NINTRVECS
; i
++) {
346 LIST_INIT(&rmixl_intrvec
[i
].iv_list
);
347 rmixl_intrvec
[i
].iv_ack
= 0;
348 rmixl_intrvec
[i
].iv_refcnt
= 0;
352 * disable watchdog NMI, timers
355 * WATCHDOG_ENB is preserved because clearing it causes
356 * hang on the XLS616 (but not on the XLS408)
358 r
= RMIXL_PICREG_READ(RMIXL_PIC_CONTROL
);
359 r
&= RMIXL_PIC_CONTROL_RESV
|RMIXL_PIC_CONTROL_WATCHDOG_ENB
;
360 RMIXL_PICREG_WRITE(RMIXL_PIC_CONTROL
, r
);
363 * initialize all IRT Entries
365 for (i
=0; i
< NIRQS
; i
++)
366 rmixl_intr_irt_init(i
);
369 * establish IRT entry for mips3 clock interrupt
371 rmixl_intr_irt_establish(7, IPL_CLOCK
, RMIXL_INTR_LEVEL
,
372 RMIXL_INTR_HIGH
, rmixl_iplvec
[IPL_CLOCK
]);
375 evbmips_intr_init_done
= 1;
380 rmixl_intr_string(int irq
)
384 if (irq
< 0 || irq
>= NIRQS
)
385 panic("%s: irq %d out of range, max %d",
386 __func__
, irq
, NIRQS
- 1);
388 switch (MIPS_PRID_IMPL(cpu_id
)) {
393 case MIPS_XLS404LITE
:
394 case MIPS_XLS408LITE
:
395 name
= rmixl_irqnames_xls1xx
[irq
];
402 name
= rmixl_irqnames_xls4xx
[irq
];
405 name
= rmixl_irqnames_generic
[irq
];
413 * rmixl_intr_irt_init
414 * - invalidate IRT Entry for irq
415 * - unmask Thread#0 in low word (assume we only have 1 thread)
418 rmixl_intr_irt_init(int irq
)
420 RMIXL_PICREG_WRITE(RMIXL_PIC_IRTENTRYC1(irq
), 0); /* high word */
421 RMIXL_PICREG_WRITE(RMIXL_PIC_IRTENTRYC0(irq
), 1); /* low word */
425 * rmixl_intr_irt_disestablish
426 * - invalidate IRT Entry for irq
427 * - writes to IRTENTRYC1 only; leave IRTENTRYC0 as-is
430 rmixl_intr_irt_disestablish(int irq
)
432 DPRINTF(("%s: irq %d, irtc1 %#x\n", __func__
, irq
, 0));
433 RMIXL_PICREG_WRITE(RMIXL_PIC_IRTENTRYC1(irq
), 0); /* high word */
437 * rmixl_intr_irt_establish
438 * - construct and IRT Entry for irq and write to PIC
439 * - writes to IRTENTRYC1 only; assumes IRTENTRYC0 has been initialized
442 rmixl_intr_irt_establish(int irq
, int ipl
, rmixl_intr_trigger_t trigger
,
443 rmixl_intr_polarity_t polarity
, int vec
)
447 irtc1
= RMIXL_PIC_IRTENTRYC1_VALID
;
448 irtc1
|= RMIXL_PIC_IRTENTRYC1_GL
; /* local */
450 if (trigger
== RMIXL_INTR_LEVEL
)
451 irtc1
|= RMIXL_PIC_IRTENTRYC1_TRG
;
453 if ((polarity
== RMIXL_INTR_FALLING
) || (polarity
== RMIXL_INTR_LOW
))
454 irtc1
|= RMIXL_PIC_IRTENTRYC1_P
;
459 * write IRT Entry to PIC (high word only)
461 DPRINTF(("%s: irq %d, irtc1 %#x\n", __func__
, irq
, irtc1
));
462 RMIXL_PICREG_WRITE(RMIXL_PIC_IRTENTRYC1(irq
), irtc1
);
466 rmixl_intr_establish(int irq
, int ipl
, rmixl_intr_trigger_t trigger
,
467 rmixl_intr_polarity_t polarity
, int (*func
)(void *), void *arg
)
469 struct evbmips_intrhand
*ih
;
470 struct rmixl_intrvec
*ivp
;
475 if (evbmips_intr_init_done
== 0)
476 panic("%s: called before evbmips_intr_init", __func__
);
480 * check args and assemble an IRT Entry
482 if (irq
< 0 || irq
>= NIRQS
)
483 panic("%s: irq %d out of range, max %d",
484 __func__
, irq
, NIRQS
- 1);
485 if (ipl
<= 0 || ipl
>= _IPL_N
)
486 panic("%s: ipl %d out of range, min %d, max %d",
487 __func__
, ipl
, 1, _IPL_N
- 1);
488 if (rmixl_irqtab
[irq
].irq_ih
!= NULL
)
489 panic("%s: irq %d busy", __func__
, irq
);
492 case RMIXL_INTR_EDGE
:
493 case RMIXL_INTR_LEVEL
:
496 panic("%s: bad trigger %d\n", __func__
, trigger
);
500 case RMIXL_INTR_RISING
:
501 case RMIXL_INTR_HIGH
:
502 case RMIXL_INTR_FALLING
:
506 panic("%s: bad polarity %d\n", __func__
, polarity
);
510 * ipl determines which vector to use
512 vec
= rmixl_iplvec
[ipl
];
513 DPRINTF(("%s: irq %d, ipl %d, vec %d\n", __func__
, irq
, ipl
, vec
));
514 KASSERT((vec
& ~RMIXL_PIC_IRTENTRYC1_INTVEC
) == 0);
518 ivp
= &rmixl_intrvec
[vec
];
519 if (ivp
->iv_refcnt
== 0) {
520 ivp
->iv_trigger
= trigger
;
521 ivp
->iv_polarity
= polarity
;
523 if (ivp
->iv_trigger
!= trigger
) {
525 printf("%s: vec %d, irqs {", __func__
, vec
);
526 LIST_FOREACH(ih
, &ivp
->iv_list
, ih_q
) {
527 printf(" %d", ih
->ih_irq
);
529 printf(" } trigger type %d; irq %d wants type %d\n",
530 ivp
->iv_trigger
, irq
, trigger
);
532 panic("%s: trigger mismatch at vec %d\n",
535 if (ivp
->iv_polarity
!= polarity
) {
537 printf("%s: vec %d, irqs {", __func__
, vec
);
538 LIST_FOREACH(ih
, &ivp
->iv_list
, ih_q
) {
539 printf(" %d", ih
->ih_irq
);
541 printf(" } polarity type %d; irq %d wants type %d\n",
542 ivp
->iv_polarity
, irq
, polarity
);
544 panic("%s: polarity mismatch at vec %d\n",
548 ivp
->iv_ack
|= (1 << irq
);
551 * allocate and initialize an interrupt handle
553 ih
= malloc(sizeof(*ih
), M_DEVBUF
, M_NOWAIT
);
563 * mark this irq as established, busy
565 rmixl_irqtab
[irq
].irq_ih
= ih
;
568 * link this ih into the tables and bump reference count
570 LIST_INSERT_HEAD(&ivp
->iv_list
, ih
, ih_q
);
574 * establish IRT Entry
576 rmixl_intr_irt_establish(irq
, ipl
, trigger
, polarity
, vec
);
584 rmixl_intr_disestablish(void *cookie
)
586 struct evbmips_intrhand
*ih
= cookie
;
587 struct rmixl_intrvec
*ivp
;
593 vec
= rmixl_iplvec
[ih
->ih_ipl
];
594 ivp
= &rmixl_intrvec
[vec
];
599 * disable the IRT Entry (high word only)
601 rmixl_intr_irt_disestablish(irq
);
604 * remove from the table and adjust the reference count
606 LIST_REMOVE(ih
, ih_q
);
608 ivp
->iv_ack
&= ~(1 << irq
);
611 * this irq now disestablished, not busy
613 rmixl_irqtab
[irq
].irq_ih
= NULL
;
621 pci_int_status(const char *s
, const int n
)
625 r
= RMIXL_IOREG_READ(RMIXL_IO_DEV_PCIE_LE
+ 0xa0);
626 printf("%s:%d: PCIE_LINK0_INT_STATUS0 %#x\n", s
, n
, r
);
631 evbmips_iointr(uint32_t status
, uint32_t cause
, uint32_t pc
, uint32_t ipending
)
633 struct evbmips_intrhand
*ih
;
634 struct rmixl_intrvec
*ivp
;
640 printf("%s: status %#x, cause %#x, pc %#x, ipending %#x\n",
641 __func__
, status
, cause
, pc
, ipending
);
643 asm volatile("dmfc0 %0, $9, 6;" : "=r"(eirr
));
644 asm volatile("dmfc0 %0, $9, 7;" : "=r"(eimr
));
645 printf("%s:%d: eirr %#lx, eimr %#lx\n", __func__
, __LINE__
, eirr
, eimr
);
646 pci_int_status(__func__
, __LINE__
);
649 for (vec
= NINTRVECS
- 1; vec
>= 2; vec
--) {
650 if ((ipending
& (MIPS_SOFT_INT_MASK_0
<< vec
)) == 0)
653 ivp
= &rmixl_intrvec
[vec
];
656 asm volatile("dmtc0 %0, $9, 6;" :: "r"(eirr
));
659 printf("%s: interrupt at vec %d\n",
661 if (LIST_EMPTY(&ivp
->iv_list
))
662 printf("%s: unexpected interrupt at vec %d\n",
665 LIST_FOREACH(ih
, &ivp
->iv_list
, ih_q
) {
666 pic_irt_print(__func__
, __LINE__
, ih
->ih_irq
);
667 RMIXL_PICREG_WRITE(RMIXL_PIC_INTRACK
,
669 if ((*ih
->ih_func
)(ih
->ih_arg
) != 0) {
670 rmixl_irqtab
[ih
->ih_irq
].irq_count
.ev_count
++;
674 pci_int_status(__func__
, __LINE__
);
676 cause
&= ~(MIPS_SOFT_INT_MASK_0
<< vec
);
680 /* Re-enable anything that we have processed. */
681 _splset(MIPS_SR_INT_IE
| ((status
& ~cause
) & MIPS_HARD_INT_MASK
));
685 int rmixl_intrvec_print(void);
687 rmixl_intrvec_print(void)
689 struct evbmips_intrhand
*ih
;
690 struct rmixl_intrvec
*ivp
;
693 ivp
= &rmixl_intrvec
[0];
694 for (vec
=0; vec
< NINTRVECS
; vec
++) {
695 printf("vec %d, irqs {", vec
);
696 LIST_FOREACH(ih
, &ivp
->iv_list
, ih_q
)
697 printf(" %d", ih
->ih_irq
);
698 printf(" } trigger type %d\n", ivp
->iv_trigger
);