kernel-brax3-ubuntu-touch/drivers/misc/mediatek/apusys/edma/edma_cmd_hnd.c
erascape f319b992b1 kernel-5.15: Initial import brax3 UT kernel
* halium configs enabled

Signed-off-by: erascape <erascape@proton.me>
2025-09-23 15:17:10 +00:00

267 lines
5.9 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/kthread.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/sched/clock.h>
#include "edma_driver.h"
#include "edma_cmd_hnd.h"
#include "apusys_power.h"
#include "edma_dbgfs.h"
#include "edma_plat_internal.h"
#define EDMA_POWEROFF_TIME_DEFAULT 2000
static inline void lock_command(struct edma_sub *edma_sub)
{
mutex_lock(&edma_sub->cmd_mutex);
edma_sub->is_cmd_done = false;
}
static inline int wait_command(struct edma_sub *edma_sub, u32 timeout)
{
return (wait_event_interruptible_timeout(edma_sub->cmd_wait,
edma_sub->is_cmd_done,
msecs_to_jiffies
(timeout)) > 0)
? 0 : -ETIMEDOUT;
}
static inline void unlock_command(struct edma_sub *edma_sub)
{
mutex_unlock(&edma_sub->cmd_mutex);
}
/**
* @brief excute request by each edma_sub
* parameters:
* edma_sub: include each edma base address
*/
int edma_ext_by_sub(struct edma_sub *edma_sub, struct edma_request *req)
{
int ret = 0;
unsigned long long get_port_time = 0;
struct edma_plat_drv *drv = (struct edma_plat_drv *)edma_sub->plat_drv;
ret = edma_power_on(edma_sub);
if (ret != 0)
return ret;
get_port_time = sched_clock();
lock_command(edma_sub);
if (drv)
ret = drv->exe_sub(edma_sub, req);
ret = wait_command(edma_sub, CMD_WAIT_TIME_MS);
if (ret) {
pr_notice
("%s:timeout\n", __func__);
if (drv)
drv->prt_error(edma_sub, req);
}
get_port_time = sched_clock() - get_port_time; //ns
edma_sub->ip_time = get_port_time/1000;
unlock_command(edma_sub);
edma_power_off(edma_sub, 0);
return ret;
}
bool edma_is_all_power_off(struct edma_device *edma_device)
{
int i;
for (i = 0; i < edma_device->edma_sub_num; i++)
if (edma_device->edma_sub[i]->power_state == EDMA_POWER_ON)
return false;
return true;
}
int edma_power_on(struct edma_sub *edma_sub)
{
struct edma_device *edma_device;
int ret = 0;
edma_device = edma_sub->edma_device;
mutex_lock(&edma_device->power_mutex);
if (edma_is_all_power_off(edma_device)) {
if (timer_pending(&edma_device->power_timer)) {
del_timer(&edma_device->power_timer);
LOG_DBG("%s deltimer no pwr on, state = %d\n",
__func__, edma_device->power_state);
} else if (edma_device->power_state ==
EDMA_POWER_ON) {
LOG_ERR("%s power on twice\n", __func__);
} else {
ret = apu_device_power_on(EDMA);
if (!ret) {
LOG_DBG("%s power on success\n", __func__);
edma_device->power_state = EDMA_POWER_ON;
} else {
LOG_ERR("%s power on fail\n",
__func__);
mutex_unlock(&edma_device->power_mutex);
return ret;
}
}
}
edma_sub->power_state = EDMA_POWER_ON;
mutex_unlock(&edma_device->power_mutex);
return ret;
}
void edma_start_power_off(struct work_struct *work)
{
int ret = 0;
struct edma_device *edmaDev = NULL;
edmaDev =
container_of(work, struct edma_device, power_off_work);
LOG_DBG("%s: contain power_state = %d!!\n", __func__,
edmaDev->power_state);
if (edmaDev->power_state == EDMA_POWER_OFF)
LOG_ERR("%s pwr off twice\n",
__func__);
mutex_lock(&edmaDev->power_mutex);
ret = apu_device_power_off(EDMA);
if (ret != 0) {
LOG_ERR("%s power off fail\n", __func__);
} else {
pr_notice("%s: power off done!!\n", __func__);
edmaDev->power_state = EDMA_POWER_OFF;
}
mutex_unlock(&edmaDev->power_mutex);
}
void edma_power_time_up(struct timer_list *tlist)
{
//struct edma_device *edma_device = (struct edma_device *)data;
struct edma_device *edma_device =
container_of(tlist, struct edma_device, power_timer);
pr_notice("%s: !!\n", __func__);
//use kwork job to prevent power off at irq
schedule_work(&edma_device->power_off_work);
}
int edma_power_off(struct edma_sub *edma_sub, u8 force)
{
struct edma_device *edma_device =
edma_sub->edma_device;
int ret = 0;
if (edma_device->dbg_cfg
& EDMA_DBG_DISABLE_PWR_OFF) {
pr_notice("%s:no power off!!\n", __func__);
return 0;
}
mutex_lock(&edma_device->power_mutex);
edma_sub->power_state = EDMA_POWER_OFF;
if (edma_is_all_power_off(edma_device)) {
if (timer_pending(&edma_device->power_timer))
del_timer(&edma_device->power_timer);
if (force == 1) {
if (edma_device->power_state != EDMA_POWER_OFF) {
ret = apu_device_power_suspend(EDMA, 1);
pr_notice("%s: force power off!!\n", __func__);
if (!ret) {
LOG_INF("%s power off success\n",
__func__);
edma_device->power_state =
EDMA_POWER_OFF;
} else {
LOG_ERR("%s power off fail\n",
__func__);
}
} else
LOG_INF("%s force power off skip\n",
__func__);
} else {
edma_device->power_timer.expires = jiffies +
msecs_to_jiffies(EDMA_POWEROFF_TIME_DEFAULT);
add_timer(&edma_device->power_timer);
}
}
mutex_unlock(&edma_device->power_mutex);
return ret;
}
#ifndef EDMA_IOCTRL
void edma_setup_ext_mode_request(struct edma_request *req,
struct edma_ext *edma_ext,
unsigned int type)
{
req->handle = (u64) req;
req->cmd = type;
req->ext_reg_addr = edma_ext->reg_addr;
req->ext_count = edma_ext->count;
req->fill_value = edma_ext->fill_value;
req->desp_iommu_en = edma_ext->desp_iommu_en;
req->cmd_result = 0;
}
#endif
int edma_execute(struct edma_sub *edma_sub, struct edma_ext *edma_ext)
{
int ret = 0;
struct edma_request req = {0};
#ifdef DEBUG
uint32_t t1, t2;
uint32_t exe_time;
t1 = ktime_get_ns();
#endif
edma_setup_ext_mode_request(&req, edma_ext, EDMA_PROC_EXT_MODE);
ret = edma_ext_by_sub(edma_sub, &req);
#ifdef DEBUG
t2 = ktime_get_ns();
exe_time = (t2 - t1)/1000;
//pr_notice("%s:ip time = %d\n", __func__, edma_sub->ip_time);
LOG_DBG("%s:func done[%d], exe_time = %d, ip time = %d\n",
__func__, ret, exe_time, edma_sub->ip_time);
#endif
return ret;
}