mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-15 15:04:27 +08:00
2478a6ae4a
Coresight architecture defines CLAIM tags for a device to negotiate control of the components (external agent vs self-hosted). Each device has a pair of registers (CLAIMSET & CLAIMCLR) for managing the CLAIM tags. However, the protocol for the CLAIM tags is IMPLEMENTATION DEFINED. PSCI has recommendations for the use of the CLAIM tags to negotiate controls for external agent vs self-hosted use. This patch implements the recommended protocol by PSCI. The claim/disclaim operations are performed from the device specific drivers. The disadvantage is that the calls are sprinkled in each driver, but this makes the operation much simpler. Cc: Mathieu Poirier <mathieu.poirier@linaro.org> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com> Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1193 lines
28 KiB
C
1193 lines
28 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2012, The Linux Foundation. All rights reserved.
|
|
*/
|
|
|
|
#include <linux/kernel.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 <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 *, tracer_path);
|
|
|
|
/*
|
|
* As of this writing only a single STM can be found in CS topologies. Since
|
|
* there is no way to know if we'll ever see more and what kind of
|
|
* configuration they will enact, for the time being only define a single path
|
|
* for STM.
|
|
*/
|
|
static struct list_head *stm_path;
|
|
|
|
/*
|
|
* When losing synchronisation a new barrier packet needs to be inserted at the
|
|
* beginning of the data collected in a buffer. That way the decoder knows that
|
|
* it needs to look for another sync sequence.
|
|
*/
|
|
const u32 barrier_pkt[4] = {0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff};
|
|
|
|
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,
|
|
struct coresight_device *parent)
|
|
{
|
|
int i;
|
|
struct coresight_connection *conn;
|
|
|
|
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 -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->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 -ENODEV;
|
|
}
|
|
|
|
static inline u32 coresight_read_claim_tags(void __iomem *base)
|
|
{
|
|
return readl_relaxed(base + CORESIGHT_CLAIMCLR);
|
|
}
|
|
|
|
static inline bool coresight_is_claimed_self_hosted(void __iomem *base)
|
|
{
|
|
return coresight_read_claim_tags(base) == CORESIGHT_CLAIM_SELF_HOSTED;
|
|
}
|
|
|
|
static inline bool coresight_is_claimed_any(void __iomem *base)
|
|
{
|
|
return coresight_read_claim_tags(base) != 0;
|
|
}
|
|
|
|
static inline void coresight_set_claim_tags(void __iomem *base)
|
|
{
|
|
writel_relaxed(CORESIGHT_CLAIM_SELF_HOSTED, base + CORESIGHT_CLAIMSET);
|
|
isb();
|
|
}
|
|
|
|
static inline void coresight_clear_claim_tags(void __iomem *base)
|
|
{
|
|
writel_relaxed(CORESIGHT_CLAIM_SELF_HOSTED, base + CORESIGHT_CLAIMCLR);
|
|
isb();
|
|
}
|
|
|
|
/*
|
|
* coresight_claim_device_unlocked : Claim the device for self-hosted usage
|
|
* to prevent an external tool from touching this device. As per PSCI
|
|
* standards, section "Preserving the execution context" => "Debug and Trace
|
|
* save and Restore", DBGCLAIM[1] is reserved for Self-hosted debug/trace and
|
|
* DBGCLAIM[0] is reserved for external tools.
|
|
*
|
|
* Called with CS_UNLOCKed for the component.
|
|
* Returns : 0 on success
|
|
*/
|
|
int coresight_claim_device_unlocked(void __iomem *base)
|
|
{
|
|
if (coresight_is_claimed_any(base))
|
|
return -EBUSY;
|
|
|
|
coresight_set_claim_tags(base);
|
|
if (coresight_is_claimed_self_hosted(base))
|
|
return 0;
|
|
/* There was a race setting the tags, clean up and fail */
|
|
coresight_clear_claim_tags(base);
|
|
return -EBUSY;
|
|
}
|
|
|
|
int coresight_claim_device(void __iomem *base)
|
|
{
|
|
int rc;
|
|
|
|
CS_UNLOCK(base);
|
|
rc = coresight_claim_device_unlocked(base);
|
|
CS_LOCK(base);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* coresight_disclaim_device_unlocked : Clear the claim tags for the device.
|
|
* Called with CS_UNLOCKed for the component.
|
|
*/
|
|
void coresight_disclaim_device_unlocked(void __iomem *base)
|
|
{
|
|
|
|
if (coresight_is_claimed_self_hosted(base))
|
|
coresight_clear_claim_tags(base);
|
|
else
|
|
/*
|
|
* The external agent may have not honoured our claim
|
|
* and has manipulated it. Or something else has seriously
|
|
* gone wrong in our driver.
|
|
*/
|
|
WARN_ON_ONCE(1);
|
|
}
|
|
|
|
void coresight_disclaim_device(void __iomem *base)
|
|
{
|
|
CS_UNLOCK(base);
|
|
coresight_disclaim_device_unlocked(base);
|
|
CS_LOCK(base);
|
|
}
|
|
|
|
static int coresight_enable_sink(struct coresight_device *csdev,
|
|
u32 mode, void *data)
|
|
{
|
|
int ret;
|
|
|
|
/*
|
|
* We need to make sure the "new" session is compatible with the
|
|
* existing "mode" of operation.
|
|
*/
|
|
if (sink_ops(csdev)->enable) {
|
|
ret = sink_ops(csdev)->enable(csdev, mode, data);
|
|
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,
|
|
struct coresight_device *parent,
|
|
struct coresight_device *child)
|
|
{
|
|
int ret;
|
|
int link_subtype;
|
|
int refport, inport, outport;
|
|
|
|
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)
|
|
refport = inport;
|
|
else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT)
|
|
refport = outport;
|
|
else
|
|
refport = 0;
|
|
|
|
if (refport < 0)
|
|
return refport;
|
|
|
|
if (atomic_inc_return(&csdev->refcnt[refport]) == 1) {
|
|
if (link_ops(csdev)->enable) {
|
|
ret = link_ops(csdev)->enable(csdev, inport, outport);
|
|
if (ret) {
|
|
atomic_dec(&csdev->refcnt[refport]);
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
csdev->enable = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
|
|
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) {
|
|
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, u32 mode)
|
|
{
|
|
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, NULL, mode);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
csdev->enable = true;
|
|
}
|
|
|
|
atomic_inc(csdev->refcnt);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*
|
|
* Returns true if the device has been disabled.
|
|
*/
|
|
static bool coresight_disable_source(struct coresight_device *csdev)
|
|
{
|
|
if (atomic_dec_return(csdev->refcnt) == 0) {
|
|
if (source_ops(csdev)->disable)
|
|
source_ops(csdev)->disable(csdev, NULL);
|
|
csdev->enable = false;
|
|
}
|
|
return !csdev->enable;
|
|
}
|
|
|
|
/*
|
|
* coresight_disable_path_from : Disable components in the given path beyond
|
|
* @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,
|
|
struct coresight_node *nd)
|
|
{
|
|
u32 type;
|
|
struct coresight_device *csdev, *parent, *child;
|
|
|
|
if (!nd)
|
|
nd = list_first_entry(path, struct coresight_node, link);
|
|
|
|
list_for_each_entry_continue(nd, path, link) {
|
|
csdev = nd->csdev;
|
|
type = csdev->type;
|
|
|
|
/*
|
|
* ETF devices are tricky... They can be a link or a sink,
|
|
* depending on how they are configured. If an ETF has been
|
|
* "activated" it will be configured as a sink, otherwise
|
|
* go ahead with the link configuration.
|
|
*/
|
|
if (type == CORESIGHT_DEV_TYPE_LINKSINK)
|
|
type = (csdev == coresight_get_sink(path)) ?
|
|
CORESIGHT_DEV_TYPE_SINK :
|
|
CORESIGHT_DEV_TYPE_LINK;
|
|
|
|
switch (type) {
|
|
case CORESIGHT_DEV_TYPE_SINK:
|
|
coresight_disable_sink(csdev);
|
|
break;
|
|
case CORESIGHT_DEV_TYPE_SOURCE:
|
|
/*
|
|
* We skip the first node in the path assuming that it
|
|
* is the source. So we don't expect a source device in
|
|
* the middle of a path.
|
|
*/
|
|
WARN_ON(1);
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
void coresight_disable_path(struct list_head *path)
|
|
{
|
|
coresight_disable_path_from(path, NULL);
|
|
}
|
|
|
|
int coresight_enable_path(struct list_head *path, u32 mode, void *sink_data)
|
|
{
|
|
|
|
int ret = 0;
|
|
u32 type;
|
|
struct coresight_node *nd;
|
|
struct coresight_device *csdev, *parent, *child;
|
|
|
|
list_for_each_entry_reverse(nd, path, link) {
|
|
csdev = nd->csdev;
|
|
type = csdev->type;
|
|
|
|
/*
|
|
* ETF devices are tricky... They can be a link or a sink,
|
|
* depending on how they are configured. If an ETF has been
|
|
* "activated" it will be configured as a sink, otherwise
|
|
* go ahead with the link configuration.
|
|
*/
|
|
if (type == CORESIGHT_DEV_TYPE_LINKSINK)
|
|
type = (csdev == coresight_get_sink(path)) ?
|
|
CORESIGHT_DEV_TYPE_SINK :
|
|
CORESIGHT_DEV_TYPE_LINK;
|
|
|
|
switch (type) {
|
|
case CORESIGHT_DEV_TYPE_SINK:
|
|
ret = coresight_enable_sink(csdev, mode, sink_data);
|
|
/*
|
|
* Sink is the first component turned on. If we
|
|
* failed to enable the sink, there are no components
|
|
* that need disabling. Disabling the path here
|
|
* would mean we could disrupt an existing session.
|
|
*/
|
|
if (ret)
|
|
goto out;
|
|
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_from(path, nd);
|
|
goto out;
|
|
}
|
|
|
|
struct coresight_device *coresight_get_sink(struct list_head *path)
|
|
{
|
|
struct coresight_device *csdev;
|
|
|
|
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;
|
|
}
|
|
|
|
static int coresight_enabled_sink(struct device *dev, void *data)
|
|
{
|
|
bool *reset = data;
|
|
struct coresight_device *csdev = to_coresight_device(dev);
|
|
|
|
if ((csdev->type == CORESIGHT_DEV_TYPE_SINK ||
|
|
csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) &&
|
|
csdev->activated) {
|
|
/*
|
|
* Now that we have a handle on the sink for this session,
|
|
* disable the sysFS "enable_sink" flag so that possible
|
|
* concurrent perf session that wish to use another sink don't
|
|
* trip on it. Doing so has no ramification for the current
|
|
* session.
|
|
*/
|
|
if (*reset)
|
|
csdev->activated = false;
|
|
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* coresight_get_enabled_sink - returns the first enabled sink found on the bus
|
|
* @deactivate: Whether the 'enable_sink' flag should be reset
|
|
*
|
|
* When operated from perf the deactivate parameter should be set to 'true'.
|
|
* That way the "enabled_sink" flag of the sink that was selected can be reset,
|
|
* allowing for other concurrent perf sessions to choose a different sink.
|
|
*
|
|
* When operated from sysFS users have full control and as such the deactivate
|
|
* parameter should be set to 'false', hence mandating users to explicitly
|
|
* clear the flag.
|
|
*/
|
|
struct coresight_device *coresight_get_enabled_sink(bool deactivate)
|
|
{
|
|
struct device *dev = NULL;
|
|
|
|
dev = bus_find_device(&coresight_bustype, NULL, &deactivate,
|
|
coresight_enabled_sink);
|
|
|
|
return dev ? to_coresight_device(dev) : NULL;
|
|
}
|
|
|
|
/*
|
|
* coresight_grab_device - Power up this device and any of the helper
|
|
* devices connected to it for trace operation. Since the helper devices
|
|
* don't appear on the trace path, they should be handled along with the
|
|
* the master device.
|
|
*/
|
|
static void coresight_grab_device(struct coresight_device *csdev)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < csdev->nr_outport; i++) {
|
|
struct coresight_device *child = csdev->conns[i].child_dev;
|
|
|
|
if (child && child->type == CORESIGHT_DEV_TYPE_HELPER)
|
|
pm_runtime_get_sync(child->dev.parent);
|
|
}
|
|
pm_runtime_get_sync(csdev->dev.parent);
|
|
}
|
|
|
|
/*
|
|
* coresight_drop_device - Release this device and any of the helper
|
|
* devices connected to it.
|
|
*/
|
|
static void coresight_drop_device(struct coresight_device *csdev)
|
|
{
|
|
int i;
|
|
|
|
pm_runtime_put(csdev->dev.parent);
|
|
for (i = 0; i < csdev->nr_outport; i++) {
|
|
struct coresight_device *child = csdev->conns[i].child_dev;
|
|
|
|
if (child && child->type == CORESIGHT_DEV_TYPE_HELPER)
|
|
pm_runtime_put(child->dev.parent);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* _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 coresight_device *sink,
|
|
struct list_head *path)
|
|
{
|
|
int i;
|
|
bool found = false;
|
|
struct coresight_node *node;
|
|
|
|
/* An activated sink has been found. Enqueue the element */
|
|
if (csdev == sink)
|
|
goto out;
|
|
|
|
/* Not a sink - recursively explore each port found on this element */
|
|
for (i = 0; i < csdev->nr_outport; i++) {
|
|
struct coresight_device *child_dev = csdev->conns[i].child_dev;
|
|
|
|
if (child_dev &&
|
|
_coresight_build_path(child_dev, sink, 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;
|
|
|
|
coresight_grab_device(csdev);
|
|
node->csdev = csdev;
|
|
list_add(&node->link, path);
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct list_head *coresight_build_path(struct coresight_device *source,
|
|
struct coresight_device *sink)
|
|
{
|
|
struct list_head *path;
|
|
int rc;
|
|
|
|
if (!sink)
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
path = kzalloc(sizeof(struct list_head), GFP_KERNEL);
|
|
if (!path)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
INIT_LIST_HEAD(path);
|
|
|
|
rc = _coresight_build_path(source, sink, path);
|
|
if (rc) {
|
|
kfree(path);
|
|
return ERR_PTR(rc);
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
|
|
list_for_each_entry_safe(nd, next, path, link) {
|
|
csdev = nd->csdev;
|
|
|
|
coresight_drop_device(csdev);
|
|
list_del(&nd->link);
|
|
kfree(nd);
|
|
}
|
|
|
|
kfree(path);
|
|
path = NULL;
|
|
}
|
|
|
|
/** coresight_validate_source - make sure a source has the right credentials
|
|
* @csdev: the device structure for a source.
|
|
* @function: the function this was called from.
|
|
*
|
|
* Assumes the coresight_mutex is held.
|
|
*/
|
|
static int coresight_validate_source(struct coresight_device *csdev,
|
|
const char *function)
|
|
{
|
|
u32 type, subtype;
|
|
|
|
type = csdev->type;
|
|
subtype = csdev->subtype.source_subtype;
|
|
|
|
if (type != CORESIGHT_DEV_TYPE_SOURCE) {
|
|
dev_err(&csdev->dev, "wrong device type in %s\n", function);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_PROC &&
|
|
subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE) {
|
|
dev_err(&csdev->dev, "wrong device subtype in %s\n", function);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int coresight_enable(struct coresight_device *csdev)
|
|
{
|
|
int cpu, ret = 0;
|
|
struct coresight_device *sink;
|
|
struct list_head *path;
|
|
enum coresight_dev_subtype_source subtype;
|
|
|
|
subtype = csdev->subtype.source_subtype;
|
|
|
|
mutex_lock(&coresight_mutex);
|
|
|
|
ret = coresight_validate_source(csdev, __func__);
|
|
if (ret)
|
|
goto out;
|
|
|
|
if (csdev->enable) {
|
|
/*
|
|
* There could be multiple applications driving the software
|
|
* source. So keep the refcount for each such user when the
|
|
* source is already enabled.
|
|
*/
|
|
if (subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE)
|
|
atomic_inc(csdev->refcnt);
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* Search for a valid sink for this session but don't reset the
|
|
* "enable_sink" flag in sysFS. Users get to do that explicitly.
|
|
*/
|
|
sink = coresight_get_enabled_sink(false);
|
|
if (!sink) {
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
path = coresight_build_path(csdev, sink);
|
|
if (IS_ERR(path)) {
|
|
pr_err("building path(s) failed\n");
|
|
ret = PTR_ERR(path);
|
|
goto out;
|
|
}
|
|
|
|
ret = coresight_enable_path(path, CS_MODE_SYSFS, NULL);
|
|
if (ret)
|
|
goto err_path;
|
|
|
|
ret = coresight_enable_source(csdev, CS_MODE_SYSFS);
|
|
if (ret)
|
|
goto err_source;
|
|
|
|
switch (subtype) {
|
|
case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
|
|
/*
|
|
* 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(tracer_path, cpu) = path;
|
|
break;
|
|
case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
|
|
stm_path = path;
|
|
break;
|
|
default:
|
|
/* We can't be here */
|
|
break;
|
|
}
|
|
|
|
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)
|
|
{
|
|
int cpu, ret;
|
|
struct list_head *path = NULL;
|
|
|
|
mutex_lock(&coresight_mutex);
|
|
|
|
ret = coresight_validate_source(csdev, __func__);
|
|
if (ret)
|
|
goto out;
|
|
|
|
if (!csdev->enable || !coresight_disable_source(csdev))
|
|
goto out;
|
|
|
|
switch (csdev->subtype.source_subtype) {
|
|
case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
|
|
cpu = source_ops(csdev)->cpu_id(csdev);
|
|
path = per_cpu(tracer_path, cpu);
|
|
per_cpu(tracer_path, cpu) = NULL;
|
|
break;
|
|
case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
|
|
path = stm_path;
|
|
stm_path = NULL;
|
|
break;
|
|
default:
|
|
/* We can't be here */
|
|
break;
|
|
}
|
|
|
|
coresight_disable_path(path);
|
|
coresight_release_path(path);
|
|
|
|
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", 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", 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,
|
|
},
|
|
{
|
|
.name = "helper",
|
|
},
|
|
};
|
|
|
|
static void coresight_device_release(struct device *dev)
|
|
{
|
|
struct coresight_device *csdev = to_coresight_device(dev);
|
|
|
|
kfree(csdev->conns);
|
|
kfree(csdev->refcnt);
|
|
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 (conn->child_name &&
|
|
!strcmp(dev_name(&csdev->dev), conn->child_name)) {
|
|
conn->child_dev = csdev;
|
|
} else {
|
|
/* 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 void coresight_fixup_device_conns(struct coresight_device *csdev)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < csdev->nr_outport; i++) {
|
|
struct coresight_connection *conn = &csdev->conns[i];
|
|
struct device *dev = NULL;
|
|
|
|
if (conn->child_name)
|
|
dev = bus_find_device_by_name(&coresight_bustype, NULL,
|
|
conn->child_name);
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
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.
|
|
* @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 ret;
|
|
int link_subtype;
|
|
int nr_refcnts = 1;
|
|
atomic_t *refcnts = NULL;
|
|
struct coresight_device *csdev;
|
|
|
|
csdev = kzalloc(sizeof(*csdev), GFP_KERNEL);
|
|
if (!csdev) {
|
|
ret = -ENOMEM;
|
|
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->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_free_csdev;
|
|
}
|
|
|
|
csdev->refcnt = refcnts;
|
|
|
|
csdev->nr_inport = desc->pdata->nr_inport;
|
|
csdev->nr_outport = desc->pdata->nr_outport;
|
|
|
|
csdev->conns = desc->pdata->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) {
|
|
put_device(&csdev->dev);
|
|
/*
|
|
* All resources are free'd explicitly via
|
|
* coresight_device_release(), triggered from put_device().
|
|
*/
|
|
goto err_out;
|
|
}
|
|
|
|
mutex_lock(&coresight_mutex);
|
|
|
|
coresight_fixup_device_conns(csdev);
|
|
coresight_fixup_orphan_conns(csdev);
|
|
|
|
mutex_unlock(&coresight_mutex);
|
|
|
|
return csdev;
|
|
|
|
err_free_csdev:
|
|
kfree(csdev);
|
|
err_out:
|
|
return ERR_PTR(ret);
|
|
}
|
|
EXPORT_SYMBOL_GPL(coresight_register);
|
|
|
|
void coresight_unregister(struct coresight_device *csdev)
|
|
{
|
|
/* Remove references of that device in the topology */
|
|
coresight_remove_conns(csdev);
|
|
device_unregister(&csdev->dev);
|
|
}
|
|
EXPORT_SYMBOL_GPL(coresight_unregister);
|