coresight-tmc-etr.c 8.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * Copyright(C) 2016 Linaro Limited. All rights reserved.
 * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License 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.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/coresight.h>
19
#include <linux/dma-mapping.h>
20 21 22
#include "coresight-priv.h"
#include "coresight-tmc.h"

23
static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
{
	u32 axictl;

	/* Zero out the memory to help with debug */
	memset(drvdata->vaddr, 0, drvdata->size);

	CS_UNLOCK(drvdata->base);

	/* Wait for TMCSReady bit to be set */
	tmc_wait_for_tmcready(drvdata);

	writel_relaxed(drvdata->size / 4, drvdata->base + TMC_RSZ);
	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;
	writel_relaxed(axictl, drvdata->base + TMC_AXICTL);
47
	tmc_write_dba(drvdata, drvdata->paddr);
48 49 50 51 52 53 54 55 56 57 58 59 60

	writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI |
		       TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT |
		       TMC_FFCR_TRIGON_TRIGIN,
		       drvdata->base + TMC_FFCR);
	writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG);
	tmc_enable_hw(drvdata);

	CS_LOCK(drvdata->base);
}

static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata)
{
61
	const u32 *barrier;
62
	u32 val;
63
	u32 *temp;
64
	u64 rwp;
65

66
	rwp = tmc_read_rwp(drvdata);
67 68
	val = readl_relaxed(drvdata->base + TMC_STS);

69 70 71 72
	/*
	 * Adjust the buffer to point to the beginning of the trace data
	 * and update the available trace data.
	 */
73
	if (val & TMC_STS_FULL) {
74
		drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr;
75
		drvdata->len = drvdata->size;
76 77 78 79 80 81 82 83 84 85

		barrier = barrier_pkt;
		temp = (u32 *)drvdata->buf;

		while (*barrier) {
			*temp = *barrier;
			temp++;
			barrier++;
		}

86
	} else {
87
		drvdata->buf = drvdata->vaddr;
88 89
		drvdata->len = rwp - drvdata->paddr;
	}
90 91
}

92
static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
93 94 95 96
{
	CS_UNLOCK(drvdata->base);

	tmc_flush_and_stop(drvdata);
97 98 99 100
	/*
	 * When operating in sysFS mode the content of the buffer needs to be
	 * read before the TMC is disabled.
	 */
101
	if (drvdata->mode == CS_MODE_SYSFS)
102
		tmc_etr_dump_hw(drvdata);
103 104 105 106 107
	tmc_disable_hw(drvdata);

	CS_LOCK(drvdata->base);
}

108
static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
109
{
110 111
	int ret = 0;
	bool used = false;
112
	unsigned long flags;
113 114
	void __iomem *vaddr = NULL;
	dma_addr_t paddr;
115 116
	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);

117 118 119 120 121

	/*
	 * If we don't have a buffer release the lock and allocate memory.
	 * Otherwise keep the lock and move along.
	 */
122
	spin_lock_irqsave(&drvdata->spinlock, flags);
123
	if (!drvdata->vaddr) {
124
		spin_unlock_irqrestore(&drvdata->spinlock, flags);
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144

		/*
		 * Contiguous  memory can't be allocated while a spinlock is
		 * held.  As such allocate memory here and free it if a buffer
		 * has already been allocated (from a previous session).
		 */
		vaddr = dma_alloc_coherent(drvdata->dev, drvdata->size,
					   &paddr, GFP_KERNEL);
		if (!vaddr)
			return -ENOMEM;

		/* Let's try again */
		spin_lock_irqsave(&drvdata->spinlock, flags);
	}

	if (drvdata->reading) {
		ret = -EBUSY;
		goto out;
	}

145 146 147 148 149
	/*
	 * In sysFS mode we can have multiple writers per sink.  Since this
	 * sink is already enabled no memory is needed and the HW need not be
	 * touched.
	 */
150
	if (drvdata->mode == CS_MODE_SYSFS)
151 152
		goto out;

153 154 155 156 157 158 159 160 161 162
	/*
	 * If drvdata::buf == NULL, use the memory allocated above.
	 * Otherwise a buffer still exists from a previous session, so
	 * simply use that.
	 */
	if (drvdata->buf == NULL) {
		used = true;
		drvdata->vaddr = vaddr;
		drvdata->paddr = paddr;
		drvdata->buf = drvdata->vaddr;
163 164
	}

165
	drvdata->mode = CS_MODE_SYSFS;
166
	tmc_etr_enable_hw(drvdata);
167
out:
168 169
	spin_unlock_irqrestore(&drvdata->spinlock, flags);

170 171 172 173 174 175 176 177
	/* Free memory outside the spinlock if need be */
	if (!used && vaddr)
		dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr);

	if (!ret)
		dev_info(drvdata->dev, "TMC-ETR enabled\n");

	return ret;
178 179
}

180
static int tmc_enable_etr_sink_perf(struct coresight_device *csdev)
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
{
	int ret = 0;
	unsigned long flags;
	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);

	spin_lock_irqsave(&drvdata->spinlock, flags);
	if (drvdata->reading) {
		ret = -EINVAL;
		goto out;
	}

	/*
	 * In Perf mode there can be only one writer per sink.  There
	 * is also no need to continue if the ETR is already operated
	 * from sysFS.
	 */
197
	if (drvdata->mode != CS_MODE_DISABLED) {
198 199 200 201
		ret = -EINVAL;
		goto out;
	}

202
	drvdata->mode = CS_MODE_PERF;
203 204 205 206 207 208 209 210 211 212 213
	tmc_etr_enable_hw(drvdata);
out:
	spin_unlock_irqrestore(&drvdata->spinlock, flags);

	return ret;
}

static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode)
{
	switch (mode) {
	case CS_MODE_SYSFS:
214
		return tmc_enable_etr_sink_sysfs(csdev);
215
	case CS_MODE_PERF:
216
		return tmc_enable_etr_sink_perf(csdev);
217 218 219 220 221 222
	}

	/* We shouldn't be here */
	return -EINVAL;
}

223 224 225 226 227 228 229 230 231 232 233
static void tmc_disable_etr_sink(struct coresight_device *csdev)
{
	unsigned long flags;
	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);

	spin_lock_irqsave(&drvdata->spinlock, flags);
	if (drvdata->reading) {
		spin_unlock_irqrestore(&drvdata->spinlock, flags);
		return;
	}

234
	/* Disable the TMC only if it needs to */
235
	if (drvdata->mode != CS_MODE_DISABLED) {
236
		tmc_etr_disable_hw(drvdata);
237 238
		drvdata->mode = CS_MODE_DISABLED;
	}
239

240 241 242 243 244 245 246 247 248 249 250 251 252
	spin_unlock_irqrestore(&drvdata->spinlock, flags);

	dev_info(drvdata->dev, "TMC-ETR disabled\n");
}

static const struct coresight_ops_sink tmc_etr_sink_ops = {
	.enable		= tmc_enable_etr_sink,
	.disable	= tmc_disable_etr_sink,
};

const struct coresight_ops tmc_etr_cs_ops = {
	.sink_ops	= &tmc_etr_sink_ops,
};
253 254 255

int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
{
256
	int ret = 0;
257 258 259 260 261 262 263
	unsigned long flags;

	/* config types are set a boot time and never change */
	if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR))
		return -EINVAL;

	spin_lock_irqsave(&drvdata->spinlock, flags);
264 265 266 267
	if (drvdata->reading) {
		ret = -EBUSY;
		goto out;
	}
268

269
	/* Don't interfere if operated from Perf */
270
	if (drvdata->mode == CS_MODE_PERF) {
271 272 273 274
		ret = -EINVAL;
		goto out;
	}

275 276 277 278 279 280
	/* If drvdata::buf is NULL the trace data has been read already */
	if (drvdata->buf == NULL) {
		ret = -EINVAL;
		goto out;
	}

281
	/* Disable the TMC if need be */
282
	if (drvdata->mode == CS_MODE_SYSFS)
283 284 285
		tmc_etr_disable_hw(drvdata);

	drvdata->reading = true;
286
out:
287 288
	spin_unlock_irqrestore(&drvdata->spinlock, flags);

289
	return ret;
290 291 292 293 294
}

int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
{
	unsigned long flags;
295 296
	dma_addr_t paddr;
	void __iomem *vaddr = NULL;
297 298 299 300 301 302 303 304

	/* config types are set a boot time and never change */
	if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR))
		return -EINVAL;

	spin_lock_irqsave(&drvdata->spinlock, flags);

	/* RE-enable the TMC if need be */
305
	if (drvdata->mode == CS_MODE_SYSFS) {
306 307
		/*
		 * The trace run will continue with the same allocated trace
308 309 310
		 * 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.
311
		 */
312
		tmc_etr_enable_hw(drvdata);
313 314 315 316 317 318 319
	} else {
		/*
		 * The ETR is not tracing and the buffer was just read.
		 * As such prepare to free the trace buffer.
		 */
		vaddr = drvdata->vaddr;
		paddr = drvdata->paddr;
320
		drvdata->buf = drvdata->vaddr = NULL;
321
	}
322 323 324 325

	drvdata->reading = false;
	spin_unlock_irqrestore(&drvdata->spinlock, flags);

326 327 328 329
	/* Free allocated memory out side of the spinlock */
	if (vaddr)
		dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr);

330 331
	return 0;
}