xref: /dflybsd-src/sys/dev/virtual/virtio/pci/virtio_pci.c (revision f41543e16b49e0294028d1f4bb5a5667694536f1)
111447b59SVenkatesh Srinivas /*-
211447b59SVenkatesh Srinivas  * Copyright (c) 2011, Bryan Venteicher <bryanv@daemoninthecloset.org>
311447b59SVenkatesh Srinivas  * All rights reserved.
411447b59SVenkatesh Srinivas  *
511447b59SVenkatesh Srinivas  * Redistribution and use in source and binary forms, with or without
611447b59SVenkatesh Srinivas  * modification, are permitted provided that the following conditions
711447b59SVenkatesh Srinivas  * are met:
811447b59SVenkatesh Srinivas  * 1. Redistributions of source code must retain the above copyright
911447b59SVenkatesh Srinivas  *    notice unmodified, this list of conditions, and the following
1011447b59SVenkatesh Srinivas  *    disclaimer.
1111447b59SVenkatesh Srinivas  * 2. Redistributions in binary form must reproduce the above copyright
1211447b59SVenkatesh Srinivas  *    notice, this list of conditions and the following disclaimer in the
1311447b59SVenkatesh Srinivas  *    documentation and/or other materials provided with the distribution.
1411447b59SVenkatesh Srinivas  *
1511447b59SVenkatesh Srinivas  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1611447b59SVenkatesh Srinivas  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1711447b59SVenkatesh Srinivas  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1811447b59SVenkatesh Srinivas  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1911447b59SVenkatesh Srinivas  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2011447b59SVenkatesh Srinivas  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2111447b59SVenkatesh Srinivas  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2211447b59SVenkatesh Srinivas  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2311447b59SVenkatesh Srinivas  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2411447b59SVenkatesh Srinivas  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2511447b59SVenkatesh Srinivas  *
2611447b59SVenkatesh Srinivas  * $FreeBSD: src/sys/dev/virtio/pci/virtio_pci.c,v 1.3 2012/04/14 05:48:04 grehan Exp $
2711447b59SVenkatesh Srinivas  */
2811447b59SVenkatesh Srinivas 
2911447b59SVenkatesh Srinivas /* Driver for the VirtIO PCI interface. */
3011447b59SVenkatesh Srinivas 
3111447b59SVenkatesh Srinivas #include <sys/param.h>
3211447b59SVenkatesh Srinivas #include <sys/systm.h>
3311447b59SVenkatesh Srinivas #include <sys/bus.h>
3411447b59SVenkatesh Srinivas #include <sys/kernel.h>
3511447b59SVenkatesh Srinivas #include <sys/module.h>
3611447b59SVenkatesh Srinivas #include <sys/malloc.h>
372f1382caSVenkatesh Srinivas #include <sys/serialize.h>
3811447b59SVenkatesh Srinivas 
3911447b59SVenkatesh Srinivas #include <bus/pci/pcivar.h>
4011447b59SVenkatesh Srinivas #include <bus/pci/pcireg.h>
4111447b59SVenkatesh Srinivas 
4211447b59SVenkatesh Srinivas #include <sys/rman.h>
4311447b59SVenkatesh Srinivas 
44dcbcbc86SVenkatesh Srinivas #include <dev/virtual/virtio/virtio/virtio.h>
45dcbcbc86SVenkatesh Srinivas #include <dev/virtual/virtio/virtio/virtqueue.h>
4611447b59SVenkatesh Srinivas #include "virtio_pci.h"
4711447b59SVenkatesh Srinivas #include "virtio_if.h"
48dcbcbc86SVenkatesh Srinivas #include "virtio_bus_if.h"
4911447b59SVenkatesh Srinivas 
5011447b59SVenkatesh Srinivas struct vtpci_softc {
5111447b59SVenkatesh Srinivas 	device_t			 vtpci_dev;
5211447b59SVenkatesh Srinivas 	struct resource			*vtpci_res;
5311447b59SVenkatesh Srinivas 	struct resource			*vtpci_msix_res;
5411447b59SVenkatesh Srinivas 	uint64_t			 vtpci_features;
5511447b59SVenkatesh Srinivas 	uint32_t			 vtpci_flags;
5611447b59SVenkatesh Srinivas 	int				 vtpci_irq_type;
5711447b59SVenkatesh Srinivas 	int				 vtpci_irq_rid;
5811447b59SVenkatesh Srinivas #define VIRTIO_PCI_FLAG_NO_MSI		 0x0001
5911447b59SVenkatesh Srinivas #define VIRTIO_PCI_FLAG_MSI		 0x0002
6011447b59SVenkatesh Srinivas #define VIRTIO_PCI_FLAG_NO_MSIX		 0x0010
6111447b59SVenkatesh Srinivas #define VIRTIO_PCI_FLAG_MSIX		 0x0020
6211447b59SVenkatesh Srinivas #define VIRTIO_PCI_FLAG_SHARED_MSIX	 0x0040
6311447b59SVenkatesh Srinivas 
6411447b59SVenkatesh Srinivas 	device_t			 vtpci_child_dev;
6511447b59SVenkatesh Srinivas 	struct virtio_feature_desc	*vtpci_child_feat_desc;
6611447b59SVenkatesh Srinivas 
6711447b59SVenkatesh Srinivas 	/*
6811447b59SVenkatesh Srinivas 	 * Ideally, each virtqueue that the driver provides a callback for
6911447b59SVenkatesh Srinivas 	 * will receive its own MSIX vector. If there are not sufficient
7011447b59SVenkatesh Srinivas 	 * vectors available, we will then attempt to have all the VQs
7111447b59SVenkatesh Srinivas 	 * share one vector. Note that when using MSIX, the configuration
7211447b59SVenkatesh Srinivas 	 * changed notifications must be on their own vector.
7311447b59SVenkatesh Srinivas 	 *
7411447b59SVenkatesh Srinivas 	 * If MSIX is not available, we will attempt to have the whole
7511447b59SVenkatesh Srinivas 	 * device share one MSI vector, and then, finally, one legacy
7611447b59SVenkatesh Srinivas 	 * interrupt.
7711447b59SVenkatesh Srinivas 	 */
7811447b59SVenkatesh Srinivas 	int				 vtpci_nvqs;
7911447b59SVenkatesh Srinivas 	struct vtpci_virtqueue {
8011447b59SVenkatesh Srinivas 		struct virtqueue *vq;
8111447b59SVenkatesh Srinivas 
8211447b59SVenkatesh Srinivas 		/* Index into vtpci_intr_res[] below. Unused, then -1. */
8311447b59SVenkatesh Srinivas 		int		  ires_idx;
8411447b59SVenkatesh Srinivas 	} vtpci_vqx[VIRTIO_MAX_VIRTQUEUES];
8511447b59SVenkatesh Srinivas 
8611447b59SVenkatesh Srinivas 	/*
8711447b59SVenkatesh Srinivas 	 * When using MSIX interrupts, the first element of vtpci_intr_res[]
8811447b59SVenkatesh Srinivas 	 * is always the configuration changed notifications. The remaining
8911447b59SVenkatesh Srinivas 	 * element(s) are used for the virtqueues.
9011447b59SVenkatesh Srinivas 	 *
9111447b59SVenkatesh Srinivas 	 * With MSI and legacy interrupts, only the first element of
9211447b59SVenkatesh Srinivas 	 * vtpci_intr_res[] is used.
9311447b59SVenkatesh Srinivas 	 */
9411447b59SVenkatesh Srinivas 	int				 vtpci_nintr_res;
9511447b59SVenkatesh Srinivas 	struct vtpci_intr_resource {
9611447b59SVenkatesh Srinivas 		struct resource	*irq;
9711447b59SVenkatesh Srinivas 		int		 rid;
9811447b59SVenkatesh Srinivas 		void		*intrhand;
9911447b59SVenkatesh Srinivas 	} vtpci_intr_res[1 + VIRTIO_MAX_VIRTQUEUES];
10011447b59SVenkatesh Srinivas };
10111447b59SVenkatesh Srinivas 
10211447b59SVenkatesh Srinivas static int	vtpci_probe(device_t);
10311447b59SVenkatesh Srinivas static int	vtpci_attach(device_t);
10411447b59SVenkatesh Srinivas static int	vtpci_detach(device_t);
10511447b59SVenkatesh Srinivas static int	vtpci_suspend(device_t);
10611447b59SVenkatesh Srinivas static int	vtpci_resume(device_t);
10711447b59SVenkatesh Srinivas static int	vtpci_shutdown(device_t);
10811447b59SVenkatesh Srinivas static void	vtpci_driver_added(device_t, driver_t *);
10911447b59SVenkatesh Srinivas static void	vtpci_child_detached(device_t, device_t);
11011447b59SVenkatesh Srinivas static int	vtpci_read_ivar(device_t, device_t, int, uintptr_t *);
11111447b59SVenkatesh Srinivas static int	vtpci_write_ivar(device_t, device_t, int, uintptr_t);
11211447b59SVenkatesh Srinivas 
11311447b59SVenkatesh Srinivas static uint64_t	vtpci_negotiate_features(device_t, uint64_t);
11411447b59SVenkatesh Srinivas static int	vtpci_with_feature(device_t, uint64_t);
11511447b59SVenkatesh Srinivas static int	vtpci_alloc_virtqueues(device_t, int, int,
11611447b59SVenkatesh Srinivas 		    struct vq_alloc_info *);
1172f1382caSVenkatesh Srinivas static int	vtpci_setup_intr(device_t, lwkt_serialize_t);
11811447b59SVenkatesh Srinivas static void	vtpci_stop(device_t);
11911447b59SVenkatesh Srinivas static int	vtpci_reinit(device_t, uint64_t);
12011447b59SVenkatesh Srinivas static void	vtpci_reinit_complete(device_t);
12111447b59SVenkatesh Srinivas static void	vtpci_notify_virtqueue(device_t, uint16_t);
12211447b59SVenkatesh Srinivas static uint8_t	vtpci_get_status(device_t);
12311447b59SVenkatesh Srinivas static void	vtpci_set_status(device_t, uint8_t);
12411447b59SVenkatesh Srinivas static void	vtpci_read_dev_config(device_t, bus_size_t, void *, int);
12511447b59SVenkatesh Srinivas static void	vtpci_write_dev_config(device_t, bus_size_t, void *, int);
12611447b59SVenkatesh Srinivas 
12711447b59SVenkatesh Srinivas static void	vtpci_describe_features(struct vtpci_softc *, const char *,
12811447b59SVenkatesh Srinivas 		    uint64_t);
12911447b59SVenkatesh Srinivas static void	vtpci_probe_and_attach_child(struct vtpci_softc *);
13011447b59SVenkatesh Srinivas 
13111447b59SVenkatesh Srinivas static int	vtpci_alloc_interrupts(struct vtpci_softc *, int, int,
13211447b59SVenkatesh Srinivas 		    struct vq_alloc_info *);
13311447b59SVenkatesh Srinivas static int	vtpci_alloc_intr_resources(struct vtpci_softc *, int,
13411447b59SVenkatesh Srinivas 		    struct vq_alloc_info *);
13511447b59SVenkatesh Srinivas static int	vtpci_alloc_msi(struct vtpci_softc *);
13611447b59SVenkatesh Srinivas static int	vtpci_alloc_msix(struct vtpci_softc *, int);
13711447b59SVenkatesh Srinivas static int	vtpci_register_msix_vector(struct vtpci_softc *, int, int);
13811447b59SVenkatesh Srinivas 
13911447b59SVenkatesh Srinivas static void	vtpci_free_interrupts(struct vtpci_softc *);
14011447b59SVenkatesh Srinivas static void	vtpci_free_virtqueues(struct vtpci_softc *);
14111447b59SVenkatesh Srinivas static void	vtpci_release_child_resources(struct vtpci_softc *);
14211447b59SVenkatesh Srinivas static void	vtpci_reset(struct vtpci_softc *);
14311447b59SVenkatesh Srinivas 
14411447b59SVenkatesh Srinivas static int	vtpci_legacy_intr(void *);
14511447b59SVenkatesh Srinivas static int	vtpci_vq_shared_intr(void *);
14611447b59SVenkatesh Srinivas static int	vtpci_vq_intr(void *);
14711447b59SVenkatesh Srinivas static int	vtpci_config_intr(void *);
14811447b59SVenkatesh Srinivas 
14911447b59SVenkatesh Srinivas /*
15011447b59SVenkatesh Srinivas  * I/O port read/write wrappers.
15111447b59SVenkatesh Srinivas  */
15211447b59SVenkatesh Srinivas #define vtpci_read_config_1(sc, o)	bus_read_1((sc)->vtpci_res, (o))
15311447b59SVenkatesh Srinivas #define vtpci_read_config_2(sc, o)	bus_read_2((sc)->vtpci_res, (o))
15411447b59SVenkatesh Srinivas #define vtpci_read_config_4(sc, o)	bus_read_4((sc)->vtpci_res, (o))
15511447b59SVenkatesh Srinivas #define vtpci_write_config_1(sc, o, v)	bus_write_1((sc)->vtpci_res, (o), (v))
15611447b59SVenkatesh Srinivas #define vtpci_write_config_2(sc, o, v)	bus_write_2((sc)->vtpci_res, (o), (v))
15711447b59SVenkatesh Srinivas #define vtpci_write_config_4(sc, o, v)	bus_write_4((sc)->vtpci_res, (o), (v))
15811447b59SVenkatesh Srinivas 
15911447b59SVenkatesh Srinivas /* Tunables. */
16011447b59SVenkatesh Srinivas static int vtpci_disable_msix = 0;
16111447b59SVenkatesh Srinivas TUNABLE_INT("hw.virtio.pci.disable_msix", &vtpci_disable_msix);
16211447b59SVenkatesh Srinivas 
16311447b59SVenkatesh Srinivas static device_method_t vtpci_methods[] = {
16411447b59SVenkatesh Srinivas 	/* Device interface. */
16511447b59SVenkatesh Srinivas 	DEVMETHOD(device_probe,			  vtpci_probe),
16611447b59SVenkatesh Srinivas 	DEVMETHOD(device_attach,		  vtpci_attach),
16711447b59SVenkatesh Srinivas 	DEVMETHOD(device_detach,		  vtpci_detach),
16811447b59SVenkatesh Srinivas 	DEVMETHOD(device_suspend,		  vtpci_suspend),
16911447b59SVenkatesh Srinivas 	DEVMETHOD(device_resume,		  vtpci_resume),
17011447b59SVenkatesh Srinivas 	DEVMETHOD(device_shutdown,		  vtpci_shutdown),
17111447b59SVenkatesh Srinivas 
17211447b59SVenkatesh Srinivas 	/* Bus interface. */
17311447b59SVenkatesh Srinivas 	DEVMETHOD(bus_driver_added,		  vtpci_driver_added),
17411447b59SVenkatesh Srinivas 	DEVMETHOD(bus_child_detached,		  vtpci_child_detached),
17511447b59SVenkatesh Srinivas 	DEVMETHOD(bus_read_ivar,		  vtpci_read_ivar),
17611447b59SVenkatesh Srinivas 	DEVMETHOD(bus_write_ivar,		  vtpci_write_ivar),
17711447b59SVenkatesh Srinivas 
17811447b59SVenkatesh Srinivas 	/* VirtIO bus interface. */
17911447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_negotiate_features,  vtpci_negotiate_features),
18011447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_with_feature,	  vtpci_with_feature),
18111447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_alloc_virtqueues,	  vtpci_alloc_virtqueues),
18211447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_setup_intr,	  vtpci_setup_intr),
18311447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_stop,		  vtpci_stop),
18411447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_reinit,		  vtpci_reinit),
18511447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_reinit_complete,	  vtpci_reinit_complete),
18611447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_notify_vq,		  vtpci_notify_virtqueue),
18711447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_read_device_config,  vtpci_read_dev_config),
18811447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_write_device_config, vtpci_write_dev_config),
18911447b59SVenkatesh Srinivas 
190d3c9c58eSSascha Wildner 	DEVMETHOD_END
19111447b59SVenkatesh Srinivas };
19211447b59SVenkatesh Srinivas 
19311447b59SVenkatesh Srinivas static driver_t vtpci_driver = {
19411447b59SVenkatesh Srinivas 	"virtio_pci",
19511447b59SVenkatesh Srinivas 	vtpci_methods,
19611447b59SVenkatesh Srinivas 	sizeof(struct vtpci_softc)
19711447b59SVenkatesh Srinivas };
19811447b59SVenkatesh Srinivas 
19911447b59SVenkatesh Srinivas devclass_t vtpci_devclass;
20011447b59SVenkatesh Srinivas 
201dfc199f7SSascha Wildner DRIVER_MODULE(virtio_pci, pci, vtpci_driver, vtpci_devclass, NULL, NULL);
20211447b59SVenkatesh Srinivas MODULE_VERSION(virtio_pci, 1);
20311447b59SVenkatesh Srinivas MODULE_DEPEND(virtio_pci, pci, 1, 1, 1);
20411447b59SVenkatesh Srinivas MODULE_DEPEND(virtio_pci, virtio, 1, 1, 1);
20511447b59SVenkatesh Srinivas 
20611447b59SVenkatesh Srinivas static int
20711447b59SVenkatesh Srinivas vtpci_probe(device_t dev)
20811447b59SVenkatesh Srinivas {
20911447b59SVenkatesh Srinivas 	char desc[36];
21011447b59SVenkatesh Srinivas 	const char *name;
21111447b59SVenkatesh Srinivas 
21211447b59SVenkatesh Srinivas 	if (pci_get_vendor(dev) != VIRTIO_PCI_VENDORID)
21311447b59SVenkatesh Srinivas 		return (ENXIO);
21411447b59SVenkatesh Srinivas 
21511447b59SVenkatesh Srinivas 	if (pci_get_device(dev) < VIRTIO_PCI_DEVICEID_MIN ||
21611447b59SVenkatesh Srinivas 	    pci_get_device(dev) > VIRTIO_PCI_DEVICEID_MAX)
21711447b59SVenkatesh Srinivas 		return (ENXIO);
21811447b59SVenkatesh Srinivas 
21911447b59SVenkatesh Srinivas 	if (pci_get_revid(dev) != VIRTIO_PCI_ABI_VERSION)
22011447b59SVenkatesh Srinivas 		return (ENXIO);
22111447b59SVenkatesh Srinivas 
22211447b59SVenkatesh Srinivas 	name = virtio_device_name(pci_get_subdevice(dev));
22311447b59SVenkatesh Srinivas 	if (name == NULL)
22411447b59SVenkatesh Srinivas 		name = "Unknown";
22511447b59SVenkatesh Srinivas 
22611447b59SVenkatesh Srinivas 	ksnprintf(desc, sizeof(desc), "VirtIO PCI %s adapter", name);
22711447b59SVenkatesh Srinivas 	device_set_desc_copy(dev, desc);
22811447b59SVenkatesh Srinivas 
22911447b59SVenkatesh Srinivas 	return (BUS_PROBE_DEFAULT);
23011447b59SVenkatesh Srinivas }
23111447b59SVenkatesh Srinivas 
23211447b59SVenkatesh Srinivas static int
23311447b59SVenkatesh Srinivas vtpci_attach(device_t dev)
23411447b59SVenkatesh Srinivas {
23511447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
23611447b59SVenkatesh Srinivas 	device_t child;
23711447b59SVenkatesh Srinivas 	int rid;
23811447b59SVenkatesh Srinivas 
23911447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
24011447b59SVenkatesh Srinivas 	sc->vtpci_dev = dev;
24111447b59SVenkatesh Srinivas 
24211447b59SVenkatesh Srinivas 	pci_enable_busmaster(dev);
24311447b59SVenkatesh Srinivas 
24411447b59SVenkatesh Srinivas 	rid = PCIR_BAR(0);
24511447b59SVenkatesh Srinivas 	sc->vtpci_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
24611447b59SVenkatesh Srinivas 	    RF_ACTIVE);
24711447b59SVenkatesh Srinivas 	if (sc->vtpci_res == NULL) {
24811447b59SVenkatesh Srinivas 		device_printf(dev, "cannot map I/O space\n");
24911447b59SVenkatesh Srinivas 		return (ENXIO);
25011447b59SVenkatesh Srinivas 	}
25111447b59SVenkatesh Srinivas 
25211447b59SVenkatesh Srinivas 	if (pci_find_extcap(dev, PCIY_MSI, NULL) != 0)
25311447b59SVenkatesh Srinivas 		sc->vtpci_flags |= VIRTIO_PCI_FLAG_NO_MSI;
2545c3e984aSSascha Wildner #if 0 /* XXX(vsrinivas): Check out how to get MSI-X */
25511447b59SVenkatesh Srinivas 	if (pci_find_extcap(dev, PCIY_MSIX, NULL) == 0) {
25611447b59SVenkatesh Srinivas 		rid = PCIR_BAR(1);
25711447b59SVenkatesh Srinivas 		sc->vtpci_msix_res = bus_alloc_resource_any(dev,
25811447b59SVenkatesh Srinivas 		    SYS_RES_MEMORY, &rid, RF_ACTIVE);
25911447b59SVenkatesh Srinivas 	}
26011447b59SVenkatesh Srinivas #endif
26111447b59SVenkatesh Srinivas 	if (sc->vtpci_msix_res == NULL)
26211447b59SVenkatesh Srinivas 		sc->vtpci_flags |= VIRTIO_PCI_FLAG_NO_MSIX;
26311447b59SVenkatesh Srinivas 
26411447b59SVenkatesh Srinivas 	vtpci_reset(sc);
26511447b59SVenkatesh Srinivas 
26611447b59SVenkatesh Srinivas 	/* Tell the host we've noticed this device. */
26711447b59SVenkatesh Srinivas 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
26811447b59SVenkatesh Srinivas 
26911447b59SVenkatesh Srinivas 	if ((child = device_add_child(dev, NULL, -1)) == NULL) {
27011447b59SVenkatesh Srinivas 		device_printf(dev, "cannot create child device\n");
27111447b59SVenkatesh Srinivas 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED);
27211447b59SVenkatesh Srinivas 		vtpci_detach(dev);
27311447b59SVenkatesh Srinivas 		return (ENOMEM);
27411447b59SVenkatesh Srinivas 	}
27511447b59SVenkatesh Srinivas 
27611447b59SVenkatesh Srinivas 	sc->vtpci_child_dev = child;
27711447b59SVenkatesh Srinivas 	vtpci_probe_and_attach_child(sc);
27811447b59SVenkatesh Srinivas 
27911447b59SVenkatesh Srinivas 	return (0);
28011447b59SVenkatesh Srinivas }
28111447b59SVenkatesh Srinivas 
28211447b59SVenkatesh Srinivas static int
28311447b59SVenkatesh Srinivas vtpci_detach(device_t dev)
28411447b59SVenkatesh Srinivas {
28511447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
28611447b59SVenkatesh Srinivas 	device_t child;
28711447b59SVenkatesh Srinivas 	int error;
28811447b59SVenkatesh Srinivas 
28911447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
29011447b59SVenkatesh Srinivas 
29111447b59SVenkatesh Srinivas 	if ((child = sc->vtpci_child_dev) != NULL) {
29211447b59SVenkatesh Srinivas 		error = device_delete_child(dev, child);
29311447b59SVenkatesh Srinivas 		if (error)
29411447b59SVenkatesh Srinivas 			return (error);
29511447b59SVenkatesh Srinivas 		sc->vtpci_child_dev = NULL;
29611447b59SVenkatesh Srinivas 	}
29711447b59SVenkatesh Srinivas 
29811447b59SVenkatesh Srinivas 	vtpci_reset(sc);
29911447b59SVenkatesh Srinivas 
30011447b59SVenkatesh Srinivas 	if (sc->vtpci_msix_res != NULL) {
30111447b59SVenkatesh Srinivas 		bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BAR(1),
30211447b59SVenkatesh Srinivas 		    sc->vtpci_msix_res);
30311447b59SVenkatesh Srinivas 		sc->vtpci_msix_res = NULL;
30411447b59SVenkatesh Srinivas 	}
30511447b59SVenkatesh Srinivas 
30611447b59SVenkatesh Srinivas 	if (sc->vtpci_res != NULL) {
30711447b59SVenkatesh Srinivas 		bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0),
30811447b59SVenkatesh Srinivas 		    sc->vtpci_res);
30911447b59SVenkatesh Srinivas 		sc->vtpci_res = NULL;
31011447b59SVenkatesh Srinivas 	}
31111447b59SVenkatesh Srinivas 
31211447b59SVenkatesh Srinivas 	return (0);
31311447b59SVenkatesh Srinivas }
31411447b59SVenkatesh Srinivas 
31511447b59SVenkatesh Srinivas static int
31611447b59SVenkatesh Srinivas vtpci_suspend(device_t dev)
31711447b59SVenkatesh Srinivas {
31811447b59SVenkatesh Srinivas 
31911447b59SVenkatesh Srinivas 	return (bus_generic_suspend(dev));
32011447b59SVenkatesh Srinivas }
32111447b59SVenkatesh Srinivas 
32211447b59SVenkatesh Srinivas static int
32311447b59SVenkatesh Srinivas vtpci_resume(device_t dev)
32411447b59SVenkatesh Srinivas {
32511447b59SVenkatesh Srinivas 
32611447b59SVenkatesh Srinivas 	return (bus_generic_resume(dev));
32711447b59SVenkatesh Srinivas }
32811447b59SVenkatesh Srinivas 
32911447b59SVenkatesh Srinivas static int
33011447b59SVenkatesh Srinivas vtpci_shutdown(device_t dev)
33111447b59SVenkatesh Srinivas {
33211447b59SVenkatesh Srinivas 
33311447b59SVenkatesh Srinivas 	(void) bus_generic_shutdown(dev);
33411447b59SVenkatesh Srinivas 	/* Forcibly stop the host device. */
33511447b59SVenkatesh Srinivas 	vtpci_stop(dev);
33611447b59SVenkatesh Srinivas 
33711447b59SVenkatesh Srinivas 	return (0);
33811447b59SVenkatesh Srinivas }
33911447b59SVenkatesh Srinivas 
34011447b59SVenkatesh Srinivas static void
34111447b59SVenkatesh Srinivas vtpci_driver_added(device_t dev, driver_t *driver)
34211447b59SVenkatesh Srinivas {
34311447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
34411447b59SVenkatesh Srinivas 
34511447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
34611447b59SVenkatesh Srinivas 
34711447b59SVenkatesh Srinivas 	vtpci_probe_and_attach_child(sc);
34811447b59SVenkatesh Srinivas }
34911447b59SVenkatesh Srinivas 
35011447b59SVenkatesh Srinivas static void
35111447b59SVenkatesh Srinivas vtpci_child_detached(device_t dev, device_t child)
35211447b59SVenkatesh Srinivas {
35311447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
35411447b59SVenkatesh Srinivas 
35511447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
35611447b59SVenkatesh Srinivas 
35711447b59SVenkatesh Srinivas 	vtpci_reset(sc);
35811447b59SVenkatesh Srinivas 	vtpci_release_child_resources(sc);
35911447b59SVenkatesh Srinivas }
36011447b59SVenkatesh Srinivas 
36111447b59SVenkatesh Srinivas static int
36211447b59SVenkatesh Srinivas vtpci_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
36311447b59SVenkatesh Srinivas {
36411447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
36511447b59SVenkatesh Srinivas 
36611447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
36711447b59SVenkatesh Srinivas 
36811447b59SVenkatesh Srinivas 	if (sc->vtpci_child_dev != child)
36911447b59SVenkatesh Srinivas 		return (ENOENT);
37011447b59SVenkatesh Srinivas 
37111447b59SVenkatesh Srinivas 	switch (index) {
37211447b59SVenkatesh Srinivas 	case VIRTIO_IVAR_DEVTYPE:
37311447b59SVenkatesh Srinivas 		*result = pci_get_subdevice(dev);
37411447b59SVenkatesh Srinivas 		break;
37511447b59SVenkatesh Srinivas 	default:
37611447b59SVenkatesh Srinivas 		return (ENOENT);
37711447b59SVenkatesh Srinivas 	}
37811447b59SVenkatesh Srinivas 
37911447b59SVenkatesh Srinivas 	return (0);
38011447b59SVenkatesh Srinivas }
38111447b59SVenkatesh Srinivas 
38211447b59SVenkatesh Srinivas static int
38311447b59SVenkatesh Srinivas vtpci_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
38411447b59SVenkatesh Srinivas {
38511447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
38611447b59SVenkatesh Srinivas 
38711447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
38811447b59SVenkatesh Srinivas 
38911447b59SVenkatesh Srinivas 	if (sc->vtpci_child_dev != child)
39011447b59SVenkatesh Srinivas 		return (ENOENT);
39111447b59SVenkatesh Srinivas 
39211447b59SVenkatesh Srinivas 	switch (index) {
39311447b59SVenkatesh Srinivas 	case VIRTIO_IVAR_FEATURE_DESC:
39411447b59SVenkatesh Srinivas 		sc->vtpci_child_feat_desc = (void *) value;
39511447b59SVenkatesh Srinivas 		break;
39611447b59SVenkatesh Srinivas 	default:
39711447b59SVenkatesh Srinivas 		return (ENOENT);
39811447b59SVenkatesh Srinivas 	}
39911447b59SVenkatesh Srinivas 
40011447b59SVenkatesh Srinivas 	return (0);
40111447b59SVenkatesh Srinivas }
40211447b59SVenkatesh Srinivas 
40311447b59SVenkatesh Srinivas static uint64_t
40411447b59SVenkatesh Srinivas vtpci_negotiate_features(device_t dev, uint64_t child_features)
40511447b59SVenkatesh Srinivas {
40611447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
40711447b59SVenkatesh Srinivas 	uint64_t host_features, features;
40811447b59SVenkatesh Srinivas 
40911447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
41011447b59SVenkatesh Srinivas 
41111447b59SVenkatesh Srinivas 	host_features = vtpci_read_config_4(sc, VIRTIO_PCI_HOST_FEATURES);
41211447b59SVenkatesh Srinivas 	vtpci_describe_features(sc, "host", host_features);
41311447b59SVenkatesh Srinivas 
41411447b59SVenkatesh Srinivas 	/*
41511447b59SVenkatesh Srinivas 	 * Limit negotiated features to what the driver, virtqueue, and
41611447b59SVenkatesh Srinivas 	 * host all support.
41711447b59SVenkatesh Srinivas 	 */
41811447b59SVenkatesh Srinivas 	features = host_features & child_features;
41911447b59SVenkatesh Srinivas 	features = virtqueue_filter_features(features);
42011447b59SVenkatesh Srinivas 	sc->vtpci_features = features;
42111447b59SVenkatesh Srinivas 
42211447b59SVenkatesh Srinivas 	vtpci_describe_features(sc, "negotiated", features);
42311447b59SVenkatesh Srinivas 	vtpci_write_config_4(sc, VIRTIO_PCI_GUEST_FEATURES, features);
42411447b59SVenkatesh Srinivas 
42511447b59SVenkatesh Srinivas 	return (features);
42611447b59SVenkatesh Srinivas }
42711447b59SVenkatesh Srinivas 
42811447b59SVenkatesh Srinivas static int
42911447b59SVenkatesh Srinivas vtpci_with_feature(device_t dev, uint64_t feature)
43011447b59SVenkatesh Srinivas {
43111447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
43211447b59SVenkatesh Srinivas 
43311447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
43411447b59SVenkatesh Srinivas 
43511447b59SVenkatesh Srinivas 	return ((sc->vtpci_features & feature) != 0);
43611447b59SVenkatesh Srinivas }
43711447b59SVenkatesh Srinivas 
43811447b59SVenkatesh Srinivas static int
43911447b59SVenkatesh Srinivas vtpci_alloc_virtqueues(device_t dev, int flags, int nvqs,
44011447b59SVenkatesh Srinivas     struct vq_alloc_info *vq_info)
44111447b59SVenkatesh Srinivas {
44211447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
44311447b59SVenkatesh Srinivas 	struct vtpci_virtqueue *vqx;
44411447b59SVenkatesh Srinivas 	struct vq_alloc_info *info;
44511447b59SVenkatesh Srinivas 	int queue, error;
44611447b59SVenkatesh Srinivas 	uint16_t vq_size;
44711447b59SVenkatesh Srinivas 
44811447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
44911447b59SVenkatesh Srinivas 
45011447b59SVenkatesh Srinivas 	if (sc->vtpci_nvqs != 0 || nvqs <= 0 ||
45111447b59SVenkatesh Srinivas 	    nvqs > VIRTIO_MAX_VIRTQUEUES)
45211447b59SVenkatesh Srinivas 		return (EINVAL);
45311447b59SVenkatesh Srinivas 
45411447b59SVenkatesh Srinivas 	error = vtpci_alloc_interrupts(sc, flags, nvqs, vq_info);
45511447b59SVenkatesh Srinivas 	if (error) {
45611447b59SVenkatesh Srinivas 		device_printf(dev, "cannot allocate interrupts\n");
45711447b59SVenkatesh Srinivas 		return (error);
45811447b59SVenkatesh Srinivas 	}
45911447b59SVenkatesh Srinivas 
46011447b59SVenkatesh Srinivas 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
46111447b59SVenkatesh Srinivas 		error = vtpci_register_msix_vector(sc,
46211447b59SVenkatesh Srinivas 		    VIRTIO_MSI_CONFIG_VECTOR, 0);
46311447b59SVenkatesh Srinivas 		if (error)
46411447b59SVenkatesh Srinivas 			return (error);
46511447b59SVenkatesh Srinivas 	}
46611447b59SVenkatesh Srinivas 
46711447b59SVenkatesh Srinivas 	for (queue = 0; queue < nvqs; queue++) {
46811447b59SVenkatesh Srinivas 		vqx = &sc->vtpci_vqx[queue];
46911447b59SVenkatesh Srinivas 		info = &vq_info[queue];
47011447b59SVenkatesh Srinivas 
47111447b59SVenkatesh Srinivas 		vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, queue);
47211447b59SVenkatesh Srinivas 
47311447b59SVenkatesh Srinivas 		vq_size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM);
47411447b59SVenkatesh Srinivas 		error = virtqueue_alloc(dev, queue, vq_size,
47511447b59SVenkatesh Srinivas 		    VIRTIO_PCI_VRING_ALIGN, 0xFFFFFFFFUL, info, &vqx->vq);
47611447b59SVenkatesh Srinivas 		if (error)
47711447b59SVenkatesh Srinivas 			return (error);
47811447b59SVenkatesh Srinivas 
47911447b59SVenkatesh Srinivas 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
48011447b59SVenkatesh Srinivas 			error = vtpci_register_msix_vector(sc,
48111447b59SVenkatesh Srinivas 			    VIRTIO_MSI_QUEUE_VECTOR, vqx->ires_idx);
48211447b59SVenkatesh Srinivas 			if (error)
48311447b59SVenkatesh Srinivas 				return (error);
48411447b59SVenkatesh Srinivas 		}
48511447b59SVenkatesh Srinivas 
48611447b59SVenkatesh Srinivas 		vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN,
48711447b59SVenkatesh Srinivas 		    virtqueue_paddr(vqx->vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
48811447b59SVenkatesh Srinivas 
48911447b59SVenkatesh Srinivas 		*info->vqai_vq = vqx->vq;
49011447b59SVenkatesh Srinivas 		sc->vtpci_nvqs++;
49111447b59SVenkatesh Srinivas 	}
49211447b59SVenkatesh Srinivas 
49311447b59SVenkatesh Srinivas 	return (0);
49411447b59SVenkatesh Srinivas }
49511447b59SVenkatesh Srinivas 
49611447b59SVenkatesh Srinivas static int
4972f1382caSVenkatesh Srinivas vtpci_setup_intr(device_t dev, lwkt_serialize_t slz)
49811447b59SVenkatesh Srinivas {
49911447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
50011447b59SVenkatesh Srinivas 	struct vtpci_intr_resource *ires;
50111447b59SVenkatesh Srinivas 	struct vtpci_virtqueue *vqx;
50211447b59SVenkatesh Srinivas 	int i, flags, error;
50311447b59SVenkatesh Srinivas 
50411447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
50511447b59SVenkatesh Srinivas 	flags = INTR_MPSAFE;
50611447b59SVenkatesh Srinivas 	ires = &sc->vtpci_intr_res[0];
50711447b59SVenkatesh Srinivas 
50811447b59SVenkatesh Srinivas 	if ((sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) == 0) {
50911447b59SVenkatesh Srinivas 		error = bus_setup_intr(dev, ires->irq, flags,
5102f1382caSVenkatesh Srinivas 				       (driver_intr_t *) vtpci_legacy_intr,
5112f1382caSVenkatesh Srinivas 				       sc, &ires->intrhand, slz);
51211447b59SVenkatesh Srinivas 		return (error);
51311447b59SVenkatesh Srinivas 	}
51411447b59SVenkatesh Srinivas 
5152f1382caSVenkatesh Srinivas 	error = bus_setup_intr(dev, ires->irq, flags,
5162f1382caSVenkatesh Srinivas 			       (driver_intr_t *) vtpci_config_intr,
5172f1382caSVenkatesh Srinivas 			       sc, &ires->intrhand, slz);
51811447b59SVenkatesh Srinivas 	if (error)
51911447b59SVenkatesh Srinivas 		return (error);
52011447b59SVenkatesh Srinivas 
52111447b59SVenkatesh Srinivas 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_SHARED_MSIX) {
52211447b59SVenkatesh Srinivas 		ires = &sc->vtpci_intr_res[1];
52311447b59SVenkatesh Srinivas 		error = bus_setup_intr(dev, ires->irq, flags,
5242f1382caSVenkatesh Srinivas 				       (driver_intr_t *) vtpci_vq_shared_intr,
5252f1382caSVenkatesh Srinivas 				       sc, &ires->intrhand, slz);
52611447b59SVenkatesh Srinivas 
52711447b59SVenkatesh Srinivas 		return (error);
52811447b59SVenkatesh Srinivas 	}
52911447b59SVenkatesh Srinivas 
53011447b59SVenkatesh Srinivas 	/* Setup an interrupt handler for each virtqueue. */
53111447b59SVenkatesh Srinivas 	for (i = 0; i < sc->vtpci_nvqs; i++) {
53211447b59SVenkatesh Srinivas 		vqx = &sc->vtpci_vqx[i];
53311447b59SVenkatesh Srinivas 		if (vqx->ires_idx < 1)
53411447b59SVenkatesh Srinivas 			continue;
53511447b59SVenkatesh Srinivas 
53611447b59SVenkatesh Srinivas 		ires = &sc->vtpci_intr_res[vqx->ires_idx];
53711447b59SVenkatesh Srinivas 		error = bus_setup_intr(dev, ires->irq, flags,
5382f1382caSVenkatesh Srinivas 				       (driver_intr_t *) vtpci_vq_intr,
5392f1382caSVenkatesh Srinivas 				       vqx->vq, &ires->intrhand, slz);
54011447b59SVenkatesh Srinivas 		if (error)
54111447b59SVenkatesh Srinivas 			return (error);
54211447b59SVenkatesh Srinivas 	}
54311447b59SVenkatesh Srinivas 
54411447b59SVenkatesh Srinivas 	return (0);
54511447b59SVenkatesh Srinivas }
54611447b59SVenkatesh Srinivas 
54711447b59SVenkatesh Srinivas static void
54811447b59SVenkatesh Srinivas vtpci_stop(device_t dev)
54911447b59SVenkatesh Srinivas {
55011447b59SVenkatesh Srinivas 	vtpci_reset(device_get_softc(dev));
55111447b59SVenkatesh Srinivas }
55211447b59SVenkatesh Srinivas 
55311447b59SVenkatesh Srinivas static int
55411447b59SVenkatesh Srinivas vtpci_reinit(device_t dev, uint64_t features)
55511447b59SVenkatesh Srinivas {
55611447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
55711447b59SVenkatesh Srinivas 	struct vtpci_virtqueue *vqx;
55811447b59SVenkatesh Srinivas 	struct virtqueue *vq;
55911447b59SVenkatesh Srinivas 	int queue, error;
56011447b59SVenkatesh Srinivas 	uint16_t vq_size;
56111447b59SVenkatesh Srinivas 
56211447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
56311447b59SVenkatesh Srinivas 
56411447b59SVenkatesh Srinivas 	/*
56511447b59SVenkatesh Srinivas 	 * Redrive the device initialization. This is a bit of an abuse
56611447b59SVenkatesh Srinivas 	 * of the specification, but both VirtualBox and QEMU/KVM seem
56711447b59SVenkatesh Srinivas 	 * to play nice. We do not allow the host device to change from
56811447b59SVenkatesh Srinivas 	 * what was originally negotiated beyond what the guest driver
56911447b59SVenkatesh Srinivas 	 * changed (MSIX state should not change, number of virtqueues
57011447b59SVenkatesh Srinivas 	 * and their size remain the same, etc).
57111447b59SVenkatesh Srinivas 	 */
57211447b59SVenkatesh Srinivas 
57311447b59SVenkatesh Srinivas 	if (vtpci_get_status(dev) != VIRTIO_CONFIG_STATUS_RESET)
57411447b59SVenkatesh Srinivas 		vtpci_stop(dev);
57511447b59SVenkatesh Srinivas 
57611447b59SVenkatesh Srinivas 	/*
57711447b59SVenkatesh Srinivas 	 * Quickly drive the status through ACK and DRIVER. The device
57811447b59SVenkatesh Srinivas 	 * does not become usable again until vtpci_reinit_complete().
57911447b59SVenkatesh Srinivas 	 */
58011447b59SVenkatesh Srinivas 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
58111447b59SVenkatesh Srinivas 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER);
58211447b59SVenkatesh Srinivas 
58311447b59SVenkatesh Srinivas 	vtpci_negotiate_features(dev, features);
58411447b59SVenkatesh Srinivas 
58511447b59SVenkatesh Srinivas 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
58611447b59SVenkatesh Srinivas 		error = vtpci_register_msix_vector(sc,
58711447b59SVenkatesh Srinivas 		    VIRTIO_MSI_CONFIG_VECTOR, 0);
58811447b59SVenkatesh Srinivas 		if (error)
58911447b59SVenkatesh Srinivas 			return (error);
59011447b59SVenkatesh Srinivas 	}
59111447b59SVenkatesh Srinivas 
59211447b59SVenkatesh Srinivas 	for (queue = 0; queue < sc->vtpci_nvqs; queue++) {
59311447b59SVenkatesh Srinivas 		vqx = &sc->vtpci_vqx[queue];
59411447b59SVenkatesh Srinivas 		vq = vqx->vq;
59511447b59SVenkatesh Srinivas 
59611447b59SVenkatesh Srinivas 		KASSERT(vq != NULL, ("vq %d not allocated", queue));
59711447b59SVenkatesh Srinivas 		vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, queue);
59811447b59SVenkatesh Srinivas 
59911447b59SVenkatesh Srinivas 		vq_size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM);
60011447b59SVenkatesh Srinivas 		error = virtqueue_reinit(vq, vq_size);
60111447b59SVenkatesh Srinivas 		if (error)
60211447b59SVenkatesh Srinivas 			return (error);
60311447b59SVenkatesh Srinivas 
60411447b59SVenkatesh Srinivas 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
60511447b59SVenkatesh Srinivas 			error = vtpci_register_msix_vector(sc,
60611447b59SVenkatesh Srinivas 			    VIRTIO_MSI_QUEUE_VECTOR, vqx->ires_idx);
60711447b59SVenkatesh Srinivas 			if (error)
60811447b59SVenkatesh Srinivas 				return (error);
60911447b59SVenkatesh Srinivas 		}
61011447b59SVenkatesh Srinivas 
61111447b59SVenkatesh Srinivas 		vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN,
61211447b59SVenkatesh Srinivas 		    virtqueue_paddr(vqx->vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
61311447b59SVenkatesh Srinivas 	}
61411447b59SVenkatesh Srinivas 
61511447b59SVenkatesh Srinivas 	return (0);
61611447b59SVenkatesh Srinivas }
61711447b59SVenkatesh Srinivas 
61811447b59SVenkatesh Srinivas static void
61911447b59SVenkatesh Srinivas vtpci_reinit_complete(device_t dev)
62011447b59SVenkatesh Srinivas {
62111447b59SVenkatesh Srinivas 
62211447b59SVenkatesh Srinivas 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
62311447b59SVenkatesh Srinivas }
62411447b59SVenkatesh Srinivas 
62511447b59SVenkatesh Srinivas static void
62611447b59SVenkatesh Srinivas vtpci_notify_virtqueue(device_t dev, uint16_t queue)
62711447b59SVenkatesh Srinivas {
62811447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
62911447b59SVenkatesh Srinivas 
63011447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
63111447b59SVenkatesh Srinivas 
63211447b59SVenkatesh Srinivas 	vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_NOTIFY, queue);
63311447b59SVenkatesh Srinivas }
63411447b59SVenkatesh Srinivas 
63511447b59SVenkatesh Srinivas static uint8_t
63611447b59SVenkatesh Srinivas vtpci_get_status(device_t dev)
63711447b59SVenkatesh Srinivas {
63811447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
63911447b59SVenkatesh Srinivas 
64011447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
64111447b59SVenkatesh Srinivas 
64211447b59SVenkatesh Srinivas 	return (vtpci_read_config_1(sc, VIRTIO_PCI_STATUS));
64311447b59SVenkatesh Srinivas }
64411447b59SVenkatesh Srinivas 
64511447b59SVenkatesh Srinivas static void
64611447b59SVenkatesh Srinivas vtpci_set_status(device_t dev, uint8_t status)
64711447b59SVenkatesh Srinivas {
64811447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
64911447b59SVenkatesh Srinivas 
65011447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
65111447b59SVenkatesh Srinivas 
65211447b59SVenkatesh Srinivas 	if (status != VIRTIO_CONFIG_STATUS_RESET)
65311447b59SVenkatesh Srinivas 		status |= vtpci_get_status(dev);
65411447b59SVenkatesh Srinivas 
65511447b59SVenkatesh Srinivas 	vtpci_write_config_1(sc, VIRTIO_PCI_STATUS, status);
65611447b59SVenkatesh Srinivas }
65711447b59SVenkatesh Srinivas 
65811447b59SVenkatesh Srinivas static void
65911447b59SVenkatesh Srinivas vtpci_read_dev_config(device_t dev, bus_size_t offset,
66011447b59SVenkatesh Srinivas     void *dst, int length)
66111447b59SVenkatesh Srinivas {
66211447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
66311447b59SVenkatesh Srinivas 	bus_size_t off;
66411447b59SVenkatesh Srinivas 	uint8_t *d;
66511447b59SVenkatesh Srinivas 	int size;
66611447b59SVenkatesh Srinivas 
66711447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
66811447b59SVenkatesh Srinivas 	off = VIRTIO_PCI_CONFIG(sc) + offset;
66911447b59SVenkatesh Srinivas 
67011447b59SVenkatesh Srinivas 	for (d = dst; length > 0; d += size, off += size, length -= size) {
67111447b59SVenkatesh Srinivas 		if (length >= 4) {
67211447b59SVenkatesh Srinivas 			size = 4;
67311447b59SVenkatesh Srinivas 			*(uint32_t *)d = vtpci_read_config_4(sc, off);
67411447b59SVenkatesh Srinivas 		} else if (length >= 2) {
67511447b59SVenkatesh Srinivas 			size = 2;
67611447b59SVenkatesh Srinivas 			*(uint16_t *)d = vtpci_read_config_2(sc, off);
67711447b59SVenkatesh Srinivas 		} else {
67811447b59SVenkatesh Srinivas 			size = 1;
67911447b59SVenkatesh Srinivas 			*d = vtpci_read_config_1(sc, off);
68011447b59SVenkatesh Srinivas 		}
68111447b59SVenkatesh Srinivas 	}
68211447b59SVenkatesh Srinivas }
68311447b59SVenkatesh Srinivas 
68411447b59SVenkatesh Srinivas static void
68511447b59SVenkatesh Srinivas vtpci_write_dev_config(device_t dev, bus_size_t offset,
68611447b59SVenkatesh Srinivas     void *src, int length)
68711447b59SVenkatesh Srinivas {
68811447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
68911447b59SVenkatesh Srinivas 	bus_size_t off;
69011447b59SVenkatesh Srinivas 	uint8_t *s;
69111447b59SVenkatesh Srinivas 	int size;
69211447b59SVenkatesh Srinivas 
69311447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
69411447b59SVenkatesh Srinivas 	off = VIRTIO_PCI_CONFIG(sc) + offset;
69511447b59SVenkatesh Srinivas 
69611447b59SVenkatesh Srinivas 	for (s = src; length > 0; s += size, off += size, length -= size) {
69711447b59SVenkatesh Srinivas 		if (length >= 4) {
69811447b59SVenkatesh Srinivas 			size = 4;
69911447b59SVenkatesh Srinivas 			vtpci_write_config_4(sc, off, *(uint32_t *)s);
70011447b59SVenkatesh Srinivas 		} else if (length >= 2) {
70111447b59SVenkatesh Srinivas 			size = 2;
70211447b59SVenkatesh Srinivas 			vtpci_write_config_2(sc, off, *(uint16_t *)s);
70311447b59SVenkatesh Srinivas 		} else {
70411447b59SVenkatesh Srinivas 			size = 1;
70511447b59SVenkatesh Srinivas 			vtpci_write_config_1(sc, off, *s);
70611447b59SVenkatesh Srinivas 		}
70711447b59SVenkatesh Srinivas 	}
70811447b59SVenkatesh Srinivas }
70911447b59SVenkatesh Srinivas 
71011447b59SVenkatesh Srinivas static void
71111447b59SVenkatesh Srinivas vtpci_describe_features(struct vtpci_softc *sc, const char *msg,
71211447b59SVenkatesh Srinivas     uint64_t features)
71311447b59SVenkatesh Srinivas {
71411447b59SVenkatesh Srinivas 	device_t dev, child;
71511447b59SVenkatesh Srinivas 
71611447b59SVenkatesh Srinivas 	dev = sc->vtpci_dev;
71711447b59SVenkatesh Srinivas 	child = sc->vtpci_child_dev;
71811447b59SVenkatesh Srinivas 
71911447b59SVenkatesh Srinivas 	if (device_is_attached(child) && bootverbose == 0)
72011447b59SVenkatesh Srinivas 		return;
72111447b59SVenkatesh Srinivas 
72211447b59SVenkatesh Srinivas 	virtio_describe(dev, msg, features, sc->vtpci_child_feat_desc);
72311447b59SVenkatesh Srinivas }
72411447b59SVenkatesh Srinivas 
72511447b59SVenkatesh Srinivas static void
72611447b59SVenkatesh Srinivas vtpci_probe_and_attach_child(struct vtpci_softc *sc)
72711447b59SVenkatesh Srinivas {
72811447b59SVenkatesh Srinivas 	device_t dev, child;
729b17fe9c5SImre Vadasz 	int error;
73011447b59SVenkatesh Srinivas 
73111447b59SVenkatesh Srinivas 	dev = sc->vtpci_dev;
73211447b59SVenkatesh Srinivas 	child = sc->vtpci_child_dev;
73311447b59SVenkatesh Srinivas 
73411447b59SVenkatesh Srinivas 	if (child == NULL)
73511447b59SVenkatesh Srinivas 		return;
73611447b59SVenkatesh Srinivas 
73711447b59SVenkatesh Srinivas 	if (device_get_state(child) != DS_NOTPRESENT)
73811447b59SVenkatesh Srinivas 		return;
73911447b59SVenkatesh Srinivas 
74011447b59SVenkatesh Srinivas 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER);
741b17fe9c5SImre Vadasz 	error = device_probe_and_attach(child);
742b17fe9c5SImre Vadasz 	if (error != 0 || device_get_state(child) == DS_NOTPRESENT) {
74311447b59SVenkatesh Srinivas 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED);
74411447b59SVenkatesh Srinivas 		vtpci_reset(sc);
74511447b59SVenkatesh Srinivas 		vtpci_release_child_resources(sc);
74611447b59SVenkatesh Srinivas 
74711447b59SVenkatesh Srinivas 		/* Reset status for future attempt. */
74811447b59SVenkatesh Srinivas 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
74911447b59SVenkatesh Srinivas 	} else
75011447b59SVenkatesh Srinivas 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
75111447b59SVenkatesh Srinivas }
75211447b59SVenkatesh Srinivas 
75311447b59SVenkatesh Srinivas static int
75411447b59SVenkatesh Srinivas vtpci_alloc_interrupts(struct vtpci_softc *sc, int flags, int nvqs,
75511447b59SVenkatesh Srinivas     struct vq_alloc_info *vq_info)
75611447b59SVenkatesh Srinivas {
75711447b59SVenkatesh Srinivas 	int i, nvectors, error;
75811447b59SVenkatesh Srinivas 
75911447b59SVenkatesh Srinivas 	/*
76011447b59SVenkatesh Srinivas 	 * Only allocate a vector for virtqueues that are actually
76111447b59SVenkatesh Srinivas 	 * expecting an interrupt.
76211447b59SVenkatesh Srinivas 	 */
76311447b59SVenkatesh Srinivas 	for (nvectors = 0, i = 0; i < nvqs; i++)
76411447b59SVenkatesh Srinivas 		if (vq_info[i].vqai_intr != NULL)
76511447b59SVenkatesh Srinivas 			nvectors++;
76611447b59SVenkatesh Srinivas 
76711447b59SVenkatesh Srinivas 	if (vtpci_disable_msix != 0 ||
76811447b59SVenkatesh Srinivas 	    sc->vtpci_flags & VIRTIO_PCI_FLAG_NO_MSIX ||
76911447b59SVenkatesh Srinivas 	    flags & VIRTIO_ALLOC_VQS_DISABLE_MSIX ||
77011447b59SVenkatesh Srinivas 	    vtpci_alloc_msix(sc, nvectors) != 0) {
77111447b59SVenkatesh Srinivas 		/*
77211447b59SVenkatesh Srinivas 		 * Use MSI interrupts if available. Otherwise, we fallback
77311447b59SVenkatesh Srinivas 		 * to legacy interrupts.
77411447b59SVenkatesh Srinivas 		 */
77511447b59SVenkatesh Srinivas 		if ((sc->vtpci_flags & VIRTIO_PCI_FLAG_NO_MSI) == 0 &&
77611447b59SVenkatesh Srinivas 		    vtpci_alloc_msi(sc) == 0)
77711447b59SVenkatesh Srinivas 			sc->vtpci_flags |= VIRTIO_PCI_FLAG_MSI;
77811447b59SVenkatesh Srinivas 
77911447b59SVenkatesh Srinivas 		sc->vtpci_nintr_res = 1;
78011447b59SVenkatesh Srinivas 	}
78111447b59SVenkatesh Srinivas 
78211447b59SVenkatesh Srinivas 	error = vtpci_alloc_intr_resources(sc, nvqs, vq_info);
78311447b59SVenkatesh Srinivas 
78411447b59SVenkatesh Srinivas 	return (error);
78511447b59SVenkatesh Srinivas }
78611447b59SVenkatesh Srinivas 
78711447b59SVenkatesh Srinivas static int
78811447b59SVenkatesh Srinivas vtpci_alloc_intr_resources(struct vtpci_softc *sc, int nvqs,
78911447b59SVenkatesh Srinivas     struct vq_alloc_info *vq_info)
79011447b59SVenkatesh Srinivas {
79111447b59SVenkatesh Srinivas 	device_t dev;
79211447b59SVenkatesh Srinivas 	struct resource *irq;
79311447b59SVenkatesh Srinivas 	struct vtpci_virtqueue *vqx;
79411447b59SVenkatesh Srinivas 	int i, rid, flags, res_idx;
79511447b59SVenkatesh Srinivas 
79611447b59SVenkatesh Srinivas 	dev = sc->vtpci_dev;
79711447b59SVenkatesh Srinivas 	flags = RF_ACTIVE;
79811447b59SVenkatesh Srinivas 
79911447b59SVenkatesh Srinivas 	if ((sc->vtpci_flags &
80011447b59SVenkatesh Srinivas 	    (VIRTIO_PCI_FLAG_MSI | VIRTIO_PCI_FLAG_MSIX)) == 0) {
80111447b59SVenkatesh Srinivas 		rid = 0;
80211447b59SVenkatesh Srinivas 		flags |= RF_SHAREABLE;
80311447b59SVenkatesh Srinivas 	} else
80411447b59SVenkatesh Srinivas 		rid = 1;
80511447b59SVenkatesh Srinivas 
80611447b59SVenkatesh Srinivas 	for (i = 0; i < sc->vtpci_nintr_res; i++) {
80711447b59SVenkatesh Srinivas 		irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, flags);
80811447b59SVenkatesh Srinivas 		if (irq == NULL)
80911447b59SVenkatesh Srinivas 			return (ENXIO);
81011447b59SVenkatesh Srinivas 
81111447b59SVenkatesh Srinivas 		sc->vtpci_intr_res[i].irq = irq;
81211447b59SVenkatesh Srinivas 		sc->vtpci_intr_res[i].rid = rid++;
81311447b59SVenkatesh Srinivas 	}
81411447b59SVenkatesh Srinivas 
81511447b59SVenkatesh Srinivas 	/*
81611447b59SVenkatesh Srinivas 	 * Map the virtqueue into the correct index in vq_intr_res[]. Note the
81711447b59SVenkatesh Srinivas 	 * first index is reserved for configuration changes notifications.
81811447b59SVenkatesh Srinivas 	 */
81911447b59SVenkatesh Srinivas 	for (i = 0, res_idx = 1; i < nvqs; i++) {
82011447b59SVenkatesh Srinivas 		vqx = &sc->vtpci_vqx[i];
82111447b59SVenkatesh Srinivas 
82211447b59SVenkatesh Srinivas 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
82311447b59SVenkatesh Srinivas 			if (vq_info[i].vqai_intr == NULL)
82411447b59SVenkatesh Srinivas 				vqx->ires_idx = -1;
82511447b59SVenkatesh Srinivas 			else if (sc->vtpci_flags & VIRTIO_PCI_FLAG_SHARED_MSIX)
82611447b59SVenkatesh Srinivas 				vqx->ires_idx = res_idx;
82711447b59SVenkatesh Srinivas 			else
82811447b59SVenkatesh Srinivas 				vqx->ires_idx = res_idx++;
82911447b59SVenkatesh Srinivas 		} else
83011447b59SVenkatesh Srinivas 			vqx->ires_idx = -1;
83111447b59SVenkatesh Srinivas 	}
83211447b59SVenkatesh Srinivas 
83311447b59SVenkatesh Srinivas 	return (0);
83411447b59SVenkatesh Srinivas }
83511447b59SVenkatesh Srinivas 
83611447b59SVenkatesh Srinivas static int
83711447b59SVenkatesh Srinivas vtpci_alloc_msi(struct vtpci_softc *sc)
83811447b59SVenkatesh Srinivas {
83911447b59SVenkatesh Srinivas 	device_t dev;
8409aa0ab36SSascha Wildner 	int nmsi;
84111447b59SVenkatesh Srinivas 	u_int irq_flags;
84211447b59SVenkatesh Srinivas 
84311447b59SVenkatesh Srinivas 	dev = sc->vtpci_dev;
84411447b59SVenkatesh Srinivas 	nmsi = pci_msi_count(dev);
84511447b59SVenkatesh Srinivas 
84611447b59SVenkatesh Srinivas 	if (nmsi < 1)
84711447b59SVenkatesh Srinivas 		return (1);
84811447b59SVenkatesh Srinivas 
84911447b59SVenkatesh Srinivas 	sc->vtpci_irq_rid = 0;
85011447b59SVenkatesh Srinivas         sc->vtpci_irq_type = pci_alloc_1intr(dev, 1,
85111447b59SVenkatesh Srinivas             &sc->vtpci_irq_rid, &irq_flags);
85211447b59SVenkatesh Srinivas 
85311447b59SVenkatesh Srinivas 
854*f41543e1SSepherosa Ziehau 	return (0);
85511447b59SVenkatesh Srinivas }
85611447b59SVenkatesh Srinivas 
85711447b59SVenkatesh Srinivas static int
85811447b59SVenkatesh Srinivas vtpci_alloc_msix(struct vtpci_softc *sc, int nvectors)
85911447b59SVenkatesh Srinivas {
86011447b59SVenkatesh Srinivas 	/* XXX(vsrinivas): Huh? Is this how MSI-X works?*/
86111447b59SVenkatesh Srinivas 	/* XXX(vsrinivas): All of this was disabled... */
8625c3e984aSSascha Wildner #if 0
86311447b59SVenkatesh Srinivas 	device_t dev;
86411447b59SVenkatesh Srinivas 	int nmsix, cnt, required;
86511447b59SVenkatesh Srinivas 
86611447b59SVenkatesh Srinivas 	dev = sc->vtpci_dev;
86711447b59SVenkatesh Srinivas 
86811447b59SVenkatesh Srinivas 	nmsix = pci_msix_count(dev);
86911447b59SVenkatesh Srinivas 	if (nmsix < 1)
87011447b59SVenkatesh Srinivas 		return (1);
87111447b59SVenkatesh Srinivas 
87211447b59SVenkatesh Srinivas 	/* An additional vector is needed for the config changes. */
87311447b59SVenkatesh Srinivas 	required = nvectors + 1;
87411447b59SVenkatesh Srinivas 	if (nmsix >= required) {
87511447b59SVenkatesh Srinivas 		cnt = required;
87611447b59SVenkatesh Srinivas 		if (pci_alloc_msix(dev, &cnt) == 0 && cnt >= required)
87711447b59SVenkatesh Srinivas 			goto out;
87811447b59SVenkatesh Srinivas 
87911447b59SVenkatesh Srinivas 		pci_release_msi(dev);
88011447b59SVenkatesh Srinivas 	}
88111447b59SVenkatesh Srinivas 
88211447b59SVenkatesh Srinivas 	/* Attempt shared MSIX configuration. */
88311447b59SVenkatesh Srinivas 	required = 2;
88411447b59SVenkatesh Srinivas 	if (nmsix >= required) {
88511447b59SVenkatesh Srinivas 		cnt = required;
88611447b59SVenkatesh Srinivas 		if (pci_alloc_msix(dev, &cnt) == 0 && cnt >= required) {
88711447b59SVenkatesh Srinivas 			sc->vtpci_flags |= VIRTIO_PCI_FLAG_SHARED_MSIX;
88811447b59SVenkatesh Srinivas 			goto out;
88911447b59SVenkatesh Srinivas 		}
89011447b59SVenkatesh Srinivas 
89111447b59SVenkatesh Srinivas 		pci_release_msi(dev);
89211447b59SVenkatesh Srinivas 	}
89311447b59SVenkatesh Srinivas 
89411447b59SVenkatesh Srinivas 	return (1);
89511447b59SVenkatesh Srinivas 
89611447b59SVenkatesh Srinivas out:
89711447b59SVenkatesh Srinivas 	sc->vtpci_nintr_res = required;
89811447b59SVenkatesh Srinivas 	sc->vtpci_flags |= VIRTIO_PCI_FLAG_MSIX;
89911447b59SVenkatesh Srinivas 
90011447b59SVenkatesh Srinivas 	if (bootverbose) {
90111447b59SVenkatesh Srinivas 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_SHARED_MSIX)
90211447b59SVenkatesh Srinivas 			device_printf(dev, "using shared virtqueue MSIX\n");
90311447b59SVenkatesh Srinivas 		else
90411447b59SVenkatesh Srinivas 			device_printf(dev, "using per virtqueue MSIX\n");
90511447b59SVenkatesh Srinivas 	}
90611447b59SVenkatesh Srinivas #endif
90711447b59SVenkatesh Srinivas 	return (0);
90811447b59SVenkatesh Srinivas }
90911447b59SVenkatesh Srinivas 
91011447b59SVenkatesh Srinivas static int
91111447b59SVenkatesh Srinivas vtpci_register_msix_vector(struct vtpci_softc *sc, int offset, int res_idx)
91211447b59SVenkatesh Srinivas {
91311447b59SVenkatesh Srinivas 	device_t dev;
91411447b59SVenkatesh Srinivas 	uint16_t vector;
91511447b59SVenkatesh Srinivas 
91611447b59SVenkatesh Srinivas 	dev = sc->vtpci_dev;
91711447b59SVenkatesh Srinivas 
91811447b59SVenkatesh Srinivas 	if (offset != VIRTIO_MSI_CONFIG_VECTOR &&
91911447b59SVenkatesh Srinivas 	    offset != VIRTIO_MSI_QUEUE_VECTOR)
92011447b59SVenkatesh Srinivas 		return (EINVAL);
92111447b59SVenkatesh Srinivas 
92211447b59SVenkatesh Srinivas 	if (res_idx != -1) {
92311447b59SVenkatesh Srinivas 		/* Map from rid to host vector. */
92411447b59SVenkatesh Srinivas 		vector = sc->vtpci_intr_res[res_idx].rid - 1;
92511447b59SVenkatesh Srinivas 	} else
92611447b59SVenkatesh Srinivas 		vector = VIRTIO_MSI_NO_VECTOR;
92711447b59SVenkatesh Srinivas 
92811447b59SVenkatesh Srinivas 	/* The first resource is special; make sure it is used correctly. */
92911447b59SVenkatesh Srinivas 	if (res_idx == 0) {
93011447b59SVenkatesh Srinivas 		KASSERT(vector == 0, ("unexpected config vector"));
93111447b59SVenkatesh Srinivas 		KASSERT(offset == VIRTIO_MSI_CONFIG_VECTOR,
93211447b59SVenkatesh Srinivas 		    ("unexpected config offset"));
93311447b59SVenkatesh Srinivas 	}
93411447b59SVenkatesh Srinivas 
93511447b59SVenkatesh Srinivas 	vtpci_write_config_2(sc, offset, vector);
93611447b59SVenkatesh Srinivas 
93711447b59SVenkatesh Srinivas 	if (vtpci_read_config_2(sc, offset) != vector) {
93811447b59SVenkatesh Srinivas 		device_printf(dev, "insufficient host resources for "
93911447b59SVenkatesh Srinivas 		    "MSIX interrupts\n");
94011447b59SVenkatesh Srinivas 		return (ENODEV);
94111447b59SVenkatesh Srinivas 	}
94211447b59SVenkatesh Srinivas 
94311447b59SVenkatesh Srinivas 	return (0);
94411447b59SVenkatesh Srinivas }
94511447b59SVenkatesh Srinivas 
94611447b59SVenkatesh Srinivas static void
94711447b59SVenkatesh Srinivas vtpci_free_interrupts(struct vtpci_softc *sc)
94811447b59SVenkatesh Srinivas {
94911447b59SVenkatesh Srinivas 	device_t dev;
95011447b59SVenkatesh Srinivas 	struct vtpci_intr_resource *ires;
95111447b59SVenkatesh Srinivas 	int i;
95211447b59SVenkatesh Srinivas 
95311447b59SVenkatesh Srinivas 	dev = sc->vtpci_dev;
95411447b59SVenkatesh Srinivas 	sc->vtpci_nintr_res = 0;
95511447b59SVenkatesh Srinivas 
95611447b59SVenkatesh Srinivas 	if (sc->vtpci_flags & (VIRTIO_PCI_FLAG_MSI | VIRTIO_PCI_FLAG_MSIX)) {
95711447b59SVenkatesh Srinivas 		pci_release_msi(dev);
95811447b59SVenkatesh Srinivas 		sc->vtpci_flags &= ~(VIRTIO_PCI_FLAG_MSI |
95911447b59SVenkatesh Srinivas 		    VIRTIO_PCI_FLAG_MSIX | VIRTIO_PCI_FLAG_SHARED_MSIX);
96011447b59SVenkatesh Srinivas 	}
96111447b59SVenkatesh Srinivas 
96211447b59SVenkatesh Srinivas 	for (i = 0; i < 1 + VIRTIO_MAX_VIRTQUEUES; i++) {
96311447b59SVenkatesh Srinivas 		ires = &sc->vtpci_intr_res[i];
96411447b59SVenkatesh Srinivas 
96511447b59SVenkatesh Srinivas 		if (ires->intrhand != NULL) {
96611447b59SVenkatesh Srinivas 			bus_teardown_intr(dev, ires->irq, ires->intrhand);
96711447b59SVenkatesh Srinivas 			ires->intrhand = NULL;
96811447b59SVenkatesh Srinivas 		}
96911447b59SVenkatesh Srinivas 
97011447b59SVenkatesh Srinivas 		if (ires->irq != NULL) {
97111447b59SVenkatesh Srinivas 			bus_release_resource(dev, SYS_RES_IRQ, ires->rid,
97211447b59SVenkatesh Srinivas 			    ires->irq);
97311447b59SVenkatesh Srinivas 			ires->irq = NULL;
97411447b59SVenkatesh Srinivas 		}
97511447b59SVenkatesh Srinivas 
97611447b59SVenkatesh Srinivas 		ires->rid = -1;
97711447b59SVenkatesh Srinivas 	}
97811447b59SVenkatesh Srinivas }
97911447b59SVenkatesh Srinivas 
98011447b59SVenkatesh Srinivas static void
98111447b59SVenkatesh Srinivas vtpci_free_virtqueues(struct vtpci_softc *sc)
98211447b59SVenkatesh Srinivas {
98311447b59SVenkatesh Srinivas 	struct vtpci_virtqueue *vqx;
98411447b59SVenkatesh Srinivas 	int i;
98511447b59SVenkatesh Srinivas 
98611447b59SVenkatesh Srinivas 	sc->vtpci_nvqs = 0;
98711447b59SVenkatesh Srinivas 
98811447b59SVenkatesh Srinivas 	for (i = 0; i < VIRTIO_MAX_VIRTQUEUES; i++) {
98911447b59SVenkatesh Srinivas 		vqx = &sc->vtpci_vqx[i];
99011447b59SVenkatesh Srinivas 
99111447b59SVenkatesh Srinivas 		if (vqx->vq != NULL) {
99211447b59SVenkatesh Srinivas 			virtqueue_free(vqx->vq);
99311447b59SVenkatesh Srinivas 			vqx->vq = NULL;
99411447b59SVenkatesh Srinivas 		}
99511447b59SVenkatesh Srinivas 	}
99611447b59SVenkatesh Srinivas }
99711447b59SVenkatesh Srinivas 
99811447b59SVenkatesh Srinivas static void
99911447b59SVenkatesh Srinivas vtpci_release_child_resources(struct vtpci_softc *sc)
100011447b59SVenkatesh Srinivas {
100111447b59SVenkatesh Srinivas 
100211447b59SVenkatesh Srinivas 	vtpci_free_interrupts(sc);
100311447b59SVenkatesh Srinivas 	vtpci_free_virtqueues(sc);
100411447b59SVenkatesh Srinivas }
100511447b59SVenkatesh Srinivas 
100611447b59SVenkatesh Srinivas static void
100711447b59SVenkatesh Srinivas vtpci_reset(struct vtpci_softc *sc)
100811447b59SVenkatesh Srinivas {
100911447b59SVenkatesh Srinivas 
101011447b59SVenkatesh Srinivas 	/*
101111447b59SVenkatesh Srinivas 	 * Setting the status to RESET sets the host device to
101211447b59SVenkatesh Srinivas 	 * the original, uninitialized state.
101311447b59SVenkatesh Srinivas 	 */
101411447b59SVenkatesh Srinivas 	vtpci_set_status(sc->vtpci_dev, VIRTIO_CONFIG_STATUS_RESET);
101511447b59SVenkatesh Srinivas }
101611447b59SVenkatesh Srinivas 
101711447b59SVenkatesh Srinivas static int
101811447b59SVenkatesh Srinivas vtpci_legacy_intr(void *xsc)
101911447b59SVenkatesh Srinivas {
102011447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
102111447b59SVenkatesh Srinivas 	struct vtpci_virtqueue *vqx;
102211447b59SVenkatesh Srinivas 	int i;
102311447b59SVenkatesh Srinivas 	uint8_t isr;
102411447b59SVenkatesh Srinivas 
102511447b59SVenkatesh Srinivas 	sc = xsc;
102611447b59SVenkatesh Srinivas 	vqx = &sc->vtpci_vqx[0];
102711447b59SVenkatesh Srinivas 
102811447b59SVenkatesh Srinivas 	/* Reading the ISR also clears it. */
102911447b59SVenkatesh Srinivas 	isr = vtpci_read_config_1(sc, VIRTIO_PCI_ISR);
103011447b59SVenkatesh Srinivas 
103111447b59SVenkatesh Srinivas 	if (isr & VIRTIO_PCI_ISR_CONFIG)
103211447b59SVenkatesh Srinivas 		vtpci_config_intr(sc);
103311447b59SVenkatesh Srinivas 
103411447b59SVenkatesh Srinivas 	if (isr & VIRTIO_PCI_ISR_INTR)
103511447b59SVenkatesh Srinivas 		for (i = 0; i < sc->vtpci_nvqs; i++, vqx++)
103611447b59SVenkatesh Srinivas 			virtqueue_intr(vqx->vq);
103711447b59SVenkatesh Srinivas 
103811447b59SVenkatesh Srinivas 	return isr;
103911447b59SVenkatesh Srinivas }
104011447b59SVenkatesh Srinivas 
104111447b59SVenkatesh Srinivas static int
104211447b59SVenkatesh Srinivas vtpci_vq_shared_intr(void *xsc)
104311447b59SVenkatesh Srinivas {
104411447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
104511447b59SVenkatesh Srinivas 	struct vtpci_virtqueue *vqx;
104611447b59SVenkatesh Srinivas 	int i, rc;
104711447b59SVenkatesh Srinivas 
104811447b59SVenkatesh Srinivas 	rc = 0;
104911447b59SVenkatesh Srinivas 	sc = xsc;
105011447b59SVenkatesh Srinivas 	vqx = &sc->vtpci_vqx[0];
105111447b59SVenkatesh Srinivas 
105211447b59SVenkatesh Srinivas 	for (i = 0; i < sc->vtpci_nvqs; i++, vqx++)
105311447b59SVenkatesh Srinivas 		rc |= virtqueue_intr(vqx->vq);
105411447b59SVenkatesh Srinivas 
105511447b59SVenkatesh Srinivas 	return rc;
105611447b59SVenkatesh Srinivas }
105711447b59SVenkatesh Srinivas 
105811447b59SVenkatesh Srinivas static int
105911447b59SVenkatesh Srinivas vtpci_vq_intr(void *xvq)
106011447b59SVenkatesh Srinivas {
106111447b59SVenkatesh Srinivas 	struct virtqueue *vq;
106211447b59SVenkatesh Srinivas 	int rc;
106311447b59SVenkatesh Srinivas 
106411447b59SVenkatesh Srinivas 	vq = xvq;
106511447b59SVenkatesh Srinivas 	rc = virtqueue_intr(vq);
106611447b59SVenkatesh Srinivas 
106711447b59SVenkatesh Srinivas 	return rc;
106811447b59SVenkatesh Srinivas }
106911447b59SVenkatesh Srinivas 
107011447b59SVenkatesh Srinivas static int
107111447b59SVenkatesh Srinivas vtpci_config_intr(void *xsc)
107211447b59SVenkatesh Srinivas {
107311447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
107411447b59SVenkatesh Srinivas 	device_t child;
107511447b59SVenkatesh Srinivas 	int rc;
107611447b59SVenkatesh Srinivas 
107711447b59SVenkatesh Srinivas 	rc = 0;
107811447b59SVenkatesh Srinivas 	sc = xsc;
107911447b59SVenkatesh Srinivas 	child = sc->vtpci_child_dev;
108011447b59SVenkatesh Srinivas 
108111447b59SVenkatesh Srinivas 	if (child != NULL)
108211447b59SVenkatesh Srinivas 		rc = VIRTIO_CONFIG_CHANGE(child);
108311447b59SVenkatesh Srinivas 
108411447b59SVenkatesh Srinivas 	return rc;
108511447b59SVenkatesh Srinivas }
1086