1a87dd741SCristian Marussi /*- 2a87dd741SCristian Marussi * SPDX-License-Identifier: BSD-2-Clause 3a87dd741SCristian Marussi * 4a87dd741SCristian Marussi * Copyright (c) 2023 Arm Ltd 5a87dd741SCristian Marussi * 6a87dd741SCristian Marussi * Redistribution and use in source and binary forms, with or without 7a87dd741SCristian Marussi * modification, are permitted provided that the following conditions 8a87dd741SCristian Marussi * are met: 9a87dd741SCristian Marussi * 1. Redistributions of source code must retain the above copyright 10a87dd741SCristian Marussi * notice, this list of conditions and the following disclaimer. 11a87dd741SCristian Marussi * 2. Redistributions in binary form must reproduce the above copyright 12a87dd741SCristian Marussi * notice, this list of conditions and the following disclaimer in the 13a87dd741SCristian Marussi * documentation and/or other materials provided with the distribution. 14a87dd741SCristian Marussi * 15a87dd741SCristian Marussi * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16a87dd741SCristian Marussi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17a87dd741SCristian Marussi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18a87dd741SCristian Marussi * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19a87dd741SCristian Marussi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20a87dd741SCristian Marussi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21a87dd741SCristian Marussi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22a87dd741SCristian Marussi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23a87dd741SCristian Marussi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24a87dd741SCristian Marussi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25a87dd741SCristian Marussi * SUCH DAMAGE. 26a87dd741SCristian Marussi */ 27a87dd741SCristian Marussi 28a87dd741SCristian Marussi #include <sys/cdefs.h> 29a87dd741SCristian Marussi #include <sys/param.h> 30a87dd741SCristian Marussi #include <sys/systm.h> 31a87dd741SCristian Marussi #include <sys/bus.h> 32a87dd741SCristian Marussi #include <sys/cpu.h> 33a87dd741SCristian Marussi #include <sys/kernel.h> 34a87dd741SCristian Marussi #include <sys/module.h> 35a87dd741SCristian Marussi 36a87dd741SCristian Marussi #include <dev/fdt/simplebus.h> 37a87dd741SCristian Marussi #include <dev/fdt/fdt_common.h> 38a87dd741SCristian Marussi #include <dev/ofw/ofw_bus_subr.h> 39a87dd741SCristian Marussi 40a87dd741SCristian Marussi #include <dev/virtio/scmi/virtio_scmi.h> 41a87dd741SCristian Marussi 42a87dd741SCristian Marussi #include "scmi.h" 43a87dd741SCristian Marussi #include "scmi_protocols.h" 44a87dd741SCristian Marussi 45a87dd741SCristian Marussi #define SCMI_VIRTIO_POLLING_INTERVAL_MS 2 46a87dd741SCristian Marussi 47a87dd741SCristian Marussi struct scmi_virtio_softc { 48a87dd741SCristian Marussi struct scmi_softc base; 49a87dd741SCristian Marussi device_t virtio_dev; 50a87dd741SCristian Marussi int cmdq_sz; 51a87dd741SCristian Marussi int evtq_sz; 52a87dd741SCristian Marussi void *p2a_pool; 53a87dd741SCristian Marussi }; 54a87dd741SCristian Marussi 55a87dd741SCristian Marussi static void scmi_virtio_callback(void *, unsigned int, void *); 56a87dd741SCristian Marussi static void *scmi_virtio_p2a_pool_init(device_t, unsigned int); 57a87dd741SCristian Marussi static int scmi_virtio_transport_init(device_t); 58a87dd741SCristian Marussi static void scmi_virtio_transport_cleanup(device_t); 59a87dd741SCristian Marussi static int scmi_virtio_xfer_msg(device_t, struct scmi_msg *); 60a87dd741SCristian Marussi static int scmi_virtio_poll_msg(device_t, struct scmi_msg *, unsigned int); 61a87dd741SCristian Marussi static void scmi_virtio_clear_channel(device_t, void *); 62a87dd741SCristian Marussi static int scmi_virtio_probe(device_t); 63a87dd741SCristian Marussi static int scmi_virtio_attach(device_t); 64a87dd741SCristian Marussi 65a87dd741SCristian Marussi static void 66a87dd741SCristian Marussi scmi_virtio_callback(void *msg, unsigned int len, void *priv) 67a87dd741SCristian Marussi { 68a87dd741SCristian Marussi struct scmi_virtio_softc *sc; 69a87dd741SCristian Marussi uint32_t hdr; 70a87dd741SCristian Marussi 71a87dd741SCristian Marussi sc = priv; 72a87dd741SCristian Marussi 73a87dd741SCristian Marussi if (msg == NULL || len < sizeof(hdr)) { 74a87dd741SCristian Marussi device_printf(sc->virtio_dev, "Ignoring malformed message.\n"); 75a87dd741SCristian Marussi return; 76a87dd741SCristian Marussi } 77a87dd741SCristian Marussi 78a87dd741SCristian Marussi hdr = le32toh(*((uint32_t *)msg)); 799342829dSCristian Marussi scmi_rx_irq_callback(sc->base.dev, msg, hdr, len); 80a87dd741SCristian Marussi } 81a87dd741SCristian Marussi 82a87dd741SCristian Marussi static void * 83a87dd741SCristian Marussi scmi_virtio_p2a_pool_init(device_t dev, unsigned int max_msg) 84a87dd741SCristian Marussi { 85a87dd741SCristian Marussi struct scmi_virtio_softc *sc; 86*103ea03eSCristian Marussi unsigned int max_msg_sz; 87a87dd741SCristian Marussi void *pool; 88a87dd741SCristian Marussi uint8_t *buf; 89a87dd741SCristian Marussi int i; 90a87dd741SCristian Marussi 91a87dd741SCristian Marussi sc = device_get_softc(dev); 92*103ea03eSCristian Marussi max_msg_sz = SCMI_MAX_MSG_SIZE(&sc->base); 93*103ea03eSCristian Marussi pool = mallocarray(max_msg, max_msg_sz, M_DEVBUF, M_ZERO | M_WAITOK); 94a87dd741SCristian Marussi 95*103ea03eSCristian Marussi for (i = 0, buf = pool; i < max_msg; i++, buf += max_msg_sz) { 96a87dd741SCristian Marussi /* Feed platform with pre-allocated P2A buffers */ 97a87dd741SCristian Marussi virtio_scmi_message_enqueue(sc->virtio_dev, 98*103ea03eSCristian Marussi VIRTIO_SCMI_CHAN_P2A, buf, 0, max_msg_sz); 99a87dd741SCristian Marussi } 100a87dd741SCristian Marussi 101a87dd741SCristian Marussi device_printf(dev, 102a87dd741SCristian Marussi "Fed %d initial P2A buffers to platform.\n", max_msg); 103a87dd741SCristian Marussi 104a87dd741SCristian Marussi return (pool); 105a87dd741SCristian Marussi } 106a87dd741SCristian Marussi 107a87dd741SCristian Marussi static void 108a87dd741SCristian Marussi scmi_virtio_clear_channel(device_t dev, void *msg) 109a87dd741SCristian Marussi { 110a87dd741SCristian Marussi struct scmi_virtio_softc *sc; 111a87dd741SCristian Marussi 112a87dd741SCristian Marussi sc = device_get_softc(dev); 113a87dd741SCristian Marussi virtio_scmi_message_enqueue(sc->virtio_dev, VIRTIO_SCMI_CHAN_P2A, 114*103ea03eSCristian Marussi msg, 0, SCMI_MAX_MSG_SIZE(&sc->base)); 115a87dd741SCristian Marussi } 116a87dd741SCristian Marussi 117a87dd741SCristian Marussi static int 118a87dd741SCristian Marussi scmi_virtio_transport_init(device_t dev) 119a87dd741SCristian Marussi { 120a87dd741SCristian Marussi struct scmi_virtio_softc *sc; 121a87dd741SCristian Marussi int ret; 122a87dd741SCristian Marussi 123a87dd741SCristian Marussi sc = device_get_softc(dev); 124a87dd741SCristian Marussi 125a87dd741SCristian Marussi sc->cmdq_sz = virtio_scmi_channel_size_get(sc->virtio_dev, 126a87dd741SCristian Marussi VIRTIO_SCMI_CHAN_A2P); 127a87dd741SCristian Marussi sc->evtq_sz = virtio_scmi_channel_size_get(sc->virtio_dev, 128a87dd741SCristian Marussi VIRTIO_SCMI_CHAN_P2A); 129a87dd741SCristian Marussi 130a87dd741SCristian Marussi if (!sc->cmdq_sz) { 131a87dd741SCristian Marussi device_printf(dev, 132a87dd741SCristian Marussi "VirtIO cmdq virtqueue not found. Aborting.\n"); 133a87dd741SCristian Marussi return (ENXIO); 134a87dd741SCristian Marussi } 135a87dd741SCristian Marussi 136a87dd741SCristian Marussi /* 137a87dd741SCristian Marussi * P2A buffers are owned by the platform initially; allocate a feed an 138a87dd741SCristian Marussi * appropriate number of buffers. 139a87dd741SCristian Marussi */ 140a87dd741SCristian Marussi if (sc->evtq_sz != 0) { 141a87dd741SCristian Marussi sc->p2a_pool = scmi_virtio_p2a_pool_init(dev, sc->evtq_sz); 142a87dd741SCristian Marussi if (sc->p2a_pool == NULL) 143a87dd741SCristian Marussi return (ENOMEM); 144a87dd741SCristian Marussi } 145a87dd741SCristian Marussi 146a87dd741SCristian Marussi /* Note that setting a callback also enables that VQ interrupts */ 147a87dd741SCristian Marussi ret = virtio_scmi_channel_callback_set(sc->virtio_dev, 148a87dd741SCristian Marussi VIRTIO_SCMI_CHAN_A2P, scmi_virtio_callback, sc); 149a87dd741SCristian Marussi if (ret) { 150a87dd741SCristian Marussi device_printf(dev, "Failed to set VirtIO cmdq callback.\n"); 151a87dd741SCristian Marussi return (ENXIO); 152a87dd741SCristian Marussi } 153a87dd741SCristian Marussi 154a87dd741SCristian Marussi device_printf(dev, 155a87dd741SCristian Marussi "VirtIO cmdq virtqueue configured - cmdq_sz:%d\n", sc->cmdq_sz); 156a87dd741SCristian Marussi 157a87dd741SCristian Marussi /* P2A channel is optional */ 158a87dd741SCristian Marussi if (sc->evtq_sz) { 159a87dd741SCristian Marussi ret = virtio_scmi_channel_callback_set(sc->virtio_dev, 160a87dd741SCristian Marussi VIRTIO_SCMI_CHAN_P2A, scmi_virtio_callback, sc); 161a87dd741SCristian Marussi if (ret == 0) { 162a87dd741SCristian Marussi device_printf(dev, 163a87dd741SCristian Marussi "VirtIO evtq virtqueue configured - evtq_sz:%d\n", 164a87dd741SCristian Marussi sc->evtq_sz); 165a87dd741SCristian Marussi } else { 166a87dd741SCristian Marussi device_printf(dev, 167a87dd741SCristian Marussi "Failed to set VirtIO evtq callback.Skip.\n"); 168a87dd741SCristian Marussi sc->evtq_sz = 0; 169a87dd741SCristian Marussi } 170a87dd741SCristian Marussi } 171a87dd741SCristian Marussi 172a87dd741SCristian Marussi sc->base.trs_desc.reply_timo_ms = 100; 173a87dd741SCristian Marussi 174a87dd741SCristian Marussi return (0); 175a87dd741SCristian Marussi } 176a87dd741SCristian Marussi 177a87dd741SCristian Marussi static void 178a87dd741SCristian Marussi scmi_virtio_transport_cleanup(device_t dev) 179a87dd741SCristian Marussi { 180a87dd741SCristian Marussi struct scmi_virtio_softc *sc; 181a87dd741SCristian Marussi 182a87dd741SCristian Marussi sc = device_get_softc(dev); 183a87dd741SCristian Marussi 184a87dd741SCristian Marussi if (sc->evtq_sz != 0) { 185a87dd741SCristian Marussi virtio_scmi_channel_callback_set(sc->virtio_dev, 186a87dd741SCristian Marussi VIRTIO_SCMI_CHAN_P2A, NULL, NULL); 187a87dd741SCristian Marussi free(sc->p2a_pool, M_DEVBUF); 188a87dd741SCristian Marussi } 189a87dd741SCristian Marussi 190a87dd741SCristian Marussi virtio_scmi_channel_callback_set(sc->virtio_dev, 191a87dd741SCristian Marussi VIRTIO_SCMI_CHAN_A2P, NULL, NULL); 192a87dd741SCristian Marussi } 193a87dd741SCristian Marussi 194a87dd741SCristian Marussi static int 195a87dd741SCristian Marussi scmi_virtio_xfer_msg(device_t dev, struct scmi_msg *msg) 196a87dd741SCristian Marussi { 197a87dd741SCristian Marussi struct scmi_virtio_softc *sc; 198a87dd741SCristian Marussi 199a87dd741SCristian Marussi sc = device_get_softc(dev); 200a87dd741SCristian Marussi 201a87dd741SCristian Marussi return (virtio_scmi_message_enqueue(sc->virtio_dev, 202a87dd741SCristian Marussi VIRTIO_SCMI_CHAN_A2P, &msg->hdr, msg->tx_len, msg->rx_len)); 203a87dd741SCristian Marussi } 204a87dd741SCristian Marussi 205a87dd741SCristian Marussi static int 206a87dd741SCristian Marussi scmi_virtio_poll_msg(device_t dev, struct scmi_msg *msg, unsigned int tmo_ms) 207a87dd741SCristian Marussi { 208a87dd741SCristian Marussi struct scmi_virtio_softc *sc; 209a87dd741SCristian Marussi device_t vdev; 210a87dd741SCristian Marussi int tmo_loops; 211a87dd741SCristian Marussi 212a87dd741SCristian Marussi sc = device_get_softc(dev); 213a87dd741SCristian Marussi vdev = sc->virtio_dev; 214a87dd741SCristian Marussi 215a87dd741SCristian Marussi tmo_loops = tmo_ms / SCMI_VIRTIO_POLLING_INTERVAL_MS; 216a87dd741SCristian Marussi while (tmo_loops-- && atomic_load_acq_int(&msg->poll_done) == 0) { 217a87dd741SCristian Marussi struct scmi_msg *rx_msg; 218a87dd741SCristian Marussi void *rx_buf; 219a87dd741SCristian Marussi uint32_t rx_len; 220a87dd741SCristian Marussi 221a87dd741SCristian Marussi rx_buf = virtio_scmi_message_poll(vdev, &rx_len); 222a87dd741SCristian Marussi if (rx_buf == NULL) { 223a87dd741SCristian Marussi DELAY(SCMI_VIRTIO_POLLING_INTERVAL_MS * 1000); 224a87dd741SCristian Marussi continue; 225a87dd741SCristian Marussi } 226a87dd741SCristian Marussi 227a87dd741SCristian Marussi rx_msg = hdr_to_msg(rx_buf); 228a87dd741SCristian Marussi /* Complete the polling on any poll path */ 229a87dd741SCristian Marussi if (rx_msg->polling) 230a87dd741SCristian Marussi atomic_store_rel_int(&rx_msg->poll_done, 1); 231a87dd741SCristian Marussi 232a87dd741SCristian Marussi if (__predict_true(rx_msg == msg)) 233a87dd741SCristian Marussi break; 234a87dd741SCristian Marussi 235a87dd741SCristian Marussi /* 236a87dd741SCristian Marussi * Polling returned an unexpected message: either a message 237a87dd741SCristian Marussi * polled by some other thread of execution or a message not 238a87dd741SCristian Marussi * supposed to be polled. 239a87dd741SCristian Marussi */ 240a87dd741SCristian Marussi device_printf(dev, "POLLED OoO HDR:|%08X| - polling:%d\n", 241a87dd741SCristian Marussi rx_msg->hdr, rx_msg->polling); 242a87dd741SCristian Marussi 243a87dd741SCristian Marussi if (!rx_msg->polling) 2449342829dSCristian Marussi scmi_rx_irq_callback(sc->base.dev, rx_msg, rx_msg->hdr, rx_len); 245a87dd741SCristian Marussi } 246a87dd741SCristian Marussi 247a87dd741SCristian Marussi return (tmo_loops > 0 ? 0 : ETIMEDOUT); 248a87dd741SCristian Marussi } 249a87dd741SCristian Marussi 250a87dd741SCristian Marussi static int 251a87dd741SCristian Marussi scmi_virtio_probe(device_t dev) 252a87dd741SCristian Marussi { 253a87dd741SCristian Marussi if (!ofw_bus_is_compatible(dev, "arm,scmi-virtio")) 254a87dd741SCristian Marussi return (ENXIO); 255a87dd741SCristian Marussi 256a87dd741SCristian Marussi if (!ofw_bus_status_okay(dev)) 257a87dd741SCristian Marussi return (ENXIO); 258a87dd741SCristian Marussi 259a87dd741SCristian Marussi device_set_desc(dev, "ARM SCMI VirtIO Transport driver"); 260a87dd741SCristian Marussi 261a87dd741SCristian Marussi return (BUS_PROBE_DEFAULT); 262a87dd741SCristian Marussi } 263a87dd741SCristian Marussi 264a87dd741SCristian Marussi static int 265a87dd741SCristian Marussi scmi_virtio_attach(device_t dev) 266a87dd741SCristian Marussi { 267a87dd741SCristian Marussi struct scmi_virtio_softc *sc; 268a87dd741SCristian Marussi 269a87dd741SCristian Marussi sc = device_get_softc(dev); 270a87dd741SCristian Marussi sc->virtio_dev = virtio_scmi_transport_get(); 271a87dd741SCristian Marussi if (sc->virtio_dev == NULL) 272a87dd741SCristian Marussi return (1); 273a87dd741SCristian Marussi 274a87dd741SCristian Marussi /* When attach fails there is nothing to cleanup*/ 275a87dd741SCristian Marussi return (scmi_attach(dev)); 276a87dd741SCristian Marussi } 277a87dd741SCristian Marussi 278a87dd741SCristian Marussi static device_method_t scmi_virtio_methods[] = { 279a87dd741SCristian Marussi DEVMETHOD(device_probe, scmi_virtio_probe), 280a87dd741SCristian Marussi DEVMETHOD(device_attach, scmi_virtio_attach), 281a87dd741SCristian Marussi 282a87dd741SCristian Marussi /* SCMI interface */ 283a87dd741SCristian Marussi DEVMETHOD(scmi_transport_init, scmi_virtio_transport_init), 284a87dd741SCristian Marussi DEVMETHOD(scmi_transport_cleanup, scmi_virtio_transport_cleanup), 285a87dd741SCristian Marussi DEVMETHOD(scmi_xfer_msg, scmi_virtio_xfer_msg), 286a87dd741SCristian Marussi DEVMETHOD(scmi_poll_msg, scmi_virtio_poll_msg), 287a87dd741SCristian Marussi DEVMETHOD(scmi_clear_channel, scmi_virtio_clear_channel), 288a87dd741SCristian Marussi 289a87dd741SCristian Marussi DEVMETHOD_END 290a87dd741SCristian Marussi }; 291a87dd741SCristian Marussi 292a87dd741SCristian Marussi DEFINE_CLASS_1(scmi_virtio, scmi_virtio_driver, scmi_virtio_methods, 293a87dd741SCristian Marussi sizeof(struct scmi_virtio_softc), scmi_driver); 294a87dd741SCristian Marussi 295a87dd741SCristian Marussi /* Needs to be after the mmio_sram driver */ 296a87dd741SCristian Marussi DRIVER_MODULE(scmi_virtio, simplebus, scmi_virtio_driver, 0, 0); 297a87dd741SCristian Marussi MODULE_VERSION(scmi_virtio, 1); 298