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 <wangbowen6@xiaomi.com>
This commit is contained in:
Bowen Wang 2025-05-10 20:50:47 +08:00 committed by Alan C. Assis
parent 63c147bfa4
commit 4f591e19db
4 changed files with 78 additions and 20 deletions

View file

@ -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;

View file

@ -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),

View file

@ -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);

View file

@ -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;