1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2022 Ruslan Bukin <br@bsdpad.com> 5 * Copyright (c) 2023 Arm Ltd 6 * 7 * This work was supported by Innovate UK project 105694, "Digital Security 8 * by Design (DSbD) Technology Platform Prototype". 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/bus.h> 35 #include <sys/cpu.h> 36 #include <sys/kernel.h> 37 #include <sys/lock.h> 38 #include <sys/module.h> 39 40 #include <machine/atomic.h> 41 42 #include <dev/fdt/simplebus.h> 43 #include <dev/fdt/fdt_common.h> 44 #include <dev/ofw/ofw_bus_subr.h> 45 46 #include "mmio_sram_if.h" 47 48 #include "scmi_shmem.h" 49 #include "scmi.h" 50 51 #define INFLIGHT_NONE 0 52 #define INFLIGHT_REQ 1 53 54 struct shmem_softc { 55 device_t dev; 56 device_t parent; 57 int reg; 58 int inflight; 59 }; 60 61 static void scmi_shmem_read(device_t, bus_size_t, void *, bus_size_t); 62 static void scmi_shmem_write(device_t, bus_size_t, const void *, 63 bus_size_t); 64 static void scmi_shmem_acquire_channel(struct shmem_softc *); 65 static void scmi_shmem_release_channel(struct shmem_softc *); 66 67 static int shmem_probe(device_t); 68 static int shmem_attach(device_t); 69 static int shmem_detach(device_t); 70 71 static int 72 shmem_probe(device_t dev) 73 { 74 75 if (!ofw_bus_is_compatible(dev, "arm,scmi-shmem")) 76 return (ENXIO); 77 78 if (!ofw_bus_status_okay(dev)) 79 return (ENXIO); 80 81 device_set_desc(dev, "ARM SCMI Shared Memory driver"); 82 83 return (BUS_PROBE_DEFAULT); 84 } 85 86 static int 87 shmem_attach(device_t dev) 88 { 89 struct shmem_softc *sc; 90 phandle_t node; 91 int reg; 92 93 sc = device_get_softc(dev); 94 sc->dev = dev; 95 sc->parent = device_get_parent(dev); 96 97 node = ofw_bus_get_node(dev); 98 if (node == -1) 99 return (ENXIO); 100 101 OF_getencprop(node, "reg", ®, sizeof(reg)); 102 103 sc->reg = reg; 104 atomic_store_rel_int(&sc->inflight, INFLIGHT_NONE); 105 106 OF_device_register_xref(OF_xref_from_node(node), dev); 107 108 return (0); 109 } 110 111 static int 112 shmem_detach(device_t dev) 113 { 114 115 return (0); 116 } 117 118 static void 119 scmi_shmem_read(device_t dev, bus_size_t offset, void *buf, bus_size_t len) 120 { 121 struct shmem_softc *sc; 122 uint8_t *addr; 123 int i; 124 125 sc = device_get_softc(dev); 126 127 addr = (uint8_t *)buf; 128 129 for (i = 0; i < len; i++) 130 addr[i] = MMIO_SRAM_READ_1(sc->parent, sc->reg + offset + i); 131 } 132 133 static void 134 scmi_shmem_write(device_t dev, bus_size_t offset, const void *buf, 135 bus_size_t len) 136 { 137 struct shmem_softc *sc; 138 const uint8_t *addr; 139 int i; 140 141 sc = device_get_softc(dev); 142 143 addr = (const uint8_t *)buf; 144 145 for (i = 0; i < len; i++) 146 MMIO_SRAM_WRITE_1(sc->parent, sc->reg + offset + i, addr[i]); 147 } 148 149 device_t 150 scmi_shmem_get(device_t dev, phandle_t node, int index) 151 { 152 phandle_t *shmems; 153 device_t shmem_dev; 154 size_t len; 155 156 len = OF_getencprop_alloc_multi(node, "shmem", sizeof(*shmems), 157 (void **)&shmems); 158 if (len <= 0) { 159 device_printf(dev, "%s: Can't get shmem node.\n", __func__); 160 return (NULL); 161 } 162 163 if (index >= len) { 164 OF_prop_free(shmems); 165 return (NULL); 166 } 167 168 shmem_dev = OF_device_from_xref(shmems[index]); 169 if (shmem_dev == NULL) 170 device_printf(dev, "%s: Can't get shmem device.\n", 171 __func__); 172 173 OF_prop_free(shmems); 174 175 return (shmem_dev); 176 } 177 178 static void 179 scmi_shmem_acquire_channel(struct shmem_softc *sc) 180 { 181 182 while ((atomic_cmpset_acq_int(&sc->inflight, INFLIGHT_NONE, 183 INFLIGHT_REQ)) == 0) 184 DELAY(1000); 185 } 186 187 static void 188 scmi_shmem_release_channel(struct shmem_softc *sc) 189 { 190 191 atomic_store_rel_int(&sc->inflight, INFLIGHT_NONE); 192 } 193 194 int 195 scmi_shmem_prepare_msg(device_t dev, uint8_t *msg, uint32_t tx_len, 196 bool polling) 197 { 198 struct shmem_softc *sc; 199 struct scmi_smt_header hdr = {}; 200 uint32_t channel_status; 201 202 sc = device_get_softc(dev); 203 204 /* Get exclusive write access to channel */ 205 scmi_shmem_acquire_channel(sc); 206 207 /* Read channel status */ 208 scmi_shmem_read(dev, SMT_OFFSET_CHAN_STATUS, &channel_status, 209 SMT_SIZE_CHAN_STATUS); 210 if ((channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE) == 0) { 211 scmi_shmem_release_channel(sc); 212 device_printf(dev, "Shmem channel busy. Abort !.\n"); 213 return (1); 214 } 215 216 /* Update header */ 217 hdr.channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE; 218 hdr.msg_header = htole32(*((uint32_t *)msg)); 219 hdr.length = htole32(tx_len); 220 if (!polling) 221 hdr.flags |= SCMI_SHMEM_FLAG_INTR_ENABLED; 222 else 223 hdr.flags &= ~SCMI_SHMEM_FLAG_INTR_ENABLED; 224 225 /* Write header */ 226 scmi_shmem_write(dev, 0, &hdr, SMT_SIZE_HEADER); 227 228 /* Write request payload if any */ 229 if (tx_len > SCMI_MSG_HDR_SIZE) 230 scmi_shmem_write(dev, SMT_SIZE_HEADER, 231 &msg[SCMI_MSG_HDR_SIZE], tx_len - SCMI_MSG_HDR_SIZE); 232 233 return (0); 234 } 235 236 void 237 scmi_shmem_clear_channel(device_t dev) 238 { 239 uint32_t channel_status = 0; 240 241 if (dev == NULL) 242 return; 243 244 channel_status |= SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE; 245 scmi_shmem_write(dev, SMT_OFFSET_CHAN_STATUS, &channel_status, 246 SMT_SIZE_CHAN_STATUS); 247 } 248 249 int 250 scmi_shmem_read_msg_header(device_t dev, uint32_t *msg_header) 251 { 252 uint32_t length, header; 253 254 /* Read and check length. */ 255 scmi_shmem_read(dev, SMT_OFFSET_LENGTH, &length, SMT_SIZE_LENGTH); 256 if (le32toh(length) < sizeof(header)) 257 return (EINVAL); 258 259 /* Read header. */ 260 scmi_shmem_read(dev, SMT_OFFSET_MSG_HEADER, &header, 261 SMT_SIZE_MSG_HEADER); 262 263 *msg_header = le32toh(header); 264 265 return (0); 266 } 267 268 int 269 scmi_shmem_read_msg_payload(device_t dev, uint8_t *buf, uint32_t buf_len) 270 { 271 uint32_t length, payld_len; 272 273 /* Read length. */ 274 scmi_shmem_read(dev, SMT_OFFSET_LENGTH, &length, SMT_SIZE_LENGTH); 275 payld_len = le32toh(length) - SCMI_MSG_HDR_SIZE; 276 277 if (payld_len > buf_len) { 278 device_printf(dev, 279 "RX payload %dbytes exceeds buflen %dbytes. Truncate.\n", 280 payld_len, buf_len); 281 payld_len = buf_len; 282 } 283 284 /* Read response payload */ 285 scmi_shmem_read(dev, SMT_SIZE_HEADER, buf, payld_len); 286 287 return (0); 288 } 289 290 void 291 scmi_shmem_tx_complete(device_t dev) 292 { 293 struct shmem_softc *sc; 294 295 sc = device_get_softc(dev); 296 scmi_shmem_release_channel(sc); 297 } 298 299 bool scmi_shmem_poll_msg(device_t dev, uint32_t *msg_header) 300 { 301 uint32_t status; 302 bool ret; 303 304 scmi_shmem_read(dev, SMT_OFFSET_CHAN_STATUS, &status, 305 SMT_SIZE_CHAN_STATUS); 306 307 ret = (status & (SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR | 308 SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE)); 309 310 if (ret) 311 scmi_shmem_read(dev, SMT_OFFSET_MSG_HEADER, msg_header, 312 SMT_SIZE_MSG_HEADER); 313 314 return (ret); 315 } 316 317 static device_method_t shmem_methods[] = { 318 DEVMETHOD(device_probe, shmem_probe), 319 DEVMETHOD(device_attach, shmem_attach), 320 DEVMETHOD(device_detach, shmem_detach), 321 DEVMETHOD_END 322 }; 323 324 DEFINE_CLASS_1(shmem, shmem_driver, shmem_methods, sizeof(struct shmem_softc), 325 simplebus_driver); 326 327 EARLY_DRIVER_MODULE(shmem, mmio_sram, shmem_driver, 0, 0, 328 BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); 329 MODULE_VERSION(scmi, 1); 330