@@ -918,6 +918,14 @@ M: Hubert Feurstein <hubert.feurstein@contec.at>
S: Maintained
F: arch/arm/mach-ep93xx/micro9.c
+ARM/CORESIGHT FRAMEWORK AND DRIVERS
+M: Mathieu Poirier <mathieu.poirier@linaro.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: drivers/coresight/*
+F: Documentation/trace/coresight.txt
+F: Documentation/devicetree/bindings/arm/coresight.txt
+
ARM/CORGI MACHINE SUPPORT
M: Richard Purdie <rpurdie@rpsys.net>
S: Maintained
@@ -1331,4 +1331,13 @@ config DEBUG_SET_MODULE_RONX
against certain classes of kernel exploits.
If in doubt, say "N".
+menuconfig CORESIGHT
+ bool "CoreSight Tracing Support"
+ select ARM_AMBA
+ help
+ This framework provides a kernel interface for the CoreSight debug
+ and trace drivers to register themselves with. It's intended to build
+ a topological view of the CoreSight components based on a DT
+ specification and configure the right serie of components when a
+ trace source gets enabled.
endmenu
@@ -161,3 +161,4 @@ obj-$(CONFIG_POWERCAP) += powercap/
obj-$(CONFIG_MCB) += mcb/
obj-$(CONFIG_RAS) += ras/
obj-$(CONFIG_THUNDERBOLT) += thunderbolt/
+obj-$(CONFIG_CORESIGHT) += coresight/
@@ -336,7 +336,7 @@ int amba_device_add(struct amba_device *dev, struct resource *parent)
amba_put_disable_pclk(dev);
- if (cid == AMBA_CID)
+ if (cid == AMBA_CID || cid == CORESIGHT_CID)
dev->periphid = pid;
if (!dev->periphid)
new file mode 100644
@@ -0,0 +1,5 @@
+#
+# Makefile for CoreSight drivers.
+#
+obj-$(CONFIG_CORESIGHT) += coresight.o
+obj-$(CONFIG_OF) += of_coresight.o
new file mode 100644
@@ -0,0 +1,63 @@
+/* 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.
+ */
+
+#ifndef _CORESIGHT_PRIV_H
+#define _CORESIGHT_PRIV_H
+
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/coresight.h>
+
+/*
+ * Coresight management registers (0xf00-0xfcc)
+ * 0xfa0 - 0xfa4: Management registers in PFTv1.0
+ * Trace registers in PFTv1.1
+ */
+#define CORESIGHT_ITCTRL 0xf00
+#define CORESIGHT_CLAIMSET 0xfa0
+#define CORESIGHT_CLAIMCLR 0xfa4
+#define CORESIGHT_LAR 0xfb0
+#define CORESIGHT_LSR 0xfb4
+#define CORESIGHT_AUTHSTATUS 0xfb8
+#define CORESIGHT_DEVID 0xfc8
+#define CORESIGHT_DEVTYPE 0xfcc
+
+#define TIMEOUT_US 100
+#define BMVAL(val, lsb, msb) ((val & GENMASK(msb, lsb)) >> lsb)
+
+static inline void CS_LOCK(void __iomem *addr)
+{
+ do {
+ /* Wait for things to settle */
+ mb();
+ writel_relaxed(0x0, addr + CORESIGHT_LAR);
+ } while (0);
+}
+
+static inline void CS_UNLOCK(void __iomem *addr)
+{
+ do {
+ writel_relaxed(CORESIGHT_UNLOCK, addr + CORESIGHT_LAR);
+ /* Make sure eveyone has seen this */
+ mb();
+ } while (0);
+}
+
+#ifdef CONFIG_CORESIGHT_SOURCE_ETM3X
+extern int etm_readl_cp14(u32 off, unsigned int *val);
+extern int etm_writel_cp14(u32 off, u32 val);
+#else
+static inline int etm_readl_cp14(u32 off, unsigned int *val) { return 0; }
+static inline int etm_writel_cp14(u32 val, u32 off) { return 0; }
+#endif
+
+#endif
new file mode 100644
@@ -0,0 +1,717 @@
+/* Copyright (c) 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/io.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/clk.h>
+#include <linux/coresight.h>
+#include <linux/of_platform.h>
+#include <linux/delay.h>
+
+#include "coresight-priv.h"
+
+static DEFINE_MUTEX(coresight_mutex);
+
+static int coresight_id_match(struct device *dev, void *data)
+{
+ int trace_id, i_trace_id;
+ struct coresight_device *csdev, *i_csdev;
+
+ csdev = data;
+ i_csdev = to_coresight_device(dev);
+
+ /*
+ * No need to care about oneself and components that are not
+ * sources or not enabled
+ */
+ if (i_csdev == csdev || !i_csdev->enable ||
+ i_csdev->type != CORESIGHT_DEV_TYPE_SOURCE)
+ return 0;
+
+ /* Get the source ID for both compoment */
+ trace_id = source_ops(csdev)->trace_id(csdev);
+ i_trace_id = source_ops(i_csdev)->trace_id(i_csdev);
+
+ /* All you need is one */
+ if (trace_id == i_trace_id)
+ return 1;
+
+ return 0;
+}
+
+static int coresight_source_is_unique(struct coresight_device *csdev)
+{
+ int trace_id = source_ops(csdev)->trace_id(csdev);
+
+ /* this shouldn't happen */
+ if (trace_id < 0)
+ return 0;
+
+ return !bus_for_each_dev(&coresight_bustype, NULL,
+ csdev, coresight_id_match);
+}
+
+static int coresight_find_link_inport(struct coresight_device *csdev)
+{
+ int i;
+ struct coresight_device *parent;
+ struct coresight_connection *conn;
+
+ parent = container_of(csdev->path_link.next,
+ struct coresight_device, path_link);
+
+ for (i = 0; i < parent->nr_outport; i++) {
+ conn = &parent->conns[i];
+ if (conn->child_dev == csdev)
+ return conn->child_port;
+ }
+
+ dev_err(&csdev->dev, "couldn't find inport, parent: %s, child: %s\n",
+ dev_name(&parent->dev), dev_name(&csdev->dev));
+
+ return 0;
+}
+
+static int coresight_find_link_outport(struct coresight_device *csdev)
+{
+ int i;
+ struct coresight_device *child;
+ struct coresight_connection *conn;
+
+ child = container_of(csdev->path_link.prev,
+ struct coresight_device, path_link);
+
+ for (i = 0; i < csdev->nr_outport; i++) {
+ conn = &csdev->conns[i];
+ if (conn->child_dev == child)
+ return conn->outport;
+ }
+
+ dev_err(&csdev->dev, "couldn't find outport, parent: %s, child: %s\n",
+ dev_name(&csdev->dev), dev_name(&child->dev));
+
+ return 0;
+}
+
+static int coresight_enable_sink(struct coresight_device *csdev)
+{
+ int ret;
+
+ if (!csdev->enable) {
+ if (sink_ops(csdev)->enable) {
+ ret = sink_ops(csdev)->enable(csdev);
+ if (ret)
+ return ret;
+ }
+ csdev->enable = true;
+ }
+
+ atomic_inc(csdev->refcnt);
+
+ return 0;
+}
+
+static void coresight_disable_sink(struct coresight_device *csdev)
+{
+ if (atomic_dec_return(csdev->refcnt) == 0) {
+ if (sink_ops(csdev)->disable) {
+ sink_ops(csdev)->disable(csdev);
+ csdev->enable = false;
+ }
+ }
+}
+
+static int coresight_enable_link(struct coresight_device *csdev)
+{
+ int ret;
+ int link_subtype;
+ int refport, inport, outport;
+
+ inport = coresight_find_link_inport(csdev);
+ outport = coresight_find_link_outport(csdev);
+ link_subtype = csdev->subtype.link_subtype;
+
+ if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG)
+ refport = inport;
+ else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT)
+ refport = outport;
+ else
+ refport = 0;
+
+ if (atomic_inc_return(&csdev->refcnt[refport]) == 1) {
+ if (link_ops(csdev)->enable) {
+ ret = link_ops(csdev)->enable(csdev, inport, outport);
+ if (ret)
+ return ret;
+ }
+ }
+
+ csdev->enable = true;
+
+ return 0;
+}
+
+static void coresight_disable_link(struct coresight_device *csdev)
+{
+ int i, nr_conns;
+ int link_subtype;
+ int refport, inport, outport;
+
+ inport = coresight_find_link_inport(csdev);
+ outport = coresight_find_link_outport(csdev);
+ link_subtype = csdev->subtype.link_subtype;
+
+ if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) {
+ refport = inport;
+ nr_conns = csdev->nr_inport;
+ } else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT) {
+ refport = outport;
+ nr_conns = csdev->nr_outport;
+ } else {
+ refport = 0;
+ nr_conns = 1;
+ }
+
+ if (atomic_dec_return(&csdev->refcnt[refport]) == 0) {
+ if (link_ops(csdev)->disable)
+ link_ops(csdev)->disable(csdev, inport, outport);
+ }
+
+ for (i = 0; i < nr_conns; i++)
+ if (atomic_read(&csdev->refcnt[i]) != 0)
+ return;
+
+ csdev->enable = false;
+}
+
+static int coresight_enable_source(struct coresight_device *csdev)
+{
+ int ret;
+
+ if (!coresight_source_is_unique(csdev)) {
+ dev_warn(&csdev->dev, "traceID %d not unique\n",
+ source_ops(csdev)->trace_id(csdev));
+ return -EINVAL;
+ }
+
+ if (!csdev->enable) {
+ if (source_ops(csdev)->enable) {
+ ret = source_ops(csdev)->enable(csdev);
+ if (ret)
+ return ret;
+ }
+ csdev->enable = true;
+ }
+
+ atomic_inc(csdev->refcnt);
+
+ return 0;
+}
+
+static void coresight_disable_source(struct coresight_device *csdev)
+{
+ if (atomic_dec_return(csdev->refcnt) == 0) {
+ if (source_ops(csdev)->disable) {
+ source_ops(csdev)->disable(csdev);
+ csdev->enable = false;
+ }
+ }
+}
+
+static int coresight_enable_path(struct list_head *path)
+{
+ int ret = 0;
+ struct coresight_device *cd;
+
+ list_for_each_entry(cd, path, path_link) {
+ if (cd == list_first_entry(path, struct coresight_device,
+ path_link)) {
+ ret = coresight_enable_sink(cd);
+ } else if (list_is_last(&cd->path_link, path)) {
+ /*
+ * Don't enable the source just yet - this needs to
+ * happen at the very end when all links and sink
+ * along the path have been configured properly.
+ */
+ ;
+ } else {
+ ret = coresight_enable_link(cd);
+ }
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+err:
+ list_for_each_entry_continue_reverse(cd, path, path_link) {
+ if (cd == list_first_entry(path, struct coresight_device,
+ path_link)) {
+ coresight_disable_sink(cd);
+ } else if (list_is_last(&cd->path_link, path)) {
+ ;
+ } else {
+ coresight_disable_link(cd);
+ }
+ }
+
+ return ret;
+}
+
+static int coresight_disable_path(struct list_head *path)
+{
+ struct coresight_device *cd;
+
+ list_for_each_entry_reverse(cd, path, path_link) {
+ if (cd == list_first_entry(path, struct coresight_device,
+ path_link)) {
+ coresight_disable_sink(cd);
+ } else if (list_is_last(&cd->path_link, path)) {
+ /*
+ * The source has already been stopped, no need
+ * to do it again here.
+ */
+ ;
+ } else {
+ coresight_disable_link(cd);
+ }
+ }
+
+ return 0;
+}
+
+static int coresight_build_paths(struct coresight_device *csdev,
+ struct list_head *path,
+ bool enable)
+{
+ int i, ret = -EINVAL;
+ struct coresight_connection *conn;
+
+ list_add(&csdev->path_link, path);
+
+ if (csdev->type == CORESIGHT_DEV_TYPE_SINK && csdev->activated) {
+ if (enable)
+ ret = coresight_enable_path(path);
+ else
+ ret = coresight_disable_path(path);
+ } else {
+ for (i = 0; i < csdev->nr_outport; i++) {
+ conn = &csdev->conns[i];
+ if (coresight_build_paths(conn->child_dev,
+ path, enable) == 0)
+ ret = 0;
+ }
+ }
+
+ if (list_first_entry(path, struct coresight_device, path_link) != csdev)
+ dev_err(&csdev->dev, "wrong device in %s\n", __func__);
+
+ list_del(&csdev->path_link);
+
+ return ret;
+}
+
+int coresight_enable(struct coresight_device *csdev)
+{
+ int ret = 0;
+ LIST_HEAD(path);
+
+ mutex_lock(&coresight_mutex);
+ if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) {
+ ret = -EINVAL;
+ dev_err(&csdev->dev, "wrong device type in %s\n", __func__);
+ goto out;
+ }
+ if (csdev->enable)
+ goto out;
+
+ if (coresight_build_paths(csdev, &path, true)) {
+ dev_err(&csdev->dev, "building path(s) failed\n");
+ goto out;
+ }
+
+ if (coresight_enable_source(csdev))
+ dev_err(&csdev->dev, "source enable failed\n");
+out:
+ mutex_unlock(&coresight_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(coresight_enable);
+
+void coresight_disable(struct coresight_device *csdev)
+{
+ LIST_HEAD(path);
+
+ mutex_lock(&coresight_mutex);
+ if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) {
+ dev_err(&csdev->dev, "wrong device type in %s\n", __func__);
+ goto out;
+ }
+ if (!csdev->enable)
+ goto out;
+
+ coresight_disable_source(csdev);
+ if (coresight_build_paths(csdev, &path, false))
+ dev_err(&csdev->dev, "releasing path(s) failed\n");
+
+out:
+ mutex_unlock(&coresight_mutex);
+}
+EXPORT_SYMBOL_GPL(coresight_disable);
+
+static ssize_t enable_sink_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct coresight_device *csdev = to_coresight_device(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", (unsigned)csdev->activated);
+}
+
+static ssize_t enable_sink_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ int ret;
+ unsigned long val;
+ struct coresight_device *csdev = to_coresight_device(dev);
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ if (val)
+ csdev->activated = true;
+ else
+ csdev->activated = false;
+
+ return size;
+
+}
+static DEVICE_ATTR_RW(enable_sink);
+
+static ssize_t enable_source_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct coresight_device *csdev = to_coresight_device(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", (unsigned)csdev->enable);
+}
+
+static ssize_t enable_source_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ int ret = 0;
+ unsigned long val;
+ struct coresight_device *csdev = to_coresight_device(dev);
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ if (val) {
+ ret = coresight_enable(csdev);
+ if (ret)
+ return ret;
+ } else {
+ coresight_disable(csdev);
+ }
+
+ return size;
+}
+static DEVICE_ATTR_RW(enable_source);
+
+static struct attribute *coresight_sink_attrs[] = {
+ &dev_attr_enable_sink.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(coresight_sink);
+
+static struct attribute *coresight_source_attrs[] = {
+ &dev_attr_enable_source.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(coresight_source);
+
+static struct device_type coresight_dev_type[] = {
+ {
+ .name = "none",
+ },
+ {
+ .name = "sink",
+ .groups = coresight_sink_groups,
+ },
+ {
+ .name = "link",
+ },
+ {
+ .name = "linksink",
+ .groups = coresight_sink_groups,
+ },
+ {
+ .name = "source",
+ .groups = coresight_source_groups,
+ },
+};
+
+static void coresight_device_release(struct device *dev)
+{
+ struct coresight_device *csdev = to_coresight_device(dev);
+
+ kfree(csdev);
+}
+
+static int coresight_orphan_match(struct device *dev, void *data)
+{
+ int i;
+ bool still_orphan = false;
+ struct coresight_device *csdev, *i_csdev;
+ struct coresight_connection *conn;
+
+ csdev = data;
+ i_csdev = to_coresight_device(dev);
+
+ /* No need to check oneself */
+ if (csdev == i_csdev)
+ return 0;
+
+ /* Move on to another component if no connection is orphan */
+ if (!i_csdev->orphan)
+ return 0;
+ /*
+ * Circle throuch all the connection of that component. If we find
+ * an orphan connection whose name matches @csdev, link it.
+ */
+ for (i = 0; i < i_csdev->nr_outport; i++) {
+ conn = &i_csdev->conns[i];
+
+ /* We have found at least one orphan connection */
+ if (conn->child_dev == NULL) {
+ /* Does it match this newly added device? */
+ if (!strcmp(dev_name(&csdev->dev), conn->child_name))
+ conn->child_dev = csdev;
+ } else {
+ /* Too bad, this component still has an orphan */
+ still_orphan = true;
+ }
+ }
+
+ i_csdev->orphan = still_orphan;
+
+ /*
+ * Returning '0' ensures that all known component on the
+ * bus will be checked.
+ */
+ return 0;
+}
+
+static void coresight_fixup_orphan_conns(struct coresight_device *csdev)
+{
+ /*
+ * No need to check for a return value as orphan connection(s)
+ * are hooked-up with each newly added component.
+ */
+ bus_for_each_dev(&coresight_bustype, NULL,
+ csdev, coresight_orphan_match);
+}
+
+
+static int coresight_name_match(struct device *dev, void *data)
+{
+ char *to_match;
+ struct coresight_device *i_csdev;
+
+ to_match = data;
+ i_csdev = to_coresight_device(dev);
+
+ if (!strcmp(to_match, dev_name(&i_csdev->dev)))
+ return 1;
+
+ return 0;
+}
+
+static void coresight_fixup_device_conns(struct coresight_device *csdev)
+{
+ int i;
+ struct device *dev = NULL;
+ struct coresight_connection *conn;
+
+ for (i = 0; i < csdev->nr_outport; i++) {
+ conn = &csdev->conns[i];
+ dev = bus_find_device(&coresight_bustype, NULL,
+ (void *)conn->child_name,
+ coresight_name_match);
+
+ if (dev) {
+ conn->child_dev = to_coresight_device(dev);
+ } else {
+ csdev->orphan = true;
+ conn->child_dev = NULL;
+ }
+ }
+}
+
+/**
+ * coresight_timeout - loop until a bit has changed to a specific state.
+ * @addr: base address of the area of interest.
+ * @offset: address of a register, starting from @addr.
+ * @position: the position of the bit of interest.
+ * @value: the value the bit should have.
+ *
+ * Return: 0 as soon as the bit has taken the desired state or -EAGAIN if
+ * TIMEOUT_US has elapsed, which ever happens first.
+ */
+
+int coresight_timeout(void __iomem *addr, u32 offset, int position, int value)
+{
+ int i;
+ u32 val;
+
+ for (i = TIMEOUT_US; i > 0; i--) {
+ val = __raw_readl(addr + offset);
+ /* waiting on the bit to go from 0 to 1 */
+ if (value) {
+ if (val & BIT(position))
+ return 0;
+ /* waiting on the bit to go from 1 to 0 */
+ } else {
+ if (!(val & BIT(position)))
+ return 0;
+ }
+
+ /*
+ * Delay is arbitrary - the specification doesn't say how long
+ * we are expected to wait. Extra check required to make sure
+ * we don't wait needlessly on the last iteration.
+ */
+ if (i - 1)
+ udelay(1);
+ }
+
+ return -EAGAIN;
+}
+
+struct bus_type coresight_bustype = {
+ .name = "coresight",
+};
+
+static int __init coresight_init(void)
+{
+ return bus_register(&coresight_bustype);
+}
+postcore_initcall(coresight_init);
+
+struct coresight_device *coresight_register(struct coresight_desc *desc)
+{
+ int i;
+ int ret;
+ int link_subtype;
+ int nr_refcnts = 1;
+ atomic_t *refcnts = NULL;
+ struct coresight_device *csdev;
+ struct coresight_connection *conns;
+
+ csdev = kzalloc(sizeof(*csdev), GFP_KERNEL);
+ if (!csdev) {
+ ret = -ENOMEM;
+ goto err_kzalloc_csdev;
+ }
+
+ if (desc->type == CORESIGHT_DEV_TYPE_LINK ||
+ desc->type == CORESIGHT_DEV_TYPE_LINKSINK) {
+ link_subtype = desc->subtype.link_subtype;
+
+ if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG)
+ nr_refcnts = desc->pdata->nr_inport;
+ else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT)
+ nr_refcnts = desc->pdata->nr_outport;
+ }
+
+ refcnts = kcalloc(nr_refcnts, sizeof(*refcnts), GFP_KERNEL);
+ if (!refcnts) {
+ ret = -ENOMEM;
+ goto err_kzalloc_refcnts;
+ }
+
+ csdev->refcnt = refcnts;
+
+ csdev->nr_inport = desc->pdata->nr_inport;
+ csdev->nr_outport = desc->pdata->nr_outport;
+ conns = kcalloc(csdev->nr_outport, sizeof(*conns), GFP_KERNEL);
+ if (!conns) {
+ ret = -ENOMEM;
+ goto err_kzalloc_conns;
+ }
+
+ for (i = 0; i < csdev->nr_outport; i++) {
+ conns[i].outport = desc->pdata->outports[i];
+ conns[i].child_name = desc->pdata->child_names[i];
+ conns[i].child_port = desc->pdata->child_ports[i];
+ }
+
+ csdev->conns = conns;
+
+ csdev->type = desc->type;
+ csdev->subtype = desc->subtype;
+ csdev->ops = desc->ops;
+ csdev->orphan = false;
+
+ csdev->dev.type = &coresight_dev_type[desc->type];
+ csdev->dev.groups = desc->groups;
+ csdev->dev.parent = desc->dev;
+ csdev->dev.release = coresight_device_release;
+ csdev->dev.bus = &coresight_bustype;
+ dev_set_name(&csdev->dev, "%s", desc->pdata->name);
+
+ ret = device_register(&csdev->dev);
+ if (ret)
+ goto err_device_register;
+
+ mutex_lock(&coresight_mutex);
+
+ coresight_fixup_device_conns(csdev);
+ coresight_fixup_orphan_conns(csdev);
+
+ mutex_unlock(&coresight_mutex);
+
+ return csdev;
+
+err_device_register:
+ kfree(conns);
+err_kzalloc_conns:
+ kfree(refcnts);
+err_kzalloc_refcnts:
+ kfree(csdev);
+err_kzalloc_csdev:
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(coresight_register);
+
+void coresight_unregister(struct coresight_device *csdev)
+{
+ mutex_lock(&coresight_mutex);
+
+ kfree(csdev->conns);
+ device_unregister(&csdev->dev);
+
+ mutex_unlock(&coresight_mutex);
+}
+EXPORT_SYMBOL_GPL(coresight_unregister);
+
+MODULE_LICENSE("GPL v2");
new file mode 100644
@@ -0,0 +1,204 @@
+/* Copyright (c) 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/module.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_graph.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/amba/bus.h>
+#include <linux/coresight.h>
+#include <asm/smp_plat.h>
+
+
+static int of_dev_node_match(struct device *dev, void *data)
+{
+ return dev->of_node == data;
+}
+
+static struct device *
+of_coresight_get_endpoint_device(struct device_node *endpoint)
+{
+ struct device *dev = NULL;
+
+ /*
+ * If we have a non-configuable replicator, it will be found on the
+ * platform bus.
+ */
+ dev = bus_find_device(&platform_bus_type, NULL,
+ endpoint, of_dev_node_match);
+ if (dev)
+ return dev;
+
+ /*
+ * We have a configurable component - circle through the AMBA bus
+ * looking for the device that matches the endpoint node.
+ */
+ return bus_find_device(&amba_bustype, NULL,
+ endpoint, of_dev_node_match);
+}
+
+static struct device_node *of_get_coresight_endpoint(
+ const struct device_node *parent, struct device_node *prev)
+{
+ struct device_node *node = of_graph_get_next_endpoint(parent, prev);
+
+ of_node_put(prev);
+ return node;
+}
+
+static void of_coresight_get_ports(struct device_node *node,
+ int *nr_inport, int *nr_outport)
+{
+ struct device_node *ep = NULL;
+ int in = 0, out = 0;
+
+ do {
+ ep = of_get_coresight_endpoint(node, ep);
+ if (!ep)
+ break;
+
+ if (of_property_read_bool(ep, "slave-mode"))
+ in++;
+ else
+ out++;
+
+ } while (ep);
+
+ *nr_inport = in;
+ *nr_outport = out;
+}
+
+static int of_coresight_alloc_memory(struct device *dev,
+ struct coresight_platform_data *pdata)
+{
+ /* List of output port on this component */
+ pdata->outports = devm_kzalloc(dev, pdata->nr_outport *
+ sizeof(*pdata->outports),
+ GFP_KERNEL);
+ if (!pdata->outports)
+ return -ENOMEM;
+
+ /* Children connected to this component via @outport */
+ pdata->child_names = devm_kzalloc(dev, pdata->nr_outport *
+ sizeof(*pdata->child_names),
+ GFP_KERNEL);
+ if (!pdata->child_names)
+ return -ENOMEM;
+
+ /* Port number on the child this component is connected to */
+ pdata->child_ports = devm_kzalloc(dev, pdata->nr_outport *
+ sizeof(*pdata->child_ports),
+ GFP_KERNEL);
+ if (!pdata->child_ports)
+ return -ENOMEM;
+
+ return 0;
+}
+
+struct coresight_platform_data *of_get_coresight_platform_data(
+ struct device *dev, struct device_node *node)
+{
+ int i = 0, ret = 0;
+ struct coresight_platform_data *pdata;
+ struct of_endpoint endpoint, rendpoint;
+ struct device *rdev;
+ struct device_node *cpu;
+ struct device_node *ep = NULL;
+ struct device_node *rparent = NULL;
+ struct device_node *rport = NULL;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ /* Use device name as debugfs handle */
+ pdata->name = dev_name(dev);
+
+ /* Get the number of input and output port for this component */
+ of_coresight_get_ports(node, &pdata->nr_inport, &pdata->nr_outport);
+
+ if (pdata->nr_outport) {
+ ret = of_coresight_alloc_memory(dev, pdata);
+ if (ret)
+ return ERR_PTR(ret);
+
+ /* Iterate through each port to discover topology */
+ do {
+ /* Get a handle on a port */
+ ep = of_get_coresight_endpoint(node, ep);
+ if (!ep)
+ break;
+
+ /*
+ * No need to deal with input ports, processing for as
+ * processing for output ports will deal with them.
+ */
+ if (of_find_property(ep, "slave-mode", NULL))
+ continue;
+
+ /* Get a handle on the local endpoint */
+ ret = of_graph_parse_endpoint(ep, &endpoint);
+
+ if (ret)
+ continue;
+
+ /* The local out port number */
+ pdata->outports[i] = endpoint.id;
+
+ /*
+ * Get a handle on the remote port and parent
+ * attached to it.
+ */
+ rparent = of_graph_get_remote_port_parent(ep);
+ rport = of_graph_get_remote_port(ep);
+
+ if (!rparent || !rport)
+ continue;
+
+ if (of_graph_parse_endpoint(rport, &rendpoint))
+ continue;
+
+ rdev = of_coresight_get_endpoint_device(rparent);
+ if (!dev)
+ continue;
+
+ pdata->child_names[i] = dev_name(rdev);
+ pdata->child_ports[i] = rendpoint.id;
+
+ i++;
+ } while (ep);
+ }
+
+ /* Affinity defaults to CPU0 */
+ pdata->cpu = 0;
+ cpu = of_parse_phandle(node, "cpu", 0);
+ if (cpu) {
+ const u32 *mpidr;
+ int len, index;
+
+ mpidr = of_get_property(cpu, "reg", &len);
+ if (mpidr && len == 4) {
+ index = get_logical_index(be32_to_cpup(mpidr));
+ if (index != -EINVAL)
+ pdata->cpu = index;
+ }
+ }
+
+ return pdata;
+}
+EXPORT_SYMBOL_GPL(of_get_coresight_platform_data);
@@ -23,6 +23,7 @@
#define AMBA_NR_IRQS 9
#define AMBA_CID 0xb105f00d
+#define CORESIGHT_CID 0xb105900d
struct clk;
new file mode 100644
@@ -0,0 +1,263 @@
+/* Copyright (c) 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.
+ */
+
+#ifndef _LINUX_CORESIGHT_H
+#define _LINUX_CORESIGHT_H
+
+#include <linux/device.h>
+
+/* Peripheral id registers (0xFD0-0xFEC) */
+#define CORESIGHT_PERIPHIDR4 0xfd0
+#define CORESIGHT_PERIPHIDR5 0xfd4
+#define CORESIGHT_PERIPHIDR6 0xfd8
+#define CORESIGHT_PERIPHIDR7 0xfdC
+#define CORESIGHT_PERIPHIDR0 0xfe0
+#define CORESIGHT_PERIPHIDR1 0xfe4
+#define CORESIGHT_PERIPHIDR2 0xfe8
+#define CORESIGHT_PERIPHIDR3 0xfeC
+/* Component id registers (0xFF0-0xFFC) */
+#define CORESIGHT_COMPIDR0 0xff0
+#define CORESIGHT_COMPIDR1 0xff4
+#define CORESIGHT_COMPIDR2 0xff8
+#define CORESIGHT_COMPIDR3 0xffC
+
+#define ETM_ARCH_V3_3 0x23
+#define ETM_ARCH_V3_5 0x25
+#define PFT_ARCH_V1_0 0x30
+#define PFT_ARCH_V1_1 0x31
+
+#define CORESIGHT_UNLOCK 0xc5acce55
+
+extern struct bus_type coresight_bustype;
+
+enum coresight_dev_type {
+ CORESIGHT_DEV_TYPE_NONE,
+ CORESIGHT_DEV_TYPE_SINK,
+ CORESIGHT_DEV_TYPE_LINK,
+ CORESIGHT_DEV_TYPE_LINKSINK,
+ CORESIGHT_DEV_TYPE_SOURCE,
+};
+
+enum coresight_dev_subtype_sink {
+ CORESIGHT_DEV_SUBTYPE_SINK_NONE,
+ CORESIGHT_DEV_SUBTYPE_SINK_PORT,
+ CORESIGHT_DEV_SUBTYPE_SINK_BUFFER,
+};
+
+enum coresight_dev_subtype_link {
+ CORESIGHT_DEV_SUBTYPE_LINK_NONE,
+ CORESIGHT_DEV_SUBTYPE_LINK_MERG,
+ CORESIGHT_DEV_SUBTYPE_LINK_SPLIT,
+ CORESIGHT_DEV_SUBTYPE_LINK_FIFO,
+};
+
+enum coresight_dev_subtype_source {
+ CORESIGHT_DEV_SUBTYPE_SOURCE_NONE,
+ CORESIGHT_DEV_SUBTYPE_SOURCE_PROC,
+ CORESIGHT_DEV_SUBTYPE_SOURCE_BUS,
+ CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE,
+};
+
+/**
+ * struct coresight_dev_subtype - further characterisation of a type
+ * @sink_subtype: type of sink this component is, as defined
+ by @coresight_dev_subtype_sink.
+ * @link_subtype: type of link this component is, as defined
+ by @coresight_dev_subtype_link.
+ * @source_subtype: type of source this component is, as defined
+ by @coresight_dev_subtype_source.
+ */
+struct coresight_dev_subtype {
+ enum coresight_dev_subtype_sink sink_subtype;
+ enum coresight_dev_subtype_link link_subtype;
+ enum coresight_dev_subtype_source source_subtype;
+};
+
+/**
+ * struct coresight_platform_data - data harvested from the DT specification
+ * @cpu: the CPU a source belongs to. Only applicable for ETM/PTMs.
+ * @name: name of the component as shown under sysfs.
+ * @nr_inport: number of input ports for this component.
+ * @outports: list of remote enpoint port number.
+ * @child_names:name of all child components connected to this device.
+ * @child_ports:child component port number the current component is
+ connected to.
+ * @nr_outport: number of output ports for this component.
+ * @clk: The clock this component is associated to.
+ */
+struct coresight_platform_data {
+ int cpu;
+ const char *name;
+ int nr_inport;
+ int *outports;
+ const char **child_names;
+ int *child_ports;
+ int nr_outport;
+ struct clk *clk;
+};
+
+/**
+ * struct coresight_desc - description of a component required from drivers
+ * @type: as defined by @coresight_dev_type.
+ * @subtype: as defined by @coresight_dev_subtype.
+ * @ops: generic operations for this component, as defined
+ by @coresight_ops.
+ * @pdata: platform data collected from DT.
+ * @dev: The device entity associated to this component.
+ * @groups :operations specific to this component. These will end up
+ in the component's sysfs sub-directory.
+ */
+struct coresight_desc {
+ enum coresight_dev_type type;
+ struct coresight_dev_subtype subtype;
+ const struct coresight_ops *ops;
+ struct coresight_platform_data *pdata;
+ struct device *dev;
+ const struct attribute_group **groups;
+};
+
+/**
+ * struct coresight_connection - representation of a single connection
+ * @ref_count: keeping count a port' references.
+ * @outport: a connection's output port number.
+ * @chid_name: remote component's name.
+ * @child_port: remote component's port number @output is connected to.
+ * @child_dev: a @coresight_device representation of the component
+ connected to @outport.
+ */
+struct coresight_connection {
+ int outport;
+ const char *child_name;
+ int child_port;
+ struct coresight_device *child_dev;
+};
+
+/**
+ * struct coresight_device - representation of a device as used by the framework
+ * @nr_inport: number of input port associated to this component.
+ * @nr_outport: number of output port associated to this component.
+ * @type: as defined by @coresight_dev_type.
+ * @subtype: as defined by @coresight_dev_subtype.
+ * @ops: generic operations for this component, as defined
+ by @coresight_ops.
+ * @dev: The device entity associated to this component.
+ * @refcnt: keep track of what is in use.
+ * @path_link: link of current component into the path being enabled.
+ * @orphan: true if the component has connections that haven't been linked.
+ * @enable: 'true' if component is currently part of an active path.
+ * @activated: 'true' only if a _sink_ has been activated. A sink can be
+ activated but not yet enabled. Enabling for a _sink_
+ happens when a source has been selected for that it.
+ */
+struct coresight_device {
+ struct coresight_connection *conns;
+ int nr_inport;
+ int nr_outport;
+ enum coresight_dev_type type;
+ struct coresight_dev_subtype subtype;
+ const struct coresight_ops *ops;
+ struct device dev;
+ atomic_t *refcnt;
+ struct list_head path_link;
+ bool orphan;
+ bool enable; /* true only if configured as part of a path */
+ bool activated; /* true only if a sink is part of a path */
+};
+
+#define to_coresight_device(d) container_of(d, struct coresight_device, dev)
+
+#define source_ops(csdev) csdev->ops->source_ops
+#define sink_ops(csdev) csdev->ops->sink_ops
+#define link_ops(csdev) csdev->ops->link_ops
+
+#define CORESIGHT_DEBUGFS_ENTRY(__name, __entry_name, \
+ __mode, __get, __set, __fmt) \
+DEFINE_SIMPLE_ATTRIBUTE(__name ## _ops, __get, __set, __fmt); \
+static const struct coresight_ops_entry __name ## _entry = { \
+ .name = __entry_name, \
+ .mode = __mode, \
+ .ops = &__name ## _ops \
+}
+
+/**
+ * struct coresight_ops_sink - basic operations for a sink
+ * Operations available for sinks
+ * @enable: enables the sink.
+ * @disable: disables the sink.
+ */
+struct coresight_ops_sink {
+ int (*enable)(struct coresight_device *csdev);
+ void (*disable)(struct coresight_device *csdev);
+};
+
+/**
+ * struct coresight_ops_link - basic operations for a link
+ * Operations available for links.
+ * @enable: enables flow between iport and oport.
+ * @disable: disables flow between iport and oport.
+ */
+struct coresight_ops_link {
+ int (*enable)(struct coresight_device *csdev, int iport, int oport);
+ void (*disable)(struct coresight_device *csdev, int iport, int oport);
+};
+
+/**
+ * struct coresight_ops_source - basic operations for a source
+ * Operations available for sources.
+ * @trace_id: returns the value of the component's trace ID as known
+ to the HW.
+ * @enable: enables tracing from a source.
+ * @disable: disables tracing for a source.
+ */
+struct coresight_ops_source {
+ int (*trace_id)(struct coresight_device *csdev);
+ int (*enable)(struct coresight_device *csdev);
+ void (*disable)(struct coresight_device *csdev);
+};
+
+struct coresight_ops {
+ const struct coresight_ops_sink *sink_ops;
+ const struct coresight_ops_link *link_ops;
+ const struct coresight_ops_source *source_ops;
+};
+
+#ifdef CONFIG_CORESIGHT
+extern struct coresight_device *
+coresight_register(struct coresight_desc *desc);
+extern void coresight_unregister(struct coresight_device *csdev);
+extern int coresight_enable(struct coresight_device *csdev);
+extern void coresight_disable(struct coresight_device *csdev);
+extern int coresight_is_bit_set(u32 val, int position, int value);
+extern int coresight_timeout(void __iomem *addr, u32 offset,
+ int position, int value);
+#ifdef CONFIG_OF
+extern struct coresight_platform_data *of_get_coresight_platform_data(
+ struct device *dev, struct device_node *node);
+#endif
+#else
+static inline struct coresight_device *
+coresight_register(struct coresight_desc *desc) { return NULL; }
+static inline void coresight_unregister(struct coresight_device *csdev) {}
+static inline int
+coresight_enable(struct coresight_device *csdev) { return -ENOSYS; }
+static inline void coresight_disable(struct coresight_device *csdev) {}
+static inline int coresight_is_bit_set(u32 val, int position, int value)
+ { return 0; }
+static inline int coresight_timeout(void __iomem *addr, u32 offset,
+ int position, int value) { return 1; }
+#ifdef CONFIG_OF
+static inline struct coresight_platform_data *of_get_coresight_platform_data(
+ struct device *dev, struct device_node *node) { return NULL; }
+#endif
+#endif
+
+#endif