xref: /dflybsd-src/sys/dev/virtual/virtio/pci/virtio_pci.c (revision b17fe9c54b9825d21bcfd7ff28cc36a5e31d74f4)
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;
25411447b59SVenkatesh Srinivas 	/* XXX(vsrinivas): Check out how to get MSI-X */
2555d0ea777SSascha Wildner #ifdef OLD_MSI
25611447b59SVenkatesh Srinivas 	if (pci_find_extcap(dev, PCIY_MSIX, NULL) == 0) {
25711447b59SVenkatesh Srinivas 		rid = PCIR_BAR(1);
25811447b59SVenkatesh Srinivas 		sc->vtpci_msix_res = bus_alloc_resource_any(dev,
25911447b59SVenkatesh Srinivas 		    SYS_RES_MEMORY, &rid, RF_ACTIVE);
26011447b59SVenkatesh Srinivas 	}
26111447b59SVenkatesh Srinivas #endif
26211447b59SVenkatesh Srinivas 	if (sc->vtpci_msix_res == NULL)
26311447b59SVenkatesh Srinivas 		sc->vtpci_flags |= VIRTIO_PCI_FLAG_NO_MSIX;
26411447b59SVenkatesh Srinivas 
26511447b59SVenkatesh Srinivas 	vtpci_reset(sc);
26611447b59SVenkatesh Srinivas 
26711447b59SVenkatesh Srinivas 	/* Tell the host we've noticed this device. */
26811447b59SVenkatesh Srinivas 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
26911447b59SVenkatesh Srinivas 
27011447b59SVenkatesh Srinivas 	if ((child = device_add_child(dev, NULL, -1)) == NULL) {
27111447b59SVenkatesh Srinivas 		device_printf(dev, "cannot create child device\n");
27211447b59SVenkatesh Srinivas 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED);
27311447b59SVenkatesh Srinivas 		vtpci_detach(dev);
27411447b59SVenkatesh Srinivas 		return (ENOMEM);
27511447b59SVenkatesh Srinivas 	}
27611447b59SVenkatesh Srinivas 
27711447b59SVenkatesh Srinivas 	sc->vtpci_child_dev = child;
27811447b59SVenkatesh Srinivas 	vtpci_probe_and_attach_child(sc);
27911447b59SVenkatesh Srinivas 
28011447b59SVenkatesh Srinivas 	return (0);
28111447b59SVenkatesh Srinivas }
28211447b59SVenkatesh Srinivas 
28311447b59SVenkatesh Srinivas static int
28411447b59SVenkatesh Srinivas vtpci_detach(device_t dev)
28511447b59SVenkatesh Srinivas {
28611447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
28711447b59SVenkatesh Srinivas 	device_t child;
28811447b59SVenkatesh Srinivas 	int error;
28911447b59SVenkatesh Srinivas 
29011447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
29111447b59SVenkatesh Srinivas 
29211447b59SVenkatesh Srinivas 	if ((child = sc->vtpci_child_dev) != NULL) {
29311447b59SVenkatesh Srinivas 		error = device_delete_child(dev, child);
29411447b59SVenkatesh Srinivas 		if (error)
29511447b59SVenkatesh Srinivas 			return (error);
29611447b59SVenkatesh Srinivas 		sc->vtpci_child_dev = NULL;
29711447b59SVenkatesh Srinivas 	}
29811447b59SVenkatesh Srinivas 
29911447b59SVenkatesh Srinivas 	vtpci_reset(sc);
30011447b59SVenkatesh Srinivas 
30111447b59SVenkatesh Srinivas 	if (sc->vtpci_msix_res != NULL) {
30211447b59SVenkatesh Srinivas 		bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BAR(1),
30311447b59SVenkatesh Srinivas 		    sc->vtpci_msix_res);
30411447b59SVenkatesh Srinivas 		sc->vtpci_msix_res = NULL;
30511447b59SVenkatesh Srinivas 	}
30611447b59SVenkatesh Srinivas 
30711447b59SVenkatesh Srinivas 	if (sc->vtpci_res != NULL) {
30811447b59SVenkatesh Srinivas 		bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0),
30911447b59SVenkatesh Srinivas 		    sc->vtpci_res);
31011447b59SVenkatesh Srinivas 		sc->vtpci_res = NULL;
31111447b59SVenkatesh Srinivas 	}
31211447b59SVenkatesh Srinivas 
31311447b59SVenkatesh Srinivas 	return (0);
31411447b59SVenkatesh Srinivas }
31511447b59SVenkatesh Srinivas 
31611447b59SVenkatesh Srinivas static int
31711447b59SVenkatesh Srinivas vtpci_suspend(device_t dev)
31811447b59SVenkatesh Srinivas {
31911447b59SVenkatesh Srinivas 
32011447b59SVenkatesh Srinivas 	return (bus_generic_suspend(dev));
32111447b59SVenkatesh Srinivas }
32211447b59SVenkatesh Srinivas 
32311447b59SVenkatesh Srinivas static int
32411447b59SVenkatesh Srinivas vtpci_resume(device_t dev)
32511447b59SVenkatesh Srinivas {
32611447b59SVenkatesh Srinivas 
32711447b59SVenkatesh Srinivas 	return (bus_generic_resume(dev));
32811447b59SVenkatesh Srinivas }
32911447b59SVenkatesh Srinivas 
33011447b59SVenkatesh Srinivas static int
33111447b59SVenkatesh Srinivas vtpci_shutdown(device_t dev)
33211447b59SVenkatesh Srinivas {
33311447b59SVenkatesh Srinivas 
33411447b59SVenkatesh Srinivas 	(void) bus_generic_shutdown(dev);
33511447b59SVenkatesh Srinivas 	/* Forcibly stop the host device. */
33611447b59SVenkatesh Srinivas 	vtpci_stop(dev);
33711447b59SVenkatesh Srinivas 
33811447b59SVenkatesh Srinivas 	return (0);
33911447b59SVenkatesh Srinivas }
34011447b59SVenkatesh Srinivas 
34111447b59SVenkatesh Srinivas static void
34211447b59SVenkatesh Srinivas vtpci_driver_added(device_t dev, driver_t *driver)
34311447b59SVenkatesh Srinivas {
34411447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
34511447b59SVenkatesh Srinivas 
34611447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
34711447b59SVenkatesh Srinivas 
34811447b59SVenkatesh Srinivas 	vtpci_probe_and_attach_child(sc);
34911447b59SVenkatesh Srinivas }
35011447b59SVenkatesh Srinivas 
35111447b59SVenkatesh Srinivas static void
35211447b59SVenkatesh Srinivas vtpci_child_detached(device_t dev, device_t child)
35311447b59SVenkatesh Srinivas {
35411447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
35511447b59SVenkatesh Srinivas 
35611447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
35711447b59SVenkatesh Srinivas 
35811447b59SVenkatesh Srinivas 	vtpci_reset(sc);
35911447b59SVenkatesh Srinivas 	vtpci_release_child_resources(sc);
36011447b59SVenkatesh Srinivas }
36111447b59SVenkatesh Srinivas 
36211447b59SVenkatesh Srinivas static int
36311447b59SVenkatesh Srinivas vtpci_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
36411447b59SVenkatesh Srinivas {
36511447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
36611447b59SVenkatesh Srinivas 
36711447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
36811447b59SVenkatesh Srinivas 
36911447b59SVenkatesh Srinivas 	if (sc->vtpci_child_dev != child)
37011447b59SVenkatesh Srinivas 		return (ENOENT);
37111447b59SVenkatesh Srinivas 
37211447b59SVenkatesh Srinivas 	switch (index) {
37311447b59SVenkatesh Srinivas 	case VIRTIO_IVAR_DEVTYPE:
37411447b59SVenkatesh Srinivas 		*result = pci_get_subdevice(dev);
37511447b59SVenkatesh Srinivas 		break;
37611447b59SVenkatesh Srinivas 	default:
37711447b59SVenkatesh Srinivas 		return (ENOENT);
37811447b59SVenkatesh Srinivas 	}
37911447b59SVenkatesh Srinivas 
38011447b59SVenkatesh Srinivas 	return (0);
38111447b59SVenkatesh Srinivas }
38211447b59SVenkatesh Srinivas 
38311447b59SVenkatesh Srinivas static int
38411447b59SVenkatesh Srinivas vtpci_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
38511447b59SVenkatesh Srinivas {
38611447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
38711447b59SVenkatesh Srinivas 
38811447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
38911447b59SVenkatesh Srinivas 
39011447b59SVenkatesh Srinivas 	if (sc->vtpci_child_dev != child)
39111447b59SVenkatesh Srinivas 		return (ENOENT);
39211447b59SVenkatesh Srinivas 
39311447b59SVenkatesh Srinivas 	switch (index) {
39411447b59SVenkatesh Srinivas 	case VIRTIO_IVAR_FEATURE_DESC:
39511447b59SVenkatesh Srinivas 		sc->vtpci_child_feat_desc = (void *) value;
39611447b59SVenkatesh Srinivas 		break;
39711447b59SVenkatesh Srinivas 	default:
39811447b59SVenkatesh Srinivas 		return (ENOENT);
39911447b59SVenkatesh Srinivas 	}
40011447b59SVenkatesh Srinivas 
40111447b59SVenkatesh Srinivas 	return (0);
40211447b59SVenkatesh Srinivas }
40311447b59SVenkatesh Srinivas 
40411447b59SVenkatesh Srinivas static uint64_t
40511447b59SVenkatesh Srinivas vtpci_negotiate_features(device_t dev, uint64_t child_features)
40611447b59SVenkatesh Srinivas {
40711447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
40811447b59SVenkatesh Srinivas 	uint64_t host_features, features;
40911447b59SVenkatesh Srinivas 
41011447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
41111447b59SVenkatesh Srinivas 
41211447b59SVenkatesh Srinivas 	host_features = vtpci_read_config_4(sc, VIRTIO_PCI_HOST_FEATURES);
41311447b59SVenkatesh Srinivas 	vtpci_describe_features(sc, "host", host_features);
41411447b59SVenkatesh Srinivas 
41511447b59SVenkatesh Srinivas 	/*
41611447b59SVenkatesh Srinivas 	 * Limit negotiated features to what the driver, virtqueue, and
41711447b59SVenkatesh Srinivas 	 * host all support.
41811447b59SVenkatesh Srinivas 	 */
41911447b59SVenkatesh Srinivas 	features = host_features & child_features;
42011447b59SVenkatesh Srinivas 	features = virtqueue_filter_features(features);
42111447b59SVenkatesh Srinivas 	sc->vtpci_features = features;
42211447b59SVenkatesh Srinivas 
42311447b59SVenkatesh Srinivas 	vtpci_describe_features(sc, "negotiated", features);
42411447b59SVenkatesh Srinivas 	vtpci_write_config_4(sc, VIRTIO_PCI_GUEST_FEATURES, features);
42511447b59SVenkatesh Srinivas 
42611447b59SVenkatesh Srinivas 	return (features);
42711447b59SVenkatesh Srinivas }
42811447b59SVenkatesh Srinivas 
42911447b59SVenkatesh Srinivas static int
43011447b59SVenkatesh Srinivas vtpci_with_feature(device_t dev, uint64_t feature)
43111447b59SVenkatesh Srinivas {
43211447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
43311447b59SVenkatesh Srinivas 
43411447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
43511447b59SVenkatesh Srinivas 
43611447b59SVenkatesh Srinivas 	return ((sc->vtpci_features & feature) != 0);
43711447b59SVenkatesh Srinivas }
43811447b59SVenkatesh Srinivas 
43911447b59SVenkatesh Srinivas static int
44011447b59SVenkatesh Srinivas vtpci_alloc_virtqueues(device_t dev, int flags, int nvqs,
44111447b59SVenkatesh Srinivas     struct vq_alloc_info *vq_info)
44211447b59SVenkatesh Srinivas {
44311447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
44411447b59SVenkatesh Srinivas 	struct vtpci_virtqueue *vqx;
44511447b59SVenkatesh Srinivas 	struct vq_alloc_info *info;
44611447b59SVenkatesh Srinivas 	int queue, error;
44711447b59SVenkatesh Srinivas 	uint16_t vq_size;
44811447b59SVenkatesh Srinivas 
44911447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
45011447b59SVenkatesh Srinivas 
45111447b59SVenkatesh Srinivas 	if (sc->vtpci_nvqs != 0 || nvqs <= 0 ||
45211447b59SVenkatesh Srinivas 	    nvqs > VIRTIO_MAX_VIRTQUEUES)
45311447b59SVenkatesh Srinivas 		return (EINVAL);
45411447b59SVenkatesh Srinivas 
45511447b59SVenkatesh Srinivas 	error = vtpci_alloc_interrupts(sc, flags, nvqs, vq_info);
45611447b59SVenkatesh Srinivas 	if (error) {
45711447b59SVenkatesh Srinivas 		device_printf(dev, "cannot allocate interrupts\n");
45811447b59SVenkatesh Srinivas 		return (error);
45911447b59SVenkatesh Srinivas 	}
46011447b59SVenkatesh Srinivas 
46111447b59SVenkatesh Srinivas 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
46211447b59SVenkatesh Srinivas 		error = vtpci_register_msix_vector(sc,
46311447b59SVenkatesh Srinivas 		    VIRTIO_MSI_CONFIG_VECTOR, 0);
46411447b59SVenkatesh Srinivas 		if (error)
46511447b59SVenkatesh Srinivas 			return (error);
46611447b59SVenkatesh Srinivas 	}
46711447b59SVenkatesh Srinivas 
46811447b59SVenkatesh Srinivas 	for (queue = 0; queue < nvqs; queue++) {
46911447b59SVenkatesh Srinivas 		vqx = &sc->vtpci_vqx[queue];
47011447b59SVenkatesh Srinivas 		info = &vq_info[queue];
47111447b59SVenkatesh Srinivas 
47211447b59SVenkatesh Srinivas 		vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, queue);
47311447b59SVenkatesh Srinivas 
47411447b59SVenkatesh Srinivas 		vq_size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM);
47511447b59SVenkatesh Srinivas 		error = virtqueue_alloc(dev, queue, vq_size,
47611447b59SVenkatesh Srinivas 		    VIRTIO_PCI_VRING_ALIGN, 0xFFFFFFFFUL, info, &vqx->vq);
47711447b59SVenkatesh Srinivas 		if (error)
47811447b59SVenkatesh Srinivas 			return (error);
47911447b59SVenkatesh Srinivas 
48011447b59SVenkatesh Srinivas 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
48111447b59SVenkatesh Srinivas 			error = vtpci_register_msix_vector(sc,
48211447b59SVenkatesh Srinivas 			    VIRTIO_MSI_QUEUE_VECTOR, vqx->ires_idx);
48311447b59SVenkatesh Srinivas 			if (error)
48411447b59SVenkatesh Srinivas 				return (error);
48511447b59SVenkatesh Srinivas 		}
48611447b59SVenkatesh Srinivas 
48711447b59SVenkatesh Srinivas 		vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN,
48811447b59SVenkatesh Srinivas 		    virtqueue_paddr(vqx->vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
48911447b59SVenkatesh Srinivas 
49011447b59SVenkatesh Srinivas 		*info->vqai_vq = vqx->vq;
49111447b59SVenkatesh Srinivas 		sc->vtpci_nvqs++;
49211447b59SVenkatesh Srinivas 	}
49311447b59SVenkatesh Srinivas 
49411447b59SVenkatesh Srinivas 	return (0);
49511447b59SVenkatesh Srinivas }
49611447b59SVenkatesh Srinivas 
49711447b59SVenkatesh Srinivas static int
4982f1382caSVenkatesh Srinivas vtpci_setup_intr(device_t dev, lwkt_serialize_t slz)
49911447b59SVenkatesh Srinivas {
50011447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
50111447b59SVenkatesh Srinivas 	struct vtpci_intr_resource *ires;
50211447b59SVenkatesh Srinivas 	struct vtpci_virtqueue *vqx;
50311447b59SVenkatesh Srinivas 	int i, flags, error;
50411447b59SVenkatesh Srinivas 
50511447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
50611447b59SVenkatesh Srinivas 	flags = INTR_MPSAFE;
50711447b59SVenkatesh Srinivas 	ires = &sc->vtpci_intr_res[0];
50811447b59SVenkatesh Srinivas 
50911447b59SVenkatesh Srinivas 	if ((sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) == 0) {
51011447b59SVenkatesh Srinivas 		error = bus_setup_intr(dev, ires->irq, flags,
5112f1382caSVenkatesh Srinivas 				       (driver_intr_t *) vtpci_legacy_intr,
5122f1382caSVenkatesh Srinivas 				       sc, &ires->intrhand, slz);
51311447b59SVenkatesh Srinivas 		return (error);
51411447b59SVenkatesh Srinivas 	}
51511447b59SVenkatesh Srinivas 
5162f1382caSVenkatesh Srinivas 	error = bus_setup_intr(dev, ires->irq, flags,
5172f1382caSVenkatesh Srinivas 			       (driver_intr_t *) vtpci_config_intr,
5182f1382caSVenkatesh Srinivas 			       sc, &ires->intrhand, slz);
51911447b59SVenkatesh Srinivas 	if (error)
52011447b59SVenkatesh Srinivas 		return (error);
52111447b59SVenkatesh Srinivas 
52211447b59SVenkatesh Srinivas 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_SHARED_MSIX) {
52311447b59SVenkatesh Srinivas 		ires = &sc->vtpci_intr_res[1];
52411447b59SVenkatesh Srinivas 		error = bus_setup_intr(dev, ires->irq, flags,
5252f1382caSVenkatesh Srinivas 				       (driver_intr_t *) vtpci_vq_shared_intr,
5262f1382caSVenkatesh Srinivas 				       sc, &ires->intrhand, slz);
52711447b59SVenkatesh Srinivas 
52811447b59SVenkatesh Srinivas 		return (error);
52911447b59SVenkatesh Srinivas 	}
53011447b59SVenkatesh Srinivas 
53111447b59SVenkatesh Srinivas 	/* Setup an interrupt handler for each virtqueue. */
53211447b59SVenkatesh Srinivas 	for (i = 0; i < sc->vtpci_nvqs; i++) {
53311447b59SVenkatesh Srinivas 		vqx = &sc->vtpci_vqx[i];
53411447b59SVenkatesh Srinivas 		if (vqx->ires_idx < 1)
53511447b59SVenkatesh Srinivas 			continue;
53611447b59SVenkatesh Srinivas 
53711447b59SVenkatesh Srinivas 		ires = &sc->vtpci_intr_res[vqx->ires_idx];
53811447b59SVenkatesh Srinivas 		error = bus_setup_intr(dev, ires->irq, flags,
5392f1382caSVenkatesh Srinivas 				       (driver_intr_t *) vtpci_vq_intr,
5402f1382caSVenkatesh Srinivas 				       vqx->vq, &ires->intrhand, slz);
54111447b59SVenkatesh Srinivas 		if (error)
54211447b59SVenkatesh Srinivas 			return (error);
54311447b59SVenkatesh Srinivas 	}
54411447b59SVenkatesh Srinivas 
54511447b59SVenkatesh Srinivas 	return (0);
54611447b59SVenkatesh Srinivas }
54711447b59SVenkatesh Srinivas 
54811447b59SVenkatesh Srinivas static void
54911447b59SVenkatesh Srinivas vtpci_stop(device_t dev)
55011447b59SVenkatesh Srinivas {
55111447b59SVenkatesh Srinivas 	vtpci_reset(device_get_softc(dev));
55211447b59SVenkatesh Srinivas }
55311447b59SVenkatesh Srinivas 
55411447b59SVenkatesh Srinivas static int
55511447b59SVenkatesh Srinivas vtpci_reinit(device_t dev, uint64_t features)
55611447b59SVenkatesh Srinivas {
55711447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
55811447b59SVenkatesh Srinivas 	struct vtpci_virtqueue *vqx;
55911447b59SVenkatesh Srinivas 	struct virtqueue *vq;
56011447b59SVenkatesh Srinivas 	int queue, error;
56111447b59SVenkatesh Srinivas 	uint16_t vq_size;
56211447b59SVenkatesh Srinivas 
56311447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
56411447b59SVenkatesh Srinivas 
56511447b59SVenkatesh Srinivas 	/*
56611447b59SVenkatesh Srinivas 	 * Redrive the device initialization. This is a bit of an abuse
56711447b59SVenkatesh Srinivas 	 * of the specification, but both VirtualBox and QEMU/KVM seem
56811447b59SVenkatesh Srinivas 	 * to play nice. We do not allow the host device to change from
56911447b59SVenkatesh Srinivas 	 * what was originally negotiated beyond what the guest driver
57011447b59SVenkatesh Srinivas 	 * changed (MSIX state should not change, number of virtqueues
57111447b59SVenkatesh Srinivas 	 * and their size remain the same, etc).
57211447b59SVenkatesh Srinivas 	 */
57311447b59SVenkatesh Srinivas 
57411447b59SVenkatesh Srinivas 	if (vtpci_get_status(dev) != VIRTIO_CONFIG_STATUS_RESET)
57511447b59SVenkatesh Srinivas 		vtpci_stop(dev);
57611447b59SVenkatesh Srinivas 
57711447b59SVenkatesh Srinivas 	/*
57811447b59SVenkatesh Srinivas 	 * Quickly drive the status through ACK and DRIVER. The device
57911447b59SVenkatesh Srinivas 	 * does not become usable again until vtpci_reinit_complete().
58011447b59SVenkatesh Srinivas 	 */
58111447b59SVenkatesh Srinivas 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
58211447b59SVenkatesh Srinivas 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER);
58311447b59SVenkatesh Srinivas 
58411447b59SVenkatesh Srinivas 	vtpci_negotiate_features(dev, features);
58511447b59SVenkatesh Srinivas 
58611447b59SVenkatesh Srinivas 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
58711447b59SVenkatesh Srinivas 		error = vtpci_register_msix_vector(sc,
58811447b59SVenkatesh Srinivas 		    VIRTIO_MSI_CONFIG_VECTOR, 0);
58911447b59SVenkatesh Srinivas 		if (error)
59011447b59SVenkatesh Srinivas 			return (error);
59111447b59SVenkatesh Srinivas 	}
59211447b59SVenkatesh Srinivas 
59311447b59SVenkatesh Srinivas 	for (queue = 0; queue < sc->vtpci_nvqs; queue++) {
59411447b59SVenkatesh Srinivas 		vqx = &sc->vtpci_vqx[queue];
59511447b59SVenkatesh Srinivas 		vq = vqx->vq;
59611447b59SVenkatesh Srinivas 
59711447b59SVenkatesh Srinivas 		KASSERT(vq != NULL, ("vq %d not allocated", queue));
59811447b59SVenkatesh Srinivas 		vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, queue);
59911447b59SVenkatesh Srinivas 
60011447b59SVenkatesh Srinivas 		vq_size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM);
60111447b59SVenkatesh Srinivas 		error = virtqueue_reinit(vq, vq_size);
60211447b59SVenkatesh Srinivas 		if (error)
60311447b59SVenkatesh Srinivas 			return (error);
60411447b59SVenkatesh Srinivas 
60511447b59SVenkatesh Srinivas 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
60611447b59SVenkatesh Srinivas 			error = vtpci_register_msix_vector(sc,
60711447b59SVenkatesh Srinivas 			    VIRTIO_MSI_QUEUE_VECTOR, vqx->ires_idx);
60811447b59SVenkatesh Srinivas 			if (error)
60911447b59SVenkatesh Srinivas 				return (error);
61011447b59SVenkatesh Srinivas 		}
61111447b59SVenkatesh Srinivas 
61211447b59SVenkatesh Srinivas 		vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN,
61311447b59SVenkatesh Srinivas 		    virtqueue_paddr(vqx->vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
61411447b59SVenkatesh Srinivas 	}
61511447b59SVenkatesh Srinivas 
61611447b59SVenkatesh Srinivas 	return (0);
61711447b59SVenkatesh Srinivas }
61811447b59SVenkatesh Srinivas 
61911447b59SVenkatesh Srinivas static void
62011447b59SVenkatesh Srinivas vtpci_reinit_complete(device_t dev)
62111447b59SVenkatesh Srinivas {
62211447b59SVenkatesh Srinivas 
62311447b59SVenkatesh Srinivas 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
62411447b59SVenkatesh Srinivas }
62511447b59SVenkatesh Srinivas 
62611447b59SVenkatesh Srinivas static void
62711447b59SVenkatesh Srinivas vtpci_notify_virtqueue(device_t dev, uint16_t queue)
62811447b59SVenkatesh Srinivas {
62911447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
63011447b59SVenkatesh Srinivas 
63111447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
63211447b59SVenkatesh Srinivas 
63311447b59SVenkatesh Srinivas 	vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_NOTIFY, queue);
63411447b59SVenkatesh Srinivas }
63511447b59SVenkatesh Srinivas 
63611447b59SVenkatesh Srinivas static uint8_t
63711447b59SVenkatesh Srinivas vtpci_get_status(device_t dev)
63811447b59SVenkatesh Srinivas {
63911447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
64011447b59SVenkatesh Srinivas 
64111447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
64211447b59SVenkatesh Srinivas 
64311447b59SVenkatesh Srinivas 	return (vtpci_read_config_1(sc, VIRTIO_PCI_STATUS));
64411447b59SVenkatesh Srinivas }
64511447b59SVenkatesh Srinivas 
64611447b59SVenkatesh Srinivas static void
64711447b59SVenkatesh Srinivas vtpci_set_status(device_t dev, uint8_t status)
64811447b59SVenkatesh Srinivas {
64911447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
65011447b59SVenkatesh Srinivas 
65111447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
65211447b59SVenkatesh Srinivas 
65311447b59SVenkatesh Srinivas 	if (status != VIRTIO_CONFIG_STATUS_RESET)
65411447b59SVenkatesh Srinivas 		status |= vtpci_get_status(dev);
65511447b59SVenkatesh Srinivas 
65611447b59SVenkatesh Srinivas 	vtpci_write_config_1(sc, VIRTIO_PCI_STATUS, status);
65711447b59SVenkatesh Srinivas }
65811447b59SVenkatesh Srinivas 
65911447b59SVenkatesh Srinivas static void
66011447b59SVenkatesh Srinivas vtpci_read_dev_config(device_t dev, bus_size_t offset,
66111447b59SVenkatesh Srinivas     void *dst, int length)
66211447b59SVenkatesh Srinivas {
66311447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
66411447b59SVenkatesh Srinivas 	bus_size_t off;
66511447b59SVenkatesh Srinivas 	uint8_t *d;
66611447b59SVenkatesh Srinivas 	int size;
66711447b59SVenkatesh Srinivas 
66811447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
66911447b59SVenkatesh Srinivas 	off = VIRTIO_PCI_CONFIG(sc) + offset;
67011447b59SVenkatesh Srinivas 
67111447b59SVenkatesh Srinivas 	for (d = dst; length > 0; d += size, off += size, length -= size) {
67211447b59SVenkatesh Srinivas 		if (length >= 4) {
67311447b59SVenkatesh Srinivas 			size = 4;
67411447b59SVenkatesh Srinivas 			*(uint32_t *)d = vtpci_read_config_4(sc, off);
67511447b59SVenkatesh Srinivas 		} else if (length >= 2) {
67611447b59SVenkatesh Srinivas 			size = 2;
67711447b59SVenkatesh Srinivas 			*(uint16_t *)d = vtpci_read_config_2(sc, off);
67811447b59SVenkatesh Srinivas 		} else {
67911447b59SVenkatesh Srinivas 			size = 1;
68011447b59SVenkatesh Srinivas 			*d = vtpci_read_config_1(sc, off);
68111447b59SVenkatesh Srinivas 		}
68211447b59SVenkatesh Srinivas 	}
68311447b59SVenkatesh Srinivas }
68411447b59SVenkatesh Srinivas 
68511447b59SVenkatesh Srinivas static void
68611447b59SVenkatesh Srinivas vtpci_write_dev_config(device_t dev, bus_size_t offset,
68711447b59SVenkatesh Srinivas     void *src, int length)
68811447b59SVenkatesh Srinivas {
68911447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
69011447b59SVenkatesh Srinivas 	bus_size_t off;
69111447b59SVenkatesh Srinivas 	uint8_t *s;
69211447b59SVenkatesh Srinivas 	int size;
69311447b59SVenkatesh Srinivas 
69411447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
69511447b59SVenkatesh Srinivas 	off = VIRTIO_PCI_CONFIG(sc) + offset;
69611447b59SVenkatesh Srinivas 
69711447b59SVenkatesh Srinivas 	for (s = src; length > 0; s += size, off += size, length -= size) {
69811447b59SVenkatesh Srinivas 		if (length >= 4) {
69911447b59SVenkatesh Srinivas 			size = 4;
70011447b59SVenkatesh Srinivas 			vtpci_write_config_4(sc, off, *(uint32_t *)s);
70111447b59SVenkatesh Srinivas 		} else if (length >= 2) {
70211447b59SVenkatesh Srinivas 			size = 2;
70311447b59SVenkatesh Srinivas 			vtpci_write_config_2(sc, off, *(uint16_t *)s);
70411447b59SVenkatesh Srinivas 		} else {
70511447b59SVenkatesh Srinivas 			size = 1;
70611447b59SVenkatesh Srinivas 			vtpci_write_config_1(sc, off, *s);
70711447b59SVenkatesh Srinivas 		}
70811447b59SVenkatesh Srinivas 	}
70911447b59SVenkatesh Srinivas }
71011447b59SVenkatesh Srinivas 
71111447b59SVenkatesh Srinivas static void
71211447b59SVenkatesh Srinivas vtpci_describe_features(struct vtpci_softc *sc, const char *msg,
71311447b59SVenkatesh Srinivas     uint64_t features)
71411447b59SVenkatesh Srinivas {
71511447b59SVenkatesh Srinivas 	device_t dev, child;
71611447b59SVenkatesh Srinivas 
71711447b59SVenkatesh Srinivas 	dev = sc->vtpci_dev;
71811447b59SVenkatesh Srinivas 	child = sc->vtpci_child_dev;
71911447b59SVenkatesh Srinivas 
72011447b59SVenkatesh Srinivas 	if (device_is_attached(child) && bootverbose == 0)
72111447b59SVenkatesh Srinivas 		return;
72211447b59SVenkatesh Srinivas 
72311447b59SVenkatesh Srinivas 	virtio_describe(dev, msg, features, sc->vtpci_child_feat_desc);
72411447b59SVenkatesh Srinivas }
72511447b59SVenkatesh Srinivas 
72611447b59SVenkatesh Srinivas static void
72711447b59SVenkatesh Srinivas vtpci_probe_and_attach_child(struct vtpci_softc *sc)
72811447b59SVenkatesh Srinivas {
72911447b59SVenkatesh Srinivas 	device_t dev, child;
730*b17fe9c5SImre Vadasz 	int error;
73111447b59SVenkatesh Srinivas 
73211447b59SVenkatesh Srinivas 	dev = sc->vtpci_dev;
73311447b59SVenkatesh Srinivas 	child = sc->vtpci_child_dev;
73411447b59SVenkatesh Srinivas 
73511447b59SVenkatesh Srinivas 	if (child == NULL)
73611447b59SVenkatesh Srinivas 		return;
73711447b59SVenkatesh Srinivas 
73811447b59SVenkatesh Srinivas 	if (device_get_state(child) != DS_NOTPRESENT)
73911447b59SVenkatesh Srinivas 		return;
74011447b59SVenkatesh Srinivas 
74111447b59SVenkatesh Srinivas 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER);
742*b17fe9c5SImre Vadasz 	error = device_probe_and_attach(child);
743*b17fe9c5SImre Vadasz 	if (error != 0 || device_get_state(child) == DS_NOTPRESENT) {
74411447b59SVenkatesh Srinivas 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED);
74511447b59SVenkatesh Srinivas 		vtpci_reset(sc);
74611447b59SVenkatesh Srinivas 		vtpci_release_child_resources(sc);
74711447b59SVenkatesh Srinivas 
74811447b59SVenkatesh Srinivas 		/* Reset status for future attempt. */
74911447b59SVenkatesh Srinivas 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
75011447b59SVenkatesh Srinivas 	} else
75111447b59SVenkatesh Srinivas 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
75211447b59SVenkatesh Srinivas }
75311447b59SVenkatesh Srinivas 
75411447b59SVenkatesh Srinivas static int
75511447b59SVenkatesh Srinivas vtpci_alloc_interrupts(struct vtpci_softc *sc, int flags, int nvqs,
75611447b59SVenkatesh Srinivas     struct vq_alloc_info *vq_info)
75711447b59SVenkatesh Srinivas {
75811447b59SVenkatesh Srinivas 	int i, nvectors, error;
75911447b59SVenkatesh Srinivas 
76011447b59SVenkatesh Srinivas 	/*
76111447b59SVenkatesh Srinivas 	 * Only allocate a vector for virtqueues that are actually
76211447b59SVenkatesh Srinivas 	 * expecting an interrupt.
76311447b59SVenkatesh Srinivas 	 */
76411447b59SVenkatesh Srinivas 	for (nvectors = 0, i = 0; i < nvqs; i++)
76511447b59SVenkatesh Srinivas 		if (vq_info[i].vqai_intr != NULL)
76611447b59SVenkatesh Srinivas 			nvectors++;
76711447b59SVenkatesh Srinivas 
76811447b59SVenkatesh Srinivas 	if (vtpci_disable_msix != 0 ||
76911447b59SVenkatesh Srinivas 	    sc->vtpci_flags & VIRTIO_PCI_FLAG_NO_MSIX ||
77011447b59SVenkatesh Srinivas 	    flags & VIRTIO_ALLOC_VQS_DISABLE_MSIX ||
77111447b59SVenkatesh Srinivas 	    vtpci_alloc_msix(sc, nvectors) != 0) {
77211447b59SVenkatesh Srinivas 		/*
77311447b59SVenkatesh Srinivas 		 * Use MSI interrupts if available. Otherwise, we fallback
77411447b59SVenkatesh Srinivas 		 * to legacy interrupts.
77511447b59SVenkatesh Srinivas 		 */
77611447b59SVenkatesh Srinivas 		if ((sc->vtpci_flags & VIRTIO_PCI_FLAG_NO_MSI) == 0 &&
77711447b59SVenkatesh Srinivas 		    vtpci_alloc_msi(sc) == 0)
77811447b59SVenkatesh Srinivas 			sc->vtpci_flags |= VIRTIO_PCI_FLAG_MSI;
77911447b59SVenkatesh Srinivas 
78011447b59SVenkatesh Srinivas 		sc->vtpci_nintr_res = 1;
78111447b59SVenkatesh Srinivas 	}
78211447b59SVenkatesh Srinivas 
78311447b59SVenkatesh Srinivas 	error = vtpci_alloc_intr_resources(sc, nvqs, vq_info);
78411447b59SVenkatesh Srinivas 
78511447b59SVenkatesh Srinivas 	return (error);
78611447b59SVenkatesh Srinivas }
78711447b59SVenkatesh Srinivas 
78811447b59SVenkatesh Srinivas static int
78911447b59SVenkatesh Srinivas vtpci_alloc_intr_resources(struct vtpci_softc *sc, int nvqs,
79011447b59SVenkatesh Srinivas     struct vq_alloc_info *vq_info)
79111447b59SVenkatesh Srinivas {
79211447b59SVenkatesh Srinivas 	device_t dev;
79311447b59SVenkatesh Srinivas 	struct resource *irq;
79411447b59SVenkatesh Srinivas 	struct vtpci_virtqueue *vqx;
79511447b59SVenkatesh Srinivas 	int i, rid, flags, res_idx;
79611447b59SVenkatesh Srinivas 
79711447b59SVenkatesh Srinivas 	dev = sc->vtpci_dev;
79811447b59SVenkatesh Srinivas 	flags = RF_ACTIVE;
79911447b59SVenkatesh Srinivas 
80011447b59SVenkatesh Srinivas 	if ((sc->vtpci_flags &
80111447b59SVenkatesh Srinivas 	    (VIRTIO_PCI_FLAG_MSI | VIRTIO_PCI_FLAG_MSIX)) == 0) {
80211447b59SVenkatesh Srinivas 		rid = 0;
80311447b59SVenkatesh Srinivas 		flags |= RF_SHAREABLE;
80411447b59SVenkatesh Srinivas 	} else
80511447b59SVenkatesh Srinivas 		rid = 1;
80611447b59SVenkatesh Srinivas 
80711447b59SVenkatesh Srinivas 	for (i = 0; i < sc->vtpci_nintr_res; i++) {
80811447b59SVenkatesh Srinivas 		irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, flags);
80911447b59SVenkatesh Srinivas 		if (irq == NULL)
81011447b59SVenkatesh Srinivas 			return (ENXIO);
81111447b59SVenkatesh Srinivas 
81211447b59SVenkatesh Srinivas 		sc->vtpci_intr_res[i].irq = irq;
81311447b59SVenkatesh Srinivas 		sc->vtpci_intr_res[i].rid = rid++;
81411447b59SVenkatesh Srinivas 	}
81511447b59SVenkatesh Srinivas 
81611447b59SVenkatesh Srinivas 	/*
81711447b59SVenkatesh Srinivas 	 * Map the virtqueue into the correct index in vq_intr_res[]. Note the
81811447b59SVenkatesh Srinivas 	 * first index is reserved for configuration changes notifications.
81911447b59SVenkatesh Srinivas 	 */
82011447b59SVenkatesh Srinivas 	for (i = 0, res_idx = 1; i < nvqs; i++) {
82111447b59SVenkatesh Srinivas 		vqx = &sc->vtpci_vqx[i];
82211447b59SVenkatesh Srinivas 
82311447b59SVenkatesh Srinivas 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
82411447b59SVenkatesh Srinivas 			if (vq_info[i].vqai_intr == NULL)
82511447b59SVenkatesh Srinivas 				vqx->ires_idx = -1;
82611447b59SVenkatesh Srinivas 			else if (sc->vtpci_flags & VIRTIO_PCI_FLAG_SHARED_MSIX)
82711447b59SVenkatesh Srinivas 				vqx->ires_idx = res_idx;
82811447b59SVenkatesh Srinivas 			else
82911447b59SVenkatesh Srinivas 				vqx->ires_idx = res_idx++;
83011447b59SVenkatesh Srinivas 		} else
83111447b59SVenkatesh Srinivas 			vqx->ires_idx = -1;
83211447b59SVenkatesh Srinivas 	}
83311447b59SVenkatesh Srinivas 
83411447b59SVenkatesh Srinivas 	return (0);
83511447b59SVenkatesh Srinivas }
83611447b59SVenkatesh Srinivas 
83711447b59SVenkatesh Srinivas static int
83811447b59SVenkatesh Srinivas vtpci_alloc_msi(struct vtpci_softc *sc)
83911447b59SVenkatesh Srinivas {
84011447b59SVenkatesh Srinivas 	device_t dev;
8419aa0ab36SSascha Wildner 	int nmsi;
84211447b59SVenkatesh Srinivas 	u_int irq_flags;
84311447b59SVenkatesh Srinivas 
84411447b59SVenkatesh Srinivas 	dev = sc->vtpci_dev;
84511447b59SVenkatesh Srinivas 	nmsi = pci_msi_count(dev);
84611447b59SVenkatesh Srinivas 
84711447b59SVenkatesh Srinivas 	if (nmsi < 1)
84811447b59SVenkatesh Srinivas 		return (1);
84911447b59SVenkatesh Srinivas 
85011447b59SVenkatesh Srinivas 	sc->vtpci_irq_rid = 0;
85111447b59SVenkatesh Srinivas         sc->vtpci_irq_type = pci_alloc_1intr(dev, 1,
85211447b59SVenkatesh Srinivas             &sc->vtpci_irq_rid, &irq_flags);
85311447b59SVenkatesh Srinivas 
85411447b59SVenkatesh Srinivas 
85511447b59SVenkatesh Srinivas 	return (1);
85611447b59SVenkatesh Srinivas }
85711447b59SVenkatesh Srinivas 
85811447b59SVenkatesh Srinivas static int
85911447b59SVenkatesh Srinivas vtpci_alloc_msix(struct vtpci_softc *sc, int nvectors)
86011447b59SVenkatesh Srinivas {
86111447b59SVenkatesh Srinivas 	/* XXX(vsrinivas): Huh? Is this how MSI-X works?*/
86211447b59SVenkatesh Srinivas 	/* XXX(vsrinivas): All of this was disabled... */
86311447b59SVenkatesh Srinivas #ifdef OLD_MSI
86411447b59SVenkatesh Srinivas 	device_t dev;
86511447b59SVenkatesh Srinivas 	int nmsix, cnt, required;
86611447b59SVenkatesh Srinivas 
86711447b59SVenkatesh Srinivas 	dev = sc->vtpci_dev;
86811447b59SVenkatesh Srinivas 
86911447b59SVenkatesh Srinivas 	nmsix = pci_msix_count(dev);
87011447b59SVenkatesh Srinivas 	if (nmsix < 1)
87111447b59SVenkatesh Srinivas 		return (1);
87211447b59SVenkatesh Srinivas 
87311447b59SVenkatesh Srinivas 	/* An additional vector is needed for the config changes. */
87411447b59SVenkatesh Srinivas 	required = nvectors + 1;
87511447b59SVenkatesh Srinivas 	if (nmsix >= required) {
87611447b59SVenkatesh Srinivas 		cnt = required;
87711447b59SVenkatesh Srinivas 		if (pci_alloc_msix(dev, &cnt) == 0 && cnt >= required)
87811447b59SVenkatesh Srinivas 			goto out;
87911447b59SVenkatesh Srinivas 
88011447b59SVenkatesh Srinivas 		pci_release_msi(dev);
88111447b59SVenkatesh Srinivas 	}
88211447b59SVenkatesh Srinivas 
88311447b59SVenkatesh Srinivas 	/* Attempt shared MSIX configuration. */
88411447b59SVenkatesh Srinivas 	required = 2;
88511447b59SVenkatesh Srinivas 	if (nmsix >= required) {
88611447b59SVenkatesh Srinivas 		cnt = required;
88711447b59SVenkatesh Srinivas 		if (pci_alloc_msix(dev, &cnt) == 0 && cnt >= required) {
88811447b59SVenkatesh Srinivas 			sc->vtpci_flags |= VIRTIO_PCI_FLAG_SHARED_MSIX;
88911447b59SVenkatesh Srinivas 			goto out;
89011447b59SVenkatesh Srinivas 		}
89111447b59SVenkatesh Srinivas 
89211447b59SVenkatesh Srinivas 		pci_release_msi(dev);
89311447b59SVenkatesh Srinivas 	}
89411447b59SVenkatesh Srinivas 
89511447b59SVenkatesh Srinivas 	return (1);
89611447b59SVenkatesh Srinivas 
89711447b59SVenkatesh Srinivas out:
89811447b59SVenkatesh Srinivas 	sc->vtpci_nintr_res = required;
89911447b59SVenkatesh Srinivas 	sc->vtpci_flags |= VIRTIO_PCI_FLAG_MSIX;
90011447b59SVenkatesh Srinivas 
90111447b59SVenkatesh Srinivas 	if (bootverbose) {
90211447b59SVenkatesh Srinivas 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_SHARED_MSIX)
90311447b59SVenkatesh Srinivas 			device_printf(dev, "using shared virtqueue MSIX\n");
90411447b59SVenkatesh Srinivas 		else
90511447b59SVenkatesh Srinivas 			device_printf(dev, "using per virtqueue MSIX\n");
90611447b59SVenkatesh Srinivas 	}
90711447b59SVenkatesh Srinivas #endif
90811447b59SVenkatesh Srinivas 	return (0);
90911447b59SVenkatesh Srinivas }
91011447b59SVenkatesh Srinivas 
91111447b59SVenkatesh Srinivas static int
91211447b59SVenkatesh Srinivas vtpci_register_msix_vector(struct vtpci_softc *sc, int offset, int res_idx)
91311447b59SVenkatesh Srinivas {
91411447b59SVenkatesh Srinivas 	device_t dev;
91511447b59SVenkatesh Srinivas 	uint16_t vector;
91611447b59SVenkatesh Srinivas 
91711447b59SVenkatesh Srinivas 	dev = sc->vtpci_dev;
91811447b59SVenkatesh Srinivas 
91911447b59SVenkatesh Srinivas 	if (offset != VIRTIO_MSI_CONFIG_VECTOR &&
92011447b59SVenkatesh Srinivas 	    offset != VIRTIO_MSI_QUEUE_VECTOR)
92111447b59SVenkatesh Srinivas 		return (EINVAL);
92211447b59SVenkatesh Srinivas 
92311447b59SVenkatesh Srinivas 	if (res_idx != -1) {
92411447b59SVenkatesh Srinivas 		/* Map from rid to host vector. */
92511447b59SVenkatesh Srinivas 		vector = sc->vtpci_intr_res[res_idx].rid - 1;
92611447b59SVenkatesh Srinivas 	} else
92711447b59SVenkatesh Srinivas 		vector = VIRTIO_MSI_NO_VECTOR;
92811447b59SVenkatesh Srinivas 
92911447b59SVenkatesh Srinivas 	/* The first resource is special; make sure it is used correctly. */
93011447b59SVenkatesh Srinivas 	if (res_idx == 0) {
93111447b59SVenkatesh Srinivas 		KASSERT(vector == 0, ("unexpected config vector"));
93211447b59SVenkatesh Srinivas 		KASSERT(offset == VIRTIO_MSI_CONFIG_VECTOR,
93311447b59SVenkatesh Srinivas 		    ("unexpected config offset"));
93411447b59SVenkatesh Srinivas 	}
93511447b59SVenkatesh Srinivas 
93611447b59SVenkatesh Srinivas 	vtpci_write_config_2(sc, offset, vector);
93711447b59SVenkatesh Srinivas 
93811447b59SVenkatesh Srinivas 	if (vtpci_read_config_2(sc, offset) != vector) {
93911447b59SVenkatesh Srinivas 		device_printf(dev, "insufficient host resources for "
94011447b59SVenkatesh Srinivas 		    "MSIX interrupts\n");
94111447b59SVenkatesh Srinivas 		return (ENODEV);
94211447b59SVenkatesh Srinivas 	}
94311447b59SVenkatesh Srinivas 
94411447b59SVenkatesh Srinivas 	return (0);
94511447b59SVenkatesh Srinivas }
94611447b59SVenkatesh Srinivas 
94711447b59SVenkatesh Srinivas static void
94811447b59SVenkatesh Srinivas vtpci_free_interrupts(struct vtpci_softc *sc)
94911447b59SVenkatesh Srinivas {
95011447b59SVenkatesh Srinivas 	device_t dev;
95111447b59SVenkatesh Srinivas 	struct vtpci_intr_resource *ires;
95211447b59SVenkatesh Srinivas 	int i;
95311447b59SVenkatesh Srinivas 
95411447b59SVenkatesh Srinivas 	dev = sc->vtpci_dev;
95511447b59SVenkatesh Srinivas 	sc->vtpci_nintr_res = 0;
95611447b59SVenkatesh Srinivas 
95711447b59SVenkatesh Srinivas 	if (sc->vtpci_flags & (VIRTIO_PCI_FLAG_MSI | VIRTIO_PCI_FLAG_MSIX)) {
95811447b59SVenkatesh Srinivas 		pci_release_msi(dev);
95911447b59SVenkatesh Srinivas 		sc->vtpci_flags &= ~(VIRTIO_PCI_FLAG_MSI |
96011447b59SVenkatesh Srinivas 		    VIRTIO_PCI_FLAG_MSIX | VIRTIO_PCI_FLAG_SHARED_MSIX);
96111447b59SVenkatesh Srinivas 	}
96211447b59SVenkatesh Srinivas 
96311447b59SVenkatesh Srinivas 	for (i = 0; i < 1 + VIRTIO_MAX_VIRTQUEUES; i++) {
96411447b59SVenkatesh Srinivas 		ires = &sc->vtpci_intr_res[i];
96511447b59SVenkatesh Srinivas 
96611447b59SVenkatesh Srinivas 		if (ires->intrhand != NULL) {
96711447b59SVenkatesh Srinivas 			bus_teardown_intr(dev, ires->irq, ires->intrhand);
96811447b59SVenkatesh Srinivas 			ires->intrhand = NULL;
96911447b59SVenkatesh Srinivas 		}
97011447b59SVenkatesh Srinivas 
97111447b59SVenkatesh Srinivas 		if (ires->irq != NULL) {
97211447b59SVenkatesh Srinivas 			bus_release_resource(dev, SYS_RES_IRQ, ires->rid,
97311447b59SVenkatesh Srinivas 			    ires->irq);
97411447b59SVenkatesh Srinivas 			ires->irq = NULL;
97511447b59SVenkatesh Srinivas 		}
97611447b59SVenkatesh Srinivas 
97711447b59SVenkatesh Srinivas 		ires->rid = -1;
97811447b59SVenkatesh Srinivas 	}
97911447b59SVenkatesh Srinivas }
98011447b59SVenkatesh Srinivas 
98111447b59SVenkatesh Srinivas static void
98211447b59SVenkatesh Srinivas vtpci_free_virtqueues(struct vtpci_softc *sc)
98311447b59SVenkatesh Srinivas {
98411447b59SVenkatesh Srinivas 	struct vtpci_virtqueue *vqx;
98511447b59SVenkatesh Srinivas 	int i;
98611447b59SVenkatesh Srinivas 
98711447b59SVenkatesh Srinivas 	sc->vtpci_nvqs = 0;
98811447b59SVenkatesh Srinivas 
98911447b59SVenkatesh Srinivas 	for (i = 0; i < VIRTIO_MAX_VIRTQUEUES; i++) {
99011447b59SVenkatesh Srinivas 		vqx = &sc->vtpci_vqx[i];
99111447b59SVenkatesh Srinivas 
99211447b59SVenkatesh Srinivas 		if (vqx->vq != NULL) {
99311447b59SVenkatesh Srinivas 			virtqueue_free(vqx->vq);
99411447b59SVenkatesh Srinivas 			vqx->vq = NULL;
99511447b59SVenkatesh Srinivas 		}
99611447b59SVenkatesh Srinivas 	}
99711447b59SVenkatesh Srinivas }
99811447b59SVenkatesh Srinivas 
99911447b59SVenkatesh Srinivas static void
100011447b59SVenkatesh Srinivas vtpci_release_child_resources(struct vtpci_softc *sc)
100111447b59SVenkatesh Srinivas {
100211447b59SVenkatesh Srinivas 
100311447b59SVenkatesh Srinivas 	vtpci_free_interrupts(sc);
100411447b59SVenkatesh Srinivas 	vtpci_free_virtqueues(sc);
100511447b59SVenkatesh Srinivas }
100611447b59SVenkatesh Srinivas 
100711447b59SVenkatesh Srinivas static void
100811447b59SVenkatesh Srinivas vtpci_reset(struct vtpci_softc *sc)
100911447b59SVenkatesh Srinivas {
101011447b59SVenkatesh Srinivas 
101111447b59SVenkatesh Srinivas 	/*
101211447b59SVenkatesh Srinivas 	 * Setting the status to RESET sets the host device to
101311447b59SVenkatesh Srinivas 	 * the original, uninitialized state.
101411447b59SVenkatesh Srinivas 	 */
101511447b59SVenkatesh Srinivas 	vtpci_set_status(sc->vtpci_dev, VIRTIO_CONFIG_STATUS_RESET);
101611447b59SVenkatesh Srinivas }
101711447b59SVenkatesh Srinivas 
101811447b59SVenkatesh Srinivas static int
101911447b59SVenkatesh Srinivas vtpci_legacy_intr(void *xsc)
102011447b59SVenkatesh Srinivas {
102111447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
102211447b59SVenkatesh Srinivas 	struct vtpci_virtqueue *vqx;
102311447b59SVenkatesh Srinivas 	int i;
102411447b59SVenkatesh Srinivas 	uint8_t isr;
102511447b59SVenkatesh Srinivas 
102611447b59SVenkatesh Srinivas 	sc = xsc;
102711447b59SVenkatesh Srinivas 	vqx = &sc->vtpci_vqx[0];
102811447b59SVenkatesh Srinivas 
102911447b59SVenkatesh Srinivas 	/* Reading the ISR also clears it. */
103011447b59SVenkatesh Srinivas 	isr = vtpci_read_config_1(sc, VIRTIO_PCI_ISR);
103111447b59SVenkatesh Srinivas 
103211447b59SVenkatesh Srinivas 	if (isr & VIRTIO_PCI_ISR_CONFIG)
103311447b59SVenkatesh Srinivas 		vtpci_config_intr(sc);
103411447b59SVenkatesh Srinivas 
103511447b59SVenkatesh Srinivas 	if (isr & VIRTIO_PCI_ISR_INTR)
103611447b59SVenkatesh Srinivas 		for (i = 0; i < sc->vtpci_nvqs; i++, vqx++)
103711447b59SVenkatesh Srinivas 			virtqueue_intr(vqx->vq);
103811447b59SVenkatesh Srinivas 
103911447b59SVenkatesh Srinivas 	return isr;
104011447b59SVenkatesh Srinivas }
104111447b59SVenkatesh Srinivas 
104211447b59SVenkatesh Srinivas static int
104311447b59SVenkatesh Srinivas vtpci_vq_shared_intr(void *xsc)
104411447b59SVenkatesh Srinivas {
104511447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
104611447b59SVenkatesh Srinivas 	struct vtpci_virtqueue *vqx;
104711447b59SVenkatesh Srinivas 	int i, rc;
104811447b59SVenkatesh Srinivas 
104911447b59SVenkatesh Srinivas 	rc = 0;
105011447b59SVenkatesh Srinivas 	sc = xsc;
105111447b59SVenkatesh Srinivas 	vqx = &sc->vtpci_vqx[0];
105211447b59SVenkatesh Srinivas 
105311447b59SVenkatesh Srinivas 	for (i = 0; i < sc->vtpci_nvqs; i++, vqx++)
105411447b59SVenkatesh Srinivas 		rc |= virtqueue_intr(vqx->vq);
105511447b59SVenkatesh Srinivas 
105611447b59SVenkatesh Srinivas 	return rc;
105711447b59SVenkatesh Srinivas }
105811447b59SVenkatesh Srinivas 
105911447b59SVenkatesh Srinivas static int
106011447b59SVenkatesh Srinivas vtpci_vq_intr(void *xvq)
106111447b59SVenkatesh Srinivas {
106211447b59SVenkatesh Srinivas 	struct virtqueue *vq;
106311447b59SVenkatesh Srinivas 	int rc;
106411447b59SVenkatesh Srinivas 
106511447b59SVenkatesh Srinivas 	vq = xvq;
106611447b59SVenkatesh Srinivas 	rc = virtqueue_intr(vq);
106711447b59SVenkatesh Srinivas 
106811447b59SVenkatesh Srinivas 	return rc;
106911447b59SVenkatesh Srinivas }
107011447b59SVenkatesh Srinivas 
107111447b59SVenkatesh Srinivas static int
107211447b59SVenkatesh Srinivas vtpci_config_intr(void *xsc)
107311447b59SVenkatesh Srinivas {
107411447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
107511447b59SVenkatesh Srinivas 	device_t child;
107611447b59SVenkatesh Srinivas 	int rc;
107711447b59SVenkatesh Srinivas 
107811447b59SVenkatesh Srinivas 	rc = 0;
107911447b59SVenkatesh Srinivas 	sc = xsc;
108011447b59SVenkatesh Srinivas 	child = sc->vtpci_child_dev;
108111447b59SVenkatesh Srinivas 
108211447b59SVenkatesh Srinivas 	if (child != NULL)
108311447b59SVenkatesh Srinivas 		rc = VIRTIO_CONFIG_CHANGE(child);
108411447b59SVenkatesh Srinivas 
108511447b59SVenkatesh Srinivas 	return rc;
108611447b59SVenkatesh Srinivas }
1087