coresight: Add trace noc support

This driver provides the support to program the hardware of
Trace Noc component when enable the source which is connected to
Trace Noc.  Trace NoC is a interconnect that is used
to collect trace from various subsystems and transport it
QDSS trace sink.

Change-Id: I70ebda4142be066bc15d8809d7ffe3bdbee10fb9
Signed-off-by: Mao Jinlong <quic_jinlmao@quicinc.com>
This commit is contained in:
Mao Jinlong 2022-08-15 04:44:05 -07:00
parent 03de227439
commit 10de05b8fe
4 changed files with 450 additions and 0 deletions

View File

@ -189,6 +189,15 @@ config CORESIGHT_CTI_INTEGRATION_REGS
registers are not used in normal operation and can leave devices in
an inconsistent state.
config CORESIGHT_TRACE_NOC
tristate "Coresight Trace Noc driver"
help
This driver provides support for Trace NoC component.
Trace NoC is a interconnect that is used to collect trace from
various subsystems and transport it QDSS trace sink.It sits in
the different tiles of SOC and aggregates the trace local to the
tile and transports it another tile or to QDSS trace sink eventually.
config CORESIGHT_DUMMY
tristate "Dummy driver support"
help

View File

@ -29,6 +29,7 @@ coresight-cti-y := coresight-cti-core.o coresight-cti-platform.o \
coresight-cti-sysfs.o
obj-$(CONFIG_CORESIGHT_DUMMY) += coresight-dummy.o
obj-$(CONFIG_CORESIGHT_REMOTE_ETM) += coresight-remote-etm.o
obj-$(CONFIG_CORESIGHT_TRACE_NOC) += coresight-trace-noc.o
obj-$(CONFIG_CORESIGHT_TGU) += coresight-tgu.o
obj-$(CONFIG_CORESIGHT_HWEVENT) += coresight-hwevent.o
obj-$(CONFIG_CORESIGHT_TPDA) += coresight-tpda.o

View File

@ -0,0 +1,374 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/amba/bus.h>
#include <linux/io.h>
#include <linux/coresight.h>
#include <linux/of.h>
#include "coresight-priv.h"
#include "coresight-common.h"
#include "coresight-trace-noc.h"
static ssize_t flush_req_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t size)
{
struct trace_noc_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
u32 reg;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
if (!drvdata->enable) {
spin_unlock(&drvdata->spinlock);
return -EPERM;
}
if (val) {
reg = readl_relaxed(drvdata->base + TRACE_NOC_CTRL);
reg = reg | TRACE_NOC_CTRL_FLUSHREQ;
writel_relaxed(reg, drvdata->base + TRACE_NOC_CTRL);
}
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_WO(flush_req);
static ssize_t flush_status_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct trace_noc_drvdata *drvdata = dev_get_drvdata(dev->parent);
u32 val;
spin_lock(&drvdata->spinlock);
if (!drvdata->enable) {
spin_unlock(&drvdata->spinlock);
return -EPERM;
}
val = readl_relaxed(drvdata->base + TRACE_NOC_CTRL);
spin_unlock(&drvdata->spinlock);
return scnprintf(buf, PAGE_SIZE, "%x\n", BMVAL(val, 2, 2));
}
static DEVICE_ATTR_RO(flush_status);
static ssize_t flag_type_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct trace_noc_drvdata *drvdata = dev_get_drvdata(dev->parent);
return scnprintf(buf, PAGE_SIZE, "%x\n", drvdata->flagType);
}
static ssize_t flag_type_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t size)
{
struct trace_noc_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
if (val)
drvdata->flagType = FLAG_TS;
else
drvdata->flagType = FLAG;
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_RW(flag_type);
static ssize_t freq_type_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct trace_noc_drvdata *drvdata = dev_get_drvdata(dev->parent);
return scnprintf(buf, PAGE_SIZE, "%x\n", drvdata->freqType);
}
static ssize_t freq_type_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t size)
{
struct trace_noc_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
if (val)
drvdata->freqType = FREQ_TS;
else
drvdata->freqType = FREQ;
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_RW(freq_type);
static ssize_t freq_req_val_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct trace_noc_drvdata *drvdata = dev_get_drvdata(dev->parent);
return scnprintf(buf, PAGE_SIZE, "%x\n", drvdata->freq_req_val);
}
static ssize_t freq_req_val_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t size)
{
struct trace_noc_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
if (kstrtoul(buf, 0, &val))
return -EINVAL;
if (val) {
spin_lock(&drvdata->spinlock);
drvdata->freq_req_val = val;
spin_unlock(&drvdata->spinlock);
}
return size;
}
static DEVICE_ATTR_RW(freq_req_val);
static ssize_t freq_ts_req_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t size)
{
struct trace_noc_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
u32 reg;
if (kstrtoul(buf, 16, &val))
return -EINVAL;
spin_lock(&drvdata->spinlock);
if (!drvdata->enable) {
spin_unlock(&drvdata->spinlock);
return -EPERM;
}
if (val) {
reg = readl_relaxed(drvdata->base + TRACE_NOC_CTRL);
reg = reg | TRACE_NOC_CTRL_FREQTSREQ;
writel_relaxed(reg, drvdata->base + TRACE_NOC_CTRL);
}
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_WO(freq_ts_req);
static struct attribute *trace_noc_attrs[] = {
&dev_attr_flush_req.attr,
&dev_attr_flush_status.attr,
&dev_attr_flag_type.attr,
&dev_attr_freq_type.attr,
&dev_attr_freq_req_val.attr,
&dev_attr_freq_ts_req.attr,
NULL,
};
static struct attribute_group trace_noc_attr_grp = {
.attrs = trace_noc_attrs,
};
static const struct attribute_group *trace_noc_attr_grps[] = {
&trace_noc_attr_grp,
NULL,
};
static int trace_noc_enable(struct coresight_device *csdev, int inport, int outport)
{
struct trace_noc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
int ret;
u32 val;
ret = pm_runtime_get_sync(drvdata->dev);
if (ret < 0) {
pm_runtime_put(drvdata->dev);
return ret;
}
spin_lock(&drvdata->spinlock);
/* Set ATID */
writel_relaxed(drvdata->atid, drvdata->base + TRACE_NOC_XLD);
/* Config sync CR */
writel_relaxed(0xffff, drvdata->base + TRACE_NOC_SYNCR);
/* Set frequency value */
if (drvdata->freq_req_val)
writel_relaxed(drvdata->freq_req_val,
drvdata->base + TRACE_NOC_FREQVAL);
/* Set Ctrl register */
val = readl_relaxed(drvdata->base + TRACE_NOC_CTRL);
if (drvdata->flagType == FLAG_TS)
val = val | TRACE_NOC_CTRL_FLAGTYPE;
else
val = val & ~TRACE_NOC_CTRL_FLAGTYPE;
if (drvdata->freqType == FREQ_TS)
val = val | TRACE_NOC_CTRL_FREQTYPE;
else
val = val & ~TRACE_NOC_CTRL_FREQTYPE;
val = val | TRACE_NOC_CTRL_PORTEN;
writel_relaxed(val, drvdata->base + TRACE_NOC_CTRL);
drvdata->enable = true;
spin_unlock(&drvdata->spinlock);
dev_info(drvdata->dev, "Trace NOC is enabled\n");
return 0;
}
static void trace_noc_disable(struct coresight_device *csdev, int inport, int outport)
{
struct trace_noc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
spin_lock(&drvdata->spinlock);
writel_relaxed(0x0, drvdata->base + TRACE_NOC_CTRL);
drvdata->enable = false;
spin_unlock(&drvdata->spinlock);
pm_runtime_put(drvdata->dev);
dev_info(drvdata->dev, "Trace NOC is disabled\n");
}
static const struct coresight_ops_link trace_noc_link_ops = {
.enable = trace_noc_enable,
.disable = trace_noc_disable,
};
static const struct coresight_ops trace_noc_cs_ops = {
.link_ops = &trace_noc_link_ops,
};
static int trace_noc_parse_of_data(struct trace_noc_drvdata *drvdata)
{
int ret;
struct device_node *node = drvdata->dev->of_node;
ret = of_property_read_u32(node, "atid", &drvdata->atid);
if (ret) {
dev_err(drvdata->dev, "Trace Noc ATID is not specified\n");
return -EINVAL;
}
dev_dbg(drvdata->dev, "Trace Noc ATID is %d\n", drvdata->atid);
return 0;
}
static void trace_noc_init_default_data(struct trace_noc_drvdata *drvdata)
{
drvdata->freqType = FREQ_TS;
drvdata->freqTsReq = true;
}
static int trace_noc_probe(struct amba_device *adev, const struct amba_id *id)
{
struct device *dev = &adev->dev;
struct coresight_platform_data *pdata;
struct trace_noc_drvdata *drvdata;
struct coresight_desc desc = { 0 };
int ret;
desc.name = coresight_alloc_device_name(&trace_noc_devs, dev);
if (!desc.name)
return -ENOMEM;
pdata = coresight_get_platform_data(dev);
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;
dev_set_drvdata(dev, drvdata);
drvdata->base = devm_ioremap_resource(dev, &adev->res);
if (!drvdata->base)
return -ENOMEM;
ret = trace_noc_parse_of_data(drvdata);
if (ret)
return ret;
desc.type = CORESIGHT_DEV_TYPE_LINK;
desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG;
desc.ops = &trace_noc_cs_ops;
desc.pdata = adev->dev.platform_data;
desc.dev = &adev->dev;
desc.groups = trace_noc_attr_grps;
drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
pm_runtime_put(&adev->dev);
spin_lock_init(&drvdata->spinlock);
trace_noc_init_default_data(drvdata);
dev_dbg(drvdata->dev, "Trace Noc initialized\n");
return 0;
}
static void __exit trace_noc_remove(struct amba_device *adev)
{
struct trace_noc_drvdata *drvdata = dev_get_drvdata(&adev->dev);
coresight_unregister(drvdata->csdev);
}
static struct amba_id trace_noc_ids[] = {
{
.id = 0x000f0c00,
.mask = 0x000fff00,
},
{ 0, 0},
};
MODULE_DEVICE_TABLE(amba, trace_noc_ids);
static struct amba_driver trace_noc_driver = {
.drv = {
.name = "coresight-trace-noc",
.owner = THIS_MODULE,
.suppress_bind_attrs = true,
},
.probe = trace_noc_probe,
.remove = trace_noc_remove,
.id_table = trace_noc_ids,
};
module_amba_driver(trace_noc_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Trace NOC driver");

View File

@ -0,0 +1,66 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#define TRACE_NOC_CTRL 0x008
#define TRACE_NOC_XLD 0x010
#define TRACE_NOC_FREQVAL 0x018
#define TRACE_NOC_SYNCR 0x020
/* Enable generation of output ATB traffic.*/
#define TRACE_NOC_CTRL_PORTEN BIT(0)
/* Writing 1 to initiate a flush sequence.*/
#define TRACE_NOC_CTRL_FLUSHREQ BIT(1)
/* 0: sequence in progress; 1: sequence has been completed.*/
#define TRACE_NOC_CTRL_FLUSHSTATUS BIT(2)
/* Writing 1 to issue a FREQ or FREQ_TS packet*/
#define TRACE_NOC_CTRL_FREQTSREQ BIT(5)
/* Sets the type of issued ATB FLAG packets. 0: 'FLAG' packets; 1: 'FLAG_TS' packets.*/
#define TRACE_NOC_CTRL_FLAGTYPE BIT(7)
/* sets the type of issued ATB FREQ packets. 0: 'FREQ' packets; 1: 'FREQ_TS' packets.*/
#define TRACE_NOC_CTRL_FREQTYPE BIT(8)
DEFINE_CORESIGHT_DEVLIST(trace_noc_devs, "traceNoc");
/**
* struct trace_noc_drvdata - specifics associated to a trace noc component
* @base: memory mapped base address for this component.
* @dev: device node for trace_noc_drvdata.
* @csdev: component vitals needed by the framework.
* @spinlock: lock for the drvdata.
* @enable: status of the component.
* @flushReq: Issue a flush request or not.
* @freqTsReq: Issue a freq_ts request or not.
* @atid: id for the trace packet.
* @freq_req_val: set frequency values carried by 'FREQ' and 'FREQ_TS' packets.
* @flushStatus: 0: sequence in progress; 1: sequence has been completed.
* @freqType: 0: 'FREQ' packets; 1: 'FREQ_TS' packets.
* @flagType: 0: 'FLAG' packets; 1: 'FLAG_TS' packets.
*/
struct trace_noc_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
spinlock_t spinlock;
bool enable;
bool flushReq;
bool freqTsReq;
u32 atid;
u32 freq_req_val;
u32 flushStatus;
u32 freqType;
u32 flagType;
};
/* freq type */
enum freq_type {
FREQ,
FREQ_TS,
};
/* flag type */
enum flag_type {
FLAG,
FLAG_TS,
};