18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2013-2016 Freescale Semiconductor Inc. 48c2ecf20Sopenharmony_ci * Copyright 2016-2017,2019-2020 NXP 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/device.h> 88c2ecf20Sopenharmony_ci#include <linux/iommu.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/mutex.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/types.h> 138c2ecf20Sopenharmony_ci#include <linux/vfio.h> 148c2ecf20Sopenharmony_ci#include <linux/fsl/mc.h> 158c2ecf20Sopenharmony_ci#include <linux/delay.h> 168c2ecf20Sopenharmony_ci#include <linux/io-64-nonatomic-hi-lo.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "vfio_fsl_mc_private.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic struct fsl_mc_driver vfio_fsl_mc_driver; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(reflck_lock); 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic void vfio_fsl_mc_reflck_get(struct vfio_fsl_mc_reflck *reflck) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci kref_get(&reflck->kref); 278c2ecf20Sopenharmony_ci} 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic void vfio_fsl_mc_reflck_release(struct kref *kref) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct vfio_fsl_mc_reflck *reflck = container_of(kref, 328c2ecf20Sopenharmony_ci struct vfio_fsl_mc_reflck, 338c2ecf20Sopenharmony_ci kref); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci mutex_destroy(&reflck->lock); 368c2ecf20Sopenharmony_ci kfree(reflck); 378c2ecf20Sopenharmony_ci mutex_unlock(&reflck_lock); 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic void vfio_fsl_mc_reflck_put(struct vfio_fsl_mc_reflck *reflck) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci kref_put_mutex(&reflck->kref, vfio_fsl_mc_reflck_release, &reflck_lock); 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic struct vfio_fsl_mc_reflck *vfio_fsl_mc_reflck_alloc(void) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci struct vfio_fsl_mc_reflck *reflck; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci reflck = kzalloc(sizeof(*reflck), GFP_KERNEL); 508c2ecf20Sopenharmony_ci if (!reflck) 518c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci kref_init(&reflck->kref); 548c2ecf20Sopenharmony_ci mutex_init(&reflck->lock); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci return reflck; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic int vfio_fsl_mc_reflck_attach(struct vfio_fsl_mc_device *vdev) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci int ret = 0; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci mutex_lock(&reflck_lock); 648c2ecf20Sopenharmony_ci if (is_fsl_mc_bus_dprc(vdev->mc_dev)) { 658c2ecf20Sopenharmony_ci vdev->reflck = vfio_fsl_mc_reflck_alloc(); 668c2ecf20Sopenharmony_ci ret = PTR_ERR_OR_ZERO(vdev->reflck); 678c2ecf20Sopenharmony_ci } else { 688c2ecf20Sopenharmony_ci struct device *mc_cont_dev = vdev->mc_dev->dev.parent; 698c2ecf20Sopenharmony_ci struct vfio_device *device; 708c2ecf20Sopenharmony_ci struct vfio_fsl_mc_device *cont_vdev; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci device = vfio_device_get_from_dev(mc_cont_dev); 738c2ecf20Sopenharmony_ci if (!device) { 748c2ecf20Sopenharmony_ci ret = -ENODEV; 758c2ecf20Sopenharmony_ci goto unlock; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci cont_vdev = vfio_device_data(device); 798c2ecf20Sopenharmony_ci if (!cont_vdev || !cont_vdev->reflck) { 808c2ecf20Sopenharmony_ci vfio_device_put(device); 818c2ecf20Sopenharmony_ci ret = -ENODEV; 828c2ecf20Sopenharmony_ci goto unlock; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci vfio_fsl_mc_reflck_get(cont_vdev->reflck); 858c2ecf20Sopenharmony_ci vdev->reflck = cont_vdev->reflck; 868c2ecf20Sopenharmony_ci vfio_device_put(device); 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ciunlock: 908c2ecf20Sopenharmony_ci mutex_unlock(&reflck_lock); 918c2ecf20Sopenharmony_ci return ret; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic int vfio_fsl_mc_regions_init(struct vfio_fsl_mc_device *vdev) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev = vdev->mc_dev; 978c2ecf20Sopenharmony_ci int count = mc_dev->obj_desc.region_count; 988c2ecf20Sopenharmony_ci int i; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci vdev->regions = kcalloc(count, sizeof(struct vfio_fsl_mc_region), 1018c2ecf20Sopenharmony_ci GFP_KERNEL); 1028c2ecf20Sopenharmony_ci if (!vdev->regions) 1038c2ecf20Sopenharmony_ci return -ENOMEM; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 1068c2ecf20Sopenharmony_ci struct resource *res = &mc_dev->regions[i]; 1078c2ecf20Sopenharmony_ci int no_mmap = is_fsl_mc_bus_dprc(mc_dev); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci vdev->regions[i].addr = res->start; 1108c2ecf20Sopenharmony_ci vdev->regions[i].size = resource_size(res); 1118c2ecf20Sopenharmony_ci vdev->regions[i].type = mc_dev->regions[i].flags & IORESOURCE_BITS; 1128c2ecf20Sopenharmony_ci /* 1138c2ecf20Sopenharmony_ci * Only regions addressed with PAGE granularity may be 1148c2ecf20Sopenharmony_ci * MMAPed securely. 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_ci if (!no_mmap && !(vdev->regions[i].addr & ~PAGE_MASK) && 1178c2ecf20Sopenharmony_ci !(vdev->regions[i].size & ~PAGE_MASK)) 1188c2ecf20Sopenharmony_ci vdev->regions[i].flags |= 1198c2ecf20Sopenharmony_ci VFIO_REGION_INFO_FLAG_MMAP; 1208c2ecf20Sopenharmony_ci vdev->regions[i].flags |= VFIO_REGION_INFO_FLAG_READ; 1218c2ecf20Sopenharmony_ci if (!(mc_dev->regions[i].flags & IORESOURCE_READONLY)) 1228c2ecf20Sopenharmony_ci vdev->regions[i].flags |= VFIO_REGION_INFO_FLAG_WRITE; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return 0; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic void vfio_fsl_mc_regions_cleanup(struct vfio_fsl_mc_device *vdev) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev = vdev->mc_dev; 1318c2ecf20Sopenharmony_ci int i; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci for (i = 0; i < mc_dev->obj_desc.region_count; i++) 1348c2ecf20Sopenharmony_ci iounmap(vdev->regions[i].ioaddr); 1358c2ecf20Sopenharmony_ci kfree(vdev->regions); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic int vfio_fsl_mc_open(void *device_data) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci struct vfio_fsl_mc_device *vdev = device_data; 1418c2ecf20Sopenharmony_ci int ret; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (!try_module_get(THIS_MODULE)) 1448c2ecf20Sopenharmony_ci return -ENODEV; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci mutex_lock(&vdev->reflck->lock); 1478c2ecf20Sopenharmony_ci if (!vdev->refcnt) { 1488c2ecf20Sopenharmony_ci ret = vfio_fsl_mc_regions_init(vdev); 1498c2ecf20Sopenharmony_ci if (ret) 1508c2ecf20Sopenharmony_ci goto err_reg_init; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci vdev->refcnt++; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci mutex_unlock(&vdev->reflck->lock); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cierr_reg_init: 1598c2ecf20Sopenharmony_ci mutex_unlock(&vdev->reflck->lock); 1608c2ecf20Sopenharmony_ci module_put(THIS_MODULE); 1618c2ecf20Sopenharmony_ci return ret; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic void vfio_fsl_mc_release(void *device_data) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct vfio_fsl_mc_device *vdev = device_data; 1678c2ecf20Sopenharmony_ci int ret; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci mutex_lock(&vdev->reflck->lock); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (!(--vdev->refcnt)) { 1728c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev = vdev->mc_dev; 1738c2ecf20Sopenharmony_ci struct device *cont_dev = fsl_mc_cont_dev(&mc_dev->dev); 1748c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_cont = to_fsl_mc_device(cont_dev); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci vfio_fsl_mc_regions_cleanup(vdev); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* reset the device before cleaning up the interrupts */ 1798c2ecf20Sopenharmony_ci ret = dprc_reset_container(mc_cont->mc_io, 0, 1808c2ecf20Sopenharmony_ci mc_cont->mc_handle, 1818c2ecf20Sopenharmony_ci mc_cont->obj_desc.id, 1828c2ecf20Sopenharmony_ci DPRC_RESET_OPTION_NON_RECURSIVE); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (ret) { 1858c2ecf20Sopenharmony_ci dev_warn(&mc_cont->dev, "VFIO_FLS_MC: reset device has failed (%d)\n", 1868c2ecf20Sopenharmony_ci ret); 1878c2ecf20Sopenharmony_ci WARN_ON(1); 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci vfio_fsl_mc_irqs_cleanup(vdev); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci fsl_mc_cleanup_irq_pool(mc_cont); 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci mutex_unlock(&vdev->reflck->lock); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci module_put(THIS_MODULE); 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic long vfio_fsl_mc_ioctl(void *device_data, unsigned int cmd, 2018c2ecf20Sopenharmony_ci unsigned long arg) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci unsigned long minsz; 2048c2ecf20Sopenharmony_ci struct vfio_fsl_mc_device *vdev = device_data; 2058c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev = vdev->mc_dev; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci switch (cmd) { 2088c2ecf20Sopenharmony_ci case VFIO_DEVICE_GET_INFO: 2098c2ecf20Sopenharmony_ci { 2108c2ecf20Sopenharmony_ci struct vfio_device_info info; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci minsz = offsetofend(struct vfio_device_info, num_irqs); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (copy_from_user(&info, (void __user *)arg, minsz)) 2158c2ecf20Sopenharmony_ci return -EFAULT; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (info.argsz < minsz) 2188c2ecf20Sopenharmony_ci return -EINVAL; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci info.flags = VFIO_DEVICE_FLAGS_FSL_MC; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (is_fsl_mc_bus_dprc(mc_dev)) 2238c2ecf20Sopenharmony_ci info.flags |= VFIO_DEVICE_FLAGS_RESET; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci info.num_regions = mc_dev->obj_desc.region_count; 2268c2ecf20Sopenharmony_ci info.num_irqs = mc_dev->obj_desc.irq_count; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci return copy_to_user((void __user *)arg, &info, minsz) ? 2298c2ecf20Sopenharmony_ci -EFAULT : 0; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci case VFIO_DEVICE_GET_REGION_INFO: 2328c2ecf20Sopenharmony_ci { 2338c2ecf20Sopenharmony_ci struct vfio_region_info info; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci minsz = offsetofend(struct vfio_region_info, offset); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (copy_from_user(&info, (void __user *)arg, minsz)) 2388c2ecf20Sopenharmony_ci return -EFAULT; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (info.argsz < minsz) 2418c2ecf20Sopenharmony_ci return -EINVAL; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (info.index >= mc_dev->obj_desc.region_count) 2448c2ecf20Sopenharmony_ci return -EINVAL; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* map offset to the physical address */ 2478c2ecf20Sopenharmony_ci info.offset = VFIO_FSL_MC_INDEX_TO_OFFSET(info.index); 2488c2ecf20Sopenharmony_ci info.size = vdev->regions[info.index].size; 2498c2ecf20Sopenharmony_ci info.flags = vdev->regions[info.index].flags; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)arg, &info, minsz)) 2528c2ecf20Sopenharmony_ci return -EFAULT; 2538c2ecf20Sopenharmony_ci return 0; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci case VFIO_DEVICE_GET_IRQ_INFO: 2568c2ecf20Sopenharmony_ci { 2578c2ecf20Sopenharmony_ci struct vfio_irq_info info; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci minsz = offsetofend(struct vfio_irq_info, count); 2608c2ecf20Sopenharmony_ci if (copy_from_user(&info, (void __user *)arg, minsz)) 2618c2ecf20Sopenharmony_ci return -EFAULT; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (info.argsz < minsz) 2648c2ecf20Sopenharmony_ci return -EINVAL; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (info.index >= mc_dev->obj_desc.irq_count) 2678c2ecf20Sopenharmony_ci return -EINVAL; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci info.flags = VFIO_IRQ_INFO_EVENTFD; 2708c2ecf20Sopenharmony_ci info.count = 1; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)arg, &info, minsz)) 2738c2ecf20Sopenharmony_ci return -EFAULT; 2748c2ecf20Sopenharmony_ci return 0; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci case VFIO_DEVICE_SET_IRQS: 2778c2ecf20Sopenharmony_ci { 2788c2ecf20Sopenharmony_ci struct vfio_irq_set hdr; 2798c2ecf20Sopenharmony_ci u8 *data = NULL; 2808c2ecf20Sopenharmony_ci int ret = 0; 2818c2ecf20Sopenharmony_ci size_t data_size = 0; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci minsz = offsetofend(struct vfio_irq_set, count); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (copy_from_user(&hdr, (void __user *)arg, minsz)) 2868c2ecf20Sopenharmony_ci return -EFAULT; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci ret = vfio_set_irqs_validate_and_prepare(&hdr, mc_dev->obj_desc.irq_count, 2898c2ecf20Sopenharmony_ci mc_dev->obj_desc.irq_count, &data_size); 2908c2ecf20Sopenharmony_ci if (ret) 2918c2ecf20Sopenharmony_ci return ret; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (data_size) { 2948c2ecf20Sopenharmony_ci data = memdup_user((void __user *)(arg + minsz), 2958c2ecf20Sopenharmony_ci data_size); 2968c2ecf20Sopenharmony_ci if (IS_ERR(data)) 2978c2ecf20Sopenharmony_ci return PTR_ERR(data); 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci mutex_lock(&vdev->igate); 3018c2ecf20Sopenharmony_ci ret = vfio_fsl_mc_set_irqs_ioctl(vdev, hdr.flags, 3028c2ecf20Sopenharmony_ci hdr.index, hdr.start, 3038c2ecf20Sopenharmony_ci hdr.count, data); 3048c2ecf20Sopenharmony_ci mutex_unlock(&vdev->igate); 3058c2ecf20Sopenharmony_ci kfree(data); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci return ret; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci case VFIO_DEVICE_RESET: 3108c2ecf20Sopenharmony_ci { 3118c2ecf20Sopenharmony_ci int ret; 3128c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev = vdev->mc_dev; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* reset is supported only for the DPRC */ 3158c2ecf20Sopenharmony_ci if (!is_fsl_mc_bus_dprc(mc_dev)) 3168c2ecf20Sopenharmony_ci return -ENOTTY; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci ret = dprc_reset_container(mc_dev->mc_io, 0, 3198c2ecf20Sopenharmony_ci mc_dev->mc_handle, 3208c2ecf20Sopenharmony_ci mc_dev->obj_desc.id, 3218c2ecf20Sopenharmony_ci DPRC_RESET_OPTION_NON_RECURSIVE); 3228c2ecf20Sopenharmony_ci return ret; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci default: 3268c2ecf20Sopenharmony_ci return -ENOTTY; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic ssize_t vfio_fsl_mc_read(void *device_data, char __user *buf, 3318c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci struct vfio_fsl_mc_device *vdev = device_data; 3348c2ecf20Sopenharmony_ci unsigned int index = VFIO_FSL_MC_OFFSET_TO_INDEX(*ppos); 3358c2ecf20Sopenharmony_ci loff_t off = *ppos & VFIO_FSL_MC_OFFSET_MASK; 3368c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev = vdev->mc_dev; 3378c2ecf20Sopenharmony_ci struct vfio_fsl_mc_region *region; 3388c2ecf20Sopenharmony_ci u64 data[8]; 3398c2ecf20Sopenharmony_ci int i; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (index >= mc_dev->obj_desc.region_count) 3428c2ecf20Sopenharmony_ci return -EINVAL; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci region = &vdev->regions[index]; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (!(region->flags & VFIO_REGION_INFO_FLAG_READ)) 3478c2ecf20Sopenharmony_ci return -EINVAL; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (!region->ioaddr) { 3508c2ecf20Sopenharmony_ci region->ioaddr = ioremap(region->addr, region->size); 3518c2ecf20Sopenharmony_ci if (!region->ioaddr) 3528c2ecf20Sopenharmony_ci return -ENOMEM; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (count != 64 || off != 0) 3568c2ecf20Sopenharmony_ci return -EINVAL; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci for (i = 7; i >= 0; i--) 3598c2ecf20Sopenharmony_ci data[i] = readq(region->ioaddr + i * sizeof(uint64_t)); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci if (copy_to_user(buf, data, 64)) 3628c2ecf20Sopenharmony_ci return -EFAULT; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci return count; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci#define MC_CMD_COMPLETION_TIMEOUT_MS 5000 3688c2ecf20Sopenharmony_ci#define MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS 500 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic int vfio_fsl_mc_send_command(void __iomem *ioaddr, uint64_t *cmd_data) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci int i; 3738c2ecf20Sopenharmony_ci enum mc_cmd_status status; 3748c2ecf20Sopenharmony_ci unsigned long timeout_usecs = MC_CMD_COMPLETION_TIMEOUT_MS * 1000; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* Write at command parameter into portal */ 3778c2ecf20Sopenharmony_ci for (i = 7; i >= 1; i--) 3788c2ecf20Sopenharmony_ci writeq_relaxed(cmd_data[i], ioaddr + i * sizeof(uint64_t)); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* Write command header in the end */ 3818c2ecf20Sopenharmony_ci writeq(cmd_data[0], ioaddr); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* Wait for response before returning to user-space 3848c2ecf20Sopenharmony_ci * This can be optimized in future to even prepare response 3858c2ecf20Sopenharmony_ci * before returning to user-space and avoid read ioctl. 3868c2ecf20Sopenharmony_ci */ 3878c2ecf20Sopenharmony_ci for (;;) { 3888c2ecf20Sopenharmony_ci u64 header; 3898c2ecf20Sopenharmony_ci struct mc_cmd_header *resp_hdr; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci header = cpu_to_le64(readq_relaxed(ioaddr)); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci resp_hdr = (struct mc_cmd_header *)&header; 3948c2ecf20Sopenharmony_ci status = (enum mc_cmd_status)resp_hdr->status; 3958c2ecf20Sopenharmony_ci if (status != MC_CMD_STATUS_READY) 3968c2ecf20Sopenharmony_ci break; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci udelay(MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS); 3998c2ecf20Sopenharmony_ci timeout_usecs -= MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS; 4008c2ecf20Sopenharmony_ci if (timeout_usecs == 0) 4018c2ecf20Sopenharmony_ci return -ETIMEDOUT; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci return 0; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic ssize_t vfio_fsl_mc_write(void *device_data, const char __user *buf, 4088c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct vfio_fsl_mc_device *vdev = device_data; 4118c2ecf20Sopenharmony_ci unsigned int index = VFIO_FSL_MC_OFFSET_TO_INDEX(*ppos); 4128c2ecf20Sopenharmony_ci loff_t off = *ppos & VFIO_FSL_MC_OFFSET_MASK; 4138c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev = vdev->mc_dev; 4148c2ecf20Sopenharmony_ci struct vfio_fsl_mc_region *region; 4158c2ecf20Sopenharmony_ci u64 data[8]; 4168c2ecf20Sopenharmony_ci int ret; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (index >= mc_dev->obj_desc.region_count) 4198c2ecf20Sopenharmony_ci return -EINVAL; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci region = &vdev->regions[index]; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (!(region->flags & VFIO_REGION_INFO_FLAG_WRITE)) 4248c2ecf20Sopenharmony_ci return -EINVAL; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (!region->ioaddr) { 4278c2ecf20Sopenharmony_ci region->ioaddr = ioremap(region->addr, region->size); 4288c2ecf20Sopenharmony_ci if (!region->ioaddr) 4298c2ecf20Sopenharmony_ci return -ENOMEM; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci if (count != 64 || off != 0) 4338c2ecf20Sopenharmony_ci return -EINVAL; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (copy_from_user(&data, buf, 64)) 4368c2ecf20Sopenharmony_ci return -EFAULT; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci ret = vfio_fsl_mc_send_command(region->ioaddr, data); 4398c2ecf20Sopenharmony_ci if (ret) 4408c2ecf20Sopenharmony_ci return ret; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci return count; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic int vfio_fsl_mc_mmap_mmio(struct vfio_fsl_mc_region region, 4478c2ecf20Sopenharmony_ci struct vm_area_struct *vma) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci u64 size = vma->vm_end - vma->vm_start; 4508c2ecf20Sopenharmony_ci u64 pgoff, base; 4518c2ecf20Sopenharmony_ci u8 region_cacheable; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci pgoff = vma->vm_pgoff & 4548c2ecf20Sopenharmony_ci ((1U << (VFIO_FSL_MC_OFFSET_SHIFT - PAGE_SHIFT)) - 1); 4558c2ecf20Sopenharmony_ci base = pgoff << PAGE_SHIFT; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci if (region.size < PAGE_SIZE || base + size > region.size) 4588c2ecf20Sopenharmony_ci return -EINVAL; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci region_cacheable = (region.type & FSL_MC_REGION_CACHEABLE) && 4618c2ecf20Sopenharmony_ci (region.type & FSL_MC_REGION_SHAREABLE); 4628c2ecf20Sopenharmony_ci if (!region_cacheable) 4638c2ecf20Sopenharmony_ci vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci vma->vm_pgoff = (region.addr >> PAGE_SHIFT) + pgoff; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, 4688c2ecf20Sopenharmony_ci size, vma->vm_page_prot); 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic int vfio_fsl_mc_mmap(void *device_data, struct vm_area_struct *vma) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci struct vfio_fsl_mc_device *vdev = device_data; 4748c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev = vdev->mc_dev; 4758c2ecf20Sopenharmony_ci unsigned int index; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci index = vma->vm_pgoff >> (VFIO_FSL_MC_OFFSET_SHIFT - PAGE_SHIFT); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (vma->vm_end < vma->vm_start) 4808c2ecf20Sopenharmony_ci return -EINVAL; 4818c2ecf20Sopenharmony_ci if (vma->vm_start & ~PAGE_MASK) 4828c2ecf20Sopenharmony_ci return -EINVAL; 4838c2ecf20Sopenharmony_ci if (vma->vm_end & ~PAGE_MASK) 4848c2ecf20Sopenharmony_ci return -EINVAL; 4858c2ecf20Sopenharmony_ci if (!(vma->vm_flags & VM_SHARED)) 4868c2ecf20Sopenharmony_ci return -EINVAL; 4878c2ecf20Sopenharmony_ci if (index >= mc_dev->obj_desc.region_count) 4888c2ecf20Sopenharmony_ci return -EINVAL; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_MMAP)) 4918c2ecf20Sopenharmony_ci return -EINVAL; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_READ) 4948c2ecf20Sopenharmony_ci && (vma->vm_flags & VM_READ)) 4958c2ecf20Sopenharmony_ci return -EINVAL; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_WRITE) 4988c2ecf20Sopenharmony_ci && (vma->vm_flags & VM_WRITE)) 4998c2ecf20Sopenharmony_ci return -EINVAL; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci vma->vm_private_data = mc_dev; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci return vfio_fsl_mc_mmap_mmio(vdev->regions[index], vma); 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic const struct vfio_device_ops vfio_fsl_mc_ops = { 5078c2ecf20Sopenharmony_ci .name = "vfio-fsl-mc", 5088c2ecf20Sopenharmony_ci .open = vfio_fsl_mc_open, 5098c2ecf20Sopenharmony_ci .release = vfio_fsl_mc_release, 5108c2ecf20Sopenharmony_ci .ioctl = vfio_fsl_mc_ioctl, 5118c2ecf20Sopenharmony_ci .read = vfio_fsl_mc_read, 5128c2ecf20Sopenharmony_ci .write = vfio_fsl_mc_write, 5138c2ecf20Sopenharmony_ci .mmap = vfio_fsl_mc_mmap, 5148c2ecf20Sopenharmony_ci}; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic int vfio_fsl_mc_bus_notifier(struct notifier_block *nb, 5178c2ecf20Sopenharmony_ci unsigned long action, void *data) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci struct vfio_fsl_mc_device *vdev = container_of(nb, 5208c2ecf20Sopenharmony_ci struct vfio_fsl_mc_device, nb); 5218c2ecf20Sopenharmony_ci struct device *dev = data; 5228c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); 5238c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_cont = to_fsl_mc_device(mc_dev->dev.parent); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci if (action == BUS_NOTIFY_ADD_DEVICE && 5268c2ecf20Sopenharmony_ci vdev->mc_dev == mc_cont) { 5278c2ecf20Sopenharmony_ci mc_dev->driver_override = kasprintf(GFP_KERNEL, "%s", 5288c2ecf20Sopenharmony_ci vfio_fsl_mc_ops.name); 5298c2ecf20Sopenharmony_ci if (!mc_dev->driver_override) 5308c2ecf20Sopenharmony_ci dev_warn(dev, "VFIO_FSL_MC: Setting driver override for device in dprc %s failed\n", 5318c2ecf20Sopenharmony_ci dev_name(&mc_cont->dev)); 5328c2ecf20Sopenharmony_ci else 5338c2ecf20Sopenharmony_ci dev_info(dev, "VFIO_FSL_MC: Setting driver override for device in dprc %s\n", 5348c2ecf20Sopenharmony_ci dev_name(&mc_cont->dev)); 5358c2ecf20Sopenharmony_ci } else if (action == BUS_NOTIFY_BOUND_DRIVER && 5368c2ecf20Sopenharmony_ci vdev->mc_dev == mc_cont) { 5378c2ecf20Sopenharmony_ci struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci if (mc_drv && mc_drv != &vfio_fsl_mc_driver) 5408c2ecf20Sopenharmony_ci dev_warn(dev, "VFIO_FSL_MC: Object %s bound to driver %s while DPRC bound to vfio-fsl-mc\n", 5418c2ecf20Sopenharmony_ci dev_name(dev), mc_drv->driver.name); 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci return 0; 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic int vfio_fsl_mc_init_device(struct vfio_fsl_mc_device *vdev) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev = vdev->mc_dev; 5508c2ecf20Sopenharmony_ci int ret; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci /* Non-dprc devices share mc_io from parent */ 5538c2ecf20Sopenharmony_ci if (!is_fsl_mc_bus_dprc(mc_dev)) { 5548c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_cont = to_fsl_mc_device(mc_dev->dev.parent); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci mc_dev->mc_io = mc_cont->mc_io; 5578c2ecf20Sopenharmony_ci return 0; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci vdev->nb.notifier_call = vfio_fsl_mc_bus_notifier; 5618c2ecf20Sopenharmony_ci ret = bus_register_notifier(&fsl_mc_bus_type, &vdev->nb); 5628c2ecf20Sopenharmony_ci if (ret) 5638c2ecf20Sopenharmony_ci return ret; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* open DPRC, allocate a MC portal */ 5668c2ecf20Sopenharmony_ci ret = dprc_setup(mc_dev); 5678c2ecf20Sopenharmony_ci if (ret) { 5688c2ecf20Sopenharmony_ci dev_err(&mc_dev->dev, "VFIO_FSL_MC: Failed to setup DPRC (%d)\n", ret); 5698c2ecf20Sopenharmony_ci goto out_nc_unreg; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci return 0; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ciout_nc_unreg: 5748c2ecf20Sopenharmony_ci bus_unregister_notifier(&fsl_mc_bus_type, &vdev->nb); 5758c2ecf20Sopenharmony_ci return ret; 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cistatic int vfio_fsl_mc_scan_container(struct fsl_mc_device *mc_dev) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci int ret; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci /* non dprc devices do not scan for other devices */ 5838c2ecf20Sopenharmony_ci if (!is_fsl_mc_bus_dprc(mc_dev)) 5848c2ecf20Sopenharmony_ci return 0; 5858c2ecf20Sopenharmony_ci ret = dprc_scan_container(mc_dev, false); 5868c2ecf20Sopenharmony_ci if (ret) { 5878c2ecf20Sopenharmony_ci dev_err(&mc_dev->dev, 5888c2ecf20Sopenharmony_ci "VFIO_FSL_MC: Container scanning failed (%d)\n", ret); 5898c2ecf20Sopenharmony_ci dprc_remove_devices(mc_dev, NULL, 0); 5908c2ecf20Sopenharmony_ci return ret; 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci return 0; 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic void vfio_fsl_uninit_device(struct vfio_fsl_mc_device *vdev) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev = vdev->mc_dev; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if (!is_fsl_mc_bus_dprc(mc_dev)) 6008c2ecf20Sopenharmony_ci return; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci dprc_cleanup(mc_dev); 6038c2ecf20Sopenharmony_ci bus_unregister_notifier(&fsl_mc_bus_type, &vdev->nb); 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_cistatic int vfio_fsl_mc_probe(struct fsl_mc_device *mc_dev) 6078c2ecf20Sopenharmony_ci{ 6088c2ecf20Sopenharmony_ci struct iommu_group *group; 6098c2ecf20Sopenharmony_ci struct vfio_fsl_mc_device *vdev; 6108c2ecf20Sopenharmony_ci struct device *dev = &mc_dev->dev; 6118c2ecf20Sopenharmony_ci int ret; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci group = vfio_iommu_group_get(dev); 6148c2ecf20Sopenharmony_ci if (!group) { 6158c2ecf20Sopenharmony_ci dev_err(dev, "VFIO_FSL_MC: No IOMMU group\n"); 6168c2ecf20Sopenharmony_ci return -EINVAL; 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci vdev = devm_kzalloc(dev, sizeof(*vdev), GFP_KERNEL); 6208c2ecf20Sopenharmony_ci if (!vdev) { 6218c2ecf20Sopenharmony_ci ret = -ENOMEM; 6228c2ecf20Sopenharmony_ci goto out_group_put; 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci vdev->mc_dev = mc_dev; 6268c2ecf20Sopenharmony_ci mutex_init(&vdev->igate); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci ret = vfio_fsl_mc_reflck_attach(vdev); 6298c2ecf20Sopenharmony_ci if (ret) 6308c2ecf20Sopenharmony_ci goto out_group_put; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci ret = vfio_fsl_mc_init_device(vdev); 6338c2ecf20Sopenharmony_ci if (ret) 6348c2ecf20Sopenharmony_ci goto out_reflck; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci ret = vfio_add_group_dev(dev, &vfio_fsl_mc_ops, vdev); 6378c2ecf20Sopenharmony_ci if (ret) { 6388c2ecf20Sopenharmony_ci dev_err(dev, "VFIO_FSL_MC: Failed to add to vfio group\n"); 6398c2ecf20Sopenharmony_ci goto out_device; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci /* 6438c2ecf20Sopenharmony_ci * This triggers recursion into vfio_fsl_mc_probe() on another device 6448c2ecf20Sopenharmony_ci * and the vfio_fsl_mc_reflck_attach() must succeed, which relies on the 6458c2ecf20Sopenharmony_ci * vfio_add_group_dev() above. It has no impact on this vdev, so it is 6468c2ecf20Sopenharmony_ci * safe to be after the vfio device is made live. 6478c2ecf20Sopenharmony_ci */ 6488c2ecf20Sopenharmony_ci ret = vfio_fsl_mc_scan_container(mc_dev); 6498c2ecf20Sopenharmony_ci if (ret) 6508c2ecf20Sopenharmony_ci goto out_group_dev; 6518c2ecf20Sopenharmony_ci return 0; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ciout_group_dev: 6548c2ecf20Sopenharmony_ci vfio_del_group_dev(dev); 6558c2ecf20Sopenharmony_ciout_device: 6568c2ecf20Sopenharmony_ci vfio_fsl_uninit_device(vdev); 6578c2ecf20Sopenharmony_ciout_reflck: 6588c2ecf20Sopenharmony_ci vfio_fsl_mc_reflck_put(vdev->reflck); 6598c2ecf20Sopenharmony_ciout_group_put: 6608c2ecf20Sopenharmony_ci vfio_iommu_group_put(group, dev); 6618c2ecf20Sopenharmony_ci return ret; 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_cistatic int vfio_fsl_mc_remove(struct fsl_mc_device *mc_dev) 6658c2ecf20Sopenharmony_ci{ 6668c2ecf20Sopenharmony_ci struct vfio_fsl_mc_device *vdev; 6678c2ecf20Sopenharmony_ci struct device *dev = &mc_dev->dev; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci vdev = vfio_del_group_dev(dev); 6708c2ecf20Sopenharmony_ci if (!vdev) 6718c2ecf20Sopenharmony_ci return -EINVAL; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci mutex_destroy(&vdev->igate); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci dprc_remove_devices(mc_dev, NULL, 0); 6768c2ecf20Sopenharmony_ci vfio_fsl_uninit_device(vdev); 6778c2ecf20Sopenharmony_ci vfio_fsl_mc_reflck_put(vdev->reflck); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci vfio_iommu_group_put(mc_dev->dev.iommu_group, dev); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci return 0; 6828c2ecf20Sopenharmony_ci} 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_cistatic struct fsl_mc_driver vfio_fsl_mc_driver = { 6858c2ecf20Sopenharmony_ci .probe = vfio_fsl_mc_probe, 6868c2ecf20Sopenharmony_ci .remove = vfio_fsl_mc_remove, 6878c2ecf20Sopenharmony_ci .driver = { 6888c2ecf20Sopenharmony_ci .name = "vfio-fsl-mc", 6898c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 6908c2ecf20Sopenharmony_ci }, 6918c2ecf20Sopenharmony_ci}; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cistatic int __init vfio_fsl_mc_driver_init(void) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci return fsl_mc_driver_register(&vfio_fsl_mc_driver); 6968c2ecf20Sopenharmony_ci} 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_cistatic void __exit vfio_fsl_mc_driver_exit(void) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci fsl_mc_driver_unregister(&vfio_fsl_mc_driver); 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_cimodule_init(vfio_fsl_mc_driver_init); 7048c2ecf20Sopenharmony_cimodule_exit(vfio_fsl_mc_driver_exit); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 7078c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("VFIO for FSL-MC devices - User Level meta-driver"); 708