diff mbox

[RFC,04/11] coresight: add CoreSight ETB driver

Message ID 1401457391-12242-5-git-send-email-mathieu.poirier@linaro.org
State New
Headers show

Commit Message

Mathieu Poirier May 30, 2014, 1:43 p.m. UTC
From: Pratik Patel <pratikp@codeaurora.org>

This driver manages CoreSight ETB (Embedded Trace Buffer) which
acts as a circular buffer sink collecting generated trace data.

Signed-off-by: Pratik Patel <pratikp@codeaurora.org>
Signed-off-by: Panchaxari Prasannamurthy <panchaxari.prasannamurthy@linaro.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
 drivers/coresight/Makefile        |   3 +-
 drivers/coresight/coresight-etb.c | 586 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 588 insertions(+), 1 deletion(-)
 create mode 100644 drivers/coresight/coresight-etb.c

Comments

Russell King - ARM Linux May 30, 2014, 1:53 p.m. UTC | #1
NAK for all the reasons I mentioned in the previous submission in 2012.

You partially did the right thing - you read through the previous
submission, and you said in your cover message that you had addressed
some of the comments from that submission.

What I find extremely distasteful is that you seem to have chosen to
completely ignore my comments - you haven't mentioned them in your
covering message at all, and you've just gone ahead and converted ETM
and ETB to be platform devices.

That gets you a NAK for that change, because you have done nothing what
so ever to address the concerns I raised.

Since you seem to have ignored my comments, this is as far as I'm looking
at your submission, and you can consider the entire submission NAK'd by
me.

Thanks.
Mathieu Poirier May 30, 2014, 4:28 p.m. UTC | #2
On 30 May 2014 07:53, Russell King - ARM Linux <linux@arm.linux.org.uk> wrote:
> NAK for all the reasons I mentioned in the previous submission in 2012.
>
> You partially did the right thing - you read through the previous
> submission, and you said in your cover message that you had addressed
> some of the comments from that submission.
>
> What I find extremely distasteful is that you seem to have chosen to
> completely ignore my comments - you haven't mentioned them in your
> covering message at all, and you've just gone ahead and converted ETM
> and ETB to be platform devices.
>
> That gets you a NAK for that change, because you have done nothing what
> so ever to address the concerns I raised.

From my initial reading of your assessment it wasn't clear to me that
your opinion leaned toward registering with the AMBA bus.  Now that
this point has been clarified I will go back to the AMBA interface for
my next submission.

Is there anything else not AMBA releated that you'd like to see modified?

Thanks,
Mathieu

>
> Since you seem to have ignored my comments, this is as far as I'm looking
> at your submission, and you can consider the entire submission NAK'd by
> me.
>
> Thanks.
>
> --
> FTTC broadband for 0.8mile line: now at 9.7Mbps down 460kbps up... slowly
> improving, and getting towards what was expected from it.
diff mbox

Patch

diff --git a/drivers/coresight/Makefile b/drivers/coresight/Makefile
index 540df99..8d4443b 100644
--- a/drivers/coresight/Makefile
+++ b/drivers/coresight/Makefile
@@ -3,4 +3,5 @@ 
 #
 obj-$(CONFIG_CORESIGHT) += coresight.o
 obj-$(CONFIG_OF) += of_coresight.o
-obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-tmc.o coresight-tpiu.o
+obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-tmc.o coresight-tpiu.o \
+					   coresight-etb.o
diff --git a/drivers/coresight/coresight-etb.c b/drivers/coresight/coresight-etb.c
new file mode 100644
index 0000000..5cb0dcb
--- /dev/null
+++ b/drivers/coresight/coresight-etb.c
@@ -0,0 +1,586 @@ 
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/clk.h>
+#include <linux/of_coresight.h>
+#include <linux/coresight.h>
+
+#include "coresight-priv.h"
+
+#define etb_writel(drvdata, val, off)	__raw_writel((val), drvdata->base + off)
+#define etb_readl(drvdata, off)		__raw_readl(drvdata->base + off)
+
+#define ETB_LOCK(drvdata)						\
+do {									\
+	/* wait for things to settle */					\
+	mb();								\
+	etb_writel(drvdata, 0x0, CORESIGHT_LAR);			\
+} while (0)
+#define ETB_UNLOCK(drvdata)						\
+do {									\
+	etb_writel(drvdata, CORESIGHT_UNLOCK, CORESIGHT_LAR);		\
+	/* make sure eveyone has seen this */				\
+	mb();								\
+} while (0)
+
+#define ETB_RAM_DEPTH_REG	(0x004)
+#define ETB_STATUS_REG		(0x00C)
+#define ETB_RAM_READ_DATA_REG	(0x010)
+#define ETB_RAM_READ_POINTER	(0x014)
+#define ETB_RAM_WRITE_POINTER	(0x018)
+#define ETB_TRG			(0x01C)
+#define ETB_CTL_REG		(0x020)
+#define ETB_RWD_REG		(0x024)
+#define ETB_FFSR		(0x300)
+#define ETB_FFCR		(0x304)
+#define ETB_ITMISCOP0		(0xEE0)
+#define ETB_ITTRFLINACK		(0xEE4)
+#define ETB_ITTRFLIN		(0xEE8)
+#define ETB_ITATBDATA0		(0xEEC)
+#define ETB_ITATBCTR2		(0xEF0)
+#define ETB_ITATBCTR1		(0xEF4)
+#define ETB_ITATBCTR0		(0xEF8)
+
+#define BYTES_PER_WORD		4
+#define FRAME_SIZE_WORDS	4
+
+struct etb_drvdata {
+	void __iomem		*base;
+	struct device		*dev;
+	struct coresight_device	*csdev;
+	struct miscdevice	miscdev;
+	struct clk		*clk;
+	spinlock_t		spinlock;
+	bool			reading;
+	atomic_t		in_use;
+	uint8_t			*buf;
+	uint32_t		buffer_depth;
+	bool			enable;
+	uint32_t		trigger_cntr;
+};
+
+static unsigned int etb_get_buffer_depth(struct etb_drvdata *drvdata)
+{
+	int ret;
+	uint32_t depth = 0;
+
+	ret = clk_prepare_enable(drvdata->clk);
+	if (ret)
+		return ret;
+
+	/* RO registers don't need locking */
+	depth = etb_readl(drvdata, ETB_RAM_DEPTH_REG);
+
+	clk_disable_unprepare(drvdata->clk);
+	return depth;
+}
+
+static void __etb_enable(struct etb_drvdata *drvdata)
+{
+	int i;
+	uint32_t depth;
+
+	ETB_UNLOCK(drvdata);
+
+	depth = drvdata->buffer_depth;
+	etb_writel(drvdata, 0x0, ETB_RAM_WRITE_POINTER);
+	for (i = 0; i < depth; i++)
+		etb_writel(drvdata, 0x0, ETB_RWD_REG);
+
+	etb_writel(drvdata, 0x0, ETB_RAM_WRITE_POINTER);
+	etb_writel(drvdata, 0x0, ETB_RAM_READ_POINTER);
+
+	etb_writel(drvdata, drvdata->trigger_cntr, ETB_TRG);
+	etb_writel(drvdata, BIT(13) | BIT(0), ETB_FFCR);
+	etb_writel(drvdata, BIT(0), ETB_CTL_REG);
+
+	ETB_LOCK(drvdata);
+}
+
+static int etb_enable(struct coresight_device *csdev)
+{
+	struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+	int ret;
+	unsigned long flags;
+
+	ret = clk_prepare_enable(drvdata->clk);
+	if (ret)
+		return ret;
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+	__etb_enable(drvdata);
+	drvdata->enable = true;
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+	dev_info(drvdata->dev, "ETB enabled\n");
+	return 0;
+}
+
+static void __etb_disable(struct etb_drvdata *drvdata)
+{
+	int count;
+	uint32_t ffcr;
+
+	ETB_UNLOCK(drvdata);
+
+	ffcr = etb_readl(drvdata, ETB_FFCR);
+	ffcr |= BIT(12);
+	etb_writel(drvdata, ffcr, ETB_FFCR);
+	ffcr |= BIT(6);
+	etb_writel(drvdata, ffcr, ETB_FFCR);
+	for (count = TIMEOUT_US; BVAL(etb_readl(drvdata, ETB_FFCR), 6) != 0
+				&& count > 0; count--)
+		udelay(1);
+	WARN(count == 0, "timeout while flushing DRVDATA, ETB_FFCR: %#x\n",
+	     etb_readl(drvdata, ETB_FFCR));
+
+	etb_writel(drvdata, 0x0, ETB_CTL_REG);
+	for (count = TIMEOUT_US; BVAL(etb_readl(drvdata, ETB_FFSR), 1) != 1
+				&& count > 0; count--)
+		udelay(1);
+	WARN(count == 0, "timeout while disabling DRVDATA, ETB_FFSR: %#x\n",
+	     etb_readl(drvdata, ETB_FFSR));
+
+	ETB_LOCK(drvdata);
+}
+
+static void __etb_dump(struct etb_drvdata *drvdata)
+{
+	int i;
+	uint8_t *buf_ptr;
+	uint32_t read_data, depth;
+	uint32_t read_ptr, write_ptr;
+	uint32_t frame_off, frame_endoff;
+
+	ETB_UNLOCK(drvdata);
+
+	read_ptr = etb_readl(drvdata, ETB_RAM_READ_POINTER);
+	write_ptr = etb_readl(drvdata, ETB_RAM_WRITE_POINTER);
+
+	frame_off = write_ptr % FRAME_SIZE_WORDS;
+	frame_endoff = FRAME_SIZE_WORDS - frame_off;
+	if (frame_off) {
+		dev_err(drvdata->dev,
+			"write_ptr: %lu not aligned to formatter frame size\n",
+			(unsigned long)write_ptr);
+		dev_err(drvdata->dev, "frameoff: %lu, frame_endoff: %lu\n",
+			(unsigned long)frame_off, (unsigned long)frame_endoff);
+		write_ptr += frame_endoff;
+	}
+
+	if ((etb_readl(drvdata, ETB_STATUS_REG) & BIT(0)) == 0)
+		etb_writel(drvdata, 0x0, ETB_RAM_READ_POINTER);
+	else
+		etb_writel(drvdata, write_ptr, ETB_RAM_READ_POINTER);
+
+	depth = drvdata->buffer_depth;
+	buf_ptr = drvdata->buf;
+	for (i = 0; i < depth; i++) {
+		read_data = etb_readl(drvdata, ETB_RAM_READ_DATA_REG);
+		*buf_ptr++ = read_data >> 0;
+		*buf_ptr++ = read_data >> 8;
+		*buf_ptr++ = read_data >> 16;
+		*buf_ptr++ = read_data >> 24;
+	}
+
+	if (frame_off) {
+		buf_ptr -= (frame_endoff * BYTES_PER_WORD);
+		for (i = 0; i < frame_endoff; i++) {
+			*buf_ptr++ = 0x0;
+			*buf_ptr++ = 0x0;
+			*buf_ptr++ = 0x0;
+			*buf_ptr++ = 0x0;
+		}
+	}
+
+	etb_writel(drvdata, read_ptr, ETB_RAM_READ_POINTER);
+
+	ETB_LOCK(drvdata);
+}
+
+static void etb_disable(struct coresight_device *csdev)
+{
+	struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+	unsigned long flags;
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+	__etb_disable(drvdata);
+	__etb_dump(drvdata);
+	drvdata->enable = false;
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+	clk_disable_unprepare(drvdata->clk);
+
+	dev_info(drvdata->dev, "ETB disabled\n");
+}
+
+static void etb_abort(struct coresight_device *csdev)
+{
+	struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+	unsigned long flags;
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+	__etb_disable(drvdata);
+	__etb_dump(drvdata);
+	drvdata->enable = false;
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+	dev_info(drvdata->dev, "ETB aborted\n");
+}
+
+static const struct coresight_ops_sink etb_sink_ops = {
+	.enable		= etb_enable,
+	.disable	= etb_disable,
+	.abort		= etb_abort,
+};
+
+static const struct coresight_ops etb_cs_ops = {
+	.sink_ops	= &etb_sink_ops,
+};
+
+static void etb_dump(struct etb_drvdata *drvdata)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+	if (drvdata->enable) {
+		__etb_disable(drvdata);
+		__etb_dump(drvdata);
+		__etb_enable(drvdata);
+	}
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+	dev_info(drvdata->dev, "ETB dumped\n");
+}
+
+static int etb_open(struct inode *inode, struct file *file)
+{
+	struct etb_drvdata *drvdata = container_of(file->private_data,
+						   struct etb_drvdata, miscdev);
+
+	if (atomic_cmpxchg(&drvdata->in_use, 0, 1))
+		return -EBUSY;
+
+	dev_dbg(drvdata->dev, "%s: successfully opened\n", __func__);
+	return 0;
+}
+
+static ssize_t etb_read(struct file *file, char __user *data,
+				size_t len, loff_t *ppos)
+{
+	uint32_t depth;
+	struct etb_drvdata *drvdata = container_of(file->private_data,
+						   struct etb_drvdata, miscdev);
+
+	if (drvdata->reading == false) {
+		etb_dump(drvdata);
+		drvdata->reading = true;
+	}
+
+	depth = drvdata->buffer_depth;
+	if (*ppos + len > depth * BYTES_PER_WORD)
+		len = depth * BYTES_PER_WORD - *ppos;
+
+	if (copy_to_user(data, drvdata->buf + *ppos, len)) {
+		dev_dbg(drvdata->dev, "%s: copy_to_user failed\n", __func__);
+		return -EFAULT;
+	}
+
+	*ppos += len;
+
+	dev_dbg(drvdata->dev, "%s: %d bytes copied, %d bytes left\n",
+		__func__, len, (int) (depth * BYTES_PER_WORD - *ppos));
+	return len;
+}
+
+static int etb_release(struct inode *inode, struct file *file)
+{
+	struct etb_drvdata *drvdata = container_of(file->private_data,
+						   struct etb_drvdata, miscdev);
+
+	drvdata->reading = false;
+	atomic_set(&drvdata->in_use, 0);
+
+	dev_dbg(drvdata->dev, "%s: released\n", __func__);
+	return 0;
+}
+
+static const struct file_operations etb_fops = {
+	.owner		= THIS_MODULE,
+	.open		= etb_open,
+	.read		= etb_read,
+	.release	= etb_release,
+	.llseek		= no_llseek,
+};
+
+static ssize_t debug_status_show(struct file *file, char __user *user_buf,
+				 size_t count, loff_t *ppos)
+{
+	ssize_t ret;
+	unsigned long flags;
+	uint32_t etb_rdr, etb_sr, etb_rrp, etb_rwp;
+	uint32_t etb_trg, etb_cr, etb_ffsr, etb_ffcr;
+	char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	struct etb_drvdata *drvdata = file->private_data;
+
+	if (!buf)
+		return -ENOMEM;
+
+	ret = clk_prepare_enable(drvdata->clk);
+	if (ret)
+		goto out;
+
+	spin_lock_irqsave(&drvdata->spinlock, flags);
+	ETB_UNLOCK(drvdata);
+
+	etb_rdr = etb_readl(drvdata, ETB_RAM_DEPTH_REG);
+	etb_sr = etb_readl(drvdata, ETB_STATUS_REG);
+	etb_rrp = etb_readl(drvdata, ETB_RAM_READ_POINTER);
+	etb_rwp = etb_readl(drvdata, ETB_RAM_WRITE_POINTER);
+	etb_trg = etb_readl(drvdata, ETB_TRG);
+	etb_cr = etb_readl(drvdata, ETB_CTL_REG);
+	etb_ffsr = etb_readl(drvdata, ETB_FFSR);
+	etb_ffcr = etb_readl(drvdata, ETB_FFCR);
+
+	ETB_LOCK(drvdata);
+	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+	clk_disable_unprepare(drvdata->clk);
+
+	ret = snprintf(buf, PAGE_SIZE,
+		       "Depth:\t\t0x%x\n"
+		       "Status:\t\t0x%x\n"
+		       "RAM read ptr:\t0x%x\n"
+		       "RAM wrt ptr:\t0x%x\n"
+		       "Trigger cnt:\t0x%x\n"
+		       "Control:\t0x%x\n"
+		       "Flush status:\t0x%x\n"
+		       "Flush ctrl:\t0x%x\n",
+			etb_rdr, etb_sr, etb_rrp, etb_rwp,
+			etb_trg, etb_cr, etb_ffsr, etb_ffcr);
+
+	ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+out:
+	kfree(buf);
+	return ret;
+}
+
+static const struct file_operations debug_status_ops = {
+	.open = simple_open,
+	.read = debug_status_show,
+};
+
+static const struct coresight_ops_entry debug_status_entry = {
+	.name = "status",
+	.mode = S_IRUGO,
+	.ops = &debug_status_ops,
+};
+
+static ssize_t debug_trigger_cntr_read(struct file *file, char __user *user_buf,
+				       size_t count, loff_t *ppos)
+{
+	ssize_t ret;
+	struct etb_drvdata *drvdata = file->private_data;
+	unsigned long val = drvdata->trigger_cntr;
+	char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+
+	ret = scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
+	ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+	kfree(buf);
+	return ret;
+}
+
+static ssize_t debug_trigger_cntr_write(struct file *file,
+					const char __user *user_buf,
+					size_t count, loff_t *ppos)
+{
+	struct etb_drvdata *drvdata = file->private_data;
+	unsigned long val = 0;
+	char *buf;
+	int ret, len = 0;
+
+	buf = kmalloc(count + 1, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	if (copy_from_user(buf, user_buf, count)) {
+		ret = -EFAULT;
+		goto out;
+	}
+
+	buf[count] = '\0';
+
+	len = sscanf(buf, "%lx", &val);
+	if (len !=  1) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	drvdata->trigger_cntr = val;
+	return count;
+
+out:
+	kfree(buf);
+	return ret;
+}
+
+static const struct file_operations debug_trigger_cntr_ops = {
+	.open = simple_open,
+	.read = debug_trigger_cntr_read,
+	.write = debug_trigger_cntr_write,
+};
+
+static const struct coresight_ops_entry debug_trigger_cntr_entry = {
+	.name = "trigger_cntr",
+	.mode =  S_IRUGO | S_IWUSR,
+	.ops = &debug_trigger_cntr_ops,
+};
+
+static const struct coresight_ops_entry *etb_attr_grps[] = {
+	&debug_trigger_cntr_entry,
+	&debug_status_entry,
+	NULL,
+};
+
+static int etb_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct device *dev = &pdev->dev;
+	struct coresight_platform_data *pdata = NULL;
+	struct etb_drvdata *drvdata;
+	struct resource *res;
+	struct coresight_desc *desc;
+	struct device_node *np = pdev->dev.of_node;
+
+	if (np) {
+		pdata = of_get_coresight_platform_data(dev, np);
+		if (IS_ERR(pdata))
+			return PTR_ERR(pdata);
+		pdev->dev.platform_data = pdata;
+	}
+
+	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
+	if (!drvdata)
+		return -ENOMEM;
+	drvdata->dev = &pdev->dev;
+	platform_set_drvdata(pdev, drvdata);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	drvdata->base = devm_ioremap(dev, res->start, resource_size(res));
+	if (!drvdata->base)
+		return -ENOMEM;
+
+	spin_lock_init(&drvdata->spinlock);
+
+	if (pdata && pdata->clk) {
+		drvdata->clk = pdata->clk;
+		ret = clk_prepare_enable(drvdata->clk);
+		if (ret)
+			return ret;
+	}
+
+	drvdata->buffer_depth =  etb_get_buffer_depth(drvdata);
+	clk_disable_unprepare(drvdata->clk);
+
+	if (drvdata->buffer_depth < 0)
+		return -EINVAL;
+	drvdata->buf = devm_kzalloc(dev,
+				    drvdata->buffer_depth * BYTES_PER_WORD,
+				    GFP_KERNEL);
+	if (!drvdata->buf)
+		return -ENOMEM;
+
+	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
+	if (!desc)
+		return -ENOMEM;
+	desc->type = CORESIGHT_DEV_TYPE_SINK;
+	desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
+	desc->ops = &etb_cs_ops;
+	desc->pdata = pdev->dev.platform_data;
+	desc->dev = &pdev->dev;
+	desc->debugfs_ops = etb_attr_grps;
+	desc->owner = THIS_MODULE;
+	drvdata->csdev = coresight_register(desc);
+	if (IS_ERR(drvdata->csdev))
+		return PTR_ERR(drvdata->csdev);
+
+	drvdata->miscdev.name = ((struct coresight_platform_data *)
+				 (pdev->dev.platform_data))->name;
+	drvdata->miscdev.minor = MISC_DYNAMIC_MINOR;
+	drvdata->miscdev.fops = &etb_fops;
+	ret = misc_register(&drvdata->miscdev);
+	if (ret)
+		goto err;
+
+	dev_info(dev, "ETB initialized\n");
+	return 0;
+err:
+	coresight_unregister(drvdata->csdev);
+	return ret;
+}
+
+static int etb_remove(struct platform_device *pdev)
+{
+	struct etb_drvdata *drvdata = platform_get_drvdata(pdev);
+
+	misc_deregister(&drvdata->miscdev);
+	coresight_unregister(drvdata->csdev);
+	return 0;
+}
+
+static struct of_device_id etb_match[] = {
+	{.compatible = "arm,coresight-etb"},
+	{}
+};
+
+static struct platform_driver etb_driver = {
+	.probe          = etb_probe,
+	.remove         = etb_remove,
+	.driver         = {
+		.name   = "coresight-etb",
+		.owner	= THIS_MODULE,
+		.of_match_table = etb_match,
+	},
+};
+
+static int __init etb_init(void)
+{
+	return platform_driver_register(&etb_driver);
+}
+module_init(etb_init);
+
+static void __exit etb_exit(void)
+{
+	platform_driver_unregister(&etb_driver);
+}
+module_exit(etb_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CoreSight Embedded Trace Buffer driver");