hwtracing: coresight: Explicitly include correct DT includes
[linux-2.6-microblaze.git] / drivers / hwtracing / coresight / coresight-core.c
index a0a0ea2..9fabe00 100644 (file)
@@ -3,6 +3,7 @@
  * Copyright (c) 2012, The Linux Foundation. All rights reserved.
  */
 
+#include <linux/build_bug.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/types.h>
@@ -16,7 +17,7 @@
 #include <linux/mutex.h>
 #include <linux/clk.h>
 #include <linux/coresight.h>
-#include <linux/of_platform.h>
+#include <linux/property.h>
 #include <linux/delay.h>
 #include <linux/pm_runtime.h>
 
@@ -112,40 +113,24 @@ struct coresight_device *coresight_get_percpu_sink(int cpu)
 }
 EXPORT_SYMBOL_GPL(coresight_get_percpu_sink);
 
-static int coresight_find_link_inport(struct coresight_device *csdev,
-                                     struct coresight_device *parent)
+static struct coresight_connection *
+coresight_find_out_connection(struct coresight_device *src_dev,
+                             struct coresight_device *dest_dev)
 {
        int i;
        struct coresight_connection *conn;
 
-       for (i = 0; i < parent->pdata->nr_outconns; i++) {
-               conn = parent->pdata->out_conns[i];
-               if (conn->dest_dev == csdev)
-                       return conn->dest_port;
+       for (i = 0; i < src_dev->pdata->nr_outconns; i++) {
+               conn = src_dev->pdata->out_conns[i];
+               if (conn->dest_dev == dest_dev)
+                       return conn;
        }
 
-       dev_err(&csdev->dev, "couldn't find inport, parent: %s, child: %s\n",
-               dev_name(&parent->dev), dev_name(&csdev->dev));
+       dev_err(&src_dev->dev,
+               "couldn't find output connection, src_dev: %s, dest_dev: %s\n",
+               dev_name(&src_dev->dev), dev_name(&dest_dev->dev));
 
-       return -ENODEV;
-}
-
-static int coresight_find_link_outport(struct coresight_device *csdev,
-                                      struct coresight_device *child)
-{
-       int i;
-       struct coresight_connection *conn;
-
-       for (i = 0; i < csdev->pdata->nr_outconns; i++) {
-               conn = csdev->pdata->out_conns[i];
-               if (conn->dest_dev == child)
-                       return conn->src_port;
-       }
-
-       dev_err(&csdev->dev, "couldn't find outport, parent: %s, child: %s\n",
-               dev_name(&csdev->dev), dev_name(&child->dev));
-
-       return -ENODEV;
+       return ERR_PTR(-ENODEV);
 }
 
 static inline u32 coresight_read_claim_tags(struct coresight_device *csdev)
@@ -252,60 +237,44 @@ void coresight_disclaim_device(struct coresight_device *csdev)
 }
 EXPORT_SYMBOL_GPL(coresight_disclaim_device);
 
-/* enable or disable an associated CTI device of the supplied CS device */
-static int
-coresight_control_assoc_ectdev(struct coresight_device *csdev, bool enable)
+/*
+ * Add a helper as an output device. This function takes the @coresight_mutex
+ * because it's assumed that it's called from the helper device, outside of the
+ * core code where the mutex would already be held. Don't add new calls to this
+ * from inside the core code, instead try to add the new helper to the DT and
+ * ACPI where it will be picked up and linked automatically.
+ */
+void coresight_add_helper(struct coresight_device *csdev,
+                         struct coresight_device *helper)
 {
-       int ect_ret = 0;
-       struct coresight_device *ect_csdev = csdev->ect_dev;
-       struct module *mod;
+       int i;
+       struct coresight_connection conn = {};
+       struct coresight_connection *new_conn;
 
-       if (!ect_csdev)
-               return 0;
-       if ((!ect_ops(ect_csdev)->enable) || (!ect_ops(ect_csdev)->disable))
-               return 0;
+       mutex_lock(&coresight_mutex);
+       conn.dest_fwnode = fwnode_handle_get(dev_fwnode(&helper->dev));
+       conn.dest_dev = helper;
+       conn.dest_port = conn.src_port = -1;
+       conn.src_dev = csdev;
 
-       mod = ect_csdev->dev.parent->driver->owner;
-       if (enable) {
-               if (try_module_get(mod)) {
-                       ect_ret = ect_ops(ect_csdev)->enable(ect_csdev);
-                       if (ect_ret) {
-                               module_put(mod);
-                       } else {
-                               get_device(ect_csdev->dev.parent);
-                               csdev->ect_enabled = true;
-                       }
-               } else
-                       ect_ret = -ENODEV;
-       } else {
-               if (csdev->ect_enabled) {
-                       ect_ret = ect_ops(ect_csdev)->disable(ect_csdev);
-                       put_device(ect_csdev->dev.parent);
-                       module_put(mod);
-                       csdev->ect_enabled = false;
-               }
-       }
+       /*
+        * Check for duplicates because this is called every time a helper
+        * device is re-loaded. Existing connections will get re-linked
+        * automatically.
+        */
+       for (i = 0; i < csdev->pdata->nr_outconns; ++i)
+               if (csdev->pdata->out_conns[i]->dest_fwnode == conn.dest_fwnode)
+                       goto unlock;
 
-       /* output warning if ECT enable is preventing trace operation */
-       if (ect_ret)
-               dev_info(&csdev->dev, "Associated ECT device (%s) %s failed\n",
-                        dev_name(&ect_csdev->dev),
-                        enable ? "enable" : "disable");
-       return ect_ret;
-}
+       new_conn = coresight_add_out_conn(csdev->dev.parent, csdev->pdata,
+                                         &conn);
+       if (!IS_ERR(new_conn))
+               coresight_add_in_conn(new_conn);
 
-/*
- * Set the associated ect / cti device while holding the coresight_mutex
- * to avoid a race with coresight_enable that may try to use this value.
- */
-void coresight_set_assoc_ectdev_mutex(struct coresight_device *csdev,
-                                     struct coresight_device *ect_csdev)
-{
-       mutex_lock(&coresight_mutex);
-       csdev->ect_dev = ect_csdev;
+unlock:
        mutex_unlock(&coresight_mutex);
 }
-EXPORT_SYMBOL_GPL(coresight_set_assoc_ectdev_mutex);
+EXPORT_SYMBOL_GPL(coresight_add_helper);
 
 static int coresight_enable_sink(struct coresight_device *csdev,
                                 enum cs_mode mode, void *data)
@@ -319,14 +288,10 @@ static int coresight_enable_sink(struct coresight_device *csdev,
        if (!sink_ops(csdev)->enable)
                return -EINVAL;
 
-       ret = coresight_control_assoc_ectdev(csdev, true);
-       if (ret)
-               return ret;
        ret = sink_ops(csdev)->enable(csdev, mode, data);
-       if (ret) {
-               coresight_control_assoc_ectdev(csdev, false);
+       if (ret)
                return ret;
-       }
+
        csdev->enable = true;
 
        return 0;
@@ -342,7 +307,6 @@ static void coresight_disable_sink(struct coresight_device *csdev)
        ret = sink_ops(csdev)->disable(csdev);
        if (ret)
                return;
-       coresight_control_assoc_ectdev(csdev, false);
        csdev->enable = false;
 }
 
@@ -352,32 +316,26 @@ static int coresight_enable_link(struct coresight_device *csdev,
 {
        int ret = 0;
        int link_subtype;
-       int inport, outport;
+       struct coresight_connection *inconn, *outconn;
 
        if (!parent || !child)
                return -EINVAL;
 
-       inport = coresight_find_link_inport(csdev, parent);
-       outport = coresight_find_link_outport(csdev, child);
+       inconn = coresight_find_out_connection(parent, csdev);
+       outconn = coresight_find_out_connection(csdev, child);
        link_subtype = csdev->subtype.link_subtype;
 
-       if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG && inport < 0)
-               return inport;
-       if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT && outport < 0)
-               return outport;
+       if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG && IS_ERR(inconn))
+               return PTR_ERR(inconn);
+       if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT && IS_ERR(outconn))
+               return PTR_ERR(outconn);
 
        if (link_ops(csdev)->enable) {
-               ret = coresight_control_assoc_ectdev(csdev, true);
-               if (!ret) {
-                       ret = link_ops(csdev)->enable(csdev, inport, outport);
-                       if (ret)
-                               coresight_control_assoc_ectdev(csdev, false);
-               }
+               ret = link_ops(csdev)->enable(csdev, inconn, outconn);
+               if (!ret)
+                       csdev->enable = true;
        }
 
-       if (!ret)
-               csdev->enable = true;
-
        return ret;
 }
 
@@ -385,79 +343,125 @@ static void coresight_disable_link(struct coresight_device *csdev,
                                   struct coresight_device *parent,
                                   struct coresight_device *child)
 {
-       int i, nr_conns;
+       int i;
        int link_subtype;
-       int inport, outport;
+       struct coresight_connection *inconn, *outconn;
 
        if (!parent || !child)
                return;
 
-       inport = coresight_find_link_inport(csdev, parent);
-       outport = coresight_find_link_outport(csdev, child);
+       inconn = coresight_find_out_connection(parent, csdev);
+       outconn = coresight_find_out_connection(csdev, child);
        link_subtype = csdev->subtype.link_subtype;
 
-       if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) {
-               nr_conns = csdev->pdata->high_inport;
-       } else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT) {
-               nr_conns = csdev->pdata->high_outport;
-       } else {
-               nr_conns = 1;
-       }
-
        if (link_ops(csdev)->disable) {
-               link_ops(csdev)->disable(csdev, inport, outport);
-               coresight_control_assoc_ectdev(csdev, false);
+               link_ops(csdev)->disable(csdev, inconn, outconn);
        }
 
-       for (i = 0; i < nr_conns; i++)
-               if (atomic_read(&csdev->refcnt[i]) != 0)
+       if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) {
+               for (i = 0; i < csdev->pdata->nr_inconns; i++)
+                       if (atomic_read(&csdev->pdata->in_conns[i]->dest_refcnt) !=
+                           0)
+                               return;
+       } else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT) {
+               for (i = 0; i < csdev->pdata->nr_outconns; i++)
+                       if (atomic_read(&csdev->pdata->out_conns[i]->src_refcnt) !=
+                           0)
+                               return;
+       } else {
+               if (atomic_read(&csdev->refcnt) != 0)
                        return;
+       }
 
        csdev->enable = false;
 }
 
-static int coresight_enable_source(struct coresight_device *csdev,
-                                  enum cs_mode mode)
+int coresight_enable_source(struct coresight_device *csdev, enum cs_mode mode,
+                           void *data)
 {
        int ret;
 
        if (!csdev->enable) {
                if (source_ops(csdev)->enable) {
-                       ret = coresight_control_assoc_ectdev(csdev, true);
+                       ret = source_ops(csdev)->enable(csdev, data, mode);
                        if (ret)
                                return ret;
-                       ret = source_ops(csdev)->enable(csdev, NULL, mode);
-                       if (ret) {
-                               coresight_control_assoc_ectdev(csdev, false);
-                               return ret;
-                       }
                }
                csdev->enable = true;
        }
 
-       atomic_inc(csdev->refcnt);
+       atomic_inc(&csdev->refcnt);
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(coresight_enable_source);
+
+static bool coresight_is_helper(struct coresight_device *csdev)
+{
+       return csdev->type == CORESIGHT_DEV_TYPE_HELPER;
+}
+
+static int coresight_enable_helper(struct coresight_device *csdev,
+                                  enum cs_mode mode, void *data)
+{
+       int ret;
+
+       if (!helper_ops(csdev)->enable)
+               return 0;
+       ret = helper_ops(csdev)->enable(csdev, mode, data);
+       if (ret)
+               return ret;
+
+       csdev->enable = true;
+       return 0;
+}
+
+static void coresight_disable_helper(struct coresight_device *csdev)
+{
+       int ret;
+
+       if (!helper_ops(csdev)->disable)
+               return;
+
+       ret = helper_ops(csdev)->disable(csdev, NULL);
+       if (ret)
+               return;
+       csdev->enable = false;
+}
+
+static void coresight_disable_helpers(struct coresight_device *csdev)
+{
+       int i;
+       struct coresight_device *helper;
+
+       for (i = 0; i < csdev->pdata->nr_outconns; ++i) {
+               helper = csdev->pdata->out_conns[i]->dest_dev;
+               if (helper && coresight_is_helper(helper))
+                       coresight_disable_helper(helper);
+       }
+}
 
 /**
  *  coresight_disable_source - Drop the reference count by 1 and disable
  *  the device if there are no users left.
  *
  *  @csdev: The coresight device to disable
+ *  @data: Opaque data to pass on to the disable function of the source device.
+ *         For example in perf mode this is a pointer to the struct perf_event.
  *
  *  Returns true if the device has been disabled.
  */
-static bool coresight_disable_source(struct coresight_device *csdev)
+bool coresight_disable_source(struct coresight_device *csdev, void *data)
 {
-       if (atomic_dec_return(csdev->refcnt) == 0) {
+       if (atomic_dec_return(&csdev->refcnt) == 0) {
                if (source_ops(csdev)->disable)
-                       source_ops(csdev)->disable(csdev, NULL);
-               coresight_control_assoc_ectdev(csdev, false);
+                       source_ops(csdev)->disable(csdev, data);
+               coresight_disable_helpers(csdev);
                csdev->enable = false;
        }
        return !csdev->enable;
 }
+EXPORT_SYMBOL_GPL(coresight_disable_source);
 
 /*
  * coresight_disable_path_from : Disable components in the given path beyond
@@ -508,6 +512,9 @@ static void coresight_disable_path_from(struct list_head *path,
                default:
                        break;
                }
+
+               /* Disable all helpers adjacent along the path last */
+               coresight_disable_helpers(csdev);
        }
 }
 
@@ -517,9 +524,28 @@ void coresight_disable_path(struct list_head *path)
 }
 EXPORT_SYMBOL_GPL(coresight_disable_path);
 
-int coresight_enable_path(struct list_head *path, enum cs_mode mode, void *sink_data)
+static int coresight_enable_helpers(struct coresight_device *csdev,
+                                   enum cs_mode mode, void *data)
 {
+       int i, ret = 0;
+       struct coresight_device *helper;
 
+       for (i = 0; i < csdev->pdata->nr_outconns; ++i) {
+               helper = csdev->pdata->out_conns[i]->dest_dev;
+               if (!helper || !coresight_is_helper(helper))
+                       continue;
+
+               ret = coresight_enable_helper(helper, mode, data);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+int coresight_enable_path(struct list_head *path, enum cs_mode mode,
+                         void *sink_data)
+{
        int ret = 0;
        u32 type;
        struct coresight_node *nd;
@@ -529,6 +555,10 @@ int coresight_enable_path(struct list_head *path, enum cs_mode mode, void *sink_
                csdev = nd->csdev;
                type = csdev->type;
 
+               /* Enable all helpers adjacent to the path first */
+               ret = coresight_enable_helpers(csdev, mode, sink_data);
+               if (ret)
+                       goto err;
                /*
                 * ETF devices are tricky... They can be a link or a sink,
                 * depending on how they are configured.  If an ETF has been
@@ -723,7 +753,7 @@ static int coresight_grab_device(struct coresight_device *csdev)
                struct coresight_device *child;
 
                child = csdev->pdata->out_conns[i]->dest_dev;
-               if (child && child->type == CORESIGHT_DEV_TYPE_HELPER)
+               if (child && coresight_is_helper(child))
                        if (!coresight_get_ref(child))
                                goto err;
        }
@@ -734,7 +764,7 @@ err:
                struct coresight_device *child;
 
                child = csdev->pdata->out_conns[i]->dest_dev;
-               if (child && child->type == CORESIGHT_DEV_TYPE_HELPER)
+               if (child && coresight_is_helper(child))
                        coresight_put_ref(child);
        }
        return -ENODEV;
@@ -753,7 +783,7 @@ static void coresight_drop_device(struct coresight_device *csdev)
                struct coresight_device *child;
 
                child = csdev->pdata->out_conns[i]->dest_dev;
-               if (child && child->type == CORESIGHT_DEV_TYPE_HELPER)
+               if (child && coresight_is_helper(child))
                        coresight_put_ref(child);
        }
 }
@@ -1094,7 +1124,7 @@ int coresight_enable(struct coresight_device *csdev)
                 * source is already enabled.
                 */
                if (subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE)
-                       atomic_inc(csdev->refcnt);
+                       atomic_inc(&csdev->refcnt);
                goto out;
        }
 
@@ -1115,7 +1145,7 @@ int coresight_enable(struct coresight_device *csdev)
        if (ret)
                goto err_path;
 
-       ret = coresight_enable_source(csdev, CS_MODE_SYSFS);
+       ret = coresight_enable_source(csdev, CS_MODE_SYSFS, NULL);
        if (ret)
                goto err_source;
 
@@ -1172,7 +1202,7 @@ void coresight_disable(struct coresight_device *csdev)
        if (ret)
                goto out;
 
-       if (!csdev->enable || !coresight_disable_source(csdev))
+       if (!csdev->enable || !coresight_disable_source(csdev, NULL))
                goto out;
 
        switch (csdev->subtype.source_subtype) {
@@ -1297,18 +1327,16 @@ static struct device_type coresight_dev_type[] = {
        },
        {
                .name = "helper",
-       },
-       {
-               .name = "ect",
-       },
+       }
 };
+/* Ensure the enum matches the names and groups */
+static_assert(ARRAY_SIZE(coresight_dev_type) == CORESIGHT_DEV_TYPE_MAX);
 
 static void coresight_device_release(struct device *dev)
 {
        struct coresight_device *csdev = to_coresight_device(dev);
 
        fwnode_handle_put(csdev->dev.fwnode);
-       kfree(csdev->refcnt);
        kfree(csdev);
 }
 
@@ -1537,9 +1565,6 @@ void coresight_release_platform_data(struct coresight_device *csdev,
 struct coresight_device *coresight_register(struct coresight_desc *desc)
 {
        int ret;
-       int link_subtype;
-       int nr_refcnts = 1;
-       atomic_t *refcnts = NULL;
        struct coresight_device *csdev;
        bool registered = false;
 
@@ -1549,25 +1574,6 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
                goto err_out;
        }
 
-       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->high_inport;
-               else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT)
-                       nr_refcnts = desc->pdata->high_outport;
-       }
-
-       refcnts = kcalloc(nr_refcnts, sizeof(*refcnts), GFP_KERNEL);
-       if (!refcnts) {
-               ret = -ENOMEM;
-               kfree(csdev);
-               goto err_out;
-       }
-
-       csdev->refcnt = refcnts;
-
        csdev->pdata = desc->pdata;
 
        csdev->type = desc->type;
@@ -1680,6 +1686,69 @@ static inline int coresight_search_device_idx(struct coresight_dev_list *dict,
        return -ENOENT;
 }
 
+static bool coresight_compare_type(enum coresight_dev_type type_a,
+                                  union coresight_dev_subtype subtype_a,
+                                  enum coresight_dev_type type_b,
+                                  union coresight_dev_subtype subtype_b)
+{
+       if (type_a != type_b)
+               return false;
+
+       switch (type_a) {
+       case CORESIGHT_DEV_TYPE_SINK:
+               return subtype_a.sink_subtype == subtype_b.sink_subtype;
+       case CORESIGHT_DEV_TYPE_LINK:
+               return subtype_a.link_subtype == subtype_b.link_subtype;
+       case CORESIGHT_DEV_TYPE_LINKSINK:
+               return subtype_a.link_subtype == subtype_b.link_subtype &&
+                      subtype_a.sink_subtype == subtype_b.sink_subtype;
+       case CORESIGHT_DEV_TYPE_SOURCE:
+               return subtype_a.source_subtype == subtype_b.source_subtype;
+       case CORESIGHT_DEV_TYPE_HELPER:
+               return subtype_a.helper_subtype == subtype_b.helper_subtype;
+       default:
+               return false;
+       }
+}
+
+struct coresight_device *
+coresight_find_input_type(struct coresight_platform_data *pdata,
+                         enum coresight_dev_type type,
+                         union coresight_dev_subtype subtype)
+{
+       int i;
+       struct coresight_connection *conn;
+
+       for (i = 0; i < pdata->nr_inconns; ++i) {
+               conn = pdata->in_conns[i];
+               if (conn &&
+                   coresight_compare_type(type, subtype, conn->src_dev->type,
+                                          conn->src_dev->subtype))
+                       return conn->src_dev;
+       }
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(coresight_find_input_type);
+
+struct coresight_device *
+coresight_find_output_type(struct coresight_platform_data *pdata,
+                          enum coresight_dev_type type,
+                          union coresight_dev_subtype subtype)
+{
+       int i;
+       struct coresight_connection *conn;
+
+       for (i = 0; i < pdata->nr_outconns; ++i) {
+               conn = pdata->out_conns[i];
+               if (conn->dest_dev &&
+                   coresight_compare_type(type, subtype, conn->dest_dev->type,
+                                          conn->dest_dev->subtype))
+                       return conn->dest_dev;
+       }
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(coresight_find_output_type);
+
 bool coresight_loses_context_with_cpu(struct device *dev)
 {
        return fwnode_property_present(dev_fwnode(dev),