1267 lines
26 KiB
C
1267 lines
26 KiB
C
|
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
|
/*
|
||
|
|
* Copyright (c) 2020 MediaTek Inc.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include <linux/mutex.h>
|
||
|
|
#include <linux/sched.h>
|
||
|
|
#include <linux/errno.h>
|
||
|
|
#include <linux/slab.h>
|
||
|
|
#include <linux/delay.h>
|
||
|
|
#include <linux/bitmap.h>
|
||
|
|
#include <linux/bitops.h>
|
||
|
|
#include <linux/module.h>
|
||
|
|
#include <linux/debugfs.h>
|
||
|
|
#include <linux/list.h>
|
||
|
|
#include <linux/kthread.h>
|
||
|
|
#include <linux/uaccess.h>
|
||
|
|
#include <linux/atomic.h>
|
||
|
|
#include <linux/vmalloc.h>
|
||
|
|
#ifdef CONFIG_PM_SLEEP
|
||
|
|
#include <linux/device.h>
|
||
|
|
#include <linux/pm_wakeup.h>
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#include "mdw_cmn.h"
|
||
|
|
#include "mdw_rsc.h"
|
||
|
|
#include "mdw_sched.h"
|
||
|
|
#include "mdw_cmd.h"
|
||
|
|
|
||
|
|
|
||
|
|
struct mdw_rsc_mgr {
|
||
|
|
unsigned long dev_sup_bmp[BITS_TO_LONGS(APUSYS_DEVICE_MAX)];
|
||
|
|
unsigned long dev_avl_bmp[BITS_TO_LONGS(APUSYS_DEVICE_MAX)];
|
||
|
|
unsigned long cmd_avl_bmp[BITS_TO_LONGS(APUSYS_DEVICE_MAX)];
|
||
|
|
struct mdw_rsc_tab **tabs;
|
||
|
|
|
||
|
|
uint32_t preempt_policy;
|
||
|
|
|
||
|
|
struct list_head r_list;
|
||
|
|
struct mutex mtx;
|
||
|
|
};
|
||
|
|
|
||
|
|
static atomic_t sthd_group = ATOMIC_INIT(0);
|
||
|
|
static struct mdw_rsc_mgr rsc_mgr;
|
||
|
|
|
||
|
|
static char * const rsc_dev_name[] = {
|
||
|
|
"none",
|
||
|
|
"sample",
|
||
|
|
"mdla",
|
||
|
|
"vpu",
|
||
|
|
"edma",
|
||
|
|
};
|
||
|
|
|
||
|
|
#ifdef CONFIG_PM_SLEEP
|
||
|
|
static struct wakeup_source *mdw_rsc_ws;
|
||
|
|
static uint32_t ws_cnt;
|
||
|
|
static struct mutex ws_mtx;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
//----------------------------------------
|
||
|
|
static void mdw_rsc_ws_init(void)
|
||
|
|
{
|
||
|
|
#if defined CONFIG_PM_SLEEP
|
||
|
|
char ws_name[16];
|
||
|
|
|
||
|
|
if (snprintf(ws_name, sizeof(ws_name)-1, "apusys_secure") < 0) {
|
||
|
|
mdw_drv_err("init rsc wakeup source fail\n");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
ws_cnt = 0;
|
||
|
|
mutex_init(&ws_mtx);
|
||
|
|
mdw_rsc_ws = wakeup_source_register(NULL, (const char *)ws_name);
|
||
|
|
if (!mdw_rsc_ws)
|
||
|
|
mdw_drv_err("register ws lock fail!\n");
|
||
|
|
#else
|
||
|
|
mdw_drv_debug("not support pm wakelock\n");
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
static void mdw_rsc_ws_destroy(void)
|
||
|
|
{
|
||
|
|
#if defined CONFIG_PM_SLEEP
|
||
|
|
ws_cnt = 0;
|
||
|
|
wakeup_source_unregister(mdw_rsc_ws);
|
||
|
|
#else
|
||
|
|
mdw_drv_debug("not support pm wakelock\n");
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
void mdw_rsc_ws_lock(void)
|
||
|
|
{
|
||
|
|
#ifdef CONFIG_PM_SLEEP
|
||
|
|
mutex_lock(&ws_mtx);
|
||
|
|
if (mdw_rsc_ws && !ws_cnt) {
|
||
|
|
mdw_flw_debug("lock\n");
|
||
|
|
__pm_stay_awake(mdw_rsc_ws);
|
||
|
|
}
|
||
|
|
ws_cnt++;
|
||
|
|
mutex_unlock(&ws_mtx);
|
||
|
|
#else
|
||
|
|
mdw_flw_debug("not support pm wakelock\n");
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
void mdw_rsc_ws_unlock(void)
|
||
|
|
{
|
||
|
|
#ifdef CONFIG_PM_SLEEP
|
||
|
|
mutex_lock(&ws_mtx);
|
||
|
|
ws_cnt--;
|
||
|
|
if (mdw_rsc_ws && !ws_cnt) {
|
||
|
|
mdw_flw_debug("unlock\n");
|
||
|
|
__pm_relax(mdw_rsc_ws);
|
||
|
|
}
|
||
|
|
mutex_unlock(&ws_mtx);
|
||
|
|
#else
|
||
|
|
mdw_flw_debug("not support pm wakelock\n");
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mdw_rsc_get_name(int type, char *name)
|
||
|
|
{
|
||
|
|
int name_idx = 0;
|
||
|
|
|
||
|
|
name_idx = type % APUSYS_DEVICE_RT;
|
||
|
|
if (name_idx >= sizeof(rsc_dev_name)/sizeof(char *)) {
|
||
|
|
mdw_drv_err("unknown dev(%d/%d) name\n", type, name_idx);
|
||
|
|
return -ENODEV;
|
||
|
|
}
|
||
|
|
|
||
|
|
mdw_flw_debug("rsc dev name size = %lu/%s\n",
|
||
|
|
(uint32_t)sizeof(rsc_dev_name)/sizeof(char *),
|
||
|
|
rsc_dev_name[name_idx]);
|
||
|
|
|
||
|
|
if (type >= APUSYS_DEVICE_RT) {
|
||
|
|
if (snprintf(name, 32, "%s_rt", rsc_dev_name[name_idx]) < 0)
|
||
|
|
return -EINVAL;
|
||
|
|
} else {
|
||
|
|
if (snprintf(name, 32, "%s", rsc_dev_name[name_idx]) < 0)
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void mdw_rsc_delete_tab(struct mdw_rsc_tab *tab)
|
||
|
|
{
|
||
|
|
mdw_queue_destroy(&tab->q);
|
||
|
|
vfree(tab);
|
||
|
|
}
|
||
|
|
|
||
|
|
static struct mdw_rsc_tab *mdw_rsc_add_tab(int type)
|
||
|
|
{
|
||
|
|
struct mdw_rsc_tab *tab = NULL;
|
||
|
|
char name[32];
|
||
|
|
|
||
|
|
tab = vzalloc(sizeof(struct mdw_rsc_tab));
|
||
|
|
if (!tab)
|
||
|
|
return NULL;
|
||
|
|
|
||
|
|
/* assign rsc mgr value */
|
||
|
|
rsc_mgr.tabs[type] = tab;
|
||
|
|
tab->type = type;
|
||
|
|
|
||
|
|
/* init device list */
|
||
|
|
INIT_LIST_HEAD(&tab->list);
|
||
|
|
|
||
|
|
/* init mutex */
|
||
|
|
mutex_init(&tab->mtx);
|
||
|
|
|
||
|
|
bitmap_set(rsc_mgr.dev_sup_bmp, type, 1);
|
||
|
|
|
||
|
|
if (mdw_rsc_get_name(type, name))
|
||
|
|
return NULL;
|
||
|
|
|
||
|
|
strncpy(tab->name, name, sizeof(tab->name)-1);
|
||
|
|
|
||
|
|
/* dbgfs */
|
||
|
|
if (!mdw_dbg_device)
|
||
|
|
goto init_queue;
|
||
|
|
|
||
|
|
tab->dbg_dir = debugfs_create_dir(name,
|
||
|
|
mdw_dbg_device);
|
||
|
|
|
||
|
|
/* create debugfs */
|
||
|
|
debugfs_create_u32("queue", 0444, tab->dbg_dir, &tab->q.normal_task_num);
|
||
|
|
|
||
|
|
init_queue:
|
||
|
|
/* init queue */
|
||
|
|
mdw_queue_init(&tab->q);
|
||
|
|
|
||
|
|
return tab;
|
||
|
|
}
|
||
|
|
|
||
|
|
struct mdw_rsc_tab *mdw_rsc_get_tab(int type)
|
||
|
|
{
|
||
|
|
if (type >= MDW_RSC_MAX_NUM || type < 0)
|
||
|
|
return NULL;
|
||
|
|
|
||
|
|
return rsc_mgr.tabs[type];
|
||
|
|
}
|
||
|
|
|
||
|
|
int mdw_rsc_avl_dev_num(int type)
|
||
|
|
{
|
||
|
|
struct mdw_rsc_tab *tab = NULL;
|
||
|
|
|
||
|
|
tab = mdw_rsc_get_tab(type);
|
||
|
|
if (!tab)
|
||
|
|
return -ENODEV;
|
||
|
|
|
||
|
|
return tab->avl_num;
|
||
|
|
}
|
||
|
|
|
||
|
|
struct mdw_queue *mdw_rsc_get_queue(int type)
|
||
|
|
{
|
||
|
|
struct mdw_rsc_tab *tab = NULL;
|
||
|
|
|
||
|
|
tab = mdw_rsc_get_tab(type);
|
||
|
|
if (!tab)
|
||
|
|
return NULL;
|
||
|
|
|
||
|
|
return &tab->q;
|
||
|
|
}
|
||
|
|
|
||
|
|
uint64_t mdw_rsc_get_avl_bmp(void)
|
||
|
|
{
|
||
|
|
unsigned long bmp[BITS_TO_LONGS(APUSYS_DEVICE_MAX)];
|
||
|
|
uint64_t b = 0;
|
||
|
|
|
||
|
|
memset(&bmp, 0, sizeof(unsigned long) *
|
||
|
|
BITS_TO_LONGS(APUSYS_DEVICE_MAX));
|
||
|
|
|
||
|
|
bitmap_and(bmp, rsc_mgr.cmd_avl_bmp,
|
||
|
|
rsc_mgr.dev_avl_bmp, APUSYS_DEVICE_MAX);
|
||
|
|
bitmap_to_arr32((uint32_t *)&b, bmp, APUSYS_DEVICE_MAX);
|
||
|
|
mdw_flw_debug("bmp(0x%llx)\n", b);
|
||
|
|
|
||
|
|
return b;
|
||
|
|
}
|
||
|
|
|
||
|
|
void mdw_rsc_update_avl_bmp(int type)
|
||
|
|
{
|
||
|
|
struct mdw_rsc_tab *tab;
|
||
|
|
struct mdw_queue *mq;
|
||
|
|
uint64_t cb = 0, db = 0;
|
||
|
|
|
||
|
|
mutex_lock(&rsc_mgr.mtx);
|
||
|
|
|
||
|
|
/* update dev bitmap */
|
||
|
|
tab = mdw_rsc_get_tab(type);
|
||
|
|
if (!tab)
|
||
|
|
goto out;
|
||
|
|
|
||
|
|
if (mdw_rsc_avl_dev_num(type))
|
||
|
|
bitmap_set(rsc_mgr.dev_avl_bmp, type, 1);
|
||
|
|
else
|
||
|
|
bitmap_clear(rsc_mgr.dev_avl_bmp, type, 1);
|
||
|
|
|
||
|
|
/* update cmd bitmap */
|
||
|
|
mq = mdw_rsc_get_queue(type);
|
||
|
|
if (!mq)
|
||
|
|
goto out;
|
||
|
|
|
||
|
|
if (mdw_queue_len(type, true) || mdw_queue_len(type, false))
|
||
|
|
bitmap_set(rsc_mgr.cmd_avl_bmp, type, 1);
|
||
|
|
else
|
||
|
|
bitmap_clear(rsc_mgr.cmd_avl_bmp, type, 1);
|
||
|
|
|
||
|
|
bitmap_to_arr32((uint32_t *)&cb,
|
||
|
|
rsc_mgr.cmd_avl_bmp, APUSYS_DEVICE_MAX);
|
||
|
|
bitmap_to_arr32((uint32_t *)&db,
|
||
|
|
rsc_mgr.dev_avl_bmp, APUSYS_DEVICE_MAX);
|
||
|
|
|
||
|
|
mdw_flw_debug("bmp: dev(0x%llx) cmd(0x%llx)\n", db, cb);
|
||
|
|
|
||
|
|
out:
|
||
|
|
mutex_unlock(&rsc_mgr.mtx);
|
||
|
|
}
|
||
|
|
|
||
|
|
#define LINEBAR \
|
||
|
|
"|------------------------------------------"\
|
||
|
|
"--------------------------------------------|\n"
|
||
|
|
#define S_LINEBAR \
|
||
|
|
"|------------------------------------------"\
|
||
|
|
"-------------------|\n"
|
||
|
|
static void mdw_rsc_dump_tab(struct seq_file *s, struct mdw_rsc_tab *tab)
|
||
|
|
{
|
||
|
|
int j = 0;
|
||
|
|
struct mdw_dev_info *d = NULL;
|
||
|
|
struct mdw_apu_sc *sc = NULL;
|
||
|
|
|
||
|
|
mutex_lock(&tab->mtx);
|
||
|
|
for (j = 0; j < tab->dev_num; j++) {
|
||
|
|
d = tab->array[j];
|
||
|
|
if (!d)
|
||
|
|
continue;
|
||
|
|
|
||
|
|
mutex_lock(&d->mtx);
|
||
|
|
sc = (struct mdw_apu_sc *)d->sc;
|
||
|
|
if (j == 0) {
|
||
|
|
/* print tab info at dev #0 */
|
||
|
|
mdw_con_info(s, "|%-14s(%7s) |%-9s#%-4d>%46s|\n",
|
||
|
|
" dev name",
|
||
|
|
tab->name,
|
||
|
|
" <device ",
|
||
|
|
j,
|
||
|
|
"");
|
||
|
|
mdw_con_info(s, "|%-14s(%7d) |%-18s= %-41d|\n",
|
||
|
|
" dev type ",
|
||
|
|
tab->type,
|
||
|
|
" device idx",
|
||
|
|
d->idx);
|
||
|
|
mdw_con_info(s, "|%-14s(%7d) |%-18s= 0x%-39llx|\n",
|
||
|
|
" core num ",
|
||
|
|
tab->dev_num,
|
||
|
|
" cmd id",
|
||
|
|
sc == NULL ? 0 : sc->parent->kid);
|
||
|
|
mdw_con_info(s, "|%-14s(%7u) |%-18s= 0x%-39d|\n",
|
||
|
|
" available",
|
||
|
|
tab->avl_num,
|
||
|
|
" subcmd idx",
|
||
|
|
sc == NULL ? 0 : sc->idx);
|
||
|
|
mdw_con_info(s, "|%-14s(%3d/%3d) |%-18s= %-41d|\n",
|
||
|
|
" cmd queue",
|
||
|
|
mdw_queue_len(tab->type, false),
|
||
|
|
mdw_queue_len(tab->type, true),
|
||
|
|
" state ",
|
||
|
|
d->state);
|
||
|
|
} else {
|
||
|
|
mdw_con_info(s, "|%-24s|%-9s#%-4d>%-46s|\n",
|
||
|
|
"",
|
||
|
|
" <device ",
|
||
|
|
j,
|
||
|
|
"");
|
||
|
|
mdw_con_info(s, "|%-24s|%-18s= %-41d|\n",
|
||
|
|
"",
|
||
|
|
" device idx",
|
||
|
|
d->idx);
|
||
|
|
mdw_con_info(s, "|%-24s|%-18s= 0x%-39llx|\n",
|
||
|
|
"",
|
||
|
|
" cmd id",
|
||
|
|
sc == NULL ? 0 : sc->parent->kid);
|
||
|
|
mdw_con_info(s, "|%-24s|%-18s= 0x%-39d|\n",
|
||
|
|
"",
|
||
|
|
" subcmd idx",
|
||
|
|
sc == NULL ? 0 : sc->idx);
|
||
|
|
mdw_con_info(s, "|%-24s|%-18s= %-41d|\n",
|
||
|
|
"",
|
||
|
|
" state ",
|
||
|
|
d->state);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (j >= tab->dev_num-1) {
|
||
|
|
mdw_con_info(s, LINEBAR);
|
||
|
|
} else {
|
||
|
|
mdw_con_info(s, "|%-24s%s",
|
||
|
|
"",
|
||
|
|
S_LINEBAR);
|
||
|
|
}
|
||
|
|
mutex_unlock(&d->mtx);
|
||
|
|
}
|
||
|
|
mutex_unlock(&tab->mtx);
|
||
|
|
}
|
||
|
|
|
||
|
|
void mdw_rsc_dump(struct seq_file *s)
|
||
|
|
{
|
||
|
|
struct mdw_rsc_tab *tab = NULL;
|
||
|
|
int i = 0;
|
||
|
|
|
||
|
|
mdw_con_info(s, LINEBAR);
|
||
|
|
mdw_con_info(s, "|%-86s|\n",
|
||
|
|
" apusys device table");
|
||
|
|
mdw_con_info(s, LINEBAR);
|
||
|
|
|
||
|
|
mutex_lock(&rsc_mgr.mtx);
|
||
|
|
|
||
|
|
/* query list to find apusys device table by type */
|
||
|
|
for (i = 0; i < APUSYS_DEVICE_MAX; i++) {
|
||
|
|
tab = mdw_rsc_get_tab(i);
|
||
|
|
if (!tab)
|
||
|
|
continue;
|
||
|
|
mdw_rsc_dump_tab(s, tab);
|
||
|
|
}
|
||
|
|
|
||
|
|
mutex_unlock(&rsc_mgr.mtx);
|
||
|
|
}
|
||
|
|
#undef LINEBAR
|
||
|
|
#undef S_LINEBAR
|
||
|
|
|
||
|
|
static int mdw_rsc_dev_exec(struct mdw_dev_info *d, void *sc)
|
||
|
|
{
|
||
|
|
if (!d)
|
||
|
|
return -ENODEV;
|
||
|
|
|
||
|
|
if (d->sc)
|
||
|
|
mdw_drv_warn("dev(%s%d) res cmd\n", d->name, d->idx);
|
||
|
|
mdw_flw_debug("dev(%s%d)\n", d->name, d->idx);
|
||
|
|
mutex_lock(&d->mtx);
|
||
|
|
d->sc = sc;
|
||
|
|
mutex_unlock(&d->mtx);
|
||
|
|
|
||
|
|
complete(&d->cmplt);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mdw_rsc_pwr_on(struct mdw_dev_info *d, int bst, int to)
|
||
|
|
{
|
||
|
|
struct apusys_power_hnd h;
|
||
|
|
|
||
|
|
memset(&h, 0, sizeof(h));
|
||
|
|
h.boost_val = bst;
|
||
|
|
h.timeout = to;
|
||
|
|
|
||
|
|
return d->dev->send_cmd(APUSYS_CMD_POWERON, &h, d->dev);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mdw_rsc_pwr_off(struct mdw_dev_info *d)
|
||
|
|
{
|
||
|
|
struct apusys_power_hnd h;
|
||
|
|
|
||
|
|
memset(&h, 0, sizeof(h));
|
||
|
|
return d->dev->send_cmd(APUSYS_CMD_POWERDOWN, &h, d->dev);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mdw_rsc_fw(struct mdw_dev_info *d, uint32_t magic, const char *name,
|
||
|
|
uint64_t kva, uint32_t iova, uint32_t size, int op)
|
||
|
|
{
|
||
|
|
struct apusys_firmware_hnd h;
|
||
|
|
|
||
|
|
memset(&h, 0, sizeof(h));
|
||
|
|
h.magic = magic;
|
||
|
|
h.kva = kva;
|
||
|
|
h.iova = iova;
|
||
|
|
h.size = size;
|
||
|
|
h.op = op;
|
||
|
|
strncpy(h.name, name, sizeof(h.name)-1);
|
||
|
|
|
||
|
|
return d->dev->send_cmd(APUSYS_CMD_FIRMWARE, &h, d->dev);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mdw_rsc_ucmd(struct mdw_dev_info *d,
|
||
|
|
uint64_t kva, uint32_t iova, uint32_t size)
|
||
|
|
{
|
||
|
|
struct apusys_usercmd_hnd h;
|
||
|
|
|
||
|
|
memset(&h, 0, sizeof(h));
|
||
|
|
h.kva = kva;
|
||
|
|
h.iova = iova;
|
||
|
|
h.size = size;
|
||
|
|
|
||
|
|
return d->dev->send_cmd(APUSYS_CMD_USER, &h, d->dev);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mdw_rsc_sec_on(struct mdw_dev_info *d)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
int type = d->type % APUSYS_DEVICE_RT;
|
||
|
|
|
||
|
|
switch (type) {
|
||
|
|
case APUSYS_DEVICE_SAMPLE:
|
||
|
|
break;
|
||
|
|
|
||
|
|
#ifdef CONFIG_MTK_GZ_SUPPORT_SDSP
|
||
|
|
case APUSYS_DEVICE_VPU:
|
||
|
|
ret = mtee_sdsp_enable(1);
|
||
|
|
if (!ret)
|
||
|
|
mdw_rsc_ws_lock();
|
||
|
|
break;
|
||
|
|
#endif
|
||
|
|
default:
|
||
|
|
mdw_drv_err("dev(%d) secure not support\n", type);
|
||
|
|
ret = -ENODEV;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mdw_rsc_sec_off(struct mdw_dev_info *d)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
int type = d->type % APUSYS_DEVICE_RT;
|
||
|
|
|
||
|
|
switch (type) {
|
||
|
|
case APUSYS_DEVICE_SAMPLE:
|
||
|
|
break;
|
||
|
|
|
||
|
|
#ifdef CONFIG_MTK_GZ_SUPPORT_SDSP
|
||
|
|
case APUSYS_DEVICE_VPU:
|
||
|
|
mdw_rsc_ws_unlock();
|
||
|
|
ret = mtee_sdsp_enable(0);
|
||
|
|
break;
|
||
|
|
#endif
|
||
|
|
default:
|
||
|
|
mdw_drv_err("dev(%d) secure not support\n", type);
|
||
|
|
ret = -ENODEV;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mdw_rsc_suspend(struct mdw_dev_info *d)
|
||
|
|
{
|
||
|
|
struct mdw_rsc_tab *t = mdw_rsc_get_tab(d->type);
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
if (!t)
|
||
|
|
return -ENODEV;
|
||
|
|
|
||
|
|
mutex_lock(&t->mtx);
|
||
|
|
if (d->state != MDW_DEV_INFO_STATE_IDLE) {
|
||
|
|
mdw_drv_warn("dev(%s%d) busy(%d)\n", d->name, d->idx, d->state);
|
||
|
|
ret = -EBUSY;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = d->dev->send_cmd(APUSYS_CMD_SUSPEND, NULL, d->dev);
|
||
|
|
out:
|
||
|
|
mutex_unlock(&t->mtx);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mdw_rsc_resume(struct mdw_dev_info *d)
|
||
|
|
{
|
||
|
|
return d->dev->send_cmd(APUSYS_CMD_RESUME, NULL, d->dev);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mdw_rsc_lock_dev(struct mdw_dev_info *d)
|
||
|
|
{
|
||
|
|
struct mdw_rsc_tab *tab = NULL;
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
tab = mdw_rsc_get_tab(d->type);
|
||
|
|
if (!tab)
|
||
|
|
return -ENODEV;
|
||
|
|
|
||
|
|
mutex_lock(&tab->mtx);
|
||
|
|
if (d->state != MDW_DEV_INFO_STATE_IDLE) {
|
||
|
|
ret = -EBUSY;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
tab->avl_num--;
|
||
|
|
list_del(&d->t_item);
|
||
|
|
d->state = MDW_DEV_INFO_STATE_LOCK;
|
||
|
|
mdw_drv_warn("dev(%s%d)\n", d->name, d->idx);
|
||
|
|
|
||
|
|
out:
|
||
|
|
mutex_unlock(&tab->mtx);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mdw_rsc_unlock_dev(struct mdw_dev_info *d)
|
||
|
|
{
|
||
|
|
struct mdw_rsc_tab *tab = NULL;
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
tab = mdw_rsc_get_tab(d->type);
|
||
|
|
if (!tab)
|
||
|
|
return -ENODEV;
|
||
|
|
|
||
|
|
mutex_lock(&tab->mtx);
|
||
|
|
if (d->state != MDW_DEV_INFO_STATE_LOCK) {
|
||
|
|
ret = -EINVAL;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
list_add_tail(&d->t_item, &tab->list);
|
||
|
|
tab->avl_num++;
|
||
|
|
d->state = MDW_DEV_INFO_STATE_IDLE;
|
||
|
|
mdw_drv_warn("dev(%s%d)\n", d->name, d->idx);
|
||
|
|
|
||
|
|
out:
|
||
|
|
mutex_unlock(&tab->mtx);
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mdw_rsc_add_dev(struct apusys_device *dev)
|
||
|
|
{
|
||
|
|
struct mdw_rsc_tab *tab;
|
||
|
|
struct mdw_dev_info *d;
|
||
|
|
char name[32], thd_name[32];
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
if (dev->dev_type >= APUSYS_DEVICE_MAX) {
|
||
|
|
mdw_drv_err("invalid device idx(%d)\n", dev->dev_type);
|
||
|
|
return -ENODEV;
|
||
|
|
}
|
||
|
|
if (mdw_rsc_get_name(dev->dev_type, name))
|
||
|
|
return -ENODEV;
|
||
|
|
|
||
|
|
/* get rscource table */
|
||
|
|
tab = mdw_rsc_get_tab(dev->dev_type);
|
||
|
|
if (!tab) {
|
||
|
|
tab = mdw_rsc_add_tab(dev->dev_type);
|
||
|
|
if (tab == NULL)
|
||
|
|
return -ENODEV;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (tab->dev_num >= MDW_RSC_TAB_DEV_MAX) {
|
||
|
|
mdw_drv_err("rsc dev only support %d dev(%d)\n",
|
||
|
|
MDW_RSC_TAB_DEV_MAX, tab->dev_num);
|
||
|
|
return -ENODEV;
|
||
|
|
}
|
||
|
|
|
||
|
|
mutex_lock(&tab->mtx);
|
||
|
|
|
||
|
|
/* new dev info */
|
||
|
|
d = vzalloc(sizeof(struct mdw_dev_info));
|
||
|
|
if (!d) {
|
||
|
|
ret = -ENOMEM;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* init dev info list item and insert to table */
|
||
|
|
if (dev->idx != tab->dev_num) {
|
||
|
|
mdw_drv_warn("dev overwrite idx(%d->%d)\n",
|
||
|
|
dev->idx, tab->dev_num);
|
||
|
|
dev->idx = tab->dev_num;
|
||
|
|
}
|
||
|
|
d->idx = dev->idx;
|
||
|
|
if (d->idx >= MDW_RSC_TAB_DEV_MAX || d->idx < 0)
|
||
|
|
goto fail_check_idx;
|
||
|
|
d->dev = dev;
|
||
|
|
d->type = dev->dev_type;
|
||
|
|
/* setup device name and thread name */
|
||
|
|
ret = snprintf(d->name, sizeof(d->name)-1, "%s", name);
|
||
|
|
if (ret < 0)
|
||
|
|
goto fail_set_name;
|
||
|
|
ret = snprintf(thd_name, sizeof(thd_name)-1,
|
||
|
|
"apusys_%s%d", name, d->idx);
|
||
|
|
if (ret < 0)
|
||
|
|
goto fail_set_name;
|
||
|
|
|
||
|
|
ret = 0;
|
||
|
|
list_add_tail(&d->t_item, &tab->list); // add list
|
||
|
|
tab->array[d->idx] = d; // add array
|
||
|
|
init_completion(&d->cmplt);
|
||
|
|
init_completion(&d->thd_done);
|
||
|
|
mutex_init(&d->mtx);
|
||
|
|
|
||
|
|
tab->dev_num++;
|
||
|
|
tab->avl_num++;
|
||
|
|
|
||
|
|
d->exec = mdw_rsc_dev_exec;
|
||
|
|
d->pwr_on = mdw_rsc_pwr_on;
|
||
|
|
d->pwr_off = mdw_rsc_pwr_off;
|
||
|
|
d->suspend = mdw_rsc_suspend;
|
||
|
|
d->resume = mdw_rsc_resume;
|
||
|
|
d->fw = mdw_rsc_fw;
|
||
|
|
d->ucmd = mdw_rsc_ucmd;
|
||
|
|
d->sec_on = mdw_rsc_sec_on;
|
||
|
|
d->sec_off = mdw_rsc_sec_off;
|
||
|
|
d->lock = mdw_rsc_lock_dev;
|
||
|
|
d->unlock = mdw_rsc_unlock_dev;
|
||
|
|
/* create kthd */
|
||
|
|
d->thd = kthread_run(mdw_sched_dev_routine, d, thd_name);
|
||
|
|
|
||
|
|
goto out;
|
||
|
|
|
||
|
|
fail_set_name:
|
||
|
|
fail_check_idx:
|
||
|
|
vfree(d);
|
||
|
|
out:
|
||
|
|
mutex_unlock(&tab->mtx);
|
||
|
|
mdw_rsc_update_avl_bmp(dev->dev_type);
|
||
|
|
mdw_flw_debug("register dev(%d-#%d) done\n", dev->dev_type, dev->idx);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mdw_rsc_delete_dev(struct mdw_dev_info *d)
|
||
|
|
{
|
||
|
|
struct mdw_rsc_tab *tab = NULL;
|
||
|
|
|
||
|
|
if (d->type >= APUSYS_DEVICE_MAX || d->type < 0 ||
|
||
|
|
d->idx >= MDW_RSC_TAB_DEV_MAX || d->idx < 0)
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
tab = mdw_rsc_get_tab(d->type);
|
||
|
|
if (!tab)
|
||
|
|
return -ENODEV;
|
||
|
|
|
||
|
|
tab->array[d->idx] = NULL;
|
||
|
|
d->stop = true;
|
||
|
|
complete(&d->cmplt);
|
||
|
|
wait_for_completion(&d->thd_done);
|
||
|
|
tab->avl_num--;
|
||
|
|
tab->dev_num--;
|
||
|
|
list_del(&d->t_item);
|
||
|
|
mdw_flw_debug("delete dev(%s%d) done\n", d->name, d->idx);
|
||
|
|
vfree(d);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mdw_rsc_check_dev_state(struct mdw_dev_info *in)
|
||
|
|
{
|
||
|
|
struct mdw_rsc_tab *tab = NULL;
|
||
|
|
int type = 0;
|
||
|
|
|
||
|
|
if (in->idx >= MDW_RSC_TAB_DEV_MAX ||
|
||
|
|
in->idx < 0) {
|
||
|
|
mdw_drv_err("dev(%s-#%d) idx over array size(%d)\n",
|
||
|
|
in->name, in->idx, MDW_RSC_TAB_DEV_MAX);
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (in->type < APUSYS_DEVICE_RT)
|
||
|
|
type = in->type + APUSYS_DEVICE_RT;
|
||
|
|
else
|
||
|
|
type = in->type % APUSYS_DEVICE_RT;
|
||
|
|
|
||
|
|
tab = mdw_rsc_get_tab(type);
|
||
|
|
if (!tab)
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
return tab->array[in->idx]->state == MDW_DEV_INFO_STATE_IDLE
|
||
|
|
? 0 : -EBUSY;
|
||
|
|
}
|
||
|
|
|
||
|
|
static struct mdw_dev_info *mdw_rsc_get_dev_sq(int type)
|
||
|
|
{
|
||
|
|
int i = 0;
|
||
|
|
struct mdw_rsc_tab *tab = NULL;
|
||
|
|
struct mdw_dev_info *d = NULL, *first_d = NULL;
|
||
|
|
|
||
|
|
tab = mdw_rsc_get_tab(type);
|
||
|
|
if (!tab)
|
||
|
|
return NULL;
|
||
|
|
|
||
|
|
for (i = 0; i < tab->dev_num; i++) {
|
||
|
|
if (tab->array[i]->state == MDW_DEV_INFO_STATE_IDLE) {
|
||
|
|
d = tab->array[i];
|
||
|
|
/* record first idle device */
|
||
|
|
if (!first_d)
|
||
|
|
first_d = d;
|
||
|
|
|
||
|
|
/* check rt device state */
|
||
|
|
if (!mdw_rsc_check_dev_state(d))
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
d = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* if all device busy, assign first idle device */
|
||
|
|
if (!d && first_d)
|
||
|
|
d = first_d;
|
||
|
|
/* remove device */
|
||
|
|
if (d) {
|
||
|
|
tab->avl_num--;
|
||
|
|
list_del(&d->t_item);
|
||
|
|
d->state = MDW_DEV_INFO_STATE_BUSY;
|
||
|
|
mdw_flw_debug("dev(%s%d)\n", d->name, d->idx);
|
||
|
|
}
|
||
|
|
|
||
|
|
return d;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mdw_rsc_get_norm_prio(struct mdw_dev_info *in)
|
||
|
|
{
|
||
|
|
struct mdw_rsc_tab *tab = NULL;
|
||
|
|
struct mdw_dev_info *d = NULL;
|
||
|
|
struct mdw_apu_sc *sc = NULL;
|
||
|
|
int type = 0, prio = -ENOENT;
|
||
|
|
|
||
|
|
if (in->idx >= MDW_RSC_TAB_DEV_MAX || in->idx < 0)
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
type = in->type % APUSYS_DEVICE_RT;
|
||
|
|
tab = mdw_rsc_get_tab(type);
|
||
|
|
if (!tab)
|
||
|
|
return -ENODEV;
|
||
|
|
|
||
|
|
d = tab->array[in->idx];
|
||
|
|
if (!d)
|
||
|
|
return -ENODEV;
|
||
|
|
|
||
|
|
mutex_lock(&d->mtx);
|
||
|
|
if (d->sc) {
|
||
|
|
sc = (struct mdw_apu_sc *)d->sc;
|
||
|
|
prio = sc->parent->hdr->priority;
|
||
|
|
}
|
||
|
|
mutex_unlock(&d->mtx);
|
||
|
|
|
||
|
|
return prio;
|
||
|
|
}
|
||
|
|
|
||
|
|
static struct mdw_dev_info *mdw_rsc_get_dev_rr(int type)
|
||
|
|
{
|
||
|
|
struct list_head *tmp = NULL, *list_ptr = NULL;
|
||
|
|
struct mdw_rsc_tab *tab = NULL;
|
||
|
|
struct mdw_dev_info *d = NULL, *d_tmp = NULL;
|
||
|
|
int prio = 0, tmp_prio = 0;
|
||
|
|
|
||
|
|
tab = mdw_rsc_get_tab(type);
|
||
|
|
if (!tab)
|
||
|
|
return NULL;
|
||
|
|
|
||
|
|
/* check normal device state to make preempt prefer idle device */
|
||
|
|
list_for_each_safe(list_ptr, tmp, &tab->list) {
|
||
|
|
d = list_entry(list_ptr, struct mdw_dev_info, t_item);
|
||
|
|
if (!mdw_rsc_check_dev_state(d))
|
||
|
|
break;
|
||
|
|
|
||
|
|
/* record if executing sc prio lower */
|
||
|
|
tmp_prio = mdw_rsc_get_norm_prio(d);
|
||
|
|
if (prio < tmp_prio) {
|
||
|
|
prio = tmp_prio;
|
||
|
|
d_tmp = d;
|
||
|
|
}
|
||
|
|
|
||
|
|
d = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (d)
|
||
|
|
goto lock_dev;
|
||
|
|
|
||
|
|
/* no idle device, get device by current plcy*/
|
||
|
|
switch (rsc_mgr.preempt_policy) {
|
||
|
|
case MDW_PREEMPT_PLCY_RR_PRIORITY:
|
||
|
|
if (d_tmp)
|
||
|
|
d = d_tmp;
|
||
|
|
else
|
||
|
|
d = list_first_entry_or_null(&tab->list,
|
||
|
|
struct mdw_dev_info, t_item);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case MDW_PREEMPT_PLCY_RR_SIMPLE:
|
||
|
|
default:
|
||
|
|
d = list_first_entry_or_null(&tab->list,
|
||
|
|
struct mdw_dev_info, t_item);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
lock_dev:
|
||
|
|
if (d) {
|
||
|
|
tab->avl_num--;
|
||
|
|
list_del(&d->t_item);
|
||
|
|
d->state = MDW_DEV_INFO_STATE_BUSY;
|
||
|
|
mdw_flw_debug("dev(%s%d)\n", d->name, d->idx);
|
||
|
|
}
|
||
|
|
|
||
|
|
return d;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void mdw_rsc_req_add(struct mdw_rsc_req *req)
|
||
|
|
{
|
||
|
|
list_add_tail(&req->r_item, &rsc_mgr.r_list);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void mdw_rsc_req_delete(struct mdw_rsc_req *req)
|
||
|
|
{
|
||
|
|
struct list_head *tmp = NULL, *list_ptr = NULL;
|
||
|
|
struct mdw_rsc_req *tmp_req = NULL;
|
||
|
|
|
||
|
|
list_for_each_safe(list_ptr, tmp, &rsc_mgr.r_list) {
|
||
|
|
tmp_req = list_entry(list_ptr, struct mdw_rsc_req, r_item);
|
||
|
|
if (tmp_req == req) {
|
||
|
|
list_del(&req->r_item);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
mdw_drv_warn("req(%u/0x%llx) not in list\n",
|
||
|
|
req->num[APUSYS_DEVICE_VPU], req->acq_bmp);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void mdw_rsc_req_done(struct kref *ref)
|
||
|
|
{
|
||
|
|
struct mdw_rsc_req *req =
|
||
|
|
container_of(ref, struct mdw_rsc_req, ref);
|
||
|
|
|
||
|
|
if (req->in_list == false)
|
||
|
|
return;
|
||
|
|
|
||
|
|
mdw_flw_debug("req(%p)\n", req);
|
||
|
|
|
||
|
|
mdw_rsc_req_delete(req);
|
||
|
|
complete(&req->complt);
|
||
|
|
|
||
|
|
if (req->cb_async)
|
||
|
|
req->cb_async(req);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mdw_rsc_req_add_dev(struct mdw_dev_info *d, struct mdw_rsc_req *req)
|
||
|
|
{
|
||
|
|
if (d->type >= APUSYS_DEVICE_MAX || d->type < 0)
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
mutex_lock(&req->mtx);
|
||
|
|
/* check this dev is needed to req */
|
||
|
|
if (!(req->acq_bmp & (1ULL << d->type)) ||
|
||
|
|
req->get_num[d->type] >= req->num[d->type]) {
|
||
|
|
mutex_unlock(&req->mtx);
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
mdw_flw_debug("put dev(%s%d) to req(%p), ref(%d)\n",
|
||
|
|
d->name, d->idx, req, kref_read(&req->ref));
|
||
|
|
|
||
|
|
/* add dev to req */
|
||
|
|
list_add_tail(&d->r_item, &req->d_list);
|
||
|
|
req->get_num[d->type]++;
|
||
|
|
if (req->get_num[d->type] >= req->num[d->type])
|
||
|
|
req->acq_bmp &= ~(1ULL << d->type);
|
||
|
|
|
||
|
|
mutex_unlock(&req->mtx);
|
||
|
|
|
||
|
|
/* check req done */
|
||
|
|
kref_put(&req->ref, mdw_rsc_req_done);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int mdw_rsc_get_dev(struct mdw_rsc_req *req)
|
||
|
|
{
|
||
|
|
struct mdw_dev_info *(*func)(int) = NULL;
|
||
|
|
struct mdw_rsc_tab *tab = NULL;
|
||
|
|
struct mdw_dev_info *d = NULL;
|
||
|
|
struct list_head *tmp = NULL, *list_ptr = NULL;
|
||
|
|
int ret = 0, n = 0, i = 0, type = -1, get_total = 0;
|
||
|
|
|
||
|
|
mdw_flw_debug("req(%p) bmp(0x%llx) total(%d) mode(%d) policy(%d)\n",
|
||
|
|
req, req->acq_bmp, req->total_num, req->mode, req->policy);
|
||
|
|
|
||
|
|
/* choose policy func ptr */
|
||
|
|
if (req->policy == MDW_DEV_INFO_GET_POLICY_SEQ)
|
||
|
|
func = mdw_rsc_get_dev_sq;
|
||
|
|
else if (req->policy == MDW_DEV_INFO_GET_POLICY_RR)
|
||
|
|
func = mdw_rsc_get_dev_rr;
|
||
|
|
else
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
init_completion(&req->complt);
|
||
|
|
INIT_LIST_HEAD(&req->d_list);
|
||
|
|
mutex_init(&req->mtx);
|
||
|
|
req->in_list = false;
|
||
|
|
refcount_set(&req->ref.refcount, req->total_num);
|
||
|
|
mutex_lock(&rsc_mgr.mtx);
|
||
|
|
|
||
|
|
while (1) {
|
||
|
|
type = find_next_bit((unsigned long *)&req->acq_bmp,
|
||
|
|
APUSYS_DEVICE_MAX, type + 1);
|
||
|
|
mdw_flw_debug("dev(%d) bmp(0x%llx)\n", type, req->acq_bmp);
|
||
|
|
if (type >= APUSYS_DEVICE_MAX || type < 0)
|
||
|
|
break;
|
||
|
|
|
||
|
|
tab = mdw_rsc_get_tab(type);
|
||
|
|
if (!tab) {
|
||
|
|
ret = -ENODEV;
|
||
|
|
goto fail_get_tab;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (req->num[type] > tab->dev_num) {
|
||
|
|
ret = -EINVAL;
|
||
|
|
goto fail_check_num;
|
||
|
|
}
|
||
|
|
|
||
|
|
mutex_lock(&tab->mtx);
|
||
|
|
n = req->num[type] < tab->avl_num ?
|
||
|
|
req->num[type] : tab->avl_num;
|
||
|
|
for (i = 0; i < n; i++) {
|
||
|
|
d = func(type);
|
||
|
|
if (!d) {
|
||
|
|
mdw_drv_warn("dev(%d) num conflict(%d/%d/%d/%d)\n",
|
||
|
|
type, i, req->num[type],
|
||
|
|
tab->avl_num, tab->dev_num);
|
||
|
|
ret = -ENODEV;
|
||
|
|
mutex_unlock(&tab->mtx);
|
||
|
|
goto fail_first_get;
|
||
|
|
}
|
||
|
|
get_total++;
|
||
|
|
if (mdw_rsc_req_add_dev(d, req))
|
||
|
|
mdw_drv_err("req(%p) add dev(%s%d) fail\n",
|
||
|
|
req, d->name, d->idx);
|
||
|
|
}
|
||
|
|
mutex_unlock(&tab->mtx);
|
||
|
|
mdw_flw_debug("dev(%d) get(%d/%d)\n",
|
||
|
|
type, req->get_num[type], req->num[type]);
|
||
|
|
};
|
||
|
|
|
||
|
|
mdw_flw_debug("bmp(0x%llx) get num(%d)\n", req->acq_bmp, get_total);
|
||
|
|
|
||
|
|
if (req->mode == MDW_DEV_INFO_GET_MODE_TRY) {
|
||
|
|
if (!get_total)
|
||
|
|
ret = -ENODEV;
|
||
|
|
mutex_unlock(&rsc_mgr.mtx);
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (req->acq_bmp) {
|
||
|
|
/* add to rsc list */
|
||
|
|
req->in_list = true;
|
||
|
|
mdw_rsc_req_add(req);
|
||
|
|
/* wait if sync mode */
|
||
|
|
if (req->mode == MDW_DEV_INFO_GET_MODE_SYNC) {
|
||
|
|
mutex_unlock(&rsc_mgr.mtx);
|
||
|
|
ret = wait_for_completion_interruptible(&req->complt);
|
||
|
|
mutex_lock(&rsc_mgr.mtx);
|
||
|
|
if (ret) {
|
||
|
|
mdw_drv_warn("wait sync completion(%d)\n", ret);
|
||
|
|
mdw_rsc_req_delete(req);
|
||
|
|
goto fail_wait_sync;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (req->mode == MDW_DEV_INFO_GET_MODE_ASYNC)
|
||
|
|
ret = -EAGAIN;
|
||
|
|
} else {
|
||
|
|
/* call async cb if done */
|
||
|
|
if (req->cb_async)
|
||
|
|
req->cb_async(req);
|
||
|
|
}
|
||
|
|
|
||
|
|
mutex_unlock(&rsc_mgr.mtx);
|
||
|
|
goto out;
|
||
|
|
|
||
|
|
fail_wait_sync:
|
||
|
|
fail_first_get:
|
||
|
|
fail_check_num:
|
||
|
|
fail_get_tab:
|
||
|
|
mutex_unlock(&rsc_mgr.mtx);
|
||
|
|
list_for_each_safe(list_ptr, tmp, &req->d_list) {
|
||
|
|
d = list_entry(list_ptr, struct mdw_dev_info, r_item);
|
||
|
|
list_del(&d->r_item);
|
||
|
|
mdw_rsc_put_dev(d);
|
||
|
|
}
|
||
|
|
out:
|
||
|
|
mdw_rsc_update_avl_bmp(type);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mdw_rsc_put_dev_req(struct mdw_dev_info *d)
|
||
|
|
{
|
||
|
|
struct list_head *tmp = NULL, *list_ptr = NULL;
|
||
|
|
struct mdw_rsc_req *req = NULL;
|
||
|
|
int ret = -ENODATA;
|
||
|
|
|
||
|
|
list_for_each_safe(list_ptr, tmp, &rsc_mgr.r_list) {
|
||
|
|
req = list_entry(list_ptr, struct mdw_rsc_req, r_item);
|
||
|
|
|
||
|
|
ret = mdw_rsc_req_add_dev(d, req);
|
||
|
|
if (!ret)
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
int mdw_rsc_put_dev(struct mdw_dev_info *d)
|
||
|
|
{
|
||
|
|
struct mdw_rsc_tab *tab = NULL;
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
mdw_flw_debug("put dev(%s%d)...\n", d->name, d->idx);
|
||
|
|
mutex_lock(&rsc_mgr.mtx);
|
||
|
|
|
||
|
|
mutex_lock(&d->mtx);
|
||
|
|
d->sc = NULL;
|
||
|
|
mutex_unlock(&d->mtx);
|
||
|
|
|
||
|
|
if (!list_empty(&rsc_mgr.r_list)) {
|
||
|
|
if (!mdw_rsc_put_dev_req(d))
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
tab = mdw_rsc_get_tab(d->type);
|
||
|
|
if (!tab) {
|
||
|
|
ret = -ENODEV;
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
mdw_flw_debug("put dev(%s%d) to table\n", d->name, d->idx);
|
||
|
|
|
||
|
|
mutex_lock(&tab->mtx);
|
||
|
|
list_add_tail(&d->t_item, &tab->list);
|
||
|
|
tab->avl_num++;
|
||
|
|
d->state = MDW_DEV_INFO_STATE_IDLE;
|
||
|
|
mutex_unlock(&tab->mtx);
|
||
|
|
|
||
|
|
out:
|
||
|
|
mutex_unlock(&rsc_mgr.mtx);
|
||
|
|
mdw_rsc_update_avl_bmp(d->type);
|
||
|
|
mdw_sched(NULL);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
struct mdw_dev_info *mdw_rsc_get_dinfo(int type, int idx)
|
||
|
|
{
|
||
|
|
struct mdw_rsc_tab *tab = NULL;
|
||
|
|
|
||
|
|
tab = mdw_rsc_get_tab(type);
|
||
|
|
if (!tab)
|
||
|
|
return NULL;
|
||
|
|
|
||
|
|
if (idx >= tab->dev_num || idx < 0)
|
||
|
|
return NULL;
|
||
|
|
|
||
|
|
return tab->array[idx];
|
||
|
|
}
|
||
|
|
|
||
|
|
uint64_t mdw_rsc_get_dev_bmp(void)
|
||
|
|
{
|
||
|
|
uint64_t b = 0;
|
||
|
|
|
||
|
|
memcpy(&b, &rsc_mgr.dev_sup_bmp, sizeof(b));
|
||
|
|
|
||
|
|
return b;
|
||
|
|
}
|
||
|
|
|
||
|
|
int mdw_rsc_get_dev_num(int type)
|
||
|
|
{
|
||
|
|
struct mdw_rsc_tab *tab = NULL;
|
||
|
|
|
||
|
|
tab = mdw_rsc_get_tab(type);
|
||
|
|
if (!tab)
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
return tab->dev_num;
|
||
|
|
}
|
||
|
|
|
||
|
|
void mdw_rsc_set_thd_group(void)
|
||
|
|
{
|
||
|
|
struct file *fd;
|
||
|
|
char buf[16];
|
||
|
|
mm_segment_t oldfs;
|
||
|
|
struct mdw_dev_info *d = NULL;
|
||
|
|
int type = 0, idx = 0;
|
||
|
|
|
||
|
|
if (atomic_read(&sthd_group))
|
||
|
|
return;
|
||
|
|
|
||
|
|
mdw_sched_set_thd_group();
|
||
|
|
|
||
|
|
oldfs = get_fs();
|
||
|
|
set_fs(get_ds());
|
||
|
|
|
||
|
|
fd = filp_open(APUSYS_THD_TASK_FILE_PATH, O_WRONLY, 0);
|
||
|
|
if (IS_ERR(fd)) {
|
||
|
|
mdw_drv_debug("don't support low latency group\n");
|
||
|
|
goto out;
|
||
|
|
}
|
||
|
|
|
||
|
|
mutex_lock(&rsc_mgr.mtx);
|
||
|
|
for (type = 0; type < APUSYS_DEVICE_MAX; type++) {
|
||
|
|
for (idx = 0; idx < mdw_rsc_get_dev_num(type); idx++) {
|
||
|
|
d = mdw_rsc_get_dinfo(type, idx);
|
||
|
|
if (!d)
|
||
|
|
continue;
|
||
|
|
memset(buf, 0, sizeof(buf));
|
||
|
|
if (snprintf(buf, sizeof(buf)-1, "%d", d->thd->pid) < 0)
|
||
|
|
goto fail_set_name;
|
||
|
|
|
||
|
|
vfs_write(fd, (__force const char __user *)buf,
|
||
|
|
sizeof(buf), &fd->f_pos);
|
||
|
|
mdw_drv_debug("dev(%s%d) set thd(%d/%s)\n",
|
||
|
|
d->name, d->idx, d->thd->pid, buf);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
fail_set_name:
|
||
|
|
mutex_unlock(&rsc_mgr.mtx);
|
||
|
|
filp_close(fd, NULL);
|
||
|
|
out:
|
||
|
|
set_fs(oldfs);
|
||
|
|
atomic_inc(&sthd_group);
|
||
|
|
}
|
||
|
|
|
||
|
|
int mdw_rsc_set_preempt_plcy(uint32_t preempt_policy)
|
||
|
|
{
|
||
|
|
if (preempt_policy >= MDW_PREEMPT_PLCY_MAX)
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
rsc_mgr.preempt_policy = preempt_policy;
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
uint32_t mdw_rsc_get_preempt_plcy(void)
|
||
|
|
{
|
||
|
|
return rsc_mgr.preempt_policy;
|
||
|
|
}
|
||
|
|
|
||
|
|
int mdw_rsc_init(void)
|
||
|
|
{
|
||
|
|
memset(&rsc_mgr, 0, sizeof(rsc_mgr));
|
||
|
|
rsc_mgr.tabs = vzalloc
|
||
|
|
(sizeof(struct mdw_rsc_tab *) * APUSYS_DEVICE_MAX);
|
||
|
|
|
||
|
|
bitmap_zero(rsc_mgr.cmd_avl_bmp, APUSYS_DEVICE_MAX);
|
||
|
|
bitmap_zero(rsc_mgr.dev_avl_bmp, APUSYS_DEVICE_MAX);
|
||
|
|
bitmap_zero(rsc_mgr.dev_sup_bmp, APUSYS_DEVICE_MAX);
|
||
|
|
INIT_LIST_HEAD(&rsc_mgr.r_list);
|
||
|
|
rsc_mgr.preempt_policy = MDW_PREEMPT_PLCY_RR_SIMPLE;
|
||
|
|
mutex_init(&rsc_mgr.mtx);
|
||
|
|
mdw_rsc_ws_init();
|
||
|
|
|
||
|
|
return mdw_sched_init();
|
||
|
|
}
|
||
|
|
|
||
|
|
void mdw_rsc_exit(void)
|
||
|
|
{
|
||
|
|
int type = 0, idx = 0;
|
||
|
|
struct mdw_dev_info *d = NULL;
|
||
|
|
struct mdw_rsc_tab *tab = NULL;
|
||
|
|
|
||
|
|
mdw_sched_exit();
|
||
|
|
|
||
|
|
for (type = 0; type < APUSYS_DEVICE_MAX; type++) {
|
||
|
|
for (idx = 0; idx < mdw_rsc_get_dev_num(type); idx++) {
|
||
|
|
d = mdw_rsc_get_dinfo(type, idx);
|
||
|
|
if (!d)
|
||
|
|
continue;
|
||
|
|
if (mdw_rsc_delete_dev(d))
|
||
|
|
mdw_drv_err("delete dev(%s%d) fail\n",
|
||
|
|
d->name, d->idx);
|
||
|
|
}
|
||
|
|
tab = mdw_rsc_get_tab(type);
|
||
|
|
if (tab)
|
||
|
|
mdw_rsc_delete_tab(tab);
|
||
|
|
}
|
||
|
|
|
||
|
|
mdw_rsc_ws_destroy();
|
||
|
|
vfree(rsc_mgr.tabs);
|
||
|
|
mdw_flw_debug("\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
int apusys_register_device(struct apusys_device *dev)
|
||
|
|
{
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
if (!dev)
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
if (dev->dev_type > APUSYS_DEVICE_NONE &&
|
||
|
|
dev->dev_type < APUSYS_DEVICE_MAX)
|
||
|
|
ret = mdw_rsc_add_dev(dev);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
int apusys_unregister_device(struct apusys_device *dev)
|
||
|
|
{
|
||
|
|
struct mdw_rsc_tab *tab = NULL;
|
||
|
|
struct list_head *tmp = NULL, *list_ptr = NULL;
|
||
|
|
struct mdw_dev_info *d = NULL;
|
||
|
|
|
||
|
|
mdw_flw_debug("\n");
|
||
|
|
|
||
|
|
tab = mdw_rsc_get_tab(dev->dev_type);
|
||
|
|
if (!tab)
|
||
|
|
return -ENODEV;
|
||
|
|
|
||
|
|
mutex_lock(&tab->mtx);
|
||
|
|
|
||
|
|
list_for_each_safe(list_ptr, tmp, &tab->list) {
|
||
|
|
d = list_entry(list_ptr, struct mdw_dev_info, t_item);
|
||
|
|
if (d->dev == dev)
|
||
|
|
break;
|
||
|
|
d = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (d)
|
||
|
|
mdw_rsc_delete_dev(d);
|
||
|
|
|
||
|
|
mutex_unlock(&tab->mtx);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|