278 lines
7.0 KiB
C
278 lines
7.0 KiB
C
/*
|
|
* PVA Debug Information file
|
|
*
|
|
* Copyright (c) 2017-2018, 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 <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/platform_device.h>
|
|
#include "dev.h"
|
|
#include "pva.h"
|
|
|
|
static void pva_read_crashdump(struct seq_file *s, struct pva_seg_info *seg_info)
|
|
{
|
|
int i = 0;
|
|
u32 *seg_addr = (u32 *) seg_info->addr;
|
|
|
|
if (!seg_addr)
|
|
return;
|
|
|
|
for (i = 0; i < (seg_info->size >> 4);) {
|
|
seq_printf(s, "0x%x 0x%x 0x%x 0x%x\n",
|
|
seg_addr[i], seg_addr[i+1],
|
|
seg_addr[i+2], seg_addr[i+3]);
|
|
i = i + 4;
|
|
}
|
|
}
|
|
|
|
static int pva_crashdump(struct seq_file *s, void *data)
|
|
{
|
|
int err = 0;
|
|
struct pva_crashdump_debugfs_entry *entry =
|
|
(struct pva_crashdump_debugfs_entry *)s->private;
|
|
struct pva *pva = entry->pva;
|
|
|
|
err = nvhost_module_busy(pva->pdev);
|
|
if (err) {
|
|
nvhost_dbg_info("err in powering up pva\n");
|
|
goto err_poweron;
|
|
}
|
|
|
|
pva_read_crashdump(s, &entry->seg_info);
|
|
|
|
nvhost_module_idle(pva->pdev);
|
|
|
|
err_poweron:
|
|
return err;
|
|
}
|
|
|
|
static int crashdump_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, pva_crashdump, inode->i_private);
|
|
}
|
|
|
|
static const struct file_operations pva_crashdump_fops = {
|
|
.open = crashdump_open,
|
|
.read = seq_read,
|
|
.release = single_release,
|
|
};
|
|
|
|
static int pva_print_function_table(struct seq_file *s, void *data)
|
|
{
|
|
struct pva_func_table fn_table;
|
|
struct pva *pva = s->private;
|
|
struct vpu_func *fn;
|
|
uint32_t entries;
|
|
int ret = 0;
|
|
int i;
|
|
|
|
ret = nvhost_module_busy(pva->pdev);
|
|
if (ret) {
|
|
nvhost_dbg_info("error in powering up pva\n");
|
|
goto err_poweron;
|
|
}
|
|
|
|
ret = pva_alloc_and_populate_function_table(pva, &fn_table);
|
|
if (ret) {
|
|
nvhost_dbg_info("unable to populate function table\n");
|
|
goto err_vpu_alloc;
|
|
}
|
|
|
|
fn = fn_table.addr;
|
|
entries = fn_table.entries;
|
|
|
|
seq_puts(s, "NAME ID\n");
|
|
for (i = 0; i < entries; i++) {
|
|
char *name = (char *)&fn->name;
|
|
|
|
seq_printf(s, "%s %d\n",
|
|
name, fn->id);
|
|
fn = (struct vpu_func *)(((u8 *)fn) + fn->next);
|
|
}
|
|
|
|
pva_dealloc_vpu_function_table(pva, &fn_table);
|
|
|
|
nvhost_module_idle(pva->pdev);
|
|
return 0;
|
|
|
|
err_vpu_alloc:
|
|
nvhost_module_idle(pva->pdev);
|
|
err_poweron:
|
|
return ret;
|
|
}
|
|
|
|
static int vpu_func_table_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, pva_print_function_table, inode->i_private);
|
|
}
|
|
|
|
static const struct file_operations pva_vpu_function_table_fops = {
|
|
.open = vpu_func_table_open,
|
|
.read = seq_read,
|
|
.release = single_release,
|
|
};
|
|
|
|
static inline void print_version(struct seq_file *s,
|
|
const char *version_str,
|
|
const u32 version)
|
|
{
|
|
const char type = PVA_EXTRACT(version, 31, 24, u8);
|
|
const u32 major = PVA_EXTRACT(version, 23, 16, u32);
|
|
const u32 minor = PVA_EXTRACT(version, 15, 8, u32);
|
|
const u32 subminor = PVA_EXTRACT(version, 7, 0, u32);
|
|
|
|
seq_printf(s, "%s: %c.%02u.%02u.%02u\n", version_str,
|
|
type, major, minor, subminor);
|
|
}
|
|
|
|
static int print_firmware_versions(struct seq_file *s, void *data)
|
|
{
|
|
struct pva *pva = s->private;
|
|
struct pva_version_info info;
|
|
int ret = 0;
|
|
|
|
ret = nvhost_module_busy(pva->pdev);
|
|
if (ret < 0)
|
|
goto err_poweron;
|
|
|
|
ret = pva_get_firmware_version(pva, &info);
|
|
if (ret < 0)
|
|
goto err_get_firmware_version;
|
|
|
|
nvhost_module_idle(pva->pdev);
|
|
|
|
print_version(s, "pva_r5_version", info.pva_r5_version);
|
|
print_version(s, "pva_compat_version", info.pva_compat_version);
|
|
seq_printf(s, "pva_revision: %x\n", info.pva_revision);
|
|
seq_printf(s, "pva_built_on: %u\n", info.pva_built_on);
|
|
|
|
return 0;
|
|
|
|
err_get_firmware_version:
|
|
nvhost_module_idle(pva->pdev);
|
|
err_poweron:
|
|
return ret;
|
|
}
|
|
|
|
static int print_version_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, print_firmware_versions, inode->i_private);
|
|
}
|
|
|
|
static const struct file_operations print_version_fops = {
|
|
.open = print_version_open,
|
|
.read = seq_read,
|
|
.release = single_release,
|
|
};
|
|
|
|
static int get_log_level(void *data, u64 *val)
|
|
{
|
|
struct pva *pva = (struct pva *) data;
|
|
|
|
*val = pva->log_level;
|
|
return 0;
|
|
}
|
|
|
|
static int set_log_level(void *data, u64 val)
|
|
{
|
|
struct pva *pva = (struct pva *) data;
|
|
|
|
pva->log_level = val;
|
|
if (pva->booted)
|
|
return pva_set_log_level(pva, val);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
DEFINE_DEBUGFS_ATTRIBUTE(log_level_fops, get_log_level, set_log_level, "%llu");
|
|
|
|
void pva_debugfs_init(struct platform_device *pdev)
|
|
{
|
|
struct dentry *ret;
|
|
struct nvhost_device_data *pdata = platform_get_drvdata(pdev);
|
|
struct pva *pva = pdata->private_data;
|
|
struct dentry *de = pdata->debugfs;
|
|
|
|
if (!de)
|
|
return;
|
|
|
|
pva->debugfs_entry_r5.pva = pva;
|
|
pva->debugfs_entry_vpu0.pva = pva;
|
|
pva->debugfs_entry_vpu1.pva = pva;
|
|
|
|
ret = debugfs_create_file("r5_crashdump", S_IRUGO, de,
|
|
&pva->debugfs_entry_r5, &pva_crashdump_fops);
|
|
if (!ret)
|
|
nvhost_dbg_info("Failed R5_crashdump file creation");
|
|
|
|
ret = debugfs_create_file("vpu0_crashdump", S_IRUGO, de,
|
|
&pva->debugfs_entry_vpu0, &pva_crashdump_fops);
|
|
if (!ret)
|
|
nvhost_dbg_info("Failed VPU0_crashdump file creation");
|
|
|
|
|
|
ret = debugfs_create_file("vpu1_crashdump", S_IRUGO, de,
|
|
&pva->debugfs_entry_vpu1, &pva_crashdump_fops);
|
|
if (!ret)
|
|
nvhost_dbg_info("Failed VPU1_crashdump file creation");
|
|
|
|
ret = debugfs_create_u32("submit_mode", S_IRUGO | S_IWUSR, de,
|
|
&pva->submit_mode);
|
|
if (!ret)
|
|
nvhost_dbg_info("Failed to create submit mode selection file");
|
|
|
|
ret = debugfs_create_file("vpu_function_table", S_IRUGO, de,
|
|
pva, &pva_vpu_function_table_fops);
|
|
if (!ret)
|
|
nvhost_dbg_info("Failed to create vpu function table file");
|
|
|
|
ret = debugfs_create_u32("vpu_app_id", S_IRUGO | S_IWUSR, de,
|
|
&pva->dbg_vpu_app_id);
|
|
if (!ret)
|
|
nvhost_dbg_info("Failed to create vpu_app id debug file");
|
|
|
|
ret = debugfs_create_u32("r5_dbg_wait", 0644, de,
|
|
&pva->r5_dbg_wait);
|
|
if (!ret)
|
|
nvhost_dbg_info("Failed to create r5_dbg_wait file");
|
|
|
|
ret = debugfs_create_bool("r5_timeout_enable", 0644, de,
|
|
&pva->timeout_enabled);
|
|
if (!ret)
|
|
nvhost_dbg_info("Failed to create r5_timeout_enable node");
|
|
|
|
ret = debugfs_create_file("firmware_version", S_IRUGO, de,
|
|
pva, &print_version_fops);
|
|
if (!ret)
|
|
nvhost_dbg_info("Failed to create PVA version file");
|
|
|
|
ret = debugfs_create_u32("cg_disable", 0644, de,
|
|
&pva->slcg_disable);
|
|
if (!ret)
|
|
nvhost_dbg_info("Failed to create cg_disable node");
|
|
|
|
ret = debugfs_create_bool("vpu_perf_counters_enable", 0644, de,
|
|
&pva->vpu_perf_counters_enable);
|
|
if (!ret)
|
|
nvhost_dbg_info("Failed to create vpu_perf_counters_enable node");
|
|
|
|
ret = debugfs_create_file("log_level", 0644, de,
|
|
pva, &log_level_fops);
|
|
if (!ret)
|
|
nvhost_dbg_info("Failed to create log_lovel node");
|
|
}
|