From 4f591e19dba673a979c3f88c8a2ae77208ea6a46 Mon Sep 17 00:00:00 2001 From: Bowen Wang Date: Sat, 10 May 2025 20:50:47 +0800 Subject: [PATCH] virtio/virtio-pci: add PCI INTX interrupt fallback support Fall back to INTX interrupt mode when MSI/MSI-X is not supported or MSI-X IRQ connection fails. This improves compatibility with more virtio-pci devices. Signed-off-by: Bowen Wang --- drivers/virtio/virtio-pci-legacy.c | 21 +++++++++++----- drivers/virtio/virtio-pci-modern.c | 39 +++++++++++++++++++++++------- drivers/virtio/virtio-pci.c | 35 +++++++++++++++++++++++---- drivers/virtio/virtio-pci.h | 3 +++ 4 files changed, 78 insertions(+), 20 deletions(-) diff --git a/drivers/virtio/virtio-pci-legacy.c b/drivers/virtio/virtio-pci-legacy.c index 512a3a2b7a1..38984bbd7f1 100644 --- a/drivers/virtio/virtio-pci-legacy.c +++ b/drivers/virtio/virtio-pci-legacy.c @@ -204,6 +204,11 @@ virtio_pci_legacy_create_virtqueue(FAR struct virtio_pci_device_s *vpdev, VIRTIO_PCI_QUEUE_ADDR_SHIFT); #if CONFIG_DRIVERS_VIRTIO_PCI_POLLING_PERIOD <= 0 + if (vpdev->intx) + { + return OK; + } + pci_write_io_word(vpdev->dev, (uintptr_t)(vpdev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR), 0); @@ -241,14 +246,17 @@ void virtio_pci_legacy_delete_virtqueue(FAR struct virtio_device *vdev, index); #if CONFIG_DRIVERS_VIRTIO_PCI_POLLING_PERIOD <= 0 - pci_write_io_word(vpdev->dev, - (uintptr_t)(vpdev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR), - VIRTIO_PCI_MSI_NO_VECTOR); + if (!vpdev->intx) + { + pci_write_io_word(vpdev->dev, + (uintptr_t)(vpdev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR), + VIRTIO_PCI_MSI_NO_VECTOR); - /* Flush the write out to device */ + /* Flush the write out to device */ - pci_read_io_byte(vpdev->dev, - (uintptr_t)(vpdev->ioaddr + VIRTIO_PCI_ISR), &isr); + pci_read_io_byte(vpdev->dev, + (uintptr_t)(vpdev->ioaddr + VIRTIO_PCI_ISR), &isr); + } #endif /* Select and deactivate the queue */ @@ -411,6 +419,7 @@ virtio_pci_legacy_init_device(FAR struct virtio_pci_device_s *vpdev) return -EINVAL; } + vpdev->isr = vpdev->ioaddr + VIRTIO_PCI_ISR; vdev->id.vendor = dev->subsystem_vendor; vdev->id.device = dev->subsystem_device; diff --git a/drivers/virtio/virtio-pci-modern.c b/drivers/virtio/virtio-pci-modern.c index 07f9aa1caf8..adb2b0a62fd 100644 --- a/drivers/virtio/virtio-pci-modern.c +++ b/drivers/virtio/virtio-pci-modern.c @@ -340,13 +340,16 @@ virtio_pci_modern_create_virtqueue(FAR struct virtio_pci_device_s *vpdev, up_addrenv_va_to_pa(vq->vq_ring.used)); #if CONFIG_DRIVERS_VIRTIO_PCI_POLLING_PERIOD <= 0 - pci_write_io_word(vpdev->dev, (uintptr_t)&cfg->queue_msix_vector, 0); - pci_read_io_word(vpdev->dev, (uintptr_t)&cfg->queue_msix_vector, - &msix_vector); - if (msix_vector == VIRTIO_PCI_MSI_NO_VECTOR) + if (!vpdev->intx) { - vrterr("Msix_vector is no vector\n"); - return -EBUSY; + pci_write_io_word(vpdev->dev, (uintptr_t)&cfg->queue_msix_vector, 0); + pci_read_io_word(vpdev->dev, (uintptr_t)&cfg->queue_msix_vector, + &msix_vector); + if (msix_vector == VIRTIO_PCI_MSI_NO_VECTOR) + { + vrterr("Msix_vector is no vector\n"); + return -EBUSY; + } } #endif @@ -383,8 +386,11 @@ void virtio_pci_modern_delete_virtqueue(FAR struct virtio_device *vdev, pci_write_io_word(vpdev->dev, (uintptr_t)&cfg->queue_select, index); #if CONFIG_DRIVERS_VIRTIO_PCI_POLLING_PERIOD <= 0 - pci_write_io_word(vpdev->dev, (uintptr_t)&cfg->queue_msix_vector, - VIRTIO_PCI_MSI_NO_VECTOR); + if (!vpdev->intx) + { + pci_write_io_word(vpdev->dev, (uintptr_t)&cfg->queue_msix_vector, + VIRTIO_PCI_MSI_NO_VECTOR); + } #endif } @@ -598,6 +604,7 @@ static int virtio_pci_init_device(FAR struct virtio_pci_device_s *vpdev) uint8_t common; uint8_t notify; uint8_t device; + uint8_t isr; if (dev->device < 0x1040) { @@ -626,11 +633,19 @@ static int virtio_pci_init_device(FAR struct virtio_pci_device_s *vpdev) return -ENODEV; } + isr = virtio_pci_find_capability(dev, VIRTIO_PCI_CAP_ISR_CFG, + PCI_RESOURCE_MEM); + if (isr == 0) + { + vrterr("Missing isr capabilities\n"); + return -EINVAL; + } + notify = virtio_pci_find_capability(dev, VIRTIO_PCI_CAP_NOTIFY_CFG, PCI_RESOURCE_MEM); if (notify == 0) { - vrterr("Missing capabilities %d %d\n", common, notify); + vrterr("Missing capabilities %d %d %d\n", isr, common, notify); return -EINVAL; } @@ -646,6 +661,12 @@ static int virtio_pci_init_device(FAR struct virtio_pci_device_s *vpdev) return -EINVAL; } + vpdev->isr = virtio_pci_map_capability(vpdev, isr, NULL); + if (vpdev->isr == NULL) + { + return -EINVAL; + } + pci_read_config_dword(dev, notify + offsetof(struct virtio_pci_notify_cap_s, notify_off_multiplier), diff --git a/drivers/virtio/virtio-pci.c b/drivers/virtio/virtio-pci.c index 2bd87ef1040..a69eb54e832 100644 --- a/drivers/virtio/virtio-pci.c +++ b/drivers/virtio/virtio-pci.c @@ -115,6 +115,16 @@ static void virtio_pci_vq_callback(FAR struct virtio_pci_device_s *vpdev) static int virtio_pci_interrupt(int irq, FAR void *context, FAR void *arg) { FAR struct virtio_pci_device_s *vpdev = arg; + uint8_t isr; + + if (vpdev->intx) + { + pci_read_io_byte(vpdev->dev, (uintptr_t)vpdev->isr, &isr); + if (isr == 0) + { + return OK; + } + } virtio_pci_vq_callback(vpdev); return OK; @@ -227,8 +237,17 @@ static int virtio_pci_probe(FAR struct pci_device_s *dev) ret = pci_connect_irq(vpdev->dev, &vpdev->irq, 1); if (ret < 0) { - vrterr("Failed to connect MSI %d\n", ret); - goto err_with_irq; + vrterr("Failed to connect MSI %d, try legacy irq mode\n", ret); + pci_release_irq(vpdev->dev, &vpdev->irq, 1); + ret = pci_get_irq(vpdev->dev); + if (ret < 0) + { + vrterr("Failed to get legacy irq %d\n", ret); + goto err_with_enable; + } + + vpdev->irq = ret; + vpdev->intx = true; } irq_attach(vpdev->irq, virtio_pci_interrupt, vpdev); @@ -251,8 +270,11 @@ static int virtio_pci_probe(FAR struct pci_device_s *dev) err_with_attach: #if CONFIG_DRIVERS_VIRTIO_PCI_POLLING_PERIOD <= 0 irq_detach(vpdev->irq); -err_with_irq: - pci_release_irq(vpdev->dev, &vpdev->irq, 1); + if (!vpdev->intx) + { + pci_release_irq(vpdev->dev, &vpdev->irq, 1); + } + #endif err_with_enable: pci_clear_master(dev); @@ -274,7 +296,10 @@ static void virtio_pci_remove(FAR struct pci_device_s *dev) #if CONFIG_DRIVERS_VIRTIO_PCI_POLLING_PERIOD <= 0 irq_detach(vpdev->irq); - pci_release_irq(vpdev->dev, &vpdev->irq, 1); + if (!vpdev->intx) + { + pci_release_irq(vpdev->dev, &vpdev->irq, 1); + } #endif pci_clear_master(dev); diff --git a/drivers/virtio/virtio-pci.h b/drivers/virtio/virtio-pci.h index 0f38a01d85d..17588f2bd03 100644 --- a/drivers/virtio/virtio-pci.h +++ b/drivers/virtio/virtio-pci.h @@ -81,10 +81,13 @@ struct virtio_pci_device_s */ #if CONFIG_DRIVERS_VIRTIO_PCI_POLLING_PERIOD <= 0 int irq; + bool intx; /* INTX irq mode */ #else struct wdog_s wdog; #endif + FAR void *isr; + /* for modern */ FAR void *common;