blob: 780d61671436e8aa7ac4b361cf0c7cf63c88f200 [file] [log] [blame]
/*
* Samsung Exynos SoC series NPU driver
*
* Copyright (c) 2017 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/ctype.h>
#include <linux/types.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <asm/bitops.h>
#include "npu-debug.h"
#include "npu-log.h"
#include "npu-ver.h"
#define DEBUG_FS_ROOT_NAME "npu"
#define DEBUG_FS_UNITTEST_NAME "idiot"
#define NPU_DEBUG_FILENAME_LEN 32 /* Maximum file name length under npu */
#ifdef CONFIG_VISION_UNITTEST
#define IDIOT_ALL_TESTCASE_INCLUDE "idiot-npu-all-tests.h"
#include "idiot-decl.h"
#endif
/* Singleton reference to debug object */
static struct npu_debug *npu_debug_ref;
typedef enum {
FS_READY = 0,
} npu_debug_state_bits_e;
static inline void set_state_bit(npu_debug_state_bits_e state_bit)
{
int old = test_and_set_bit(state_bit, &(npu_debug_ref->state));
if (old) {
npu_warn("state(%d): state set requested but already set.", state_bit);
}
}
static inline void clear_state_bit(npu_debug_state_bits_e state_bit)
{
int old = test_and_clear_bit(state_bit, &(npu_debug_ref->state));
if (!old) {
npu_warn("state(%d): state clear requested but already cleared.", state_bit);
}
}
static inline int check_state_bit(npu_debug_state_bits_e state_bit)
{
return test_bit(state_bit, &(npu_debug_ref->state));
}
#ifdef CONFIG_VISION_UNITTEST
static const struct file_operations npu_debug_unittest_fops = {
.owner = THIS_MODULE, .open = IDIOT_open, .read = IDIOT_read, .write = IDIOT_write,
};
#endif
int npu_debug_register_arg(
const char *name, void *private_arg,
const struct file_operations *ops)
{
int ret = 0;
struct npu_device *npu_device;
struct device *dev;
mode_t mode = 0;
struct dentry *dbgfs_entry;
/* Check whether the debugfs is properly initialized */
if (!check_state_bit(FS_READY) || !IS_ENABLED(CONFIG_DEBUG_FS)) {
npu_warn("DebugFS not initialized or disabled. Skip creation [%s]\n", name);
ret = 0;
goto err_exit;
}
/* Default parameter is npu_debug object */
if (private_arg == NULL) {
private_arg = (void *)npu_debug_ref;
}
/* Retrieve struct device to use devm_XX api */
npu_device = container_of(npu_debug_ref, struct npu_device, debug);
BUG_ON(!npu_device);
dev = npu_device->dev;
BUG_ON(!dev);
if (name == NULL) {
npu_err("name is null\n");
ret = -EFAULT;
goto err_exit;
}
if (ops == NULL) {
npu_err("ops is null\n");
ret = -EFAULT;
goto err_exit;
}
/* Setting file permission based on file_operation member */
if (ops->read || ops->compat_ioctl || ops->unlocked_ioctl)
mode |= 0400; /* Read permission to owner */
if (ops->write)
mode |= 0200; /* Write permission to owner */
/* Register */
dbgfs_entry = debugfs_create_file(name, mode,
npu_debug_ref->dfile_root, private_arg, ops);
if (IS_ERR(dbgfs_entry)) {
npu_err("fail in DebugFS registration (%s)\n", name);
ret = PTR_ERR(dbgfs_entry);
goto err_exit;
}
npu_info("success in DebugFS registration (%s) : Mode %04o\n"
, name, mode);
return 0;
err_exit:
return ret;
}
int npu_debug_register(const char *name, const struct file_operations *ops)
{
return npu_debug_register_arg(name, NULL, ops);
}
int npu_debug_probe(struct npu_device *npu_device)
{
int ret = 0;
BUG_ON(!npu_device);
/* Save reference */
npu_debug_ref = &npu_device->debug;
memset(npu_debug_ref, 0, sizeof(*npu_debug_ref));
probe_info("Loading npu_debug : starting\n");
npu_debug_ref->dfile_root = debugfs_create_dir(DEBUG_FS_ROOT_NAME, NULL);
if (!npu_debug_ref->dfile_root) {
probe_err("Loading npu_debug : debugfs root [%s] can not be created\n",
DEBUG_FS_ROOT_NAME);
npu_debug_ref->dfile_root = NULL;
ret = -ENOENT;
goto err_exit;
}
probe_info("Loading npu_debug : debugfs root [%s] created\n"
, DEBUG_FS_ROOT_NAME);
set_state_bit(FS_READY);
#ifdef CONFIG_VISION_UNITTEST
ret = npu_debug_register(DEBUG_FS_UNITTEST_NAME
, &npu_debug_unittest_fops);
if (ret) {
probe_err("loading npu_debug : debugfs for unittest can not be created\n");
goto err_exit;
}
#endif
ret = npu_ver_probe(npu_device);
if (ret) {
probe_err("loading npu_debug : npu_ver_probe failed.\n");
goto err_exit;
}
probe_info("loading npu_debug : completed.\n");
return 0;
err_exit:
return ret;
}
int npu_debug_release(void)
{
int ret = 0;
struct npu_device *npu_device;
struct device *dev;
npu_info("start in unloading npu_debug\n");
if (!check_state_bit(FS_READY)) {
/* No need to clean-up */
npu_err("not ready in npu_debug\n");
return -1;
}
/* Retrieve struct device to use devm_XX api */
npu_device = container_of(npu_debug_ref, struct npu_device, debug);
dev = npu_device->dev;
/* remove version info */
ret = npu_ver_release(npu_device);
if (ret) {
npu_err("npu_ver_release error : ret = %d\n", ret);
return ret;
}
/* Remove debug fs entries */
debugfs_remove_recursive(npu_debug_ref->dfile_root);
npu_info("unloading npu_debug: root node is removed.\n");
clear_state_bit(FS_READY);
npu_info("complete in unloading npu_debug\n");
return ret;
}
int npu_debug_open(struct npu_device *npu_device)
{
npu_info("start in npu_debug open\n");
npu_info("complete in npu_debug open\n");
return 0;
}
int npu_debug_close(struct npu_device *npu_device)
{
npu_info("start in npu_debug close\n");
npu_info("complete in npu_debug close\n");
return 0;
}
int npu_debug_start(struct npu_device *npu_device)
{
npu_info("start in npu_debug start\n");
npu_info("complete in npu_debug start\n");
return 0;
}
int npu_debug_stop(struct npu_device *npu_device)
{
npu_info("start in npu_debug stop\n");
npu_info("complete in npu_debug stop\n");
return 0;
}