mirror of
https://github.com/physwizz/a155-U-u1.git
synced 2025-09-26 19:04:54 +00:00
1341 lines
30 KiB
C
1341 lines
30 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2020 MediaTek Inc.
|
|
*/
|
|
#include "vpu_cfg.h"
|
|
#include "vpu_hw.h"
|
|
|
|
#include <linux/irq.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/timer.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/sched/clock.h>
|
|
|
|
#include "vpu_algo.h"
|
|
#include "vpu_cmd.h"
|
|
#include "vpu_mem.h"
|
|
#include "vpu_power.h"
|
|
#include "vpu_debug.h"
|
|
#include "vpu_cmn.h"
|
|
#include "vpu_dump.h"
|
|
#include "vpu_trace.h"
|
|
#include "vpu_met.h"
|
|
#include "vpu_tag.h"
|
|
#define CREATE_TRACE_POINTS
|
|
#include "vpu_events.h"
|
|
|
|
#define VPU_TS_INIT(a) \
|
|
struct timespec64 a##_s, a##_e
|
|
|
|
#define VPU_TS_START(a) \
|
|
ktime_get_ts64(&a##_s)
|
|
|
|
#define VPU_TS_END(a) \
|
|
ktime_get_ts64(&a##_e)
|
|
|
|
#define VPU_TS_NS(a) \
|
|
((uint64_t)(timespec64_to_ns(&a##_e) - timespec64_to_ns(&a##_s)))
|
|
|
|
#define VPU_TS_US(a) \
|
|
(VPU_TS_NS(a) / 1000)
|
|
|
|
static int wait_idle(struct vpu_device *vd, uint32_t latency, uint32_t retry);
|
|
static int vpu_set_ftrace(struct vpu_device *vd);
|
|
static void vpu_run(struct vpu_device *vd, int prio, uint32_t cmd)
|
|
{
|
|
struct vpu_register *r = vd_reg(vd);
|
|
|
|
vpu_reg_write(vd, xtensa_info01, cmd);
|
|
vpu_cmd_run(vd, prio, cmd);
|
|
vpu_reg_clr(vd, ctrl, r->stall);
|
|
}
|
|
|
|
static inline void __vpu_stall(struct vpu_device *vd)
|
|
{
|
|
struct vpu_register *r = vd_reg(vd);
|
|
|
|
vpu_reg_set(vd, ctrl, r->stall);
|
|
}
|
|
|
|
static inline void vpu_stall(struct vpu_device *vd)
|
|
{
|
|
if (xos_type(vd) == VPU_NON_XOS)
|
|
__vpu_stall(vd);
|
|
|
|
/* XOS doesn't need stall */
|
|
}
|
|
|
|
static void vpu_cmd(struct vpu_device *vd, int prio, uint32_t cmd)
|
|
{
|
|
vpu_cmd_debug("%s: vpu%d: cmd: 0x%x (%s)\n",
|
|
__func__, vd->id, cmd, vpu_debug_cmd_str(cmd));
|
|
vpu_run(vd, prio, cmd);
|
|
wmb(); /* make sure register committed */
|
|
vpu_reg_set(vd, ctl_xtensa_int, 1);
|
|
}
|
|
|
|
#define XOS_UNLOCKED 0
|
|
#define XOS_LOCKED 1
|
|
|
|
/**
|
|
* vpu_xos_lock() - lock vpu for control
|
|
*
|
|
* @vd: vpu device
|
|
*
|
|
* Returns:
|
|
* 0: lock succeed
|
|
* -ETIME: lock timeout
|
|
* -EAGAIN: crashed by other priority
|
|
*/
|
|
static int vpu_xos_lock(struct vpu_device *vd)
|
|
{
|
|
int s = XOS_LOCKED;
|
|
uint64_t t;
|
|
int ret = 0;
|
|
|
|
VPU_TS_INIT(xos);
|
|
VPU_TS_START(xos);
|
|
|
|
if (xos_type(vd) != VPU_XOS)
|
|
return 0;
|
|
|
|
while (1) {
|
|
s = atomic_cmpxchg(&vd->xos_state,
|
|
XOS_UNLOCKED, XOS_LOCKED);
|
|
|
|
if (s == XOS_UNLOCKED)
|
|
break;
|
|
|
|
VPU_TS_END(xos);
|
|
t = VPU_TS_US(xos);
|
|
|
|
if (t >= XOS_TIMEOUT_US) {
|
|
pr_info("%s: vpu%d: xos lock timeout: %lld us\n",
|
|
__func__, vd->id, t);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Other priorites had timeout, shutdown by vpu_excp */
|
|
if (s == XOS_UNLOCKED) {
|
|
if ((vd->state <= VS_DOWN) ||
|
|
(vd->state >= VS_REMOVING)) {
|
|
ret = -EAGAIN;
|
|
atomic_set(&vd->xos_state, XOS_UNLOCKED);
|
|
}
|
|
}
|
|
/* XOS lock timeout */
|
|
else if (s == XOS_LOCKED)
|
|
ret = -ETIME;
|
|
|
|
VPU_TS_END(xos);
|
|
vpu_cmd_debug("%s: vpu%d: %d, %lld us\n",
|
|
__func__, vd->id, ret, VPU_TS_US(xos));
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* vpu_xos_unlock - unlock xos when forced power down
|
|
* @vd: vpu device
|
|
*/
|
|
static void vpu_xos_unlock(struct vpu_device *vd)
|
|
{
|
|
if (xos_type(vd) != VPU_XOS)
|
|
return;
|
|
|
|
vpu_cmd_debug("%s: vpu%d\n", __func__, vd->id);
|
|
atomic_set(&vd->xos_state, XOS_UNLOCKED);
|
|
}
|
|
|
|
/**
|
|
* vpu_xos_wait_idle - wait for xos idle when forced power down
|
|
* @vd: vpu device
|
|
*
|
|
* Retruns -EBUSY, if device is still busy after
|
|
* (WAIT_XOS_LATENCY_US * WAIT_XOS_RETRY) us
|
|
*/
|
|
static int vpu_xos_wait_idle(struct vpu_device *vd)
|
|
{
|
|
struct vpu_config *cfg = vd_cfg(vd);
|
|
|
|
if (cfg->xos != VPU_XOS)
|
|
return 0;
|
|
|
|
return wait_idle(vd, cfg->wait_xos_latency_us, cfg->wait_xos_retry);
|
|
}
|
|
|
|
/**
|
|
* vpu_reg_lock() - lock vpu register for control
|
|
*
|
|
* @vd: vpu device
|
|
* @boot: called at boot sequence, Eq. vd->lock is acquired
|
|
* @flags: saved irq flags
|
|
*
|
|
* Returns:
|
|
* 0: lock succeed
|
|
* -ETIME: lock timeout
|
|
* -EAGAIN: crashed by other priority
|
|
*/
|
|
static inline
|
|
int vpu_reg_lock(struct vpu_device *vd, bool boot, unsigned long *flags)
|
|
{
|
|
int ret = 0;
|
|
struct vpu_sys_ops *sops = vd_sops(vd);
|
|
|
|
if (sops->xos_lock)
|
|
ret = sops->xos_lock(vd);
|
|
|
|
if (ret == 0) {
|
|
spin_lock_irqsave(&vd->reg_lock, (*flags));
|
|
} else if (ret == -ETIME) {
|
|
if (boot) {
|
|
vpu_excp_locked(vd, NULL,
|
|
"VPU Timeout",
|
|
"vpu%d: XOS lock timeout (boot)\n",
|
|
vd->id);
|
|
} else {
|
|
vpu_excp(vd, NULL,
|
|
"VPU Timeout",
|
|
"vpu%d: XOS lock timeout\n",
|
|
vd->id);
|
|
}
|
|
} else if (ret == -EAGAIN) {
|
|
pr_info("%s: vpu%d: failed caused by other thread\n",
|
|
__func__, vd->id);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static inline void vpu_reg_unlock(struct vpu_device *vd, unsigned long *flags)
|
|
{
|
|
spin_unlock_irqrestore(&vd->reg_lock, (*flags));
|
|
}
|
|
|
|
static int wait_idle(struct vpu_device *vd, uint32_t latency, uint32_t retry)
|
|
{
|
|
uint32_t st = 0;
|
|
uint32_t pwait = 0;
|
|
unsigned int count = 0;
|
|
struct vpu_register *r = vd_reg(vd);
|
|
|
|
do {
|
|
st = vpu_reg_read(vd, done_st);
|
|
count++;
|
|
pwait = !!(st & r->pwaitmode);
|
|
if (pwait)
|
|
return 0;
|
|
udelay(latency);
|
|
trace_vpu_wait(vd->id, st,
|
|
vpu_reg_read(vd, xtensa_info00),
|
|
vpu_reg_read(vd, xtensa_info25),
|
|
vpu_reg_read(vd, debug_info05));
|
|
} while (count < retry);
|
|
|
|
pr_info("%s: vpu%d: %d us: done_st: 0x%x, pwaitmode: %d, info00: 0x%x, info25: 0x%x\n",
|
|
__func__, vd->id, (latency * retry), st, pwait,
|
|
vpu_reg_read(vd, xtensa_info00),
|
|
vpu_reg_read(vd, xtensa_info25));
|
|
|
|
return -EBUSY;
|
|
}
|
|
|
|
static int wait_command(struct vpu_device *vd, int prio_s)
|
|
{
|
|
int ret = 0;
|
|
unsigned int prio = prio_s;
|
|
bool retry = true;
|
|
struct vpu_config *cfg = vd_cfg(vd);
|
|
|
|
start:
|
|
ret = wait_event_interruptible_timeout(
|
|
vd->cmd[prio].wait,
|
|
vd->cmd[prio].done,
|
|
msecs_to_jiffies(vd->cmd_timeout));
|
|
|
|
if (ret == -ERESTARTSYS) {
|
|
pr_info("%s: vpu%d: interrupt by signal: ret=%d\n",
|
|
__func__, vd->id, ret);
|
|
|
|
if (retry) {
|
|
pr_info("%s: vpu%d: try wait again\n",
|
|
__func__, vd->id);
|
|
retry = false;
|
|
goto start;
|
|
}
|
|
goto out;
|
|
}
|
|
|
|
/* Other priorites had timeout, shutdown by vpu_excp */
|
|
if (vd->state <= VS_DOWN) {
|
|
ret = -EAGAIN;
|
|
goto out;
|
|
}
|
|
|
|
/* command success */
|
|
if (ret) {
|
|
ret = 0;
|
|
goto out;
|
|
}
|
|
|
|
/* timeout handling */
|
|
ret = -ETIMEDOUT;
|
|
|
|
/* debug: check PWAITMODE */
|
|
wait_idle(vd, cfg->wait_cmd_latency_us, cfg->wait_cmd_retry);
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static void vpu_exit_dev_algo_general(struct vpu_device *vd,
|
|
struct vpu_algo_list *al)
|
|
{
|
|
struct __vpu_algo *alg;
|
|
struct list_head *ptr, *tmp;
|
|
|
|
if (!al)
|
|
return;
|
|
|
|
vpu_alg_debug("%s: %s\n", __func__, al->name);
|
|
list_for_each_safe(ptr, tmp, &al->a) {
|
|
alg = list_entry(ptr, struct __vpu_algo, list);
|
|
al->ops->put(alg);
|
|
}
|
|
}
|
|
|
|
static void *bin_header_legacy(int i)
|
|
{
|
|
struct vpu_img_hdr_legacy *hl;
|
|
unsigned long bin = (unsigned long)vpu_drv->bin_va;
|
|
|
|
hl = (struct vpu_img_hdr_legacy *)(bin +
|
|
vpu_drv->vp->cfg->bin_ofs_header);
|
|
|
|
return &hl[i];
|
|
}
|
|
|
|
static struct vpu_algo_info *bin_alg_info_legacy(void *h, int j)
|
|
{
|
|
struct vpu_img_hdr_legacy *hl = h;
|
|
|
|
return &hl->algo_infos[j];
|
|
}
|
|
|
|
static int bin_alg_info_cnt_legacy(void *h)
|
|
{
|
|
struct vpu_img_hdr_legacy *hl = h;
|
|
|
|
return hl->algo_info_count;
|
|
}
|
|
|
|
static void *bin_header_preload(int index)
|
|
{
|
|
int i;
|
|
uint64_t ptr = (unsigned long)vpu_drv->bin_va;
|
|
struct vpu_img_hdr_preload *hp;
|
|
|
|
ptr += vpu_drv->bin_head_ofs;
|
|
hp = (void *)ptr;
|
|
|
|
for (i = 0; i < index; i++) {
|
|
ptr += hp->header_size;
|
|
hp = (void *)ptr;
|
|
}
|
|
|
|
return (void *)hp;
|
|
}
|
|
|
|
static struct vpu_algo_info *bin_algo_info_preload(
|
|
void *h, int j)
|
|
{
|
|
struct vpu_img_hdr_preload *hp = h;
|
|
struct vpu_algo_info *algo_info;
|
|
|
|
algo_info = (void *)((unsigned long)hp + hp->alg_info);
|
|
return &algo_info[j];
|
|
}
|
|
|
|
static int bin_alg_info_cnt_preload(void *h)
|
|
{
|
|
struct vpu_img_hdr_preload *hp = h;
|
|
|
|
return hp->algo_info_count;
|
|
}
|
|
|
|
static uint32_t bin_pre_info_preload(void *h)
|
|
{
|
|
struct vpu_img_hdr_preload *hp = h;
|
|
|
|
return hp->pre_info;
|
|
}
|
|
|
|
static int bin_pre_info_cnt_preload(void *h)
|
|
{
|
|
struct vpu_img_hdr_preload *hp = h;
|
|
|
|
return hp->pre_info_count;
|
|
}
|
|
|
|
static int preload_iova_check(struct vpu_iova *i)
|
|
{
|
|
unsigned int bin_end = vpu_drv->bin_pa + vpu_drv->bin_size;
|
|
|
|
#define is_align_4k(x) ((x & 0xFFF))
|
|
#define is_align_64k(x) ((x & 0xFFFF))
|
|
if (is_align_64k(i->size) && i->addr) {
|
|
pr_info("%s: size(0x%x) not 64k aligned\n", __func__, i->size);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (is_align_4k(i->addr) ||
|
|
is_align_4k(i->size) ||
|
|
is_align_4k(i->bin)) {
|
|
pr_info("%s: addr/size not 4k aligned\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ((i->bin + i->size) > bin_end) {
|
|
pr_info("%s: wrong size\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
#undef is_align_4k
|
|
#undef is_align_64k
|
|
|
|
return 0;
|
|
}
|
|
|
|
static dma_addr_t preload_iova_alloc(
|
|
struct vpu_device *vd, struct vpu_iova *vi,
|
|
uint32_t addr, uint32_t size, uint32_t bin)
|
|
{
|
|
dma_addr_t mva;
|
|
|
|
vi->addr = addr;
|
|
vi->size = size;
|
|
vi->bin = bin;
|
|
|
|
if (preload_iova_check(vi))
|
|
return 0;
|
|
|
|
mva = vd_mops(vd)->alloc(vd->dev, vi);
|
|
|
|
if (!mva)
|
|
pr_info("%s: vpu%d: iova allcation failed\n", __func__, vd->id);
|
|
|
|
vpu_drv_debug(
|
|
"%s: vpu%d: addr:0x%X, size: 0x%X, bin: 0x%X, mva: 0x%lx\n",
|
|
__func__, vd->id, vi->addr, vi->size, vi->bin,
|
|
(unsigned long) mva);
|
|
|
|
return mva;
|
|
}
|
|
|
|
#define PRELOAD_IRAM 0xFFFFFFFF
|
|
|
|
static uint32_t vpu_init_dev_algo_preload_entry(
|
|
struct vpu_device *vd,
|
|
struct vpu_algo_list *al, struct vpu_pre_info *info,
|
|
uint32_t bin)
|
|
{
|
|
struct __vpu_algo *alg;
|
|
struct vpu_iova dummy_iova;
|
|
struct vpu_iova *vi;
|
|
uint64_t mva;
|
|
uint32_t addr;
|
|
uint32_t size = __ALIGN_KERNEL(info->file_sz, 0x1000);
|
|
|
|
alg = al->ops->get(al, info->name, NULL);
|
|
|
|
/* algo is already existed in the list */
|
|
if (alg) {
|
|
al->ops->put(alg);
|
|
if (info->pAddr == PRELOAD_IRAM) {
|
|
addr = 0; /* dynamic alloc iova */
|
|
vi = &alg->iram;
|
|
mva = preload_iova_alloc(vd, vi, addr,
|
|
size, info->off);
|
|
alg->a.iram_mva = mva;
|
|
goto added;
|
|
}
|
|
|
|
/* other segments had been merged into EXE_SEG */
|
|
size = 0;
|
|
goto out;
|
|
}
|
|
|
|
/* add new algo to the list */
|
|
addr = info->start_addr & 0xFFFF0000; /* static alloc iova */
|
|
if (info->info)
|
|
size = info->info; // already aligned at packing stage
|
|
|
|
alg = vpu_alg_alloc(al);
|
|
if (!alg)
|
|
goto out;
|
|
|
|
al->cnt++;
|
|
strncpy(alg->a.name, info->name, (ALGO_NAMELEN - 1));
|
|
|
|
if (info->flag & 0x1 /* EXE_SEG */) {
|
|
vi = &alg->prog;
|
|
alg->a.entry_off = info->pAddr - addr;
|
|
} else {
|
|
size = 0;
|
|
vi = &dummy_iova;
|
|
pr_info("%s: vpu%d: unexpected segment: flags: %x\n",
|
|
__func__, vd->id, info->flag);
|
|
}
|
|
|
|
mva = preload_iova_alloc(vd, vi, addr, size, info->off);
|
|
alg->a.mva = mva;
|
|
|
|
if (!alg->a.mva) {
|
|
vpu_alg_free(alg);
|
|
goto out;
|
|
}
|
|
|
|
alg->builtin = true;
|
|
list_add_tail(&alg->list, &al->a);
|
|
|
|
added:
|
|
vpu_drv_debug("%s: vpu%d(%xh): %s <%s>: off: %x, mva: %llx, size: %x, addr: %x\n",
|
|
__func__, vd->id, info->vpu_core, info->name,
|
|
(info->pAddr == PRELOAD_IRAM) ? "IRAM" : "PROG",
|
|
info->off, mva, size, addr);
|
|
out:
|
|
return bin + size;
|
|
}
|
|
|
|
static int vpu_init_dev_algo_preload(
|
|
struct vpu_device *vd, struct vpu_algo_list *al)
|
|
{
|
|
int i, j, ret = 0;
|
|
uint32_t offset;
|
|
struct vpu_pre_info *info = NULL;
|
|
struct vpu_bin_ops *bops = vd_bops(vd);
|
|
|
|
if (!bops->pre_info || !bops->pre_info_cnt)
|
|
return 0;
|
|
|
|
vpu_algo_list_init(vd, al, &vpu_prelaod_aops, "Preload");
|
|
offset = vpu_drv->bin_preload_ofs;
|
|
|
|
for (i = 0; i < VPU_NUMS_IMAGE_HEADER; i++) {
|
|
void *h = bops->header(i);
|
|
int cnt;
|
|
|
|
info = (void *)((unsigned long)h + bops->pre_info(h));
|
|
cnt = bops->pre_info_cnt(h);
|
|
|
|
for (j = 0; j < cnt; j++, info++) {
|
|
if (!((info->vpu_core & 0xF) & (1 << vd->id)))
|
|
continue;
|
|
|
|
offset = vpu_init_dev_algo_preload_entry(
|
|
vd, al, info, offset);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int vpu_init_dev_algo_normal(
|
|
struct vpu_device *vd, struct vpu_algo_list *al)
|
|
{
|
|
int i, j;
|
|
int ret = 0;
|
|
unsigned int mva;
|
|
struct vpu_algo_info *algo_info;
|
|
struct vpu_bin_ops *bops = vd_bops(vd);
|
|
|
|
vpu_algo_list_init(vd, al, &vpu_normal_aops, "Normal");
|
|
|
|
/* for each algo in the image header, add them to device's algo list */
|
|
for (i = 0; i < VPU_NUMS_IMAGE_HEADER; i++) {
|
|
void *h = bops->header(i);
|
|
int cnt = bops->alg_info_cnt(h);
|
|
|
|
for (j = 0; j < cnt; j++) {
|
|
struct __vpu_algo *alg;
|
|
|
|
algo_info = bops->alg_info(h, j);
|
|
mva = algo_info->offset - vpu_drv->iova_algo.bin +
|
|
vpu_drv->mva_algo;
|
|
|
|
/* skips, if the core mask mismatch */
|
|
if (!((algo_info->vpu_core & 0xF) & (1 << vd->id)))
|
|
continue;
|
|
|
|
vpu_drv_debug("%s: vpu%d(%xh): %s: off: %x, mva: %x, len: %x\n",
|
|
__func__,
|
|
vd->id,
|
|
algo_info->vpu_core,
|
|
algo_info->name,
|
|
algo_info->offset,
|
|
mva,
|
|
algo_info->length);
|
|
|
|
alg = vpu_alg_alloc(al);
|
|
if (!alg) {
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
strncpy(alg->a.name,
|
|
algo_info->name, (ALGO_NAMELEN - 1));
|
|
alg->a.mva = mva;
|
|
alg->a.len = algo_info->length;
|
|
alg->builtin = true;
|
|
|
|
list_add_tail(&alg->list, &al->a);
|
|
al->cnt++;
|
|
}
|
|
vpu_drv_debug("%s: vpu%d, total algo count: %d\n",
|
|
__func__, vd->id, al->cnt);
|
|
}
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
/* called by vpu_probe() */
|
|
int vpu_init_dev_algo(struct platform_device *pdev, struct vpu_device *vd)
|
|
{
|
|
int ret;
|
|
|
|
ret = vpu_init_dev_algo_normal(vd, &vd->aln);
|
|
if (ret)
|
|
goto out;
|
|
|
|
if (bin_type(vd) == VPU_IMG_PRELOAD)
|
|
ret = vpu_init_dev_algo_preload(vd, &vd->alp);
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
/* called by vpu_remove() */
|
|
void vpu_exit_dev_algo(struct platform_device *pdev, struct vpu_device *vd)
|
|
{
|
|
vpu_exit_dev_algo_general(vd, &vd->aln);
|
|
|
|
if (bin_type(vd) == VPU_IMG_PRELOAD)
|
|
vpu_exit_dev_algo_general(vd, &vd->alp);
|
|
}
|
|
|
|
static wait_queue_head_t *vpu_isr_check_cmd_xos(struct vpu_device *vd,
|
|
uint32_t inf, uint32_t prio)
|
|
{
|
|
wait_queue_head_t *wake = NULL;
|
|
uint32_t ret = 0;
|
|
|
|
if ((DS_PREEMPT_DONE | DS_ALG_DONE | DS_ALG_RDY |
|
|
DS_DSP_RDY | DS_DBG_RDY | DS_FTRACE_RDY) & inf) {
|
|
ret = vpu_reg_read(vd, xtensa_info00);
|
|
ret = (ret >> (prio * 8)) & 0xFF;
|
|
vpu_cmd_done(vd, prio, ret, vpu_reg_read(vd, xtensa_info02));
|
|
wake = vpu_cmd_waitq(vd, prio);
|
|
}
|
|
|
|
#define DS(a) \
|
|
((inf & DS_##a) ? ":"#a : "")
|
|
vpu_cmd_debug(
|
|
"%s: vpu%d: wake: %d, val: %d, prio: %d, %xh%s%s%s%s%s%s%s%s\n",
|
|
__func__, vd->id, (wake) ? 1 : 0, ret, prio, inf,
|
|
DS(DSP_RDY), /* boot-up done */
|
|
DS(DBG_RDY), /* set-debug done */
|
|
DS(ALG_RDY), /* do-loader done */
|
|
DS(ALG_DONE), /* d2d done */
|
|
DS(ALG_GOT), /* get-algo done */
|
|
DS(PREEMPT_RDY), /* context switch done */
|
|
DS(PREEMPT_DONE), /* d2d-ext done */
|
|
DS(FTRACE_RDY) /* set-ftrace done */);
|
|
#undef DS
|
|
|
|
vd->dev_state = inf; /* for debug */
|
|
return wake;
|
|
}
|
|
|
|
static bool vpu_isr_check_unlock_xos(uint32_t inf)
|
|
{
|
|
return (DS_PREEMPT_RDY & inf);
|
|
}
|
|
|
|
static wait_queue_head_t *vpu_isr_check_cmd(struct vpu_device *vd,
|
|
uint32_t inf, uint32_t prio)
|
|
{
|
|
wait_queue_head_t *wake = NULL;
|
|
uint32_t ret = 0;
|
|
|
|
ret = vpu_reg_read(vd, xtensa_info00);
|
|
|
|
if (ret != VPU_STATE_BUSY) {
|
|
ret = vpu_reg_read(vd, xtensa_info00);
|
|
vpu_cmd_done(vd, prio, ret, vpu_reg_read(vd, xtensa_info02));
|
|
wake = vpu_cmd_waitq(vd, prio);
|
|
}
|
|
|
|
vpu_cmd_debug(
|
|
"%s: vpu%d: wake: %d, val: %d, prio: %d, %xh\n",
|
|
__func__, vd->id, (wake) ? 1 : 0, ret, prio, inf);
|
|
|
|
vd->dev_state = inf; /* for debug */
|
|
return wake;
|
|
}
|
|
|
|
static bool vpu_isr_check_unlock(uint32_t inf)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static irqreturn_t vpu_isr(int irq, void *dev_id)
|
|
{
|
|
struct vpu_device *vd = (struct vpu_device *)dev_id;
|
|
struct vpu_sys_ops *sops = vd_sops(vd);
|
|
uint32_t ret, req_cmd, prio, inf, val;
|
|
wait_queue_head_t *waitq = NULL;
|
|
bool unlock = false;
|
|
|
|
/* INFO17: device state */
|
|
ret = vpu_reg_read(vd, xtensa_info17);
|
|
vpu_reg_write(vd, xtensa_info17, 0);
|
|
req_cmd = ret & 0xFFFF; /* request cmd */
|
|
inf = ret & 0x00FF0000; /* dev state */
|
|
prio = (ret >> 28) & 0xF; /* priroity */
|
|
|
|
vpu_cmd_debug("%s: vpu%d: INFO17: %0xh, req: %xh, inf: %xh, prio: %xh\n",
|
|
__func__, vd->id, ret, req_cmd, inf, prio);
|
|
|
|
if (req_cmd == VPU_REQ_DO_CHECK_STATE) {
|
|
waitq = sops->isr_check_cmd(vd, inf, prio);
|
|
unlock = sops->isr_check_unlock(inf);
|
|
}
|
|
|
|
/* MET */
|
|
vpu_met_isr(vd);
|
|
|
|
/* clear int */
|
|
val = vpu_reg_read(vd, xtensa_int);
|
|
|
|
if (val) {
|
|
vpu_cmd_debug("%s: vpu%d: xtensa_int = (%d)\n",
|
|
__func__, vd->id, val);
|
|
}
|
|
vpu_reg_write(vd, xtensa_int, 1);
|
|
|
|
if (unlock && sops->xos_unlock)
|
|
sops->xos_unlock(vd);
|
|
|
|
if (waitq && (waitqueue_active(waitq))) /* cmd wait */
|
|
wake_up_interruptible(waitq);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
// vd->cmd_lock, should be acquired before calling this function
|
|
static int vpu_execute_d2d(struct vpu_device *vd, struct vpu_request *req)
|
|
{
|
|
int ret;
|
|
int result = 0;
|
|
uint32_t cmd = 0;
|
|
unsigned long flags;
|
|
uint64_t start_t;
|
|
|
|
VPU_TS_INIT(d2d);
|
|
VPU_TS_START(d2d);
|
|
start_t = sched_clock();
|
|
req->busy_time = 0;
|
|
req->algo_ret = 0;
|
|
|
|
vpu_cmd_debug("%s: vpu%d: prio: %d, %s: bw: %d, buf_cnt: %x, sett: %llx +%x\n",
|
|
__func__, vd->id, req->prio,
|
|
vpu_cmd_alg_name(vd, req->prio),
|
|
req->power_param.bw, req->buffer_count,
|
|
req->sett_ptr, req->sett_length);
|
|
|
|
ret = vpu_cmd_buf_set(vd, req->prio, req->buffers,
|
|
sizeof(struct vpu_buffer) * req->buffer_count);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = vpu_reg_lock(vd, false, &flags);
|
|
if (ret)
|
|
goto out;
|
|
|
|
/* D2D_EXT: preload algorithm */
|
|
if (VPU_REQ_FLAG_TST(req, ALG_PRELOAD)) {
|
|
struct __vpu_algo *pre;
|
|
|
|
vd->state = VS_CMD_D2D_EXT;
|
|
cmd = VPU_CMD_DO_D2D_EXT;
|
|
pre = vpu_cmd_alg(vd, req->prio);
|
|
vpu_reg_write(vd, xtensa_info11, req->prio);
|
|
vpu_reg_write(vd, xtensa_info16, pre->a.mva + pre->a.entry_off);
|
|
vpu_reg_write(vd, xtensa_info19, pre->a.iram_mva);
|
|
} else { /* D2D: normal algorithm */
|
|
vpu_reg_write(vd, xtensa_info11, 0);
|
|
vd->state = VS_CMD_D2D;
|
|
cmd = VPU_CMD_DO_D2D;
|
|
}
|
|
|
|
vpu_reg_write(vd, xtensa_info12, req->buffer_count);
|
|
vpu_reg_write(vd, xtensa_info13, vpu_cmd_buf_iova(vd, req->prio));
|
|
vpu_reg_write(vd, xtensa_info14, req->sett_ptr);
|
|
vpu_reg_write(vd, xtensa_info15, req->sett_length);
|
|
vpu_trace_begin("vpu_%d|hw_processing_request(%s),prio:%d",
|
|
vd->id, req->algo, req->prio);
|
|
vpu_cmd(vd, req->prio, cmd);
|
|
vpu_reg_unlock(vd, &flags);
|
|
ret = wait_command(vd, req->prio);
|
|
vpu_stall(vd);
|
|
vpu_trace_end("vpu_%d|hw_processing_request(%s),prio:%d",
|
|
vd->id, req->algo, req->prio);
|
|
|
|
result = vpu_cmd_result(vd, req->prio);
|
|
req->status = result ? VPU_REQ_STATUS_FAILURE : VPU_REQ_STATUS_SUCCESS;
|
|
|
|
if ((ret == -ERESTARTSYS) || (ret == -EAGAIN))
|
|
goto err_cmd;
|
|
|
|
/* Error handling */
|
|
if (ret) {
|
|
req->status = VPU_REQ_STATUS_TIMEOUT;
|
|
vpu_excp(vd, req, "VPU Timeout",
|
|
"vpu%d: request (%s) timeout, priority: %d, algo: %s\n",
|
|
vd->id,
|
|
(cmd == VPU_CMD_DO_D2D_EXT) ? "D2D_EXT" : "D2D",
|
|
req->prio, req->algo);
|
|
goto err_cmd;
|
|
}
|
|
|
|
err_cmd:
|
|
req->algo_ret = vpu_cmd_alg_ret(vd, req->prio);
|
|
|
|
out:
|
|
VPU_TS_END(d2d);
|
|
req->busy_time = VPU_TS_NS(d2d);
|
|
trace_vpu_cmd(vd->id, req->prio, req->algo, cmd,
|
|
vpu_cmd_boost(vd, req->prio), start_t, ret,
|
|
req->algo_ret, result);
|
|
vpu_cmd_debug("%s: vpu%d: prio: %d, %s: time: %llu ns, ret: %d, alg_ret: %d, result: %d\n",
|
|
__func__, vd->id, req->prio,
|
|
req->algo, req->busy_time, ret, req->algo_ret, result);
|
|
return ret;
|
|
}
|
|
|
|
// reference: vpu_boot_up
|
|
// vd->cmd_lock, should be acquired before calling this function
|
|
int vpu_dev_boot(struct vpu_device *vd)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (vd->state <= VS_DOWN) {
|
|
pr_info("%s: unexpected state: %d\n", __func__, vd->state);
|
|
ret = -ENODEV;
|
|
goto err;
|
|
}
|
|
|
|
if (vd->state != VS_UP && vd->state != VS_BOOT)
|
|
return ret;
|
|
|
|
vd->state = VS_BOOT;
|
|
vd->dev_state = 0;
|
|
|
|
vpu_trace_begin("vpu_%d|%s", vd->id, __func__);
|
|
|
|
/* VPU boot up sequence */
|
|
ret = vpu_dev_boot_sequence(vd);
|
|
if (ret) {
|
|
pr_info("%s: vpu_dev_boot_sequence: %d\n", __func__, ret);
|
|
goto err;
|
|
}
|
|
|
|
/* VPU set debug log */
|
|
ret = vpu_dev_set_debug(vd);
|
|
if (ret) {
|
|
pr_info("%s: vpu_dev_set_debug: %d\n", __func__, ret);
|
|
goto err;
|
|
}
|
|
|
|
/* MET: ftrace setup */
|
|
ret = vpu_set_ftrace(vd);
|
|
if (ret) {
|
|
pr_info("%s: vpu_met_set_ftrace: %d\n", __func__, ret);
|
|
goto err;
|
|
}
|
|
|
|
/* MET: pm setup */
|
|
vpu_met_pm_get(vd);
|
|
|
|
vd->state = VS_IDLE;
|
|
|
|
err:
|
|
vpu_trace_end("vpu_%d|%s", vd->id, __func__);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* vpu_execute - execute a vpu request
|
|
* @vd: vpu device
|
|
* @req: vpu request
|
|
*
|
|
* returns: 0: success, otherwise: fail
|
|
*/
|
|
int vpu_execute(struct vpu_device *vd, struct vpu_request *req)
|
|
{
|
|
int ret = 0;
|
|
int ret_pwr = 0;
|
|
int boost = 0;
|
|
uint64_t flags;
|
|
struct vpu_algo_list *al;
|
|
enum vpu_state state;
|
|
|
|
vpu_cmd_lock(vd, req->prio);
|
|
/* Backup/Restore request flags for elevate to preload algo */
|
|
flags = req->flags;
|
|
/* Bootup VPU */
|
|
mutex_lock_nested(&vd->lock, VPU_MUTEX_DEV);
|
|
if (!vd_is_available(vd)) {
|
|
ret_pwr = -ENODEV;
|
|
goto nodev;
|
|
}
|
|
boost = vpu_cmd_boost_set(vd, req->prio, req->power_param.boost_value);
|
|
ret_pwr = vpu_pwr_get_locked(vd, boost);
|
|
if (!ret_pwr)
|
|
ret = vpu_dev_boot(vd);
|
|
/* Backup original state */
|
|
state = vd->state;
|
|
nodev:
|
|
mutex_unlock(&vd->lock);
|
|
|
|
if (ret_pwr || (ret == -ETIMEDOUT))
|
|
goto err_remove;
|
|
if (ret)
|
|
goto err_boot;
|
|
|
|
/* Load Algorithm (DO_LOADER) */
|
|
if (!req->prio &&
|
|
!VPU_REQ_FLAG_TST(req, ALG_PRELOAD) &&
|
|
!VPU_REQ_FLAG_TST(req, ALG_RELOAD) &&
|
|
vd->cmd[0].alg &&
|
|
(vd->cmd[0].alg->al == &vd->aln) &&
|
|
(!strcmp(vd->cmd[0].alg->a.name, req->algo)))
|
|
goto send_req;
|
|
|
|
al = VPU_REQ_FLAG_TST(req, ALG_PRELOAD) ? (&vd->alp) : (&vd->aln);
|
|
|
|
retry:
|
|
ret = al->ops->load(al, req->algo, NULL, req->prio);
|
|
|
|
/* Elevate to preloaded algo, if normal algo was not found */
|
|
if ((ret == -ENOENT) && (al == &vd->aln)) {
|
|
VPU_REQ_FLAG_SET(req, ALG_PRELOAD);
|
|
al = &vd->alp;
|
|
goto retry;
|
|
}
|
|
|
|
if (ret) {
|
|
pr_info("%s: vpu%d: \"%s\" was not found\n",
|
|
__func__, al->vd->id, req->algo);
|
|
goto err_alg;
|
|
}
|
|
|
|
send_req:
|
|
/* Send Request (DO_D2D) */
|
|
ret = vpu_execute_d2d(vd, req);
|
|
|
|
err_alg:
|
|
if (ret == -ETIMEDOUT)
|
|
goto err_remove;
|
|
|
|
err_boot:
|
|
mutex_lock_nested(&vd->lock, VPU_MUTEX_DEV);
|
|
/* Skip, if other priorities had timeout and powered down */
|
|
if (vd->state != VS_DOWN) {
|
|
/* There's no more active commands => idle */
|
|
if (!atomic_read(&vd->cmd_active))
|
|
vd->state = VS_IDLE;
|
|
/* Otherwise, restore to original state */
|
|
else
|
|
vd->state = state;
|
|
boost = vpu_cmd_boost_put(vd, req->prio);
|
|
vpu_pwr_put_locked(vd, boost);
|
|
}
|
|
mutex_unlock(&vd->lock);
|
|
|
|
err_remove:
|
|
req->flags = flags;
|
|
vpu_cmd_unlock(vd, req->prio);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* vpu_preempt - Run vpu request of a preloaded algorithm
|
|
* @vd: vpu device
|
|
* @req: vpu request
|
|
*
|
|
* returns: 0: success, otherwise: fail
|
|
*/
|
|
int vpu_preempt(struct vpu_device *vd, struct vpu_request *req)
|
|
{
|
|
int ret = 0;
|
|
int prio;
|
|
|
|
prio = atomic_inc_return(&vd->cmd_prio);
|
|
if (prio >= vd->cmd_prio_max) {
|
|
ret = -EPERM;
|
|
goto out;
|
|
}
|
|
req->prio = prio;
|
|
ret = vpu_execute(vd, req);
|
|
out:
|
|
atomic_dec(&vd->cmd_prio);
|
|
return ret;
|
|
}
|
|
|
|
/* driver hw init */
|
|
int vpu_init_drv_hw(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* device hw init */
|
|
int vpu_init_dev_hw(struct platform_device *pdev, struct vpu_device *vd)
|
|
{
|
|
int ret = 0;
|
|
struct vpu_sys_ops *sops = vd_sops(vd);
|
|
|
|
ret = request_irq(vd->irq_num, sops->isr,
|
|
irq_get_trigger_type(vd->irq_num),
|
|
vd->name, vd);
|
|
|
|
if (ret) {
|
|
pr_info("%s: %s: fail to request irq: %d\n",
|
|
__func__, vd->name, ret);
|
|
goto out;
|
|
}
|
|
|
|
mutex_init(&vd->lock);
|
|
spin_lock_init(&vd->reg_lock);
|
|
|
|
if (sops->xos_unlock)
|
|
sops->xos_unlock(vd);
|
|
|
|
ret = vpu_cmd_init(vd);
|
|
if (ret) {
|
|
pr_info("%s: %s: fail to init commands: %d\n",
|
|
__func__, vd->name, ret);
|
|
vpu_cmd_exit(vd);
|
|
free_irq(vd->irq_num, vd);
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
/* driver hw exit function */
|
|
int vpu_exit_drv_hw(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* device hw exit function */
|
|
int vpu_exit_dev_hw(struct platform_device *pdev, struct vpu_device *vd)
|
|
{
|
|
/* stall vpu to prevent iommu translation faults after free iovas */
|
|
__vpu_stall(vd);
|
|
vpu_cmd_exit(vd);
|
|
free_irq(vd->irq_num, vd);
|
|
return 0;
|
|
}
|
|
|
|
int vpu_dev_boot_sequence(struct vpu_device *vd)
|
|
{
|
|
uint64_t start_t;
|
|
int ret;
|
|
struct vpu_register *r = vd_reg(vd);
|
|
struct vpu_config *cfg = vd_cfg(vd);
|
|
|
|
start_t = sched_clock();
|
|
/* No need to take vd->reg_lock,
|
|
* 1. boot-up may take a while.
|
|
* 2. Only one process can do boot sequence, since it'sprotected
|
|
* by device lock vd->lock,
|
|
*/
|
|
vpu_trace_begin("vpu_%d|%s", vd->id, __func__);
|
|
vpu_reg_write(vd, xtensa_altresetvec, vd->iova_reset.addr);
|
|
|
|
vpu_reg_set(vd, ctrl, r->p_debug_enable | r->state_vector_select);
|
|
vpu_reg_set(vd, ctrl, r->pbclk_enable);
|
|
vpu_reg_clr(vd, ctrl, r->prid);
|
|
vpu_reg_set(vd, ctrl, (vd->id << 1) & r->prid);
|
|
vpu_reg_set(vd, sw_rst, r->ocdhaltonreset);
|
|
vpu_reg_clr(vd, sw_rst, r->ocdhaltonreset);
|
|
vpu_reg_set(vd, sw_rst, r->apu_b_rst | r->apu_d_rst);
|
|
vpu_reg_clr(vd, sw_rst, r->apu_b_rst | r->apu_d_rst);
|
|
/* pif gated disable, to prevent unknown propagate to bus */
|
|
vpu_reg_clr(vd, ctrl, r->pif_gated);
|
|
/* axi control */
|
|
vpu_reg_set(vd, default0, r->awuser | r->aruser);
|
|
vpu_reg_set(vd, default1, r->aruser_idma | r->awuser_idma);
|
|
/* default set pre-ultra instead of ultra */
|
|
vpu_reg_set(vd, default0, r->qos_swap);
|
|
/* jtag enable */
|
|
if (vd->jtag_enabled) {
|
|
vpu_reg_set(vd, cg_clr, r->jtag_cg_clr);
|
|
vpu_reg_set(vd, default2, r->dbg_en);
|
|
}
|
|
|
|
/* mbox interrupt mask */
|
|
if (vd_cfg(vd)->dmp_reg_cnt_mbox)
|
|
vpu_reg_set(vd, mbox_inbox_mask, 0xffffffff);
|
|
|
|
/* set log buffer */
|
|
vpu_reg_write(vd, xtensa_info19, vd->mva_iram);
|
|
vpu_reg_write(vd, xtensa_info21,
|
|
vd->iova_work.m.pa + cfg->log_ofs);
|
|
vpu_reg_write(vd, xtensa_info22, vd->wb_log_size);
|
|
|
|
vpu_run(vd, 0, 0);
|
|
|
|
pr_info("%s: vpu%d: ALTRESETVEC: 0x%x\n", __func__,
|
|
vd->id, vpu_reg_read(vd, xtensa_altresetvec));
|
|
|
|
ret = wait_command(vd, 0);
|
|
vpu_stall(vd);
|
|
if (ret == -ETIMEDOUT)
|
|
goto err_timeout;
|
|
if ((ret == -ERESTARTSYS) || (ret == -EAGAIN))
|
|
goto out;
|
|
|
|
ret = vpu_cmd_result(vd, 0);
|
|
|
|
err_timeout:
|
|
if (ret) {
|
|
vpu_excp_locked(vd, NULL, "VPU Timeout",
|
|
"vpu%d: boot-up timeout\n", vd->id);
|
|
}
|
|
|
|
out:
|
|
trace_vpu_cmd(vd->id, 0, "", 0, atomic_read(&vd->pw_boost),
|
|
start_t, ret, 0, 0);
|
|
vpu_trace_end("vpu_%d|%s", vd->id, __func__);
|
|
return ret;
|
|
}
|
|
|
|
// vd->cmd_lock, should be acquired before calling this function
|
|
int vpu_dev_set_debug(struct vpu_device *vd)
|
|
{
|
|
int ret;
|
|
struct timespec64 now;
|
|
unsigned int device_version = 0x0;
|
|
unsigned long flags;
|
|
uint64_t start_t;
|
|
|
|
vpu_cmd_debug("%s: vpu%d\n", __func__, vd->id);
|
|
start_t = sched_clock();
|
|
ktime_get_ts64(&now);
|
|
|
|
/* SET_DEBUG */
|
|
ret = vpu_reg_lock(vd, true, &flags);
|
|
if (ret)
|
|
goto out;
|
|
|
|
vpu_reg_write(vd, xtensa_info01, VPU_CMD_SET_DEBUG);
|
|
vpu_reg_write(vd, xtensa_info23,
|
|
now.tv_sec * 1000000 + now.tv_nsec / 1000);
|
|
|
|
vpu_reg_write(vd, xtensa_info29, HOST_VERSION);
|
|
vpu_trace_begin("vpu_%d|%s", vd->id, __func__);
|
|
vpu_cmd(vd, 0, VPU_CMD_SET_DEBUG);
|
|
vpu_reg_unlock(vd, &flags);
|
|
|
|
vpu_cmd_debug("%s: vpu%d: iram: %x, log: %x, time: %x\n",
|
|
__func__, vd->id,
|
|
vpu_reg_read(vd, xtensa_info19),
|
|
vpu_reg_read(vd, xtensa_info21),
|
|
vpu_reg_read(vd, xtensa_info23));
|
|
vpu_cmd_debug("%s: vpu%d: timestamp: %.2lu:%.2lu:%.2lu:%.6lu\n",
|
|
__func__, vd->id,
|
|
(now.tv_sec / 3600) % (24),
|
|
(now.tv_sec / 60) % (60),
|
|
now.tv_sec % 60,
|
|
now.tv_nsec / 1000);
|
|
|
|
/* 3. wait until done */
|
|
ret = wait_command(vd, 0);
|
|
vpu_stall(vd);
|
|
vpu_trace_end("vpu_%d|%s", vd->id, __func__);
|
|
|
|
if ((ret == -ERESTARTSYS) || (ret == -EAGAIN))
|
|
goto out;
|
|
|
|
if (ret)
|
|
goto err;
|
|
|
|
/*3-additional. check vpu device/host version is matched or not*/
|
|
device_version = vpu_reg_read(vd, xtensa_info20);
|
|
|
|
if ((int)device_version < (int)HOST_VERSION) {
|
|
pr_info("%s: vpu%d: incompatible ftrace version: vd: %x, host: %x\n",
|
|
__func__, vd->id,
|
|
vpu_reg_read(vd, xtensa_info20),
|
|
vpu_reg_read(vd, xtensa_info29));
|
|
vd->ftrace_avail = false;
|
|
} else {
|
|
vd->ftrace_avail = true;
|
|
}
|
|
|
|
err:
|
|
if (ret) {
|
|
vpu_excp_locked(vd, NULL, "VPU Timeout",
|
|
"vpu%d: set debug (SET_DEBUG) timeout\n",
|
|
vd->id);
|
|
goto out;
|
|
}
|
|
|
|
/* 4. check the result */
|
|
ret = vpu_cmd_result(vd, 0);
|
|
|
|
out:
|
|
trace_vpu_cmd(vd->id, 0, "", VPU_CMD_SET_DEBUG,
|
|
atomic_read(&vd->pw_boost), start_t, ret, 0, 0);
|
|
if (ret)
|
|
pr_info("%s: vpu%d: fail to set debug: %d\n",
|
|
__func__, vd->id, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
// vd->cmd_lock, should be acquired before calling this function
|
|
int vpu_hw_alg_init(struct vpu_algo_list *al, struct __vpu_algo *algo)
|
|
{
|
|
struct vpu_device *vd = al->vd;
|
|
unsigned long flags;
|
|
uint64_t start_t;
|
|
int ret;
|
|
|
|
start_t = sched_clock();
|
|
vpu_cmd_debug("%s: vpu%d: %s: mva/length (0x%lx/0x%x)\n",
|
|
__func__, vd->id, algo->a.name,
|
|
(unsigned long)algo->a.mva, algo->a.len);
|
|
|
|
/* DO_LOADER */
|
|
ret = vpu_reg_lock(vd, false, &flags);
|
|
if (ret)
|
|
goto out;
|
|
|
|
vd->state = VS_CMD_ALG;
|
|
vpu_reg_write(vd, xtensa_info12, algo->a.mva);
|
|
vpu_reg_write(vd, xtensa_info13, algo->a.len);
|
|
vpu_reg_write(vd, xtensa_info15, 0);
|
|
vpu_reg_write(vd, xtensa_info16, 0);
|
|
vpu_trace_begin("vpu_%d|%s", vd->id, __func__);
|
|
vpu_cmd(vd, 0, VPU_CMD_DO_LOADER);
|
|
vpu_reg_unlock(vd, &flags);
|
|
|
|
ret = wait_command(vd, 0);
|
|
vpu_stall(vd);
|
|
|
|
vpu_trace_end("vpu_%d|%s", vd->id, __func__);
|
|
|
|
if ((ret == -ERESTARTSYS) || (ret == -EAGAIN))
|
|
goto out;
|
|
if (ret) {
|
|
vpu_excp(vd, NULL, "VPU Timeout",
|
|
"vpu%d: load algo (DO_LOADER) timeout, algo: %s\n",
|
|
vd->id, algo->a.name);
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
trace_vpu_cmd(vd->id, 0, algo->a.name, VPU_CMD_DO_LOADER,
|
|
vpu_cmd_boost(vd, 0), start_t, ret, 0, 0);
|
|
vpu_cmd_debug("%s: vpu%d: %s: %d\n",
|
|
__func__, vd->id, algo->a.name, ret);
|
|
return ret;
|
|
}
|
|
|
|
static int vpu_set_ftrace(struct vpu_device *vd)
|
|
{
|
|
int ret = 0;
|
|
unsigned long flags;
|
|
uint64_t start_t;
|
|
|
|
start_t = sched_clock();
|
|
/* SET_FTRACE */
|
|
ret = vpu_reg_lock(vd, true, &flags);
|
|
if (ret)
|
|
goto out;
|
|
|
|
vpu_reg_write(vd, xtensa_info05, (vpu_drv->met) ? 1 : 0);
|
|
/* set vpu internal log level */
|
|
vpu_reg_write(vd, xtensa_info06, vpu_drv->ilog);
|
|
/* clear info18 */
|
|
vpu_reg_write(vd, xtensa_info18, 0);
|
|
vpu_cmd(vd, 0, VPU_CMD_SET_FTRACE_LOG);
|
|
vpu_reg_unlock(vd, &flags);
|
|
ret = wait_command(vd, 0);
|
|
vpu_stall(vd);
|
|
|
|
if ((ret == -ERESTARTSYS) || (ret == -EAGAIN))
|
|
goto out;
|
|
|
|
/* Error handling */
|
|
if (ret) {
|
|
vpu_excp_locked(vd, NULL, "VPU Timeout",
|
|
"vpu%d: request (SET_FTRACE) timeout\n", vd->id);
|
|
goto out;
|
|
}
|
|
ret = vpu_cmd_result(vd, 0);
|
|
|
|
out:
|
|
trace_vpu_cmd(vd->id, 0, "", VPU_CMD_SET_FTRACE_LOG,
|
|
atomic_read(&vd->pw_boost), start_t, ret, 0, 0);
|
|
if (ret)
|
|
pr_info("%s: vpu%d: fail to set ftrace: %d\n",
|
|
__func__, vd->id, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
struct vpu_sys_ops vpu_sops_mt68xx = {
|
|
.xos_lock = vpu_xos_lock,
|
|
.xos_unlock = vpu_xos_unlock,
|
|
.xos_wait_idle = vpu_xos_wait_idle,
|
|
.isr = vpu_isr,
|
|
.isr_check_cmd = vpu_isr_check_cmd_xos,
|
|
.isr_check_unlock = vpu_isr_check_unlock_xos,
|
|
};
|
|
|
|
struct vpu_sys_ops vpu_sops_mt67xx = {
|
|
.xos_lock = NULL,
|
|
.xos_unlock = NULL,
|
|
.xos_wait_idle = NULL,
|
|
.isr = vpu_isr,
|
|
.isr_check_cmd = vpu_isr_check_cmd,
|
|
.isr_check_unlock = vpu_isr_check_unlock,
|
|
};
|
|
|
|
struct vpu_bin_ops vpu_bops_legacy = {
|
|
.header = bin_header_legacy,
|
|
.alg_info = bin_alg_info_legacy,
|
|
.alg_info_cnt = bin_alg_info_cnt_legacy,
|
|
.pre_info = NULL,
|
|
.pre_info_cnt = NULL,
|
|
};
|
|
|
|
struct vpu_bin_ops vpu_bops_preload = {
|
|
.header = bin_header_preload,
|
|
.alg_info = bin_algo_info_preload,
|
|
.alg_info_cnt = bin_alg_info_cnt_preload,
|
|
.pre_info = bin_pre_info_preload,
|
|
.pre_info_cnt = bin_pre_info_cnt_preload,
|
|
};
|
|
|
|
|