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-hun...@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

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-hun...@ti.com>
+ *
+ * Based upon CTI Helpers by Ming Lei <ming....@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-hun...@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 */
-- 
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to