diff options
Diffstat (limited to 'drivers/hwtracing')
-rw-r--r-- | drivers/hwtracing/coresight/coresight-etm-perf.c | 31 | ||||
-rw-r--r-- | drivers/hwtracing/coresight/coresight-etm.h | 5 | ||||
-rw-r--r-- | drivers/hwtracing/coresight/coresight-etm3x-sysfs.c | 12 | ||||
-rw-r--r-- | drivers/hwtracing/coresight/coresight-priv.h | 4 | ||||
-rw-r--r-- | drivers/hwtracing/coresight/coresight-stm.c | 9 | ||||
-rw-r--r-- | drivers/hwtracing/coresight/coresight-tmc-etf.c | 48 | ||||
-rw-r--r-- | drivers/hwtracing/coresight/coresight-tmc-etr.c | 43 | ||||
-rw-r--r-- | drivers/hwtracing/coresight/coresight-tmc.h | 2 | ||||
-rw-r--r-- | drivers/hwtracing/coresight/coresight.c | 74 | ||||
-rw-r--r-- | drivers/hwtracing/intel_th/core.c | 28 | ||||
-rw-r--r-- | drivers/hwtracing/intel_th/gth.c | 26 | ||||
-rw-r--r-- | drivers/hwtracing/intel_th/intel_th.h | 4 | ||||
-rw-r--r-- | drivers/hwtracing/stm/core.c | 8 |
13 files changed, 196 insertions, 98 deletions
diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 2cd7c718198a..17741969026e 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -202,6 +202,21 @@ static void *etm_setup_aux(int event_cpu, void **pages, if (!event_data) return NULL; + /* + * In theory nothing prevent tracers in a trace session from being + * associated with different sinks, nor having a sink per tracer. But + * until we have HW with this kind of topology we need to assume tracers + * in a trace session are using the same sink. Therefore go through + * the coresight bus and pick the first enabled sink. + * + * When operated from sysFS users are responsible to enable the sink + * while from perf, the perf tools will do it based on the choice made + * on the cmd line. As such the "enable_sink" flag in sysFS is reset. + */ + sink = coresight_get_enabled_sink(true); + if (!sink) + goto err; + INIT_WORK(&event_data->work, free_event_data); mask = &event_data->mask; @@ -219,25 +234,11 @@ static void *etm_setup_aux(int event_cpu, void **pages, * list of devices from source to sink that can be * referenced later when the path is actually needed. */ - event_data->path[cpu] = coresight_build_path(csdev); + event_data->path[cpu] = coresight_build_path(csdev, sink); if (IS_ERR(event_data->path[cpu])) goto err; } - /* - * In theory nothing prevent tracers in a trace session from being - * associated with different sinks, nor having a sink per tracer. But - * until we have HW with this kind of topology and a way to convey - * sink assignement from the perf cmd line we need to assume tracers - * in a trace session are using the same sink. Therefore pick the sink - * found at the end of the first available path. - */ - cpu = cpumask_first(mask); - /* Grab the sink at the end of the path */ - sink = coresight_get_sink(event_data->path[cpu]); - if (!sink) - goto err; - if (!sink_ops(sink)->alloc_buffer) goto err; diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index 4a18ee499965..ad063d7444e1 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h @@ -89,11 +89,13 @@ /* ETMCR - 0x00 */ #define ETMCR_PWD_DWN BIT(0) #define ETMCR_STALL_MODE BIT(7) +#define ETMCR_BRANCH_BROADCAST BIT(8) #define ETMCR_ETM_PRG BIT(10) #define ETMCR_ETM_EN BIT(11) #define ETMCR_CYC_ACC BIT(12) #define ETMCR_CTXID_SIZE (BIT(14)|BIT(15)) #define ETMCR_TIMESTAMP_EN BIT(28) +#define ETMCR_RETURN_STACK BIT(29) /* ETMCCR - 0x04 */ #define ETMCCR_FIFOFULL BIT(23) /* ETMPDCR - 0x310 */ @@ -110,8 +112,11 @@ #define ETM_MODE_STALL BIT(2) #define ETM_MODE_TIMESTAMP BIT(3) #define ETM_MODE_CTXID BIT(4) +#define ETM_MODE_BBROAD BIT(5) +#define ETM_MODE_RET_STACK BIT(6) #define ETM_MODE_ALL (ETM_MODE_EXCLUDE | ETM_MODE_CYCACC | \ ETM_MODE_STALL | ETM_MODE_TIMESTAMP | \ + ETM_MODE_BBROAD | ETM_MODE_RET_STACK | \ ETM_MODE_CTXID | ETM_MODE_EXCL_KERN | \ ETM_MODE_EXCL_USER) diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index e9b071953f80..ca98ad13bb8c 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -146,7 +146,7 @@ static ssize_t mode_store(struct device *dev, goto err_unlock; } config->ctrl |= ETMCR_STALL_MODE; - } else + } else config->ctrl &= ~ETMCR_STALL_MODE; if (config->mode & ETM_MODE_TIMESTAMP) { @@ -164,6 +164,16 @@ static ssize_t mode_store(struct device *dev, else config->ctrl &= ~ETMCR_CTXID_SIZE; + if (config->mode & ETM_MODE_BBROAD) + config->ctrl |= ETMCR_BRANCH_BROADCAST; + else + config->ctrl &= ~ETMCR_BRANCH_BROADCAST; + + if (config->mode & ETM_MODE_RET_STACK) + config->ctrl |= ETMCR_RETURN_STACK; + else + config->ctrl &= ~ETMCR_RETURN_STACK; + if (config->mode & (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER)) etm_config_trace_mode(config); diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 196a14be4b3d..ef9d8e93e3b2 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -111,7 +111,9 @@ static inline void CS_UNLOCK(void __iomem *addr) void coresight_disable_path(struct list_head *path); int coresight_enable_path(struct list_head *path, u32 mode); struct coresight_device *coresight_get_sink(struct list_head *path); -struct list_head *coresight_build_path(struct coresight_device *csdev); +struct coresight_device *coresight_get_enabled_sink(bool reset); +struct list_head *coresight_build_path(struct coresight_device *csdev, + struct coresight_device *sink); void coresight_release_path(struct list_head *path); #ifdef CONFIG_CORESIGHT_SOURCE_ETM3X diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index 49e0f1b925a5..944c17b48d23 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -419,10 +419,10 @@ static ssize_t stm_generic_packet(struct stm_data *stm_data, struct stm_drvdata, stm); if (!(drvdata && local_read(&drvdata->mode))) - return 0; + return -EACCES; if (channel >= drvdata->numsp) - return 0; + return -EINVAL; ch_addr = (unsigned long)stm_channel_addr(drvdata, channel); @@ -920,6 +920,11 @@ static struct amba_id stm_ids[] = { .mask = 0x0003ffff, .data = "STM32", }, + { + .id = 0x0003b963, + .mask = 0x0003ffff, + .data = "STM500", + }, { 0, 0}, }; diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index d6941ea24d8d..1549436e2492 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -70,7 +70,7 @@ static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata) * When operating in sysFS mode the content of the buffer needs to be * read before the TMC is disabled. */ - if (local_read(&drvdata->mode) == CS_MODE_SYSFS) + if (drvdata->mode == CS_MODE_SYSFS) tmc_etb_dump_hw(drvdata); tmc_disable_hw(drvdata); @@ -103,19 +103,14 @@ static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata) CS_LOCK(drvdata->base); } -static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev, u32 mode) +static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev) { int ret = 0; bool used = false; char *buf = NULL; - long val; unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - /* This shouldn't be happening */ - if (WARN_ON(mode != CS_MODE_SYSFS)) - return -EINVAL; - /* * If we don't have a buffer release the lock and allocate memory. * Otherwise keep the lock and move along. @@ -138,13 +133,12 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev, u32 mode) goto out; } - val = local_xchg(&drvdata->mode, mode); /* * 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. */ - if (val == CS_MODE_SYSFS) + if (drvdata->mode == CS_MODE_SYSFS) goto out; /* @@ -163,6 +157,7 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev, u32 mode) drvdata->buf = buf; } + drvdata->mode = CS_MODE_SYSFS; tmc_etb_enable_hw(drvdata); out: spin_unlock_irqrestore(&drvdata->spinlock, flags); @@ -177,34 +172,29 @@ out: return ret; } -static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, u32 mode) +static int tmc_enable_etf_sink_perf(struct coresight_device *csdev) { int ret = 0; - long val; unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - /* This shouldn't be happening */ - if (WARN_ON(mode != CS_MODE_PERF)) - return -EINVAL; - spin_lock_irqsave(&drvdata->spinlock, flags); if (drvdata->reading) { ret = -EINVAL; goto out; } - val = local_xchg(&drvdata->mode, mode); /* * In Perf mode there can be only one writer per sink. There * is also no need to continue if the ETB/ETR is already operated * from sysFS. */ - if (val != CS_MODE_DISABLED) { + if (drvdata->mode != CS_MODE_DISABLED) { ret = -EINVAL; goto out; } + drvdata->mode = CS_MODE_PERF; tmc_etb_enable_hw(drvdata); out: spin_unlock_irqrestore(&drvdata->spinlock, flags); @@ -216,9 +206,9 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode) { switch (mode) { case CS_MODE_SYSFS: - return tmc_enable_etf_sink_sysfs(csdev, mode); + return tmc_enable_etf_sink_sysfs(csdev); case CS_MODE_PERF: - return tmc_enable_etf_sink_perf(csdev, mode); + return tmc_enable_etf_sink_perf(csdev); } /* We shouldn't be here */ @@ -227,7 +217,6 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode) static void tmc_disable_etf_sink(struct coresight_device *csdev) { - long val; unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -237,10 +226,11 @@ static void tmc_disable_etf_sink(struct coresight_device *csdev) return; } - val = local_xchg(&drvdata->mode, CS_MODE_DISABLED); /* Disable the TMC only if it needs to */ - if (val != CS_MODE_DISABLED) + if (drvdata->mode != CS_MODE_DISABLED) { tmc_etb_disable_hw(drvdata); + drvdata->mode = CS_MODE_DISABLED; + } spin_unlock_irqrestore(&drvdata->spinlock, flags); @@ -260,7 +250,7 @@ static int tmc_enable_etf_link(struct coresight_device *csdev, } tmc_etf_enable_hw(drvdata); - local_set(&drvdata->mode, CS_MODE_SYSFS); + drvdata->mode = CS_MODE_SYSFS; spin_unlock_irqrestore(&drvdata->spinlock, flags); dev_info(drvdata->dev, "TMC-ETF enabled\n"); @@ -280,7 +270,7 @@ static void tmc_disable_etf_link(struct coresight_device *csdev, } tmc_etf_disable_hw(drvdata); - local_set(&drvdata->mode, CS_MODE_DISABLED); + drvdata->mode = CS_MODE_DISABLED; spin_unlock_irqrestore(&drvdata->spinlock, flags); dev_info(drvdata->dev, "TMC disabled\n"); @@ -383,7 +373,7 @@ static void tmc_update_etf_buffer(struct coresight_device *csdev, return; /* This shouldn't happen */ - if (WARN_ON_ONCE(local_read(&drvdata->mode) != CS_MODE_PERF)) + if (WARN_ON_ONCE(drvdata->mode != CS_MODE_PERF)) return; CS_UNLOCK(drvdata->base); @@ -504,7 +494,6 @@ const struct coresight_ops tmc_etf_cs_ops = { int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) { - long val; enum tmc_mode mode; int ret = 0; unsigned long flags; @@ -528,9 +517,8 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) goto out; } - val = local_read(&drvdata->mode); /* Don't interfere if operated from Perf */ - if (val == CS_MODE_PERF) { + if (drvdata->mode == CS_MODE_PERF) { ret = -EINVAL; goto out; } @@ -542,7 +530,7 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) } /* Disable the TMC if need be */ - if (val == CS_MODE_SYSFS) + if (drvdata->mode == CS_MODE_SYSFS) tmc_etb_disable_hw(drvdata); drvdata->reading = true; @@ -573,7 +561,7 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata) } /* Re-enable the TMC if need be */ - if (local_read(&drvdata->mode) == CS_MODE_SYSFS) { + if (drvdata->mode == CS_MODE_SYSFS) { /* * The trace run will continue with the same allocated trace * buffer. As such zero-out the buffer so that we don't end diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 886ea83c68e0..5d312699b3b9 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -86,26 +86,22 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) * When operating in sysFS mode the content of the buffer needs to be * read before the TMC is disabled. */ - if (local_read(&drvdata->mode) == CS_MODE_SYSFS) + if (drvdata->mode == CS_MODE_SYSFS) tmc_etr_dump_hw(drvdata); tmc_disable_hw(drvdata); CS_LOCK(drvdata->base); } -static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev, u32 mode) +static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev) { int ret = 0; bool used = false; - long val; unsigned long flags; void __iomem *vaddr = NULL; dma_addr_t paddr; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - /* This shouldn't be happening */ - if (WARN_ON(mode != CS_MODE_SYSFS)) - return -EINVAL; /* * If we don't have a buffer release the lock and allocate memory. @@ -134,13 +130,12 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev, u32 mode) goto out; } - val = local_xchg(&drvdata->mode, mode); /* * 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. */ - if (val == CS_MODE_SYSFS) + if (drvdata->mode == CS_MODE_SYSFS) goto out; /* @@ -155,8 +150,7 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev, u32 mode) drvdata->buf = drvdata->vaddr; } - memset(drvdata->vaddr, 0, drvdata->size); - + drvdata->mode = CS_MODE_SYSFS; tmc_etr_enable_hw(drvdata); out: spin_unlock_irqrestore(&drvdata->spinlock, flags); @@ -171,34 +165,29 @@ out: return ret; } -static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, u32 mode) +static int tmc_enable_etr_sink_perf(struct coresight_device *csdev) { int ret = 0; - long val; unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - /* This shouldn't be happening */ - if (WARN_ON(mode != CS_MODE_PERF)) - return -EINVAL; - spin_lock_irqsave(&drvdata->spinlock, flags); if (drvdata->reading) { ret = -EINVAL; goto out; } - val = local_xchg(&drvdata->mode, mode); /* * In Perf mode there can be only one writer per sink. There * is also no need to continue if the ETR is already operated * from sysFS. */ - if (val != CS_MODE_DISABLED) { + if (drvdata->mode != CS_MODE_DISABLED) { ret = -EINVAL; goto out; } + drvdata->mode = CS_MODE_PERF; tmc_etr_enable_hw(drvdata); out: spin_unlock_irqrestore(&drvdata->spinlock, flags); @@ -210,9 +199,9 @@ static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode) { switch (mode) { case CS_MODE_SYSFS: - return tmc_enable_etr_sink_sysfs(csdev, mode); + return tmc_enable_etr_sink_sysfs(csdev); case CS_MODE_PERF: - return tmc_enable_etr_sink_perf(csdev, mode); + return tmc_enable_etr_sink_perf(csdev); } /* We shouldn't be here */ @@ -221,7 +210,6 @@ static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode) static void tmc_disable_etr_sink(struct coresight_device *csdev) { - long val; unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -231,10 +219,11 @@ static void tmc_disable_etr_sink(struct coresight_device *csdev) return; } - val = local_xchg(&drvdata->mode, CS_MODE_DISABLED); /* Disable the TMC only if it needs to */ - if (val != CS_MODE_DISABLED) + if (drvdata->mode != CS_MODE_DISABLED) { tmc_etr_disable_hw(drvdata); + drvdata->mode = CS_MODE_DISABLED; + } spin_unlock_irqrestore(&drvdata->spinlock, flags); @@ -253,7 +242,6 @@ const struct coresight_ops tmc_etr_cs_ops = { int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) { int ret = 0; - long val; unsigned long flags; /* config types are set a boot time and never change */ @@ -266,9 +254,8 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) goto out; } - val = local_read(&drvdata->mode); /* Don't interfere if operated from Perf */ - if (val == CS_MODE_PERF) { + if (drvdata->mode == CS_MODE_PERF) { ret = -EINVAL; goto out; } @@ -280,7 +267,7 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) } /* Disable the TMC if need be */ - if (val == CS_MODE_SYSFS) + if (drvdata->mode == CS_MODE_SYSFS) tmc_etr_disable_hw(drvdata); drvdata->reading = true; @@ -303,7 +290,7 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) spin_lock_irqsave(&drvdata->spinlock, flags); /* RE-enable the TMC if need be */ - if (local_read(&drvdata->mode) == CS_MODE_SYSFS) { + if (drvdata->mode == CS_MODE_SYSFS) { /* * The trace run will continue with the same allocated trace * buffer. The trace buffer is cleared in tmc_etr_enable_hw(), diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 44b3ae346118..51c01851533e 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -117,7 +117,7 @@ struct tmc_drvdata { void __iomem *vaddr; u32 size; u32 len; - local_t mode; + u32 mode; enum tmc_config_type config_type; enum tmc_mem_intf_width memwidth; u32 trigger_cntr; diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 7bf00a0beb6f..0c37356e417c 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -368,6 +368,52 @@ struct coresight_device *coresight_get_sink(struct list_head *path) return csdev; } +static int coresight_enabled_sink(struct device *dev, void *data) +{ + bool *reset = data; + struct coresight_device *csdev = to_coresight_device(dev); + + if ((csdev->type == CORESIGHT_DEV_TYPE_SINK || + csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) && + csdev->activated) { + /* + * Now that we have a handle on the sink for this session, + * disable the sysFS "enable_sink" flag so that possible + * concurrent perf session that wish to use another sink don't + * trip on it. Doing so has no ramification for the current + * session. + */ + if (*reset) + csdev->activated = false; + + return 1; + } + + return 0; +} + +/** + * coresight_get_enabled_sink - returns the first enabled sink found on the bus + * @deactivate: Whether the 'enable_sink' flag should be reset + * + * When operated from perf the deactivate parameter should be set to 'true'. + * That way the "enabled_sink" flag of the sink that was selected can be reset, + * allowing for other concurrent perf sessions to choose a different sink. + * + * When operated from sysFS users have full control and as such the deactivate + * parameter should be set to 'false', hence mandating users to explicitly + * clear the flag. + */ +struct coresight_device *coresight_get_enabled_sink(bool deactivate) +{ + struct device *dev = NULL; + + dev = bus_find_device(&coresight_bustype, NULL, &deactivate, + coresight_enabled_sink); + + return dev ? to_coresight_device(dev) : NULL; +} + /** * _coresight_build_path - recursively build a path from a @csdev to a sink. * @csdev: The device to start from. @@ -380,6 +426,7 @@ struct coresight_device *coresight_get_sink(struct list_head *path) * last one. */ static int _coresight_build_path(struct coresight_device *csdev, + struct coresight_device *sink, struct list_head *path) { int i; @@ -387,15 +434,15 @@ static int _coresight_build_path(struct coresight_device *csdev, struct coresight_node *node; /* An activated sink has been found. Enqueue the element */ - if ((csdev->type == CORESIGHT_DEV_TYPE_SINK || - csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) && csdev->activated) + if (csdev == sink) goto out; /* Not a sink - recursively explore each port found on this element */ for (i = 0; i < csdev->nr_outport; i++) { struct coresight_device *child_dev = csdev->conns[i].child_dev; - if (child_dev && _coresight_build_path(child_dev, path) == 0) { + if (child_dev && + _coresight_build_path(child_dev, sink, path) == 0) { found = true; break; } @@ -422,18 +469,22 @@ out: return 0; } -struct list_head *coresight_build_path(struct coresight_device *csdev) +struct list_head *coresight_build_path(struct coresight_device *source, + struct coresight_device *sink) { struct list_head *path; int rc; + if (!sink) + return ERR_PTR(-EINVAL); + path = kzalloc(sizeof(struct list_head), GFP_KERNEL); if (!path) return ERR_PTR(-ENOMEM); INIT_LIST_HEAD(path); - rc = _coresight_build_path(csdev, path); + rc = _coresight_build_path(source, sink, path); if (rc) { kfree(path); return ERR_PTR(rc); @@ -497,6 +548,7 @@ static int coresight_validate_source(struct coresight_device *csdev, int coresight_enable(struct coresight_device *csdev) { int cpu, ret = 0; + struct coresight_device *sink; struct list_head *path; mutex_lock(&coresight_mutex); @@ -508,7 +560,17 @@ int coresight_enable(struct coresight_device *csdev) if (csdev->enable) goto out; - path = coresight_build_path(csdev); + /* + * Search for a valid sink for this session but don't reset the + * "enable_sink" flag in sysFS. Users get to do that explicitly. + */ + sink = coresight_get_enabled_sink(false); + if (!sink) { + ret = -EINVAL; + goto out; + } + + path = coresight_build_path(csdev, sink); if (IS_ERR(path)) { pr_err("building path(s) failed\n"); ret = PTR_ERR(path); diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index 6f0a51a2c6ec..cdd9b3b26195 100644 --- a/drivers/hwtracing/intel_th/core.c +++ b/drivers/hwtracing/intel_th/core.c @@ -29,6 +29,9 @@ #include "intel_th.h" #include "debug.h" +static bool host_mode __read_mostly; +module_param(host_mode, bool, 0444); + static DEFINE_IDA(intel_th_ida); static int intel_th_match(struct device *dev, struct device_driver *driver) @@ -380,7 +383,7 @@ static void intel_th_device_free(struct intel_th_device *thdev) /* * Intel(R) Trace Hub subdevices */ -static struct intel_th_subdevice { +static const struct intel_th_subdevice { const char *name; struct resource res[3]; unsigned nres; @@ -527,14 +530,19 @@ static int intel_th_populate(struct intel_th *th, struct resource *devres, { struct resource res[3]; unsigned int req = 0; - int i, err; + int src, dst, err; /* create devices for each intel_th_subdevice */ - for (i = 0; i < ARRAY_SIZE(intel_th_subdevices); i++) { - struct intel_th_subdevice *subdev = &intel_th_subdevices[i]; + for (src = 0, dst = 0; src < ARRAY_SIZE(intel_th_subdevices); src++) { + const struct intel_th_subdevice *subdev = + &intel_th_subdevices[src]; struct intel_th_device *thdev; int r; + /* only allow SOURCE and SWITCH devices in host mode */ + if (host_mode && subdev->type == INTEL_TH_OUTPUT) + continue; + thdev = intel_th_device_alloc(th, subdev->type, subdev->name, subdev->id); if (!thdev) { @@ -577,10 +585,12 @@ static int intel_th_populate(struct intel_th *th, struct resource *devres, } if (subdev->type == INTEL_TH_OUTPUT) { - thdev->dev.devt = MKDEV(th->major, i); + thdev->dev.devt = MKDEV(th->major, dst); thdev->output.type = subdev->otype; thdev->output.port = -1; thdev->output.scratchpad = subdev->scrpd; + } else if (subdev->type == INTEL_TH_SWITCH) { + thdev->host_mode = host_mode; } err = device_add(&thdev->dev); @@ -597,14 +607,14 @@ static int intel_th_populate(struct intel_th *th, struct resource *devres, req++; } - th->thdev[i] = thdev; + th->thdev[dst++] = thdev; } return 0; kill_subdevs: - for (i-- ; i >= 0; i--) - intel_th_device_remove(th->thdev[i]); + for (; dst >= 0; dst--) + intel_th_device_remove(th->thdev[dst]); return err; } @@ -717,7 +727,7 @@ void intel_th_free(struct intel_th *th) intel_th_request_hub_module_flush(th); for (i = 0; i < TH_SUBDEVICE_MAX; i++) - if (th->thdev[i] != th->hub) + if (th->thdev[i] && th->thdev[i] != th->hub) intel_th_device_remove(th->thdev[i]); intel_th_device_remove(th->hub); diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c index 33e09369a491..dd32d0bad687 100644 --- a/drivers/hwtracing/intel_th/gth.c +++ b/drivers/hwtracing/intel_th/gth.c @@ -564,6 +564,9 @@ static int intel_th_gth_assign(struct intel_th_device *thdev, struct gth_device *gth = dev_get_drvdata(&thdev->dev); int i, id; + if (thdev->host_mode) + return -EBUSY; + if (othdev->type != INTEL_TH_OUTPUT) return -EINVAL; @@ -600,6 +603,9 @@ static void intel_th_gth_unassign(struct intel_th_device *thdev, struct gth_device *gth = dev_get_drvdata(&thdev->dev); int port = othdev->output.port; + if (thdev->host_mode) + return; + spin_lock(>h->gth_lock); othdev->output.port = -1; othdev->output.active = false; @@ -654,9 +660,24 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) gth->base = base; spin_lock_init(>h->gth_lock); + /* + * Host mode can be signalled via SW means or via SCRPD_DEBUGGER_IN_USE + * bit. Either way, don't reset HW in this case, and don't export any + * capture configuration attributes. Also, refuse to assign output + * drivers to ports, see intel_th_gth_assign(). + */ + if (thdev->host_mode) + goto done; + ret = intel_th_gth_reset(gth); - if (ret) - return ret; + if (ret) { + if (ret != -EBUSY) + return ret; + + thdev->host_mode = true; + + goto done; + } for (i = 0; i < TH_CONFIGURABLE_MASTERS + 1; i++) gth->master[i] = -1; @@ -677,6 +698,7 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) return -ENOMEM; } +done: dev_set_drvdata(dev, gth); return 0; diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h index 4c195786bf1f..3096e7054f6d 100644 --- a/drivers/hwtracing/intel_th/intel_th.h +++ b/drivers/hwtracing/intel_th/intel_th.h @@ -54,6 +54,7 @@ struct intel_th_output { * @num_resources: number of resources in @resource array * @type: INTEL_TH_{SOURCE,OUTPUT,SWITCH} * @id: device instance or -1 + * @host_mode: Intel TH is controlled by an external debug host * @output: output descriptor for INTEL_TH_OUTPUT devices * @name: device name to match the driver */ @@ -64,6 +65,9 @@ struct intel_th_device { unsigned int type; int id; + /* INTEL_TH_SWITCH specific */ + bool host_mode; + /* INTEL_TH_OUTPUT specific */ struct intel_th_output output; diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 51f81d64ca37..a6ea387b5b00 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -361,7 +361,7 @@ static int stm_char_open(struct inode *inode, struct file *file) struct stm_file *stmf; struct device *dev; unsigned int major = imajor(inode); - int err = -ENODEV; + int err = -ENOMEM; dev = class_find_device(&stm_class, NULL, &major, major_match); if (!dev) @@ -369,8 +369,9 @@ static int stm_char_open(struct inode *inode, struct file *file) stmf = kzalloc(sizeof(*stmf), GFP_KERNEL); if (!stmf) - return -ENOMEM; + goto err_put_device; + err = -ENODEV; stm_output_init(&stmf->output); stmf->stm = to_stm_device(dev); @@ -382,9 +383,10 @@ static int stm_char_open(struct inode *inode, struct file *file) return nonseekable_open(inode, file); err_free: + kfree(stmf); +err_put_device: /* matches class_find_device() above */ put_device(dev); - kfree(stmf); return err; } |