xref: /freebsd/sys/dev/firmware/arm/scmi_virtio.c (revision 103ea03e6713a0f3bb874472fbd9fd81e2278805)
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
scmi_virtio_callback(void * msg,unsigned int len,void * priv)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 *
scmi_virtio_p2a_pool_init(device_t dev,unsigned int max_msg)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
scmi_virtio_clear_channel(device_t dev,void * msg)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
scmi_virtio_transport_init(device_t dev)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
scmi_virtio_transport_cleanup(device_t dev)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
scmi_virtio_xfer_msg(device_t dev,struct scmi_msg * msg)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
scmi_virtio_poll_msg(device_t dev,struct scmi_msg * msg,unsigned int tmo_ms)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
scmi_virtio_probe(device_t dev)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
scmi_virtio_attach(device_t dev)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