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