summaryrefslogtreecommitdiffstats
path: root/kernel/drivers/vfio/platform
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/drivers/vfio/platform')
-rw-r--r--kernel/drivers/vfio/platform/Kconfig4
-rw-r--r--kernel/drivers/vfio/platform/Makefile8
-rw-r--r--kernel/drivers/vfio/platform/reset/Kconfig15
-rw-r--r--kernel/drivers/vfio/platform/reset/Makefile7
-rw-r--r--kernel/drivers/vfio/platform/reset/vfio_platform_amdxgbe.c127
-rw-r--r--kernel/drivers/vfio/platform/reset/vfio_platform_calxedaxgmac.c85
-rw-r--r--kernel/drivers/vfio/platform/vfio_amba.c1
-rw-r--r--kernel/drivers/vfio/platform/vfio_platform.c2
-rw-r--r--kernel/drivers/vfio/platform/vfio_platform_common.c159
-rw-r--r--kernel/drivers/vfio/platform/vfio_platform_irq.c1
-rw-r--r--kernel/drivers/vfio/platform/vfio_platform_private.h39
11 files changed, 418 insertions, 30 deletions
diff --git a/kernel/drivers/vfio/platform/Kconfig b/kernel/drivers/vfio/platform/Kconfig
index 9a4403e2a..bb3012878 100644
--- a/kernel/drivers/vfio/platform/Kconfig
+++ b/kernel/drivers/vfio/platform/Kconfig
@@ -1,6 +1,6 @@
config VFIO_PLATFORM
tristate "VFIO support for platform devices"
- depends on VFIO && EVENTFD && ARM
+ depends on VFIO && EVENTFD && (ARM || ARM64)
select VFIO_VIRQFD
help
Support for platform devices with VFIO. This is required to make
@@ -18,3 +18,5 @@ config VFIO_AMBA
framework.
If you don't know what to do here, say N.
+
+source "drivers/vfio/platform/reset/Kconfig"
diff --git a/kernel/drivers/vfio/platform/Makefile b/kernel/drivers/vfio/platform/Makefile
index 81de144c0..41a6224f5 100644
--- a/kernel/drivers/vfio/platform/Makefile
+++ b/kernel/drivers/vfio/platform/Makefile
@@ -1,8 +1,12 @@
-
-vfio-platform-y := vfio_platform.o vfio_platform_common.o vfio_platform_irq.o
+vfio-platform-base-y := vfio_platform_common.o vfio_platform_irq.o
+vfio-platform-y := vfio_platform.o
obj-$(CONFIG_VFIO_PLATFORM) += vfio-platform.o
+obj-$(CONFIG_VFIO_PLATFORM) += vfio-platform-base.o
+obj-$(CONFIG_VFIO_PLATFORM) += reset/
vfio-amba-y := vfio_amba.o
obj-$(CONFIG_VFIO_AMBA) += vfio-amba.o
+obj-$(CONFIG_VFIO_AMBA) += vfio-platform-base.o
+obj-$(CONFIG_VFIO_AMBA) += reset/
diff --git a/kernel/drivers/vfio/platform/reset/Kconfig b/kernel/drivers/vfio/platform/reset/Kconfig
new file mode 100644
index 000000000..70cccc582
--- /dev/null
+++ b/kernel/drivers/vfio/platform/reset/Kconfig
@@ -0,0 +1,15 @@
+config VFIO_PLATFORM_CALXEDAXGMAC_RESET
+ tristate "VFIO support for calxeda xgmac reset"
+ depends on VFIO_PLATFORM
+ help
+ Enables the VFIO platform driver to handle reset for Calxeda xgmac
+
+ If you don't know what to do here, say N.
+
+config VFIO_PLATFORM_AMDXGBE_RESET
+ tristate "VFIO support for AMD XGBE reset"
+ depends on VFIO_PLATFORM
+ help
+ Enables the VFIO platform driver to handle reset for AMD XGBE
+
+ If you don't know what to do here, say N.
diff --git a/kernel/drivers/vfio/platform/reset/Makefile b/kernel/drivers/vfio/platform/reset/Makefile
new file mode 100644
index 000000000..93f4e2326
--- /dev/null
+++ b/kernel/drivers/vfio/platform/reset/Makefile
@@ -0,0 +1,7 @@
+vfio-platform-calxedaxgmac-y := vfio_platform_calxedaxgmac.o
+vfio-platform-amdxgbe-y := vfio_platform_amdxgbe.o
+
+ccflags-y += -Idrivers/vfio/platform
+
+obj-$(CONFIG_VFIO_PLATFORM_CALXEDAXGMAC_RESET) += vfio-platform-calxedaxgmac.o
+obj-$(CONFIG_VFIO_PLATFORM_AMDXGBE_RESET) += vfio-platform-amdxgbe.o
diff --git a/kernel/drivers/vfio/platform/reset/vfio_platform_amdxgbe.c b/kernel/drivers/vfio/platform/reset/vfio_platform_amdxgbe.c
new file mode 100644
index 000000000..da5356f48
--- /dev/null
+++ b/kernel/drivers/vfio/platform/reset/vfio_platform_amdxgbe.c
@@ -0,0 +1,127 @@
+/*
+ * VFIO platform driver specialized for AMD xgbe reset
+ * reset code is inherited from AMD xgbe native driver
+ *
+ * Copyright (c) 2015 Linaro Ltd.
+ * www.linaro.org
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <uapi/linux/mdio.h>
+#include <linux/delay.h>
+
+#include "vfio_platform_private.h"
+
+#define DMA_MR 0x3000
+#define MAC_VR 0x0110
+#define DMA_ISR 0x3008
+#define MAC_ISR 0x00b0
+#define PCS_MMD_SELECT 0xff
+#define MDIO_AN_INT 0x8002
+#define MDIO_AN_INTMASK 0x8001
+
+static unsigned int xmdio_read(void *ioaddr, unsigned int mmd,
+ unsigned int reg)
+{
+ unsigned int mmd_address, value;
+
+ mmd_address = (mmd << 16) | ((reg) & 0xffff);
+ iowrite32(mmd_address >> 8, ioaddr + (PCS_MMD_SELECT << 2));
+ value = ioread32(ioaddr + ((mmd_address & 0xff) << 2));
+ return value;
+}
+
+static void xmdio_write(void *ioaddr, unsigned int mmd,
+ unsigned int reg, unsigned int value)
+{
+ unsigned int mmd_address;
+
+ mmd_address = (mmd << 16) | ((reg) & 0xffff);
+ iowrite32(mmd_address >> 8, ioaddr + (PCS_MMD_SELECT << 2));
+ iowrite32(value, ioaddr + ((mmd_address & 0xff) << 2));
+}
+
+int vfio_platform_amdxgbe_reset(struct vfio_platform_device *vdev)
+{
+ struct vfio_platform_region *xgmac_regs = &vdev->regions[0];
+ struct vfio_platform_region *xpcs_regs = &vdev->regions[1];
+ u32 dma_mr_value, pcs_value, value;
+ unsigned int count;
+
+ if (!xgmac_regs->ioaddr) {
+ xgmac_regs->ioaddr =
+ ioremap_nocache(xgmac_regs->addr, xgmac_regs->size);
+ if (!xgmac_regs->ioaddr)
+ return -ENOMEM;
+ }
+ if (!xpcs_regs->ioaddr) {
+ xpcs_regs->ioaddr =
+ ioremap_nocache(xpcs_regs->addr, xpcs_regs->size);
+ if (!xpcs_regs->ioaddr)
+ return -ENOMEM;
+ }
+
+ /* reset the PHY through MDIO*/
+ pcs_value = xmdio_read(xpcs_regs->ioaddr, MDIO_MMD_PCS, MDIO_CTRL1);
+ pcs_value |= MDIO_CTRL1_RESET;
+ xmdio_write(xpcs_regs->ioaddr, MDIO_MMD_PCS, MDIO_CTRL1, pcs_value);
+
+ count = 50;
+ do {
+ msleep(20);
+ pcs_value = xmdio_read(xpcs_regs->ioaddr, MDIO_MMD_PCS,
+ MDIO_CTRL1);
+ } while ((pcs_value & MDIO_CTRL1_RESET) && --count);
+
+ if (pcs_value & MDIO_CTRL1_RESET)
+ pr_warn("%s XGBE PHY reset timeout\n", __func__);
+
+ /* disable auto-negotiation */
+ value = xmdio_read(xpcs_regs->ioaddr, MDIO_MMD_AN, MDIO_CTRL1);
+ value &= ~MDIO_AN_CTRL1_ENABLE;
+ xmdio_write(xpcs_regs->ioaddr, MDIO_MMD_AN, MDIO_CTRL1, value);
+
+ /* disable AN IRQ */
+ xmdio_write(xpcs_regs->ioaddr, MDIO_MMD_AN, MDIO_AN_INTMASK, 0);
+
+ /* clear AN IRQ */
+ xmdio_write(xpcs_regs->ioaddr, MDIO_MMD_AN, MDIO_AN_INT, 0);
+
+ /* MAC software reset */
+ dma_mr_value = ioread32(xgmac_regs->ioaddr + DMA_MR);
+ dma_mr_value |= 0x1;
+ iowrite32(dma_mr_value, xgmac_regs->ioaddr + DMA_MR);
+
+ usleep_range(10, 15);
+
+ count = 2000;
+ while (count-- && (ioread32(xgmac_regs->ioaddr + DMA_MR) & 1))
+ usleep_range(500, 600);
+
+ if (!count)
+ pr_warn("%s MAC SW reset failed\n", __func__);
+
+ return 0;
+}
+
+module_vfio_reset_handler("amd,xgbe-seattle-v1a", vfio_platform_amdxgbe_reset);
+
+MODULE_VERSION("0.1");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Eric Auger <eric.auger@linaro.org>");
+MODULE_DESCRIPTION("Reset support for AMD xgbe vfio platform device");
diff --git a/kernel/drivers/vfio/platform/reset/vfio_platform_calxedaxgmac.c b/kernel/drivers/vfio/platform/reset/vfio_platform_calxedaxgmac.c
new file mode 100644
index 000000000..e3d3d948e
--- /dev/null
+++ b/kernel/drivers/vfio/platform/reset/vfio_platform_calxedaxgmac.c
@@ -0,0 +1,85 @@
+/*
+ * VFIO platform driver specialized for Calxeda xgmac reset
+ * reset code is inherited from calxeda xgmac native driver
+ *
+ * Copyright 2010-2011 Calxeda, Inc.
+ * Copyright (c) 2015 Linaro Ltd.
+ * www.linaro.org
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+
+#include "vfio_platform_private.h"
+
+#define DRIVER_VERSION "0.1"
+#define DRIVER_AUTHOR "Eric Auger <eric.auger@linaro.org>"
+#define DRIVER_DESC "Reset support for Calxeda xgmac vfio platform device"
+
+/* XGMAC Register definitions */
+#define XGMAC_CONTROL 0x00000000 /* MAC Configuration */
+
+/* DMA Control and Status Registers */
+#define XGMAC_DMA_CONTROL 0x00000f18 /* Ctrl (Operational Mode) */
+#define XGMAC_DMA_INTR_ENA 0x00000f1c /* Interrupt Enable */
+
+/* DMA Control registe defines */
+#define DMA_CONTROL_ST 0x00002000 /* Start/Stop Transmission */
+#define DMA_CONTROL_SR 0x00000002 /* Start/Stop Receive */
+
+/* Common MAC defines */
+#define MAC_ENABLE_TX 0x00000008 /* Transmitter Enable */
+#define MAC_ENABLE_RX 0x00000004 /* Receiver Enable */
+
+static inline void xgmac_mac_disable(void __iomem *ioaddr)
+{
+ u32 value = readl(ioaddr + XGMAC_DMA_CONTROL);
+
+ value &= ~(DMA_CONTROL_ST | DMA_CONTROL_SR);
+ writel(value, ioaddr + XGMAC_DMA_CONTROL);
+
+ value = readl(ioaddr + XGMAC_CONTROL);
+ value &= ~(MAC_ENABLE_TX | MAC_ENABLE_RX);
+ writel(value, ioaddr + XGMAC_CONTROL);
+}
+
+int vfio_platform_calxedaxgmac_reset(struct vfio_platform_device *vdev)
+{
+ struct vfio_platform_region *reg = &vdev->regions[0];
+
+ if (!reg->ioaddr) {
+ reg->ioaddr =
+ ioremap_nocache(reg->addr, reg->size);
+ if (!reg->ioaddr)
+ return -ENOMEM;
+ }
+
+ /* disable IRQ */
+ writel(0, reg->ioaddr + XGMAC_DMA_INTR_ENA);
+
+ /* Disable the MAC core */
+ xgmac_mac_disable(reg->ioaddr);
+
+ return 0;
+}
+
+module_vfio_reset_handler("calxeda,hb-xgmac", vfio_platform_calxedaxgmac_reset);
+
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/kernel/drivers/vfio/platform/vfio_amba.c b/kernel/drivers/vfio/platform/vfio_amba.c
index ff0331f72..a66479bd0 100644
--- a/kernel/drivers/vfio/platform/vfio_amba.c
+++ b/kernel/drivers/vfio/platform/vfio_amba.c
@@ -67,6 +67,7 @@ static int vfio_amba_probe(struct amba_device *adev, const struct amba_id *id)
vdev->flags = VFIO_DEVICE_FLAGS_AMBA;
vdev->get_resource = get_amba_resource;
vdev->get_irq = get_amba_irq;
+ vdev->parent_module = THIS_MODULE;
ret = vfio_platform_probe_common(vdev, &adev->dev);
if (ret) {
diff --git a/kernel/drivers/vfio/platform/vfio_platform.c b/kernel/drivers/vfio/platform/vfio_platform.c
index cef645c83..b1cc3a768 100644
--- a/kernel/drivers/vfio/platform/vfio_platform.c
+++ b/kernel/drivers/vfio/platform/vfio_platform.c
@@ -65,6 +65,7 @@ static int vfio_platform_probe(struct platform_device *pdev)
vdev->flags = VFIO_DEVICE_FLAGS_PLATFORM;
vdev->get_resource = get_platform_resource;
vdev->get_irq = get_platform_irq;
+ vdev->parent_module = THIS_MODULE;
ret = vfio_platform_probe_common(vdev, &pdev->dev);
if (ret)
@@ -91,7 +92,6 @@ static struct platform_driver vfio_platform_driver = {
.remove = vfio_platform_remove,
.driver = {
.name = "vfio-platform",
- .owner = THIS_MODULE,
},
};
diff --git a/kernel/drivers/vfio/platform/vfio_platform_common.c b/kernel/drivers/vfio/platform/vfio_platform_common.c
index abcff7a1a..e65b142d3 100644
--- a/kernel/drivers/vfio/platform/vfio_platform_common.c
+++ b/kernel/drivers/vfio/platform/vfio_platform_common.c
@@ -23,8 +23,49 @@
#include "vfio_platform_private.h"
+#define DRIVER_VERSION "0.10"
+#define DRIVER_AUTHOR "Antonios Motakis <a.motakis@virtualopensystems.com>"
+#define DRIVER_DESC "VFIO platform base module"
+
+static LIST_HEAD(reset_list);
static DEFINE_MUTEX(driver_lock);
+static vfio_platform_reset_fn_t vfio_platform_lookup_reset(const char *compat,
+ struct module **module)
+{
+ struct vfio_platform_reset_node *iter;
+ vfio_platform_reset_fn_t reset_fn = NULL;
+
+ mutex_lock(&driver_lock);
+ list_for_each_entry(iter, &reset_list, link) {
+ if (!strcmp(iter->compat, compat) &&
+ try_module_get(iter->owner)) {
+ *module = iter->owner;
+ reset_fn = iter->reset;
+ break;
+ }
+ }
+ mutex_unlock(&driver_lock);
+ return reset_fn;
+}
+
+static void vfio_platform_get_reset(struct vfio_platform_device *vdev)
+{
+ vdev->reset = vfio_platform_lookup_reset(vdev->compat,
+ &vdev->reset_module);
+ if (!vdev->reset) {
+ request_module("vfio-reset:%s", vdev->compat);
+ vdev->reset = vfio_platform_lookup_reset(vdev->compat,
+ &vdev->reset_module);
+ }
+}
+
+static void vfio_platform_put_reset(struct vfio_platform_device *vdev)
+{
+ if (vdev->reset)
+ module_put(vdev->reset_module);
+}
+
static int vfio_platform_regions_init(struct vfio_platform_device *vdev)
{
int cnt = 0, i;
@@ -100,13 +141,19 @@ static void vfio_platform_release(void *device_data)
mutex_lock(&driver_lock);
if (!(--vdev->refcnt)) {
+ if (vdev->reset) {
+ dev_info(vdev->device, "reset\n");
+ vdev->reset(vdev);
+ } else {
+ dev_warn(vdev->device, "no reset function found!\n");
+ }
vfio_platform_regions_cleanup(vdev);
vfio_platform_irq_cleanup(vdev);
}
mutex_unlock(&driver_lock);
- module_put(THIS_MODULE);
+ module_put(vdev->parent_module);
}
static int vfio_platform_open(void *device_data)
@@ -114,7 +161,7 @@ static int vfio_platform_open(void *device_data)
struct vfio_platform_device *vdev = device_data;
int ret;
- if (!try_module_get(THIS_MODULE))
+ if (!try_module_get(vdev->parent_module))
return -ENODEV;
mutex_lock(&driver_lock);
@@ -127,6 +174,13 @@ static int vfio_platform_open(void *device_data)
ret = vfio_platform_irq_init(vdev);
if (ret)
goto err_irq;
+
+ if (vdev->reset) {
+ dev_info(vdev->device, "reset\n");
+ vdev->reset(vdev);
+ } else {
+ dev_warn(vdev->device, "no reset function found!\n");
+ }
}
vdev->refcnt++;
@@ -159,11 +213,14 @@ static long vfio_platform_ioctl(void *device_data,
if (info.argsz < minsz)
return -EINVAL;
+ if (vdev->reset)
+ vdev->flags |= VFIO_DEVICE_FLAGS_RESET;
info.flags = vdev->flags;
info.num_regions = vdev->num_regions;
info.num_irqs = vdev->num_irqs;
- return copy_to_user((void __user *)arg, &info, minsz);
+ return copy_to_user((void __user *)arg, &info, minsz) ?
+ -EFAULT : 0;
} else if (cmd == VFIO_DEVICE_GET_REGION_INFO) {
struct vfio_region_info info;
@@ -184,7 +241,8 @@ static long vfio_platform_ioctl(void *device_data,
info.size = vdev->regions[info.index].size;
info.flags = vdev->regions[info.index].flags;
- return copy_to_user((void __user *)arg, &info, minsz);
+ return copy_to_user((void __user *)arg, &info, minsz) ?
+ -EFAULT : 0;
} else if (cmd == VFIO_DEVICE_GET_IRQ_INFO) {
struct vfio_irq_info info;
@@ -203,7 +261,8 @@ static long vfio_platform_ioctl(void *device_data,
info.flags = vdev->irqs[info.index].flags;
info.count = vdev->irqs[info.index].count;
- return copy_to_user((void __user *)arg, &info, minsz);
+ return copy_to_user((void __user *)arg, &info, minsz) ?
+ -EFAULT : 0;
} else if (cmd == VFIO_DEVICE_SET_IRQS) {
struct vfio_irq_set hdr;
@@ -252,23 +311,27 @@ static long vfio_platform_ioctl(void *device_data,
return ret;
- } else if (cmd == VFIO_DEVICE_RESET)
- return -EINVAL;
+ } else if (cmd == VFIO_DEVICE_RESET) {
+ if (vdev->reset)
+ return vdev->reset(vdev);
+ else
+ return -EINVAL;
+ }
return -ENOTTY;
}
-static ssize_t vfio_platform_read_mmio(struct vfio_platform_region reg,
+static ssize_t vfio_platform_read_mmio(struct vfio_platform_region *reg,
char __user *buf, size_t count,
loff_t off)
{
unsigned int done = 0;
- if (!reg.ioaddr) {
- reg.ioaddr =
- ioremap_nocache(reg.addr, reg.size);
+ if (!reg->ioaddr) {
+ reg->ioaddr =
+ ioremap_nocache(reg->addr, reg->size);
- if (!reg.ioaddr)
+ if (!reg->ioaddr)
return -ENOMEM;
}
@@ -278,7 +341,7 @@ static ssize_t vfio_platform_read_mmio(struct vfio_platform_region reg,
if (count >= 4 && !(off % 4)) {
u32 val;
- val = ioread32(reg.ioaddr + off);
+ val = ioread32(reg->ioaddr + off);
if (copy_to_user(buf, &val, 4))
goto err;
@@ -286,7 +349,7 @@ static ssize_t vfio_platform_read_mmio(struct vfio_platform_region reg,
} else if (count >= 2 && !(off % 2)) {
u16 val;
- val = ioread16(reg.ioaddr + off);
+ val = ioread16(reg->ioaddr + off);
if (copy_to_user(buf, &val, 2))
goto err;
@@ -294,7 +357,7 @@ static ssize_t vfio_platform_read_mmio(struct vfio_platform_region reg,
} else {
u8 val;
- val = ioread8(reg.ioaddr + off);
+ val = ioread8(reg->ioaddr + off);
if (copy_to_user(buf, &val, 1))
goto err;
@@ -327,7 +390,7 @@ static ssize_t vfio_platform_read(void *device_data, char __user *buf,
return -EINVAL;
if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_MMIO)
- return vfio_platform_read_mmio(vdev->regions[index],
+ return vfio_platform_read_mmio(&vdev->regions[index],
buf, count, off);
else if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_PIO)
return -EINVAL; /* not implemented */
@@ -335,17 +398,17 @@ static ssize_t vfio_platform_read(void *device_data, char __user *buf,
return -EINVAL;
}
-static ssize_t vfio_platform_write_mmio(struct vfio_platform_region reg,
+static ssize_t vfio_platform_write_mmio(struct vfio_platform_region *reg,
const char __user *buf, size_t count,
loff_t off)
{
unsigned int done = 0;
- if (!reg.ioaddr) {
- reg.ioaddr =
- ioremap_nocache(reg.addr, reg.size);
+ if (!reg->ioaddr) {
+ reg->ioaddr =
+ ioremap_nocache(reg->addr, reg->size);
- if (!reg.ioaddr)
+ if (!reg->ioaddr)
return -ENOMEM;
}
@@ -357,7 +420,7 @@ static ssize_t vfio_platform_write_mmio(struct vfio_platform_region reg,
if (copy_from_user(&val, buf, 4))
goto err;
- iowrite32(val, reg.ioaddr + off);
+ iowrite32(val, reg->ioaddr + off);
filled = 4;
} else if (count >= 2 && !(off % 2)) {
@@ -365,7 +428,7 @@ static ssize_t vfio_platform_write_mmio(struct vfio_platform_region reg,
if (copy_from_user(&val, buf, 2))
goto err;
- iowrite16(val, reg.ioaddr + off);
+ iowrite16(val, reg->ioaddr + off);
filled = 2;
} else {
@@ -373,7 +436,7 @@ static ssize_t vfio_platform_write_mmio(struct vfio_platform_region reg,
if (copy_from_user(&val, buf, 1))
goto err;
- iowrite8(val, reg.ioaddr + off);
+ iowrite8(val, reg->ioaddr + off);
filled = 1;
}
@@ -403,7 +466,7 @@ static ssize_t vfio_platform_write(void *device_data, const char __user *buf,
return -EINVAL;
if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_MMIO)
- return vfio_platform_write_mmio(vdev->regions[index],
+ return vfio_platform_write_mmio(&vdev->regions[index],
buf, count, off);
else if (vdev->regions[index].type & VFIO_PLATFORM_REGION_TYPE_PIO)
return -EINVAL; /* not implemented */
@@ -490,6 +553,14 @@ int vfio_platform_probe_common(struct vfio_platform_device *vdev,
if (!vdev)
return -EINVAL;
+ ret = device_property_read_string(dev, "compatible", &vdev->compat);
+ if (ret) {
+ pr_err("VFIO: cannot retrieve compat for %s\n", vdev->name);
+ return -EINVAL;
+ }
+
+ vdev->device = dev;
+
group = iommu_group_get(dev);
if (!group) {
pr_err("VFIO: No IOMMU group for device %s\n", vdev->name);
@@ -502,6 +573,8 @@ int vfio_platform_probe_common(struct vfio_platform_device *vdev,
return ret;
}
+ vfio_platform_get_reset(vdev);
+
mutex_init(&vdev->igate);
return 0;
@@ -513,9 +586,43 @@ struct vfio_platform_device *vfio_platform_remove_common(struct device *dev)
struct vfio_platform_device *vdev;
vdev = vfio_del_group_dev(dev);
- if (vdev)
+
+ if (vdev) {
+ vfio_platform_put_reset(vdev);
iommu_group_put(dev->iommu_group);
+ }
return vdev;
}
EXPORT_SYMBOL_GPL(vfio_platform_remove_common);
+
+void __vfio_platform_register_reset(struct vfio_platform_reset_node *node)
+{
+ mutex_lock(&driver_lock);
+ list_add(&node->link, &reset_list);
+ mutex_unlock(&driver_lock);
+}
+EXPORT_SYMBOL_GPL(__vfio_platform_register_reset);
+
+void vfio_platform_unregister_reset(const char *compat,
+ vfio_platform_reset_fn_t fn)
+{
+ struct vfio_platform_reset_node *iter, *temp;
+
+ mutex_lock(&driver_lock);
+ list_for_each_entry_safe(iter, temp, &reset_list, link) {
+ if (!strcmp(iter->compat, compat) && (iter->reset == fn)) {
+ list_del(&iter->link);
+ break;
+ }
+ }
+
+ mutex_unlock(&driver_lock);
+
+}
+EXPORT_SYMBOL_GPL(vfio_platform_unregister_reset);
+
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
diff --git a/kernel/drivers/vfio/platform/vfio_platform_irq.c b/kernel/drivers/vfio/platform/vfio_platform_irq.c
index 88bba57b3..46d4750f4 100644
--- a/kernel/drivers/vfio/platform/vfio_platform_irq.c
+++ b/kernel/drivers/vfio/platform/vfio_platform_irq.c
@@ -185,6 +185,7 @@ static int vfio_set_trigger(struct vfio_platform_device *vdev, int index,
int ret;
if (irq->trigger) {
+ irq_clear_status_flags(irq->hwirq, IRQ_NOAUTOEN);
free_irq(irq->hwirq, irq);
kfree(irq->name);
eventfd_ctx_put(irq->trigger);
diff --git a/kernel/drivers/vfio/platform/vfio_platform_private.h b/kernel/drivers/vfio/platform/vfio_platform_private.h
index 5d31e0473..42816dd28 100644
--- a/kernel/drivers/vfio/platform/vfio_platform_private.h
+++ b/kernel/drivers/vfio/platform/vfio_platform_private.h
@@ -56,6 +56,10 @@ struct vfio_platform_device {
u32 num_irqs;
int refcnt;
struct mutex igate;
+ struct module *parent_module;
+ const char *compat;
+ struct module *reset_module;
+ struct device *device;
/*
* These fields should be filled by the bus specific binder
@@ -67,6 +71,16 @@ struct vfio_platform_device {
struct resource*
(*get_resource)(struct vfio_platform_device *vdev, int i);
int (*get_irq)(struct vfio_platform_device *vdev, int i);
+ int (*reset)(struct vfio_platform_device *vdev);
+};
+
+typedef int (*vfio_platform_reset_fn_t)(struct vfio_platform_device *vdev);
+
+struct vfio_platform_reset_node {
+ struct list_head link;
+ char *compat;
+ struct module *owner;
+ vfio_platform_reset_fn_t reset;
};
extern int vfio_platform_probe_common(struct vfio_platform_device *vdev,
@@ -82,4 +96,29 @@ extern int vfio_platform_set_irqs_ioctl(struct vfio_platform_device *vdev,
unsigned start, unsigned count,
void *data);
+extern void __vfio_platform_register_reset(struct vfio_platform_reset_node *n);
+extern void vfio_platform_unregister_reset(const char *compat,
+ vfio_platform_reset_fn_t fn);
+#define vfio_platform_register_reset(__compat, __reset) \
+static struct vfio_platform_reset_node __reset ## _node = { \
+ .owner = THIS_MODULE, \
+ .compat = __compat, \
+ .reset = __reset, \
+}; \
+__vfio_platform_register_reset(&__reset ## _node)
+
+#define module_vfio_reset_handler(compat, reset) \
+MODULE_ALIAS("vfio-reset:" compat); \
+static int __init reset ## _module_init(void) \
+{ \
+ vfio_platform_register_reset(compat, reset); \
+ return 0; \
+}; \
+static void __exit reset ## _module_exit(void) \
+{ \
+ vfio_platform_unregister_reset(compat, reset); \
+}; \
+module_init(reset ## _module_init); \
+module_exit(reset ## _module_exit)
+
#endif /* VFIO_PLATFORM_PRIVATE_H */