1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2022 Ruslan Bukin <br@bsdpad.com> 5 * 6 * This work was supported by Innovate UK project 105694, "Digital Security 7 * by Design (DSbD) Technology Platform Prototype". 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/bus.h> 34 #include <sys/cpu.h> 35 #include <sys/kernel.h> 36 #include <sys/lock.h> 37 #include <sys/module.h> 38 #include <sys/mutex.h> 39 40 #include <dev/clk/clk.h> 41 #include <dev/fdt/simplebus.h> 42 #include <dev/fdt/fdt_common.h> 43 #include <dev/ofw/ofw_bus_subr.h> 44 45 #include "dev/mailbox/arm/arm_doorbell.h" 46 47 #include "scmi.h" 48 #include "scmi_protocols.h" 49 50 struct scmi_softc { 51 struct simplebus_softc simplebus_sc; 52 device_t dev; 53 device_t tx_shmem; 54 struct arm_doorbell *db; 55 struct mtx mtx; 56 int req_done; 57 }; 58 59 static device_t 60 scmi_get_shmem(struct scmi_softc *sc, int index) 61 { 62 phandle_t *shmems; 63 phandle_t node; 64 device_t dev; 65 size_t len; 66 67 node = ofw_bus_get_node(sc->dev); 68 if (node <= 0) 69 return (NULL); 70 71 len = OF_getencprop_alloc_multi(node, "shmem", sizeof(*shmems), 72 (void **)&shmems); 73 if (len <= 0) { 74 device_printf(sc->dev, "%s: Can't get shmem node.\n", __func__); 75 return (NULL); 76 } 77 78 if (index >= len) { 79 OF_prop_free(shmems); 80 return (NULL); 81 } 82 83 dev = OF_device_from_xref(shmems[index]); 84 if (dev == NULL) 85 device_printf(sc->dev, "%s: Can't get shmem device.\n", 86 __func__); 87 88 OF_prop_free(shmems); 89 90 return (dev); 91 } 92 93 static void 94 scmi_callback(void *arg) 95 { 96 struct scmi_softc *sc; 97 98 sc = arg; 99 100 dprintf("%s sc %p\n", __func__, sc); 101 102 SCMI_LOCK(sc); 103 sc->req_done = 1; 104 wakeup(sc); 105 SCMI_UNLOCK(sc); 106 } 107 108 static int 109 scmi_request_locked(struct scmi_softc *sc, struct scmi_req *req) 110 { 111 struct scmi_smt_header hdr; 112 int timeout; 113 114 bzero(&hdr, sizeof(struct scmi_smt_header)); 115 116 SCMI_ASSERT_LOCKED(sc); 117 118 /* Read header */ 119 scmi_shmem_read(sc->tx_shmem, 0, &hdr, SMT_HEADER_SIZE); 120 121 if ((hdr.channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE) == 0) 122 return (1); 123 124 /* Update header */ 125 hdr.channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE; 126 hdr.msg_header = req->protocol_id << SMT_HEADER_PROTOCOL_ID_S; 127 hdr.msg_header |= req->message_id << SMT_HEADER_MESSAGE_ID_S; 128 hdr.length = sizeof(hdr.msg_header) + req->in_size; 129 hdr.flags |= SCMI_SHMEM_FLAG_INTR_ENABLED; 130 131 /* Write header */ 132 scmi_shmem_write(sc->tx_shmem, 0, &hdr, SMT_HEADER_SIZE); 133 134 /* Write request */ 135 scmi_shmem_write(sc->tx_shmem, SMT_HEADER_SIZE, req->in_buf, 136 req->in_size); 137 138 sc->req_done = 0; 139 140 /* Interrupt SCP firmware. */ 141 arm_doorbell_set(sc->db); 142 143 timeout = 200; 144 145 dprintf("%s: request\n", __func__); 146 147 do { 148 if (cold) { 149 if (arm_doorbell_get(sc->db)) 150 break; 151 DELAY(10000); 152 } else { 153 msleep(sc, &sc->mtx, 0, "scmi", hz / 10); 154 if (sc->req_done) 155 break; 156 } 157 } while (timeout--); 158 159 if (timeout <= 0) 160 return (-1); 161 162 dprintf("%s: got reply, timeout %d\n", __func__, timeout); 163 164 /* Read header. */ 165 scmi_shmem_read(sc->tx_shmem, 0, &hdr, SMT_HEADER_SIZE); 166 167 /* Read response */ 168 scmi_shmem_read(sc->tx_shmem, SMT_HEADER_SIZE, req->out_buf, 169 req->out_size); 170 171 return (0); 172 } 173 174 int 175 scmi_request(device_t dev, struct scmi_req *req) 176 { 177 struct scmi_softc *sc; 178 int error; 179 180 sc = device_get_softc(dev); 181 182 SCMI_LOCK(sc); 183 error = scmi_request_locked(sc, req); 184 SCMI_UNLOCK(sc); 185 186 return (error); 187 } 188 189 static int 190 scmi_probe(device_t dev) 191 { 192 193 if (!ofw_bus_is_compatible(dev, "arm,scmi")) 194 return (ENXIO); 195 196 if (!ofw_bus_status_okay(dev)) 197 return (ENXIO); 198 199 device_set_desc(dev, "ARM SCMI interface driver"); 200 201 return (BUS_PROBE_DEFAULT); 202 } 203 204 static int 205 scmi_attach(device_t dev) 206 { 207 struct scmi_softc *sc; 208 phandle_t node; 209 int error; 210 211 sc = device_get_softc(dev); 212 sc->dev = dev; 213 214 node = ofw_bus_get_node(dev); 215 if (node == -1) 216 return (ENXIO); 217 218 sc->tx_shmem = scmi_get_shmem(sc, 0); 219 if (sc->tx_shmem == NULL) { 220 device_printf(dev, "TX shmem dev not found.\n"); 221 return (ENXIO); 222 } 223 224 sc->db = arm_doorbell_ofw_get(sc->dev, "tx"); 225 if (sc->db == NULL) { 226 device_printf(dev, "Doorbell device not found.\n"); 227 return (ENXIO); 228 } 229 230 mtx_init(&sc->mtx, device_get_nameunit(dev), "SCMI", MTX_DEF); 231 232 arm_doorbell_set_handler(sc->db, scmi_callback, sc); 233 234 simplebus_init(dev, node); 235 236 /* 237 * Allow devices to identify. 238 */ 239 bus_generic_probe(dev); 240 241 /* 242 * Now walk the OFW tree and attach top-level devices. 243 */ 244 for (node = OF_child(node); node > 0; node = OF_peer(node)) 245 simplebus_add_device(dev, node, 0, NULL, -1, NULL); 246 247 error = bus_generic_attach(dev); 248 249 return (error); 250 } 251 252 static int 253 scmi_detach(device_t dev) 254 { 255 256 return (0); 257 } 258 259 static device_method_t scmi_methods[] = { 260 DEVMETHOD(device_probe, scmi_probe), 261 DEVMETHOD(device_attach, scmi_attach), 262 DEVMETHOD(device_detach, scmi_detach), 263 DEVMETHOD_END 264 }; 265 266 DEFINE_CLASS_1(scmi, scmi_driver, scmi_methods, sizeof(struct scmi_softc), 267 simplebus_driver); 268 269 DRIVER_MODULE(scmi, simplebus, scmi_driver, 0, 0); 270 MODULE_VERSION(scmi, 1); 271