162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2013 - Virtual Open Systems 462306a36Sopenharmony_ci * Author: Antonios Motakis <a.motakis@virtualopensystems.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/vfio.h> 1062306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1162306a36Sopenharmony_ci#include <linux/amba/bus.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "vfio_platform_private.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define DRIVER_VERSION "0.10" 1662306a36Sopenharmony_ci#define DRIVER_AUTHOR "Antonios Motakis <a.motakis@virtualopensystems.com>" 1762306a36Sopenharmony_ci#define DRIVER_DESC "VFIO for AMBA devices - User Level meta-driver" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* probing devices from the AMBA bus */ 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic struct resource *get_amba_resource(struct vfio_platform_device *vdev, 2262306a36Sopenharmony_ci int i) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci struct amba_device *adev = (struct amba_device *) vdev->opaque; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci if (i == 0) 2762306a36Sopenharmony_ci return &adev->res; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci return NULL; 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic int get_amba_irq(struct vfio_platform_device *vdev, int i) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci struct amba_device *adev = (struct amba_device *) vdev->opaque; 3562306a36Sopenharmony_ci int ret = 0; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (i < AMBA_NR_IRQS) 3862306a36Sopenharmony_ci ret = adev->irq[i]; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci /* zero is an unset IRQ for AMBA devices */ 4162306a36Sopenharmony_ci return ret ? ret : -ENXIO; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic int vfio_amba_init_dev(struct vfio_device *core_vdev) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci struct vfio_platform_device *vdev = 4762306a36Sopenharmony_ci container_of(core_vdev, struct vfio_platform_device, vdev); 4862306a36Sopenharmony_ci struct amba_device *adev = to_amba_device(core_vdev->dev); 4962306a36Sopenharmony_ci int ret; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci vdev->name = kasprintf(GFP_KERNEL, "vfio-amba-%08x", adev->periphid); 5262306a36Sopenharmony_ci if (!vdev->name) 5362306a36Sopenharmony_ci return -ENOMEM; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci vdev->opaque = (void *) adev; 5662306a36Sopenharmony_ci vdev->flags = VFIO_DEVICE_FLAGS_AMBA; 5762306a36Sopenharmony_ci vdev->get_resource = get_amba_resource; 5862306a36Sopenharmony_ci vdev->get_irq = get_amba_irq; 5962306a36Sopenharmony_ci vdev->reset_required = false; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci ret = vfio_platform_init_common(vdev); 6262306a36Sopenharmony_ci if (ret) 6362306a36Sopenharmony_ci kfree(vdev->name); 6462306a36Sopenharmony_ci return ret; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic const struct vfio_device_ops vfio_amba_ops; 6862306a36Sopenharmony_cistatic int vfio_amba_probe(struct amba_device *adev, const struct amba_id *id) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct vfio_platform_device *vdev; 7162306a36Sopenharmony_ci int ret; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci vdev = vfio_alloc_device(vfio_platform_device, vdev, &adev->dev, 7462306a36Sopenharmony_ci &vfio_amba_ops); 7562306a36Sopenharmony_ci if (IS_ERR(vdev)) 7662306a36Sopenharmony_ci return PTR_ERR(vdev); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci ret = vfio_register_group_dev(&vdev->vdev); 7962306a36Sopenharmony_ci if (ret) 8062306a36Sopenharmony_ci goto out_put_vdev; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci pm_runtime_enable(&adev->dev); 8362306a36Sopenharmony_ci dev_set_drvdata(&adev->dev, vdev); 8462306a36Sopenharmony_ci return 0; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ciout_put_vdev: 8762306a36Sopenharmony_ci vfio_put_device(&vdev->vdev); 8862306a36Sopenharmony_ci return ret; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic void vfio_amba_release_dev(struct vfio_device *core_vdev) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct vfio_platform_device *vdev = 9462306a36Sopenharmony_ci container_of(core_vdev, struct vfio_platform_device, vdev); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci vfio_platform_release_common(vdev); 9762306a36Sopenharmony_ci kfree(vdev->name); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic void vfio_amba_remove(struct amba_device *adev) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct vfio_platform_device *vdev = dev_get_drvdata(&adev->dev); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci vfio_unregister_group_dev(&vdev->vdev); 10562306a36Sopenharmony_ci pm_runtime_disable(vdev->device); 10662306a36Sopenharmony_ci vfio_put_device(&vdev->vdev); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic const struct vfio_device_ops vfio_amba_ops = { 11062306a36Sopenharmony_ci .name = "vfio-amba", 11162306a36Sopenharmony_ci .init = vfio_amba_init_dev, 11262306a36Sopenharmony_ci .release = vfio_amba_release_dev, 11362306a36Sopenharmony_ci .open_device = vfio_platform_open_device, 11462306a36Sopenharmony_ci .close_device = vfio_platform_close_device, 11562306a36Sopenharmony_ci .ioctl = vfio_platform_ioctl, 11662306a36Sopenharmony_ci .read = vfio_platform_read, 11762306a36Sopenharmony_ci .write = vfio_platform_write, 11862306a36Sopenharmony_ci .mmap = vfio_platform_mmap, 11962306a36Sopenharmony_ci .bind_iommufd = vfio_iommufd_physical_bind, 12062306a36Sopenharmony_ci .unbind_iommufd = vfio_iommufd_physical_unbind, 12162306a36Sopenharmony_ci .attach_ioas = vfio_iommufd_physical_attach_ioas, 12262306a36Sopenharmony_ci .detach_ioas = vfio_iommufd_physical_detach_ioas, 12362306a36Sopenharmony_ci}; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic const struct amba_id pl330_ids[] = { 12662306a36Sopenharmony_ci { 0, 0 }, 12762306a36Sopenharmony_ci}; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(amba, pl330_ids); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic struct amba_driver vfio_amba_driver = { 13262306a36Sopenharmony_ci .probe = vfio_amba_probe, 13362306a36Sopenharmony_ci .remove = vfio_amba_remove, 13462306a36Sopenharmony_ci .id_table = pl330_ids, 13562306a36Sopenharmony_ci .drv = { 13662306a36Sopenharmony_ci .name = "vfio-amba", 13762306a36Sopenharmony_ci .owner = THIS_MODULE, 13862306a36Sopenharmony_ci }, 13962306a36Sopenharmony_ci .driver_managed_dma = true, 14062306a36Sopenharmony_ci}; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cimodule_amba_driver(vfio_amba_driver); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ciMODULE_VERSION(DRIVER_VERSION); 14562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 14662306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 14762306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 148