diff --git a/sys/dev/firmware/arm/scmi.c b/sys/dev/firmware/arm/scmi.c index ee41ad4383c0..a797e52d74b1 100644 --- a/sys/dev/firmware/arm/scmi.c +++ b/sys/dev/firmware/arm/scmi.c @@ -1,181 +1,188 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2022 Ruslan Bukin * Copyright (c) 2023 Arm Ltd * * This work was supported by Innovate UK project 105694, "Digital Security * by Design (DSbD) Technology Platform Prototype". * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "dev/mailbox/arm/arm_doorbell.h" #include "scmi.h" #include "scmi_protocols.h" #include "scmi_shmem.h" #define SCMI_HDR_TOKEN_S 18 #define SCMI_HDR_TOKEN_BF (0x3fff) #define SCMI_HDR_TOKEN_M (SCMI_HDR_TOKEN_BF << SCMI_HDR_TOKEN_S) #define SCMI_HDR_PROTOCOL_ID_S 10 #define SCMI_HDR_PROTOCOL_ID_BF (0xff) #define SCMI_HDR_PROTOCOL_ID_M \ (SCMI_HDR_PROTOCOL_ID_BF << SCMI_HDR_PROTOCOL_ID_S) #define SCMI_HDR_MESSAGE_TYPE_S 8 #define SCMI_HDR_MESSAGE_TYPE_BF (0x3) #define SCMI_HDR_MESSAGE_TYPE_M \ (SCMI_HDR_MESSAGE_TYPE_BF << SCMI_HDR_MESSAGE_TYPE_S) #define SCMI_HDR_MESSAGE_ID_S 0 #define SCMI_HDR_MESSAGE_ID_BF (0xff) #define SCMI_HDR_MESSAGE_ID_M \ (SCMI_HDR_MESSAGE_ID_BF << SCMI_HDR_MESSAGE_ID_S) #define SCMI_MSG_TYPE_CMD 0 #define SCMI_MSG_TYPE_DRESP 2 #define SCMI_MSG_TYPE_NOTIF 3 static int scmi_request_locked(struct scmi_softc *sc, struct scmi_req *req) { uint32_t reply_header; int ret; SCMI_ASSERT_LOCKED(sc); req->msg_header = req->message_id << SCMI_HDR_MESSAGE_ID_S; /* TODO: Allocate a token */ req->msg_header |= SCMI_MSG_TYPE_CMD << SCMI_HDR_MESSAGE_TYPE_S; req->msg_header |= req->protocol_id << SCMI_HDR_PROTOCOL_ID_S; ret = scmi_shmem_prepare_msg(sc->a2p_dev, req, cold); if (ret != 0) return (ret); ret = SCMI_XFER_MSG(sc->dev); if (ret != 0) - return (ret); + goto out; /* Read header. */ ret = scmi_shmem_read_msg_header(sc->a2p_dev, &reply_header); if (ret != 0) - return (ret); + goto out; + + if (reply_header != req->msg_header) { + ret = EPROTO; + goto out; + } + + ret = scmi_shmem_read_msg_payload(sc->a2p_dev, req->out_buf, + req->out_size); - if (reply_header != req->msg_header) - return (EPROTO); +out: + scmi_shmem_tx_complete(sc->a2p_dev); - return (scmi_shmem_read_msg_payload(sc->a2p_dev, req->out_buf, - req->out_size)); + return (ret); } int scmi_request(device_t dev, struct scmi_req *req) { struct scmi_softc *sc; int error; sc = device_get_softc(dev); SCMI_LOCK(sc); error = scmi_request_locked(sc, req); SCMI_UNLOCK(sc); return (error); } int scmi_attach(device_t dev) { struct scmi_softc *sc; phandle_t node; int error; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(dev); if (node == -1) return (ENXIO); sc->a2p_dev = scmi_shmem_get(dev, node, SCMI_CHAN_A2P); if (sc->a2p_dev == NULL) { device_printf(dev, "A2P shmem dev not found.\n"); return (ENXIO); } mtx_init(&sc->mtx, device_get_nameunit(dev), "SCMI", MTX_DEF); simplebus_init(dev, node); /* * Allow devices to identify. */ bus_generic_probe(dev); /* * Now walk the OFW tree and attach top-level devices. */ for (node = OF_child(node); node > 0; node = OF_peer(node)) simplebus_add_device(dev, node, 0, NULL, -1, NULL); error = bus_generic_attach(dev); return (error); } static int scmi_detach(device_t dev) { return (0); } static device_method_t scmi_methods[] = { DEVMETHOD(device_attach, scmi_attach), DEVMETHOD(device_detach, scmi_detach), DEVMETHOD_END }; DEFINE_CLASS_1(scmi, scmi_driver, scmi_methods, sizeof(struct scmi_softc), simplebus_driver); DRIVER_MODULE(scmi, simplebus, scmi_driver, 0, 0); MODULE_VERSION(scmi, 1); diff --git a/sys/dev/firmware/arm/scmi_shmem.c b/sys/dev/firmware/arm/scmi_shmem.c index 066f28777cb7..5fb41af05246 100644 --- a/sys/dev/firmware/arm/scmi_shmem.c +++ b/sys/dev/firmware/arm/scmi_shmem.c @@ -1,268 +1,309 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2022 Ruslan Bukin * Copyright (c) 2023 Arm Ltd * * This work was supported by Innovate UK project 105694, "Digital Security * by Design (DSbD) Technology Platform Prototype". * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include +#include + #include #include #include #include "mmio_sram_if.h" #include "scmi_shmem.h" #include "scmi.h" +#define INFLIGHT_NONE 0 +#define INFLIGHT_REQ 1 + struct shmem_softc { device_t dev; device_t parent; int reg; + int inflight; }; static void scmi_shmem_read(device_t, bus_size_t, void *, bus_size_t); static void scmi_shmem_write(device_t, bus_size_t, const void *, bus_size_t); +static void scmi_shmem_acquire_channel(struct shmem_softc *); +static void scmi_shmem_release_channel(struct shmem_softc *); static int shmem_probe(device_t); static int shmem_attach(device_t); static int shmem_detach(device_t); static int shmem_probe(device_t dev) { if (!ofw_bus_is_compatible(dev, "arm,scmi-shmem")) return (ENXIO); if (!ofw_bus_status_okay(dev)) return (ENXIO); device_set_desc(dev, "ARM SCMI Shared Memory driver"); return (BUS_PROBE_DEFAULT); } static int shmem_attach(device_t dev) { struct shmem_softc *sc; phandle_t node; int reg; sc = device_get_softc(dev); sc->dev = dev; sc->parent = device_get_parent(dev); node = ofw_bus_get_node(dev); if (node == -1) return (ENXIO); OF_getencprop(node, "reg", ®, sizeof(reg)); dprintf("%s: reg %x\n", __func__, reg); sc->reg = reg; + atomic_store_rel_int(&sc->inflight, INFLIGHT_NONE); OF_device_register_xref(OF_xref_from_node(node), dev); return (0); } static int shmem_detach(device_t dev) { return (0); } static void scmi_shmem_read(device_t dev, bus_size_t offset, void *buf, bus_size_t len) { struct shmem_softc *sc; uint8_t *addr; int i; sc = device_get_softc(dev); addr = (uint8_t *)buf; for (i = 0; i < len; i++) addr[i] = MMIO_SRAM_READ_1(sc->parent, sc->reg + offset + i); } static void scmi_shmem_write(device_t dev, bus_size_t offset, const void *buf, bus_size_t len) { struct shmem_softc *sc; const uint8_t *addr; int i; sc = device_get_softc(dev); addr = (const uint8_t *)buf; for (i = 0; i < len; i++) MMIO_SRAM_WRITE_1(sc->parent, sc->reg + offset + i, addr[i]); } device_t scmi_shmem_get(device_t dev, phandle_t node, int index) { phandle_t *shmems; device_t shmem_dev; size_t len; len = OF_getencprop_alloc_multi(node, "shmem", sizeof(*shmems), (void **)&shmems); if (len <= 0) { device_printf(dev, "%s: Can't get shmem node.\n", __func__); return (NULL); } if (index >= len) { OF_prop_free(shmems); return (NULL); } shmem_dev = OF_device_from_xref(shmems[index]); if (shmem_dev == NULL) device_printf(dev, "%s: Can't get shmem device.\n", __func__); OF_prop_free(shmems); return (shmem_dev); } +static void +scmi_shmem_acquire_channel(struct shmem_softc *sc) +{ + + while ((atomic_cmpset_acq_int(&sc->inflight, INFLIGHT_NONE, + INFLIGHT_REQ)) == 0) + DELAY(1000); +} + +static void +scmi_shmem_release_channel(struct shmem_softc *sc) +{ + + atomic_store_rel_int(&sc->inflight, INFLIGHT_NONE); +} + int scmi_shmem_prepare_msg(device_t dev, struct scmi_req *req, bool polling) { + struct shmem_softc *sc; struct scmi_smt_header hdr = {}; uint32_t channel_status; + sc = device_get_softc(dev); + + /* Get exclusive write access to channel */ + scmi_shmem_acquire_channel(sc); + /* Read channel status */ scmi_shmem_read(dev, SMT_OFFSET_CHAN_STATUS, &channel_status, SMT_SIZE_CHAN_STATUS); if ((channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE) == 0) { + scmi_shmem_release_channel(sc); device_printf(dev, "Shmem channel busy. Abort !.\n"); return (1); } /* Update header */ hdr.channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE; hdr.msg_header = htole32(req->msg_header); hdr.length = htole32(sizeof(req->msg_header) + req->in_size); if (!polling) hdr.flags |= SCMI_SHMEM_FLAG_INTR_ENABLED; else hdr.flags &= ~SCMI_SHMEM_FLAG_INTR_ENABLED; /* Write header */ scmi_shmem_write(dev, 0, &hdr, SMT_SIZE_HEADER); /* Write request payload if any */ if (req->in_size) scmi_shmem_write(dev, SMT_SIZE_HEADER, req->in_buf, req->in_size); return (0); } int scmi_shmem_read_msg_header(device_t dev, uint32_t *msg_header) { uint32_t length, header; /* Read and check length. */ scmi_shmem_read(dev, SMT_OFFSET_LENGTH, &length, SMT_SIZE_LENGTH); if (le32toh(length) < sizeof(header)) return (EINVAL); /* Read header. */ scmi_shmem_read(dev, SMT_OFFSET_MSG_HEADER, &header, SMT_SIZE_MSG_HEADER); *msg_header = le32toh(header); return (0); } int scmi_shmem_read_msg_payload(device_t dev, uint8_t *buf, uint32_t buf_len) { uint32_t length, payld_len; /* Read length. */ scmi_shmem_read(dev, SMT_OFFSET_LENGTH, &length, SMT_SIZE_LENGTH); payld_len = le32toh(length) - SCMI_MSG_HDR_SIZE; if (payld_len > buf_len) { device_printf(dev, "RX payload %dbytes exceeds buflen %dbytes. Truncate.\n", payld_len, buf_len); payld_len = buf_len; } /* Read response payload */ scmi_shmem_read(dev, SMT_SIZE_HEADER, buf, payld_len); return (0); } +void +scmi_shmem_tx_complete(device_t dev) +{ + struct shmem_softc *sc; + + sc = device_get_softc(dev); + scmi_shmem_release_channel(sc); +} + bool scmi_shmem_poll_msg(device_t dev) { uint32_t status; scmi_shmem_read(dev, SMT_OFFSET_CHAN_STATUS, &status, SMT_SIZE_CHAN_STATUS); return (status & (SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR | SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE)); } static device_method_t shmem_methods[] = { DEVMETHOD(device_probe, shmem_probe), DEVMETHOD(device_attach, shmem_attach), DEVMETHOD(device_detach, shmem_detach), DEVMETHOD_END }; DEFINE_CLASS_1(shmem, shmem_driver, shmem_methods, sizeof(struct shmem_softc), simplebus_driver); EARLY_DRIVER_MODULE(shmem, mmio_sram, shmem_driver, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); MODULE_VERSION(scmi, 1); diff --git a/sys/dev/firmware/arm/scmi_shmem.h b/sys/dev/firmware/arm/scmi_shmem.h index d46493dc0342..149b7c1d89bb 100644 --- a/sys/dev/firmware/arm/scmi_shmem.h +++ b/sys/dev/firmware/arm/scmi_shmem.h @@ -1,72 +1,73 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2022 Ruslan Bukin * Copyright (c) 2023 Arm Ltd * * This work was supported by Innovate UK project 105694, "Digital Security * by Design (DSbD) Technology Platform Prototype". * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _ARM64_SCMI_SCMI_SHMEM_H_ #define _ARM64_SCMI_SCMI_SHMEM_H_ /* Shared Memory Transfer. */ struct scmi_smt_header { uint32_t reserved; uint32_t channel_status; #define SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR (1 << 1) #define SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE (1 << 0) uint32_t reserved1[2]; uint32_t flags; #define SCMI_SHMEM_FLAG_INTR_ENABLED (1 << 0) uint32_t length; uint32_t msg_header; uint8_t msg_payload[0]; }; #define SMT_SIZE_HEADER sizeof(struct scmi_smt_header) #define SMT_OFFSET_CHAN_STATUS \ __offsetof(struct scmi_smt_header, channel_status) #define SMT_SIZE_CHAN_STATUS sizeof(uint32_t) #define SMT_OFFSET_LENGTH \ __offsetof(struct scmi_smt_header, length) #define SMT_SIZE_LENGTH sizeof(uint32_t) #define SMT_OFFSET_MSG_HEADER \ __offsetof(struct scmi_smt_header, msg_header) #define SMT_SIZE_MSG_HEADER sizeof(uint32_t) struct scmi_req; device_t scmi_shmem_get(device_t sdev, phandle_t node, int index); int scmi_shmem_prepare_msg(device_t dev, struct scmi_req *req, bool polling); bool scmi_shmem_poll_msg(device_t); int scmi_shmem_read_msg_header(device_t dev, uint32_t *msg_header); int scmi_shmem_read_msg_payload(device_t dev, uint8_t *buf, uint32_t buf_len); +void scmi_shmem_tx_complete(device_t); #endif /* !_ARM64_SCMI_SCMI_SHMEM_H_ */