xref: /openbsd-src/sys/arch/octeon/dev/octcib.c (revision 5e9543b67bd9f2e117237e2e926c413f1fdb328a)
1*5e9543b6Svisa /*	$OpenBSD: octcib.c,v 1.5 2019/09/01 12:16:01 visa Exp $	*/
23f75081cSvisa 
33f75081cSvisa /*
40dd6b0daSvisa  * Copyright (c) 2017, 2019 Visa Hankala
53f75081cSvisa  *
63f75081cSvisa  * Permission to use, copy, modify, and distribute this software for any
73f75081cSvisa  * purpose with or without fee is hereby granted, provided that the above
83f75081cSvisa  * copyright notice and this permission notice appear in all copies.
93f75081cSvisa  *
103f75081cSvisa  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
113f75081cSvisa  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
123f75081cSvisa  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
133f75081cSvisa  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
143f75081cSvisa  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
153f75081cSvisa  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
163f75081cSvisa  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
173f75081cSvisa  */
183f75081cSvisa 
193f75081cSvisa /*
203f75081cSvisa  * Driver for Cavium Interrupt Bus (CIB) widget.
213f75081cSvisa  */
223f75081cSvisa 
233f75081cSvisa #include <sys/param.h>
243f75081cSvisa #include <sys/systm.h>
253f75081cSvisa #include <sys/device.h>
26095618ebSvisa #include <sys/evcount.h>
273f75081cSvisa #include <sys/malloc.h>
283f75081cSvisa #include <sys/queue.h>
293f75081cSvisa 
303f75081cSvisa #include <dev/ofw/fdt.h>
313f75081cSvisa #include <dev/ofw/openfirm.h>
323f75081cSvisa 
333f75081cSvisa #include <machine/fdt.h>
343f75081cSvisa 
353f75081cSvisa #define CIB_HIGHIPL		IPL_BIO
363f75081cSvisa #define CIB_MAXBITS		64
373f75081cSvisa #define CIB_IRQNUM(sc, bit)	(256 + (sc)->sc_dev.dv_unit * CIB_MAXBITS + \
383f75081cSvisa 				    (bit))
393f75081cSvisa 
403f75081cSvisa #define CIB_EN_RD(sc) \
413f75081cSvisa 	bus_space_read_8((sc)->sc_iot, (sc)->sc_en_ioh, 0)
423f75081cSvisa #define CIB_EN_WR(sc, val) \
433f75081cSvisa 	bus_space_write_8((sc)->sc_iot, (sc)->sc_en_ioh, 0, (val))
443f75081cSvisa #define CIB_RAW_RD(sc) \
453f75081cSvisa 	bus_space_read_8((sc)->sc_iot, (sc)->sc_raw_ioh, 0)
463f75081cSvisa #define CIB_RAW_WR(sc, val) \
473f75081cSvisa 	bus_space_write_8((sc)->sc_iot, (sc)->sc_raw_ioh, 0, (val))
483f75081cSvisa 
493f75081cSvisa struct octcib_softc;
503f75081cSvisa 
513f75081cSvisa struct octcib_intrhand {
523f75081cSvisa 	LIST_ENTRY(octcib_intrhand) cih_list;
533f75081cSvisa 	int			(*cih_func)(void *);
543f75081cSvisa 	void			*cih_arg;
553f75081cSvisa 	uint32_t		 cih_bit;
563f75081cSvisa 	uint32_t		 cih_flags;
573f75081cSvisa #define CIH_MPSAFE			0x01
583f75081cSvisa #define CIH_EDGE			0x02	/* edge-triggered */
593f75081cSvisa 	struct evcount		 cih_count;
603f75081cSvisa 	unsigned int		 cih_irq;	/* for cih_count */
613f75081cSvisa 	struct octcib_softc	*cih_sc;
623f75081cSvisa };
633f75081cSvisa 
643f75081cSvisa struct octcib_softc {
653f75081cSvisa 	struct device		 sc_dev;
663f75081cSvisa 	void			*sc_ih;
673f75081cSvisa 	bus_space_tag_t		 sc_iot;
683f75081cSvisa 	bus_space_handle_t	 sc_en_ioh;
693f75081cSvisa 	bus_space_handle_t	 sc_raw_ioh;
703f75081cSvisa 
713f75081cSvisa 	LIST_HEAD(, octcib_intrhand) sc_bits[CIB_MAXBITS];
723f75081cSvisa 	uint32_t		 sc_maxbits;
733f75081cSvisa 
743f75081cSvisa 	struct intr_controller	 sc_ic;
753f75081cSvisa };
763f75081cSvisa 
773f75081cSvisa int	 octcib_match(struct device *, void *, void *);
783f75081cSvisa void	 octcib_attach(struct device *, struct device *, void *);
793f75081cSvisa 
803f75081cSvisa void	*octcib_establish(void *, int, int, int, int (*func)(void *),
813f75081cSvisa 	    void *, const char *);
823f75081cSvisa void	 octcib_disestablish(void *);
830dd6b0daSvisa void	 octcib_intr_barrier(void *);
843f75081cSvisa int	 octcib_intr(void *);
853f75081cSvisa 
863f75081cSvisa const struct cfattach octcib_ca = {
873f75081cSvisa 	sizeof(struct octcib_softc), octcib_match, octcib_attach
883f75081cSvisa };
893f75081cSvisa 
903f75081cSvisa struct cfdriver octcib_cd = {
913f75081cSvisa 	NULL, "octcib", DV_DULL
923f75081cSvisa };
933f75081cSvisa 
943f75081cSvisa int
octcib_match(struct device * parent,void * match,void * aux)953f75081cSvisa octcib_match(struct device *parent, void *match, void *aux)
963f75081cSvisa {
973f75081cSvisa 	struct fdt_attach_args *faa = aux;
983f75081cSvisa 
993f75081cSvisa 	return OF_is_compatible(faa->fa_node, "cavium,octeon-7130-cib");
1003f75081cSvisa }
1013f75081cSvisa 
1023f75081cSvisa void
octcib_attach(struct device * parent,struct device * self,void * aux)1033f75081cSvisa octcib_attach(struct device *parent, struct device *self, void *aux)
1043f75081cSvisa {
1053f75081cSvisa 	struct fdt_attach_args *faa = aux;
1063f75081cSvisa 	struct octcib_softc *sc = (struct octcib_softc *)self;
1073f75081cSvisa 	unsigned int i;
1083f75081cSvisa 
1093f75081cSvisa 	if (faa->fa_nreg != 2) {
1103f75081cSvisa 		printf(": expected 2 IO spaces, got %d\n", faa->fa_nreg);
1113f75081cSvisa 		return;
1123f75081cSvisa 	}
1133f75081cSvisa 
1143f75081cSvisa 	sc->sc_iot = faa->fa_iot;
1153f75081cSvisa 	sc->sc_maxbits = OF_getpropint(faa->fa_node, "cavium,max-bits", 0);
1163f75081cSvisa 
1173f75081cSvisa 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size,
1183f75081cSvisa 	    0, &sc->sc_raw_ioh)) {
1193f75081cSvisa 		printf(": could not map RAW\n");
1203f75081cSvisa 		goto error;
1213f75081cSvisa 	}
1223f75081cSvisa 	if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr, faa->fa_reg[1].size,
1233f75081cSvisa 	    0, &sc->sc_en_ioh)) {
1243f75081cSvisa 		printf(": could not map EN\n");
1253f75081cSvisa 		goto error;
1263f75081cSvisa 	}
1273f75081cSvisa 
1283f75081cSvisa 	/* Disable all interrupts. */
1293f75081cSvisa 	CIB_EN_WR(sc, 0);
1303f75081cSvisa 	/* Acknowledge any pending interrupts. */
1313f75081cSvisa 	CIB_RAW_WR(sc, ~0ul);
1323f75081cSvisa 
1333f75081cSvisa 	sc->sc_ih = octeon_intr_establish_fdt(faa->fa_node,
1343f75081cSvisa 	    CIB_HIGHIPL | IPL_MPSAFE, octcib_intr, sc, sc->sc_dev.dv_xname);
1353f75081cSvisa 	if (sc->sc_ih == NULL) {
1363f75081cSvisa 		printf(": failed to register interrupt\n");
1373f75081cSvisa 		goto error;
1383f75081cSvisa 	}
1393f75081cSvisa 
1403f75081cSvisa 	printf(": max-bits %u\n", sc->sc_maxbits);
1413f75081cSvisa 
1423f75081cSvisa 	for (i = 0; i < CIB_MAXBITS; i++)
1433f75081cSvisa 		LIST_INIT(&sc->sc_bits[i]);
1443f75081cSvisa 
1453f75081cSvisa 	sc->sc_ic.ic_cookie = sc;
1463f75081cSvisa 	sc->sc_ic.ic_node = faa->fa_node;
1473f75081cSvisa 	sc->sc_ic.ic_establish_fdt_idx = octcib_establish;
1483f75081cSvisa 	sc->sc_ic.ic_disestablish = octcib_disestablish;
1490dd6b0daSvisa 	sc->sc_ic.ic_intr_barrier = octcib_intr_barrier;
1503f75081cSvisa 	octeon_intr_register(&sc->sc_ic);
1513f75081cSvisa 	return;
1523f75081cSvisa 
1533f75081cSvisa error:
1543f75081cSvisa 	if (sc->sc_en_ioh != 0)
1553f75081cSvisa 		bus_space_unmap(sc->sc_iot, sc->sc_en_ioh,
1563f75081cSvisa 		    faa->fa_reg[1].size);
1573f75081cSvisa 	if (sc->sc_raw_ioh != 0)
1583f75081cSvisa 		bus_space_unmap(sc->sc_iot, sc->sc_raw_ioh,
1593f75081cSvisa 		    faa->fa_reg[0].size);
1603f75081cSvisa }
1613f75081cSvisa 
1623f75081cSvisa void *
octcib_establish(void * cookie,int node,int idx,int level,int (* func)(void *),void * arg,const char * name)1633f75081cSvisa octcib_establish(void *cookie, int node, int idx, int level,
1643f75081cSvisa     int (*func)(void *), void *arg, const char *name)
1653f75081cSvisa {
1663f75081cSvisa 	struct octcib_intrhand *cih;
1673f75081cSvisa 	struct octcib_softc *sc = cookie;
1683f75081cSvisa 	uint64_t en;
1693f75081cSvisa 	uint32_t *cells;
1703f75081cSvisa 	uint32_t bit, type;
1713f75081cSvisa 	int flags, len, s;
1723f75081cSvisa 
1733f75081cSvisa 	flags = (level & IPL_MPSAFE) ? CIH_MPSAFE : 0;
1743f75081cSvisa 	level &= ~IPL_MPSAFE;
1753f75081cSvisa 
1763f75081cSvisa 	if (level > CIB_HIGHIPL)
1773f75081cSvisa 		return NULL;
1783f75081cSvisa 
1793f75081cSvisa 	len = OF_getproplen(node, "interrupts");
1803f75081cSvisa 	if (len / (sizeof(uint32_t) * 2) <= idx ||
1813f75081cSvisa 	    len % (sizeof(uint32_t) * 2) != 0)
1823f75081cSvisa 		return NULL;
1833f75081cSvisa 
1843f75081cSvisa 	cells = malloc(len, M_TEMP, M_NOWAIT);
1853f75081cSvisa 	if (cells == NULL)
1863f75081cSvisa 		return NULL;
1873f75081cSvisa 	OF_getpropintarray(node, "interrupts", cells, len);
1883f75081cSvisa 	bit = cells[idx * 2];
1893f75081cSvisa 	type = cells[idx * 2 + 1];
1903f75081cSvisa 	free(cells, M_TEMP, len);
1913f75081cSvisa 
1923f75081cSvisa 	if (bit >= sc->sc_maxbits)
1933f75081cSvisa 		return NULL;
1943b1275e8Svisa 	if (type != 4)
1953f75081cSvisa 		flags |= CIH_EDGE;
1963f75081cSvisa 
1973f75081cSvisa 	cih = malloc(sizeof(*cih), M_DEVBUF, M_NOWAIT);
1983f75081cSvisa 	if (cih == NULL)
1993f75081cSvisa 		return NULL;
2003f75081cSvisa 	cih->cih_func = func;
2013f75081cSvisa 	cih->cih_arg = arg;
2023f75081cSvisa 	cih->cih_bit = bit;
2033f75081cSvisa 	cih->cih_flags = flags;
2043f75081cSvisa 	cih->cih_irq = CIB_IRQNUM(sc, bit);
2053f75081cSvisa 	cih->cih_sc = sc;
2063f75081cSvisa 
2073f75081cSvisa 	s = splhigh();
2083f75081cSvisa 
2093f75081cSvisa 	evcount_attach(&cih->cih_count, name, &cih->cih_irq);
2103f75081cSvisa 	LIST_INSERT_HEAD(&sc->sc_bits[bit], cih, cih_list);
2113f75081cSvisa 
2123f75081cSvisa 	/* Enable the interrupt. */
2133f75081cSvisa 	en = CIB_EN_RD(sc);
2143f75081cSvisa 	en |= 1ul << bit;
2153f75081cSvisa 	CIB_EN_WR(sc, en);
2163f75081cSvisa 
2173f75081cSvisa 	splx(s);
2183f75081cSvisa 
2193f75081cSvisa 	return cih;
2203f75081cSvisa }
2213f75081cSvisa 
2223f75081cSvisa void
octcib_disestablish(void * cookie)2233f75081cSvisa octcib_disestablish(void *cookie)
2243f75081cSvisa {
2253f75081cSvisa 	struct octcib_intrhand *cih = cookie;
2263f75081cSvisa 	struct octcib_softc *sc = cih->cih_sc;
2273f75081cSvisa 	uint64_t val;
2283f75081cSvisa 	uint32_t bit = cih->cih_bit;
2293f75081cSvisa 	int s;
2303f75081cSvisa #ifdef DIAGNOSTIC
2313f75081cSvisa 	struct octcib_intrhand *tmp;
2323f75081cSvisa 	int found;
2333f75081cSvisa #endif
2343f75081cSvisa 
2353f75081cSvisa 	s = splhigh();
2363f75081cSvisa 
2373f75081cSvisa #ifdef DIAGNOSTIC
2383f75081cSvisa 	found = 0;
2393f75081cSvisa 	LIST_FOREACH(tmp, &sc->sc_bits[bit], cih_list) {
2403f75081cSvisa 		if (tmp == cih) {
2413f75081cSvisa 			found = 1;
2423f75081cSvisa 			break;
2433f75081cSvisa 		}
2443f75081cSvisa 	}
2453f75081cSvisa 	if (found == 0)
2463f75081cSvisa 		panic("%s: intrhand %p not registered", __func__, cih);
2473f75081cSvisa #endif
2483f75081cSvisa 
2493f75081cSvisa 	LIST_REMOVE(cih, cih_list);
2503f75081cSvisa 	evcount_detach(&cih->cih_count);
2513f75081cSvisa 
2523f75081cSvisa 	if (LIST_EMPTY(&sc->sc_bits[bit])) {
2533f75081cSvisa 		/* Disable the interrupt. */
2543f75081cSvisa 		val = CIB_EN_RD(sc);
2553f75081cSvisa 		val &= ~(1ul << bit);
2563f75081cSvisa 		CIB_EN_WR(sc, val);
2573f75081cSvisa 	}
2583f75081cSvisa 
2593f75081cSvisa 	splx(s);
2603f75081cSvisa 
2613f75081cSvisa 	free(cih, M_DEVBUF, sizeof(*cih));
2623f75081cSvisa }
2633f75081cSvisa 
2640dd6b0daSvisa void
octcib_intr_barrier(void * cookie)2650dd6b0daSvisa octcib_intr_barrier(void *cookie)
2660dd6b0daSvisa {
2670dd6b0daSvisa 	struct octcib_intrhand *cih = cookie;
2680dd6b0daSvisa 	struct octcib_softc *sc = cih->cih_sc;
2690dd6b0daSvisa 
2700dd6b0daSvisa 	intr_barrier(sc->sc_ih);
2710dd6b0daSvisa }
2720dd6b0daSvisa 
2733f75081cSvisa int
octcib_intr(void * arg)2743f75081cSvisa octcib_intr(void *arg)
2753f75081cSvisa {
2763f75081cSvisa 	struct octcib_intrhand *cih;
2773f75081cSvisa 	struct octcib_softc *sc = arg;
2783f75081cSvisa 	uint64_t en, isr, mask;
2793f75081cSvisa 	uint32_t bit;
2803f75081cSvisa 	int handled = 0;
2813f75081cSvisa #ifdef MULTIPROCESSOR
2823f75081cSvisa 	int need_lock;
2833f75081cSvisa #endif
2843f75081cSvisa 
2853f75081cSvisa 	en = CIB_EN_RD(sc);
2863f75081cSvisa 	isr = CIB_RAW_RD(sc);
2873f75081cSvisa 	isr &= en;
2883f75081cSvisa 
2893f75081cSvisa 	for (bit = 0; isr != 0 && bit < sc->sc_maxbits; bit++) {
2903f75081cSvisa 		mask = 1ul << bit;
2913f75081cSvisa 
2923f75081cSvisa 		if ((isr & mask) == 0)
2933f75081cSvisa 			continue;
2943f75081cSvisa 		isr &= ~mask;
2953f75081cSvisa 
2963f75081cSvisa 		handled = 0;
2973f75081cSvisa 		LIST_FOREACH(cih, &sc->sc_bits[bit], cih_list) {
2983f75081cSvisa 			/* Acknowledge the interrupt. */
2993f75081cSvisa 			if (ISSET(cih->cih_flags, CIH_EDGE))
3003f75081cSvisa 				CIB_RAW_WR(sc, mask);
3013f75081cSvisa 
3023f75081cSvisa #ifdef MULTIPROCESSOR
3033f75081cSvisa 			if (!ISSET(cih->cih_flags, CIH_MPSAFE))
3043f75081cSvisa 				need_lock = 1;
3053f75081cSvisa 			else
3063f75081cSvisa 				need_lock = 0;
3073f75081cSvisa 			if (need_lock)
3083f75081cSvisa 				__mp_lock(&kernel_lock);
3093f75081cSvisa #endif
3103f75081cSvisa 			if (cih->cih_func(cih->cih_arg)) {
3113f75081cSvisa 				handled = 1;
3123f75081cSvisa 				cih->cih_count.ec_count++;
3133f75081cSvisa 			}
3143f75081cSvisa #ifdef MULTIPROCESSOR
3153f75081cSvisa 			if (need_lock)
3163f75081cSvisa 				__mp_unlock(&kernel_lock);
3173f75081cSvisa #endif
3183f75081cSvisa 		}
3193f75081cSvisa 
3203f75081cSvisa 		if (handled == 0)
321*5e9543b6Svisa 			printf("%s: spurious interrupt %u (bit %u) "
322*5e9543b6Svisa 			    "on cpu %lu\n",
323*5e9543b6Svisa 			    sc->sc_dev.dv_xname, CIB_IRQNUM(sc, bit), bit,
324*5e9543b6Svisa 			    cpu_number());
3253f75081cSvisa 	}
3263f75081cSvisa 
3273f75081cSvisa 	return 1;
3283f75081cSvisa }
329