summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorVisa Hankala <visa@cvs.openbsd.org>2017-07-13 12:50:51 +0000
committerVisa Hankala <visa@cvs.openbsd.org>2017-07-13 12:50:51 +0000
commitd515ae33fe6c62e3ec7648d28f9c243543ddaeed (patch)
tree3581eb149326e778ea5d622485c19db27be61f7c /sys
parenteb7bfe35527a48ed28c0190d658d6ff38077111c (diff)
Add a driver for the CIB interrupt controller. Certain device
controllers need it on CN70xx/CN71xx.
Diffstat (limited to 'sys')
-rw-r--r--sys/arch/octeon/conf/GENERIC3
-rw-r--r--sys/arch/octeon/conf/RAMDISK3
-rw-r--r--sys/arch/octeon/conf/files.octeon6
-rw-r--r--sys/arch/octeon/dev/octcib.c314
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;
+}