summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/hwtracing/coresight/coresight-etm-perf.c68
-rw-r--r--drivers/hwtracing/coresight/coresight-etm-perf.h38
-rw-r--r--drivers/hwtracing/coresight/coresight-etm3x-core.c51
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x-core.c175
-rw-r--r--drivers/hwtracing/coresight/coresight-etm4x.h92
-rw-r--r--drivers/hwtracing/coresight/coresight-tmc-etr.c43
-rw-r--r--drivers/hwtracing/coresight/coresight-tmc.h2
-rw-r--r--drivers/hwtracing/coresight/coresight-tnoc.c136
-rw-r--r--drivers/hwtracing/coresight/coresight-tpda.c284
-rw-r--r--drivers/hwtracing/coresight/coresight-tpda.h76
10 files changed, 773 insertions, 192 deletions
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c
index 17afa0f4cdee..3c8a6f795094 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.c
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.c
@@ -13,6 +13,7 @@
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/perf_event.h>
+#include <linux/perf/arm_pmu.h>
#include <linux/percpu-defs.h>
#include <linux/slab.h>
#include <linux/stringhash.h>
@@ -50,26 +51,22 @@ struct etm_ctxt {
static DEFINE_PER_CPU(struct etm_ctxt, etm_ctxt);
static DEFINE_PER_CPU(struct coresight_device *, csdev_src);
-/*
- * The PMU formats were orignally for ETMv3.5/PTM's ETMCR 'config';
- * now take them as general formats and apply on all ETMs.
- */
-PMU_FORMAT_ATTR(branch_broadcast, "config:"__stringify(ETM_OPT_BRANCH_BROADCAST));
-PMU_FORMAT_ATTR(cycacc, "config:" __stringify(ETM_OPT_CYCACC));
-/* contextid1 enables tracing CONTEXTIDR_EL1 for ETMv4 */
-PMU_FORMAT_ATTR(contextid1, "config:" __stringify(ETM_OPT_CTXTID));
-/* contextid2 enables tracing CONTEXTIDR_EL2 for ETMv4 */
-PMU_FORMAT_ATTR(contextid2, "config:" __stringify(ETM_OPT_CTXTID2));
-PMU_FORMAT_ATTR(timestamp, "config:" __stringify(ETM_OPT_TS));
-PMU_FORMAT_ATTR(retstack, "config:" __stringify(ETM_OPT_RETSTK));
+GEN_PMU_FORMAT_ATTR(cycacc);
+GEN_PMU_FORMAT_ATTR(timestamp);
+GEN_PMU_FORMAT_ATTR(retstack);
+GEN_PMU_FORMAT_ATTR(sinkid);
+
+#if IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM4X)
+GEN_PMU_FORMAT_ATTR(branch_broadcast);
+/* contextid1 enables tracing CONTEXTIDR_EL1*/
+GEN_PMU_FORMAT_ATTR(contextid1);
+/* contextid2 enables tracing CONTEXTIDR_EL2*/
+GEN_PMU_FORMAT_ATTR(contextid2);
/* preset - if sink ID is used as a configuration selector */
-PMU_FORMAT_ATTR(preset, "config:0-3");
-/* Sink ID - same for all ETMs */
-PMU_FORMAT_ATTR(sinkid, "config2:0-31");
+GEN_PMU_FORMAT_ATTR(preset);
/* config ID - set if a system configuration is selected */
-PMU_FORMAT_ATTR(configid, "config2:32-63");
-PMU_FORMAT_ATTR(cc_threshold, "config3:0-11");
-
+GEN_PMU_FORMAT_ATTR(configid);
+GEN_PMU_FORMAT_ATTR(cc_threshold);
/*
* contextid always traces the "PID". The PID is in CONTEXTIDR_EL1
@@ -80,29 +77,35 @@ static ssize_t format_attr_contextid_show(struct device *dev,
struct device_attribute *attr,
char *page)
{
- int pid_fmt = ETM_OPT_CTXTID;
-
-#if IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM4X)
- pid_fmt = is_kernel_in_hyp_mode() ? ETM_OPT_CTXTID2 : ETM_OPT_CTXTID;
-#endif
- return sprintf(page, "config:%d\n", pid_fmt);
+ if (is_kernel_in_hyp_mode())
+ return contextid2_show(dev, attr, page);
+ return contextid1_show(dev, attr, page);
}
static struct device_attribute format_attr_contextid =
__ATTR(contextid, 0444, format_attr_contextid_show, NULL);
+#endif
+/*
+ * ETMv3 only uses the first 3 attributes for programming itself (see
+ * ETM3X_SUPPORTED_OPTIONS). Sink ID is also supported for selecting a
+ * sink in both, but not used for configuring the ETM. The remaining
+ * attributes are ETMv4 specific.
+ */
static struct attribute *etm_config_formats_attr[] = {
&format_attr_cycacc.attr,
- &format_attr_contextid.attr,
- &format_attr_contextid1.attr,
- &format_attr_contextid2.attr,
&format_attr_timestamp.attr,
&format_attr_retstack.attr,
&format_attr_sinkid.attr,
+#if IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM4X)
+ &format_attr_contextid.attr,
+ &format_attr_contextid1.attr,
+ &format_attr_contextid2.attr,
&format_attr_preset.attr,
&format_attr_configid.attr,
&format_attr_branch_broadcast.attr,
&format_attr_cc_threshold.attr,
+#endif
NULL,
};
@@ -315,7 +318,7 @@ static bool sinks_compatible(struct coresight_device *a,
static void *etm_setup_aux(struct perf_event *event, void **pages,
int nr_pages, bool overwrite)
{
- u32 id, cfg_hash;
+ u32 sink_hash, cfg_hash;
int cpu = event->cpu;
cpumask_t *mask;
struct coresight_device *sink = NULL;
@@ -328,13 +331,12 @@ static void *etm_setup_aux(struct perf_event *event, void **pages,
INIT_WORK(&event_data->work, free_event_data);
/* First get the selected sink from user space. */
- if (event->attr.config2 & GENMASK_ULL(31, 0)) {
- id = (u32)event->attr.config2;
- sink = user_sink = coresight_get_sink_by_id(id);
- }
+ sink_hash = ATTR_CFG_GET_FLD(&event->attr, sinkid);
+ if (sink_hash)
+ sink = user_sink = coresight_get_sink_by_id(sink_hash);
/* check if user wants a coresight configuration selected */
- cfg_hash = (u32)((event->attr.config2 & GENMASK_ULL(63, 32)) >> 32);
+ cfg_hash = ATTR_CFG_GET_FLD(&event->attr, configid);
if (cfg_hash) {
if (cscfg_activate_config(cfg_hash))
goto err;
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.h b/drivers/hwtracing/coresight/coresight-etm-perf.h
index 5febbcdb8696..24d929428633 100644
--- a/drivers/hwtracing/coresight/coresight-etm-perf.h
+++ b/drivers/hwtracing/coresight/coresight-etm-perf.h
@@ -20,6 +20,44 @@ struct cscfg_config_desc;
*/
#define ETM_ADDR_CMP_MAX 8
+#define ATTR_CFG_FLD_preset_CFG config
+#define ATTR_CFG_FLD_preset_LO 0
+#define ATTR_CFG_FLD_preset_HI 3
+#define ATTR_CFG_FLD_timestamp_CFG config
+#define ATTR_CFG_FLD_timestamp_LO 4
+#define ATTR_CFG_FLD_timestamp_HI 7
+#define ATTR_CFG_FLD_branch_broadcast_CFG config
+#define ATTR_CFG_FLD_branch_broadcast_LO 8
+#define ATTR_CFG_FLD_branch_broadcast_HI 8
+#define ATTR_CFG_FLD_cycacc_CFG config
+#define ATTR_CFG_FLD_cycacc_LO 12
+#define ATTR_CFG_FLD_cycacc_HI 12
+#define ATTR_CFG_FLD_contextid1_CFG config
+#define ATTR_CFG_FLD_contextid1_LO 14
+#define ATTR_CFG_FLD_contextid1_HI 14
+#define ATTR_CFG_FLD_contextid2_CFG config
+#define ATTR_CFG_FLD_contextid2_LO 15
+#define ATTR_CFG_FLD_contextid2_HI 15
+/*
+ * Old position of 'timestamp' and not published in sysfs. Remove at a later
+ * date if necessary.
+ */
+#define ATTR_CFG_FLD_deprecated_timestamp_CFG config
+#define ATTR_CFG_FLD_deprecated_timestamp_LO 28
+#define ATTR_CFG_FLD_deprecated_timestamp_HI 28
+#define ATTR_CFG_FLD_retstack_CFG config
+#define ATTR_CFG_FLD_retstack_LO 29
+#define ATTR_CFG_FLD_retstack_HI 29
+#define ATTR_CFG_FLD_sinkid_CFG config2
+#define ATTR_CFG_FLD_sinkid_LO 0
+#define ATTR_CFG_FLD_sinkid_HI 31
+#define ATTR_CFG_FLD_configid_CFG config2
+#define ATTR_CFG_FLD_configid_LO 32
+#define ATTR_CFG_FLD_configid_HI 63
+#define ATTR_CFG_FLD_cc_threshold_CFG config3
+#define ATTR_CFG_FLD_cc_threshold_LO 0
+#define ATTR_CFG_FLD_cc_threshold_HI 11
+
/**
* struct etm_filter - single instruction range or start/stop configuration.
* @start_addr: The address to start tracing on.
diff --git a/drivers/hwtracing/coresight/coresight-etm3x-core.c b/drivers/hwtracing/coresight/coresight-etm3x-core.c
index a5e809589d3e..a547a6d2e0bd 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x-core.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x-core.c
@@ -28,6 +28,7 @@
#include <linux/uaccess.h>
#include <linux/clk.h>
#include <linux/perf_event.h>
+#include <linux/perf/arm_pmu.h>
#include <asm/sections.h>
#include "coresight-etm.h"
@@ -309,6 +310,7 @@ static int etm_parse_event_config(struct etm_drvdata *drvdata,
{
struct etm_config *config = &drvdata->config;
struct perf_event_attr *attr = &event->attr;
+ u8 ts_level;
if (!attr)
return -EINVAL;
@@ -332,28 +334,31 @@ static int etm_parse_event_config(struct etm_drvdata *drvdata,
if (config->mode)
etm_config_trace_mode(config);
- /*
- * At this time only cycle accurate, return stack and timestamp
- * options are available.
- */
- if (attr->config & ~ETM3X_SUPPORTED_OPTIONS)
- return -EINVAL;
+ config->ctrl = 0;
+
+ if (ATTR_CFG_GET_FLD(attr, cycacc))
+ config->ctrl |= ETMCR_CYC_ACC;
- config->ctrl = attr->config;
+ ts_level = max(ATTR_CFG_GET_FLD(attr, timestamp),
+ ATTR_CFG_GET_FLD(attr, deprecated_timestamp));
+
+ if (ts_level > 1) {
+ dev_dbg(&drvdata->csdev->dev,
+ "timestamp format attribute should be 0 (off) or 1 (on)\n");
+ return -EINVAL;
+ }
- /* Don't trace contextID when runs in non-root PID namespace */
- if (!task_is_in_init_pid_ns(current))
- config->ctrl &= ~ETMCR_CTXID_SIZE;
+ if (ts_level)
+ config->ctrl |= ETMCR_TIMESTAMP_EN;
/*
- * Possible to have cores with PTM (supports ret stack) and ETM
- * (never has ret stack) on the same SoC. So if we have a request
- * for return stack that can't be honoured on this core then
- * clear the bit - trace will still continue normally
+ * Possible to have cores with PTM (supports ret stack) and ETM (never
+ * has ret stack) on the same SoC. So only enable when it can be honored
+ * - trace will still continue normally otherwise.
*/
- if ((config->ctrl & ETMCR_RETURN_STACK) &&
- !(drvdata->etmccer & ETMCCER_RETSTACK))
- config->ctrl &= ~ETMCR_RETURN_STACK;
+ if (ATTR_CFG_GET_FLD(attr, retstack) &&
+ (drvdata->etmccer & ETMCCER_RETSTACK))
+ config->ctrl |= ETMCR_RETURN_STACK;
return 0;
}
@@ -795,16 +800,16 @@ static int __init etm_hp_setup(void)
{
int ret;
- ret = cpuhp_setup_state_nocalls_cpuslocked(CPUHP_AP_ARM_CORESIGHT_STARTING,
- "arm/coresight:starting",
- etm_starting_cpu, etm_dying_cpu);
+ ret = cpuhp_setup_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING,
+ "arm/coresight:starting",
+ etm_starting_cpu, etm_dying_cpu);
if (ret)
return ret;
- ret = cpuhp_setup_state_nocalls_cpuslocked(CPUHP_AP_ONLINE_DYN,
- "arm/coresight:online",
- etm_online_cpu, NULL);
+ ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
+ "arm/coresight:online",
+ etm_online_cpu, NULL);
/* HP dyn state ID returned in ret on success */
if (ret > 0) {
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
index 560975b70474..d565a73f0042 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
@@ -29,6 +29,7 @@
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/perf_event.h>
+#include <linux/perf/arm_pmu.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
@@ -642,18 +643,34 @@ static void etm4_enable_sysfs_smp_call(void *info)
* TRCRSCTLR1 (always true) used to get the counter to decrement. From
* there a resource selector is configured with the counter and the
* timestamp control register to use the resource selector to trigger the
- * event that will insert a timestamp packet in the stream.
+ * event that will insert a timestamp packet in the stream:
+ *
+ * +--------------+
+ * | Resource 1 | fixed "always-true" resource
+ * +--------------+
+ * |
+ * +------v-------+
+ * | Counter x | (reload to 2 ^ (ts_level - 1) on underflow)
+ * +--------------+
+ * |
+ * +------v--------------+
+ * | Resource Selector y | (trigger on counter x == 0)
+ * +---------------------+
+ * |
+ * +------v---------------+
+ * | Timestamp Generator | (timestamp on resource y)
+ * +----------------------+
*/
-static int etm4_config_timestamp_event(struct etmv4_drvdata *drvdata)
+static int etm4_config_timestamp_event(struct etmv4_drvdata *drvdata,
+ u8 ts_level)
{
- int ctridx, ret = -EINVAL;
- int counter, rselector;
- u32 val = 0;
+ int ctridx;
+ int rselector;
struct etmv4_config *config = &drvdata->config;
/* No point in trying if we don't have at least one counter */
if (!drvdata->nr_cntr)
- goto out;
+ return -EINVAL;
/* Find a counter that hasn't been initialised */
for (ctridx = 0; ctridx < drvdata->nr_cntr; ctridx++)
@@ -663,15 +680,19 @@ static int etm4_config_timestamp_event(struct etmv4_drvdata *drvdata)
/* All the counters have been configured already, bail out */
if (ctridx == drvdata->nr_cntr) {
pr_debug("%s: no available counter found\n", __func__);
- ret = -ENOSPC;
- goto out;
+ return -ENOSPC;
}
/*
- * Searching for an available resource selector to use, starting at
- * '2' since every implementation has at least 2 resource selector.
- * ETMIDR4 gives the number of resource selector _pairs_,
- * hence multiply by 2.
+ * Searching for an available resource selector to use, starting at '2'
+ * since resource 0 is the fixed 'always returns false' resource and 1
+ * is the fixed 'always returns true' resource. See IHI0064H_b '7.3.64
+ * TRCRSCTLRn, Resource Selection Control Registers, n=2-31'. If there
+ * are no resources, there would also be no counters so wouldn't get
+ * here.
+ *
+ * ETMIDR4 gives the number of resource selector _pairs_, hence multiply
+ * by 2.
*/
for (rselector = 2; rselector < drvdata->nr_resource * 2; rselector++)
if (!config->res_ctrl[rselector])
@@ -680,40 +701,47 @@ static int etm4_config_timestamp_event(struct etmv4_drvdata *drvdata)
if (rselector == drvdata->nr_resource * 2) {
pr_debug("%s: no available resource selector found\n",
__func__);
- ret = -ENOSPC;
- goto out;
+ return -ENOSPC;
}
- /* Remember what counter we used */
- counter = 1 << ctridx;
+ /* Initialise original and reload counter value. */
+ config->cntr_val[ctridx] = config->cntrldvr[ctridx] = 1 << (ts_level - 1);
/*
- * Initialise original and reload counter value to the smallest
- * possible value in order to get as much precision as we can.
+ * Trace Counter Control Register TRCCNTCTLRn
+ *
+ * CNTCHAIN = 0, don't reload on the previous counter
+ * RLDSELF = true, reload counter automatically on underflow
+ * RLDEVENT = RES_SEL_FALSE (0), reload on single false resource (never reload)
+ * CNTEVENT = RES_SEL_TRUE (1), count single fixed 'always true' resource (always decrement)
*/
- config->cntr_val[ctridx] = 1;
- config->cntrldvr[ctridx] = 1;
-
- /* Set the trace counter control register */
- val = 0x1 << 16 | /* Bit 16, reload counter automatically */
- 0x0 << 7 | /* Select single resource selector */
- 0x1; /* Resource selector 1, i.e always true */
-
- config->cntr_ctrl[ctridx] = val;
+ config->cntr_ctrl[ctridx] = TRCCNTCTLRn_RLDSELF |
+ FIELD_PREP(TRCCNTCTLRn_RLDEVENT_MASK,
+ etm4_res_sel_single(ETM4_RES_SEL_FALSE)) |
+ FIELD_PREP(TRCCNTCTLRn_CNTEVENT_MASK,
+ etm4_res_sel_single(ETM4_RES_SEL_TRUE));
- val = 0x2 << 16 | /* Group 0b0010 - Counter and sequencers */
- counter << 0; /* Counter to use */
-
- config->res_ctrl[rselector] = val;
-
- val = 0x0 << 7 | /* Select single resource selector */
- rselector; /* Resource selector */
+ /*
+ * Resource Selection Control Register TRCRSCTLRn
+ *
+ * PAIRINV = 0, INV = 0, don't invert
+ * GROUP = 2, SELECT = ctridx, trigger when counter 'ctridx' reaches 0
+ *
+ * Multiple counters can be selected, and each bit signifies a counter,
+ * so set bit 'ctridx' to select our counter.
+ */
+ config->res_ctrl[rselector] = FIELD_PREP(TRCRSCTLRn_GROUP_MASK, 2) |
+ FIELD_PREP(TRCRSCTLRn_SELECT_MASK, 1 << ctridx);
- config->ts_ctrl = val;
+ /*
+ * Global Timestamp Control Register TRCTSCTLR
+ *
+ * EVENT = generate timestamp on single resource 'rselector'
+ */
+ config->ts_ctrl = FIELD_PREP(TRCTSCTLR_EVENT_MASK,
+ etm4_res_sel_single(rselector));
- ret = 0;
-out:
- return ret;
+ return 0;
}
static int etm4_parse_event_config(struct coresight_device *csdev,
@@ -722,9 +750,13 @@ static int etm4_parse_event_config(struct coresight_device *csdev,
int ret = 0;
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
struct etmv4_config *config = &drvdata->config;
+ struct perf_event_attr max_timestamp = {
+ .ATTR_CFG_FLD_timestamp_CFG = U64_MAX,
+ };
struct perf_event_attr *attr = &event->attr;
unsigned long cfg_hash;
int preset, cc_threshold;
+ u8 ts_level;
/* Clear configuration from previous run */
memset(config, 0, sizeof(struct etmv4_config));
@@ -750,47 +782,51 @@ static int etm4_parse_event_config(struct coresight_device *csdev,
goto out;
/* Go from generic option to ETMv4 specifics */
- if (attr->config & BIT(ETM_OPT_CYCACC)) {
+ if (ATTR_CFG_GET_FLD(attr, cycacc)) {
config->cfg |= TRCCONFIGR_CCI;
/* TRM: Must program this for cycacc to work */
- cc_threshold = attr->config3 & ETM_CYC_THRESHOLD_MASK;
+ cc_threshold = ATTR_CFG_GET_FLD(attr, cc_threshold);
if (!cc_threshold)
cc_threshold = ETM_CYC_THRESHOLD_DEFAULT;
if (cc_threshold < drvdata->ccitmin)
cc_threshold = drvdata->ccitmin;
config->ccctlr = cc_threshold;
}
- if (attr->config & BIT(ETM_OPT_TS)) {
- /*
- * Configure timestamps to be emitted at regular intervals in
- * order to correlate instructions executed on different CPUs
- * (CPU-wide trace scenarios).
- */
- ret = etm4_config_timestamp_event(drvdata);
+ ts_level = max(ATTR_CFG_GET_FLD(attr, timestamp),
+ ATTR_CFG_GET_FLD(attr, deprecated_timestamp));
+ if (ts_level) {
/*
- * No need to go further if timestamp intervals can't
- * be configured.
+ * Don't do counter generated timestamps when ts_level == MAX.
+ * Leave only SYNC timestamps from TRCCONFIGR_TS.
*/
- if (ret)
- goto out;
+ if (ts_level != ATTR_CFG_GET_FLD(&max_timestamp, timestamp)) {
+ ret = etm4_config_timestamp_event(drvdata, ts_level);
+
+ /*
+ * Error if user asked for timestamps but there was no
+ * free counter.
+ */
+ if (ret)
+ goto out;
+ }
/* bit[11], Global timestamp tracing bit */
config->cfg |= TRCCONFIGR_TS;
}
/* Only trace contextID when runs in root PID namespace */
- if ((attr->config & BIT(ETM_OPT_CTXTID)) &&
+ if (ATTR_CFG_GET_FLD(attr, contextid1) &&
task_is_in_init_pid_ns(current))
/* bit[6], Context ID tracing bit */
config->cfg |= TRCCONFIGR_CID;
/*
- * If set bit ETM_OPT_CTXTID2 in perf config, this asks to trace VMID
- * for recording CONTEXTIDR_EL2. Do not enable VMID tracing if the
- * kernel is not running in EL2.
+ * If set bit contextid2 in perf config, this asks to trace VMID for
+ * recording CONTEXTIDR_EL2. Do not enable VMID tracing if the kernel
+ * is not running in EL2.
*/
- if (attr->config & BIT(ETM_OPT_CTXTID2)) {
+ if (ATTR_CFG_GET_FLD(attr, contextid2)) {
if (!is_kernel_in_hyp_mode()) {
ret = -EINVAL;
goto out;
@@ -801,26 +837,22 @@ static int etm4_parse_event_config(struct coresight_device *csdev,
}
/* return stack - enable if selected and supported */
- if ((attr->config & BIT(ETM_OPT_RETSTK)) && drvdata->retstack)
+ if (ATTR_CFG_GET_FLD(attr, retstack) && drvdata->retstack)
/* bit[12], Return stack enable bit */
config->cfg |= TRCCONFIGR_RS;
/*
- * Set any selected configuration and preset.
- *
- * This extracts the values of PMU_FORMAT_ATTR(configid) and PMU_FORMAT_ATTR(preset)
- * in the perf attributes defined in coresight-etm-perf.c.
- * configid uses bits 63:32 of attr->config2, preset uses bits 3:0 of attr->config.
- * A zero configid means no configuration active, preset = 0 means no preset selected.
+ * Set any selected configuration and preset. A zero configid means no
+ * configuration active, preset = 0 means no preset selected.
*/
- if (attr->config2 & GENMASK_ULL(63, 32)) {
- cfg_hash = (u32)(attr->config2 >> 32);
- preset = attr->config & 0xF;
+ cfg_hash = ATTR_CFG_GET_FLD(attr, configid);
+ if (cfg_hash) {
+ preset = ATTR_CFG_GET_FLD(attr, preset);
ret = cscfg_csdev_enable_active_config(csdev, cfg_hash, preset);
}
/* branch broadcast - enable if selected and supported */
- if (attr->config & BIT(ETM_OPT_BRANCH_BROADCAST)) {
+ if (ATTR_CFG_GET_FLD(attr, branch_broadcast)) {
if (!drvdata->trcbb) {
/*
* Missing BB support could cause silent decode errors
@@ -829,7 +861,7 @@ static int etm4_parse_event_config(struct coresight_device *csdev,
ret = -EINVAL;
goto out;
} else {
- config->cfg |= BIT(ETM4_CFG_BIT_BB);
+ config->cfg |= TRCCONFIGR_BB;
}
}
@@ -1053,11 +1085,8 @@ static int etm4_disable_perf(struct coresight_device *csdev,
return -EINVAL;
etm4_disable_hw(drvdata);
- /*
- * The config_id occupies bits 63:32 of the config2 perf event attr
- * field. If this is non-zero then we will have enabled a config.
- */
- if (attr->config2 & GENMASK_ULL(63, 32))
+ /* If configid is non-zero then we will have enabled a config. */
+ if (ATTR_CFG_GET_FLD(attr, configid))
cscfg_csdev_disable_active_config(csdev);
/*
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h
index 012c52fd1933..89d81ce4e04e 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.h
+++ b/drivers/hwtracing/coresight/coresight-etm4x.h
@@ -225,6 +225,50 @@
#define TRCRSCTLRn_GROUP_MASK GENMASK(19, 16)
#define TRCRSCTLRn_SELECT_MASK GENMASK(15, 0)
+#define TRCCNTCTLRn_CNTCHAIN BIT(17)
+#define TRCCNTCTLRn_RLDSELF BIT(16)
+#define TRCCNTCTLRn_RLDEVENT_MASK GENMASK(15, 8)
+#define TRCCNTCTLRn_CNTEVENT_MASK GENMASK(7, 0)
+
+#define TRCTSCTLR_EVENT_MASK GENMASK(7, 0)
+
+#define ETM4_RES_SEL_FALSE 0 /* Fixed function 'always false' resource selector */
+#define ETM4_RES_SEL_TRUE 1 /* Fixed function 'always true' resource selector */
+
+#define ETM4_RES_SEL_SINGLE_MASK GENMASK(4, 0)
+#define ETM4_RES_SEL_PAIR_MASK GENMASK(3, 0)
+#define ETM4_RES_SEL_TYPE_PAIR BIT(7)
+
+/*
+ * Utilities for programming EVENT resource selectors, e.g. TRCCNTCTLRn_RLDEVENT.
+ *
+ * Resource selectors have a common format across registers:
+ *
+ * 7 6 5 4 0
+ * +------+------+-------+
+ * | TYPE | RES0 | SEL |
+ * +------+------+-------+
+ *
+ * Where TYPE indicates whether the selector is for a single event or a pair.
+ * When TYPE is pair, SEL is 4 bits wide and using pair 0 is UNPREDICTABLE.
+ * Otherwise for single it's 5 bits wide.
+ */
+static inline u32 etm4_res_sel_single(u8 res_sel_idx)
+{
+ WARN_ON_ONCE(!FIELD_FIT(ETM4_RES_SEL_SINGLE_MASK, res_sel_idx));
+ return FIELD_PREP(ETM4_RES_SEL_SINGLE_MASK, res_sel_idx);
+}
+
+static inline u32 etm4_res_sel_pair(u8 res_sel_idx)
+{
+ if (__builtin_constant_p(res_sel_idx))
+ BUILD_BUG_ON(res_sel_idx == 0);
+ WARN_ON_ONCE(!FIELD_FIT(ETM4_RES_SEL_PAIR_MASK, res_sel_idx) ||
+ (res_sel_idx == 0));
+ return FIELD_PREP(ETM4_RES_SEL_PAIR_MASK, res_sel_idx) |
+ ETM4_RES_SEL_TYPE_PAIR;
+}
+
/*
* System instructions to access ETM registers.
* See ETMv4.4 spec ARM IHI0064F section 4.3.6 System instructions
@@ -824,8 +868,7 @@ struct etmv4_config {
u32 eventctrl0;
u32 eventctrl1;
u32 stall_ctrl;
- u32 ts_ctrl;
- u32 syncfreq;
+ u32 ts_ctrl; /* TRCTSCTLR */
u32 ccctlr;
u32 bb_ctrl;
u32 vinst_ctrl;
@@ -833,15 +876,16 @@ struct etmv4_config {
u32 vissctlr;
u32 vipcssctlr;
u8 seq_idx;
+ u8 syncfreq;
u32 seq_ctrl[ETM_MAX_SEQ_STATES];
u32 seq_rst;
u32 seq_state;
u8 cntr_idx;
- u32 cntrldvr[ETMv4_MAX_CNTR];
- u32 cntr_ctrl[ETMv4_MAX_CNTR];
- u32 cntr_val[ETMv4_MAX_CNTR];
+ u32 cntrldvr[ETMv4_MAX_CNTR]; /* TRCCNTRLDVRn */
+ u32 cntr_ctrl[ETMv4_MAX_CNTR]; /* TRCCNTCTLRn */
+ u32 cntr_val[ETMv4_MAX_CNTR]; /* TRCCNTVRn */
u8 res_idx;
- u32 res_ctrl[ETM_MAX_RES_SEL];
+ u32 res_ctrl[ETM_MAX_RES_SEL]; /* TRCRSCTLRn */
u8 ss_idx;
u32 ss_ctrl[ETM_MAX_SS_CMP];
u32 ss_status[ETM_MAX_SS_CMP];
@@ -1016,27 +1060,27 @@ struct etmv4_drvdata {
u8 ns_ex_level;
u8 q_support;
u8 os_lock_model;
- bool sticky_enable;
- bool boot_enable;
- bool os_unlock;
- bool instrp0;
- bool q_filt;
- bool trcbb;
- bool trccond;
- bool retstack;
- bool trccci;
- bool trc_error;
- bool syncpr;
- bool stallctl;
- bool sysstall;
- bool nooverflow;
- bool atbtrig;
- bool lpoverride;
+ bool sticky_enable : 1;
+ bool boot_enable : 1;
+ bool os_unlock : 1;
+ bool instrp0 : 1;
+ bool q_filt : 1;
+ bool trcbb : 1;
+ bool trccond : 1;
+ bool retstack : 1;
+ bool trccci : 1;
+ bool trc_error : 1;
+ bool syncpr : 1;
+ bool stallctl : 1;
+ bool sysstall : 1;
+ bool nooverflow : 1;
+ bool atbtrig : 1;
+ bool lpoverride : 1;
+ bool skip_power_up : 1;
+ bool paused : 1;
u64 trfcr;
struct etmv4_config config;
struct etmv4_save_state *save_state;
- bool skip_power_up;
- bool paused;
DECLARE_BITMAP(arch_features, ETM4_IMPDEF_FEATURE_MAX);
};
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index e0d83ee01b77..cee82e52c4ea 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -1307,6 +1307,19 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
/*
+ * Since the sysfs buffer allocation and the hardware enablement is not
+ * in the same critical region, it's possible to race with the perf.
+ */
+ if (coresight_get_mode(csdev) == CS_MODE_PERF) {
+ drvdata->sysfs_buf = NULL;
+ raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+ /* Free allocated memory out side of the spinlock */
+ tmc_etr_free_sysfs_buf(sysfs_buf);
+ return -EBUSY;
+ }
+
+ /*
* In sysFS mode we can have multiple writers per sink. Since this
* sink is already enabled no memory is needed and the HW need not be
* touched, even if the buffer size has changed.
@@ -1354,9 +1367,7 @@ EXPORT_SYMBOL_GPL(tmc_etr_get_buffer);
/*
* alloc_etr_buf: Allocate ETR buffer for use by perf.
- * The size of the hardware buffer is dependent on the size configured
- * via sysfs and the perf ring buffer size. We prefer to allocate the
- * largest possible size, scaling down the size by half until it
+ * Allocate the largest possible size, scaling down the size by half until it
* reaches a minimum limit (1M), beyond which we give up.
*/
static struct etr_buf *
@@ -1365,36 +1376,26 @@ alloc_etr_buf(struct tmc_drvdata *drvdata, struct perf_event *event,
{
int node;
struct etr_buf *etr_buf;
- unsigned long size;
+ ssize_t size;
node = (event->cpu == -1) ? NUMA_NO_NODE : cpu_to_node(event->cpu);
- /*
- * Try to match the perf ring buffer size if it is larger
- * than the size requested via sysfs.
- */
- if ((nr_pages << PAGE_SHIFT) > drvdata->size) {
- etr_buf = tmc_alloc_etr_buf(drvdata, ((ssize_t)nr_pages << PAGE_SHIFT),
- 0, node, NULL);
- if (!IS_ERR(etr_buf))
- goto done;
- }
+
+ /* Use the minimum limit if the required size is smaller */
+ size = nr_pages << PAGE_SHIFT;
+ size = max_t(ssize_t, size, TMC_ETR_PERF_MIN_BUF_SIZE);
/*
- * Else switch to configured size for this ETR
- * and scale down until we hit the minimum limit.
+ * Try to allocate the required size for this ETR, if failed scale
+ * down until we hit the minimum limit.
*/
- size = drvdata->size;
do {
etr_buf = tmc_alloc_etr_buf(drvdata, size, 0, node, NULL);
if (!IS_ERR(etr_buf))
- goto done;
+ return etr_buf;
size /= 2;
} while (size >= TMC_ETR_PERF_MIN_BUF_SIZE);
return ERR_PTR(-ENOMEM);
-
-done:
- return etr_buf;
}
static struct etr_buf *
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 95473d131032..319a354ede9f 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -221,6 +221,7 @@ struct tmc_resrv_buf {
* @pid: Process ID of the process that owns the session that is using
* this component. For example this would be the pid of the Perf
* process.
+ * @reading: buffer's in the reading through "/dev/xyz.tmc" entry
* @stop_on_flush: Stop on flush trigger user configuration.
* @buf: Snapshot of the trace data for ETF/ETB.
* @etr_buf: details of buffer used in TMC-ETR
@@ -233,6 +234,7 @@ struct tmc_resrv_buf {
* @trigger_cntr: amount of words to store after a trigger.
* @etr_caps: Bitmask of capabilities of the TMC ETR, inferred from the
* device configuration register (DEVID)
+ * @etr_mode: User preferred mode of the ETR device, default auto mode.
* @idr: Holds etr_bufs allocated for this ETR.
* @idr_mutex: Access serialisation for idr.
* @sysfs_buf: SYSFS buffer for ETR.
diff --git a/drivers/hwtracing/coresight/coresight-tnoc.c b/drivers/hwtracing/coresight/coresight-tnoc.c
index ff9a0a9cfe96..1128612e70a7 100644
--- a/drivers/hwtracing/coresight/coresight-tnoc.c
+++ b/drivers/hwtracing/coresight/coresight-tnoc.c
@@ -34,6 +34,7 @@
* @base: memory mapped base address for this component.
* @dev: device node for trace_noc_drvdata.
* @csdev: component vitals needed by the framework.
+ * @pclk: APB clock if present, otherwise NULL
* @spinlock: serialize enable/disable operation.
* @atid: id for the trace packet.
*/
@@ -41,8 +42,9 @@ struct trace_noc_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
+ struct clk *pclk;
spinlock_t spinlock;
- u32 atid;
+ int atid;
};
DEFINE_CORESIGHT_DEVLIST(trace_noc_devs, "traceNoc");
@@ -51,6 +53,12 @@ static void trace_noc_enable_hw(struct trace_noc_drvdata *drvdata)
{
u32 val;
+ /* No valid ATID, simply enable the unit */
+ if (drvdata->atid == -EOPNOTSUPP) {
+ writel(TRACE_NOC_CTRL_PORTEN, drvdata->base + TRACE_NOC_CTRL);
+ return;
+ }
+
/* Set ATID */
writel_relaxed(drvdata->atid, drvdata->base + TRACE_NOC_XLD);
@@ -124,6 +132,11 @@ static int trace_noc_init_default_data(struct trace_noc_drvdata *drvdata)
{
int atid;
+ if (!dev_is_amba(drvdata->dev)) {
+ drvdata->atid = -EOPNOTSUPP;
+ return 0;
+ }
+
atid = coresight_trace_id_get_system_id();
if (atid < 0)
return atid;
@@ -149,8 +162,21 @@ static struct attribute *coresight_tnoc_attrs[] = {
NULL,
};
+static umode_t trace_id_is_visible(struct kobject *kobj,
+ struct attribute *attr, int idx)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct trace_noc_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+ if (attr == &dev_attr_traceid.attr && drvdata->atid < 0)
+ return 0;
+
+ return attr->mode;
+}
+
static const struct attribute_group coresight_tnoc_group = {
.attrs = coresight_tnoc_attrs,
+ .is_visible = trace_id_is_visible,
};
static const struct attribute_group *coresight_tnoc_groups[] = {
@@ -158,9 +184,8 @@ static const struct attribute_group *coresight_tnoc_groups[] = {
NULL,
};
-static int trace_noc_probe(struct amba_device *adev, const struct amba_id *id)
+static int _tnoc_probe(struct device *dev, struct resource *res)
{
- struct device *dev = &adev->dev;
struct coresight_platform_data *pdata;
struct trace_noc_drvdata *drvdata;
struct coresight_desc desc = { 0 };
@@ -173,16 +198,20 @@ static int trace_noc_probe(struct amba_device *adev, const struct amba_id *id)
pdata = coresight_get_platform_data(dev);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
- adev->dev.platform_data = pdata;
+ dev->platform_data = pdata;
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
- drvdata->dev = &adev->dev;
+ drvdata->dev = dev;
dev_set_drvdata(dev, drvdata);
- drvdata->base = devm_ioremap_resource(dev, &adev->res);
+ ret = coresight_get_enable_clocks(dev, &drvdata->pclk, NULL);
+ if (ret)
+ return ret;
+
+ drvdata->base = devm_ioremap_resource(dev, res);
if (IS_ERR(drvdata->base))
return PTR_ERR(drvdata->base);
@@ -195,20 +224,31 @@ static int trace_noc_probe(struct amba_device *adev, const struct amba_id *id)
desc.ops = &trace_noc_cs_ops;
desc.type = CORESIGHT_DEV_TYPE_LINK;
desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG;
- desc.pdata = adev->dev.platform_data;
- desc.dev = &adev->dev;
+ desc.pdata = pdata;
+ desc.dev = dev;
desc.access = CSDEV_ACCESS_IOMEM(drvdata->base);
desc.groups = coresight_tnoc_groups;
drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev)) {
- coresight_trace_id_put_system_id(drvdata->atid);
+ if (drvdata->atid > 0)
+ coresight_trace_id_put_system_id(drvdata->atid);
return PTR_ERR(drvdata->csdev);
}
- pm_runtime_put(&adev->dev);
return 0;
}
+static int trace_noc_probe(struct amba_device *adev, const struct amba_id *id)
+{
+ int ret;
+
+ ret = _tnoc_probe(&adev->dev, &adev->res);
+ if (!ret)
+ pm_runtime_put(&adev->dev);
+
+ return ret;
+}
+
static void trace_noc_remove(struct amba_device *adev)
{
struct trace_noc_drvdata *drvdata = dev_get_drvdata(&adev->dev);
@@ -240,7 +280,81 @@ static struct amba_driver trace_noc_driver = {
.id_table = trace_noc_ids,
};
-module_amba_driver(trace_noc_driver);
+static int itnoc_probe(struct platform_device *pdev)
+{
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ int ret;
+
+ pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = _tnoc_probe(&pdev->dev, res);
+ pm_runtime_put(&pdev->dev);
+ if (ret)
+ pm_runtime_disable(&pdev->dev);
+
+ return ret;
+}
+
+static void itnoc_remove(struct platform_device *pdev)
+{
+ struct trace_noc_drvdata *drvdata = platform_get_drvdata(pdev);
+
+ coresight_unregister(drvdata->csdev);
+ pm_runtime_disable(&pdev->dev);
+}
+
+#ifdef CONFIG_PM
+static int itnoc_runtime_suspend(struct device *dev)
+{
+ struct trace_noc_drvdata *drvdata = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(drvdata->pclk);
+
+ return 0;
+}
+
+static int itnoc_runtime_resume(struct device *dev)
+{
+ struct trace_noc_drvdata *drvdata = dev_get_drvdata(dev);
+
+ return clk_prepare_enable(drvdata->pclk);
+}
+#endif
+
+static const struct dev_pm_ops itnoc_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(itnoc_runtime_suspend, itnoc_runtime_resume, NULL)
+};
+
+static const struct of_device_id itnoc_of_match[] = {
+ { .compatible = "qcom,coresight-itnoc" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, itnoc_of_match);
+
+static struct platform_driver itnoc_driver = {
+ .probe = itnoc_probe,
+ .remove = itnoc_remove,
+ .driver = {
+ .name = "coresight-itnoc",
+ .of_match_table = itnoc_of_match,
+ .suppress_bind_attrs = true,
+ .pm = &itnoc_dev_pm_ops,
+ },
+};
+
+static int __init tnoc_init(void)
+{
+ return coresight_init_driver("tnoc", &trace_noc_driver, &itnoc_driver, THIS_MODULE);
+}
+
+static void __exit tnoc_exit(void)
+{
+ coresight_remove_driver(&trace_noc_driver, &itnoc_driver);
+}
+module_init(tnoc_init);
+module_exit(tnoc_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Trace NOC driver");
diff --git a/drivers/hwtracing/coresight/coresight-tpda.c b/drivers/hwtracing/coresight/coresight-tpda.c
index 3a3825d27f86..7055f8f13427 100644
--- a/drivers/hwtracing/coresight/coresight-tpda.c
+++ b/drivers/hwtracing/coresight/coresight-tpda.c
@@ -137,12 +137,46 @@ static int tpda_get_element_size(struct tpda_drvdata *drvdata,
/* Settings pre enabling port control register */
static void tpda_enable_pre_port(struct tpda_drvdata *drvdata)
{
- u32 val;
+ u32 val = 0;
- val = readl_relaxed(drvdata->base + TPDA_CR);
- val &= ~TPDA_CR_ATID;
val |= FIELD_PREP(TPDA_CR_ATID, drvdata->atid);
+ if (drvdata->trig_async)
+ val |= TPDA_CR_SRIE;
+
+ if (drvdata->trig_flag_ts)
+ val |= TPDA_CR_FLRIE;
+
+ if (drvdata->trig_freq)
+ val |= TPDA_CR_FRIE;
+
+ if (drvdata->freq_ts)
+ val |= TPDA_CR_FREQTS;
+
+ if (drvdata->cmbchan_mode)
+ val |= TPDA_CR_CMBCHANMODE;
+
writel_relaxed(val, drvdata->base + TPDA_CR);
+
+ /*
+ * If FLRIE bit is set, set the master and channel
+ * id as zero
+ */
+ if (drvdata->trig_flag_ts)
+ writel_relaxed(0x0, drvdata->base + TPDA_FPID_CR);
+
+ /* Initialize with a value of 0 */
+ val = 0;
+ if (drvdata->syncr_mode)
+ val |= TPDA_SYNCR_MODE_CTRL_MASK;
+
+ if (drvdata->syncr_count > 0 &&
+ drvdata->syncr_count < TPDA_SYNCR_COUNT_MASK)
+ val |= drvdata->syncr_count;
+ else
+ /* Program the count to its MAX value by default */
+ val |= TPDA_SYNCR_COUNT_MASK;
+
+ writel_relaxed(val, drvdata->base + TPDA_SYNCR);
}
static int tpda_enable_port(struct tpda_drvdata *drvdata, int port)
@@ -258,6 +292,248 @@ static const struct coresight_ops tpda_cs_ops = {
.link_ops = &tpda_link_ops,
};
+/* Read cross-trigger register member */
+static ssize_t tpda_trig_sysfs_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct tpda_trig_sysfs_attribute *tpda_attr =
+ container_of(attr, struct tpda_trig_sysfs_attribute, attr);
+ struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+ guard(spinlock)(&drvdata->spinlock);
+ switch (tpda_attr->mem) {
+ case FREQTS:
+ return sysfs_emit(buf, "%u\n", (unsigned int)drvdata->freq_ts);
+ case FRIE:
+ return sysfs_emit(buf, "%u\n", (unsigned int)drvdata->trig_freq);
+ case FLRIE:
+ return sysfs_emit(buf, "%u\n", (unsigned int)drvdata->trig_flag_ts);
+ case SRIE:
+ return sysfs_emit(buf, "%u\n", (unsigned int)drvdata->trig_async);
+ case CMBCHANMODE:
+ return sysfs_emit(buf, "%u\n", (unsigned int)drvdata->cmbchan_mode);
+
+ }
+ return -EINVAL;
+}
+
+static ssize_t tpda_trig_sysfs_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t size)
+{
+ struct tpda_trig_sysfs_attribute *tpda_attr =
+ container_of(attr, struct tpda_trig_sysfs_attribute, attr);
+ struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long val;
+
+ if (kstrtoul(buf, 0, &val))
+ return -EINVAL;
+
+ guard(spinlock)(&drvdata->spinlock);
+ switch (tpda_attr->mem) {
+ case FREQTS:
+ drvdata->freq_ts = !!val;
+ break;
+ case FRIE:
+ drvdata->trig_freq = !!val;
+ break;
+ case FLRIE:
+ drvdata->trig_flag_ts = !!val;
+ break;
+ case SRIE:
+ drvdata->trig_async = !!val;
+ break;
+ case CMBCHANMODE:
+ drvdata->cmbchan_mode = !!val;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return size;
+}
+
+static ssize_t global_flush_req_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long val;
+
+ if (!drvdata->csdev->refcnt)
+ return -EINVAL;
+
+ guard(spinlock)(&drvdata->spinlock);
+ val = readl_relaxed(drvdata->base + TPDA_CR);
+ /* read global_flush_req bit */
+ val &= TPDA_CR_FLREQ;
+
+ return sysfs_emit(buf, "%lu\n", val);
+}
+
+static ssize_t global_flush_req_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t size)
+{
+ struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long val;
+
+ if (kstrtoul(buf, 0, &val))
+ return -EINVAL;
+
+ if (!drvdata->csdev->refcnt || !val)
+ return -EINVAL;
+
+ guard(spinlock)(&drvdata->spinlock);
+ val = readl_relaxed(drvdata->base + TPDA_CR);
+ /* set global_flush_req bit */
+ val |= TPDA_CR_FLREQ;
+ CS_UNLOCK(drvdata->base);
+ writel_relaxed(val, drvdata->base + TPDA_CR);
+ CS_LOCK(drvdata->base);
+
+ return size;
+}
+static DEVICE_ATTR_RW(global_flush_req);
+
+static ssize_t syncr_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long val, syncr_val;
+
+ if (!drvdata->csdev->refcnt)
+ return -EINVAL;
+
+ guard(spinlock)(&drvdata->spinlock);
+ syncr_val = readl_relaxed(drvdata->base + TPDA_SYNCR);
+ val = FIELD_GET(TPDA_SYNCR_MODE_CTRL_MASK, syncr_val);
+
+ return sysfs_emit(buf, "%lu\n", val);
+}
+
+static ssize_t syncr_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t size)
+{
+ struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long val;
+
+ if (kstrtoul(buf, 0, &val))
+ return -EINVAL;
+
+ guard(spinlock)(&drvdata->spinlock);
+ /* set the mode when first enabling the device */
+ drvdata->syncr_mode = !!val;
+
+ return size;
+}
+static DEVICE_ATTR_RW(syncr_mode);
+
+static ssize_t syncr_count_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long val;
+
+ if (!drvdata->csdev->refcnt)
+ return -EINVAL;
+
+ guard(spinlock)(&drvdata->spinlock);
+ val = readl_relaxed(drvdata->base + TPDA_SYNCR);
+ val &= TPDA_SYNCR_COUNT_MASK;
+
+ return sysfs_emit(buf, "%lu\n", val);
+}
+
+static ssize_t syncr_count_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t size)
+{
+ struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long val;
+
+ if (kstrtoul(buf, 0, &val))
+ return -EINVAL;
+
+ if (val > TPDA_SYNCR_COUNT_MASK)
+ return -EINVAL;
+
+ guard(spinlock)(&drvdata->spinlock);
+ drvdata->syncr_count = val;
+
+ return size;
+}
+static DEVICE_ATTR_RW(syncr_count);
+
+static ssize_t port_flush_req_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ unsigned long val;
+
+ if (!drvdata->csdev->refcnt)
+ return -EINVAL;
+
+ guard(spinlock)(&drvdata->spinlock);
+ val = readl_relaxed(drvdata->base + TPDA_FLUSH_CR);
+
+ return sysfs_emit(buf, "0x%lx\n", val);
+}
+
+static ssize_t port_flush_req_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t size)
+{
+ struct tpda_drvdata *drvdata = dev_get_drvdata(dev->parent);
+ u32 val;
+
+ if (kstrtou32(buf, 0, &val))
+ return -EINVAL;
+
+ if (!drvdata->csdev->refcnt || !val)
+ return -EINVAL;
+
+ guard(spinlock)(&drvdata->spinlock);
+ CS_UNLOCK(drvdata->base);
+ writel_relaxed(val, drvdata->base + TPDA_FLUSH_CR);
+ CS_LOCK(drvdata->base);
+
+ return size;
+}
+static DEVICE_ATTR_RW(port_flush_req);
+
+static struct attribute *tpda_attrs[] = {
+ &dev_attr_global_flush_req.attr,
+ &dev_attr_syncr_mode.attr,
+ &dev_attr_syncr_count.attr,
+ &dev_attr_port_flush_req.attr,
+ tpda_trig_sysfs_rw(freq_ts_enable, FREQTS),
+ tpda_trig_sysfs_rw(trig_freq_enable, FRIE),
+ tpda_trig_sysfs_rw(trig_flag_ts_enable, FLRIE),
+ tpda_trig_sysfs_rw(trig_async_enable, SRIE),
+ tpda_trig_sysfs_rw(cmbchan_mode, CMBCHANMODE),
+ NULL,
+};
+
+static struct attribute_group tpda_attr_grp = {
+ .attrs = tpda_attrs,
+};
+
+static const struct attribute_group *tpda_attr_grps[] = {
+ &tpda_attr_grp,
+ NULL,
+};
+
static int tpda_init_default_data(struct tpda_drvdata *drvdata)
{
int atid;
@@ -273,6 +549,7 @@ static int tpda_init_default_data(struct tpda_drvdata *drvdata)
return atid;
drvdata->atid = atid;
+ drvdata->freq_ts = true;
return 0;
}
@@ -316,6 +593,7 @@ static int tpda_probe(struct amba_device *adev, const struct amba_id *id)
desc.ops = &tpda_cs_ops;
desc.pdata = adev->dev.platform_data;
desc.dev = &adev->dev;
+ desc.groups = tpda_attr_grps;
desc.access = CSDEV_ACCESS_IOMEM(base);
drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev))
diff --git a/drivers/hwtracing/coresight/coresight-tpda.h b/drivers/hwtracing/coresight/coresight-tpda.h
index c6af3d2da3ef..a090352009bb 100644
--- a/drivers/hwtracing/coresight/coresight-tpda.h
+++ b/drivers/hwtracing/coresight/coresight-tpda.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
- * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2023,2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef _CORESIGHT_CORESIGHT_TPDA_H
@@ -8,6 +8,29 @@
#define TPDA_CR (0x000)
#define TPDA_Pn_CR(n) (0x004 + (n * 4))
+#define TPDA_FPID_CR (0x084)
+#define TPDA_SYNCR (0x08C)
+#define TPDA_FLUSH_CR (0x090)
+
+/* Cross trigger global (all ports) flush request bit */
+#define TPDA_CR_FLREQ BIT(0)
+/* Cross trigger FREQ packets timestamp bit */
+#define TPDA_CR_FREQTS BIT(2)
+/* Cross trigger FREQ packet request bit */
+#define TPDA_CR_FRIE BIT(3)
+/* Cross trigger FLAG packet request interface bit */
+#define TPDA_CR_FLRIE BIT(4)
+/* Cross trigger synchronization bit */
+#define TPDA_CR_SRIE BIT(5)
+/* Bits 6 ~ 12 is for atid value */
+#define TPDA_CR_ATID GENMASK(12, 6)
+/*
+ * Channel mode bit of the packetization of CMB/MCB traffic
+ * 0 - raw channel mapping mode
+ * 1 - channel pair marking mode
+ */
+#define TPDA_CR_CMBCHANMODE BIT(20)
+
/* Aggregator port enable bit */
#define TPDA_Pn_CR_ENA BIT(0)
/* Aggregator port CMB data set element size bit */
@@ -15,10 +38,12 @@
/* Aggregator port DSB data set element size bit */
#define TPDA_Pn_CR_DSBSIZE BIT(8)
-#define TPDA_MAX_INPORTS 32
+/* TPDA_SYNCR count mask */
+#define TPDA_SYNCR_COUNT_MASK GENMASK(11, 0)
+/* TPDA_SYNCR mode control bit */
+#define TPDA_SYNCR_MODE_CTRL_MASK GENMASK(12, 12)
-/* Bits 6 ~ 12 is for atid value */
-#define TPDA_CR_ATID GENMASK(12, 6)
+#define TPDA_MAX_INPORTS 32
/**
* struct tpda_drvdata - specifics associated to an TPDA component
@@ -29,6 +54,13 @@
* @enable: enable status of the component.
* @dsb_esize Record the DSB element size.
* @cmb_esize Record the CMB element size.
+ * @trig_async: Enable/disable cross trigger synchronization sequence interface.
+ * @trig_flag_ts: Enable/disable cross trigger FLAG packet request interface.
+ * @trig_freq: Enable/disable cross trigger FREQ packet request interface.
+ * @freq_ts: Enable/disable the timestamp for all FREQ packets.
+ * @cmbchan_mode: Configure the CMB/MCMB channel mode.
+ * @syncr_mode: Setting the mode for counting packets.
+ * @syncr_count: Setting the value of the count.
*/
struct tpda_drvdata {
void __iomem *base;
@@ -38,6 +70,42 @@ struct tpda_drvdata {
u8 atid;
u32 dsb_esize;
u32 cmb_esize;
+ bool trig_async;
+ bool trig_flag_ts;
+ bool trig_freq;
+ bool freq_ts;
+ bool cmbchan_mode;
+ bool syncr_mode;
+ u32 syncr_count;
+};
+
+/* Enumerate members of global control register(cr) */
+enum tpda_cr_mem {
+ FREQTS,
+ FRIE,
+ FLRIE,
+ SRIE,
+ CMBCHANMODE
+};
+
+/**
+ * struct tpda_trig_sysfs_attribute - Record the member variables of cross
+ * trigger register that need to be operated by sysfs file
+ * @attr: The device attribute
+ * @mem: The member in the control register data structure
+ */
+struct tpda_trig_sysfs_attribute {
+ struct device_attribute attr;
+ enum tpda_cr_mem mem;
};
+#define tpda_trig_sysfs_rw(name, mem) \
+ (&((struct tpda_trig_sysfs_attribute[]) { \
+ { \
+ __ATTR(name, 0644, tpda_trig_sysfs_show, \
+ tpda_trig_sysfs_store), \
+ mem, \
+ } \
+ })[0].attr.attr)
+
#endif /* _CORESIGHT_CORESIGHT_TPDA_H */