diff options
Diffstat (limited to 'drivers/hwtracing/coresight/coresight-core.c')
-rw-r--r-- | drivers/hwtracing/coresight/coresight-core.c | 192 |
1 files changed, 161 insertions, 31 deletions
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 0a9380350fb5..fb43ef6a3b1f 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -19,10 +19,12 @@ #include <linux/property.h> #include <linux/delay.h> #include <linux/pm_runtime.h> +#include <linux/panic_notifier.h> #include "coresight-etm-perf.h" #include "coresight-priv.h" #include "coresight-syscfg.h" +#include "coresight-trace-id.h" /* * Mutex used to lock all sysfs enable and disable actions and loading and @@ -75,14 +77,14 @@ struct coresight_device *coresight_get_percpu_sink(int cpu) } EXPORT_SYMBOL_GPL(coresight_get_percpu_sink); -static struct coresight_device *coresight_get_source(struct list_head *path) +static struct coresight_device *coresight_get_source(struct coresight_path *path) { struct coresight_device *csdev; if (!path) return NULL; - csdev = list_first_entry(path, struct coresight_node, link)->csdev; + csdev = list_first_entry(&path->path_list, struct coresight_node, link)->csdev; if (!coresight_is_device_source(csdev)) return NULL; @@ -331,12 +333,12 @@ static int coresight_enable_helper(struct coresight_device *csdev, return helper_ops(csdev)->enable(csdev, mode, data); } -static void coresight_disable_helper(struct coresight_device *csdev) +static void coresight_disable_helper(struct coresight_device *csdev, void *data) { - helper_ops(csdev)->disable(csdev, NULL); + helper_ops(csdev)->disable(csdev, data); } -static void coresight_disable_helpers(struct coresight_device *csdev) +static void coresight_disable_helpers(struct coresight_device *csdev, void *data) { int i; struct coresight_device *helper; @@ -344,7 +346,7 @@ static void coresight_disable_helpers(struct coresight_device *csdev) 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_helper(helper, data); } } @@ -361,7 +363,7 @@ static void coresight_disable_helpers(struct coresight_device *csdev) void coresight_disable_source(struct coresight_device *csdev, void *data) { source_ops(csdev)->disable(csdev, data); - coresight_disable_helpers(csdev); + coresight_disable_helpers(csdev, NULL); } EXPORT_SYMBOL_GPL(coresight_disable_source); @@ -370,16 +372,16 @@ EXPORT_SYMBOL_GPL(coresight_disable_source); * @nd in the list. If @nd is NULL, all the components, except the SOURCE are * disabled. */ -static void coresight_disable_path_from(struct list_head *path, +static void coresight_disable_path_from(struct coresight_path *path, struct coresight_node *nd) { u32 type; struct coresight_device *csdev, *parent, *child; if (!nd) - nd = list_first_entry(path, struct coresight_node, link); + nd = list_first_entry(&path->path_list, struct coresight_node, link); - list_for_each_entry_continue(nd, path, link) { + list_for_each_entry_continue(nd, &path->path_list, link) { csdev = nd->csdev; type = csdev->type; @@ -417,11 +419,11 @@ static void coresight_disable_path_from(struct list_head *path, } /* Disable all helpers adjacent along the path last */ - coresight_disable_helpers(csdev); + coresight_disable_helpers(csdev, path); } } -void coresight_disable_path(struct list_head *path) +void coresight_disable_path(struct coresight_path *path) { coresight_disable_path_from(path, NULL); } @@ -446,7 +448,7 @@ static int coresight_enable_helpers(struct coresight_device *csdev, return 0; } -int coresight_enable_path(struct list_head *path, enum cs_mode mode, +int coresight_enable_path(struct coresight_path *path, enum cs_mode mode, void *sink_data) { int ret = 0; @@ -456,12 +458,12 @@ int coresight_enable_path(struct list_head *path, enum cs_mode mode, struct coresight_device *source; source = coresight_get_source(path); - list_for_each_entry_reverse(nd, path, link) { + list_for_each_entry_reverse(nd, &path->path_list, link) { csdev = nd->csdev; type = csdev->type; /* Enable all helpers adjacent to the path first */ - ret = coresight_enable_helpers(csdev, mode, sink_data); + ret = coresight_enable_helpers(csdev, mode, path); if (ret) goto err; /* @@ -509,20 +511,21 @@ err: goto out; } -struct coresight_device *coresight_get_sink(struct list_head *path) +struct coresight_device *coresight_get_sink(struct coresight_path *path) { struct coresight_device *csdev; if (!path) return NULL; - csdev = list_last_entry(path, struct coresight_node, link)->csdev; + csdev = list_last_entry(&path->path_list, struct coresight_node, link)->csdev; if (csdev->type != CORESIGHT_DEV_TYPE_SINK && csdev->type != CORESIGHT_DEV_TYPE_LINKSINK) return NULL; return csdev; } +EXPORT_SYMBOL_GPL(coresight_get_sink); u32 coresight_get_sink_id(struct coresight_device *csdev) { @@ -653,6 +656,50 @@ static void coresight_drop_device(struct coresight_device *csdev) } } +/* + * coresight device will read their existing or alloc a trace ID, if their trace_id + * callback is set. + * + * Return 0 if the trace_id callback is not set. + * Return the result of the trace_id callback if it is set. The return value + * will be the trace_id if successful, and an error number if it fails. + */ +static int coresight_get_trace_id(struct coresight_device *csdev, + enum cs_mode mode, + struct coresight_device *sink) +{ + if (coresight_ops(csdev)->trace_id) + return coresight_ops(csdev)->trace_id(csdev, mode, sink); + + return 0; +} + +/* + * Call this after creating the path and before enabling it. This leaves + * the trace ID set on the path, or it remains 0 if it couldn't be assigned. + */ +void coresight_path_assign_trace_id(struct coresight_path *path, + enum cs_mode mode) +{ + struct coresight_device *sink = coresight_get_sink(path); + struct coresight_node *nd; + int trace_id; + + list_for_each_entry(nd, &path->path_list, link) { + /* Assign a trace ID to the path for the first device that wants to do it */ + trace_id = coresight_get_trace_id(nd->csdev, mode, sink); + + /* + * 0 in this context is that it didn't want to assign so keep searching. + * Non 0 is either success or fail. + */ + if (trace_id != 0) { + path->trace_id = trace_id; + return; + } + } +} + /** * _coresight_build_path - recursively build a path from a @csdev to a sink. * @csdev: The device to start from. @@ -668,7 +715,7 @@ static void coresight_drop_device(struct coresight_device *csdev) static int _coresight_build_path(struct coresight_device *csdev, struct coresight_device *source, struct coresight_device *sink, - struct list_head *path) + struct coresight_path *path) { int i, ret; bool found = false; @@ -721,25 +768,25 @@ out: return -ENOMEM; node->csdev = csdev; - list_add(&node->link, path); + list_add(&node->link, &path->path_list); return 0; } -struct list_head *coresight_build_path(struct coresight_device *source, +struct coresight_path *coresight_build_path(struct coresight_device *source, struct coresight_device *sink) { - struct list_head *path; + struct coresight_path *path; int rc; if (!sink) return ERR_PTR(-EINVAL); - path = kzalloc(sizeof(struct list_head), GFP_KERNEL); + path = kzalloc(sizeof(struct coresight_path), GFP_KERNEL); if (!path) return ERR_PTR(-ENOMEM); - INIT_LIST_HEAD(path); + INIT_LIST_HEAD(&path->path_list); rc = _coresight_build_path(source, source, sink, path); if (rc) { @@ -757,12 +804,12 @@ struct list_head *coresight_build_path(struct coresight_device *source, * 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) +void coresight_release_path(struct coresight_path *path) { struct coresight_device *csdev; struct coresight_node *nd, *next; - list_for_each_entry_safe(nd, next, path, link) { + list_for_each_entry_safe(nd, next, &path->path_list, link) { csdev = nd->csdev; coresight_drop_device(csdev); @@ -1092,18 +1139,20 @@ static void coresight_remove_conns(struct coresight_device *csdev) } /** - * coresight_timeout - loop until a bit has changed to a specific register - * state. + * coresight_timeout_action - loop until a bit has changed to a specific register + * state, with a callback after every trial. * @csa: coresight device access for the device * @offset: Offset of the register from the base of the device. * @position: the position of the bit of interest. * @value: the value the bit should have. + * @cb: Call back after each trial. * * 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(struct csdev_access *csa, u32 offset, - int position, int value) +int coresight_timeout_action(struct csdev_access *csa, u32 offset, + int position, int value, + coresight_timeout_cb_t cb) { int i; u32 val; @@ -1119,7 +1168,8 @@ int coresight_timeout(struct csdev_access *csa, u32 offset, if (!(val & BIT(position))) return 0; } - + if (cb) + cb(csa, offset, position, value); /* * Delay is arbitrary - the specification doesn't say how long * we are expected to wait. Extra check required to make sure @@ -1131,6 +1181,13 @@ int coresight_timeout(struct csdev_access *csa, u32 offset, return -EAGAIN; } +EXPORT_SYMBOL_GPL(coresight_timeout_action); + +int coresight_timeout(struct csdev_access *csa, u32 offset, + int position, int value) +{ + return coresight_timeout_action(csa, offset, position, value, NULL); +} EXPORT_SYMBOL_GPL(coresight_timeout); u32 coresight_relaxed_read32(struct coresight_device *csdev, u32 offset) @@ -1239,7 +1296,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) if (csdev->type == CORESIGHT_DEV_TYPE_SINK || csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) { - spin_lock_init(&csdev->perf_sink_id_map.lock); + raw_spin_lock_init(&csdev->perf_sink_id_map.lock); csdev->perf_sink_id_map.cpu_map = alloc_percpu(atomic_t); if (!csdev->perf_sink_id_map.cpu_map) { kfree(csdev); @@ -1453,6 +1510,36 @@ const struct bus_type coresight_bustype = { .name = "coresight", }; +static int coresight_panic_sync(struct device *dev, void *data) +{ + int mode; + struct coresight_device *csdev; + + /* Run through panic sync handlers for all enabled devices */ + csdev = container_of(dev, struct coresight_device, dev); + mode = coresight_get_mode(csdev); + + if ((mode == CS_MODE_SYSFS) || (mode == CS_MODE_PERF)) { + if (panic_ops(csdev)) + panic_ops(csdev)->sync(csdev); + } + + return 0; +} + +static int coresight_panic_cb(struct notifier_block *self, + unsigned long v, void *p) +{ + bus_for_each_dev(&coresight_bustype, NULL, NULL, + coresight_panic_sync); + + return 0; +} + +static struct notifier_block coresight_notifier = { + .notifier_call = coresight_panic_cb, +}; + static int __init coresight_init(void) { int ret; @@ -1465,11 +1552,20 @@ static int __init coresight_init(void) if (ret) goto exit_bus_unregister; + /* Register function to be called for panic */ + ret = atomic_notifier_chain_register(&panic_notifier_list, + &coresight_notifier); + if (ret) + goto exit_perf; + /* initialise the coresight syscfg API */ ret = cscfg_init(); if (!ret) return 0; + atomic_notifier_chain_unregister(&panic_notifier_list, + &coresight_notifier); +exit_perf: etm_perf_exit(); exit_bus_unregister: bus_unregister(&coresight_bustype); @@ -1479,6 +1575,8 @@ exit_bus_unregister: static void __exit coresight_exit(void) { cscfg_exit(); + atomic_notifier_chain_unregister(&panic_notifier_list, + &coresight_notifier); etm_perf_exit(); bus_unregister(&coresight_bustype); } @@ -1515,6 +1613,38 @@ void coresight_remove_driver(struct amba_driver *amba_drv, } EXPORT_SYMBOL_GPL(coresight_remove_driver); +int coresight_etm_get_trace_id(struct coresight_device *csdev, enum cs_mode mode, + struct coresight_device *sink) +{ + int cpu, trace_id; + + if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE || !source_ops(csdev)->cpu_id) + return -EINVAL; + + cpu = source_ops(csdev)->cpu_id(csdev); + switch (mode) { + case CS_MODE_SYSFS: + trace_id = coresight_trace_id_get_cpu_id(cpu); + break; + case CS_MODE_PERF: + if (WARN_ON(!sink)) + return -EINVAL; + + trace_id = coresight_trace_id_get_cpu_id_map(cpu, &sink->perf_sink_id_map); + break; + default: + trace_id = -EINVAL; + break; + } + + if (!IS_VALID_CS_TRACE_ID(trace_id)) + dev_err(&csdev->dev, + "Failed to allocate trace ID on CPU%d\n", cpu); + + return trace_id; +} +EXPORT_SYMBOL_GPL(coresight_etm_get_trace_id); + MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Pratik Patel <pratikp@codeaurora.org>"); MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>"); |