summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorPatrick Wildt <patrick@cvs.openbsd.org>2023-05-19 21:26:11 +0000
committerPatrick Wildt <patrick@cvs.openbsd.org>2023-05-19 21:26:11 +0000
commit5d4ba52e1ddac4de815c8a58146cec0a52f1bca8 (patch)
tree5d0d0c45e73dbe125f35ab4575a4dd14823cd0da /sys
parent758558c00d30df9429a130a29b6cb3f3c56c0ac8 (diff)
Add qcsmptp(4), a driver to share 32-bit values between (co-)processors.
The inbound path is typically used as interrupt controller, e.g. to handle handover and ready interrupts when a remoteproc boots. The outbound path seems to be used to stop cores, but we don't do that yet. ok kettenis@
Diffstat (limited to 'sys')
-rw-r--r--sys/arch/arm64/conf/GENERIC3
-rw-r--r--sys/arch/arm64/conf/RAMDISK3
-rw-r--r--sys/dev/fdt/files.fdt7
-rw-r--r--sys/dev/fdt/qcsmptp.c384
4 files changed, 394 insertions, 3 deletions
diff --git a/sys/arch/arm64/conf/GENERIC b/sys/arch/arm64/conf/GENERIC
index 5fc10417ff0..158e9d21e4d 100644
--- a/sys/arch/arm64/conf/GENERIC
+++ b/sys/arch/arm64/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.270 2023/05/19 21:13:49 patrick Exp $
+# $OpenBSD: GENERIC,v 1.271 2023/05/19 21:26:09 patrick Exp $
#
# GENERIC machine description file
#
@@ -331,6 +331,7 @@ qcmtx* at fdt? early 1
qcpdc* at fdt?
qcscm* at fdt?
qcsmem* at fdt?
+qcsmptp* at fdt?
qcspmi* at fdt?
qcpmic* at qcspmi?
qcpmicgpio* at qcpmic?
diff --git a/sys/arch/arm64/conf/RAMDISK b/sys/arch/arm64/conf/RAMDISK
index b892bd9964b..a33a70a1c83 100644
--- a/sys/arch/arm64/conf/RAMDISK
+++ b/sys/arch/arm64/conf/RAMDISK
@@ -1,4 +1,4 @@
-# $OpenBSD: RAMDISK,v 1.203 2023/05/19 21:13:49 patrick Exp $
+# $OpenBSD: RAMDISK,v 1.204 2023/05/19 21:26:09 patrick Exp $
machine arm64
maxusers 4
@@ -255,6 +255,7 @@ qcmtx* at fdt? early 1
qcpdc* at fdt?
qcscm* at fdt?
qcsmem* at fdt?
+qcsmptp* at fdt?
qcspmi* at fdt?
qcpmic* at qcspmi?
qcpmicgpio* at qcpmic?
diff --git a/sys/dev/fdt/files.fdt b/sys/dev/fdt/files.fdt
index 48ee7667ee2..1d19d5a00e2 100644
--- a/sys/dev/fdt/files.fdt
+++ b/sys/dev/fdt/files.fdt
@@ -1,4 +1,4 @@
-# $OpenBSD: files.fdt,v 1.190 2023/05/19 21:13:49 patrick Exp $
+# $OpenBSD: files.fdt,v 1.191 2023/05/19 21:26:10 patrick Exp $
#
# Config file and device description for machine-independent FDT code.
# Included by ports that need it.
@@ -696,6 +696,11 @@ device qcsmem
attach qcsmem at fdt
file dev/fdt/qcsmem.c qcsmem
+# Qualcomm Shared Memory Point-to-Point
+device qcsmptp
+attach qcsmptp at fdt
+file dev/fdt/qcsmptp.c qcsmptp
+
# Qualcomm SPMI controller
device qcspmi: spmi
attach qcspmi at fdt
diff --git a/sys/dev/fdt/qcsmptp.c b/sys/dev/fdt/qcsmptp.c
new file mode 100644
index 00000000000..50fe85786ac
--- /dev/null
+++ b/sys/dev/fdt/qcsmptp.c
@@ -0,0 +1,384 @@
+/* $OpenBSD: qcsmptp.c,v 1.1 2023/05/19 21:26:10 patrick Exp $ */
+/*
+ * Copyright (c) 2023 Patrick Wildt <patrick@blueri.se>
+ *
+ * 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/malloc.h>
+#include <sys/atomic.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_misc.h>
+#include <dev/ofw/fdt.h>
+
+#define SMP2P_MAX_ENTRY 16
+#define SMP2P_MAX_ENTRY_NAME 16
+
+struct qcsmptp_smem_item {
+ uint32_t magic;
+#define SMP2P_MAGIC 0x504d5324
+ uint8_t version;
+#define SMP2P_VERSION 1
+ unsigned features:24;
+#define SMP2P_FEATURE_SSR_ACK (1 << 0)
+ uint16_t local_pid;
+ uint16_t remote_pid;
+ uint16_t total_entries;
+ uint16_t valid_entries;
+ uint32_t flags;
+#define SMP2P_FLAGS_RESTART_DONE (1 << 0)
+#define SMP2P_FLAGS_RESTART_ACK (1 << 1)
+
+ struct {
+ uint8_t name[SMP2P_MAX_ENTRY_NAME];
+ uint32_t value;
+ } entries[SMP2P_MAX_ENTRY];
+} __packed;
+
+struct qcsmptp_intrhand {
+ TAILQ_ENTRY(qcsmptp_intrhand) ih_q;
+ int (*ih_func)(void *);
+ void *ih_arg;
+ void *ih_ic;
+ int ih_pin;
+ int ih_enabled;
+};
+
+struct qcsmptp_interrupt_controller {
+ TAILQ_HEAD(,qcsmptp_intrhand) ic_intrq;
+ struct qcsmptp_softc *ic_sc;
+ struct interrupt_controller ic_ic;
+};
+
+struct qcsmptp_entry {
+ TAILQ_ENTRY(qcsmptp_entry) e_q;
+ char *e_name;
+ int e_namelen;
+ uint32_t *e_value;
+ uint32_t e_last_value;
+ struct qcsmptp_interrupt_controller *e_ic;
+};
+
+struct qcsmptp_softc {
+ struct device sc_dev;
+ int sc_node;
+ void *sc_ih;
+
+ uint16_t sc_local_pid;
+ uint16_t sc_remote_pid;
+ uint32_t sc_smem_id[2];
+
+ struct qcsmptp_smem_item *sc_in;
+ struct qcsmptp_smem_item *sc_out;
+
+ TAILQ_HEAD(,qcsmptp_entry) sc_inboundq;
+ TAILQ_HEAD(,qcsmptp_entry) sc_outboundq;
+
+ int sc_negotiated;
+ int sc_ssr_ack_enabled;
+ int sc_ssr_ack;
+
+ uint16_t sc_valid_entries;
+
+ struct mbox_channel *sc_mc;
+};
+
+int qcsmptp_match(struct device *, void *, void *);
+void qcsmptp_attach(struct device *, struct device *, void *);
+void qcsmptp_deferred(struct device *);
+
+const struct cfattach qcsmptp_ca = {
+ sizeof (struct qcsmptp_softc), qcsmptp_match, qcsmptp_attach
+};
+
+struct cfdriver qcsmptp_cd = {
+ NULL, "qcsmptp", DV_DULL
+};
+
+int qcsmptp_intr(void *);
+void *qcsmptp_intr_establish(void *, int *, int, struct cpu_info *,
+ int (*)(void *), void *, char *);
+void qcsmptp_intr_disestablish(void *);
+void qcsmptp_intr_enable(void *);
+void qcsmptp_intr_disable(void *);
+void qcsmptp_intr_barrier(void *);
+
+extern int qcsmem_alloc(int, int, int);
+extern void *qcsmem_get(int, int, int *);
+
+int
+qcsmptp_match(struct device *parent, void *match, void *aux)
+{
+ struct fdt_attach_args *faa = aux;
+
+ return OF_is_compatible(faa->fa_node, "qcom,smp2p");
+}
+
+void
+qcsmptp_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct qcsmptp_softc *sc = (struct qcsmptp_softc *)self;
+ struct fdt_attach_args *faa = aux;
+
+ sc->sc_node = faa->fa_node;
+
+ TAILQ_INIT(&sc->sc_inboundq);
+ TAILQ_INIT(&sc->sc_outboundq);
+
+ sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_BIO,
+ qcsmptp_intr, sc, sc->sc_dev.dv_xname);
+ if (sc->sc_ih == NULL) {
+ printf(": can't establish interrupt\n");
+ return;
+ }
+
+ sc->sc_local_pid = OF_getpropint(faa->fa_node,
+ "qcom,local-pid", 0xffff);
+ sc->sc_remote_pid = OF_getpropint(faa->fa_node,
+ "qcom,remote-pid", 0xffff);
+ if (sc->sc_local_pid == 0xffff || sc->sc_remote_pid == 0xffff) {
+ printf(": can't get pids\n");
+ return;
+ }
+
+ if (OF_getpropintarray(faa->fa_node, "qcom,smem", sc->sc_smem_id,
+ sizeof(sc->sc_smem_id)) != sizeof(sc->sc_smem_id)) {
+ printf(": can't get smem property \n");
+ return;
+ }
+
+ printf("\n");
+
+ config_defer(self, qcsmptp_deferred);
+}
+
+void
+qcsmptp_deferred(struct device *self)
+{
+ struct qcsmptp_softc *sc = (struct qcsmptp_softc *)self;
+ struct qcsmptp_entry *e;
+ int node, size;
+
+ sc->sc_mc = mbox_channel_idx(sc->sc_node, 0, NULL);
+ if (sc->sc_mc == NULL) {
+ printf(": can't get mailbox\n");
+ return;
+ }
+
+ if (qcsmem_alloc(sc->sc_remote_pid, sc->sc_smem_id[1],
+ sizeof(*sc->sc_out)) != 0) {
+ printf(": can't alloc smp2p item\n");
+ return;
+ }
+
+ sc->sc_out = qcsmem_get(sc->sc_remote_pid, sc->sc_smem_id[1], NULL);
+ if (sc->sc_out == NULL) {
+ printf(": can't get smp2p item\n");
+ return;
+ }
+
+ memset(sc->sc_out, 0, sizeof(*sc->sc_out));
+ sc->sc_out->magic = SMP2P_MAGIC;
+ sc->sc_out->local_pid = sc->sc_local_pid;
+ sc->sc_out->remote_pid = sc->sc_remote_pid;
+ sc->sc_out->total_entries = SMP2P_MAX_ENTRY;
+ sc->sc_out->features = SMP2P_FEATURE_SSR_ACK;
+ membar_sync();
+ sc->sc_out->version = SMP2P_VERSION;
+ mbox_send(sc->sc_mc, NULL, 0);
+
+ for (node = OF_child(sc->sc_node); node; node = OF_peer(node)) {
+ size = OF_getproplen(node, "qcom,entry-name");
+ if (size <= 0)
+ continue;
+ e = malloc(sizeof(*e), M_DEVBUF, M_WAITOK | M_ZERO);
+ e->e_namelen = size;
+ e->e_name = malloc(e->e_namelen, M_DEVBUF, M_WAITOK);
+ OF_getprop(node, "qcom,entry-name", e->e_name,
+ e->e_namelen);
+ e->e_name[e->e_namelen - 1] = '\0';
+
+ if (OF_getproplen(node, "interrupt-controller") == 0) {
+ struct qcsmptp_interrupt_controller *ic;
+ ic = malloc(sizeof(*ic), M_DEVBUF, M_WAITOK | M_ZERO);
+ TAILQ_INIT(&ic->ic_intrq);
+ ic->ic_sc = sc;
+ ic->ic_ic.ic_node = node;
+ ic->ic_ic.ic_cookie = ic;
+ ic->ic_ic.ic_establish = qcsmptp_intr_establish;
+ ic->ic_ic.ic_disestablish = qcsmptp_intr_disestablish;
+ ic->ic_ic.ic_enable = qcsmptp_intr_enable;
+ ic->ic_ic.ic_disable = qcsmptp_intr_disable;
+ ic->ic_ic.ic_barrier = qcsmptp_intr_barrier;
+ fdt_intr_register(&ic->ic_ic);
+ e->e_ic = ic;
+ TAILQ_INSERT_TAIL(&sc->sc_inboundq, e, e_q);
+ } else {
+ if (sc->sc_out->valid_entries >= SMP2P_MAX_ENTRY)
+ continue;
+
+ strlcpy(sc->sc_out->entries[sc->sc_out->valid_entries].name,
+ e->e_name, SMP2P_MAX_ENTRY_NAME);
+ e->e_value = &sc->sc_out->entries[sc->sc_out->valid_entries].value;
+ sc->sc_out->valid_entries++;
+ TAILQ_INSERT_TAIL(&sc->sc_outboundq, e, e_q);
+
+ /* TODO: provide as smem state */
+ }
+ }
+}
+
+int
+qcsmptp_intr(void *arg)
+{
+ struct qcsmptp_softc *sc = arg;
+ struct qcsmptp_entry *e;
+ struct qcsmptp_intrhand *ih;
+ uint32_t changed, val;
+ int do_ack = 0, i;
+
+ /* Inbound item exists as soon as remoteproc is up. */
+ if (sc->sc_in == NULL)
+ sc->sc_in = qcsmem_get(sc->sc_remote_pid,
+ sc->sc_smem_id[0], NULL);
+ if (sc->sc_in == NULL) {
+ printf("%s: can't get smp2p item\n", sc->sc_dev.dv_xname);
+ return 1;
+ }
+
+ /* Do initial feature negotiation if inbound is new. */
+ if (!sc->sc_negotiated) {
+ if (sc->sc_in->version != sc->sc_out->version)
+ return 1;
+ sc->sc_out->features &= sc->sc_in->features;
+ if (sc->sc_out->features & SMP2P_FEATURE_SSR_ACK)
+ sc->sc_ssr_ack_enabled = 1;
+ sc->sc_negotiated = 1;
+ }
+ if (!sc->sc_negotiated)
+ return 1;
+
+ /* Use ACK mechanism if negotiated. */
+ if (sc->sc_ssr_ack_enabled &&
+ !!(sc->sc_in->flags & SMP2P_FLAGS_RESTART_DONE) != sc->sc_ssr_ack)
+ do_ack = 1;
+
+ /* Catch up on new inbound entries that got added in the meantime. */
+ for (i = sc->sc_valid_entries; i < sc->sc_in->valid_entries; i++) {
+ TAILQ_FOREACH(e, &sc->sc_inboundq, e_q) {
+ if (strncmp(sc->sc_in->entries[i].name, e->e_name,
+ SMP2P_MAX_ENTRY_NAME) != 0)
+ continue;
+ e->e_value = &sc->sc_in->entries[i].value;
+ }
+ }
+ sc->sc_valid_entries = i;
+
+ /* For each inbound "interrupt controller". */
+ TAILQ_FOREACH(e, &sc->sc_inboundq, e_q) {
+ if (e->e_value == NULL)
+ continue;
+ val = *e->e_value;
+ if (val == e->e_last_value)
+ continue;
+ changed = val ^ e->e_last_value;
+ e->e_last_value = val;
+ TAILQ_FOREACH(ih, &e->e_ic->ic_intrq, ih_q) {
+ if (!ih->ih_enabled)
+ continue;
+ if ((changed & (1 << ih->ih_pin)) == 0)
+ continue;
+ ih->ih_func(ih->ih_arg);
+ }
+ }
+
+ if (do_ack) {
+ sc->sc_ssr_ack = !sc->sc_ssr_ack;
+ if (sc->sc_ssr_ack)
+ sc->sc_out->flags |= SMP2P_FLAGS_RESTART_ACK;
+ else
+ sc->sc_out->flags &= ~SMP2P_FLAGS_RESTART_ACK;
+ membar_sync();
+ mbox_send(sc->sc_mc, NULL, 0);
+ }
+
+ return 1;
+}
+
+void *
+qcsmptp_intr_establish(void *cookie, int *cells, int ipl,
+ struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
+{
+ struct qcsmptp_interrupt_controller *ic = cookie;
+ struct qcsmptp_softc *sc = ic->ic_sc;
+ struct qcsmptp_intrhand *ih;
+
+ ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK | M_ZERO);
+ ih->ih_func = func;
+ ih->ih_arg = arg;
+ ih->ih_ic = ic;
+ ih->ih_pin = cells[0];
+ TAILQ_INSERT_TAIL(&ic->ic_intrq, ih, ih_q);
+
+ qcsmptp_intr_enable(ih);
+
+ if (ipl & IPL_WAKEUP)
+ intr_set_wakeup(sc->sc_ih);
+
+ return ih;
+}
+
+void
+qcsmptp_intr_disestablish(void *cookie)
+{
+ struct qcsmptp_intrhand *ih = cookie;
+ struct qcsmptp_interrupt_controller *ic = ih->ih_ic;
+
+ qcsmptp_intr_disable(ih);
+
+ TAILQ_REMOVE(&ic->ic_intrq, ih, ih_q);
+ free(ih, M_DEVBUF, sizeof(*ih));
+}
+
+void
+qcsmptp_intr_enable(void *cookie)
+{
+ struct qcsmptp_intrhand *ih = cookie;
+
+ ih->ih_enabled = 1;
+}
+
+void
+qcsmptp_intr_disable(void *cookie)
+{
+ struct qcsmptp_intrhand *ih = cookie;
+
+ ih->ih_enabled = 0;
+}
+
+void
+qcsmptp_intr_barrier(void *cookie)
+{
+ struct qcsmptp_intrhand *ih = cookie;
+ struct qcsmptp_interrupt_controller *ic = ih->ih_ic;
+ struct qcsmptp_softc *sc = ic->ic_sc;
+
+ intr_barrier(sc->sc_ih);
+}