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/cdefs.h> 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 #include <sys/mutex.h> 40 41 #include <dev/extres/clk/clk.h> 42 #include <dev/fdt/simplebus.h> 43 #include <dev/fdt/fdt_common.h> 44 #include <dev/ofw/ofw_bus_subr.h> 45 46 #include "dev/mailbox/arm/arm_doorbell.h" 47 48 #include "scmi.h" 49 #include "scmi_protocols.h" 50 51 struct scmi_softc { 52 struct simplebus_softc simplebus_sc; 53 device_t dev; 54 device_t tx_shmem; 55 struct arm_doorbell *db; 56 struct mtx mtx; 57 int req_done; 58 }; 59 60 static device_t 61 scmi_get_shmem(struct scmi_softc *sc, int index) 62 { 63 phandle_t *shmems; 64 phandle_t node; 65 device_t dev; 66 size_t len; 67 68 node = ofw_bus_get_node(sc->dev); 69 if (node <= 0) 70 return (NULL); 71 72 len = OF_getencprop_alloc_multi(node, "shmem", sizeof(*shmems), 73 (void **)&shmems); 74 if (len <= 0) { 75 device_printf(sc->dev, "%s: Can't get shmem node.\n", __func__); 76 return (NULL); 77 } 78 79 if (index >= len) { 80 OF_prop_free(shmems); 81 return (NULL); 82 } 83 84 dev = OF_device_from_xref(shmems[index]); 85 if (dev == NULL) 86 device_printf(sc->dev, "%s: Can't get shmem device.\n", 87 __func__); 88 89 OF_prop_free(shmems); 90 91 return (dev); 92 } 93 94 static void 95 scmi_callback(void *arg) 96 { 97 struct scmi_softc *sc; 98 99 sc = arg; 100 101 dprintf("%s sc %p\n", __func__, sc); 102 103 SCMI_LOCK(sc); 104 sc->req_done = 1; 105 wakeup(sc); 106 SCMI_UNLOCK(sc); 107 } 108 109 static int 110 scmi_request_locked(struct scmi_softc *sc, struct scmi_req *req) 111 { 112 struct scmi_smt_header hdr; 113 int timeout; 114 115 bzero(&hdr, sizeof(struct scmi_smt_header)); 116 117 SCMI_ASSERT_LOCKED(sc); 118 119 /* Read header */ 120 scmi_shmem_read(sc->tx_shmem, 0, &hdr, SMT_HEADER_SIZE); 121 122 if ((hdr.channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE) == 0) 123 return (1); 124 125 /* Update header */ 126 hdr.channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE; 127 hdr.msg_header = req->protocol_id << SMT_HEADER_PROTOCOL_ID_S; 128 hdr.msg_header |= req->message_id << SMT_HEADER_MESSAGE_ID_S; 129 hdr.length = sizeof(hdr.msg_header) + req->in_size; 130 hdr.flags |= SCMI_SHMEM_FLAG_INTR_ENABLED; 131 132 /* Write header */ 133 scmi_shmem_write(sc->tx_shmem, 0, &hdr, SMT_HEADER_SIZE); 134 135 /* Write request */ 136 scmi_shmem_write(sc->tx_shmem, SMT_HEADER_SIZE, req->in_buf, 137 req->in_size); 138 139 sc->req_done = 0; 140 141 /* Interrupt SCP firmware. */ 142 arm_doorbell_set(sc->db); 143 144 timeout = 200; 145 146 dprintf("%s: request\n", __func__); 147 148 do { 149 if (cold) { 150 if (arm_doorbell_get(sc->db)) 151 break; 152 DELAY(10000); 153 } else { 154 msleep(sc, &sc->mtx, 0, "scmi", hz / 10); 155 if (sc->req_done) 156 break; 157 } 158 } while (timeout--); 159 160 if (timeout <= 0) 161 return (-1); 162 163 dprintf("%s: got reply, timeout %d\n", __func__, timeout); 164 165 /* Read header. */ 166 scmi_shmem_read(sc->tx_shmem, 0, &hdr, SMT_HEADER_SIZE); 167 168 /* Read response */ 169 scmi_shmem_read(sc->tx_shmem, SMT_HEADER_SIZE, req->out_buf, 170 req->out_size); 171 172 return (0); 173 } 174 175 int 176 scmi_request(device_t dev, struct scmi_req *req) 177 { 178 struct scmi_softc *sc; 179 int error; 180 181 sc = device_get_softc(dev); 182 183 SCMI_LOCK(sc); 184 error = scmi_request_locked(sc, req); 185 SCMI_UNLOCK(sc); 186 187 return (error); 188 } 189 190 static int 191 scmi_probe(device_t dev) 192 { 193 194 if (!ofw_bus_is_compatible(dev, "arm,scmi")) 195 return (ENXIO); 196 197 if (!ofw_bus_status_okay(dev)) 198 return (ENXIO); 199 200 device_set_desc(dev, "ARM SCMI interface driver"); 201 202 return (BUS_PROBE_DEFAULT); 203 } 204 205 static int 206 scmi_attach(device_t dev) 207 { 208 struct scmi_softc *sc; 209 phandle_t node; 210 int error; 211 212 sc = device_get_softc(dev); 213 sc->dev = dev; 214 215 node = ofw_bus_get_node(dev); 216 if (node == -1) 217 return (ENXIO); 218 219 sc->tx_shmem = scmi_get_shmem(sc, 0); 220 if (sc->tx_shmem == NULL) { 221 device_printf(dev, "TX shmem dev not found.\n"); 222 return (ENXIO); 223 } 224 225 sc->db = arm_doorbell_ofw_get(sc->dev, "tx"); 226 if (sc->db == NULL) { 227 device_printf(dev, "Doorbell device not found.\n"); 228 return (ENXIO); 229 } 230 231 mtx_init(&sc->mtx, device_get_nameunit(dev), "SCMI", MTX_DEF); 232 233 arm_doorbell_set_handler(sc->db, scmi_callback, sc); 234 235 simplebus_init(dev, node); 236 237 /* 238 * Allow devices to identify. 239 */ 240 bus_generic_probe(dev); 241 242 /* 243 * Now walk the OFW tree and attach top-level devices. 244 */ 245 for (node = OF_child(node); node > 0; node = OF_peer(node)) 246 simplebus_add_device(dev, node, 0, NULL, -1, NULL); 247 248 error = bus_generic_attach(dev); 249 250 return (error); 251 } 252 253 static int 254 scmi_detach(device_t dev) 255 { 256 257 return (0); 258 } 259 260 static device_method_t scmi_methods[] = { 261 DEVMETHOD(device_probe, scmi_probe), 262 DEVMETHOD(device_attach, scmi_attach), 263 DEVMETHOD(device_detach, scmi_detach), 264 DEVMETHOD_END 265 }; 266 267 DEFINE_CLASS_1(scmi, scmi_driver, scmi_methods, sizeof(struct scmi_softc), 268 simplebus_driver); 269 270 DRIVER_MODULE(scmi, simplebus, scmi_driver, 0, 0); 271 MODULE_VERSION(scmi, 1); 272