diff options
Diffstat (limited to 'drivers/hwtracing')
29 files changed, 1013 insertions, 292 deletions
diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index 8d55d6d79015..ef9cb3c164e1 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -70,13 +70,13 @@ config CORESIGHT_SOURCE_ETM4X for instruction level tracing. Depending on the implemented version data tracing may also be available. -config CORESIGHT_QCOM_REPLICATOR - bool "Qualcomm CoreSight Replicator driver" +config CORESIGHT_DYNAMIC_REPLICATOR + bool "CoreSight Programmable Replicator driver" depends on CORESIGHT_LINKS_AND_SINKS help - This enables support for Qualcomm CoreSight link driver. The - programmable ATB replicator sends the ATB trace stream from the - ETB/ETF to the TPIUi and ETR. + This enables support for dynamic CoreSight replicator link driver. + The programmable ATB replicator allows independent filtering of the + trace data based on the traceid. config CORESIGHT_STM bool "CoreSight System Trace Macrocell driver" diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 433d59025eb6..5bae90ce794d 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -14,6 +14,6 @@ obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \ coresight-etm3x-sysfs.o obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o \ coresight-etm4x-sysfs.o -obj-$(CONFIG_CORESIGHT_QCOM_REPLICATOR) += coresight-replicator-qcom.o +obj-$(CONFIG_CORESIGHT_DYNAMIC_REPLICATOR) += coresight-dynamic-replicator.o obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o diff --git a/drivers/hwtracing/coresight/coresight-cpu-debug.c b/drivers/hwtracing/coresight/coresight-cpu-debug.c index 64a77e00eaa6..6ea62c62ff27 100644 --- a/drivers/hwtracing/coresight/coresight-cpu-debug.c +++ b/drivers/hwtracing/coresight/coresight-cpu-debug.c @@ -667,7 +667,7 @@ static int debug_remove(struct amba_device *adev) return 0; } -static struct amba_id debug_ids[] = { +static const struct amba_id debug_ids[] = { { /* Debug for Cortex-A53 */ .id = 0x000bbd03, .mask = 0x000fffff, diff --git a/drivers/hwtracing/coresight/coresight-replicator-qcom.c b/drivers/hwtracing/coresight/coresight-dynamic-replicator.c index 0a3d15f0b009..accc2056f7c6 100644 --- a/drivers/hwtracing/coresight/coresight-replicator-qcom.c +++ b/drivers/hwtracing/coresight/coresight-dynamic-replicator.c @@ -95,6 +95,28 @@ static const struct coresight_ops replicator_cs_ops = { .link_ops = &replicator_link_ops, }; +#define coresight_replicator_reg(name, offset) \ + coresight_simple_reg32(struct replicator_state, name, offset) + +coresight_replicator_reg(idfilter0, REPLICATOR_IDFILTER0); +coresight_replicator_reg(idfilter1, REPLICATOR_IDFILTER1); + +static struct attribute *replicator_mgmt_attrs[] = { + &dev_attr_idfilter0.attr, + &dev_attr_idfilter1.attr, + NULL, +}; + +static const struct attribute_group replicator_mgmt_group = { + .attrs = replicator_mgmt_attrs, + .name = "mgmt", +}; + +static const struct attribute_group *replicator_groups[] = { + &replicator_mgmt_group, + NULL, +}; + static int replicator_probe(struct amba_device *adev, const struct amba_id *id) { int ret; @@ -139,11 +161,11 @@ static int replicator_probe(struct amba_device *adev, const struct amba_id *id) desc.ops = &replicator_cs_ops; desc.pdata = adev->dev.platform_data; desc.dev = &adev->dev; + desc.groups = replicator_groups; drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev); - dev_info(dev, "%s initialized\n", (char *)id->data); return 0; } @@ -175,18 +197,22 @@ static const struct dev_pm_ops replicator_dev_pm_ops = { NULL) }; -static struct amba_id replicator_ids[] = { +static const struct amba_id replicator_ids[] = { { .id = 0x0003b909, .mask = 0x0003ffff, - .data = "REPLICATOR 1.0", + }, + { + /* Coresight SoC-600 */ + .id = 0x000bb9ec, + .mask = 0x000fffff, }, { 0, 0 }, }; static struct amba_driver replicator_driver = { .drv = { - .name = "coresight-replicator-qcom", + .name = "coresight-dynamic-replicator", .pm = &replicator_dev_pm_ops, .suppress_bind_attrs = true, }, diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index d5b96423e1a5..56ecd7aff5eb 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -200,8 +200,10 @@ static void etb_disable_hw(struct etb_drvdata *drvdata) static void etb_dump_hw(struct etb_drvdata *drvdata) { + bool lost = false; int i; u8 *buf_ptr; + const u32 *barrier; u32 read_data, depth; u32 read_ptr, write_ptr; u32 frame_off, frame_endoff; @@ -223,20 +225,26 @@ static void etb_dump_hw(struct etb_drvdata *drvdata) } if ((readl_relaxed(drvdata->base + ETB_STATUS_REG) - & ETB_STATUS_RAM_FULL) == 0) + & ETB_STATUS_RAM_FULL) == 0) { writel_relaxed(0x0, drvdata->base + ETB_RAM_READ_POINTER); - else + } else { writel_relaxed(write_ptr, drvdata->base + ETB_RAM_READ_POINTER); + lost = true; + } depth = drvdata->buffer_depth; buf_ptr = drvdata->buf; + barrier = barrier_pkt; for (i = 0; i < depth; i++) { read_data = readl_relaxed(drvdata->base + ETB_RAM_READ_DATA_REG); - *buf_ptr++ = read_data >> 0; - *buf_ptr++ = read_data >> 8; - *buf_ptr++ = read_data >> 16; - *buf_ptr++ = read_data >> 24; + if (lost && *barrier) { + read_data = *barrier; + barrier++; + } + + *(u32 *)buf_ptr = read_data; + buf_ptr += 4; } if (frame_off) { @@ -353,8 +361,10 @@ static void etb_update_buffer(struct coresight_device *csdev, struct perf_output_handle *handle, void *sink_config) { + bool lost = false; int i, cur; u8 *buf_ptr; + const u32 *barrier; u32 read_ptr, write_ptr, capacity; u32 status, read_data, to_read; unsigned long offset; @@ -366,8 +376,8 @@ static void etb_update_buffer(struct coresight_device *csdev, capacity = drvdata->buffer_depth * ETB_FRAME_SIZE_WORDS; - CS_UNLOCK(drvdata->base); etb_disable_hw(drvdata); + CS_UNLOCK(drvdata->base); /* unit is in words, not bytes */ read_ptr = readl_relaxed(drvdata->base + ETB_RAM_READ_POINTER); @@ -384,7 +394,7 @@ static void etb_update_buffer(struct coresight_device *csdev, (unsigned long)write_ptr); write_ptr &= ~(ETB_FRAME_SIZE_WORDS - 1); - perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); + lost = true; } /* @@ -395,7 +405,7 @@ static void etb_update_buffer(struct coresight_device *csdev, */ status = readl_relaxed(drvdata->base + ETB_STATUS_REG); if (status & ETB_STATUS_RAM_FULL) { - perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); + lost = true; to_read = capacity; read_ptr = write_ptr; } else { @@ -428,22 +438,30 @@ static void etb_update_buffer(struct coresight_device *csdev, if (read_ptr > (drvdata->buffer_depth - 1)) read_ptr -= drvdata->buffer_depth; /* let the decoder know we've skipped ahead */ - perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); + lost = true; } + if (lost) + perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); + /* finally tell HW where we want to start reading from */ writel_relaxed(read_ptr, drvdata->base + ETB_RAM_READ_POINTER); cur = buf->cur; offset = buf->offset; + barrier = barrier_pkt; + for (i = 0; i < to_read; i += 4) { buf_ptr = buf->data_pages[cur] + offset; read_data = readl_relaxed(drvdata->base + ETB_RAM_READ_DATA_REG); - *buf_ptr++ = read_data >> 0; - *buf_ptr++ = read_data >> 8; - *buf_ptr++ = read_data >> 16; - *buf_ptr++ = read_data >> 24; + if (lost && *barrier) { + read_data = *barrier; + barrier++; + } + + *(u32 *)buf_ptr = read_data; + buf_ptr += 4; offset += 4; if (offset >= PAGE_SIZE) { @@ -557,17 +575,17 @@ static const struct file_operations etb_fops = { .llseek = no_llseek, }; -#define coresight_etb10_simple_func(name, offset) \ - coresight_simple_func(struct etb_drvdata, NULL, name, offset) +#define coresight_etb10_reg(name, offset) \ + coresight_simple_reg32(struct etb_drvdata, name, offset) -coresight_etb10_simple_func(rdp, ETB_RAM_DEPTH_REG); -coresight_etb10_simple_func(sts, ETB_STATUS_REG); -coresight_etb10_simple_func(rrp, ETB_RAM_READ_POINTER); -coresight_etb10_simple_func(rwp, ETB_RAM_WRITE_POINTER); -coresight_etb10_simple_func(trg, ETB_TRG); -coresight_etb10_simple_func(ctl, ETB_CTL_REG); -coresight_etb10_simple_func(ffsr, ETB_FFSR); -coresight_etb10_simple_func(ffcr, ETB_FFCR); +coresight_etb10_reg(rdp, ETB_RAM_DEPTH_REG); +coresight_etb10_reg(sts, ETB_STATUS_REG); +coresight_etb10_reg(rrp, ETB_RAM_READ_POINTER); +coresight_etb10_reg(rwp, ETB_RAM_WRITE_POINTER); +coresight_etb10_reg(trg, ETB_TRG); +coresight_etb10_reg(ctl, ETB_CTL_REG); +coresight_etb10_reg(ffsr, ETB_FFSR); +coresight_etb10_reg(ffcr, ETB_FFCR); static struct attribute *coresight_etb_mgmt_attrs[] = { &dev_attr_rdp.attr, @@ -728,7 +746,7 @@ static const struct dev_pm_ops etb_dev_pm_ops = { SET_RUNTIME_PM_OPS(etb_runtime_suspend, etb_runtime_resume, NULL) }; -static struct amba_id etb_ids[] = { +static const struct amba_id etb_ids[] = { { .id = 0x0003b907, .mask = 0x0003ffff, diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 8f546f59a3fd..8a0ad77574e7 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -53,14 +53,16 @@ static DEFINE_PER_CPU(struct coresight_device *, csdev_src); /* ETMv3.5/PTM's ETMCR is 'config' */ PMU_FORMAT_ATTR(cycacc, "config:" __stringify(ETM_OPT_CYCACC)); PMU_FORMAT_ATTR(timestamp, "config:" __stringify(ETM_OPT_TS)); +PMU_FORMAT_ATTR(retstack, "config:" __stringify(ETM_OPT_RETSTK)); static struct attribute *etm_config_formats_attr[] = { &format_attr_cycacc.attr, &format_attr_timestamp.attr, + &format_attr_retstack.attr, NULL, }; -static struct attribute_group etm_pmu_format_group = { +static const struct attribute_group etm_pmu_format_group = { .name = "format", .attrs = etm_config_formats_attr, }; diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index ad063d7444e1..70b0a248c321 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h @@ -106,6 +106,7 @@ #define ETMTECR1_START_STOP BIT(25) /* ETMCCER - 0x1E8 */ #define ETMCCER_TIMESTAMP BIT(22) +#define ETMCCER_RETSTACK BIT(23) #define ETM_MODE_EXCLUDE BIT(0) #define ETM_MODE_CYCACC BIT(1) diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index ca98ad13bb8c..6e547ec6fead 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -1232,19 +1232,19 @@ static struct attribute *coresight_etm_attrs[] = { NULL, }; -#define coresight_etm3x_simple_func(name, offset) \ - coresight_simple_func(struct etm_drvdata, NULL, name, offset) - -coresight_etm3x_simple_func(etmccr, ETMCCR); -coresight_etm3x_simple_func(etmccer, ETMCCER); -coresight_etm3x_simple_func(etmscr, ETMSCR); -coresight_etm3x_simple_func(etmidr, ETMIDR); -coresight_etm3x_simple_func(etmcr, ETMCR); -coresight_etm3x_simple_func(etmtraceidr, ETMTRACEIDR); -coresight_etm3x_simple_func(etmteevr, ETMTEEVR); -coresight_etm3x_simple_func(etmtssvr, ETMTSSCR); -coresight_etm3x_simple_func(etmtecr1, ETMTECR1); -coresight_etm3x_simple_func(etmtecr2, ETMTECR2); +#define coresight_etm3x_reg(name, offset) \ + coresight_simple_reg32(struct etm_drvdata, name, offset) + +coresight_etm3x_reg(etmccr, ETMCCR); +coresight_etm3x_reg(etmccer, ETMCCER); +coresight_etm3x_reg(etmscr, ETMSCR); +coresight_etm3x_reg(etmidr, ETMIDR); +coresight_etm3x_reg(etmcr, ETMCR); +coresight_etm3x_reg(etmtraceidr, ETMTRACEIDR); +coresight_etm3x_reg(etmteevr, ETMTEEVR); +coresight_etm3x_reg(etmtssvr, ETMTSSCR); +coresight_etm3x_reg(etmtecr1, ETMTECR1); +coresight_etm3x_reg(etmtecr2, ETMTECR2); static struct attribute *coresight_etm_mgmt_attrs[] = { &dev_attr_etmccr.attr, diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 93ee8fc539be..e5b1ec57dbde 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -243,6 +243,8 @@ void etm_set_default(struct etm_config *config) } config->ctxid_mask = 0x0; + /* Setting default to 1024 as per TRM recommendation */ + config->sync_freq = 0x400; } void etm_config_trace_mode(struct etm_config *config) @@ -308,7 +310,9 @@ void etm_config_trace_mode(struct etm_config *config) config->addr_type[1] = ETM_ADDR_TYPE_RANGE; } -#define ETM3X_SUPPORTED_OPTIONS (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN) +#define ETM3X_SUPPORTED_OPTIONS (ETMCR_CYC_ACC | \ + ETMCR_TIMESTAMP_EN | \ + ETMCR_RETURN_STACK) static int etm_parse_event_config(struct etm_drvdata *drvdata, struct perf_event *event) @@ -339,14 +343,24 @@ static int etm_parse_event_config(struct etm_drvdata *drvdata, etm_config_trace_mode(config); /* - * At this time only cycle accurate and timestamp options are - * available. + * At this time only cycle accurate, return stack and timestamp + * options are available. */ if (attr->config & ~ETM3X_SUPPORTED_OPTIONS) return -EINVAL; config->ctrl = attr->config; + /* + * 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 + */ + if ((config->ctrl & ETMCR_RETURN_STACK) && + !(drvdata->etmccer & ETMCCER_RETSTACK)) + config->ctrl &= ~ETMCR_RETURN_STACK; + return 0; } @@ -885,7 +899,7 @@ static const struct dev_pm_ops etm_dev_pm_ops = { SET_RUNTIME_PM_OPS(etm_runtime_suspend, etm_runtime_resume, NULL) }; -static struct amba_id etm_ids[] = { +static const struct amba_id etm_ids[] = { { /* ETM 3.3 */ .id = 0x0003b921, .mask = 0x0003ffff, diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index b9b1e9c8f4c4..4e6eab53e34e 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -2066,23 +2066,23 @@ static u32 etmv4_cross_read(const struct device *dev, u32 offset) return reg.data; } -#define coresight_etm4x_simple_func(name, offset) \ - coresight_simple_func(struct etmv4_drvdata, NULL, name, offset) +#define coresight_etm4x_reg(name, offset) \ + coresight_simple_reg32(struct etmv4_drvdata, name, offset) #define coresight_etm4x_cross_read(name, offset) \ coresight_simple_func(struct etmv4_drvdata, etmv4_cross_read, \ name, offset) -coresight_etm4x_simple_func(trcpdcr, TRCPDCR); -coresight_etm4x_simple_func(trcpdsr, TRCPDSR); -coresight_etm4x_simple_func(trclsr, TRCLSR); -coresight_etm4x_simple_func(trcauthstatus, TRCAUTHSTATUS); -coresight_etm4x_simple_func(trcdevid, TRCDEVID); -coresight_etm4x_simple_func(trcdevtype, TRCDEVTYPE); -coresight_etm4x_simple_func(trcpidr0, TRCPIDR0); -coresight_etm4x_simple_func(trcpidr1, TRCPIDR1); -coresight_etm4x_simple_func(trcpidr2, TRCPIDR2); -coresight_etm4x_simple_func(trcpidr3, TRCPIDR3); +coresight_etm4x_reg(trcpdcr, TRCPDCR); +coresight_etm4x_reg(trcpdsr, TRCPDSR); +coresight_etm4x_reg(trclsr, TRCLSR); +coresight_etm4x_reg(trcauthstatus, TRCAUTHSTATUS); +coresight_etm4x_reg(trcdevid, TRCDEVID); +coresight_etm4x_reg(trcdevtype, TRCDEVTYPE); +coresight_etm4x_reg(trcpidr0, TRCPIDR0); +coresight_etm4x_reg(trcpidr1, TRCPIDR1); +coresight_etm4x_reg(trcpidr2, TRCPIDR2); +coresight_etm4x_reg(trcpidr3, TRCPIDR3); coresight_etm4x_cross_read(trcoslsr, TRCOSLSR); coresight_etm4x_cross_read(trcconfig, TRCCONFIGR); coresight_etm4x_cross_read(trctraceid, TRCTRACEIDR); diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 532adc9dd32a..cf364a514c12 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -224,6 +224,10 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, if (attr->config & BIT(ETM_OPT_TS)) /* bit[11], Global timestamp tracing bit */ config->cfg |= BIT(11); + /* return stack - enable if selected and supported */ + if ((attr->config & BIT(ETM_OPT_RETSTK)) && drvdata->retstack) + /* bit[12], Return stack enable bit */ + config->cfg |= BIT(12); out: return ret; @@ -1048,7 +1052,7 @@ err_arch_supported: return ret; } -static struct amba_id etm4_ids[] = { +static const struct amba_id etm4_ids[] = { { /* ETM 4.0 - Cortex-A53 */ .id = 0x000bb95d, .mask = 0x000fffff, diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index 860fe6ef5632..77642e0e955b 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -246,11 +246,16 @@ static const struct dev_pm_ops funnel_dev_pm_ops = { SET_RUNTIME_PM_OPS(funnel_runtime_suspend, funnel_runtime_resume, NULL) }; -static struct amba_id funnel_ids[] = { +static const struct amba_id funnel_ids[] = { { .id = 0x0003b908, .mask = 0x0003ffff, }, + { + /* Coresight SoC-600 */ + .id = 0x000bb9eb, + .mask = 0x000fffff, + }, { 0, 0}, }; diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 5f662d82052c..f1d0e21d8cab 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -39,23 +39,33 @@ #define ETM_MODE_EXCL_USER BIT(31) typedef u32 (*coresight_read_fn)(const struct device *, u32 offset); -#define coresight_simple_func(type, func, name, offset) \ +#define __coresight_simple_func(type, func, name, lo_off, hi_off) \ static ssize_t name##_show(struct device *_dev, \ struct device_attribute *attr, char *buf) \ { \ type *drvdata = dev_get_drvdata(_dev->parent); \ coresight_read_fn fn = func; \ - u32 val; \ + u64 val; \ pm_runtime_get_sync(_dev->parent); \ if (fn) \ - val = fn(_dev->parent, offset); \ + val = (u64)fn(_dev->parent, lo_off); \ else \ - val = readl_relaxed(drvdata->base + offset); \ + val = coresight_read_reg_pair(drvdata->base, \ + lo_off, hi_off); \ pm_runtime_put_sync(_dev->parent); \ - return scnprintf(buf, PAGE_SIZE, "0x%x\n", val); \ + return scnprintf(buf, PAGE_SIZE, "0x%llx\n", val); \ } \ static DEVICE_ATTR_RO(name) +#define coresight_simple_func(type, func, name, offset) \ + __coresight_simple_func(type, func, name, offset, -1) +#define coresight_simple_reg32(type, name, offset) \ + __coresight_simple_func(type, NULL, name, offset, -1) +#define coresight_simple_reg64(type, name, lo_off, hi_off) \ + __coresight_simple_func(type, NULL, name, lo_off, hi_off) + +extern const u32 barrier_pkt[5]; + enum etm_addr_type { ETM_ADDR_TYPE_NONE, ETM_ADDR_TYPE_SINGLE, @@ -106,6 +116,25 @@ static inline void CS_UNLOCK(void __iomem *addr) } while (0); } +static inline u64 +coresight_read_reg_pair(void __iomem *addr, s32 lo_offset, s32 hi_offset) +{ + u64 val; + + val = readl_relaxed(addr + lo_offset); + val |= (hi_offset < 0) ? 0 : + (u64)readl_relaxed(addr + hi_offset) << 32; + return val; +} + +static inline void coresight_write_reg_pair(void __iomem *addr, u64 val, + s32 lo_offset, s32 hi_offset) +{ + writel_relaxed((u32)val, addr + lo_offset); + if (hi_offset >= 0) + writel_relaxed((u32)(val >> 32), addr + hi_offset); +} + 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); diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index 93fc26f01bab..92a780a6df1d 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -276,7 +276,7 @@ static void stm_disable(struct coresight_device *csdev, spin_unlock(&drvdata->spinlock); /* Wait until the engine has completely stopped */ - coresight_timeout(drvdata, STMTCSR, STMTCSR_BUSY_BIT, 0); + coresight_timeout(drvdata->base, STMTCSR, STMTCSR_BUSY_BIT, 0); pm_runtime_put(drvdata->dev); @@ -307,7 +307,8 @@ static inline bool stm_addr_unaligned(const void *addr, u8 write_bytes) return ((unsigned long)addr & (write_bytes - 1)); } -static void stm_send(void *addr, const void *data, u32 size, u8 write_bytes) +static void stm_send(void __iomem *addr, const void *data, + u32 size, u8 write_bytes) { u8 paload[8]; @@ -414,7 +415,7 @@ static ssize_t notrace stm_generic_packet(struct stm_data *stm_data, unsigned int size, const unsigned char *payload) { - unsigned long ch_addr; + void __iomem *ch_addr; struct stm_drvdata *drvdata = container_of(stm_data, struct stm_drvdata, stm); @@ -424,7 +425,7 @@ static ssize_t notrace stm_generic_packet(struct stm_data *stm_data, if (channel >= drvdata->numsp) return -EINVAL; - ch_addr = (unsigned long)stm_channel_addr(drvdata, channel); + ch_addr = stm_channel_addr(drvdata, channel); flags = (flags == STP_PACKET_TIMESTAMPED) ? STM_FLAG_TIMESTAMPED : 0; flags |= test_bit(channel, drvdata->chs.guaranteed) ? @@ -437,20 +438,20 @@ static ssize_t notrace stm_generic_packet(struct stm_data *stm_data, switch (packet) { case STP_PACKET_FLAG: - ch_addr |= stm_channel_off(STM_PKT_TYPE_FLAG, flags); + ch_addr += stm_channel_off(STM_PKT_TYPE_FLAG, flags); /* * The generic STM core sets a size of '0' on flag packets. * As such send a flag packet of size '1' and tell the * core we did so. */ - stm_send((void *)ch_addr, payload, 1, drvdata->write_bytes); + stm_send(ch_addr, payload, 1, drvdata->write_bytes); size = 1; break; case STP_PACKET_DATA: - ch_addr |= stm_channel_off(STM_PKT_TYPE_DATA, flags); - stm_send((void *)ch_addr, payload, size, + ch_addr += stm_channel_off(STM_PKT_TYPE_DATA, flags); + stm_send(ch_addr, payload, size, drvdata->write_bytes); break; @@ -635,21 +636,21 @@ static ssize_t traceid_store(struct device *dev, } static DEVICE_ATTR_RW(traceid); -#define coresight_stm_simple_func(name, offset) \ - coresight_simple_func(struct stm_drvdata, NULL, name, offset) - -coresight_stm_simple_func(tcsr, STMTCSR); -coresight_stm_simple_func(tsfreqr, STMTSFREQR); -coresight_stm_simple_func(syncr, STMSYNCR); -coresight_stm_simple_func(sper, STMSPER); -coresight_stm_simple_func(spter, STMSPTER); -coresight_stm_simple_func(privmaskr, STMPRIVMASKR); -coresight_stm_simple_func(spscr, STMSPSCR); -coresight_stm_simple_func(spmscr, STMSPMSCR); -coresight_stm_simple_func(spfeat1r, STMSPFEAT1R); -coresight_stm_simple_func(spfeat2r, STMSPFEAT2R); -coresight_stm_simple_func(spfeat3r, STMSPFEAT3R); -coresight_stm_simple_func(devid, CORESIGHT_DEVID); +#define coresight_stm_reg(name, offset) \ + coresight_simple_reg32(struct stm_drvdata, name, offset) + +coresight_stm_reg(tcsr, STMTCSR); +coresight_stm_reg(tsfreqr, STMTSFREQR); +coresight_stm_reg(syncr, STMSYNCR); +coresight_stm_reg(sper, STMSPER); +coresight_stm_reg(spter, STMSPTER); +coresight_stm_reg(privmaskr, STMPRIVMASKR); +coresight_stm_reg(spscr, STMSPSCR); +coresight_stm_reg(spmscr, STMSPMSCR); +coresight_stm_reg(spfeat1r, STMSPFEAT1R); +coresight_stm_reg(spfeat2r, STMSPFEAT2R); +coresight_stm_reg(spfeat3r, STMSPFEAT3R); +coresight_stm_reg(devid, CORESIGHT_DEVID); static struct attribute *coresight_stm_attrs[] = { &dev_attr_hwevent_enable.attr, @@ -914,7 +915,7 @@ static const struct dev_pm_ops stm_dev_pm_ops = { SET_RUNTIME_PM_OPS(stm_runtime_suspend, stm_runtime_resume, NULL) }; -static struct amba_id stm_ids[] = { +static const struct amba_id stm_ids[] = { { .id = 0x0003b962, .mask = 0x0003ffff, diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index e3b9fb82eb8d..e2513b786242 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -43,17 +43,34 @@ static void tmc_etb_enable_hw(struct tmc_drvdata *drvdata) static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata) { + bool lost = false; char *bufp; - u32 read_data; + const u32 *barrier; + u32 read_data, status; int i; + /* + * Get a hold of the status register and see if a wrap around + * has occurred. + */ + status = readl_relaxed(drvdata->base + TMC_STS); + if (status & TMC_STS_FULL) + lost = true; + bufp = drvdata->buf; drvdata->len = 0; + barrier = barrier_pkt; while (1) { for (i = 0; i < drvdata->memwidth; i++) { read_data = readl_relaxed(drvdata->base + TMC_RRD); if (read_data == 0xFFFFFFFF) return; + + if (lost && *barrier) { + read_data = *barrier; + barrier++; + } + memcpy(bufp, &read_data, 4); bufp += 4; drvdata->len += 4; @@ -369,9 +386,11 @@ static void tmc_update_etf_buffer(struct coresight_device *csdev, struct perf_output_handle *handle, void *sink_config) { + bool lost = false; int i, cur; + const u32 *barrier; u32 *buf_ptr; - u32 read_ptr, write_ptr; + u64 read_ptr, write_ptr; u32 status, to_read; unsigned long offset; struct cs_buffers *buf = sink_config; @@ -388,8 +407,8 @@ static void tmc_update_etf_buffer(struct coresight_device *csdev, tmc_flush_and_stop(drvdata); - read_ptr = readl_relaxed(drvdata->base + TMC_RRP); - write_ptr = readl_relaxed(drvdata->base + TMC_RWP); + read_ptr = tmc_read_rrp(drvdata); + write_ptr = tmc_read_rwp(drvdata); /* * Get a hold of the status register and see if a wrap around @@ -397,7 +416,7 @@ static void tmc_update_etf_buffer(struct coresight_device *csdev, */ status = readl_relaxed(drvdata->base + TMC_STS); if (status & TMC_STS_FULL) { - perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); + lost = true; to_read = drvdata->size; } else { to_read = CIRC_CNT(write_ptr, read_ptr, drvdata->size); @@ -441,18 +460,27 @@ static void tmc_update_etf_buffer(struct coresight_device *csdev, if (read_ptr > (drvdata->size - 1)) read_ptr -= drvdata->size; /* Tell the HW */ - writel_relaxed(read_ptr, drvdata->base + TMC_RRP); - perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); + tmc_write_rrp(drvdata, read_ptr); + lost = true; } + if (lost) + perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); + cur = buf->cur; offset = buf->offset; + barrier = barrier_pkt; /* for every byte to read */ for (i = 0; i < to_read; i += 4) { buf_ptr = buf->data_pages[cur] + offset; *buf_ptr = readl_relaxed(drvdata->base + TMC_RRD); + if (lost && *barrier) { + *buf_ptr = *barrier; + barrier++; + } + offset += 4; if (offset >= PAGE_SIZE) { offset = 0; diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 5d312699b3b9..68fbc8f7450e 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -22,7 +22,7 @@ static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) { - u32 axictl; + u32 axictl, sts; /* Zero out the memory to help with debug */ memset(drvdata->vaddr, 0, drvdata->size); @@ -36,17 +36,29 @@ static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE); axictl = readl_relaxed(drvdata->base + TMC_AXICTL); - axictl |= TMC_AXICTL_WR_BURST_16; - writel_relaxed(axictl, drvdata->base + TMC_AXICTL); - axictl &= ~TMC_AXICTL_SCT_GAT_MODE; - writel_relaxed(axictl, drvdata->base + TMC_AXICTL); - axictl = (axictl & - ~(TMC_AXICTL_PROT_CTL_B0 | TMC_AXICTL_PROT_CTL_B1)) | - TMC_AXICTL_PROT_CTL_B1; + axictl &= ~TMC_AXICTL_CLEAR_MASK; + axictl |= (TMC_AXICTL_PROT_CTL_B1 | TMC_AXICTL_WR_BURST_16); + axictl |= TMC_AXICTL_AXCACHE_OS; + + if (tmc_etr_has_cap(drvdata, TMC_ETR_AXI_ARCACHE)) { + axictl &= ~TMC_AXICTL_ARCACHE_MASK; + axictl |= TMC_AXICTL_ARCACHE_OS; + } + writel_relaxed(axictl, drvdata->base + TMC_AXICTL); + tmc_write_dba(drvdata, drvdata->paddr); + /* + * If the TMC pointers must be programmed before the session, + * we have to set it properly (i.e, RRP/RWP to base address and + * STS to "not full"). + */ + if (tmc_etr_has_cap(drvdata, TMC_ETR_SAVE_RESTORE)) { + tmc_write_rrp(drvdata, drvdata->paddr); + tmc_write_rwp(drvdata, drvdata->paddr); + sts = readl_relaxed(drvdata->base + TMC_STS) & ~TMC_STS_FULL; + writel_relaxed(sts, drvdata->base + TMC_STS); + } - writel_relaxed(drvdata->paddr, drvdata->base + TMC_DBALO); - writel_relaxed(0x0, drvdata->base + TMC_DBAHI); writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI | TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT | TMC_FFCR_TRIGON_TRIGIN, @@ -59,9 +71,12 @@ static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata) { - u32 rwp, val; + const u32 *barrier; + u32 val; + u32 *temp; + u64 rwp; - rwp = readl_relaxed(drvdata->base + TMC_RWP); + rwp = tmc_read_rwp(drvdata); val = readl_relaxed(drvdata->base + TMC_STS); /* @@ -71,6 +86,16 @@ static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata) if (val & TMC_STS_FULL) { drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr; drvdata->len = drvdata->size; + + barrier = barrier_pkt; + temp = (u32 *)drvdata->buf; + + while (*barrier) { + *temp = *barrier; + temp++; + barrier++; + } + } else { drvdata->buf = drvdata->vaddr; drvdata->len = rwp - drvdata->paddr; diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 864488793f09..2ff4a66a3caa 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -217,20 +217,24 @@ static enum tmc_mem_intf_width tmc_get_memwidth(u32 devid) return memwidth; } -#define coresight_tmc_simple_func(name, offset) \ - coresight_simple_func(struct tmc_drvdata, NULL, name, offset) - -coresight_tmc_simple_func(rsz, TMC_RSZ); -coresight_tmc_simple_func(sts, TMC_STS); -coresight_tmc_simple_func(rrp, TMC_RRP); -coresight_tmc_simple_func(rwp, TMC_RWP); -coresight_tmc_simple_func(trg, TMC_TRG); -coresight_tmc_simple_func(ctl, TMC_CTL); -coresight_tmc_simple_func(ffsr, TMC_FFSR); -coresight_tmc_simple_func(ffcr, TMC_FFCR); -coresight_tmc_simple_func(mode, TMC_MODE); -coresight_tmc_simple_func(pscr, TMC_PSCR); -coresight_tmc_simple_func(devid, CORESIGHT_DEVID); +#define coresight_tmc_reg(name, offset) \ + coresight_simple_reg32(struct tmc_drvdata, name, offset) +#define coresight_tmc_reg64(name, lo_off, hi_off) \ + coresight_simple_reg64(struct tmc_drvdata, name, lo_off, hi_off) + +coresight_tmc_reg(rsz, TMC_RSZ); +coresight_tmc_reg(sts, TMC_STS); +coresight_tmc_reg(trg, TMC_TRG); +coresight_tmc_reg(ctl, TMC_CTL); +coresight_tmc_reg(ffsr, TMC_FFSR); +coresight_tmc_reg(ffcr, TMC_FFCR); +coresight_tmc_reg(mode, TMC_MODE); +coresight_tmc_reg(pscr, TMC_PSCR); +coresight_tmc_reg(axictl, TMC_AXICTL); +coresight_tmc_reg(devid, CORESIGHT_DEVID); +coresight_tmc_reg64(rrp, TMC_RRP, TMC_RRPHI); +coresight_tmc_reg64(rwp, TMC_RWP, TMC_RWPHI); +coresight_tmc_reg64(dba, TMC_DBALO, TMC_DBAHI); static struct attribute *coresight_tmc_mgmt_attrs[] = { &dev_attr_rsz.attr, @@ -244,6 +248,8 @@ static struct attribute *coresight_tmc_mgmt_attrs[] = { &dev_attr_mode.attr, &dev_attr_pscr.attr, &dev_attr_devid.attr, + &dev_attr_dba.attr, + &dev_attr_axictl.attr, NULL, }; @@ -293,6 +299,42 @@ const struct attribute_group *coresight_tmc_groups[] = { NULL, }; +/* Detect and initialise the capabilities of a TMC ETR */ +static int tmc_etr_setup_caps(struct tmc_drvdata *drvdata, + u32 devid, void *dev_caps) +{ + u32 dma_mask = 0; + + /* Set the unadvertised capabilities */ + tmc_etr_init_caps(drvdata, (u32)(unsigned long)dev_caps); + + if (!(devid & TMC_DEVID_NOSCAT)) + tmc_etr_set_cap(drvdata, TMC_ETR_SG); + + /* Check if the AXI address width is available */ + if (devid & TMC_DEVID_AXIAW_VALID) + dma_mask = ((devid >> TMC_DEVID_AXIAW_SHIFT) & + TMC_DEVID_AXIAW_MASK); + + /* + * Unless specified in the device configuration, ETR uses a 40-bit + * AXI master in place of the embedded SRAM of ETB/ETF. + */ + switch (dma_mask) { + case 32: + case 40: + case 44: + case 48: + case 52: + dev_info(drvdata->dev, "Detected dma mask %dbits\n", dma_mask); + break; + default: + dma_mask = 40; + } + + return dma_set_mask_and_coherent(drvdata->dev, DMA_BIT_MASK(dma_mask)); +} + static int tmc_probe(struct amba_device *adev, const struct amba_id *id) { int ret = 0; @@ -354,25 +396,29 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) desc.dev = dev; desc.groups = coresight_tmc_groups; - if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { + switch (drvdata->config_type) { + case TMC_CONFIG_TYPE_ETB: desc.type = CORESIGHT_DEV_TYPE_SINK; desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; desc.ops = &tmc_etb_cs_ops; - } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { + break; + case TMC_CONFIG_TYPE_ETR: desc.type = CORESIGHT_DEV_TYPE_SINK; desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; desc.ops = &tmc_etr_cs_ops; - /* - * ETR configuration uses a 40-bit AXI master in place of - * the embedded SRAM of ETB/ETF. - */ - ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40)); + ret = tmc_etr_setup_caps(drvdata, devid, id->data); if (ret) goto out; - } else { + break; + case TMC_CONFIG_TYPE_ETF: desc.type = CORESIGHT_DEV_TYPE_LINKSINK; desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO; desc.ops = &tmc_etf_cs_ops; + break; + default: + pr_err("%s: Unsupported TMC config\n", pdata->name); + ret = -EINVAL; + goto out; } drvdata->csdev = coresight_register(&desc); @@ -391,11 +437,27 @@ out: return ret; } -static struct amba_id tmc_ids[] = { +static const struct amba_id tmc_ids[] = { { .id = 0x0003b961, .mask = 0x0003ffff, }, + { + /* Coresight SoC 600 TMC-ETR/ETS */ + .id = 0x000bb9e8, + .mask = 0x000fffff, + .data = (void *)(unsigned long)CORESIGHT_SOC_600_ETR_CAPS, + }, + { + /* Coresight SoC 600 TMC-ETB */ + .id = 0x000bb9e9, + .mask = 0x000fffff, + }, + { + /* Coresight SoC 600 TMC-ETF */ + .id = 0x000bb9ea, + .mask = 0x000fffff, + }, { 0, 0}, }; diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 51c01851533e..8df7a813f537 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -54,11 +54,32 @@ #define TMC_STS_TMCREADY_BIT 2 #define TMC_STS_FULL BIT(0) #define TMC_STS_TRIGGERED BIT(1) -/* TMC_AXICTL - 0x110 */ +/* + * TMC_AXICTL - 0x110 + * + * TMC AXICTL format for SoC-400 + * Bits [0-1] : ProtCtrlBit0-1 + * Bits [2-5] : CacheCtrlBits 0-3 (AXCACHE) + * Bit 6 : Reserved + * Bit 7 : ScatterGatherMode + * Bits [8-11] : WrBurstLen + * Bits [12-31] : Reserved. + * TMC AXICTL format for SoC-600, as above except: + * Bits [2-5] : AXI WCACHE + * Bits [16-19] : AXI RCACHE + * Bits [20-31] : Reserved + */ +#define TMC_AXICTL_CLEAR_MASK 0xfbf +#define TMC_AXICTL_ARCACHE_MASK (0xf << 16) + #define TMC_AXICTL_PROT_CTL_B0 BIT(0) #define TMC_AXICTL_PROT_CTL_B1 BIT(1) #define TMC_AXICTL_SCT_GAT_MODE BIT(7) #define TMC_AXICTL_WR_BURST_16 0xF00 +/* Write-back Read and Write-allocate */ +#define TMC_AXICTL_AXCACHE_OS (0xf << 2) +#define TMC_AXICTL_ARCACHE_OS (0xf << 16) + /* TMC_FFCR - 0x304 */ #define TMC_FFCR_FLUSHMAN_BIT 6 #define TMC_FFCR_EN_FMT BIT(0) @@ -69,6 +90,12 @@ #define TMC_FFCR_STOP_ON_FLUSH BIT(12) +#define TMC_DEVID_NOSCAT BIT(24) + +#define TMC_DEVID_AXIAW_VALID BIT(16) +#define TMC_DEVID_AXIAW_SHIFT 17 +#define TMC_DEVID_AXIAW_MASK 0x7f + enum tmc_config_type { TMC_CONFIG_TYPE_ETB, TMC_CONFIG_TYPE_ETR, @@ -88,6 +115,24 @@ enum tmc_mem_intf_width { TMC_MEM_INTF_WIDTH_256BITS = 8, }; +/* TMC ETR Capability bit definitions */ +#define TMC_ETR_SG (0x1U << 0) +/* ETR has separate read/write cache encodings */ +#define TMC_ETR_AXI_ARCACHE (0x1U << 1) +/* + * TMC_ETR_SAVE_RESTORE - Values of RRP/RWP/STS.Full are + * retained when TMC leaves Disabled state, allowing us to continue + * the tracing from a point where we stopped. This also implies that + * the RRP/RWP/STS.Full should always be programmed to the correct + * value. Unfortunately this is not advertised by the hardware, + * so we have to rely on PID of the IP to detect the functionality. + */ +#define TMC_ETR_SAVE_RESTORE (0x1U << 2) + +/* Coresight SoC-600 TMC-ETR unadvertised capabilities */ +#define CORESIGHT_SOC_600_ETR_CAPS \ + (TMC_ETR_SAVE_RESTORE | TMC_ETR_AXI_ARCACHE) + /** * struct tmc_drvdata - specifics associated to an TMC component * @base: memory mapped base address for this component. @@ -104,6 +149,8 @@ enum tmc_mem_intf_width { * @config_type: TMC variant, must be of type @tmc_config_type. * @memwidth: width of the memory interface databus, in bytes. * @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) */ struct tmc_drvdata { void __iomem *base; @@ -121,6 +168,7 @@ struct tmc_drvdata { enum tmc_config_type config_type; enum tmc_mem_intf_width memwidth; u32 trigger_cntr; + u32 etr_caps; }; /* Generic functions */ @@ -139,4 +187,39 @@ extern const struct coresight_ops tmc_etf_cs_ops; int tmc_read_prepare_etr(struct tmc_drvdata *drvdata); int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata); extern const struct coresight_ops tmc_etr_cs_ops; + + +#define TMC_REG_PAIR(name, lo_off, hi_off) \ +static inline u64 \ +tmc_read_##name(struct tmc_drvdata *drvdata) \ +{ \ + return coresight_read_reg_pair(drvdata->base, lo_off, hi_off); \ +} \ +static inline void \ +tmc_write_##name(struct tmc_drvdata *drvdata, u64 val) \ +{ \ + coresight_write_reg_pair(drvdata->base, val, lo_off, hi_off); \ +} + +TMC_REG_PAIR(rrp, TMC_RRP, TMC_RRPHI) +TMC_REG_PAIR(rwp, TMC_RWP, TMC_RWPHI) +TMC_REG_PAIR(dba, TMC_DBALO, TMC_DBAHI) + +/* Initialise the caps from unadvertised static capabilities of the device */ +static inline void tmc_etr_init_caps(struct tmc_drvdata *drvdata, u32 dev_caps) +{ + WARN_ON(drvdata->etr_caps); + drvdata->etr_caps = dev_caps; +} + +static inline void tmc_etr_set_cap(struct tmc_drvdata *drvdata, u32 cap) +{ + drvdata->etr_caps |= cap; +} + +static inline bool tmc_etr_has_cap(struct tmc_drvdata *drvdata, u32 cap) +{ + return !!(drvdata->etr_caps & cap); +} + #endif diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index 0673baf0f2f5..d7a3e453016d 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -192,7 +192,7 @@ static const struct dev_pm_ops tpiu_dev_pm_ops = { SET_RUNTIME_PM_OPS(tpiu_runtime_suspend, tpiu_runtime_resume, NULL) }; -static struct amba_id tpiu_ids[] = { +static const struct amba_id tpiu_ids[] = { { .id = 0x0003b912, .mask = 0x0003ffff, @@ -201,6 +201,11 @@ static struct amba_id tpiu_ids[] = { .id = 0x0004b912, .mask = 0x0007ffff, }, + { + /* Coresight SoC-600 */ + .id = 0x000bb9e7, + .mask = 0x000fffff, + }, { 0, 0}, }; diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 6a0202b7384f..b8091bef21dc 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -53,6 +53,14 @@ static DEFINE_PER_CPU(struct list_head *, tracer_path); */ static struct list_head *stm_path; +/* + * When losing synchronisation a new barrier packet needs to be inserted at the + * beginning of the data collected in a buffer. That way the decoder knows that + * it needs to look for another sync sequence. + */ +const u32 barrier_pkt[5] = {0x7fffffff, 0x7fffffff, + 0x7fffffff, 0x7fffffff, 0x0}; + static int coresight_id_match(struct device *dev, void *data) { int trace_id, i_trace_id; diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index 8da567abc0ce..1a023e30488c 100644 --- a/drivers/hwtracing/intel_th/core.c +++ b/drivers/hwtracing/intel_th/core.c @@ -101,17 +101,53 @@ out_pm: return ret; } +static void intel_th_device_remove(struct intel_th_device *thdev); + static int intel_th_remove(struct device *dev) { struct intel_th_driver *thdrv = to_intel_th_driver(dev->driver); struct intel_th_device *thdev = to_intel_th_device(dev); - struct intel_th_device *hub = to_intel_th_device(dev->parent); + struct intel_th_device *hub = to_intel_th_hub(thdev); int err; if (thdev->type == INTEL_TH_SWITCH) { + struct intel_th *th = to_intel_th(hub); + int i, lowest; + + /* disconnect outputs */ err = device_for_each_child(dev, thdev, intel_th_child_remove); if (err) return err; + + /* + * Remove outputs, that is, hub's children: they are created + * at hub's probe time by having the hub call + * intel_th_output_enable() for each of them. + */ + for (i = 0, lowest = -1; i < th->num_thdevs; i++) { + /* + * Move the non-output devices from higher up the + * th->thdev[] array to lower positions to maintain + * a contiguous array. + */ + if (th->thdev[i]->type != INTEL_TH_OUTPUT) { + if (lowest >= 0) { + th->thdev[lowest] = th->thdev[i]; + th->thdev[i] = NULL; + ++lowest; + } + + continue; + } + + if (lowest == -1) + lowest = i; + + intel_th_device_remove(th->thdev[i]); + th->thdev[i] = NULL; + } + + th->num_thdevs = lowest; } if (thdrv->attr_group) @@ -156,21 +192,6 @@ static struct device_type intel_th_source_device_type = { .release = intel_th_device_release, }; -static struct intel_th *to_intel_th(struct intel_th_device *thdev) -{ - /* - * subdevice tree is flat: if this one is not a switch, its - * parent must be - */ - if (thdev->type != INTEL_TH_SWITCH) - thdev = to_intel_th_hub(thdev); - - if (WARN_ON_ONCE(!thdev || thdev->type != INTEL_TH_SWITCH)) - return NULL; - - return dev_get_drvdata(thdev->dev.parent); -} - static char *intel_th_output_devnode(struct device *dev, umode_t *mode, kuid_t *uid, kgid_t *gid) { @@ -205,6 +226,7 @@ static int intel_th_output_activate(struct intel_th_device *thdev) { struct intel_th_driver *thdrv = to_intel_th_driver_or_null(thdev->dev.driver); + struct intel_th *th = to_intel_th(thdev); int ret = 0; if (!thdrv) @@ -215,15 +237,28 @@ static int intel_th_output_activate(struct intel_th_device *thdev) pm_runtime_get_sync(&thdev->dev); + if (th->activate) + ret = th->activate(th); + if (ret) + goto fail_put; + if (thdrv->activate) ret = thdrv->activate(thdev); else intel_th_trace_enable(thdev); - if (ret) { - pm_runtime_put(&thdev->dev); - module_put(thdrv->driver.owner); - } + if (ret) + goto fail_deactivate; + + return 0; + +fail_deactivate: + if (th->deactivate) + th->deactivate(th); + +fail_put: + pm_runtime_put(&thdev->dev); + module_put(thdrv->driver.owner); return ret; } @@ -232,6 +267,7 @@ static void intel_th_output_deactivate(struct intel_th_device *thdev) { struct intel_th_driver *thdrv = to_intel_th_driver_or_null(thdev->dev.driver); + struct intel_th *th = to_intel_th(thdev); if (!thdrv) return; @@ -241,6 +277,9 @@ static void intel_th_output_deactivate(struct intel_th_device *thdev) else intel_th_trace_disable(thdev); + if (th->deactivate) + th->deactivate(th); + pm_runtime_put(&thdev->dev); module_put(thdrv->driver.owner); } @@ -326,10 +365,10 @@ intel_th_device_alloc(struct intel_th *th, unsigned int type, const char *name, struct device *parent; struct intel_th_device *thdev; - if (type == INTEL_TH_SWITCH) - parent = th->dev; - else + if (type == INTEL_TH_OUTPUT) parent = &th->hub->dev; + else + parent = th->dev; thdev = kzalloc(sizeof(*thdev) + strlen(name) + 1, GFP_KERNEL); if (!thdev) @@ -392,13 +431,14 @@ static const struct intel_th_subdevice { unsigned otype; unsigned scrpd; int id; -} intel_th_subdevices[TH_SUBDEVICE_MAX] = { +} intel_th_subdevices[] = { { .nres = 1, .res = { { + /* Handle TSCU from GTH driver */ .start = REG_GTH_OFFSET, - .end = REG_GTH_OFFSET + REG_GTH_LENGTH - 1, + .end = REG_TSCU_OFFSET + REG_TSCU_LENGTH - 1, .flags = IORESOURCE_MEM, }, }, @@ -483,6 +523,21 @@ static const struct intel_th_subdevice { .nres = 1, .res = { { + .start = REG_PTI_OFFSET, + .end = REG_PTI_OFFSET + REG_PTI_LENGTH - 1, + .flags = IORESOURCE_MEM, + }, + }, + .id = -1, + .name = "lpp", + .type = INTEL_TH_OUTPUT, + .otype = GTH_LPP, + .scrpd = SCRPD_PTI_IS_PRIM_DEST, + }, + { + .nres = 1, + .res = { + { .start = REG_DCIH_OFFSET, .end = REG_DCIH_OFFSET + REG_DCIH_LENGTH - 1, .flags = IORESOURCE_MEM, @@ -526,98 +581,182 @@ static inline void intel_th_request_hub_module_flush(struct intel_th *th) } #endif /* CONFIG_MODULES */ -static int intel_th_populate(struct intel_th *th, struct resource *devres, - unsigned int ndevres, int irq) +static struct intel_th_device * +intel_th_subdevice_alloc(struct intel_th *th, + const struct intel_th_subdevice *subdev) { + struct intel_th_device *thdev; struct resource res[3]; unsigned int req = 0; - int src, dst, err; + int r, err; - /* create devices for each intel_th_subdevice */ - 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; + thdev = intel_th_device_alloc(th, subdev->type, subdev->name, + subdev->id); + if (!thdev) + return ERR_PTR(-ENOMEM); - /* only allow SOURCE and SWITCH devices in host mode */ - if (host_mode && subdev->type == INTEL_TH_OUTPUT) - continue; + thdev->drvdata = th->drvdata; + + memcpy(res, subdev->res, + sizeof(struct resource) * subdev->nres); + + for (r = 0; r < subdev->nres; r++) { + struct resource *devres = th->resource; + int bar = TH_MMIO_CONFIG; + + /* + * Take .end == 0 to mean 'take the whole bar', + * .start then tells us which bar it is. Default to + * TH_MMIO_CONFIG. + */ + if (!res[r].end && res[r].flags == IORESOURCE_MEM) { + bar = res[r].start; + res[r].start = 0; + res[r].end = resource_size(&devres[bar]) - 1; + } + + if (res[r].flags & IORESOURCE_MEM) { + res[r].start += devres[bar].start; + res[r].end += devres[bar].start; - thdev = intel_th_device_alloc(th, subdev->type, subdev->name, - subdev->id); - if (!thdev) { - err = -ENOMEM; - goto kill_subdevs; + dev_dbg(th->dev, "%s:%d @ %pR\n", + subdev->name, r, &res[r]); + } else if (res[r].flags & IORESOURCE_IRQ) { + res[r].start = th->irq; } + } - memcpy(res, subdev->res, - sizeof(struct resource) * subdev->nres); + err = intel_th_device_add_resources(thdev, res, subdev->nres); + if (err) { + put_device(&thdev->dev); + goto fail_put_device; + } - for (r = 0; r < subdev->nres; r++) { - int bar = TH_MMIO_CONFIG; + if (subdev->type == INTEL_TH_OUTPUT) { + thdev->dev.devt = MKDEV(th->major, th->num_thdevs); + 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; + th->hub = thdev; + } - /* - * Take .end == 0 to mean 'take the whole bar', - * .start then tells us which bar it is. Default to - * TH_MMIO_CONFIG. - */ - if (!res[r].end && res[r].flags == IORESOURCE_MEM) { - bar = res[r].start; - res[r].start = 0; - res[r].end = resource_size(&devres[bar]) - 1; - } + err = device_add(&thdev->dev); + if (err) { + put_device(&thdev->dev); + goto fail_free_res; + } - if (res[r].flags & IORESOURCE_MEM) { - res[r].start += devres[bar].start; - res[r].end += devres[bar].start; + /* need switch driver to be loaded to enumerate the rest */ + if (subdev->type == INTEL_TH_SWITCH && !req) { + err = intel_th_request_hub_module(th); + if (!err) + req++; + } - dev_dbg(th->dev, "%s:%d @ %pR\n", - subdev->name, r, &res[r]); - } else if (res[r].flags & IORESOURCE_IRQ) { - res[r].start = irq; - } - } + return thdev; - err = intel_th_device_add_resources(thdev, res, subdev->nres); - if (err) { - put_device(&thdev->dev); - goto kill_subdevs; - } +fail_free_res: + kfree(thdev->resource); - if (subdev->type == INTEL_TH_OUTPUT) { - 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; - } +fail_put_device: + put_device(&thdev->dev); + + return ERR_PTR(err); +} - err = device_add(&thdev->dev); - if (err) { - put_device(&thdev->dev); - goto kill_subdevs; +/** + * intel_th_output_enable() - find and enable a device for a given output type + * @th: Intel TH instance + * @otype: output type + * + * Go through the unallocated output devices, find the first one whos type + * matches @otype and instantiate it. These devices are removed when the hub + * device is removed, see intel_th_remove(). + */ +int intel_th_output_enable(struct intel_th *th, unsigned int otype) +{ + struct intel_th_device *thdev; + int src = 0, dst = 0; + + for (src = 0, dst = 0; dst <= th->num_thdevs; src++, dst++) { + for (; src < ARRAY_SIZE(intel_th_subdevices); src++) { + if (intel_th_subdevices[src].type != INTEL_TH_OUTPUT) + continue; + + if (intel_th_subdevices[src].otype != otype) + continue; + + break; } - /* need switch driver to be loaded to enumerate the rest */ - if (subdev->type == INTEL_TH_SWITCH && !req) { - th->hub = thdev; - err = intel_th_request_hub_module(th); - if (!err) - req++; + /* no unallocated matching subdevices */ + if (src == ARRAY_SIZE(intel_th_subdevices)) + return -ENODEV; + + for (; dst < th->num_thdevs; dst++) { + if (th->thdev[dst]->type != INTEL_TH_OUTPUT) + continue; + + if (th->thdev[dst]->output.type != otype) + continue; + + break; } - th->thdev[dst++] = thdev; + /* + * intel_th_subdevices[src] matches our requirements and is + * not matched in th::thdev[] + */ + if (dst == th->num_thdevs) + goto found; } + return -ENODEV; + +found: + thdev = intel_th_subdevice_alloc(th, &intel_th_subdevices[src]); + if (IS_ERR(thdev)) + return PTR_ERR(thdev); + + th->thdev[th->num_thdevs++] = thdev; + return 0; +} +EXPORT_SYMBOL_GPL(intel_th_output_enable); + +static int intel_th_populate(struct intel_th *th) +{ + int src; + + /* create devices for each intel_th_subdevice */ + for (src = 0; src < ARRAY_SIZE(intel_th_subdevices); src++) { + const struct intel_th_subdevice *subdev = + &intel_th_subdevices[src]; + struct intel_th_device *thdev; + + /* only allow SOURCE and SWITCH devices in host mode */ + if (host_mode && subdev->type == INTEL_TH_OUTPUT) + continue; -kill_subdevs: - for (; dst >= 0; dst--) - intel_th_device_remove(th->thdev[dst]); + /* + * don't enable port OUTPUTs in this path; SWITCH enables them + * via intel_th_output_enable() + */ + if (subdev->type == INTEL_TH_OUTPUT && + subdev->otype != GTH_NONE) + continue; + + thdev = intel_th_subdevice_alloc(th, subdev); + /* note: caller should free subdevices from th::thdev[] */ + if (IS_ERR(thdev)) + return PTR_ERR(thdev); + + th->thdev[th->num_thdevs++] = thdev; + } - return err; + return 0; } static int match_devt(struct device *dev, void *data) @@ -670,8 +809,8 @@ static const struct file_operations intel_th_output_fops = { * @irq: irq number */ struct intel_th * -intel_th_alloc(struct device *dev, struct resource *devres, - unsigned int ndevres, int irq) +intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata, + struct resource *devres, unsigned int ndevres, int irq) { struct intel_th *th; int err; @@ -693,6 +832,11 @@ intel_th_alloc(struct device *dev, struct resource *devres, goto err_ida; } th->dev = dev; + th->drvdata = drvdata; + + th->resource = devres; + th->num_resources = ndevres; + th->irq = irq; dev_set_drvdata(dev, th); @@ -700,18 +844,15 @@ intel_th_alloc(struct device *dev, struct resource *devres, pm_runtime_put(dev); pm_runtime_allow(dev); - err = intel_th_populate(th, devres, ndevres, irq); - if (err) - goto err_chrdev; + err = intel_th_populate(th); + if (err) { + /* free the subdevices and undo everything */ + intel_th_free(th); + return ERR_PTR(err); + } return th; -err_chrdev: - pm_runtime_forbid(dev); - - __unregister_chrdev(th->major, 0, TH_POSSIBLE_OUTPUTS, - "intel_th/output"); - err_ida: ida_simple_remove(&intel_th_ida, th->id); @@ -727,11 +868,15 @@ void intel_th_free(struct intel_th *th) int i; intel_th_request_hub_module_flush(th); - for (i = 0; i < TH_SUBDEVICE_MAX; i++) - if (th->thdev[i] && th->thdev[i] != th->hub) - intel_th_device_remove(th->thdev[i]); intel_th_device_remove(th->hub); + for (i = 0; i < th->num_thdevs; i++) { + if (th->thdev[i] != th->hub) + intel_th_device_remove(th->thdev[i]); + th->thdev[i] = NULL; + } + + th->num_thdevs = 0; pm_runtime_get_sync(th->dev); pm_runtime_forbid(th->dev); diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c index dd32d0bad687..018678ec3c13 100644 --- a/drivers/hwtracing/intel_th/gth.c +++ b/drivers/hwtracing/intel_th/gth.c @@ -285,16 +285,16 @@ gth_output_parm_get(struct gth_device *gth, int port, unsigned int parm) */ static int intel_th_gth_reset(struct gth_device *gth) { - u32 scratchpad; + u32 reg; int port, i; - scratchpad = ioread32(gth->base + REG_GTH_SCRPD0); - if (scratchpad & SCRPD_DEBUGGER_IN_USE) + reg = ioread32(gth->base + REG_GTH_SCRPD0); + if (reg & SCRPD_DEBUGGER_IN_USE) return -EBUSY; /* Always save/restore STH and TU registers in S0ix entry/exit */ - scratchpad |= SCRPD_STH_IS_ENABLED | SCRPD_TRIGGER_IS_ENABLED; - iowrite32(scratchpad, gth->base + REG_GTH_SCRPD0); + reg |= SCRPD_STH_IS_ENABLED | SCRPD_TRIGGER_IS_ENABLED; + iowrite32(reg, gth->base + REG_GTH_SCRPD0); /* output ports */ for (port = 0; port < 8; port++) { @@ -512,6 +512,15 @@ static void intel_th_gth_disable(struct intel_th_device *thdev, iowrite32(reg, gth->base + REG_GTH_SCRPD0); } +static void gth_tscu_resync(struct gth_device *gth) +{ + u32 reg; + + reg = ioread32(gth->base + REG_TSCU_TSUCTRL); + reg &= ~TSUCTRL_CTCRESYNC; + iowrite32(reg, gth->base + REG_TSCU_TSUCTRL); +} + /** * intel_th_gth_enable() - enable tracing to an output device * @thdev: GTH device @@ -524,6 +533,7 @@ static void intel_th_gth_enable(struct intel_th_device *thdev, struct intel_th_output *output) { struct gth_device *gth = dev_get_drvdata(&thdev->dev); + struct intel_th *th = to_intel_th(thdev); u32 scr = 0xfc0000, scrpd; int master; @@ -539,6 +549,9 @@ static void intel_th_gth_enable(struct intel_th_device *thdev, output->active = true; spin_unlock(>h->gth_lock); + if (INTEL_TH_CAP(th, tscu_enable)) + gth_tscu_resync(gth); + scrpd = ioread32(gth->base + REG_GTH_SCRPD0); scrpd |= output->scratchpad; iowrite32(scrpd, gth->base + REG_GTH_SCRPD0); @@ -639,6 +652,7 @@ intel_th_gth_set_output(struct intel_th_device *thdev, unsigned int master) static int intel_th_gth_probe(struct intel_th_device *thdev) { struct device *dev = &thdev->dev; + struct intel_th *th = dev_get_drvdata(dev->parent); struct gth_device *gth; struct resource *res; void __iomem *base; @@ -660,6 +674,8 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) gth->base = base; spin_lock_init(>h->gth_lock); + dev_set_drvdata(dev, gth); + /* * 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 @@ -667,7 +683,7 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) * drivers to ports, see intel_th_gth_assign(). */ if (thdev->host_mode) - goto done; + return 0; ret = intel_th_gth_reset(gth); if (ret) { @@ -676,7 +692,7 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) thdev->host_mode = true; - goto done; + return 0; } for (i = 0; i < TH_CONFIGURABLE_MASTERS + 1; i++) @@ -687,6 +703,13 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) gth->output[i].index = i; gth->output[i].port_type = gth_output_parm_get(gth, i, TH_OUTPUT_PARM(port)); + if (gth->output[i].port_type == GTH_NONE) + continue; + + ret = intel_th_output_enable(th, gth->output[i].port_type); + /* -ENODEV is ok, we just won't have that device enumerated */ + if (ret && ret != -ENODEV) + return ret; } if (intel_th_output_attributes(gth) || @@ -698,9 +721,6 @@ 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/gth.h b/drivers/hwtracing/intel_th/gth.h index 56f0d2620577..f3d234251a12 100644 --- a/drivers/hwtracing/intel_th/gth.h +++ b/drivers/hwtracing/intel_th/gth.h @@ -55,9 +55,14 @@ enum { REG_GTH_SCRPD1 = 0xe4, /* ScratchPad[1] */ REG_GTH_SCRPD2 = 0xe8, /* ScratchPad[2] */ REG_GTH_SCRPD3 = 0xec, /* ScratchPad[3] */ + REG_TSCU_TSUCTRL = 0x2000, /* TSCU control register */ + REG_TSCU_TSCUSTAT = 0x2004, /* TSCU status register */ }; /* waiting for Pipeline Empty bit(s) to assert for GTH */ #define GTH_PLE_WAITLOOP_DEPTH 10000 +#define TSUCTRL_CTCRESYNC BIT(0) +#define TSCUSTAT_CTCSYNCING BIT(1) + #endif /* __INTEL_TH_GTH_H__ */ diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h index 3096e7054f6d..99ad563fc40d 100644 --- a/drivers/hwtracing/intel_th/intel_th.h +++ b/drivers/hwtracing/intel_th/intel_th.h @@ -48,8 +48,19 @@ struct intel_th_output { }; /** + * struct intel_th_drvdata - describes hardware capabilities and quirks + * @tscu_enable: device needs SW to enable time stamping unit + */ +struct intel_th_drvdata { + unsigned int tscu_enable : 1; +}; + +#define INTEL_TH_CAP(_th, _cap) ((_th)->drvdata ? (_th)->drvdata->_cap : 0) + +/** * struct intel_th_device - device on the intel_th bus * @dev: device + * @drvdata: hardware capabilities/quirks * @resource: array of resources available to this device * @num_resources: number of resources in @resource array * @type: INTEL_TH_{SOURCE,OUTPUT,SWITCH} @@ -59,11 +70,12 @@ struct intel_th_output { * @name: device name to match the driver */ struct intel_th_device { - struct device dev; - struct resource *resource; - unsigned int num_resources; - unsigned int type; - int id; + struct device dev; + struct intel_th_drvdata *drvdata; + struct resource *resource; + unsigned int num_resources; + unsigned int type; + int id; /* INTEL_TH_SWITCH specific */ bool host_mode; @@ -96,6 +108,17 @@ intel_th_device_get_resource(struct intel_th_device *thdev, unsigned int type, return NULL; } +/* + * GTH, output ports configuration + */ +enum { + GTH_NONE = 0, + GTH_MSU, /* memory/usb */ + GTH_CTP, /* Common Trace Port */ + GTH_LPP, /* Low Power Path */ + GTH_PTI, /* MIPI-PTI */ +}; + /** * intel_th_output_assigned() - if an output device is assigned to a switch port * @thdev: the output device @@ -106,7 +129,8 @@ static inline bool intel_th_output_assigned(struct intel_th_device *thdev) { return thdev->type == INTEL_TH_OUTPUT && - thdev->output.port >= 0; + (thdev->output.port >= 0 || + thdev->output.type == GTH_NONE); } /** @@ -161,8 +185,18 @@ struct intel_th_driver { #define to_intel_th_driver_or_null(_d) \ ((_d) ? to_intel_th_driver(_d) : NULL) +/* + * Subdevice tree structure is as follows: + * + struct intel_th device (pci; dev_{get,set}_drvdata() + * + struct intel_th_device INTEL_TH_SWITCH (GTH) + * + struct intel_th_device INTEL_TH_OUTPUT (MSU, PTI) + * + struct intel_th_device INTEL_TH_SOURCE (STH) + * + * In other words, INTEL_TH_OUTPUT devices are children of INTEL_TH_SWITCH; + * INTEL_TH_SWITCH and INTEL_TH_SOURCE are children of the intel_th device. + */ static inline struct intel_th_device * -to_intel_th_hub(struct intel_th_device *thdev) +to_intel_th_parent(struct intel_th_device *thdev) { struct device *parent = thdev->dev.parent; @@ -172,9 +206,20 @@ to_intel_th_hub(struct intel_th_device *thdev) return to_intel_th_device(parent); } +static inline struct intel_th *to_intel_th(struct intel_th_device *thdev) +{ + if (thdev->type == INTEL_TH_OUTPUT) + thdev = to_intel_th_parent(thdev); + + if (WARN_ON_ONCE(!thdev || thdev->type == INTEL_TH_OUTPUT)) + return NULL; + + return dev_get_drvdata(thdev->dev.parent); +} + struct intel_th * -intel_th_alloc(struct device *dev, struct resource *devres, - unsigned int ndevres, int irq); +intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata, + struct resource *devres, unsigned int ndevres, int irq); void intel_th_free(struct intel_th *th); int intel_th_driver_register(struct intel_th_driver *thdrv); @@ -184,6 +229,7 @@ int intel_th_trace_enable(struct intel_th_device *thdev); int intel_th_trace_disable(struct intel_th_device *thdev); int intel_th_set_output(struct intel_th_device *thdev, unsigned int master); +int intel_th_output_enable(struct intel_th *th, unsigned int otype); enum { TH_MMIO_CONFIG = 0, @@ -191,8 +237,9 @@ enum { TH_MMIO_END, }; -#define TH_SUBDEVICE_MAX 6 #define TH_POSSIBLE_OUTPUTS 8 +/* Total number of possible subdevices: outputs + GTH + STH */ +#define TH_SUBDEVICE_MAX (TH_POSSIBLE_OUTPUTS + 2) #define TH_CONFIGURABLE_MASTERS 256 #define TH_MSC_MAX 2 @@ -201,6 +248,10 @@ enum { * @dev: driver core's device * @thdev: subdevices * @hub: "switch" subdevice (GTH) + * @resource: resources of the entire controller + * @num_thdevs: number of devices in the @thdev array + * @num_resources: number or resources in the @resource array + * @irq: irq number * @id: this Intel TH controller's device ID in the system * @major: device node major for output devices */ @@ -209,6 +260,14 @@ struct intel_th { struct intel_th_device *thdev[TH_SUBDEVICE_MAX]; struct intel_th_device *hub; + struct intel_th_drvdata *drvdata; + + struct resource *resource; + int (*activate)(struct intel_th *); + void (*deactivate)(struct intel_th *); + unsigned int num_thdevs; + unsigned int num_resources; + int irq; int id; int major; @@ -220,6 +279,17 @@ struct intel_th { #endif }; +static inline struct intel_th_device * +to_intel_th_hub(struct intel_th_device *thdev) +{ + if (thdev->type == INTEL_TH_SWITCH) + return thdev; + else if (thdev->type == INTEL_TH_OUTPUT) + return to_intel_th_parent(thdev); + + return to_intel_th(thdev)->hub; +} + /* * Register windows */ @@ -228,6 +298,10 @@ enum { REG_GTH_OFFSET = 0x0000, REG_GTH_LENGTH = 0x2000, + /* Timestamp counter unit (TSCU) */ + REG_TSCU_OFFSET = 0x2000, + REG_TSCU_LENGTH = 0x1000, + /* Software Trace Hub (STH) [0x4000..0x4fff] */ REG_STH_OFFSET = 0x4000, REG_STH_LENGTH = 0x2000, @@ -250,16 +324,6 @@ enum { }; /* - * GTH, output ports configuration - */ -enum { - GTH_NONE = 0, - GTH_MSU, /* memory/usb */ - GTH_CTP, /* Common Trace Port */ - GTH_PTI = 4, /* MIPI-PTI */ -}; - -/* * Scratchpad bits: tell firmware and external debuggers * what we are up to. */ diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index dbbe31df74df..dfb57eaa9f22 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -709,17 +709,17 @@ static int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_blocks) } for (i = 0; i < nr_blocks; i++) { - win->block[i].bdesc = dma_alloc_coherent(msc_dev(msc), size, - &win->block[i].addr, - GFP_KERNEL); + win->block[i].bdesc = + dma_alloc_coherent(msc_dev(msc)->parent->parent, size, + &win->block[i].addr, GFP_KERNEL); + + if (!win->block[i].bdesc) + goto err_nomem; #ifdef CONFIG_X86 /* Set the page as uncached */ set_memory_uc((unsigned long)win->block[i].bdesc, 1); #endif - - if (!win->block[i].bdesc) - goto err_nomem; } win->msc = msc; diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c index 590cf90dd21a..bc9cebc30526 100644 --- a/drivers/hwtracing/intel_th/pci.c +++ b/drivers/hwtracing/intel_th/pci.c @@ -27,9 +27,53 @@ #define BAR_MASK (BIT(TH_MMIO_CONFIG) | BIT(TH_MMIO_SW)) +#define PCI_REG_NPKDSC 0x80 +#define NPKDSC_TSACT BIT(5) + +static int intel_th_pci_activate(struct intel_th *th) +{ + struct pci_dev *pdev = to_pci_dev(th->dev); + u32 npkdsc; + int err; + + if (!INTEL_TH_CAP(th, tscu_enable)) + return 0; + + err = pci_read_config_dword(pdev, PCI_REG_NPKDSC, &npkdsc); + if (!err) { + npkdsc |= NPKDSC_TSACT; + err = pci_write_config_dword(pdev, PCI_REG_NPKDSC, npkdsc); + } + + if (err) + dev_err(&pdev->dev, "failed to read NPKDSC register\n"); + + return err; +} + +static void intel_th_pci_deactivate(struct intel_th *th) +{ + struct pci_dev *pdev = to_pci_dev(th->dev); + u32 npkdsc; + int err; + + if (!INTEL_TH_CAP(th, tscu_enable)) + return; + + err = pci_read_config_dword(pdev, PCI_REG_NPKDSC, &npkdsc); + if (!err) { + npkdsc |= NPKDSC_TSACT; + err = pci_write_config_dword(pdev, PCI_REG_NPKDSC, npkdsc); + } + + if (err) + dev_err(&pdev->dev, "failed to read NPKDSC register\n"); +} + static int intel_th_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { + struct intel_th_drvdata *drvdata = (void *)id->driver_data; struct intel_th *th; int err; @@ -41,11 +85,16 @@ static int intel_th_pci_probe(struct pci_dev *pdev, if (err) return err; - th = intel_th_alloc(&pdev->dev, pdev->resource, + th = intel_th_alloc(&pdev->dev, drvdata, pdev->resource, DEVICE_COUNT_RESOURCE, pdev->irq); if (IS_ERR(th)) return PTR_ERR(th); + th->activate = intel_th_pci_activate; + th->deactivate = intel_th_pci_deactivate; + + pci_set_master(pdev); + return 0; } @@ -56,6 +105,10 @@ static void intel_th_pci_remove(struct pci_dev *pdev) intel_th_free(th); } +static const struct intel_th_drvdata intel_th_2x = { + .tscu_enable = 1, +}; + static const struct pci_device_id intel_th_pci_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9d26), @@ -93,7 +146,17 @@ static const struct pci_device_id intel_th_pci_id_table[] = { { /* Gemini Lake */ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x318e), - .driver_data = (kernel_ulong_t)0, + .driver_data = (kernel_ulong_t)&intel_th_2x, + }, + { + /* Cannon Lake H */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa326), + .driver_data = (kernel_ulong_t)&intel_th_2x, + }, + { + /* Cannon Lake LP */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9da6), + .driver_data = (kernel_ulong_t)&intel_th_2x, }, { 0 }, }; diff --git a/drivers/hwtracing/intel_th/pti.c b/drivers/hwtracing/intel_th/pti.c index 35738b5bfccd..e96a1fcb57b2 100644 --- a/drivers/hwtracing/intel_th/pti.c +++ b/drivers/hwtracing/intel_th/pti.c @@ -1,7 +1,7 @@ /* * Intel(R) Trace Hub PTI output driver * - * Copyright (C) 2014-2015 Intel Corporation. + * Copyright (C) 2014-2016 Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -34,6 +34,8 @@ struct pti_device { unsigned int freeclk; unsigned int clkdiv; unsigned int patgen; + unsigned int lpp_dest_mask; + unsigned int lpp_dest; }; /* map PTI widths to MODE settings of PTI_CTL register */ @@ -163,6 +165,7 @@ static int intel_th_pti_activate(struct intel_th_device *thdev) ctl |= PTI_FCEN; ctl |= pti->mode << __ffs(PTI_MODE); ctl |= pti->clkdiv << __ffs(PTI_CLKDIV); + ctl |= pti->lpp_dest << __ffs(LPP_DEST); iowrite32(ctl, pti->base + REG_PTI_CTL); @@ -192,6 +195,15 @@ static void read_hw_config(struct pti_device *pti) pti->mode = pti_width_mode(4); if (!pti->clkdiv) pti->clkdiv = 1; + + if (pti->thdev->output.type == GTH_LPP) { + if (ctl & LPP_PTIPRESENT) + pti->lpp_dest_mask |= LPP_DEST_PTI; + if (ctl & LPP_BSSBPRESENT) + pti->lpp_dest_mask |= LPP_DEST_EXI; + if (ctl & LPP_DEST) + pti->lpp_dest = 1; + } } static int intel_th_pti_probe(struct intel_th_device *thdev) @@ -239,10 +251,103 @@ static struct intel_th_driver intel_th_pti_driver = { }, }; -module_driver(intel_th_pti_driver, - intel_th_driver_register, - intel_th_driver_unregister); +static const char * const lpp_dest_str[] = { "pti", "exi" }; + +static ssize_t lpp_dest_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct pti_device *pti = dev_get_drvdata(dev); + ssize_t ret = 0; + int i; + + for (i = ARRAY_SIZE(lpp_dest_str) - 1; i >= 0; i--) { + const char *fmt = pti->lpp_dest == i ? "[%s] " : "%s "; + + if (!(pti->lpp_dest_mask & BIT(i))) + continue; + + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + fmt, lpp_dest_str[i]); + } + + if (ret) + buf[ret - 1] = '\n'; + + return ret; +} + +static ssize_t lpp_dest_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct pti_device *pti = dev_get_drvdata(dev); + ssize_t ret = -EINVAL; + int i; + + for (i = 0; i < ARRAY_SIZE(lpp_dest_str); i++) + if (sysfs_streq(buf, lpp_dest_str[i])) + break; + + if (i < ARRAY_SIZE(lpp_dest_str) && pti->lpp_dest_mask & BIT(i)) { + pti->lpp_dest = i; + ret = size; + } + + return ret; +} + +static DEVICE_ATTR_RW(lpp_dest); + +static struct attribute *lpp_output_attrs[] = { + &dev_attr_mode.attr, + &dev_attr_freerunning_clock.attr, + &dev_attr_clock_divider.attr, + &dev_attr_lpp_dest.attr, + NULL, +}; + +static struct attribute_group lpp_output_group = { + .attrs = lpp_output_attrs, +}; + +static struct intel_th_driver intel_th_lpp_driver = { + .probe = intel_th_pti_probe, + .remove = intel_th_pti_remove, + .activate = intel_th_pti_activate, + .deactivate = intel_th_pti_deactivate, + .attr_group = &lpp_output_group, + .driver = { + .name = "lpp", + .owner = THIS_MODULE, + }, +}; + +static int __init intel_th_pti_lpp_init(void) +{ + int err; + + err = intel_th_driver_register(&intel_th_pti_driver); + if (err) + return err; + + err = intel_th_driver_register(&intel_th_lpp_driver); + if (err) { + intel_th_driver_unregister(&intel_th_pti_driver); + return err; + } + + return 0; +} + +module_init(intel_th_pti_lpp_init); + +static void __exit intel_th_pti_lpp_exit(void) +{ + intel_th_driver_unregister(&intel_th_pti_driver); + intel_th_driver_unregister(&intel_th_lpp_driver); +} + +module_exit(intel_th_pti_lpp_exit); MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Intel(R) Trace Hub PTI output driver"); +MODULE_DESCRIPTION("Intel(R) Trace Hub PTI/LPP output driver"); MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>"); diff --git a/drivers/hwtracing/intel_th/pti.h b/drivers/hwtracing/intel_th/pti.h index 20883f5628cf..30827be67b4c 100644 --- a/drivers/hwtracing/intel_th/pti.h +++ b/drivers/hwtracing/intel_th/pti.h @@ -23,7 +23,15 @@ enum { #define PTI_EN BIT(0) #define PTI_FCEN BIT(1) #define PTI_MODE 0xf0 +#define LPP_PTIPRESENT BIT(8) +#define LPP_BSSBPRESENT BIT(9) #define PTI_CLKDIV 0x000f0000 #define PTI_PATGENMODE 0x00f00000 +#define LPP_DEST BIT(25) +#define LPP_BSSBACT BIT(30) +#define LPP_LPPBUSY BIT(31) + +#define LPP_DEST_PTI BIT(0) +#define LPP_DEST_EXI BIT(1) #endif /* __INTEL_TH_STH_H__ */ diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 0e731143f6a4..9414900575d8 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -566,7 +566,7 @@ static int stm_char_policy_set_ioctl(struct stm_file *stmf, void __user *arg) if (copy_from_user(&size, arg, sizeof(size))) return -EFAULT; - if (size >= PATH_MAX + sizeof(*id)) + if (size < sizeof(*id) || size >= PATH_MAX + sizeof(*id)) return -EINVAL; /* |