diff mbox

[RFC,3/5] ARM: CTI: Convert CTI helpers to AMBA bus driver

Message ID 1355348588-22318-4-git-send-email-jon-hunter@ti.com (mailing list archive)
State New, archived
Headers show

Commit Message

Hunter, Jon Dec. 12, 2012, 9:43 p.m. UTC
Convert the Cross Trigger Interface (CTI) helpers in cti.h into a
AMBA bus driver so that we can use device-tree to look-up the hardware
specific information such as base address and interrupt number during
the device probe. This also add APIs to request, cti_get() and release,
cti_put(), a CTI module so that drivers can allocate a module at
runtime.

Currently, the driver only supports looking-up the CTI hardware
information via device-tree, however, the driver could be extended to
support non-device-tree configurations if needed for a particular
architecture.

The CTI driver only currently supports CTI modules that have a single
CPU interrupt, however, could be extended in the future to support more
interrupts if a device requires this.

Signed-off-by: Jon Hunter <jon-hunter@ti.com>
---
 arch/arm/include/asm/cti.h |  153 ------------------------
 drivers/Kconfig            |    2 +
 drivers/amba/Kconfig       |   20 ++++
 drivers/amba/Makefile      |    1 +
 drivers/amba/cti.c         |  284 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/amba/cti.h   |   82 +++++++++++++
 6 files changed, 389 insertions(+), 153 deletions(-)
 delete mode 100644 arch/arm/include/asm/cti.h
 create mode 100644 drivers/amba/Kconfig
 create mode 100644 drivers/amba/cti.c
 create mode 100644 include/linux/amba/cti.h

Comments

Hunter, Jon Dec. 13, 2012, 7:35 p.m. UTC | #1
On 12/13/2012 09:08 AM, Will Deacon wrote:
> On Wed, Dec 12, 2012 at 09:43:06PM +0000, Jon Hunter wrote:
>> Convert the Cross Trigger Interface (CTI) helpers in cti.h into a
>> AMBA bus driver so that we can use device-tree to look-up the hardware
>> specific information such as base address and interrupt number during
>> the device probe. This also add APIs to request, cti_get() and release,
>> cti_put(), a CTI module so that drivers can allocate a module at
>> runtime.
>>
>> Currently, the driver only supports looking-up the CTI hardware
>> information via device-tree, however, the driver could be extended to
>> support non-device-tree configurations if needed for a particular
>> architecture.
>>
>> The CTI driver only currently supports CTI modules that have a single
>> CPU interrupt, however, could be extended in the future to support more
>> interrupts if a device requires this.
> 
> Aha, so elaborating on my earlier comments, we basically want to do the same
> thing for ETB I reckon. This does raise the question about namespaces
> though...
> 
>> +/**
>> + * struct cti - Cross Trigger Interface (CTI) struct
>> + *
>> + * @node:      Connects CTI instance to list of CTI instances
>> + * @dev:       Pointer to device structure
>> + * @base:      Mapped virtual address of the CTI module
>> + * @name:      Name associated with CTI instance
>> + * @irq:       Interrupt associated with CTI instance
>> + * @trig_out:  Trigger output associated with interrupt (@irq)
>> + * @reserved:  Used to indicate if CTI instance has been allocated
>> + * @enabled:   Used to indicate if CTI instance has been enabled
>> + */
>> +struct cti {
>> +       struct list_head node;
>> +       struct device *dev;
>> +       void __iomem *base;
>> +       const char *name;
>> +       int irq;
>> +       int trig_out;
>> +       bool reserved;
>> +       bool enabled;
>> +};
>> +
>> +#ifdef CONFIG_ARM_AMBA_CTI
>> +
>> +int cti_map_trigger(struct cti *cti, int trig_in, int trig_out, int chan);
>> +int cti_enable(struct cti *cti);
>> +int cti_disable(struct cti *cti);
>> +int cti_irq_ack(struct cti *cti);
>> +struct cti *cti_get(const char *name);
>> +void cti_put(struct cti *cti);
> 
> I wonder whether we should stick these all into a struct and have a general
> way to see which coresight devices we have and then retrieve their ops
> structures (so things like perf can walk a virtual coresight bus containing
> initialised devices).

Yes we could use a struct here. Hopefully, the enable/disable/get/put
could be used across coresight devices. I would need to think more about
the custom functions such as map_trigger which are specific to CTI.

> It might also help if we decide to describe the
> plumbing in the device tree, like Rob suggested.

Yes, I did propose adding more information to the binding for CTI to
describe trigger-ins/outs for a device. However, we could go a step
further and try and come up with a way to link the devices. Though I am
not sure if there are any other possible use-cases for CTI where that
may not be suitable and we just want to be able to configure it to map
trigger input to trigger output. Anyway, open to any ideas to improve this.

Cheers
Jon
Pratik Patel Dec. 21, 2012, 10:27 p.m. UTC | #2
On Wed, Dec 12, 2012 at 03:43:06PM -0600, Jon Hunter wrote:
> +
> +/**
> + * cti_irq_ack - acknowledges the CTI trigger output
> + * @cti: CTI instance
> + *
> + * Acknowledges the CTI trigger output by writting to the appropriate
> + * bit in the CTI interrupt acknowledge register.
> + */
> +int cti_irq_ack(struct cti *cti)
> +{
> +	u32 v;
> +
> +	if (!cti || !cti->enabled)
> +		return -EINVAL;
> +
> +	v = cti_readl(cti, CTIINTACK);

Just curious if CTIINTACK is a read-write register? This is a
read-only for us.

> +	v |= BIT(cti->trig_out);
> +	cti_writel(v, cti, CTIINTACK);
> +
> +	return 0;
> +}
> +
> +
> +static int cti_probe(struct amba_device *dev, const struct amba_id *id)
> +{
> +	struct cti *cti;
> +	struct device_node *np = dev->dev.of_node;
> +	int rc;
> +
> +	if (!np) {
> +		dev_err(&dev->dev, "device-tree not found!\n");
> +		return -ENODEV;
> +	}
> +
> +	cti = devm_kzalloc(&dev->dev, sizeof(struct cti), GFP_KERNEL);
> +	if (!cti) {
> +		dev_err(&dev->dev, "memory allocation failed!\n");
> +		return -ENOMEM;
> +	}
> +
> +	rc = of_property_read_string_index(np, "arm,cti-name", 0, &cti->name);
> +	if (rc) {
> +		dev_err(&dev->dev, "no name found for CTI!\n");
> +		return rc;
> +	}

Shouldn't the CTI driver have some kind of clock management that
it does for itself?

> +
> +	if (!dev->irq[0]) {
> +		dev_err(&dev->dev, "no CTI interrupt found!\n");
> +		return -ENODEV;
> +	}
> +
> +	cti->irq = dev->irq[0];
> +	cti->base = of_iomap(np, 0);
> +	if (!cti->base) {
> +		dev_err(&dev->dev, "unable to map CTI registers!\n");
> +		return -ENOMEM;
> +	}
> +
> +	cti->dev = &dev->dev;
> +	amba_set_drvdata(dev, cti);
> +	list_add_tail(&cti->node, &cti_list);
> +
> +	/*
> +	 * AMBA bus driver has already enabled RPM and incremented
> +	 * use-count, so now we can safely decrement the use-count
> +	 * and allow the CTI driver to manage RPM for the device.
> +	 */
> +	pm_runtime_put(&dev->dev);
> +
> +	dev_info(&dev->dev, "ARM CTI driver");
> +
> +	return 0;
> +}
> +
Pratik Patel Dec. 21, 2012, 10:35 p.m. UTC | #3
On Fri, Dec 21, 2012 at 02:27:03PM -0800, Pratik Patel wrote:
> On Wed, Dec 12, 2012 at 03:43:06PM -0600, Jon Hunter wrote:
> > +
> > +/**
> > + * cti_irq_ack - acknowledges the CTI trigger output
> > + * @cti: CTI instance
> > + *
> > + * Acknowledges the CTI trigger output by writting to the appropriate
> > + * bit in the CTI interrupt acknowledge register.
> > + */
> > +int cti_irq_ack(struct cti *cti)
> > +{
> > +	u32 v;
> > +
> > +	if (!cti || !cti->enabled)
> > +		return -EINVAL;
> > +
> > +	v = cti_readl(cti, CTIINTACK);
> 
> Just curious if CTIINTACK is a read-write register? This is a
> read-only for us.
> 
Mistyped - its a write-only for us
Hunter, Jon Jan. 2, 2013, 7:13 p.m. UTC | #4
On 12/21/2012 04:35 PM, Pratik Patel wrote:
> On Fri, Dec 21, 2012 at 02:27:03PM -0800, Pratik Patel wrote:
>> On Wed, Dec 12, 2012 at 03:43:06PM -0600, Jon Hunter wrote:
>>> +
>>> +/**
>>> + * cti_irq_ack - acknowledges the CTI trigger output
>>> + * @cti: CTI instance
>>> + *
>>> + * Acknowledges the CTI trigger output by writting to the appropriate
>>> + * bit in the CTI interrupt acknowledge register.
>>> + */
>>> +int cti_irq_ack(struct cti *cti)
>>> +{
>>> +	u32 v;
>>> +
>>> +	if (!cti || !cti->enabled)
>>> +		return -EINVAL;
>>> +
>>> +	v = cti_readl(cti, CTIINTACK);
>>
>> Just curious if CTIINTACK is a read-write register? This is a
>> read-only for us.
>>
> Mistyped - its a write-only for us

You are right. Looking at the ARM documentation this is a write-only
register. I had copied this function from the original helpers but had
not checked if this was readable. I will correct this.

Cheers
Jon
Hunter, Jon Jan. 2, 2013, 7:23 p.m. UTC | #5
On 12/21/2012 04:27 PM, Pratik Patel wrote:
> On Wed, Dec 12, 2012 at 03:43:06PM -0600, Jon Hunter wrote:
>> +
>> +/**
>> + * cti_irq_ack - acknowledges the CTI trigger output
>> + * @cti: CTI instance
>> + *
>> + * Acknowledges the CTI trigger output by writting to the appropriate
>> + * bit in the CTI interrupt acknowledge register.
>> + */
>> +int cti_irq_ack(struct cti *cti)
>> +{
>> +	u32 v;
>> +
>> +	if (!cti || !cti->enabled)
>> +		return -EINVAL;
>> +
>> +	v = cti_readl(cti, CTIINTACK);
> 
> Just curious if CTIINTACK is a read-write register? This is a
> read-only for us.
> 
>> +	v |= BIT(cti->trig_out);
>> +	cti_writel(v, cti, CTIINTACK);
>> +
>> +	return 0;
>> +}
>> +
>> +
>> +static int cti_probe(struct amba_device *dev, const struct amba_id *id)
>> +{
>> +	struct cti *cti;
>> +	struct device_node *np = dev->dev.of_node;
>> +	int rc;
>> +
>> +	if (!np) {
>> +		dev_err(&dev->dev, "device-tree not found!\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	cti = devm_kzalloc(&dev->dev, sizeof(struct cti), GFP_KERNEL);
>> +	if (!cti) {
>> +		dev_err(&dev->dev, "memory allocation failed!\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	rc = of_property_read_string_index(np, "arm,cti-name", 0, &cti->name);
>> +	if (rc) {
>> +		dev_err(&dev->dev, "no name found for CTI!\n");
>> +		return rc;
>> +	}
> 
> Shouldn't the CTI driver have some kind of clock management that
> it does for itself?

It does by using runtime PM. If you look at the cti_get/put functions,
you will see calls to pm_runtime_get/put. These calls will enable the
AMBA apb-clock. If you need to enable additional clocks then you could
register pm runtime resume/idle call backs to do this.

Cheers
Jon
Pratik Patel Jan. 3, 2013, 6:47 p.m. UTC | #6
On Wed, Jan 02, 2013 at 01:23:32PM -0600, Jon Hunter wrote:
> 
> On 12/21/2012 04:27 PM, Pratik Patel wrote:
> > On Wed, Dec 12, 2012 at 03:43:06PM -0600, Jon Hunter wrote:
> >> +
> >> +/**
> >> + * cti_irq_ack - acknowledges the CTI trigger output
> >> + * @cti: CTI instance
> >> + *
> >> + * Acknowledges the CTI trigger output by writting to the appropriate
> >> + * bit in the CTI interrupt acknowledge register.
> >> + */
> >> +int cti_irq_ack(struct cti *cti)
> >> +{
> >> +	u32 v;
> >> +
> >> +	if (!cti || !cti->enabled)
> >> +		return -EINVAL;
> >> +
> >> +	v = cti_readl(cti, CTIINTACK);
> > 
> > Just curious if CTIINTACK is a read-write register? This is a
> > read-only for us.
> > 
> >> +	v |= BIT(cti->trig_out);
> >> +	cti_writel(v, cti, CTIINTACK);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +
> >> +static int cti_probe(struct amba_device *dev, const struct amba_id *id)
> >> +{
> >> +	struct cti *cti;
> >> +	struct device_node *np = dev->dev.of_node;
> >> +	int rc;
> >> +
> >> +	if (!np) {
> >> +		dev_err(&dev->dev, "device-tree not found!\n");
> >> +		return -ENODEV;
> >> +	}
> >> +
> >> +	cti = devm_kzalloc(&dev->dev, sizeof(struct cti), GFP_KERNEL);
> >> +	if (!cti) {
> >> +		dev_err(&dev->dev, "memory allocation failed!\n");
> >> +		return -ENOMEM;
> >> +	}
> >> +
> >> +	rc = of_property_read_string_index(np, "arm,cti-name", 0, &cti->name);
> >> +	if (rc) {
> >> +		dev_err(&dev->dev, "no name found for CTI!\n");
> >> +		return rc;
> >> +	}
> > 
> > Shouldn't the CTI driver have some kind of clock management that
> > it does for itself?
> 
> It does by using runtime PM. If you look at the cti_get/put functions,
> you will see calls to pm_runtime_get/put. These calls will enable the
> AMBA apb-clock. If you need to enable additional clocks then you could
> register pm runtime resume/idle call backs to do this.
> 

Ok
diff mbox

Patch

diff --git a/arch/arm/include/asm/cti.h b/arch/arm/include/asm/cti.h
deleted file mode 100644
index 00add00..0000000
--- a/arch/arm/include/asm/cti.h
+++ /dev/null
@@ -1,153 +0,0 @@ 
-#ifndef __ASMARM_CTI_H
-#define __ASMARM_CTI_H
-
-#include	<asm/io.h>
-#include	<asm/hardware/coresight.h>
-
-/* The registers' definition is from section 3.2 of
- * Embedded Cross Trigger Revision: r0p0
- */
-#define		CTICONTROL		0x000
-#define		CTISTATUS		0x004
-#define		CTILOCK			0x008
-#define		CTIPROTECTION		0x00C
-#define		CTIINTACK		0x010
-#define		CTIAPPSET		0x014
-#define		CTIAPPCLEAR		0x018
-#define		CTIAPPPULSE		0x01c
-#define		CTIINEN			0x020
-#define		CTIOUTEN		0x0A0
-#define		CTITRIGINSTATUS		0x130
-#define		CTITRIGOUTSTATUS	0x134
-#define		CTICHINSTATUS		0x138
-#define		CTICHOUTSTATUS		0x13c
-#define		CTIPERIPHID0		0xFE0
-#define		CTIPERIPHID1		0xFE4
-#define		CTIPERIPHID2		0xFE8
-#define		CTIPERIPHID3		0xFEC
-#define		CTIPCELLID0		0xFF0
-#define		CTIPCELLID1		0xFF4
-#define		CTIPCELLID2		0xFF8
-#define		CTIPCELLID3		0xFFC
-
-/**
- * struct cti - cross trigger interface struct
- * @base: mapped virtual address for the cti base
- * @irq: irq number for the cti
- * @trig_out_for_irq: triger out number which will cause
- *	the @irq happen
- *
- * cti struct used to operate cti registers.
- */
-struct cti {
-	void __iomem *base;
-	int irq;
-	int trig_out_for_irq;
-};
-
-/**
- * cti_init - initialize the cti instance
- * @cti: cti instance
- * @base: mapped virtual address for the cti base
- * @irq: irq number for the cti
- * @trig_out: triger out number which will cause
- *	the @irq happen
- *
- * called by machine code to pass the board dependent
- * @base, @irq and @trig_out to cti.
- */
-static inline void cti_init(struct cti *cti,
-	void __iomem *base, int irq, int trig_out)
-{
-	cti->base = base;
-	cti->irq  = irq;
-	cti->trig_out_for_irq = trig_out;
-}
-
-/**
- * cti_map_trigger - use the @chan to map @trig_in to @trig_out
- * @cti: cti instance
- * @trig_in: trigger in number
- * @trig_out: trigger out number
- * @channel: channel number
- *
- * This function maps one trigger in of @trig_in to one trigger
- * out of @trig_out using the channel @chan.
- */
-static inline void cti_map_trigger(struct cti *cti,
-	int trig_in, int trig_out, int chan)
-{
-	void __iomem *base = cti->base;
-	unsigned long val;
-
-	val = __raw_readl(base + CTIINEN + trig_in * 4);
-	val |= BIT(chan);
-	__raw_writel(val, base + CTIINEN + trig_in * 4);
-
-	val = __raw_readl(base + CTIOUTEN + trig_out * 4);
-	val |= BIT(chan);
-	__raw_writel(val, base + CTIOUTEN + trig_out * 4);
-}
-
-/**
- * cti_enable - enable the cti module
- * @cti: cti instance
- *
- * enable the cti module
- */
-static inline void cti_enable(struct cti *cti)
-{
-	__raw_writel(0x1, cti->base + CTICONTROL);
-}
-
-/**
- * cti_disable - disable the cti module
- * @cti: cti instance
- *
- * enable the cti module
- */
-static inline void cti_disable(struct cti *cti)
-{
-	__raw_writel(0, cti->base + CTICONTROL);
-}
-
-/**
- * cti_irq_ack - clear the cti irq
- * @cti: cti instance
- *
- * clear the cti irq
- */
-static inline void cti_irq_ack(struct cti *cti)
-{
-	void __iomem *base = cti->base;
-	unsigned long val;
-
-	val = __raw_readl(base + CTIINTACK);
-	val |= BIT(cti->trig_out_for_irq);
-	__raw_writel(val, base + CTIINTACK);
-}
-
-/**
- * cti_unlock - unlock cti module
- * @cti: cti instance
- *
- * unlock the cti module, or else any writes to the cti
- * module is not allowed.
- */
-static inline void cti_unlock(struct cti *cti)
-{
-	coresight_unlock(cti->base);
-}
-
-/**
- * cti_lock - lock cti module
- * @cti: cti instance
- *
- * lock the cti module, so any writes to the cti
- * module will be not allowed.
- */
-static inline void cti_lock(struct cti *cti)
-{
-	coresight_lock(cti->base);
-}
-#endif
diff --git a/drivers/Kconfig b/drivers/Kconfig
index dbdefa3..e857075 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -2,6 +2,8 @@  menu "Device Drivers"
 
 source "drivers/base/Kconfig"
 
+source "drivers/amba/Kconfig"
+
 source "drivers/bus/Kconfig"
 
 source "drivers/connector/Kconfig"
diff --git a/drivers/amba/Kconfig b/drivers/amba/Kconfig
new file mode 100644
index 0000000..b97ea23
--- /dev/null
+++ b/drivers/amba/Kconfig
@@ -0,0 +1,20 @@ 
+#
+# AMBA Devices
+#
+
+menu "AMBA devices"
+
+config ARM_AMBA_CTI
+	bool "Cross-Trigger Interface"
+	depends on ARM && OF
+	select ARM_AMBA
+	help
+	  The ARM Cross Trigger Interface provides a way to route events
+	  between processor modules. For example, debug events from one
+	  processor can be broadcasted to other processors. The events that
+	  can be routed between processors are specific to the device.
+	  Currently, the driver only supports looking-up the CTI hardware
+	  information (base address and interrupts) from device-tree (and
+	  hence, is dependent upon CONFIG_OF).
+
+endmenu
diff --git a/drivers/amba/Makefile b/drivers/amba/Makefile
index 66e81c2..f74abe9 100644
--- a/drivers/amba/Makefile
+++ b/drivers/amba/Makefile
@@ -1,2 +1,3 @@ 
 obj-$(CONFIG_ARM_AMBA)		+= bus.o
+obj-$(CONFIG_ARM_AMBA_CTI)	+= cti.o
 obj-$(CONFIG_TEGRA_AHB)		+= tegra-ahb.o
diff --git a/drivers/amba/cti.c b/drivers/amba/cti.c
new file mode 100644
index 0000000..04debe7
--- /dev/null
+++ b/drivers/amba/cti.c
@@ -0,0 +1,284 @@ 
+/*
+ * ARM Cross Trigger Interface (CTI) Driver
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *  Jon Hunter <jon-hunter@ti.com>
+ *
+ * Based upon CTI Helpers by Ming Lei <ming.lei@canonical.com>.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <asm/hardware/coresight.h>
+
+#include <linux/amba/bus.h>
+#include <linux/amba/cti.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+
+/* The registers' definition is from section 3.2 of
+ * Embedded Cross Trigger Revision: r0p0
+ */
+#define		CTICONTROL		0x000
+#define		CTISTATUS		0x004
+#define		CTILOCK			0x008
+#define		CTIPROTECTION		0x00C
+#define		CTIINTACK		0x010
+#define		CTIAPPSET		0x014
+#define		CTIAPPCLEAR		0x018
+#define		CTIAPPPULSE		0x01c
+#define		CTIINEN			0x020
+#define		CTIOUTEN		0x0A0
+#define		CTITRIGINSTATUS		0x130
+#define		CTITRIGOUTSTATUS	0x134
+#define		CTICHINSTATUS		0x138
+#define		CTICHOUTSTATUS		0x13c
+#define		CTIPERIPHID0		0xFE0
+#define		CTIPERIPHID1		0xFE4
+#define		CTIPERIPHID2		0xFE8
+#define		CTIPERIPHID3		0xFEC
+#define		CTIPCELLID0		0xFF0
+#define		CTIPCELLID1		0xFF4
+#define		CTIPCELLID2		0xFF8
+#define		CTIPCELLID3		0xFFC
+#define		CTI_MAX_CHANNELS	15
+#define		CTI_MAX_TRIGGERS	7
+
+#define cti_writel(v, c, x) (__raw_writel((v), (c)->base + (x)))
+#define cti_readl(c, x) (__raw_readl((c)->base + (x)))
+
+static DEFINE_SPINLOCK(cti_lock);
+static LIST_HEAD(cti_list);
+
+/**
+ * cti_map_trigger - use the @chan to map @trig_in to @trig_out
+ * @cti:	CTI instance
+ * @trig_in:	trigger in number
+ * @trig_out:	trigger out number
+ * @chan:	channel number
+ *
+ * Maps one trigger in of @trig_in to one trigger out of @trig_out
+ * using the channel @chan. The CTI module must not be enabled when
+ * calling this function.
+ */
+int cti_map_trigger(struct cti *cti, int trig_in, int trig_out, int chan)
+{
+	u32 v;
+
+	if (!cti)
+		return -EINVAL;
+
+	if (cti->enabled)
+		return -EBUSY;
+
+	if (chan > CTI_MAX_CHANNELS)
+		return -EINVAL;
+
+	if ((trig_in > CTI_MAX_TRIGGERS) || (trig_out > CTI_MAX_TRIGGERS))
+		return -EINVAL;
+
+	coresight_unlock(cti->base);
+
+	v = cti_readl(cti, CTIINEN + trig_in * 4);
+	v |= BIT(chan);
+	cti_writel(v, cti, CTIINEN + trig_in * 4);
+	v = cti_readl(cti, CTIOUTEN + trig_out * 4);
+	v |= BIT(chan);
+	cti_writel(v, cti, CTIOUTEN + trig_out * 4);
+	cti->trig_out = trig_out;
+
+	coresight_lock(cti->base);
+
+	return 0;
+}
+
+/**
+ * cti_enable - enable the CTI module
+ * @cti: CTI instance
+ *
+ * Unlocks and enables the CTI module. The CTI module cannot be
+ * programmed again until it has been disabled.
+ */
+int cti_enable(struct cti *cti)
+{
+	if (!cti || cti->enabled)
+			return -EINVAL;
+
+	coresight_unlock(cti->base);
+	cti_writel(1, cti, CTICONTROL);
+	cti->enabled = true;
+
+	return 0;
+}
+
+/**
+ * cti_disable - disable the CTI module
+ * @cti: CTI instance
+ *
+ * Disables and locks the CTI module.
+ */
+int cti_disable(struct cti *cti)
+{
+	if (!cti || !cti->enabled)
+		return -EINVAL;
+
+	cti_writel(0, cti, CTICONTROL);
+	cti->enabled = false;
+	coresight_lock(cti->base);
+
+	return 0;
+}
+
+/**
+ * cti_irq_ack - acknowledges the CTI trigger output
+ * @cti: CTI instance
+ *
+ * Acknowledges the CTI trigger output by writting to the appropriate
+ * bit in the CTI interrupt acknowledge register.
+ */
+int cti_irq_ack(struct cti *cti)
+{
+	u32 v;
+
+	if (!cti || !cti->enabled)
+		return -EINVAL;
+
+	v = cti_readl(cti, CTIINTACK);
+	v |= BIT(cti->trig_out);
+	cti_writel(v, cti, CTIINTACK);
+
+	return 0;
+}
+
+/**
+ * cti_get - acquire a CTI module
+ * @name: name of CTI instance
+ *
+ * Acquires a CTI module from a list of CTI modules by name. If the CTI
+ * module is already in use then return NULL, otherwise return a valid
+ * handle to the CTI module.
+ */
+struct cti *cti_get(const char *name)
+{
+	struct cti *cti = NULL;
+	unsigned long flags;
+
+	if (!name)
+		return NULL;
+
+	spin_lock_irqsave(&cti_lock, flags);
+
+	if (list_empty(&cti_list))
+		goto out;
+
+	list_for_each_entry(cti, &cti_list, node) {
+		if (!strcmp(cti->name, name) && (!cti->reserved)) {
+			cti->reserved = true;
+			goto out;
+		}
+	}
+
+out:
+	spin_unlock_irqrestore(&cti_lock, flags);
+
+	if (cti)
+		pm_runtime_get_sync(cti->dev);
+
+	return cti;
+}
+
+/**
+ * cti_put - release handle to CTI module
+ * @cti: CTI instance
+ *
+ * Releases a handle to CTI module that was previously acquired.
+ */
+void cti_put(struct cti *cti)
+{
+	if (!cti || !cti->reserved)
+		return;
+
+	cti->reserved = false;
+
+	pm_runtime_put(cti->dev);
+}
+
+static int cti_probe(struct amba_device *dev, const struct amba_id *id)
+{
+	struct cti *cti;
+	struct device_node *np = dev->dev.of_node;
+	int rc;
+
+	if (!np) {
+		dev_err(&dev->dev, "device-tree not found!\n");
+		return -ENODEV;
+	}
+
+	cti = devm_kzalloc(&dev->dev, sizeof(struct cti), GFP_KERNEL);
+	if (!cti) {
+		dev_err(&dev->dev, "memory allocation failed!\n");
+		return -ENOMEM;
+	}
+
+	rc = of_property_read_string_index(np, "arm,cti-name", 0, &cti->name);
+	if (rc) {
+		dev_err(&dev->dev, "no name found for CTI!\n");
+		return rc;
+	}
+
+	if (!dev->irq[0]) {
+		dev_err(&dev->dev, "no CTI interrupt found!\n");
+		return -ENODEV;
+	}
+
+	cti->irq = dev->irq[0];
+	cti->base = of_iomap(np, 0);
+	if (!cti->base) {
+		dev_err(&dev->dev, "unable to map CTI registers!\n");
+		return -ENOMEM;
+	}
+
+	cti->dev = &dev->dev;
+	amba_set_drvdata(dev, cti);
+	list_add_tail(&cti->node, &cti_list);
+
+	/*
+	 * AMBA bus driver has already enabled RPM and incremented
+	 * use-count, so now we can safely decrement the use-count
+	 * and allow the CTI driver to manage RPM for the device.
+	 */
+	pm_runtime_put(&dev->dev);
+
+	dev_info(&dev->dev, "ARM CTI driver");
+
+	return 0;
+}
+
+static const struct amba_id cti_ids[] = {
+	{
+		.id	= 0x003bb906,
+		.mask	= 0x00ffffff,
+	},
+	{ 0, 0 },
+};
+
+static struct amba_driver cti_driver = {
+	.drv		= {
+		.name	= "cti",
+	},
+	.id_table	= cti_ids,
+	.probe		= cti_probe,
+};
+
+static int __init cti_init(void)
+{
+	return amba_driver_register(&cti_driver);
+}
+subsys_initcall(cti_init);
diff --git a/include/linux/amba/cti.h b/include/linux/amba/cti.h
new file mode 100644
index 0000000..a82ae76
--- /dev/null
+++ b/include/linux/amba/cti.h
@@ -0,0 +1,82 @@ 
+/*
+ * ARM Cross Trigger Interface Platform Driver
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *  Jon Hunter <jon-hunter@ti.com>
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef AMBA_CTI_H
+#define AMBA_CTI_H
+
+#include <linux/io.h>
+
+/**
+ * struct cti - Cross Trigger Interface (CTI) struct
+ *
+ * @node:	Connects CTI instance to list of CTI instances
+ * @dev:	Pointer to device structure
+ * @base:	Mapped virtual address of the CTI module
+ * @name:	Name associated with CTI instance
+ * @irq:	Interrupt associated with CTI instance
+ * @trig_out:	Trigger output associated with interrupt (@irq)
+ * @reserved:	Used to indicate if CTI instance has been allocated
+ * @enabled:	Used to indicate if CTI instance has been enabled
+ */
+struct cti {
+	struct list_head node;
+	struct device *dev;
+	void __iomem *base;
+	const char *name;
+	int irq;
+	int trig_out;
+	bool reserved;
+	bool enabled;
+};
+
+#ifdef CONFIG_ARM_AMBA_CTI
+
+int cti_map_trigger(struct cti *cti, int trig_in, int trig_out, int chan);
+int cti_enable(struct cti *cti);
+int cti_disable(struct cti *cti);
+int cti_irq_ack(struct cti *cti);
+struct cti *cti_get(const char *name);
+void cti_put(struct cti *cti);
+
+#else
+
+static inline int cti_map_trigger(struct cti *cti, int trig_in, int trig_out,
+				  int chan)
+{
+	return 0;
+}
+
+static inline int cti_enable(struct cti *cti)
+{
+	return 0;
+}
+
+static inline int cti_disable(struct cti *cti)
+{
+	return 0;
+}
+
+static inline int cti_irq_ack(struct cti *cti)
+{
+	return 0;
+}
+
+static inline struct cti *cti_get(const char *name)
+{
+	return NULL;
+}
+
+static inline void cti_put(struct cti *cti) {}
+
+#endif /* ARM_AMBA_CTI */
+
+#endif /* AMBA_CTI_H */