summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBharat Bhushan <Bharat.Bhushan@nxp.com>2017-04-18 09:02:27 (GMT)
committerXie Xiaobo <xiaobo.xie@nxp.com>2017-09-25 07:25:34 (GMT)
commit3671aa51f97ae3f4ec4fa12f2aa97549da1e964d (patch)
tree8f7113be6311980bf2170b50fce0946cf6023599
parentb9b3b8e70f03d921a3eac1a893186d54817f2bb6 (diff)
downloadlinux-3671aa51f97ae3f4ec4fa12f2aa97549da1e964d.tar.xz
vfio/fsl-mc: Return get_irq info for fsl-mc device
This patch add support for VFIO_DEVICE_GET_IRQ_INFO for fsl-mc devices Signed-off-by: Bharat Bhushan <Bharat.Bhushan@nxp.com>
-rw-r--r--drivers/vfio/fsl-mc/Makefile2
-rw-r--r--drivers/vfio/fsl-mc/vfio_fsl_mc.c83
-rw-r--r--drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c64
-rw-r--r--drivers/vfio/fsl-mc/vfio_fsl_mc_private.h8
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 */