kernel_samsung_a34x-permissive/drivers/misc/mediatek/apusys/edma/edma_v30.c
2024-04-28 15:51:13 +02:00

195 lines
5.4 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 "edma3_reg.h"
#include "apusys_power.h"
#include "edma_dbgfs.h"
#include "edma_plat_internal.h"
#define NO_INTERRUPT 0
#define EDMA_POWEROFF_TIME_DEFAULT 2000
void printV30_error_status(struct edma_sub *edma_sub,
struct edma_request *req)
{
u32 status, i, j;
unsigned int *ext_reg = NULL;
u32 portID = edma_sub->dbg_portID;
status = edma_read_reg32(edma_sub->base_addr, APU_EDMA3_ERR_STATUS);
pr_notice("%s error status %x\n", edma_sub->sub_name,
status);
for (i = 0; i < (EDMA30_REG_SHOW_RANGE >> 2); i++) {
status = edma_read_reg32(edma_sub->base_addr, i*4);
pr_notice("edma error dump register[0x%x] = 0x%x\n",
i*4, status);
}
pr_notice("---------------- dump port [%d] registers ----------------\n"
, portID);
for (i = ((0x800+portID*0x100)/4); i < ((0x900+portID*0x100)/4); i++) {
status = edma_read_reg32(edma_sub->base_addr, i*4);
pr_notice("edma error dump register[0x%x] = 0x%x\n",
i*4, status);
}
if (req->ext_reg_addr != 0) {
ext_reg = (unsigned int *)
apusys_mem_query_kva(req->ext_reg_addr);
pr_notice("kva ext_reg = 0x%p, req->ext_count = %d\n",
ext_reg, req->ext_count);
if (ext_reg != 0)
for (i = 0; i < req->ext_count; i++) {
for (j = 0; j < EDMA_EXT_MODE_SIZE/4; j++)
pr_notice("descriptor%d [0x%x] = 0x%x\n",
i, j*4, *(ext_reg + j));
}
else
pr_notice("not support ext_reg dump!!\n");
}
}
irqreturn_t edmaV30_isr_handler(int irq, void *edma_sub_info)
{
struct edma_sub *edma_sub = (struct edma_sub *)edma_sub_info;
u32 status;
u32 portID = edma_sub->dbg_portID;
status = edma_read_reg32(edma_sub->base_addr, APU_EDMA3_DONE_STATUS);
//pr_notice("%s in, done status = 0x%x!!\r\n", __func__, status);
//status = edma_read_reg32(edma_sub->base_addr, 0x814+portID*0x100);
//pr_notice("status portID[%d][0x%x] = 0x%x!!\r\n", portID, 0x814+portID*0x100, status);
//status = edma_read_reg32(edma_sub->base_addr, APU_EDMA3_ERR_STATUS);
//pr_notice("APU_EDMA3_ERR_STATUS = 0x%x ", status);
/* if ((*(uint32_t*)((uint8_t*)edma_sub->base_addr + 0x01c)) & 0x1) { */
/* check port 5 low channel ==> lo5 = 0x20 */
/* {hi7, hi6, hi5, hi4, hi3, hi2, hi1, hi0, lo7, lo6, lo5, lo4, lo3, lo2, lo1, lo0} */
if (edma_read_reg32(edma_sub->base_addr, APU_EDMA3_DONE_STATUS) & (0x1 << portID)) {
edma_sub->is_cmd_done = true;
wake_up_interruptible(&edma_sub->cmd_wait);
} else if (edma_read_reg32(edma_sub->base_addr, APU_EDMA3_ERR_STATUS) != 0) {
status = edma_read_reg32(edma_sub->base_addr, APU_EDMA3_ERR_STATUS);
pr_notice("APU_EDMA3_ERR_STATUS = 0x%x, clear it... ", status);
edma_write_reg32(edma_sub->base_addr, APU_EDMA3_ERR_STATUS, status);
edma_sub->is_cmd_done = true;
wake_up_interruptible(&edma_sub->cmd_wait);
} else {
pr_notice("EDMA3_DONE_STATUS not done & not error...");
edma_sub->is_cmd_done = true;
wake_up_interruptible(&edma_sub->cmd_wait);
}
/* *(uint32_t*)((uint8_t*)edma_sub->base_addr + 0x814) |= 0x1; */
edma_set_reg32(edma_sub->base_addr, 0x814+portID*0x100, 0x1);
return IRQ_HANDLED;
}
static void edmaV30_sw_reset(struct edma_sub *edma_sub)
{
unsigned long flags;
//pr_notice("%s: new init for edma 3.0\n", __func__);
spin_lock_irqsave(&edma_sub->reg_lock, flags);
edma_set_reg32(edma_sub->base_addr, 0x004, (0x1 << 0));
udelay(5);
edma_set_reg32(edma_sub->base_addr, 0x004, (0x1 << 4));
udelay(5);
edma_clear_reg32(edma_sub->base_addr, 0x004, (0x1 << 4));
spin_unlock_irqrestore(&edma_sub->reg_lock, flags);
//LOG_DBG("%s edma 3.0 skip sw reset\n", __func__);
}
/**
* @brief trigger edma external mode
* parameters:
* base_addr: each edma base address
* ext_addr: external descriptor mem addr
* num_desp: number of desc.
* desp_iommu_en: enable iommu or not
*/
void edmaV30_trigger_external(struct edma_sub *edma_sub, u32 ext_addr, u32 num_desp,
u8 desp_iommu_en)
{
unsigned long flags;
u32 portID = edma_sub->dbg_portID;
pr_notice("%s:port id == %d\n", __func__, portID);
spin_lock_irqsave(&edma_sub->reg_lock, flags);
if (desp_iommu_en)
edma_set_reg32(edma_sub->base_addr, 0x004, ((0x2 << 30) | (0x2 << 28)));
udelay(5);
edma_write_reg32(edma_sub->base_addr, 0x800+portID*0x100, ext_addr);
/* [28] irq_en, [20] vc_sw_rst, [19:0] num */
edma_write_reg32(edma_sub->base_addr, 0x804+portID*0x100,
((0x1 << 28) | ((num_desp << 0) & 0xfffff)));
/* [16] stop, [0] fire */
edma_write_reg32(edma_sub->base_addr, 0x808+portID*0x100, (0x1 << 0));
spin_unlock_irqrestore(&edma_sub->reg_lock, flags);
}
/**
* @brief excute request by each edma_sub
* parameters:
* edma_sub: include each edma base address
*/
int edma_exe_v30(struct edma_sub *edma_sub, struct edma_request *req)
{
int ret = 0;
void __iomem *base_addr;
base_addr = edma_sub->base_addr;
//edma_enable_sequence(edma_sub);
edmaV30_sw_reset(edma_sub); // no need in edma 3.0
edmaV30_trigger_external(edma_sub,
req->ext_reg_addr,
req->ext_count,
req->desp_iommu_en);
return ret;
}