coresight: etm3x: implementing perf_enable/disable() API
[firefly-linux-kernel-4.4.55.git] / drivers / hwtracing / coresight / coresight.c
index e25492137d8bec86a5ff406ff1025724df095fbd..95cccb179763cfa0585db25b1e5e688049a3b8a2 100644 (file)
 #include <linux/coresight.h>
 #include <linux/of_platform.h>
 #include <linux/delay.h>
+#include <linux/pm_runtime.h>
 
 #include "coresight-priv.h"
 
 static DEFINE_MUTEX(coresight_mutex);
 
+/**
+ * struct coresight_node - elements of a path, from source to sink
+ * @csdev:     Address of an element.
+ * @link:      hook to the list.
+ */
+struct coresight_node {
+       struct coresight_device *csdev;
+       struct list_head link;
+};
+
+/*
+ * When operating Coresight drivers from the sysFS interface, only a single
+ * path can exist from a tracer (associated to a CPU) to a sink.
+ */
+static DEFINE_PER_CPU(struct list_head *, sysfs_path);
+
 static int coresight_id_match(struct device *dev, void *data)
 {
        int trace_id, i_trace_id;
@@ -68,15 +85,12 @@ static int coresight_source_is_unique(struct coresight_device *csdev)
                                 csdev, coresight_id_match);
 }
 
-static int coresight_find_link_inport(struct coresight_device *csdev)
+static int coresight_find_link_inport(struct coresight_device *csdev,
+                                     struct coresight_device *parent)
 {
        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)
@@ -89,15 +103,12 @@ static int coresight_find_link_inport(struct coresight_device *csdev)
        return 0;
 }
 
-static int coresight_find_link_outport(struct coresight_device *csdev)
+static int coresight_find_link_outport(struct coresight_device *csdev,
+                                      struct coresight_device *child)
 {
        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)
@@ -138,14 +149,19 @@ static void coresight_disable_sink(struct coresight_device *csdev)
        }
 }
 
-static int coresight_enable_link(struct coresight_device *csdev)
+static int coresight_enable_link(struct coresight_device *csdev,
+                                struct coresight_device *parent,
+                                struct coresight_device *child)
 {
        int ret;
        int link_subtype;
        int refport, inport, outport;
 
-       inport = coresight_find_link_inport(csdev);
-       outport = coresight_find_link_outport(csdev);
+       if (!parent || !child)
+               return -EINVAL;
+
+       inport = coresight_find_link_inport(csdev, parent);
+       outport = coresight_find_link_outport(csdev, child);
        link_subtype = csdev->subtype.link_subtype;
 
        if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG)
@@ -168,14 +184,19 @@ static int coresight_enable_link(struct coresight_device *csdev)
        return 0;
 }
 
-static void coresight_disable_link(struct coresight_device *csdev)
+static void coresight_disable_link(struct coresight_device *csdev,
+                                  struct coresight_device *parent,
+                                  struct coresight_device *child)
 {
        int i, nr_conns;
        int link_subtype;
        int refport, inport, outport;
 
-       inport = coresight_find_link_inport(csdev);
-       outport = coresight_find_link_outport(csdev);
+       if (!parent || !child)
+               return;
+
+       inport = coresight_find_link_inport(csdev, parent);
+       outport = coresight_find_link_outport(csdev, child);
        link_subtype = csdev->subtype.link_subtype;
 
        if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) {
@@ -201,7 +222,7 @@ static void coresight_disable_link(struct coresight_device *csdev)
        csdev->enable = false;
 }
 
-static int coresight_enable_source(struct coresight_device *csdev)
+static int coresight_enable_source(struct coresight_device *csdev, u32 mode)
 {
        int ret;
 
@@ -213,7 +234,7 @@ static int coresight_enable_source(struct coresight_device *csdev)
 
        if (!csdev->enable) {
                if (source_ops(csdev)->enable) {
-                       ret = source_ops(csdev)->enable(csdev);
+                       ret = source_ops(csdev)->enable(csdev, NULL, mode);
                        if (ret)
                                return ret;
                }
@@ -235,109 +256,188 @@ static void coresight_disable_source(struct coresight_device *csdev)
        }
 }
 
-static int coresight_enable_path(struct list_head *path)
+void coresight_disable_path(struct list_head *path)
 {
-       int ret = 0;
-       struct coresight_device *cd;
-
-       /*
-        * At this point we have a full @path, from source to sink.  The
-        * sink is the first entry and the source the last one.  Go through
-        * all the components and enable them one by one.
-        */
-       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);
+       struct coresight_node *nd;
+       struct coresight_device *csdev, *parent, *child;
+
+       list_for_each_entry(nd, path, link) {
+               csdev = nd->csdev;
+
+               switch (csdev->type) {
+               case CORESIGHT_DEV_TYPE_SINK:
+               case CORESIGHT_DEV_TYPE_LINKSINK:
+                       coresight_disable_sink(csdev);
+                       break;
+               case CORESIGHT_DEV_TYPE_SOURCE:
+                       /* sources are disabled from either sysFS or Perf */
+                       break;
+               case CORESIGHT_DEV_TYPE_LINK:
+                       parent = list_prev_entry(nd, link)->csdev;
+                       child = list_next_entry(nd, link)->csdev;
+                       coresight_disable_link(csdev, parent, child);
+                       break;
+               default:
+                       break;
                }
-               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);
+int coresight_enable_path(struct list_head *path)
+{
+
+       int ret = 0;
+       struct coresight_node *nd;
+       struct coresight_device *csdev, *parent, *child;
+
+       list_for_each_entry_reverse(nd, path, link) {
+               csdev = nd->csdev;
+
+               switch (csdev->type) {
+               case CORESIGHT_DEV_TYPE_SINK:
+               case CORESIGHT_DEV_TYPE_LINKSINK:
+                       ret = coresight_enable_sink(csdev);
+                       if (ret)
+                               goto err;
+                       break;
+               case CORESIGHT_DEV_TYPE_SOURCE:
+                       /* sources are enabled from either sysFS or Perf */
+                       break;
+               case CORESIGHT_DEV_TYPE_LINK:
+                       parent = list_prev_entry(nd, link)->csdev;
+                       child = list_next_entry(nd, link)->csdev;
+                       ret = coresight_enable_link(csdev, parent, child);
+                       if (ret)
+                               goto err;
+                       break;
+               default:
+                       goto err;
                }
        }
 
+out:
        return ret;
+err:
+       coresight_disable_path(path);
+       goto out;
 }
 
-static int coresight_disable_path(struct list_head *path)
+struct coresight_device *coresight_get_sink(struct list_head *path)
 {
-       struct coresight_device *cd;
+       struct coresight_device *csdev;
 
-       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);
+       if (!path)
+               return NULL;
+
+       csdev = list_last_entry(path, struct coresight_node, link)->csdev;
+       if (csdev->type != CORESIGHT_DEV_TYPE_SINK &&
+           csdev->type != CORESIGHT_DEV_TYPE_LINKSINK)
+               return NULL;
+
+       return csdev;
+}
+
+/**
+ * _coresight_build_path - recursively build a path from a @csdev to a sink.
+ * @csdev:     The device to start from.
+ * @path:      The list to add devices to.
+ *
+ * The tree of Coresight device is traversed until an activated sink is
+ * found.  From there the sink is added to the list along with all the
+ * devices that led to that point - the end result is a list from source
+ * to sink. In that list the source is the first device and the sink the
+ * last one.
+ */
+static int _coresight_build_path(struct coresight_device *csdev,
+                                struct list_head *path)
+{
+       int i;
+       bool found = false;
+       struct coresight_node *node;
+       struct coresight_connection *conn;
+
+       /* An activated sink has been found.  Enqueue the element */
+       if ((csdev->type == CORESIGHT_DEV_TYPE_SINK ||
+            csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) && csdev->activated)
+               goto out;
+
+       /* Not a sink - recursively explore each port found on this element */
+       for (i = 0; i < csdev->nr_outport; i++) {
+               conn = &csdev->conns[i];
+               if (_coresight_build_path(conn->child_dev, path) == 0) {
+                       found = true;
+                       break;
                }
        }
 
+       if (!found)
+               return -ENODEV;
+
+out:
+       /*
+        * A path from this element to a sink has been found.  The elements
+        * leading to the sink are already enqueued, all that is left to do
+        * is tell the PM runtime core we need this element and add a node
+        * for it.
+        */
+       node = kzalloc(sizeof(struct coresight_node), GFP_KERNEL);
+       if (!node)
+               return -ENOMEM;
+
+       node->csdev = csdev;
+       list_add(&node->link, path);
+       pm_runtime_get_sync(csdev->dev.parent);
+
        return 0;
 }
 
-static int coresight_build_paths(struct coresight_device *csdev,
-                                struct list_head *path,
-                                bool enable)
+struct list_head *coresight_build_path(struct coresight_device *csdev)
 {
-       int i, ret = -EINVAL;
-       struct coresight_connection *conn;
+       struct list_head *path;
 
-       list_add(&csdev->path_link, path);
+       path = kzalloc(sizeof(struct list_head), GFP_KERNEL);
+       if (!path)
+               return NULL;
 
-       if ((csdev->type == CORESIGHT_DEV_TYPE_SINK ||
-           csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) &&
-           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;
-               }
+       INIT_LIST_HEAD(path);
+
+       if (_coresight_build_path(csdev, path)) {
+               kfree(path);
+               path = NULL;
        }
 
-       if (list_first_entry(path, struct coresight_device, path_link) != csdev)
-               dev_err(&csdev->dev, "wrong device in %s\n", __func__);
+       return path;
+}
 
-       list_del(&csdev->path_link);
+/**
+ * coresight_release_path - release a previously built path.
+ * @path:      the path to release.
+ *
+ * Go through all the elements of a path and 1) removed it from the list and
+ * 2) free the memory allocated for each node.
+ */
+void coresight_release_path(struct list_head *path)
+{
+       struct coresight_device *csdev;
+       struct coresight_node *nd, *next;
 
-       return ret;
+       list_for_each_entry_safe(nd, next, path, link) {
+               csdev = nd->csdev;
+
+               pm_runtime_put_sync(csdev->dev.parent);
+               list_del(&nd->link);
+               kfree(nd);
+       }
+
+       kfree(path);
+       path = NULL;
 }
 
 int coresight_enable(struct coresight_device *csdev)
 {
        int ret = 0;
-       LIST_HEAD(path);
+       int cpu;
+       struct list_head *path;
 
        mutex_lock(&coresight_mutex);
        if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) {
@@ -348,22 +448,47 @@ int coresight_enable(struct coresight_device *csdev)
        if (csdev->enable)
                goto out;
 
-       if (coresight_build_paths(csdev, &path, true)) {
-               dev_err(&csdev->dev, "building path(s) failed\n");
+       path = coresight_build_path(csdev);
+       if (!path) {
+               pr_err("building path(s) failed\n");
                goto out;
        }
 
-       if (coresight_enable_source(csdev))
-               dev_err(&csdev->dev, "source enable failed\n");
+       ret = coresight_enable_path(path);
+       if (ret)
+               goto err_path;
+
+       ret = coresight_enable_source(csdev, CS_MODE_SYSFS);
+       if (ret)
+               goto err_source;
+
+       /*
+        * When working from sysFS it is important to keep track
+        * of the paths that were created so that they can be
+        * undone in 'coresight_disable()'.  Since there can only
+        * be a single session per tracer (when working from sysFS)
+        * a per-cpu variable will do just fine.
+        */
+       cpu = source_ops(csdev)->cpu_id(csdev);
+       per_cpu(sysfs_path, cpu) = path;
+
 out:
        mutex_unlock(&coresight_mutex);
        return ret;
+
+err_source:
+       coresight_disable_path(path);
+
+err_path:
+       coresight_release_path(path);
+       goto out;
 }
 EXPORT_SYMBOL_GPL(coresight_enable);
 
 void coresight_disable(struct coresight_device *csdev)
 {
-       LIST_HEAD(path);
+       int cpu;
+       struct list_head *path;
 
        mutex_lock(&coresight_mutex);
        if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) {
@@ -373,9 +498,12 @@ void coresight_disable(struct coresight_device *csdev)
        if (!csdev->enable)
                goto out;
 
+       cpu = source_ops(csdev)->cpu_id(csdev);
+       path = per_cpu(sysfs_path, cpu);
        coresight_disable_source(csdev);
-       if (coresight_build_paths(csdev, &path, false))
-               dev_err(&csdev->dev, "releasing path(s) failed\n");
+       coresight_disable_path(path);
+       coresight_release_path(path);
+       per_cpu(sysfs_path, cpu) = NULL;
 
 out:
        mutex_unlock(&coresight_mutex);
@@ -481,6 +609,8 @@ static void coresight_device_release(struct device *dev)
 {
        struct coresight_device *csdev = to_coresight_device(dev);
 
+       kfree(csdev->conns);
+       kfree(csdev->refcnt);
        kfree(csdev);
 }
 
@@ -536,7 +666,7 @@ static void coresight_fixup_orphan_conns(struct coresight_device *csdev)
         * are hooked-up with each newly added component.
         */
        bus_for_each_dev(&coresight_bustype, NULL,
-                                csdev, coresight_orphan_match);
+                        csdev, coresight_orphan_match);
 }
 
 
@@ -548,7 +678,7 @@ static int coresight_name_match(struct device *dev, void *data)
        to_match = data;
        i_csdev = to_coresight_device(dev);
 
-       if (!strcmp(to_match, dev_name(&i_csdev->dev)))
+       if (to_match && !strcmp(to_match, dev_name(&i_csdev->dev)))
                return 1;
 
        return 0;
@@ -568,6 +698,8 @@ static void coresight_fixup_device_conns(struct coresight_device *csdev)
 
                if (dev) {
                        conn->child_dev = to_coresight_device(dev);
+                       /* and put reference from 'bus_find_device()' */
+                       put_device(dev);
                } else {
                        csdev->orphan = true;
                        conn->child_dev = NULL;
@@ -575,6 +707,50 @@ static void coresight_fixup_device_conns(struct coresight_device *csdev)
        }
 }
 
+static int coresight_remove_match(struct device *dev, void *data)
+{
+       int i;
+       struct coresight_device *csdev, *iterator;
+       struct coresight_connection *conn;
+
+       csdev = data;
+       iterator = to_coresight_device(dev);
+
+       /* No need to check oneself */
+       if (csdev == iterator)
+               return 0;
+
+       /*
+        * Circle throuch all the connection of that component.  If we find
+        * a connection whose name matches @csdev, remove it.
+        */
+       for (i = 0; i < iterator->nr_outport; i++) {
+               conn = &iterator->conns[i];
+
+               if (conn->child_dev == NULL)
+                       continue;
+
+               if (!strcmp(dev_name(&csdev->dev), conn->child_name)) {
+                       iterator->orphan = true;
+                       conn->child_dev = NULL;
+                       /* No need to continue */
+                       break;
+               }
+       }
+
+       /*
+        * Returning '0' ensures that all known component on the
+        * bus will be checked.
+        */
+       return 0;
+}
+
+static void coresight_remove_conns(struct coresight_device *csdev)
+{
+       bus_for_each_dev(&coresight_bustype, NULL,
+                        csdev, coresight_remove_match);
+}
+
 /**
  * coresight_timeout - loop until a bit has changed to a specific state.
  * @addr: base address of the area of interest.
@@ -713,12 +889,9 @@ EXPORT_SYMBOL_GPL(coresight_register);
 
 void coresight_unregister(struct coresight_device *csdev)
 {
-       mutex_lock(&coresight_mutex);
-
-       kfree(csdev->conns);
+       /* Remove references of that device in the topology */
+       coresight_remove_conns(csdev);
        device_unregister(&csdev->dev);
-
-       mutex_unlock(&coresight_mutex);
 }
 EXPORT_SYMBOL_GPL(coresight_unregister);