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