xref: /netbsd-src/sys/arch/evbmips/loongson/loongson_intr.c (revision ad1425b7c2cb0cb2be0b7c7293cd6994cfbbd4b7)
1 /*      $NetBSD: loongson_intr.c,v 1.8 2020/11/21 15:36:36 thorpej Exp $      */
2 
3 /*-
4  * Copyright (c) 2001 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: loongson_intr.c,v 1.8 2020/11/21 15:36:36 thorpej Exp $");
34 
35 #define __INTR_PRIVATE
36 
37 #include <sys/param.h>
38 #include <sys/device.h>
39 #include <sys/cpu.h>
40 #include <sys/intr.h>
41 #include <sys/bus.h>
42 #include <sys/kmem.h>
43 
44 #include <mips/mips3_clock.h>
45 #include <machine/locore.h>
46 
47 #include <evbmips/loongson/autoconf.h>
48 #include <evbmips/loongson/loongson_intr.h>
49 
50 #include <mips/locore.h>
51 
52 #include <mips/bonito/bonitoreg.h>
53 #include <mips/bonito/bonitovar.h>
54 
55 #include <dev/pci/pciidereg.h>
56 #include <dev/isa/isavar.h>
57 
58 #include "isa.h"
59 
60 #ifdef INTR_DEBUG
61 #define DPRINTF(x) printf x
62 #else
63 #define DPRINTF(x)
64 #endif
65 
66 struct bonito_intrhead bonito_intrhead[BONITO_NINTS];
67 
68 /*
69  * This is a mask of bits to clear in the SR when we go to a
70  * given hardware interrupt priority level.
71  */
72 static const struct ipl_sr_map loongson_ipl_sr_map = {
73     .sr_bits = {
74 	[IPL_NONE] =		0,
75 	[IPL_SOFTCLOCK] =	MIPS_SOFT_INT_MASK_0,
76 	[IPL_SOFTNET] =		MIPS_SOFT_INT_MASK_0 | MIPS_SOFT_INT_MASK_1,
77 	[IPL_VM] =
78 	    MIPS_SOFT_INT_MASK_0 | MIPS_SOFT_INT_MASK_1 |
79 	    MIPS_INT_MASK_0 |
80 	    MIPS_INT_MASK_1 |
81 	    MIPS_INT_MASK_2 |
82 	    MIPS_INT_MASK_3 |
83 	    MIPS_INT_MASK_4,
84 	[IPL_SCHED] =
85 	    MIPS_SOFT_INT_MASK_0 | MIPS_SOFT_INT_MASK_1 |
86 	    MIPS_INT_MASK_0 |
87 	    MIPS_INT_MASK_1 |
88 	    MIPS_INT_MASK_2 |
89 	    MIPS_INT_MASK_3 |
90 	    MIPS_INT_MASK_4 |
91 	    MIPS_INT_MASK_5,
92 	[IPL_DDB] =		MIPS_INT_MASK,
93 	[IPL_HIGH] =            MIPS_INT_MASK,
94     },
95 };
96 
97 
98 void
evbmips_intr_init(void)99 evbmips_intr_init(void)
100 {
101 	const struct bonito_config * const bc = sys_platform->bonito_config;
102 	const struct bonito_irqmap *irqmap;
103 	int i;
104 
105 	ipl_sr_map = loongson_ipl_sr_map;
106 
107 	for (i = 0; i < BONITO_NDIRECT; i++) {
108 		irqmap = &sys_platform->irq_map[i];
109 		if (irqmap->name == NULL)
110 			continue;
111 		DPRINTF(("attach %d %s\n", i, irqmap->name));
112 		evcnt_attach_dynamic(&bonito_intrhead[i].intr_count,
113 		    EVCNT_TYPE_INTR, NULL, "bonito", irqmap->name);
114 		LIST_INIT(&bonito_intrhead[i].intrhand_head);
115 	}
116 
117 	REGVAL(BONITO_GPIOIE) = bc->bc_gpioIE;
118 	REGVAL(BONITO_INTEDGE) = bc->bc_intEdge;
119 	/* REGVAL(BONITO_INTSTEER) = bc->bc_intSteer; XXX */
120 	REGVAL(BONITO_INTPOL) = bc->bc_intPol;
121 	REGVAL(BONITO_INTENCLR) = 0xffffffff;
122 	(void)REGVAL(BONITO_INTENCLR);
123 
124 	if (sys_platform->isa_chipset != NULL) {
125 		int irq;
126 		static char irqstr[8];
127 		for (irq = 0; irq < BONITO_NISA; irq++) {
128 			i = BONITO_ISA_IRQ(irq);
129 			snprintf(irqstr, sizeof(irqstr), "irq %d", irq);
130 			DPRINTF(("attach %d %d %s\n", i, irq, irqstr));
131 			evcnt_attach_dynamic(&bonito_intrhead[i].intr_count,
132 			    EVCNT_TYPE_INTR, NULL, "isa", irqstr);
133 			LIST_INIT(&bonito_intrhead[i].intrhand_head);
134 		}
135 	}
136 }
137 
138 void
evbmips_iointr(int ppl,uint32_t ipending,struct clockframe * cf)139 evbmips_iointr(int ppl, uint32_t ipending, struct clockframe *cf)
140 {
141 	struct evbmips_intrhand *ih;
142 	int irq;
143 	uint32_t isr0, isr, imr;
144 
145 	/*
146 	 * Read the interrupt pending registers, mask them with the
147 	 * ones we have enabled, and service them in order of decreasing
148 	 * priority.
149 	 */
150 	isr0 = REGVAL(BONITO_INTISR);
151 	imr = REGVAL(BONITO_INTEN);
152 
153 	if (ipending & sys_platform->bonito_mips_intr) {
154 		isr = isr0 & imr & LOONGSON_INTRMASK_LVL4;
155 
156 		REGVAL(BONITO_INTENCLR) = isr;
157 		(void)REGVAL(BONITO_INTENCLR);
158 
159 		for (irq = 0; irq < BONITO_NINTS; irq++) {
160 			if ((isr & (1 << irq)) == 0)
161 				continue;
162 			bonito_intrhead[irq].intr_count.ev_count++;
163 			LIST_FOREACH (ih,
164 			    &bonito_intrhead[irq].intrhand_head, ih_q) {
165 				if (ih->ih_arg)
166 					(*ih->ih_func)(ih->ih_arg);
167 				else
168 					(*ih->ih_func)(cf);
169 			}
170 		}
171 		REGVAL(BONITO_INTENSET) = isr;
172 		(void)REGVAL(BONITO_INTENSET);
173 	}
174 	if (isr0 & LOONGSON_INTRMASK_INT0)
175 		sys_platform->isa_intr(ppl, cf->pc, ipending);
176 }
177 
178 void *
loongson_pciide_compat_intr_establish(void * v,device_t dev,const struct pci_attach_args * pa,int chan,int (* func)(void *),void * arg)179 loongson_pciide_compat_intr_establish(void *v, device_t dev,
180     const struct pci_attach_args *pa, int chan, int (*func)(void *), void *arg)
181 {
182 	pci_chipset_tag_t pc = pa->pa_pc;
183 	void *cookie;
184 	int bus, irq;
185 	char buf[PCI_INTRSTR_LEN];
186 
187 	pci_decompose_tag(pc, pa->pa_tag, &bus, NULL, NULL);
188 
189 	/*
190 	 * If this isn't PCI bus #0, all bets are off.
191 	 */
192 	if (bus != 0)
193 		return (NULL);
194 
195 	irq = PCIIDE_COMPAT_IRQ(chan);
196 #if NISA > 0
197 	if (sys_platform->isa_chipset != NULL)
198 		cookie = isa_intr_establish(sys_platform->isa_chipset, irq,
199 		    IST_EDGE, IPL_BIO, func, arg);
200 	else
201 #endif
202 		cookie = NULL;
203 	if (cookie == NULL)
204 		return (NULL);
205 	printf("%s: %s channel interrupting at %s\n", device_xname(dev),
206 	    PCIIDE_CHANNEL_NAME(chan),
207 	    isa_intr_string(sys_platform->isa_chipset, irq, buf, sizeof(buf)));
208 	return (cookie);
209 }
210 
211 int
loongson_pci_intr_map(const struct pci_attach_args * pa,pci_intr_handle_t * ihp)212 loongson_pci_intr_map(const struct pci_attach_args *pa,
213     pci_intr_handle_t *ihp)
214 {
215 	pcitag_t bustag = pa->pa_intrtag;
216 	int buspin = pa->pa_intrpin;
217 	pci_chipset_tag_t pc = pa->pa_pc;
218 	int device, function;
219 
220 	if (buspin == 0) {
221 		/* No IRQ used. */
222 		return (1);
223 	}
224 
225 	if (buspin > 4) {
226 		printf("loongson_pci_intr_map: bad interrupt pin %d\n",
227 		    buspin);
228 		return (1);
229 	}
230 
231 	pci_decompose_tag(pc, bustag, NULL, &device, &function);
232 	return (sys_platform->p_pci_intr_map(device, function, buspin, ihp));
233 }
234 
235 const char *
loongson_pci_intr_string(void * v,pci_intr_handle_t ih,char * buf,size_t len)236 loongson_pci_intr_string(void *v, pci_intr_handle_t ih, char *buf, size_t len)
237 {
238 
239 	const struct bonito_config *bc = v;
240 	return loongson_intr_string(bc, ih, buf, len);
241 }
242 
243 const struct evcnt *
loongson_pci_intr_evcnt(void * v,pci_intr_handle_t ih)244 loongson_pci_intr_evcnt(void *v, pci_intr_handle_t ih)
245 {
246 
247 	return &bonito_intrhead[ih].intr_count;
248 }
249 
250 void *
loongson_pci_intr_establish(void * v,pci_intr_handle_t ih,int level,int (* func)(void *),void * arg)251 loongson_pci_intr_establish(void *v, pci_intr_handle_t ih, int level,
252     int (*func)(void *), void *arg)
253 {
254 	if (BONITO_IRQ_IS_ISA(ih)) {
255 		if (sys_platform->isa_chipset == NULL)
256 			panic("ISA interrupt on non-ISA platform");
257 		return sys_platform->isa_chipset->ic_intr_establish(v,
258 		    BONITO_IRQ_TO_ISA(ih), IST_EDGE, level, func, arg);
259 	}
260 	return evbmips_intr_establish(ih, func, arg);
261 }
262 
263 void
loongson_pci_intr_disestablish(void * v,void * cookie)264 loongson_pci_intr_disestablish(void *v, void *cookie)
265 {
266 	struct evbmips_intrhand *ih = cookie;
267 	if (BONITO_IRQ_IS_ISA(ih->ih_irq)) {
268 		if (sys_platform->isa_chipset == NULL)
269 			panic("ISA interrupt on non-ISA platform");
270 		sys_platform->isa_chipset->ic_intr_disestablish(v, ih);
271 		return;
272 	}
273 	return (evbmips_intr_disestablish(cookie));
274 }
275 
276 void
loongson_pci_conf_interrupt(void * v,int bus,int dev,int pin,int swiz,int * iline)277 loongson_pci_conf_interrupt(void *v, int bus, int dev, int pin, int swiz,
278     int *iline)
279 {
280 
281 	/*
282 	 * We actually don't need to do anything; everything is handled
283 	 * in pci_intr_map().
284 	 */
285 	*iline = 0;
286 }
287 
288 
289 void *
evbmips_intr_establish(int irq,int (* func)(void *),void * arg)290 evbmips_intr_establish(int irq, int (*func)(void *), void *arg)
291 {
292 	struct evbmips_intrhand *ih;
293 	int s;
294 
295 	KASSERT(irq < BONITO_NINTS);
296 	DPRINTF(("loongson_intr_establish %d %p", irq, func));
297 
298 	ih = kmem_zalloc(sizeof(*ih), KM_SLEEP);
299 	ih->ih_func = func;
300 	ih->ih_arg = arg;
301 	ih->ih_irq = irq;
302 	DPRINTF((" ih %p", ih));
303 
304 	/* link it into tables */
305 	s = splhigh();
306 	LIST_INSERT_HEAD(&bonito_intrhead[irq].intrhand_head, ih, ih_q);
307 	/* and enable it */
308 	DPRINTF((" inten 0x%x", REGVAL(BONITO_INTEN)));
309 	if (bonito_intrhead[irq].refcnt++ == 0 && !BONITO_IRQ_IS_ISA(irq))
310 		REGVAL(BONITO_INTENSET) = (1 << ih->ih_irq);
311 	DPRINTF((" now 0x%x\n", REGVAL(BONITO_INTEN)));
312 	splx(s);
313 
314 	return (ih);
315 }
316 
317 void
evbmips_intr_disestablish(void * cookie)318 evbmips_intr_disestablish(void *cookie)
319 {
320 	struct evbmips_intrhand *ih = cookie;
321 	int s;
322 
323 	s = splhigh();
324 	LIST_REMOVE(ih, ih_q);
325 	bonito_intrhead[ih->ih_irq].refcnt--;
326 	if (bonito_intrhead[ih->ih_irq].refcnt == 0 &&
327 	    !BONITO_IRQ_IS_ISA(ih->ih_irq))
328 		REGVAL(BONITO_INTENCLR) = (1 << ih->ih_irq);
329 	splx(s);
330 	kmem_free(ih, sizeof(*ih));
331 }
332 
333 const char *
loongson_intr_string(const struct bonito_config * bc,int irq,char * buf,size_t len)334 loongson_intr_string(const struct bonito_config *bc, int irq, char *buf, size_t len)
335 {
336 	if (BONITO_IRQ_IS_ISA(irq))
337 		snprintf(buf, len, "isa irq %d", BONITO_IRQ_TO_ISA(irq));
338 	else
339 		strlcpy(buf, sys_platform->irq_map[irq].name, len);
340 	return buf;
341 }
342