xref: /netbsd-src/sys/dev/virtio/virtio_mmio.c (revision 2cd310f610b7cfa27083b8fb09464f39df3a0163)
1*2cd310f6Sisaki /*	$NetBSD: virtio_mmio.c,v 1.14 2024/03/09 11:55:59 isaki Exp $	*/
271cde4cbSjakllsch /*	$OpenBSD: virtio_mmio.c,v 1.2 2017/02/24 17:12:31 patrick Exp $	*/
371cde4cbSjakllsch 
4b8df88e2Sthorpej /*-
5b8df88e2Sthorpej  * Copyright (c) 2024 The NetBSD Foundation, Inc.
6b8df88e2Sthorpej  * All rights reserved.
7b8df88e2Sthorpej  *
8b8df88e2Sthorpej  * This code is derived from software contributed to The NetBSD Foundation
9b8df88e2Sthorpej  * by Jason R. Thorpe.
10b8df88e2Sthorpej  *
11b8df88e2Sthorpej  * Redistribution and use in source and binary forms, with or without
12b8df88e2Sthorpej  * modification, are permitted provided that the following conditions
13b8df88e2Sthorpej  * are met:
14b8df88e2Sthorpej  * 1. Redistributions of source code must retain the above copyright
15b8df88e2Sthorpej  *    notice, this list of conditions and the following disclaimer.
16b8df88e2Sthorpej  * 2. Redistributions in binary form must reproduce the above copyright
17b8df88e2Sthorpej  *    notice, this list of conditions and the following disclaimer in the
18b8df88e2Sthorpej  *    documentation and/or other materials provided with the distribution.
19b8df88e2Sthorpej  *
20b8df88e2Sthorpej  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21b8df88e2Sthorpej  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22b8df88e2Sthorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23b8df88e2Sthorpej  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24b8df88e2Sthorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25b8df88e2Sthorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26b8df88e2Sthorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27b8df88e2Sthorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28b8df88e2Sthorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29b8df88e2Sthorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30b8df88e2Sthorpej  * POSSIBILITY OF SUCH DAMAGE.
31b8df88e2Sthorpej  */
32b8df88e2Sthorpej 
3371cde4cbSjakllsch /*
3471cde4cbSjakllsch  * Copyright (c) 2014 Patrick Wildt <patrick@blueri.se>
3571cde4cbSjakllsch  * Copyright (c) 2012 Stefan Fritsch.
3671cde4cbSjakllsch  * Copyright (c) 2010 Minoura Makoto.
3771cde4cbSjakllsch  * All rights reserved.
3871cde4cbSjakllsch  *
3971cde4cbSjakllsch  * Redistribution and use in source and binary forms, with or without
4071cde4cbSjakllsch  * modification, are permitted provided that the following conditions
4171cde4cbSjakllsch  * are met:
4271cde4cbSjakllsch  * 1. Redistributions of source code must retain the above copyright
4371cde4cbSjakllsch  *    notice, this list of conditions and the following disclaimer.
4471cde4cbSjakllsch  * 2. Redistributions in binary form must reproduce the above copyright
4571cde4cbSjakllsch  *    notice, this list of conditions and the following disclaimer in the
4671cde4cbSjakllsch  *    documentation and/or other materials provided with the distribution.
4771cde4cbSjakllsch  *
4871cde4cbSjakllsch  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
4971cde4cbSjakllsch  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
5071cde4cbSjakllsch  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
5171cde4cbSjakllsch  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
5271cde4cbSjakllsch  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
5371cde4cbSjakllsch  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
5471cde4cbSjakllsch  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
5571cde4cbSjakllsch  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
5671cde4cbSjakllsch  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
5771cde4cbSjakllsch  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
5871cde4cbSjakllsch  */
5971cde4cbSjakllsch 
6071cde4cbSjakllsch #include <sys/cdefs.h>
61*2cd310f6Sisaki __KERNEL_RCSID(0, "$NetBSD: virtio_mmio.c,v 1.14 2024/03/09 11:55:59 isaki Exp $");
6271cde4cbSjakllsch 
6371cde4cbSjakllsch #include <sys/param.h>
6471cde4cbSjakllsch #include <sys/systm.h>
6571cde4cbSjakllsch #include <sys/kernel.h>
6671cde4cbSjakllsch #include <sys/device.h>
6771cde4cbSjakllsch #include <sys/mutex.h>
6871cde4cbSjakllsch 
6971cde4cbSjakllsch #define VIRTIO_PRIVATE
7071cde4cbSjakllsch #include <dev/virtio/virtio_mmiovar.h>
7171cde4cbSjakllsch 
7271cde4cbSjakllsch #define VIRTIO_MMIO_MAGIC		('v' | 'i' << 8 | 'r' << 16 | 't' << 24)
7371cde4cbSjakllsch 
7471cde4cbSjakllsch #define VIRTIO_MMIO_MAGIC_VALUE		0x000
7571cde4cbSjakllsch #define VIRTIO_MMIO_VERSION		0x004
7671cde4cbSjakllsch #define VIRTIO_MMIO_DEVICE_ID		0x008
7771cde4cbSjakllsch #define VIRTIO_MMIO_VENDOR_ID		0x00c
78b8df88e2Sthorpej #define VIRTIO_MMIO_DEVICE_FEATURES	0x010	/* "HostFeatures" in v1 */
79b8df88e2Sthorpej #define VIRTIO_MMIO_DEVICE_FEATURES_SEL	0x014	/* "HostFeaturesSel" in v1 */
80b8df88e2Sthorpej #define VIRTIO_MMIO_DRIVER_FEATURES	0x020	/* "GuestFeatures" in v1 */
81b8df88e2Sthorpej #define VIRTIO_MMIO_DRIVER_FEATURES_SEL	0x024	/* "GuestFeaturesSel" in v1 */
82b8df88e2Sthorpej #define VIRTIO_MMIO_V1_GUEST_PAGE_SIZE	0x028
8371cde4cbSjakllsch #define VIRTIO_MMIO_QUEUE_SEL		0x030
8471cde4cbSjakllsch #define VIRTIO_MMIO_QUEUE_NUM_MAX	0x034
8571cde4cbSjakllsch #define VIRTIO_MMIO_QUEUE_NUM		0x038
86b8df88e2Sthorpej #define VIRTIO_MMIO_V1_QUEUE_ALIGN	0x03c
87b8df88e2Sthorpej #define VIRTIO_MMIO_V1_QUEUE_PFN	0x040
88b8df88e2Sthorpej #define	VIRTIO_MMIO_QUEUE_READY		0x044
8971cde4cbSjakllsch #define VIRTIO_MMIO_QUEUE_NOTIFY	0x050
9071cde4cbSjakllsch #define VIRTIO_MMIO_INTERRUPT_STATUS	0x060
9171cde4cbSjakllsch #define VIRTIO_MMIO_INTERRUPT_ACK	0x064
9271cde4cbSjakllsch #define VIRTIO_MMIO_STATUS		0x070
93b8df88e2Sthorpej #define	VIRTIO_MMIO_V2_QUEUE_DESC_LOW	0x080
94b8df88e2Sthorpej #define	VIRTIO_MMIO_V2_QUEUE_DESC_HIGH	0x084
95b8df88e2Sthorpej #define	VIRTIO_MMIO_V2_QUEUE_AVAIL_LOW	0x090
96b8df88e2Sthorpej #define	VIRTIO_MMIO_V2_QUEUE_AVAIL_HIGH	0x094
97b8df88e2Sthorpej #define	VIRTIO_MMIO_V2_QUEUE_USED_LOW	0x0a0
98b8df88e2Sthorpej #define	VIRTIO_MMIO_V2_QUEUE_USED_HIGH	0x0a4
99b8df88e2Sthorpej #define	VIRTIO_MMIO_V2_CONFIG_GEN	0x0fc
10071cde4cbSjakllsch #define VIRTIO_MMIO_CONFIG		0x100
10171cde4cbSjakllsch 
10271cde4cbSjakllsch /*
10303c5faf6Sreinoud  * MMIO configuration space for virtio-mmio v1 is in guest byte order.
10403c5faf6Sreinoud  *
1059c018325Srin  * XXX For big-endian aarch64 and arm, see note in virtio_pci.c.
106ac87b8e9Sjmcneill  */
10703c5faf6Sreinoud 
1089c018325Srin #if (defined(__aarch64__) || defined(__arm__)) && BYTE_ORDER == BIG_ENDIAN
109e225d1d2Sreinoud #	define READ_ENDIAN	LITTLE_ENDIAN
110e225d1d2Sreinoud #	define STRUCT_ENDIAN	BIG_ENDIAN
111e225d1d2Sreinoud #elif BYTE_ORDER == BIG_ENDIAN
112e225d1d2Sreinoud #	define READ_ENDIAN	BIG_ENDIAN
113e225d1d2Sreinoud #	define STRUCT_ENDIAN	BIG_ENDIAN
114ac87b8e9Sjmcneill #else
115e225d1d2Sreinoud #	define READ_ENDIAN	LITTLE_ENDIAN
116e225d1d2Sreinoud #	define STRUCT_ENDIAN	LITTLE_ENDIAN
117ac87b8e9Sjmcneill #endif
118ac87b8e9Sjmcneill 
11971cde4cbSjakllsch 
12071cde4cbSjakllsch static void	virtio_mmio_kick(struct virtio_softc *, uint16_t);
12171cde4cbSjakllsch static uint16_t	virtio_mmio_read_queue_size(struct virtio_softc *, uint16_t);
122b8df88e2Sthorpej static void	virtio_mmio_v1_setup_queue(struct virtio_softc *, uint16_t, uint64_t);
123b8df88e2Sthorpej static void	virtio_mmio_v2_setup_queue(struct virtio_softc *, uint16_t, uint64_t);
124372fc0a7Sthorpej static int	virtio_mmio_get_status(struct virtio_softc *);
12571cde4cbSjakllsch static void	virtio_mmio_set_status(struct virtio_softc *, int);
12603c5faf6Sreinoud static void	virtio_mmio_negotiate_features(struct virtio_softc *, uint64_t);
127198576feSyamaguchi static int	virtio_mmio_alloc_interrupts(struct virtio_softc *);
12871cde4cbSjakllsch static void	virtio_mmio_free_interrupts(struct virtio_softc *);
129198576feSyamaguchi static int	virtio_mmio_setup_interrupts(struct virtio_softc *, int);
13071cde4cbSjakllsch 
131b8df88e2Sthorpej static uint32_t
virtio_mmio_reg_read(struct virtio_mmio_softc * sc,bus_addr_t reg)132b8df88e2Sthorpej virtio_mmio_reg_read(struct virtio_mmio_softc *sc, bus_addr_t reg)
133b8df88e2Sthorpej {
134b8df88e2Sthorpej 	uint32_t val;
135b8df88e2Sthorpej 
136b8df88e2Sthorpej 	val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, reg);
137b8df88e2Sthorpej 	if (sc->sc_le_regs) {
138b8df88e2Sthorpej 		val = le32toh(val);
139b8df88e2Sthorpej 	}
140b8df88e2Sthorpej 	return val;
141b8df88e2Sthorpej }
142b8df88e2Sthorpej 
143b8df88e2Sthorpej static void
virtio_mmio_reg_write(struct virtio_mmio_softc * sc,bus_addr_t reg,uint32_t val)144b8df88e2Sthorpej virtio_mmio_reg_write(struct virtio_mmio_softc *sc, bus_addr_t reg,
145b8df88e2Sthorpej     uint32_t val)
146b8df88e2Sthorpej {
147b8df88e2Sthorpej 	if (sc->sc_le_regs) {
148b8df88e2Sthorpej 		val = htole32(val);
149b8df88e2Sthorpej 	}
150b8df88e2Sthorpej 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, reg, val);
151b8df88e2Sthorpej }
152b8df88e2Sthorpej 
153b8df88e2Sthorpej static void
virtio_mmio_v2_set_addr(struct virtio_mmio_softc * sc,bus_addr_t reg,uint64_t addr)154b8df88e2Sthorpej virtio_mmio_v2_set_addr(struct virtio_mmio_softc *sc, bus_addr_t reg,
155b8df88e2Sthorpej     uint64_t addr)
156b8df88e2Sthorpej {
157b8df88e2Sthorpej 	virtio_mmio_reg_write(sc, reg,     BUS_ADDR_LO32(addr));
158b8df88e2Sthorpej 	virtio_mmio_reg_write(sc, reg + 4, BUS_ADDR_HI32(addr));
159b8df88e2Sthorpej }
160b8df88e2Sthorpej 
161b8df88e2Sthorpej static const struct virtio_ops virtio_mmio_v1_ops = {
16271cde4cbSjakllsch 	.kick = virtio_mmio_kick,
16371cde4cbSjakllsch 	.read_queue_size = virtio_mmio_read_queue_size,
164b8df88e2Sthorpej 	.setup_queue = virtio_mmio_v1_setup_queue,
165b8df88e2Sthorpej 	.set_status = virtio_mmio_set_status,
166b8df88e2Sthorpej 	.neg_features = virtio_mmio_negotiate_features,
167b8df88e2Sthorpej 	.alloc_interrupts = virtio_mmio_alloc_interrupts,
168b8df88e2Sthorpej 	.free_interrupts = virtio_mmio_free_interrupts,
169b8df88e2Sthorpej 	.setup_interrupts = virtio_mmio_setup_interrupts,
170b8df88e2Sthorpej };
171b8df88e2Sthorpej 
172b8df88e2Sthorpej static const struct virtio_ops virtio_mmio_v2_ops = {
173b8df88e2Sthorpej 	.kick = virtio_mmio_kick,
174b8df88e2Sthorpej 	.read_queue_size = virtio_mmio_read_queue_size,
175b8df88e2Sthorpej 	.setup_queue = virtio_mmio_v2_setup_queue,
17671cde4cbSjakllsch 	.set_status = virtio_mmio_set_status,
17771cde4cbSjakllsch 	.neg_features = virtio_mmio_negotiate_features,
178198576feSyamaguchi 	.alloc_interrupts = virtio_mmio_alloc_interrupts,
17971cde4cbSjakllsch 	.free_interrupts = virtio_mmio_free_interrupts,
180198576feSyamaguchi 	.setup_interrupts = virtio_mmio_setup_interrupts,
18171cde4cbSjakllsch };
18271cde4cbSjakllsch 
18371cde4cbSjakllsch static uint16_t
virtio_mmio_read_queue_size(struct virtio_softc * vsc,uint16_t idx)18471cde4cbSjakllsch virtio_mmio_read_queue_size(struct virtio_softc *vsc, uint16_t idx)
18571cde4cbSjakllsch {
18671cde4cbSjakllsch 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
187b8df88e2Sthorpej 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_SEL, idx);
188b8df88e2Sthorpej 	return virtio_mmio_reg_read(sc, VIRTIO_MMIO_QUEUE_NUM_MAX);
18971cde4cbSjakllsch }
19071cde4cbSjakllsch 
19171cde4cbSjakllsch static void
virtio_mmio_v1_setup_queue(struct virtio_softc * vsc,uint16_t idx,uint64_t addr)192b8df88e2Sthorpej virtio_mmio_v1_setup_queue(struct virtio_softc *vsc, uint16_t idx,
193b8df88e2Sthorpej     uint64_t addr)
19471cde4cbSjakllsch {
19571cde4cbSjakllsch 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
19671cde4cbSjakllsch 
197b8df88e2Sthorpej 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_SEL, idx);
198b8df88e2Sthorpej 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_NUM,
199b8df88e2Sthorpej 	    virtio_mmio_reg_read(sc, VIRTIO_MMIO_QUEUE_NUM_MAX));
200b8df88e2Sthorpej 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_V1_QUEUE_ALIGN,
20171cde4cbSjakllsch 	    VIRTIO_PAGE_SIZE);
202b8df88e2Sthorpej 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_V1_QUEUE_PFN,
20303c5faf6Sreinoud 	    addr / VIRTIO_PAGE_SIZE);
20471cde4cbSjakllsch }
20571cde4cbSjakllsch 
20671cde4cbSjakllsch static void
virtio_mmio_v2_setup_queue(struct virtio_softc * vsc,uint16_t idx,uint64_t addr)207b8df88e2Sthorpej virtio_mmio_v2_setup_queue(struct virtio_softc *vsc, uint16_t idx,
208b8df88e2Sthorpej     uint64_t addr)
209b8df88e2Sthorpej {
210b8df88e2Sthorpej 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
211*2cd310f6Sisaki 	struct virtqueue *vq;
212b8df88e2Sthorpej 
213b8df88e2Sthorpej 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_SEL, idx);
214b8df88e2Sthorpej 	if (addr == 0) {
215b8df88e2Sthorpej 		virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_READY, 0);
216b8df88e2Sthorpej 		virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_DESC_LOW, 0);
217b8df88e2Sthorpej 		virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_AVAIL_LOW, 0);
218b8df88e2Sthorpej 		virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_USED_LOW, 0);
219b8df88e2Sthorpej 	} else {
220*2cd310f6Sisaki 		vq = &vsc->sc_vqs[idx];
221*2cd310f6Sisaki 		KASSERT(vq->vq_index == idx);
222*2cd310f6Sisaki 
223b8df88e2Sthorpej 		virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_NUM,
224b8df88e2Sthorpej 		    virtio_mmio_reg_read(sc, VIRTIO_MMIO_QUEUE_NUM_MAX));
225b8df88e2Sthorpej 		virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_DESC_LOW,
226b8df88e2Sthorpej 		    addr);
227b8df88e2Sthorpej 		virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_AVAIL_LOW,
228b8df88e2Sthorpej 		    addr + vq->vq_availoffset);
229b8df88e2Sthorpej 		virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_USED_LOW,
230b8df88e2Sthorpej 		    addr + vq->vq_usedoffset);
231b8df88e2Sthorpej 		virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_READY, 1);
232b8df88e2Sthorpej 	}
233b8df88e2Sthorpej }
234b8df88e2Sthorpej 
235372fc0a7Sthorpej static int
virtio_mmio_get_status(struct virtio_softc * vsc)236372fc0a7Sthorpej virtio_mmio_get_status(struct virtio_softc *vsc)
237372fc0a7Sthorpej {
238372fc0a7Sthorpej 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
239372fc0a7Sthorpej 
240372fc0a7Sthorpej 	return virtio_mmio_reg_read(sc, VIRTIO_MMIO_STATUS);
241372fc0a7Sthorpej }
242372fc0a7Sthorpej 
243b8df88e2Sthorpej static void
virtio_mmio_set_status(struct virtio_softc * vsc,int status)24471cde4cbSjakllsch virtio_mmio_set_status(struct virtio_softc *vsc, int status)
24571cde4cbSjakllsch {
24671cde4cbSjakllsch 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
24771cde4cbSjakllsch 	int old = 0;
24871cde4cbSjakllsch 
24971cde4cbSjakllsch 	if (status != 0)
250b8df88e2Sthorpej 		old = virtio_mmio_reg_read(sc, VIRTIO_MMIO_STATUS);
251b8df88e2Sthorpej 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_STATUS, status|old);
25271cde4cbSjakllsch }
25371cde4cbSjakllsch 
2545ccc1416Sreinoud bool
virtio_mmio_common_probe_present(struct virtio_mmio_softc * sc)2555ccc1416Sreinoud virtio_mmio_common_probe_present(struct virtio_mmio_softc *sc)
2565ccc1416Sreinoud {
2575ccc1416Sreinoud 	uint32_t magic;
2585ccc1416Sreinoud 
259b8df88e2Sthorpej 	/* XXX */
2605ccc1416Sreinoud 	magic = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
2615ccc1416Sreinoud 	    VIRTIO_MMIO_MAGIC_VALUE);
2625ccc1416Sreinoud 	return (magic == VIRTIO_MMIO_MAGIC);
2635ccc1416Sreinoud }
2645ccc1416Sreinoud 
265b8df88e2Sthorpej 
26671cde4cbSjakllsch void
virtio_mmio_common_attach(struct virtio_mmio_softc * sc)26771cde4cbSjakllsch virtio_mmio_common_attach(struct virtio_mmio_softc *sc)
26871cde4cbSjakllsch {
26971cde4cbSjakllsch 	struct virtio_softc *vsc = &sc->sc_sc;
27003c5faf6Sreinoud 	device_t self = vsc->sc_dev;
271372fc0a7Sthorpej 	uint32_t id, magic;
272372fc0a7Sthorpej 	int virtio_vers;
27371cde4cbSjakllsch 
27471cde4cbSjakllsch 	magic = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
27571cde4cbSjakllsch 	    VIRTIO_MMIO_MAGIC_VALUE);
27671cde4cbSjakllsch 	if (magic != VIRTIO_MMIO_MAGIC) {
277b8df88e2Sthorpej 		if (magic == le32toh(VIRTIO_MMIO_MAGIC)) {
278b8df88e2Sthorpej 			sc->sc_le_regs = true;
279b8df88e2Sthorpej 		} else {
28071cde4cbSjakllsch 			aprint_error_dev(vsc->sc_dev,
28171cde4cbSjakllsch 			    "wrong magic value 0x%08x; giving up\n", magic);
28271cde4cbSjakllsch 			return;
28371cde4cbSjakllsch 		}
284b8df88e2Sthorpej 	}
285b8df88e2Sthorpej 	vsc->sc_bus_endian    = READ_ENDIAN;
286b8df88e2Sthorpej 	vsc->sc_struct_endian = STRUCT_ENDIAN;
28771cde4cbSjakllsch 
288372fc0a7Sthorpej 	sc->sc_mmio_vers = virtio_mmio_reg_read(sc, VIRTIO_MMIO_VERSION);
289372fc0a7Sthorpej 	switch (sc->sc_mmio_vers) {
290b8df88e2Sthorpej 	case 1:
291b8df88e2Sthorpej 		/* we could use PAGE_SIZE, but virtio(4) assumes 4KiB for now */
292b8df88e2Sthorpej 		virtio_mmio_reg_write(sc,
293b8df88e2Sthorpej 		    VIRTIO_MMIO_V1_GUEST_PAGE_SIZE, VIRTIO_PAGE_SIZE);
294b8df88e2Sthorpej 		vsc->sc_ops = &virtio_mmio_v1_ops;
295372fc0a7Sthorpej 		/*
296372fc0a7Sthorpej 		 * MMIO v1 ("legacy") is documented in the VirtIO 0.9.x
297372fc0a7Sthorpej 		 * draft(s) and uses the same page-oriented queue setup,
298372fc0a7Sthorpej 		 * so that's what we'll report as the VirtIO version.
299372fc0a7Sthorpej 		 */
300372fc0a7Sthorpej 		virtio_vers = 0;
301b8df88e2Sthorpej 		break;
302b8df88e2Sthorpej 
303b8df88e2Sthorpej 	case 2:
304b8df88e2Sthorpej 		vsc->sc_ops = &virtio_mmio_v2_ops;
305372fc0a7Sthorpej 		/*
306372fc0a7Sthorpej 		 * MMIO v2 is documented in the VirtIO 1.0 spec.
307372fc0a7Sthorpej 		 */
308372fc0a7Sthorpej 		virtio_vers = 1;
309b8df88e2Sthorpej 		break;
310b8df88e2Sthorpej 
311b8df88e2Sthorpej 	default:
31271cde4cbSjakllsch 		aprint_error_dev(vsc->sc_dev,
313372fc0a7Sthorpej 		    "unknown version 0x%08x; giving up\n", sc->sc_mmio_vers);
314b8df88e2Sthorpej 		return;
315b8df88e2Sthorpej 	}
316372fc0a7Sthorpej 	aprint_normal_dev(self, "VirtIO-MMIO-v%u\n", sc->sc_mmio_vers);
317b8df88e2Sthorpej 
318b8df88e2Sthorpej 	id = virtio_mmio_reg_read(sc, VIRTIO_MMIO_DEVICE_ID);
319b8df88e2Sthorpej 	if (id == 0) {
320b8df88e2Sthorpej 		/* no device connected. */
32171cde4cbSjakllsch 		return;
32271cde4cbSjakllsch 	}
32371cde4cbSjakllsch 
324372fc0a7Sthorpej 	virtio_print_device_type(self, id, virtio_vers);
32503c5faf6Sreinoud 
32603c5faf6Sreinoud 	/* set up our device config tag */
32703c5faf6Sreinoud 	vsc->sc_devcfg_iosize = sc->sc_iosize - VIRTIO_MMIO_CONFIG;
32803c5faf6Sreinoud 	vsc->sc_devcfg_iot = sc->sc_iot;
32903c5faf6Sreinoud 	if (bus_space_subregion(sc->sc_iot, sc->sc_ioh,
33003c5faf6Sreinoud 			VIRTIO_MMIO_CONFIG, vsc->sc_devcfg_iosize,
33103c5faf6Sreinoud 			&vsc->sc_devcfg_ioh)) {
33203c5faf6Sreinoud 		aprint_error_dev(self, "can't map config i/o space\n");
33303c5faf6Sreinoud 		return;
33403c5faf6Sreinoud 	}
33571cde4cbSjakllsch 
33671cde4cbSjakllsch 	virtio_device_reset(vsc);
33771cde4cbSjakllsch 	virtio_mmio_set_status(vsc, VIRTIO_CONFIG_DEVICE_STATUS_ACK);
33871cde4cbSjakllsch 	virtio_mmio_set_status(vsc, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER);
33971cde4cbSjakllsch 
34071cde4cbSjakllsch 	/* XXX: use softc as aux... */
34171cde4cbSjakllsch 	vsc->sc_childdevid = id;
34271cde4cbSjakllsch 	vsc->sc_child = NULL;
34371cde4cbSjakllsch }
34471cde4cbSjakllsch 
34571cde4cbSjakllsch int
virtio_mmio_common_detach(struct virtio_mmio_softc * sc,int flags)34671cde4cbSjakllsch virtio_mmio_common_detach(struct virtio_mmio_softc *sc, int flags)
34771cde4cbSjakllsch {
34871cde4cbSjakllsch 	struct virtio_softc *vsc = &sc->sc_sc;
34971cde4cbSjakllsch 	int r;
35071cde4cbSjakllsch 
3514487631aSyamaguchi 	r = config_detach_children(vsc->sc_dev, flags);
35237b0b4baSyamaguchi 	if (r != 0)
35371cde4cbSjakllsch 		return r;
35437b0b4baSyamaguchi 
355c2646fd4Syamaguchi 	KASSERT(vsc->sc_child == NULL);
35671cde4cbSjakllsch 	KASSERT(vsc->sc_vqs == NULL);
35771cde4cbSjakllsch 	KASSERT(sc->sc_ih == NULL);
35871cde4cbSjakllsch 
35971cde4cbSjakllsch 	if (sc->sc_iosize) {
36071cde4cbSjakllsch 		bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_iosize);
36171cde4cbSjakllsch 		sc->sc_iosize = 0;
36271cde4cbSjakllsch 	}
36371cde4cbSjakllsch 
36471cde4cbSjakllsch 	return 0;
36571cde4cbSjakllsch }
36671cde4cbSjakllsch 
36771cde4cbSjakllsch /*
36871cde4cbSjakllsch  * Feature negotiation.
369372fc0a7Sthorpej  *
370372fc0a7Sthorpej  * We fold pre-VirtIO-1.0 feature negotiation into this single routine
371372fc0a7Sthorpej  * because the "legacy" (MMIO-v1) also had the feature sel registers.
37271cde4cbSjakllsch  */
37303c5faf6Sreinoud static void
virtio_mmio_negotiate_features(struct virtio_softc * vsc,uint64_t driver_features)37403c5faf6Sreinoud virtio_mmio_negotiate_features(struct virtio_softc *vsc, uint64_t
375b8df88e2Sthorpej     driver_features)
37671cde4cbSjakllsch {
37771cde4cbSjakllsch 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
378372fc0a7Sthorpej 	device_t self = vsc->sc_dev;
379372fc0a7Sthorpej 	uint64_t saved_driver_features = driver_features;
380372fc0a7Sthorpej 	uint64_t device_features, negotiated;
381372fc0a7Sthorpej 	uint32_t device_status;
382372fc0a7Sthorpej 
383372fc0a7Sthorpej 	driver_features |= VIRTIO_F_VERSION_1;
384372fc0a7Sthorpej 	vsc->sc_active_features = 0;
38571cde4cbSjakllsch 
386b8df88e2Sthorpej 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_DEVICE_FEATURES_SEL, 0);
387372fc0a7Sthorpej 	device_features = virtio_mmio_reg_read(sc, VIRTIO_MMIO_DEVICE_FEATURES);
388372fc0a7Sthorpej 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_DEVICE_FEATURES_SEL, 1);
389372fc0a7Sthorpej 	device_features |= (uint64_t)
390372fc0a7Sthorpej 	    virtio_mmio_reg_read(sc, VIRTIO_MMIO_DEVICE_FEATURES) << 32;
39103c5faf6Sreinoud 
392372fc0a7Sthorpej 	/* notify on empty is 0.9 only */
393372fc0a7Sthorpej 	if (device_features & VIRTIO_F_VERSION_1) {
394372fc0a7Sthorpej 		driver_features &= ~VIRTIO_F_NOTIFY_ON_EMPTY;
395372fc0a7Sthorpej 	} else {
396372fc0a7Sthorpej 		/*
397372fc0a7Sthorpej 		 * Require version 1 for MMIO-v2 transport.
398372fc0a7Sthorpej 		 */
399372fc0a7Sthorpej 		if (sc->sc_mmio_vers >= 2) {
400372fc0a7Sthorpej 			aprint_error_dev(self, "MMIO-v%u requires version 1\n",
401372fc0a7Sthorpej 			    sc->sc_mmio_vers);
402372fc0a7Sthorpej 			virtio_mmio_set_status(vsc,
403372fc0a7Sthorpej 			    VIRTIO_CONFIG_DEVICE_STATUS_FAILED);
404372fc0a7Sthorpej 			return;
405372fc0a7Sthorpej 		}
406372fc0a7Sthorpej 		/*
407372fc0a7Sthorpej 		 * If the driver requires version 1, but the device doesn't
408372fc0a7Sthorpej 		 * support it, fail now.
409372fc0a7Sthorpej 		 */
410372fc0a7Sthorpej 		if (saved_driver_features & VIRTIO_F_VERSION_1) {
411372fc0a7Sthorpej 			aprint_error_dev(self, "device rejected version 1\n");
412372fc0a7Sthorpej 			virtio_mmio_set_status(vsc,
413372fc0a7Sthorpej 			    VIRTIO_CONFIG_DEVICE_STATUS_FAILED);
414372fc0a7Sthorpej 			return;
415372fc0a7Sthorpej 		}
416372fc0a7Sthorpej 	}
417372fc0a7Sthorpej 
418372fc0a7Sthorpej 	negotiated = device_features & driver_features;
419372fc0a7Sthorpej 
420372fc0a7Sthorpej 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_DRIVER_FEATURES_SEL, 0);
421372fc0a7Sthorpej 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_DRIVER_FEATURES,
422372fc0a7Sthorpej 	    (uint32_t)negotiated);
423372fc0a7Sthorpej 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_DRIVER_FEATURES_SEL, 1);
424372fc0a7Sthorpej 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_DRIVER_FEATURES,
425372fc0a7Sthorpej 	    (uint32_t)(negotiated >> 32));
426372fc0a7Sthorpej 
427372fc0a7Sthorpej 	/*
428372fc0a7Sthorpej 	 * FEATURES_OK status is not present pre-1.0.
429372fc0a7Sthorpej 	 */
430372fc0a7Sthorpej 	if (device_features & VIRTIO_F_VERSION_1) {
431372fc0a7Sthorpej 		virtio_mmio_set_status(vsc,
432372fc0a7Sthorpej 		    VIRTIO_CONFIG_DEVICE_STATUS_FEATURES_OK);
433372fc0a7Sthorpej 		device_status = virtio_mmio_get_status(vsc);
434372fc0a7Sthorpej 		if ((device_status &
435372fc0a7Sthorpej 		     VIRTIO_CONFIG_DEVICE_STATUS_FEATURES_OK) == 0) {
436372fc0a7Sthorpej 			aprint_error_dev(self, "feature negotiation failed\n");
437372fc0a7Sthorpej 			virtio_mmio_set_status(vsc,
438372fc0a7Sthorpej 			    VIRTIO_CONFIG_DEVICE_STATUS_FAILED);
439372fc0a7Sthorpej 			return;
440372fc0a7Sthorpej 		}
441372fc0a7Sthorpej 	}
442372fc0a7Sthorpej 
443372fc0a7Sthorpej 	if (negotiated & VIRTIO_F_VERSION_1) {
444372fc0a7Sthorpej 		/*
445372fc0a7Sthorpej 		 * All VirtIO 1.0 access is little-endian.
446372fc0a7Sthorpej 		 */
447372fc0a7Sthorpej 		vsc->sc_bus_endian    = LITTLE_ENDIAN;
448372fc0a7Sthorpej 		vsc->sc_struct_endian = LITTLE_ENDIAN;
449372fc0a7Sthorpej 	}
450372fc0a7Sthorpej 
451372fc0a7Sthorpej 	vsc->sc_active_features = negotiated;
45271cde4cbSjakllsch }
45371cde4cbSjakllsch 
45471cde4cbSjakllsch /*
45571cde4cbSjakllsch  * Interrupt handler.
45671cde4cbSjakllsch  */
45771cde4cbSjakllsch int
virtio_mmio_intr(void * arg)45871cde4cbSjakllsch virtio_mmio_intr(void *arg)
45971cde4cbSjakllsch {
46071cde4cbSjakllsch 	struct virtio_mmio_softc *sc = arg;
46171cde4cbSjakllsch 	struct virtio_softc *vsc = &sc->sc_sc;
46271cde4cbSjakllsch 	int isr, r = 0;
46371cde4cbSjakllsch 
46471cde4cbSjakllsch 	/* check and ack the interrupt */
465b8df88e2Sthorpej 	isr = virtio_mmio_reg_read(sc, VIRTIO_MMIO_INTERRUPT_STATUS);
466b8df88e2Sthorpej 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_INTERRUPT_ACK, isr);
467372fc0a7Sthorpej 	if ((isr & VIRTIO_CONFIG_ISR_CONFIG_CHANGE) &&
46871cde4cbSjakllsch 	    (vsc->sc_config_change != NULL))
46971cde4cbSjakllsch 		r = (vsc->sc_config_change)(vsc);
470372fc0a7Sthorpej 	if ((isr & VIRTIO_CONFIG_ISR_QUEUE_INTERRUPT) &&
47171cde4cbSjakllsch 	    (vsc->sc_intrhand != NULL)) {
47271cde4cbSjakllsch 		if (vsc->sc_soft_ih != NULL)
47371cde4cbSjakllsch 			softint_schedule(vsc->sc_soft_ih);
47471cde4cbSjakllsch 		else
47571cde4cbSjakllsch 			r |= (vsc->sc_intrhand)(vsc);
47671cde4cbSjakllsch 	}
47771cde4cbSjakllsch 
47871cde4cbSjakllsch 	return r;
47971cde4cbSjakllsch }
48071cde4cbSjakllsch 
48171cde4cbSjakllsch static void
virtio_mmio_kick(struct virtio_softc * vsc,uint16_t idx)48271cde4cbSjakllsch virtio_mmio_kick(struct virtio_softc *vsc, uint16_t idx)
48371cde4cbSjakllsch {
48471cde4cbSjakllsch 	struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc;
485b8df88e2Sthorpej 	virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_NOTIFY, idx);
48671cde4cbSjakllsch }
48771cde4cbSjakllsch 
48871cde4cbSjakllsch static int
virtio_mmio_alloc_interrupts(struct virtio_softc * vsc)489198576feSyamaguchi virtio_mmio_alloc_interrupts(struct virtio_softc *vsc)
49071cde4cbSjakllsch {
49171cde4cbSjakllsch 	struct virtio_mmio_softc * const sc = (struct virtio_mmio_softc *)vsc;
49271cde4cbSjakllsch 
493198576feSyamaguchi 	return sc->sc_alloc_interrupts(sc);
49471cde4cbSjakllsch }
49571cde4cbSjakllsch 
49671cde4cbSjakllsch static void
virtio_mmio_free_interrupts(struct virtio_softc * vsc)49771cde4cbSjakllsch virtio_mmio_free_interrupts(struct virtio_softc *vsc)
49871cde4cbSjakllsch {
49971cde4cbSjakllsch 	struct virtio_mmio_softc * const sc = (struct virtio_mmio_softc *)vsc;
50071cde4cbSjakllsch 
50171cde4cbSjakllsch 	sc->sc_free_interrupts(sc);
50271cde4cbSjakllsch }
503198576feSyamaguchi 
504198576feSyamaguchi static int
virtio_mmio_setup_interrupts(struct virtio_softc * vsc __unused,int reinit __unused)505198576feSyamaguchi virtio_mmio_setup_interrupts(struct virtio_softc *vsc __unused,
506198576feSyamaguchi     int reinit __unused)
507198576feSyamaguchi {
508198576feSyamaguchi 
509198576feSyamaguchi 	return 0;
510198576feSyamaguchi }
511