blob: 0b00e5e75898821f7509094813229f5ca8f19757 [file]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/device.h>
#include <linux/kconfig.h>
#include <linux/kthread.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/pm_wakeup.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <slbc.h>
#include <slbc_ops.h>
/* #define CREATE_TRACE_POINTS */
/* #include <slbc_events.h> */
#define trace_slbc_api(f, id)
#define trace_slbc_data(f, data)
#include <linux/kthread.h>
#include <linux/pm_qos.h>
#include <linux/cpuidle.h>
static struct pm_qos_request slbc_qos_request;
#if IS_ENABLED(CONFIG_MTK_SLBC_MMSRAM)
#include <mmsram.h>
static struct mmsram_data mmsram;
#endif /* CONFIG_MTK_SLBC_MMSRAM */
#if IS_ENABLED(CONFIG_MTK_L3C_PART)
#include <l3c_part.h>
#endif /* CONFIG_MTK_L3C_PART */
/* #define SLBC_THREAD */
/* #define SLBC_TRACE */
static struct mtk_slbc *slbc;
#ifdef SLBC_THREAD
static struct task_struct *slbc_request_task;
static struct task_struct *slbc_release_task;
#endif /* SLBC_THREAD */
static int buffer_ref;
static int acp_ref;
static int slbc_ref;
static int debug_level;
static int uid_ref[UID_MAX];
static struct slbc_data test_d;
static struct slbc_data slbc_pd[UID_MAX];
static LIST_HEAD(slbc_ops_list);
static DEFINE_MUTEX(slbc_ops_lock);
/* 1 in bit is from request done to relase done */
static unsigned long slbc_uid_used;
/* 1 in bit is for mask */
static unsigned long slbc_sid_mask;
/* 1 in bit is under request flow */
static unsigned long slbc_sid_req_q;
/* 1 int bit is under release flow */
static unsigned long slbc_sid_rel_q;
/* 1 int bit is for slot ussage */
static unsigned long slbc_slot_used;
#define SLBC_CHECK_TIME msecs_to_jiffies(1000)
static struct timer_list slbc_deactivate_timer;
static struct slbc_config p_config[] = {
/* SLBC_ENTRY(id, sid, max, fix, p, extra, res, cache) */
SLBC_ENTRY(UID_MM_VENC, 0, 0, 1408, 0, 0x0, 0x1, 0),
SLBC_ENTRY(UID_MM_DISP, 1, 0, 1383, 0, 0x0, 0x1, 0),
SLBC_ENTRY(UID_MM_MDP, 2, 0, 1383, 0, 0x0, 0x1, 0),
SLBC_ENTRY(UID_AI_MDLA, 3, 0, 1408, 1, 0x0, 0x1, 0),
SLBC_ENTRY(UID_AI_ISP, 4, 0, 1408, 1, 0x0, 0x1, 0),
SLBC_ENTRY(UID_TEST_BUFFER, 5, 0, 1408, 1, 0x0, 0x1, 0),
SLBC_ENTRY(UID_TEST_CACHE, 6, 0, 0, 1, 0x0, 0x0, 0),
SLBC_ENTRY(UID_TEST_ACP, 7, 0, 0, 1, 0x0, 0x0, 0),
};
#if IS_ENABLED(CONFIG_MTK_SLBC_MMSRAM)
static int slbc_check_mmsram(void)
{
if (!mmsram.size) {
mmsram_get_info(&mmsram);
if (!mmsram.size) {
pr_info("#@# %s(%d) mmsram is wrong!\n",
__func__, __LINE__);
mmsram.paddr = 0;
mmsram.vaddr = 0;
return -EINVAL;
}
}
pr_info("%s: pa:%p va:%p size:%#lx\n",
__func__, mmsram.paddr, mmsram.vaddr, mmsram.size);
return 0;
}
#endif /* CONFIG_MTK_SLBC_MMSRAM */
static void slbc_set_sram_data(struct slbc_data *d)
{
#if IS_ENABLED(CONFIG_MTK_SLBC_MMSRAM)
WARN_ON(slbc_check_mmsram());
d->paddr = mmsram.paddr;
d->vaddr = mmsram.vaddr;
pr_info("slbc: pa:%lx va:%lx\n",
(unsigned long)d->paddr, (unsigned long)d->vaddr);
#endif /* CONFIG_MTK_SLBC_MMSRAM */
}
static void slbc_clr_sram_data(struct slbc_data *d)
{
d->paddr = 0;
d->vaddr = 0;
pr_info("slbc: pa:%lx va:%lx\n",
(unsigned long)d->paddr, (unsigned long)d->vaddr);
}
static void slbc_debug_log(const char *fmt, ...)
{
#ifdef SLBC_DEBUG
static char buf[1024];
va_list va;
int len;
va_start(va, fmt);
len = vsnprintf(buf, sizeof(buf), fmt, va);
va_end(va);
if (len)
pr_info("#@# %s\n", buf);
#endif /* SLBC_DEBUG */
}
static int slbc_get_sid_by_uid(enum slbc_uid uid)
{
int i;
for (i = 0; i < ARRAY_SIZE(p_config); i++) {
if (p_config[i].uid == uid)
return p_config[i].slot_id;
}
return SID_NOT_FOUND;
}
static void slbc_backup_private_data(int uid, struct slbc_data *d)
{
struct slbc_data *pd = &slbc_pd[uid];
memcpy(pd, d, offsetof(struct slbc_data, private));
pd->private = d;
d->private = pd;
}
static void slbc_restore_private_data(int uid, struct slbc_data *d)
{
struct slbc_data *pd = &slbc_pd[uid];
memcpy(pd, d, offsetof(struct slbc_data, sid));
memcpy(&d->sid, &pd->sid, sizeof(struct slbc_data) -
offsetof(struct slbc_data, sid));
pd->private = d;
d->private = pd;
}
static void slbc_deactivate_timer_fn(struct timer_list *timer)
{
struct slbc_ops *ops;
int ref = 0;
slbc_debug_log("%s: slbc_uid_used %lx", __func__, slbc_uid_used);
slbc_debug_log("%s: slbc_sid_rel_q %lx", __func__,
slbc_sid_rel_q);
mutex_lock(&slbc_ops_lock);
list_for_each_entry(ops, &slbc_ops_list, node) {
struct slbc_data *d = ops->data;
unsigned int uid = d->uid;
if (test_bit(uid, &slbc_sid_rel_q)) {
#ifdef SLBC_TRACE
trace_slbc_api((void *)__func__, slbc_uid_str[uid]);
#endif /* SLBC_TRACE */
slbc_debug_log("%s: %s", __func__, slbc_uid_str[uid]);
if (test_bit(uid, &slbc_uid_used)) {
ref++;
pr_info("#@# %s(%d) %s not released!\n",
__func__, __LINE__,
slbc_uid_str[uid]);
} else {
clear_bit(uid, &slbc_sid_rel_q);
slbc_debug_log("%s: slbc_sid_rel_q %lx",
__func__, slbc_sid_rel_q);
pr_info("#@# %s(%d) %s released!\n",
__func__, __LINE__,
slbc_uid_str[uid]);
}
}
}
mutex_unlock(&slbc_ops_lock);
if (ref) {
unsigned long expires;
expires = jiffies + SLBC_CHECK_TIME;
mod_timer(&slbc_deactivate_timer, expires);
}
}
int slbc_activate(struct slbc_data *d)
{
struct slbc_ops *ops;
unsigned int uid = d->uid;
int ret = 0;
if (slbc_enable == 0)
return -EDISABLED;
if (d->uid <= 0)
return -EINVAL;
#ifdef SLBC_TRACE
trace_slbc_api((void *)__func__, slbc_uid_str[uid]);
#endif /* SLBC_TRACE */
slbc_debug_log("%s: %s", __func__, slbc_uid_str[uid]);
ops = container_of(&d, struct slbc_ops, data);
if (ops && ops->activate) {
ret = slbc_request(d);
if (ret) {
pr_info("#@# %s(%d) %s request fail!\n",
__func__, __LINE__, slbc_uid_str[uid]);
return ret;
}
if (ops->activate(d) == CB_DONE) {
pr_info("#@# %s(%d) %s activate fail!\n",
__func__, __LINE__, slbc_uid_str[uid]);
ret = slbc_release(d);
if (ret) {
pr_info("#@# %s(%d) %s release fail!\n",
__func__, __LINE__,
slbc_uid_str[uid]);
return ret;
}
} else {
#ifdef SLBC_TRACE
trace_slbc_data((void *)__func__, d);
#endif /* SLBC_TRACE */
slbc_debug_log("%s: %s %s", __func__, slbc_uid_str[uid],
"done");
}
return 0;
}
pr_info("#@# %s(%d) %s data not found!\n",
__func__, __LINE__, slbc_uid_str[uid]);
return -EFAULT;
}
int slbc_deactivate(struct slbc_data *d)
{
struct slbc_ops *ops;
unsigned int uid = d->uid;
if (slbc_enable == 0)
return -EDISABLED;
if (d->uid <= 0)
return -EINVAL;
#ifdef SLBC_TRACE
trace_slbc_api((void *)__func__, slbc_uid_str[uid]);
#endif /* SLBC_TRACE */
slbc_debug_log("%s: %s", __func__, slbc_uid_str[uid]);
ops = container_of(&d, struct slbc_ops, data);
if (ops && ops->deactivate) {
unsigned long expires;
ops->deactivate(d);
#ifdef SLBC_TRACE
trace_slbc_data((void *)__func__, d);
#endif /* SLBC_TRACE */
slbc_debug_log("%s: %s %s", __func__, slbc_uid_str[uid],
"done");
if (test_bit(uid, &slbc_sid_rel_q)) {
slbc_debug_log("%s: %s already in release_q!",
__func__, slbc_uid_str[uid],
slbc_sid_rel_q);
}
set_bit(uid, &slbc_sid_rel_q);
slbc_debug_log("%s: slbc_sid_rel_q %lx", __func__,
slbc_sid_rel_q);
expires = jiffies + SLBC_CHECK_TIME;
mod_timer(&slbc_deactivate_timer, expires);
return 0;
}
pr_info("#@# %s(%d) %s data not found!\n",
__func__, __LINE__, slbc_uid_str[uid]);
return -EFAULT;
}
#ifdef SLBC_THREAD
static struct slbc_data *slbc_find_next_low_used(struct slbc_data *d_old)
{
struct slbc_ops *ops;
struct slbc_config *config_old = d_old->config;
unsigned int p_old = config_old->priority;
unsigned int res_old = config_old->res_slot;
struct slbc_data *d_used = NULL;
slbc_debug_log("%s: slbc_uid_used %lx", __func__, slbc_uid_used);
list_for_each_entry(ops, &slbc_ops_list, node) {
struct slbc_data *d = ops->data;
struct slbc_config *config = d->config;
unsigned int uid = d->uid;
unsigned int p = config->priority;
if (test_bit(uid, &slbc_uid_used) &&
(p > p_old) &&
(d->slot_used & res_old)) {
d_used = d;
break;
}
}
if (d_used) {
#ifdef SLBC_TRACE
trace_slbc_data((void *)__func__, d_used);
#endif /* SLBC_TRACE */
slbc_debug_log("%s: %s", __func__,
slbc_uid_str[d_used->uid]);
}
return d_used;
}
static struct slbc_data *slbc_find_next_high_req(struct slbc_data *d_old)
{
struct slbc_ops *ops;
struct slbc_config *config_old = d_old->config;
unsigned int p_old = config_old->priority;
unsigned int res_old = config_old->res_slot;
struct slbc_data *d_req = NULL;
slbc_debug_log("%s: slbc_sid_req_q %lx", __func__, slbc_sid_req_q);
list_for_each_entry(ops, &slbc_ops_list, node) {
struct slbc_data *d = ops->data;
struct slbc_config *config = d->config;
unsigned int res = config->res_slot;
unsigned int uid = d->uid;
unsigned int p = config->priority;
if (test_bit(uid, &slbc_sid_req_q) &&
(p <= p_old) &&
(res & res_old)) {
p_old = p;
d_req = d;
if (!p)
break;
}
}
if (d_req) {
#ifdef SLBC_TRACE
trace_slbc_data((void *)__func__, d_req);
#endif /* SLBC_TRACE */
slbc_debug_log("%s: %s", __func__, slbc_uid_str[d_req->uid]);
}
return d_req;
}
static int slbc_activate_thread(void *arg)
{
struct slbc_data *d = arg;
unsigned int uid = d->uid;
#ifdef SLBC_TRACE
trace_slbc_api((void *)__func__, slbc_uid_str[uid]);
#endif /* SLBC_TRACE */
slbc_debug_log("%s: %s", __func__, slbc_uid_str[uid]);
/* check return value */
slbc_activate(d);
return 0;
}
static int slbc_deactivate_thread(void *arg)
{
struct slbc_data *d = arg;
unsigned int uid = d->uid;
struct slbc_data *d_used;
#ifdef SLBC_TRACE
trace_slbc_api((void *)__func__, slbc_uid_str[uid]);
#endif /* SLBC_TRACE */
slbc_debug_log("%s: %s", __func__, slbc_uid_str[uid]);
while ((d_used = slbc_find_next_low_used(d))) {
/* check return value */
slbc_deactivate(d_used);
};
return 0;
}
#endif /* SLBC_THREAD */
static void check_slot_by_data(struct slbc_data *d)
{
struct slbc_config *config = d->config;
unsigned int res = config->res_slot;
unsigned int uid = d->uid;
#ifdef SLBC_TRACE
trace_slbc_api((void *)__func__, slbc_uid_str[uid]);
#endif /* SLBC_TRACE */
slbc_debug_log("%s: %s", __func__, slbc_uid_str[uid]);
/* check all need slot */
if ((d->type) == TP_BUFFER)
d->slot_used = res;
else
d->slot_used = 0;
}
static int find_slbc_slot_by_data(struct slbc_data *d)
{
unsigned int uid = d->uid;
#ifdef SLBC_TRACE
trace_slbc_api((void *)__func__, slbc_uid_str[uid]);
#endif /* SLBC_TRACE */
slbc_debug_log("%s: %s", __func__, slbc_uid_str[uid]);
slbc_debug_log("%s: slbc_slot_used %lx", __func__, slbc_slot_used);
if (!(slbc_slot_used & d->slot_used))
return SLOT_AVAILABLE;
return SLOT_USED;
}
static void slbc_set_slot_by_data(struct slbc_data *d)
{
slbc_slot_used |= d->slot_used;
slbc_debug_log("%s: slbc_slot_used %lx", __func__, slbc_slot_used);
}
static void slbc_clr_slot_by_data(struct slbc_data *d)
{
slbc_slot_used &= ~d->slot_used;
slbc_debug_log("%s: slbc_slot_used %lx", __func__, slbc_slot_used);
}
static void slbc_debug_dump_data(struct slbc_data *d)
{
unsigned int uid = d->uid;
pr_info("ID %s\t", slbc_uid_str[uid]);
if (test_bit(uid, &slbc_uid_used))
pr_info(" activate\n");
else
pr_info(" deactivate\n");
pr_info("uid: %d\n", uid);
pr_info("type: 0x%x\n", d->type);
pr_info("size: %ld\n", d->size);
pr_info("paddr: %lx\n", (unsigned long)d->paddr);
pr_info("vaddr: %lx\n", (unsigned long)d->vaddr);
pr_info("sid: %d\n", d->sid);
pr_info("slot_used: 0x%x\n", d->slot_used);
pr_info("config: %p\n", d->config);
pr_info("ref: %d\n", d->ref);
pr_info("pwr_ref: %d\n", d->pwr_ref);
}
static int slbc_debug_all(void)
{
struct slbc_ops *ops;
int i;
int sid;
pr_info("slbc_enable %x\n", slbc_enable);
pr_info("slbc_uid_used %lx\n", slbc_uid_used);
pr_info("slbc_sid_mask %lx\n", slbc_sid_mask);
pr_info("slbc_sid_req_q %lx\n", slbc_sid_req_q);
pr_info("slbc_sid_rel_q %lx\n", slbc_sid_rel_q);
pr_info("slbc_slot_used %lx\n", slbc_slot_used);
pr_info("buffer_ref %x\n", buffer_ref);
pr_info("acp_ref %x\n", acp_ref);
pr_info("slbc_ref %x\n", slbc_ref);
for (i = 0; i < UID_MAX; i++) {
sid = slbc_get_sid_by_uid(i);
if (sid != SID_NOT_FOUND)
pr_info("uid_ref %s %x\n", slbc_uid_str[i], uid_ref[i]);
}
mutex_lock(&slbc_ops_lock);
list_for_each_entry(ops, &slbc_ops_list, node) {
struct slbc_data *d = ops->data;
slbc_debug_dump_data(d);
}
mutex_unlock(&slbc_ops_lock);
pr_info("\n");
return 0;
}
static int slbc_request_cache(struct slbc_data *d)
{
int ret = 0;
slbc_debug_log("%s: TP_CACHE\n", __func__);
return ret;
}
static int slbc_request_buffer(struct slbc_data *d)
{
int ret = 0;
slbc_debug_log("%s: TP_BUFFER\n", __func__);
#if IS_ENABLED(CONFIG_MTK_SLBC_MMSRAM)
if (!buffer_ref) {
d->pwr_ref++;
enable_mmsram();
}
#endif /* CONFIG_MTK_SLBC_MMSRAM */
slbc_set_sram_data(d);
buffer_ref++;
return ret;
}
static int slbc_request_acp(void *ptr)
{
int ret = 0;
#if IS_ENABLED(CONFIG_MTK_L3C_PART)
struct slbc_data *d = ptr;
slbc_debug_log("%s: TP_ACP\n", __func__);
if (!acp_ref) {
#if IS_ENABLED(CONFIG_PM_SLEEP)
__pm_stay_awake(slbc->ws);
#endif /* CONFIG_PM_SLEEP */
cpu_latency_qos_update_request(&slbc_qos_request,
slbc->slbc_qos_latency);
}
if (acp_ref++ == 0) {
const unsigned long ratio_bits = d->flag & FG_ACP_BITS;
int ratio = find_first_bit(&ratio_bits, 32)
- ACP_ONLY_BIT;
slbc_debug_log("%s: before %s:0x%x, %s:0x%x\n",
__func__,
"MTK_L3C_PART_MCU",
mtk_l3c_get_part(MTK_L3C_PART_MCU),
"MTK_L3C_PART_ACP",
mtk_l3c_get_part(MTK_L3C_PART_ACP));
if (d->flag & FG_ACP_ONLY)
mtk_l3c_set_mcu_part(4 - ratio);
else
mtk_l3c_set_mcu_part(4);
mtk_l3c_set_acp_part(ratio);
slbc_debug_log("%s: after %s:0x%x, %s:0x%x\n",
__func__,
"MTK_L3C_PART_MCU",
mtk_l3c_get_part(MTK_L3C_PART_MCU),
"MTK_L3C_PART_ACP",
mtk_l3c_get_part(MTK_L3C_PART_ACP));
}
#endif /* CONFIG_MTK_L3C_PART */
return ret;
}
int slbc_request(struct slbc_data *d)
{
struct slbc_data *pd;
unsigned int uid;
unsigned int sid;
int ret = 0;
if (slbc_enable == 0)
return -EDISABLED;
if (d == 0)
return -EINVAL;
if (d->uid <= 0)
return -EINVAL;
mutex_lock(&slbc_ops_lock);
uid = d->uid;
sid = slbc_get_sid_by_uid(uid);
if (sid != SID_NOT_FOUND) {
memset(&d->sid, 0, sizeof(struct slbc_data) -
offsetof(struct slbc_data, sid));
d->sid = sid;
d->config = &p_config[sid];
} else {
pr_info("#@# %s(%d) %s sid is wrong!\n",
__func__, __LINE__, slbc_uid_str[uid]);
ret = -EINVAL;
goto error;
}
pd = &slbc_pd[uid];
if (pd->private)
slbc_restore_private_data(uid, d);
#ifdef SLBC_TRACE
trace_slbc_api((void *)__func__, slbc_uid_str[uid]);
#endif /* SLBC_TRACE */
slbc_debug_log("%s: %s", __func__, slbc_uid_str[uid]);
slbc_debug_log("%s: slbc_sid_mask %lx", __func__, slbc_sid_mask);
if (test_bit(uid, &slbc_sid_mask)) {
ret = -EREQ_MASKED;
goto error;
}
if (test_bit(uid, &slbc_sid_req_q)) {
slbc_debug_log("%s: %s already in request_q!",
__func__, slbc_uid_str[uid],
slbc_sid_req_q);
}
set_bit(uid, &slbc_sid_req_q);
slbc_debug_log("%s: slbc_sid_req_q %lx", __func__, slbc_sid_req_q);
slbc_debug_log("%s: slbc_uid_used %lx", __func__, slbc_uid_used);
if (test_bit(uid, &slbc_uid_used)) {
slbc_debug_log("%s: %s already requested!",
__func__, slbc_uid_str[uid]);
slbc_set_sram_data(d);
goto request_done1;
}
if (BIT_IN_MM_BITS_1(BIT(uid)) &&
!BIT_IN_MM_BITS_1(slbc_uid_used)) {
slbc_debug_log("%s: %s use the same request!",
__func__, slbc_uid_str[uid]);
slbc_set_sram_data(d);
goto request_done;
}
check_slot_by_data(d);
if (((d->type) == TP_BUFFER) &&
(find_slbc_slot_by_data(d) != SLOT_AVAILABLE)) {
#ifdef SLBC_THREAD
struct slbc_data *d_used;
d_used = slbc_find_next_low_used(d);
if (d_used) {
slbc_release_task = kthread_run(slbc_deactivate_thread,
d, "slbc_deactivate_thread");
ret = -EWAIT_RELEASE;
goto error;
}
#endif /* SLBC_THREAD */
ret = -ENOT_AVAILABLE;
goto error;
}
if ((d->type) == TP_BUFFER)
slbc_request_buffer(d);
else if ((d->type) == TP_CACHE)
slbc_request_cache(d);
else if ((d->type) == TP_ACP)
slbc_request_acp(d);
slbc_set_slot_by_data(d);
#ifdef SLBC_TRACE
trace_slbc_data((void *)__func__, d);
#endif /* SLBC_TRACE */
request_done:
set_bit(uid, &slbc_uid_used);
slbc_debug_log("%s: slbc_uid_used %lx", __func__, slbc_uid_used);
clear_bit(uid, &slbc_sid_req_q);
slbc_debug_log("%s: slbc_sid_req_q %lx", __func__, slbc_sid_req_q);
request_done1:
slbc_ref++;
d->ref++;
uid_ref[uid]++;
slbc_debug_dump_data(d);
error:
slbc_backup_private_data(uid, d);
mutex_unlock(&slbc_ops_lock);
return 0;
}
static int slbc_release_cache(struct slbc_data *d)
{
int ret = 0;
slbc_debug_log("%s: TP_CACHE\n", __func__);
return ret;
}
static int slbc_release_buffer(struct slbc_data *d)
{
int ret = 0;
slbc_debug_log("%s: TP_BUFFER\n", __func__);
buffer_ref--;
WARN_ON(buffer_ref < 0);
slbc_clr_sram_data(d);
#if IS_ENABLED(CONFIG_MTK_SLBC_MMSRAM)
if (!buffer_ref) {
d->pwr_ref--;
disable_mmsram();
}
#endif /* CONFIG_MTK_SLBC_MMSRAM */
return ret;
}
static int slbc_release_acp(void *ptr)
{
int ret = 0;
#if IS_ENABLED(CONFIG_MTK_L3C_PART)
struct slbc_data *d = ptr;
slbc_debug_log("%s: TP_ACP\n", __func__);
if (--acp_ref == 0) {
slbc_debug_log("%s: before %s:0x%x, %s:0x%x\n",
__func__,
"MTK_L3C_PART_MCU",
mtk_l3c_get_part(MTK_L3C_PART_MCU),
"MTK_L3C_PART_ACP",
mtk_l3c_get_part(MTK_L3C_PART_ACP));
mtk_l3c_set_mcu_part(4);
mtk_l3c_set_acp_part(1);
slbc_debug_log("%s: after %s:0x%x, %s:0x%x\n",
__func__,
"MTK_L3C_PART_MCU",
mtk_l3c_get_part(MTK_L3C_PART_MCU),
"MTK_L3C_PART_ACP",
mtk_l3c_get_part(MTK_L3C_PART_ACP));
}
WARN_ON(acp_ref < 0);
if (!acp_ref) {
#if IS_ENABLED(CONFIG_PM_SLEEP)
__pm_relax(slbc->ws);
#endif /* CONFIG_PM_SLEEP */
cpu_latency_qos_update_request(&slbc_qos_request,
PM_QOS_DEFAULT_VALUE);
}
#endif /* CONFIG_MTK_L3C_PART */
return ret;
}
int slbc_release(struct slbc_data *d)
{
struct slbc_data *pd;
#ifdef SLBC_THREAD
struct slbc_data *d_req;
#endif /* SLBC_THREAD */
unsigned int uid;
unsigned int sid;
int ret = 0;
if (slbc_enable == 0)
return -EDISABLED;
if (d == 0)
return -EINVAL;
if (d->uid <= 0)
return -EINVAL;
mutex_lock(&slbc_ops_lock);
uid = d->uid;
sid = slbc_get_sid_by_uid(uid);
if (sid != SID_NOT_FOUND) {
d->sid = sid;
d->config = &p_config[sid];
} else {
pr_info("#@# %s(%d) %s sid is wrong!\n",
__func__, __LINE__, slbc_uid_str[uid]);
ret = -EINVAL;
goto error;
}
if (!uid_ref[uid]) {
pr_info("#@# %s(%d) %s uid_ref[%d] is zero!\n",
__func__, __LINE__, slbc_uid_str[uid], uid);
ret = -EINVAL;
goto error;
}
pd = &slbc_pd[uid];
if (pd->private)
slbc_restore_private_data(uid, d);
#ifdef SLBC_TRACE
trace_slbc_api((void *)__func__, slbc_uid_str[uid]);
#endif /* SLBC_TRACE */
slbc_debug_log("%s: %s", __func__, slbc_uid_str[uid]);
slbc_debug_log("%s: slbc_sid_mask %lx", __func__, slbc_sid_mask);
if (test_bit(uid, &slbc_sid_mask)) {
ret = -EREQ_MASKED;
goto error;
}
slbc_debug_log("%s: slbc_uid_used %lx", __func__, slbc_uid_used);
if (!test_bit(uid, &slbc_uid_used)) {
slbc_debug_log("%s: %s already released!",
__func__, slbc_uid_str[uid]);
slbc_clr_sram_data(d);
goto release_done1;
}
if (uid_ref[uid] > 1) {
slbc_debug_log("%s: %s already released!",
__func__, slbc_uid_str[uid]);
slbc_clr_sram_data(d);
goto release_done1;
}
if (BIT_IN_MM_BITS_1(BIT(uid)) &&
!BIT_IN_MM_BITS_1(slbc_uid_used & ~BIT(uid))) {
slbc_debug_log("%s: %s use the same release!",
__func__, slbc_uid_str[uid]);
slbc_clr_sram_data(d);
goto release_done;
}
if ((d->type) == TP_BUFFER)
slbc_release_buffer(d);
else if ((d->type) == TP_CACHE)
slbc_release_cache(d);
else if ((d->type) == TP_ACP)
slbc_release_acp(d);
slbc_clr_slot_by_data(d);
#ifdef SLBC_TRACE
trace_slbc_data((void *)__func__, d);
#endif /* SLBC_TRACE */
#ifdef SLBC_THREAD
d_req = slbc_find_next_high_req(d);
if (d_req) {
slbc_request_task = kthread_run(slbc_activate_thread,
d_req, "slbc_activate_thread");
}
#endif /* SLBC_THREAD */
release_done:
clear_bit(uid, &slbc_uid_used);
slbc_debug_log("%s: slbc_uid_used %lx", __func__, slbc_uid_used);
release_done1:
slbc_ref--;
d->ref--;
uid_ref[uid]--;
if ((d->ref < 0) || (uid_ref[uid] < 0)) {
slbc_debug_dump_data(d);
slbc_debug_all();
}
WARN((d->ref < 0) || (uid_ref[uid] < 0),
"%s: release %s fail! %d %d\n",
__func__, slbc_uid_str[uid], d->ref, uid_ref[uid]);
error:
slbc_backup_private_data(uid, d);
mutex_unlock(&slbc_ops_lock);
return 0;
}
int slbc_power_on(struct slbc_data *d)
{
unsigned int uid;
if (slbc_enable == 0)
return -EDISABLED;
if (!d)
return -EINVAL;
uid = d->uid;
if (uid <= UID_ZERO || uid >= UID_MAX)
return -EINVAL;
#ifdef SLBC_TRACE
trace_slbc_api((void *)__func__, slbc_uid_str[uid]);
#endif /* SLBC_TRACE */
/* slbc_debug_log("%s: %s flag %x", __func__, */
/* slbc_uid_str[uid], d->flag); */
#if IS_ENABLED(CONFIG_MTK_SLBC_MMSRAM)
if (IS_ENABLED(CONFIG_MTK_SLBC_MMSRAM) &&
(SLBC_TRY_FLAG_BIT(d, FG_POWER) ||
d->ref)) {
d->pwr_ref++;
return mmsram_power_on();
}
#endif /* CONFIG_MTK_SLBC_MMSRAM */
return 0;
}
int slbc_power_off(struct slbc_data *d)
{
unsigned int uid;
if (slbc_enable == 0)
return -EDISABLED;
if (!d)
return -EINVAL;
uid = d->uid;
if (uid <= UID_ZERO || uid >= UID_MAX)
return -EINVAL;
#ifdef SLBC_TRACE
trace_slbc_api((void *)__func__, slbc_uid_str[uid]);
#endif /* SLBC_TRACE */
/* slbc_debug_log("%s: %s flag %x", __func__, */
/* slbc_uid_str[uid], d->flag); */
#if IS_ENABLED(CONFIG_MTK_SLBC_MMSRAM)
if (IS_ENABLED(CONFIG_MTK_SLBC_MMSRAM) &&
(SLBC_TRY_FLAG_BIT(d, FG_POWER) ||
d->ref)) {
d->pwr_ref--;
mmsram_power_off();
}
#endif /* CONFIG_MTK_SLBC_MMSRAM */
return 0;
}
int slbc_secure_on(struct slbc_data *d)
{
unsigned int uid;
if (slbc_enable == 0)
return -EDISABLED;
if (!d)
return -EINVAL;
uid = d->uid;
if (uid <= UID_ZERO || uid >= UID_MAX)
return -EINVAL;
#ifdef SLBC_TRACE
trace_slbc_api((void *)__func__, slbc_uid_str[uid]);
#endif /* SLBC_TRACE */
slbc_debug_log("%s: %s flag %x", __func__, slbc_uid_str[uid], d->flag);
#if IS_ENABLED(CONFIG_MTK_SLBC_MMSRAM)
if (IS_ENABLED(CONFIG_MTK_SLBC_MMSRAM) &&
SLBC_TRY_FLAG_BIT(d, FG_SECURE)) {
mmsram_set_secure(true);
}
#endif /* CONFIG_MTK_SLBC_MMSRAM */
return 0;
}
int slbc_secure_off(struct slbc_data *d)
{
unsigned int uid;
if (slbc_enable == 0)
return -EDISABLED;
if (!d)
return -EINVAL;
uid = d->uid;
if (uid <= UID_ZERO || uid >= UID_MAX)
return -EINVAL;
#ifdef SLBC_TRACE
trace_slbc_api((void *)__func__, slbc_uid_str[uid]);
#endif /* SLBC_TRACE */
slbc_debug_log("%s: %s flag %x", __func__, slbc_uid_str[uid], d->flag);
#if IS_ENABLED(CONFIG_MTK_SLBC_MMSRAM)
if (IS_ENABLED(CONFIG_MTK_SLBC_MMSRAM) &&
SLBC_TRY_FLAG_BIT(d, FG_SECURE)) {
mmsram_set_secure(false);
}
#endif /* CONFIG_MTK_SLBC_MMSRAM */
return 0;
}
static void slbc_dump_data(struct seq_file *m, struct slbc_data *d)
{
unsigned int uid = d->uid;
seq_printf(m, "ID %s\t", slbc_uid_str[uid]);
if (test_bit(uid, &slbc_uid_used))
seq_puts(m, " activate\n");
else
seq_puts(m, " deactivate\n");
seq_printf(m, "uid: %d\n", uid);
seq_printf(m, "type: 0x%x\n", d->type);
seq_printf(m, "size: %ld\n", d->size);
seq_printf(m, "paddr: %lx\n", (unsigned long)d->paddr);
seq_printf(m, "vaddr: %lx\n", (unsigned long)d->vaddr);
seq_printf(m, "sid: %d\n", d->sid);
seq_printf(m, "slot_used: 0x%x\n", d->slot_used);
seq_printf(m, "config: %p\n", d->config);
seq_printf(m, "ref: %d\n", d->ref);
seq_printf(m, "pwr_ref: %d\n", d->pwr_ref);
}
static int dbg_slbc_proc_show(struct seq_file *m, void *v)
{
struct slbc_ops *ops;
int i;
int sid;
seq_printf(m, "slbc_enable %x\n", slbc_enable);
seq_printf(m, "slbc_uid_used 0x%lx\n", slbc_uid_used);
seq_printf(m, "slbc_sid_mask 0x%lx\n", slbc_sid_mask);
seq_printf(m, "slbc_sid_req_q 0x%lx\n", slbc_sid_req_q);
seq_printf(m, "slbc_sid_rel_q 0x%lx\n", slbc_sid_rel_q);
seq_printf(m, "slbc_slot_used 0x%lx\n", slbc_slot_used);
seq_printf(m, "buffer_ref %x\n", buffer_ref);
seq_printf(m, "acp_ref %x\n", acp_ref);
seq_printf(m, "slbc_ref %x\n", slbc_ref);
seq_printf(m, "debug_level %x\n", debug_level);
for (i = 0; i < UID_MAX; i++) {
sid = slbc_get_sid_by_uid(i);
if (sid != SID_NOT_FOUND)
seq_printf(m, "uid_ref %s %x\n",
slbc_uid_str[i], uid_ref[i]);
}
mutex_lock(&slbc_ops_lock);
list_for_each_entry(ops, &slbc_ops_list, node) {
struct slbc_data *d = ops->data;
slbc_dump_data(m, d);
}
mutex_unlock(&slbc_ops_lock);
seq_puts(m, "\n");
return 0;
}
static ssize_t dbg_slbc_proc_write(struct file *file,
const char __user *buffer, size_t count, loff_t *pos)
{
int ret = 0;
char *buf = (char *) __get_free_page(GFP_USER);
char cmd[64];
unsigned long val_1;
unsigned long val_2;
if (!buf)
return -ENOMEM;
ret = -EINVAL;
if (count >= PAGE_SIZE)
goto out;
ret = -EFAULT;
if (copy_from_user(buf, buffer, count))
goto out;
buf[count] = '\0';
ret = sscanf(buf, "%63s %lx %lx", cmd, &val_1, &val_2);
if (ret < 1) {
ret = -EPERM;
goto out;
}
if (!strcmp(cmd, "slbc_enable")) {
slbc_enable = !!val_1;
if (slbc_enable == 0) {
struct slbc_ops *ops;
mutex_lock(&slbc_ops_lock);
list_for_each_entry(ops, &slbc_ops_list, node) {
struct slbc_data *d = ops->data;
unsigned int uid = d->uid;
if (test_bit(uid, &slbc_uid_used))
ops->deactivate(d);
}
mutex_unlock(&slbc_ops_lock);
}
} else if (!strcmp(cmd, "slbc_uid_used"))
slbc_uid_used = val_1;
else if (!strcmp(cmd, "slbc_sid_mask"))
slbc_sid_mask = val_1;
else if (!strcmp(cmd, "slbc_sid_req_q"))
slbc_sid_req_q = val_1;
else if (!strcmp(cmd, "slbc_sid_rel_q"))
slbc_sid_rel_q = val_1;
else if (!strcmp(cmd, "slbc_slot_used"))
slbc_slot_used = val_1;
else if (!strcmp(cmd, "test_acp_request")) {
test_d.uid = UID_TEST_ACP;
test_d.type = TP_ACP;
test_d.flag = val_1;
slbc_request(&test_d);
} else if (!strcmp(cmd, "test_acp_release")) {
test_d.uid = UID_TEST_ACP;
test_d.type = TP_ACP;
test_d.flag = val_1;
slbc_release(&test_d);
} else if (!strcmp(cmd, "debug_level")) {
debug_level = val_1;
}
out:
free_page((unsigned long)buf);
if (ret < 0)
return ret;
return count;
}
PROC_FOPS_RW(dbg_slbc);
static int slbc_create_debug_fs(void)
{
int i;
struct proc_dir_entry *dir = NULL;
struct pentry {
const char *name;
const struct proc_ops *fops;
void *data;
};
const struct pentry entries[] = {
PROC_ENTRY(dbg_slbc),
};
/* create /proc/slbc */
dir = proc_mkdir("slbc", NULL);
if (!dir) {
pr_info("fail to create /proc/slbc @ %s()\n", __func__);
return -ENOMEM;
}
for (i = 0; i < ARRAY_SIZE(entries); i++) {
if (!proc_create_data(entries[i].name, 0660,
dir, entries[i].fops, entries[i].data))
pr_info("%s(), create /proc/slbc/%s failed\n",
__func__, entries[i].name);
}
return 0;
}
static struct slbc_common_ops common_ops = {
.slbc_request = slbc_request,
.slbc_release = slbc_release,
.slbc_power_on = slbc_power_on,
.slbc_power_off = slbc_power_off,
.slbc_secure_on = slbc_secure_on,
.slbc_secure_off = slbc_secure_off,
};
static int slbc_probe(struct platform_device *pdev)
{
struct device_node *node;
struct device *dev = &pdev->dev;
int ret = 0;
const char *buf;
struct cpuidle_driver *drv = cpuidle_get_driver();
slbc = devm_kzalloc(dev, sizeof(struct mtk_slbc), GFP_KERNEL);
if (!slbc)
return -ENOMEM;
slbc->dev = dev;
node = of_find_compatible_node(NULL, NULL,
"mediatek,mtk-slbc");
if (node) {
ret = of_property_read_string(node,
"status", (const char **)&buf);
if (ret == 0) {
if (!strcmp(buf, "enable"))
slbc_enable = 1;
else
slbc_enable = 0;
}
pr_info("#@# %s(%d) slbc_enable %d\n", __func__, __LINE__,
slbc_enable);
} else
pr_info("find slbc node failed\n");
#if IS_ENABLED(CONFIG_PM_SLEEP)
slbc->ws = wakeup_source_register(NULL, "slbc");
if (!slbc->ws)
pr_debug("slbc wakelock register fail!\n");
#endif /* CONFIG_PM_SLEEP */
ret = slbc_create_debug_fs();
if (ret) {
pr_info("SLBC FAILED TO CREATE DEBUG FILESYSTEM (%d)\n", ret);
return ret;
}
if (drv)
slbc->slbc_qos_latency = drv->states[2].exit_latency - 10;
else
slbc->slbc_qos_latency = 300;
pr_info("slbc_qos_latency %dus\n", slbc->slbc_qos_latency);
cpu_latency_qos_add_request(&slbc_qos_request,
PM_QOS_DEFAULT_VALUE);
timer_setup(&slbc_deactivate_timer, slbc_deactivate_timer_fn,
TIMER_DEFERRABLE);
slbc_register_common_ops(&common_ops);
return 0;
}
static int slbc_remove(struct platform_device *pdev)
{
slbc_unregister_common_ops(&common_ops);
return 0;
}
static int slbc_suspend(struct platform_device *pdev, pm_message_t state)
{
return 0;
}
static int slbc_resume(struct platform_device *pdev)
{
return 0;
}
static const struct of_device_id slbc_of_match[] = {
{ .compatible = "mediatek,mtk-slbc", },
{}
};
static struct platform_driver slbc_pdrv = {
.probe = slbc_probe,
.remove = slbc_remove,
.suspend = slbc_suspend,
.resume = slbc_resume,
.driver = {
.name = "slbc",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(slbc_of_match),
},
};
struct platform_device slbc_pdev = {
.name = "slbc",
.id = -1,
};
int __init slbc_module_init(void)
{
int r;
/* register platform device/driver */
r = platform_device_register(&slbc_pdev);
if (r) {
pr_info("Failed to register platform device.\n");
return -EINVAL;
}
r = platform_driver_register(&slbc_pdrv);
if (r) {
pr_info("fail to register slbc driver @ %s()\n", __func__);
return -EINVAL;
}
return 0;
}
late_initcall(slbc_module_init);
MODULE_DESCRIPTION("SLBC Driver mt6893 v0.1");
MODULE_LICENSE("GPL");