154b96380SRuslan Bukin /*- 254b96380SRuslan Bukin * SPDX-License-Identifier: BSD-2-Clause 354b96380SRuslan Bukin * 454b96380SRuslan Bukin * Copyright (c) 2022 Ruslan Bukin <br@bsdpad.com> 5d220b1cfSCristian Marussi * Copyright (c) 2023 Arm Ltd 654b96380SRuslan Bukin * 754b96380SRuslan Bukin * This work was supported by Innovate UK project 105694, "Digital Security 854b96380SRuslan Bukin * by Design (DSbD) Technology Platform Prototype". 954b96380SRuslan Bukin * 1054b96380SRuslan Bukin * Redistribution and use in source and binary forms, with or without 1154b96380SRuslan Bukin * modification, are permitted provided that the following conditions 1254b96380SRuslan Bukin * are met: 1354b96380SRuslan Bukin * 1. Redistributions of source code must retain the above copyright 1454b96380SRuslan Bukin * notice, this list of conditions and the following disclaimer. 1554b96380SRuslan Bukin * 2. Redistributions in binary form must reproduce the above copyright 1654b96380SRuslan Bukin * notice, this list of conditions and the following disclaimer in the 1754b96380SRuslan Bukin * documentation and/or other materials provided with the distribution. 1854b96380SRuslan Bukin * 1954b96380SRuslan Bukin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2054b96380SRuslan Bukin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2154b96380SRuslan Bukin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2254b96380SRuslan Bukin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2354b96380SRuslan Bukin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2454b96380SRuslan Bukin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2554b96380SRuslan Bukin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2654b96380SRuslan Bukin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2754b96380SRuslan Bukin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2854b96380SRuslan Bukin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2954b96380SRuslan Bukin * SUCH DAMAGE. 3054b96380SRuslan Bukin */ 3154b96380SRuslan Bukin 3254b96380SRuslan Bukin #include <sys/param.h> 3354b96380SRuslan Bukin #include <sys/systm.h> 3454b96380SRuslan Bukin #include <sys/bus.h> 3554b96380SRuslan Bukin #include <sys/cpu.h> 3654b96380SRuslan Bukin #include <sys/kernel.h> 3735f93203SCristian Marussi #include <sys/lock.h> 3854b96380SRuslan Bukin #include <sys/module.h> 3954b96380SRuslan Bukin 40a0ba2a97SCristian Marussi #include <machine/atomic.h> 41a0ba2a97SCristian Marussi 4254b96380SRuslan Bukin #include <dev/fdt/simplebus.h> 4354b96380SRuslan Bukin #include <dev/fdt/fdt_common.h> 4454b96380SRuslan Bukin #include <dev/ofw/ofw_bus_subr.h> 4554b96380SRuslan Bukin 4654b96380SRuslan Bukin #include "mmio_sram_if.h" 4754b96380SRuslan Bukin 48d220b1cfSCristian Marussi #include "scmi_shmem.h" 4954b96380SRuslan Bukin #include "scmi.h" 5054b96380SRuslan Bukin 51a0ba2a97SCristian Marussi #define INFLIGHT_NONE 0 52a0ba2a97SCristian Marussi #define INFLIGHT_REQ 1 53a0ba2a97SCristian Marussi 5454b96380SRuslan Bukin struct shmem_softc { 5554b96380SRuslan Bukin device_t dev; 5654b96380SRuslan Bukin device_t parent; 5754b96380SRuslan Bukin int reg; 58a0ba2a97SCristian Marussi int inflight; 5954b96380SRuslan Bukin }; 6054b96380SRuslan Bukin 61d220b1cfSCristian Marussi static void scmi_shmem_read(device_t, bus_size_t, void *, bus_size_t); 62d220b1cfSCristian Marussi static void scmi_shmem_write(device_t, bus_size_t, const void *, 63d220b1cfSCristian Marussi bus_size_t); 64a0ba2a97SCristian Marussi static void scmi_shmem_acquire_channel(struct shmem_softc *); 65a0ba2a97SCristian Marussi static void scmi_shmem_release_channel(struct shmem_softc *); 66d220b1cfSCristian Marussi 67d220b1cfSCristian Marussi static int shmem_probe(device_t); 68d220b1cfSCristian Marussi static int shmem_attach(device_t); 69d220b1cfSCristian Marussi static int shmem_detach(device_t); 70d220b1cfSCristian Marussi 7154b96380SRuslan Bukin static int 7254b96380SRuslan Bukin shmem_probe(device_t dev) 7354b96380SRuslan Bukin { 7454b96380SRuslan Bukin 7554b96380SRuslan Bukin if (!ofw_bus_is_compatible(dev, "arm,scmi-shmem")) 7654b96380SRuslan Bukin return (ENXIO); 7754b96380SRuslan Bukin 7854b96380SRuslan Bukin if (!ofw_bus_status_okay(dev)) 7954b96380SRuslan Bukin return (ENXIO); 8054b96380SRuslan Bukin 8154b96380SRuslan Bukin device_set_desc(dev, "ARM SCMI Shared Memory driver"); 8254b96380SRuslan Bukin 8354b96380SRuslan Bukin return (BUS_PROBE_DEFAULT); 8454b96380SRuslan Bukin } 8554b96380SRuslan Bukin 8654b96380SRuslan Bukin static int 8754b96380SRuslan Bukin shmem_attach(device_t dev) 8854b96380SRuslan Bukin { 8954b96380SRuslan Bukin struct shmem_softc *sc; 9054b96380SRuslan Bukin phandle_t node; 9154b96380SRuslan Bukin int reg; 9254b96380SRuslan Bukin 9354b96380SRuslan Bukin sc = device_get_softc(dev); 9454b96380SRuslan Bukin sc->dev = dev; 9554b96380SRuslan Bukin sc->parent = device_get_parent(dev); 9654b96380SRuslan Bukin 9754b96380SRuslan Bukin node = ofw_bus_get_node(dev); 9854b96380SRuslan Bukin if (node == -1) 9954b96380SRuslan Bukin return (ENXIO); 10054b96380SRuslan Bukin 10154b96380SRuslan Bukin OF_getencprop(node, "reg", ®, sizeof(reg)); 10254b96380SRuslan Bukin 10354b96380SRuslan Bukin sc->reg = reg; 104a0ba2a97SCristian Marussi atomic_store_rel_int(&sc->inflight, INFLIGHT_NONE); 10554b96380SRuslan Bukin 10654b96380SRuslan Bukin OF_device_register_xref(OF_xref_from_node(node), dev); 10754b96380SRuslan Bukin 10854b96380SRuslan Bukin return (0); 10954b96380SRuslan Bukin } 11054b96380SRuslan Bukin 11154b96380SRuslan Bukin static int 11254b96380SRuslan Bukin shmem_detach(device_t dev) 11354b96380SRuslan Bukin { 11454b96380SRuslan Bukin 11554b96380SRuslan Bukin return (0); 11654b96380SRuslan Bukin } 11754b96380SRuslan Bukin 118d220b1cfSCristian Marussi static void 11954b96380SRuslan Bukin scmi_shmem_read(device_t dev, bus_size_t offset, void *buf, bus_size_t len) 12054b96380SRuslan Bukin { 12154b96380SRuslan Bukin struct shmem_softc *sc; 12254b96380SRuslan Bukin uint8_t *addr; 12354b96380SRuslan Bukin int i; 12454b96380SRuslan Bukin 12554b96380SRuslan Bukin sc = device_get_softc(dev); 12654b96380SRuslan Bukin 12754b96380SRuslan Bukin addr = (uint8_t *)buf; 12854b96380SRuslan Bukin 12954b96380SRuslan Bukin for (i = 0; i < len; i++) 13054b96380SRuslan Bukin addr[i] = MMIO_SRAM_READ_1(sc->parent, sc->reg + offset + i); 13154b96380SRuslan Bukin } 13254b96380SRuslan Bukin 133d220b1cfSCristian Marussi static void 13454b96380SRuslan Bukin scmi_shmem_write(device_t dev, bus_size_t offset, const void *buf, 13554b96380SRuslan Bukin bus_size_t len) 13654b96380SRuslan Bukin { 13754b96380SRuslan Bukin struct shmem_softc *sc; 13854b96380SRuslan Bukin const uint8_t *addr; 13954b96380SRuslan Bukin int i; 14054b96380SRuslan Bukin 14154b96380SRuslan Bukin sc = device_get_softc(dev); 14254b96380SRuslan Bukin 14354b96380SRuslan Bukin addr = (const uint8_t *)buf; 14454b96380SRuslan Bukin 14554b96380SRuslan Bukin for (i = 0; i < len; i++) 14654b96380SRuslan Bukin MMIO_SRAM_WRITE_1(sc->parent, sc->reg + offset + i, addr[i]); 14754b96380SRuslan Bukin } 14854b96380SRuslan Bukin 149d220b1cfSCristian Marussi device_t 150d220b1cfSCristian Marussi scmi_shmem_get(device_t dev, phandle_t node, int index) 151d220b1cfSCristian Marussi { 152d220b1cfSCristian Marussi phandle_t *shmems; 153d220b1cfSCristian Marussi device_t shmem_dev; 154d220b1cfSCristian Marussi size_t len; 155d220b1cfSCristian Marussi 156d220b1cfSCristian Marussi len = OF_getencprop_alloc_multi(node, "shmem", sizeof(*shmems), 157d220b1cfSCristian Marussi (void **)&shmems); 158d220b1cfSCristian Marussi if (len <= 0) { 159d220b1cfSCristian Marussi device_printf(dev, "%s: Can't get shmem node.\n", __func__); 160d220b1cfSCristian Marussi return (NULL); 161d220b1cfSCristian Marussi } 162d220b1cfSCristian Marussi 163d220b1cfSCristian Marussi if (index >= len) { 164d220b1cfSCristian Marussi OF_prop_free(shmems); 165d220b1cfSCristian Marussi return (NULL); 166d220b1cfSCristian Marussi } 167d220b1cfSCristian Marussi 168d220b1cfSCristian Marussi shmem_dev = OF_device_from_xref(shmems[index]); 169d220b1cfSCristian Marussi if (shmem_dev == NULL) 170d220b1cfSCristian Marussi device_printf(dev, "%s: Can't get shmem device.\n", 171d220b1cfSCristian Marussi __func__); 172d220b1cfSCristian Marussi 173d220b1cfSCristian Marussi OF_prop_free(shmems); 174d220b1cfSCristian Marussi 175d220b1cfSCristian Marussi return (shmem_dev); 176d220b1cfSCristian Marussi } 177d220b1cfSCristian Marussi 178a0ba2a97SCristian Marussi static void 179a0ba2a97SCristian Marussi scmi_shmem_acquire_channel(struct shmem_softc *sc) 180a0ba2a97SCristian Marussi { 181a0ba2a97SCristian Marussi 182a0ba2a97SCristian Marussi while ((atomic_cmpset_acq_int(&sc->inflight, INFLIGHT_NONE, 183a0ba2a97SCristian Marussi INFLIGHT_REQ)) == 0) 184a0ba2a97SCristian Marussi DELAY(1000); 185a0ba2a97SCristian Marussi } 186a0ba2a97SCristian Marussi 187a0ba2a97SCristian Marussi static void 188a0ba2a97SCristian Marussi scmi_shmem_release_channel(struct shmem_softc *sc) 189a0ba2a97SCristian Marussi { 190a0ba2a97SCristian Marussi 191a0ba2a97SCristian Marussi atomic_store_rel_int(&sc->inflight, INFLIGHT_NONE); 192a0ba2a97SCristian Marussi } 193a0ba2a97SCristian Marussi 194d220b1cfSCristian Marussi int 19535f93203SCristian Marussi scmi_shmem_prepare_msg(device_t dev, uint8_t *msg, uint32_t tx_len, 19635f93203SCristian Marussi bool polling) 197d220b1cfSCristian Marussi { 198a0ba2a97SCristian Marussi struct shmem_softc *sc; 199d220b1cfSCristian Marussi struct scmi_smt_header hdr = {}; 200d220b1cfSCristian Marussi uint32_t channel_status; 201d220b1cfSCristian Marussi 202a0ba2a97SCristian Marussi sc = device_get_softc(dev); 203a0ba2a97SCristian Marussi 204a0ba2a97SCristian Marussi /* Get exclusive write access to channel */ 205a0ba2a97SCristian Marussi scmi_shmem_acquire_channel(sc); 206a0ba2a97SCristian Marussi 207d220b1cfSCristian Marussi /* Read channel status */ 208d220b1cfSCristian Marussi scmi_shmem_read(dev, SMT_OFFSET_CHAN_STATUS, &channel_status, 209d220b1cfSCristian Marussi SMT_SIZE_CHAN_STATUS); 210d220b1cfSCristian Marussi if ((channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE) == 0) { 211a0ba2a97SCristian Marussi scmi_shmem_release_channel(sc); 212d220b1cfSCristian Marussi device_printf(dev, "Shmem channel busy. Abort !.\n"); 21335f93203SCristian Marussi return (1); 214d220b1cfSCristian Marussi } 215d220b1cfSCristian Marussi 216d220b1cfSCristian Marussi /* Update header */ 217d220b1cfSCristian Marussi hdr.channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE; 21835f93203SCristian Marussi hdr.msg_header = htole32(*((uint32_t *)msg)); 21935f93203SCristian Marussi hdr.length = htole32(tx_len); 220cbcfdff0SCristian Marussi if (!polling) 221d220b1cfSCristian Marussi hdr.flags |= SCMI_SHMEM_FLAG_INTR_ENABLED; 222cbcfdff0SCristian Marussi else 223cbcfdff0SCristian Marussi hdr.flags &= ~SCMI_SHMEM_FLAG_INTR_ENABLED; 224d220b1cfSCristian Marussi 225d220b1cfSCristian Marussi /* Write header */ 226d220b1cfSCristian Marussi scmi_shmem_write(dev, 0, &hdr, SMT_SIZE_HEADER); 227d220b1cfSCristian Marussi 228d220b1cfSCristian Marussi /* Write request payload if any */ 22935f93203SCristian Marussi if (tx_len > SCMI_MSG_HDR_SIZE) 23035f93203SCristian Marussi scmi_shmem_write(dev, SMT_SIZE_HEADER, 23135f93203SCristian Marussi &msg[SCMI_MSG_HDR_SIZE], tx_len - SCMI_MSG_HDR_SIZE); 232d220b1cfSCristian Marussi 233d220b1cfSCristian Marussi return (0); 234d220b1cfSCristian Marussi } 235d220b1cfSCristian Marussi 2363595f18fSCristian Marussi void 2373595f18fSCristian Marussi scmi_shmem_clear_channel(device_t dev) 2383595f18fSCristian Marussi { 2393595f18fSCristian Marussi uint32_t channel_status = 0; 2403595f18fSCristian Marussi 2413595f18fSCristian Marussi if (dev == NULL) 2423595f18fSCristian Marussi return; 2433595f18fSCristian Marussi 2443595f18fSCristian Marussi channel_status |= SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE; 2453595f18fSCristian Marussi scmi_shmem_write(dev, SMT_OFFSET_CHAN_STATUS, &channel_status, 2463595f18fSCristian Marussi SMT_SIZE_CHAN_STATUS); 2473595f18fSCristian Marussi } 2483595f18fSCristian Marussi 249d220b1cfSCristian Marussi int 250*9342829dSCristian Marussi scmi_shmem_read_msg_header(device_t dev, uint32_t *msg_header, unsigned int *rx_len) 251d220b1cfSCristian Marussi { 252d220b1cfSCristian Marussi uint32_t length, header; 253d220b1cfSCristian Marussi 254d220b1cfSCristian Marussi /* Read and check length. */ 255d220b1cfSCristian Marussi scmi_shmem_read(dev, SMT_OFFSET_LENGTH, &length, SMT_SIZE_LENGTH); 256d220b1cfSCristian Marussi if (le32toh(length) < sizeof(header)) 257d220b1cfSCristian Marussi return (EINVAL); 258d220b1cfSCristian Marussi 259*9342829dSCristian Marussi *rx_len = le32toh(length); 260d220b1cfSCristian Marussi /* Read header. */ 261d220b1cfSCristian Marussi scmi_shmem_read(dev, SMT_OFFSET_MSG_HEADER, &header, 262d220b1cfSCristian Marussi SMT_SIZE_MSG_HEADER); 263d220b1cfSCristian Marussi 264d220b1cfSCristian Marussi *msg_header = le32toh(header); 265d220b1cfSCristian Marussi 266d220b1cfSCristian Marussi return (0); 267d220b1cfSCristian Marussi } 268d220b1cfSCristian Marussi 269d220b1cfSCristian Marussi int 270*9342829dSCristian Marussi scmi_shmem_read_msg_payload(device_t dev, uint8_t *buf, uint32_t buf_len, uint32_t rx_len) 271d220b1cfSCristian Marussi { 272*9342829dSCristian Marussi uint32_t payld_len; 273d220b1cfSCristian Marussi 274*9342829dSCristian Marussi payld_len = rx_len - SCMI_MSG_HDR_SIZE; 275d220b1cfSCristian Marussi if (payld_len > buf_len) { 276d220b1cfSCristian Marussi device_printf(dev, 277d220b1cfSCristian Marussi "RX payload %dbytes exceeds buflen %dbytes. Truncate.\n", 278d220b1cfSCristian Marussi payld_len, buf_len); 279d220b1cfSCristian Marussi payld_len = buf_len; 280d220b1cfSCristian Marussi } 281d220b1cfSCristian Marussi 282d220b1cfSCristian Marussi /* Read response payload */ 283d220b1cfSCristian Marussi scmi_shmem_read(dev, SMT_SIZE_HEADER, buf, payld_len); 284d220b1cfSCristian Marussi 285d220b1cfSCristian Marussi return (0); 286d220b1cfSCristian Marussi } 287d220b1cfSCristian Marussi 288a0ba2a97SCristian Marussi void 289a0ba2a97SCristian Marussi scmi_shmem_tx_complete(device_t dev) 290a0ba2a97SCristian Marussi { 291a0ba2a97SCristian Marussi struct shmem_softc *sc; 292a0ba2a97SCristian Marussi 293a0ba2a97SCristian Marussi sc = device_get_softc(dev); 294a0ba2a97SCristian Marussi scmi_shmem_release_channel(sc); 295a0ba2a97SCristian Marussi } 296a0ba2a97SCristian Marussi 297*9342829dSCristian Marussi bool scmi_shmem_poll_msg(device_t dev, uint32_t *msg_header, uint32_t *rx_len) 298cbcfdff0SCristian Marussi { 29935f93203SCristian Marussi uint32_t status; 30035f93203SCristian Marussi bool ret; 301cbcfdff0SCristian Marussi 302cbcfdff0SCristian Marussi scmi_shmem_read(dev, SMT_OFFSET_CHAN_STATUS, &status, 303cbcfdff0SCristian Marussi SMT_SIZE_CHAN_STATUS); 304cbcfdff0SCristian Marussi 30535f93203SCristian Marussi ret = (status & (SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR | 306cbcfdff0SCristian Marussi SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE)); 307*9342829dSCristian Marussi if (ret == 0) 30835f93203SCristian Marussi return (ret); 309*9342829dSCristian Marussi 310*9342829dSCristian Marussi return (scmi_shmem_read_msg_header(dev, msg_header, rx_len)); 311cbcfdff0SCristian Marussi } 312cbcfdff0SCristian Marussi 31354b96380SRuslan Bukin static device_method_t shmem_methods[] = { 31454b96380SRuslan Bukin DEVMETHOD(device_probe, shmem_probe), 31554b96380SRuslan Bukin DEVMETHOD(device_attach, shmem_attach), 31654b96380SRuslan Bukin DEVMETHOD(device_detach, shmem_detach), 31754b96380SRuslan Bukin DEVMETHOD_END 31854b96380SRuslan Bukin }; 31954b96380SRuslan Bukin 32054b96380SRuslan Bukin DEFINE_CLASS_1(shmem, shmem_driver, shmem_methods, sizeof(struct shmem_softc), 32154b96380SRuslan Bukin simplebus_driver); 32254b96380SRuslan Bukin 32354b96380SRuslan Bukin EARLY_DRIVER_MODULE(shmem, mmio_sram, shmem_driver, 0, 0, 32454b96380SRuslan Bukin BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); 32523f025e2SAndrew Turner MODULE_VERSION(scmi_shmem, 1); 326