xref: /openbsd-src/sys/arch/octeon/dev/octciu.c (revision ae38162bb0b271e754e50c0aab431efcff91f60d)
1*ae38162bSvisa /*	$OpenBSD: octciu.c,v 1.19 2022/12/11 05:31:05 visa Exp $	*/
289182934Svisa 
389182934Svisa /*
489182934Svisa  * Copyright (c) 2000-2004 Opsycon AB  (www.opsycon.se)
589182934Svisa  *
689182934Svisa  * Redistribution and use in source and binary forms, with or without
789182934Svisa  * modification, are permitted provided that the following conditions
889182934Svisa  * are met:
989182934Svisa  * 1. Redistributions of source code must retain the above copyright
1089182934Svisa  *    notice, this list of conditions and the following disclaimer.
1189182934Svisa  * 2. Redistributions in binary form must reproduce the above copyright
1289182934Svisa  *    notice, this list of conditions and the following disclaimer in the
1389182934Svisa  *    documentation and/or other materials provided with the distribution.
1489182934Svisa  *
1589182934Svisa  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
1689182934Svisa  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1789182934Svisa  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1889182934Svisa  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
1989182934Svisa  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2089182934Svisa  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2189182934Svisa  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2289182934Svisa  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2389182934Svisa  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2489182934Svisa  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2589182934Svisa  * SUCH DAMAGE.
2689182934Svisa  *
2789182934Svisa  */
2889182934Svisa 
2989182934Svisa /*
3089182934Svisa  * Driver for OCTEON Central Interrupt Unit (CIU).
3189182934Svisa  *
3289182934Svisa  * CIU is present at least on CN3xxx, CN5xxx, CN60xx, CN61xx,
3389182934Svisa  * CN70xx, and CN71xx.
3489182934Svisa  */
3589182934Svisa 
3689182934Svisa #include <sys/param.h>
3789182934Svisa #include <sys/systm.h>
3889182934Svisa #include <sys/conf.h>
3989182934Svisa #include <sys/device.h>
40095618ebSvisa #include <sys/evcount.h>
4189182934Svisa #include <sys/kernel.h>
4289182934Svisa #include <sys/malloc.h>
4389182934Svisa 
4489182934Svisa #include <dev/ofw/fdt.h>
4589182934Svisa #include <dev/ofw/openfirm.h>
4689182934Svisa 
4789182934Svisa #include <mips64/mips_cpu.h>
4889182934Svisa 
4989182934Svisa #include <machine/autoconf.h>
5089182934Svisa #include <machine/fdt.h>
5189182934Svisa #include <machine/intr.h>
5289182934Svisa #include <machine/octeonreg.h>
5389182934Svisa 
54429e2826Svisa #define OCTCIU_NINTS 192
5589182934Svisa 
5689182934Svisa #define INTPRI_CIU_0	(INTPRI_CLOCK + 1)
57429e2826Svisa #define INTPRI_CIU_1	(INTPRI_CLOCK + 2)
5889182934Svisa 
5989182934Svisa struct intrbank {
6089182934Svisa 	uint64_t	en;		/* enable mask register */
6189182934Svisa 	uint64_t	sum;		/* service request register */
6289182934Svisa 	int		id;		/* bank number */
6389182934Svisa };
6489182934Svisa 
65429e2826Svisa #define NBANKS		3
6689182934Svisa #define BANK_SIZE	64
6789182934Svisa #define IRQ_TO_BANK(x)	((x) >> 6)
6889182934Svisa #define IRQ_TO_BIT(x)	((x) & 0x3f)
6989182934Svisa 
70e842824bSvisa #define IS_WORKQ_IRQ(x)	((unsigned int)(x) < 16)
71e842824bSvisa 
72273c963eSvisa struct octciu_intrhand {
73041e95aeSvisa 	SLIST_ENTRY(octciu_intrhand)
74041e95aeSvisa 				 ih_list;
75273c963eSvisa 	int			(*ih_fun)(void *);
76273c963eSvisa 	void			*ih_arg;
77273c963eSvisa 	int			 ih_level;
78273c963eSvisa 	int			 ih_irq;
79273c963eSvisa 	struct evcount		 ih_count;
80273c963eSvisa 	int			 ih_flags;
81273c963eSvisa 	cpuid_t			 ih_cpuid;
82273c963eSvisa };
83273c963eSvisa 
84273c963eSvisa /* ih_flags */
85273c963eSvisa #define CIH_MPSAFE	0x01
86273c963eSvisa 
874196a899Svisa struct octciu_cpu {
884196a899Svisa 	struct intrbank		 scpu_ibank[NBANKS];
894196a899Svisa 	uint64_t		 scpu_intem[NBANKS];
904196a899Svisa 	uint64_t		 scpu_imask[NIPLS][NBANKS];
914196a899Svisa };
924196a899Svisa 
9389182934Svisa struct octciu_softc {
9489182934Svisa 	struct device		 sc_dev;
9589182934Svisa 	bus_space_tag_t		 sc_iot;
9689182934Svisa 	bus_space_handle_t	 sc_ioh;
974196a899Svisa 	struct octciu_cpu	 sc_cpu[MAXCPUS];
98041e95aeSvisa 	SLIST_HEAD(, octciu_intrhand)
99041e95aeSvisa 				 sc_intrhand[OCTCIU_NINTS];
100429e2826Svisa 	unsigned int		 sc_nbanks;
10189182934Svisa 
10289182934Svisa 	int			(*sc_ipi_handler)(void *);
10389182934Svisa 
10489182934Svisa 	struct intr_controller	 sc_ic;
10589182934Svisa };
10689182934Svisa 
10789182934Svisa int	 octciu_match(struct device *, void *, void *);
10889182934Svisa void	 octciu_attach(struct device *, struct device *, void *);
10989182934Svisa 
11089182934Svisa void	 octciu_init(void);
11189182934Svisa void	 octciu_intr_makemasks(struct octciu_softc *);
112429e2826Svisa uint32_t octciu_intr0(uint32_t, struct trapframe *);
113429e2826Svisa uint32_t octciu_intr2(uint32_t, struct trapframe *);
11489182934Svisa uint32_t octciu_intr_bank(struct octciu_softc *, struct intrbank *,
11589182934Svisa 	    struct trapframe *);
11689182934Svisa void	*octciu_intr_establish(int, int, int (*)(void *), void *,
11789182934Svisa 	    const char *);
11889182934Svisa void	*octciu_intr_establish_fdt_idx(void *, int, int, int,
11989182934Svisa 	    int (*)(void *), void *, const char *);
12089182934Svisa void	 octciu_intr_disestablish(void *);
1210dd6b0daSvisa void	 octciu_intr_barrier(void *);
12289182934Svisa void	 octciu_splx(int);
12389182934Svisa 
12489182934Svisa uint32_t octciu_ipi_intr(uint32_t, struct trapframe *);
12589182934Svisa int	 octciu_ipi_establish(int (*)(void *), cpuid_t);
12689182934Svisa void	 octciu_ipi_set(cpuid_t);
12789182934Svisa void	 octciu_ipi_clear(cpuid_t);
12889182934Svisa 
12989182934Svisa const struct cfattach octciu_ca = {
13089182934Svisa 	sizeof(struct octciu_softc), octciu_match, octciu_attach
13189182934Svisa };
13289182934Svisa 
13389182934Svisa struct cfdriver octciu_cd = {
13489182934Svisa 	NULL, "octciu", DV_DULL
13589182934Svisa };
13689182934Svisa 
13789182934Svisa struct octciu_softc	*octciu_sc;
13889182934Svisa 
13989182934Svisa int
octciu_match(struct device * parent,void * match,void * aux)14089182934Svisa octciu_match(struct device *parent, void *match, void *aux)
14189182934Svisa {
14289182934Svisa 	struct fdt_attach_args *faa = aux;
14389182934Svisa 
14489182934Svisa 	return OF_is_compatible(faa->fa_node, "cavium,octeon-3860-ciu");
14589182934Svisa }
14689182934Svisa 
14789182934Svisa void
octciu_attach(struct device * parent,struct device * self,void * aux)14889182934Svisa octciu_attach(struct device *parent, struct device *self, void *aux)
14989182934Svisa {
150f83d439fSvisa 	struct fdt_attach_args *faa = aux;
15189182934Svisa 	struct octciu_softc *sc = (struct octciu_softc *)self;
152041e95aeSvisa 	int i;
15389182934Svisa 
154f83d439fSvisa 	if (faa->fa_nreg != 1) {
155f83d439fSvisa 		printf(": expected one IO space, got %d\n", faa->fa_nreg);
15689182934Svisa 		return;
15789182934Svisa 	}
15889182934Svisa 
159f83d439fSvisa 	sc->sc_iot = faa->fa_iot;
160f83d439fSvisa 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size,
16189182934Svisa 	    0, &sc->sc_ioh)) {
16289182934Svisa 		printf(": could not map IO space\n");
16389182934Svisa 		return;
16489182934Svisa 	}
16589182934Svisa 
166429e2826Svisa 	if (octeon_ver == OCTEON_2 || octeon_ver == OCTEON_3)
167429e2826Svisa 		sc->sc_nbanks = 3;
168429e2826Svisa 	else
169429e2826Svisa 		sc->sc_nbanks = 2;
170429e2826Svisa 
171041e95aeSvisa 	for (i = 0; i < OCTCIU_NINTS; i++)
172041e95aeSvisa 		SLIST_INIT(&sc->sc_intrhand[i]);
173041e95aeSvisa 
17489182934Svisa 	printf("\n");
17589182934Svisa 
17689182934Svisa 	sc->sc_ic.ic_cookie = sc;
177f83d439fSvisa 	sc->sc_ic.ic_node = faa->fa_node;
17889182934Svisa 	sc->sc_ic.ic_init = octciu_init;
17989182934Svisa 	sc->sc_ic.ic_establish = octciu_intr_establish;
18089182934Svisa 	sc->sc_ic.ic_establish_fdt_idx = octciu_intr_establish_fdt_idx;
18189182934Svisa 	sc->sc_ic.ic_disestablish = octciu_intr_disestablish;
1820dd6b0daSvisa 	sc->sc_ic.ic_intr_barrier = octciu_intr_barrier;
18389182934Svisa #ifdef MULTIPROCESSOR
18489182934Svisa 	sc->sc_ic.ic_ipi_establish = octciu_ipi_establish;
18589182934Svisa 	sc->sc_ic.ic_ipi_set = octciu_ipi_set;
18689182934Svisa 	sc->sc_ic.ic_ipi_clear = octciu_ipi_clear;
18789182934Svisa #endif
18889182934Svisa 
18989182934Svisa 	octciu_sc = sc;
19089182934Svisa 
191429e2826Svisa 	set_intr(INTPRI_CIU_0, CR_INT_0, octciu_intr0);
192429e2826Svisa 	if (sc->sc_nbanks == 3)
193429e2826Svisa 		set_intr(INTPRI_CIU_1, CR_INT_2, octciu_intr2);
19489182934Svisa #ifdef MULTIPROCESSOR
19589182934Svisa 	set_intr(INTPRI_IPI, CR_INT_1, octciu_ipi_intr);
19689182934Svisa #endif
19789182934Svisa 
19889182934Svisa 	octciu_init();
19989182934Svisa 
20089182934Svisa 	register_splx_handler(octciu_splx);
20189182934Svisa 	octeon_intr_register(&sc->sc_ic);
20289182934Svisa }
20389182934Svisa 
20489182934Svisa void
octciu_init(void)20589182934Svisa octciu_init(void)
20689182934Svisa {
20789182934Svisa 	struct octciu_softc *sc = octciu_sc;
2084196a899Svisa 	struct octciu_cpu *scpu;
20989182934Svisa 	int cpuid = cpu_number();
210e842824bSvisa 	int s;
21189182934Svisa 
2124196a899Svisa 	scpu = &sc->sc_cpu[cpuid];
2134196a899Svisa 
21489182934Svisa 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, CIU_IP2_EN0(cpuid), 0);
21589182934Svisa 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, CIU_IP3_EN0(cpuid), 0);
21689182934Svisa 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, CIU_IP2_EN1(cpuid), 0);
21789182934Svisa 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, CIU_IP3_EN1(cpuid), 0);
21889182934Svisa 
219429e2826Svisa 	if (sc->sc_nbanks == 3)
220429e2826Svisa 		bus_space_write_8(sc->sc_iot, sc->sc_ioh,
221429e2826Svisa 		    CIU_IP4_EN2(cpuid), 0);
222429e2826Svisa 
2234196a899Svisa 	scpu->scpu_ibank[0].en = CIU_IP2_EN0(cpuid);
2244196a899Svisa 	scpu->scpu_ibank[0].sum = CIU_IP2_SUM0(cpuid);
2254196a899Svisa 	scpu->scpu_ibank[0].id = 0;
2264196a899Svisa 	scpu->scpu_ibank[1].en = CIU_IP2_EN1(cpuid);
2274196a899Svisa 	scpu->scpu_ibank[1].sum = CIU_INT32_SUM1;
2284196a899Svisa 	scpu->scpu_ibank[1].id = 1;
229429e2826Svisa 	scpu->scpu_ibank[2].en = CIU_IP4_EN2(cpuid);
230429e2826Svisa 	scpu->scpu_ibank[2].sum = CIU_IP4_SUM2(cpuid);
231429e2826Svisa 	scpu->scpu_ibank[2].id = 2;
232e842824bSvisa 
233e842824bSvisa 	s = splhigh();
234e842824bSvisa 	octciu_intr_makemasks(sc);
235e842824bSvisa 	splx(s);	/* causes hw mask update */
23689182934Svisa }
23789182934Svisa 
23889182934Svisa void *
octciu_intr_establish(int irq,int level,int (* ih_fun)(void *),void * ih_arg,const char * ih_what)23989182934Svisa octciu_intr_establish(int irq, int level, int (*ih_fun)(void *),
24089182934Svisa     void *ih_arg, const char *ih_what)
24189182934Svisa {
24289182934Svisa 	struct octciu_softc *sc = octciu_sc;
243041e95aeSvisa 	struct octciu_intrhand *ih, *last, *tmp;
24489182934Svisa 	int cpuid = cpu_number();
24589182934Svisa 	int flags;
24689182934Svisa 	int s;
24789182934Svisa 
24889182934Svisa #ifdef DIAGNOSTIC
249429e2826Svisa 	if (irq >= sc->sc_nbanks * BANK_SIZE || irq < 0)
25034745855Svisa 		panic("%s: illegal irq %d", __func__, irq);
25189182934Svisa #endif
25289182934Svisa 
253e842824bSvisa #ifdef MULTIPROCESSOR
254e842824bSvisa 	/* Span work queue interrupts across CPUs. */
255cb20e335Svisa 	if (IS_WORKQ_IRQ(irq))
256fee69528Svisa 		cpuid = irq % ncpus;
257e842824bSvisa #endif
258e842824bSvisa 
259273c963eSvisa 	flags = (level & IPL_MPSAFE) ? CIH_MPSAFE : 0;
26089182934Svisa 	level &= ~IPL_MPSAFE;
26189182934Svisa 
26289182934Svisa 	ih = malloc(sizeof *ih, M_DEVBUF, M_NOWAIT);
26389182934Svisa 	if (ih == NULL)
26489182934Svisa 		return NULL;
26589182934Svisa 
26689182934Svisa 	ih->ih_fun = ih_fun;
26789182934Svisa 	ih->ih_arg = ih_arg;
26889182934Svisa 	ih->ih_level = level;
26989182934Svisa 	ih->ih_flags = flags;
27089182934Svisa 	ih->ih_irq = irq;
271e842824bSvisa 	ih->ih_cpuid = cpuid;
27289182934Svisa 	evcount_attach(&ih->ih_count, ih_what, &ih->ih_irq);
273*ae38162bSvisa 	evcount_percpu(&ih->ih_count);
27489182934Svisa 
27589182934Svisa 	s = splhigh();
27689182934Svisa 
277041e95aeSvisa 	if (SLIST_EMPTY(&sc->sc_intrhand[irq])) {
278041e95aeSvisa 		SLIST_INSERT_HEAD(&sc->sc_intrhand[irq], ih, ih_list);
279041e95aeSvisa 	} else {
280041e95aeSvisa 		last = NULL;
281041e95aeSvisa 		SLIST_FOREACH(tmp, &sc->sc_intrhand[irq], ih_list)
282041e95aeSvisa 			last = tmp;
283041e95aeSvisa 		SLIST_INSERT_AFTER(last, ih, ih_list);
284041e95aeSvisa 	}
28589182934Svisa 
2864196a899Svisa 	sc->sc_cpu[cpuid].scpu_intem[IRQ_TO_BANK(irq)] |=
2874196a899Svisa 	    1UL << IRQ_TO_BIT(irq);
28889182934Svisa 	octciu_intr_makemasks(sc);
28989182934Svisa 
29089182934Svisa 	splx(s);	/* causes hw mask update */
29189182934Svisa 
29289182934Svisa 	return (ih);
29389182934Svisa }
29489182934Svisa 
29589182934Svisa void *
octciu_intr_establish_fdt_idx(void * cookie,int node,int idx,int level,int (* ih_fun)(void *),void * ih_arg,const char * ih_what)29689182934Svisa octciu_intr_establish_fdt_idx(void *cookie, int node, int idx, int level,
29789182934Svisa     int (*ih_fun)(void *), void *ih_arg, const char *ih_what)
29889182934Svisa {
29989182934Svisa 	uint32_t *cells;
30089182934Svisa 	int irq, len;
30189182934Svisa 
30289182934Svisa 	len = OF_getproplen(node, "interrupts");
30389182934Svisa 	if (len / (sizeof(uint32_t) * 2) <= idx ||
30489182934Svisa 	    len % (sizeof(uint32_t) * 2) != 0)
30589182934Svisa 		return NULL;
30689182934Svisa 
30789182934Svisa 	cells = malloc(len, M_TEMP, M_NOWAIT);
30889182934Svisa 	if (cells == NULL)
30989182934Svisa 		return NULL;
31089182934Svisa 
31189182934Svisa 	OF_getpropintarray(node, "interrupts", cells, len);
31289182934Svisa 	irq = cells[idx * 2] * BANK_SIZE + cells[idx * 2 + 1];
31389182934Svisa 
31489182934Svisa 	free(cells, M_TEMP, len);
31589182934Svisa 
31689182934Svisa 	return octciu_intr_establish(irq, level, ih_fun, ih_arg, ih_what);
31789182934Svisa }
31889182934Svisa 
31989182934Svisa void
octciu_intr_disestablish(void * _ih)32089182934Svisa octciu_intr_disestablish(void *_ih)
32189182934Svisa {
322273c963eSvisa 	struct octciu_intrhand *ih = _ih;
323041e95aeSvisa 	struct octciu_intrhand *tmp;
32489182934Svisa 	struct octciu_softc *sc = octciu_sc;
32589182934Svisa 	unsigned int irq = ih->ih_irq;
32689182934Svisa 	int cpuid = cpu_number();
327041e95aeSvisa 	int found = 0;
32889182934Svisa 	int s;
32989182934Svisa 
330429e2826Svisa 	KASSERT(irq < sc->sc_nbanks * BANK_SIZE);
331e842824bSvisa 	KASSERT(!IS_WORKQ_IRQ(irq));
33289182934Svisa 
33389182934Svisa 	s = splhigh();
33489182934Svisa 
335041e95aeSvisa 	SLIST_FOREACH(tmp, &sc->sc_intrhand[irq], ih_list) {
336041e95aeSvisa 		if (tmp == ih) {
337041e95aeSvisa 			found = 1;
33889182934Svisa 			break;
33989182934Svisa 		}
34089182934Svisa 	}
341041e95aeSvisa 	if (found == 0)
342041e95aeSvisa 		panic("%s: intrhand %p not registered", __func__, ih);
343041e95aeSvisa 
344041e95aeSvisa 	SLIST_REMOVE(&sc->sc_intrhand[irq], ih, octciu_intrhand, ih_list);
3457abddf2dSvisa 	evcount_detach(&ih->ih_count);
346041e95aeSvisa 
347041e95aeSvisa 	if (SLIST_EMPTY(&sc->sc_intrhand[irq])) {
348041e95aeSvisa 		sc->sc_cpu[cpuid].scpu_intem[IRQ_TO_BANK(irq)] &=
349041e95aeSvisa 		    ~(1UL << IRQ_TO_BIT(irq));
35089182934Svisa 	}
35189182934Svisa 
35289182934Svisa 	octciu_intr_makemasks(sc);
35389182934Svisa 	splx(s);	/* causes hw mask update */
354041e95aeSvisa 
355041e95aeSvisa 	free(ih, M_DEVBUF, sizeof(*ih));
35689182934Svisa }
35789182934Svisa 
3580dd6b0daSvisa void
octciu_intr_barrier(void * _ih)3590dd6b0daSvisa octciu_intr_barrier(void *_ih)
3600dd6b0daSvisa {
3610dd6b0daSvisa 	struct cpu_info *ci = NULL;
3620dd6b0daSvisa #ifdef MULTIPROCESSOR
3630dd6b0daSvisa 	struct octciu_intrhand *ih = _ih;
3640dd6b0daSvisa 
3650dd6b0daSvisa 	if (IS_WORKQ_IRQ(ih->ih_irq))
3660dd6b0daSvisa 		ci = get_cpu_info(ih->ih_irq % ncpus);
3670dd6b0daSvisa #endif
3680dd6b0daSvisa 
3690dd6b0daSvisa 	sched_barrier(ci);
3700dd6b0daSvisa }
3710dd6b0daSvisa 
37289182934Svisa /*
37389182934Svisa  * Recompute interrupt masks.
37489182934Svisa  */
37589182934Svisa void
octciu_intr_makemasks(struct octciu_softc * sc)37689182934Svisa octciu_intr_makemasks(struct octciu_softc *sc)
37789182934Svisa {
378e842824bSvisa 	cpuid_t cpuid = cpu_number();
379e842824bSvisa 	struct octciu_cpu *scpu = &sc->sc_cpu[cpuid];
380273c963eSvisa 	struct octciu_intrhand *q;
38189182934Svisa 	uint intrlevel[OCTCIU_NINTS];
3824196a899Svisa 	int irq, level;
38389182934Svisa 
38489182934Svisa 	/* First, figure out which levels each IRQ uses. */
38589182934Svisa 	for (irq = 0; irq < OCTCIU_NINTS; irq++) {
38689182934Svisa 		uint levels = 0;
387041e95aeSvisa 		SLIST_FOREACH(q, &sc->sc_intrhand[irq], ih_list) {
388e842824bSvisa 			if (q->ih_cpuid == cpuid)
38989182934Svisa 				levels |= 1 << q->ih_level;
390e842824bSvisa 		}
39189182934Svisa 		intrlevel[irq] = levels;
39289182934Svisa 	}
39389182934Svisa 
39489182934Svisa 	/*
39589182934Svisa 	 * Then figure out which IRQs use each level.
39689182934Svisa 	 * Note that we make sure never to overwrite imask[IPL_HIGH], in
39789182934Svisa 	 * case an interrupt occurs during intr_disestablish() and causes
39889182934Svisa 	 * an unfortunate splx() while we are here recomputing the masks.
39989182934Svisa 	 */
40089182934Svisa 	for (level = IPL_NONE; level < NIPLS; level++) {
40189182934Svisa 		uint64_t mask[NBANKS] = {};
40289182934Svisa 		for (irq = 0; irq < OCTCIU_NINTS; irq++)
40389182934Svisa 			if (intrlevel[irq] & (1 << level))
40489182934Svisa 				mask[IRQ_TO_BANK(irq)] |=
40589182934Svisa 				    1UL << IRQ_TO_BIT(irq);
4064196a899Svisa 		scpu->scpu_imask[level][0] = mask[0];
4074196a899Svisa 		scpu->scpu_imask[level][1] = mask[1];
408429e2826Svisa 		scpu->scpu_imask[level][2] = mask[2];
40989182934Svisa 	}
41089182934Svisa 	/*
41189182934Svisa 	 * There are tty, network and disk drivers that use free() at interrupt
41289182934Svisa 	 * time, so vm > (tty | net | bio).
41389182934Svisa 	 *
41489182934Svisa 	 * Enforce a hierarchy that gives slow devices a better chance at not
41589182934Svisa 	 * dropping data.
41689182934Svisa 	 */
41789182934Svisa #define ADD_MASK(dst, src) do {	\
41889182934Svisa 	dst[0] |= src[0];	\
41989182934Svisa 	dst[1] |= src[1];	\
420429e2826Svisa 	dst[2] |= src[2];	\
42189182934Svisa } while (0)
4224196a899Svisa 	ADD_MASK(scpu->scpu_imask[IPL_NET], scpu->scpu_imask[IPL_BIO]);
4234196a899Svisa 	ADD_MASK(scpu->scpu_imask[IPL_TTY], scpu->scpu_imask[IPL_NET]);
4244196a899Svisa 	ADD_MASK(scpu->scpu_imask[IPL_VM], scpu->scpu_imask[IPL_TTY]);
4254196a899Svisa 	ADD_MASK(scpu->scpu_imask[IPL_CLOCK], scpu->scpu_imask[IPL_VM]);
4264196a899Svisa 	ADD_MASK(scpu->scpu_imask[IPL_HIGH], scpu->scpu_imask[IPL_CLOCK]);
4274196a899Svisa 	ADD_MASK(scpu->scpu_imask[IPL_IPI], scpu->scpu_imask[IPL_HIGH]);
42889182934Svisa 
42989182934Svisa 	/*
43089182934Svisa 	 * These are pseudo-levels.
43189182934Svisa 	 */
4324196a899Svisa 	scpu->scpu_imask[IPL_NONE][0] = 0;
4334196a899Svisa 	scpu->scpu_imask[IPL_NONE][1] = 0;
434429e2826Svisa 	scpu->scpu_imask[IPL_NONE][2] = 0;
43589182934Svisa }
43689182934Svisa 
43789182934Svisa static inline int
octciu_next_irq(uint64_t * isr)43889182934Svisa octciu_next_irq(uint64_t *isr)
43989182934Svisa {
44089182934Svisa 	uint64_t irq, tmp = *isr;
44189182934Svisa 
44289182934Svisa 	if (tmp == 0)
44389182934Svisa 		return -1;
44489182934Svisa 
44589182934Svisa 	asm volatile (
44689182934Svisa 	"	.set push\n"
44789182934Svisa 	"	.set mips64\n"
44889182934Svisa 	"	dclz	%0, %0\n"
44989182934Svisa 	"	.set pop\n"
45089182934Svisa 	: "=r" (tmp) : "0" (tmp));
45189182934Svisa 
45289182934Svisa 	irq = 63u - tmp;
45389182934Svisa 	*isr &= ~(1u << irq);
45489182934Svisa 	return irq;
45589182934Svisa }
45689182934Svisa 
45789182934Svisa /*
45889182934Svisa  * Dispatch interrupts in given bank.
45989182934Svisa  */
46089182934Svisa uint32_t
octciu_intr_bank(struct octciu_softc * sc,struct intrbank * bank,struct trapframe * frame)46189182934Svisa octciu_intr_bank(struct octciu_softc *sc, struct intrbank *bank,
46289182934Svisa     struct trapframe *frame)
46389182934Svisa {
46489182934Svisa 	struct cpu_info *ci = curcpu();
465273c963eSvisa 	struct octciu_intrhand *ih;
466d7d80ecdSvisa 	struct octciu_cpu *scpu = &sc->sc_cpu[ci->ci_cpuid];
46789182934Svisa 	uint64_t imr, isr, mask;
46889182934Svisa 	int handled, ipl, irq;
46989182934Svisa #ifdef MULTIPROCESSOR
47089182934Svisa 	register_t sr;
47189182934Svisa 	int need_lock;
47289182934Svisa #endif
47389182934Svisa 
47489182934Svisa 	isr = bus_space_read_8(sc->sc_iot, sc->sc_ioh, bank->sum);
47589182934Svisa 	imr = bus_space_read_8(sc->sc_iot, sc->sc_ioh, bank->en);
47689182934Svisa 
47789182934Svisa 	isr &= imr;
47889182934Svisa 	if (isr == 0)
47989182934Svisa 		return 0;	/* not for us */
48089182934Svisa 
48189182934Svisa 	/*
48289182934Svisa 	 * Mask all pending interrupts.
48389182934Svisa 	 */
48489182934Svisa 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, bank->en, imr & ~isr);
48589182934Svisa 
48689182934Svisa 	/*
48789182934Svisa 	 * If interrupts are spl-masked, mask them and wait for splx()
48889182934Svisa 	 * to reenable them when necessary.
48989182934Svisa 	 */
4904196a899Svisa 	if ((mask = isr & scpu->scpu_imask[frame->ipl][bank->id])
49189182934Svisa 	    != 0) {
49289182934Svisa 		isr &= ~mask;
49389182934Svisa 		imr &= ~mask;
49489182934Svisa 	}
49589182934Svisa 	if (isr == 0)
49689182934Svisa 		return 1;
49789182934Svisa 
49889182934Svisa 	/*
49989182934Svisa 	 * Now process allowed interrupts.
50089182934Svisa 	 */
50189182934Svisa 
50289182934Svisa 	ipl = ci->ci_ipl;
50389182934Svisa 
50489182934Svisa 	while ((irq = octciu_next_irq(&isr)) >= 0) {
50589182934Svisa 		irq += bank->id * BANK_SIZE;
50689182934Svisa 		handled = 0;
507041e95aeSvisa 		SLIST_FOREACH(ih, &sc->sc_intrhand[irq], ih_list) {
50889182934Svisa 			splraise(ih->ih_level);
50989182934Svisa #ifdef MULTIPROCESSOR
51089182934Svisa 			if (ih->ih_level < IPL_IPI) {
51189182934Svisa 				sr = getsr();
51289182934Svisa 				ENABLEIPI();
51389182934Svisa 			}
514273c963eSvisa 			if (ih->ih_flags & CIH_MPSAFE)
51589182934Svisa 				need_lock = 0;
51689182934Svisa 			else
517f8189779Svisa 				need_lock = 1;
51889182934Svisa 			if (need_lock)
51989182934Svisa 				__mp_lock(&kernel_lock);
52089182934Svisa #endif
52189182934Svisa 			if ((*ih->ih_fun)(ih->ih_arg) != 0) {
52289182934Svisa 				handled = 1;
523*ae38162bSvisa 				evcount_inc(&ih->ih_count);
52489182934Svisa 			}
52589182934Svisa #ifdef MULTIPROCESSOR
52689182934Svisa 			if (need_lock)
52789182934Svisa 				__mp_unlock(&kernel_lock);
52889182934Svisa 			if (ih->ih_level < IPL_IPI)
52989182934Svisa 				setsr(sr);
53089182934Svisa #endif
53189182934Svisa 		}
53289182934Svisa 		if (!handled)
5335e9543b6Svisa 			printf("%s: spurious interrupt %d on cpu %lu\n",
5345e9543b6Svisa 			    sc->sc_dev.dv_xname, irq, ci->ci_cpuid);
53589182934Svisa 	}
53689182934Svisa 
53789182934Svisa 	ci->ci_ipl = ipl;
53889182934Svisa 
53989182934Svisa 	/*
54089182934Svisa 	 * Reenable interrupts which have been serviced.
54189182934Svisa 	 */
54289182934Svisa 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, bank->en, imr);
54389182934Svisa 
54489182934Svisa 	return 1;
54589182934Svisa }
54689182934Svisa 
54789182934Svisa uint32_t
octciu_intr0(uint32_t hwpend,struct trapframe * frame)548429e2826Svisa octciu_intr0(uint32_t hwpend, struct trapframe *frame)
54989182934Svisa {
55089182934Svisa 	struct octciu_softc *sc = octciu_sc;
5514196a899Svisa 	struct octciu_cpu *scpu = &sc->sc_cpu[cpu_number()];
55289182934Svisa 	int handled;
55389182934Svisa 
5544196a899Svisa 	handled = octciu_intr_bank(sc, &scpu->scpu_ibank[0], frame);
5554196a899Svisa 	handled |= octciu_intr_bank(sc, &scpu->scpu_ibank[1], frame);
55689182934Svisa 	return handled ? hwpend : 0;
55789182934Svisa }
55889182934Svisa 
559429e2826Svisa uint32_t
octciu_intr2(uint32_t hwpend,struct trapframe * frame)560429e2826Svisa octciu_intr2(uint32_t hwpend, struct trapframe *frame)
561429e2826Svisa {
562429e2826Svisa 	struct octciu_softc *sc = octciu_sc;
563429e2826Svisa 	struct octciu_cpu *scpu = &sc->sc_cpu[cpu_number()];
564429e2826Svisa 	int handled;
565429e2826Svisa 
566429e2826Svisa 	handled = octciu_intr_bank(sc, &scpu->scpu_ibank[2], frame);
567429e2826Svisa 	return handled ? hwpend : 0;
568429e2826Svisa }
569429e2826Svisa 
57089182934Svisa void
octciu_splx(int newipl)57189182934Svisa octciu_splx(int newipl)
57289182934Svisa {
57389182934Svisa 	struct cpu_info *ci = curcpu();
5744196a899Svisa 	struct octciu_softc *sc = octciu_sc;
5754196a899Svisa 	struct octciu_cpu *scpu = &sc->sc_cpu[ci->ci_cpuid];
57689182934Svisa 
57789182934Svisa 	ci->ci_ipl = newipl;
57889182934Svisa 
57989182934Svisa 	/* Set hardware masks. */
5804196a899Svisa 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, scpu->scpu_ibank[0].en,
5814196a899Svisa 	    scpu->scpu_intem[0] & ~scpu->scpu_imask[newipl][0]);
5824196a899Svisa 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, scpu->scpu_ibank[1].en,
5834196a899Svisa 	    scpu->scpu_intem[1] & ~scpu->scpu_imask[newipl][1]);
58489182934Svisa 
585429e2826Svisa 	if (sc->sc_nbanks == 3)
586429e2826Svisa 		bus_space_write_8(sc->sc_iot, sc->sc_ioh,
587429e2826Svisa 		    scpu->scpu_ibank[2].en,
588429e2826Svisa 		    scpu->scpu_intem[2] & ~scpu->scpu_imask[newipl][2]);
589429e2826Svisa 
59085caa4b9Scheloha 	/* Trigger deferred clock interrupt if it is now unmasked. */
59185caa4b9Scheloha 	if (ci->ci_clock_deferred && newipl < IPL_CLOCK)
59285caa4b9Scheloha 		md_triggerclock();
59385caa4b9Scheloha 
59489182934Svisa 	/* If we still have softints pending trigger processing. */
59589182934Svisa 	if (ci->ci_softpending != 0 && newipl < IPL_SOFTINT)
59689182934Svisa 		setsoftintr0();
59789182934Svisa }
59889182934Svisa 
59989182934Svisa #ifdef MULTIPROCESSOR
60089182934Svisa uint32_t
octciu_ipi_intr(uint32_t hwpend,struct trapframe * frame)60189182934Svisa octciu_ipi_intr(uint32_t hwpend, struct trapframe *frame)
60289182934Svisa {
60389182934Svisa 	struct octciu_softc *sc = octciu_sc;
60489182934Svisa 	u_long cpuid = cpu_number();
60589182934Svisa 
60689182934Svisa 	/*
60789182934Svisa 	 * Mask all pending interrupts.
60889182934Svisa 	 */
60989182934Svisa 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, CIU_IP3_EN0(cpuid), 0);
61089182934Svisa 
61189182934Svisa 	if (sc->sc_ipi_handler == NULL)
61289182934Svisa 		return hwpend;
61389182934Svisa 
61489182934Svisa 	sc->sc_ipi_handler((void *)cpuid);
61589182934Svisa 
61689182934Svisa 	/*
61789182934Svisa 	 * Reenable interrupts which have been serviced.
61889182934Svisa 	 */
61989182934Svisa 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, CIU_IP3_EN0(cpuid),
62089182934Svisa 		(1ULL << CIU_INT_MBOX0)|(1ULL << CIU_INT_MBOX1));
62189182934Svisa 	return hwpend;
62289182934Svisa }
62389182934Svisa 
62489182934Svisa int
octciu_ipi_establish(int (* func)(void *),cpuid_t cpuid)62589182934Svisa octciu_ipi_establish(int (*func)(void *), cpuid_t cpuid)
62689182934Svisa {
62789182934Svisa 	struct octciu_softc *sc = octciu_sc;
62889182934Svisa 
62989182934Svisa 	if (cpuid == 0)
63089182934Svisa 		sc->sc_ipi_handler = func;
63189182934Svisa 
63289182934Svisa 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, CIU_MBOX_CLR(cpuid),
63389182934Svisa 		0xffffffff);
63489182934Svisa 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, CIU_IP3_EN0(cpuid),
63589182934Svisa 		(1ULL << CIU_INT_MBOX0)|(1ULL << CIU_INT_MBOX1));
63689182934Svisa 
63789182934Svisa 	return 0;
63889182934Svisa }
63989182934Svisa 
64089182934Svisa void
octciu_ipi_set(cpuid_t cpuid)64189182934Svisa octciu_ipi_set(cpuid_t cpuid)
64289182934Svisa {
64389182934Svisa 	struct octciu_softc *sc = octciu_sc;
64489182934Svisa 
64589182934Svisa 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, CIU_MBOX_SET(cpuid), 1);
64689182934Svisa }
64789182934Svisa 
64889182934Svisa void
octciu_ipi_clear(cpuid_t cpuid)64989182934Svisa octciu_ipi_clear(cpuid_t cpuid)
65089182934Svisa {
65189182934Svisa 	struct octciu_softc *sc = octciu_sc;
65289182934Svisa 	uint64_t clr;
65389182934Svisa 
65489182934Svisa 	clr = bus_space_read_8(sc->sc_iot, sc->sc_ioh, CIU_MBOX_CLR(cpuid));
65589182934Svisa 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, CIU_MBOX_CLR(cpuid), clr);
65689182934Svisa }
65789182934Svisa #endif /* MULTIPROCESSOR */
658