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