diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index a10abbabeeae..f2c84054e0af 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -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 diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 867e868ba757..1d19122d7006 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -30,6 +30,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 diff --git a/drivers/hwtracing/coresight/coresight-trace-noc.c b/drivers/hwtracing/coresight/coresight-trace-noc.c new file mode 100644 index 000000000000..d40576fa1d44 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-trace-noc.c @@ -0,0 +1,374 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#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"); diff --git a/drivers/hwtracing/coresight/coresight-trace-noc.h b/drivers/hwtracing/coresight/coresight-trace-noc.h new file mode 100644 index 000000000000..a7ac01f75966 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-trace-noc.h @@ -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, +};