|
15 | 15 | * this program. If not, see <http://www.gnu.org/licenses/>.
|
16 | 16 | */
|
17 | 17 |
|
| 18 | +#include <linux/circ_buf.h> |
18 | 19 | #include <linux/coresight.h>
|
| 20 | +#include <linux/perf_event.h> |
19 | 21 | #include <linux/slab.h>
|
20 | 22 | #include "coresight-priv.h"
|
21 | 23 | #include "coresight-tmc.h"
|
@@ -282,9 +284,206 @@ static void tmc_disable_etf_link(struct coresight_device *csdev,
|
282 | 284 | dev_info(drvdata->dev, "TMC disabled\n");
|
283 | 285 | }
|
284 | 286 |
|
| 287 | +static void *tmc_alloc_etf_buffer(struct coresight_device *csdev, int cpu, |
| 288 | + void **pages, int nr_pages, bool overwrite) |
| 289 | +{ |
| 290 | + int node; |
| 291 | + struct cs_buffers *buf; |
| 292 | + |
| 293 | + if (cpu == -1) |
| 294 | + cpu = smp_processor_id(); |
| 295 | + node = cpu_to_node(cpu); |
| 296 | + |
| 297 | + /* Allocate memory structure for interaction with Perf */ |
| 298 | + buf = kzalloc_node(sizeof(struct cs_buffers), GFP_KERNEL, node); |
| 299 | + if (!buf) |
| 300 | + return NULL; |
| 301 | + |
| 302 | + buf->snapshot = overwrite; |
| 303 | + buf->nr_pages = nr_pages; |
| 304 | + buf->data_pages = pages; |
| 305 | + |
| 306 | + return buf; |
| 307 | +} |
| 308 | + |
| 309 | +static void tmc_free_etf_buffer(void *config) |
| 310 | +{ |
| 311 | + struct cs_buffers *buf = config; |
| 312 | + |
| 313 | + kfree(buf); |
| 314 | +} |
| 315 | + |
| 316 | +static int tmc_set_etf_buffer(struct coresight_device *csdev, |
| 317 | + struct perf_output_handle *handle, |
| 318 | + void *sink_config) |
| 319 | +{ |
| 320 | + int ret = 0; |
| 321 | + unsigned long head; |
| 322 | + struct cs_buffers *buf = sink_config; |
| 323 | + |
| 324 | + /* wrap head around to the amount of space we have */ |
| 325 | + head = handle->head & ((buf->nr_pages << PAGE_SHIFT) - 1); |
| 326 | + |
| 327 | + /* find the page to write to */ |
| 328 | + buf->cur = head / PAGE_SIZE; |
| 329 | + |
| 330 | + /* and offset within that page */ |
| 331 | + buf->offset = head % PAGE_SIZE; |
| 332 | + |
| 333 | + local_set(&buf->data_size, 0); |
| 334 | + |
| 335 | + return ret; |
| 336 | +} |
| 337 | + |
| 338 | +static unsigned long tmc_reset_etf_buffer(struct coresight_device *csdev, |
| 339 | + struct perf_output_handle *handle, |
| 340 | + void *sink_config, bool *lost) |
| 341 | +{ |
| 342 | + long size = 0; |
| 343 | + struct cs_buffers *buf = sink_config; |
| 344 | + |
| 345 | + if (buf) { |
| 346 | + /* |
| 347 | + * In snapshot mode ->data_size holds the new address of the |
| 348 | + * ring buffer's head. The size itself is the whole address |
| 349 | + * range since we want the latest information. |
| 350 | + */ |
| 351 | + if (buf->snapshot) |
| 352 | + handle->head = local_xchg(&buf->data_size, |
| 353 | + buf->nr_pages << PAGE_SHIFT); |
| 354 | + /* |
| 355 | + * Tell the tracer PMU how much we got in this run and if |
| 356 | + * something went wrong along the way. Nobody else can use |
| 357 | + * this cs_buffers instance until we are done. As such |
| 358 | + * resetting parameters here and squaring off with the ring |
| 359 | + * buffer API in the tracer PMU is fine. |
| 360 | + */ |
| 361 | + *lost = !!local_xchg(&buf->lost, 0); |
| 362 | + size = local_xchg(&buf->data_size, 0); |
| 363 | + } |
| 364 | + |
| 365 | + return size; |
| 366 | +} |
| 367 | + |
| 368 | +static void tmc_update_etf_buffer(struct coresight_device *csdev, |
| 369 | + struct perf_output_handle *handle, |
| 370 | + void *sink_config) |
| 371 | +{ |
| 372 | + int i, cur; |
| 373 | + u32 *buf_ptr; |
| 374 | + u32 read_ptr, write_ptr; |
| 375 | + u32 status, to_read; |
| 376 | + unsigned long offset; |
| 377 | + struct cs_buffers *buf = sink_config; |
| 378 | + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); |
| 379 | + |
| 380 | + if (!buf) |
| 381 | + return; |
| 382 | + |
| 383 | + /* This shouldn't happen */ |
| 384 | + if (WARN_ON_ONCE(local_read(&drvdata->mode) != CS_MODE_PERF)) |
| 385 | + return; |
| 386 | + |
| 387 | + CS_UNLOCK(drvdata->base); |
| 388 | + |
| 389 | + tmc_flush_and_stop(drvdata); |
| 390 | + |
| 391 | + read_ptr = readl_relaxed(drvdata->base + TMC_RRP); |
| 392 | + write_ptr = readl_relaxed(drvdata->base + TMC_RWP); |
| 393 | + |
| 394 | + /* |
| 395 | + * Get a hold of the status register and see if a wrap around |
| 396 | + * has occurred. If so adjust things accordingly. |
| 397 | + */ |
| 398 | + status = readl_relaxed(drvdata->base + TMC_STS); |
| 399 | + if (status & TMC_STS_FULL) { |
| 400 | + local_inc(&buf->lost); |
| 401 | + to_read = drvdata->size; |
| 402 | + } else { |
| 403 | + to_read = CIRC_CNT(write_ptr, read_ptr, drvdata->size); |
| 404 | + } |
| 405 | + |
| 406 | + /* |
| 407 | + * The TMC RAM buffer may be bigger than the space available in the |
| 408 | + * perf ring buffer (handle->size). If so advance the RRP so that we |
| 409 | + * get the latest trace data. |
| 410 | + */ |
| 411 | + if (to_read > handle->size) { |
| 412 | + u32 mask = 0; |
| 413 | + |
| 414 | + /* |
| 415 | + * The value written to RRP must be byte-address aligned to |
| 416 | + * the width of the trace memory databus _and_ to a frame |
| 417 | + * boundary (16 byte), whichever is the biggest. For example, |
| 418 | + * for 32-bit, 64-bit and 128-bit wide trace memory, the four |
| 419 | + * LSBs must be 0s. For 256-bit wide trace memory, the five |
| 420 | + * LSBs must be 0s. |
| 421 | + */ |
| 422 | + switch (drvdata->memwidth) { |
| 423 | + case TMC_MEM_INTF_WIDTH_32BITS: |
| 424 | + case TMC_MEM_INTF_WIDTH_64BITS: |
| 425 | + case TMC_MEM_INTF_WIDTH_128BITS: |
| 426 | + mask = GENMASK(31, 5); |
| 427 | + break; |
| 428 | + case TMC_MEM_INTF_WIDTH_256BITS: |
| 429 | + mask = GENMASK(31, 6); |
| 430 | + break; |
| 431 | + } |
| 432 | + |
| 433 | + /* |
| 434 | + * Make sure the new size is aligned in accordance with the |
| 435 | + * requirement explained above. |
| 436 | + */ |
| 437 | + to_read = handle->size & mask; |
| 438 | + /* Move the RAM read pointer up */ |
| 439 | + read_ptr = (write_ptr + drvdata->size) - to_read; |
| 440 | + /* Make sure we are still within our limits */ |
| 441 | + if (read_ptr > (drvdata->size - 1)) |
| 442 | + read_ptr -= drvdata->size; |
| 443 | + /* Tell the HW */ |
| 444 | + writel_relaxed(read_ptr, drvdata->base + TMC_RRP); |
| 445 | + local_inc(&buf->lost); |
| 446 | + } |
| 447 | + |
| 448 | + cur = buf->cur; |
| 449 | + offset = buf->offset; |
| 450 | + |
| 451 | + /* for every byte to read */ |
| 452 | + for (i = 0; i < to_read; i += 4) { |
| 453 | + buf_ptr = buf->data_pages[cur] + offset; |
| 454 | + *buf_ptr = readl_relaxed(drvdata->base + TMC_RRD); |
| 455 | + |
| 456 | + offset += 4; |
| 457 | + if (offset >= PAGE_SIZE) { |
| 458 | + offset = 0; |
| 459 | + cur++; |
| 460 | + /* wrap around at the end of the buffer */ |
| 461 | + cur &= buf->nr_pages - 1; |
| 462 | + } |
| 463 | + } |
| 464 | + |
| 465 | + /* |
| 466 | + * In snapshot mode all we have to do is communicate to |
| 467 | + * perf_aux_output_end() the address of the current head. In full |
| 468 | + * trace mode the same function expects a size to move rb->aux_head |
| 469 | + * forward. |
| 470 | + */ |
| 471 | + if (buf->snapshot) |
| 472 | + local_set(&buf->data_size, (cur * PAGE_SIZE) + offset); |
| 473 | + else |
| 474 | + local_add(to_read, &buf->data_size); |
| 475 | + |
| 476 | + CS_LOCK(drvdata->base); |
| 477 | +} |
| 478 | + |
285 | 479 | static const struct coresight_ops_sink tmc_etf_sink_ops = {
|
286 | 480 | .enable = tmc_enable_etf_sink,
|
287 | 481 | .disable = tmc_disable_etf_sink,
|
| 482 | + .alloc_buffer = tmc_alloc_etf_buffer, |
| 483 | + .free_buffer = tmc_free_etf_buffer, |
| 484 | + .set_buffer = tmc_set_etf_buffer, |
| 485 | + .reset_buffer = tmc_reset_etf_buffer, |
| 486 | + .update_buffer = tmc_update_etf_buffer, |
288 | 487 | };
|
289 | 488 |
|
290 | 489 | static const struct coresight_ops_link tmc_etf_link_ops = {
|
|
0 commit comments