162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include "linux/virtio_pci.h" 462306a36Sopenharmony_ci#include <linux/virtio_pci_legacy.h> 562306a36Sopenharmony_ci#include <linux/module.h> 662306a36Sopenharmony_ci#include <linux/pci.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/* 1062306a36Sopenharmony_ci * vp_legacy_probe: probe the legacy virtio pci device, note that the 1162306a36Sopenharmony_ci * caller is required to enable PCI device before calling this function. 1262306a36Sopenharmony_ci * @ldev: the legacy virtio-pci device 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Return 0 on succeed otherwise fail 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ciint vp_legacy_probe(struct virtio_pci_legacy_device *ldev) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci struct pci_dev *pci_dev = ldev->pci_dev; 1962306a36Sopenharmony_ci int rc; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci /* We only own devices >= 0x1000 and <= 0x103f: leave the rest. */ 2262306a36Sopenharmony_ci if (pci_dev->device < 0x1000 || pci_dev->device > 0x103f) 2362306a36Sopenharmony_ci return -ENODEV; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci if (pci_dev->revision != VIRTIO_PCI_ABI_VERSION) 2662306a36Sopenharmony_ci return -ENODEV; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci rc = dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(64)); 2962306a36Sopenharmony_ci if (rc) { 3062306a36Sopenharmony_ci rc = dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(32)); 3162306a36Sopenharmony_ci } else { 3262306a36Sopenharmony_ci /* 3362306a36Sopenharmony_ci * The virtio ring base address is expressed as a 32-bit PFN, 3462306a36Sopenharmony_ci * with a page size of 1 << VIRTIO_PCI_QUEUE_ADDR_SHIFT. 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci dma_set_coherent_mask(&pci_dev->dev, 3762306a36Sopenharmony_ci DMA_BIT_MASK(32 + VIRTIO_PCI_QUEUE_ADDR_SHIFT)); 3862306a36Sopenharmony_ci } 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (rc) 4162306a36Sopenharmony_ci dev_warn(&pci_dev->dev, "Failed to enable 64-bit or 32-bit DMA. Trying to continue, but this might not work.\n"); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci rc = pci_request_region(pci_dev, 0, "virtio-pci-legacy"); 4462306a36Sopenharmony_ci if (rc) 4562306a36Sopenharmony_ci return rc; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci ldev->ioaddr = pci_iomap(pci_dev, 0, 0); 4862306a36Sopenharmony_ci if (!ldev->ioaddr) { 4962306a36Sopenharmony_ci rc = -EIO; 5062306a36Sopenharmony_ci goto err_iomap; 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci ldev->isr = ldev->ioaddr + VIRTIO_PCI_ISR; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci ldev->id.vendor = pci_dev->subsystem_vendor; 5662306a36Sopenharmony_ci ldev->id.device = pci_dev->subsystem_device; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci return 0; 5962306a36Sopenharmony_cierr_iomap: 6062306a36Sopenharmony_ci pci_release_region(pci_dev, 0); 6162306a36Sopenharmony_ci return rc; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vp_legacy_probe); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* 6662306a36Sopenharmony_ci * vp_legacy_probe: remove and cleanup the legacy virtio pci device 6762306a36Sopenharmony_ci * @ldev: the legacy virtio-pci device 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_civoid vp_legacy_remove(struct virtio_pci_legacy_device *ldev) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci struct pci_dev *pci_dev = ldev->pci_dev; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci pci_iounmap(pci_dev, ldev->ioaddr); 7462306a36Sopenharmony_ci pci_release_region(pci_dev, 0); 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vp_legacy_remove); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* 7962306a36Sopenharmony_ci * vp_legacy_get_features - get features from device 8062306a36Sopenharmony_ci * @ldev: the legacy virtio-pci device 8162306a36Sopenharmony_ci * 8262306a36Sopenharmony_ci * Returns the features read from the device 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_ciu64 vp_legacy_get_features(struct virtio_pci_legacy_device *ldev) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci return ioread32(ldev->ioaddr + VIRTIO_PCI_HOST_FEATURES); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vp_legacy_get_features); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* 9262306a36Sopenharmony_ci * vp_legacy_get_driver_features - get driver features from device 9362306a36Sopenharmony_ci * @ldev: the legacy virtio-pci device 9462306a36Sopenharmony_ci * 9562306a36Sopenharmony_ci * Returns the driver features read from the device 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_ciu64 vp_legacy_get_driver_features(struct virtio_pci_legacy_device *ldev) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci return ioread32(ldev->ioaddr + VIRTIO_PCI_GUEST_FEATURES); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vp_legacy_get_driver_features); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/* 10462306a36Sopenharmony_ci * vp_legacy_set_features - set features to device 10562306a36Sopenharmony_ci * @ldev: the legacy virtio-pci device 10662306a36Sopenharmony_ci * @features: the features set to device 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_civoid vp_legacy_set_features(struct virtio_pci_legacy_device *ldev, 10962306a36Sopenharmony_ci u32 features) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci iowrite32(features, ldev->ioaddr + VIRTIO_PCI_GUEST_FEATURES); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vp_legacy_set_features); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* 11662306a36Sopenharmony_ci * vp_legacy_get_status - get the device status 11762306a36Sopenharmony_ci * @ldev: the legacy virtio-pci device 11862306a36Sopenharmony_ci * 11962306a36Sopenharmony_ci * Returns the status read from device 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_ciu8 vp_legacy_get_status(struct virtio_pci_legacy_device *ldev) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci return ioread8(ldev->ioaddr + VIRTIO_PCI_STATUS); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vp_legacy_get_status); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/* 12862306a36Sopenharmony_ci * vp_legacy_set_status - set status to device 12962306a36Sopenharmony_ci * @ldev: the legacy virtio-pci device 13062306a36Sopenharmony_ci * @status: the status set to device 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_civoid vp_legacy_set_status(struct virtio_pci_legacy_device *ldev, 13362306a36Sopenharmony_ci u8 status) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci iowrite8(status, ldev->ioaddr + VIRTIO_PCI_STATUS); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vp_legacy_set_status); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci/* 14062306a36Sopenharmony_ci * vp_legacy_queue_vector - set the MSIX vector for a specific virtqueue 14162306a36Sopenharmony_ci * @ldev: the legacy virtio-pci device 14262306a36Sopenharmony_ci * @index: queue index 14362306a36Sopenharmony_ci * @vector: the config vector 14462306a36Sopenharmony_ci * 14562306a36Sopenharmony_ci * Returns the config vector read from the device 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_ciu16 vp_legacy_queue_vector(struct virtio_pci_legacy_device *ldev, 14862306a36Sopenharmony_ci u16 index, u16 vector) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci iowrite16(index, ldev->ioaddr + VIRTIO_PCI_QUEUE_SEL); 15162306a36Sopenharmony_ci iowrite16(vector, ldev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR); 15262306a36Sopenharmony_ci /* Flush the write out to device */ 15362306a36Sopenharmony_ci return ioread16(ldev->ioaddr + VIRTIO_MSI_QUEUE_VECTOR); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vp_legacy_queue_vector); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci/* 15862306a36Sopenharmony_ci * vp_legacy_config_vector - set the vector for config interrupt 15962306a36Sopenharmony_ci * @ldev: the legacy virtio-pci device 16062306a36Sopenharmony_ci * @vector: the config vector 16162306a36Sopenharmony_ci * 16262306a36Sopenharmony_ci * Returns the config vector read from the device 16362306a36Sopenharmony_ci */ 16462306a36Sopenharmony_ciu16 vp_legacy_config_vector(struct virtio_pci_legacy_device *ldev, 16562306a36Sopenharmony_ci u16 vector) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci /* Setup the vector used for configuration events */ 16862306a36Sopenharmony_ci iowrite16(vector, ldev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR); 16962306a36Sopenharmony_ci /* Verify we had enough resources to assign the vector */ 17062306a36Sopenharmony_ci /* Will also flush the write out to device */ 17162306a36Sopenharmony_ci return ioread16(ldev->ioaddr + VIRTIO_MSI_CONFIG_VECTOR); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vp_legacy_config_vector); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci/* 17662306a36Sopenharmony_ci * vp_legacy_set_queue_address - set the virtqueue address 17762306a36Sopenharmony_ci * @ldev: the legacy virtio-pci device 17862306a36Sopenharmony_ci * @index: the queue index 17962306a36Sopenharmony_ci * @queue_pfn: pfn of the virtqueue 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_civoid vp_legacy_set_queue_address(struct virtio_pci_legacy_device *ldev, 18262306a36Sopenharmony_ci u16 index, u32 queue_pfn) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci iowrite16(index, ldev->ioaddr + VIRTIO_PCI_QUEUE_SEL); 18562306a36Sopenharmony_ci iowrite32(queue_pfn, ldev->ioaddr + VIRTIO_PCI_QUEUE_PFN); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vp_legacy_set_queue_address); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci/* 19062306a36Sopenharmony_ci * vp_legacy_get_queue_enable - enable a virtqueue 19162306a36Sopenharmony_ci * @ldev: the legacy virtio-pci device 19262306a36Sopenharmony_ci * @index: the queue index 19362306a36Sopenharmony_ci * 19462306a36Sopenharmony_ci * Returns whether a virtqueue is enabled or not 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_cibool vp_legacy_get_queue_enable(struct virtio_pci_legacy_device *ldev, 19762306a36Sopenharmony_ci u16 index) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci iowrite16(index, ldev->ioaddr + VIRTIO_PCI_QUEUE_SEL); 20062306a36Sopenharmony_ci return ioread32(ldev->ioaddr + VIRTIO_PCI_QUEUE_PFN); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vp_legacy_get_queue_enable); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci/* 20562306a36Sopenharmony_ci * vp_legacy_get_queue_size - get size for a virtqueue 20662306a36Sopenharmony_ci * @ldev: the legacy virtio-pci device 20762306a36Sopenharmony_ci * @index: the queue index 20862306a36Sopenharmony_ci * 20962306a36Sopenharmony_ci * Returns the size of the virtqueue 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_ciu16 vp_legacy_get_queue_size(struct virtio_pci_legacy_device *ldev, 21262306a36Sopenharmony_ci u16 index) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci iowrite16(index, ldev->ioaddr + VIRTIO_PCI_QUEUE_SEL); 21562306a36Sopenharmony_ci return ioread16(ldev->ioaddr + VIRTIO_PCI_QUEUE_NUM); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vp_legacy_get_queue_size); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ciMODULE_VERSION("0.1"); 22062306a36Sopenharmony_ciMODULE_DESCRIPTION("Legacy Virtio PCI Device"); 22162306a36Sopenharmony_ciMODULE_AUTHOR("Wu Zongyong <wuzongyong@linux.alibaba.com>"); 22262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 223