coresight-tmc-etr.c 8.0 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 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
{
	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);

	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,
		       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)
{
	u32 rwp, val;

	rwp = readl_relaxed(drvdata->base + TMC_RWP);
	val = readl_relaxed(drvdata->base + TMC_STS);

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

80
static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
81 82 83 84
{
	CS_UNLOCK(drvdata->base);

	tmc_flush_and_stop(drvdata);
85 86 87 88
	/*
	 * When operating in sysFS mode the content of the buffer needs to be
	 * read before the TMC is disabled.
	 */
89
	if (drvdata->mode == CS_MODE_SYSFS)
90
		tmc_etr_dump_hw(drvdata);
91 92 93 94 95
	tmc_disable_hw(drvdata);

	CS_LOCK(drvdata->base);
}

96
static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
97
{
98 99
	int ret = 0;
	bool used = false;
100
	unsigned long flags;
101 102
	void __iomem *vaddr = NULL;
	dma_addr_t paddr;
103 104
	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);

105 106 107 108 109

	/*
	 * If we don't have a buffer release the lock and allocate memory.
	 * Otherwise keep the lock and move along.
	 */
110
	spin_lock_irqsave(&drvdata->spinlock, flags);
111
	if (!drvdata->vaddr) {
112
		spin_unlock_irqrestore(&drvdata->spinlock, flags);
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132

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

133 134 135 136 137
	/*
	 * 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.
	 */
138
	if (drvdata->mode == CS_MODE_SYSFS)
139 140
		goto out;

141 142 143 144 145 146 147 148 149 150
	/*
	 * 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;
151 152
	}

153
	drvdata->mode = CS_MODE_SYSFS;
154
	tmc_etr_enable_hw(drvdata);
155
out:
156 157
	spin_unlock_irqrestore(&drvdata->spinlock, flags);

158 159 160 161 162 163 164 165
	/* 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;
166 167
}

168
static int tmc_enable_etr_sink_perf(struct coresight_device *csdev)
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
{
	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.
	 */
185
	if (drvdata->mode != CS_MODE_DISABLED) {
186 187 188 189
		ret = -EINVAL;
		goto out;
	}

190
	drvdata->mode = CS_MODE_PERF;
191 192 193 194 195 196 197 198 199 200 201
	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:
202
		return tmc_enable_etr_sink_sysfs(csdev);
203
	case CS_MODE_PERF:
204
		return tmc_enable_etr_sink_perf(csdev);
205 206 207 208 209 210
	}

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

211 212 213 214 215 216 217 218 219 220 221
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;
	}

222
	/* Disable the TMC only if it needs to */
223
	if (drvdata->mode != CS_MODE_DISABLED) {
224
		tmc_etr_disable_hw(drvdata);
225 226
		drvdata->mode = CS_MODE_DISABLED;
	}
227

228 229 230 231 232 233 234 235 236 237 238 239 240
	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,
};
241 242 243

int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
{
244
	int ret = 0;
245 246 247 248 249 250 251
	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);
252 253 254 255
	if (drvdata->reading) {
		ret = -EBUSY;
		goto out;
	}
256

257
	/* Don't interfere if operated from Perf */
258
	if (drvdata->mode == CS_MODE_PERF) {
259 260 261 262
		ret = -EINVAL;
		goto out;
	}

263 264 265 266 267 268
	/* If drvdata::buf is NULL the trace data has been read already */
	if (drvdata->buf == NULL) {
		ret = -EINVAL;
		goto out;
	}

269
	/* Disable the TMC if need be */
270
	if (drvdata->mode == CS_MODE_SYSFS)
271 272 273
		tmc_etr_disable_hw(drvdata);

	drvdata->reading = true;
274
out:
275 276
	spin_unlock_irqrestore(&drvdata->spinlock, flags);

277
	return ret;
278 279 280 281 282
}

int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
{
	unsigned long flags;
283 284
	dma_addr_t paddr;
	void __iomem *vaddr = NULL;
285 286 287 288 289 290 291 292

	/* 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 */
293
	if (drvdata->mode == CS_MODE_SYSFS) {
294 295
		/*
		 * The trace run will continue with the same allocated trace
296 297 298
		 * 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.
299
		 */
300
		tmc_etr_enable_hw(drvdata);
301 302 303 304 305 306 307
	} 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;
308
		drvdata->buf = drvdata->vaddr = NULL;
309
	}
310 311 312 313

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

314 315 316 317
	/* Free allocated memory out side of the spinlock */
	if (vaddr)
		dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr);

318 319
	return 0;
}