1*e1c4c8ddSCristian Marussi /*- 2*e1c4c8ddSCristian Marussi * SPDX-License-Identifier: BSD-2-Clause 3*e1c4c8ddSCristian Marussi * 4*e1c4c8ddSCristian Marussi * Copyright (c) 2023 Arm Ltd 5*e1c4c8ddSCristian Marussi * 6*e1c4c8ddSCristian Marussi * Redistribution and use in source and binary forms, with or without 7*e1c4c8ddSCristian Marussi * modification, are permitted provided that the following conditions 8*e1c4c8ddSCristian Marussi * are met: 9*e1c4c8ddSCristian Marussi * 1. Redistributions of source code must retain the above copyright 10*e1c4c8ddSCristian Marussi * notice unmodified, this list of conditions, and the following 11*e1c4c8ddSCristian Marussi * disclaimer. 12*e1c4c8ddSCristian Marussi * 2. Redistributions in binary form must reproduce the above copyright 13*e1c4c8ddSCristian Marussi * notice, this list of conditions and the following disclaimer in the 14*e1c4c8ddSCristian Marussi * documentation and/or other materials provided with the distribution. 15*e1c4c8ddSCristian Marussi * 16*e1c4c8ddSCristian Marussi * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17*e1c4c8ddSCristian Marussi * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18*e1c4c8ddSCristian Marussi * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19*e1c4c8ddSCristian Marussi * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20*e1c4c8ddSCristian Marussi * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21*e1c4c8ddSCristian Marussi * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22*e1c4c8ddSCristian Marussi * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23*e1c4c8ddSCristian Marussi * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24*e1c4c8ddSCristian Marussi * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25*e1c4c8ddSCristian Marussi * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26*e1c4c8ddSCristian Marussi */ 27*e1c4c8ddSCristian Marussi 28*e1c4c8ddSCristian Marussi /* Driver for VirtIO SCMI device. */ 29*e1c4c8ddSCristian Marussi 30*e1c4c8ddSCristian Marussi #include <sys/cdefs.h> 31*e1c4c8ddSCristian Marussi #include <sys/param.h> 32*e1c4c8ddSCristian Marussi #include <sys/types.h> 33*e1c4c8ddSCristian Marussi #include <sys/eventhandler.h> 34*e1c4c8ddSCristian Marussi #include <sys/kernel.h> 35*e1c4c8ddSCristian Marussi #include <sys/malloc.h> 36*e1c4c8ddSCristian Marussi #include <sys/module.h> 37*e1c4c8ddSCristian Marussi #include <sys/queue.h> 38*e1c4c8ddSCristian Marussi #include <sys/sglist.h> 39*e1c4c8ddSCristian Marussi 40*e1c4c8ddSCristian Marussi #include <machine/bus.h> 41*e1c4c8ddSCristian Marussi #include <machine/resource.h> 42*e1c4c8ddSCristian Marussi #include <sys/bus.h> 43*e1c4c8ddSCristian Marussi 44*e1c4c8ddSCristian Marussi #include <dev/virtio/virtio.h> 45*e1c4c8ddSCristian Marussi #include <dev/virtio/virtqueue.h> 46*e1c4c8ddSCristian Marussi #include <dev/virtio/scmi/virtio_scmi.h> 47*e1c4c8ddSCristian Marussi 48*e1c4c8ddSCristian Marussi struct vtscmi_pdu { 49*e1c4c8ddSCristian Marussi enum vtscmi_chan chan; 50*e1c4c8ddSCristian Marussi struct sglist sg; 51*e1c4c8ddSCristian Marussi struct sglist_seg segs[2]; 52*e1c4c8ddSCristian Marussi void *buf; 53*e1c4c8ddSCristian Marussi SLIST_ENTRY(vtscmi_pdu) next; 54*e1c4c8ddSCristian Marussi }; 55*e1c4c8ddSCristian Marussi 56*e1c4c8ddSCristian Marussi struct vtscmi_queue { 57*e1c4c8ddSCristian Marussi device_t dev; 58*e1c4c8ddSCristian Marussi int vq_id; 59*e1c4c8ddSCristian Marussi unsigned int vq_sz; 60*e1c4c8ddSCristian Marussi struct virtqueue *vq; 61*e1c4c8ddSCristian Marussi struct mtx vq_mtx; 62*e1c4c8ddSCristian Marussi struct vtscmi_pdu *pdus; 63*e1c4c8ddSCristian Marussi SLIST_HEAD(pdus_head, vtscmi_pdu) p_head; 64*e1c4c8ddSCristian Marussi struct mtx p_mtx; 65*e1c4c8ddSCristian Marussi virtio_scmi_rx_callback_t *rx_callback; 66*e1c4c8ddSCristian Marussi void *priv; 67*e1c4c8ddSCristian Marussi }; 68*e1c4c8ddSCristian Marussi 69*e1c4c8ddSCristian Marussi struct vtscmi_softc { 70*e1c4c8ddSCristian Marussi device_t vtscmi_dev; 71*e1c4c8ddSCristian Marussi uint64_t vtscmi_features; 72*e1c4c8ddSCristian Marussi uint8_t vtscmi_vqs_cnt; 73*e1c4c8ddSCristian Marussi struct vtscmi_queue vtscmi_queues[VIRTIO_SCMI_CHAN_MAX]; 74*e1c4c8ddSCristian Marussi bool has_p2a; 75*e1c4c8ddSCristian Marussi bool has_shared; 76*e1c4c8ddSCristian Marussi }; 77*e1c4c8ddSCristian Marussi 78*e1c4c8ddSCristian Marussi static device_t vtscmi_dev; 79*e1c4c8ddSCristian Marussi 80*e1c4c8ddSCristian Marussi static int vtscmi_modevent(module_t, int, void *); 81*e1c4c8ddSCristian Marussi 82*e1c4c8ddSCristian Marussi static int vtscmi_probe(device_t); 83*e1c4c8ddSCristian Marussi static int vtscmi_attach(device_t); 84*e1c4c8ddSCristian Marussi static int vtscmi_detach(device_t); 85*e1c4c8ddSCristian Marussi static int vtscmi_shutdown(device_t); 86*e1c4c8ddSCristian Marussi static int vtscmi_negotiate_features(struct vtscmi_softc *); 87*e1c4c8ddSCristian Marussi static int vtscmi_setup_features(struct vtscmi_softc *); 88*e1c4c8ddSCristian Marussi static void vtscmi_vq_intr(void *); 89*e1c4c8ddSCristian Marussi static int vtscmi_alloc_virtqueues(struct vtscmi_softc *); 90*e1c4c8ddSCristian Marussi static int vtscmi_alloc_queues(struct vtscmi_softc *); 91*e1c4c8ddSCristian Marussi static void vtscmi_free_queues(struct vtscmi_softc *); 92*e1c4c8ddSCristian Marussi static void *virtio_scmi_pdu_get(struct vtscmi_queue *, void *, 93*e1c4c8ddSCristian Marussi unsigned int, unsigned int); 94*e1c4c8ddSCristian Marussi static void virtio_scmi_pdu_put(device_t, struct vtscmi_pdu *); 95*e1c4c8ddSCristian Marussi 96*e1c4c8ddSCristian Marussi static struct virtio_feature_desc vtscmi_feature_desc[] = { 97*e1c4c8ddSCristian Marussi { VIRTIO_SCMI_F_P2A_CHANNELS, "P2AChannel" }, 98*e1c4c8ddSCristian Marussi { VIRTIO_SCMI_F_SHARED_MEMORY, "SharedMem" }, 99*e1c4c8ddSCristian Marussi { 0, NULL } 100*e1c4c8ddSCristian Marussi }; 101*e1c4c8ddSCristian Marussi 102*e1c4c8ddSCristian Marussi static device_method_t vtscmi_methods[] = { 103*e1c4c8ddSCristian Marussi /* Device methods. */ 104*e1c4c8ddSCristian Marussi DEVMETHOD(device_probe, vtscmi_probe), 105*e1c4c8ddSCristian Marussi DEVMETHOD(device_attach, vtscmi_attach), 106*e1c4c8ddSCristian Marussi DEVMETHOD(device_detach, vtscmi_detach), 107*e1c4c8ddSCristian Marussi DEVMETHOD(device_shutdown, vtscmi_shutdown), 108*e1c4c8ddSCristian Marussi 109*e1c4c8ddSCristian Marussi DEVMETHOD_END 110*e1c4c8ddSCristian Marussi }; 111*e1c4c8ddSCristian Marussi 112*e1c4c8ddSCristian Marussi static driver_t vtscmi_driver = { 113*e1c4c8ddSCristian Marussi "vtscmi", 114*e1c4c8ddSCristian Marussi vtscmi_methods, 115*e1c4c8ddSCristian Marussi sizeof(struct vtscmi_softc) 116*e1c4c8ddSCristian Marussi }; 117*e1c4c8ddSCristian Marussi 118*e1c4c8ddSCristian Marussi VIRTIO_DRIVER_MODULE(virtio_scmi, vtscmi_driver, vtscmi_modevent, NULL); 119*e1c4c8ddSCristian Marussi MODULE_VERSION(virtio_scmi, 1); 120*e1c4c8ddSCristian Marussi MODULE_DEPEND(virtio_scmi, virtio, 1, 1, 1); 121*e1c4c8ddSCristian Marussi 122*e1c4c8ddSCristian Marussi VIRTIO_SIMPLE_PNPINFO(virtio_scmi, VIRTIO_ID_SCMI, "VirtIO SCMI Adapter"); 123*e1c4c8ddSCristian Marussi 124*e1c4c8ddSCristian Marussi static int 125*e1c4c8ddSCristian Marussi vtscmi_modevent(module_t mod, int type, void *unused) 126*e1c4c8ddSCristian Marussi { 127*e1c4c8ddSCristian Marussi int error; 128*e1c4c8ddSCristian Marussi 129*e1c4c8ddSCristian Marussi switch (type) { 130*e1c4c8ddSCristian Marussi case MOD_LOAD: 131*e1c4c8ddSCristian Marussi case MOD_QUIESCE: 132*e1c4c8ddSCristian Marussi case MOD_UNLOAD: 133*e1c4c8ddSCristian Marussi case MOD_SHUTDOWN: 134*e1c4c8ddSCristian Marussi error = 0; 135*e1c4c8ddSCristian Marussi break; 136*e1c4c8ddSCristian Marussi default: 137*e1c4c8ddSCristian Marussi error = EOPNOTSUPP; 138*e1c4c8ddSCristian Marussi break; 139*e1c4c8ddSCristian Marussi } 140*e1c4c8ddSCristian Marussi 141*e1c4c8ddSCristian Marussi return (error); 142*e1c4c8ddSCristian Marussi } 143*e1c4c8ddSCristian Marussi 144*e1c4c8ddSCristian Marussi static int 145*e1c4c8ddSCristian Marussi vtscmi_probe(device_t dev) 146*e1c4c8ddSCristian Marussi { 147*e1c4c8ddSCristian Marussi return (VIRTIO_SIMPLE_PROBE(dev, virtio_scmi)); 148*e1c4c8ddSCristian Marussi } 149*e1c4c8ddSCristian Marussi 150*e1c4c8ddSCristian Marussi static int 151*e1c4c8ddSCristian Marussi vtscmi_attach(device_t dev) 152*e1c4c8ddSCristian Marussi { 153*e1c4c8ddSCristian Marussi struct vtscmi_softc *sc; 154*e1c4c8ddSCristian Marussi int error; 155*e1c4c8ddSCristian Marussi 156*e1c4c8ddSCristian Marussi /* Only one SCMI device per-agent */ 157*e1c4c8ddSCristian Marussi if (vtscmi_dev != NULL) 158*e1c4c8ddSCristian Marussi return (EEXIST); 159*e1c4c8ddSCristian Marussi 160*e1c4c8ddSCristian Marussi sc = device_get_softc(dev); 161*e1c4c8ddSCristian Marussi sc->vtscmi_dev = dev; 162*e1c4c8ddSCristian Marussi 163*e1c4c8ddSCristian Marussi virtio_set_feature_desc(dev, vtscmi_feature_desc); 164*e1c4c8ddSCristian Marussi error = vtscmi_setup_features(sc); 165*e1c4c8ddSCristian Marussi if (error) { 166*e1c4c8ddSCristian Marussi device_printf(dev, "cannot setup features\n"); 167*e1c4c8ddSCristian Marussi goto fail; 168*e1c4c8ddSCristian Marussi } 169*e1c4c8ddSCristian Marussi 170*e1c4c8ddSCristian Marussi error = vtscmi_alloc_virtqueues(sc); 171*e1c4c8ddSCristian Marussi if (error) { 172*e1c4c8ddSCristian Marussi device_printf(dev, "cannot allocate virtqueues\n"); 173*e1c4c8ddSCristian Marussi goto fail; 174*e1c4c8ddSCristian Marussi } 175*e1c4c8ddSCristian Marussi 176*e1c4c8ddSCristian Marussi error = vtscmi_alloc_queues(sc); 177*e1c4c8ddSCristian Marussi if (error) { 178*e1c4c8ddSCristian Marussi device_printf(dev, "cannot allocate queues\n"); 179*e1c4c8ddSCristian Marussi goto fail; 180*e1c4c8ddSCristian Marussi } 181*e1c4c8ddSCristian Marussi 182*e1c4c8ddSCristian Marussi error = virtio_setup_intr(dev, INTR_TYPE_MISC); 183*e1c4c8ddSCristian Marussi if (error) { 184*e1c4c8ddSCristian Marussi device_printf(dev, "cannot setup intr\n"); 185*e1c4c8ddSCristian Marussi vtscmi_free_queues(sc); 186*e1c4c8ddSCristian Marussi goto fail; 187*e1c4c8ddSCristian Marussi } 188*e1c4c8ddSCristian Marussi 189*e1c4c8ddSCristian Marussi /* Save unique device */ 190*e1c4c8ddSCristian Marussi vtscmi_dev = sc->vtscmi_dev; 191*e1c4c8ddSCristian Marussi 192*e1c4c8ddSCristian Marussi fail: 193*e1c4c8ddSCristian Marussi 194*e1c4c8ddSCristian Marussi return (error); 195*e1c4c8ddSCristian Marussi } 196*e1c4c8ddSCristian Marussi 197*e1c4c8ddSCristian Marussi static int 198*e1c4c8ddSCristian Marussi vtscmi_detach(device_t dev) 199*e1c4c8ddSCristian Marussi { 200*e1c4c8ddSCristian Marussi struct vtscmi_softc *sc; 201*e1c4c8ddSCristian Marussi 202*e1c4c8ddSCristian Marussi sc = device_get_softc(dev); 203*e1c4c8ddSCristian Marussi 204*e1c4c8ddSCristian Marussi /* These also disable related interrupts */ 205*e1c4c8ddSCristian Marussi virtio_scmi_channel_callback_set(dev, VIRTIO_SCMI_CHAN_A2P, NULL, NULL); 206*e1c4c8ddSCristian Marussi virtio_scmi_channel_callback_set(dev, VIRTIO_SCMI_CHAN_P2A, NULL, NULL); 207*e1c4c8ddSCristian Marussi 208*e1c4c8ddSCristian Marussi virtio_stop(dev); 209*e1c4c8ddSCristian Marussi 210*e1c4c8ddSCristian Marussi vtscmi_free_queues(sc); 211*e1c4c8ddSCristian Marussi 212*e1c4c8ddSCristian Marussi return (0); 213*e1c4c8ddSCristian Marussi } 214*e1c4c8ddSCristian Marussi 215*e1c4c8ddSCristian Marussi static int 216*e1c4c8ddSCristian Marussi vtscmi_shutdown(device_t dev) 217*e1c4c8ddSCristian Marussi { 218*e1c4c8ddSCristian Marussi 219*e1c4c8ddSCristian Marussi return (0); 220*e1c4c8ddSCristian Marussi } 221*e1c4c8ddSCristian Marussi 222*e1c4c8ddSCristian Marussi static int 223*e1c4c8ddSCristian Marussi vtscmi_negotiate_features(struct vtscmi_softc *sc) 224*e1c4c8ddSCristian Marussi { 225*e1c4c8ddSCristian Marussi device_t dev; 226*e1c4c8ddSCristian Marussi uint64_t features; 227*e1c4c8ddSCristian Marussi 228*e1c4c8ddSCristian Marussi dev = sc->vtscmi_dev; 229*e1c4c8ddSCristian Marussi /* We still don't support shared mem (stats)...so don't advertise it */ 230*e1c4c8ddSCristian Marussi features = VIRTIO_SCMI_F_P2A_CHANNELS; 231*e1c4c8ddSCristian Marussi 232*e1c4c8ddSCristian Marussi sc->vtscmi_features = virtio_negotiate_features(dev, features); 233*e1c4c8ddSCristian Marussi return (virtio_finalize_features(dev)); 234*e1c4c8ddSCristian Marussi } 235*e1c4c8ddSCristian Marussi 236*e1c4c8ddSCristian Marussi static int 237*e1c4c8ddSCristian Marussi vtscmi_setup_features(struct vtscmi_softc *sc) 238*e1c4c8ddSCristian Marussi { 239*e1c4c8ddSCristian Marussi device_t dev; 240*e1c4c8ddSCristian Marussi int error; 241*e1c4c8ddSCristian Marussi 242*e1c4c8ddSCristian Marussi dev = sc->vtscmi_dev; 243*e1c4c8ddSCristian Marussi error = vtscmi_negotiate_features(sc); 244*e1c4c8ddSCristian Marussi if (error) 245*e1c4c8ddSCristian Marussi return (error); 246*e1c4c8ddSCristian Marussi 247*e1c4c8ddSCristian Marussi if (virtio_with_feature(dev, VIRTIO_SCMI_F_P2A_CHANNELS)) 248*e1c4c8ddSCristian Marussi sc->has_p2a = true; 249*e1c4c8ddSCristian Marussi if (virtio_with_feature(dev, VIRTIO_SCMI_F_SHARED_MEMORY)) 250*e1c4c8ddSCristian Marussi sc->has_shared = true; 251*e1c4c8ddSCristian Marussi 252*e1c4c8ddSCristian Marussi device_printf(dev, "Platform %s P2A channel.\n", 253*e1c4c8ddSCristian Marussi sc->has_p2a ? "supports" : "does NOT support"); 254*e1c4c8ddSCristian Marussi 255*e1c4c8ddSCristian Marussi return (0); 256*e1c4c8ddSCristian Marussi } 257*e1c4c8ddSCristian Marussi 258*e1c4c8ddSCristian Marussi static int 259*e1c4c8ddSCristian Marussi vtscmi_alloc_queues(struct vtscmi_softc *sc) 260*e1c4c8ddSCristian Marussi { 261*e1c4c8ddSCristian Marussi int idx; 262*e1c4c8ddSCristian Marussi 263*e1c4c8ddSCristian Marussi for (idx = VIRTIO_SCMI_CHAN_A2P; idx < VIRTIO_SCMI_CHAN_MAX; idx++) { 264*e1c4c8ddSCristian Marussi int i, vq_sz; 265*e1c4c8ddSCristian Marussi struct vtscmi_queue *q; 266*e1c4c8ddSCristian Marussi struct vtscmi_pdu *pdu; 267*e1c4c8ddSCristian Marussi 268*e1c4c8ddSCristian Marussi if (idx == VIRTIO_SCMI_CHAN_P2A && !sc->has_p2a) 269*e1c4c8ddSCristian Marussi continue; 270*e1c4c8ddSCristian Marussi 271*e1c4c8ddSCristian Marussi q = &sc->vtscmi_queues[idx]; 272*e1c4c8ddSCristian Marussi q->dev = sc->vtscmi_dev; 273*e1c4c8ddSCristian Marussi q->vq_id = idx; 274*e1c4c8ddSCristian Marussi vq_sz = virtqueue_size(q->vq); 275*e1c4c8ddSCristian Marussi q->vq_sz = idx != VIRTIO_SCMI_CHAN_A2P ? vq_sz : vq_sz / 2; 276*e1c4c8ddSCristian Marussi 277*e1c4c8ddSCristian Marussi q->pdus = mallocarray(q->vq_sz, sizeof(*pdu), M_DEVBUF, 278*e1c4c8ddSCristian Marussi M_ZERO | M_WAITOK); 279*e1c4c8ddSCristian Marussi 280*e1c4c8ddSCristian Marussi SLIST_INIT(&q->p_head); 281*e1c4c8ddSCristian Marussi for (i = 0, pdu = q->pdus; i < q->vq_sz; i++, pdu++) { 282*e1c4c8ddSCristian Marussi pdu->chan = idx; 283*e1c4c8ddSCristian Marussi //XXX Maybe one seg redndant for P2A 284*e1c4c8ddSCristian Marussi sglist_init(&pdu->sg, 285*e1c4c8ddSCristian Marussi idx == VIRTIO_SCMI_CHAN_A2P ? 2 : 1, pdu->segs); 286*e1c4c8ddSCristian Marussi SLIST_INSERT_HEAD(&q->p_head, pdu, next); 287*e1c4c8ddSCristian Marussi } 288*e1c4c8ddSCristian Marussi 289*e1c4c8ddSCristian Marussi mtx_init(&q->p_mtx, "vtscmi_pdus", "VTSCMI", MTX_SPIN); 290*e1c4c8ddSCristian Marussi mtx_init(&q->vq_mtx, "vtscmi_vq", "VTSCMI", MTX_SPIN); 291*e1c4c8ddSCristian Marussi } 292*e1c4c8ddSCristian Marussi 293*e1c4c8ddSCristian Marussi return (0); 294*e1c4c8ddSCristian Marussi } 295*e1c4c8ddSCristian Marussi 296*e1c4c8ddSCristian Marussi static void 297*e1c4c8ddSCristian Marussi vtscmi_free_queues(struct vtscmi_softc *sc) 298*e1c4c8ddSCristian Marussi { 299*e1c4c8ddSCristian Marussi int idx; 300*e1c4c8ddSCristian Marussi 301*e1c4c8ddSCristian Marussi for (idx = VIRTIO_SCMI_CHAN_A2P; idx < VIRTIO_SCMI_CHAN_MAX; idx++) { 302*e1c4c8ddSCristian Marussi struct vtscmi_queue *q; 303*e1c4c8ddSCristian Marussi 304*e1c4c8ddSCristian Marussi if (idx == VIRTIO_SCMI_CHAN_P2A && !sc->has_p2a) 305*e1c4c8ddSCristian Marussi continue; 306*e1c4c8ddSCristian Marussi 307*e1c4c8ddSCristian Marussi q = &sc->vtscmi_queues[idx]; 308*e1c4c8ddSCristian Marussi if (q->vq_sz == 0) 309*e1c4c8ddSCristian Marussi continue; 310*e1c4c8ddSCristian Marussi 311*e1c4c8ddSCristian Marussi free(q->pdus, M_DEVBUF); 312*e1c4c8ddSCristian Marussi mtx_destroy(&q->p_mtx); 313*e1c4c8ddSCristian Marussi mtx_destroy(&q->vq_mtx); 314*e1c4c8ddSCristian Marussi } 315*e1c4c8ddSCristian Marussi } 316*e1c4c8ddSCristian Marussi 317*e1c4c8ddSCristian Marussi static void 318*e1c4c8ddSCristian Marussi vtscmi_vq_intr(void *arg) 319*e1c4c8ddSCristian Marussi { 320*e1c4c8ddSCristian Marussi struct vtscmi_queue *q = arg; 321*e1c4c8ddSCristian Marussi 322*e1c4c8ddSCristian Marussi /* 323*e1c4c8ddSCristian Marussi * TODO 324*e1c4c8ddSCristian Marussi * - consider pressure on RX by msg floods 325*e1c4c8ddSCristian Marussi * + Does it need a taskqueue_ like virtio/net to postpone processing 326*e1c4c8ddSCristian Marussi * under pressure ? (SCMI is low_freq compared to network though) 327*e1c4c8ddSCristian Marussi */ 328*e1c4c8ddSCristian Marussi for (;;) { 329*e1c4c8ddSCristian Marussi struct vtscmi_pdu *pdu; 330*e1c4c8ddSCristian Marussi uint32_t rx_len; 331*e1c4c8ddSCristian Marussi 332*e1c4c8ddSCristian Marussi mtx_lock_spin(&q->vq_mtx); 333*e1c4c8ddSCristian Marussi pdu = virtqueue_dequeue(q->vq, &rx_len); 334*e1c4c8ddSCristian Marussi mtx_unlock_spin(&q->vq_mtx); 335*e1c4c8ddSCristian Marussi if (!pdu) 336*e1c4c8ddSCristian Marussi return; 337*e1c4c8ddSCristian Marussi 338*e1c4c8ddSCristian Marussi if (q->rx_callback) 339*e1c4c8ddSCristian Marussi q->rx_callback(pdu->buf, rx_len, q->priv); 340*e1c4c8ddSCristian Marussi 341*e1c4c8ddSCristian Marussi /* Note that this only frees the PDU, NOT the buffer itself */ 342*e1c4c8ddSCristian Marussi virtio_scmi_pdu_put(q->dev, pdu); 343*e1c4c8ddSCristian Marussi } 344*e1c4c8ddSCristian Marussi } 345*e1c4c8ddSCristian Marussi 346*e1c4c8ddSCristian Marussi static int 347*e1c4c8ddSCristian Marussi vtscmi_alloc_virtqueues(struct vtscmi_softc *sc) 348*e1c4c8ddSCristian Marussi { 349*e1c4c8ddSCristian Marussi device_t dev; 350*e1c4c8ddSCristian Marussi struct vq_alloc_info vq_info[VIRTIO_SCMI_CHAN_MAX]; 351*e1c4c8ddSCristian Marussi 352*e1c4c8ddSCristian Marussi dev = sc->vtscmi_dev; 353*e1c4c8ddSCristian Marussi sc->vtscmi_vqs_cnt = sc->has_p2a ? 2 : 1; 354*e1c4c8ddSCristian Marussi 355*e1c4c8ddSCristian Marussi VQ_ALLOC_INFO_INIT(&vq_info[VIRTIO_SCMI_CHAN_A2P], 0, 356*e1c4c8ddSCristian Marussi vtscmi_vq_intr, 357*e1c4c8ddSCristian Marussi &sc->vtscmi_queues[VIRTIO_SCMI_CHAN_A2P], 358*e1c4c8ddSCristian Marussi &sc->vtscmi_queues[VIRTIO_SCMI_CHAN_A2P].vq, 359*e1c4c8ddSCristian Marussi "%s cmdq", device_get_nameunit(dev)); 360*e1c4c8ddSCristian Marussi 361*e1c4c8ddSCristian Marussi if (sc->has_p2a) { 362*e1c4c8ddSCristian Marussi VQ_ALLOC_INFO_INIT(&vq_info[VIRTIO_SCMI_CHAN_P2A], 0, 363*e1c4c8ddSCristian Marussi vtscmi_vq_intr, 364*e1c4c8ddSCristian Marussi &sc->vtscmi_queues[VIRTIO_SCMI_CHAN_P2A], 365*e1c4c8ddSCristian Marussi &sc->vtscmi_queues[VIRTIO_SCMI_CHAN_P2A].vq, 366*e1c4c8ddSCristian Marussi "%s evtq", device_get_nameunit(dev)); 367*e1c4c8ddSCristian Marussi } 368*e1c4c8ddSCristian Marussi 369*e1c4c8ddSCristian Marussi return (virtio_alloc_virtqueues(dev, sc->vtscmi_vqs_cnt, vq_info)); 370*e1c4c8ddSCristian Marussi } 371*e1c4c8ddSCristian Marussi 372*e1c4c8ddSCristian Marussi static void * 373*e1c4c8ddSCristian Marussi virtio_scmi_pdu_get(struct vtscmi_queue *q, void *buf, unsigned int tx_len, 374*e1c4c8ddSCristian Marussi unsigned int rx_len) 375*e1c4c8ddSCristian Marussi { 376*e1c4c8ddSCristian Marussi struct vtscmi_pdu *pdu = NULL; 377*e1c4c8ddSCristian Marussi 378*e1c4c8ddSCristian Marussi if (rx_len == 0) 379*e1c4c8ddSCristian Marussi return (NULL); 380*e1c4c8ddSCristian Marussi 381*e1c4c8ddSCristian Marussi mtx_lock_spin(&q->p_mtx); 382*e1c4c8ddSCristian Marussi if (!SLIST_EMPTY(&q->p_head)) { 383*e1c4c8ddSCristian Marussi pdu = SLIST_FIRST(&q->p_head); 384*e1c4c8ddSCristian Marussi SLIST_REMOVE_HEAD(&q->p_head, next); 385*e1c4c8ddSCristian Marussi } 386*e1c4c8ddSCristian Marussi mtx_unlock_spin(&q->p_mtx); 387*e1c4c8ddSCristian Marussi 388*e1c4c8ddSCristian Marussi if (pdu == NULL) { 389*e1c4c8ddSCristian Marussi device_printf(q->dev, "Cannnot allocate PDU.\n"); 390*e1c4c8ddSCristian Marussi return (NULL); 391*e1c4c8ddSCristian Marussi } 392*e1c4c8ddSCristian Marussi 393*e1c4c8ddSCristian Marussi /*Save msg buffer for easy access */ 394*e1c4c8ddSCristian Marussi pdu->buf = buf; 395*e1c4c8ddSCristian Marussi if (tx_len != 0) 396*e1c4c8ddSCristian Marussi sglist_append(&pdu->sg, pdu->buf, tx_len); 397*e1c4c8ddSCristian Marussi sglist_append(&pdu->sg, pdu->buf, rx_len); 398*e1c4c8ddSCristian Marussi 399*e1c4c8ddSCristian Marussi return (pdu); 400*e1c4c8ddSCristian Marussi } 401*e1c4c8ddSCristian Marussi 402*e1c4c8ddSCristian Marussi static void 403*e1c4c8ddSCristian Marussi virtio_scmi_pdu_put(device_t dev, struct vtscmi_pdu *pdu) 404*e1c4c8ddSCristian Marussi { 405*e1c4c8ddSCristian Marussi struct vtscmi_softc *sc; 406*e1c4c8ddSCristian Marussi struct vtscmi_queue *q; 407*e1c4c8ddSCristian Marussi 408*e1c4c8ddSCristian Marussi if (pdu == NULL) 409*e1c4c8ddSCristian Marussi return; 410*e1c4c8ddSCristian Marussi 411*e1c4c8ddSCristian Marussi sc = device_get_softc(dev); 412*e1c4c8ddSCristian Marussi q = &sc->vtscmi_queues[pdu->chan]; 413*e1c4c8ddSCristian Marussi 414*e1c4c8ddSCristian Marussi sglist_reset(&pdu->sg); 415*e1c4c8ddSCristian Marussi 416*e1c4c8ddSCristian Marussi mtx_lock_spin(&q->p_mtx); 417*e1c4c8ddSCristian Marussi SLIST_INSERT_HEAD(&q->p_head, pdu, next); 418*e1c4c8ddSCristian Marussi mtx_unlock_spin(&q->p_mtx); 419*e1c4c8ddSCristian Marussi } 420*e1c4c8ddSCristian Marussi 421*e1c4c8ddSCristian Marussi device_t 422*e1c4c8ddSCristian Marussi virtio_scmi_transport_get(void) 423*e1c4c8ddSCristian Marussi { 424*e1c4c8ddSCristian Marussi return (vtscmi_dev); 425*e1c4c8ddSCristian Marussi } 426*e1c4c8ddSCristian Marussi 427*e1c4c8ddSCristian Marussi int 428*e1c4c8ddSCristian Marussi virtio_scmi_channel_size_get(device_t dev, enum vtscmi_chan chan) 429*e1c4c8ddSCristian Marussi { 430*e1c4c8ddSCristian Marussi struct vtscmi_softc *sc; 431*e1c4c8ddSCristian Marussi 432*e1c4c8ddSCristian Marussi sc = device_get_softc(dev); 433*e1c4c8ddSCristian Marussi if (chan >= sc->vtscmi_vqs_cnt) 434*e1c4c8ddSCristian Marussi return (0); 435*e1c4c8ddSCristian Marussi 436*e1c4c8ddSCristian Marussi return (sc->vtscmi_queues[chan].vq_sz); 437*e1c4c8ddSCristian Marussi } 438*e1c4c8ddSCristian Marussi 439*e1c4c8ddSCristian Marussi int 440*e1c4c8ddSCristian Marussi virtio_scmi_channel_callback_set(device_t dev, enum vtscmi_chan chan, 441*e1c4c8ddSCristian Marussi virtio_scmi_rx_callback_t *cb, void *priv) 442*e1c4c8ddSCristian Marussi { 443*e1c4c8ddSCristian Marussi struct vtscmi_softc *sc; 444*e1c4c8ddSCristian Marussi 445*e1c4c8ddSCristian Marussi sc = device_get_softc(dev); 446*e1c4c8ddSCristian Marussi if (chan >= sc->vtscmi_vqs_cnt) 447*e1c4c8ddSCristian Marussi return (1); 448*e1c4c8ddSCristian Marussi 449*e1c4c8ddSCristian Marussi if (cb == NULL) 450*e1c4c8ddSCristian Marussi virtqueue_disable_intr(sc->vtscmi_queues[chan].vq); 451*e1c4c8ddSCristian Marussi 452*e1c4c8ddSCristian Marussi sc->vtscmi_queues[chan].rx_callback = cb; 453*e1c4c8ddSCristian Marussi sc->vtscmi_queues[chan].priv = priv; 454*e1c4c8ddSCristian Marussi 455*e1c4c8ddSCristian Marussi /* Enable Interrupt on VQ once the callback is set */ 456*e1c4c8ddSCristian Marussi if (cb != NULL) 457*e1c4c8ddSCristian Marussi /* 458*e1c4c8ddSCristian Marussi * TODO 459*e1c4c8ddSCristian Marussi * Does this need a taskqueue_ task to process already pending 460*e1c4c8ddSCristian Marussi * messages ? 461*e1c4c8ddSCristian Marussi */ 462*e1c4c8ddSCristian Marussi virtqueue_enable_intr(sc->vtscmi_queues[chan].vq); 463*e1c4c8ddSCristian Marussi 464*e1c4c8ddSCristian Marussi device_printf(dev, "%sabled interrupts on VQ[%d].\n", 465*e1c4c8ddSCristian Marussi cb ? "En" : "Dis", chan); 466*e1c4c8ddSCristian Marussi 467*e1c4c8ddSCristian Marussi return (0); 468*e1c4c8ddSCristian Marussi } 469*e1c4c8ddSCristian Marussi 470*e1c4c8ddSCristian Marussi int 471*e1c4c8ddSCristian Marussi virtio_scmi_message_enqueue(device_t dev, enum vtscmi_chan chan, 472*e1c4c8ddSCristian Marussi void *buf, unsigned int tx_len, unsigned int rx_len) 473*e1c4c8ddSCristian Marussi { 474*e1c4c8ddSCristian Marussi struct vtscmi_softc *sc; 475*e1c4c8ddSCristian Marussi struct vtscmi_pdu *pdu; 476*e1c4c8ddSCristian Marussi struct vtscmi_queue *q; 477*e1c4c8ddSCristian Marussi int ret; 478*e1c4c8ddSCristian Marussi 479*e1c4c8ddSCristian Marussi sc = device_get_softc(dev); 480*e1c4c8ddSCristian Marussi if (chan >= sc->vtscmi_vqs_cnt) 481*e1c4c8ddSCristian Marussi return (1); 482*e1c4c8ddSCristian Marussi 483*e1c4c8ddSCristian Marussi q = &sc->vtscmi_queues[chan]; 484*e1c4c8ddSCristian Marussi pdu = virtio_scmi_pdu_get(q, buf, tx_len, rx_len); 485*e1c4c8ddSCristian Marussi if (pdu == NULL) 486*e1c4c8ddSCristian Marussi return (ENXIO); 487*e1c4c8ddSCristian Marussi 488*e1c4c8ddSCristian Marussi mtx_lock_spin(&q->vq_mtx); 489*e1c4c8ddSCristian Marussi ret = virtqueue_enqueue(q->vq, pdu, &pdu->sg, 490*e1c4c8ddSCristian Marussi chan == VIRTIO_SCMI_CHAN_A2P ? 1 : 0, 1); 491*e1c4c8ddSCristian Marussi if (ret == 0) 492*e1c4c8ddSCristian Marussi virtqueue_notify(q->vq); 493*e1c4c8ddSCristian Marussi mtx_unlock_spin(&q->vq_mtx); 494*e1c4c8ddSCristian Marussi 495*e1c4c8ddSCristian Marussi return (ret); 496*e1c4c8ddSCristian Marussi } 497*e1c4c8ddSCristian Marussi 498*e1c4c8ddSCristian Marussi void * 499*e1c4c8ddSCristian Marussi virtio_scmi_message_poll(device_t dev, uint32_t *rx_len) 500*e1c4c8ddSCristian Marussi { 501*e1c4c8ddSCristian Marussi struct vtscmi_softc *sc; 502*e1c4c8ddSCristian Marussi struct vtscmi_queue *q; 503*e1c4c8ddSCristian Marussi struct vtscmi_pdu *pdu; 504*e1c4c8ddSCristian Marussi void *buf = NULL; 505*e1c4c8ddSCristian Marussi 506*e1c4c8ddSCristian Marussi sc = device_get_softc(dev); 507*e1c4c8ddSCristian Marussi 508*e1c4c8ddSCristian Marussi q = &sc->vtscmi_queues[VIRTIO_SCMI_CHAN_A2P]; 509*e1c4c8ddSCristian Marussi 510*e1c4c8ddSCristian Marussi mtx_lock_spin(&q->vq_mtx); 511*e1c4c8ddSCristian Marussi /* Not using virtqueue_poll since has no configurable timeout */ 512*e1c4c8ddSCristian Marussi pdu = virtqueue_dequeue(q->vq, rx_len); 513*e1c4c8ddSCristian Marussi mtx_unlock_spin(&q->vq_mtx); 514*e1c4c8ddSCristian Marussi if (pdu != NULL) { 515*e1c4c8ddSCristian Marussi buf = pdu->buf; 516*e1c4c8ddSCristian Marussi virtio_scmi_pdu_put(dev, pdu); 517*e1c4c8ddSCristian Marussi } 518*e1c4c8ddSCristian Marussi 519*e1c4c8ddSCristian Marussi return (buf); 520*e1c4c8ddSCristian Marussi } 521