xref: /freebsd/sys/dev/virtio/scmi/virtio_scmi.c (revision e1c4c8dd8d2d10b6104f06856a77bd5b4813a801)
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