xref: /openbsd-src/sys/arch/powerpc64/powerpc64/intr.c (revision 0a255fef3effb1c60f624a80f4e6d53ce037efe8)
1 /*	$OpenBSD: intr.c,v 1.12 2025/01/12 21:54:07 kettenis Exp $	*/
2 
3 /*
4  * Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org>
5  * Copyright (c) 2011 Dale Rahn <drahn@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/param.h>
21 #include <sys/malloc.h>
22 #include <sys/systm.h>
23 
24 #include <machine/atomic.h>
25 #include <machine/intr.h>
26 
27 #include <dev/ofw/openfirm.h>
28 
29 /* Dummy implementations. */
30 void	dummy_exi(struct trapframe *);
31 void	dummy_hvi(struct trapframe *);
32 void	*dummy_intr_establish(uint32_t, int, int, struct cpu_info *,
33 	    int (*)(void *), void *, const char *);
34 void	dummy_intr_send_ipi(void *);
35 void	dummy_setipl(int);
36 
37 /*
38  * The function pointers are overridden when the driver for the real
39  * interrupt controller attaches.
40  */
41 void	(*_exi)(struct trapframe *) = dummy_exi;
42 void	(*_hvi)(struct trapframe *) = dummy_hvi;
43 void	*(*_intr_establish)(uint32_t, int, int, struct cpu_info *,
44 	    int (*)(void *), void *, const char *) = dummy_intr_establish;
45 void	(*_intr_send_ipi)(void *) = dummy_intr_send_ipi;
46 void	(*_setipl)(int) = dummy_setipl;
47 
48 void
49 exi_intr(struct trapframe *frame)
50 {
51 	(*_exi)(frame);
52 }
53 
54 void
55 hvi_intr(struct trapframe *frame)
56 {
57 	(*_hvi)(frame);
58 }
59 
60 void *
61 intr_establish(uint32_t girq, int type, int level, struct cpu_info *ci,
62     int (*func)(void *), void *arg, const char *name)
63 {
64 	return (*_intr_establish)(girq, type, level, ci, func, arg, name);
65 }
66 
67 #define SI_TO_IRQBIT(x) (1 << (x))
68 uint32_t intr_smask[NIPL];
69 
70 void
71 intr_init(void)
72 {
73 	int i;
74 
75 	for (i = IPL_NONE; i <= IPL_HIGH; i++)  {
76 		intr_smask[i] = 0;
77 		if (i < IPL_SOFT)
78 			intr_smask[i] |= SI_TO_IRQBIT(SIR_SOFT);
79 		if (i < IPL_SOFTCLOCK)
80 			intr_smask[i] |= SI_TO_IRQBIT(SIR_CLOCK);
81 		if (i < IPL_SOFTNET)
82 			intr_smask[i] |= SI_TO_IRQBIT(SIR_NET);
83 		if (i < IPL_SOFTTTY)
84 			intr_smask[i] |= SI_TO_IRQBIT(SIR_TTY);
85 	}
86 }
87 
88 void
89 intr_do_pending(int new)
90 {
91 	struct cpu_info *ci = curcpu();
92 	u_long msr;
93 
94 	msr = intr_disable();
95 
96 #define DO_SOFTINT(si, ipl) \
97 	if ((ci->ci_ipending & intr_smask[new]) & SI_TO_IRQBIT(si)) {	\
98 		ci->ci_ipending &= ~SI_TO_IRQBIT(si);			\
99 		_setipl(ipl);						\
100 		intr_restore(msr);					\
101 		softintr_dispatch(si);					\
102 		msr = intr_disable();					\
103 	}
104 
105 	do {
106 		DO_SOFTINT(SIR_TTY, IPL_SOFTTTY);
107 		DO_SOFTINT(SIR_NET, IPL_SOFTNET);
108 		DO_SOFTINT(SIR_CLOCK, IPL_SOFTCLOCK);
109 		DO_SOFTINT(SIR_SOFT, IPL_SOFT);
110 	} while (ci->ci_ipending & intr_smask[new]);
111 
112 	intr_restore(msr);
113 }
114 
115 int
116 splraise(int new)
117 {
118 	struct cpu_info *ci = curcpu();
119 	int old = ci->ci_cpl;
120 
121 	if (new > old)
122 		(*_setipl)(new);
123 	return old;
124 }
125 
126 int
127 spllower(int new)
128 {
129 	struct cpu_info *ci = curcpu();
130 	int old = ci->ci_cpl;
131 
132 	if (new < old)
133 		(*_setipl)(new);
134 	return old;
135 }
136 
137 void
138 splx(int new)
139 {
140 	struct cpu_info *ci = curcpu();
141 
142 	if (ci->ci_dec_deferred && new < IPL_CLOCK) {
143 		mtdec(0);
144 		mtdec(UINT32_MAX);	/* raise DEC exception */
145 	}
146 
147 	if (ci->ci_ipending & intr_smask[new])
148 		intr_do_pending(new);
149 
150 	if (ci->ci_cpl != new)
151 		(*_setipl)(new);
152 }
153 
154 #ifdef DIAGNOSTIC
155 void
156 splassert_check(int wantipl, const char *func)
157 {
158 	int oldipl = curcpu()->ci_cpl;
159 
160 	if (oldipl < wantipl) {
161 		splassert_fail(wantipl, oldipl, func);
162 		/*
163 		 * If the splassert_ctl is set to not panic, raise the ipl
164 		 * in a feeble attempt to reduce damage.
165 		 */
166 		(*_setipl)(wantipl);
167 	}
168 
169 	if (wantipl == IPL_NONE && curcpu()->ci_idepth != 0) {
170 		splassert_fail(-1, curcpu()->ci_idepth, func);
171 	}
172 }
173 #endif
174 
175 void
176 dummy_exi(struct trapframe *frame)
177 {
178 	panic("Unhandled external interrupt");
179 }
180 
181 void
182 dummy_hvi(struct trapframe *frame)
183 {
184 	panic("Unhandled hypervisor virtualization interrupt");
185 }
186 
187 void *
188 dummy_intr_establish(uint32_t girq, int type, int level, struct cpu_info *ci,
189 	    int (*func)(void *), void *arg, const char *name)
190 {
191 	return NULL;
192 }
193 
194 void
195 dummy_setipl(int new)
196 {
197 	struct cpu_info *ci = curcpu();
198 	ci->ci_cpl = new;
199 }
200 
201 void
202 dummy_intr_send_ipi(void *cookie)
203 {
204 }
205 
206 /*
207  * FDT interrupt support.
208  */
209 
210 #define MAX_INTERRUPT_CELLS	4
211 
212 struct fdt_intr_handle {
213 	struct interrupt_controller *ih_ic;
214 	void *ih_ih;
215 };
216 
217 LIST_HEAD(, interrupt_controller) interrupt_controllers =
218 	LIST_HEAD_INITIALIZER(interrupt_controllers);
219 
220 void
221 interrupt_controller_register(struct interrupt_controller *ic)
222 {
223 	ic->ic_cells = OF_getpropint(ic->ic_node, "#interrupt-cells", 0);
224 	ic->ic_phandle = OF_getpropint(ic->ic_node, "phandle", 0);
225 	if (ic->ic_phandle == 0)
226 		return;
227 	KASSERT(ic->ic_cells <= MAX_INTERRUPT_CELLS);
228 
229 	LIST_INSERT_HEAD(&interrupt_controllers, ic, ic_list);
230 }
231 
232 /*
233  * Find the interrupt parent by walking up the tree.
234  */
235 uint32_t
236 fdt_intr_get_parent(int node)
237 {
238 	uint32_t phandle = 0;
239 
240 	while (node && !phandle) {
241 		phandle = OF_getpropint(node, "interrupt-parent", 0);
242 		node = OF_parent(node);
243 	}
244 
245 	return phandle;
246 }
247 
248 void *
249 fdt_intr_establish_idx_cpu(int node, int idx, int level, struct cpu_info *ci,
250     int (*func)(void *), void *cookie, char *name)
251 {
252 	struct interrupt_controller *ic;
253 	int i, len, ncells, extended = 1;
254 	uint32_t *cell, *cells, phandle;
255 	struct fdt_intr_handle *ih;
256 	void *val = NULL;
257 
258 	len = OF_getproplen(node, "interrupts-extended");
259 	if (len <= 0) {
260 		len = OF_getproplen(node, "interrupts");
261 		extended = 0;
262 	}
263 	if (len <= 0 || (len % sizeof(uint32_t) != 0))
264 		return NULL;
265 
266 	/* Old style. */
267 	if (!extended) {
268 		phandle = fdt_intr_get_parent(node);
269 		LIST_FOREACH(ic, &interrupt_controllers, ic_list) {
270 			if (ic->ic_phandle == phandle)
271 				break;
272 		}
273 
274 		if (ic == NULL)
275 			return NULL;
276 	}
277 
278 	cell = cells = malloc(len, M_TEMP, M_WAITOK);
279 	if (extended)
280 		OF_getpropintarray(node, "interrupts-extended", cells, len);
281 	else
282 		OF_getpropintarray(node, "interrupts", cells, len);
283 	ncells = len / sizeof(uint32_t);
284 
285 	for (i = 0; i <= idx && ncells > 0; i++) {
286 		if (extended) {
287 			phandle = cell[0];
288 
289 			/* Handle "empty" phandle reference. */
290 			if (phandle == 0) {
291 				cell++;
292 				ncells--;
293 				continue;
294 			}
295 
296 			LIST_FOREACH(ic, &interrupt_controllers, ic_list) {
297 				if (ic->ic_phandle == phandle)
298 					break;
299 			}
300 
301 			if (ic == NULL)
302 				break;
303 
304 			cell++;
305 			ncells--;
306 		}
307 
308 		if (i == idx && ncells >= ic->ic_cells && ic->ic_establish) {
309 			val = ic->ic_establish(ic->ic_cookie, cell, level,
310 			    ci, func, cookie, name);
311 			break;
312 		}
313 
314 		cell += ic->ic_cells;
315 		ncells -= ic->ic_cells;
316 	}
317 
318 	free(cells, M_TEMP, len);
319 
320 	if (val == NULL)
321 		return NULL;
322 
323 	ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK);
324 	ih->ih_ic = ic;
325 	ih->ih_ih = val;
326 
327 	return ih;
328 }
329 
330 void *
331 fdt_intr_establish_imap(int node, int *reg, int nreg, int level,
332     int (*func)(void *), void *cookie, char *name)
333 {
334 	return fdt_intr_establish_imap_cpu(node, reg, nreg, level, NULL,
335 	    func, cookie, name);
336 }
337 
338 void *
339 fdt_intr_establish_imap_cpu(int node, int *reg, int nreg, int level,
340     struct cpu_info *ci, int (*func)(void *), void *cookie, char *name)
341 {
342 	struct interrupt_controller *ic;
343 	struct fdt_intr_handle *ih;
344 	uint32_t *cell;
345 	uint32_t map_mask[4], *map;
346 	int len, acells, ncells;
347 	void *val = NULL;
348 
349 	if (nreg != sizeof(map_mask))
350 		return NULL;
351 
352 	if (OF_getpropintarray(node, "interrupt-map-mask", map_mask,
353 	    sizeof(map_mask)) != sizeof(map_mask))
354 		return NULL;
355 
356 	len = OF_getproplen(node, "interrupt-map");
357 	if (len <= 0)
358 		return NULL;
359 
360 	map = malloc(len, M_DEVBUF, M_WAITOK);
361 	OF_getpropintarray(node, "interrupt-map", map, len);
362 
363 	cell = map;
364 	ncells = len / sizeof(uint32_t);
365 	while (ncells > 5) {
366 		LIST_FOREACH(ic, &interrupt_controllers, ic_list) {
367 			if (ic->ic_phandle == cell[4])
368 				break;
369 		}
370 
371 		if (ic == NULL)
372 			break;
373 
374 		acells = OF_getpropint(ic->ic_node, "#address-cells", 0);
375 		if (ncells >= (5 + acells + ic->ic_cells) &&
376 		    (reg[0] & map_mask[0]) == cell[0] &&
377 		    (reg[1] & map_mask[1]) == cell[1] &&
378 		    (reg[2] & map_mask[2]) == cell[2] &&
379 		    (reg[3] & map_mask[3]) == cell[3] &&
380 		    ic->ic_establish) {
381 			val = ic->ic_establish(ic->ic_cookie, &cell[5 + acells],
382 			    level, ci, func, cookie, name);
383 			break;
384 		}
385 
386 		cell += (5 + acells + ic->ic_cells);
387 		ncells -= (5 + acells + ic->ic_cells);
388 	}
389 
390 	if (val == NULL) {
391 		free(map, M_DEVBUF, len);
392 		return NULL;
393 	}
394 
395 	ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK);
396 	ih->ih_ic = ic;
397 	ih->ih_ih = val;
398 
399 	free(map, M_DEVBUF, len);
400 	return ih;
401 }
402 
403 void
404 fdt_intr_disestablish(void *cookie)
405 {
406 	panic("%s: not implemented", __func__);
407 }
408 
409 #ifdef MULTIPROCESSOR
410 
411 void
412 intr_send_ipi(struct cpu_info *ci, int reason)
413 {
414 	struct fdt_intr_handle *ih = ci->ci_ipi;
415 
416 	if (ci == curcpu() && reason == IPI_NOP)
417 		return;
418 
419 	if (reason != IPI_NOP)
420 		atomic_setbits_int(&ci->ci_ipi_reason, reason);
421 
422 	if (ih && ih->ih_ic)
423 		ih->ih_ic->ic_send_ipi(ih->ih_ih);
424 }
425 
426 #endif
427