xref: /netbsd-src/sys/arch/mips/cavium/octeon_intr.c (revision efc604cfaf23cfe18df346637435f9904b99ff04)
1*efc604cfSriastradh /*	$NetBSD: octeon_intr.c,v 1.27 2022/04/09 23:34:40 riastradh Exp $	*/
2f693c922Shikaru /*
3f693c922Shikaru  * Copyright 2001, 2002 Wasabi Systems, Inc.
4f693c922Shikaru  * All rights reserved.
5f693c922Shikaru  *
6f693c922Shikaru  * Written by Jason R. Thorpe and Simon Burge for Wasabi Systems, Inc.
7f693c922Shikaru  *
8f693c922Shikaru  * Redistribution and use in source and binary forms, with or without
9f693c922Shikaru  * modification, are permitted provided that the following conditions
10f693c922Shikaru  * are met:
11f693c922Shikaru  * 1. Redistributions of source code must retain the above copyright
12f693c922Shikaru  *    notice, this list of conditions and the following disclaimer.
13f693c922Shikaru  * 2. Redistributions in binary form must reproduce the above copyright
14f693c922Shikaru  *    notice, this list of conditions and the following disclaimer in the
15f693c922Shikaru  *    documentation and/or other materials provided with the distribution.
16f693c922Shikaru  * 3. All advertising materials mentioning features or use of this software
17f693c922Shikaru  *    must display the following acknowledgement:
18f693c922Shikaru  *      This product includes software developed for the NetBSD Project by
19f693c922Shikaru  *      Wasabi Systems, Inc.
20f693c922Shikaru  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
21f693c922Shikaru  *    or promote products derived from this software without specific prior
22f693c922Shikaru  *    written permission.
23f693c922Shikaru  *
24f693c922Shikaru  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
25f693c922Shikaru  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26f693c922Shikaru  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27f693c922Shikaru  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
28f693c922Shikaru  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29f693c922Shikaru  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30f693c922Shikaru  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31f693c922Shikaru  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32f693c922Shikaru  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33f693c922Shikaru  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34f693c922Shikaru  * POSSIBILITY OF SUCH DAMAGE.
35f693c922Shikaru  */
36f693c922Shikaru 
37f693c922Shikaru /*
38f693c922Shikaru  * Platform-specific interrupt support for the MIPS Malta.
39f693c922Shikaru  */
40f693c922Shikaru 
414ac893a7Sskrll #include "opt_multiprocessor.h"
424ac893a7Sskrll 
43847a1893Smatt #include "cpunode.h"
44f693c922Shikaru #define __INTR_PRIVATE
45f693c922Shikaru 
46f693c922Shikaru #include <sys/cdefs.h>
47*efc604cfSriastradh __KERNEL_RCSID(0, "$NetBSD: octeon_intr.c,v 1.27 2022/04/09 23:34:40 riastradh Exp $");
48f693c922Shikaru 
49f693c922Shikaru #include <sys/param.h>
50f693c922Shikaru #include <sys/cpu.h>
51f693c922Shikaru #include <sys/systm.h>
52f693c922Shikaru #include <sys/device.h>
53f693c922Shikaru #include <sys/intr.h>
54f693c922Shikaru #include <sys/kernel.h>
552d299731Smatt #include <sys/kmem.h>
562d299731Smatt #include <sys/atomic.h>
577bd5abd3Sriastradh #include <sys/xcall.h>
58f693c922Shikaru 
59f693c922Shikaru #include <lib/libkern/libkern.h>
60f693c922Shikaru 
61f693c922Shikaru #include <mips/locore.h>
62f693c922Shikaru 
63f693c922Shikaru #include <mips/cavium/dev/octeon_ciureg.h>
64f693c922Shikaru #include <mips/cavium/octeonvar.h>
65f693c922Shikaru 
66f693c922Shikaru /*
674b99df67Ssimonb  * XXX:
684b99df67Ssimonb  * Force all interrupts (except clock intrs and IPIs) to be routed
694b99df67Ssimonb  * through cpu0 until MP on MIPS is more stable.
704b99df67Ssimonb  */
714b99df67Ssimonb #define	OCTEON_CPU0_INTERRUPTS
724b99df67Ssimonb 
734b99df67Ssimonb 
744b99df67Ssimonb /*
75f693c922Shikaru  * This is a mask of bits to clear in the SR when we go to a
76f693c922Shikaru  * given hardware interrupt priority level.
77f693c922Shikaru  */
78f693c922Shikaru static const struct ipl_sr_map octeon_ipl_sr_map = {
79f693c922Shikaru     .sr_bits = {
80f693c922Shikaru 	[IPL_NONE] =		0,
81f693c922Shikaru 	[IPL_SOFTCLOCK] =	MIPS_SOFT_INT_MASK_0,
82f693c922Shikaru 	[IPL_SOFTNET] =		MIPS_SOFT_INT_MASK,
83f693c922Shikaru 	[IPL_VM] =		MIPS_SOFT_INT_MASK | MIPS_INT_MASK_0,
84f693c922Shikaru 	[IPL_SCHED] =		MIPS_SOFT_INT_MASK | MIPS_INT_MASK_0
857a18db21Sskrll 				    | MIPS_INT_MASK_1 | MIPS_INT_MASK_5,
862d299731Smatt 	[IPL_DDB] =		MIPS_SOFT_INT_MASK | MIPS_INT_MASK_0
872d299731Smatt 				    | MIPS_INT_MASK_1 | MIPS_INT_MASK_5,
88f693c922Shikaru 	[IPL_HIGH] =		MIPS_INT_MASK,
89f693c922Shikaru     },
90f693c922Shikaru };
91f693c922Shikaru 
924f2a7f3aSsimonb static const char * octeon_intrnames[NIRQS] = {
93f693c922Shikaru 	"workq 0",
94f693c922Shikaru 	"workq 1",
95f693c922Shikaru 	"workq 2",
96f693c922Shikaru 	"workq 3",
97f693c922Shikaru 	"workq 4",
98f693c922Shikaru 	"workq 5",
99f693c922Shikaru 	"workq 6",
100f693c922Shikaru 	"workq 7",
101f693c922Shikaru 	"workq 8",
102f693c922Shikaru 	"workq 9",
103f693c922Shikaru 	"workq 10",
104f693c922Shikaru 	"workq 11",
105f693c922Shikaru 	"workq 12",
106f693c922Shikaru 	"workq 13",
107f693c922Shikaru 	"workq 14",
108f693c922Shikaru 	"workq 15",
109f693c922Shikaru 	"gpio 0",
110f693c922Shikaru 	"gpio 1",
111f693c922Shikaru 	"gpio 2",
112f693c922Shikaru 	"gpio 3",
113f693c922Shikaru 	"gpio 4",
114f693c922Shikaru 	"gpio 5",
115f693c922Shikaru 	"gpio 6",
116f693c922Shikaru 	"gpio 7",
117f693c922Shikaru 	"gpio 8",
118f693c922Shikaru 	"gpio 9",
119f693c922Shikaru 	"gpio 10",
120f693c922Shikaru 	"gpio 11",
121f693c922Shikaru 	"gpio 12",
122f693c922Shikaru 	"gpio 13",
123f693c922Shikaru 	"gpio 14",
124f693c922Shikaru 	"gpio 15",
125f693c922Shikaru 	"mbox 0-15",
126f693c922Shikaru 	"mbox 16-31",
127f693c922Shikaru 	"uart 0",
128f693c922Shikaru 	"uart 1",
129f693c922Shikaru 	"pci inta",
130f693c922Shikaru 	"pci intb",
131f693c922Shikaru 	"pci intc",
132f693c922Shikaru 	"pci intd",
133f693c922Shikaru 	"pci msi 0-15",
134f693c922Shikaru 	"pci msi 16-31",
135f693c922Shikaru 	"pci msi 32-47",
136f693c922Shikaru 	"pci msi 48-63",
137f693c922Shikaru 	"wdog summary",
138f693c922Shikaru 	"twsi",
139f693c922Shikaru 	"rml",
140f693c922Shikaru 	"trace",
141f693c922Shikaru 	"gmx drop",
142f693c922Shikaru 	"reserved",
143f693c922Shikaru 	"ipd drop",
144f693c922Shikaru 	"reserved",
145f693c922Shikaru 	"timer 0",
146f693c922Shikaru 	"timer 1",
147f693c922Shikaru 	"timer 2",
148f693c922Shikaru 	"timer 3",
149f693c922Shikaru 	"usb",
150f693c922Shikaru 	"pcm/tdm",
151f693c922Shikaru 	"mpi/spi",
152f693c922Shikaru 	"reserved",
153f693c922Shikaru 	"reserved",
154f693c922Shikaru 	"reserved",
155f693c922Shikaru 	"reserved",
156f693c922Shikaru 	"reserved",
157f693c922Shikaru };
158f693c922Shikaru 
159f693c922Shikaru struct octeon_intrhand {
160f693c922Shikaru 	int (*ih_func)(void *);
161f693c922Shikaru 	void *ih_arg;
162f693c922Shikaru 	int ih_irq;
163f693c922Shikaru 	int ih_ipl;
164f693c922Shikaru };
165f693c922Shikaru 
1662d299731Smatt #ifdef MULTIPROCESSOR
1672d299731Smatt static int octeon_send_ipi(struct cpu_info *, int);
1682d299731Smatt static int octeon_ipi_intr(void *);
1692d299731Smatt 
170ccc08332Sskrll static struct octeon_intrhand ipi_intrhands[2] = {
1712d299731Smatt 	[0] = {
1722d299731Smatt 		.ih_func = octeon_ipi_intr,
1732d299731Smatt 		.ih_arg = (void *)(uintptr_t)__BITS(15,0),
17482a25ec3Ssimonb 		.ih_irq = CIU_INT_MBOX_15_0,
1752d299731Smatt 		.ih_ipl = IPL_HIGH,
1762d299731Smatt 	},
1774a584abcSjmcneill 	[1] = {
1784a584abcSjmcneill 		.ih_func = octeon_ipi_intr,
1794a584abcSjmcneill 		.ih_arg = (void *)(uintptr_t)__BITS(31,16),
1804a584abcSjmcneill 		.ih_irq = CIU_INT_MBOX_31_16,
1814a584abcSjmcneill 		.ih_ipl = IPL_SCHED,
1824a584abcSjmcneill 	},
183f693c922Shikaru };
1844a584abcSjmcneill 
1854a584abcSjmcneill static int ipi_prio[NIPIS] = {
1864a584abcSjmcneill 	[IPI_NOP] = IPL_HIGH,
1874a584abcSjmcneill 	[IPI_AST] = IPL_HIGH,
1884a584abcSjmcneill 	[IPI_SHOOTDOWN] = IPL_SCHED,
1894a584abcSjmcneill 	[IPI_SYNCICACHE] = IPL_HIGH,
1904a584abcSjmcneill 	[IPI_KPREEMPT] = IPL_HIGH,
1914a584abcSjmcneill 	[IPI_SUSPEND] = IPL_HIGH,
1924a584abcSjmcneill 	[IPI_HALT] = IPL_HIGH,
1934a584abcSjmcneill 	[IPI_XCALL] = IPL_HIGH,
1944a584abcSjmcneill 	[IPI_GENERIC] = IPL_HIGH,
1954a584abcSjmcneill 	[IPI_WDOG] = IPL_HIGH,
1964a584abcSjmcneill };
1974a584abcSjmcneill 
198f693c922Shikaru #endif
199f693c922Shikaru 
2004f2a7f3aSsimonb static struct octeon_intrhand *octciu_intrs[NIRQS] = {
2012d299731Smatt #ifdef MULTIPROCESSOR
20282a25ec3Ssimonb 	[CIU_INT_MBOX_15_0] = &ipi_intrhands[0],
2034a584abcSjmcneill 	[CIU_INT_MBOX_31_16] = &ipi_intrhands[1],
2042d299731Smatt #endif
2052d299731Smatt };
2062d299731Smatt 
2074f2a7f3aSsimonb static kmutex_t octeon_intr_lock;
2082d299731Smatt 
209793877b0Sjmcneill #if defined(MULTIPROCESSOR)
210793877b0Sjmcneill #define	OCTEON_NCPU	MAXCPUS
211793877b0Sjmcneill #else
212793877b0Sjmcneill #define	OCTEON_NCPU	1
213793877b0Sjmcneill #endif
214793877b0Sjmcneill 
215793877b0Sjmcneill struct cpu_softc octeon_cpu_softc[OCTEON_NCPU];
216793877b0Sjmcneill 
217793877b0Sjmcneill static void
octeon_intr_setup(void)218793877b0Sjmcneill octeon_intr_setup(void)
219793877b0Sjmcneill {
220793877b0Sjmcneill 	struct cpu_softc *cpu;
221793877b0Sjmcneill 	int cpunum;
222793877b0Sjmcneill 
2232d299731Smatt #define X(a)	MIPS_PHYS_TO_XKPHYS(OCTEON_CCA_NONE, (a))
2242d299731Smatt 
225793877b0Sjmcneill 	for (cpunum = 0; cpunum < OCTEON_NCPU; cpunum++) {
226793877b0Sjmcneill 		cpu = &octeon_cpu_softc[cpunum];
2272d299731Smatt 
228793877b0Sjmcneill 		cpu->cpu_ip2_sum0 = X(CIU_IP2_SUM0(cpunum));
229793877b0Sjmcneill 		cpu->cpu_ip3_sum0 = X(CIU_IP3_SUM0(cpunum));
230793877b0Sjmcneill 		cpu->cpu_ip4_sum0 = X(CIU_IP4_SUM0(cpunum));
231d911ef3aSjmcneill 
232793877b0Sjmcneill 		cpu->cpu_int_sum1 = X(CIU_INT_SUM1);
2332d299731Smatt 
234793877b0Sjmcneill 		cpu->cpu_ip2_en[0] = X(CIU_IP2_EN0(cpunum));
235793877b0Sjmcneill 		cpu->cpu_ip3_en[0] = X(CIU_IP3_EN0(cpunum));
236793877b0Sjmcneill 		cpu->cpu_ip4_en[0] = X(CIU_IP4_EN0(cpunum));
2372d299731Smatt 
238793877b0Sjmcneill 		cpu->cpu_ip2_en[1] = X(CIU_IP2_EN1(cpunum));
239793877b0Sjmcneill 		cpu->cpu_ip3_en[1] = X(CIU_IP3_EN1(cpunum));
240793877b0Sjmcneill 		cpu->cpu_ip4_en[1] = X(CIU_IP4_EN1(cpunum));
2412d299731Smatt 
242793877b0Sjmcneill 		cpu->cpu_wdog = X(CIU_WDOG(cpunum));
243793877b0Sjmcneill 		cpu->cpu_pp_poke = X(CIU_PP_POKE(cpunum));
244847a1893Smatt 
2452d299731Smatt #ifdef MULTIPROCESSOR
246793877b0Sjmcneill 		cpu->cpu_mbox_set = X(CIU_MBOX_SET(cpunum));
247793877b0Sjmcneill 		cpu->cpu_mbox_clr = X(CIU_MBOX_CLR(cpunum));
2482d299731Smatt #endif
249847a1893Smatt 	}
250847a1893Smatt 
2512d299731Smatt #undef X
2522d299731Smatt 
253793877b0Sjmcneill }
254793877b0Sjmcneill 
255f693c922Shikaru void
octeon_intr_init(struct cpu_info * ci)2562d299731Smatt octeon_intr_init(struct cpu_info *ci)
257f693c922Shikaru {
2582d299731Smatt 	const int cpunum = cpu_index(ci);
259793877b0Sjmcneill 	struct cpu_softc *cpu = &octeon_cpu_softc[cpunum];
2602d299731Smatt 	const char * const xname = cpu_name(ci);
261e78c0566Sjmcneill 	int bank;
2622d299731Smatt 
263793877b0Sjmcneill 	cpu->cpu_ci = ci;
264793877b0Sjmcneill 	ci->ci_softc = cpu;
265793877b0Sjmcneill 
266793877b0Sjmcneill 	KASSERT(cpunum == ci->ci_cpuid);
267f693c922Shikaru 
2682d299731Smatt 	if (ci->ci_cpuid == 0) {
269847a1893Smatt 		ipl_sr_map = octeon_ipl_sr_map;
2702d299731Smatt 		mutex_init(&octeon_intr_lock, MUTEX_DEFAULT, IPL_HIGH);
2712d299731Smatt #ifdef MULTIPROCESSOR
2722d299731Smatt 		mips_locoresw.lsw_send_ipi = octeon_send_ipi;
2732d299731Smatt #endif
274793877b0Sjmcneill 
275793877b0Sjmcneill 		octeon_intr_setup();
276f693c922Shikaru 	}
2772d299731Smatt 
2782d299731Smatt #ifdef MULTIPROCESSOR
2792d299731Smatt 	// Enable the IPIs
2807d9c74a0Sjmcneill 	cpu->cpu_ip4_enable[0] |= __BIT(CIU_INT_MBOX_15_0);
2814a584abcSjmcneill 	cpu->cpu_ip3_enable[0] |= __BIT(CIU_INT_MBOX_31_16);
2822d299731Smatt #endif
2832d299731Smatt 
284e78c0566Sjmcneill 	if (ci->ci_dev) {
285e78c0566Sjmcneill 		for (bank = 0; bank < NBANKS; bank++) {
286847a1893Smatt 			aprint_verbose_dev(ci->ci_dev,
287e78c0566Sjmcneill 			    "enabling intr masks %u "
288e78c0566Sjmcneill 			    " %#"PRIx64"/%#"PRIx64"/%#"PRIx64"\n",
289e78c0566Sjmcneill 			    bank,
290793877b0Sjmcneill 			    cpu->cpu_ip2_enable[bank],
291793877b0Sjmcneill 			    cpu->cpu_ip3_enable[bank],
292793877b0Sjmcneill 			    cpu->cpu_ip4_enable[bank]);
293e78c0566Sjmcneill 		}
294e78c0566Sjmcneill 	}
295847a1893Smatt 
296e78c0566Sjmcneill 	for (bank = 0; bank < NBANKS; bank++) {
297793877b0Sjmcneill 		mips3_sd(cpu->cpu_ip2_en[bank], cpu->cpu_ip2_enable[bank]);
298793877b0Sjmcneill 		mips3_sd(cpu->cpu_ip3_en[bank], cpu->cpu_ip3_enable[bank]);
299793877b0Sjmcneill 		mips3_sd(cpu->cpu_ip4_en[bank], cpu->cpu_ip4_enable[bank]);
300e78c0566Sjmcneill 	}
3012d299731Smatt 
3022d299731Smatt #ifdef MULTIPROCESSOR
303d7e78fcfSmatt 	mips3_sd(cpu->cpu_mbox_clr, __BITS(31,0));
3042d299731Smatt #endif
305f693c922Shikaru 
306d911ef3aSjmcneill 	for (int i = 0; i < NIRQS; i++) {
307d911ef3aSjmcneill 		if (octeon_intrnames[i] == NULL)
308d911ef3aSjmcneill 			octeon_intrnames[i] = kmem_asprintf("irq %d", i);
3092d299731Smatt 		evcnt_attach_dynamic(&cpu->cpu_intr_evs[i],
3102d299731Smatt 		    EVCNT_TYPE_INTR, NULL, xname, octeon_intrnames[i]);
311f693c922Shikaru 	}
312f693c922Shikaru }
313f693c922Shikaru 
314f693c922Shikaru void
octeon_cal_timer(int corefreq)315f693c922Shikaru octeon_cal_timer(int corefreq)
316f693c922Shikaru {
317f693c922Shikaru 	/* Compute the number of cycles per second. */
318f693c922Shikaru 	curcpu()->ci_cpu_freq = corefreq;
319f693c922Shikaru 
320f693c922Shikaru 	/* Compute the number of ticks for hz. */
321f693c922Shikaru 	curcpu()->ci_cycles_per_hz = (curcpu()->ci_cpu_freq + hz / 2) / hz;
322f693c922Shikaru 
323f693c922Shikaru 	/* Compute the delay divisor and reciprical. */
324f693c922Shikaru 	curcpu()->ci_divisor_delay =
325f693c922Shikaru 	    ((curcpu()->ci_cpu_freq + 500000) / 1000000);
326f693c922Shikaru #if 0
327f693c922Shikaru 	MIPS_SET_CI_RECIPRICAL(curcpu());
328f693c922Shikaru #endif
329f693c922Shikaru 
330f693c922Shikaru 	mips3_cp0_count_write(0);
331f693c922Shikaru 	mips3_cp0_compare_write(0);
332f693c922Shikaru }
333f693c922Shikaru 
334f693c922Shikaru void *
octeon_intr_establish(int irq,int ipl,int (* func)(void *),void * arg)3352d299731Smatt octeon_intr_establish(int irq, int ipl, int (*func)(void *), void *arg)
336f693c922Shikaru {
337f693c922Shikaru 	struct octeon_intrhand *ih;
338793877b0Sjmcneill 	struct cpu_softc *cpu;
3394b99df67Ssimonb #ifndef OCTEON_CPU0_INTERRUPTS
340793877b0Sjmcneill 	int cpunum;
3414b99df67Ssimonb #endif
342f693c922Shikaru 
343f693c922Shikaru 	if (irq >= NIRQS)
344f693c922Shikaru 		panic("octeon_intr_establish: bogus IRQ %d", irq);
3452d299731Smatt 	if (ipl < IPL_VM)
3462d299731Smatt 		panic("octeon_intr_establish: bogus IPL %d", ipl);
347f693c922Shikaru 
3482d299731Smatt 	ih = kmem_zalloc(sizeof(*ih), KM_NOSLEEP);
349f693c922Shikaru 	if (ih == NULL)
350f693c922Shikaru 		return (NULL);
351f693c922Shikaru 
352f693c922Shikaru 	ih->ih_func = func;
353f693c922Shikaru 	ih->ih_arg = arg;
354f693c922Shikaru 	ih->ih_irq = irq;
3552d299731Smatt 	ih->ih_ipl = ipl;
356f693c922Shikaru 
3572d299731Smatt 	mutex_enter(&octeon_intr_lock);
358f693c922Shikaru 
359f693c922Shikaru 	/*
3602d299731Smatt 	 * First, make it known.
361f693c922Shikaru 	 */
3623f508e4dSsimonb 	KASSERTMSG(octciu_intrs[irq] == NULL, "irq %d in use! (%p)",
3633f508e4dSsimonb 	    irq, octciu_intrs[irq]);
3642d299731Smatt 
3653270682cSriastradh 	atomic_store_release(&octciu_intrs[irq], ih);
366f693c922Shikaru 
367f693c922Shikaru 	/*
368f693c922Shikaru 	 * Now enable it.
369f693c922Shikaru 	 */
370d911ef3aSjmcneill 	const int bank = irq / 64;
371d911ef3aSjmcneill 	const uint64_t irq_mask = __BIT(irq % 64);
372f693c922Shikaru 
3732d299731Smatt 	switch (ipl) {
3742d299731Smatt 	case IPL_VM:
375793877b0Sjmcneill 		cpu = &octeon_cpu_softc[0];
376793877b0Sjmcneill 		cpu->cpu_ip2_enable[bank] |= irq_mask;
377793877b0Sjmcneill 		mips3_sd(cpu->cpu_ip2_en[bank], cpu->cpu_ip2_enable[bank]);
378f693c922Shikaru 		break;
379f693c922Shikaru 
3802d299731Smatt 	case IPL_SCHED:
3814b99df67Ssimonb #ifdef OCTEON_CPU0_INTERRUPTS
3824b99df67Ssimonb 		cpu = &octeon_cpu_softc[0];
3834b99df67Ssimonb 		cpu->cpu_ip3_enable[bank] |= irq_mask;
3844b99df67Ssimonb 		mips3_sd(cpu->cpu_ip3_en[bank], cpu->cpu_ip3_enable[bank]);
3854b99df67Ssimonb #else	/* OCTEON_CPU0_INTERRUPTS */
386793877b0Sjmcneill 		for (cpunum = 0; cpunum < OCTEON_NCPU; cpunum++) {
387793877b0Sjmcneill 			cpu = &octeon_cpu_softc[cpunum];
388793877b0Sjmcneill 			if (cpu->cpu_ci == NULL)
389793877b0Sjmcneill 				break;
390793877b0Sjmcneill 			cpu->cpu_ip3_enable[bank] |= irq_mask;
391793877b0Sjmcneill 			mips3_sd(cpu->cpu_ip3_en[bank], cpu->cpu_ip3_enable[bank]);
392793877b0Sjmcneill 		}
3934b99df67Ssimonb #endif	/* OCTEON_CPU0_INTERRUPTS */
3942d299731Smatt 		break;
3952d299731Smatt 
3962d299731Smatt 	case IPL_DDB:
3972d299731Smatt 	case IPL_HIGH:
3984b99df67Ssimonb #ifdef OCTEON_CPU0_INTERRUPTS
3994b99df67Ssimonb 		cpu = &octeon_cpu_softc[0];
4004b99df67Ssimonb 		cpu->cpu_ip4_enable[bank] |= irq_mask;
4014b99df67Ssimonb 		mips3_sd(cpu->cpu_ip4_en[bank], cpu->cpu_ip4_enable[bank]);
4024b99df67Ssimonb #else	/* OCTEON_CPU0_INTERRUPTS */
403793877b0Sjmcneill 		for (cpunum = 0; cpunum < OCTEON_NCPU; cpunum++) {
404793877b0Sjmcneill 			cpu = &octeon_cpu_softc[cpunum];
405793877b0Sjmcneill 			if (cpu->cpu_ci == NULL)
406793877b0Sjmcneill 				break;
407793877b0Sjmcneill 			cpu->cpu_ip4_enable[bank] |= irq_mask;
408793877b0Sjmcneill 			mips3_sd(cpu->cpu_ip4_en[bank], cpu->cpu_ip4_enable[bank]);
409793877b0Sjmcneill 		}
4104b99df67Ssimonb #endif	/* OCTEON_CPU0_INTERRUPTS */
411f693c922Shikaru 		break;
412f693c922Shikaru 	}
413f693c922Shikaru 
4142d299731Smatt 	mutex_exit(&octeon_intr_lock);
4152d299731Smatt 
4162d299731Smatt 	return ih;
417f693c922Shikaru }
418f693c922Shikaru 
419f693c922Shikaru void
octeon_intr_disestablish(void * cookie)420f693c922Shikaru octeon_intr_disestablish(void *cookie)
421f693c922Shikaru {
4222d299731Smatt 	struct octeon_intrhand * const ih = cookie;
423793877b0Sjmcneill 	struct cpu_softc *cpu;
4242d299731Smatt 	const int irq = ih->ih_irq & (NIRQS-1);
4252d299731Smatt 	const int ipl = ih->ih_ipl;
426793877b0Sjmcneill 	int cpunum;
427f693c922Shikaru 
4282d299731Smatt 	mutex_enter(&octeon_intr_lock);
429f693c922Shikaru 
430f693c922Shikaru 	/*
4312d299731Smatt 	 * First disable it.
432f693c922Shikaru 	 */
433d911ef3aSjmcneill 	const int bank = irq / 64;
434d911ef3aSjmcneill 	const uint64_t irq_mask = ~__BIT(irq % 64);
4352d299731Smatt 
4362d299731Smatt 	switch (ipl) {
4372d299731Smatt 	case IPL_VM:
438793877b0Sjmcneill 		cpu = &octeon_cpu_softc[0];
439793877b0Sjmcneill 		cpu->cpu_ip2_enable[bank] &= ~irq_mask;
440793877b0Sjmcneill 		mips3_sd(cpu->cpu_ip2_en[bank], cpu->cpu_ip2_enable[bank]);
4412d299731Smatt 		break;
4422d299731Smatt 
4432d299731Smatt 	case IPL_SCHED:
444793877b0Sjmcneill 		for (cpunum = 0; cpunum < OCTEON_NCPU; cpunum++) {
445793877b0Sjmcneill 			cpu = &octeon_cpu_softc[cpunum];
446793877b0Sjmcneill 			if (cpu->cpu_ci == NULL)
447793877b0Sjmcneill 				break;
448793877b0Sjmcneill 			cpu->cpu_ip3_enable[bank] &= ~irq_mask;
449793877b0Sjmcneill 			mips3_sd(cpu->cpu_ip3_en[bank], cpu->cpu_ip3_enable[bank]);
450793877b0Sjmcneill 		}
4512d299731Smatt 		break;
4522d299731Smatt 
4532d299731Smatt 	case IPL_DDB:
4542d299731Smatt 	case IPL_HIGH:
455793877b0Sjmcneill 		for (cpunum = 0; cpunum < OCTEON_NCPU; cpunum++) {
456793877b0Sjmcneill 			cpu = &octeon_cpu_softc[cpunum];
457793877b0Sjmcneill 			if (cpu->cpu_ci == NULL)
458793877b0Sjmcneill 				break;
459793877b0Sjmcneill 			cpu->cpu_ip4_enable[bank] &= ~irq_mask;
460793877b0Sjmcneill 			mips3_sd(cpu->cpu_ip4_en[bank], cpu->cpu_ip4_enable[bank]);
461793877b0Sjmcneill 		}
4622d299731Smatt 		break;
4632d299731Smatt 	}
464f693c922Shikaru 
4657bd5abd3Sriastradh 	atomic_store_relaxed(&octciu_intrs[irq], NULL);
466f693c922Shikaru 
4672d299731Smatt 	mutex_exit(&octeon_intr_lock);
468f693c922Shikaru 
4697bd5abd3Sriastradh 	/*
4707bd5abd3Sriastradh 	 * Wait until the interrupt handler is no longer running on all
4717bd5abd3Sriastradh 	 * CPUs before freeing ih and returning.
4727bd5abd3Sriastradh 	 */
4737bd5abd3Sriastradh 	xc_barrier(0);
4742d299731Smatt 	kmem_free(ih, sizeof(*ih));
475f693c922Shikaru }
476f693c922Shikaru 
477f693c922Shikaru void
octeon_iointr(int ipl,vaddr_t pc,uint32_t ipending)478f693c922Shikaru octeon_iointr(int ipl, vaddr_t pc, uint32_t ipending)
479f693c922Shikaru {
4802d299731Smatt 	struct cpu_info * const ci = curcpu();
4812d299731Smatt 	struct cpu_softc * const cpu = ci->ci_softc;
482d911ef3aSjmcneill 	int bank;
4832d299731Smatt 
484847a1893Smatt 	KDASSERT(mips_cp0_status_read() & MIPS_SR_INT_IE);
4852d299731Smatt 	KASSERT((ipending & ~MIPS_INT_MASK) == 0);
4862d299731Smatt 	KASSERT(ipending & MIPS_HARD_INT_MASK);
487d911ef3aSjmcneill 	uint64_t hwpend[2] = { 0, 0 };
488d911ef3aSjmcneill 
489d911ef3aSjmcneill 	const uint64_t sum1 = mips3_ld(cpu->cpu_int_sum1);
490f693c922Shikaru 
4912d299731Smatt 	if (ipending & MIPS_INT_MASK_2) {
492793877b0Sjmcneill 		hwpend[0] = mips3_ld(cpu->cpu_ip4_sum0)
493793877b0Sjmcneill 		    & cpu->cpu_ip4_enable[0];
494793877b0Sjmcneill 		hwpend[1] = sum1 & cpu->cpu_ip4_enable[1];
4952d299731Smatt 	} else if (ipending & MIPS_INT_MASK_1) {
496793877b0Sjmcneill 		hwpend[0] = mips3_ld(cpu->cpu_ip3_sum0)
497793877b0Sjmcneill 		    & cpu->cpu_ip3_enable[0];
498793877b0Sjmcneill 		hwpend[1] = sum1 & cpu->cpu_ip3_enable[1];
4992d299731Smatt 	} else if (ipending & MIPS_INT_MASK_0) {
500793877b0Sjmcneill 		hwpend[0] = mips3_ld(cpu->cpu_ip2_sum0)
501793877b0Sjmcneill 		    & cpu->cpu_ip2_enable[0];
502793877b0Sjmcneill 		hwpend[1] = sum1 & cpu->cpu_ip2_enable[1];
5032d299731Smatt 	} else {
5042d299731Smatt 		panic("octeon_iointr: unexpected ipending %#x", ipending);
505f693c922Shikaru 	}
506d911ef3aSjmcneill 	for (bank = 0; bank <= 1; bank++) {
507d911ef3aSjmcneill 		while (hwpend[bank] != 0) {
508d911ef3aSjmcneill 			const int bit = ffs64(hwpend[bank]) - 1;
509d911ef3aSjmcneill 			const int irq = (bank * 64) + bit;
510d911ef3aSjmcneill 			hwpend[bank] &= ~__BIT(bit);
5112d299731Smatt 
5127bd5abd3Sriastradh 			struct octeon_intrhand * const ih =
5133270682cSriastradh 			    atomic_load_consume(&octciu_intrs[irq]);
5142d299731Smatt 			cpu->cpu_intr_evs[irq].ev_count++;
5152d299731Smatt 			if (__predict_true(ih != NULL)) {
5162d299731Smatt #ifdef MULTIPROCESSOR
5172d299731Smatt 				if (ipl == IPL_VM) {
5182d299731Smatt 					KERNEL_LOCK(1, NULL);
5192d299731Smatt #endif
5202d299731Smatt 					(*ih->ih_func)(ih->ih_arg);
5212d299731Smatt #ifdef MULTIPROCESSOR
5222d299731Smatt 					KERNEL_UNLOCK_ONE(NULL);
5232d299731Smatt 				} else {
524f693c922Shikaru 					(*ih->ih_func)(ih->ih_arg);
525f693c922Shikaru 				}
5262d299731Smatt #endif
527847a1893Smatt 				KDASSERT(mips_cp0_status_read() & MIPS_SR_INT_IE);
528f693c922Shikaru 			}
5292d299731Smatt 		}
530d911ef3aSjmcneill 	}
531847a1893Smatt 	KDASSERT(mips_cp0_status_read() & MIPS_SR_INT_IE);
5322d299731Smatt }
5332d299731Smatt 
5342d299731Smatt #ifdef MULTIPROCESSOR
5352d299731Smatt __CTASSERT(NIPIS < 16);
5362d299731Smatt 
5372d299731Smatt int
octeon_ipi_intr(void * arg)5382d299731Smatt octeon_ipi_intr(void *arg)
5392d299731Smatt {
5402d299731Smatt 	struct cpu_info * const ci = curcpu();
5412d299731Smatt 	struct cpu_softc * const cpu = ci->ci_softc;
5424a584abcSjmcneill 	const uint32_t mbox_mask = (uintptr_t) arg;
5434a584abcSjmcneill 	uint32_t ipi_mask = mbox_mask;
544847a1893Smatt 
5454a584abcSjmcneill 	KASSERTMSG((mbox_mask & __BITS(31,16)) == 0 || ci->ci_cpl >= IPL_SCHED,
5464a584abcSjmcneill 	    "mbox_mask %#"PRIx32" cpl %d", mbox_mask, ci->ci_cpl);
5472d299731Smatt 
5487d9c74a0Sjmcneill 	ipi_mask &= mips3_ld(cpu->cpu_mbox_set);
5497d9c74a0Sjmcneill 	if (ipi_mask == 0)
550847a1893Smatt 		return 0;
551*efc604cfSriastradh 	membar_acquire();
552847a1893Smatt 
5537d9c74a0Sjmcneill 	mips3_sd(cpu->cpu_mbox_clr, ipi_mask);
5542d299731Smatt 
5554a584abcSjmcneill 	KASSERT(__SHIFTOUT(ipi_mask, mbox_mask) < __BIT(NIPIS));
5562d299731Smatt 
557847a1893Smatt #if NWDOG > 0
558847a1893Smatt 	// Handle WDOG requests ourselves.
559847a1893Smatt 	if (ipi_mask & __BIT(IPI_WDOG)) {
560847a1893Smatt 		softint_schedule(cpu->cpu_wdog_sih);
561847a1893Smatt 		atomic_and_64(&ci->ci_request_ipis, ~__BIT(IPI_WDOG));
562847a1893Smatt 		ipi_mask &= ~__BIT(IPI_WDOG);
563847a1893Smatt 		ci->ci_evcnt_per_ipi[IPI_WDOG].ev_count++;
564847a1893Smatt 		if (__predict_true(ipi_mask == 0))
565847a1893Smatt 			return 1;
566847a1893Smatt 	}
567847a1893Smatt #endif
568847a1893Smatt 
5692d299731Smatt 	/* if the request is clear, it was previously processed */
570*efc604cfSriastradh 	if ((atomic_load_relaxed(&ci->ci_request_ipis) & ipi_mask) == 0)
5712d299731Smatt 		return 0;
572*efc604cfSriastradh 	membar_acquire();
5732d299731Smatt 
5742d299731Smatt 	atomic_or_64(&ci->ci_active_ipis, ipi_mask);
5752d299731Smatt 	atomic_and_64(&ci->ci_request_ipis, ~ipi_mask);
5762d299731Smatt 
5774a584abcSjmcneill 	ipi_process(ci, __SHIFTOUT(ipi_mask, mbox_mask));
5782d299731Smatt 
5792d299731Smatt 	atomic_and_64(&ci->ci_active_ipis, ~ipi_mask);
5802d299731Smatt 
5812d299731Smatt 	return 1;
5822d299731Smatt }
5832d299731Smatt 
5842d299731Smatt int
octeon_send_ipi(struct cpu_info * ci,int req)5852d299731Smatt octeon_send_ipi(struct cpu_info *ci, int req)
5862d299731Smatt {
5872d299731Smatt 	KASSERT(req < NIPIS);
5882d299731Smatt 	if (ci == NULL) {
589847a1893Smatt 		CPU_INFO_ITERATOR cii;
590847a1893Smatt 		for (CPU_INFO_FOREACH(cii, ci)) {
591847a1893Smatt 			if (ci != curcpu()) {
592847a1893Smatt 				octeon_send_ipi(ci, req);
5932d299731Smatt 			}
594847a1893Smatt 		}
595847a1893Smatt 		return 0;
596847a1893Smatt 	}
597847a1893Smatt 	KASSERT(cold || ci->ci_softc != NULL);
598847a1893Smatt 	if (ci->ci_softc == NULL)
599847a1893Smatt 		return -1;
6002d299731Smatt 
6012d299731Smatt 	struct cpu_softc * const cpu = ci->ci_softc;
6024a584abcSjmcneill 	const u_int ipi_shift = ipi_prio[req] == IPL_SCHED ? 16 : 0;
6034a584abcSjmcneill 	const uint32_t ipi_mask = __BIT(req + ipi_shift);
6042d299731Smatt 
605*efc604cfSriastradh 	membar_release();
606441d694cSskrll 	atomic_or_64(&ci->ci_request_ipis, ipi_mask);
6072d299731Smatt 
608*efc604cfSriastradh 	membar_release();
6097d9c74a0Sjmcneill 	mips3_sd(cpu->cpu_mbox_set, ipi_mask);
610679b5d89Sjmcneill 
6112d299731Smatt 	return 0;
6122d299731Smatt }
6132d299731Smatt #endif	/* MULTIPROCESSOR */
614