From 0b5b3cbd3b87324f32dea9ea52dd53ec90fd8c7a Mon Sep 17 00:00:00 2001 From: patrick Date: Fri, 19 May 2023 21:26:09 +0000 Subject: [PATCH] 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@ --- sys/arch/arm64/conf/GENERIC | 3 +- sys/arch/arm64/conf/RAMDISK | 3 +- sys/dev/fdt/files.fdt | 7 +- sys/dev/fdt/qcsmptp.c | 384 ++++++++++++++++++++++++++++++++++++ 4 files changed, 394 insertions(+), 3 deletions(-) create mode 100644 sys/dev/fdt/qcsmptp.c 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 + * + * 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 +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#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); +} -- 2.20.1