summaryrefslogtreecommitdiff
path: root/sys/arch
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2020-08-23 19:18:43 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2020-08-23 19:18:43 +0000
commitc38f614ac6902413b6c92ac4ef94848f25510326 (patch)
tree5dca59eabbcdc1c8ee6f20e34c5c5b15670d42de /sys/arch
parentdc86ed14900bbc3c508c5024c5f783e5bcbb9d75 (diff)
Add xicp(4), a driver for the interrupt control presenter hardware found
on POWER8 CPUs.
Diffstat (limited to 'sys/arch')
-rw-r--r--sys/arch/powerpc64/conf/GENERIC3
-rw-r--r--sys/arch/powerpc64/conf/files.powerpc646
-rw-r--r--sys/arch/powerpc64/dev/xicp.c283
3 files changed, 290 insertions, 2 deletions
diff --git a/sys/arch/powerpc64/conf/GENERIC b/sys/arch/powerpc64/conf/GENERIC
index c75ef9d44bb..f22e71a9842 100644
--- a/sys/arch/powerpc64/conf/GENERIC
+++ b/sys/arch/powerpc64/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.15 2020/08/03 19:05:46 deraadt Exp $
+# $OpenBSD: GENERIC,v 1.16 2020/08/23 19:18:42 kettenis Exp $
#
# For further information on compiling OpenBSD kernels, see the config(8)
# man page.
@@ -27,6 +27,7 @@ opalsens* at fdt?
phb* at fdt?
pci* at phb?
xics* at fdt?
+xicp* at fdt?
xive* at fdt?
ppb* at pci?
diff --git a/sys/arch/powerpc64/conf/files.powerpc64 b/sys/arch/powerpc64/conf/files.powerpc64
index 68af464f2e1..2158a027749 100644
--- a/sys/arch/powerpc64/conf/files.powerpc64
+++ b/sys/arch/powerpc64/conf/files.powerpc64
@@ -1,4 +1,4 @@
-# $OpenBSD: files.powerpc64,v 1.22 2020/07/16 19:37:58 kettenis Exp $
+# $OpenBSD: files.powerpc64,v 1.23 2020/08/23 19:18:42 kettenis Exp $
maxpartitions 16
maxusers 2 8 128
@@ -94,6 +94,10 @@ device xics
attach xics at fdt
file arch/powerpc64/dev/xics.c xics
+device xicp
+attach xicp at fdt
+file arch/powerpc64/dev/xicp.c xicp
+
device xive
attach xive at fdt
file arch/powerpc64/dev/xive.c xive
diff --git a/sys/arch/powerpc64/dev/xicp.c b/sys/arch/powerpc64/dev/xicp.c
new file mode 100644
index 00000000000..7ad28e23703
--- /dev/null
+++ b/sys/arch/powerpc64/dev/xicp.c
@@ -0,0 +1,283 @@
+/* $OpenBSD: xicp.c,v 1.1 2020/08/23 19:18:42 kettenis Exp $ */
+/*
+ * Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org>
+ *
+ * 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.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/evcount.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+#include <machine/opal.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/fdt.h>
+
+#define XICP_NUM_IRQS 1024
+
+#define XICP_CPPR 0x04
+#define XICP_XIRR 0x04
+#define XICP_XIRR_XISR_MASK 0x00ffffff
+#define XICP_XIRR_CPPR_SHIFT 24
+#define XICP_MFRR 0x0c
+
+static inline uint8_t
+xicp_prio(int ipl)
+{
+ return ((IPL_IPI - ipl) > 7 ? 0xff : IPL_IPI - ipl);
+}
+
+struct intrhand {
+ LIST_ENTRY(intrhand) ih_hash;
+ int (*ih_func)(void *);
+ void *ih_arg;
+ int ih_ipl;
+ int ih_flags;
+ uint32_t ih_girq;
+ struct evcount ih_count;
+ const char *ih_name;
+};
+
+struct xicp_softc {
+ struct device sc_dev;
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh;
+};
+
+struct xicp_softc *xicp_sc[MAXCPUS];
+
+/* Hash table for interrupt handlers. */
+#define XICP_GIRQHASH(girq) (&xicp_girqhashtbl[(girq) & xicp_girqhash])
+LIST_HEAD(,intrhand) *xicp_girqhashtbl;
+u_long xicp_girqhash;
+
+static inline void
+xicp_write_1(struct xicp_softc *sc, bus_size_t off, uint8_t val)
+{
+ bus_space_write_1(sc->sc_iot, sc->sc_ioh, off, val);
+}
+
+static inline uint32_t
+xicp_read_4(struct xicp_softc *sc, bus_size_t off)
+{
+ return bus_space_read_4(sc->sc_iot, sc->sc_ioh, off);
+}
+
+static inline void
+xicp_write_4(struct xicp_softc *sc, bus_size_t off, uint32_t val)
+{
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, val);
+}
+
+int xicp_match(struct device *, void *, void *);
+void xicp_attach(struct device *, struct device *, void *);
+
+struct cfattach xicp_ca = {
+ sizeof (struct xicp_softc), xicp_match, xicp_attach
+};
+
+struct cfdriver xicp_cd = {
+ NULL, "xicp", DV_DULL
+};
+
+void xicp_exi(struct trapframe *);
+void *xicp_intr_establish(uint32_t, int, int,
+ int (*)(void *), void *, const char *);
+void xicp_intr_send_ipi(void *);
+void xicp_setipl(int);
+
+int
+xicp_match(struct device *parent, void *match, void *aux)
+{
+ struct fdt_attach_args *faa = aux;
+
+ return (OF_is_compatible(faa->fa_node, "ibm,ppc-xicp") ||
+ OF_is_compatible(faa->fa_node, "IBM,ppc-xicp"));
+}
+
+void
+xicp_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct xicp_softc *sc = (struct xicp_softc *)self;
+ struct fdt_attach_args *faa = aux;
+ struct cpu_info *ci;
+ CPU_INFO_ITERATOR cii;
+ uint32_t ranges[2];
+
+ if (faa->fa_nreg < 1) {
+ printf(": no registers\n");
+ return;
+ }
+
+ ranges[0] = ranges[1] = 0;
+ OF_getpropintarray(faa->fa_node, "ibm,interrupt-server-ranges",
+ ranges, sizeof(ranges));
+ if (ranges[1] == 0)
+ return;
+
+ /*
+ * There is supposed to be one ICP node for each core. Since
+ * we only support a single thread, we only need to map the
+ * first set of registers.
+ */
+ sc->sc_iot = faa->fa_iot;
+ if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
+ faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
+ printf(": can't map registers\n");
+ return;
+ }
+
+ printf("\n");
+
+ /*
+ * Allocate global hash table for interrupt handlers if we
+ * haven't done so already.
+ */
+ if (xicp_girqhash == 0) {
+ xicp_girqhashtbl = hashinit(XICP_NUM_IRQS,
+ M_DEVBUF, M_WAITOK, &xicp_girqhash);
+ }
+
+ CPU_INFO_FOREACH(cii, ci) {
+ if (ranges[0] == ci->ci_pir)
+ xicp_sc[ci->ci_cpuid] = sc;
+ }
+
+ _exi = xicp_exi;
+ _intr_establish = xicp_intr_establish;
+ _intr_send_ipi = xicp_intr_send_ipi;
+ _setipl = xicp_setipl;
+
+ /* Synchronize hardware state to software state. */
+ xicp_write_1(sc, XICP_CPPR, xicp_prio(curcpu()->ci_cpl));
+}
+
+void
+xicp_intr_send_ipi(void *cookie)
+{
+ panic("%s", __func__);
+}
+
+void *
+xicp_intr_establish(uint32_t girq, int type, int level,
+ int (*func)(void *), void *arg, const char *name)
+{
+ struct cpu_info *ci = cpu_info_primary;
+ struct intrhand *ih;
+ int64_t error;
+ uint16_t server;
+
+ ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK);
+ ih->ih_func = func;
+ ih->ih_arg = arg;
+ ih->ih_ipl = level & IPL_IRQMASK;
+ ih->ih_flags = level & IPL_FLAGMASK;
+ ih->ih_girq = girq;
+ ih->ih_name = name;
+ LIST_INSERT_HEAD(XICP_GIRQHASH(girq), ih, ih_hash);
+
+ if (name != NULL)
+ evcount_attach(&ih->ih_count, name, &ih->ih_girq);
+
+ server = ci->ci_pir << 2;
+ error = opal_set_xive(girq, server, xicp_prio(level & IPL_IRQMASK));
+ if (error != OPAL_SUCCESS) {
+ if (name)
+ evcount_detach(&ih->ih_count);
+ LIST_REMOVE(ih, ih_hash);
+ free(ih, M_DEVBUF, sizeof(ih));
+ return NULL;
+ }
+
+ return ih;
+}
+
+void
+xicp_setipl(int new)
+{
+ struct xicp_softc *sc = xicp_sc[cpu_number()];
+ struct cpu_info *ci = curcpu();
+ uint8_t oldprio = xicp_prio(ci->ci_cpl);
+ uint8_t newprio = xicp_prio(new);
+ u_long msr;
+
+ msr = intr_disable();
+ ci->ci_cpl = new;
+ if (newprio != oldprio)
+ xicp_write_1(sc, XICP_CPPR, newprio);
+ intr_restore(msr);
+}
+
+void
+xicp_exi(struct trapframe *frame)
+{
+ struct xicp_softc *sc = xicp_sc[cpu_number()];
+ struct cpu_info *ci = curcpu();
+ struct intrhand *ih;
+ uint32_t xirr, xisr;
+ int handled, old;
+
+ KASSERT(sc);
+
+ old = ci->ci_cpl;
+
+ while (1) {
+ xirr = xicp_read_4(sc, XICP_XIRR);
+ xisr = xirr & XICP_XIRR_XISR_MASK;
+
+ if (xisr == 0)
+ break;
+
+ /* Lookup the interrupt handle in the has table. */
+ LIST_FOREACH(ih, XICP_GIRQHASH(xisr), ih_hash) {
+ if (ih->ih_girq == xisr)
+ break;
+ }
+
+ if (ih != NULL) {
+#ifdef MULTIPROCESSOR
+ int need_lock;
+
+ if (ih->ih_flags & IPL_MPSAFE)
+ need_lock = 0;
+ else
+ need_lock = (ih->ih_ipl < IPL_SCHED);
+
+ if (need_lock)
+ KERNEL_LOCK();
+#endif
+ ci->ci_cpl = ih->ih_ipl;
+ xicp_write_1(sc, XICP_CPPR, xicp_prio(ih->ih_ipl));
+
+ intr_enable();
+ handled = ih->ih_func(ih->ih_arg);
+ intr_disable();
+ if (handled)
+ ih->ih_count.ec_count++;
+#ifdef MULTIPROCESSOR
+ if (need_lock)
+ KERNEL_UNLOCK();
+#endif
+ }
+
+ /* Signal EOI. */
+ xicp_write_4(sc, XICP_XIRR, xirr);
+ ci->ci_cpl = old;
+ }
+}