1fb1ff4c1SBharat Bhushan // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) 2fb1ff4c1SBharat Bhushan /* 3fb1ff4c1SBharat Bhushan * Copyright 2013-2016 Freescale Semiconductor Inc. 4fb1ff4c1SBharat Bhushan * Copyright 2016-2017,2019-2020 NXP 5fb1ff4c1SBharat Bhushan */ 6fb1ff4c1SBharat Bhushan 7fb1ff4c1SBharat Bhushan #include <linux/device.h> 8fb1ff4c1SBharat Bhushan #include <linux/iommu.h> 9fb1ff4c1SBharat Bhushan #include <linux/module.h> 10fb1ff4c1SBharat Bhushan #include <linux/mutex.h> 11fb1ff4c1SBharat Bhushan #include <linux/slab.h> 12fb1ff4c1SBharat Bhushan #include <linux/types.h> 13fb1ff4c1SBharat Bhushan #include <linux/vfio.h> 14fb1ff4c1SBharat Bhushan #include <linux/fsl/mc.h> 15fb1ff4c1SBharat Bhushan 16fb1ff4c1SBharat Bhushan #include "vfio_fsl_mc_private.h" 17fb1ff4c1SBharat Bhushan 18*704f5082SDiana Craciun static struct fsl_mc_driver vfio_fsl_mc_driver; 19*704f5082SDiana Craciun 20fb1ff4c1SBharat Bhushan static int vfio_fsl_mc_open(void *device_data) 21fb1ff4c1SBharat Bhushan { 22fb1ff4c1SBharat Bhushan if (!try_module_get(THIS_MODULE)) 23fb1ff4c1SBharat Bhushan return -ENODEV; 24fb1ff4c1SBharat Bhushan 25fb1ff4c1SBharat Bhushan return 0; 26fb1ff4c1SBharat Bhushan } 27fb1ff4c1SBharat Bhushan 28fb1ff4c1SBharat Bhushan static void vfio_fsl_mc_release(void *device_data) 29fb1ff4c1SBharat Bhushan { 30fb1ff4c1SBharat Bhushan module_put(THIS_MODULE); 31fb1ff4c1SBharat Bhushan } 32fb1ff4c1SBharat Bhushan 33fb1ff4c1SBharat Bhushan static long vfio_fsl_mc_ioctl(void *device_data, unsigned int cmd, 34fb1ff4c1SBharat Bhushan unsigned long arg) 35fb1ff4c1SBharat Bhushan { 36fb1ff4c1SBharat Bhushan switch (cmd) { 37fb1ff4c1SBharat Bhushan case VFIO_DEVICE_GET_INFO: 38fb1ff4c1SBharat Bhushan { 39fb1ff4c1SBharat Bhushan return -ENOTTY; 40fb1ff4c1SBharat Bhushan } 41fb1ff4c1SBharat Bhushan case VFIO_DEVICE_GET_REGION_INFO: 42fb1ff4c1SBharat Bhushan { 43fb1ff4c1SBharat Bhushan return -ENOTTY; 44fb1ff4c1SBharat Bhushan } 45fb1ff4c1SBharat Bhushan case VFIO_DEVICE_GET_IRQ_INFO: 46fb1ff4c1SBharat Bhushan { 47fb1ff4c1SBharat Bhushan return -ENOTTY; 48fb1ff4c1SBharat Bhushan } 49fb1ff4c1SBharat Bhushan case VFIO_DEVICE_SET_IRQS: 50fb1ff4c1SBharat Bhushan { 51fb1ff4c1SBharat Bhushan return -ENOTTY; 52fb1ff4c1SBharat Bhushan } 53fb1ff4c1SBharat Bhushan case VFIO_DEVICE_RESET: 54fb1ff4c1SBharat Bhushan { 55fb1ff4c1SBharat Bhushan return -ENOTTY; 56fb1ff4c1SBharat Bhushan } 57fb1ff4c1SBharat Bhushan default: 58fb1ff4c1SBharat Bhushan return -ENOTTY; 59fb1ff4c1SBharat Bhushan } 60fb1ff4c1SBharat Bhushan } 61fb1ff4c1SBharat Bhushan 62fb1ff4c1SBharat Bhushan static ssize_t vfio_fsl_mc_read(void *device_data, char __user *buf, 63fb1ff4c1SBharat Bhushan size_t count, loff_t *ppos) 64fb1ff4c1SBharat Bhushan { 65fb1ff4c1SBharat Bhushan return -EINVAL; 66fb1ff4c1SBharat Bhushan } 67fb1ff4c1SBharat Bhushan 68fb1ff4c1SBharat Bhushan static ssize_t vfio_fsl_mc_write(void *device_data, const char __user *buf, 69fb1ff4c1SBharat Bhushan size_t count, loff_t *ppos) 70fb1ff4c1SBharat Bhushan { 71fb1ff4c1SBharat Bhushan return -EINVAL; 72fb1ff4c1SBharat Bhushan } 73fb1ff4c1SBharat Bhushan 74fb1ff4c1SBharat Bhushan static int vfio_fsl_mc_mmap(void *device_data, struct vm_area_struct *vma) 75fb1ff4c1SBharat Bhushan { 76fb1ff4c1SBharat Bhushan return -EINVAL; 77fb1ff4c1SBharat Bhushan } 78fb1ff4c1SBharat Bhushan 79fb1ff4c1SBharat Bhushan static const struct vfio_device_ops vfio_fsl_mc_ops = { 80fb1ff4c1SBharat Bhushan .name = "vfio-fsl-mc", 81fb1ff4c1SBharat Bhushan .open = vfio_fsl_mc_open, 82fb1ff4c1SBharat Bhushan .release = vfio_fsl_mc_release, 83fb1ff4c1SBharat Bhushan .ioctl = vfio_fsl_mc_ioctl, 84fb1ff4c1SBharat Bhushan .read = vfio_fsl_mc_read, 85fb1ff4c1SBharat Bhushan .write = vfio_fsl_mc_write, 86fb1ff4c1SBharat Bhushan .mmap = vfio_fsl_mc_mmap, 87fb1ff4c1SBharat Bhushan }; 88fb1ff4c1SBharat Bhushan 89*704f5082SDiana Craciun static int vfio_fsl_mc_bus_notifier(struct notifier_block *nb, 90*704f5082SDiana Craciun unsigned long action, void *data) 91*704f5082SDiana Craciun { 92*704f5082SDiana Craciun struct vfio_fsl_mc_device *vdev = container_of(nb, 93*704f5082SDiana Craciun struct vfio_fsl_mc_device, nb); 94*704f5082SDiana Craciun struct device *dev = data; 95*704f5082SDiana Craciun struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); 96*704f5082SDiana Craciun struct fsl_mc_device *mc_cont = to_fsl_mc_device(mc_dev->dev.parent); 97*704f5082SDiana Craciun 98*704f5082SDiana Craciun if (action == BUS_NOTIFY_ADD_DEVICE && 99*704f5082SDiana Craciun vdev->mc_dev == mc_cont) { 100*704f5082SDiana Craciun mc_dev->driver_override = kasprintf(GFP_KERNEL, "%s", 101*704f5082SDiana Craciun vfio_fsl_mc_ops.name); 102*704f5082SDiana Craciun if (!mc_dev->driver_override) 103*704f5082SDiana Craciun dev_warn(dev, "VFIO_FSL_MC: Setting driver override for device in dprc %s failed\n", 104*704f5082SDiana Craciun dev_name(&mc_cont->dev)); 105*704f5082SDiana Craciun else 106*704f5082SDiana Craciun dev_info(dev, "VFIO_FSL_MC: Setting driver override for device in dprc %s\n", 107*704f5082SDiana Craciun dev_name(&mc_cont->dev)); 108*704f5082SDiana Craciun } else if (action == BUS_NOTIFY_BOUND_DRIVER && 109*704f5082SDiana Craciun vdev->mc_dev == mc_cont) { 110*704f5082SDiana Craciun struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(dev->driver); 111*704f5082SDiana Craciun 112*704f5082SDiana Craciun if (mc_drv && mc_drv != &vfio_fsl_mc_driver) 113*704f5082SDiana Craciun dev_warn(dev, "VFIO_FSL_MC: Object %s bound to driver %s while DPRC bound to vfio-fsl-mc\n", 114*704f5082SDiana Craciun dev_name(dev), mc_drv->driver.name); 115*704f5082SDiana Craciun } 116*704f5082SDiana Craciun 117*704f5082SDiana Craciun return 0; 118*704f5082SDiana Craciun } 119*704f5082SDiana Craciun 120*704f5082SDiana Craciun static int vfio_fsl_mc_init_device(struct vfio_fsl_mc_device *vdev) 121*704f5082SDiana Craciun { 122*704f5082SDiana Craciun struct fsl_mc_device *mc_dev = vdev->mc_dev; 123*704f5082SDiana Craciun int ret; 124*704f5082SDiana Craciun 125*704f5082SDiana Craciun /* Non-dprc devices share mc_io from parent */ 126*704f5082SDiana Craciun if (!is_fsl_mc_bus_dprc(mc_dev)) { 127*704f5082SDiana Craciun struct fsl_mc_device *mc_cont = to_fsl_mc_device(mc_dev->dev.parent); 128*704f5082SDiana Craciun 129*704f5082SDiana Craciun mc_dev->mc_io = mc_cont->mc_io; 130*704f5082SDiana Craciun return 0; 131*704f5082SDiana Craciun } 132*704f5082SDiana Craciun 133*704f5082SDiana Craciun vdev->nb.notifier_call = vfio_fsl_mc_bus_notifier; 134*704f5082SDiana Craciun ret = bus_register_notifier(&fsl_mc_bus_type, &vdev->nb); 135*704f5082SDiana Craciun if (ret) 136*704f5082SDiana Craciun return ret; 137*704f5082SDiana Craciun 138*704f5082SDiana Craciun /* open DPRC, allocate a MC portal */ 139*704f5082SDiana Craciun ret = dprc_setup(mc_dev); 140*704f5082SDiana Craciun if (ret) { 141*704f5082SDiana Craciun dev_err(&mc_dev->dev, "VFIO_FSL_MC: Failed to setup DPRC (%d)\n", ret); 142*704f5082SDiana Craciun goto out_nc_unreg; 143*704f5082SDiana Craciun } 144*704f5082SDiana Craciun 145*704f5082SDiana Craciun ret = dprc_scan_container(mc_dev, false); 146*704f5082SDiana Craciun if (ret) { 147*704f5082SDiana Craciun dev_err(&mc_dev->dev, "VFIO_FSL_MC: Container scanning failed (%d)\n", ret); 148*704f5082SDiana Craciun goto out_dprc_cleanup; 149*704f5082SDiana Craciun } 150*704f5082SDiana Craciun 151*704f5082SDiana Craciun return 0; 152*704f5082SDiana Craciun 153*704f5082SDiana Craciun out_dprc_cleanup: 154*704f5082SDiana Craciun dprc_remove_devices(mc_dev, NULL, 0); 155*704f5082SDiana Craciun dprc_cleanup(mc_dev); 156*704f5082SDiana Craciun out_nc_unreg: 157*704f5082SDiana Craciun bus_unregister_notifier(&fsl_mc_bus_type, &vdev->nb); 158*704f5082SDiana Craciun vdev->nb.notifier_call = NULL; 159*704f5082SDiana Craciun 160*704f5082SDiana Craciun return ret; 161*704f5082SDiana Craciun } 162*704f5082SDiana Craciun 163fb1ff4c1SBharat Bhushan static int vfio_fsl_mc_probe(struct fsl_mc_device *mc_dev) 164fb1ff4c1SBharat Bhushan { 165fb1ff4c1SBharat Bhushan struct iommu_group *group; 166fb1ff4c1SBharat Bhushan struct vfio_fsl_mc_device *vdev; 167fb1ff4c1SBharat Bhushan struct device *dev = &mc_dev->dev; 168fb1ff4c1SBharat Bhushan int ret; 169fb1ff4c1SBharat Bhushan 170fb1ff4c1SBharat Bhushan group = vfio_iommu_group_get(dev); 171fb1ff4c1SBharat Bhushan if (!group) { 172fb1ff4c1SBharat Bhushan dev_err(dev, "VFIO_FSL_MC: No IOMMU group\n"); 173fb1ff4c1SBharat Bhushan return -EINVAL; 174fb1ff4c1SBharat Bhushan } 175fb1ff4c1SBharat Bhushan 176fb1ff4c1SBharat Bhushan vdev = devm_kzalloc(dev, sizeof(*vdev), GFP_KERNEL); 177fb1ff4c1SBharat Bhushan if (!vdev) { 178fb1ff4c1SBharat Bhushan ret = -ENOMEM; 179fb1ff4c1SBharat Bhushan goto out_group_put; 180fb1ff4c1SBharat Bhushan } 181fb1ff4c1SBharat Bhushan 182fb1ff4c1SBharat Bhushan vdev->mc_dev = mc_dev; 183fb1ff4c1SBharat Bhushan 184fb1ff4c1SBharat Bhushan ret = vfio_add_group_dev(dev, &vfio_fsl_mc_ops, vdev); 185fb1ff4c1SBharat Bhushan if (ret) { 186fb1ff4c1SBharat Bhushan dev_err(dev, "VFIO_FSL_MC: Failed to add to vfio group\n"); 187fb1ff4c1SBharat Bhushan goto out_group_put; 188fb1ff4c1SBharat Bhushan } 189*704f5082SDiana Craciun 190*704f5082SDiana Craciun ret = vfio_fsl_mc_init_device(vdev); 191*704f5082SDiana Craciun if (ret) 192*704f5082SDiana Craciun goto out_group_dev; 193*704f5082SDiana Craciun 194fb1ff4c1SBharat Bhushan return 0; 195fb1ff4c1SBharat Bhushan 196*704f5082SDiana Craciun out_group_dev: 197*704f5082SDiana Craciun vfio_del_group_dev(dev); 198fb1ff4c1SBharat Bhushan out_group_put: 199fb1ff4c1SBharat Bhushan vfio_iommu_group_put(group, dev); 200fb1ff4c1SBharat Bhushan return ret; 201fb1ff4c1SBharat Bhushan } 202fb1ff4c1SBharat Bhushan 203fb1ff4c1SBharat Bhushan static int vfio_fsl_mc_remove(struct fsl_mc_device *mc_dev) 204fb1ff4c1SBharat Bhushan { 205fb1ff4c1SBharat Bhushan struct vfio_fsl_mc_device *vdev; 206fb1ff4c1SBharat Bhushan struct device *dev = &mc_dev->dev; 207fb1ff4c1SBharat Bhushan 208fb1ff4c1SBharat Bhushan vdev = vfio_del_group_dev(dev); 209fb1ff4c1SBharat Bhushan if (!vdev) 210fb1ff4c1SBharat Bhushan return -EINVAL; 211fb1ff4c1SBharat Bhushan 212*704f5082SDiana Craciun if (is_fsl_mc_bus_dprc(mc_dev)) { 213*704f5082SDiana Craciun dprc_remove_devices(mc_dev, NULL, 0); 214*704f5082SDiana Craciun dprc_cleanup(mc_dev); 215*704f5082SDiana Craciun } 216*704f5082SDiana Craciun 217*704f5082SDiana Craciun if (vdev->nb.notifier_call) 218*704f5082SDiana Craciun bus_unregister_notifier(&fsl_mc_bus_type, &vdev->nb); 219*704f5082SDiana Craciun 220fb1ff4c1SBharat Bhushan vfio_iommu_group_put(mc_dev->dev.iommu_group, dev); 221fb1ff4c1SBharat Bhushan 222fb1ff4c1SBharat Bhushan return 0; 223fb1ff4c1SBharat Bhushan } 224fb1ff4c1SBharat Bhushan 225fb1ff4c1SBharat Bhushan static struct fsl_mc_driver vfio_fsl_mc_driver = { 226fb1ff4c1SBharat Bhushan .probe = vfio_fsl_mc_probe, 227fb1ff4c1SBharat Bhushan .remove = vfio_fsl_mc_remove, 228fb1ff4c1SBharat Bhushan .driver = { 229fb1ff4c1SBharat Bhushan .name = "vfio-fsl-mc", 230fb1ff4c1SBharat Bhushan .owner = THIS_MODULE, 231fb1ff4c1SBharat Bhushan }, 232fb1ff4c1SBharat Bhushan }; 233fb1ff4c1SBharat Bhushan 234fb1ff4c1SBharat Bhushan static int __init vfio_fsl_mc_driver_init(void) 235fb1ff4c1SBharat Bhushan { 236fb1ff4c1SBharat Bhushan return fsl_mc_driver_register(&vfio_fsl_mc_driver); 237fb1ff4c1SBharat Bhushan } 238fb1ff4c1SBharat Bhushan 239fb1ff4c1SBharat Bhushan static void __exit vfio_fsl_mc_driver_exit(void) 240fb1ff4c1SBharat Bhushan { 241fb1ff4c1SBharat Bhushan fsl_mc_driver_unregister(&vfio_fsl_mc_driver); 242fb1ff4c1SBharat Bhushan } 243fb1ff4c1SBharat Bhushan 244fb1ff4c1SBharat Bhushan module_init(vfio_fsl_mc_driver_init); 245fb1ff4c1SBharat Bhushan module_exit(vfio_fsl_mc_driver_exit); 246fb1ff4c1SBharat Bhushan 247fb1ff4c1SBharat Bhushan MODULE_LICENSE("Dual BSD/GPL"); 248fb1ff4c1SBharat Bhushan MODULE_DESCRIPTION("VFIO for FSL-MC devices - User Level meta-driver"); 249