blob: 3b1d7e65a13b46289d485c84009d83d627c46e9b [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/atomic.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/atomic.h>
#include <linux/timekeeping.h>
#include "npu-log.h"
#include "npu-protodrv.h"
#include "npu-util-liststatemgr.h"
#include "npu-debug.h"
#include "npu-errno.h"
#include "npu-device.h"
#include "npu-queue.h"
#include "npu-if-protodrv-mbox2.h"
#include "npu-if-session-protodrv.h"
#include "npu-profile.h"
#define NPU_PROTO_DRV_SIZE 1024
#define NPU_PROTO_DRV_NAME "npu-proto_driver"
#define NPU_PROTO_DRV_FRAME_LSM_NAME "npu-proto_frame_lsm"
#define NPU_PROTO_DRV_NW_LSM_NAME "npu-proto_nw_lsm"
#define NPU_PROTO_DRV_AST_NAME "npu-proto_AST"
#ifdef NPU_LOG_TAG
#undef NPU_LOG_TAG
#endif
#define NPU_LOG_TAG "proto-drv"
#define UNLINK_CHECK_OWNERSHIP
#define TIME_STAT_BUF_LEN 256
const char *TYPE_NAME_FRAME = "frame";
const char *TYPE_NAME_NW = "Netwrok mgmt.";
/* Print log if driver is idle more than 10 seconds */
const s64 NPU_PROTO_DRV_IDLE_LOG_DELAY_NS = 10L * 1000 * 1000 * 1000;
#ifdef MBOX_MOCK_ENABLE
int deinit_for_mbox_mock(void);
int setup_for_mbox_mock(void);
#endif
/* State management */
typedef enum {
PROTO_DRV_STATE_UNLOADED = 0,
PROTO_DRV_STATE_PROBED,
PROTO_DRV_STATE_OPENED,
PROTO_DRV_STATE_INVALID,
} proto_drv_state_e;
const char *PROTO_DRV_STATE_NAME[PROTO_DRV_STATE_INVALID + 1]
= {"UNLOADED", "PROBED", "OPENED", "INVALID"};
static const u8 proto_drv_thread_state_transition[][PROTO_DRV_STATE_INVALID+1] = {
/* From - To UNLOADED PROBED OPENED INVALID*/
/* UNLOADED */ { 0, 1, 0, 0},
/* PROBED */ { 1, 0, 1, 0},
/* OPENED */ { 1, 1, 0, 0},
/* INVALID */ { 0, 0, 0, 0},
};
/* Declare List State Manager object for frame and nw*/
LSM_DECLARE(proto_frame_lsm, struct proto_req_frame, NPU_PROTO_DRV_SIZE, NPU_PROTO_DRV_FRAME_LSM_NAME);
LSM_DECLARE(proto_nw_lsm, struct proto_req_nw, NPU_PROTO_DRV_SIZE, NPU_PROTO_DRV_NW_LSM_NAME);
/* Definition of proto-drv singleton object */
struct npu_proto_drv npu_proto_drv = {
.magic_head = PROTO_DRV_MAGIC_HEAD,
.frame_lsm = &proto_frame_lsm,
.nw_lsm = &proto_nw_lsm,
.ast_param = {0},
.state = ATOMIC_INIT(PROTO_DRV_STATE_UNLOADED),
.req_id_gen = ATOMIC_INIT(NPU_REQ_ID_INITIAL - 1),
.if_session_ctx = NULL,
.npu_device = NULL,
.session_ref = {
.hash_table = {NULL},
.entry_list = LIST_HEAD_INIT(npu_proto_drv.session_ref.entry_list),
},
.magic_tail = PROTO_DRV_MAGIC_TAIL,
};
struct proto_drv_ops {
struct session_nw_ops session_nw_ops;
struct session_frame_ops session_frame_ops;
struct mbox_nw_ops mbox_nw_ops;
struct mbox_frame_ops mbox_frame_ops;
};
/*******************************************************************************
* State management functions
*
*/
static proto_drv_state_e state_transition(proto_drv_state_e new_state)
{
proto_drv_state_e old_state;
BUG_ON(new_state < 0);
BUG_ON(new_state >= PROTO_DRV_STATE_INVALID);
old_state = atomic_xchg(&npu_proto_drv.state, new_state);
/* Check after transition is made - To ensure atomicity */
if (!proto_drv_thread_state_transition[old_state][new_state]) {
npu_err("Invalid transition [%s] -> [%s]\n",
PROTO_DRV_STATE_NAME[old_state],
PROTO_DRV_STATE_NAME[new_state]);
BUG_ON(1);
}
return old_state;
}
static inline proto_drv_state_e get_state(void)
{
return atomic_read(&npu_proto_drv.state);
}
#define IS_TRANSITABLE(TARGET) \
({ \
proto_drv_state_e __curr_state = get_state(); \
proto_drv_state_e __target_state = (TARGET); \
u8 __ret; \
BUG_ON(__target_state < 0); \
BUG_ON(__target_state >= PROTO_DRV_STATE_INVALID); \
__ret = proto_drv_thread_state_transition[__curr_state][__target_state]; \
if (!__ret) \
npu_err("Invalid transition (%s) -> (%s)\n", \
PROTO_DRV_STATE_NAME[__curr_state], \
PROTO_DRV_STATE_NAME[__target_state]); \
__ret; \
})
#define EXPECT_STATE(STATE) \
({ \
proto_drv_state_e __curr_state = get_state(); \
proto_drv_state_e __expect_state = (STATE); \
BUG_ON(__expect_state < 0); \
BUG_ON(__expect_state >= PROTO_DRV_STATE_INVALID); \
if (__curr_state != __expect_state) \
npu_err("Requires state (%s), but current state is (%s)\n", \
PROTO_DRV_STATE_NAME[__expect_state], \
PROTO_DRV_STATE_NAME[__curr_state]); \
(__curr_state == __expect_state); \
})
/*******************************************************************************
* Utility functions
*
*/
static inline s64 get_time_ns(void)
{
return ktime_to_ns(ktime_get_boottime());
}
static inline const char *getTypeName(const proto_drv_req_type_e type)
{
switch (type) {
case PROTO_DRV_REQ_TYPE_FRAME:
return TYPE_NAME_FRAME;
case PROTO_DRV_REQ_TYPE_NW:
return TYPE_NAME_NW;
case PROTO_DRV_REQ_TYPE_INVALID:
default:
return "INVALID";
}
}
/* Look-up table for NW names */
static const char* NW_CMD_NAMES[NPU_NW_CMD_END - NPU_NW_CMD_BASE] = {
NULL, /* For NPU_NW_CMD_BASE */
"LOAD",
"UNLOAD",
"STREAM_ON",
"STREAM_OFF",
"POWER_DOWN",
"PROFILE_START",
"PROFILE_STOP",
"FW_TC_EXECUTE",
"CLEAR_CB",
};
/* Convinient function to get stringfy name of command */
static const char* __cmd_name(const u32 cmd)
{
u32 idx = cmd - NPU_NW_CMD_BASE;
BUG_ON(idx < 0);
BUG_ON(idx >= ARRAY_SIZE(NW_CMD_NAMES));
BUG_ON(!NW_CMD_NAMES[idx]);
return NW_CMD_NAMES[idx];
}
/*******************************************************************************
* Session reference management
*
*/
static void reset_session_ref(void)
{
struct session_ref *sess_ref = &(npu_proto_drv.session_ref);
/* Clear to Zero and initialize list head */
memset(sess_ref, 0, sizeof(*sess_ref));
INIT_LIST_HEAD(&sess_ref->entry_list);
}
static u32 __find_hash_id(const npu_uid_t uid)
{
u32 hash_id;
u32 add_val = 0;
struct session_ref *sess_ref = &(npu_proto_drv.session_ref);
struct session_ref_entry *sess_entry;
/* Retrive hash_id from session uid */
hash_id = uid % SESS_REF_HASH_TABLE_SIZE;
for (add_val = 0; add_val < SESS_REF_HASH_TABLE_SIZE; add_val += SESS_REF_HASH_MAGIC) {
hash_id = (hash_id + add_val) % SESS_REF_HASH_TABLE_SIZE;
sess_entry = sess_ref->hash_table[hash_id];
if (sess_entry != NULL) {
if (sess_entry->uid == uid) {
/* Match */
return hash_id;
}
}
}
/* Failed to find */
return SESS_REF_INVALID_HASH_ID;
}
/* Return 0 if the nw object is not belong to any session.
* Currently, POWER_DOWN and Profiling commands are not belong to session.
*/
static int is_belong_session(const struct npu_nw *nw)
{
BUG_ON(!nw);
switch (nw->cmd) {
case NPU_NW_CMD_POWER_DOWN:
case NPU_NW_CMD_PROFILE_START:
case NPU_NW_CMD_PROFILE_STOP:
case NPU_NW_CMD_FW_TC_EXECUTE:
return 0;
default:
return 1;
}
}
/*
* Return true for errcode whose proto_req_* object need to be
* kept on STUCKED state (To prepare out-of-order msgid arrival)
*/
static int is_stucked_result_code(const npu_errno_t result_code)
{
/* Determine by its result code */
switch (NPU_ERR_CODE(result_code)) {
case NPU_ERR_NPU_TIMEOUT:
case NPU_ERR_QUEUE_TIMEOUT:
return 1;
default:
return 0;
}
}
static int is_stucked_req_frame(const struct proto_req_frame *req_frame)
{
return is_stucked_result_code(req_frame->frame.result_code);
}
static int is_stucked_req_nw(const struct proto_req_nw *req_nw)
{
return is_stucked_result_code(req_nw->nw.result_code);
}
/* Dump the session reference to npu log */
static void log_session_ref(void)
{
struct session_ref *sess_ref = &(npu_proto_drv.session_ref);
struct session_ref_entry *sess_entry;
const struct npu_session *sess;
int session_cnt = 0;
u64 u_pid;
BUG_ON(!sess_ref);
npu_info("Session state list =============================\n");
list_for_each_entry(sess_entry, &sess_ref->entry_list, list) {
npu_info("Entry[%d] : UID[%u] state[%d] frame[%s] nw[%s]\n",
session_cnt, sess_entry->uid, sess_entry->s_state,
(list_empty(&sess_entry->frame_list)) ? "EMPTY" : "NOT_EMPTY",
(list_empty(&sess_entry->nw_list)) ? "EMPTY" : "NOT_EMPTY");
sess = sess_entry->session;
if (sess) {
u_pid = (u64)sess->pid;
npu_info("Entry[%d] : Session UID[%u] PID[%llu] frame_id[%u]\n",
session_cnt, sess->uid, u_pid, sess->frame_id);
} else {
npu_info("Entry[%d] : NULL Session\n", session_cnt);
}
session_cnt++;
}
npu_info("End of session state list [%d] entries =========\n", session_cnt);
}
static struct session_ref_entry *__find_session_ref(const npu_uid_t uid)
{
u32 hash_id;
struct session_ref *sess_ref = &(npu_proto_drv.session_ref);
struct session_ref_entry *sess_entry;
/* Try search on hash table */
hash_id = __find_hash_id(uid);
if (hash_id != SESS_REF_INVALID_HASH_ID) {
/* Find on hash table */
sess_entry = sess_ref->hash_table[hash_id];
goto find_entry;
}
/* No entry found on hash table -> Look up the linked list */
npu_warn("UID=%u cannot be found on hashtable. Lookup on linked list.\n", uid);
list_for_each_entry(sess_entry, &sess_ref->entry_list, list) {
if (sess_entry->uid == uid) {
/* Match */
goto find_entry;
}
}
npu_warn("UID=%u cannot be found on hash and linked list.\n", uid);
return NULL;
find_entry:
/* Now the sess_entry points valid session_ref_entry to session */
return sess_entry;
}
static struct session_ref_entry *find_session_ref_frame(const struct proto_req_frame *req_frame)
{
const struct npu_frame *frame;
BUG_ON(!req_frame);
frame = &req_frame->frame;
BUG_ON(!frame);
return __find_session_ref(frame->uid);
}
static struct session_ref_entry *find_session_ref_nw(const struct proto_req_nw *req_nw)
{
const struct npu_nw *nw;
BUG_ON(!req_nw);
nw = &req_nw->nw;
BUG_ON(!nw);
npu_trace("cmd:%u / nw->uid : %u\n", nw->cmd, nw->uid);
if (is_belong_session(nw)) {
return __find_session_ref(nw->uid);
} else {
/* REQ_NW not belong to session */
return NULL;
}
}
static enum protodrv_session_state_e get_session_ref_state_frame(const struct proto_req_frame *req_frame)
{
struct session_ref_entry *ref;
ref = find_session_ref_frame(req_frame);
if (ref == NULL) {
return SESSION_REF_STATE_INVALID;
} else {
return ref->s_state;
}
}
static enum protodrv_session_state_e get_session_ref_state_nw(const struct proto_req_nw *req_nw)
{
struct session_ref_entry *ref;
ref = find_session_ref_nw(req_nw);
if (ref == NULL) {
return SESSION_REF_STATE_INVALID;
} else {
return ref->s_state;
}
}
static struct session_ref_entry *alloc_session_ref_entry(const struct npu_session *sess)
{
struct session_ref_entry *entry;
BUG_ON(!sess);
/* TODO: Use slab allocator */
entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
if (entry == NULL) {
return NULL;
}
memset(entry, 0, sizeof(*entry));
entry->hash_id = SESS_REF_INVALID_HASH_ID;
entry->s_state = SESSION_REF_STATE_INVALID;
entry->uid = sess->uid;
entry->session = sess;
INIT_LIST_HEAD(&entry->list);
INIT_LIST_HEAD(&entry->frame_list);
INIT_LIST_HEAD(&entry->nw_list);
return entry;
}
static void free_session_ref_entry(struct session_ref_entry *sess_entry)
{
if (sess_entry == NULL) {
npu_warn("free request for null pointer.\n");
return;
}
kfree(sess_entry);
}
int register_session_ref(struct npu_session *sess)
{
u32 hash_id;
u32 add_val = 0;
struct session_ref *sess_ref = &(npu_proto_drv.session_ref);
struct session_ref_entry *sess_entry = NULL;
BUG_ON(!sess);
/* Create entry */
sess_entry = alloc_session_ref_entry(sess);
if (sess_entry == NULL) {
npu_uerr("fail in alloc_session_ref_entry\n", sess);
return -ENOMEM;
}
/* Find free entry in hash table */
hash_id = (sess->uid) % SESS_REF_HASH_TABLE_SIZE;
for (add_val = 0; add_val < SESS_REF_HASH_TABLE_SIZE; add_val += SESS_REF_HASH_MAGIC) {
hash_id = (hash_id + add_val) % SESS_REF_HASH_TABLE_SIZE;
if (sess_ref->hash_table[hash_id] == NULL) {
/* Find room */
npu_udbg("Register UID at hashtable[%u]\n", sess, hash_id);
sess_ref->hash_table[hash_id] = sess_entry;
sess_entry->hash_id = hash_id;
break;
}
}
if (add_val >= SESS_REF_HASH_TABLE_SIZE) {
/* Hash table is not available even thought it is registered */
npu_uwarn("UID cannot be stored on hash table.\n", sess);
}
list_add_tail(&sess_entry->list, &sess_ref->entry_list);
npu_udbg("session ref @%pK registered.\n", sess, sess_entry);
return 0;
}
int drop_session_ref(const npu_uid_t uid)
{
u32 hash_id;
struct session_ref *sess_ref = &(npu_proto_drv.session_ref);
struct session_ref_entry *sess_entry = NULL;
sess_entry = __find_session_ref(uid);
if (sess_entry == NULL) {
npu_err("cannot found session reference for UID [%u].\n", uid);
return -EINVAL;
}
/* Remove from hash table */
hash_id = __find_hash_id(uid);
if (hash_id != SESS_REF_INVALID_HASH_ID) {
/* Found on hash table */
BUG_ON(sess_entry != sess_ref->hash_table[hash_id]);
sess_ref->hash_table[hash_id] = NULL;
}
/* Check entries */
if (!list_empty(&sess_entry->frame_list)) {
npu_warn("frame list for UID [%u] is not empty. First is at %pK\n",
uid, list_first_entry_or_null(&sess_entry->frame_list, struct proto_req_frame, sess_ref_list));
}
if (!list_empty(&sess_entry->nw_list)) {
npu_warn("network mgmt. list for UID [%u] is not empty. First is at %pK\n",
uid, list_first_entry_or_null(&sess_entry->nw_list, struct proto_req_nw, sess_ref_list));
}
npu_info("[U%u]session ref @%pK dropped.\n", uid, sess_entry);
/* Remove from Linked list */
list_del_init(&sess_entry->list);
/* Free entry */
free_session_ref_entry(sess_entry);
return 0;
}
static int is_list_used(struct list_head *list)
{
if (!list_empty(list)) {
/* next is not self and next is not null -> Belong to other list */
if (list->next != NULL) {
return 1;
}
}
return 0;
}
int link_session_frame(struct proto_req_frame *req_frame)
{
const struct npu_frame *frame;
struct session_ref_entry *sess_entry = NULL;
BUG_ON(!req_frame);
frame = &req_frame->frame;
sess_entry = find_session_ref_frame(req_frame);
if (sess_entry == NULL) {
npu_uferr("cannot found session ref.\n", frame);
return -EINVAL;
}
if (is_list_used(&req_frame->sess_ref_list)) {
npu_uerr("frame seemed to belong other session. next pointer is %pK\n"
, frame, req_frame->sess_ref_list.next);
return -EFAULT;
}
INIT_LIST_HEAD(&req_frame->sess_ref_list);
list_add_tail(&req_frame->sess_ref_list, &sess_entry->frame_list);
npu_ufdbg("frame linked to ref@%pK.\n", frame, sess_entry);
return 0;
}
int link_session_nw(struct proto_req_nw *req_nw)
{
const struct npu_nw *nw;
struct session_ref_entry *sess_entry = NULL;
BUG_ON(!req_nw);
nw = &req_nw->nw;
if (!is_belong_session(nw)) {
/* No link necessary */
npu_uinfo("NW no need to linked to session reference.\n", nw);
return 0;
}
sess_entry = find_session_ref_nw(req_nw);
if (sess_entry == NULL) {
npu_uerr("cannot found session ref.\n", nw);
return -EINVAL;
}
if (is_list_used(&req_nw->sess_ref_list)) {
npu_uerr("network mgmt. seemed to belong other session. next pointer is %pK\n"
, nw, req_nw->sess_ref_list.next);
return -EFAULT;
}
INIT_LIST_HEAD(&req_nw->sess_ref_list);
list_add_tail(&req_nw->sess_ref_list, &sess_entry->nw_list);
npu_udbg("NW linked to ref@%pK.\n", nw, sess_entry);
return 0;
}
int unlink_session_frame(struct proto_req_frame *req_frame)
{
#ifdef UNLINK_CHECK_OWNERSHIP
/* This check is somewhat time consuming operation */
struct session_ref_entry *sess_entry = NULL;
struct proto_req_frame *iter_frame;
const struct npu_frame *frame;
BUG_ON(!req_frame);
frame = &req_frame->frame;
sess_entry = find_session_ref_frame(req_frame);
if (sess_entry == NULL) {
npu_uferr("cannot found session ref.\n", frame);
return -EINVAL;
}
list_for_each_entry(iter_frame, &sess_entry->frame_list, sess_ref_list) {
if (iter_frame == req_frame) {
goto found_match;
}
}
npu_uferr("frame does not belong to session ref@%pK.\n", frame, sess_entry);
return -EINVAL;
found_match:
#endif /* UNLINK_CHECK_OWNERSHIP */
list_del_init(&req_frame->sess_ref_list);
npu_ufdbg("frame unlinked from ref@%pK.\n", frame, sess_entry);
return 0;
}
int unlink_session_nw(struct proto_req_nw *req_nw)
{
#ifdef UNLINK_CHECK_OWNERSHIP
/* This check is somewhat time consuming operation */
struct session_ref_entry *sess_entry = NULL;
struct proto_req_nw *iter_nw;
const struct npu_nw *nw;
BUG_ON(!req_nw);
nw = &req_nw->nw;
if (!is_belong_session(nw)) {
/* No unlink necessary */
npu_uinfo("NW no need to be unlinked from session reference.\n", nw);
return 0;
}
sess_entry = find_session_ref_nw(req_nw);
if (sess_entry == NULL) {
npu_uerr("cannot found session ref.\n", nw);
return -EINVAL;
}
list_for_each_entry(iter_nw, &sess_entry->nw_list, sess_ref_list) {
if (iter_nw == req_nw) {
goto found_match;
}
}
npu_uerr("network mgmt. does not belong to session ref@%pK.\n", nw, sess_entry);
return -EINVAL;
found_match:
#endif /* UNLINK_CHECK_OWNERSHIP */
list_del_init(&req_nw->sess_ref_list);
npu_uinfo("NW unlinked from ref@%pK.\n", nw, sess_entry);
return 0;
}
/*
* Nullify src_queue(for frame) and notify_func(for nw)
* for all requests linked with session reference for specified nw
* (But do not clear notify_func for req_nw itself)
* to make no more notificatin sent for this session.
*/
static int force_clear_cb(struct proto_req_nw *req_nw)
{
struct session_ref_entry *sess_entry = NULL;
struct proto_req_nw *iter_nw;
struct proto_req_frame *iter_frame;
struct npu_nw *nw;
int cnt;
BUG_ON(!req_nw);
nw = &req_nw->nw;
sess_entry = find_session_ref_nw(req_nw);
if (sess_entry == NULL) {
npu_uerr("cannot found session ref.\n", nw);
return -ENOENT;
}
/* Clear callback from associated frame list */
cnt = 0;
list_for_each_entry(iter_frame, &sess_entry->frame_list, sess_ref_list) {
npu_ufinfo("Reset src_queue for frame in state [%d]\n",
&iter_frame->frame, iter_frame->state);
iter_frame->frame.src_queue = NULL;
cnt++;
}
npu_uinfo("Clear src_queue for frame: %d entries\n", nw, cnt);
cnt = 0;
list_for_each_entry(iter_nw, &sess_entry->nw_list, sess_ref_list) {
/* Do not clear CB for req_nw itself */
if (iter_nw != req_nw) {
npu_uinfo("Reset notify_func for nw in state [%d]\n",
&iter_nw->nw, iter_nw->state);
iter_nw->nw.notify_func = NULL;
cnt++;
}
}
npu_uinfo("Clear notify_func for nw: %d entries\n", nw, cnt);
return 0;
}
/*
* Returns 1 if the specified npu_nw object is the only linked to its associated session.
* Otherwise, returns 0.
*/
int is_last_session_ref(struct proto_req_nw *req_nw)
{
struct session_ref_entry *sess_entry = NULL;
struct proto_req_nw *iter_nw;
const struct npu_nw *nw;
int ret;
BUG_ON(!req_nw);
nw = &req_nw->nw;
sess_entry = find_session_ref_nw(req_nw);
if (sess_entry == NULL) {
npu_uwarn("cannot found session ref.\n", nw);
return 0;
}
/* No frame shall be associated */
if (!list_empty(&sess_entry->frame_list)) {
return 0; /* FALSE */
}
/* Only one frame shall be available, and it should be nw */
ret = 0;
list_for_each_entry(iter_nw, &sess_entry->nw_list, sess_ref_list) {
if (iter_nw == req_nw) {
ret = 1; /* TRUE, but need to check other entry */
} else {
return 0; /* Other entry except nw -> FALSE */
}
}
return ret;
}
/* Returns 0 if no session ref is exist.
* Otherwise, return 1
*/
static int is_session_ref_exist(void)
{
struct session_ref *sess_ref = &(npu_proto_drv.session_ref);
return !list_empty(&sess_ref->entry_list);
}
/* Iniitialize all entry's sess_ref_list on start up.
* This macro is called on npu_protodrv_open()
*/
#define NPU_PROTODRV_LSM_ENTRY_INIT(LSM_NAME, TYPE) \
do { \
TYPE *__entry; \
LSM_FOR_EACH_ENTRY_IN(LSM_NAME, FREE, __entry, \
memset(__entry, 0, sizeof(*__entry)); \
INIT_LIST_HEAD(&__entry->sess_ref_list); \
atomic_set(&__entry->ts.curr_entry, 0); \
) \
} while (0)
/*******************************************************************************
* NPU data handling functions
*/
static inline int check_time(const struct npu_timestamp *tstamp, const u64 timeout_ns, const u64 now_ns)
{
u64 diff;
const struct npu_timestamp_entry *ts_entry;
if (timeout_ns == 0) {
return 0; /* No timeout defined */
}
ts_entry = &(tstamp->hist[atomic_read(&tstamp->curr_entry)]);
diff = now_ns - ts_entry->enter;
return diff > timeout_ns;
}
/*
* Returns true if request's ts.curr exceedes specified timeout.
* (PROTO_REQ_PTR can be both proto_req_frame* or proto_req_nw*)
* Otherwise, return false.
*/
#define is_timedout(PROTO_REQ_PTR, timeout_ns, now_ns) \
({ \
typeof(PROTO_REQ_PTR) __p = (PROTO_REQ_PTR); \
BUG_ON(!__p); \
check_time(&__p->ts, (timeout_ns), (now_ns)); \
}) \
/*
* Generate a unique npu_req_id and return it.
* Current implementation implies the npu_req_id_gen is
* signed integer type.
*/
static npu_req_id_t get_next_npu_req_id(void)
{
/* handling roll over
The initial value is NPU_REQ_ID_INITIAL - 1 because
the return value is made by current counter + 1 */
atomic_cmpxchg(&npu_proto_drv.req_id_gen, NPU_REQ_ID_ROLLOVER, (NPU_REQ_ID_INITIAL - 1));
/* return next count */
return (npu_req_id_t)atomic_inc_return(&npu_proto_drv.req_id_gen);
}
/*******************************************************************************
* Timestamp handling functions
*/
static void __update_npu_timestamp(const lsm_list_type_e old_state, const lsm_list_type_e new_state, struct npu_timestamp *ts)
{
int ts_entry_idx_curr, ts_entry_idx_next;
s64 now = get_time_ns();
BUG_ON(!ts);
do {
ts_entry_idx_curr = atomic_read(&ts->curr_entry);
ts_entry_idx_next = (ts_entry_idx_curr + 1) % LSM_ELEM_STATUS_TRACK_LEN;
} while (atomic_cmpxchg(&ts->curr_entry, ts_entry_idx_curr, ts_entry_idx_next) != ts_entry_idx_curr);
if (ts->hist[ts_entry_idx_curr].state != old_state) {
npu_warn("history index(%d)'s state(%d) does not match with last state (%d).",
ts_entry_idx_curr, ts->hist[ts_entry_idx_curr].state, old_state);
}
ts->hist[ts_entry_idx_curr].leave = ts->hist[ts_entry_idx_next].enter = now;
ts->hist[ts_entry_idx_next].state = new_state;
}
/* Print timestamp history, until it gets FREE state */
static char *__print_npu_timestamp(struct npu_timestamp *ts, char *buf, const size_t len)
{
s64 now = get_time_ns();
int i, ret;
int idx, tmp_idx;
int idx_start;
int buf_idx = 0;
struct timespec ts_time;
BUG_ON(!ts);
idx_start = atomic_read(&ts->curr_entry);
if (idx_start < 0 || idx_start >= LSM_ELEM_STATUS_TRACK_LEN) {
npu_err("invalid curr_entry (%d)\n", idx_start);
ret = scnprintf(buf + buf_idx, len - buf_idx, "<Invalid curr_entry (%d)>", idx_start);
buf_idx += ret;
goto ret_exit;
}
/* Back track to start(FREE) state */
idx = idx_start;
while (ts->hist[idx].state != FREE) {
tmp_idx = idx - 1;
if (tmp_idx < 0)
tmp_idx = LSM_ELEM_STATUS_TRACK_LEN - 1;
if (tmp_idx == idx_start) {
/* Not enough history to FREE */
ret = scnprintf(buf + buf_idx, len - buf_idx, "<Not enough history>");
buf_idx += ret;
break;
}
idx = tmp_idx;
}
/* printout history */
i = idx;
while (1) {
ts_time = ns_to_timespec(ts->hist[i].enter);
ret = scnprintf(buf + buf_idx, len - buf_idx, "[%s](%ld.%09ld) ",
LSM_STATE_NAMES[ts->hist[i].state], ts_time.tv_sec, ts_time.tv_nsec);
if (!ret)
break;
buf_idx += ret;
if (i == idx_start)
break; /* This is normal termination condition */
i = (i + 1) % LSM_ELEM_STATUS_TRACK_LEN;
}
ts_time = ns_to_timespec(now);
scnprintf(buf + buf_idx, len - buf_idx, "[DONE](%ld.%09ld) ",
ts_time.tv_sec, ts_time.tv_nsec);
ret_exit:
buf[len - 1] = '\0';
return buf;
}
int session_fault_listener(void)
{
#define mn_state_f(x) #x
struct session_ref_entry *sess_entry = NULL;
struct session_ref *sess_ref = &(npu_proto_drv.session_ref);
struct proto_req_frame *iter_frame;
struct proto_req_nw *iter_nw;
struct npu_frame *frame;
struct npu_nw *nw;
struct npu_session *session;
struct av_info *IFM_info;
struct av_info *OFM_info;
struct addr_info *IFM_addr;
struct addr_info *OFM_addr;
struct addr_info *IMB_addr;
struct addr_info *WGT_addr;
int qbuf_IOFM_idx;
int dqbuf_IOFM_idx;
size_t IFM_cnt;
size_t OFM_cnt;
size_t IMB_cnt;
size_t WGT_cnt;
int session_processing_cnt = 0;
size_t idx0 = 0;
size_t idx1 = 0;
size_t idx2 = 0;
int session_chk[NPU_MAX_SESSION] = {0};
u32 session_uid;
u32 req_cnt = 0;
idx2 = 0;
list_for_each_entry(sess_entry, &sess_ref->entry_list, list) {
list_for_each_entry(iter_nw, &sess_entry->nw_list, sess_ref_list) {
if (iter_nw->state == PROCESSING)
req_cnt++;
}
list_for_each_entry(iter_frame, &sess_entry->frame_list, sess_ref_list) {
if (iter_frame->state == PROCESSING)
req_cnt++;
}
}
npu_info("<Outstanding requeset [%u]>\n", req_cnt);
npu_info("[Index] [Type] [KVA] [DVA] [Size]\n");
idx2 = 0;
list_for_each_entry(sess_entry, &sess_ref->entry_list, list) {
list_for_each_entry(iter_nw, &sess_entry->nw_list, sess_ref_list) {
if (iter_nw->state == PROCESSING) {
nw = &iter_nw->nw;
npu_info("[%zu] Type:NW UID:%u ReqID:%u FrameID:N/A CMD:%d MsgID:%d\n",
idx2, nw->uid, nw->npu_req_id, nw->cmd, nw->msgid);
idx2++;
}
}
list_for_each_entry(iter_frame, &sess_entry->frame_list, sess_ref_list) {
if (iter_frame->state == PROCESSING) {
frame = &iter_frame->frame;
npu_info("[%zu] Type:FRAME UID:%u ReqID:%u FrameID:%u CMD:%d MsgID:%d\n",
idx2, frame->uid, frame->npu_req_id, frame->frame_id, frame->cmd, frame->msgid);
IFM_info = &frame->IFM_info[0];
OFM_info = &frame->OFM_info[0];
IFM_addr = IFM_info->addr_info;
OFM_addr = OFM_info->addr_info;
IFM_cnt = IFM_info->address_vector_cnt;
OFM_cnt = OFM_info->address_vector_cnt;
for (idx1 = 0; idx1 < IFM_cnt; idx1++) {
npu_info("- %u %d %pK %pad %zu\n",
(IFM_addr + idx1)->av_index, MEMORY_TYPE_IN_FMAP,
(IFM_addr + idx1)->vaddr, &((IFM_addr + idx1)->daddr),
(IFM_addr + idx1)->size);
}
for (idx1 = 0; idx1 < OFM_cnt; idx1++) {
npu_info("- %u %d %pK %pad %zu\n",
(OFM_addr + idx1)->av_index, MEMORY_TYPE_OT_FMAP,
(OFM_addr + idx1)->vaddr, &((OFM_addr + idx1)->daddr),
(OFM_addr + idx1)->size);
}
idx2++;
}
}
}
idx2 = 0;
list_for_each_entry(sess_entry, &sess_ref->entry_list, list) {
list_for_each_entry(iter_frame, &sess_entry->frame_list, sess_ref_list) {
if (iter_frame->state == PROCESSING) {
frame = &iter_frame->frame;
session = frame->session;
if (!session)
continue;
session_uid = session->uid;
session_uid++;
for (idx2 = 0; idx2 < NPU_MAX_SESSION; idx2++) {
if (session_chk[idx2] != session_uid)
session_chk[idx2] = session_uid;
}
}
}
}
for (idx2 = 0; idx2 < NPU_MAX_SESSION; idx2++) {
if (session_chk[idx2] == 0) {
session_processing_cnt = idx2;
break;
}
}
npu_info("<Session information [%d]>\n", session_processing_cnt);
npu_info("[Index] [Type] [KVA] [DVA] [Size]\n");
idx2 = 0;
list_for_each_entry(sess_entry, &sess_ref->entry_list, list) {
list_for_each_entry(iter_frame, &sess_entry->frame_list, sess_ref_list) {
if (iter_frame->state == PROCESSING) {
frame = &iter_frame->frame;
session = frame->session;
if (!session)
continue;
session_uid = session->uid;
session_uid++;
for (idx2 = 0; idx2 < NPU_MAX_SESSION; idx2++) {
if (session_chk[idx2] == session_uid)
continue;
}
session_uid--;
qbuf_IOFM_idx = session->qbuf_IOFM_idx;
dqbuf_IOFM_idx = session->dqbuf_IOFM_idx;
npu_info("[%zu] UID:%d q/dq_buf_idx(%d/%d)\n",
idx2, session_uid, qbuf_IOFM_idx, dqbuf_IOFM_idx);
IFM_cnt = session->IFM_cnt;
OFM_cnt = session->OFM_cnt;
IMB_cnt = session->IMB_cnt;
WGT_cnt = session->WGT_cnt;
for (idx0 = 0; idx0 < VISION_MAX_BUFFER; idx0++) {
IFM_info = &session->IFM_info[idx0];
OFM_info = &session->OFM_info[idx0];
IFM_addr = IFM_info->addr_info;
OFM_addr = OFM_info->addr_info;
for (idx1 = 0; idx1 < IFM_cnt; idx1++) {
npu_info("- %zu %d %pK %pad %zu\n",
idx1, MEMORY_TYPE_IN_FMAP, (IFM_addr + idx1)->vaddr,
&((IFM_addr + idx1)->daddr), (IFM_addr + idx1)->size);
}
for (idx1 = 0; idx1 < OFM_cnt; idx1++) {
npu_info("- %zu %d %pK %pad %zu\n",
idx1, MEMORY_TYPE_OT_FMAP, (OFM_addr + idx1)->vaddr,
&((OFM_addr + idx1)->daddr), (OFM_addr + idx1)->size);
}
}
IMB_addr = session->IMB_info;
WGT_addr = session->WGT_info;
for (idx1 = 0; idx1 < IMB_cnt; idx1++) {
npu_info("- %zu %d %pK %pad %zu\n",
idx1, MEMORY_TYPE_IM_FMAP, (IMB_addr + idx1)->vaddr,
&((IMB_addr + idx1)->daddr), (IMB_addr + idx1)->size);
}
for (idx1 = 0; idx1 < WGT_cnt; idx1++) {
npu_info("- %zu %d %pK %pad %zu\n",
idx1, MEMORY_TYPE_WEIGHT, (WGT_addr + idx1)->vaddr,
&((WGT_addr + idx1)->daddr), (WGT_addr + idx1)->size);
}
idx2++;
}
}
}
return 0;
}
int proto_req_fault_listener(void)
{
#define mn_state_f(x) #x
struct session_ref_entry *sess_entry = NULL;
struct session_ref *sess_ref = &(npu_proto_drv.session_ref);
struct proto_req_frame *iter_frame;
struct proto_req_nw *iter_nw;
struct npu_frame *frame;
struct npu_nw *nw;
int ss_Cnt = 0;
int fr_Cnt = 0;
int nw_Cnt = 0;
char *mn_state;
char stat_buf[TIME_STAT_BUF_LEN];
size_t i = 0;
list_for_each_entry(sess_entry, &sess_ref->entry_list, list) {
list_for_each_entry(iter_frame, &sess_entry->frame_list, sess_ref_list) {
frame = &iter_frame->frame;
mn_state = mn_state_f(iter_frame->state);
npu_err("(TIMEOUT):%s (%s): ss_cnt[%d] nFrame[%d] uid[%u] fid[%u] a_vec_cnt[%u] a_vec_st_daddr[%pad] NPU-TIMESTAT:%s\n",
TYPE_NAME_FRAME,
mn_state,
ss_Cnt,
fr_Cnt,
frame->uid,
frame->frame_id,
frame->mbox_process_dat.address_vector_cnt,
&(frame->mbox_process_dat.address_vector_start_daddr),
__print_npu_timestamp(&iter_frame->ts, stat_buf, TIME_STAT_BUF_LEN));
if (iter_frame->state == PROCESSING) {
npu_err("FRAME info:\n");
do {
printk("%08x ", *(volatile u32 *)(frame->session->IOFM_mem_buf->vaddr +
(sizeof(64) * i)));
i++;
if ((i % 4) == 0)
printk("\n");
} while (sizeof(u32) * i < frame->session->IOFM_mem_buf->size);
}
fr_Cnt++;
}
list_for_each_entry(iter_nw, &sess_entry->nw_list, sess_ref_list) {
nw = &iter_nw->nw;
mn_state = mn_state_f(iter_nw->state);
npu_err("(TIMEOUT):%s (%s): ss_cnt[%d] nNw[%d] cmd[%d] uid[%u] cmd.length[%zu] payload[%pad] NPU-TIMESTAT:%s\n",
TYPE_NAME_NW,
mn_state,
ss_Cnt,
nw_Cnt,
nw->cmd,
nw->uid,
nw->ncp_addr.size,
&nw->ncp_addr.daddr,
__print_npu_timestamp(&iter_nw->ts, stat_buf, TIME_STAT_BUF_LEN));
nw_Cnt++;
}
ss_Cnt++;
}
return 0;
}
/*******************************************************************************
* Interfacing functions
*/
/* nw_mgmp_ops -> Use functions in npu-if-session-protodrv */
static int nw_mgmt_op_is_available(void)
{
return npu_ncp_mgmt_is_available();
}
#ifdef T32_GROUP_TRACE_SUPPORT
void t32_trace_ncp_load(void)
{
npu_info("T32_TRACE: continue execution.\n");
}
#endif
static int nw_mgmt_op_get_request(struct proto_req_nw *target)
{
if (npu_ncp_mgmt_get(&target->nw) == 0) {
return 0; /* Empty */
} else {
target->nw.npu_req_id = get_next_npu_req_id();
#ifdef T32_GROUP_TRACE_SUPPORT
if (target->nw.cmd == NPU_NW_CMD_LOAD) {
/* Trap NCP load command for trace */
struct npu_session *sess;
sess = target->nw.session;
if (sess && sess->ncp_info.ncp_addr.vaddr) {
npu_info("T32_TRACE: NCP at d:0x%pK\n", sess->ncp_info.ncp_addr.vaddr);
} else {
npu_info("T32_TRACE: Cannot locate NCP.\n");
}
t32_trace_ncp_load();
}
#endif
return 1;
}
}
static int nw_mgmt_op_put_result(const struct proto_req_nw *src)
{
int ret;
struct nw_result result;
BUG_ON(!src);
/* Save result */
result.result_code = src->nw.result_code;
result.nw = src->nw;
/* Invoke callback registered on npu_nw object with result code */
ret = npu_ncp_mgmt_save_result(src->nw.notify_func, src->nw.session, result);
if (ret) {
npu_uerr("error(%d) in npu_ncp_mgmt_save_result\n", &src->nw, ret);
return ret;
}
return 0;
}
/* frame_proc_ops -> Use functions in npu-if-session-protodrv */
static int npu_queue_op_is_available(void)
{
return npu_buffer_q_is_available();
}
static int npu_queue_op_get_request_pair(struct proto_req_frame *target)
{
if (npu_buffer_q_get(&target->frame) == 0) {
return 0; /* Empty */
} else {
target->frame.npu_req_id = get_next_npu_req_id();
return 1;
}
}
static int npu_queue_op_put_result(const struct proto_req_frame *src)
{
const struct npu_frame *result;
BUG_ON(!src);
result = &(src->frame);
/* Copu input's id to output' id */
#ifdef MATCH_ID_INCL_OTCL
src->output.id = src->input.id;
#endif
#ifdef CONFIG_NPU_GOLDEN_MATCH
if (result->result_code == NPU_ERR_NO_ERROR) {
npu_ufdbg("invoke golden match.\n", result);
npu_golden_compare(result);
}
#endif
npu_buffer_q_notify_done(result);
return 1;
}
static int get_msgid_type(const int msgid)
{
return msgid_get_pt_type(&npu_proto_drv.msgid_pool, msgid);
}
/* nw_mbox_ops -> Use npu-if-protodrv-mbox object stored in npu_proto_drv */
static int nw_mbox_op_is_available(void)
{
return npu_nw_mbox_op_is_available();
}
static int nw_mbox_ops_get(struct proto_req_nw **target)
{
return npu_nw_mbox_ops_get(&npu_proto_drv.msgid_pool, target);
}
static int nw_mbox_ops_put(struct proto_req_nw *src)
{
return npu_nw_mbox_ops_put(&npu_proto_drv.msgid_pool, src);
}
/* frame_mbox_ops -> Use npu-if-protodrv-mbox object stored in npu_proto_drv */
static int frame_mbox_op_is_available(void)
{
return npu_frame_mbox_op_is_available();
}
static int frame_mbox_ops_get(struct proto_req_frame **target)
{
return npu_frame_mbox_ops_get(&npu_proto_drv.msgid_pool, target);
}
static int frame_mbox_ops_put(struct proto_req_frame *src)
{
return npu_frame_mbox_ops_put(&npu_proto_drv.msgid_pool, src);
}
/**********************************************
*
* Emergency Error Set Function in Proto Drv
*
***********************************************/
static void set_emergency_err_from_req_frame(struct proto_req_frame *req_frame)
{
struct npu_device *device = npu_proto_drv.npu_device;
BUG_ON(!device);
npu_warn("call npu_device_set_emergency_err(device)\n");
npu_device_set_emergency_err(device);
}
static void set_emergency_err_from_req_nw(struct proto_req_nw *req_nw)
{
struct npu_device *device = npu_proto_drv.npu_device;
BUG_ON(!device);
npu_warn("call npu_device_set_emergency_err(device)\n");
npu_device_set_emergency_err(device);
}
/*******************************************************************************
* LSM state transition functions
*/
/*
* Collect network management request and start processing.
*
* FREE -> REQUESTED
*/
static int npu_protodrv_handler_nw_free(void)
{
int ret = 0;
int handle_cnt = 0;
int err_cnt = 0;
struct proto_req_nw *entry;
struct session_ref_entry *session_ref_entry;
s64 now = get_time_ns();
/* Take a entry from FREE list, before access the queue */
while ((entry = proto_nw_lsm.lsm_get_entry(FREE)) != NULL) {
/* Is request available ? */
if (nw_mgmt_op_get_request(entry) != 0) {
/* Save the request and put it to REQUESTED state */
entry->ts.init = now;
npu_uinfo("(REQ)NW: cmd:%u(%s) / req_id:%u\n",
&entry->nw, entry->nw.cmd, __cmd_name(entry->nw.cmd),
entry->nw.npu_req_id);
if (entry->nw.cmd == NPU_NW_CMD_LOAD) {
/* Load command -> Register the session */
ret = register_session_ref(entry->nw.session);
if (ret) {
npu_uerr("cannot register session: ret = %d\n",
&entry->nw, ret);
entry->nw.result_code = NPU_ERR_DRIVER(NPU_ERR_SESSION_REGISTER_FAILED);
goto error_req;
}
}
/* Link to the session reference */
ret = link_session_nw(entry);
if (ret) {
npu_uerr("cannot link session: ret = %d\n",
&entry->nw, ret);
entry->nw.result_code = NPU_ERR_DRIVER(NPU_ERR_INVALID_UID);
goto error_req;
}
/* Command specific error handling */
switch (entry->nw.cmd) {
case NPU_NW_CMD_STREAMOFF:
/* Update status */
session_ref_entry = find_session_ref_nw(entry);
if (session_ref_entry == NULL) {
npu_uerr("requested STREAM_OFF, but Session entry find error.\n", &entry->nw);
entry->nw.result_code = NPU_ERR_DRIVER(NPU_ERR_INVALID_UID);
goto error_req;
}
npu_udbg("requested STREAM_OFF : Change s_state [%d] -> [%d]\n",
&entry->nw, session_ref_entry->s_state, SESSION_REF_STATE_STOPPING);
switch (session_ref_entry->s_state) {
case SESSION_REF_STATE_ACTIVE:
/* No problem */
break;
case SESSION_REF_STATE_STOPPING:
/* Retry of failed stream off */
npu_uwarn("stream off on SESSION_REF_STATE_STOPPING state. It seems retry of STREAM_OFF.\n",
&entry->nw);
break;
default:
/* Invalid state */
npu_uerr("requested STREAM_OFF, but Session state is not Active (%d).\n",
&entry->nw, session_ref_entry->s_state);
entry->nw.result_code = NPU_ERR_DRIVER(NPU_ERR_INVALID_STATE);
goto error_req;
}
session_ref_entry->s_state = SESSION_REF_STATE_STOPPING;
break;
case NPU_NW_CMD_UNLOAD:
session_ref_entry = find_session_ref_nw(entry);
if (session_ref_entry == NULL) {
npu_uerr("requested UNLOAD, but Session entry find error.\n", &entry->nw);
entry->nw.result_code = NPU_ERR_DRIVER(NPU_ERR_INVALID_UID);
goto error_req;
}
/* Accepts UNLOAD command only in INACTIVE status */
if (session_ref_entry->s_state != SESSION_REF_STATE_INACTIVE) {
npu_uerr("unload command on invalid state (%d).\n"
, &entry->nw, session_ref_entry->s_state);
entry->nw.result_code = NPU_ERR_DRIVER(NPU_ERR_INVALID_STATE);
goto error_req;
}
break;
case NPU_NW_CMD_CLEAR_CB:
/* Clear all callbacks associated with referred session */
ret = force_clear_cb(entry);
if (ret)
npu_uwarn("force_clear_cb error (%d).\n", &entry->nw, ret);
default:
/* No additional checking for other commands */
break;
}
/* Move to REQUESTED state */
proto_nw_lsm.lsm_put_entry(REQUESTED, entry);
handle_cnt++;
goto finally;
error_req:
/* Error occured -> Move to COMPLETED state */
proto_nw_lsm.lsm_put_entry(COMPLETED, entry);
err_cnt++;
goto finally;
finally:
;
} else {
/* No more request available. rollbacks the entry to FREE */
proto_nw_lsm.lsm_put_entry(FREE, entry);
goto break_entry_iter;
}
}
break_entry_iter:
if (entry == NULL) {
/* No FREE entry ? */
npu_warn("no free entry for handling %s.\n", TYPE_NAME_NW);
}
if (handle_cnt)
npu_dbg("(%s) free ---> [%d] ---> requested.\n", TYPE_NAME_NW, handle_cnt);
if (err_cnt)
npu_dbg("(%s) free ---> [%d] ---> completed.\n", TYPE_NAME_NW, err_cnt);
return handle_cnt + err_cnt;
} \
/*
* Collect frame processing request and start processing.
*
* FREE -> REQUESTED
*/
static int npu_protodrv_handler_frame_free(void)
{
int ret = 0;
int handle_cnt = 0;
struct proto_req_frame *entry_lsm;
s64 now = get_time_ns();
/* Take a entry from FREE list, before access the queue */
while ((entry_lsm = proto_frame_lsm.lsm_get_entry(FREE)) != NULL) {
/* Is request available ? */
if (npu_queue_op_get_request_pair(entry_lsm) != 0) {
/* Save the request and put it to REQUESTED state */
entry_lsm->ts.init = now;
npu_ufdbg("(REQ)FRAME: cmd:%u / req_id:%u / frame_id:%u\n",
&entry_lsm->frame, entry_lsm->frame.cmd, entry_lsm->frame.npu_req_id, entry_lsm->frame.frame_id);
/* Link to the session reference */
ret = link_session_frame(entry_lsm);
if (ret) {
npu_uerr("cannot link session: ret = %d\n",
&entry_lsm->frame, ret);
entry_lsm->frame.result_code = NPU_ERR_DRIVER(NPU_ERR_INVALID_UID);
goto error_req;
}
/* Move to REQUESTED state */
proto_frame_lsm.lsm_put_entry(REQUESTED, entry_lsm);
goto finally;
error_req:
/* Error occured -> Move to COMPLETED state */
proto_frame_lsm.lsm_put_entry(COMPLETED, entry_lsm);
finally:
handle_cnt++;
} else {
/* No more request available. rollbacks the entry to FREE */
proto_frame_lsm.lsm_put_entry(FREE, entry_lsm);
goto break_entry_iter;
}
}
break_entry_iter:
if (entry_lsm == NULL) {
/* No FREE entry ? */
npu_warn("no free entry for handling %s request.\n", TYPE_NAME_FRAME);
}
if (handle_cnt)
npu_dbg("(%s) free ---> (%d) ---> requested.\n", TYPE_NAME_FRAME, handle_cnt);
return handle_cnt;
}
/* Error handler helper function */
static int __mbox_frame_ops_put(struct proto_req_frame *entry)
{
/*
* No further request is pumping into hardware
* on emergency recovery mode
*/
if (npu_device_is_emergency_err(npu_proto_drv.npu_device)) {
/* Print log message only if the list is not empty */
npu_ufwarn("EMERGENCY - do not send request to hardware.\n", &entry->frame);
return 0;
}
return frame_mbox_ops_put(entry);
}
static int npu_protodrv_handler_frame_requested(void)
{
int proc_handle_cnt = 0;
int compl_handle_cnt = 0;
int entryCnt = 0; /* For trace */
int q_full = 0; /* Prevent reordering */
struct proto_req_frame *entry;
enum protodrv_session_state_e s_state;
/* Process each element in REQUESTED list one by one */
LSM_FOR_EACH_ENTRY_IN(proto_frame_lsm, REQUESTED, entry,
entryCnt++;
/* Retrive Session state of entry */
switch (s_state = get_session_ref_state_frame(entry)) {
case SESSION_REF_STATE_INACTIVE:
/* Inactive state - Don't move */
npu_uftrace("INACTIVE\n", &entry->frame);
break;
case SESSION_REF_STATE_ACTIVE:
npu_uftrace("ACTIVE\n", &entry->frame);
/* Move to Processing state */
if (q_full) {
break;
}
if (__mbox_frame_ops_put(entry) > 0) {
/* Success */
proto_frame_lsm.lsm_move_entry(PROCESSING, entry);
proc_handle_cnt++;
} else {
npu_ufwarn("REQUESTED %s cannot be queued to mbox [frame_id=%u, npu_req_id=%u]\n",
&entry->frame, TYPE_NAME_FRAME, entry->frame.frame_id, entry->frame.npu_req_id);
q_full = 1;
}
break;
case SESSION_REF_STATE_STOPPING:
npu_uftrace("STOPPING\n", &entry->frame);
/* Set error code */
entry->frame.result_code = NPU_ERR_DRIVER(NPU_ERR_FRAME_CANCELED);
/* Move to completed state */
proto_frame_lsm.lsm_move_entry(COMPLETED, entry);
compl_handle_cnt++;
break;
case SESSION_REF_STATE_INVALID:
default:
npu_uftrace("INVALID\n", &entry->frame);
npu_uerr("Invalid Session state [%d]\n", &entry->frame, s_state);
break;
}
) /* End of LSM_FOR_EACH_ENTRY_IN */
if (proc_handle_cnt)
npu_dbg("(%s) [%d]REQUESTED ---> [%d] ---> PROCESSING.\n", TYPE_NAME_FRAME, entryCnt, proc_handle_cnt);
if (compl_handle_cnt)
npu_dbg("(%s) [%d]REQUESTED ---> [%d] ---> COMPLETED.\n", TYPE_NAME_FRAME, entryCnt, compl_handle_cnt);
return proc_handle_cnt + compl_handle_cnt;
}
/* Error handler helper function */
static int __mbox_nw_ops_put(struct proto_req_nw *entry)
{
int ret;
/*
* No further request is pumping into hardware
* on emergency recovery mode
*/
if (npu_device_is_emergency_err(npu_proto_drv.npu_device)) {
/* Print log message only if the list is not empty */
npu_uwarn("EMERGENCY - do not send request [%d:%s] to hardware.\n",
&entry->nw, entry->nw.cmd, __cmd_name(entry->nw.cmd));
return 0;
}
ret = nw_mbox_ops_put(entry);
if (ret <= 0)
npu_uwarn("REQUESTED %s cannot be queued to mbox [npu_req_id=%u]\n",
&entry->nw, TYPE_NAME_NW, entry->nw.npu_req_id);
return ret;
}
static int npu_protodrv_handler_nw_requested(void)
{
int proc_handle_cnt = 0;
int compl_handle_cnt = 0;
int entryCnt = 0; /* For trace */
struct proto_req_nw *entry;
enum protodrv_session_state_e s_state;
/* Process each element in REQUESTED list one by one */
LSM_FOR_EACH_ENTRY_IN(proto_nw_lsm, REQUESTED, entry,
entryCnt++;
/* Retrive Session state of entry */
s_state = get_session_ref_state_nw(entry);
switch (entry->nw.cmd) {
case NPU_NW_CMD_STREAMOFF:
#ifndef BYPASS_HW_STREAMOFF
/* Publish message to mailbox */
if (__mbox_nw_ops_put(entry) > 0) {
/* Success */
proto_nw_lsm.lsm_move_entry(PROCESSING, entry);
proc_handle_cnt++;
}
#else
npu_uinfo("BYPASS_HW_STREAMOFF enabled. Bypassing Streamoff.\n", &entry->nw);
entry->nw.result_code = NPU_ERR_NO_ERROR;
proto_nw_lsm.lsm_move_entry(COMPLETED, entry);
compl_handle_cnt++;
#endif /* BYPASS_HW_STREAMOFF */
break;
case NPU_NW_CMD_STREAMON:
/* Move to completed state */
proto_nw_lsm.lsm_move_entry(COMPLETED, entry);
compl_handle_cnt++;
break;
case NPU_NW_CMD_UNLOAD:
/* Publish if INACTIVE */
if (s_state == SESSION_REF_STATE_INACTIVE) {
if (__mbox_nw_ops_put(entry) > 0) {
/* Success */
proto_nw_lsm.lsm_move_entry(PROCESSING, entry);
proc_handle_cnt++;
}
}
break;
case NPU_NW_CMD_POWER_DOWN:
/* Publish if no session is active */
if (!is_session_ref_exist()) {
if (__mbox_nw_ops_put(entry) > 0) {
/* Success */
proto_nw_lsm.lsm_move_entry(PROCESSING, entry);
proc_handle_cnt++;
}
}
break;
case NPU_NW_CMD_CLEAR_CB:
/* Move to completed state */
proto_nw_lsm.lsm_move_entry(COMPLETED, entry);
compl_handle_cnt++;
break;
default:
npu_utrace("Conventional command cmd:(%u)(%s)\n",
&entry->nw, entry->nw.cmd, __cmd_name(entry->nw.cmd));
/* Conventional command -> Publish message to mailbox */
if (__mbox_nw_ops_put(entry) > 0) {
/* Success */
proto_nw_lsm.lsm_move_entry(PROCESSING, entry);
proc_handle_cnt++;
}
break;
}
) /* End of LSM_FOR_EACH_ENTRY_IN */
if (proc_handle_cnt)
npu_dbg("(%s) [%d]REQUESTED ---> [%d] ---> PROCESSING.\n", TYPE_NAME_NW, entryCnt, proc_handle_cnt);
if (compl_handle_cnt)
npu_dbg("(%s) [%d]REQUESTED ---> [%d] ---> COMPLETED.\n", TYPE_NAME_NW, entryCnt, compl_handle_cnt);
return proc_handle_cnt + compl_handle_cnt;
}
/*
* Get result of frame processing from NPU hardware
* and mark it as completed
*
* PROCESSING -> COMPLETED
*/
static int npu_protodrv_handler_frame_processing(void)
{
int handle_cnt = 0;
struct proto_req_frame *entry;
while (frame_mbox_ops_get(&entry) > 0) {
/* Check entry's state */
if (entry->state != PROCESSING) {
npu_warn("out of order response from mbox");
continue;
}
/* Result code already set on frame_mbox_ops_get() -> Just change its state */
proto_frame_lsm.lsm_move_entry(COMPLETED, entry);
handle_cnt++;
}
if (handle_cnt)
npu_dbg("(%s) PROCESSING ---> [%d] ---> COMPLETED.\n", TYPE_NAME_FRAME, handle_cnt);
return handle_cnt;
}
/*
* Get result of network management from NPU hardware
* and mark it as completed
*
* PROCESSING -> COMPLETED
*/
static int npu_protodrv_handler_nw_processing(void)
{
int handle_cnt = 0;
struct proto_req_nw *entry;
while (nw_mbox_ops_get(&entry) > 0) {
/* Check entry's state */
if (entry->state != PROCESSING) {
npu_warn("out of order response from mbox");
continue;
}
switch (entry->nw.cmd) {
case NPU_NW_CMD_STREAMON:
npu_uerr("invalid command(%u)\n", &entry->nw, entry->nw.cmd);
BUG_ON(1);
default:
/* Result code already set on nw_mbox_ops_get() -> Just change its state */
proto_nw_lsm.lsm_move_entry(COMPLETED, entry);
handle_cnt++;
break;
}
}
if (handle_cnt)
npu_dbg("(%s) processing ---> (%d) ---> completed.\n", TYPE_NAME_NW, handle_cnt);
return handle_cnt;
}
static int npu_protodrv_handler_frame_completed(void)
{
int ret = 0;
int handle_cnt = 0;
int entryCnt = 0; /* For trace */
struct proto_req_frame *entry;
char stat_buf[TIME_STAT_BUF_LEN];
/* Process each element in REQUESTED list one by one */
LSM_FOR_EACH_ENTRY_IN(proto_frame_lsm, COMPLETED, entry,
entryCnt++;
/* Unlink from session ref */
ret = unlink_session_frame(entry);
if (ret) {
npu_uferr("unlink_session_frame failed : %d\n", &entry->frame, ret);
}
/* Time keeping */
npu_ufdbg("(COMPLETED)frame: cmd(%u) / req_id(%d) / result(%u/0x%08x) NPU-TIMESTAT:%s\n",
&entry->frame, entry->frame.cmd, entry->frame.npu_req_id,
entry->frame.result_code, entry->frame.result_code,
__print_npu_timestamp(&entry->ts, stat_buf, TIME_STAT_BUF_LEN));
if (entry->frame.result_code != NPU_ERR_NO_ERROR)
fw_will_note(FW_LOGSIZE/4);
/* Write back result */
if (npu_queue_op_put_result(entry) > 0) {
/* Check whether the request need to be stucked or not */
if (unlikely(is_stucked_req_frame(entry))) {
proto_frame_lsm.lsm_move_entry(STUCKED, entry);
// when request frame is stucked,
// set npu device emergency error
npu_dbg("call set_emergency_err_from_req_frame()\n");
set_emergency_err_from_req_frame(entry);
} else {
/* If result was written, move back to free state */
proto_frame_lsm.lsm_move_entry(FREE, entry);
}
handle_cnt++;
} else {
npu_ufwarn("COMPLETED %s cannot be queued to result [frame_id=%u, npu_req_id=%u]\n",
&entry->frame, TYPE_NAME_FRAME, entry->frame.frame_id, entry->frame.npu_req_id);
}
npu_ufinfo("(COMPLETED)FRAME: Notification sent.\n", &entry->frame);
) /* End of LSM_FOR_EACH_ENTRY_IN */
if (handle_cnt)
npu_dbg("(%s) [%d]COMPLETED ---> [%d] ---> FREE.\n", TYPE_NAME_FRAME, entryCnt, handle_cnt);
return handle_cnt;
}
static int npu_protodrv_handler_nw_completed(void)
{
int ret = 0;
int handle_cnt = 0;
int entryCnt = 0; /* For trace */
struct proto_req_nw *entry;
struct session_ref_entry *session_ref_entry;
int transition;
char stat_buf[TIME_STAT_BUF_LEN];
/* Process each element in REQUESTED list one by one */
LSM_FOR_EACH_ENTRY_IN(proto_nw_lsm, COMPLETED, entry,
entryCnt++;
transition = 0;
if (!is_belong_session(&entry->nw)) {
/* Special treatment because no session is associated with it */
npu_udbg("complete (%s), with result code (0x%08x)\n",
&entry->nw, __cmd_name(entry->nw.cmd), entry->nw.result_code);
if (entry->nw.result_code != NPU_ERR_NO_ERROR)
fw_will_note(FW_LOGSIZE/4);
transition = 1;
} else {
/* Command other than POWER DOWN -> Associated with session */
/* Retrive Session state of entry */
session_ref_entry = find_session_ref_nw(entry);
if (session_ref_entry == NULL) {
npu_uerr("Session entry find error.\n", &entry->nw);
transition = 1;
} else {
switch (entry->nw.cmd) {
case NPU_NW_CMD_LOAD:
/* Update status */
if (entry->nw.result_code == NPU_ERR_NO_ERROR) {
npu_udbg("complete load : Change s_state (%d) -> (%d)\n",
&entry->nw, session_ref_entry->s_state, SESSION_REF_STATE_INACTIVE);
session_ref_entry->s_state = SESSION_REF_STATE_INACTIVE;
} else {
npu_uerr("complete load, with error(%u/0x%08x) :"
" Keep current s_state [%d]\n",
&entry->nw, entry->nw.result_code, entry->nw.result_code,
session_ref_entry->s_state);
fw_will_note(FW_LOGSIZE/4);
}
transition = 1;
break;
case NPU_NW_CMD_STREAMON:
/* Update status */
if (entry->nw.result_code == NPU_ERR_NO_ERROR) {
npu_udbg("complete STREAM_ON : change s_state (%d) -> (%d)\n",
&entry->nw, session_ref_entry->s_state, SESSION_REF_STATE_ACTIVE);
session_ref_entry->s_state = SESSION_REF_STATE_ACTIVE;
} else {
npu_uerr("complete STREAM_ON, with error(%u/0x%08x) :"
" keep current s_state (%d)\n",
&entry->nw, entry->nw.result_code, entry->nw.result_code,
session_ref_entry->s_state);
fw_will_note(FW_LOGSIZE/4);
}
transition = 1;
break;
case NPU_NW_CMD_STREAMOFF:
/* Claim the packet if the current is last one and result is DONE / or result is NDONE */
if ((entry->nw.result_code == NPU_ERR_NO_ERROR) && is_last_session_ref(entry)) {
npu_udbg("complete STREAM_OFF : change s_state (%d) -> (%d)\n",
&entry->nw, session_ref_entry->s_state, SESSION_REF_STATE_INACTIVE);
session_ref_entry->s_state = SESSION_REF_STATE_INACTIVE;
transition = 1;
} else if (entry->nw.result_code != NPU_ERR_NO_ERROR) {
npu_uerr("complete STREAM_OFF, with error(%u/0x%08x) :"
" keep current s_state(%d)\n",
&entry->nw, entry->nw.result_code, entry->nw.result_code,
session_ref_entry->s_state);
transition = 1;
fw_will_note(FW_LOGSIZE/4);
} else {
/* No error but there are outstanding requests */
npu_udbg("there are pending request(s). STREAM_OFF blocked at s_state [%d]\n",
&entry->nw, session_ref_entry->s_state);
transition = 0;
}
break;
case NPU_NW_CMD_UNLOAD:
/* Claim the packet if the current is last one and result is DONE / or result is NDONE */
if ((entry->nw.result_code == NPU_ERR_NO_ERROR) && is_last_session_ref(entry)) {
npu_udbg("complete UNLOAD : change s_state (%d) -> (%d)\n",
&entry->nw, session_ref_entry->s_state, SESSION_REF_STATE_INVALID);
session_ref_entry->s_state = SESSION_REF_STATE_INVALID;
transition = 1;
} else if (entry->nw.result_code != NPU_ERR_NO_ERROR) {
npu_uerr("complete UNLOAD, with error(%u/0x%08x) :"
" Keep current s_state [%d]\n",
&entry->nw, entry->nw.result_code, entry->nw.result_code,
session_ref_entry->s_state);
transition = 1;
fw_will_note(FW_LOGSIZE/4);
} else {
/* No error but there are outstanding requests */
npu_udbg("there are pending request(s). UNLOAD blocked at s_state (%d)\n",
&entry->nw, session_ref_entry->s_state);
transition = 0;
}
break;
case NPU_NW_CMD_CLEAR_CB:
/* Always success */
entry->nw.result_code = NPU_ERR_NO_ERROR;
npu_udbg("complete CLEAR_CB\n", &entry->nw);
/* Leave assertion if this message is request other than emergency mode */
if (!npu_device_is_emergency_err(npu_proto_drv.npu_device)) {
npu_uwarn("NPU_NW_CMD_CLEAR_CB posted on non-emergency situation.\n",
&entry->nw);
}
transition = 1;
break;
case NPU_NW_CMD_POWER_DOWN:
case NPU_NW_CMD_PROFILE_START:
case NPU_NW_CMD_PROFILE_STOP:
/* Should be processed on above if clause */
default:
npu_uerr("invalid command(%u)\n", &entry->nw, entry->nw.cmd);
BUG_ON(1);
}
}
}
/* Post result if processing can be completed */
if (transition) {
npu_udbg("(COMPLETED)NW: cmd(%u)(%s) / req_id(%u) / result(%u/0x%08x) NPU-TIMESTAT:%s\n",
&entry->nw, entry->nw.cmd, __cmd_name(entry->nw.cmd),
entry->nw.npu_req_id, entry->nw.result_code, entry->nw.result_code,
__print_npu_timestamp(&entry->ts, stat_buf, TIME_STAT_BUF_LEN));
if (!nw_mgmt_op_put_result(entry)) {
npu_uinfo("(COMPLETED)NW: notification sent result(0x%08x)\n",
&entry->nw, entry->nw.result_code);
} else {
/* Shall be retried on next iteration */
npu_uwarn("COMPLETED %s cannot be queued to result npu_req_id(%u)\n",
&entry->nw, TYPE_NAME_NW, entry->nw.npu_req_id);
goto retry_on_next;
}
ret = unlink_session_nw(entry);
if (ret) {
npu_uerr("unlink_session_nw for CMD[%u] failed : %d\n",
&entry->nw, entry->nw.cmd, ret);
}
if (entry->nw.cmd == NPU_NW_CMD_UNLOAD) {
/* Failed UNLOAD - left warning but session ref shall be destroyed anyway */
if (unlikely(entry->nw.result_code != NPU_ERR_NO_ERROR))
npu_uwarn("Unload failed but Session ref will be destroyed.\n", &entry->nw);
/* Destroy session ref */
ret = drop_session_ref(entry->nw.uid);
if (ret) {
npu_uerr("drop_session_ref error : %d", &entry->nw, ret);
}
}
/* Check whether the request need to be stucked or not */
if (unlikely(is_stucked_req_nw(entry))) {
proto_nw_lsm.lsm_move_entry(STUCKED, entry);
// when nw request is stucked,
// set npu_device emergency_error
npu_dbg("call set_emergency_err_from_req_nw(entry)\n");
set_emergency_err_from_req_nw(entry);
} else {
proto_nw_lsm.lsm_move_entry(FREE, entry);
}
handle_cnt++;
retry_on_next:
;
}
) /* End of LSM_FOR_EACH_ENTRY_IN */
if (handle_cnt)
npu_dbg("(%s) [%d]COMPLETED ---> [%d] ---> FREE.\n", TYPE_NAME_NW, entryCnt, handle_cnt);
return handle_cnt;
}
/*
* Map for timeout handling.
* Zero means no timeout
*/
#define S2N 1000000000 /* Seconds to nano seconds */
static struct {
u64 timeout_ns;
npu_errno_t err_code; /* Error code on timeout. Refer npu-error.h */
#ifdef CONFIG_NPU_ZEBU_EMULATION
} NPU_PROTODRV_TIMEOUT_MAP[LSM_LIST_TYPE_INVALID][PROTO_DRV_REQ_TYPE_INVALID] = {
/*Dummy*/ /* Frame request */ /* NCP mgmt. request */
/* FREE - (NA) */ {{0, 0}, {.timeout_ns = 0, .err_code = 0 }, {.timeout_ns = 0, .err_code = 0} },
/* REQUESTED */ {{0, 0}, {.timeout_ns = 0, .err_code = NPU_CRITICAL_DRIVER(NPU_ERR_SCHED_TIMEOUT)}, {.timeout_ns = 0, .err_code = 0} },
/* PROCESSING */ {{0, 0}, {.timeout_ns = 0, .err_code = NPU_CRITICAL_DRIVER(NPU_ERR_NPU_TIMEOUT) }, {.timeout_ns = 0, .err_code = 0} },
/* COMPLETED - (NA) */ {{0, 0}, {.timeout_ns = 0, .err_code = 0 }, {.timeout_ns = 0, .err_code = 0} },
};
#else
} NPU_PROTODRV_TIMEOUT_MAP[LSM_LIST_TYPE_INVALID][PROTO_DRV_REQ_TYPE_INVALID] = {
/*Dummy*/ /* Frame request */ /* NCP mgmt. request */
/* FREE - (NA) */ {{0, 0}, {.timeout_ns = 0, .err_code = 0 }, {.timeout_ns = 0, .err_code = 0} },
/* REQUESTED */ {{0, 0}, {.timeout_ns = (5L * S2N), .err_code = NPU_CRITICAL_DRIVER(NPU_ERR_SCHED_TIMEOUT)}, {.timeout_ns = (5L * S2N), .err_code = NPU_CRITICAL_DRIVER(NPU_ERR_SCHED_TIMEOUT)} },
/* PROCESSING */ {{0, 0}, {.timeout_ns = (10L * S2N), .err_code = NPU_CRITICAL_DRIVER(NPU_ERR_NPU_TIMEOUT) }, {.timeout_ns = (S2N / 2), .err_code = NPU_CRITICAL_DRIVER(NPU_ERR_QUEUE_TIMEOUT)} },
/* COMPLETED - (NA) */ {{0, 0}, {.timeout_ns = 0, .err_code = 0 }, {.timeout_ns = 0, .err_code = 0} },
};
#endif
static int proto_drv_timedout_handling(const lsm_list_type_e state)
{
s64 now = get_time_ns();
int timeout_entry_cnt = 0;
{
struct proto_req_frame *entry;
LSM_FOR_EACH_ENTRY_IN(proto_frame_lsm, state, entry,
if (is_timedout(entry, NPU_PROTODRV_TIMEOUT_MAP[state][PROTO_DRV_REQ_TYPE_FRAME].timeout_ns, now)) {
timeout_entry_cnt++;
/* Timeout */
entry->frame.result_code = NPU_PROTODRV_TIMEOUT_MAP[state][PROTO_DRV_REQ_TYPE_FRAME].err_code;
proto_frame_lsm.lsm_move_entry(COMPLETED, entry);
npu_uwarn("timeout entry (%s) on state %s - req_id(%u), result_code(%u/0x%08x)\n",
&entry->frame,
getTypeName(PROTO_DRV_REQ_TYPE_FRAME), LSM_STATE_NAMES[state],
entry->frame.npu_req_id, entry->frame.result_code, entry->frame.result_code);
}
)
}
{
struct proto_req_nw *entry;
LSM_FOR_EACH_ENTRY_IN(proto_nw_lsm, state, entry,
if (is_timedout(entry, NPU_PROTODRV_TIMEOUT_MAP[state][PROTO_DRV_REQ_TYPE_NW].timeout_ns, now)) {
timeout_entry_cnt++;
/* Timeout */
entry->nw.result_code = NPU_PROTODRV_TIMEOUT_MAP[state][PROTO_DRV_REQ_TYPE_NW].err_code;
proto_nw_lsm.lsm_move_entry(COMPLETED, entry);
npu_uwarn("timeout entry (%s) on state %s - req_id(%u), result_code(%u/0x%08x)\n",
&entry->nw,
getTypeName(PROTO_DRV_REQ_TYPE_NW), LSM_STATE_NAMES[state],
entry->nw.npu_req_id, entry->nw.result_code, entry->nw.result_code);
}
)
}
return timeout_entry_cnt;
}
/* Hook function which is called on every LSM transition */
static void proto_drv_put_hook_frame(lsm_list_type_e state, struct proto_req_frame *entry)
{
lsm_list_type_e old_state;
struct npu_frame *frame;
BUG_ON(!entry);
old_state = entry->state;
entry->state = state;
/* Update timestamp on state transition */
if (old_state != state) {
__update_npu_timestamp(old_state, state, &entry->ts);
/* Save profiling point */
frame = &entry->frame;
switch (state) {
case REQUESTED:
profile_point3(PROBE_ID_DD_FRAME_SCHEDULED, frame->uid, frame->frame_id, 0, frame->npu_req_id, 0);
break;
case PROCESSING:
profile_point3(PROBE_ID_DD_FRAME_PROCESS, frame->uid, frame->frame_id, 0, frame->npu_req_id, 0);
break;
case COMPLETED:
profile_point3(PROBE_ID_DD_FRAME_COMPLETED, frame->uid, frame->frame_id, 0, frame->npu_req_id, 0);
break;
case FREE:
profile_point3(PROBE_ID_DD_FRAME_DONE, frame->uid, frame->frame_id, 0, frame->npu_req_id, 0);
break;
default:
break;
}
}
if (old_state != FREE && state == FREE) {
/* Transition from other state to free - clean-up its contents */
if (is_list_used(&entry->sess_ref_list)) {
npu_warn("session ref list is not empty but claimed to FREE.\n");
}
INIT_LIST_HEAD(&entry->sess_ref_list);
/* ts and frame are intentionally left for debugging */
}
}
static void proto_drv_put_hook_nw(lsm_list_type_e state, struct proto_req_nw *entry)
{
lsm_list_type_e old_state;
struct npu_nw *nw;
BUG_ON(!entry);
old_state = entry->state;
entry->state = state;
/* Update timestamp on state transition */
if (old_state != state) {
__update_npu_timestamp(old_state, state, &entry->ts);
/* Save profiling point */
nw = &entry->nw;
switch (state) {
case REQUESTED:
profile_point3(PROBE_ID_DD_NW_SCHEDULED, nw->uid, 0, nw->cmd, nw->npu_req_id, 0);
break;
case PROCESSING:
profile_point3(PROBE_ID_DD_NW_PROCESS, nw->uid, 0, nw->cmd, nw->npu_req_id, 0);
break;
case COMPLETED:
profile_point3(PROBE_ID_DD_NW_COMPLETED, nw->uid, 0, nw->cmd, nw->npu_req_id, 0);
break;
case FREE:
profile_point3(PROBE_ID_DD_NW_DONE, nw->uid, 0, nw->cmd, nw->npu_req_id, 0);
break;
default:
break;
}
}
if (old_state != FREE && state == FREE) {
/* Transition from other state to free - clean-up its contents */
if (is_list_used(&entry->sess_ref_list)) {
npu_warn("session ref list is not empty but claimed to FREE");
}
INIT_LIST_HEAD(&entry->sess_ref_list);
/* ts and frame are intentionally left for debugging */
}
}
/* Main thread function performed by AST */
static int proto_drv_do_task(struct auto_sleep_thread_param *data)
{
int ret = 0;
if (!EXPECT_STATE(PROTO_DRV_STATE_OPENED)) {
return 0;
}
ret += npu_protodrv_handler_frame_processing();
ret += npu_protodrv_handler_nw_processing();
ret += npu_protodrv_handler_frame_completed();
ret += npu_protodrv_handler_nw_completed();
ret += npu_protodrv_handler_frame_free();
ret += npu_protodrv_handler_nw_free();
ret += npu_protodrv_handler_frame_requested();
ret += npu_protodrv_handler_nw_requested();
/* Timeout handling */
ret += proto_drv_timedout_handling(REQUESTED);
ret += proto_drv_timedout_handling(PROCESSING);
npu_trace("return value = %d\n", ret);
return ret;
}
static int proto_drv_check_work(struct auto_sleep_thread_param *data)
{
if (!EXPECT_STATE(PROTO_DRV_STATE_OPENED))
return 0;
return (nw_mgmt_op_is_available() > 0)
|| (npu_queue_op_is_available() > 0)
|| (nw_mbox_op_is_available() > 0)
|| (frame_mbox_op_is_available() > 0);
}
static void proto_drv_on_idle(struct auto_sleep_thread_param *data, s64 idle_duration_ns)
{
if (idle_duration_ns < NPU_PROTO_DRV_IDLE_LOG_DELAY_NS)
return;
/* Idle is longer than NPU_PROTO_DRV_IDLE_LOG_DELAY_NS */
npu_warn("NPU driver is idle for [%lld ns].\n", idle_duration_ns);
/* Print out session info */
log_session_ref();
}
static void proto_drv_ast_signal_from_session(void)
{
auto_sleep_thread_signal(&npu_proto_drv.ast);
}
static void proto_drv_ast_signal_from_mailbox(void *arg)
{
auto_sleep_thread_signal(&npu_proto_drv.ast);
}
struct lsm_state_stat {
u32 entry_cnt[LSM_LIST_TYPE_INVALID];
s64 max_curr[LSM_LIST_TYPE_INVALID];
s64 max_init[LSM_LIST_TYPE_INVALID];
s64 avg_curr[LSM_LIST_TYPE_INVALID];
s64 avg_init[LSM_LIST_TYPE_INVALID];
void *max_curr_entry[LSM_LIST_TYPE_INVALID];
void *max_init_entry[LSM_LIST_TYPE_INVALID];
};
/* Structure to keep the call back function to retrive
* information from two LSM defined in protodrv
*/
struct lsm_getinfo_ops {
size_t (*get_entry_num)(void);
lsm_list_type_e (*get_qtype)(int idx);
struct npu_timestamp* (*get_timestamp)(int idx);
void* (*get_entry_ptr)(int idx);
};
#define DEF_LSM_GET_INFO_OPS(LSM_NAME, OPS_NAME) \
size_t LSM_NAME##_get_entry_num(void) \
{ \
return LSM_NAME.lsm_data.entry_num; \
} \
lsm_list_type_e LSM_NAME##_get_qtype(int idx) \
{ \
if (idx >= 0 && idx < LSM_NAME.lsm_data.entry_num) \
return LSM_NAME.entry_array[idx].data.state; \
else \
return LSM_LIST_TYPE_INVALID; \
} \
struct npu_timestamp *LSM_NAME##_get_timestamp(int idx) \
{ \
if (idx >= 0 && idx < LSM_NAME.lsm_data.entry_num) \
return &LSM_NAME.entry_array[idx].data.ts; \
else \
return NULL; \
} \
void *LSM_NAME##_get_entry_ptr(int idx) \
{ \
if (idx >= 0 && idx < LSM_NAME.lsm_data.entry_num) \
return (void *)&LSM_NAME.entry_array[idx].data; \
else \
return NULL; \
} \
static struct lsm_getinfo_ops OPS_NAME = { \
.get_entry_num = LSM_NAME##_get_entry_num, \
.get_qtype = LSM_NAME##_get_qtype, \
.get_timestamp = LSM_NAME##_get_timestamp, \
.get_entry_ptr = LSM_NAME##_get_entry_ptr, \
}; \
/* OPS definition for Frame LSM */
DEF_LSM_GET_INFO_OPS(proto_frame_lsm, proto_frame_lsm_getinfo_ops)
/* OPS definition for Network management LSM */
DEF_LSM_GET_INFO_OPS(proto_nw_lsm, proto_nw_lsm_getinfo_ops)
/* Collect time statistics with supplied lsm_getinfo_ops */
static int retrive_lsm_stat(struct lsm_state_stat *stat, const struct lsm_getinfo_ops *getinfo_ops)
{
struct npu_timestamp *ts;
struct npu_timestamp_entry *ts_entry;
int ts_entry_idx;
size_t all_entry_num = getinfo_ops->get_entry_num();
size_t i;
s64 curr_diff, init_diff;
lsm_list_type_e qtype;
s64 now_ns;
/* Get current time */
now_ns = get_time_ns();
BUG_ON(!stat);
memset(stat, 0, sizeof(*stat));
/* Traverse each list */
for (i = 0; i < all_entry_num; ++i) {
qtype = getinfo_ops->get_qtype(i);
if (qtype < 0 || qtype >= LSM_LIST_TYPE_INVALID) {
npu_err("invalid state [%d] on entry_array[%lu]\n",
qtype, i);
continue;
}
ts = getinfo_ops->get_timestamp(i);
if (!ts) {
npu_err("no timestamp info on entry_array[%lu]\n", i);
continue;
}
ts_entry_idx = atomic_read(&ts->curr_entry);
if (ts_entry_idx < 0 || ts_entry_idx >= LSM_ELEM_STATUS_TRACK_LEN) {
npu_err("invalid entry_idx[%d] on timestamp on entry_array[%lu]\n"
, ts_entry_idx, i);
continue;
}
ts_entry = ts->hist + ts_entry_idx;
// Get elapsed time
curr_diff = now_ns - ts_entry->enter;
init_diff = now_ns - ts->init;
// Update statistics
stat->entry_cnt[qtype]++;
if (stat->max_curr[qtype] < curr_diff) {
stat->max_curr[qtype] = curr_diff;
stat->max_curr_entry[qtype] = getinfo_ops->get_entry_ptr(i);
}
if (stat->max_init[qtype] < init_diff) {
stat->max_init[qtype] = init_diff;
stat->max_init_entry[qtype] = getinfo_ops->get_entry_ptr(i);
}
stat->avg_curr[qtype] += ((curr_diff - stat->avg_curr[qtype])
/ stat->entry_cnt[qtype]);
stat->avg_init[qtype] += ((init_diff - stat->avg_init[qtype])
/ stat->entry_cnt[qtype]);
}
return 0;
}
/* Stringfy information stored in the struct lsm_state_stat */
static size_t stat_to_string(const struct lsm_state_stat *stat, char *outbuf, const size_t len)
{
size_t remain_len = len;
int i, ret;
struct timespec ts_avg_curr, ts_avg_init, ts_max_curr, ts_max_init;
BUG_ON(!stat);
BUG_ON(!outbuf);
/* Printout for each state */
for (i = 0; i < LSM_LIST_TYPE_INVALID; ++i) {
ret = scnprintf(outbuf, remain_len,
"State [%s] - Entry count [%u]\n",
LSM_STATE_NAMES[i], stat->entry_cnt[i]);
if (ret > 0) {
remain_len -= ret;
outbuf += ret;
} else {
return 0;
}
/* Skip if the state is FREE of no entry exist on this state */
if (i == FREE || stat->entry_cnt[i] == 0) {
continue;
}
/* Print more detailed information */
ts_avg_curr = ns_to_timespec(stat->avg_curr[i]);
ts_avg_init = ns_to_timespec(stat->avg_init[i]);
ts_max_curr = ns_to_timespec(stat->max_curr[i]);
ts_max_init = ns_to_timespec(stat->max_init[i]);
ret = scnprintf(outbuf, remain_len,
"Average curr age %ld.%09ldns, init age %ld.%09ldns\n"
" Max curr age %ld.%09ldns - entry at [%pK]\n"
" Max init age %ld.%09ldns - entry at [%pK]\n",
ts_avg_curr.tv_sec, ts_avg_curr.tv_nsec,
ts_avg_init.tv_sec, ts_avg_init.tv_nsec,
ts_max_curr.tv_sec, ts_max_curr.tv_nsec, stat->max_curr_entry[i],
ts_max_init.tv_sec, ts_max_init.tv_nsec, stat->max_init_entry[i]);
if (ret > 0) {
remain_len -= ret;
outbuf += ret;
} else {
return 0;
}
}
return len - remain_len;
}
/*******************************************************************************
* Debugging functions (via debugfs)
*/
static ssize_t proto_drv_dump_status(char *outbuf, const size_t len)
{
int ret;
struct lsm_state_stat frame_stat;
struct lsm_state_stat nw_stat;
size_t remain_len = len; /* Remaining size of output */
proto_drv_state_e current_state = get_state();
/* Print global state */
ret = scnprintf(outbuf, remain_len, "Protodrv state [%s]\n",
PROTO_DRV_STATE_NAME[current_state]);
if (ret > 0) {
remain_len -= ret;
outbuf += ret;
} else {
npu_err("dump error in output writing\n");
return -EFAULT;
}
if (current_state != PROTO_DRV_STATE_OPENED) {
/* Protodrv is not operational. Just print current state */
goto ok_exit;
}
/* Collect statistics */
if (retrive_lsm_stat(&frame_stat, &proto_frame_lsm_getinfo_ops) != 0) {
npu_err("fail in retrive_lsm_stat(FRAME)");
return -EFAULT;
}
if (retrive_lsm_stat(&nw_stat, &proto_nw_lsm_getinfo_ops) != 0) {
npu_err("fail retrive_lsm_stat(NW).");
return -EFAULT;
}
/* Print stat for Frame LSM */
ret = scnprintf(outbuf, remain_len,
"--- Printing out LSM [%s]\n",
proto_frame_lsm.lsm_data.name);
if (ret > 0) {
remain_len -= ret;
outbuf += ret;
} else {
npu_err("dump error in output writing");
return -EFAULT;
}
ret = stat_to_string(&frame_stat, outbuf, remain_len);
if (ret > 0) {
remain_len -= ret;
outbuf += ret;
} else {
npu_err("dump error in output writing");
return -EFAULT;
}
ret = scnprintf(outbuf, remain_len,
"--- End of Printing out LSM [%s]\n",
proto_frame_lsm.lsm_data.name);
if (ret > 0) {
remain_len -= ret;
outbuf += ret;
} else {
npu_err("dump error in output writing\n");
return -EFAULT;
}
/* Print stat for Network Mgmt. LSM */
ret = scnprintf(outbuf, remain_len,
"--- Printing out LSM [%s]\n",
proto_nw_lsm.lsm_data.name);
if (ret > 0) {
remain_len -= ret;
outbuf += ret;
} else {
npu_err("dump error in output writing");
return -EFAULT;
}
ret = stat_to_string(&nw_stat, outbuf, remain_len);
if (ret > 0) {
remain_len -= ret;
outbuf += ret;
} else {
npu_err("dump error in output writing");
return -EFAULT;
}
ret = scnprintf(outbuf, remain_len,
"--- End of Printing out LSM [%s]\n",
proto_nw_lsm.lsm_data.name);
if (ret > 0) {
remain_len -= ret;
outbuf += ret;
} else {
npu_err("dump error in output writing\n");
return -EFAULT;
}
ok_exit:
return len - remain_len;
}
/* Functions for debugfs */
struct proto_drv_dump_debug_data {
char *buf;
size_t len;
size_t cur;
};
#define DUMP_BUF_SIZE 4096
/*
* Open :
* Allocate a buffer to store dumped result,
* and populate the buffer with dump result.
*/
int proto_drv_dump_debug_open(struct inode *inode, struct file *file)
{
struct proto_drv_dump_debug_data *dump_data = NULL;
int ret;
ssize_t dump_len;
npu_info("starti in proto_drv_dump_debug_open\n");
BUG_ON(!file);
/* Memory allocation */
dump_data = kzalloc(sizeof(*dump_data), GFP_KERNEL);
if (!dump_data) {
npu_err("fail in dump_debug_data allocation\n");
ret = -ENOMEM;
goto err_exit;
}
dump_data->buf = NULL;
dump_data->buf = kzalloc(DUMP_BUF_SIZE, GFP_KERNEL);
if (!dump_data->buf) {
npu_err("fail in output buffer allocation\n");
ret = -ENOMEM;
goto err_exit;
}
/* Store result on buffer */
dump_data->cur = 0;
dump_len = proto_drv_dump_status(dump_data->buf, DUMP_BUF_SIZE);
if (dump_len < 0) {
npu_err("error(%zd) in dump status\n", dump_len);
ret = -EFAULT;
goto err_exit;
}
dump_data->len = (size_t)dump_len;
/* Store the buffer as private data */
file->private_data = (void *)dump_data;
npu_info("complete in proto_drv_dump_debug_open, [%lu] bytes output.\n"
, dump_data->len);
return 0;
err_exit:
if (dump_data != NULL) {
if (dump_data->buf != NULL) {
kfree(dump_data->buf);
}
kfree(dump_data);
}
return ret;
}
/*
* Close : Deallocate the buffer made on open
*/
int proto_drv_dump_debug_close(struct inode *inode, struct file *file)
{
struct proto_drv_dump_debug_data *dump_data;
npu_info("start in proto_drv_dump_debug_close\n");
BUG_ON(!file);
dump_data = (struct proto_drv_dump_debug_data *)file->private_data;
BUG_ON(!dump_data);
if (dump_data->buf != NULL) {
kfree(dump_data->buf);
}
kfree(dump_data);
file->private_data = NULL;
npu_info("complete in proto_drv_dump_debug_close\n");
return 0;
}
/*
* Read : Returns contents in dump_data,
* which was generated and kept in open() function
*/
ssize_t proto_drv_dump_debug_read(struct file *file, char __user *outbuf, size_t outlen, loff_t *loff)
{
struct proto_drv_dump_debug_data *dump_data;
ssize_t copy_len;
unsigned long ul_ret;
npu_dbg("start in proto_drv_dump_debug_read\n");
BUG_ON(!file);
dump_data = (struct proto_drv_dump_debug_data *)file->private_data;
BUG_ON(!dump_data);
/* No more data */
if (dump_data->len <= dump_data->cur) {
npu_dbg("complete in eead for dump debug\n");
return 0;
}
copy_len = min(outlen, (dump_data->len - dump_data->cur));
ul_ret = copy_to_user(outbuf, &(dump_data->buf[dump_data->cur]), copy_len);
if (ul_ret != 0) {
npu_err("error on copy_to_user: [%lu]\n", ul_ret);
return -EFAULT;
}
dump_data->cur += copy_len;
npu_dbg("complete in proto_drv_dump_debug_read, [%lu] bytes are read.\n"
, copy_len);
return copy_len;
}
/* file operation structure which will be passed to npu-debug */
static const struct file_operations npu_proto_drv_dump_debug_fops = {
.owner = THIS_MODULE,
.open = proto_drv_dump_debug_open,
.release = proto_drv_dump_debug_close,
.read = proto_drv_dump_debug_read,
};
/*******************************************************************************
* Object lifecycle functions
*
* Those functions are exported and invoked by npu-device
*/
int proto_drv_probe(struct npu_device *npu_device)
{
int ret = 0;
probe_info("start in proto_drv_probe\n");
if (!IS_TRANSITABLE(PROTO_DRV_STATE_PROBED)) {
return -EBADR;
}
/* Registre dump function on debugfs */
ret = npu_debug_register("proto-drv-dump", &npu_proto_drv_dump_debug_fops);
if (ret)
probe_err("fail(%d) in npu_debug_register\n", ret);
/* Pass reference of npu_proto_drv via npu_device */
npu_device->proto_drv = &npu_proto_drv;
npu_proto_drv.npu_device = npu_device;
#ifdef T32_GROUP_TRACE_SUPPORT
probe_info("T32_TRACE: to do trace, use following T32 command.\n Break.Set 0x%pK\n",
t32_trace_ncp_load);
#endif
state_transition(PROTO_DRV_STATE_PROBED);
probe_info("complete in proto_drv_probe\n");
return ret;
}
int proto_drv_release(void)
{
probe_info("start in proto_drv_release\n");
if (!IS_TRANSITABLE(PROTO_DRV_STATE_UNLOADED)) {
return -EBADR;
}
npu_proto_drv.npu_device = NULL;
#ifdef MBOX_MOCK_ENABLE
npu_dbg("deinit mbox_mock()");
deinit_for_mbox_mock();
#endif
state_transition(PROTO_DRV_STATE_UNLOADED);
probe_info("complete in proto_drv_release\n");
return 0;
}
int proto_drv_open(struct npu_device *npu_device)
{
int ret = 0;
npu_info("start in Open Protodrv : Starting.\n");
if (!IS_TRANSITABLE(PROTO_DRV_STATE_OPENED)) {
return -EBADR;
}
npu_proto_drv.open_steps = 0;
#ifdef MBOX_MOCK_ENABLE
npu_dbg("start mbox_mock()");
setup_for_mbox_mock();
set_bit(PROTO_DRV_OPEN_SETUP_MBOX_MOCK, &npu_proto_drv.open_steps);
#endif
/* Initialize session ref */
reset_session_ref();
set_bit(PROTO_DRV_OPEN_SESSION_REF, &npu_proto_drv.open_steps);
/* Initialize interface with session */
npu_proto_drv.if_session_ctx = npu_if_session_protodrv_ctx_open();
if (!npu_proto_drv.if_session_ctx) {
npu_err("npu_if_session_protodrv init failed.\n");
ret = -EFAULT;
goto err_exit;
}
set_bit(PROTO_DRV_OPEN_IF_SESSION_CTX, &npu_proto_drv.open_steps);
/* Register signaling callback - Session side */
npu_buffer_q_register_cb(proto_drv_ast_signal_from_session);
npu_ncp_mgmt_register_cb(proto_drv_ast_signal_from_session);
set_bit(PROTO_DRV_OPEN_REGISTER_CB, &npu_proto_drv.open_steps);
/* Register signaling callback and msgid type lookup function
* will be called from Mailbox handler */
npu_mbox_op_register_notifier(proto_drv_ast_signal_from_mailbox);
npu_mbox_op_register_msgid_type_getter(get_msgid_type);
set_bit(PROTO_DRV_OPEN_REGISTER_NOTIFIER, &npu_proto_drv.open_steps);
#if 0
/* Initialize interface with mailbox manager */
npu_proto_drv.if_mbox_ctx = npu_if_protodrv_mbox_ctx_open();
if (!npu_proto_drv.if_mbox_ctx) {
npu_err("npu_if_protodrv_mbox init failed.\n");
ret = -EFAULT;
goto err_exit;
}
LLQ_REGISTER_TASK(npu_proto_drv.if_mbox_ctx->mbox_nw_resp,
proto_drv_ast_signal);
LLQ_REGISTER_TASK(npu_proto_drv.if_mbox_ctx->mbox_frame_resp,
proto_drv_ast_signal);
#endif
/* MSGID pool initialization */
msgid_pool_init(&npu_proto_drv.msgid_pool);
set_bit(PROTO_DRV_OPEN_MSGID_POOL, &npu_proto_drv.open_steps);
/* LSM initialization (without AST) */
ret = proto_frame_lsm.lsm_init(NULL, NULL, NULL);
if (ret) {
npu_err("init fail(%d) in LSM(frame)\n", ret);
goto err_exit;
}
proto_frame_lsm.lsm_set_hook(proto_drv_put_hook_frame);
NPU_PROTODRV_LSM_ENTRY_INIT(proto_frame_lsm, struct proto_req_frame);
set_bit(PROTO_DRV_OPEN_FRAME_LSM, &npu_proto_drv.open_steps);
ret = proto_nw_lsm.lsm_init(NULL, NULL, NULL);
if (ret) {
npu_err("init fail(%d) in LSM(nw)\n", ret);
goto err_exit;
}
proto_nw_lsm.lsm_set_hook(proto_drv_put_hook_nw);
NPU_PROTODRV_LSM_ENTRY_INIT(proto_nw_lsm, struct proto_req_nw);
set_bit(PROTO_DRV_OPEN_NW_LSM, &npu_proto_drv.open_steps);
/* AST initialization */
ret = auto_sleep_thread_create(&npu_proto_drv.ast,
NPU_PROTO_DRV_AST_NAME,
proto_drv_do_task, proto_drv_check_work, proto_drv_on_idle);
if (ret) {
npu_err("fail(%d) in AST create\n", ret);
goto err_exit;
}
set_bit(PROTO_DRV_OPEN_AST_CREATE, &npu_proto_drv.open_steps);
state_transition(PROTO_DRV_STATE_OPENED);
ret = auto_sleep_thread_start(&npu_proto_drv.ast, npu_proto_drv.ast_param);
if (ret) {
npu_err("fail(%d) in AST start\n", ret);
goto err_exit;
}
set_bit(PROTO_DRV_OPEN_AST_START, &npu_proto_drv.open_steps);
npu_info("complete in proto_drv_open\n");
set_bit(PROTO_DRV_OPEN_COMPLETE, &npu_proto_drv.open_steps);
return 0;
err_exit:
return ret;
}
int proto_drv_close(struct npu_device *npu_device)
{
npu_info("start in proto_drv_close\n");
if (!IS_TRANSITABLE(PROTO_DRV_STATE_PROBED)) {
return -EBADR;
}
BIT_CHECK_AND_EXECUTE(PROTO_DRV_OPEN_COMPLETE, &npu_proto_drv.open_steps, NULL, ;);
BIT_CHECK_AND_EXECUTE(PROTO_DRV_OPEN_AST_START, &npu_proto_drv.open_steps, "Stopping AST", {
auto_sleep_thread_terminate(&npu_proto_drv.ast);
});
BIT_CHECK_AND_EXECUTE(PROTO_DRV_OPEN_AST_CREATE, &npu_proto_drv.open_steps, NULL, ;);
BIT_CHECK_AND_EXECUTE(PROTO_DRV_OPEN_NW_LSM, &npu_proto_drv.open_steps, "Close NW LSM", {
proto_nw_lsm.lsm_destroy();
});
BIT_CHECK_AND_EXECUTE(PROTO_DRV_OPEN_FRAME_LSM, &npu_proto_drv.open_steps, "Close FRAME LSM", {
proto_frame_lsm.lsm_destroy();
});
#if 0
npu_info("Close Protodrv : Closing npu_if_protodrv_mbox.\n");
npu_if_protodrv_mbox_ctx_close();
npu_proto_drv.if_mbox_ctx = NULL;
#endif
BIT_CHECK_AND_EXECUTE(PROTO_DRV_OPEN_MSGID_POOL, &npu_proto_drv.open_steps, NULL, ;);
BIT_CHECK_AND_EXECUTE(PROTO_DRV_OPEN_REGISTER_NOTIFIER, &npu_proto_drv.open_steps, NULL, ;);
BIT_CHECK_AND_EXECUTE(PROTO_DRV_OPEN_REGISTER_CB, &npu_proto_drv.open_steps, NULL, ;);
BIT_CHECK_AND_EXECUTE(PROTO_DRV_OPEN_IF_SESSION_CTX,
&npu_proto_drv.open_steps,
"Close npu_if_session_protodrv", {
npu_if_session_protodrv_ctx_close();
npu_proto_drv.if_session_ctx = NULL;
});
BIT_CHECK_AND_EXECUTE(PROTO_DRV_OPEN_SESSION_REF, &npu_proto_drv.open_steps, NULL, ;);
#ifdef MBOX_MOCK_ENABLE
BIT_CHECK_AND_EXECUTE(PROTO_DRV_OPEN_SETUP_MBOX_MOCK, &npu_proto_drv.open_steps, NULL, ;);
#endif
if (npu_proto_drv.open_steps != 0)
npu_warn("Missing clean-up steps [%lu] found.\n", npu_proto_drv.open_steps);
state_transition(PROTO_DRV_STATE_PROBED);
npu_info("complete in proto_drv_close\n");
return 0;
}
int proto_drv_start(struct npu_device *npu_device)
{
npu_info("start in proto_drv_start\n");
npu_info("complete in proto_drv_start\n");
return 0;
}
int proto_drv_stop(struct npu_device *npu_device)
{
npu_info("start in proto_drv_stop\n");
npu_info("complete in proto_drv_stop\n");
return 0;
}
/* Unit test */
#ifdef CONFIG_VISION_UNITTEST
#define IDIOT_TESTCASE_IMPL "npu-protodrv.idiot"
#include "idiot-def.h"
#endif