diff options
-rw-r--r-- | drivers/vfio/fsl-mc/Makefile | 2 | ||||
-rw-r--r-- | drivers/vfio/fsl-mc/vfio_fsl_mc.c | 83 | ||||
-rw-r--r-- | drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c | 64 | ||||
-rw-r--r-- | drivers/vfio/fsl-mc/vfio_fsl_mc_private.h | 8 |
4 files changed, 152 insertions, 5 deletions
diff --git a/drivers/vfio/fsl-mc/Makefile b/drivers/vfio/fsl-mc/Makefile index 6f2b806..2aca75a 100644 --- a/drivers/vfio/fsl-mc/Makefile +++ b/drivers/vfio/fsl-mc/Makefile @@ -1,2 +1,2 @@ vfio-fsl_mc-y := vfio_fsl_mc.o -obj-$(CONFIG_VFIO_FSL_MC) += vfio_fsl_mc.o +obj-$(CONFIG_VFIO_FSL_MC) += vfio_fsl_mc.o vfio_fsl_mc_intr.o diff --git a/drivers/vfio/fsl-mc/vfio_fsl_mc.c b/drivers/vfio/fsl-mc/vfio_fsl_mc.c index 29312b9..91decea 100644 --- a/drivers/vfio/fsl-mc/vfio_fsl_mc.c +++ b/drivers/vfio/fsl-mc/vfio_fsl_mc.c @@ -87,12 +87,18 @@ static int vfio_fsl_mc_open(void *device_data) ret = vfio_fsl_mc_regions_init(vdev); if (ret) goto error_region_init; + + ret = vfio_fsl_mc_irqs_init(vdev); + if (ret) + goto error_irq_init; } vdev->refcnt++; mutex_unlock(&driver_lock); return 0; +error_irq_init: + vfio_fsl_mc_regions_cleanup(vdev); error_region_init: mutex_unlock(&driver_lock); if (ret) @@ -107,8 +113,10 @@ static void vfio_fsl_mc_release(void *device_data) mutex_lock(&driver_lock); - if (!(--vdev->refcnt)) + if (!(--vdev->refcnt)) { vfio_fsl_mc_regions_cleanup(vdev); + vfio_fsl_mc_irqs_cleanup(vdev); + } mutex_unlock(&driver_lock); @@ -168,7 +176,31 @@ static long vfio_fsl_mc_ioctl(void *device_data, unsigned int cmd, } case VFIO_DEVICE_GET_IRQ_INFO: { - return -EINVAL; + struct vfio_irq_info info; + + minsz = offsetofend(struct vfio_irq_info, count); + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if (info.argsz < minsz) + return -EINVAL; + + if (info.index >= mc_dev->obj_desc.irq_count) + return -EINVAL; + + if (vdev->mc_irqs != NULL) { + info.flags = vdev->mc_irqs[info.index].flags; + info.count = vdev->mc_irqs[info.index].count; + } else { + /* + * If IRQs are not initialized then these can not + * be configuted and used by user-space/ + */ + info.flags = 0; + info.count = 0; + } + + return copy_to_user((void __user *)arg, &info, minsz); } case VFIO_DEVICE_SET_IRQS: { @@ -282,6 +314,7 @@ static int vfio_fsl_mc_initialize_dprc(struct vfio_fsl_mc_device *vdev) struct fsl_mc_device *mc_dev = vdev->mc_dev; struct device *dev = &mc_dev->dev; struct fsl_mc_bus *mc_bus; + struct irq_domain *mc_msi_domain; unsigned int irq_count; int ret; @@ -301,7 +334,7 @@ static int vfio_fsl_mc_initialize_dprc(struct vfio_fsl_mc_device *vdev) FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, &mc_dev->mc_io); if (ret < 0) - return ret; + goto clean_msi_domain; /* Reset MCP before move on */ ret = fsl_mc_portal_reset(mc_dev->mc_io); @@ -310,6 +343,13 @@ static int vfio_fsl_mc_initialize_dprc(struct vfio_fsl_mc_device *vdev) goto free_mc_portal; } + /* MSI domain set up */ + ret = fsl_mc_find_msi_domain(root_dprc_dev->parent, &mc_msi_domain); + if (ret < 0) + goto free_mc_portal; + + dev_set_msi_domain(&mc_dev->dev, mc_msi_domain); + ret = dprc_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id, &mc_dev->mc_handle); if (ret) { @@ -322,6 +362,15 @@ static int vfio_fsl_mc_initialize_dprc(struct vfio_fsl_mc_device *vdev) mc_bus = to_fsl_mc_bus(mc_dev); + if (!mc_bus->irq_resources) { + irq_count = FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS; + ret = fsl_mc_populate_irq_pool(mc_bus, irq_count); + if (ret < 0) { + dev_err(dev, "%s: Failed to init irq-pool\n", __func__); + goto clean_resource_pool; + } + } + mutex_init(&mc_bus->scan_mutex); mutex_lock(&mc_bus->scan_mutex); @@ -330,17 +379,30 @@ static int vfio_fsl_mc_initialize_dprc(struct vfio_fsl_mc_device *vdev) mutex_unlock(&mc_bus->scan_mutex); if (ret) { dev_err(dev, "dprc_scan_objects() fails (%d)\n", ret); - goto clean_resource_pool; + goto clean_irq_pool; + } + + if (irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS) { + dev_warn(&mc_dev->dev, + "IRQs needed (%u) exceed IRQs preallocated (%u)\n", + irq_count, FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS); } return 0; +clean_irq_pool: + fsl_mc_cleanup_irq_pool(mc_bus); + clean_resource_pool: fsl_mc_cleanup_all_resource_pools(mc_dev); dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); free_mc_portal: fsl_mc_portal_free(mc_dev->mc_io); + +clean_msi_domain: + dev_set_msi_domain(&mc_dev->dev, NULL); + return ret; } @@ -361,6 +423,7 @@ static int vfio_fsl_mc_device_remove(struct device *dev, void *data) static void vfio_fsl_mc_cleanup_dprc(struct vfio_fsl_mc_device *vdev) { struct fsl_mc_device *mc_dev = vdev->mc_dev; + struct fsl_mc_bus *mc_bus; /* device must be DPRC */ if (strcmp(mc_dev->obj_desc.type, "dprc")) @@ -368,6 +431,12 @@ static void vfio_fsl_mc_cleanup_dprc(struct vfio_fsl_mc_device *vdev) device_for_each_child(&mc_dev->dev, NULL, vfio_fsl_mc_device_remove); + mc_bus = to_fsl_mc_bus(mc_dev); + if (dev_get_msi_domain(&mc_dev->dev)) + fsl_mc_cleanup_irq_pool(mc_bus); + + dev_set_msi_domain(&mc_dev->dev, NULL); + fsl_mc_cleanup_all_resource_pools(mc_dev); dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); fsl_mc_portal_free(mc_dev->mc_io); @@ -418,6 +487,10 @@ static int vfio_fsl_mc_probe(struct fsl_mc_device *mc_dev) } mc_dev->mc_io = mc_bus_dev->mc_io; + + /* Inherit parent MSI domain */ + dev_set_msi_domain(&mc_dev->dev, + dev_get_msi_domain(mc_dev->dev.parent)); } return 0; @@ -438,6 +511,8 @@ static int vfio_fsl_mc_remove(struct fsl_mc_device *mc_dev) if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) vfio_fsl_mc_cleanup_dprc(vdev); + else + dev_set_msi_domain(&mc_dev->dev, NULL); mc_dev->mc_io = NULL; diff --git a/drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c b/drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c new file mode 100644 index 0000000..d86dc04 --- /dev/null +++ b/drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c @@ -0,0 +1,64 @@ +/* + * Freescale Management Complex (MC) device passthrough using VFIO + * + * Copyright (C) 2013-2016 Freescale Semiconductor, Inc. + * Author: Bharat Bhushan <bharat.bhushan@nxp.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/vfio.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include "vfio_fsl_mc_private.h" +#include "../../staging/fsl-mc/include/mc.h" + +int vfio_fsl_mc_irqs_init(struct vfio_fsl_mc_device *vdev) +{ + struct fsl_mc_device *mc_dev = vdev->mc_dev; + struct vfio_fsl_mc_irq *mc_irq; + int irq_count; + int ret, i; + + /* Device does not support any interrupt */ + if (mc_dev->obj_desc.irq_count == 0) + return 0; + + irq_count = mc_dev->obj_desc.irq_count; + + mc_irq = kcalloc(irq_count, sizeof(*mc_irq), GFP_KERNEL); + if (mc_irq == NULL) + return -ENOMEM; + + /* Allocate IRQs */ + ret = fsl_mc_allocate_irqs(mc_dev); + if (ret) { + kfree(mc_irq); + return ret; + } + + for (i = 0; i < irq_count; i++) { + mc_irq[i].count = 1; + mc_irq[i].flags = 0; + } + + vdev->mc_irqs = mc_irq; + + return 0; +} + +/* Free All IRQs for the given MC object */ +void vfio_fsl_mc_irqs_cleanup(struct vfio_fsl_mc_device *vdev) +{ + struct fsl_mc_device *mc_dev = vdev->mc_dev; + + /* Device does not support any interrupt */ + if (mc_dev->obj_desc.irq_count == 0) + return; + + fsl_mc_free_irqs(mc_dev); + kfree(vdev->mc_irqs); +} diff --git a/drivers/vfio/fsl-mc/vfio_fsl_mc_private.h b/drivers/vfio/fsl-mc/vfio_fsl_mc_private.h index e9d7f3b..6d31c3c 100644 --- a/drivers/vfio/fsl-mc/vfio_fsl_mc_private.h +++ b/drivers/vfio/fsl-mc/vfio_fsl_mc_private.h @@ -21,6 +21,11 @@ #define VFIO_FSL_MC_INDEX_TO_OFFSET(index) \ ((u64)(index) << VFIO_FSL_MC_OFFSET_SHIFT) +struct vfio_fsl_mc_irq { + u32 flags; + u32 count; +}; + struct vfio_fsl_mc_region { u32 flags; #define VFIO_FSL_MC_REGION_TYPE_MMIO 1 @@ -35,6 +40,9 @@ struct vfio_fsl_mc_device { int refcnt; u32 num_regions; struct vfio_fsl_mc_region *regions; + struct vfio_fsl_mc_irq *mc_irqs; }; +int vfio_fsl_mc_irqs_init(struct vfio_fsl_mc_device *vdev); +void vfio_fsl_mc_irqs_cleanup(struct vfio_fsl_mc_device *vdev); #endif /* VFIO_PCI_PRIVATE_H */ |