1*0345cf90Schristos /* $NetBSD: virtio_pci.c,v 1.55 2024/09/25 17:12:47 christos Exp $ */ 2da413d61Scherry 3da413d61Scherry /* 403c5faf6Sreinoud * Copyright (c) 2020 The NetBSD Foundation, Inc. 503c5faf6Sreinoud * Copyright (c) 2012 Stefan Fritsch. 6da413d61Scherry * Copyright (c) 2010 Minoura Makoto. 7da413d61Scherry * All rights reserved. 8da413d61Scherry * 9da413d61Scherry * Redistribution and use in source and binary forms, with or without 10da413d61Scherry * modification, are permitted provided that the following conditions 11da413d61Scherry * are met: 12da413d61Scherry * 1. Redistributions of source code must retain the above copyright 13da413d61Scherry * notice, this list of conditions and the following disclaimer. 14da413d61Scherry * 2. Redistributions in binary form must reproduce the above copyright 15da413d61Scherry * notice, this list of conditions and the following disclaimer in the 16da413d61Scherry * documentation and/or other materials provided with the distribution. 17da413d61Scherry * 18da413d61Scherry * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19da413d61Scherry * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20da413d61Scherry * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21da413d61Scherry * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22da413d61Scherry * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23da413d61Scherry * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24da413d61Scherry * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25da413d61Scherry * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26da413d61Scherry * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27da413d61Scherry * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28da413d61Scherry */ 29da413d61Scherry 30da413d61Scherry #include <sys/cdefs.h> 31*0345cf90Schristos __KERNEL_RCSID(0, "$NetBSD: virtio_pci.c,v 1.55 2024/09/25 17:12:47 christos Exp $"); 32da413d61Scherry 33da413d61Scherry #include <sys/param.h> 34f1cc1c63Sriastradh #include <sys/types.h> 35da413d61Scherry 36da413d61Scherry #include <sys/device.h> 37f1cc1c63Sriastradh #include <sys/endian.h> 38f1cc1c63Sriastradh #include <sys/interrupt.h> 39f1cc1c63Sriastradh #include <sys/kmem.h> 40f1cc1c63Sriastradh #include <sys/module.h> 41f1cc1c63Sriastradh #include <sys/syslog.h> 42f1cc1c63Sriastradh #include <sys/systm.h> 43da413d61Scherry 44da413d61Scherry #include <dev/pci/pcidevs.h> 45da413d61Scherry #include <dev/pci/pcireg.h> 46da413d61Scherry #include <dev/pci/pcivar.h> 47da413d61Scherry 48da413d61Scherry #include <dev/pci/virtioreg.h> /* XXX: move to non-pci */ 4903c5faf6Sreinoud #include <dev/pci/virtio_pcireg.h> 5003c5faf6Sreinoud 5103c5faf6Sreinoud #define VIRTIO_PRIVATE 52da413d61Scherry #include <dev/pci/virtiovar.h> /* XXX: move to non-pci */ 53da413d61Scherry 544eae4625Sthorpej #if defined(__alpha__) || defined(__sparc64__) 554eae4625Sthorpej /* 564eae4625Sthorpej * XXX VIRTIO_F_ACCESS_PLATFORM is required for standard PCI DMA 574eae4625Sthorpej * XXX to work on these platforms, at least by Qemu. 584eae4625Sthorpej * XXX 594eae4625Sthorpej * XXX Generalize this later. 604eae4625Sthorpej */ 614eae4625Sthorpej #define __NEED_VIRTIO_F_ACCESS_PLATFORM 624eae4625Sthorpej #endif /* __alpha__ || __sparc64__ */ 6303c5faf6Sreinoud 64c6fd71d9Syamaguchi #define VIRTIO_PCI_LOG(_sc, _use_log, _fmt, _args...) \ 65c6fd71d9Syamaguchi do { \ 66c6fd71d9Syamaguchi if ((_use_log)) { \ 67c6fd71d9Syamaguchi log(LOG_DEBUG, "%s: " _fmt, \ 68c6fd71d9Syamaguchi device_xname((_sc)->sc_dev), \ 69c6fd71d9Syamaguchi ##_args); \ 70c6fd71d9Syamaguchi } else { \ 71c6fd71d9Syamaguchi aprint_error_dev((_sc)->sc_dev, \ 72c6fd71d9Syamaguchi _fmt, ##_args); \ 73c6fd71d9Syamaguchi } \ 74c6fd71d9Syamaguchi } while(0) 75c6fd71d9Syamaguchi 761a7766abSjakllsch static int virtio_pci_match(device_t, cfdata_t, void *); 771a7766abSjakllsch static void virtio_pci_attach(device_t, device_t, void *); 781a7766abSjakllsch static int virtio_pci_rescan(device_t, const char *, const int *); 791a7766abSjakllsch static int virtio_pci_detach(device_t, int); 801a7766abSjakllsch 818647d884Sreinoud #define NMAPREG ((PCI_MAPREG_END - PCI_MAPREG_START) / \ 828647d884Sreinoud sizeof(pcireg_t)) 831a7766abSjakllsch struct virtio_pci_softc { 841a7766abSjakllsch struct virtio_softc sc_sc; 8586598168Syamaguchi bool sc_intr_pervq; 8603c5faf6Sreinoud 8703c5faf6Sreinoud /* IO space */ 881a7766abSjakllsch bus_space_tag_t sc_iot; 891a7766abSjakllsch bus_space_handle_t sc_ioh; 901a7766abSjakllsch bus_size_t sc_iosize; 9103c5faf6Sreinoud 9203c5faf6Sreinoud /* BARs */ 93cf7c091fSreinoud bus_space_tag_t sc_bars_iot[NMAPREG]; 94cf7c091fSreinoud bus_space_handle_t sc_bars_ioh[NMAPREG]; 95cf7c091fSreinoud bus_size_t sc_bars_iosize[NMAPREG]; 9603c5faf6Sreinoud 9703c5faf6Sreinoud /* notify space */ 9803c5faf6Sreinoud bus_space_tag_t sc_notify_iot; 9903c5faf6Sreinoud bus_space_handle_t sc_notify_ioh; 10003c5faf6Sreinoud bus_size_t sc_notify_iosize; 10103c5faf6Sreinoud uint32_t sc_notify_off_multiplier; 10203c5faf6Sreinoud 10303c5faf6Sreinoud /* isr space */ 10403c5faf6Sreinoud bus_space_tag_t sc_isr_iot; 10503c5faf6Sreinoud bus_space_handle_t sc_isr_ioh; 10603c5faf6Sreinoud bus_size_t sc_isr_iosize; 10703c5faf6Sreinoud 10803c5faf6Sreinoud /* generic */ 1091a7766abSjakllsch struct pci_attach_args sc_pa; 1101a7766abSjakllsch pci_intr_handle_t *sc_ihp; 1111a7766abSjakllsch void **sc_ihs; 1121a7766abSjakllsch int sc_ihs_num; 11303c5faf6Sreinoud int sc_devcfg_offset; /* for 0.9 */ 1141a7766abSjakllsch }; 1151a7766abSjakllsch 11603c5faf6Sreinoud static int virtio_pci_attach_09(device_t, void *); 11703c5faf6Sreinoud static void virtio_pci_kick_09(struct virtio_softc *, uint16_t); 11803c5faf6Sreinoud static uint16_t virtio_pci_read_queue_size_09(struct virtio_softc *, uint16_t); 1196691b5f9Sriastradh static void virtio_pci_setup_queue_09(struct virtio_softc *, uint16_t, 1206691b5f9Sriastradh uint64_t); 12103c5faf6Sreinoud static void virtio_pci_set_status_09(struct virtio_softc *, int); 1226691b5f9Sriastradh static void virtio_pci_negotiate_features_09(struct virtio_softc *, 1236691b5f9Sriastradh uint64_t); 12403c5faf6Sreinoud 12503c5faf6Sreinoud static int virtio_pci_attach_10(device_t, void *); 12603c5faf6Sreinoud static void virtio_pci_kick_10(struct virtio_softc *, uint16_t); 12703c5faf6Sreinoud static uint16_t virtio_pci_read_queue_size_10(struct virtio_softc *, uint16_t); 1286691b5f9Sriastradh static void virtio_pci_setup_queue_10(struct virtio_softc *, uint16_t, 1296691b5f9Sriastradh uint64_t); 13003c5faf6Sreinoud static void virtio_pci_set_status_10(struct virtio_softc *, int); 1316691b5f9Sriastradh static void virtio_pci_negotiate_features_10(struct virtio_softc *, 1326691b5f9Sriastradh uint64_t); 1336691b5f9Sriastradh static int virtio_pci_find_cap(struct virtio_pci_softc *, int, void *, 1346691b5f9Sriastradh int); 13503c5faf6Sreinoud 1362b9a3babSyamaguchi static int virtio_pci_alloc_interrupts(struct virtio_softc *); 1371a7766abSjakllsch static void virtio_pci_free_interrupts(struct virtio_softc *); 1386691b5f9Sriastradh static int virtio_pci_adjust_config_region(struct virtio_pci_softc *); 1396691b5f9Sriastradh static int virtio_pci_intr(void *); 1401a7766abSjakllsch static int virtio_pci_msix_queue_intr(void *); 1411a7766abSjakllsch static int virtio_pci_msix_config_intr(void *); 142243e58f6Syamaguchi static int virtio_pci_setup_interrupts_09(struct virtio_softc *, int); 143243e58f6Syamaguchi static int virtio_pci_setup_interrupts_10(struct virtio_softc *, int); 1442b9a3babSyamaguchi static int virtio_pci_establish_msix_interrupts(struct virtio_softc *, 1457a157899Sriastradh const struct pci_attach_args *); 1462b9a3babSyamaguchi static int virtio_pci_establish_intx_interrupt(struct virtio_softc *, 1477a157899Sriastradh const struct pci_attach_args *); 1482b9a3babSyamaguchi static bool virtio_pci_msix_enabled(struct virtio_pci_softc *); 1491a7766abSjakllsch 1501a7766abSjakllsch #define VIRTIO_MSIX_CONFIG_VECTOR_INDEX 0 1511a7766abSjakllsch #define VIRTIO_MSIX_QUEUE_VECTOR_INDEX 1 1521a7766abSjakllsch 153e225d1d2Sreinoud /* 1549c018325Srin * For big-endian aarch64/armv7 on QEMU (and most real HW), only CPU cores 1559c018325Srin * are running in big-endian mode, with all peripheral being configured to 1569c018325Srin * little-endian mode. Their default bus_space(9) functions forcibly swap 1579c018325Srin * byte-order. This guarantees that PIO'ed data from pci(4), e.g., are 1589c018325Srin * correctly handled by bus_space(9), while DMA'ed ones should be swapped 1599c018325Srin * by hand, in violation of virtio(4) specifications. 160e225d1d2Sreinoud */ 1611a7766abSjakllsch 1629c018325Srin #if (defined(__aarch64__) || defined(__arm__)) && BYTE_ORDER == BIG_ENDIAN 1639c018325Srin # define READ_ENDIAN_09 BIG_ENDIAN 164e225d1d2Sreinoud # define READ_ENDIAN_10 BIG_ENDIAN 165e225d1d2Sreinoud # define STRUCT_ENDIAN_09 BIG_ENDIAN 166e225d1d2Sreinoud # define STRUCT_ENDIAN_10 LITTLE_ENDIAN 167e225d1d2Sreinoud #elif BYTE_ORDER == BIG_ENDIAN 168e225d1d2Sreinoud # define READ_ENDIAN_09 LITTLE_ENDIAN 169e225d1d2Sreinoud # define READ_ENDIAN_10 BIG_ENDIAN 170e225d1d2Sreinoud # define STRUCT_ENDIAN_09 BIG_ENDIAN 171e225d1d2Sreinoud # define STRUCT_ENDIAN_10 LITTLE_ENDIAN 172e225d1d2Sreinoud #else /* little endian */ 173e225d1d2Sreinoud # define READ_ENDIAN_09 LITTLE_ENDIAN 174e225d1d2Sreinoud # define READ_ENDIAN_10 LITTLE_ENDIAN 175e225d1d2Sreinoud # define STRUCT_ENDIAN_09 LITTLE_ENDIAN 176e225d1d2Sreinoud # define STRUCT_ENDIAN_10 LITTLE_ENDIAN 17703c5faf6Sreinoud #endif 17803c5faf6Sreinoud 1791a7766abSjakllsch CFATTACH_DECL3_NEW(virtio_pci, sizeof(struct virtio_pci_softc), 1801a7766abSjakllsch virtio_pci_match, virtio_pci_attach, virtio_pci_detach, NULL, 18112dd7759Sriastradh virtio_pci_rescan, NULL, 0); 1821a7766abSjakllsch 18303c5faf6Sreinoud static const struct virtio_ops virtio_pci_ops_09 = { 18403c5faf6Sreinoud .kick = virtio_pci_kick_09, 18503c5faf6Sreinoud .read_queue_size = virtio_pci_read_queue_size_09, 18603c5faf6Sreinoud .setup_queue = virtio_pci_setup_queue_09, 18703c5faf6Sreinoud .set_status = virtio_pci_set_status_09, 18803c5faf6Sreinoud .neg_features = virtio_pci_negotiate_features_09, 1892b9a3babSyamaguchi .alloc_interrupts = virtio_pci_alloc_interrupts, 19003c5faf6Sreinoud .free_interrupts = virtio_pci_free_interrupts, 1912b9a3babSyamaguchi .setup_interrupts = virtio_pci_setup_interrupts_09, 19203c5faf6Sreinoud }; 19303c5faf6Sreinoud 19403c5faf6Sreinoud static const struct virtio_ops virtio_pci_ops_10 = { 19503c5faf6Sreinoud .kick = virtio_pci_kick_10, 19603c5faf6Sreinoud .read_queue_size = virtio_pci_read_queue_size_10, 19703c5faf6Sreinoud .setup_queue = virtio_pci_setup_queue_10, 19803c5faf6Sreinoud .set_status = virtio_pci_set_status_10, 19903c5faf6Sreinoud .neg_features = virtio_pci_negotiate_features_10, 2002b9a3babSyamaguchi .alloc_interrupts = virtio_pci_alloc_interrupts, 2011a7766abSjakllsch .free_interrupts = virtio_pci_free_interrupts, 2022b9a3babSyamaguchi .setup_interrupts = virtio_pci_setup_interrupts_10, 2031a7766abSjakllsch }; 204da413d61Scherry 205da413d61Scherry static int 2061a7766abSjakllsch virtio_pci_match(device_t parent, cfdata_t match, void *aux) 207da413d61Scherry { 2087a157899Sriastradh const struct pci_attach_args * const pa = aux; 209da413d61Scherry 210da413d61Scherry switch (PCI_VENDOR(pa->pa_id)) { 211da413d61Scherry case PCI_VENDOR_QUMRANET: 212331aee3eSuwe /* Transitional devices MUST have a PCI Revision ID of 0. */ 21303c5faf6Sreinoud if (((PCI_PRODUCT_QUMRANET_VIRTIO_1000 <= 214da413d61Scherry PCI_PRODUCT(pa->pa_id)) && 215da413d61Scherry (PCI_PRODUCT(pa->pa_id) <= 21603c5faf6Sreinoud PCI_PRODUCT_QUMRANET_VIRTIO_103F)) && 21703c5faf6Sreinoud PCI_REVISION(pa->pa_class) == 0) 21803c5faf6Sreinoud return 1; 219331aee3eSuwe /* 220331aee3eSuwe * Non-transitional devices SHOULD have a PCI Revision 221331aee3eSuwe * ID of 1 or higher. Drivers MUST match any PCI 222331aee3eSuwe * Revision ID value. 223331aee3eSuwe */ 22403c5faf6Sreinoud if (((PCI_PRODUCT_QUMRANET_VIRTIO_1040 <= 22503c5faf6Sreinoud PCI_PRODUCT(pa->pa_id)) && 22603c5faf6Sreinoud (PCI_PRODUCT(pa->pa_id) <= 22703c5faf6Sreinoud PCI_PRODUCT_QUMRANET_VIRTIO_107F)) && 228331aee3eSuwe /* XXX: TODO */ 22903c5faf6Sreinoud PCI_REVISION(pa->pa_class) == 1) 230da413d61Scherry return 1; 231da413d61Scherry break; 232da413d61Scherry } 233da413d61Scherry 234da413d61Scherry return 0; 235da413d61Scherry } 236da413d61Scherry 237da413d61Scherry static void 2381a7766abSjakllsch virtio_pci_attach(device_t parent, device_t self, void *aux) 239da413d61Scherry { 2401a7766abSjakllsch struct virtio_pci_softc * const psc = device_private(self); 2411a7766abSjakllsch struct virtio_softc * const sc = &psc->sc_sc; 2427a157899Sriastradh const struct pci_attach_args * const pa = aux; 243da413d61Scherry pci_chipset_tag_t pc = pa->pa_pc; 244da413d61Scherry pcitag_t tag = pa->pa_tag; 245da413d61Scherry int revision; 24603c5faf6Sreinoud int ret; 247da413d61Scherry pcireg_t id; 2486ef25358Suwe pcireg_t csr; 249da413d61Scherry 250da413d61Scherry revision = PCI_REVISION(pa->pa_class); 25103c5faf6Sreinoud switch (revision) { 25203c5faf6Sreinoud case 0: 25303c5faf6Sreinoud /* subsystem ID shows what I am */ 25403c5faf6Sreinoud id = PCI_SUBSYS_ID(pci_conf_read(pc, tag, PCI_SUBSYS_ID_REG)); 25503c5faf6Sreinoud break; 25603c5faf6Sreinoud case 1: 25703c5faf6Sreinoud /* pci product number shows what I am */ 25803c5faf6Sreinoud id = PCI_PRODUCT(pa->pa_id) - PCI_PRODUCT_QUMRANET_VIRTIO_1040; 25903c5faf6Sreinoud break; 26003c5faf6Sreinoud default: 261da413d61Scherry aprint_normal(": unknown revision 0x%02x; giving up\n", 262da413d61Scherry revision); 263da413d61Scherry return; 264da413d61Scherry } 26503c5faf6Sreinoud 266da413d61Scherry aprint_normal("\n"); 267da413d61Scherry aprint_naive("\n"); 26803c5faf6Sreinoud virtio_print_device_type(self, id, revision); 269da413d61Scherry 2706ef25358Suwe csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG); 2716ef25358Suwe csr |= PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_IO_ENABLE; 2726ef25358Suwe pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr); 2736ef25358Suwe 274da413d61Scherry sc->sc_dev = self; 2751a7766abSjakllsch psc->sc_pa = *pa; 2761a7766abSjakllsch psc->sc_iot = pa->pa_iot; 27703c5faf6Sreinoud 27803c5faf6Sreinoud sc->sc_dmat = pa->pa_dmat; 279da413d61Scherry if (pci_dma64_available(pa)) 280da413d61Scherry sc->sc_dmat = pa->pa_dmat64; 281da413d61Scherry 28203c5faf6Sreinoud /* attach is dependent on revision */ 28303c5faf6Sreinoud ret = 0; 28403c5faf6Sreinoud if (revision == 1) { 28503c5faf6Sreinoud /* try to attach 1.0 */ 28603c5faf6Sreinoud ret = virtio_pci_attach_10(self, aux); 28703c5faf6Sreinoud } 28803c5faf6Sreinoud if (ret == 0 && revision == 0) { 2894eae4625Sthorpej /* 2904eae4625Sthorpej * revision 0 means 0.9 only or both 0.9 and 1.0. The 2914eae4625Sthorpej * latter are so-called "Transitional Devices". For 2924eae4625Sthorpej * those devices, we want to use the 1.0 interface if 2934eae4625Sthorpej * possible. 2944eae4625Sthorpej * 2954eae4625Sthorpej * XXX Currently only on platforms that require 1.0 2964eae4625Sthorpej * XXX features, such as VIRTIO_F_ACCESS_PLATFORM. 2974eae4625Sthorpej */ 2984eae4625Sthorpej #ifdef __NEED_VIRTIO_F_ACCESS_PLATFORM 2994eae4625Sthorpej /* First, try to attach 1.0 */ 3004eae4625Sthorpej ret = virtio_pci_attach_10(self, aux); 3014eae4625Sthorpej if (ret != 0) { 3024eae4625Sthorpej aprint_error_dev(self, 3034eae4625Sthorpej "VirtIO 1.0 error = %d, falling back to 0.9\n", 3044eae4625Sthorpej ret); 3054eae4625Sthorpej /* Fall back to 0.9. */ 30603c5faf6Sreinoud ret = virtio_pci_attach_09(self, aux); 30703c5faf6Sreinoud } 3084eae4625Sthorpej #else 3094eae4625Sthorpej ret = virtio_pci_attach_09(self, aux); 3104eae4625Sthorpej #endif /* __NEED_VIRTIO_F_ACCESS_PLATFORM */ 3114eae4625Sthorpej } 31203c5faf6Sreinoud if (ret) { 31303c5faf6Sreinoud aprint_error_dev(self, "cannot attach (%d)\n", ret); 314da413d61Scherry return; 315da413d61Scherry } 31603c5faf6Sreinoud KASSERT(sc->sc_ops); 317da413d61Scherry 31803c5faf6Sreinoud /* preset config region */ 31903c5faf6Sreinoud psc->sc_devcfg_offset = VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI; 32003c5faf6Sreinoud if (virtio_pci_adjust_config_region(psc)) 32103c5faf6Sreinoud return; 32203c5faf6Sreinoud 32303c5faf6Sreinoud /* generic */ 324da413d61Scherry virtio_device_reset(sc); 325da413d61Scherry virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_ACK); 326da413d61Scherry virtio_set_status(sc, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER); 327da413d61Scherry 32803c5faf6Sreinoud sc->sc_childdevid = id; 329da413d61Scherry sc->sc_child = NULL; 3302685996bSthorpej virtio_pci_rescan(self, NULL, NULL); 331da413d61Scherry return; 332da413d61Scherry } 333da413d61Scherry 334da413d61Scherry /* ARGSUSED */ 335da413d61Scherry static int 3362685996bSthorpej virtio_pci_rescan(device_t self, const char *ifattr, const int *locs) 337da413d61Scherry { 3381a7766abSjakllsch struct virtio_pci_softc * const psc = device_private(self); 3391a7766abSjakllsch struct virtio_softc * const sc = &psc->sc_sc; 340da413d61Scherry struct virtio_attach_args va; 341da413d61Scherry 342da413d61Scherry if (sc->sc_child) /* Child already attached? */ 343da413d61Scherry return 0; 344da413d61Scherry 345da413d61Scherry memset(&va, 0, sizeof(va)); 346da413d61Scherry va.sc_childdevid = sc->sc_childdevid; 347da413d61Scherry 348c7fb772bSthorpej config_found(self, &va, NULL, CFARGS_NONE); 349da413d61Scherry 35003c5faf6Sreinoud if (virtio_attach_failed(sc)) 351da413d61Scherry return 0; 352da413d61Scherry 353da413d61Scherry return 0; 354da413d61Scherry } 355da413d61Scherry 356da413d61Scherry static int 3571a7766abSjakllsch virtio_pci_detach(device_t self, int flags) 358da413d61Scherry { 3591a7766abSjakllsch struct virtio_pci_softc * const psc = device_private(self); 3601a7766abSjakllsch struct virtio_softc * const sc = &psc->sc_sc; 3612932e126Sriastradh unsigned i; 362da413d61Scherry int r; 363da413d61Scherry 36437b0b4baSyamaguchi r = config_detach_children(self, flags); 36537b0b4baSyamaguchi if (r != 0) 366da413d61Scherry return r; 367da413d61Scherry 368a536f00aSriastradh /* Check that child never attached, or detached properly */ 369c2646fd4Syamaguchi KASSERT(sc->sc_child == NULL); 370da413d61Scherry KASSERT(sc->sc_vqs == NULL); 3711a7766abSjakllsch KASSERT(psc->sc_ihs_num == 0); 372da413d61Scherry 3732932e126Sriastradh if (sc->sc_version_1) { 3742932e126Sriastradh for (i = 0; i < __arraycount(psc->sc_bars_iot); i++) { 3752932e126Sriastradh if (psc->sc_bars_iosize[i] == 0) 3762932e126Sriastradh continue; 3772932e126Sriastradh bus_space_unmap(psc->sc_bars_iot[i], 3782932e126Sriastradh psc->sc_bars_ioh[i], psc->sc_bars_iosize[i]); 3792932e126Sriastradh psc->sc_bars_iosize[i] = 0; 3802932e126Sriastradh } 3812932e126Sriastradh } else { 3822932e126Sriastradh if (psc->sc_iosize) { 38303c5faf6Sreinoud bus_space_unmap(psc->sc_iot, psc->sc_ioh, 384be8d2e7cSriastradh psc->sc_iosize); 3851a7766abSjakllsch psc->sc_iosize = 0; 3862932e126Sriastradh } 3872932e126Sriastradh } 388da413d61Scherry 389da413d61Scherry return 0; 390da413d61Scherry } 3911a7766abSjakllsch 39203c5faf6Sreinoud static int 39303c5faf6Sreinoud virtio_pci_attach_09(device_t self, void *aux) 39403c5faf6Sreinoud { 39503c5faf6Sreinoud struct virtio_pci_softc * const psc = device_private(self); 3967a157899Sriastradh const struct pci_attach_args * const pa = aux; 39703c5faf6Sreinoud struct virtio_softc * const sc = &psc->sc_sc; 39803c5faf6Sreinoud 39903c5faf6Sreinoud /* complete IO region */ 40003c5faf6Sreinoud if (pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_TYPE_IO, 0, 40103c5faf6Sreinoud &psc->sc_iot, &psc->sc_ioh, NULL, &psc->sc_iosize)) { 40203c5faf6Sreinoud aprint_error_dev(self, "can't map i/o space\n"); 40303c5faf6Sreinoud return EIO; 40403c5faf6Sreinoud } 40503c5faf6Sreinoud 40603c5faf6Sreinoud /* queue space */ 40703c5faf6Sreinoud if (bus_space_subregion(psc->sc_iot, psc->sc_ioh, 40803c5faf6Sreinoud VIRTIO_CONFIG_QUEUE_NOTIFY, 2, &psc->sc_notify_ioh)) { 40903c5faf6Sreinoud aprint_error_dev(self, "can't map notify i/o space\n"); 41003c5faf6Sreinoud return EIO; 41103c5faf6Sreinoud } 41203c5faf6Sreinoud psc->sc_notify_iosize = 2; 41303c5faf6Sreinoud psc->sc_notify_iot = psc->sc_iot; 41403c5faf6Sreinoud 41503c5faf6Sreinoud /* ISR space */ 41603c5faf6Sreinoud if (bus_space_subregion(psc->sc_iot, psc->sc_ioh, 41703c5faf6Sreinoud VIRTIO_CONFIG_ISR_STATUS, 1, &psc->sc_isr_ioh)) { 41803c5faf6Sreinoud aprint_error_dev(self, "can't map isr i/o space\n"); 41903c5faf6Sreinoud return EIO; 42003c5faf6Sreinoud } 42103c5faf6Sreinoud psc->sc_isr_iosize = 1; 42203c5faf6Sreinoud psc->sc_isr_iot = psc->sc_iot; 42303c5faf6Sreinoud 42403c5faf6Sreinoud /* set our version 0.9 ops */ 42503c5faf6Sreinoud sc->sc_ops = &virtio_pci_ops_09; 426e225d1d2Sreinoud sc->sc_bus_endian = READ_ENDIAN_09; 427e225d1d2Sreinoud sc->sc_struct_endian = STRUCT_ENDIAN_09; 42803c5faf6Sreinoud return 0; 4291a7766abSjakllsch } 4301a7766abSjakllsch 43103c5faf6Sreinoud static int 43203c5faf6Sreinoud virtio_pci_attach_10(device_t self, void *aux) 43303c5faf6Sreinoud { 43403c5faf6Sreinoud struct virtio_pci_softc * const psc = device_private(self); 4357a157899Sriastradh const struct pci_attach_args * const pa = aux; 43603c5faf6Sreinoud struct virtio_softc * const sc = &psc->sc_sc; 4377a157899Sriastradh const pci_chipset_tag_t pc = pa->pa_pc; 4387a157899Sriastradh const pcitag_t tag = pa->pa_tag; 43903c5faf6Sreinoud 44003c5faf6Sreinoud struct virtio_pci_cap common, isr, device; 44103c5faf6Sreinoud struct virtio_pci_notify_cap notify; 44203c5faf6Sreinoud int have_device_cfg = 0; 44303c5faf6Sreinoud bus_size_t bars[NMAPREG] = { 0 }; 44403c5faf6Sreinoud int bars_idx[NMAPREG] = { 0 }; 4456691b5f9Sriastradh struct virtio_pci_cap * const caps[] = 4466691b5f9Sriastradh { &common, &isr, &device, ¬ify.cap }; 4478ccb2f41Sreinoud int i, j, ret = 0; 44803c5faf6Sreinoud 44903c5faf6Sreinoud if (virtio_pci_find_cap(psc, VIRTIO_PCI_CAP_COMMON_CFG, 45003c5faf6Sreinoud &common, sizeof(common))) 45103c5faf6Sreinoud return ENODEV; 45203c5faf6Sreinoud if (virtio_pci_find_cap(psc, VIRTIO_PCI_CAP_NOTIFY_CFG, 45303c5faf6Sreinoud ¬ify, sizeof(notify))) 45403c5faf6Sreinoud return ENODEV; 45503c5faf6Sreinoud if (virtio_pci_find_cap(psc, VIRTIO_PCI_CAP_ISR_CFG, 45603c5faf6Sreinoud &isr, sizeof(isr))) 45703c5faf6Sreinoud return ENODEV; 45803c5faf6Sreinoud if (virtio_pci_find_cap(psc, VIRTIO_PCI_CAP_DEVICE_CFG, 45903c5faf6Sreinoud &device, sizeof(device))) 46003c5faf6Sreinoud memset(&device, 0, sizeof(device)); 46103c5faf6Sreinoud else 46203c5faf6Sreinoud have_device_cfg = 1; 46303c5faf6Sreinoud 46403c5faf6Sreinoud /* Figure out which bars we need to map */ 46503c5faf6Sreinoud for (i = 0; i < __arraycount(caps); i++) { 46603c5faf6Sreinoud int bar = caps[i]->bar; 46703c5faf6Sreinoud bus_size_t len = caps[i]->offset + caps[i]->length; 4686691b5f9Sriastradh 46903c5faf6Sreinoud if (caps[i]->length == 0) 47003c5faf6Sreinoud continue; 47103c5faf6Sreinoud if (bars[bar] < len) 47203c5faf6Sreinoud bars[bar] = len; 47303c5faf6Sreinoud } 47403c5faf6Sreinoud 4758ccb2f41Sreinoud for (i = j = 0; i < __arraycount(bars); i++) { 47603c5faf6Sreinoud int reg; 47703c5faf6Sreinoud pcireg_t type; 4786691b5f9Sriastradh 47903c5faf6Sreinoud if (bars[i] == 0) 48003c5faf6Sreinoud continue; 4815c30743aSuwe reg = PCI_BAR(i); 48203c5faf6Sreinoud type = pci_mapreg_type(pc, tag, reg); 48303c5faf6Sreinoud if (pci_mapreg_map(pa, reg, type, 0, 48403c5faf6Sreinoud &psc->sc_bars_iot[j], &psc->sc_bars_ioh[j], 48503c5faf6Sreinoud NULL, &psc->sc_bars_iosize[j])) { 48603c5faf6Sreinoud aprint_error_dev(self, "can't map bar %u \n", i); 48703c5faf6Sreinoud ret = EIO; 48803c5faf6Sreinoud goto err; 48903c5faf6Sreinoud } 4903c3f3d1aSmartin aprint_debug_dev(self, 4913c3f3d1aSmartin "bar[%d]: iot %p, size 0x%" PRIxBUSSIZE "\n", 49203c5faf6Sreinoud j, psc->sc_bars_iot[j], psc->sc_bars_iosize[j]); 49303c5faf6Sreinoud bars_idx[i] = j; 49403c5faf6Sreinoud j++; 49503c5faf6Sreinoud } 49603c5faf6Sreinoud 49703c5faf6Sreinoud i = bars_idx[notify.cap.bar]; 49803c5faf6Sreinoud if (bus_space_subregion(psc->sc_bars_iot[i], psc->sc_bars_ioh[i], 4996691b5f9Sriastradh notify.cap.offset, notify.cap.length, &psc->sc_notify_ioh)) { 50003c5faf6Sreinoud aprint_error_dev(self, "can't map notify i/o space\n"); 50103c5faf6Sreinoud ret = EIO; 50203c5faf6Sreinoud goto err; 50303c5faf6Sreinoud } 50403c5faf6Sreinoud psc->sc_notify_iosize = notify.cap.length; 50503c5faf6Sreinoud psc->sc_notify_iot = psc->sc_bars_iot[i]; 50603c5faf6Sreinoud psc->sc_notify_off_multiplier = le32toh(notify.notify_off_multiplier); 50703c5faf6Sreinoud 50803c5faf6Sreinoud if (have_device_cfg) { 50903c5faf6Sreinoud i = bars_idx[device.bar]; 5106691b5f9Sriastradh if (bus_space_subregion(psc->sc_bars_iot[i], 5116691b5f9Sriastradh psc->sc_bars_ioh[i], device.offset, device.length, 51203c5faf6Sreinoud &sc->sc_devcfg_ioh)) { 51303c5faf6Sreinoud aprint_error_dev(self, "can't map devcfg i/o space\n"); 51403c5faf6Sreinoud ret = EIO; 51503c5faf6Sreinoud goto err; 51603c5faf6Sreinoud } 51703c5faf6Sreinoud aprint_debug_dev(self, 51803c5faf6Sreinoud "device.offset = 0x%x, device.length = 0x%x\n", 51903c5faf6Sreinoud device.offset, device.length); 52003c5faf6Sreinoud sc->sc_devcfg_iosize = device.length; 52103c5faf6Sreinoud sc->sc_devcfg_iot = psc->sc_bars_iot[i]; 52203c5faf6Sreinoud } 52303c5faf6Sreinoud 52403c5faf6Sreinoud i = bars_idx[isr.bar]; 52503c5faf6Sreinoud if (bus_space_subregion(psc->sc_bars_iot[i], psc->sc_bars_ioh[i], 52603c5faf6Sreinoud isr.offset, isr.length, &psc->sc_isr_ioh)) { 52703c5faf6Sreinoud aprint_error_dev(self, "can't map isr i/o space\n"); 52803c5faf6Sreinoud ret = EIO; 52903c5faf6Sreinoud goto err; 53003c5faf6Sreinoud } 53103c5faf6Sreinoud psc->sc_isr_iosize = isr.length; 53203c5faf6Sreinoud psc->sc_isr_iot = psc->sc_bars_iot[i]; 53303c5faf6Sreinoud 53403c5faf6Sreinoud i = bars_idx[common.bar]; 53503c5faf6Sreinoud if (bus_space_subregion(psc->sc_bars_iot[i], psc->sc_bars_ioh[i], 53603c5faf6Sreinoud common.offset, common.length, &psc->sc_ioh)) { 53703c5faf6Sreinoud aprint_error_dev(self, "can't map common i/o space\n"); 53803c5faf6Sreinoud ret = EIO; 53903c5faf6Sreinoud goto err; 54003c5faf6Sreinoud } 54103c5faf6Sreinoud psc->sc_iosize = common.length; 54203c5faf6Sreinoud psc->sc_iot = psc->sc_bars_iot[i]; 54303c5faf6Sreinoud 54403c5faf6Sreinoud psc->sc_sc.sc_version_1 = 1; 54503c5faf6Sreinoud 54603c5faf6Sreinoud /* set our version 1.0 ops */ 54703c5faf6Sreinoud sc->sc_ops = &virtio_pci_ops_10; 548e225d1d2Sreinoud sc->sc_bus_endian = READ_ENDIAN_10; 549e225d1d2Sreinoud sc->sc_struct_endian = STRUCT_ENDIAN_10; 55003c5faf6Sreinoud return 0; 55103c5faf6Sreinoud 55203c5faf6Sreinoud err: 55325f47adaSreinoud /* undo our pci_mapreg_map()s */ 55425f47adaSreinoud for (i = 0; i < __arraycount(bars); i++) { 5558ccb2f41Sreinoud if (psc->sc_bars_iosize[i] == 0) 55625f47adaSreinoud continue; 5578ccb2f41Sreinoud bus_space_unmap(psc->sc_bars_iot[i], psc->sc_bars_ioh[i], 5588ccb2f41Sreinoud psc->sc_bars_iosize[i]); 559c1ec78f6Sriastradh psc->sc_bars_iosize[i] = 0; 56025f47adaSreinoud } 56103c5faf6Sreinoud return ret; 56203c5faf6Sreinoud } 56303c5faf6Sreinoud 56403c5faf6Sreinoud /* v1.0 attach helper */ 56503c5faf6Sreinoud static int 5666691b5f9Sriastradh virtio_pci_find_cap(struct virtio_pci_softc *psc, int cfg_type, void *buf, 5676691b5f9Sriastradh int buflen) 56803c5faf6Sreinoud { 56903c5faf6Sreinoud device_t self = psc->sc_sc.sc_dev; 57003c5faf6Sreinoud pci_chipset_tag_t pc = psc->sc_pa.pa_pc; 57103c5faf6Sreinoud pcitag_t tag = psc->sc_pa.pa_tag; 57203c5faf6Sreinoud unsigned int offset, i, len; 57303c5faf6Sreinoud union { 57403c5faf6Sreinoud pcireg_t reg[8]; 57503c5faf6Sreinoud struct virtio_pci_cap vcap; 57603c5faf6Sreinoud } *v = buf; 57703c5faf6Sreinoud 57803c5faf6Sreinoud if (buflen < sizeof(struct virtio_pci_cap)) 57903c5faf6Sreinoud return ERANGE; 58003c5faf6Sreinoud 5816691b5f9Sriastradh if (!pci_get_capability(pc, tag, PCI_CAP_VENDSPEC, &offset, 5826691b5f9Sriastradh &v->reg[0])) 58303c5faf6Sreinoud return ENOENT; 58403c5faf6Sreinoud 58503c5faf6Sreinoud do { 58603c5faf6Sreinoud for (i = 0; i < 4; i++) 58703c5faf6Sreinoud v->reg[i] = 58803c5faf6Sreinoud le32toh(pci_conf_read(pc, tag, offset + i * 4)); 58903c5faf6Sreinoud if (v->vcap.cfg_type == cfg_type) 59003c5faf6Sreinoud break; 59103c5faf6Sreinoud offset = v->vcap.cap_next; 59203c5faf6Sreinoud } while (offset != 0); 59303c5faf6Sreinoud 59403c5faf6Sreinoud if (offset == 0) 59503c5faf6Sreinoud return ENOENT; 59603c5faf6Sreinoud 59703c5faf6Sreinoud if (v->vcap.cap_len > sizeof(struct virtio_pci_cap)) { 59803c5faf6Sreinoud len = roundup(v->vcap.cap_len, sizeof(pcireg_t)); 59903c5faf6Sreinoud if (len > buflen) { 60003c5faf6Sreinoud aprint_error_dev(self, "%s cap too large\n", __func__); 60103c5faf6Sreinoud return ERANGE; 60203c5faf6Sreinoud } 60303c5faf6Sreinoud for (i = 4; i < len / sizeof(pcireg_t); i++) 60403c5faf6Sreinoud v->reg[i] = 60503c5faf6Sreinoud le32toh(pci_conf_read(pc, tag, offset + i * 4)); 60603c5faf6Sreinoud } 60703c5faf6Sreinoud 60803c5faf6Sreinoud /* endian fixup */ 60903c5faf6Sreinoud v->vcap.offset = le32toh(v->vcap.offset); 61003c5faf6Sreinoud v->vcap.length = le32toh(v->vcap.length); 61103c5faf6Sreinoud return 0; 61203c5faf6Sreinoud } 61303c5faf6Sreinoud 61403c5faf6Sreinoud /* ------------------------------------- 61503c5faf6Sreinoud * Version 0.9 support 61603c5faf6Sreinoud * -------------------------------------*/ 61703c5faf6Sreinoud 61803c5faf6Sreinoud static void 61903c5faf6Sreinoud virtio_pci_kick_09(struct virtio_softc *sc, uint16_t idx) 6201a7766abSjakllsch { 62107394025Sriastradh struct virtio_pci_softc * const psc = container_of(sc, 62207394025Sriastradh struct virtio_pci_softc, sc_sc); 62303c5faf6Sreinoud 62403c5faf6Sreinoud bus_space_write_2(psc->sc_notify_iot, psc->sc_notify_ioh, 0, idx); 62503c5faf6Sreinoud } 62603c5faf6Sreinoud 62703c5faf6Sreinoud /* only applicable for v 0.9 but also called for 1.0 */ 62803c5faf6Sreinoud static int 62903c5faf6Sreinoud virtio_pci_adjust_config_region(struct virtio_pci_softc *psc) 63003c5faf6Sreinoud { 63101c675b8Suwe struct virtio_softc * const sc = &psc->sc_sc; 63201c675b8Suwe device_t self = sc->sc_dev; 63303c5faf6Sreinoud 63403c5faf6Sreinoud if (psc->sc_sc.sc_version_1) 63503c5faf6Sreinoud return 0; 63603c5faf6Sreinoud 63703c5faf6Sreinoud sc->sc_devcfg_iosize = psc->sc_iosize - psc->sc_devcfg_offset; 63803c5faf6Sreinoud sc->sc_devcfg_iot = psc->sc_iot; 63903c5faf6Sreinoud if (bus_space_subregion(psc->sc_iot, psc->sc_ioh, 64003c5faf6Sreinoud psc->sc_devcfg_offset, sc->sc_devcfg_iosize, 64103c5faf6Sreinoud &sc->sc_devcfg_ioh)) { 64203c5faf6Sreinoud aprint_error_dev(self, "can't map config i/o space\n"); 64303c5faf6Sreinoud return EIO; 64403c5faf6Sreinoud } 64503c5faf6Sreinoud 64603c5faf6Sreinoud return 0; 6471a7766abSjakllsch } 6481a7766abSjakllsch 6491a7766abSjakllsch static uint16_t 65003c5faf6Sreinoud virtio_pci_read_queue_size_09(struct virtio_softc *sc, uint16_t idx) 6511a7766abSjakllsch { 65207394025Sriastradh struct virtio_pci_softc * const psc = container_of(sc, 65307394025Sriastradh struct virtio_pci_softc, sc_sc); 6541a7766abSjakllsch 65503c5faf6Sreinoud bus_space_write_2(psc->sc_iot, psc->sc_ioh, 6561a7766abSjakllsch VIRTIO_CONFIG_QUEUE_SELECT, idx); 65703c5faf6Sreinoud return bus_space_read_2(psc->sc_iot, psc->sc_ioh, 6581a7766abSjakllsch VIRTIO_CONFIG_QUEUE_SIZE); 6591a7766abSjakllsch } 6601a7766abSjakllsch 6611a7766abSjakllsch static void 66203c5faf6Sreinoud virtio_pci_setup_queue_09(struct virtio_softc *sc, uint16_t idx, uint64_t addr) 6631a7766abSjakllsch { 66407394025Sriastradh struct virtio_pci_softc * const psc = container_of(sc, 66507394025Sriastradh struct virtio_pci_softc, sc_sc); 6661a7766abSjakllsch 66703c5faf6Sreinoud bus_space_write_2(psc->sc_iot, psc->sc_ioh, 6681a7766abSjakllsch VIRTIO_CONFIG_QUEUE_SELECT, idx); 66903c5faf6Sreinoud bus_space_write_4(psc->sc_iot, psc->sc_ioh, 67003c5faf6Sreinoud VIRTIO_CONFIG_QUEUE_ADDRESS, addr / VIRTIO_PAGE_SIZE); 6711a7766abSjakllsch 6721a7766abSjakllsch if (psc->sc_ihs_num > 1) { 6731a7766abSjakllsch int vec = VIRTIO_MSIX_QUEUE_VECTOR_INDEX; 67486598168Syamaguchi if (psc->sc_intr_pervq) 6751a7766abSjakllsch vec += idx; 67603c5faf6Sreinoud bus_space_write_2(psc->sc_iot, psc->sc_ioh, 6771a7766abSjakllsch VIRTIO_CONFIG_MSI_QUEUE_VECTOR, vec); 6781a7766abSjakllsch } 6791a7766abSjakllsch } 6801a7766abSjakllsch 6811a7766abSjakllsch static void 68203c5faf6Sreinoud virtio_pci_set_status_09(struct virtio_softc *sc, int status) 6831a7766abSjakllsch { 68407394025Sriastradh struct virtio_pci_softc * const psc = container_of(sc, 68507394025Sriastradh struct virtio_pci_softc, sc_sc); 6861a7766abSjakllsch int old = 0; 6871a7766abSjakllsch 6881a7766abSjakllsch if (status != 0) { 68903c5faf6Sreinoud old = bus_space_read_1(psc->sc_iot, psc->sc_ioh, 6901a7766abSjakllsch VIRTIO_CONFIG_DEVICE_STATUS); 6911a7766abSjakllsch } 69203c5faf6Sreinoud bus_space_write_1(psc->sc_iot, psc->sc_ioh, 6931a7766abSjakllsch VIRTIO_CONFIG_DEVICE_STATUS, status|old); 6941a7766abSjakllsch } 6951a7766abSjakllsch 69603c5faf6Sreinoud static void 6976691b5f9Sriastradh virtio_pci_negotiate_features_09(struct virtio_softc *sc, 6986691b5f9Sriastradh uint64_t guest_features) 6991a7766abSjakllsch { 70007394025Sriastradh struct virtio_pci_softc * const psc = container_of(sc, 70107394025Sriastradh struct virtio_pci_softc, sc_sc); 7021a7766abSjakllsch uint32_t r; 7031a7766abSjakllsch 70403c5faf6Sreinoud r = bus_space_read_4(psc->sc_iot, psc->sc_ioh, 7051a7766abSjakllsch VIRTIO_CONFIG_DEVICE_FEATURES); 70603c5faf6Sreinoud 7071a7766abSjakllsch r &= guest_features; 70803c5faf6Sreinoud 70903c5faf6Sreinoud bus_space_write_4(psc->sc_iot, psc->sc_ioh, 7101a7766abSjakllsch VIRTIO_CONFIG_GUEST_FEATURES, r); 7111a7766abSjakllsch 71203c5faf6Sreinoud sc->sc_active_features = r; 71303c5faf6Sreinoud } 71403c5faf6Sreinoud 71503c5faf6Sreinoud /* ------------------------------------- 71603c5faf6Sreinoud * Version 1.0 support 71703c5faf6Sreinoud * -------------------------------------*/ 71803c5faf6Sreinoud 71903c5faf6Sreinoud static void 72003c5faf6Sreinoud virtio_pci_kick_10(struct virtio_softc *sc, uint16_t idx) 72103c5faf6Sreinoud { 72207394025Sriastradh struct virtio_pci_softc * const psc = container_of(sc, 72307394025Sriastradh struct virtio_pci_softc, sc_sc); 72403c5faf6Sreinoud unsigned offset = sc->sc_vqs[idx].vq_notify_off * 72503c5faf6Sreinoud psc->sc_notify_off_multiplier; 72603c5faf6Sreinoud 72703c5faf6Sreinoud bus_space_write_2(psc->sc_notify_iot, psc->sc_notify_ioh, offset, idx); 7281a7766abSjakllsch } 7291a7766abSjakllsch 73003c5faf6Sreinoud static uint16_t 73103c5faf6Sreinoud virtio_pci_read_queue_size_10(struct virtio_softc *sc, uint16_t idx) 7321a7766abSjakllsch { 73307394025Sriastradh struct virtio_pci_softc * const psc = container_of(sc, 73407394025Sriastradh struct virtio_pci_softc, sc_sc); 73503c5faf6Sreinoud bus_space_tag_t iot = psc->sc_iot; 73603c5faf6Sreinoud bus_space_handle_t ioh = psc->sc_ioh; 73703c5faf6Sreinoud 73803c5faf6Sreinoud bus_space_write_2(iot, ioh, VIRTIO_CONFIG1_QUEUE_SELECT, idx); 73903c5faf6Sreinoud return bus_space_read_2(iot, ioh, VIRTIO_CONFIG1_QUEUE_SIZE); 74003c5faf6Sreinoud } 74103c5faf6Sreinoud 7421a1991f0Sreinoud /* 743b696d163Suwe * By definition little endian only in v1.0. NB: "MAY" in the text 744b696d163Suwe * below refers to "independently" (i.e. the order of accesses) not 745b696d163Suwe * "32-bit" (which is restricted by the earlier "MUST"). 746523df2c8Sthorpej * 747b696d163Suwe * 4.1.3.1 Driver Requirements: PCI Device Layout 748b696d163Suwe * 749b696d163Suwe * For device configuration access, the driver MUST use ... 32-bit 750b696d163Suwe * wide and aligned accesses for ... 64-bit wide fields. For 64-bit 751b696d163Suwe * fields, the driver MAY access each of the high and low 32-bit parts 752b696d163Suwe * of the field independently. 753b9fb14b9Schristos */ 7548c1c74a0Schristos static __inline void 755523df2c8Sthorpej virtio_pci_bus_space_write_8(bus_space_tag_t iot, bus_space_handle_t ioh, 7568c1c74a0Schristos bus_size_t offset, uint64_t value) 7578c1c74a0Schristos { 7588c1c74a0Schristos bus_space_write_4(iot, ioh, offset, BUS_ADDR_LO32(value)); 7598c1c74a0Schristos bus_space_write_4(iot, ioh, offset + 4, BUS_ADDR_HI32(value)); 7608c1c74a0Schristos } 7611a1991f0Sreinoud 76203c5faf6Sreinoud static void 76303c5faf6Sreinoud virtio_pci_setup_queue_10(struct virtio_softc *sc, uint16_t idx, uint64_t addr) 76403c5faf6Sreinoud { 76507394025Sriastradh struct virtio_pci_softc * const psc = container_of(sc, 76607394025Sriastradh struct virtio_pci_softc, sc_sc); 76703c5faf6Sreinoud struct virtqueue *vq = &sc->sc_vqs[idx]; 76803c5faf6Sreinoud bus_space_tag_t iot = psc->sc_iot; 76903c5faf6Sreinoud bus_space_handle_t ioh = psc->sc_ioh; 77003c5faf6Sreinoud KASSERT(vq->vq_index == idx); 77103c5faf6Sreinoud 77203c5faf6Sreinoud bus_space_write_2(iot, ioh, VIRTIO_CONFIG1_QUEUE_SELECT, vq->vq_index); 77303c5faf6Sreinoud if (addr == 0) { 77403c5faf6Sreinoud bus_space_write_2(iot, ioh, VIRTIO_CONFIG1_QUEUE_ENABLE, 0); 775523df2c8Sthorpej virtio_pci_bus_space_write_8(iot, ioh, 776523df2c8Sthorpej VIRTIO_CONFIG1_QUEUE_DESC, 0); 777523df2c8Sthorpej virtio_pci_bus_space_write_8(iot, ioh, 778523df2c8Sthorpej VIRTIO_CONFIG1_QUEUE_AVAIL, 0); 779523df2c8Sthorpej virtio_pci_bus_space_write_8(iot, ioh, 780523df2c8Sthorpej VIRTIO_CONFIG1_QUEUE_USED, 0); 78103c5faf6Sreinoud } else { 782523df2c8Sthorpej virtio_pci_bus_space_write_8(iot, ioh, 78303c5faf6Sreinoud VIRTIO_CONFIG1_QUEUE_DESC, addr); 784523df2c8Sthorpej virtio_pci_bus_space_write_8(iot, ioh, 78503c5faf6Sreinoud VIRTIO_CONFIG1_QUEUE_AVAIL, addr + vq->vq_availoffset); 786523df2c8Sthorpej virtio_pci_bus_space_write_8(iot, ioh, 78703c5faf6Sreinoud VIRTIO_CONFIG1_QUEUE_USED, addr + vq->vq_usedoffset); 78803c5faf6Sreinoud bus_space_write_2(iot, ioh, 78903c5faf6Sreinoud VIRTIO_CONFIG1_QUEUE_ENABLE, 1); 79003c5faf6Sreinoud vq->vq_notify_off = bus_space_read_2(iot, ioh, 79103c5faf6Sreinoud VIRTIO_CONFIG1_QUEUE_NOTIFY_OFF); 79203c5faf6Sreinoud } 79303c5faf6Sreinoud 79403c5faf6Sreinoud if (psc->sc_ihs_num > 1) { 79503c5faf6Sreinoud int vec = VIRTIO_MSIX_QUEUE_VECTOR_INDEX; 79686598168Syamaguchi if (psc->sc_intr_pervq) 79703c5faf6Sreinoud vec += idx; 79803c5faf6Sreinoud bus_space_write_2(iot, ioh, 79903c5faf6Sreinoud VIRTIO_CONFIG1_QUEUE_MSIX_VECTOR, vec); 80003c5faf6Sreinoud } 80103c5faf6Sreinoud } 80203c5faf6Sreinoud 80303c5faf6Sreinoud static void 80403c5faf6Sreinoud virtio_pci_set_status_10(struct virtio_softc *sc, int status) 80503c5faf6Sreinoud { 80607394025Sriastradh struct virtio_pci_softc * const psc = container_of(sc, 80707394025Sriastradh struct virtio_pci_softc, sc_sc); 80803c5faf6Sreinoud bus_space_tag_t iot = psc->sc_iot; 80903c5faf6Sreinoud bus_space_handle_t ioh = psc->sc_ioh; 81003c5faf6Sreinoud int old = 0; 81103c5faf6Sreinoud 81203c5faf6Sreinoud if (status) 81303c5faf6Sreinoud old = bus_space_read_1(iot, ioh, VIRTIO_CONFIG1_DEVICE_STATUS); 8146691b5f9Sriastradh bus_space_write_1(iot, ioh, VIRTIO_CONFIG1_DEVICE_STATUS, 8156691b5f9Sriastradh status | old); 81603c5faf6Sreinoud } 81703c5faf6Sreinoud 81803c5faf6Sreinoud void 8196691b5f9Sriastradh virtio_pci_negotiate_features_10(struct virtio_softc *sc, 8206691b5f9Sriastradh uint64_t guest_features) 82103c5faf6Sreinoud { 82207394025Sriastradh struct virtio_pci_softc * const psc = container_of(sc, 82307394025Sriastradh struct virtio_pci_softc, sc_sc); 82403c5faf6Sreinoud device_t self = sc->sc_dev; 82503c5faf6Sreinoud bus_space_tag_t iot = psc->sc_iot; 82603c5faf6Sreinoud bus_space_handle_t ioh = psc->sc_ioh; 82703c5faf6Sreinoud uint64_t host, negotiated, device_status; 82803c5faf6Sreinoud 82903c5faf6Sreinoud guest_features |= VIRTIO_F_VERSION_1; 8304eae4625Sthorpej #ifdef __NEED_VIRTIO_F_ACCESS_PLATFORM 8314eae4625Sthorpej /* XXX This could use some work. */ 8324eae4625Sthorpej guest_features |= VIRTIO_F_ACCESS_PLATFORM; 8334eae4625Sthorpej #endif /* __NEED_VIRTIO_F_ACCESS_PLATFORM */ 83403c5faf6Sreinoud /* notify on empty is 0.9 only */ 83503c5faf6Sreinoud guest_features &= ~VIRTIO_F_NOTIFY_ON_EMPTY; 83603c5faf6Sreinoud sc->sc_active_features = 0; 83703c5faf6Sreinoud 83803c5faf6Sreinoud bus_space_write_4(iot, ioh, VIRTIO_CONFIG1_DEVICE_FEATURE_SELECT, 0); 83903c5faf6Sreinoud host = bus_space_read_4(iot, ioh, VIRTIO_CONFIG1_DEVICE_FEATURE); 84003c5faf6Sreinoud bus_space_write_4(iot, ioh, VIRTIO_CONFIG1_DEVICE_FEATURE_SELECT, 1); 8416691b5f9Sriastradh host |= (uint64_t)bus_space_read_4(iot, ioh, 8426691b5f9Sriastradh VIRTIO_CONFIG1_DEVICE_FEATURE) << 32; 84303c5faf6Sreinoud 84403c5faf6Sreinoud negotiated = host & guest_features; 84503c5faf6Sreinoud 84603c5faf6Sreinoud bus_space_write_4(iot, ioh, VIRTIO_CONFIG1_DRIVER_FEATURE_SELECT, 0); 84703c5faf6Sreinoud bus_space_write_4(iot, ioh, VIRTIO_CONFIG1_DRIVER_FEATURE, 84803c5faf6Sreinoud negotiated & 0xffffffff); 84903c5faf6Sreinoud bus_space_write_4(iot, ioh, VIRTIO_CONFIG1_DRIVER_FEATURE_SELECT, 1); 85003c5faf6Sreinoud bus_space_write_4(iot, ioh, VIRTIO_CONFIG1_DRIVER_FEATURE, 85103c5faf6Sreinoud negotiated >> 32); 85203c5faf6Sreinoud virtio_pci_set_status_10(sc, VIRTIO_CONFIG_DEVICE_STATUS_FEATURES_OK); 85303c5faf6Sreinoud 8546691b5f9Sriastradh device_status = bus_space_read_1(iot, ioh, 8556691b5f9Sriastradh VIRTIO_CONFIG1_DEVICE_STATUS); 85603c5faf6Sreinoud if ((device_status & VIRTIO_CONFIG_DEVICE_STATUS_FEATURES_OK) == 0) { 85703c5faf6Sreinoud aprint_error_dev(self, "feature negotiation failed\n"); 85803c5faf6Sreinoud bus_space_write_1(iot, ioh, VIRTIO_CONFIG1_DEVICE_STATUS, 85903c5faf6Sreinoud VIRTIO_CONFIG_DEVICE_STATUS_FAILED); 86003c5faf6Sreinoud return; 86103c5faf6Sreinoud } 86203c5faf6Sreinoud 86303c5faf6Sreinoud if ((negotiated & VIRTIO_F_VERSION_1) == 0) { 86403c5faf6Sreinoud aprint_error_dev(self, "host rejected version 1\n"); 86503c5faf6Sreinoud bus_space_write_1(iot, ioh, VIRTIO_CONFIG1_DEVICE_STATUS, 86603c5faf6Sreinoud VIRTIO_CONFIG_DEVICE_STATUS_FAILED); 86703c5faf6Sreinoud return; 86803c5faf6Sreinoud } 86903c5faf6Sreinoud 87003c5faf6Sreinoud sc->sc_active_features = negotiated; 87103c5faf6Sreinoud return; 87203c5faf6Sreinoud } 87303c5faf6Sreinoud 87403c5faf6Sreinoud /* ------------------------------------- 87503c5faf6Sreinoud * Generic PCI interrupt code 87603c5faf6Sreinoud * -------------------------------------*/ 87703c5faf6Sreinoud 87803c5faf6Sreinoud static int 879243e58f6Syamaguchi virtio_pci_setup_interrupts_10(struct virtio_softc *sc, int reinit) 88003c5faf6Sreinoud { 88107394025Sriastradh struct virtio_pci_softc * const psc = container_of(sc, 88207394025Sriastradh struct virtio_pci_softc, sc_sc); 88303c5faf6Sreinoud bus_space_tag_t iot = psc->sc_iot; 88403c5faf6Sreinoud bus_space_handle_t ioh = psc->sc_ioh; 88503c5faf6Sreinoud int vector, ret, qid; 88603c5faf6Sreinoud 8872b9a3babSyamaguchi if (!virtio_pci_msix_enabled(psc)) 8882b9a3babSyamaguchi return 0; 8892b9a3babSyamaguchi 89003c5faf6Sreinoud vector = VIRTIO_MSIX_CONFIG_VECTOR_INDEX; 8916691b5f9Sriastradh bus_space_write_2(iot, ioh, VIRTIO_CONFIG1_CONFIG_MSIX_VECTOR, vector); 89203c5faf6Sreinoud ret = bus_space_read_2(iot, ioh, VIRTIO_CONFIG1_CONFIG_MSIX_VECTOR); 89303c5faf6Sreinoud if (ret != vector) { 8946691b5f9Sriastradh VIRTIO_PCI_LOG(sc, reinit, "can't set config msix vector\n"); 89503c5faf6Sreinoud return -1; 89603c5faf6Sreinoud } 89703c5faf6Sreinoud 89803c5faf6Sreinoud for (qid = 0; qid < sc->sc_nvqs; qid++) { 89903c5faf6Sreinoud vector = VIRTIO_MSIX_QUEUE_VECTOR_INDEX; 90003c5faf6Sreinoud 90186598168Syamaguchi if (psc->sc_intr_pervq) 90203c5faf6Sreinoud vector += qid; 90303c5faf6Sreinoud bus_space_write_2(iot, ioh, VIRTIO_CONFIG1_QUEUE_SELECT, qid); 90403c5faf6Sreinoud bus_space_write_2(iot, ioh, VIRTIO_CONFIG1_QUEUE_MSIX_VECTOR, 90503c5faf6Sreinoud vector); 90603c5faf6Sreinoud ret = bus_space_read_2(iot, ioh, 90703c5faf6Sreinoud VIRTIO_CONFIG1_QUEUE_MSIX_VECTOR); 90803c5faf6Sreinoud if (ret != vector) { 909c6fd71d9Syamaguchi VIRTIO_PCI_LOG(sc, reinit, "can't set queue %d " 91003c5faf6Sreinoud "msix vector\n", qid); 91103c5faf6Sreinoud return -1; 91203c5faf6Sreinoud } 91303c5faf6Sreinoud } 91403c5faf6Sreinoud 91503c5faf6Sreinoud return 0; 91603c5faf6Sreinoud } 91703c5faf6Sreinoud 91803c5faf6Sreinoud static int 919243e58f6Syamaguchi virtio_pci_setup_interrupts_09(struct virtio_softc *sc, int reinit) 92003c5faf6Sreinoud { 92107394025Sriastradh struct virtio_pci_softc * const psc = container_of(sc, 92207394025Sriastradh struct virtio_pci_softc, sc_sc); 9231a7766abSjakllsch int offset, vector, ret, qid; 9241a7766abSjakllsch 9252b9a3babSyamaguchi if (!virtio_pci_msix_enabled(psc)) 9262b9a3babSyamaguchi return 0; 9272b9a3babSyamaguchi 9281a7766abSjakllsch offset = VIRTIO_CONFIG_MSI_CONFIG_VECTOR; 9291a7766abSjakllsch vector = VIRTIO_MSIX_CONFIG_VECTOR_INDEX; 9301a7766abSjakllsch 93103c5faf6Sreinoud bus_space_write_2(psc->sc_iot, psc->sc_ioh, offset, vector); 93203c5faf6Sreinoud ret = bus_space_read_2(psc->sc_iot, psc->sc_ioh, offset); 93303c5faf6Sreinoud if (ret != vector) { 934c4afc10eSriastradh aprint_debug_dev(sc->sc_dev, "%s: expected=%d, actual=%d\n", 935c4afc10eSriastradh __func__, vector, ret); 936c6fd71d9Syamaguchi VIRTIO_PCI_LOG(sc, reinit, 937243e58f6Syamaguchi "can't set config msix vector\n"); 9381a7766abSjakllsch return -1; 93903c5faf6Sreinoud } 9401a7766abSjakllsch 9411a7766abSjakllsch for (qid = 0; qid < sc->sc_nvqs; qid++) { 9421a7766abSjakllsch offset = VIRTIO_CONFIG_QUEUE_SELECT; 94303c5faf6Sreinoud bus_space_write_2(psc->sc_iot, psc->sc_ioh, offset, qid); 9441a7766abSjakllsch 9451a7766abSjakllsch offset = VIRTIO_CONFIG_MSI_QUEUE_VECTOR; 9461a7766abSjakllsch vector = VIRTIO_MSIX_QUEUE_VECTOR_INDEX; 9471a7766abSjakllsch 94886598168Syamaguchi if (psc->sc_intr_pervq) 94994b53882Syamaguchi vector += qid; 95094b53882Syamaguchi 95103c5faf6Sreinoud bus_space_write_2(psc->sc_iot, psc->sc_ioh, offset, vector); 95203c5faf6Sreinoud ret = bus_space_read_2(psc->sc_iot, psc->sc_ioh, offset); 95303c5faf6Sreinoud if (ret != vector) { 954c4afc10eSriastradh aprint_debug_dev(sc->sc_dev, "%s[qid=%d]:" 955c4afc10eSriastradh " expected=%d, actual=%d\n", 956c4afc10eSriastradh __func__, qid, vector, ret); 957c6fd71d9Syamaguchi VIRTIO_PCI_LOG(sc, reinit, "can't set queue %d " 95803c5faf6Sreinoud "msix vector\n", qid); 9591a7766abSjakllsch return -1; 9601a7766abSjakllsch } 96103c5faf6Sreinoud } 9621a7766abSjakllsch 9631a7766abSjakllsch return 0; 9641a7766abSjakllsch } 9651a7766abSjakllsch 9661a7766abSjakllsch static int 9672b9a3babSyamaguchi virtio_pci_establish_msix_interrupts(struct virtio_softc *sc, 9687a157899Sriastradh const struct pci_attach_args *pa) 9691a7766abSjakllsch { 97007394025Sriastradh struct virtio_pci_softc * const psc = container_of(sc, 97107394025Sriastradh struct virtio_pci_softc, sc_sc); 9721a7766abSjakllsch device_t self = sc->sc_dev; 9731a7766abSjakllsch pci_chipset_tag_t pc = pa->pa_pc; 9748005e629Syamaguchi struct virtqueue *vq; 9751a7766abSjakllsch char intrbuf[PCI_INTRSTR_LEN]; 97694b53882Syamaguchi char intr_xname[INTRDEVNAMEBUF]; 9771a7766abSjakllsch char const *intrstr; 97894b53882Syamaguchi int idx, qid, n; 9791a7766abSjakllsch 9801a7766abSjakllsch idx = VIRTIO_MSIX_CONFIG_VECTOR_INDEX; 98103c5faf6Sreinoud if (sc->sc_flags & VIRTIO_F_INTR_MPSAFE) 9821a7766abSjakllsch pci_intr_setattr(pc, &psc->sc_ihp[idx], PCI_INTR_MPSAFE, true); 9831a7766abSjakllsch 98494b53882Syamaguchi snprintf(intr_xname, sizeof(intr_xname), "%s config", 98594b53882Syamaguchi device_xname(sc->sc_dev)); 98694b53882Syamaguchi 9871a7766abSjakllsch psc->sc_ihs[idx] = pci_intr_establish_xname(pc, psc->sc_ihp[idx], 98894b53882Syamaguchi sc->sc_ipl, virtio_pci_msix_config_intr, sc, intr_xname); 9891a7766abSjakllsch if (psc->sc_ihs[idx] == NULL) { 9906691b5f9Sriastradh aprint_error_dev(self, 9916691b5f9Sriastradh "couldn't establish MSI-X for config\n"); 9921a7766abSjakllsch goto error; 9931a7766abSjakllsch } 9941a7766abSjakllsch 9951a7766abSjakllsch idx = VIRTIO_MSIX_QUEUE_VECTOR_INDEX; 99686598168Syamaguchi if (psc->sc_intr_pervq) { 99794b53882Syamaguchi for (qid = 0; qid < sc->sc_nvqs; qid++) { 99894b53882Syamaguchi n = idx + qid; 9998005e629Syamaguchi vq = &sc->sc_vqs[qid]; 100094b53882Syamaguchi 100194b53882Syamaguchi snprintf(intr_xname, sizeof(intr_xname), "%s vq#%d", 100294b53882Syamaguchi device_xname(sc->sc_dev), qid); 100394b53882Syamaguchi 100403c5faf6Sreinoud if (sc->sc_flags & VIRTIO_F_INTR_MPSAFE) { 100594b53882Syamaguchi pci_intr_setattr(pc, &psc->sc_ihp[n], 100694b53882Syamaguchi PCI_INTR_MPSAFE, true); 100794b53882Syamaguchi } 100894b53882Syamaguchi 10096691b5f9Sriastradh psc->sc_ihs[n] = pci_intr_establish_xname(pc, 10106691b5f9Sriastradh psc->sc_ihp[n], sc->sc_ipl, 10116691b5f9Sriastradh vq->vq_intrhand, vq->vq_intrhand_arg, intr_xname); 101294b53882Syamaguchi if (psc->sc_ihs[n] == NULL) { 10136691b5f9Sriastradh aprint_error_dev(self, 10146691b5f9Sriastradh "couldn't establish MSI-X for a vq\n"); 101594b53882Syamaguchi goto error; 101694b53882Syamaguchi } 101794b53882Syamaguchi } 101894b53882Syamaguchi } else { 10196691b5f9Sriastradh if (sc->sc_flags & VIRTIO_F_INTR_MPSAFE) { 10206691b5f9Sriastradh pci_intr_setattr(pc, &psc->sc_ihp[idx], 10216691b5f9Sriastradh PCI_INTR_MPSAFE, true); 10226691b5f9Sriastradh } 10231a7766abSjakllsch 102494b53882Syamaguchi snprintf(intr_xname, sizeof(intr_xname), "%s queues", 102594b53882Syamaguchi device_xname(sc->sc_dev)); 10266691b5f9Sriastradh psc->sc_ihs[idx] = pci_intr_establish_xname(pc, 10276691b5f9Sriastradh psc->sc_ihp[idx], sc->sc_ipl, 10286691b5f9Sriastradh virtio_pci_msix_queue_intr, sc, intr_xname); 10291a7766abSjakllsch if (psc->sc_ihs[idx] == NULL) { 10306691b5f9Sriastradh aprint_error_dev(self, 10316691b5f9Sriastradh "couldn't establish MSI-X for queues\n"); 10321a7766abSjakllsch goto error; 10331a7766abSjakllsch } 103494b53882Syamaguchi } 10351a7766abSjakllsch 10361a7766abSjakllsch idx = VIRTIO_MSIX_CONFIG_VECTOR_INDEX; 10376691b5f9Sriastradh intrstr = pci_intr_string(pc, psc->sc_ihp[idx], intrbuf, 10386691b5f9Sriastradh sizeof(intrbuf)); 10391a7766abSjakllsch aprint_normal_dev(self, "config interrupting at %s\n", intrstr); 10401a7766abSjakllsch idx = VIRTIO_MSIX_QUEUE_VECTOR_INDEX; 104186598168Syamaguchi if (psc->sc_intr_pervq) { 104294b53882Syamaguchi kcpuset_t *affinity; 104394b53882Syamaguchi int affinity_to, r; 104494b53882Syamaguchi 104594b53882Syamaguchi kcpuset_create(&affinity, false); 104694b53882Syamaguchi 104794b53882Syamaguchi for (qid = 0; qid < sc->sc_nvqs; qid++) { 104894b53882Syamaguchi n = idx + qid; 104994b53882Syamaguchi affinity_to = (qid / 2) % ncpu; 105094b53882Syamaguchi 105194b53882Syamaguchi intrstr = pci_intr_string(pc, psc->sc_ihp[n], 105294b53882Syamaguchi intrbuf, sizeof(intrbuf)); 105394b53882Syamaguchi 105494b53882Syamaguchi kcpuset_zero(affinity); 105594b53882Syamaguchi kcpuset_set(affinity, affinity_to); 10566691b5f9Sriastradh r = interrupt_distribute(psc->sc_ihs[n], affinity, 10576691b5f9Sriastradh NULL); 105894b53882Syamaguchi if (r == 0) { 105994b53882Syamaguchi aprint_normal_dev(self, 10606691b5f9Sriastradh "for vq #%d interrupting at %s" 10616691b5f9Sriastradh " affinity to %u\n", 106294b53882Syamaguchi qid, intrstr, affinity_to); 106394b53882Syamaguchi } else { 106494b53882Syamaguchi aprint_normal_dev(self, 106594b53882Syamaguchi "for vq #%d interrupting at %s\n", 106694b53882Syamaguchi qid, intrstr); 106794b53882Syamaguchi } 106894b53882Syamaguchi } 106994b53882Syamaguchi 107094b53882Syamaguchi kcpuset_destroy(affinity); 107194b53882Syamaguchi } else { 10726691b5f9Sriastradh intrstr = pci_intr_string(pc, psc->sc_ihp[idx], intrbuf, 10736691b5f9Sriastradh sizeof(intrbuf)); 10746691b5f9Sriastradh aprint_normal_dev(self, "queues interrupting at %s\n", 10756691b5f9Sriastradh intrstr); 107694b53882Syamaguchi } 10771a7766abSjakllsch 10781a7766abSjakllsch return 0; 10791a7766abSjakllsch 10801a7766abSjakllsch error: 10811a7766abSjakllsch idx = VIRTIO_MSIX_CONFIG_VECTOR_INDEX; 10821a7766abSjakllsch if (psc->sc_ihs[idx] != NULL) 10831a7766abSjakllsch pci_intr_disestablish(psc->sc_pa.pa_pc, psc->sc_ihs[idx]); 10841a7766abSjakllsch idx = VIRTIO_MSIX_QUEUE_VECTOR_INDEX; 108586598168Syamaguchi if (psc->sc_intr_pervq) { 108694b53882Syamaguchi for (qid = 0; qid < sc->sc_nvqs; qid++) { 108794b53882Syamaguchi n = idx + qid; 108894b53882Syamaguchi if (psc->sc_ihs[n] == NULL) 108994b53882Syamaguchi continue; 10906691b5f9Sriastradh pci_intr_disestablish(psc->sc_pa.pa_pc, 10916691b5f9Sriastradh psc->sc_ihs[n]); 109294b53882Syamaguchi } 109394b53882Syamaguchi 109494b53882Syamaguchi } else { 10956691b5f9Sriastradh if (psc->sc_ihs[idx] != NULL) { 10966691b5f9Sriastradh pci_intr_disestablish(psc->sc_pa.pa_pc, 10976691b5f9Sriastradh psc->sc_ihs[idx]); 10986691b5f9Sriastradh } 109994b53882Syamaguchi } 11001a7766abSjakllsch 11011a7766abSjakllsch return -1; 11021a7766abSjakllsch } 11031a7766abSjakllsch 11041a7766abSjakllsch static int 11052b9a3babSyamaguchi virtio_pci_establish_intx_interrupt(struct virtio_softc *sc, 11067a157899Sriastradh const struct pci_attach_args *pa) 11071a7766abSjakllsch { 110807394025Sriastradh struct virtio_pci_softc * const psc = container_of(sc, 110907394025Sriastradh struct virtio_pci_softc, sc_sc); 11101a7766abSjakllsch device_t self = sc->sc_dev; 11111a7766abSjakllsch pci_chipset_tag_t pc = pa->pa_pc; 11121a7766abSjakllsch char intrbuf[PCI_INTRSTR_LEN]; 11131a7766abSjakllsch char const *intrstr; 11141a7766abSjakllsch 111503c5faf6Sreinoud if (sc->sc_flags & VIRTIO_F_INTR_MPSAFE) 11161a7766abSjakllsch pci_intr_setattr(pc, &psc->sc_ihp[0], PCI_INTR_MPSAFE, true); 11171a7766abSjakllsch 11181a7766abSjakllsch psc->sc_ihs[0] = pci_intr_establish_xname(pc, psc->sc_ihp[0], 11191a7766abSjakllsch sc->sc_ipl, virtio_pci_intr, sc, device_xname(sc->sc_dev)); 11201a7766abSjakllsch if (psc->sc_ihs[0] == NULL) { 11211a7766abSjakllsch aprint_error_dev(self, "couldn't establish INTx\n"); 11221a7766abSjakllsch return -1; 11231a7766abSjakllsch } 11241a7766abSjakllsch 11256691b5f9Sriastradh intrstr = pci_intr_string(pc, psc->sc_ihp[0], intrbuf, 11266691b5f9Sriastradh sizeof(intrbuf)); 11271a7766abSjakllsch aprint_normal_dev(self, "interrupting at %s\n", intrstr); 11281a7766abSjakllsch 11291a7766abSjakllsch return 0; 11301a7766abSjakllsch } 11311a7766abSjakllsch 11321a7766abSjakllsch static int 11332b9a3babSyamaguchi virtio_pci_alloc_interrupts(struct virtio_softc *sc) 11341a7766abSjakllsch { 113507394025Sriastradh struct virtio_pci_softc * const psc = container_of(sc, 113607394025Sriastradh struct virtio_pci_softc, sc_sc); 11371a7766abSjakllsch device_t self = sc->sc_dev; 11381a7766abSjakllsch pci_chipset_tag_t pc = psc->sc_pa.pa_pc; 1139459e90b0Sjakllsch pcitag_t tag = psc->sc_pa.pa_tag; 11401a7766abSjakllsch int error; 11411a7766abSjakllsch int nmsix; 1142459e90b0Sjakllsch int off; 11431a7766abSjakllsch int counts[PCI_INTR_TYPE_SIZE]; 11441a7766abSjakllsch pci_intr_type_t max_type; 1145459e90b0Sjakllsch pcireg_t ctl; 11461a7766abSjakllsch 11471a7766abSjakllsch nmsix = pci_msix_count(psc->sc_pa.pa_pc, psc->sc_pa.pa_tag); 11481a7766abSjakllsch aprint_debug_dev(self, "pci_msix_count=%d\n", nmsix); 11491a7766abSjakllsch 11501a7766abSjakllsch /* We need at least two: one for config and the other for queues */ 115103c5faf6Sreinoud if ((sc->sc_flags & VIRTIO_F_INTR_MSIX) == 0 || nmsix < 2) { 11521a7766abSjakllsch /* Try INTx only */ 11531a7766abSjakllsch max_type = PCI_INTR_TYPE_INTX; 11541a7766abSjakllsch counts[PCI_INTR_TYPE_INTX] = 1; 11551a7766abSjakllsch } else { 11561a7766abSjakllsch /* Try MSI-X first and INTx second */ 115786598168Syamaguchi if (ISSET(sc->sc_flags, VIRTIO_F_INTR_PERVQ) && 115886598168Syamaguchi sc->sc_nvqs + VIRTIO_MSIX_QUEUE_VECTOR_INDEX <= nmsix) { 11599424fb3bSyamaguchi nmsix = sc->sc_nvqs + VIRTIO_MSIX_QUEUE_VECTOR_INDEX; 11609424fb3bSyamaguchi } else { 11619424fb3bSyamaguchi nmsix = 2; 11629424fb3bSyamaguchi } 11639424fb3bSyamaguchi 11641a7766abSjakllsch max_type = PCI_INTR_TYPE_MSIX; 116594b53882Syamaguchi counts[PCI_INTR_TYPE_MSIX] = nmsix; 11661a7766abSjakllsch counts[PCI_INTR_TYPE_MSI] = 0; 11671a7766abSjakllsch counts[PCI_INTR_TYPE_INTX] = 1; 11681a7766abSjakllsch } 11691a7766abSjakllsch 11701a7766abSjakllsch retry: 11711a7766abSjakllsch error = pci_intr_alloc(&psc->sc_pa, &psc->sc_ihp, counts, max_type); 11721a7766abSjakllsch if (error != 0) { 11731a7766abSjakllsch aprint_error_dev(self, "couldn't map interrupt\n"); 11741a7766abSjakllsch return -1; 11751a7766abSjakllsch } 11761a7766abSjakllsch 11771a7766abSjakllsch if (pci_intr_type(pc, psc->sc_ihp[0]) == PCI_INTR_TYPE_MSIX) { 117886598168Syamaguchi psc->sc_intr_pervq = nmsix > 2 ? true : false; 117917c07ff2Sjakllsch psc->sc_ihs = kmem_zalloc(sizeof(*psc->sc_ihs) * nmsix, 11801a7766abSjakllsch KM_SLEEP); 11811a7766abSjakllsch 11822b9a3babSyamaguchi error = virtio_pci_establish_msix_interrupts(sc, &psc->sc_pa); 11831a7766abSjakllsch if (error != 0) { 118494b53882Syamaguchi kmem_free(psc->sc_ihs, sizeof(*psc->sc_ihs) * nmsix); 118594b53882Syamaguchi pci_intr_release(pc, psc->sc_ihp, nmsix); 11861a7766abSjakllsch 11871a7766abSjakllsch /* Retry INTx */ 11881a7766abSjakllsch max_type = PCI_INTR_TYPE_INTX; 11891a7766abSjakllsch counts[PCI_INTR_TYPE_INTX] = 1; 11901a7766abSjakllsch goto retry; 11911a7766abSjakllsch } 11921a7766abSjakllsch 119394b53882Syamaguchi psc->sc_ihs_num = nmsix; 119403c5faf6Sreinoud psc->sc_devcfg_offset = VIRTIO_CONFIG_DEVICE_CONFIG_MSI; 119503c5faf6Sreinoud virtio_pci_adjust_config_region(psc); 11961a7766abSjakllsch } else if (pci_intr_type(pc, psc->sc_ihp[0]) == PCI_INTR_TYPE_INTX) { 119786598168Syamaguchi psc->sc_intr_pervq = false; 119817c07ff2Sjakllsch psc->sc_ihs = kmem_zalloc(sizeof(*psc->sc_ihs) * 1, 11991a7766abSjakllsch KM_SLEEP); 12001a7766abSjakllsch 12012b9a3babSyamaguchi error = virtio_pci_establish_intx_interrupt(sc, &psc->sc_pa); 12021a7766abSjakllsch if (error != 0) { 12031a7766abSjakllsch kmem_free(psc->sc_ihs, sizeof(*psc->sc_ihs) * 1); 12041a7766abSjakllsch pci_intr_release(pc, psc->sc_ihp, 1); 12051a7766abSjakllsch return -1; 12061a7766abSjakllsch } 12071a7766abSjakllsch 12081a7766abSjakllsch psc->sc_ihs_num = 1; 120903c5faf6Sreinoud psc->sc_devcfg_offset = VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI; 121003c5faf6Sreinoud virtio_pci_adjust_config_region(psc); 1211459e90b0Sjakllsch 1212459e90b0Sjakllsch error = pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, NULL); 1213459e90b0Sjakllsch if (error != 0) { 1214459e90b0Sjakllsch ctl = pci_conf_read(pc, tag, off + PCI_MSIX_CTL); 1215459e90b0Sjakllsch ctl &= ~PCI_MSIX_CTL_ENABLE; 1216459e90b0Sjakllsch pci_conf_write(pc, tag, off + PCI_MSIX_CTL, ctl); 1217459e90b0Sjakllsch } 12181a7766abSjakllsch } 12191a7766abSjakllsch 122086598168Syamaguchi if (!psc->sc_intr_pervq) 122186598168Syamaguchi CLR(sc->sc_flags, VIRTIO_F_INTR_PERVQ); 12221a7766abSjakllsch return 0; 12231a7766abSjakllsch } 12241a7766abSjakllsch 12251a7766abSjakllsch static void 12261a7766abSjakllsch virtio_pci_free_interrupts(struct virtio_softc *sc) 12271a7766abSjakllsch { 122807394025Sriastradh struct virtio_pci_softc * const psc = container_of(sc, 122907394025Sriastradh struct virtio_pci_softc, sc_sc); 12301a7766abSjakllsch 12311a7766abSjakllsch for (int i = 0; i < psc->sc_ihs_num; i++) { 12321a7766abSjakllsch if (psc->sc_ihs[i] == NULL) 12331a7766abSjakllsch continue; 12341a7766abSjakllsch pci_intr_disestablish(psc->sc_pa.pa_pc, psc->sc_ihs[i]); 12351a7766abSjakllsch psc->sc_ihs[i] = NULL; 12361a7766abSjakllsch } 12371a7766abSjakllsch 12386691b5f9Sriastradh if (psc->sc_ihs_num > 0) { 12396691b5f9Sriastradh pci_intr_release(psc->sc_pa.pa_pc, psc->sc_ihp, 12406691b5f9Sriastradh psc->sc_ihs_num); 12416691b5f9Sriastradh } 12421a7766abSjakllsch 12431a7766abSjakllsch if (psc->sc_ihs != NULL) { 12441a7766abSjakllsch kmem_free(psc->sc_ihs, sizeof(*psc->sc_ihs) * psc->sc_ihs_num); 12451a7766abSjakllsch psc->sc_ihs = NULL; 12461a7766abSjakllsch } 12471a7766abSjakllsch psc->sc_ihs_num = 0; 12481a7766abSjakllsch } 12491a7766abSjakllsch 12502b9a3babSyamaguchi static bool 12512b9a3babSyamaguchi virtio_pci_msix_enabled(struct virtio_pci_softc *psc) 12522b9a3babSyamaguchi { 12532b9a3babSyamaguchi pci_chipset_tag_t pc = psc->sc_pa.pa_pc; 12542b9a3babSyamaguchi 12552b9a3babSyamaguchi if (pci_intr_type(pc, psc->sc_ihp[0]) == PCI_INTR_TYPE_MSIX) 12562b9a3babSyamaguchi return true; 12572b9a3babSyamaguchi 12582b9a3babSyamaguchi return false; 12592b9a3babSyamaguchi } 12602b9a3babSyamaguchi 12611a7766abSjakllsch /* 12621a7766abSjakllsch * Interrupt handler. 12631a7766abSjakllsch */ 12641a7766abSjakllsch static int 12651a7766abSjakllsch virtio_pci_intr(void *arg) 12661a7766abSjakllsch { 12671a7766abSjakllsch struct virtio_softc *sc = arg; 126807394025Sriastradh struct virtio_pci_softc * const psc = container_of(sc, 126907394025Sriastradh struct virtio_pci_softc, sc_sc); 12701a7766abSjakllsch int isr, r = 0; 12711a7766abSjakllsch 12721a7766abSjakllsch /* check and ack the interrupt */ 127303c5faf6Sreinoud isr = bus_space_read_1(psc->sc_isr_iot, psc->sc_isr_ioh, 0); 12741a7766abSjakllsch if (isr == 0) 12751a7766abSjakllsch return 0; 12761a7766abSjakllsch if ((isr & VIRTIO_CONFIG_ISR_CONFIG_CHANGE) && 12771a7766abSjakllsch (sc->sc_config_change != NULL)) 12781a7766abSjakllsch r = (sc->sc_config_change)(sc); 12791a7766abSjakllsch if (sc->sc_intrhand != NULL) { 12801a7766abSjakllsch if (sc->sc_soft_ih != NULL) 12811a7766abSjakllsch softint_schedule(sc->sc_soft_ih); 12821a7766abSjakllsch else 12831a7766abSjakllsch r |= (sc->sc_intrhand)(sc); 12841a7766abSjakllsch } 12851a7766abSjakllsch 12861a7766abSjakllsch return r; 12871a7766abSjakllsch } 12881a7766abSjakllsch 12891a7766abSjakllsch static int 12901a7766abSjakllsch virtio_pci_msix_queue_intr(void *arg) 12911a7766abSjakllsch { 12921a7766abSjakllsch struct virtio_softc *sc = arg; 12931a7766abSjakllsch int r = 0; 12941a7766abSjakllsch 12951a7766abSjakllsch if (sc->sc_intrhand != NULL) { 12961a7766abSjakllsch if (sc->sc_soft_ih != NULL) 12971a7766abSjakllsch softint_schedule(sc->sc_soft_ih); 12981a7766abSjakllsch else 12991a7766abSjakllsch r |= (sc->sc_intrhand)(sc); 13001a7766abSjakllsch } 13011a7766abSjakllsch 13021a7766abSjakllsch return r; 13031a7766abSjakllsch } 13041a7766abSjakllsch 13051a7766abSjakllsch static int 13061a7766abSjakllsch virtio_pci_msix_config_intr(void *arg) 13071a7766abSjakllsch { 13081a7766abSjakllsch struct virtio_softc *sc = arg; 13091a7766abSjakllsch int r = 0; 13101a7766abSjakllsch 13111a7766abSjakllsch if (sc->sc_config_change != NULL) 13121a7766abSjakllsch r = (sc->sc_config_change)(sc); 13131a7766abSjakllsch return r; 13141a7766abSjakllsch } 131509582ef9Sjakllsch 131609582ef9Sjakllsch MODULE(MODULE_CLASS_DRIVER, virtio_pci, "pci,virtio"); 131709582ef9Sjakllsch 131809582ef9Sjakllsch #ifdef _MODULE 131909582ef9Sjakllsch #include "ioconf.c" 132009582ef9Sjakllsch #endif 132109582ef9Sjakllsch 132209582ef9Sjakllsch static int 132309582ef9Sjakllsch virtio_pci_modcmd(modcmd_t cmd, void *opaque) 132409582ef9Sjakllsch { 132509582ef9Sjakllsch int error = 0; 132609582ef9Sjakllsch 132709582ef9Sjakllsch #ifdef _MODULE 132809582ef9Sjakllsch switch (cmd) { 132909582ef9Sjakllsch case MODULE_CMD_INIT: 133009582ef9Sjakllsch error = config_init_component(cfdriver_ioconf_virtio_pci, 133109582ef9Sjakllsch cfattach_ioconf_virtio_pci, cfdata_ioconf_virtio_pci); 133209582ef9Sjakllsch break; 133309582ef9Sjakllsch case MODULE_CMD_FINI: 133409582ef9Sjakllsch error = config_fini_component(cfdriver_ioconf_virtio_pci, 133509582ef9Sjakllsch cfattach_ioconf_virtio_pci, cfdata_ioconf_virtio_pci); 133609582ef9Sjakllsch break; 133709582ef9Sjakllsch default: 133809582ef9Sjakllsch error = ENOTTY; 133909582ef9Sjakllsch break; 134009582ef9Sjakllsch } 134109582ef9Sjakllsch #endif 134209582ef9Sjakllsch 134309582ef9Sjakllsch return error; 134409582ef9Sjakllsch } 1345