c05564c4d8
Android 13
244 lines
6.2 KiB
C
Executable file
244 lines
6.2 KiB
C
Executable file
// 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 "edma_reg.h"
|
|
|
|
#include "apusys_power.h"
|
|
#include "edma_dbgfs.h"
|
|
#include "edma_plat_internal.h"
|
|
#include "apusys_dbg.h"
|
|
|
|
#define NO_INTERRUPT 0
|
|
#define EDMA_POWEROFF_TIME_DEFAULT 2000
|
|
|
|
static void edma_sw_reset(struct edma_sub *edma_sub);
|
|
|
|
void print_error_status(struct edma_sub *edma_sub,
|
|
struct edma_request *req)
|
|
{
|
|
u32 status, i, j;
|
|
unsigned int *ext_reg = NULL;
|
|
|
|
status = edma_read_reg32(edma_sub->base_addr, APU_EDMA2_ERR_STATUS);
|
|
pr_notice("%s error status %x\n", edma_sub->sub_name,
|
|
status);
|
|
|
|
for (i = 0; i < (EDMA_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);
|
|
}
|
|
|
|
for (i = (0xC00/4); i < (0xD00/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");
|
|
}
|
|
edma_sw_reset(edma_sub);
|
|
}
|
|
|
|
irqreturn_t edma_isr_handler(int irq, void *edma_sub_info)
|
|
{
|
|
struct edma_sub *edma_sub = (struct edma_sub *)edma_sub_info;
|
|
u32 status;
|
|
u32 desp0_done;
|
|
u32 desp0_intr;
|
|
u32 desp1_done;
|
|
u32 desp1_intr;
|
|
u32 desp2_done;
|
|
u32 desp2_intr;
|
|
u32 desp3_done;
|
|
u32 desp3_intr;
|
|
u32 desp4_done;
|
|
u32 desp4_intr;
|
|
|
|
status = edma_read_reg32(edma_sub->base_addr, APU_EDMA2_INT_STATUS);
|
|
desp0_done = status & DESP0_DONE_STATUS;
|
|
desp1_done = status & DESP1_DONE_STATUS;
|
|
desp2_done = status & DESP2_DONE_STATUS;
|
|
desp3_done = status & DESP3_DONE_STATUS;
|
|
desp4_done = status & EXT_DESP_DONE_STATUS;
|
|
desp0_intr = status & DESP0_DONE_INT_STATUS;
|
|
desp1_intr = status & DESP1_DONE_INT_STATUS;
|
|
desp2_intr = status & DESP2_DONE_INT_STATUS;
|
|
desp3_intr = status & DESP3_DONE_INT_STATUS;
|
|
desp4_intr = status & EXT_DESP_DONE_INT_STATUS;
|
|
|
|
edma_sub->is_cmd_done = true;
|
|
if (desp0_done | desp1_done | desp2_done | desp3_done | desp4_done)
|
|
wake_up_interruptible(&edma_sub->cmd_wait);
|
|
|
|
if (desp0_intr)
|
|
edma_set_reg32(edma_sub->base_addr, APU_EDMA2_INT_STATUS,
|
|
DESP0_DONE_INT_STATUS);
|
|
else if (desp1_intr)
|
|
edma_set_reg32(edma_sub->base_addr, APU_EDMA2_INT_STATUS,
|
|
DESP1_DONE_INT_STATUS);
|
|
else if (desp2_intr)
|
|
edma_set_reg32(edma_sub->base_addr, APU_EDMA2_INT_STATUS,
|
|
DESP2_DONE_INT_STATUS);
|
|
else if (desp3_intr)
|
|
edma_set_reg32(edma_sub->base_addr, APU_EDMA2_INT_STATUS,
|
|
DESP3_DONE_INT_STATUS);
|
|
else if (desp4_intr)
|
|
edma_set_reg32(edma_sub->base_addr, APU_EDMA2_INT_STATUS,
|
|
EXT_DESP_DONE_INT_STATUS);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
void edma_enable_sequence(struct edma_sub *edma_sub)
|
|
{
|
|
edma_set_reg32(edma_sub->base_addr, APU_EDMA2_CTL_0, CLK_ENABLE);
|
|
edma_set_reg32(edma_sub->base_addr, APU_EDMA2_CTL_0, DMA_SW_RST);
|
|
edma_clear_reg32(edma_sub->base_addr, APU_EDMA2_CTL_0, DMA_SW_RST);
|
|
}
|
|
|
|
static void edma_sw_reset(struct edma_sub *edma_sub)
|
|
{
|
|
u32 value = 0, count = 0;
|
|
unsigned long flags;
|
|
|
|
LOG_DBG("%s\n", __func__);
|
|
|
|
spin_lock_irqsave(&edma_sub->reg_lock, flags);
|
|
|
|
edma_set_reg32(edma_sub->base_addr, APU_EDMA2_CTL_0, CLK_ENABLE);
|
|
|
|
edma_set_reg32(edma_sub->base_addr, APU_EDMA2_CTL_0, AXI_PROT_EN);
|
|
|
|
while (!(value & RST_PROT_IDLE)) {
|
|
value = edma_read_reg32(edma_sub->base_addr, APU_EDMA2_CTL_0);
|
|
udelay(5);
|
|
count++;
|
|
if (count > 10000) {
|
|
u32 status, i;
|
|
|
|
spin_unlock_irqrestore(&edma_sub->reg_lock, flags);
|
|
|
|
/* dump error log */
|
|
pr_notice("hang on %s direct dump...\n", __func__);
|
|
for (i = 0; i < (EDMA_REG_SHOW_RANGE >> 2); i++) {
|
|
status = edma_read_reg32(edma_sub->base_addr,
|
|
i*4);
|
|
pr_notice("hang %s edma error dump [0x%x] = 0x%x\n",
|
|
__func__, i*4, status);
|
|
}
|
|
apusys_reg_dump();
|
|
|
|
/* continues do edma */
|
|
spin_lock_irqsave(&edma_sub->reg_lock, flags);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
LOG_DBG("value = 0x%x\n", value);
|
|
|
|
edma_set_reg32(edma_sub->base_addr, APU_EDMA2_CTL_0, DMA_SW_RST);
|
|
udelay(5);
|
|
edma_clear_reg32(edma_sub->base_addr, APU_EDMA2_CTL_0, DMA_SW_RST);
|
|
udelay(5);
|
|
edma_clear_reg32(edma_sub->base_addr, APU_EDMA2_CTL_0, AXI_PROT_EN);
|
|
|
|
spin_unlock_irqrestore(&edma_sub->reg_lock, flags);
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* @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 edma_trigger_external(void __iomem *base_addr, u32 ext_addr, u32 num_desp,
|
|
u8 desp_iommu_en)
|
|
{
|
|
edma_set_reg32(base_addr, APU_EDMA2_CTL_0, EDMA_DESCRIPTOR_MODE);
|
|
|
|
num_desp--;
|
|
if (desp_iommu_en)
|
|
edma_write_reg32(base_addr, APU_EDMA2_EXT_DESP_CFG_0,
|
|
EXT_DESP_INT_ENABLE | EXT_DESP_USER_IOMMU | num_desp);
|
|
else
|
|
edma_write_reg32(base_addr, APU_EDMA2_EXT_DESP_CFG_0,
|
|
EXT_DESP_INT_ENABLE | num_desp);
|
|
|
|
edma_write_reg32(base_addr, APU_EDMA2_EXT_DESP_CFG_1, ext_addr);
|
|
edma_set_reg32(base_addr, APU_EDMA2_CFG_0, EXT_DESP_START);
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief excute request by each edma_sub
|
|
* parameters:
|
|
* edma_sub: include each edma base address
|
|
*/
|
|
int edma_exe_v20(struct edma_sub *edma_sub, struct edma_request *req)
|
|
{
|
|
int ret = 0;
|
|
void __iomem *base_addr;
|
|
unsigned long flags;
|
|
|
|
|
|
base_addr = edma_sub->base_addr;
|
|
//edma_enable_sequence(edma_sub);
|
|
|
|
edma_sw_reset(edma_sub); // no need in edma 3.0
|
|
|
|
|
|
|
|
spin_lock_irqsave(&edma_sub->reg_lock, flags);
|
|
|
|
edma_write_reg32(base_addr, APU_EDMA2_FILL_VALUE, req->fill_value);
|
|
edma_trigger_external(edma_sub->base_addr,
|
|
req->ext_reg_addr,
|
|
req->ext_count,
|
|
req->desp_iommu_en);
|
|
|
|
spin_unlock_irqrestore(&edma_sub->reg_lock, flags);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|