diff options
Diffstat (limited to 'drivers/hwtracing/coresight/coresight.c')
-rw-r--r-- | drivers/hwtracing/coresight/coresight.c | 388 |
1 files changed, 279 insertions, 109 deletions
diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 93738dfbf631..2ea5961092c1 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -11,7 +11,6 @@ */ #include <linux/kernel.h> -#include <linux/module.h> #include <linux/init.h> #include <linux/types.h> #include <linux/device.h> @@ -24,11 +23,28 @@ #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 +84,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 +102,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) @@ -110,13 +120,13 @@ static int coresight_find_link_outport(struct coresight_device *csdev) return 0; } -static int coresight_enable_sink(struct coresight_device *csdev) +static int coresight_enable_sink(struct coresight_device *csdev, u32 mode) { int ret; if (!csdev->enable) { if (sink_ops(csdev)->enable) { - ret = sink_ops(csdev)->enable(csdev); + ret = sink_ops(csdev)->enable(csdev, mode); if (ret) return ret; } @@ -138,14 +148,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 +183,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 +221,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 +233,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 +255,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, u32 mode) +{ + + 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, mode); + 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 +447,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, CS_MODE_SYSFS); + 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 +497,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 +608,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 +665,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); } @@ -568,6 +697,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 +706,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,13 +888,8 @@ 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); - -MODULE_LICENSE("GPL v2"); |