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

Commit d8c66962 authored by Pratik Patel's avatar Pratik Patel Committed by Greg Kroah-Hartman
Browse files

coresight-etm4x: Controls pertaining to the reset, mode, pe and events



Adding sysfs entries to:
. set the tracing entity with default values.
. set various mode associated to the tracing entity.
. select the processing entity the tracing entity relates to.
. select various events of interest.

Signed-off-by: default avatarPratik Patel <pratikp@codeaurora.org>
Signed-off-by: default avatarMathieu Poirier <mathieu.poirier@linaro.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent c0ddbfea
Loading
Loading
Loading
Loading
+33 −0
Original line number Diff line number Diff line
@@ -75,3 +75,36 @@ KernelVersion: 4.01
Contact:	Mathieu Poirier <mathieu.poirier@linaro.org>
Description:	(R) Indicates the number of single-shot comparator controls that
		are available for tracing.

What:		/sys/bus/coresight/devices/<memory_map>.etm/reset
Date:		April 2015
KernelVersion:	4.01
Contact:	Mathieu Poirier <mathieu.poirier@linaro.org>
Description: 	(W) Cancels all configuration on a trace unit and set it back
		to its boot configuration.

What:		/sys/bus/coresight/devices/<memory_map>.etm/mode
Date:		April 2015
KernelVersion:	4.01
Contact:	Mathieu Poirier <mathieu.poirier@linaro.org>
Description: 	(RW) Controls various modes supported by this ETM, for example
		P0 instruction tracing, branch broadcast, cycle counting and
		context ID tracing.

What:		/sys/bus/coresight/devices/<memory_map>.etm/pe
Date:		April 2015
KernelVersion:	4.01
Contact:	Mathieu Poirier <mathieu.poirier@linaro.org>
Description: 	(RW) Controls which PE to trace.

What:		/sys/bus/coresight/devices/<memory_map>.etm/event
Date:		April 2015
KernelVersion:	4.01
Contact:	Mathieu Poirier <mathieu.poirier@linaro.org>
Description: 	(RW) Controls the tracing of arbitrary events from bank 0 to 3.

What:		/sys/bus/coresight/devices/<memory_map>.etm/event_instren
Date:		April 2015
KernelVersion:	4.01
Contact:	Mathieu Poirier <mathieu.poirier@linaro.org>
Description: 	(RW) Controls the behavior of the events in bank 0 to 3.
+441 −0
Original line number Diff line number Diff line
@@ -268,6 +268,46 @@ static const struct coresight_ops etm4_cs_ops = {
	.source_ops	= &etm4_source_ops,
};

static int etm4_set_mode_exclude(struct etmv4_drvdata *drvdata, bool exclude)
{
	u8 idx = drvdata->addr_idx;

	/*
	 * TRCACATRn.TYPE bit[1:0]: type of comparison
	 * the trace unit performs
	 */
	if (BMVAL(drvdata->addr_acc[idx], 0, 1) == ETM_INSTR_ADDR) {
		if (idx % 2 != 0)
			return -EINVAL;

		/*
		 * We are performing instruction address comparison. Set the
		 * relevant bit of ViewInst Include/Exclude Control register
		 * for corresponding address comparator pair.
		 */
		if (drvdata->addr_type[idx] != ETM_ADDR_TYPE_RANGE ||
		    drvdata->addr_type[idx + 1] != ETM_ADDR_TYPE_RANGE)
			return -EINVAL;

		if (exclude == true) {
			/*
			 * Set exclude bit and unset the include bit
			 * corresponding to comparator pair
			 */
			drvdata->viiectlr |= BIT(idx / 2 + 16);
			drvdata->viiectlr &= ~BIT(idx / 2);
		} else {
			/*
			 * Set include bit and unset exclude bit
			 * corresponding to comparator pair
			 */
			drvdata->viiectlr |= BIT(idx / 2);
			drvdata->viiectlr &= ~BIT(idx / 2 + 16);
		}
	}
	return 0;
}

static ssize_t nr_pe_cmp_show(struct device *dev,
			      struct device_attribute *attr,
			      char *buf)
@@ -376,6 +416,402 @@ static ssize_t nr_ss_cmp_show(struct device *dev,
}
static DEVICE_ATTR_RO(nr_ss_cmp);

static ssize_t reset_store(struct device *dev,
			   struct device_attribute *attr,
			   const char *buf, size_t size)
{
	int i;
	unsigned long val;
	struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);

	if (kstrtoul(buf, 16, &val))
		return -EINVAL;

	spin_lock(&drvdata->spinlock);
	if (val)
		drvdata->mode = 0x0;

	/* Disable data tracing: do not trace load and store data transfers */
	drvdata->mode &= ~(ETM_MODE_LOAD | ETM_MODE_STORE);
	drvdata->cfg &= ~(BIT(1) | BIT(2));

	/* Disable data value and data address tracing */
	drvdata->mode &= ~(ETM_MODE_DATA_TRACE_ADDR |
			   ETM_MODE_DATA_TRACE_VAL);
	drvdata->cfg &= ~(BIT(16) | BIT(17));

	/* Disable all events tracing */
	drvdata->eventctrl0 = 0x0;
	drvdata->eventctrl1 = 0x0;

	/* Disable timestamp event */
	drvdata->ts_ctrl = 0x0;

	/* Disable stalling */
	drvdata->stall_ctrl = 0x0;

	/* Reset trace synchronization period  to 2^8 = 256 bytes*/
	if (drvdata->syncpr == false)
		drvdata->syncfreq = 0x8;

	/*
	 * Enable ViewInst to trace everything with start-stop logic in
	 * started state. ARM recommends start-stop logic is set before
	 * each trace run.
	 */
	drvdata->vinst_ctrl |= BIT(0);
	if (drvdata->nr_addr_cmp == true) {
		drvdata->mode |= ETM_MODE_VIEWINST_STARTSTOP;
		/* SSSTATUS, bit[9] */
		drvdata->vinst_ctrl |= BIT(9);
	}

	/* No address range filtering for ViewInst */
	drvdata->viiectlr = 0x0;

	/* No start-stop filtering for ViewInst */
	drvdata->vissctlr = 0x0;

	/* Disable seq events */
	for (i = 0; i < drvdata->nrseqstate-1; i++)
		drvdata->seq_ctrl[i] = 0x0;
	drvdata->seq_rst = 0x0;
	drvdata->seq_state = 0x0;

	/* Disable external input events */
	drvdata->ext_inp = 0x0;

	drvdata->cntr_idx = 0x0;
	for (i = 0; i < drvdata->nr_cntr; i++) {
		drvdata->cntrldvr[i] = 0x0;
		drvdata->cntr_ctrl[i] = 0x0;
		drvdata->cntr_val[i] = 0x0;
	}

	drvdata->res_idx = 0x0;
	for (i = 0; i < drvdata->nr_resource; i++)
		drvdata->res_ctrl[i] = 0x0;

	for (i = 0; i < drvdata->nr_ss_cmp; i++) {
		drvdata->ss_ctrl[i] = 0x0;
		drvdata->ss_pe_cmp[i] = 0x0;
	}

	drvdata->addr_idx = 0x0;
	for (i = 0; i < drvdata->nr_addr_cmp * 2; i++) {
		drvdata->addr_val[i] = 0x0;
		drvdata->addr_acc[i] = 0x0;
		drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE;
	}

	drvdata->ctxid_idx = 0x0;
	for (i = 0; i < drvdata->numcidc; i++)
		drvdata->ctxid_val[i] = 0x0;
	drvdata->ctxid_mask0 = 0x0;
	drvdata->ctxid_mask1 = 0x0;

	drvdata->vmid_idx = 0x0;
	for (i = 0; i < drvdata->numvmidc; i++)
		drvdata->vmid_val[i] = 0x0;
	drvdata->vmid_mask0 = 0x0;
	drvdata->vmid_mask1 = 0x0;

	drvdata->trcid = drvdata->cpu + 1;
	spin_unlock(&drvdata->spinlock);
	return size;
}
static DEVICE_ATTR_WO(reset);

static ssize_t mode_show(struct device *dev,
			 struct device_attribute *attr,
			 char *buf)
{
	unsigned long val;
	struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);

	val = drvdata->mode;
	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
}

static ssize_t mode_store(struct device *dev,
			  struct device_attribute *attr,
			  const char *buf, size_t size)
{
	unsigned long val, mode;
	struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);

	if (kstrtoul(buf, 16, &val))
		return -EINVAL;

	spin_lock(&drvdata->spinlock);
	drvdata->mode = val & ETMv4_MODE_ALL;

	if (drvdata->mode & ETM_MODE_EXCLUDE)
		etm4_set_mode_exclude(drvdata, true);
	else
		etm4_set_mode_exclude(drvdata, false);

	if (drvdata->instrp0 == true) {
		/* start by clearing instruction P0 field */
		drvdata->cfg  &= ~(BIT(1) | BIT(2));
		if (drvdata->mode & ETM_MODE_LOAD)
			/* 0b01 Trace load instructions as P0 instructions */
			drvdata->cfg  |= BIT(1);
		if (drvdata->mode & ETM_MODE_STORE)
			/* 0b10 Trace store instructions as P0 instructions */
			drvdata->cfg  |= BIT(2);
		if (drvdata->mode & ETM_MODE_LOAD_STORE)
			/*
			 * 0b11 Trace load and store instructions
			 * as P0 instructions
			 */
			drvdata->cfg  |= BIT(1) | BIT(2);
	}

	/* bit[3], Branch broadcast mode */
	if ((drvdata->mode & ETM_MODE_BB) && (drvdata->trcbb == true))
		drvdata->cfg |= BIT(3);
	else
		drvdata->cfg &= ~BIT(3);

	/* bit[4], Cycle counting instruction trace bit */
	if ((drvdata->mode & ETMv4_MODE_CYCACC) &&
		(drvdata->trccci == true))
		drvdata->cfg |= BIT(4);
	else
		drvdata->cfg &= ~BIT(4);

	/* bit[6], Context ID tracing bit */
	if ((drvdata->mode & ETMv4_MODE_CTXID) && (drvdata->ctxid_size))
		drvdata->cfg |= BIT(6);
	else
		drvdata->cfg &= ~BIT(6);

	if ((drvdata->mode & ETM_MODE_VMID) && (drvdata->vmid_size))
		drvdata->cfg |= BIT(7);
	else
		drvdata->cfg &= ~BIT(7);

	/* bits[10:8], Conditional instruction tracing bit */
	mode = ETM_MODE_COND(drvdata->mode);
	if (drvdata->trccond == true) {
		drvdata->cfg &= ~(BIT(8) | BIT(9) | BIT(10));
		drvdata->cfg |= mode << 8;
	}

	/* bit[11], Global timestamp tracing bit */
	if ((drvdata->mode & ETMv4_MODE_TIMESTAMP) && (drvdata->ts_size))
		drvdata->cfg |= BIT(11);
	else
		drvdata->cfg &= ~BIT(11);

	/* bit[12], Return stack enable bit */
	if ((drvdata->mode & ETM_MODE_RETURNSTACK) &&
		(drvdata->retstack == true))
		drvdata->cfg |= BIT(12);
	else
		drvdata->cfg &= ~BIT(12);

	/* bits[14:13], Q element enable field */
	mode = ETM_MODE_QELEM(drvdata->mode);
	/* start by clearing QE bits */
	drvdata->cfg &= ~(BIT(13) | BIT(14));
	/* if supported, Q elements with instruction counts are enabled */
	if ((mode & BIT(0)) && (drvdata->q_support & BIT(0)))
		drvdata->cfg |= BIT(13);
	/*
	 * if supported, Q elements with and without instruction
	 * counts are enabled
	 */
	if ((mode & BIT(1)) && (drvdata->q_support & BIT(1)))
		drvdata->cfg |= BIT(14);

	/* bit[11], AMBA Trace Bus (ATB) trigger enable bit */
	if ((drvdata->mode & ETM_MODE_ATB_TRIGGER) &&
	    (drvdata->atbtrig == true))
		drvdata->eventctrl1 |= BIT(11);
	else
		drvdata->eventctrl1 &= ~BIT(11);

	/* bit[12], Low-power state behavior override bit */
	if ((drvdata->mode & ETM_MODE_LPOVERRIDE) &&
	    (drvdata->lpoverride == true))
		drvdata->eventctrl1 |= BIT(12);
	else
		drvdata->eventctrl1 &= ~BIT(12);

	/* bit[8], Instruction stall bit */
	if (drvdata->mode & ETM_MODE_ISTALL_EN)
		drvdata->stall_ctrl |= BIT(8);
	else
		drvdata->stall_ctrl &= ~BIT(8);

	/* bit[10], Prioritize instruction trace bit */
	if (drvdata->mode & ETM_MODE_INSTPRIO)
		drvdata->stall_ctrl |= BIT(10);
	else
		drvdata->stall_ctrl &= ~BIT(10);

	/* bit[13], Trace overflow prevention bit */
	if ((drvdata->mode & ETM_MODE_NOOVERFLOW) &&
		(drvdata->nooverflow == true))
		drvdata->stall_ctrl |= BIT(13);
	else
		drvdata->stall_ctrl &= ~BIT(13);

	/* bit[9] Start/stop logic control bit */
	if (drvdata->mode & ETM_MODE_VIEWINST_STARTSTOP)
		drvdata->vinst_ctrl |= BIT(9);
	else
		drvdata->vinst_ctrl &= ~BIT(9);

	/* bit[10], Whether a trace unit must trace a Reset exception */
	if (drvdata->mode & ETM_MODE_TRACE_RESET)
		drvdata->vinst_ctrl |= BIT(10);
	else
		drvdata->vinst_ctrl &= ~BIT(10);

	/* bit[11], Whether a trace unit must trace a system error exception */
	if ((drvdata->mode & ETM_MODE_TRACE_ERR) &&
		(drvdata->trc_error == true))
		drvdata->vinst_ctrl |= BIT(11);
	else
		drvdata->vinst_ctrl &= ~BIT(11);

	spin_unlock(&drvdata->spinlock);
	return size;
}
static DEVICE_ATTR_RW(mode);

static ssize_t pe_show(struct device *dev,
		       struct device_attribute *attr,
		       char *buf)
{
	unsigned long val;
	struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);

	val = drvdata->pe_sel;
	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
}

static ssize_t pe_store(struct device *dev,
			struct device_attribute *attr,
			const char *buf, size_t size)
{
	unsigned long val;
	struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);

	if (kstrtoul(buf, 16, &val))
		return -EINVAL;

	spin_lock(&drvdata->spinlock);
	if (val > drvdata->nr_pe) {
		spin_unlock(&drvdata->spinlock);
		return -EINVAL;
	}

	drvdata->pe_sel = val;
	spin_unlock(&drvdata->spinlock);
	return size;
}
static DEVICE_ATTR_RW(pe);

static ssize_t event_show(struct device *dev,
			  struct device_attribute *attr,
			  char *buf)
{
	unsigned long val;
	struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);

	val = drvdata->eventctrl0;
	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
}

static ssize_t event_store(struct device *dev,
			   struct device_attribute *attr,
			   const char *buf, size_t size)
{
	unsigned long val;
	struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);

	if (kstrtoul(buf, 16, &val))
		return -EINVAL;

	spin_lock(&drvdata->spinlock);
	switch (drvdata->nr_event) {
	case 0x0:
		/* EVENT0, bits[7:0] */
		drvdata->eventctrl0 = val & 0xFF;
		break;
	case 0x1:
		 /* EVENT1, bits[15:8] */
		drvdata->eventctrl0 = val & 0xFFFF;
		break;
	case 0x2:
		/* EVENT2, bits[23:16] */
		drvdata->eventctrl0 = val & 0xFFFFFF;
		break;
	case 0x3:
		/* EVENT3, bits[31:24] */
		drvdata->eventctrl0 = val;
		break;
	default:
		break;
	}
	spin_unlock(&drvdata->spinlock);
	return size;
}
static DEVICE_ATTR_RW(event);

static ssize_t event_instren_show(struct device *dev,
				  struct device_attribute *attr,
				  char *buf)
{
	unsigned long val;
	struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);

	val = BMVAL(drvdata->eventctrl1, 0, 3);
	return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
}

static ssize_t event_instren_store(struct device *dev,
				   struct device_attribute *attr,
				   const char *buf, size_t size)
{
	unsigned long val;
	struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);

	if (kstrtoul(buf, 16, &val))
		return -EINVAL;

	spin_lock(&drvdata->spinlock);
	/* start by clearing all instruction event enable bits */
	drvdata->eventctrl1 &= ~(BIT(0) | BIT(1) | BIT(2) | BIT(3));
	switch (drvdata->nr_event) {
	case 0x0:
		/* generate Event element for event 1 */
		drvdata->eventctrl1 |= val & BIT(1);
		break;
	case 0x1:
		/* generate Event element for event 1 and 2 */
		drvdata->eventctrl1 |= val & (BIT(0) | BIT(1));
		break;
	case 0x2:
		/* generate Event element for event 1, 2 and 3 */
		drvdata->eventctrl1 |= val & (BIT(0) | BIT(1) | BIT(2));
		break;
	case 0x3:
		/* generate Event element for all 4 events */
		drvdata->eventctrl1 |= val & 0xF;
		break;
	default:
		break;
	}
	spin_unlock(&drvdata->spinlock);
	return size;
}
static DEVICE_ATTR_RW(event_instren);

static ssize_t cpu_show(struct device *dev,
			struct device_attribute *attr, char *buf)
{
@@ -398,6 +834,11 @@ static struct attribute *coresight_etmv4_attrs[] = {
	&dev_attr_nrseqstate.attr,
	&dev_attr_nr_resource.attr,
	&dev_attr_nr_ss_cmp.attr,
	&dev_attr_reset.attr,
	&dev_attr_mode.attr,
	&dev_attr_pe.attr,
	&dev_attr_event.attr,
	&dev_attr_event_instren.attr,
	&dev_attr_cpu.attr,
	NULL,
};