A stop event is triggered by an instruction address match on a comparator. This address match event is then used to stop the tracing across all ETM source devices with the help of external input/output signals and ECT.
The event flow diagram is like this, Address comparator --> EXT OUT --> ECT---> EXT IN --> Counter-->ViewInst
Since the External input is a pulse, a counter has been used to convert that into a level trigger. So the counter initialized to 1, starts counting upon EXT IN trigger and becomes active when it reaches 0. Also the counter resource is programmed such a way that ViewInst is inactive when the counter is active and viceversa.
This feature would be useful in stopping trace on all trace sources when a kernel panic gets triggered for example without software intervention.
This feature can be enabled and configured on a ETM source by, echo 1 > /sys/bus/coresight/devices/<etmxx>/sync_stop_event echo <address> > /sys/bus/coresight/devices/<etmxx>/sync_stop_address
Signed-off-by: Linu Cherian lcherian@marvell.com --- .../coresight/coresight-etm4x-core.c | 167 ++++++++++++++++++ .../coresight/coresight-etm4x-sysfs.c | 64 +++++++ drivers/hwtracing/coresight/coresight-etm4x.h | 16 ++ 3 files changed, 247 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index e24252eaf8e4..73c57e03da2c 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -60,6 +60,8 @@ static void etm4_set_default_config(struct etmv4_config *config); static int etm4_set_event_filters(struct etmv4_drvdata *drvdata, struct perf_event *event); static u64 etm4_get_access_type(struct etmv4_config *config); +static u64 etm4_get_comparator_access_type(struct etmv4_config *config); +static int etm4_get_next_comparator(struct etmv4_drvdata *drvdata, u32 type);
static enum cpuhp_state hp_online;
@@ -329,6 +331,146 @@ static void etm4_check_arch_features(struct etmv4_drvdata *drvdata, } #endif /* CONFIG_ETM4X_IMPDEF_FEATURE */
+static int etm4_get_next_counter(struct etmv4_drvdata *drvdata) +{ + int ctridx; + struct etmv4_config *config = &drvdata->config; + + /* find a counter that hasn't been initialised */ + for (ctridx = 0; ctridx < drvdata->nr_cntr; ctridx++) + if (config->cntr_val[ctridx] == 0) + break; + + /* all the counters have been configured already, bail out */ + if (ctridx == drvdata->nr_cntr) { + pr_debug("%s: no available counter found\n", __func__); + return -ENOSPC; + } + + return ctridx; +} + +static int etm4_get_next_rselector(struct etmv4_drvdata *drvdata) +{ + int rselector; + struct etmv4_config *config = &drvdata->config; + + for (rselector = 2; rselector < drvdata->nr_resource * 2; rselector++) + if (!config->res_ctrl[rselector]) + break; + + if (rselector == drvdata->nr_resource * 2) { + pr_debug("%s: no available resource selector found\n", + __func__); + return -ENOSPC; + } + + return rselector; +} + +static int etm4_rselector_config(struct etmv4_drvdata *drvdata, int resgrp, + int grpidx, bool inv) +{ + int rselector; + struct etmv4_config *config = &drvdata->config; + + rselector = etm4_get_next_rselector(drvdata); + if (rselector < 0) + goto out; + + config->res_ctrl[rselector] = resgrp << 16 | BIT(grpidx); + if (inv) + config->res_ctrl[rselector] |= 1 << 20; + +out: + return rselector; +} + +/* Trigger external output based on address comparator match event */ +static int etm4_trigger_extout_on_addrmatch(struct etmv4_drvdata *drvdata, + u64 address) +{ + int rselector, err = 0; + int comparator, type; + struct etmv4_config *config = &drvdata->config; + u64 access_type = etm4_get_comparator_access_type(config); + + type = ETM_ADDR_TYPE_STOP; + + /* Get a free single comparator */ + comparator = etm4_get_next_comparator(drvdata, type); + if (comparator < 0) { + err = comparator; + goto out; + } + + /* Configure the comparator */ + config->addr_val[comparator] = address; + config->addr_acc[comparator] = access_type; + config->addr_type[comparator] = type; + + /* Find a resorce selector to map this comparator */ + rselector = etm4_rselector_config(drvdata, ETM_RESGRP_SADDRCMP, + comparator, false); + if (!rselector) { + err = rselector; + goto out; + } + + /* Now finally connect external output with comparator */ + config->eventctrl0 = 0x0 << 7 | rselector; + +out: + return err; +} + + +/* Stop ViewInst function based on external input trigger */ +static int etm4_stop_viewinst_on_extin(struct etmv4_drvdata *drvdata) +{ + int err = 0; + int rselector, counter; + struct etmv4_config *config = &drvdata->config; + + /* Map a rselector to external input #0 */ + rselector = etm4_rselector_config(drvdata, ETM_RESGRP_EXTIN, + ETM_EXTIN_0, false); + if (!rselector) { + err = rselector; + goto out; + } + + /* Drive a down counter based on external input #0 */ + counter = etm4_get_next_counter(drvdata); + if (counter < 0) { + err = counter; + goto out; + } + + config->cntr_val[counter] = 1; /* Initial down counter value */ + config->cntr_ctrl[counter] = 0x0 << 16 | /* Normal mode */ + 0x0 << 7 | /* Select single resource selector */ + rselector; + + /* Map a resource selector to the down counter */ + rselector = etm4_rselector_config(drvdata, ETM_RESGRP_CNTRSEQ, + counter, true); + if (!rselector) { + err = rselector; + goto out; + } + + + /* Finally setup viewinst function */ + config->vinst_ctrl &= 0xffffff00; + config->vinst_ctrl = 0x0 << 7 | rselector; + config->vinst_ctrl |= BIT(9); + config->vinst_ctrl |= (0xb << 16); +out: + return err; +} + + static int etm4_enable_hw(struct etmv4_drvdata *drvdata) { int i, rc; @@ -347,6 +489,18 @@ static int etm4_enable_hw(struct etmv4_drvdata *drvdata) if (rc) goto done;
+ /* Stop viewInst function on address match */ + if (config->sync_stop_event) { + rc = etm4_trigger_extout_on_addrmatch(drvdata, + config->sync_stop_addr); + if (rc) + goto done; + rc = etm4_stop_viewinst_on_extin(drvdata); + if (rc) + goto done; + + } + /* Disable the trace unit before programming trace registers */ etm4x_relaxed_write32(csa, 0, TRCPRGCTLR);
@@ -892,11 +1046,23 @@ static void etm4_disable(struct coresight_device *csdev, local_set(&drvdata->mode, CS_MODE_DISABLED); }
+static int etm4_get_tevent_idx(struct coresight_device *csdev) +{ + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + struct etmv4_config *config = &drvdata->config; + + if (config->sync_stop_event) + return TRACE_EVENT_0; + else + return -1; +} + static const struct coresight_ops_source etm4_source_ops = { .cpu_id = etm4_cpu_id, .trace_id = etm4_trace_id, .enable = etm4_enable, .disable = etm4_disable, + .get_trace_event_idx = etm4_get_tevent_idx, };
static const struct coresight_ops etm4_cs_ops = { @@ -1335,6 +1501,7 @@ static void etm4_set_start_stop_filter(struct etmv4_config *config,
static void etm4_set_default_filter(struct etmv4_config *config) { + /* Trace everything 'default' filter achieved by no filtering */ config->viiectlr = 0x0;
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index a0640fa5c55b..d1fb904c3761 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -522,6 +522,68 @@ static ssize_t event_store(struct device *dev, } static DEVICE_ATTR_RW(event);
+static ssize_t sync_stop_event_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; + + val = config->sync_stop_event; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t sync_stop_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + config->sync_stop_event = val & 0xFF; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(sync_stop_event); + +static ssize_t sync_stop_addr_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; + + val = config->sync_stop_addr; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t sync_stop_addr_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + config->sync_stop_addr = val; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(sync_stop_addr); + static ssize_t event_instren_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -2278,6 +2340,8 @@ static struct attribute *coresight_etmv4_attrs[] = { &dev_attr_mode.attr, &dev_attr_pe.attr, &dev_attr_event.attr, + &dev_attr_sync_stop_event.attr, + &dev_attr_sync_stop_addr.attr, &dev_attr_event_instren.attr, &dev_attr_event_ts.attr, &dev_attr_syncfreq.attr, diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index e5b79bdb9851..4558446a8c04 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -513,6 +513,20 @@ #define ETM_CNTR_MAX_VAL 0xFFFF #define ETM_TRACEID_MASK 0x3f
+/* ETM resource group encoding */ +#define ETM_RESGRP_EXTIN 0x0 +#define ETM_RESGRP_PECMP 0x1 +#define ETM_RESGRP_CNTRSEQ 0x2 +#define ETM_RESGRP_SSCMP 0x3 +#define ETM_RESGRP_SADDRCMP 0x4 +#define ETM_RESGRP_ADDRRANGECMP 0x5 +#define ETM_RESGRP_CIDCMP 0x6 +#define ETM_RESGRP_VCIDCMP 0x7 + +#define ETM_EXTIN_0 0x0 +#define TRACE_EVENT_0 0x0 + + /* ETMv4 programming modes */ #define ETM_MODE_EXCLUDE BIT(0) #define ETM_MODE_LOAD BIT(1) @@ -804,6 +818,8 @@ struct etmv4_config { u32 vmid_mask1; u32 ext_inp; u8 s_ex_level; + u8 sync_stop_event; + u64 sync_stop_addr; };
/**