diff mbox

[3/5] drivers: uio: Add Xgene QMTM UIO driver

Message ID 1410256619-3213-4-git-send-email-ankit.jindal@linaro.org
State New
Headers show

Commit Message

Ankit Jindal Sept. 9, 2014, 9:56 a.m. UTC
The Applied Micro X-Gene SOC has on-chip QMTM (Queue manager
and Traffic manager) which is hardware based Queue or Ring
manager. This QMTM devices can be used in conjuction with
other devices such as DMA Engine, Ethernet, Security Engine,
etc to assign work to based on Queues or Rings.

This patch allows user space access to Xgene QMTM device.

Signed-off-by: Ankit Jindal <ankit.jindal@linaro.org>
Signed-off-by: Tushar Jagad <tushar.jagad@linaro.org>
---
 drivers/uio/Kconfig          |    8 ++
 drivers/uio/Makefile         |    1 +
 drivers/uio/uio_xgene_qmtm.c |  289 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 298 insertions(+)
 create mode 100644 drivers/uio/uio_xgene_qmtm.c

Comments

Russell King - ARM Linux Sept. 14, 2014, 8:23 p.m. UTC | #1
On Tue, Sep 09, 2014 at 03:26:57PM +0530, Ankit Jindal wrote:
> diff --git a/drivers/uio/uio_xgene_qmtm.c b/drivers/uio/uio_xgene_qmtm.c
...
> +/* QMTM CSR read/write routine */
> +static inline void qmtm_csr_write(struct uio_qmtm_dev *qmtm_dev, u32 offset,
> +		u32 data)
> +{
> +	void __iomem *addr = (u8 *)qmtm_dev->info->mem[0].internal_addr +
> +		offset;
> +
> +	writel(data, addr);

Why not...
	void __iomem *base = qmtm_dev->info->mem[0].internal_addr;

	writel(data, addr + offset);

We permit void pointer arithmetic in the kernel.

> +static int qmtm_reset(struct uio_qmtm_dev *qmtm_dev)
> +{
...
> +	/* check whether device is out of reset or not */
> +	do {
> +		val = qmtm_csr_read(qmtm_dev, QMTM_CFG_MEM_RAM_SHUTDOWN);
> +
> +		if (!wait--)
> +			return -1;
> +		udelay(1);
> +	} while (val == 0xffffffff);

There's two points about the above:

1. The loop is buggy.  A correct implementation would:
	- test for the success condition
	- on success, break out of the loop
	- decrement the timeout, and break out of the loop if we have timed out
	- delay the required delay
	- repeat

   Note that this means that we will always check for success before
   deciding whether we've failed or not, /and/ without penalty of an
   unnecessary delay (as you have.)

2. returning -1 here is not on.  This value can (via your probe
   function) be propagated to userspace, where it will be interpreted
   as an -EPERM error.  Wouldn't a proper errno be better?

> +static void qmtm_cleanup(struct platform_device *pdev,
> +		struct uio_qmtm_dev *qmtm_dev)
> +{
> +	struct uio_info *info = qmtm_dev->info;
> +
> +	uio_unregister_device(info);
> +
> +	kfree(info->name);
> +
> +	if (!IS_ERR(info->mem[0].internal_addr))
> +		devm_iounmap(&pdev->dev, info->mem[0].internal_addr);

So what if we hit a failure at

	platform_get_resource(pdev, IORESOURCE_MEM, 0);

in the probe function below, and call this function.  The purpose of the
devm_* stuff is to avoid these kinds of error-cleanup errors by automating
that stuff.  devm_* APIs record the resource allocations against the
device, and when the probe function fails, or the driver is unbound, the
devm_* resources claimed in the probe function are freed.

> +
> +	kfree(info);
> +	clk_put(qmtm_dev->qmtm_clk);
> +	kfree(qmtm_dev);

All of the above, with the exception of uio_unregister_device() and the
missing clk_unprepare_disable() can be left to the devm_* stuff to deal
with, if you'd used the devm_* functions in the probe function.

> +}
> +
> +static int qmtm_probe(struct platform_device *pdev)
> +{
> +	struct uio_info *info;
> +	struct uio_qmtm_dev *qmtm_dev;
> +	struct resource *csr;
> +	struct resource *fabric;
> +	struct resource *qpool;
> +	unsigned int num_queues;
> +	unsigned int devid;
> +	int ret = -ENODEV;

Probably a bad idea to use a standard value.  You probably have some
error codes you've forgotten to propagate.

> +
> +	qmtm_dev = kzalloc(sizeof(struct uio_qmtm_dev), GFP_KERNEL);

devm_kzalloc

> +	if (!qmtm_dev)
> +		return -ENOMEM;
> +
> +	qmtm_dev->info = kzalloc(sizeof(*info), GFP_KERNEL);

devm_kzalloc

> +	if (!qmtm_dev->info) {
> +		kfree(qmtm_dev);

No need for this free.

> +		return -ENOMEM;
> +	}
> +
> +	/* Power on qmtm in case its not done as part of boot-loader */
> +	qmtm_dev->qmtm_clk = clk_get(&pdev->dev, NULL);

devm_clk_get

> +	if (IS_ERR(qmtm_dev->qmtm_clk)) {
> +		dev_err(&pdev->dev, "Failed to get clock\n");
> +		ret = PTR_ERR(qmtm_dev->qmtm_clk);
> +		kfree(qmtm_dev->info);
> +		kfree(qmtm_dev);

Both kfree's can be removed.

> +		return ret;
> +	} else {
> +		clk_prepare_enable(qmtm_dev->qmtm_clk);
> +	}
> +
> +	csr = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!csr) {
> +		dev_err(&pdev->dev, "No QMTM CSR resource specified\n");
> +		goto out_free;
> +	}
> +
> +	if (!csr->start) {
> +		dev_err(&pdev->dev, "Invalid CSR resource\n");
> +		goto out_free;
> +	}
> +
> +	fabric = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	if (!fabric) {
> +		dev_err(&pdev->dev, "No QMTM Fabric resource specified\n");
> +		goto out_free;
> +	}
> +
> +	if (!fabric->start) {
> +		dev_err(&pdev->dev, "Invalid Fabric resource\n");
> +		goto out_free;
> +	}
> +
> +	qpool = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> +	if (!qpool) {
> +		dev_err(&pdev->dev, "No QMTM Qpool resource specified\n");
> +		goto out_free;
> +	}
> +
> +	if (!qpool->start) {
> +		dev_err(&pdev->dev, "Invalid Qpool resource\n");
> +		goto out_free;
> +	}
> +
> +	ret = of_property_read_u32(pdev->dev.of_node, "num_queues",
> +			&num_queues);

You may wish to consider checking that you have a pdev->dev.of_node and
cleanly error if you don't.

> +
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "No num_queues resource specified\n");
> +		goto out_free;
> +	}
> +
> +	/* check whether sufficient memory is provided for the given queues */
> +	if (!((num_queues * QMTM_DEFAULT_QSIZE) <= resource_size(qpool))) {
> +		dev_err(&pdev->dev, "Insufficient Qpool for the given queues\n");
> +		goto out_free;
> +	}
> +
> +	ret = of_property_read_u32(pdev->dev.of_node, "devid", &devid);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "No devid resource specified\n");
> +		goto out_free;
> +	}
> +
> +	info = qmtm_dev->info;
> +	info->mem[0].name = "csr";
> +	info->mem[0].addr = csr->start;
> +	info->mem[0].size = resource_size(csr);
> +	info->mem[0].memtype = UIO_MEM_PHYS;
> +	info->mem[0].internal_addr = devm_ioremap_resource(&pdev->dev, csr);
> +
> +	if (IS_ERR(info->mem[0].internal_addr)) {
> +		dev_err(&pdev->dev, "Failed to ioremap CSR region\n");

How about printing the error code, and propagating that error code to your
caller?

> +		goto out_free;
> +	}
> +
> +	info->mem[1].name = "fabric";
> +	info->mem[1].addr = fabric->start;
> +	info->mem[1].size = resource_size(fabric);
> +	info->mem[1].memtype = UIO_MEM_PHYS;
> +
> +	info->mem[2].name = "qpool";
> +	info->mem[2].addr = qpool->start;
> +	info->mem[2].size = resource_size(qpool);
> +	info->mem[2].memtype = UIO_MEM_PHYS_CACHE;
> +
> +	info->name = kasprintf(GFP_KERNEL, "qmtm%d", devid);

devm_kasprintf

> +	info->version = DRV_VERSION;
> +
> +	info->priv = qmtm_dev;
> +	info->open = qmtm_open;
> +	info->release = qmtm_release;
> +
> +	/* get the qmtm out of reset */
> +	ret = qmtm_reset(qmtm_dev);
> +	if (ret < 0)
> +		goto out_free;

Here you propagate that -1 value out of your probe function to userspace.
If you want to do this, please choose a reasonable error code, rather
than -EPERM.

> +
> +	/* register with uio framework */
> +	ret = uio_register_device(&pdev->dev, info);
> +	if (ret < 0)
> +		goto out_free;
> +
> +	dev_info(&pdev->dev, "%s registered as UIO device.\n", info->name);

Does this printk serve a useful purpose?  Most other UIO drivers don't
bother printing anything, though if you do print something, it may be
useful to mention which uio device it is associated with.
Ankit Jindal Sept. 18, 2014, 12:04 a.m. UTC | #2
On 15 September 2014 01:53, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:
> On Tue, Sep 09, 2014 at 03:26:57PM +0530, Ankit Jindal wrote:
>> diff --git a/drivers/uio/uio_xgene_qmtm.c b/drivers/uio/uio_xgene_qmtm.c
> ...
>> +/* QMTM CSR read/write routine */
>> +static inline void qmtm_csr_write(struct uio_qmtm_dev *qmtm_dev, u32 offset,
>> +             u32 data)
>> +{
>> +     void __iomem *addr = (u8 *)qmtm_dev->info->mem[0].internal_addr +
>> +             offset;
>> +
>> +     writel(data, addr);
>
> Why not...
>         void __iomem *base = qmtm_dev->info->mem[0].internal_addr;
>
>         writel(data, addr + offset);
>
> We permit void pointer arithmetic in the kernel.
>
>> +static int qmtm_reset(struct uio_qmtm_dev *qmtm_dev)
>> +{
> ...
>> +     /* check whether device is out of reset or not */
>> +     do {
>> +             val = qmtm_csr_read(qmtm_dev, QMTM_CFG_MEM_RAM_SHUTDOWN);
>> +
>> +             if (!wait--)
>> +                     return -1;
>> +             udelay(1);
>> +     } while (val == 0xffffffff);
>
> There's two points about the above:
>
> 1. The loop is buggy.  A correct implementation would:
>         - test for the success condition
>         - on success, break out of the loop
>         - decrement the timeout, and break out of the loop if we have timed out
>         - delay the required delay
>         - repeat
>
>    Note that this means that we will always check for success before
>    deciding whether we've failed or not, /and/ without penalty of an
>    unnecessary delay (as you have.)
>
> 2. returning -1 here is not on.  This value can (via your probe
>    function) be propagated to userspace, where it will be interpreted
>    as an -EPERM error.  Wouldn't a proper errno be better?
>
>> +static void qmtm_cleanup(struct platform_device *pdev,
>> +             struct uio_qmtm_dev *qmtm_dev)
>> +{
>> +     struct uio_info *info = qmtm_dev->info;
>> +
>> +     uio_unregister_device(info);
>> +
>> +     kfree(info->name);
>> +
>> +     if (!IS_ERR(info->mem[0].internal_addr))
>> +             devm_iounmap(&pdev->dev, info->mem[0].internal_addr);
>
> So what if we hit a failure at
>
>         platform_get_resource(pdev, IORESOURCE_MEM, 0);
>
> in the probe function below, and call this function.  The purpose of the
> devm_* stuff is to avoid these kinds of error-cleanup errors by automating
> that stuff.  devm_* APIs record the resource allocations against the
> device, and when the probe function fails, or the driver is unbound, the
> devm_* resources claimed in the probe function are freed.
>
>> +
>> +     kfree(info);
>> +     clk_put(qmtm_dev->qmtm_clk);
>> +     kfree(qmtm_dev);
>
> All of the above, with the exception of uio_unregister_device() and the
> missing clk_unprepare_disable() can be left to the devm_* stuff to deal
> with, if you'd used the devm_* functions in the probe function.
>
>> +}
>> +
>> +static int qmtm_probe(struct platform_device *pdev)
>> +{
>> +     struct uio_info *info;
>> +     struct uio_qmtm_dev *qmtm_dev;
>> +     struct resource *csr;
>> +     struct resource *fabric;
>> +     struct resource *qpool;
>> +     unsigned int num_queues;
>> +     unsigned int devid;
>> +     int ret = -ENODEV;
>
> Probably a bad idea to use a standard value.  You probably have some
> error codes you've forgotten to propagate.
>
>> +
>> +     qmtm_dev = kzalloc(sizeof(struct uio_qmtm_dev), GFP_KERNEL);
>
> devm_kzalloc
>
>> +     if (!qmtm_dev)
>> +             return -ENOMEM;
>> +
>> +     qmtm_dev->info = kzalloc(sizeof(*info), GFP_KERNEL);
>
> devm_kzalloc
>
>> +     if (!qmtm_dev->info) {
>> +             kfree(qmtm_dev);
>
> No need for this free.
>
>> +             return -ENOMEM;
>> +     }
>> +
>> +     /* Power on qmtm in case its not done as part of boot-loader */
>> +     qmtm_dev->qmtm_clk = clk_get(&pdev->dev, NULL);
>
> devm_clk_get
>
>> +     if (IS_ERR(qmtm_dev->qmtm_clk)) {
>> +             dev_err(&pdev->dev, "Failed to get clock\n");
>> +             ret = PTR_ERR(qmtm_dev->qmtm_clk);
>> +             kfree(qmtm_dev->info);
>> +             kfree(qmtm_dev);
>
> Both kfree's can be removed.
>
>> +             return ret;
>> +     } else {
>> +             clk_prepare_enable(qmtm_dev->qmtm_clk);
>> +     }
>> +
>> +     csr = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +     if (!csr) {
>> +             dev_err(&pdev->dev, "No QMTM CSR resource specified\n");
>> +             goto out_free;
>> +     }
>> +
>> +     if (!csr->start) {
>> +             dev_err(&pdev->dev, "Invalid CSR resource\n");
>> +             goto out_free;
>> +     }
>> +
>> +     fabric = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>> +     if (!fabric) {
>> +             dev_err(&pdev->dev, "No QMTM Fabric resource specified\n");
>> +             goto out_free;
>> +     }
>> +
>> +     if (!fabric->start) {
>> +             dev_err(&pdev->dev, "Invalid Fabric resource\n");
>> +             goto out_free;
>> +     }
>> +
>> +     qpool = platform_get_resource(pdev, IORESOURCE_MEM, 2);
>> +     if (!qpool) {
>> +             dev_err(&pdev->dev, "No QMTM Qpool resource specified\n");
>> +             goto out_free;
>> +     }
>> +
>> +     if (!qpool->start) {
>> +             dev_err(&pdev->dev, "Invalid Qpool resource\n");
>> +             goto out_free;
>> +     }
>> +
>> +     ret = of_property_read_u32(pdev->dev.of_node, "num_queues",
>> +                     &num_queues);
>
> You may wish to consider checking that you have a pdev->dev.of_node and
> cleanly error if you don't.
>
>> +
>> +     if (ret < 0) {
>> +             dev_err(&pdev->dev, "No num_queues resource specified\n");
>> +             goto out_free;
>> +     }
>> +
>> +     /* check whether sufficient memory is provided for the given queues */
>> +     if (!((num_queues * QMTM_DEFAULT_QSIZE) <= resource_size(qpool))) {
>> +             dev_err(&pdev->dev, "Insufficient Qpool for the given queues\n");
>> +             goto out_free;
>> +     }
>> +
>> +     ret = of_property_read_u32(pdev->dev.of_node, "devid", &devid);
>> +     if (ret < 0) {
>> +             dev_err(&pdev->dev, "No devid resource specified\n");
>> +             goto out_free;
>> +     }
>> +
>> +     info = qmtm_dev->info;
>> +     info->mem[0].name = "csr";
>> +     info->mem[0].addr = csr->start;
>> +     info->mem[0].size = resource_size(csr);
>> +     info->mem[0].memtype = UIO_MEM_PHYS;
>> +     info->mem[0].internal_addr = devm_ioremap_resource(&pdev->dev, csr);
>> +
>> +     if (IS_ERR(info->mem[0].internal_addr)) {
>> +             dev_err(&pdev->dev, "Failed to ioremap CSR region\n");
>
> How about printing the error code, and propagating that error code to your
> caller?
>
>> +             goto out_free;
>> +     }
>> +
>> +     info->mem[1].name = "fabric";
>> +     info->mem[1].addr = fabric->start;
>> +     info->mem[1].size = resource_size(fabric);
>> +     info->mem[1].memtype = UIO_MEM_PHYS;
>> +
>> +     info->mem[2].name = "qpool";
>> +     info->mem[2].addr = qpool->start;
>> +     info->mem[2].size = resource_size(qpool);
>> +     info->mem[2].memtype = UIO_MEM_PHYS_CACHE;
>> +
>> +     info->name = kasprintf(GFP_KERNEL, "qmtm%d", devid);
>
> devm_kasprintf
>
>> +     info->version = DRV_VERSION;
>> +
>> +     info->priv = qmtm_dev;
>> +     info->open = qmtm_open;
>> +     info->release = qmtm_release;
>> +
>> +     /* get the qmtm out of reset */
>> +     ret = qmtm_reset(qmtm_dev);
>> +     if (ret < 0)
>> +             goto out_free;
>
> Here you propagate that -1 value out of your probe function to userspace.
> If you want to do this, please choose a reasonable error code, rather
> than -EPERM.
>
>> +
>> +     /* register with uio framework */
>> +     ret = uio_register_device(&pdev->dev, info);
>> +     if (ret < 0)
>> +             goto out_free;
>> +
>> +     dev_info(&pdev->dev, "%s registered as UIO device.\n", info->name);
>
> Does this printk serve a useful purpose?  Most other UIO drivers don't
> bother printing anything, though if you do print something, it may be
> useful to mention which uio device it is associated with.
>
Thanks, will incorporate all the suggested changes in next version.

Thanks,
Ankit
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
diff mbox

Patch

diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig
index 5a90914..d9e46bd 100644
--- a/drivers/uio/Kconfig
+++ b/drivers/uio/Kconfig
@@ -135,4 +135,12 @@  config UIO_MF624
 
 	  If you compile this as a module, it will be called uio_mf624.
 
+config UIO_XGENE_QMTM
+	tristate "Applied Micro Xgene QMTM driver"
+	depends on OF
+	help
+	  Userspace I/O interface for the Xgene QMTM. The userspace part of
+	  this driver will be available for download from the Applied Micro
+	  web site (http://www.apm.com/).
+
 endif
diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile
index d3218bd..633eaa0 100644
--- a/drivers/uio/Makefile
+++ b/drivers/uio/Makefile
@@ -8,3 +8,4 @@  obj-$(CONFIG_UIO_PCI_GENERIC)	+= uio_pci_generic.o
 obj-$(CONFIG_UIO_NETX)	+= uio_netx.o
 obj-$(CONFIG_UIO_PRUSS)         += uio_pruss.o
 obj-$(CONFIG_UIO_MF624)         += uio_mf624.o
+obj-$(CONFIG_UIO_XGENE_QMTM)	+= uio_xgene_qmtm.o
diff --git a/drivers/uio/uio_xgene_qmtm.c b/drivers/uio/uio_xgene_qmtm.c
new file mode 100644
index 0000000..895a385
--- /dev/null
+++ b/drivers/uio/uio_xgene_qmtm.c
@@ -0,0 +1,289 @@ 
+/*
+ * Xgene Queue Manager Traffic Manager (QMTM) UIO driver (uio_xgene_qmtm)
+ *
+ * This driver exports QMTM CSRs, Fabric and memory for queues to user-space
+ *
+ * Copyright (C) 2014 Applied Micro - http://www.apm.com/
+ * Copyright (C) 2014 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/uio_driver.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/of_platform.h>
+
+#define DRV_NAME "qmtm_uio"
+#define DRV_VERSION "1.0"
+
+#define QMTM_CONFIG			0x00000004
+#define QMTM_SRST			0x0000c200
+#define QMTM_CLKEN			0x0000c208
+#define QMTM_CFG_MEM_RAM_SHUTDOWN	0x0000d070
+
+#define QMTM_DEFAULT_QSIZE		0x00010000
+
+struct uio_qmtm_dev {
+	struct uio_info *info;
+	struct clk *qmtm_clk;
+};
+
+/* QMTM CSR read/write routine */
+static inline void qmtm_csr_write(struct uio_qmtm_dev *qmtm_dev, u32 offset,
+		u32 data)
+{
+	void __iomem *addr = (u8 *)qmtm_dev->info->mem[0].internal_addr +
+		offset;
+
+	writel(data, addr);
+}
+
+static inline u32 qmtm_csr_read(struct uio_qmtm_dev *qmtm_dev, u32 offset)
+{
+	void __iomem *addr = (u8 *)qmtm_dev->info->mem[0].internal_addr +
+		offset;
+
+	return readl(addr);
+}
+
+static int qmtm_reset(struct uio_qmtm_dev *qmtm_dev)
+{
+	u32 val;
+	int wait = 1000;
+
+	/* get device out of reset */
+	qmtm_csr_write(qmtm_dev, QMTM_CLKEN, 3);
+	qmtm_csr_write(qmtm_dev, QMTM_SRST, 3);
+	udelay(1000);
+	qmtm_csr_write(qmtm_dev, QMTM_SRST, 0);
+	qmtm_csr_write(qmtm_dev, QMTM_CFG_MEM_RAM_SHUTDOWN, 0);
+
+	/* check whether device is out of reset or not */
+	do {
+		val = qmtm_csr_read(qmtm_dev, QMTM_CFG_MEM_RAM_SHUTDOWN);
+
+		if (!wait--)
+			return -1;
+		udelay(1);
+	} while (val == 0xffffffff);
+
+	return 0;
+}
+
+static int qmtm_open(struct uio_info *info, struct inode *inode)
+{
+	struct uio_qmtm_dev *qmtm_dev = info->priv;
+
+	/* Enable QPcore */
+	qmtm_csr_write(qmtm_dev, QMTM_CONFIG, 0x80000000);
+
+	return 0;
+}
+
+static int qmtm_release(struct uio_info *info, struct inode *inode)
+{
+	struct uio_qmtm_dev *qmtm_dev = info->priv;
+
+	/* Disable QPcore */
+	qmtm_csr_write(qmtm_dev, QMTM_CONFIG, 0);
+
+	return 0;
+}
+
+static void qmtm_cleanup(struct platform_device *pdev,
+		struct uio_qmtm_dev *qmtm_dev)
+{
+	struct uio_info *info = qmtm_dev->info;
+
+	uio_unregister_device(info);
+
+	kfree(info->name);
+
+	if (!IS_ERR(info->mem[0].internal_addr))
+		devm_iounmap(&pdev->dev, info->mem[0].internal_addr);
+
+	kfree(info);
+	clk_put(qmtm_dev->qmtm_clk);
+	kfree(qmtm_dev);
+}
+
+static int qmtm_probe(struct platform_device *pdev)
+{
+	struct uio_info *info;
+	struct uio_qmtm_dev *qmtm_dev;
+	struct resource *csr;
+	struct resource *fabric;
+	struct resource *qpool;
+	unsigned int num_queues;
+	unsigned int devid;
+	int ret = -ENODEV;
+
+	qmtm_dev = kzalloc(sizeof(struct uio_qmtm_dev), GFP_KERNEL);
+	if (!qmtm_dev)
+		return -ENOMEM;
+
+	qmtm_dev->info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!qmtm_dev->info) {
+		kfree(qmtm_dev);
+		return -ENOMEM;
+	}
+
+	/* Power on qmtm in case its not done as part of boot-loader */
+	qmtm_dev->qmtm_clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(qmtm_dev->qmtm_clk)) {
+		dev_err(&pdev->dev, "Failed to get clock\n");
+		ret = PTR_ERR(qmtm_dev->qmtm_clk);
+		kfree(qmtm_dev->info);
+		kfree(qmtm_dev);
+		return ret;
+	} else {
+		clk_prepare_enable(qmtm_dev->qmtm_clk);
+	}
+
+	csr = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!csr) {
+		dev_err(&pdev->dev, "No QMTM CSR resource specified\n");
+		goto out_free;
+	}
+
+	if (!csr->start) {
+		dev_err(&pdev->dev, "Invalid CSR resource\n");
+		goto out_free;
+	}
+
+	fabric = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!fabric) {
+		dev_err(&pdev->dev, "No QMTM Fabric resource specified\n");
+		goto out_free;
+	}
+
+	if (!fabric->start) {
+		dev_err(&pdev->dev, "Invalid Fabric resource\n");
+		goto out_free;
+	}
+
+	qpool = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+	if (!qpool) {
+		dev_err(&pdev->dev, "No QMTM Qpool resource specified\n");
+		goto out_free;
+	}
+
+	if (!qpool->start) {
+		dev_err(&pdev->dev, "Invalid Qpool resource\n");
+		goto out_free;
+	}
+
+	ret = of_property_read_u32(pdev->dev.of_node, "num_queues",
+			&num_queues);
+
+	if (ret < 0) {
+		dev_err(&pdev->dev, "No num_queues resource specified\n");
+		goto out_free;
+	}
+
+	/* check whether sufficient memory is provided for the given queues */
+	if (!((num_queues * QMTM_DEFAULT_QSIZE) <= resource_size(qpool))) {
+		dev_err(&pdev->dev, "Insufficient Qpool for the given queues\n");
+		goto out_free;
+	}
+
+	ret = of_property_read_u32(pdev->dev.of_node, "devid", &devid);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "No devid resource specified\n");
+		goto out_free;
+	}
+
+	info = qmtm_dev->info;
+	info->mem[0].name = "csr";
+	info->mem[0].addr = csr->start;
+	info->mem[0].size = resource_size(csr);
+	info->mem[0].memtype = UIO_MEM_PHYS;
+	info->mem[0].internal_addr = devm_ioremap_resource(&pdev->dev, csr);
+
+	if (IS_ERR(info->mem[0].internal_addr)) {
+		dev_err(&pdev->dev, "Failed to ioremap CSR region\n");
+		goto out_free;
+	}
+
+	info->mem[1].name = "fabric";
+	info->mem[1].addr = fabric->start;
+	info->mem[1].size = resource_size(fabric);
+	info->mem[1].memtype = UIO_MEM_PHYS;
+
+	info->mem[2].name = "qpool";
+	info->mem[2].addr = qpool->start;
+	info->mem[2].size = resource_size(qpool);
+	info->mem[2].memtype = UIO_MEM_PHYS_CACHE;
+
+	info->name = kasprintf(GFP_KERNEL, "qmtm%d", devid);
+	info->version = DRV_VERSION;
+
+	info->priv = qmtm_dev;
+	info->open = qmtm_open;
+	info->release = qmtm_release;
+
+	/* get the qmtm out of reset */
+	ret = qmtm_reset(qmtm_dev);
+	if (ret < 0)
+		goto out_free;
+
+	/* register with uio framework */
+	ret = uio_register_device(&pdev->dev, info);
+	if (ret < 0)
+		goto out_free;
+
+	dev_info(&pdev->dev, "%s registered as UIO device.\n", info->name);
+
+	platform_set_drvdata(pdev, qmtm_dev);
+	return 0;
+
+out_free:
+	qmtm_cleanup(pdev, qmtm_dev);
+	return ret;
+}
+
+static int qmtm_remove(struct platform_device *pdev)
+{
+	struct uio_qmtm_dev *qmtm_dev = platform_get_drvdata(pdev);
+
+	qmtm_cleanup(pdev, qmtm_dev);
+	return 0;
+}
+
+static struct of_device_id qmtm_match[] = {
+	{.compatible = "apm,xgene-qmtm-uio",},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, qmtm_match);
+
+static struct platform_driver qmtm_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = qmtm_match,
+	},
+	.probe = qmtm_probe,
+	.remove = qmtm_remove,
+};
+
+module_platform_driver(qmtm_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(DRV_VERSION);
+MODULE_AUTHOR("Ankit Jindal <ankit.jindal@linaro.org>");
+MODULE_AUTHOR("Tushar Jagad <tushar.jagad@linaro.org>");