diff options
author | Visa Hankala <visa@cvs.openbsd.org> | 2017-07-13 12:50:51 +0000 |
---|---|---|
committer | Visa Hankala <visa@cvs.openbsd.org> | 2017-07-13 12:50:51 +0000 |
commit | d515ae33fe6c62e3ec7648d28f9c243543ddaeed (patch) | |
tree | 3581eb149326e778ea5d622485c19db27be61f7c /sys/arch | |
parent | eb7bfe35527a48ed28c0190d658d6ff38077111c (diff) |
Add a driver for the CIB interrupt controller. Certain device
controllers need it on CN70xx/CN71xx.
Diffstat (limited to 'sys/arch')
-rw-r--r-- | sys/arch/octeon/conf/GENERIC | 3 | ||||
-rw-r--r-- | sys/arch/octeon/conf/RAMDISK | 3 | ||||
-rw-r--r-- | sys/arch/octeon/conf/files.octeon | 6 | ||||
-rw-r--r-- | sys/arch/octeon/dev/octcib.c | 314 |
4 files changed, 323 insertions, 3 deletions
diff --git a/sys/arch/octeon/conf/GENERIC b/sys/arch/octeon/conf/GENERIC index cec46389cb2..e791f7b0c53 100644 --- a/sys/arch/octeon/conf/GENERIC +++ b/sys/arch/octeon/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.34 2017/07/03 08:17:20 visa Exp $ +# $OpenBSD: GENERIC,v 1.35 2017/07/13 12:50:50 visa Exp $ # # For further information on compiling OpenBSD kernels, see the config(8) # man page. @@ -49,6 +49,7 @@ pci* at ppb? cn30xxsmi* at fdt? # MDIO controller com* at fdt? +octcib* at fdt? # Interrupt controller octciu* at fdt? # Interrupt controller v1 octmmc* at fdt? # MMC host controller sdmmc* at octmmc? # SD/MMC bus diff --git a/sys/arch/octeon/conf/RAMDISK b/sys/arch/octeon/conf/RAMDISK index 0e9d6413a91..1486f8bf979 100644 --- a/sys/arch/octeon/conf/RAMDISK +++ b/sys/arch/octeon/conf/RAMDISK @@ -1,4 +1,4 @@ -# $OpenBSD: RAMDISK,v 1.32 2017/07/03 08:17:20 visa Exp $ +# $OpenBSD: RAMDISK,v 1.33 2017/07/13 12:50:50 visa Exp $ machine octeon mips64 maxusers 4 @@ -50,6 +50,7 @@ pci* at ppb? cn30xxsmi* at fdt? # MDIO controller com* at fdt? +octcib* at fdt? # Interrupt controller octciu* at fdt? # Interrupt controller v1 octmmc* at fdt? # MMC host controller sdmmc* at octmmc? # SD/MMC bus diff --git a/sys/arch/octeon/conf/files.octeon b/sys/arch/octeon/conf/files.octeon index d8d521902db..5549c6be693 100644 --- a/sys/arch/octeon/conf/files.octeon +++ b/sys/arch/octeon/conf/files.octeon @@ -1,4 +1,4 @@ -# $OpenBSD: files.octeon,v 1.38 2017/07/03 08:17:20 visa Exp $ +# $OpenBSD: files.octeon,v 1.39 2017/07/13 12:50:50 visa Exp $ # Standard stanzas config(8) can't run without maxpartitions 16 @@ -125,6 +125,10 @@ file arch/octeon/dev/octeon_bus_space.c file arch/octeon/octeon/pciide_machdep.c pciide +device octcib +attach octcib at fdt +file arch/octeon/dev/octcib.c octcib + device octciu attach octciu at fdt file arch/octeon/dev/octciu.c octciu diff --git a/sys/arch/octeon/dev/octcib.c b/sys/arch/octeon/dev/octcib.c new file mode 100644 index 00000000000..ebc1ebb8d8d --- /dev/null +++ b/sys/arch/octeon/dev/octcib.c @@ -0,0 +1,314 @@ +/* $OpenBSD: octcib.c,v 1.1 2017/07/13 12:50:50 visa Exp $ */ + +/* + * Copyright (c) 2017 Visa Hankala + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Driver for Cavium Interrupt Bus (CIB) widget. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/malloc.h> +#include <sys/queue.h> + +#include <dev/ofw/fdt.h> +#include <dev/ofw/openfirm.h> + +#include <machine/fdt.h> + +#define CIB_HIGHIPL IPL_BIO +#define CIB_MAXBITS 64 +#define CIB_IRQNUM(sc, bit) (256 + (sc)->sc_dev.dv_unit * CIB_MAXBITS + \ + (bit)) + +#define CIB_EN_RD(sc) \ + bus_space_read_8((sc)->sc_iot, (sc)->sc_en_ioh, 0) +#define CIB_EN_WR(sc, val) \ + bus_space_write_8((sc)->sc_iot, (sc)->sc_en_ioh, 0, (val)) +#define CIB_RAW_RD(sc) \ + bus_space_read_8((sc)->sc_iot, (sc)->sc_raw_ioh, 0) +#define CIB_RAW_WR(sc, val) \ + bus_space_write_8((sc)->sc_iot, (sc)->sc_raw_ioh, 0, (val)) + +struct octcib_softc; + +struct octcib_intrhand { + LIST_ENTRY(octcib_intrhand) cih_list; + int (*cih_func)(void *); + void *cih_arg; + uint32_t cih_bit; + uint32_t cih_flags; +#define CIH_MPSAFE 0x01 +#define CIH_EDGE 0x02 /* edge-triggered */ + struct evcount cih_count; + unsigned int cih_irq; /* for cih_count */ + struct octcib_softc *cih_sc; +}; + +struct octcib_softc { + struct device sc_dev; + void *sc_ih; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_en_ioh; + bus_space_handle_t sc_raw_ioh; + + LIST_HEAD(, octcib_intrhand) sc_bits[CIB_MAXBITS]; + uint32_t sc_maxbits; + + struct intr_controller sc_ic; +}; + +int octcib_match(struct device *, void *, void *); +void octcib_attach(struct device *, struct device *, void *); + +void *octcib_establish(void *, int, int, int, int (*func)(void *), + void *, const char *); +void octcib_disestablish(void *); +int octcib_intr(void *); + +const struct cfattach octcib_ca = { + sizeof(struct octcib_softc), octcib_match, octcib_attach +}; + +struct cfdriver octcib_cd = { + NULL, "octcib", DV_DULL +}; + +int +octcib_match(struct device *parent, void *match, void *aux) +{ + struct fdt_attach_args *faa = aux; + + return OF_is_compatible(faa->fa_node, "cavium,octeon-7130-cib"); +} + +void +octcib_attach(struct device *parent, struct device *self, void *aux) +{ + struct fdt_attach_args *faa = aux; + struct octcib_softc *sc = (struct octcib_softc *)self; + unsigned int i; + + if (faa->fa_nreg != 2) { + printf(": expected 2 IO spaces, got %d\n", faa->fa_nreg); + return; + } + + sc->sc_iot = faa->fa_iot; + sc->sc_maxbits = OF_getpropint(faa->fa_node, "cavium,max-bits", 0); + + if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size, + 0, &sc->sc_raw_ioh)) { + printf(": could not map RAW\n"); + goto error; + } + if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr, faa->fa_reg[1].size, + 0, &sc->sc_en_ioh)) { + printf(": could not map EN\n"); + goto error; + } + + /* Disable all interrupts. */ + CIB_EN_WR(sc, 0); + /* Acknowledge any pending interrupts. */ + CIB_RAW_WR(sc, ~0ul); + + sc->sc_ih = octeon_intr_establish_fdt(faa->fa_node, + CIB_HIGHIPL | IPL_MPSAFE, octcib_intr, sc, sc->sc_dev.dv_xname); + if (sc->sc_ih == NULL) { + printf(": failed to register interrupt\n"); + goto error; + } + + printf(": max-bits %u\n", sc->sc_maxbits); + + for (i = 0; i < CIB_MAXBITS; i++) + LIST_INIT(&sc->sc_bits[i]); + + sc->sc_ic.ic_cookie = sc; + sc->sc_ic.ic_node = faa->fa_node; + sc->sc_ic.ic_establish_fdt_idx = octcib_establish; + sc->sc_ic.ic_disestablish = octcib_disestablish; + octeon_intr_register(&sc->sc_ic); + return; + +error: + if (sc->sc_en_ioh != 0) + bus_space_unmap(sc->sc_iot, sc->sc_en_ioh, + faa->fa_reg[1].size); + if (sc->sc_raw_ioh != 0) + bus_space_unmap(sc->sc_iot, sc->sc_raw_ioh, + faa->fa_reg[0].size); +} + +void * +octcib_establish(void *cookie, int node, int idx, int level, + int (*func)(void *), void *arg, const char *name) +{ + struct octcib_intrhand *cih; + struct octcib_softc *sc = cookie; + uint64_t en; + uint32_t *cells; + uint32_t bit, type; + int flags, len, s; + + flags = (level & IPL_MPSAFE) ? CIH_MPSAFE : 0; + level &= ~IPL_MPSAFE; + + if (level > CIB_HIGHIPL) + return NULL; + + len = OF_getproplen(node, "interrupts"); + if (len / (sizeof(uint32_t) * 2) <= idx || + len % (sizeof(uint32_t) * 2) != 0) + return NULL; + + cells = malloc(len, M_TEMP, M_NOWAIT); + if (cells == NULL) + return NULL; + OF_getpropintarray(node, "interrupts", cells, len); + bit = cells[idx * 2]; + type = cells[idx * 2 + 1]; + free(cells, M_TEMP, len); + + if (bit >= sc->sc_maxbits) + return NULL; + if (type == 1 || type == 2) + flags |= CIH_EDGE; + + cih = malloc(sizeof(*cih), M_DEVBUF, M_NOWAIT); + if (cih == NULL) + return NULL; + cih->cih_func = func; + cih->cih_arg = arg; + cih->cih_bit = bit; + cih->cih_flags = flags; + cih->cih_irq = CIB_IRQNUM(sc, bit); + cih->cih_sc = sc; + + s = splhigh(); + + evcount_attach(&cih->cih_count, name, &cih->cih_irq); + LIST_INSERT_HEAD(&sc->sc_bits[bit], cih, cih_list); + + /* Enable the interrupt. */ + en = CIB_EN_RD(sc); + en |= 1ul << bit; + CIB_EN_WR(sc, en); + + splx(s); + + return cih; +} + +void +octcib_disestablish(void *cookie) +{ + struct octcib_intrhand *cih = cookie; + struct octcib_softc *sc = cih->cih_sc; + uint64_t val; + uint32_t bit = cih->cih_bit; + int s; +#ifdef DIAGNOSTIC + struct octcib_intrhand *tmp; + int found; +#endif + + s = splhigh(); + +#ifdef DIAGNOSTIC + found = 0; + LIST_FOREACH(tmp, &sc->sc_bits[bit], cih_list) { + if (tmp == cih) { + found = 1; + break; + } + } + if (found == 0) + panic("%s: intrhand %p not registered", __func__, cih); +#endif + + LIST_REMOVE(cih, cih_list); + evcount_detach(&cih->cih_count); + + if (LIST_EMPTY(&sc->sc_bits[bit])) { + /* Disable the interrupt. */ + val = CIB_EN_RD(sc); + val &= ~(1ul << bit); + CIB_EN_WR(sc, val); + } + + splx(s); + + free(cih, M_DEVBUF, sizeof(*cih)); +} + +int +octcib_intr(void *arg) +{ + struct octcib_intrhand *cih; + struct octcib_softc *sc = arg; + uint64_t en, isr, mask; + uint32_t bit; + int handled = 0; +#ifdef MULTIPROCESSOR + int need_lock; +#endif + + en = CIB_EN_RD(sc); + isr = CIB_RAW_RD(sc); + isr &= en; + + for (bit = 0; isr != 0 && bit < sc->sc_maxbits; bit++) { + mask = 1ul << bit; + + if ((isr & mask) == 0) + continue; + isr &= ~mask; + + handled = 0; + LIST_FOREACH(cih, &sc->sc_bits[bit], cih_list) { + /* Acknowledge the interrupt. */ + if (ISSET(cih->cih_flags, CIH_EDGE)) + CIB_RAW_WR(sc, mask); + +#ifdef MULTIPROCESSOR + if (!ISSET(cih->cih_flags, CIH_MPSAFE)) + need_lock = 1; + else + need_lock = 0; + if (need_lock) + __mp_lock(&kernel_lock); +#endif + if (cih->cih_func(cih->cih_arg)) { + handled = 1; + cih->cih_count.ec_count++; + } +#ifdef MULTIPROCESSOR + if (need_lock) + __mp_unlock(&kernel_lock); +#endif + } + + if (handled == 0) + printf("%s: spurious interrupt %u (bit %u)\n", + sc->sc_dev.dv_xname, CIB_IRQNUM(sc, bit), bit); + } + + return 1; +} |