[PATCH v3 01/11] coresight-etm4x: Adding CoreSight ETM4x driver
Greg KH
gregkh at linuxfoundation.org
Sun May 10 06:37:10 PDT 2015
On Wed, May 06, 2015 at 09:27:17AM -0600, Mathieu Poirier wrote:
> From: Pratik Patel <pratikp at codeaurora.org>
>
> This driver manages the CoreSight ETMv4 (Embedded Trace Macrocell) IP block
> to support HW assisted tracing on ARMv7 and ARMv8 architectures.
>
> Signed-off-by: Pratik Patel <pratikp at codeaurora.org>
> Signed-off-by: Kaixu Xia <xiakaixu at huawei.com>
> Signed-off-by: Mathieu Poirier <mathieu.poirier at linaro.org>
> ---
> .../ABI/testing/sysfs-bus-coresight-devices-etm4x | 28 +
> drivers/hwtracing/coresight/Kconfig | 11 +
> drivers/hwtracing/coresight/Makefile | 1 +
> drivers/hwtracing/coresight/coresight-etm4x.c | 825 +++++++++++++++++++++
> drivers/hwtracing/coresight/coresight-etm4x.h | 391 ++++++++++
> 5 files changed, 1256 insertions(+)
> create mode 100644 Documentation/ABI/testing/sysfs-bus-coresight-devices-etm4x
> create mode 100644 drivers/hwtracing/coresight/coresight-etm4x.c
> create mode 100644 drivers/hwtracing/coresight/coresight-etm4x.h
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm4x b/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm4x
> new file mode 100644
> index 000000000000..a4b623871ca0
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm4x
> @@ -0,0 +1,28 @@
> +What: /sys/bus/coresight/devices/<memory_map>.etm/enable_source
> +Date: April 2015
> +KernelVersion: 4.01
> +Contact: Mathieu Poirier <mathieu.poirier at linaro.org>
> +Description: (RW) Enable/disable tracing on this specific trace entiry.
> + Enabling a source implies the source has been configured
> + properly and a sink has been identidifed for it. The path
> + of coresight components linking the source to the sink is
> + configured and managed automatically by the coresight framework.
> +
> +What: /sys/bus/coresight/devices/<memory_map>.etm/status
> +Date: April 2015
> +KernelVersion: 4.01
> +Contact: Mathieu Poirier <mathieu.poirier at linaro.org>
> +Description: (R) List various control and status registers. The specific
> + layout and content is driver specific.
> +
> +What: /sys/bus/coresight/devices/<memory_map>.etm/mgmt
> +Date: April 2015
> +KernelVersion: 4.01
> +Contact: Mathieu Poirier <mathieu.poirier at linaro.org>
> +Description: (R) Provides the current value of all the management registers.
> +
> +What: /sys/bus/coresight/devices/<memory_map>.etm/trcidr
> +Date: April 2015
> +KernelVersion: 4.01
> +Contact: Mathieu Poirier <mathieu.poirier at linaro.org>
> +Description: (R) Provides value of all the ID registers (TRCIDRx).
> diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig
> index fc1f1ae7a49d..8fac01eedee7 100644
> --- a/drivers/hwtracing/coresight/Kconfig
> +++ b/drivers/hwtracing/coresight/Kconfig
> @@ -58,4 +58,15 @@ config CORESIGHT_SOURCE_ETM3X
> which allows tracing the instructions that a processor is executing
> This is primarily useful for instruction level tracing. Depending
> the ETM version data tracing may also be available.
> +
> +config CORESIGHT_SOURCE_ETM4X
> + bool "CoreSight Embedded Trace Macrocell 4.x driver"
> + depends on ARM64
> + select CORESIGHT_LINKS_AND_SINKS
> + help
> + This driver provides support for the ETM4.x tracer module, tracing the
> + instructions that a processor is executing. This is primarily useful
> + for instruction level tracing. Depending on the implemented version
> + data tracing may also be available.
> +
> endif
> diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
> index 4b4bec890ef5..0af28d43465c 100644
> --- a/drivers/hwtracing/coresight/Makefile
> +++ b/drivers/hwtracing/coresight/Makefile
> @@ -9,3 +9,4 @@ obj-$(CONFIG_CORESIGHT_SINK_ETBV10) += coresight-etb10.o
> obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \
> coresight-replicator.o
> obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o
> +obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o
> diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c
> new file mode 100644
> index 000000000000..db0bea4d4661
> --- /dev/null
> +++ b/drivers/hwtracing/coresight/coresight-etm4x.c
> @@ -0,0 +1,825 @@
> +/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/moduleparam.h>
> +#include <linux/init.h>
> +#include <linux/types.h>
> +#include <linux/device.h>
> +#include <linux/io.h>
> +#include <linux/err.h>
> +#include <linux/fs.h>
> +#include <linux/slab.h>
> +#include <linux/delay.h>
> +#include <linux/smp.h>
> +#include <linux/sysfs.h>
> +#include <linux/stat.h>
> +#include <linux/clk.h>
> +#include <linux/cpu.h>
> +#include <linux/coresight.h>
> +#include <linux/pm_wakeup.h>
> +#include <linux/amba/bus.h>
> +#include <linux/seq_file.h>
> +#include <linux/uaccess.h>
> +#include <linux/pm_runtime.h>
> +#include <asm/sections.h>
> +
> +#include "coresight-etm4x.h"
> +
> +static int boot_enable;
> +module_param_named(boot_enable, boot_enable, int, S_IRUGO);
> +
> +/* The number of ETMv4 currently registered */
> +static int etm4_count;
> +static struct etmv4_drvdata *etmdrvdata[NR_CPUS];
> +
> +static void etm4_os_unlock(void *info)
> +{
> + struct etmv4_drvdata *drvdata = (struct etmv4_drvdata *)info;
> +
> + /* Writing any value to ETMOSLAR unlocks the trace registers */
> + writel_relaxed(0x0, drvdata->base + TRCOSLAR);
> + isb();
> +}
> +
> +static bool etm4_arch_supported(u8 arch)
> +{
> + switch (arch) {
> + case ETM_ARCH_V4:
> + break;
> + default:
> + return false;
> + }
> + return true;
> +}
> +
> +static int etm4_trace_id(struct coresight_device *csdev)
> +{
> + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> + unsigned long flags;
> + int trace_id = -1;
> +
> + if (!drvdata->enable)
> + return drvdata->trcid;
> +
> + pm_runtime_get_sync(drvdata->dev);
> + spin_lock_irqsave(&drvdata->spinlock, flags);
> +
> + CS_UNLOCK(drvdata->base);
> + trace_id = readl_relaxed(drvdata->base + TRCTRACEIDR);
> + trace_id &= ETM_TRACEID_MASK;
> + CS_LOCK(drvdata->base);
> +
> + spin_unlock_irqrestore(&drvdata->spinlock, flags);
> + pm_runtime_put(drvdata->dev);
> +
> + return trace_id;
> +}
> +
> +static void etm4_enable_hw(void *info)
> +{
> + int i;
> + struct etmv4_drvdata *drvdata = info;
> +
> + CS_UNLOCK(drvdata->base);
> +
> + etm4_os_unlock(drvdata);
> +
> + /* Disable the trace unit before programming trace registers */
> + writel_relaxed(0, drvdata->base + TRCPRGCTLR);
> +
> + /* wait for TRCSTATR.IDLE to go up */
> + if (coresight_timeout(drvdata->base, TRCSTATR, TRCSTATR_IDLE_BIT, 1))
> + dev_err(drvdata->dev,
> + "timeout observed when probing at offset %#x\n",
> + TRCSTATR);
> +
> + writel_relaxed(drvdata->pe_sel, drvdata->base + TRCPROCSELR);
> + writel_relaxed(drvdata->cfg, drvdata->base + TRCCONFIGR);
> + /* nothing specific implemented */
> + writel_relaxed(0x0, drvdata->base + TRCAUXCTLR);
> + writel_relaxed(drvdata->eventctrl0, drvdata->base + TRCEVENTCTL0R);
> + writel_relaxed(drvdata->eventctrl1, drvdata->base + TRCEVENTCTL1R);
> + writel_relaxed(drvdata->stall_ctrl, drvdata->base + TRCSTALLCTLR);
> + writel_relaxed(drvdata->ts_ctrl, drvdata->base + TRCTSCTLR);
> + writel_relaxed(drvdata->syncfreq, drvdata->base + TRCSYNCPR);
> + writel_relaxed(drvdata->ccctlr, drvdata->base + TRCCCCTLR);
> + writel_relaxed(drvdata->bb_ctrl, drvdata->base + TRCBBCTLR);
> + writel_relaxed(drvdata->trcid, drvdata->base + TRCTRACEIDR);
> + writel_relaxed(drvdata->vinst_ctrl, drvdata->base + TRCVICTLR);
> + writel_relaxed(drvdata->viiectlr, drvdata->base + TRCVIIECTLR);
> + writel_relaxed(drvdata->vissctlr,
> + drvdata->base + TRCVISSCTLR);
> + writel_relaxed(drvdata->vipcssctlr,
> + drvdata->base + TRCVIPCSSCTLR);
> + for (i = 0; i < drvdata->nrseqstate - 1; i++)
> + writel_relaxed(drvdata->seq_ctrl[i],
> + drvdata->base + TRCSEQEVRn(i));
> + writel_relaxed(drvdata->seq_rst, drvdata->base + TRCSEQRSTEVR);
> + writel_relaxed(drvdata->seq_state, drvdata->base + TRCSEQSTR);
> + writel_relaxed(drvdata->ext_inp, drvdata->base + TRCEXTINSELR);
> + for (i = 0; i < drvdata->nr_cntr; i++) {
> + writel_relaxed(drvdata->cntrldvr[i],
> + drvdata->base + TRCCNTRLDVRn(i));
> + writel_relaxed(drvdata->cntr_ctrl[i],
> + drvdata->base + TRCCNTCTLRn(i));
> + writel_relaxed(drvdata->cntr_val[i],
> + drvdata->base + TRCCNTVRn(i));
> + }
> + for (i = 0; i < drvdata->nr_resource; i++)
> + writel_relaxed(drvdata->res_ctrl[i],
> + drvdata->base + TRCRSCTLRn(i));
> +
> + for (i = 0; i < drvdata->nr_ss_cmp; i++) {
> + writel_relaxed(drvdata->ss_ctrl[i],
> + drvdata->base + TRCSSCCRn(i));
> + writel_relaxed(drvdata->ss_status[i],
> + drvdata->base + TRCSSCSRn(i));
> + writel_relaxed(drvdata->ss_pe_cmp[i],
> + drvdata->base + TRCSSPCICRn(i));
> + }
> + for (i = 0; i < drvdata->nr_addr_cmp; i++) {
> + writeq_relaxed(drvdata->addr_val[i],
> + drvdata->base + TRCACVRn(i));
> + writeq_relaxed(drvdata->addr_acc[i],
> + drvdata->base + TRCACATRn(i));
> + }
> + for (i = 0; i < drvdata->numcidc; i++)
> + writeq_relaxed(drvdata->ctxid_val[i],
> + drvdata->base + TRCCIDCVRn(i));
> + writel_relaxed(drvdata->ctxid_mask0, drvdata->base + TRCCIDCCTLR0);
> + writel_relaxed(drvdata->ctxid_mask1, drvdata->base + TRCCIDCCTLR1);
> +
> + for (i = 0; i < drvdata->numvmidc; i++)
> + writeq_relaxed(drvdata->vmid_val[i],
> + drvdata->base + TRCVMIDCVRn(i));
> + writel_relaxed(drvdata->vmid_mask0, drvdata->base + TRCVMIDCCTLR0);
> + writel_relaxed(drvdata->vmid_mask1, drvdata->base + TRCVMIDCCTLR1);
> +
> + /* Enable the trace unit */
> + writel_relaxed(1, drvdata->base + TRCPRGCTLR);
> +
> + /* wait for TRCSTATR.IDLE to go back down to '0' */
> + if (coresight_timeout(drvdata->base, TRCSTATR, TRCSTATR_IDLE_BIT, 0))
> + dev_err(drvdata->dev,
> + "timeout observed when probing at offset %#x\n",
> + TRCSTATR);
> +
> + CS_LOCK(drvdata->base);
> +
> + dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu);
> +}
> +
> +static int etm4_enable(struct coresight_device *csdev)
> +{
> + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> + int ret;
> +
> + pm_runtime_get_sync(drvdata->dev);
> + spin_lock(&drvdata->spinlock);
> +
> + /*
> + * Executing etm4_enable_hw on the cpu whose ETM is being enabled
> + * ensures that register writes occur when cpu is powered.
> + */
> + ret = smp_call_function_single(drvdata->cpu,
> + etm4_enable_hw, drvdata, 1);
> + if (ret)
> + goto err;
> + drvdata->enable = true;
> + drvdata->sticky_enable = true;
> +
> + spin_unlock(&drvdata->spinlock);
> +
> + dev_info(drvdata->dev, "ETM tracing enabled\n");
> + return 0;
> +err:
> + spin_unlock(&drvdata->spinlock);
> + pm_runtime_put(drvdata->dev);
> + return ret;
> +}
> +
> +static void etm4_disable_hw(void *info)
> +{
> + u32 control;
> + struct etmv4_drvdata *drvdata = info;
> +
> + CS_UNLOCK(drvdata->base);
> +
> + control = readl_relaxed(drvdata->base + TRCPRGCTLR);
> +
> + /* EN, bit[0] Trace unit enable bit */
> + control &= ~0x1;
> +
> + /* make sure everything completes before disabling */
> + mb();
> + isb();
> + writel_relaxed(control, drvdata->base + TRCPRGCTLR);
> +
> + CS_LOCK(drvdata->base);
> +
> + dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu);
> +}
> +
> +static void etm4_disable(struct coresight_device *csdev)
> +{
> + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> +
> + /*
> + * Taking hotplug lock here protects from clocks getting disabled
> + * with tracing being left on (crash scenario) if user disable occurs
> + * after cpu online mask indicates the cpu is offline but before the
> + * DYING hotplug callback is serviced by the ETM driver.
> + */
> + get_online_cpus();
> + spin_lock(&drvdata->spinlock);
> +
> + /*
> + * Executing etm4_disable_hw on the cpu whose ETM is being disabled
> + * ensures that register writes occur when cpu is powered.
> + */
> + smp_call_function_single(drvdata->cpu, etm4_disable_hw, drvdata, 1);
> + drvdata->enable = false;
> +
> + spin_unlock(&drvdata->spinlock);
> + put_online_cpus();
> +
> + pm_runtime_put(drvdata->dev);
> +
> + dev_info(drvdata->dev, "ETM tracing disabled\n");
> +}
> +
> +static const struct coresight_ops_source etm4_source_ops = {
> + .trace_id = etm4_trace_id,
> + .enable = etm4_enable,
> + .disable = etm4_disable,
> +};
> +
> +static const struct coresight_ops etm4_cs_ops = {
> + .source_ops = &etm4_source_ops,
> +};
> +
> +static ssize_t status_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + int ret;
> + unsigned long flags;
> + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
> +
> + pm_runtime_get_sync(drvdata->dev);
> +
> + spin_lock_irqsave(&drvdata->spinlock, flags);
> + CS_UNLOCK(drvdata->base);
> + ret = sprintf(buf,
> + "TRCCONFIGR:\t0x%08x\n"
> + "TRCEVENTCTL0R:\t0x%08x\n"
> + "TRCEVENTCTL1R:\t0x%08x\n"
> + "TRCSTALLCTLR:\t0x%08x\n"
> + "TRCSYNCPR:\t0x%08x\n"
> + "TRCTRACEIDR:\t0x%08x\n"
> + "TRCTSCTLR:\t0x%08x\n"
> + "TRCVDARCCTLR:\t0x%08x\n"
> + "TRCVDCTLR:\t0x%08x\n"
> + "TRCVDSACCTLR:\t0x%08x\n"
> + "TRCVICTLR:\t0x%08x\n"
> + "TRCVIIECTLR:\t0x%08x\n"
> + "TRCVISSCTLR:\t0x%08x\n"
> + "TRCPRGCTLR:\t0x%08x\n"
> + "CPU affinity:\t%d\n",
That is not one-value-per-file, as is the rules for sysfs. Sorry,
please fix up.
More information about the linux-arm-kernel
mailing list