mirror of
https://github.com/revyos/thead-kernel.git
synced 2026-09-18 21:32:16 +02:00
Add a new driver npu-ax3386-gpl, default compile as module. The default generated modules are: - vha.ko - img_mem.ko - vha_info.ko Signed-off-by: Mingzheng Xing <xingmingzheng@iscas.ac.cn>
751 lines
22 KiB
C
751 lines
22 KiB
C
/*
|
|
*****************************************************************************
|
|
* Copyright (c) Imagination Technologies Ltd.
|
|
*
|
|
* The contents of this file are subject to the MIT license as set out below.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of the
|
|
* GNU General Public License Version 2 ("GPL")in which case the provisions of
|
|
* GPL are applicable instead of those above.
|
|
*
|
|
* If you wish to allow use of your version of this file only under the terms
|
|
* of GPL, and not to allow others to use your version of this file under the
|
|
* terms of the MIT license, indicate your decision by deleting the provisions
|
|
* above and replace them with the notice and other provisions required by GPL
|
|
* as set out in the file called "GPLHEADER" included in this distribution. If
|
|
* you do not delete the provisions above, a recipient may use your version of
|
|
* this file under the terms of either the MIT license or GPL.
|
|
*
|
|
* This License is also included in this distribution in the file called
|
|
* "MIT_COPYING".
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include <linux/slab.h>
|
|
#include <linux/device.h>
|
|
#include <linux/gfp.h>
|
|
#include <linux/moduleparam.h>
|
|
#include <linux/delay.h>
|
|
|
|
#include <uapi/vha.h>
|
|
#include "vha_common.h"
|
|
#include "vha_plat.h"
|
|
#include "vha_regs.h"
|
|
|
|
static uint32_t cnn_pdump_poll_count = 10000000;
|
|
module_param(cnn_pdump_poll_count, uint, 0444);
|
|
MODULE_PARM_DESC(cnn_pdump_poll_count,
|
|
"PDUMP: Number of times to poll for CNN status");
|
|
|
|
static bool cnn_preloads_disable;
|
|
module_param(cnn_preloads_disable, bool, 0444);
|
|
MODULE_PARM_DESC(cnn_preloads_disable,
|
|
"Disables CNN preloads");
|
|
|
|
/*
|
|
* submit a command stream to the CNN hardware
|
|
* input buffers:
|
|
* command
|
|
* input
|
|
* coeff
|
|
* output buffers:
|
|
* output
|
|
* accum_load
|
|
* data:
|
|
* none
|
|
*/
|
|
static int do_cmd_cnn_submit(struct vha_cmd *cmd)
|
|
{
|
|
int i;
|
|
uint32_t val32;
|
|
const struct vha_user_cnn_submit_cmd *user_cmd =
|
|
(struct vha_user_cnn_submit_cmd *)&cmd->user_cmd;
|
|
struct vha_session *session = cmd->session;
|
|
struct vha_dev *vha = session->vha;
|
|
struct vha_buffer *buf = NULL;
|
|
struct vha_onchip_map *onchip_map = NULL;
|
|
int ret = -EINVAL;
|
|
uint64_t alt_addrs_used = 0;
|
|
size_t user_cmd_size;
|
|
|
|
if (vha->hw_bypass) {
|
|
ret = -EAGAIN;
|
|
dev_info(vha->dev, "%s skip\n", __func__);
|
|
goto out_error;
|
|
}
|
|
|
|
img_pdump_printf("-- CNN_SETUP_BEGIN\n");
|
|
/* Wait for the previous kick to be accepted */
|
|
if (vha->low_latency != VHA_LL_DISABLED) {
|
|
/* Sanity wait for the kick bit to be deasserted */
|
|
IOPOLL64_PDUMP(0, 1000, 10, (uint64_t)VHA_CR_OS(CNN_CONTROL_START_EN),
|
|
VHA_CR_OS(CNN_CONTROL));
|
|
if (cmd->queued &&
|
|
vha->low_latency == VHA_LL_SW_KICK)
|
|
goto hw_kick;
|
|
}
|
|
|
|
if (vha->pendcmd[VHA_CNN_CMD].cmd != NULL &&
|
|
vha->low_latency == VHA_LL_DISABLED) {
|
|
dev_err(vha->dev, "%s: trying to submit cnn cmd when hw busy!\n",
|
|
__func__);
|
|
goto out_error;
|
|
}
|
|
|
|
user_cmd_size = sizeof(*user_cmd);
|
|
if (user_cmd->subseg_num > 0)
|
|
user_cmd_size += (user_cmd->subseg_num - 1) * sizeof(struct vha_subseg_info);
|
|
if (cmd->size != user_cmd_size) {
|
|
dev_err(vha->dev, "%s: command buffer wrong size: %zu/%zu",
|
|
__func__, cmd->size, sizeof(*user_cmd));
|
|
goto out_error;
|
|
}
|
|
|
|
if (!vha_dev_check_hw_capab(vha, user_cmd->expected_ip_capab)) {
|
|
ret = -ENODEV;
|
|
goto out_error;
|
|
}
|
|
|
|
/* at least CMD and (IN or OUT)*/
|
|
if (user_cmd->msg.num_inbufs < 2 ||
|
|
/* and maybe TMP and others */
|
|
user_cmd->msg.num_bufs > VHA_CORE_MAX_ALT_ADDRS) {
|
|
dev_err(vha->dev, "%s: wrong number of bufs: %u,%u\n",
|
|
__func__,
|
|
user_cmd->msg.num_inbufs, user_cmd->msg.num_bufs);
|
|
goto out_error;
|
|
}
|
|
|
|
if (user_cmd->onchipram_map_id != 0) {
|
|
onchip_map = idr_find(&session->onchip_maps, user_cmd->onchipram_map_id);
|
|
if (!onchip_map) {
|
|
dev_warn(vha->dev, "%s: idr_find failed\n", __func__);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* write buffer address to each register,
|
|
* and pdump LDB each of the the input buffers
|
|
*/
|
|
img_pdump_printf("-- Load inputs\n");
|
|
for (i = 0; i < user_cmd->msg.num_bufs; i++) {
|
|
uint32_t offset;
|
|
uint32_t size;
|
|
uint32_t reg;
|
|
|
|
/* buffer id == 0 means no buffer */
|
|
if (user_cmd->msg.data[i] == 0)
|
|
continue;
|
|
|
|
buf = vha_find_bufid(session, user_cmd->msg.data[i]);
|
|
if (buf == NULL) {
|
|
dev_err(vha->dev, "%s: invalid buffer id:%d\n",
|
|
__func__, user_cmd->msg.data[i]);
|
|
goto out_error;
|
|
}
|
|
if (buf->id == user_cmd->cmdbuf) {
|
|
/* cmdstream always starts at offset 0 */
|
|
if (user_cmd->subseg_info[cmd->subseg_current].cmdbuf_size)
|
|
size = cmd->stream_size = user_cmd->subseg_info[cmd->subseg_current].cmdbuf_size;
|
|
else
|
|
size = cmd->stream_size = buf->size;
|
|
|
|
offset = user_cmd->subseg_info[cmd->subseg_current].cmdbuf_offset;
|
|
if (size == 0) {
|
|
dev_err(vha->dev,
|
|
"%s: invalid cmdstream size\n",
|
|
__func__);
|
|
goto out_error;
|
|
}
|
|
reg = VHA_CR_OS(CNN_CMD_BASE_ADDRESS);
|
|
img_pdump_printf("-- Setup command stream\n");
|
|
} else {
|
|
/*
|
|
* offset can be specified for all
|
|
* buffers except cmdstream buf
|
|
*/
|
|
offset = user_cmd->bufoffsets[i-1];
|
|
size = user_cmd->bufsizes[i-1];
|
|
|
|
if (size + offset > buf->size) {
|
|
dev_err(vha->dev,
|
|
"%s: invalid size+offset: %x+%x > %zx\n",
|
|
__func__, size, offset, buf->size);
|
|
goto out_error;
|
|
}
|
|
|
|
reg = VHA_CR_OS(CNN_ALT_ADDRESS0)
|
|
+ user_cmd->regidx[i-1]
|
|
* (VHA_CR_OS(CNN_ALT_ADDRESS1)
|
|
- VHA_CR_OS(CNN_ALT_ADDRESS0));
|
|
/* record what alt address is in use */
|
|
alt_addrs_used |= 1 << user_cmd->regidx[i-1];
|
|
#if defined(HW_AX3)
|
|
/* Alternative addresses from 8 to 15 are
|
|
* located in different place */
|
|
if (user_cmd->regidx[i-1] >= 8) {
|
|
reg = VHA_CR_OS(CNN_ALT_ADDRESS8)
|
|
+ (user_cmd->regidx[i-1] - 8)
|
|
* (VHA_CR_OS(CNN_ALT_ADDRESS1)
|
|
- VHA_CR_OS(CNN_ALT_ADDRESS0));
|
|
}
|
|
alt_addrs_used |= buf->req_type <<
|
|
(VHA_CR_OS(CNN_ALT_ADDRESS_USED_ALT_ADDR0_BUF_TYPE_SHIFT) +
|
|
user_cmd->regidx[i-1]);
|
|
#elif defined(HW_AX2)
|
|
if (user_cmd->regidx[i-1] > 8) {
|
|
dev_err(vha->dev,
|
|
"%s: extended alternative addresses not supported!\n",
|
|
__func__);
|
|
goto out_error;
|
|
}
|
|
#endif
|
|
}
|
|
/* pdump the input buffers (not filled by the hw),
|
|
* try to cache buffers filled by SW,
|
|
* to avoid unnecessary LDBs */
|
|
if (i < user_cmd->msg.num_inbufs &&
|
|
!(buf->status == VHA_BUF_FILLED_BY_HW))
|
|
vha_pdump_ldb_buf(session, PDUMP_PRM,
|
|
buf, offset, size,
|
|
buf->status == VHA_BUF_FILLED_BY_SW);
|
|
|
|
vha_dump_digest(session, buf, cmd);
|
|
/*
|
|
* write to all of the index registers.
|
|
* in no-MMU mode, write phys address of a contig buffer.
|
|
* in MMU mode, write virt address of buffer.
|
|
* If onchip_map selected, use different virt address of buffer
|
|
*/
|
|
if (onchip_map != NULL && onchip_map->bufid == buf->id)
|
|
IOWRITE64_PDUMP(onchip_map->devvirt + offset, reg);
|
|
else
|
|
IOWRITE_PDUMP_BUFADDR(session, buf, offset, reg);
|
|
|
|
if (vha_buf_needs_flush(session, buf->id))
|
|
img_mem_sync_cpu_to_device(session->mem_ctx, buf->id);
|
|
}
|
|
|
|
if (!cnn_preloads_disable) {
|
|
/* Inform the hw what alt addresses are in use,
|
|
* so the command decoder can prefetch */
|
|
img_pdump_printf("-- Setup CNN prefetch register\n");
|
|
IOWRITE64_PDUMP(alt_addrs_used, VHA_CR_OS(CNN_ALT_ADDRESS_USED));
|
|
}
|
|
|
|
/* write the stream size only */
|
|
ret = 0;
|
|
if (vha->pendcmd[VHA_CNN_CMD].cmd) {
|
|
vha->queuedcmd[VHA_CNN_CMD].cmd = cmd;
|
|
cmd->queued = true;
|
|
vha->stats.cnn_kicks_queued++;
|
|
img_pdump_printf("-- CNN already kicked queueing!\n");
|
|
dev_dbg(vha->dev, "%s: -> kicked:%p queueing:%p\n",
|
|
__func__, vha->pendcmd[VHA_CNN_CMD].cmd, cmd);
|
|
if (vha->low_latency == VHA_LL_SW_KICK)
|
|
return ret;
|
|
}
|
|
hw_kick:
|
|
/* Change mmu context */
|
|
ret = vha_mmu_setup(cmd->session);
|
|
if (ret) {
|
|
dev_err(vha->dev,
|
|
"%s: Error during MMU setup!\n", __func__);
|
|
goto out_error;
|
|
}
|
|
/* Setup memory stuff */
|
|
vha_dev_mh_setup(vha, session->mmu_ctxs[VHA_MMU_REQ_MODEL_CTXID].hw_id, NULL);
|
|
|
|
/* Prepare debug buffer registers */
|
|
vha_dbg_prepare_hwbufs(session, cmd, NULL);
|
|
|
|
/* Setup cnn hw watchdog before kicking the hw */
|
|
{
|
|
uint64_t cycles, mode;
|
|
|
|
ret = vha_dev_hwwdt_calculate(vha, cmd, &cycles, &mode);
|
|
if (!ret)
|
|
vha_dev_hwwdt_setup(session->vha, cycles, mode);
|
|
else if (ret != -EIO) {
|
|
dev_err(vha->dev,
|
|
"%s: can't obtain HWWDT info!\n",
|
|
__func__);
|
|
goto out_error;
|
|
}
|
|
}
|
|
|
|
if (CMD_EXEC_ON_HW(cmd)) {
|
|
cmd->in_hw = true;
|
|
if (!cmd->queued)
|
|
vha->pendcmd[VHA_CNN_CMD].cmd = cmd;
|
|
}
|
|
#ifdef CONFIG_VHA_DUMMY_SIMULATE_HW_PROCESSING_TIME
|
|
/* Mark kick for dummy driver */
|
|
cmd->dummy_kicked = true;
|
|
#endif
|
|
|
|
/* Consider this cmd as kicked. */
|
|
vha->pri_q_counters[cmd->user_cmd.priority]--;
|
|
cmd->subseg_current++;
|
|
|
|
ret = 0;
|
|
/* Setup kick info */
|
|
val32 = vha_dev_kick_prepare(vha, cmd,
|
|
session->mmu_ctxs[VHA_MMU_REQ_MODEL_CTXID].hw_id);
|
|
|
|
img_pdump_printf("-- CNN_SETUP_END\n");
|
|
|
|
/* Remember the time cnn is kicked */
|
|
GETNSTIMEOFDAY(&cmd->hw_proc_start);
|
|
vha->stats.hw_proc_start = cmd->hw_proc_start;
|
|
/* Need to generate proper pdump */
|
|
if (cmd->queued &&
|
|
vha->low_latency == VHA_LL_SW_KICK) {
|
|
/* Do not write to pdump
|
|
* this needs to be done after irq POL*/
|
|
IOWRITE64(vha->reg_base, VHA_CR_OS(CNN_CONTROL), val32);
|
|
dev_dbg(vha->dev, "%s: CNN kick queued (%p)!\n",
|
|
__func__, cmd);
|
|
cmd->queued = false;
|
|
} else {
|
|
img_pdump_printf("-- CNN_KICK_BEGIN\n");
|
|
img_pdump_printf("-- CNN kick!\n");
|
|
IOWRITE64_PDUMP(val32, VHA_CR_OS(CNN_CONTROL));
|
|
dev_dbg(vha->dev, "%s: CNN kick %s (%p)!\n",
|
|
__func__, cmd->queued ? "queued" : "", cmd);
|
|
img_pdump_printf("-- CNN_KICK_END\n");
|
|
}
|
|
|
|
vha->stats.cnn_kicks++;
|
|
|
|
/* notify any observers of the submit event */
|
|
if (vha_observers.submitted)
|
|
vha_observers.submitted(vha->id, session->id, cmd->user_cmd.cmd_id,
|
|
(cmd->subseg_current == VHA_CMD_SUBSEG_NUM(cmd)),
|
|
cmd->user_cmd.priority);
|
|
|
|
out_error:
|
|
if (ret != 0) {
|
|
/* Consider this cmd as kicked for errors too. */
|
|
vha->pri_q_counters[cmd->user_cmd.priority]--;
|
|
cmd->subseg_current++;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* append a string to the pdump TXT file
|
|
* buffers:
|
|
* none
|
|
* data:
|
|
* string to be printed
|
|
*/
|
|
static int do_cmd_cnn_pdump_msg(const struct vha_cmd *cmd)
|
|
{
|
|
const struct vha_user_cmd *user_cmd = &cmd->user_cmd;
|
|
struct vha_session *session = cmd->session;
|
|
struct vha_dev* vha = session->vha;
|
|
int ret = 0;
|
|
|
|
if (user_cmd->num_inbufs != 0 || user_cmd->num_bufs != 0) {
|
|
dev_err(session->vha->dev, ">0 buffers in cmd is wrong\n");
|
|
ret = -EINVAL;
|
|
}
|
|
/* remember the pdump message may not be null terminated */
|
|
img_pdump_printf("%.*s\n", (int)cmd->size, (char *)user_cmd->data);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Simple procedure that generates watchdog interrupt
|
|
*/
|
|
void vha_cnn_start_calib(struct vha_dev *vha)
|
|
{
|
|
uint64_t clk;
|
|
uint32_t start;
|
|
|
|
/* Setup hw watchdog before kicking the hw */
|
|
vha_dev_hwwdt_setup(vha, vha->calibration_cycles, 0);
|
|
|
|
/* Disabling command decoder, so we can generate wdt interrupt,
|
|
* without providing any buffer address */
|
|
clk = IOREAD64(vha->reg_base, VHA_CR_CLK_CTRL0);
|
|
VHA_CR_CLEARBITS(clk, CLK_CTRL0, CNN_CMD);
|
|
IOWRITE64(vha->reg_base, VHA_CR_CLK_CTRL0, clk);
|
|
|
|
/* To be sure the cmd clock has switched off*/
|
|
udelay(100);
|
|
|
|
/* Enable MMU bypass */
|
|
IOWRITE64_PDUMP(VHA_CR_OS(MMU_CTRL_BYPASS_EN),
|
|
VHA_CR_OS(MMU_CTRL));
|
|
|
|
/* Set minimal command stream size */
|
|
start = (2048/32-1) << VHA_CR_OS(CNN_CONTROL_CMD_SIZE_MIN1_SHIFT);
|
|
start |= VHA_CR_OS(CNN_CONTROL_START_EN);
|
|
/* write the START bit */
|
|
IOWRITE64(vha->reg_base, VHA_CR_OS(CNN_CONTROL), start);
|
|
/* Remember the time cnn is kicked */
|
|
GETNSTIMEOFDAY(&vha->stats.hw_proc_start);
|
|
}
|
|
|
|
void vha_cnn_update_stats(struct vha_dev *vha)
|
|
{
|
|
vha->stats.cnn_last_proc_us =
|
|
vha->stats.last_proc_us;
|
|
vha->stats.cnn_total_proc_us +=
|
|
vha->stats.last_proc_us;
|
|
|
|
if (vha->stats.cnn_kicks) {
|
|
uint64_t avg = vha->stats.cnn_total_proc_us;
|
|
do_div(avg, vha->stats.cnn_kicks);
|
|
vha->stats.cnn_avg_proc_us = avg;
|
|
}
|
|
#if defined(HW_AX2)
|
|
vha->stats.cnn_last_cycles =
|
|
IOREAD64(vha->reg_base, VHA_CR_CNN_WDT_TIMER);
|
|
#elif defined(HW_AX3)
|
|
vha->stats.cnn_last_cycles =
|
|
IOREAD64(vha->reg_base, VHA_CR_OS(CNN_PERFORMANCE));
|
|
#endif
|
|
if (vha->stats.cnn_last_cycles && vha->freq_khz) {
|
|
uint64_t est_proc_us = 1000UL * vha->stats.cnn_last_cycles;
|
|
do_div(est_proc_us, vha->freq_khz);
|
|
vha->stats.cnn_last_est_proc_us = est_proc_us;
|
|
}
|
|
vha->stats.cnn_total_cycles += vha->stats.cnn_last_cycles;
|
|
if (vha->stats.cnn_kicks &&
|
|
vha->stats.cnn_total_cycles && vha->freq_khz) {
|
|
uint64_t avg = 1000UL * vha->stats.cnn_total_cycles;
|
|
do_div(avg, vha->stats.cnn_kicks);
|
|
do_div(avg, vha->freq_khz);
|
|
vha->stats.cnn_avg_est_proc_us = avg;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* a command has completed. sent notification to user
|
|
*/
|
|
void vha_cnn_cmd_completed(struct vha_cmd *cmd, int status)
|
|
{
|
|
struct vha_session *session = cmd->session;
|
|
struct vha_dev* vha = session->vha;
|
|
struct vha_rsp *rsp = NULL;
|
|
int i;
|
|
struct vha_user_cnn_submit_rsp * cnn_submit_rsp = NULL;
|
|
|
|
const struct vha_user_cmd *user_cmd = &cmd->user_cmd;
|
|
|
|
switch (user_cmd->cmd_type) {
|
|
case VHA_CMD_CNN_SUBMIT:
|
|
{
|
|
size_t mem_usage;
|
|
/* allocate sufficient space for the response */
|
|
size_t sz = sizeof(*rsp)
|
|
+ sizeof(struct vha_user_cnn_submit_rsp)
|
|
- sizeof(struct vha_user_rsp);
|
|
uint32_t status_mask;
|
|
uint32_t ready_mask;
|
|
uint32_t cmpl_val = VHA_CR_OS(VHA_EVENT_STATUS_VHA_CNN0_COMPLETE_EN);
|
|
#if defined(HW_AX2)
|
|
/* status change: wait for any status change:
|
|
* WDT, MMU_PF, ERROR, COMPLETE
|
|
*/
|
|
status_mask = 0xffffffff;
|
|
ready_mask = 0xffffffff;
|
|
#elif defined(HW_AX3)
|
|
/* status mask: wait for a status change: either ERROR, COMPLETE:
|
|
* note that, unlike the live driver, pdump will ignore the MMU_PF,
|
|
* which will have to be detected by the WDT
|
|
*/
|
|
status_mask = VHA_CR_OS(VHA_EVENT_STATUS_VHA_ERROR_CLRMSK)
|
|
| VHA_CR_OS(VHA_EVENT_STATUS_VHA_CNN0_COMPLETE_CLRMSK);
|
|
ready_mask = VHA_CR_OS(VHA_EVENT_STATUS_VHA_READY_CLRMSK);
|
|
|
|
/* Ignore PARITY when waiting for status change */
|
|
status_mask &= VHA_CR_OS(VHA_EVENT_STATUS_PARITY_CLRMSK);
|
|
#ifdef VHA_SCF
|
|
if (session->vha->hw_props.supported.parity &&
|
|
!session->vha->parity_disable) {
|
|
/* If complete bit is set then parity bit must be set as well ! */
|
|
cmpl_val |= VHA_CR_OS(VHA_EVENT_STATUS_PARITY_EN);
|
|
}
|
|
#else
|
|
/* Ignore PARITY, so that non-SCF pdump may work with SC CSIM */
|
|
ready_mask &= VHA_CR_OS(VHA_EVENT_STATUS_PARITY_CLRMSK);
|
|
#endif
|
|
#endif
|
|
rsp = kzalloc(sz, GFP_KERNEL);
|
|
if (rsp == NULL) {
|
|
session->oom = true;
|
|
return;
|
|
}
|
|
|
|
cnn_submit_rsp = (struct vha_user_cnn_submit_rsp*)&rsp->user_rsp;
|
|
rsp->size = sizeof(struct vha_user_cnn_submit_rsp);
|
|
|
|
if (session->vha->hw_bypass) {
|
|
session->vha->hw_bypass--;
|
|
break;
|
|
}
|
|
|
|
img_pdump_printf("-- CNN_WAIT_BEGIN\n");
|
|
/* pdump POL for status change
|
|
* count=cnn_pdump_poll_count, delay=1000cycles
|
|
*/
|
|
img_pdump_printf("-- Wait for any CNN status\n"
|
|
"POL :REG:%#x 0 %#x 3 %u 1000\n",
|
|
VHA_CR_OS(VHA_EVENT_STATUS),
|
|
status_mask,
|
|
cnn_pdump_poll_count);
|
|
|
|
/* quick pdump POL for the status complete flag only:
|
|
* count=1, delay=10cycles
|
|
*/
|
|
img_pdump_printf("-- Check for CNN_COMPLETE flag only\n"
|
|
"POL :REG:%#x %#x 0x%x 0 1 10\n",
|
|
VHA_CR_OS(VHA_EVENT_STATUS),
|
|
cmpl_val,
|
|
ready_mask);
|
|
#ifdef VHA_SCF
|
|
if (session->vha->hw_props.supported.parity &&
|
|
!session->vha->parity_disable) {
|
|
/* Check CNN_STATUS parity */
|
|
uint32_t cnn_status = VHA_CR_SETBITS_OS(CNN_STATUS,
|
|
STREAM_COUNT, 1);
|
|
cnn_status |= VHA_CR_SETBITS_OS(CNN_STATUS,
|
|
PARITY, 1);
|
|
img_pdump_printf("-- Check for CNN_STATUS parity\n"
|
|
"POL :REG:%#x %#x 0xffffffff 0 1 10\n",
|
|
VHA_CR_OS(CNN_STATUS), cnn_status);
|
|
}
|
|
#endif
|
|
/* quick pdump POL for AXI errors:
|
|
* count=1, delay=10cycles
|
|
*/
|
|
img_pdump_printf("-- Post check of AXI status\n"
|
|
"POL :REG:%#x 0 0xffffffff 0 1 10\n",
|
|
VHA_CR_ACE_STATUS);
|
|
|
|
/* We do clear interrupts in the irq handler,
|
|
* but this is not recorded into pdump because
|
|
* of the irq context, so do it here */
|
|
img_pdump_printf("-- Clear CNN events\n"
|
|
"WRW64 :REG:%#x %#x\n",
|
|
VHA_CR_OS(VHA_EVENT_CLEAR),
|
|
VHA_CR_OS(VHA_EVENT_CLEAR_VHA_CNN0_COMPLETE_EN) |
|
|
VHA_CNN_ERR_EVNTS);
|
|
|
|
/* Try to flush hw debug buffers first
|
|
* - this does pdump SAB when proper checkpoint is set */
|
|
vha_dbg_flush_hwbufs(session, 1, 0);
|
|
|
|
/* pdump SAB for each of the output buffers */
|
|
img_pdump_printf("-- Save outputs\n");
|
|
for (i = user_cmd->num_inbufs; i < user_cmd->num_bufs; i++) {
|
|
struct vha_buffer *buf;
|
|
struct vha_user_cnn_submit_cmd *msg =
|
|
container_of(user_cmd,
|
|
struct vha_user_cnn_submit_cmd,
|
|
msg);
|
|
uint32_t offset;
|
|
uint32_t size;
|
|
|
|
buf = vha_find_bufid(session, user_cmd->data[i]);
|
|
if (buf == NULL) {
|
|
dev_err(session->vha->dev,
|
|
"%s: invalid buffer id:%d\n",
|
|
__func__, user_cmd->data[i]);
|
|
continue;
|
|
}
|
|
if (buf->id == msg->cmdbuf) {
|
|
offset = 0;
|
|
size = buf->size;
|
|
} else {
|
|
offset = msg->bufoffsets[i-1];
|
|
size = msg->bufsizes[i-1];
|
|
}
|
|
|
|
vha_pdump_sab_buf(session, PDUMP_RES,
|
|
buf, offset, size);
|
|
|
|
/* Update status, do not signal fence yet,
|
|
* it's is done explicitly below, after cache invalidation */
|
|
vha_set_buf_status(session, buf->id, VHA_BUF_FILLED_BY_HW,
|
|
VHA_SYNC_NONE, false);
|
|
|
|
if (vha_buf_needs_inval(session, buf->id) && !status)
|
|
img_mem_sync_device_to_cpu(session->mem_ctx, buf->id);
|
|
|
|
#ifdef KERNEL_DMA_FENCE_SUPPORT
|
|
img_mem_signal_fence(session->mem_ctx, buf->id);
|
|
#endif
|
|
vha_dump_digest(session, buf, cmd);
|
|
}
|
|
|
|
if (session->vha->low_latency == VHA_LL_SW_KICK) {
|
|
struct vha_cmd *cmd =
|
|
session->vha->queuedcmd[VHA_CNN_CMD].cmd;
|
|
|
|
if (cmd && cmd->queued) {
|
|
/* Setup kick info */
|
|
uint64_t val = vha_dev_kick_prepare(session->vha, cmd,
|
|
session->mmu_ctxs[VHA_MMU_REQ_MODEL_CTXID].hw_id);
|
|
img_pdump_printf("-- CNN kick (queued)!\n");
|
|
img_pdump_printf("WRW64 :REG:%#x %#llx\n",
|
|
VHA_CR_OS(CNN_CONTROL), val);
|
|
}
|
|
}
|
|
img_pdump_printf("-- CNN_WAIT_END\n");
|
|
|
|
img_mem_get_usage(session->mem_ctx, NULL, &mem_usage);
|
|
/* send out an event when submit is complete */
|
|
if (vha_observers.completed)
|
|
vha_observers.completed(
|
|
session->vha->id,
|
|
session->id,
|
|
user_cmd->cmd_id,
|
|
status,
|
|
session->vha->stats.cnn_last_cycles,
|
|
mem_usage,
|
|
user_cmd->priority);
|
|
|
|
/* post some metrics about the hw to user space */
|
|
#ifdef MEM_USAGE_LAST_METRICS_ARE_AVAILABLE
|
|
cnn_submit_rsp->mem_usage = mem_usage;
|
|
#else
|
|
cnn_submit_rsp->mem_usage = ~0;
|
|
#endif
|
|
cnn_submit_rsp->last_proc_us = cmd->proc_us;
|
|
cnn_submit_rsp->hw_cycles = cmd->hw_cycles;
|
|
dev_dbg(session->vha->dev, "%s: %p, hw_cycles %llx\n", __func__,
|
|
cmd, session->vha->stats.cnn_last_cycles);
|
|
|
|
if (session->vha->stats.cnn_last_cycles > (uint32_t)~0)
|
|
dev_warn(session->vha->dev,
|
|
"%s: hw_cycles %llx exceeds 32bit limit\n",
|
|
__func__,
|
|
session->vha->stats.cnn_last_cycles);
|
|
break;
|
|
}
|
|
case VHA_CMD_CNN_PDUMP_MSG:
|
|
default:
|
|
/* allocate space for standard response */
|
|
rsp = kzalloc(sizeof(*rsp), GFP_KERNEL);
|
|
if (rsp == NULL) {
|
|
session->oom = true;
|
|
return;
|
|
}
|
|
rsp->size = sizeof(rsp->user_rsp);
|
|
break;
|
|
}
|
|
|
|
if (user_cmd->flags & VHA_CMDFLAG_NOTIFY) {
|
|
rsp->user_rsp.cmd_id = cmd->user_cmd.cmd_id;
|
|
rsp->user_rsp.err_no = session->vha->hw_bypass ? 0 : status;
|
|
|
|
cmd->rsp = rsp;
|
|
} else
|
|
kfree(rsp);
|
|
}
|
|
|
|
/*
|
|
* Perform a command, as requested by user.
|
|
* note: this function is called with vha_dev.lock == locked
|
|
*/
|
|
int vha_do_cnn_cmd(struct vha_cmd *cmd)
|
|
{
|
|
struct vha_session *session = cmd->session;
|
|
const struct vha_user_cmd *user_cmd = &cmd->user_cmd;
|
|
int status = -EINVAL;
|
|
|
|
dev_dbg(session->vha->dev,
|
|
"CNN command: id:%x type:%x nin:%x nbufs:%x\n",
|
|
user_cmd->cmd_id, user_cmd->cmd_type,
|
|
user_cmd->num_inbufs, user_cmd->num_bufs);
|
|
#if 0
|
|
print_hex_dump_debug("VHA CMD: ", DUMP_PREFIX_NONE, 4, 4,
|
|
user_cmd, ALIGN(cmd->size, 4), false);
|
|
#endif
|
|
|
|
switch (user_cmd->cmd_type) {
|
|
case VHA_CMD_CNN_SUBMIT:
|
|
status = do_cmd_cnn_submit(cmd);
|
|
#ifdef CONFIG_VHA_DUMMY_SIMULATE_HW_PROCESSING_TIME
|
|
if (cmd->dummy_kicked) {
|
|
struct vha_dev *vha = cmd->session->vha;
|
|
const struct vha_user_cnn_submit_cmd *cnn_user_cmd =
|
|
(struct vha_user_cnn_submit_cmd *)&cmd->user_cmd;
|
|
uint32_t estimated_cycles = cnn_user_cmd->estimated_cycles;
|
|
if (estimated_cycles == 0)
|
|
estimated_cycles = VHA_DUMMY_HW_PROCESSING_TIME_CYCLES;
|
|
cmd->dummy_exec_time = (estimated_cycles / (vha->freq_khz / 1000));
|
|
schedule_delayed_work(&vha->dummy_dwork,
|
|
usecs_to_jiffies(cmd->dummy_exec_time));
|
|
cmd->dummy_kicked = false;
|
|
}
|
|
#endif
|
|
break;
|
|
case VHA_CMD_CNN_PDUMP_MSG:
|
|
status = do_cmd_cnn_pdump_msg(cmd);
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Immediately send notification to user if not using hw at all
|
|
* or submitting failed.
|
|
*/
|
|
if (!CMD_EXEC_ON_HW(cmd) || status) {
|
|
vha_cnn_cmd_completed(cmd, status);
|
|
vha_cmd_notify(cmd);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void vha_cnn_dump_status(struct vha_dev *vha)
|
|
{
|
|
struct device *dev = vha->dev;
|
|
|
|
dev_err(dev, " CNN_STATUS:%llx ",
|
|
IOREAD64(vha->reg_base,
|
|
VHA_CR_OS(CNN_STATUS)));
|
|
#ifdef HW_AX2
|
|
dev_err(dev, " CNN_WDT_COMPAREMATCH:%llx ",
|
|
IOREAD64(vha->reg_base,
|
|
VHA_CR_CNN_WDT_COMPAREMATCH));
|
|
dev_err(dev, " CNN_WDT_TIMER:%llx ",
|
|
IOREAD64(vha->reg_base,
|
|
VHA_CR_CNN_WDT_TIMER));
|
|
#endif
|
|
dev_err(dev, " CNN_MEM_WDT_COMPAREMATCH:%llx ",
|
|
IOREAD64(vha->reg_base,
|
|
VHA_CR_CNN_MEM_WDT_COMPAREMATCH));
|
|
dev_err(dev, " CNN_MEM_WDT_TIMER:%llx ",
|
|
IOREAD64(vha->reg_base,
|
|
VHA_CR_CNN_MEM_WDT_TIMER));
|
|
dev_err(dev, " BIF_OUTSTANDING_READ:%llx\n",
|
|
IOREAD64(vha->reg_base,
|
|
VHA_CR_BIF_OUTSTANDING_READ));
|
|
}
|