blob: 02a0c508864417b05154adc064c0e8cfa3be9e00 [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/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <linux/sysfs.h>
#include <asm/cacheflush.h>
#include "npu-device.h"
#include "npu-common.h"
#include "npu-memory.h"
#include "npu-util-statekeeper.h"
#include "npu-if-session-protodrv.h"
#include "npu-system.h"
#include "npu-profile.h"
#include "npu-log.h"
#include "npu-debug.h"
#define SIMPLE_PROFILER
enum npu_golden_state_e {
NPU_PROFILE_STATE_NOT_INITIALIZED,
NPU_PROFILE_STATE_PWRON_WAITING,
NPU_PROFILE_STATE_STARTING,
NPU_PROFILE_STATE_GATHERING,
NPU_PROFILE_STATE_STOPPING,
NPU_PROFILE_STATE_EXPORT_READY,
NPU_PROFILE_STATE_INVALID,
};
static const char *state_names[NPU_PROFILE_STATE_INVALID + 1]
= {"NOT INITIALIZED", "PWRON_WAITING", "STARTING", "GATHERING", "STOPPING", "EXPORT_READY", "INVALID"};
static const u8 transition_map[][STATE_KEEPER_MAX_STATE] = {
/* From - To NOT INITIALIZED PWRON_WAITING STARTING GATHERING STOPPING EXPORT_READY INVALID */
/* NOT INITIALIZED */ { 1, 1, 1, 0, 0, 0, 0},
/* PWRON_WAITING */ { 1, 1, 1, 0, 1, 0, 0},
/* STARTING */ { 1, 0, 0, 1, 0, 0, 0},
/* GATHERING */ { 1, 0, 0, 1, 1, 0, 0},
/* STOPPING */ { 1, 0, 0, 0, 0, 1, 0},
/* EXPORT_READY */ { 1, 1, 1, 0, 0, 1, 0},
/* INVALID */ { 0, 0, 0, 0, 0, 0, 0},
};
static const char *ERR_STR_NO_PROFILE_AVAILABLE = "No profile data available.\n";
/* Convenient reference to profile control */
struct npu_profile_control *profile_ctl_ref;
#define GET_SYSTEM(pt_profile_ctl) container_of(pt_profile_ctl, struct npu_system, profile_ctl)
struct read_pos {
struct npu_profile_control *profile_ctl;
const char *data;
size_t size;
size_t rpos;
char in_buf[32];
};
/* Global configuration for simple profiler */
volatile struct {
int enabled;
int sysfs_ok;
struct device *dev;
} s_profiler_ctl;
/* Set initial value of profile_data */
static void init_profile_data(struct npu_profile *profile_data)
{
struct probe_meta *meta;
BUG_ON(!profile_data);
meta = profile_data->meta;
/* Clear with 0 */
memset(profile_data, 0, sizeof(*profile_data));
profile_data->point_cnt = NPU_PROFILE_POINT_NUM;
/* Set profile section */
meta[0].profile_unit_id = PROFILE_UNIT_FIRMWARE; /* Firmware */
meta[0].start_idx = 0;
meta[0].total_count = NPU_PROFILE_POINT_NUM - 2048;
meta[0].valid_count = 0;
meta[1].profile_unit_id = PROFILE_UNIT_DEVICE_DRIVER; /* Driver */
meta[1].start_idx = meta[0].total_count;
meta[1].total_count = 2048;
meta[1].valid_count = 0;
/* Cache flush */
__dma_map_area(profile_data, sizeof(*profile_data), DMA_TO_DEVICE);
}
/* Allocate profile_data */
static int alloc_profile_data(struct npu_profile_control *profile_ctl)
{
int ret;
struct npu_system *system;
BUG_ON(!profile_ctl);
system = GET_SYSTEM(profile_ctl);
BUG_ON(!system);
if (profile_ctl->buf.size > 0)
npu_warn("profile data was not cleared. memory leak might be happened.\n");
/* TODO: Size dynamic adjustment might be necessary */
profile_ctl->buf.size = sizeof(*(profile_ctl->profile_data));
ret = npu_memory_alloc(&system->memory, &profile_ctl->buf);
if (ret) {
npu_err("npu_memory_alloc for profile buffer memory, ret(%d)\n", ret);
goto err_exit;
}
npu_info("profiling buffer allocated, size(%lu) / dv(%pad) / kv(%pK)",
profile_ctl->buf.size, &profile_ctl->buf.daddr, profile_ctl->buf.vaddr);
/* Save pointer */
profile_ctl->profile_data = profile_ctl->buf.vaddr;
/* Initialize profile data */
init_profile_data(profile_ctl->profile_data);
ret = 0;
err_exit:
return ret;
}
/* Deallocate profile data */
static void dealloc_profile_data(struct npu_profile_control *profile_ctl)
{
struct npu_system *system;
BUG_ON(!profile_ctl);
system = GET_SYSTEM(profile_ctl);
BUG_ON(!system);
if (profile_ctl->buf.size == 0) {
npu_warn("profile data was not allocated. skipping deallocation.\n");
return;
}
npu_memory_free(&system->memory, &profile_ctl->buf);
npu_info("profiling buffer deallocated.\n");
/* Clear pointer */
profile_ctl->profile_data = NULL;
profile_ctl->buf.size = 0;
}
/* Call-back from Protodrv */
static int profile_save_result(struct npu_session *dummy, struct nw_result result)
{
BUG_ON(!profile_ctl_ref);
npu_trace("Profiling request completed : result = %u\n", result.result_code);
profile_ctl_ref->result_code = result.result_code;
atomic_set(&profile_ctl_ref->result_available, 1);
wake_up_all(&profile_ctl_ref->wq);
return 0;
}
/* Send profiling request (start or stop) to HW and check its response */
static int request_profile_ctl_to_hw(struct npu_profile_control *profile_ctl,
nw_cmd_e cmd, struct npu_memory_buffer *buf_handle)
{
int ret;
struct npu_nw nw;
int retry_cnt;
memset(&nw, 0, sizeof(nw));
nw.cmd = cmd;
/* Set callback function on completion */
nw.notify_func = profile_save_result;
switch (cmd) {
case NPU_NW_CMD_PROFILE_START:
if (buf_handle) {
nw.ncp_addr.av_index = 1;
nw.uid = PROFILE_UID;
nw.ncp_addr.vaddr = buf_handle->vaddr;
nw.ncp_addr.daddr = buf_handle->daddr;
nw.ncp_addr.size = (u32)buf_handle->size;
npu_dbg("prepare profile start command: cmd(%u) v(%pK) d(%pad) size(%zu)\n",
nw.cmd, nw.ncp_addr.vaddr, &nw.ncp_addr.daddr, nw.ncp_addr.size);
} else {
npu_err("buf_handle is null on profile start command.\n");
ret = -EINVAL;
goto err_exit;
}
break;
case NPU_NW_CMD_PROFILE_STOP:
npu_dbg("prepare profile stop command: cmd(%u)\n", nw.cmd);
break;
default:
npu_err("invalid cmd(%d)\n", cmd);
ret = -EINVAL;
goto err_exit;
}
retry_cnt = 0;
atomic_set(&profile_ctl->result_available, 0);
while ((ret = npu_ncp_mgmt_put(&nw)) <= 0) {
npu_dbg("queue full when insearting profile control message. Retrying...");
if (retry_cnt++ >= PROFILE_CMD_POST_RETRY_CNT) {
npu_err("timeout exceeded.\n");
ret = -EWOULDBLOCK;
goto err_exit;
}
msleep(PROFILE_CMD_POST_RETRY_INTERVAL);
}
/* Success */
npu_dbg("profile control message has posted\n");
ret = wait_event_timeout(
profile_ctl->wq,
atomic_read(&profile_ctl->result_available),
PROFILE_HW_RESP_TIMEOUT);
if (ret < 0) {
npu_err("wait_event_timeout error(%d)\n", ret);
goto err_exit;
}
if (!atomic_read(&profile_ctl->result_available)) {
npu_err("timeout waiting H/W response\n");
ret = -ETIMEDOUT;
goto err_exit;
}
if (profile_ctl->result_code != NPU_ERR_NO_ERROR) {
npu_err("hardware reply with NDONE(%d)\n", profile_ctl->result_code);
ret = -EFAULT;
goto err_exit;
}
ret = 0;
err_exit:
return ret;
}
static void npu_profile_clear(struct npu_profile_control *profile_ctl)
{
BUG_ON(!profile_ctl);
npu_dbg("start in npu_profile_clear\n");
dealloc_profile_data(profile_ctl);
npu_statekeeper_transition(&profile_ctl->statekeeper, NPU_PROFILE_STATE_NOT_INITIALIZED);
npu_dbg("complete in npu_profile_clear.\n");
}
static int npu_profile_stop(struct npu_profile_control *profile_ctl)
{
int ret;
struct npu_profile *profile_data;
struct probe_meta *fw_probe_meta;
void *fw_probe_start;
size_t fw_probe_size;
BUG_ON(!profile_ctl);
npu_dbg("start in npu_profile_stop.\n");
profile_data = profile_ctl->profile_data;
if (SKEEPER_COMPARE_STATE(&profile_ctl->statekeeper, NPU_PROFILE_STATE_PWRON_WAITING)) {
/* Noting need to be done but just change state */
npu_info("postponed starting is canceled by stop request.\n");
npu_statekeeper_transition(&profile_ctl->statekeeper, NPU_PROFILE_STATE_NOT_INITIALIZED);
ret = 0;
goto ok_exit;
}
/* Start stopping */
npu_statekeeper_transition(&profile_ctl->statekeeper, NPU_PROFILE_STATE_STOPPING);
/* Send stop request to firmware */
npu_dbg("sending NPU_NW_CMD_PROFILE_STOP to NPU H/W.\n");
ret = request_profile_ctl_to_hw(profile_ctl, NPU_NW_CMD_PROFILE_STOP, NULL);
if (ret) {
npu_err("fail(%d) in request_profile_ctl_to_hw\n", ret);
goto err_exit;
}
/* Invalidate cache of firmware area */
fw_probe_meta = &profile_data->meta[PROFILE_UNIT_FIRMWARE];
__dma_unmap_area(fw_probe_meta, sizeof(*fw_probe_meta), DMA_FROM_DEVICE);
npu_dbg("fw profile id(%u), start(%u) total(%u) valid(%u)\n",
fw_probe_meta->profile_unit_id, fw_probe_meta->start_idx,
fw_probe_meta->total_count, fw_probe_meta->valid_count);
fw_probe_start = &profile_data->point[fw_probe_meta->start_idx];
fw_probe_size = sizeof(struct probe_point) * fw_probe_meta->total_count;
npu_dbg("fw profile invalidate addr(%pK), size(%ld)\n",
fw_probe_start, fw_probe_size);
__dma_unmap_area(fw_probe_start, fw_probe_size, DMA_FROM_DEVICE);
/* Save index on meta */
profile_data->meta[PROFILE_UNIT_DEVICE_DRIVER].valid_count = atomic_read(&profile_ctl->next_probe_point_idx);
/* Transition to data ready state */
npu_statekeeper_transition(&profile_ctl->statekeeper, NPU_PROFILE_STATE_EXPORT_READY);
ret = 0;
goto ok_exit;
err_exit:
/* Stop failed */
npu_err("stopping failed. clean-up profile data.\n");
npu_profile_clear(profile_ctl);
ok_exit:
npu_dbg("complete in npu_profile_stop\n");
return ret;
}
static int npu_profile_start(struct npu_profile_control *profile_ctl)
{
int ret;
int startidx;
struct npu_profile *profile_data;
BUG_ON(!profile_ctl);
npu_dbg("start in npu_profile_start\n");
if (SKEEPER_COMPARE_STATE(&profile_ctl->statekeeper, NPU_PROFILE_STATE_EXPORT_READY)) {
npu_info("profiling data is remaining. cleaning it first.");
npu_profile_clear(profile_ctl);
}
if (!npu_if_session_protodrv_is_opened()) {
/* NPU is not working now. Delay initialization until it is opened */
npu_info("not opened in NPU\n");
npu_statekeeper_transition(&profile_ctl->statekeeper, NPU_PROFILE_STATE_PWRON_WAITING);
ret = 0;
goto ok_exit;
}
npu_statekeeper_transition(&profile_ctl->statekeeper, NPU_PROFILE_STATE_STARTING);
ret = alloc_profile_data(profile_ctl);
if (ret) {
npu_err("fail(%d) in alloc_profile_data", ret);
goto err_exit;
}
profile_data = profile_ctl->profile_data;
startidx = profile_data->meta[PROFILE_UNIT_DEVICE_DRIVER].start_idx;
atomic_set(&profile_ctl->next_probe_point_idx, 0);
npu_dbg("initializing startidx (%d), writing from index (%d)\n",
atomic_read(&profile_ctl->next_probe_point_idx), startidx);
npu_dbg("sending NPU_NW_CMD_PROFILE_START to NPU H/W.\n");
ret = request_profile_ctl_to_hw(profile_ctl, NPU_NW_CMD_PROFILE_START, &profile_ctl->buf);
if (ret) {
npu_err("fail(%d) in request_profile_ctl_to_hw\n", ret);
goto err_exit;
}
npu_statekeeper_transition(&profile_ctl->statekeeper, NPU_PROFILE_STATE_GATHERING);
ret = 0;
goto ok_exit;
err_exit:
npu_dbg("error occurred, deaalocated profiling data\n");
dealloc_profile_data(profile_ctl);
npu_statekeeper_transition(&profile_ctl->statekeeper, NPU_PROFILE_STATE_NOT_INITIALIZED);
ok_exit:
npu_dbg("complete in npu_profile_start\n");
return ret;
}
/**************************************************************************
* fops definitions
*/
static int npu_profile_ctl_open(struct inode *inode, struct file *file)
{
int ret;
struct read_pos *rp;
struct npu_profile_control *profile_ctl = inode->i_private;
const char *state_name;
npu_dbg("start in npu_profile_ctl_open\n");
BUG_ON(!profile_ctl);
mutex_lock(&profile_ctl->lock);
rp = kzalloc(sizeof(*rp), GFP_KERNEL);
if (!rp) {
npu_err("fail in struct read_pos. allocation\n");
ret = -ENOMEM;
goto err_exit;
}
state_name = npu_statekeeper_state_name(&profile_ctl->statekeeper);
rp->size = scnprintf(rp->in_buf, sizeof(rp->in_buf), "%s\n", state_name);
rp->data = rp->in_buf;
rp->rpos = 0;
rp->profile_ctl = profile_ctl;
file->private_data = rp;
ret = 0;
err_exit:
npu_dbg("complete in npu_profile_ctl_open.\n");
mutex_unlock(&profile_ctl->lock);
return ret;
}
static int npu_profile_ctl_close(struct inode *inode, struct file *file)
{
struct read_pos *rp = file->private_data;
struct npu_profile_control *profile_ctl;
npu_dbg("start in npu_profile_ctl_close\n");
BUG_ON(!rp);
profile_ctl = rp->profile_ctl;
BUG_ON(!profile_ctl);
mutex_lock(&profile_ctl->lock);
kfree(rp);
mutex_unlock(&profile_ctl->lock);
npu_dbg("completed in npu_profile_ctl_close\n");
return 0;
}
static ssize_t npu_profile_ctl_read(
struct file *file,
char __user *outbuf,
size_t outlen, loff_t *loff)
{
int ret;
size_t copy_len;
struct read_pos *rp = file->private_data;
struct npu_profile_control *profile_ctl;
BUG_ON(!rp);
profile_ctl = rp->profile_ctl;
BUG_ON(!profile_ctl);
npu_dbg("start in npu_profile_ctl_read\n");
mutex_lock(&profile_ctl->lock);
if (rp->rpos >= rp->size) {
ret = 0;
} else {
copy_len = min(outlen, rp->size - rp->rpos);
npu_trace("Copy [%lu] bytes.", copy_len);
if (copy_to_user(outbuf, &rp->data[rp->rpos], copy_len) != 0) {
npu_err("copy_to_user failed.\n");
ret = -EFAULT;
} else {
rp->rpos += copy_len;
ret = copy_len;
}
}
npu_dbg("complete(%d) in npu_profile_ctl_read\n", ret);
mutex_unlock(&profile_ctl->lock);
return ret;
}
static ssize_t npu_profile_ctl_write(
struct file *file,
const char *__user userbuf,
size_t count, loff_t *offp)
{
int ret;
struct read_pos *rp = file->private_data;
struct npu_profile_control *profile_ctl;
char c;
BUG_ON(!rp);
profile_ctl = rp->profile_ctl;
BUG_ON(!profile_ctl);
npu_dbg("start in npu_profile_ctl_write\n");
mutex_lock(&profile_ctl->lock);
if (count <= 0) {
ret = 0;
} else {
ret = get_user(c, userbuf);
if (ret) {
npu_err("fail(%d) in get_user\n", ret);
} else {
switch (c) {
case '0':
/* Stop */
if (SKEEPER_IS_TRANSITABLE(&profile_ctl->statekeeper, NPU_PROFILE_STATE_STOPPING))
ret = npu_profile_stop(profile_ctl);
break;
case '1':
/* Start */
if (SKEEPER_IS_TRANSITABLE(&profile_ctl->statekeeper, NPU_PROFILE_STATE_STARTING))
ret = npu_profile_start(profile_ctl);
break;
default:
npu_err("invalid character (%c)\n", c);
ret = -EINVAL;
break;
}
}
}
npu_dbg("complete(%d) in npu_profile_ctl_write\n", ret);
mutex_unlock(&profile_ctl->lock);
if (ret == 0)
/* No error -> return number of character */
return count;
else
return ret;
}
static int npu_profile_result_open(struct inode *inode, struct file *file)
{
int ret;
struct read_pos *rp;
struct npu_profile_control *profile_ctl = inode->i_private;
npu_dbg("start in npu_profile_result_open\n");
BUG_ON(!profile_ctl);
mutex_lock(&profile_ctl->lock);
rp = kzalloc(sizeof(*rp), GFP_KERNEL);
if (!rp) {
npu_err("fail in struct read_pos. allocation\n");
ret = -ENOMEM;
goto err_exit;
}
if (SKEEPER_COMPARE_STATE(&profile_ctl->statekeeper, NPU_PROFILE_STATE_EXPORT_READY)) {
/* Result is available */
rp->data = (char *)profile_ctl->profile_data;
rp->size = sizeof(struct npu_profile);
} else {
/* Test data is not ready */
npu_dbg("not ready in profiling data\n");
rp->data = ERR_STR_NO_PROFILE_AVAILABLE;
rp->size = strlen(rp->data);
}
rp->rpos = 0;
rp->profile_ctl = profile_ctl;
file->private_data = rp;
ret = 0;
err_exit:
npu_dbg("complete in npu_profile_result_open\n");
mutex_unlock(&profile_ctl->lock);
return ret;
}
static int npu_profile_result_close(struct inode *inode, struct file *file)
{
struct read_pos *rp = file->private_data;
struct npu_profile_control *profile_ctl;
npu_dbg("start in npu_profile_result_close\n");
BUG_ON(!rp);
profile_ctl = rp->profile_ctl;
BUG_ON(!profile_ctl);
mutex_lock(&profile_ctl->lock);
kfree(rp);
mutex_unlock(&profile_ctl->lock);
npu_dbg("complete in npu_profile_result_close\n");
return 0;
}
static ssize_t npu_profile_result_read(struct file *file, char __user *outbuf, size_t outlen, loff_t *loff)
{
int ret;
size_t copy_len;
struct read_pos *rp = file->private_data;
struct npu_profile_control *profile_ctl;
BUG_ON(!rp);
profile_ctl = rp->profile_ctl;
BUG_ON(!profile_ctl);
npu_dbg("start in npu_profile_result_read\n");
mutex_lock(&profile_ctl->lock);
if ((rp->data != ERR_STR_NO_PROFILE_AVAILABLE)
&& !SKEEPER_COMPARE_STATE(&profile_ctl->statekeeper, NPU_PROFILE_STATE_EXPORT_READY)) {
npu_warn("profiling data had gone while reading data.\n");
ret = 0;
} else if (rp->rpos >= rp->size) {
ret = 0;
} else {
copy_len = min(outlen, rp->size - rp->rpos);
npu_trace("copy (%lu) bytes.", copy_len);
if (copy_to_user(outbuf, &rp->data[rp->rpos], copy_len) != 0) {
npu_err("faile in copy_to_user\n");
ret = -EFAULT;
} else {
rp->rpos += copy_len;
ret = copy_len;
}
}
npu_dbg("complete(%d) in npu_profile_result_read\n", ret);
mutex_unlock(&profile_ctl->lock);
return ret;
}
static const struct file_operations profile_ctl_fops = {
.owner = THIS_MODULE,
.open = npu_profile_ctl_open,
.release = npu_profile_ctl_close,
.read = npu_profile_ctl_read,
.write = npu_profile_ctl_write,
};
static const struct file_operations profile_result_fops = {
.owner = THIS_MODULE,
.open = npu_profile_result_open,
.release = npu_profile_result_close,
.read = npu_profile_result_read,
};
/**************************************************************************
* simple profiler sysfs control interface
*/
#define S_PROFILE_PREFIX "S-PROFILE: "
static ssize_t s_profiler_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int ret;
ret = scnprintf(buf, PAGE_SIZE,
"Simple profiler state : %s\n"
" - echo 0 > s_profiler : Disable simple profiler\n"
" echo 1 > s_profiler : Enable simple profiler\n",
(s_profiler_ctl.enabled) ? "Enabled" : "Disabled");
return ret;
}
static ssize_t s_profiler_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len)
{
if (len <= 0)
return 0;
switch (buf[0]) {
case '0':
npu_info(S_PROFILE_PREFIX "Simple profiler disabled\n");
s_profiler_ctl.enabled = 0;
break;
case '1':
npu_info(S_PROFILE_PREFIX "Simple profiler enabled\n");
s_profiler_ctl.enabled = 1;
break;
default:
npu_info(S_PROFILE_PREFIX "Invalid input [%c]\n", buf[0]);
break;
}
return len;
}
static inline int is_s_profile_enabled(void)
{
return (s_profiler_ctl.enabled == 1);
}
/*
* sysfs attribute for simple profiler
* name = dev_attr_s_profiler
* show = s_profiler_show
* store = s_profiler_store
*/
static DEVICE_ATTR_RW(s_profiler);
/**************************************************************************
* npu_profile driver lifecycle management
*/
int npu_profile_probe(struct npu_system *system)
{
int ret = 0;
struct npu_profile_control *profile_ctl;
struct npu_device *npu_device;
struct device *dev;
BUG_ON(!system);
npu_device = container_of(system, struct npu_device, system);
dev = npu_device->dev;
BUG_ON(!dev);
probe_trace("start in npu_profile_probe\n");
/* Register simple profiler sysfs entry */
memset((void *)&s_profiler_ctl, 0, sizeof(s_profiler_ctl));
s_profiler_ctl.dev = dev;
ret = sysfs_create_file(&dev->kobj, &dev_attr_s_profiler.attr);
if (ret) {
probe_err("sysfs_create_file error : ret = %d\n", ret);
s_profiler_ctl.sysfs_ok = 0;
goto err_exit;
} else {
s_profiler_ctl.sysfs_ok = 1;
}
/* Initialize profile_ctl */
profile_ctl = &system->profile_ctl;
memset(profile_ctl, 0, sizeof(*profile_ctl));
mutex_init(&profile_ctl->lock);
/* Save PWM register address */
profile_ctl->pwm = system->pwm_npu.vaddr + TCNTO0_OFF;
probe_info("PWM timer address = %pK\n", profile_ctl->pwm);
/* Initialize state keeper */
npu_statekeeper_initialize(
&profile_ctl->statekeeper,
NPU_PROFILE_STATE_INVALID,
state_names, transition_map);
/* Initialized debugfs interface */
ret = npu_debug_register_arg("profile-control", profile_ctl, &profile_ctl_fops);
if (ret) {
probe_err("fail(%d) in npu_debug_register_arg(profile-control)\n", ret);
goto err_exit;
}
ret = npu_debug_register_arg("profile-result", profile_ctl, &profile_result_fops);
if (ret) {
probe_err("fail(%d) in npu_debug_register_arg(profile-result)\n", ret);
goto err_exit;
}
init_waitqueue_head(&profile_ctl->wq);
/* Save reference in global variable for future use */
profile_ctl_ref = profile_ctl;
probe_info("complete in npu_profile_probe\n");
err_exit:
return ret;
}
int npu_profile_open(struct npu_system *system)
{
int ret;
struct npu_profile_control *profile_ctl;
BUG_ON(!system);
npu_dbg("start in npu_profile_open\n");
profile_ctl = &system->profile_ctl;
mutex_lock(&profile_ctl->lock);
if (SKEEPER_COMPARE_STATE(&profile_ctl->statekeeper, NPU_PROFILE_STATE_PWRON_WAITING)) {
/* Continue postponed initialization */
npu_dbg("initiating delayed starting procedure.\n");
ret = npu_profile_start(profile_ctl);
if (ret) {
npu_err("fail(%d) in npu_profile_start\n", ret);
goto err_exit;
}
}
ret = 0;
err_exit:
mutex_unlock(&profile_ctl->lock);
npu_dbg("complete in npu_profile_open\n");
return ret;
}
int npu_profile_close(struct npu_system *system)
{
int ret;
struct npu_profile_control *profile_ctl;
BUG_ON(!system);
npu_dbg("start in npu_profile_close\n");
profile_ctl = &system->profile_ctl;
mutex_lock(&profile_ctl->lock);
if (SKEEPER_COMPARE_STATE(&profile_ctl->statekeeper, NPU_PROFILE_STATE_GATHERING)) {
/* Stop current profiling session if gathering is in progress */
npu_dbg("stopping current profiling session.\n");
ret = npu_profile_stop(profile_ctl);
if (ret) {
npu_err("fail(%d) in npu_profile_stop\n", ret);
goto err_exit;
}
}
ret = 0;
err_exit:
mutex_unlock(&profile_ctl->lock);
npu_dbg("complete in npu_profile_close\n");
return 0;
}
int npu_profile_release(void)
{
int ret = 0;
struct npu_profile_control *profile_ctl = profile_ctl_ref;
BUG_ON(!profile_ctl);
npu_dbg("start in npu_profile_release\n");
if (s_profiler_ctl.sysfs_ok) {
sysfs_remove_file(&s_profiler_ctl.dev->kobj, &dev_attr_s_profiler.attr);
s_profiler_ctl.sysfs_ok = 0;
}
mutex_lock(&profile_ctl->lock);
if (SKEEPER_COMPARE_STATE(&profile_ctl->statekeeper, NPU_PROFILE_STATE_GATHERING)) {
npu_info("profiling is GATHERING state. stopping it first.");
ret = npu_profile_stop(profile_ctl);
if (ret) {
mutex_unlock(&profile_ctl->lock);
npu_err("fail(%d) in npu_profile_stop\n", ret);
goto err_exit;
}
}
if (SKEEPER_COMPARE_STATE(&profile_ctl->statekeeper, NPU_PROFILE_STATE_EXPORT_READY)) {
npu_info("profiling data is remaining. cleaning it first.");
npu_profile_clear(profile_ctl);
}
if (SKEEPER_EXPECT_STATE(&profile_ctl->statekeeper, NPU_PROFILE_STATE_NOT_INITIALIZED))
BUG_ON(1);
profile_ctl_ref = NULL;
mutex_unlock(&profile_ctl->lock);
npu_dbg("Completed.\n");
err_exit:
return ret;
}
static inline void __profile_point(
const u32 point_id, const u32 uid, const u32 frame_id,
const u32 p0, const u32 p1, const u32 p2)
{
struct probe_point *pt;
int write_idx;
struct timeval tv;
/* Check for simple profiler */
if (is_s_profile_enabled()) {
do_gettimeofday(&tv);
npu_dbg(S_PROFILE_PREFIX "[%08lu]s[%06lu]us id:%u uid:%u fid:%u p[] = [%u %u %u]\n",
tv.tv_sec, tv.tv_usec,
point_id, uid, frame_id, p0, p1, p2);
}
BUG_ON(!profile_ctl_ref);
if (!SKEEPER_COMPARE_STATE(&profile_ctl_ref->statekeeper, NPU_PROFILE_STATE_GATHERING)) {
/* Profiling is not startted */
return;
}
BUG_ON(!profile_ctl_ref->profile_data);
write_idx = atomic_inc_return(&profile_ctl_ref->next_probe_point_idx);
if (write_idx >= profile_ctl_ref->profile_data->meta[PROFILE_UNIT_DEVICE_DRIVER].total_count) {
npu_dbg("profiling buffer is full(try:%d / capacity:%u). ignore profiling data.\n",
write_idx, profile_ctl_ref->profile_data->meta[PROFILE_UNIT_DEVICE_DRIVER].total_count);
return;
}
write_idx += profile_ctl_ref->profile_data->meta[PROFILE_UNIT_DEVICE_DRIVER].start_idx;
if (write_idx >= profile_ctl_ref->profile_data->point_cnt) {
npu_dbg("invalid write_idx(%u). point_cnt(%u)\n",
write_idx, profile_ctl_ref->profile_data->point_cnt);
return;
}
pt = &(profile_ctl_ref->profile_data->point[write_idx]);
npu_dbg("profiling data is written to (%pK), index(%d)\n", pt, write_idx);
pt->id = point_id;
pt->timestamp = readl(profile_ctl_ref->pwm);
pt->session_id = uid;
pt->frame_id = frame_id;
pt->param[0] = p0;
pt->param[1] = p1;
pt->param[2] = p2;
}
void profile_point1(const u32 point_id, const u32 uid, const u32 frame_id, const u32 p0)
{
__profile_point(point_id, uid, frame_id, p0, 0, 0);
}
void profile_point3(
const u32 point_id, const u32 uid, const u32 frame_id,
const u32 p0, const u32 p1, const u32 p2)
{
__profile_point(point_id, uid, frame_id, p0, p1, p2);
}