/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "soc/tegra/camrtc-trace.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/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 <asm/cacheflush.h>
#ifdef CONFIG_EVENTLIB
#include <linux/keventlib.h>
#include <uapi/linux/nvhost_events.h>
#include "rtcpu/device-group.h"
#endif
#define CREATE_TRACE_POINTS
#include <trace/events/tegra_rtcpu.h>
#include <trace/events/freertos.h>
#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
|