| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * UIO PCI Express sva driver
- *
- * Copyright (c) 2025 Beijing Institute of Open Source Chip (BOSC)
- */
- #include <linux/device.h>
- #include <linux/module.h>
- #include <linux/pci.h>
- #include <linux/uio_driver.h>
- #include <linux/iommu.h>
- struct uio_pci_sva_dev {
- struct pci_dev *pdev;
- struct uio_info info;
- struct iommu_sva *sva_handle;
- int pasid;
- };
- static irqreturn_t irq_handler(int irq, struct uio_info *dev_info)
- {
- return IRQ_HANDLED;
- }
- static int uio_pci_sva_open(struct uio_info *info, struct inode *inode)
- {
- struct iommu_sva *handle;
- struct uio_pci_sva_dev *udev = info->priv;
- struct iommu_domain *domain;
- if (!udev || !udev->pdev)
- return -ENODEV;
- domain = iommu_get_domain_for_dev(&udev->pdev->dev);
- if (domain)
- iommu_detach_device(domain, &udev->pdev->dev);
- handle = iommu_sva_bind_device(&udev->pdev->dev, current->mm);
- if (IS_ERR(handle))
- return -EINVAL;
- udev->pasid = iommu_sva_get_pasid(handle);
- udev->sva_handle = handle;
- return 0;
- }
- static int uio_pci_sva_release(struct uio_info *info, struct inode *inode)
- {
- struct uio_pci_sva_dev *udev = info->priv;
- if (!udev || !udev->pdev)
- return -ENODEV;
- iommu_sva_unbind_device(udev->sva_handle);
- return 0;
- }
- static int probe(struct pci_dev *pdev, const struct pci_device_id *id)
- {
- struct uio_pci_sva_dev *udev;
- int ret, i, irq = 0;
- ret = pci_enable_device(pdev);
- if (ret) {
- dev_err(&pdev->dev, "pci_enable_device failed: %d\n", ret);
- return ret;
- }
- ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
- if (ret)
- goto out_disable;
- pci_set_master(pdev);
- ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSIX | PCI_IRQ_MSI);
- if (ret > 0) {
- irq = pci_irq_vector(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "Failed to get MSI vector\n");
- ret = irq;
- goto out_disable;
- }
- } else
- dev_warn(&pdev->dev,
- "No IRQ vectors available (%d), using polling\n", ret);
- udev = devm_kzalloc(&pdev->dev, sizeof(struct uio_pci_sva_dev),
- GFP_KERNEL);
- if (!udev) {
- ret = -ENOMEM;
- goto out_disable;
- }
- udev->pdev = pdev;
- udev->info.name = "uio_pci_sva";
- udev->info.version = "0.0.1";
- udev->info.open = uio_pci_sva_open;
- udev->info.release = uio_pci_sva_release;
- udev->info.irq = irq;
- udev->info.handler = irq_handler;
- udev->info.priv = udev;
- for (i = 0; i < MAX_UIO_MAPS; i++) {
- struct resource *r = &pdev->resource[i];
- struct uio_mem *uiomem = &udev->info.mem[i];
- if (r->flags != (IORESOURCE_SIZEALIGN | IORESOURCE_MEM))
- continue;
- if (uiomem >= &udev->info.mem[MAX_UIO_MAPS]) {
- dev_warn(&pdev->dev, "Do not support more than %d iomem\n",
- MAX_UIO_MAPS);
- break;
- }
- uiomem->memtype = UIO_MEM_PHYS;
- uiomem->addr = r->start & PAGE_MASK;
- uiomem->offs = r->start & ~PAGE_MASK;
- uiomem->size =
- (uiomem->offs + resource_size(r) + PAGE_SIZE - 1) &
- PAGE_MASK;
- uiomem->name = r->name;
- }
- ret = devm_uio_register_device(&pdev->dev, &udev->info);
- if (ret) {
- dev_err(&pdev->dev, "Failed to register uio device\n");
- goto out_free;
- }
- pci_set_drvdata(pdev, udev);
- return 0;
- out_free:
- kfree(udev);
- out_disable:
- pci_disable_device(pdev);
- return ret;
- }
- static void remove(struct pci_dev *pdev)
- {
- struct uio_pci_sva_dev *udev = pci_get_drvdata(pdev);
- pci_release_regions(pdev);
- pci_disable_device(pdev);
- kfree(udev);
- }
- static ssize_t pasid_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct pci_dev *pdev = to_pci_dev(dev);
- struct uio_pci_sva_dev *udev = pci_get_drvdata(pdev);
- return sysfs_emit(buf, "%d\n", udev->pasid);
- }
- static DEVICE_ATTR_RO(pasid);
- static struct attribute *uio_pci_sva_attrs[] = {
- &dev_attr_pasid.attr,
- NULL
- };
- static const struct attribute_group uio_pci_sva_attr_group = {
- .attrs = uio_pci_sva_attrs,
- };
- static const struct attribute_group *uio_pci_sva_attr_groups[] = {
- &uio_pci_sva_attr_group,
- NULL
- };
- static struct pci_driver uio_pci_generic_sva_driver = {
- .name = "uio_pci_sva",
- .dev_groups = uio_pci_sva_attr_groups,
- .id_table = NULL,
- .probe = probe,
- .remove = remove,
- };
- module_pci_driver(uio_pci_generic_sva_driver);
- MODULE_VERSION("0.0.01");
- MODULE_LICENSE("GPL v2");
- MODULE_AUTHOR("Yaxing Guo <guoyaxing@bosc.ac.cn>");
- MODULE_DESCRIPTION("Generic UIO sva driver for PCI");
|