Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 46b4dd0c authored by Alex Shi's avatar Alex Shi
Browse files

Merge branch 'v4.4/topic/coresight' into linux-linaro-lsk-v4.4

parents c66b2190 09e1b6ff
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -1007,6 +1007,10 @@ F: drivers/hwtracing/coresight/*
F:	Documentation/trace/coresight.txt
F:	Documentation/devicetree/bindings/arm/coresight.txt
F:	Documentation/ABI/testing/sysfs-bus-coresight-devices-*
F:	tools/perf/arch/arm/util/pmu.c
F:	tools/perf/arch/arm/util/auxtrace.c
F:	tools/perf/arch/arm/util/cs_etm.c
F:	tools/perf/arch/arm/util/cs_etm.h

ARM/CORGI MACHINE SUPPORT
M:	Richard Purdie <rpurdie@rpsys.net>
+305 −0
Original line number Diff line number Diff line
@@ -143,5 +143,310 @@
				     <&A53_3>;
	};

	etr@20070000 {
		compatible = "arm,coresight-tmc", "arm,primecell";
		reg = <0 0x20070000 0 0x1000>;

		clocks = <&soc_smc50mhz>;
		clock-names = "apb_pclk";
		port {
			etr_in_port: endpoint {
				slave-mode;
				remote-endpoint = <&replicator_out_port1>;
			};
		};
	};

	tpiu@20030000 {
		compatible = "arm,coresight-tpiu", "arm,primecell";
		reg = <0 0x20030000 0 0x1000>;

		clocks = <&soc_smc50mhz>;
		clock-names = "apb_pclk";
		port {
			tpiu_in_port: endpoint {
				slave-mode;
				remote-endpoint = <&replicator_out_port0>;
			};
		};
	};

	replicator@20020000 {
		/* non-configurable replicators don't show up on the
		 * AMBA bus.  As such no need to add "arm,primecell".
		 */
		compatible = "arm,coresight-replicator";

		ports {
			#address-cells = <1>;
			#size-cells = <0>;

			/* replicator output ports */
			port@0 {
				reg = <0>;
				replicator_out_port0: endpoint {
					remote-endpoint = <&tpiu_in_port>;
				};
			};

			port@1 {
				reg = <1>;
				replicator_out_port1: endpoint {
					remote-endpoint = <&etr_in_port>;
				};
			};

			/* replicator input port */
			port@2 {
				reg = <0>;
				replicator_in_port0: endpoint {
					slave-mode;
					remote-endpoint = <&etf_out_port>;
				};
			};
		};
	};

	etf@20010000 {
		compatible = "arm,coresight-tmc", "arm,primecell";
		reg = <0 0x20010000 0 0x1000>;

		clocks = <&soc_smc50mhz>;
		clock-names = "apb_pclk";
		ports {
			#address-cells = <1>;
			#size-cells = <0>;

			/* input port */
			port@0 {
				reg = <0>;
				etf_in_port: endpoint {
					slave-mode;
					remote-endpoint =
						<&main_funnel_out_port>;
				};
			};

			/* output port */
			port@1 {
				reg = <0>;
				etf_out_port: endpoint {
					remote-endpoint =
						<&replicator_in_port0>;
				};
			};
		};
	};

	main_funnel@20040000 {
		compatible = "arm,coresight-funnel", "arm,primecell";
		reg = <0 0x20040000 0 0x1000>;

		clocks = <&soc_smc50mhz>;
		clock-names = "apb_pclk";
		ports {
			#address-cells = <1>;
			#size-cells = <0>;

			port@0 {
				reg = <0>;
				main_funnel_out_port: endpoint {
					remote-endpoint =
						<&etf_in_port>;
				};
			};

			port@1 {
				reg = <0>;
				main_funnel_in_port0: endpoint {
					slave-mode;
					remote-endpoint =
						<&A72_57_funnel_out_port>;
				};
			};

			port@2 {
				reg = <1>;
				main_funnel_in_port1: endpoint {
					slave-mode;
					remote-endpoint = <&A53_funnel_out_port>;
				};
			};

		};
	};

	A72_57_funnel@220c0000 {
		compatible = "arm,coresight-funnel", "arm,primecell";
		reg = <0 0x220c0000 0 0x1000>;

		clocks = <&soc_smc50mhz>;
		clock-names = "apb_pclk";
		ports {
			#address-cells = <1>;
			#size-cells = <0>;

			port@0 {
				reg = <0>;
				A72_57_funnel_out_port: endpoint {
					remote-endpoint =
						<&main_funnel_in_port0>;
				};
			};

			port@1 {
				reg = <0>;
				A72_57_funnel_in_port0: endpoint {
					slave-mode;
					remote-endpoint =
							<&A72_57_etm0_out_port>;
				};
			};

			port@2 {
				reg = <1>;
				A72_57_funnel_in_port1: endpoint {
					slave-mode;
					remote-endpoint =
							<&A72_57_etm1_out_port>;
				};
			};
		};
	};

	A53_funnel@220c0000 {
		compatible = "arm,coresight-funnel", "arm,primecell";
		reg = <0 0x230c0000 0 0x1000>;

		clocks = <&soc_smc50mhz>;
		clock-names = "apb_pclk";
		ports {
			#address-cells = <1>;
			#size-cells = <0>;

			port@0 {
				reg = <0>;
				A53_funnel_out_port: endpoint {
					remote-endpoint =
						<&main_funnel_in_port1>;
				};
			};

			port@1 {
				reg = <0>;
				A53_funnel_in_port0: endpoint {
					slave-mode;
					remote-endpoint = <&A53_etm0_out_port>;
				};
			};

			port@2 {
				reg = <1>;
				A53_funnel_in_port1: endpoint {
					slave-mode;
					remote-endpoint = <&A53_etm1_out_port>;
				};
			};
			port@3 {
				reg = <2>;
				A53_funnel_in_port2: endpoint {
					slave-mode;
					remote-endpoint = <&A53_etm2_out_port>;
				};
			};
			port@4 {
				reg = <3>;
				A53_funnel_in_port3: endpoint {
					slave-mode;
					remote-endpoint = <&A53_etm3_out_port>;
				};
			};
		};
	};

	etm@22040000 {
		compatible = "arm,coresight-etm4x", "arm,primecell";
		reg = <0 0x22040000 0 0x1000>;

		cpu = <&A57_0>;
		clocks = <&soc_smc50mhz>;
		clock-names = "apb_pclk";
		port {
			A72_57_etm0_out_port: endpoint {
				remote-endpoint = <&A72_57_funnel_in_port0>;
			};
		};
	};

	etm@22140000 {
		compatible = "arm,coresight-etm4x", "arm,primecell";
		reg = <0 0x22140000 0 0x1000>;

		cpu = <&A57_1>;
		clocks = <&soc_smc50mhz>;
		clock-names = "apb_pclk";
		port {
			A72_57_etm1_out_port: endpoint {
				remote-endpoint = <&A72_57_funnel_in_port1>;
			};
		};
	};

	etm@23040000 {
		compatible = "arm,coresight-etm4x", "arm,primecell";
		reg = <0 0x23040000 0 0x1000>;

		cpu = <&A53_0>;
		clocks = <&soc_smc50mhz>;
		clock-names = "apb_pclk";
		port {
			A53_etm0_out_port: endpoint {
				remote-endpoint = <&A53_funnel_in_port0>;
			};
		};
	};

	etm@23140000 {
		compatible = "arm,coresight-etm4x", "arm,primecell";
		reg = <0 0x23140000 0 0x1000>;

		cpu = <&A53_1>;
		clocks = <&soc_smc50mhz>;
		clock-names = "apb_pclk";
		port {
			A53_etm1_out_port: endpoint {
				remote-endpoint = <&A53_funnel_in_port1>;
			};
		};
	};

	etm@23240000 {
		compatible = "arm,coresight-etm4x", "arm,primecell";
		reg = <0 0x23240000 0 0x1000>;

		cpu = <&A53_2>;
		clocks = <&soc_smc50mhz>;
		clock-names = "apb_pclk";
		port {
			A53_etm2_out_port: endpoint {
				remote-endpoint = <&A53_funnel_in_port2>;
			};
		};
	};

	etm@23340000 {
		compatible = "arm,coresight-etm4x", "arm,primecell";
		reg = <0 0x23340000 0 0x1000>;

		cpu = <&A53_3>;
		clocks = <&soc_smc50mhz>;
		clock-names = "apb_pclk";
		port {
			A53_etm3_out_port: endpoint {
				remote-endpoint = <&A53_funnel_in_port3>;
			};
		};
	};

	#include "juno-base.dtsi"
};
+120 −3
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include <linux/list.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/parser.h>
#include <linux/perf_event.h>
#include <linux/slab.h>
#include <linux/types.h>
@@ -46,6 +47,17 @@ struct etm_event_data {
	struct list_head **path;
};

/**
 * struct perf_pmu_drv_config - Driver specific configuration needed
 *				before a session can start.
 * @sink:		The name of the sink this session should use.
 * @entry:		Hook to the event->drv_configs list.
 */
struct perf_pmu_drv_config {
	char *sink;
	struct list_head entry;
};

static DEFINE_PER_CPU(struct perf_output_handle, ctx_handle);
static DEFINE_PER_CPU(struct coresight_device *, csdev_src);

@@ -155,15 +167,28 @@ static void etm_free_aux(void *data)
	schedule_work(&event_data->work);
}

static void *etm_setup_aux(int event_cpu, void **pages,
static void *etm_setup_aux(struct perf_event *event, void **pages,
			   int nr_pages, bool overwrite)
{
	int cpu;
	char *sink_def = NULL;
	cpumask_t *mask;
	struct coresight_device *sink;
	struct etm_event_data *event_data = NULL;
	struct perf_pmu_drv_config *drv_config;

	/*
	 * Search the driver configurables looking for a sink.  If more than
	 * one sink was specified the last one is taken.
	 */
	list_for_each_entry(drv_config, &event->drv_configs, entry) {
		if (drv_config && drv_config->sink) {
			sink_def = drv_config->sink;
			break;
		}
	}

	event_data = alloc_event_data(event_cpu);
	event_data = alloc_event_data(event->cpu);
	if (!event_data)
		return NULL;

@@ -184,7 +209,7 @@ 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_def);
		if (!event_data->path[cpu])
			goto err;
	}
@@ -342,6 +367,95 @@ static void etm_event_del(struct perf_event *event, int mode)
	etm_event_stop(event, PERF_EF_UPDATE);
}

enum {
	ETM_TOKEN_SINK_CPU,
	ETM_TOKEN_SINK,
	ETM_TOKEN_ERR,
};

static const match_table_t drv_cfg_tokens = {
	{ETM_TOKEN_SINK_CPU, "sink=cpu%d:%s"},
	{ETM_TOKEN_SINK, "sink=%s"},
	{ETM_TOKEN_ERR,	NULL},
};

static int etm_get_drv_configs(struct perf_event *event, void __user *arg)
{
	char *config, *sink = NULL;
	int cpu = -1, token, ret = 0;
	substring_t args[MAX_OPT_ARGS];
	struct perf_pmu_drv_config *drv_config = NULL;

	/* Make user supplied input usable */
	config = strndup_user(arg, PAGE_SIZE);
	if (IS_ERR(config))
		return PTR_ERR(config);

	/* See above declared @drv_cfg_tokens for the usable formats */
	token = match_token(config, drv_cfg_tokens, args);
	switch (token) {
	case ETM_TOKEN_SINK:
		/* Just a sink has been specified */
		sink = match_strdup(&args[0]);
		if (IS_ERR(sink)) {
			ret = PTR_ERR(sink);
			goto err;
		}
		break;
	case ETM_TOKEN_SINK_CPU:
		/* We have a sink and a CPU */
		if (match_int(&args[0], &cpu)) {
			ret = -EINVAL;
			goto err;
		}
		sink = match_strdup(&args[1]);
		if (IS_ERR(sink)) {
			ret = PTR_ERR(sink);
			goto err;
		}
		break;
	default:
		ret = -EINVAL;
		goto err;
	}

	/* If the CPUs don't match the sink is destined to another path */
	if (event->cpu != cpu)
		goto err;

	/*
	 * We have a valid configuration, allocate memory and add to the list
	 * of driver configurables.
	 */
	drv_config = kzalloc(sizeof(*drv_config), GFP_KERNEL);
	if (IS_ERR(drv_config)) {
		ret = PTR_ERR(drv_config);
		goto err;
	}

	drv_config->sink = sink;
	list_add(&drv_config->entry, &event->drv_configs);

out:
	kfree(config);
	return ret;

err:
	kfree(sink);
	goto out;
}

static void etm_free_drv_configs(struct perf_event *event)
{
	struct perf_pmu_drv_config *config, *itr;

	list_for_each_entry_safe(config, itr, &event->drv_configs, entry) {
		list_del(&config->entry);
		kfree(config->sink);
		kfree(config);
	}
}

int etm_perf_symlink(struct coresight_device *csdev, bool link)
{
	char entry[sizeof("cpu9999999")];
@@ -383,6 +497,9 @@ static int __init etm_perf_init(void)
	etm_pmu.stop		= etm_event_stop;
	etm_pmu.add		= etm_event_add;
	etm_pmu.del		= etm_event_del;
	etm_pmu.get_drv_configs	= etm_get_drv_configs;
	etm_pmu.free_drv_configs
				= etm_free_drv_configs;

	ret = perf_pmu_register(&etm_pmu, CORESIGHT_ETM_PMU_NAME, -1);
	if (ret == 0)
+2 −1
Original line number Diff line number Diff line
@@ -94,7 +94,8 @@ 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 list_head *coresight_build_path(struct coresight_device *csdev,
				       const char *sink);
void coresight_release_path(struct list_head *path);

#ifdef CONFIG_CORESIGHT_SOURCE_ETM3X
+247 −7
Original line number Diff line number Diff line
@@ -15,11 +15,30 @@
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/circ_buf.h>
#include <linux/coresight.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>

#include "coresight-priv.h"
#include "coresight-tmc.h"

/**
 * struct cs_etr_buffer - keep track of a recording session' specifics
 * @tmc:	generic portion of the TMC buffers
 * @paddr:	the physical address of a DMA'able contiguous memory area
 * @vaddr:	the virtual address associated to @paddr
 * @size:	how much memory we have, starting at @paddr
 * @dev:	the device @vaddr has been tied to
 */
struct cs_etr_buffers {
	struct cs_buffers	tmc;
	dma_addr_t		paddr;
	void __iomem		*vaddr;
	u32			size;
	struct device		*dev;
};

void tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
{
	u32 axictl;
@@ -235,9 +254,233 @@ static void tmc_disable_etr_sink(struct coresight_device *csdev)
	dev_info(drvdata->dev, "TMC-ETR disabled\n");
}

static void *tmc_alloc_etr_buffer(struct coresight_device *csdev, int cpu,
				  void **pages, int nr_pages, bool overwrite)
{
	int node;
	struct cs_etr_buffers *buf;
	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);

	if (cpu == -1)
		cpu = smp_processor_id();
	node = cpu_to_node(cpu);

	/* Allocate memory structure for interaction with Perf */
	buf = kzalloc_node(sizeof(struct cs_etr_buffers), GFP_KERNEL, node);
	if (!buf)
		return NULL;

	buf->dev = drvdata->dev;
	buf->size = drvdata->size;
	buf->vaddr = dma_alloc_coherent(buf->dev, buf->size,
					&buf->paddr, GFP_KERNEL);
	if (!buf->vaddr) {
		kfree(buf);
		return NULL;
	}

	buf->tmc.snapshot = overwrite;
	buf->tmc.nr_pages = nr_pages;
	buf->tmc.data_pages = pages;

	return buf;
}

static void tmc_free_etr_buffer(void *config)
{
	struct cs_etr_buffers *buf = config;

	dma_free_coherent(buf->dev, buf->size, buf->vaddr, buf->paddr);
	kfree(buf);
}

static int tmc_set_etr_buffer(struct coresight_device *csdev,
			      struct perf_output_handle *handle,
			      void *sink_config)
{
	int ret = 0;
	unsigned long head;
	struct cs_etr_buffers *buf = sink_config;
	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);

	/* wrap head around to the amount of space we have */
	head = handle->head & ((buf->tmc.nr_pages << PAGE_SHIFT) - 1);

	/* find the page to write to */
	buf->tmc.cur = head / PAGE_SIZE;

	/* and offset within that page */
	buf->tmc.offset = head % PAGE_SIZE;

	local_set(&buf->tmc.data_size, 0);

	/* Tell the HW where to put the trace data */
	drvdata->vaddr = buf->vaddr;
	drvdata->paddr = buf->paddr;
	memset(drvdata->vaddr, 0, drvdata->size);

	return ret;
}

static unsigned long tmc_reset_etr_buffer(struct coresight_device *csdev,
					  struct perf_output_handle *handle,
					  void *sink_config, bool *lost)
{
	long size = 0;
	struct cs_etr_buffers *buf = sink_config;
	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);

	if (buf) {
		/*
		 * In snapshot mode ->data_size holds the new address of the
		 * ring buffer's head.  The size itself is the whole address
		 * range since we want the latest information.
		 */
		if (buf->tmc.snapshot) {
			size = buf->tmc.nr_pages << PAGE_SHIFT;
			handle->head = local_xchg(&buf->tmc.data_size, size);
		}

		/*
		 * Tell the tracer PMU how much we got in this run and if
		 * something went wrong along the way.  Nobody else can use
		 * this cs_etr_buffers instance until we are done.  As such
		 * resetting parameters here and squaring off with the ring
		 * buffer API in the tracer PMU is fine.
		 */
		*lost = !!local_xchg(&buf->tmc.lost, 0);
		size = local_xchg(&buf->tmc.data_size, 0);
	}

	/* Get ready for another run */
	drvdata->vaddr = NULL;
	drvdata->paddr = 0;

	return size;
}

static void tmc_update_etr_buffer(struct coresight_device *csdev,
				  struct perf_output_handle *handle,
				  void *sink_config)
{
	int i, cur;
	u32 *buf_ptr;
	u32 read_ptr, write_ptr;
	u32 status, to_read;
	unsigned long offset;
	struct cs_buffers *buf = sink_config;
	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);

	if (!buf)
		return;

	/* This shouldn't happen */
	if (WARN_ON_ONCE(local_read(&drvdata->mode) != CS_MODE_PERF))
		return;

	CS_UNLOCK(drvdata->base);

	tmc_flush_and_stop(drvdata);

	read_ptr = readl_relaxed(drvdata->base + TMC_RRP);
	write_ptr = readl_relaxed(drvdata->base + TMC_RWP);

	/*
	 * Get a hold of the status register and see if a wrap around
	 * has occurred.  If so adjust things accordingly.
	 */
	status = readl_relaxed(drvdata->base + TMC_STS);
	if (status & TMC_STS_FULL) {
		local_inc(&buf->lost);
		to_read = drvdata->size;
	} else {
		to_read = CIRC_CNT(write_ptr, read_ptr, drvdata->size);
	}

	/*
	 * The TMC RAM buffer may be bigger than the space available in the
	 * perf ring buffer (handle->size).  If so advance the RRP so that we
	 * get the latest trace data.
	 */
	if (to_read > handle->size) {
		u32 buffer_start, mask = 0;

		/* Read buffer start address in system memory */
		buffer_start = readl_relaxed(drvdata->base + TMC_DBALO);

		/*
		 * The value written to RRP must be byte-address aligned to
		 * the width of the trace memory databus _and_ to a frame
		 * boundary (16 byte), whichever is the biggest. For example,
		 * for 32-bit, 64-bit and 128-bit wide trace memory, the four
		 * LSBs must be 0s. For 256-bit wide trace memory, the five
		 * LSBs must be 0s.
		 */
		switch (drvdata->memwidth) {
		case TMC_MEM_INTF_WIDTH_32BITS:
		case TMC_MEM_INTF_WIDTH_64BITS:
		case TMC_MEM_INTF_WIDTH_128BITS:
			mask = GENMASK(31, 5);
			break;
		case TMC_MEM_INTF_WIDTH_256BITS:
			mask = GENMASK(31, 6);
			break;
		}

		/*
		 * Make sure the new size is aligned in accordance with the
		 * requirement explained above.
		 */
		to_read = handle->size & mask;
		/* Move the RAM read pointer up */
		read_ptr = (write_ptr + drvdata->size) - to_read;
		/* Make sure we are still within our limits */
		if (read_ptr > (buffer_start + (drvdata->size - 1)))
			read_ptr -= drvdata->size;
		/* Tell the HW */
		writel_relaxed(read_ptr, drvdata->base + TMC_RRP);
		local_inc(&buf->lost);
	}

	cur = buf->cur;
	offset = buf->offset;

	/* 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);

		offset += 4;
		if (offset >= PAGE_SIZE) {
			offset = 0;
			cur++;
			/* wrap around at the end of the buffer */
			cur &= buf->nr_pages - 1;
		}
	}

	/*
	 * In snapshot mode all we have to do is communicate to
	 * perf_aux_output_end() the address of the current head.  In full
	 * trace mode the same function expects a size to move rb->aux_head
	 * forward.
	 */
	if (buf->snapshot)
		local_set(&buf->data_size, (cur * PAGE_SIZE) + offset);
	else
		local_add(to_read, &buf->data_size);

	CS_LOCK(drvdata->base);
}

static const struct coresight_ops_sink tmc_etr_sink_ops = {
	.enable		= tmc_enable_etr_sink,
	.disable	= tmc_disable_etr_sink,
	.alloc_buffer	= tmc_alloc_etr_buffer,
	.free_buffer	= tmc_free_etr_buffer,
	.set_buffer	= tmc_set_etr_buffer,
	.reset_buffer	= tmc_reset_etr_buffer,
	.update_buffer	= tmc_update_etr_buffer,
};

const struct coresight_ops tmc_etr_cs_ops = {
@@ -300,13 +543,10 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
	if (local_read(&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
		 * up with stale data.
		 *
		 * Since the tracer is still enabled drvdata::buf
		 * can't be NULL.
		 * buffer. The trace buffer is cleared in tmc_etr_enable_hw(),
		 * so we don't have to explicitly clear it. Also, since the
		 * tracer is still enabled drvdata::buf can't be NULL.
		 */
		memset(drvdata->buf, 0, drvdata->size);
		tmc_etr_enable_hw(drvdata);
	} else {
		/*
@@ -315,7 +555,7 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
		 */
		vaddr = drvdata->vaddr;
		paddr = drvdata->paddr;
		drvdata->buf = NULL;
		drvdata->buf = drvdata->vaddr = NULL;
	}

	drvdata->reading = false;
Loading