xref: /netbsd-src/sys/arch/virt68k/virt68k/intr.c (revision ab00f1ee3aae25900cb15e921325c9cf00a8dfa9)
1 /*	$NetBSD: intr.c,v 1.4 2024/11/01 14:28:08 mlelstv Exp $	*/
2 
3 /*-
4  * Copyright (c) 1996, 2023 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Adam Glass, Gordon W. Ross, and 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 /*
33  * Link and dispatch interrupts.
34  */
35 
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.4 2024/11/01 14:28:08 mlelstv Exp $");
38 
39 #define _VIRT68K_INTR_PRIVATE
40 
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/kmem.h>
44 #include <sys/vmmeter.h>
45 #include <sys/device.h>
46 #include <sys/cpu.h>
47 #include <sys/bus.h>
48 #include <sys/intr.h>
49 
50 #include <machine/vectors.h>
51 
52 #include <uvm/uvm_extern.h>
53 
54 #include <dev/goldfish/gfpicvar.h>
55 
56 struct intrhand {
57 	LIST_ENTRY(intrhand)	ih_link;
58 	int			(*ih_func)(void *);
59 	void			*ih_arg;
60 	struct evcnt		*ih_evcnt;
61 	int			ih_irq;
62 };
63 
64 static const char * const cpu_irq_group = "cpu irq";
65 
66 static const char * const pic_irq_names[] = {
67 	"irq 1",  "irq 2",  "irq 3",  "irq 4",
68 	"irq 5",  "irq 6",  "irq 7",  "irq 8",
69 	"irq 9",  "irq 10", "irq 11", "irq 12",
70 	"irq 13", "irq 14", "irq 15", "irq 16",
71 	"irq 17", "irq 18", "irq 19", "irq 20",
72 	"irq 21", "irq 22", "irq 23", "irq 24",
73 	"irq 25", "irq 26", "irq 27", "irq 28",
74 	"irq 29", "irq 30", "irq 31", "irq 32",
75 };
76 
77 #define	PIC_EVCNTS(g)							  \
78 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[0]), \
79 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[1]), \
80 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[2]), \
81 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[3]), \
82 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[4]), \
83 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[5]), \
84 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[6]), \
85 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[7]), \
86 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[8]), \
87 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[9]), \
88 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[10]),\
89 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[11]),\
90 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[12]),\
91 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[13]),\
92 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[14]),\
93 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[15]),\
94 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[16]),\
95 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[17]),\
96 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[18]),\
97 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[19]),\
98 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[20]),\
99 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[21]),\
100 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[22]),\
101 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[23]),\
102 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[24]),\
103 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[25]),\
104 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[26]),\
105 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[27]),\
106 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[28]),\
107 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[29]),\
108 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[30]),\
109 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[31])
110 
111 static struct evcnt intr_evcnt[] = {
112 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, cpu_irq_group, "spur"),
113 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, cpu_irq_group, "lev1"),
114 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, cpu_irq_group, "lev2"),
115 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, cpu_irq_group, "lev3"),
116 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, cpu_irq_group, "lev4"),
117 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, cpu_irq_group, "lev5"),
118 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, cpu_irq_group, "lev6"),
119 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, cpu_irq_group, "nmi"),
120 	PIC_EVCNTS(pic_irq_groups[0]),
121 	PIC_EVCNTS(pic_irq_groups[1]),
122 	PIC_EVCNTS(pic_irq_groups[2]),
123 	PIC_EVCNTS(pic_irq_groups[3]),
124 	PIC_EVCNTS(pic_irq_groups[4]),
125 	PIC_EVCNTS(pic_irq_groups[5]),
126 };
127 __CTASSERT(__arraycount(intr_evcnt) == NIRQ);
128 
129 #define	IHLIST_COUNT	 (NIRQ - IRQ_PIC_BASE)
130 #define	IRQ_TO_IHLIST(x) ((x) - IRQ_PIC_BASE)
131 #define	IRQ_TO_IPL(x)	 ((IRQ_TO_IHLIST(x) >> 5) + 1)
132 static LIST_HEAD(intrhand_list, intrhand) intrhands[IHLIST_COUNT];
133 
134 volatile unsigned int	intr_depth;	/* used in locore.s */
135 
136 static struct gfpic_softc *pics[NPIC];
137 
138 static inline int
139 ipl_to_pic(const int ipl)
140 {
141 	if (__predict_true(ipl >= 1 && ipl <= 6)) {
142 		return (ipl - 1);
143 	}
144 	return -1;
145 }
146 
147 /*
148  * intr_init --
149  *	Initialize the interrupt subsystem.
150  */
151 void
152 intr_init(void)
153 {
154 	int i;
155 
156 	for (i = 0; i < IHLIST_COUNT; i++) {
157 		LIST_INIT(&intrhands[i]);
158 	}
159 
160 	/*
161 	 * Attach the CPU IRQ event counters first.  The
162 	 * PIC event counters will be attached as the PICs
163 	 * are registered with us.
164 	 */
165 	for (i = 0; i < IRQ_PIC_BASE; i++) {
166 		evcnt_attach_static(&intr_evcnt[i]);
167 	}
168 }
169 
170 /*
171  * intr_register_pic --
172  *	Register a Goldfish PIC at the specified CPU IRQ.
173  */
174 void
175 intr_register_pic(device_t dev, int ipl)
176 {
177 	const int idx = ipl_to_pic(ipl);
178 
179 	KASSERT(idx >= 0);
180 
181 	const int base = IRQ_PIC_BASE + (idx * NIRQ_PER_PIC);
182 	int pirq;
183 
184 	KASSERT(pics[idx] == NULL);
185 	pics[idx] = device_private(dev);
186 
187 	for (pirq = 0; pirq < NIRQ_PER_PIC; pirq++) {
188 		intr_evcnt[base + pirq].ev_group = device_xname(dev);
189 		evcnt_attach_static(&intr_evcnt[base + pirq]);
190 	}
191 }
192 
193 /*
194  * intr_establish --
195  *	Establish an interrupt handler at the specified system IRQ.
196  *	XXX We don't do anything with the flags yet.
197  */
198 void *
199 intr_establish(int (*func)(void *), void *arg, int irq, int ipl,
200     int flags __unused)
201 {
202 	struct intrhand *ih;
203 	int s;
204 
205 	if (irq < IRQ_PIC_BASE || irq >= NIRQ) {
206 		return NULL;
207 	}
208 	if (ipl < IPL_BIO || ipl > IPL_SCHED) {
209 		return NULL;
210 	}
211 
212 	const int pic = IRQ_TO_PIC(irq);
213 	if (pics[pic] == NULL) {
214 		return NULL;
215 	}
216 
217 	ih = kmem_zalloc(sizeof(*ih), KM_SLEEP);
218 	ih->ih_func = func;
219 	ih->ih_arg = arg;
220 	ih->ih_irq = irq;
221 	ih->ih_evcnt = &intr_evcnt[irq];
222 
223 	s = splhigh();
224 	LIST_INSERT_HEAD(&intrhands[IRQ_TO_IHLIST(irq)], ih, ih_link);
225 	gfpic_enable(pics[pic], IRQ_TO_PIRQ(irq));
226 	splx(s);
227 
228 	return ih;
229 }
230 
231 /*
232  * intr_disestablish --
233  *	Remove an interrupt handler.
234  */
235 void
236 intr_disestablish(void *v)
237 {
238 	struct intrhand *ih = v;
239 	const int pic = IRQ_TO_PIC(ih->ih_irq);
240 	int s;
241 
242 	KASSERT(pics[pic] != NULL);
243 
244 	s = splhigh();
245 	LIST_REMOVE(ih, ih_link);
246 	if (LIST_EMPTY(&intrhands[IRQ_TO_IHLIST(ih->ih_irq)])) {
247 		gfpic_disable(pics[pic], IRQ_TO_PIRQ(ih->ih_irq));
248 	}
249 	splx(s);
250 }
251 
252 /*
253  * intr_string --
254  *	Return a string in the specified buffer describing the
255  *	interrupt.
256  */
257 const char *
258 intr_string(void *v, char *buf, size_t bufsize)
259 {
260 	struct intrhand *ih = v;
261 	const int ipl = IRQ_TO_IPL(ih->ih_irq);
262 
263 	snprintf(buf, bufsize, "%s %s (IPL %d)", ih->ih_evcnt->ev_group,
264 	    ih->ih_evcnt->ev_name, ipl);
265 	return buf;
266 }
267 
268 /* Auto-vectored interrupts start at vector 0x18. */
269 #define	VEC_AVINTR	0x18
270 
271 void
272 intr_dispatch(struct clockframe frame)
273 {
274 	const int ipl = VECO_TO_VECI(frame.cf_vo) - VEC_AVINTR;
275 	const int pic = ipl_to_pic(ipl);
276 	int pirq;
277 
278 	if (__predict_false(pic < 0)) {
279 		return;
280 	}
281 
282 	if (pics[pic] == NULL) {
283 		printf("Interrupt without a cause on CPU ipl %d\n", ipl);
284 		return;
285 	}
286 
287 	const int base = pic * NIRQ_PER_PIC;
288 	struct intrhand *ih;
289 	bool rv = false;
290 
291 	while ((pirq = gfpic_pending(pics[pic])) >= 0) {
292 		LIST_FOREACH(ih, &intrhands[base + pirq], ih_link) {
293 			void *arg = ih->ih_arg ? ih->ih_arg : &frame;
294 			if (ih->ih_func(arg)) {
295 				ih->ih_evcnt->ev_count++;
296 				rv = true;
297 			}
298 		}
299 	}
300 	if (!rv) {
301 		printf("Spurious interrupt on CPU ipl %d\n", ipl);
302 	}
303 }
304 
305 bool
306 cpu_intr_p(void)
307 {
308 	return intr_depth != 0;
309 }
310