linux/drivers/hwtracing/coresight/coresight-dynamic-replicator.c
Suzuki K Poulose 30af4fb619 coresight: dynamic-replicator: Handle multiple connections
When a replicator port is enabled, we block the traffic
on the other port and route all traffic to the new enabled
port. If there are two active trace sessions each targeting
the two different paths from the replicator, the second session
will disable the first session and route all the data to the
second path.
                    ETR
                 /
e.g, replicator
                 \
                    ETB

If CPU0 is operated in sysfs mode to ETR and CPU1 is operated
in perf mode to ETB, depending on the order in which the
replicator is enabled one device is blocked.

Ideally we need trace-id for the session to make the
right choice. That implies we need a trace-id allocation
logic for the coresight subsystem and use that to route
the traffic. The short term solution is to only manage
the "target port" and leave the other port untouched.
That leaves both the paths unaffected, except that some
unwanted traffic may be pushed to the paths (if the Trace-IDs
are not far enough), which is still fine and can be filtered
out while processing rather than silently blocking the data.

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>
2018-09-25 20:09:19 +02:00

243 lines
5.5 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
*/
#include <linux/amba/bus.h>
#include <linux/clk.h>
#include <linux/coresight.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include "coresight-priv.h"
#define REPLICATOR_IDFILTER0 0x000
#define REPLICATOR_IDFILTER1 0x004
/**
* struct replicator_state - specifics associated to a replicator component
* @base: memory mapped base address for this component.
* @dev: the device entity associated with this component
* @atclk: optional clock for the core parts of the replicator.
* @csdev: component vitals needed by the framework
*/
struct replicator_state {
void __iomem *base;
struct device *dev;
struct clk *atclk;
struct coresight_device *csdev;
};
/*
* replicator_reset : Reset the replicator configuration to sane values.
*/
static void replicator_reset(struct replicator_state *drvdata)
{
CS_UNLOCK(drvdata->base);
writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER0);
writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER1);
CS_LOCK(drvdata->base);
}
static int replicator_enable(struct coresight_device *csdev, int inport,
int outport)
{
u32 reg;
struct replicator_state *drvdata = dev_get_drvdata(csdev->dev.parent);
switch (outport) {
case 0:
reg = REPLICATOR_IDFILTER0;
break;
case 1:
reg = REPLICATOR_IDFILTER1;
break;
default:
WARN_ON(1);
return -EINVAL;
}
CS_UNLOCK(drvdata->base);
/* Ensure that the outport is enabled. */
writel_relaxed(0x00, drvdata->base + reg);
CS_LOCK(drvdata->base);
dev_dbg(drvdata->dev, "REPLICATOR enabled\n");
return 0;
}
static void replicator_disable(struct coresight_device *csdev, int inport,
int outport)
{
u32 reg;
struct replicator_state *drvdata = dev_get_drvdata(csdev->dev.parent);
switch (outport) {
case 0:
reg = REPLICATOR_IDFILTER0;
break;
case 1:
reg = REPLICATOR_IDFILTER1;
break;
default:
WARN_ON(1);
return;
}
CS_UNLOCK(drvdata->base);
/* disable the flow of ATB data through port */
writel_relaxed(0xff, drvdata->base + reg);
CS_LOCK(drvdata->base);
dev_dbg(drvdata->dev, "REPLICATOR disabled\n");
}
static const struct coresight_ops_link replicator_link_ops = {
.enable = replicator_enable,
.disable = replicator_disable,
};
static const struct coresight_ops replicator_cs_ops = {
.link_ops = &replicator_link_ops,
};
#define coresight_replicator_reg(name, offset) \
coresight_simple_reg32(struct replicator_state, name, offset)
coresight_replicator_reg(idfilter0, REPLICATOR_IDFILTER0);
coresight_replicator_reg(idfilter1, REPLICATOR_IDFILTER1);
static struct attribute *replicator_mgmt_attrs[] = {
&dev_attr_idfilter0.attr,
&dev_attr_idfilter1.attr,
NULL,
};
static const struct attribute_group replicator_mgmt_group = {
.attrs = replicator_mgmt_attrs,
.name = "mgmt",
};
static const struct attribute_group *replicator_groups[] = {
&replicator_mgmt_group,
NULL,
};
static int replicator_probe(struct amba_device *adev, const struct amba_id *id)
{
int ret;
struct device *dev = &adev->dev;
struct resource *res = &adev->res;
struct coresight_platform_data *pdata = NULL;
struct replicator_state *drvdata;
struct coresight_desc desc = { 0 };
struct device_node *np = adev->dev.of_node;
void __iomem *base;
if (np) {
pdata = of_get_coresight_platform_data(dev, np);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
adev->dev.platform_data = pdata;
}
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
drvdata->dev = &adev->dev;
drvdata->atclk = devm_clk_get(&adev->dev, "atclk"); /* optional */
if (!IS_ERR(drvdata->atclk)) {
ret = clk_prepare_enable(drvdata->atclk);
if (ret)
return ret;
}
/* Validity for the resource is already checked by the AMBA core */
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
drvdata->base = base;
dev_set_drvdata(dev, drvdata);
pm_runtime_put(&adev->dev);
desc.type = CORESIGHT_DEV_TYPE_LINK;
desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT;
desc.ops = &replicator_cs_ops;
desc.pdata = adev->dev.platform_data;
desc.dev = &adev->dev;
desc.groups = replicator_groups;
drvdata->csdev = coresight_register(&desc);
if (!IS_ERR(drvdata->csdev)) {
replicator_reset(drvdata);
return 0;
}
return PTR_ERR(drvdata->csdev);
}
#ifdef CONFIG_PM
static int replicator_runtime_suspend(struct device *dev)
{
struct replicator_state *drvdata = dev_get_drvdata(dev);
if (drvdata && !IS_ERR(drvdata->atclk))
clk_disable_unprepare(drvdata->atclk);
return 0;
}
static int replicator_runtime_resume(struct device *dev)
{
struct replicator_state *drvdata = dev_get_drvdata(dev);
if (drvdata && !IS_ERR(drvdata->atclk))
clk_prepare_enable(drvdata->atclk);
return 0;
}
#endif
static const struct dev_pm_ops replicator_dev_pm_ops = {
SET_RUNTIME_PM_OPS(replicator_runtime_suspend,
replicator_runtime_resume,
NULL)
};
static const struct amba_id replicator_ids[] = {
{
.id = 0x000bb909,
.mask = 0x000fffff,
},
{
/* Coresight SoC-600 */
.id = 0x000bb9ec,
.mask = 0x000fffff,
},
{ 0, 0 },
};
static struct amba_driver replicator_driver = {
.drv = {
.name = "coresight-dynamic-replicator",
.pm = &replicator_dev_pm_ops,
.suppress_bind_attrs = true,
},
.probe = replicator_probe,
.id_table = replicator_ids,
};
builtin_amba_driver(replicator_driver);