Files
linux-nv-oot/drivers/platform/tegra/rtcpu/tegra-rtcpu-trace.c
Evgeny Kornev c4d1462fd6 tracing: Fix ftrace for ISP trace events
Fix ISP trace events due to 64 bit TS cahnges

Jira CAMERASW-30345

Change-Id: I33aa9325603ec939979e11964224bf3f113a0adb
Signed-off-by: Evgeny Kornev <ekornev@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3266979
Reviewed-by: Jukka Kaartinen <jkaartinen@nvidia.com>
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: svcacv <svcacv@nvidia.com>
Reviewed-by: Semi Malinen <smalinen@nvidia.com>
Reviewed-by: Sudhir Vyas <svyas@nvidia.com>
2025-07-24 10:19:12 +00:00

1936 lines
52 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
// SPDX-FileCopyrightText: Copyright (c) 2022-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#include <nvidia/conftest.h>
#include "soc/tegra/camrtc-trace.h"
#include <linux/cdev.h>
#include <linux/completion.h>
#include <linux/debugfs.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/nospec.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_reserved_mem.h>
#include <linux/printk.h>
#include <linux/seq_buf.h>
#include <linux/slab.h>
#include <linux/tegra-camera-rtcpu.h>
#include <linux/tegra-rtcpu-trace.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
#include <linux/nvhost.h>
#include <linux/uaccess.h>
#include <linux/poll.h>
#include <linux/version.h>
#include <asm/cacheflush.h>
#include <uapi/linux/nvdev_fence.h>
#include "device-group.h"
#include <nvidia/conftest.h>
#define CREATE_TRACE_POINTS
#include <trace/events/tegra_rtcpu.h>
#include <trace/events/tegra_capture.h>
#include <trace/events/freertos.h>
/* Tracepoints used by other modules */
EXPORT_TRACEPOINT_SYMBOL_GPL(capture_ivc_send);
EXPORT_TRACEPOINT_SYMBOL_GPL(capture_ivc_send_error);
EXPORT_TRACEPOINT_SYMBOL_GPL(capture_ivc_notify);
EXPORT_TRACEPOINT_SYMBOL_GPL(capture_ivc_recv);
#define NV(p) "nvidia," #p
#define WORK_INTERVAL_DEFAULT 100
#define EXCEPTION_STR_LENGTH 2048
#define ISP_CLASS_ID 0x32
#define VI_CLASS_ID 0x30
#define DEVICE_NAME "rtcpu-raw-trace"
#define MAX_READ_SIZE ((ssize_t)(~0U >> 1))
/*
* Private driver data structure
*/
struct tegra_rtcpu_trace {
struct device *dev;
struct device_node *of_node;
struct mutex lock;
/* memory */
void *trace_memory;
u32 trace_memory_size;
dma_addr_t dma_handle;
/* pointers to each block */
void *exceptions_base;
struct camrtc_event_struct *events;
dma_addr_t dma_handle_pointers;
dma_addr_t dma_handle_exceptions;
dma_addr_t dma_handle_events;
/* limit */
u32 exception_entries;
u32 event_entries;
/* exception pointer */
u32 exception_last_idx;
/* last pointer */
u32 event_last_idx;
/* worker */
struct delayed_work work;
unsigned long work_interval_jiffies;
/* statistics */
u32 n_exceptions;
u64 n_events;
/* copy of the latest exception and event */
char last_exception_str[EXCEPTION_STR_LENGTH];
struct camrtc_event_struct copy_last_event;
/* debugfs */
struct dentry *debugfs_root;
struct platform_device *vi_platform_device;
struct platform_device *vi1_platform_device;
struct platform_device *isp_platform_device;
struct platform_device *isp1_platform_device;
/* printk logging */
const char *log_prefix;
bool enable_printk;
u32 printk_used;
char printk[EXCEPTION_STR_LENGTH];
struct cdev s_dev;
wait_queue_head_t wait_queue;
};
struct rtcpu_raw_trace_context {
struct tegra_rtcpu_trace *tracer;
u32 raw_trace_last_read_event_idx;
bool first_read_call;
};
/*
* Trace memory
*/
static int rtcpu_trace_setup_memory(struct tegra_rtcpu_trace *tracer)
{
struct device *dev = tracer->dev;
struct of_phandle_args reg_spec;
int ret;
void *trace_memory;
size_t mem_size;
dma_addr_t dma_addr;
ret = of_parse_phandle_with_fixed_args(dev->of_node, NV(trace),
3, 0, &reg_spec);
if (unlikely(ret != 0)) {
dev_err(dev, "Cannot find trace entry\n");
return -EINVAL;
}
mem_size = reg_spec.args[2];
trace_memory = dma_alloc_coherent(dev, mem_size, &dma_addr,
GFP_KERNEL | __GFP_ZERO);
if (trace_memory == NULL) {
ret = -ENOMEM;
goto error;
}
/* Save the information */
tracer->trace_memory = trace_memory;
tracer->trace_memory_size = mem_size;
tracer->dma_handle = dma_addr;
tracer->of_node = reg_spec.np;
return 0;
error:
of_node_put(reg_spec.np);
return ret;
}
static void rtcpu_trace_init_memory(struct tegra_rtcpu_trace *tracer)
{
/* memory map */
tracer->dma_handle_pointers = tracer->dma_handle +
offsetof(struct camrtc_trace_memory_header, exception_next_idx);
tracer->exceptions_base = tracer->trace_memory +
CAMRTC_TRACE_EXCEPTION_OFFSET;
tracer->exception_entries = 7;
tracer->dma_handle_exceptions = tracer->dma_handle +
CAMRTC_TRACE_EXCEPTION_OFFSET;
tracer->events = tracer->trace_memory + CAMRTC_TRACE_EVENT_OFFSET;
tracer->event_entries =
(tracer->trace_memory_size - CAMRTC_TRACE_EVENT_OFFSET) /
CAMRTC_TRACE_EVENT_SIZE;
tracer->dma_handle_events = tracer->dma_handle +
CAMRTC_TRACE_EXCEPTION_OFFSET;
{
struct camrtc_trace_memory_header header = {
.tlv.tag = CAMRTC_TAG_NV_TRCON,
.tlv.len = tracer->trace_memory_size,
.revision = 1,
.exception_offset = CAMRTC_TRACE_EXCEPTION_OFFSET,
.exception_size = CAMRTC_TRACE_EXCEPTION_SIZE,
.exception_entries = tracer->exception_entries,
.event_offset = CAMRTC_TRACE_EVENT_OFFSET,
.event_size = CAMRTC_TRACE_EVENT_SIZE,
.event_entries = tracer->event_entries,
};
memcpy(tracer->trace_memory, &header, sizeof(header));
dma_sync_single_for_device(tracer->dev,
tracer->dma_handle, sizeof(header),
DMA_TO_DEVICE);
}
}
/*
* Worker
*/
static void rtcpu_trace_invalidate_entries(struct tegra_rtcpu_trace *tracer,
dma_addr_t dma_handle, u32 old_next, u32 new_next,
u32 entry_size, u32 entry_count)
{
/* invalidate cache */
if (new_next > old_next) {
dma_sync_single_for_cpu(tracer->dev,
dma_handle + old_next * entry_size,
(new_next - old_next) * entry_size,
DMA_FROM_DEVICE);
} else {
dma_sync_single_for_cpu(tracer->dev,
dma_handle + old_next * entry_size,
(entry_count - old_next) * entry_size,
DMA_FROM_DEVICE);
dma_sync_single_for_cpu(tracer->dev,
dma_handle, new_next * entry_size,
DMA_FROM_DEVICE);
}
}
static void rtcpu_trace_exception(struct tegra_rtcpu_trace *tracer,
struct camrtc_trace_armv7_exception *exc)
{
static const char * const s_str_exc_type[] = {
"Invalid (Reset)",
"Undefined instruction",
"Invalid (SWI)",
"Prefetch abort",
"Data abort",
"Invalid (Reserved)",
"IRQ",
"FIQ",
};
struct seq_buf sb;
unsigned int i, count;
char *buf = tracer->last_exception_str;
size_t buf_size = sizeof(tracer->last_exception_str);
char const header[] =
"###################### RTCPU EXCEPTION ######################";
char const trailer[] =
"#############################################################";
seq_buf_init(&sb, buf, buf_size);
seq_buf_printf(&sb, "%s %s\n",
tracer->log_prefix,
(exc->type < ARRAY_SIZE(s_str_exc_type)) ?
s_str_exc_type[exc->type] : "Unknown");
seq_buf_printf(&sb,
" R0: %08x R1: %08x R2: %08x R3: %08x\n",
exc->gpr.r0, exc->gpr.r1, exc->gpr.r2, exc->gpr.r3);
seq_buf_printf(&sb,
" R4: %08x R5: %08x R6: %08x R7: %08x\n",
exc->gpr.r4, exc->gpr.r5, exc->gpr.r6, exc->gpr.r7);
seq_buf_printf(&sb,
" R8: %08x R9: %08x R10: %08x R11: %08x\n",
exc->gpr.r8, exc->gpr.r9, exc->gpr.r10, exc->gpr.r11);
seq_buf_printf(&sb,
" R12: %08x SP: %08x LR: %08x PC: %08x\n",
exc->gpr.r12, exc->gpr.sp, exc->gpr.lr, exc->gpr.pc);
if (exc->type == CAMRTC_ARMV7_EXCEPTION_FIQ) {
seq_buf_printf(&sb,
" R8: %08x R9: %08x R10: %08x R11: %08x, R12: %08x\n",
exc->gpr.r8_prev, exc->gpr.r9_prev,
exc->gpr.r10_prev, exc->gpr.r11_prev,
exc->gpr.r12_prev);
}
seq_buf_printf(&sb, " SP: %08x LR: %08x\n",
exc->gpr.sp_prev, exc->gpr.lr_prev);
seq_buf_printf(&sb, " CPSR: %08x SPSR: %08x\n",
exc->cpsr, exc->spsr);
seq_buf_printf(&sb, " DFSR: %08x DFAR: %08x ADFSR: %08x\n",
exc->dfsr, exc->dfar, exc->adfsr);
seq_buf_printf(&sb, " IFSR: %08x IFAR: %08x AIFSR: %08x\n",
exc->ifsr, exc->ifar, exc->aifsr);
count = (exc->len -
offsetof(struct camrtc_trace_armv7_exception, callstack)) /
sizeof(struct camrtc_trace_callstack);
if (count > 0)
seq_buf_printf(&sb, "Callstack\n");
for (i = 0; i < count; ++i) {
if (i >= CAMRTC_TRACE_CALLSTACK_MAX)
break;
seq_buf_printf(&sb, " [%08x]: %08x\n",
exc->callstack[i].lr_stack_addr, exc->callstack[i].lr);
}
if (i < count)
seq_buf_printf(&sb, " ... [skipping %u entries]\n", count - i);
printk(KERN_INFO "%s\n%s\n%s\n%s%s%s\n%s\n",
" ", " ", header, buf, trailer, " ", " ");
}
static inline void rtcpu_trace_exceptions(struct tegra_rtcpu_trace *tracer)
{
const struct camrtc_trace_memory_header *header = tracer->trace_memory;
union {
struct camrtc_trace_armv7_exception exc;
uint8_t mem[CAMRTC_TRACE_EXCEPTION_SIZE];
} exc;
u32 old_next = tracer->exception_last_idx;
u32 new_next = header->exception_next_idx;
if (old_next == new_next)
return;
if (new_next >= tracer->exception_entries) {
WARN_ON_ONCE(new_next >= tracer->exception_entries);
dev_warn_ratelimited(tracer->dev,
"exception entry %u outside range 0..%u\n",
new_next, tracer->exception_entries - 1);
return;
}
new_next = array_index_nospec(new_next, tracer->exception_entries);
rtcpu_trace_invalidate_entries(tracer,
tracer->dma_handle_exceptions,
old_next, new_next,
CAMRTC_TRACE_EXCEPTION_SIZE,
tracer->exception_entries);
while (old_next != new_next) {
void *emem;
old_next = array_index_nospec(old_next, tracer->exception_entries);
emem = tracer->exceptions_base +
CAMRTC_TRACE_EXCEPTION_SIZE * old_next;
memcpy(&exc.mem, emem, CAMRTC_TRACE_EXCEPTION_SIZE);
rtcpu_trace_exception(tracer, &exc.exc);
++tracer->n_exceptions;
if (++old_next == tracer->exception_entries)
old_next = 0;
}
tracer->exception_last_idx = new_next;
}
static uint16_t rtcpu_trace_event_len(const struct camrtc_event_struct *event)
{
uint16_t len = event->header.len;
if (len > CAMRTC_TRACE_EVENT_SIZE)
len = CAMRTC_TRACE_EVENT_SIZE - CAMRTC_TRACE_EVENT_HEADER_SIZE;
else if (len > CAMRTC_TRACE_EVENT_HEADER_SIZE)
len = len - CAMRTC_TRACE_EVENT_HEADER_SIZE;
else
len = 0;
return len;
}
static void rtcpu_unknown_trace_event(struct camrtc_event_struct *event)
{
uint32_t id = event->header.id;
uint16_t len = rtcpu_trace_event_len(event);
uint64_t tstamp = event->header.tstamp;
trace_rtcpu_unknown(tstamp, id, len, &event->data.data8[0]);
}
static void rtcpu_trace_base_event(struct camrtc_event_struct *event)
{
switch (event->header.id) {
case camrtc_trace_base_target_init:
trace_rtcpu_target_init(event->header.tstamp);
break;
case camrtc_trace_base_start_scheduler:
trace_rtcpu_start_scheduler(event->header.tstamp);
break;
default:
rtcpu_unknown_trace_event(event);
break;
}
}
static void rtcpu_trace_rtos_event(struct camrtc_event_struct *event)
{
switch (event->header.id) {
case camrtc_trace_rtos_task_switched_in:
trace_rtos_task_switched_in(event->header.tstamp);
break;
case camrtc_trace_rtos_increase_tick_count:
trace_rtos_increase_tick_count(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_low_power_idle_begin:
trace_rtos_low_power_idle_begin(event->header.tstamp);
break;
case camrtc_trace_rtos_low_power_idle_end:
trace_rtos_low_power_idle_end(event->header.tstamp);
break;
case camrtc_trace_rtos_task_switched_out:
trace_rtos_task_switched_out(event->header.tstamp);
break;
case camrtc_trace_rtos_task_priority_inherit:
trace_rtos_task_priority_inherit(event->header.tstamp,
event->data.data32[0],
event->data.data32[1]);
break;
case camrtc_trace_rtos_task_priority_disinherit:
trace_rtos_task_priority_disinherit(event->header.tstamp,
event->data.data32[0],
event->data.data32[1]);
break;
case camrtc_trace_rtos_blocking_on_queue_receive:
trace_rtos_blocking_on_queue_receive(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_blocking_on_queue_send:
trace_rtos_blocking_on_queue_send(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_moved_task_to_ready_state:
trace_rtos_moved_task_to_ready_state(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_queue_create:
trace_rtos_queue_create(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_queue_create_failed:
trace_rtos_queue_create_failed(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_create_mutex:
trace_rtos_create_mutex(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_create_mutex_failed:
trace_rtos_create_mutex_failed(event->header.tstamp);
break;
case camrtc_trace_rtos_give_mutex_recursive:
trace_rtos_give_mutex_recursive(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_give_mutex_recursive_failed:
trace_rtos_give_mutex_recursive_failed(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_take_mutex_recursive:
trace_rtos_take_mutex_recursive(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_take_mutex_recursive_failed:
trace_rtos_take_mutex_recursive_failed(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_create_counting_semaphore:
trace_rtos_create_counting_semaphore(event->header.tstamp);
break;
case camrtc_trace_rtos_create_counting_semaphore_failed:
trace_rtos_create_counting_semaphore_failed(
event->header.tstamp);
break;
case camrtc_trace_rtos_queue_send:
trace_rtos_queue_send(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_queue_send_failed:
trace_rtos_queue_send_failed(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_queue_receive:
trace_rtos_queue_receive(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_queue_peek:
trace_rtos_queue_peek(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_queue_peek_from_isr:
trace_rtos_queue_peek_from_isr(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_queue_receive_failed:
trace_rtos_queue_receive_failed(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_queue_send_from_isr:
trace_rtos_queue_send_from_isr(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_queue_send_from_isr_failed:
trace_rtos_queue_send_from_isr_failed(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_queue_receive_from_isr:
trace_rtos_queue_receive_from_isr(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_queue_receive_from_isr_failed:
trace_rtos_queue_receive_from_isr_failed(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_queue_peek_from_isr_failed:
trace_rtos_queue_peek_from_isr_failed(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_queue_delete:
trace_rtos_queue_delete(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_task_create:
trace_rtos_task_create(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_task_create_failed:
trace_rtos_task_create_failed(event->header.tstamp);
break;
case camrtc_trace_rtos_task_delete:
trace_rtos_task_delete(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_task_delay_until:
trace_rtos_task_delay_until(event->header.tstamp);
break;
case camrtc_trace_rtos_task_delay:
trace_rtos_task_delay(event->header.tstamp);
break;
case camrtc_trace_rtos_task_priority_set:
trace_rtos_task_priority_set(event->header.tstamp,
event->data.data32[0],
event->data.data32[1]);
break;
case camrtc_trace_rtos_task_suspend:
trace_rtos_task_suspend(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_task_resume:
trace_rtos_task_resume(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_task_resume_from_isr:
trace_rtos_task_resume_from_isr(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_task_increment_tick:
trace_rtos_task_increment_tick(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_timer_create:
trace_rtos_timer_create(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_timer_create_failed:
trace_rtos_timer_create_failed(event->header.tstamp);
break;
case camrtc_trace_rtos_timer_command_send:
trace_rtos_timer_command_send(event->header.tstamp,
event->data.data32[0],
event->data.data32[1],
event->data.data32[2],
event->data.data32[3]);
break;
case camrtc_trace_rtos_timer_expired:
trace_rtos_timer_expired(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_timer_command_received:
trace_rtos_timer_command_received(event->header.tstamp,
event->data.data32[0],
event->data.data32[1],
event->data.data32[2]);
break;
case camrtc_trace_rtos_malloc:
trace_rtos_malloc(event->header.tstamp,
event->data.data32[0],
event->data.data32[1]);
break;
case camrtc_trace_rtos_free:
trace_rtos_free(event->header.tstamp,
event->data.data32[0],
event->data.data32[1]);
break;
case camrtc_trace_rtos_event_group_create:
trace_rtos_event_group_create(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_event_group_create_failed:
trace_rtos_event_group_create_failed(event->header.tstamp);
break;
case camrtc_trace_rtos_event_group_sync_block:
trace_rtos_event_group_sync_block(event->header.tstamp,
event->data.data32[0],
event->data.data32[1],
event->data.data32[2]);
break;
case camrtc_trace_rtos_event_group_sync_end:
trace_rtos_event_group_sync_end(event->header.tstamp,
event->data.data32[0],
event->data.data32[1],
event->data.data32[2],
event->data.data32[3]);
break;
case camrtc_trace_rtos_event_group_wait_bits_block:
trace_rtos_event_group_wait_bits_block(event->header.tstamp,
event->data.data32[0],
event->data.data32[1]);
break;
case camrtc_trace_rtos_event_group_wait_bits_end:
trace_rtos_event_group_wait_bits_end(event->header.tstamp,
event->data.data32[0],
event->data.data32[1],
event->data.data32[2]);
break;
case camrtc_trace_rtos_event_group_clear_bits:
trace_rtos_event_group_clear_bits(event->header.tstamp,
event->data.data32[0],
event->data.data32[1]);
break;
case camrtc_trace_rtos_event_group_clear_bits_from_isr:
trace_rtos_event_group_clear_bits_from_isr(event->header.tstamp,
event->data.data32[0],
event->data.data32[1]);
break;
case camrtc_trace_rtos_event_group_set_bits:
trace_rtos_event_group_set_bits(event->header.tstamp,
event->data.data32[0],
event->data.data32[1]);
break;
case camrtc_trace_rtos_event_group_set_bits_from_isr:
trace_rtos_event_group_set_bits_from_isr(event->header.tstamp,
event->data.data32[0],
event->data.data32[1]);
break;
case camrtc_trace_rtos_event_group_delete:
trace_rtos_event_group_delete(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_rtos_pend_func_call:
trace_rtos_pend_func_call(event->header.tstamp,
event->data.data32[0],
event->data.data32[1],
event->data.data32[2],
event->data.data32[3]);
break;
case camrtc_trace_rtos_pend_func_call_from_isr:
trace_rtos_pend_func_call_from_isr(event->header.tstamp,
event->data.data32[0],
event->data.data32[1],
event->data.data32[2],
event->data.data32[3]);
break;
case camrtc_trace_rtos_queue_registry_add:
trace_rtos_queue_registry_add(event->header.tstamp,
event->data.data32[0],
event->data.data32[1]);
break;
default:
rtcpu_unknown_trace_event(event);
break;
}
}
static void rtcpu_trace_dbg_event(struct camrtc_event_struct *event)
{
switch (event->header.id) {
case camrtc_trace_dbg_unknown:
trace_rtcpu_dbg_unknown(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_dbg_enter:
trace_rtcpu_dbg_enter(event->header.tstamp,
event->data.data32[0]);
break;
case camrtc_trace_dbg_exit:
trace_rtcpu_dbg_exit(event->header.tstamp);
break;
case camrtc_trace_dbg_set_loglevel:
trace_rtcpu_dbg_set_loglevel(event->header.tstamp,
event->data.data32[0],
event->data.data32[1]);
break;
default:
rtcpu_unknown_trace_event(event);
break;
}
}
const char * const g_trace_vinotify_tag_strs[] = {
"FS", "FE",
"CSIMUX_FRAME", "CSIMUX_STREAM",
"CHANSEL_PXL_SOF", "CHANSEL_PXL_EOF",
"CHANSEL_EMBED_SOF", "CHANSEL_EMBED_EOF",
"CHANSEL_NLINES", "CHANSEL_FAULT",
"CHANSEL_FAULT_FE", "CHANSEL_NOMATCH",
"CHANSEL_COLLISION", "CHANSEL_SHORT_FRAME",
"CHANSEL_LOAD_FRAMED", "ATOMP_PACKER_OVERFLOW",
"ATOMP_FS", "ATOMP_FE",
"ATOMP_FRAME_DONE", "ATOMP_EMB_DATA_DONE",
"ATOMP_FRAME_NLINES_DONE", "ATOMP_FRAME_TRUNCATED",
"ATOMP_FRAME_TOSSED", "ATOMP_PDAF_DATA_DONE",
"VIFALC_TDSTATE", "VIFALC_ACTIONLST",
"ISPBUF_FIFO_OVERFLOW", "ISPBUF_FS",
"ISPBUF_FE", "VGP0_DONE",
"VGP1_DONE", "FMLITE_DONE",
};
const unsigned int g_trace_vinotify_tag_str_count =
ARRAY_SIZE(g_trace_vinotify_tag_strs);
static void rtcpu_trace_vinotify_event(struct camrtc_event_struct *event)
{
switch (event->header.id) {
case camrtc_trace_vinotify_event_ts64:
trace_rtcpu_vinotify_event_ts64(event->header.tstamp,
(event->data.data32[0] >> 1) & 0x7f, event->data.data32[0],
((u64)event->data.data32[3] << 32) | event->data.data32[1],
event->data.data32[2]);
break;
case camrtc_trace_vinotify_event:
trace_rtcpu_vinotify_event(event->header.tstamp,
event->data.data32[0], event->data.data32[1],
event->data.data32[2], event->data.data32[3],
event->data.data32[4], event->data.data32[5],
event->data.data32[6]);
break;
case camrtc_trace_vinotify_error:
trace_rtcpu_vinotify_error(event->header.tstamp,
event->data.data32[0], event->data.data32[1],
event->data.data32[2], event->data.data32[3],
event->data.data32[4], event->data.data32[5],
event->data.data32[6]);
break;
default:
rtcpu_unknown_trace_event(event);
break;
}
}
static void rtcpu_trace_vi_frame_event(struct tegra_rtcpu_trace *tracer,
struct camrtc_event_struct *event)
{
struct nvhost_device_data *pdata = NULL;
u64 ts = 0;
u32 vi_unit_id = event->data.data32[6];
if (tracer->vi_platform_device == NULL)
return;
if (vi_unit_id == 0)
pdata = platform_get_drvdata(tracer->vi_platform_device);
else if (vi_unit_id == 1)
pdata = platform_get_drvdata(tracer->vi1_platform_device);
if (pdata == NULL)
return;
switch (event->header.id) {
case camrtc_trace_vi_frame_begin:
ts = ((u64)event->data.data32[5] << 32) |
(u64)event->data.data32[4];
trace_vi_frame_begin(
ts,
event->data.data32[2],
event->data.data32[0],
event->data.data32[1],
pdata->class
);
break;
case camrtc_trace_vi_frame_end:
ts = ((u64)event->data.data32[5] << 32) |
(u64)event->data.data32[4];
trace_vi_frame_end(
ts,
event->data.data32[2],
event->data.data32[0],
event->data.data32[1],
pdata->class
);
trace_task_fence(
NVDEV_FENCE_KIND_POST,
VI_CLASS_ID,
event->data.data32[0],
event->data.data32[1],
NVDEV_FENCE_TYPE_SYNCPT,
event->data.data32[0],
event->data.data32[1],
0, 0, 0, 0, 0
);
break;
default:
pr_warn("%pS invalid event id %d\n",
__func__, event->header.id);
break;
}
}
static void rtcpu_trace_vi_event(struct tegra_rtcpu_trace *tracer,
struct camrtc_event_struct *event)
{
switch (event->header.id) {
case camrtc_trace_vi_frame_begin:
case camrtc_trace_vi_frame_end:
rtcpu_trace_vi_frame_event(tracer, event);
break;
default:
rtcpu_unknown_trace_event(event);
break;
}
}
const char * const g_trace_isp_falcon_task_strs[] = {
"UNUSED",
"SCHED_ERROR",
"SCHED_HANDLE_STAT",
"SCHED_FINISH_TILE",
"SCHED_FINISH_SLICE",
"HANDLE_EVENT",
"INPUT_ACTION",
"ISR"
};
const unsigned int g_trace_isp_falcon_task_str_count =
ARRAY_SIZE(g_trace_isp_falcon_task_strs);
#define TRACE_ISP_FALCON_EVENT_TS 13U
#define TRACE_ISP_FALCON_EVENT_TE 14U
#define TRACE_ISP_FALCON_PROFILE_START 16U
#define TRACE_ISP_FALCON_PROFILE_END 17U
static void rtcpu_trace_isp_task_event(struct tegra_rtcpu_trace *tracer,
struct camrtc_event_struct *event)
{
struct nvhost_device_data *pdata = NULL;
struct platform_device *pdev = NULL;
u32 isp_unit_id = event->data.data32[4];
if (isp_unit_id == 0U)
pdev = tracer->isp_platform_device;
else if (isp_unit_id == 1U)
pdev = tracer->isp1_platform_device;
else
pdev = NULL;
if (pdev == NULL)
return;
pdata = platform_get_drvdata(pdev);
if (pdata == NULL)
return;
switch (event->header.id) {
case camrtc_trace_isp_task_begin:
trace_isp_task_begin(
event->header.tstamp,
event->data.data32[2],
event->data.data32[0],
event->data.data32[1],
pdata->class
);
trace_task_fence(
NVDEV_FENCE_KIND_PRE,
ISP_CLASS_ID,
event->data.data32[0],
event->data.data32[1],
NVDEV_FENCE_TYPE_SYNCPT,
event->data.data32[0],
event->data.data32[1],
0, 0, 0, 0, 0
);
break;
case camrtc_trace_isp_task_end:
trace_isp_task_end(
event->header.tstamp,
event->data.data32[2],
event->data.data32[0],
event->data.data32[1],
pdata->class
);
trace_task_fence(
NVDEV_FENCE_KIND_POST,
ISP_CLASS_ID,
event->data.data32[0],
event->data.data32[1],
NVDEV_FENCE_TYPE_SYNCPT,
event->data.data32[0],
event->data.data32[1],
0, 0, 0, 0, 0
);
break;
default:
pr_warn("%pS invalid event id %d\n",
__func__, event->header.id);
break;
}
}
static void rtcpu_trace_isp_falcon_event(struct camrtc_event_struct *event)
{
u8 ispfalcon_tag = (u8) ((event->data.data32[0] & 0xFF) >> 1U);
u8 ch = (u8) ((event->data.data32[0] & 0xFF00) >> 8U);
u8 seq = (u8) ((event->data.data32[0] & 0xFF0000) >> 16U);
u32 low_bits_ts = event->data.data32[1];
u32 high_bits_ts = event->data.data32[2];
u64 tstamp = ((u64)high_bits_ts << 32U) | low_bits_ts;
u32 isp_unit_id = event->data.data32[5];
switch (ispfalcon_tag) {
case TRACE_ISP_FALCON_EVENT_TS:
trace_rtcpu_isp_falcon_tile_start(
ch, seq, tstamp, isp_unit_id,
(u8) (event->data.data32[4] & 0xFF),
(u8) ((event->data.data32[4] & 0xFF00) >> 8U),
(u16) (event->data.data32[3] & 0xFFFF),
(u16) ((event->data.data32[3] & 0xFFFF0000) >> 16U));
break;
case TRACE_ISP_FALCON_EVENT_TE:
trace_rtcpu_isp_falcon_tile_end(
ch, seq, tstamp, isp_unit_id,
(u8) (event->data.data32[4] & 0xFF),
(u8) ((event->data.data32[4] & 0xFF00) >> 8U));
break;
case TRACE_ISP_FALCON_PROFILE_START:
trace_rtcpu_isp_falcon_task_start(
ch, tstamp, isp_unit_id,
event->data.data32[3]);
break;
case TRACE_ISP_FALCON_PROFILE_END:
trace_rtcpu_isp_falcon_task_end(
tstamp, isp_unit_id,
event->data.data32[3]);
break;
default:
trace_rtcpu_isp_falcon(
ispfalcon_tag, ch, seq, tstamp, isp_unit_id,
event->data.data32[3],
event->data.data32[4]);
break;
}
}
static void rtcpu_trace_isp_event(struct tegra_rtcpu_trace *tracer,
struct camrtc_event_struct *event)
{
switch (event->header.id) {
case camrtc_trace_isp_task_begin:
case camrtc_trace_isp_task_end:
rtcpu_trace_isp_task_event(tracer, event);
break;
case camrtc_trace_isp_falcon_traces_event:
rtcpu_trace_isp_falcon_event(event);
break;
default:
rtcpu_unknown_trace_event(event);
break;
}
}
const char * const g_trace_nvcsi_intr_class_strs[] = {
"GLOBAL",
"CORRECTABLE_ERR",
"UNCORRECTABLE_ERR",
};
const unsigned int g_trace_nvcsi_intr_class_str_count =
ARRAY_SIZE(g_trace_nvcsi_intr_class_strs);
const char * const g_trace_nvcsi_intr_type_strs[] = {
"SW_DEBUG",
"HOST1X",
"PHY_INTR", "PHY_INTR0", "PHY_INTR1",
"STREAM_NOVC", "STREAM_VC",
};
const unsigned int g_trace_nvcsi_intr_type_str_count =
ARRAY_SIZE(g_trace_nvcsi_intr_type_strs);
static void rtcpu_trace_nvcsi_event(struct camrtc_event_struct *event)
{
u64 ts_tsc = ((u64)event->data.data32[5] << 32) |
(u64)event->data.data32[4];
switch (event->header.id) {
case camrtc_trace_nvcsi_intr:
trace_rtcpu_nvcsi_intr(ts_tsc,
(event->data.data32[0] & 0xff),
(event->data.data32[1] & 0xff),
event->data.data32[2],
event->data.data32[3]);
break;
default:
rtcpu_unknown_trace_event(event);
break;
}
}
struct capture_event_progress {
uint32_t channel_id;
uint32_t sequence;
};
struct capture_event_isp {
uint32_t channel_id;
uint32_t prog_sequence;
uint32_t cap_sequence;
uint8_t isp_settings_id;
uint8_t vi_channel_id;
uint8_t pad_[2];
};
struct capture_event {
union {
struct capture_event_progress progress;
struct capture_event_isp isp;
bool suspend;
};
};
static void rtcpu_trace_capture_event(struct camrtc_event_struct *event)
{
const struct capture_event *ev = (const void *)&event->data;
switch (event->header.id) {
case camrtc_trace_capture_event_sof:
trace_capture_event_sof(event->header.tstamp,
ev->progress.channel_id, ev->progress.sequence);
break;
case camrtc_trace_capture_event_eof:
trace_capture_event_eof(event->header.tstamp,
ev->progress.channel_id, ev->progress.sequence);
break;
case camrtc_trace_capture_event_error:
trace_capture_event_error(event->header.tstamp,
ev->progress.channel_id, ev->progress.sequence);
break;
case camrtc_trace_capture_event_reschedule:
trace_capture_event_reschedule(event->header.tstamp,
ev->progress.channel_id, ev->progress.sequence);
break;
case camrtc_trace_capture_event_reschedule_isp:
trace_capture_event_reschedule_isp(event->header.tstamp,
ev->isp.channel_id, ev->isp.cap_sequence, ev->isp.prog_sequence,
ev->isp.isp_settings_id, ev->isp.vi_channel_id);
break;
case camrtc_trace_capture_event_isp_done:
trace_capture_event_isp_done(event->header.tstamp,
ev->isp.channel_id, ev->isp.cap_sequence, ev->isp.prog_sequence,
ev->isp.isp_settings_id, ev->isp.vi_channel_id);
break;
case camrtc_trace_capture_event_isp_error:
trace_capture_event_isp_error(event->header.tstamp,
ev->isp.channel_id, ev->isp.cap_sequence, ev->isp.prog_sequence,
ev->isp.isp_settings_id, ev->isp.vi_channel_id);
break;
case camrtc_trace_capture_event_wdt:
trace_capture_event_wdt(event->header.tstamp);
break;
case camrtc_trace_capture_event_report_program:
trace_capture_event_report_program(event->header.tstamp,
ev->progress.channel_id, ev->progress.sequence);
break;
case camrtc_trace_capture_event_suspend:
trace_capture_event_suspend(event->header.tstamp, ev->suspend);
break;
case camrtc_trace_capture_event_suspend_isp:
trace_capture_event_suspend_isp(event->header.tstamp, ev->suspend);
break;
case camrtc_trace_capture_event_inject:
case camrtc_trace_capture_event_sensor:
default:
rtcpu_unknown_trace_event(event);
break;
}
}
static void rtcpu_trace_perf_event(struct camrtc_event_struct *event)
{
const struct camrtc_trace_perf_counter_data *perf = (const void *)&event->data;
switch (event->header.id) {
case camrtc_trace_perf_reset:
trace_rtcpu_perf_reset(event->header.tstamp, perf);
break;
case camrtc_trace_perf_counters:
trace_rtcpu_perf_counters(event->header.tstamp, perf);
break;
default:
rtcpu_unknown_trace_event(event);
break;
}
}
static void rtcpu_trace_array_event(struct tegra_rtcpu_trace *tracer,
struct camrtc_event_struct *event)
{
switch (CAMRTC_EVENT_MODULE_FROM_ID(event->header.id)) {
case CAMRTC_EVENT_MODULE_BASE:
rtcpu_trace_base_event(event);
break;
case CAMRTC_EVENT_MODULE_RTOS:
rtcpu_trace_rtos_event(event);
break;
case CAMRTC_EVENT_MODULE_DBG:
rtcpu_trace_dbg_event(event);
break;
case CAMRTC_EVENT_MODULE_VINOTIFY:
rtcpu_trace_vinotify_event(event);
break;
case CAMRTC_EVENT_MODULE_I2C:
break;
case CAMRTC_EVENT_MODULE_VI:
rtcpu_trace_vi_event(tracer, event);
break;
case CAMRTC_EVENT_MODULE_ISP:
rtcpu_trace_isp_event(tracer, event);
break;
case CAMRTC_EVENT_MODULE_NVCSI:
rtcpu_trace_nvcsi_event(event);
break;
case CAMRTC_EVENT_MODULE_CAPTURE:
rtcpu_trace_capture_event(event);
break;
case CAMRTC_EVENT_MODULE_PERF:
rtcpu_trace_perf_event(event);
break;
default:
rtcpu_unknown_trace_event(event);
break;
}
}
static void trace_rtcpu_log(struct tegra_rtcpu_trace *tracer,
uint32_t id, uint32_t len, const uint8_t *data8)
{
size_t used;
if (unlikely(id != camrtc_trace_type_string))
return;
if (len >= CAMRTC_TRACE_EVENT_PAYLOAD_SIZE)
/* Ignore NULs at the end of buffer */
len = strnlen(data8, CAMRTC_TRACE_EVENT_PAYLOAD_SIZE);
used = tracer->printk_used;
if (unlikely(used + len > sizeof(tracer->printk))) {
/* Too long concatenated message, print it out now */
pr_info("%s %.*s\n", tracer->log_prefix,
(int)used, tracer->printk);
used = 0;
}
memcpy(tracer->printk + used, data8, len);
used += len;
if (likely(used > 0)) {
char end = tracer->printk[used - 1];
/*
* Some log entries from rtcpu consists of multiple
* messages. If the string does not end with \r or
* \n, do not print it now but rather wait for the
* next piece.
*/
if (end == '\r' || end == '\n') {
while (--used > 0) {
end = tracer->printk[used - 1];
if (!(end == '\r' || end == '\n'))
break;
}
pr_info("%s %.*s\n", tracer->log_prefix,
(int)used, tracer->printk);
used = 0;
}
}
tracer->printk_used = used;
}
static void rtcpu_trace_event(struct tegra_rtcpu_trace *tracer,
struct camrtc_event_struct *event)
{
uint32_t id = event->header.id;
uint32_t type = CAMRTC_EVENT_TYPE_FROM_ID(id);
uint16_t len = rtcpu_trace_event_len(event);
uint8_t *data8 = &event->data.data8[0];
switch (type) {
case CAMRTC_EVENT_TYPE_ARRAY:
rtcpu_trace_array_event(tracer, event);
break;
case CAMRTC_EVENT_TYPE_ARMV7_EXCEPTION:
trace_rtcpu_armv7_exception(event->header.tstamp,
event->data.data32[0]);
break;
case CAMRTC_EVENT_TYPE_PAD:
/* ignore */
break;
case CAMRTC_EVENT_TYPE_START:
trace_rtcpu_start(event->header.tstamp);
break;
case CAMRTC_EVENT_TYPE_STRING:
trace_rtcpu_string(event->header.tstamp, id, len, (char *)data8);
if (likely(tracer->enable_printk))
trace_rtcpu_log(tracer, id, len, data8);
break;
case CAMRTC_EVENT_TYPE_BULK:
trace_rtcpu_bulk(event->header.tstamp, id, len, data8);
break;
default:
rtcpu_unknown_trace_event(event);
break;
}
}
static inline void rtcpu_trace_events(struct tegra_rtcpu_trace *tracer)
{
const struct camrtc_trace_memory_header *header = tracer->trace_memory;
u32 old_next = tracer->event_last_idx;
u32 new_next = header->event_next_idx;
struct camrtc_event_struct *event, *last_event;
if (new_next >= tracer->event_entries) {
WARN_ON_ONCE(new_next >= tracer->event_entries);
dev_warn_ratelimited(tracer->dev,
"trace entry %u outside range 0..%u\n",
new_next, tracer->event_entries - 1);
return;
}
new_next = array_index_nospec(new_next, tracer->event_entries);
if (old_next == new_next)
return;
/* Wake up any polling process waiting for data */
wake_up_all(&tracer->wait_queue);
rtcpu_trace_invalidate_entries(tracer,
tracer->dma_handle_events,
old_next, new_next,
CAMRTC_TRACE_EVENT_SIZE,
tracer->event_entries);
/* pull events */
while (old_next != new_next) {
old_next = array_index_nospec(old_next, tracer->event_entries);
event = &tracer->events[old_next];
last_event = event;
rtcpu_trace_event(tracer, event);
tracer->n_events++;
if (++old_next == tracer->event_entries)
old_next = 0;
}
tracer->event_last_idx = new_next;
tracer->copy_last_event = *last_event;
}
void tegra_rtcpu_trace_flush(struct tegra_rtcpu_trace *tracer)
{
if (tracer == NULL)
return;
mutex_lock(&tracer->lock);
/* invalidate the cache line for the pointers */
dma_sync_single_for_cpu(tracer->dev, tracer->dma_handle_pointers,
CAMRTC_TRACE_NEXT_IDX_SIZE, DMA_FROM_DEVICE);
/* process exceptions and events */
rtcpu_trace_exceptions(tracer);
rtcpu_trace_events(tracer);
mutex_unlock(&tracer->lock);
}
EXPORT_SYMBOL(tegra_rtcpu_trace_flush);
static void rtcpu_trace_worker(struct work_struct *work)
{
struct tegra_rtcpu_trace *tracer;
tracer = container_of(work, struct tegra_rtcpu_trace, work.work);
tegra_rtcpu_trace_flush(tracer);
/* reschedule */
schedule_delayed_work(&tracer->work, tracer->work_interval_jiffies);
}
static int32_t raw_trace_read_impl(
struct tegra_rtcpu_trace *tracer,
char __user *user_buffer,
ssize_t *events_copied,
uint32_t *last_read_event_idx,
const u32 num_events_requested)
{
bool buffer_wrapped;
const struct camrtc_trace_memory_header *header = tracer->trace_memory;
u32 old_next = *last_read_event_idx;
u32 new_next = header->event_next_idx;
uint32_t num_events_to_copy;
if (new_next >= tracer->event_entries) {
WARN_ON_ONCE(new_next >= tracer->event_entries);
dev_warn_ratelimited(
tracer->dev,
"trace entry %u outside range 0..%u\n",
new_next,
tracer->event_entries - 1);
return -EIO;
}
new_next = array_index_nospec(new_next, tracer->event_entries);
old_next = array_index_nospec(old_next, tracer->event_entries);
if (old_next == new_next)
return 0;
rtcpu_trace_invalidate_entries(
tracer,
tracer->dma_handle_events,
old_next, new_next,
CAMRTC_TRACE_EVENT_SIZE,
tracer->event_entries);
buffer_wrapped = (new_next < old_next);
num_events_to_copy =
(!buffer_wrapped) ?
(new_next - old_next) : (tracer->event_entries - old_next + new_next);
num_events_to_copy =
num_events_requested > num_events_to_copy ?
num_events_to_copy : num_events_requested;
/* No wrap around */
if (!buffer_wrapped) {
if (copy_to_user(
&user_buffer[(*events_copied) * sizeof(struct camrtc_event_struct)],
&tracer->events[old_next],
num_events_to_copy * sizeof(struct camrtc_event_struct))) {
return -EFAULT;
}
}
/* Handling the buffer's circular wrap around */
else {
u32 first_part;
u32 second_part;
/* copy from old_next to the end of buffer
* or till max number of events that can be copied.
*/
first_part = tracer->event_entries - old_next;
if (first_part > num_events_to_copy)
first_part = num_events_to_copy;
if (copy_to_user(
&user_buffer[(*events_copied) * sizeof(struct camrtc_event_struct)],
&tracer->events[old_next],
first_part * sizeof(struct camrtc_event_struct)))
return -EFAULT;
/* for wrap around usecase, copy from buffer's beginning */
second_part = num_events_to_copy - first_part;
if (second_part > 0)
if (copy_to_user(
&user_buffer[
(*events_copied + first_part) * sizeof(struct camrtc_event_struct)],
&tracer->events[0],
second_part * sizeof(struct camrtc_event_struct)))
return -EFAULT;
}
*last_read_event_idx = (old_next + num_events_to_copy) % tracer->event_entries;
*events_copied += num_events_to_copy;
return 0;
}
static bool check_event_availability(
struct rtcpu_raw_trace_context *fd_context,
struct tegra_rtcpu_trace *tracer)
{
bool ret;
u32 last_read_event_idx;
struct camrtc_trace_memory_header *header;
last_read_event_idx = fd_context->raw_trace_last_read_event_idx;
header = tracer->trace_memory;
/* If buffer has already wrapped around before the 1st read */
if (unlikely(fd_context->first_read_call)) {
if (header->wrapped_counter > 0) {
last_read_event_idx = header->event_next_idx + 1;
if (last_read_event_idx == tracer->event_entries)
last_read_event_idx = 0;
}
}
/* check if new event on worker thread is relavant for current reader */
ret = header->event_next_idx != last_read_event_idx;
return ret;
}
static ssize_t
rtcpu_raw_trace_read(struct file *file, char __user *user_buffer, size_t buffer_size, loff_t *ppos)
{
struct tegra_rtcpu_trace *tracer;
u32 last_read_event_idx;
u32 num_events_requested;
struct camrtc_trace_memory_header *header;
ssize_t events_copied = 0;
bool blocking_call = !(file->f_flags & O_NONBLOCK);
struct rtcpu_raw_trace_context *fd_context = file->private_data;
if (!fd_context) {
pr_err("file descriptor context is not set in private data\n");
return -ENODEV;
}
tracer = fd_context->tracer;
if (!tracer) {
pr_err("Tracer is not set in file descriptor context\n");
return -ENODEV;
}
last_read_event_idx = fd_context->raw_trace_last_read_event_idx;
header = tracer->trace_memory;
/* If buffer has already wrapped around before the 1st read */
if (unlikely(fd_context->first_read_call)) {
if (header->wrapped_counter > 0) {
last_read_event_idx = header->event_next_idx + 1;
if (last_read_event_idx == tracer->event_entries)
last_read_event_idx = 0;
}
fd_context->first_read_call = false;
}
/* Truncate buffer_size if it exceeds the maximum read size */
if (buffer_size > MAX_READ_SIZE) {
dev_dbg(tracer->dev,
"Requested read size too large, truncating to %zd\n", MAX_READ_SIZE);
buffer_size = MAX_READ_SIZE;
}
num_events_requested = buffer_size / sizeof(struct camrtc_event_struct);
if (num_events_requested == 0) {
dev_dbg(tracer->dev, "Invalid buffer size\n");
return -ENOMEM;
}
/* Validate if user buffer is a valid address */
if (!access_ok(user_buffer, buffer_size)) {
dev_err(tracer->dev, "Invalid user buffer address\n");
return -EINVAL;
}
do {
int32_t ret = raw_trace_read_impl(
tracer,
user_buffer,
&events_copied,
&last_read_event_idx,
num_events_requested - events_copied);
if (ret < 0) {
dev_err(tracer->dev, "Call to raw_trace_read_impl() failed.\n");
return ret;
}
if (!blocking_call)
break;
/* Wait indefinitely until event is not available */
ret = wait_event_interruptible(
tracer->wait_queue,
check_event_availability(fd_context, tracer));
if (ret < 0)
return ret;
} while (events_copied < num_events_requested);
fd_context->raw_trace_last_read_event_idx = last_read_event_idx;
file->private_data = fd_context;
return events_copied * sizeof(struct camrtc_event_struct);
}
static ssize_t rtcpu_raw_trace_write(
struct file *file, const char __user *user_buffer, size_t buffer_size, loff_t *ppos)
{
struct tegra_rtcpu_trace *tracer;
const struct camrtc_trace_memory_header *header;
struct rtcpu_raw_trace_context *fd_context = file->private_data;
if (!fd_context) {
pr_err("file descriptor context is not set in private data\n");
return -ENODEV;
}
tracer = fd_context->tracer;
if (!tracer) {
pr_err("Tracer is not set in file descriptor context\n");
return -ENODEV;
}
header = tracer->trace_memory;
fd_context->raw_trace_last_read_event_idx = header->event_next_idx;
file->private_data = fd_context;
return buffer_size;
}
static unsigned int rtcpu_raw_trace_poll(struct file *file, poll_table *wait)
{
struct tegra_rtcpu_trace *tracer;
unsigned int ret = 0;
struct rtcpu_raw_trace_context *fd_context = file->private_data;
if (!fd_context) {
pr_err("file descriptor context is not set in private data\n");
return -ENODEV;
}
tracer = fd_context->tracer;
if (!tracer) {
pr_err("Tracer is not set in file descriptor context\n");
return -ENODEV;
}
/* check if new event on worker thread is relavant for current reader */
if (check_event_availability(fd_context, tracer)) {
ret = POLLIN | POLLRDNORM; // event is available to read for current reader
return ret;
}
/* No data available, register for wait queue */
poll_wait(file, &tracer->wait_queue, wait);
return ret;
}
static int rtcpu_raw_trace_open(struct inode *inode, struct file *file)
{
struct tegra_rtcpu_trace *tracer;
struct rtcpu_raw_trace_context *fd_context;
fd_context = kzalloc(sizeof(*fd_context), GFP_KERNEL);
if (unlikely(fd_context == NULL))
return -ENOMEM;
tracer = container_of(inode->i_cdev, struct tegra_rtcpu_trace, s_dev);
if (!tracer) {
pr_err("Failed to retrieve tracer\n");
kfree(fd_context);
return -ENODEV;
}
fd_context->tracer = tracer;
fd_context->raw_trace_last_read_event_idx = 0;
fd_context->first_read_call = true;
file->private_data = fd_context;
return nonseekable_open(inode, file);
}
static int rtcpu_raw_trace_release(struct inode *inode, struct file *file)
{
kfree(file->private_data);
return 0;
}
/*
* Debugfs
*/
#define DEFINE_SEQ_FOPS(_fops_, _show_) \
static int _fops_ ## _open(struct inode *inode, struct file *file) \
{ \
return single_open(file, _show_, inode->i_private); \
} \
static const struct file_operations _fops_ = { \
.open = _fops_ ## _open, \
.read = seq_read, \
.llseek = seq_lseek, \
.release = single_release }
static const struct file_operations rtcpu_raw_trace_fops = {
.owner = THIS_MODULE,
#if defined(NV_NO_LLSEEK_PRESENT)
.llseek = no_llseek,
#endif
.read = rtcpu_raw_trace_read,
.write = rtcpu_raw_trace_write,
.poll = rtcpu_raw_trace_poll,
.open = rtcpu_raw_trace_open,
.release = rtcpu_raw_trace_release,
};
static int rtcpu_trace_debugfs_stats_read(
struct seq_file *file, void *data)
{
struct tegra_rtcpu_trace *tracer = file->private;
seq_printf(file, "Exceptions: %u\nEvents: %llu\n",
tracer->n_exceptions, tracer->n_events);
return 0;
}
DEFINE_SEQ_FOPS(rtcpu_trace_debugfs_stats, rtcpu_trace_debugfs_stats_read);
static int rtcpu_trace_debugfs_last_exception_read(
struct seq_file *file, void *data)
{
struct tegra_rtcpu_trace *tracer = file->private;
seq_puts(file, tracer->last_exception_str);
return 0;
}
DEFINE_SEQ_FOPS(rtcpu_trace_debugfs_last_exception,
rtcpu_trace_debugfs_last_exception_read);
static int rtcpu_trace_debugfs_last_event_read(
struct seq_file *file, void *data)
{
struct tegra_rtcpu_trace *tracer = file->private;
struct camrtc_event_struct *event = &tracer->copy_last_event;
unsigned int i;
uint16_t payload_len;
if (tracer->n_events == 0)
return 0;
payload_len = rtcpu_trace_event_len(event);
seq_printf(file, "Len: %u\nID: 0x%08x\nTimestamp: %llu\n",
event->header.len, event->header.id, event->header.tstamp);
switch (CAMRTC_EVENT_TYPE_FROM_ID(event->header.id)) {
case CAMRTC_EVENT_TYPE_ARRAY:
for (i = 0; i < payload_len / 4; ++i)
seq_printf(file, "0x%08x ", event->data.data32[i]);
seq_puts(file, "\n");
break;
case CAMRTC_EVENT_TYPE_ARMV7_EXCEPTION:
seq_puts(file, "Exception.\n");
break;
case CAMRTC_EVENT_TYPE_PAD:
break;
case CAMRTC_EVENT_TYPE_START:
seq_puts(file, "Start.\n");
break;
case CAMRTC_EVENT_TYPE_STRING:
seq_puts(file, (char *) event->data.data8);
break;
case CAMRTC_EVENT_TYPE_BULK:
for (i = 0; i < payload_len; ++i)
seq_printf(file, "0x%02x ", event->data.data8[i]);
seq_puts(file, "\n");
break;
default:
seq_puts(file, "Unknown type.\n");
break;
}
return 0;
}
DEFINE_SEQ_FOPS(rtcpu_trace_debugfs_last_event,
rtcpu_trace_debugfs_last_event_read);
static void rtcpu_trace_debugfs_deinit(struct tegra_rtcpu_trace *tracer)
{
debugfs_remove_recursive(tracer->debugfs_root);
}
static void rtcpu_trace_debugfs_init(struct tegra_rtcpu_trace *tracer)
{
struct dentry *entry;
tracer->debugfs_root = debugfs_create_dir("tegra_rtcpu_trace", NULL);
if (IS_ERR_OR_NULL(tracer->debugfs_root))
return;
entry = debugfs_create_file("stats", S_IRUGO,
tracer->debugfs_root, tracer, &rtcpu_trace_debugfs_stats);
if (IS_ERR_OR_NULL(entry))
goto failed_create;
entry = debugfs_create_file("last_exception", S_IRUGO,
tracer->debugfs_root, tracer, &rtcpu_trace_debugfs_last_exception);
if (IS_ERR_OR_NULL(entry))
goto failed_create;
entry = debugfs_create_file("last_event", S_IRUGO,
tracer->debugfs_root, tracer, &rtcpu_trace_debugfs_last_event);
if (IS_ERR_OR_NULL(entry))
goto failed_create;
return;
failed_create:
debugfs_remove_recursive(tracer->debugfs_root);
}
/* Character device */
static struct class *rtcpu_raw_trace_class;
static int rtcpu_raw_trace_major;
static int raw_trace_node_drv_register(struct tegra_rtcpu_trace *tracer)
{
dev_t devt;
int ret;
rtcpu_raw_trace_major = register_chrdev(0, DEVICE_NAME, &rtcpu_raw_trace_fops);
if (rtcpu_raw_trace_major < 0) {
dev_err(tracer->dev, "Register_chrdev failed\n");
return rtcpu_raw_trace_major;
}
devt = MKDEV(rtcpu_raw_trace_major, 0);
cdev_init(&tracer->s_dev, &rtcpu_raw_trace_fops);
tracer->s_dev.owner = THIS_MODULE;
tracer->s_dev.ops = &rtcpu_raw_trace_fops;
ret = cdev_add(&tracer->s_dev, devt, 1);
if (ret < 0) {
dev_err(tracer->dev, "cdev_add() failed %d\n", ret);
return ret;
}
#if defined(NV_CLASS_CREATE_HAS_NO_OWNER_ARG) /* Linux v6.4 */
rtcpu_raw_trace_class = class_create(DEVICE_NAME);
#else
rtcpu_raw_trace_class = class_create(THIS_MODULE, DEVICE_NAME);
#endif
if (IS_ERR(rtcpu_raw_trace_class)) {
dev_err(tracer->dev, "device class file already in use\n");
unregister_chrdev(rtcpu_raw_trace_major, DEVICE_NAME);
return PTR_ERR(rtcpu_raw_trace_class);
}
device_create(rtcpu_raw_trace_class, tracer->dev, devt, tracer, DEVICE_NAME);
return 0;
}
static void raw_trace_node_unregister(
struct tegra_rtcpu_trace *tracer)
{
dev_t devt;
devt = MKDEV(rtcpu_raw_trace_major, 0);
device_destroy(rtcpu_raw_trace_class, devt);
cdev_del(&tracer->s_dev);
class_destroy(rtcpu_raw_trace_class);
unregister_chrdev(rtcpu_raw_trace_major, DEVICE_NAME);
}
/*
* Init/Cleanup
*/
struct tegra_rtcpu_trace *tegra_rtcpu_trace_create(struct device *dev,
struct camrtc_device_group *camera_devices)
{
struct tegra_rtcpu_trace *tracer;
u32 param;
int ret;
tracer = kzalloc(sizeof(*tracer), GFP_KERNEL);
if (unlikely(tracer == NULL))
return NULL;
tracer->dev = dev;
mutex_init(&tracer->lock);
/* Get the trace memory */
ret = rtcpu_trace_setup_memory(tracer);
if (ret) {
dev_err(dev, "Trace memory setup failed: %d\n", ret);
kfree(tracer);
return NULL;
}
/* Initialize the trace memory */
rtcpu_trace_init_memory(tracer);
/* Debugfs */
rtcpu_trace_debugfs_init(tracer);
if (camera_devices != NULL) {
tracer->isp_platform_device =
camrtc_device_get_byname(camera_devices, "isp");
if (IS_ERR(tracer->isp_platform_device)) {
dev_info(dev, "no camera-device \"%s\"\n", "isp");
tracer->isp_platform_device = NULL;
}
tracer->isp1_platform_device =
camrtc_device_get_byname(camera_devices, "isp1");
if (IS_ERR(tracer->isp1_platform_device)) {
dev_info(dev, "no camera-device \"%s\"\n", "isp1");
tracer->isp1_platform_device = NULL;
}
tracer->vi_platform_device =
camrtc_device_get_byname(camera_devices, "vi0");
if (IS_ERR(tracer->vi_platform_device)) {
dev_info(dev, "no camera-device \"%s\"\n", "vi0");
tracer->vi_platform_device = NULL;
}
tracer->vi1_platform_device =
camrtc_device_get_byname(camera_devices, "vi1");
if (IS_ERR(tracer->vi1_platform_device)) {
dev_info(dev, "no camera-device \"%s\"\n", "vi1");
tracer->vi1_platform_device = NULL;
}
}
/* Initialize the wait queue */
init_waitqueue_head(&tracer->wait_queue);
/* Worker */
param = WORK_INTERVAL_DEFAULT;
if (of_property_read_u32(tracer->of_node, NV(interval-ms), &param)) {
dev_err(dev, "interval-ms property not present\n");
kfree(tracer);
return NULL;
}
tracer->enable_printk = of_property_read_bool(tracer->of_node,
NV(enable-printk));
tracer->log_prefix = "[RTCPU]";
if (of_property_read_string(tracer->of_node, NV(log-prefix),
&tracer->log_prefix)) {
dev_err(dev, "RTCPU property not present\n");
kfree(tracer);
return NULL;
}
INIT_DELAYED_WORK(&tracer->work, rtcpu_trace_worker);
tracer->work_interval_jiffies = msecs_to_jiffies(param);
/* Done with initialization */
schedule_delayed_work(&tracer->work, 0);
dev_info(dev, "Trace buffer configured at IOVA=0x%08x\n",
(u32)tracer->dma_handle);
ret = raw_trace_node_drv_register(tracer);
if (ret) {
dev_err(dev, "Failed to register device node\n");
kfree(tracer);
return NULL;
}
return tracer;
}
EXPORT_SYMBOL(tegra_rtcpu_trace_create);
int tegra_rtcpu_trace_boot_sync(struct tegra_rtcpu_trace *tracer)
{
int ret;
if (tracer == NULL)
return 0;
ret = tegra_camrtc_iovm_setup(tracer->dev, tracer->dma_handle);
if (ret == 0)
return 0;
dev_err(tracer->dev, "RTCPU trace: IOVM setup error: %d\n", ret);
return -EIO;
}
EXPORT_SYMBOL(tegra_rtcpu_trace_boot_sync);
void tegra_rtcpu_trace_destroy(struct tegra_rtcpu_trace *tracer)
{
if (IS_ERR_OR_NULL(tracer))
return;
platform_device_put(tracer->isp_platform_device);
platform_device_put(tracer->vi_platform_device);
platform_device_put(tracer->vi1_platform_device);
of_node_put(tracer->of_node);
cancel_delayed_work_sync(&tracer->work);
flush_delayed_work(&tracer->work);
raw_trace_node_unregister(tracer);
rtcpu_trace_debugfs_deinit(tracer);
dma_free_coherent(tracer->dev, tracer->trace_memory_size,
tracer->trace_memory, tracer->dma_handle);
kfree(tracer);
}
EXPORT_SYMBOL(tegra_rtcpu_trace_destroy);
MODULE_DESCRIPTION("NVIDIA Tegra RTCPU trace driver");
MODULE_LICENSE("GPL v2");