/* * Copyright (c) 2016-2019, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope 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 . */ #include "soc/tegra/camrtc-trace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_EVENTLIB #include #include #include "rtcpu/device-group.h" #endif #define CREATE_TRACE_POINTS #include #include #define NV(p) "nvidia," #p #define WORK_INTERVAL_DEFAULT 100 #define EXCEPTION_STR_LENGTH 2048 /* * 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; /* eventlib */ struct platform_device *vi_platform_device; struct platform_device *isp_platform_device; /* printk logging */ const char *log_prefix; bool enable_printk; u32 printk_used; char printk[EXCEPTION_STR_LENGTH]; }; /* * 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, ®_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 = { .signature[0] = CAMRTC_TRACE_SIGNATURE_1, .signature[1] = CAMRTC_TRACE_SIGNATURE_2, .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; } 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 = 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 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: trace_rtcpu_unknown(event->header.tstamp, event->header.id, event->header.len - CAMRTC_TRACE_EVENT_HEADER_SIZE, event->data.data8); 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: trace_rtcpu_unknown(event->header.tstamp, event->header.id, event->header.len - CAMRTC_TRACE_EVENT_HEADER_SIZE, event->data.data8); 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: trace_rtcpu_unknown(event->header.tstamp, event->header.id, event->header.len - CAMRTC_TRACE_EVENT_HEADER_SIZE, event->data.data8); 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", "RESERVED_18", "RESERVED_19", "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); #ifndef camrtc_trace_vinotify_event_ts64 #define camrtc_trace_vinotify_event_ts64 (camrtc_trace_vinotify_handle_msg + 1) #endif #ifndef camrtc_trace_vinotify_error_ts64 #define camrtc_trace_vinotify_error_ts64 (camrtc_trace_vinotify_handle_msg + 2) #endif static void rtcpu_trace_vinotify_event(struct camrtc_event_struct *event) { switch (event->header.id) { case camrtc_trace_vinotify_handle_msg: trace_rtcpu_vinotify_handle_msg(event->header.tstamp, (event->data.data32[0] >> 1) & 0x7f, event->data.data32[0], event->data.data32[1], event->data.data32[2]); break; case camrtc_trace_vinotify_event_ts64: trace_rtcpu_vinotify_event(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_error_ts64: trace_rtcpu_vinotify_error(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; default: trace_rtcpu_unknown(event->header.tstamp, event->header.id, event->header.len - CAMRTC_TRACE_EVENT_HEADER_SIZE, event->data.data8); break; } } static void rtcpu_trace_vi_event(struct tegra_rtcpu_trace *tracer, struct camrtc_event_struct *event) { #if !defined(CONFIG_EVENTLIB) || \ !defined(camrtc_trace_vi_frame_begin) || \ !defined(camrtc_trace_vi_frame_end) trace_rtcpu_unknown(event->header.tstamp, event->header.id, event->header.len - CAMRTC_TRACE_EVENT_HEADER_SIZE, event->data.data8); #else struct nvhost_device_data *pdata; struct nvhost_task_begin task_begin; struct nvhost_task_end task_end; u64 ts = 0; if (tracer->vi_platform_device == NULL) return; pdata = platform_get_drvdata(tracer->vi_platform_device); if (pdata == NULL) return; if (!pdata->eventlib_id) { pr_warn("%s kernel eventlib id %d cannot be found\n", __func__, pdata->eventlib_id); return; } switch (event->header.id) { case camrtc_trace_vi_frame_begin: /* Write task start event */ task_begin.syncpt_id = event->data.data32[0]; task_begin.syncpt_thresh = event->data.data32[1]; task_begin.class_id = pdata->class; ts = ((u64)event->data.data32[5] << 32) | (u64)event->data.data32[4]; keventlib_write(pdata->eventlib_id, &task_begin, sizeof(task_begin), NVHOST_TASK_BEGIN, ts); break; case camrtc_trace_vi_frame_end: /* Write task end event */ task_end.syncpt_id = event->data.data32[0]; task_end.syncpt_thresh = event->data.data32[1]; task_end.class_id = pdata->class; ts = ((u64)event->data.data32[5] << 32) | (u64)event->data.data32[4]; keventlib_write(pdata->eventlib_id, &task_end, sizeof(task_end), NVHOST_TASK_END, ts); break; default: pr_warn("%pFn event id %d cannot be found\n", __func__, pdata->eventlib_id); break; } #endif } static void rtcpu_trace_isp_event(struct tegra_rtcpu_trace *tracer, struct camrtc_event_struct *event) { #ifndef CONFIG_EVENTLIB trace_rtcpu_unknown(event->header.tstamp, event->header.id, event->header.len - CAMRTC_TRACE_EVENT_HEADER_SIZE, event->data.data8); #else struct nvhost_device_data *pdata = NULL; struct nvhost_task_begin task_begin; struct nvhost_task_end task_end; if (tracer->isp_platform_device == NULL) return; pdata = platform_get_drvdata(tracer->isp_platform_device); if (!pdata->eventlib_id) { pr_warn("%s kernel eventlib id %d cannot be found\n", __func__, pdata->eventlib_id); return; } switch (event->header.id) { case camrtc_trace_isp_task_begin: /* Write task start event */ task_begin.syncpt_id = event->data.data32[0]; task_begin.syncpt_thresh = event->data.data32[1]; task_begin.class_id = pdata->class; keventlib_write(pdata->eventlib_id, &task_begin, sizeof(task_begin), NVHOST_TASK_BEGIN, event->header.tstamp); break; case camrtc_trace_isp_task_end: /* Write task end event */ task_end.syncpt_id = event->data.data32[0]; task_end.syncpt_thresh = event->data.data32[1]; task_end.class_id = pdata->class; keventlib_write(pdata->eventlib_id, &task_end, sizeof(task_end), NVHOST_TASK_END, event->header.tstamp); break; default: pr_warn("%s event id %d cannot be found\n", __func__, pdata->eventlib_id); break; } #endif } 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: trace_rtcpu_unknown(event->header.tstamp, event->header.id, event->header.len - CAMRTC_TRACE_EVENT_HEADER_SIZE, event->data.data8); 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; default: trace_rtcpu_unknown(event->header.tstamp, event->header.id, event->header.len - CAMRTC_TRACE_EVENT_HEADER_SIZE, event->data.data8); break; } } static void trace_rtcpu_log(struct tegra_rtcpu_trace *tracer, struct camrtc_event_struct *event) { size_t len, used; if (unlikely(event->header.id != camrtc_trace_type_string)) return; len = event->header.len - CAMRTC_TRACE_EVENT_HEADER_SIZE; if (len == CAMRTC_TRACE_EVENT_PAYLOAD_SIZE) /* Ignore NULs at the end of buffer */ len = strnlen(event->data.data8, len); 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, event->data.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) { switch (CAMRTC_EVENT_TYPE_FROM_ID(event->header.id)) { 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, event->header.id, event->header.len - CAMRTC_TRACE_EVENT_HEADER_SIZE, (char *) event->data.data8); if (likely(tracer->enable_printk)) trace_rtcpu_log(tracer, event); break; case CAMRTC_EVENT_TYPE_BULK: trace_rtcpu_bulk(event->header.tstamp, event->header.id, event->header.len - CAMRTC_TRACE_EVENT_HEADER_SIZE, event->data.data8); break; default: trace_rtcpu_unknown(event->header.tstamp, event->header.id, event->header.len - CAMRTC_TRACE_EVENT_HEADER_SIZE, event->data.data8); 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; while (old_next == new_next) return; 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; } 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) { 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); } /* * 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 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, payload_len; if (tracer->n_events == 0) return 0; payload_len = event->header.len - CAMRTC_TRACE_EVENT_HEADER_SIZE; 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); } /* * 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); #ifdef CONFIG_EVENTLIB if (camera_devices != NULL) { /* Eventlib */ 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->vi_platform_device = camrtc_device_get_byname(camera_devices, "vi"); if (IS_ERR(tracer->vi_platform_device)) { dev_info(dev, "no camera-device \"%s\"\n", "vi"); tracer->vi_platform_device = NULL; } } #endif /* Worker */ param = WORK_INTERVAL_DEFAULT; of_property_read_u32(tracer->of_node, NV(interval-ms), ¶m); tracer->enable_printk = of_property_read_bool(tracer->of_node, NV(enable-printk)); tracer->log_prefix = "[RTCPU]"; of_property_read_string(tracer->of_node, NV(log-prefix), &tracer->log_prefix); 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); return tracer; } EXPORT_SYMBOL(tegra_rtcpu_trace_create); int tegra_rtcpu_trace_boot_sync(struct tegra_rtcpu_trace *tracer) { int 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); of_node_put(tracer->of_node); cancel_delayed_work_sync(&tracer->work); flush_delayed_work(&tracer->work); 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");