1
0
mirror of https://github.com/physwizz/a155-U-u1.git synced 2025-09-26 19:04:54 +00:00
Files
physwizz 99537be4e2 first
2024-03-11 06:53:12 +11:00

588 lines
14 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020 MediaTek Inc.
*/
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/iommu.h>
#include <linux/delay.h>
#include <mt-plat/aee.h>
#include "apu.h"
#include "apu_excep.h"
#include "apu_config.h"
#include "apusys_secure.h"
#include "hw_logger.h"
#include "apu_regdump.h"
static const uint32_t TaskContext[] = {
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // GPR
0x0, // PC
0x0, // SP
0x300, // CSR
0x341, // CSR
0x301, // CSR
0x315, // CSR
0x7c0, // CSR
0xb00, // CSR
0xc01, // CSR
0xb02, // CSR
0xb80, // CSR
0xc81, // CSR
0xb82, // CSR
0xbc0, // CSR
0xbd0, // CSR
0xf11, // CSR
0xf12, // CSR
0xf13, // CSR
0xf14, // CSR
0xfc1, // CSR
0xfc2, // CSR
0xfc3, // CSR
0xfc4, // CSR
0x5c0, // CSR
0x5d0, // CSR
0x5d1, // CSR
0x5d2, // CSR
0x5d3, // CSR
0x5d4, // CSR
0x5d5, // CSR
0x5d6, // CSR
0x5d7, // CSR
0x5d8, // CSR
0x5d9, // CSR
0x5da, // CSR
0x5db, // CSR
0x5dc, // CSR
0x5dd, // CSR
0x5de, // CSR
0x5df, // CSR
0x5e0, // CSR
0x5e1, // CSR
0x5e2, // CSR
0x5e3, // CSR
0x5e4, // CSR
0x5e5, // CSR
0x5e6, // CSR
0x5e7, // CSR
0x5e8, // CSR
0x5e9, // CSR
0x5ea, // CSR
0x5eb, // CSR
0x5ec, // CSR
0x5ed, // CSR
0x5ee, // CSR
0x5ef, // CSR
0x5f0, // CSR
0x5f1, // CSR
0x5f2, // CSR
0x5f3, // CSR
0x5f4, // CSR
0x5f5, // CSR
0x5f6, // CSR
0x5f7, // CSR
0x302, // CSR
0x303, // CSR
0x304, // CSR
0x344, // CSR
0x9c0, // CSR
0x9dc, // CSR
0x9dd, // CSR
0x9de, // CSR
0x9df, // CSR
0x9e0, // CSR
0x9e1, // CSR
0x9e2, // CSR
0x9e3, // CSR
0x9e4, // CSR
0x9e5, // CSR
0x9e6, // CSR
0x9e7, // CSR
0x9e8, // CSR
0x9e9, // CSR
0x9ea, // CSR
0x9eb, // CSR
0x9ec, // CSR
0x9ed, // CSR
0x9ee, // CSR
0x9ef, // CSR
0x9f0, // CSR
0x9f1, // CSR
0x9f2, // CSR
0x9f3, // CSR
0x9f4, // CSR
0x9f5, // CSR
0x9f6, // CSR
0x9f7, // CSR
0x9f8, // CSR
0x9f9, // CSR
0x9fa, // CSR
0x9fb, // CSR
0x9fc, // CSR
0x9fd, // CSR
0x9fe, // CSR
0x9ff, // CSR
0x7d0, // CSR
0x7d1, // CSR
0x7d2, // CSR
0x7d3, // CSR
0x7d4, // CSR
0x7d8, // CSR
0x7d9, // CSR
0x7da, // CSR
0x7db, // CSR
0x340, // CSR
0x342, // CSR
0x343, // CSR
0x7c5, // CSR
0x7c6, // CSR
};
enum apusys_assert_module {
assert_apusys_rv = 0,
assert_apusys_power,
assert_apusys_middleware,
assert_apusys_edma,
assert_apusys_mdla,
assert_apusys_mvpu,
assert_apusys_reviser,
assert_apusys_devapc,
assert_apusys_mnoc,
assert_apusys_qos,
assert_module_max,
};
static const char * const apusys_assert_module_name[assert_module_max] = {
"APUSYS_RV",
"APUSYS_POWER",
"APUSYS_MIDDLEWARE",
"APUSYS_EDMA",
"APUSYS_MDLA",
"APUSYS_MVPU",
"APUSYS_REVISER",
"APUSYS_DEVAPC",
"APUSYS_MNOC",
"APUSYS_QOS",
};
struct apu_coredump_work_struct {
struct mtk_apu *apu;
struct work_struct work;
};
static struct apu_coredump_work_struct apu_coredump_work;
static void apu_do_tcmdump(struct mtk_apu *apu)
{
struct apu_coredump *coredump =
(struct apu_coredump *) apu->coredump_buf;
memcpy(coredump->tcmdump, (char *) apu->md32_tcm, TCM_SIZE);
}
static void apu_do_ramdump(struct mtk_apu *apu)
{
struct apu_coredump *coredump =
(struct apu_coredump *) apu->coredump_buf;
/* first 128kB is only for bootstrap */
memcpy(coredump->ramdump,
(char *) apu->code_buf + DRAM_DUMP_OFFSET, DRAM_DUMP_SIZE);
}
static void dbg_apb_dw(struct mtk_apu *apu, uint32_t dbg_reg, uint32_t val)
{
unsigned long flags;
spin_lock_irqsave(&apu->reg_lock, flags);
iowrite32(dbg_reg, apu->md32_debug_apb + DBG_INSTR);
iowrite32(0x1, apu->md32_debug_apb + DBG_INSTR_WR);
iowrite32(val, apu->md32_debug_apb + DBG_WDATA);
iowrite32(0x1, apu->md32_debug_apb + DBG_WDATA_WR);
spin_unlock_irqrestore(&apu->reg_lock, flags);
}
static void dbg_apb_iw(struct mtk_apu *apu, uint32_t dbg_cmd)
{
unsigned long flags;
spin_lock_irqsave(&apu->reg_lock, flags);
iowrite32(dbg_cmd, apu->md32_debug_apb + DBG_INSTR);
iowrite32(0x1, apu->md32_debug_apb + DBG_INSTR_WR);
spin_unlock_irqrestore(&apu->reg_lock, flags);
}
static uint32_t dbg_apb_dr(struct mtk_apu *apu, uint32_t dbg_reg)
{
unsigned long flags;
uint32_t ret;
spin_lock_irqsave(&apu->reg_lock, flags);
iowrite32(dbg_reg, apu->md32_debug_apb + DBG_INSTR);
iowrite32(0x1, apu->md32_debug_apb + DBG_INSTR_WR);
ret = ioread32(apu->md32_debug_apb + DBG_RDATA);
spin_unlock_irqrestore(&apu->reg_lock, flags);
return ret;
}
static uint32_t dbg_read_csr(struct mtk_apu *apu, uint32_t csr_id)
{
dbg_apb_dw(apu, DBG_INSTR_REG_INSTR, 0x2473 | (csr_id << 20));
dbg_apb_iw(apu, DBG_EXECUTE_INSTR);
dbg_apb_dw(apu, DBG_INSTR_REG_INSTR, 0x7DF41073);
dbg_apb_iw(apu, DBG_EXECUTE_INSTR);
return dbg_apb_dr(apu, DBG_DATA_REG_INSTR);
}
static uint32_t apusys_rv_smc_call(struct device *dev, uint32_t smc_id,
uint32_t a2)
{
struct arm_smccc_res res;
dev_info(dev, "%s: smc call %d\n",
__func__, smc_id);
arm_smccc_smc(MTK_SIP_APUSYS_CONTROL, smc_id,
a2, 0, 0, 0, 0, 0, &res);
if (smc_id == MTK_APUSYS_KERNEL_OP_APUSYS_RV_DBG_APB_ATTACH)
dev_info(dev, "%s: smc call return(0x%x)\n",
__func__, res.a0);
else if (((int) res.a0) < 0)
dev_info(dev, "%s: smc call %d return error(%d)\n",
__func__, smc_id, res.a0);
return res.a0;
}
static void apu_coredump_work_func(struct work_struct *p_work)
{
unsigned long flags;
int i, j;
struct apu_coredump_work_struct *apu_coredump_work =
container_of(p_work, struct apu_coredump_work_struct, work);
struct mtk_apu *apu = apu_coredump_work->apu;
struct device *dev = apu->dev;
struct mtk_apu_hw_ops *hw_ops = &apu->platdata->ops;
struct apu_coredump *coredump =
(struct apu_coredump *) apu->coredump_buf;
uint32_t pc, lr, sp;
uint32_t reg_dump[REG_SIZE/sizeof(uint32_t)];
uint32_t tbuf_dump[TBUF_SIZE/sizeof(uint32_t)];
uint32_t tbuf_cur_ptr;
uint32_t status, data, timeout;
uint32_t val;
uint32_t *ptr;
/* bypass AP coredump flow if wdt timeout
* is triggered by uP exception
*/
if (apu->conf_buf->ramdump_type == 0x1) {
if (apu->conf_buf->ramdump_module >= assert_module_max) {
dev_info(dev, "%s: ramdump_module(%u) >= assert_module_max(%u)\n",
__func__, apu->conf_buf->ramdump_module, assert_module_max);
apu->conf_buf->ramdump_module = 0;
}
if ((apu->platdata->flags & F_SECURE_COREDUMP)) {
apusys_rv_smc_call(dev,
MTK_APUSYS_KERNEL_OP_APUSYS_RV_COREDUMP_SHADOW_COPY, 0);
/* gating md32 cg for cache dump */
apusys_rv_smc_call(dev,
MTK_APUSYS_KERNEL_OP_APUSYS_RV_CG_GATING, 0);
apusys_rv_smc_call(dev,
MTK_APUSYS_KERNEL_OP_APUSYS_RV_CACHEDUMP, 0);
apusys_rv_smc_call(dev,
MTK_APUSYS_KERNEL_OP_APUSYS_RV_CLEAR_WDT_ISR, 0);
}
apu_regdump();
apusys_rv_aee_warn(apusys_assert_module_name[apu->conf_buf->ramdump_module],
"APUSYS_RV_EXCEPTION");
dev_info(dev, "%s: done\n", __func__);
return;
}
if ((apu->platdata->flags & F_SECURE_COREDUMP)) {
apusys_rv_smc_call(dev,
MTK_APUSYS_KERNEL_OP_APUSYS_RV_TCMDUMP, 0);
apusys_rv_smc_call(dev,
MTK_APUSYS_KERNEL_OP_APUSYS_RV_RAMDUMP, 0);
apusys_rv_smc_call(dev,
MTK_APUSYS_KERNEL_OP_APUSYS_RV_TBUFDUMP, 0);
/* ungate md32 cg for debug apb connection */
apusys_rv_smc_call(dev,
MTK_APUSYS_KERNEL_OP_APUSYS_RV_CG_UNGATING, 0);
status = apusys_rv_smc_call(dev,
MTK_APUSYS_KERNEL_OP_APUSYS_RV_DBG_APB_ATTACH, 0);
apusys_rv_smc_call(dev,
MTK_APUSYS_KERNEL_OP_APUSYS_RV_REGDUMP, (status & 0x1));
/* gating md32 cg for cache dump */
apusys_rv_smc_call(dev,
MTK_APUSYS_KERNEL_OP_APUSYS_RV_CG_GATING, 0);
apusys_rv_smc_call(dev,
MTK_APUSYS_KERNEL_OP_APUSYS_RV_CACHEDUMP, 0);
apusys_rv_smc_call(dev,
MTK_APUSYS_KERNEL_OP_APUSYS_RV_CLEAR_WDT_ISR, 0);
} else {
pc = ioread32(apu->md32_sysctrl + MON_PC);
lr = ioread32(apu->md32_sysctrl + MON_LR);
sp = ioread32(apu->md32_sysctrl + MON_SP);
reg_dump[0] = REG_SIZE; // reg dump size
reg_dump[1] = lr;
reg_dump[2] = sp;
reg_dump[32] = pc;
reg_dump[33] = sp;
apu_do_tcmdump(apu);
apu_do_ramdump(apu);
tbuf_cur_ptr = (ioread32(apu->md32_sysctrl + MD32_STATUS) >> 16 & 0x7);
for (i = 0; i < 8; i++) {
spin_lock_irqsave(&apu->reg_lock, flags);
iowrite32(tbuf_cur_ptr, apu->md32_sysctrl + TBUF_DBG_SEL);
spin_unlock_irqrestore(&apu->reg_lock, flags);
if (tbuf_cur_ptr > 0)
tbuf_cur_ptr--;
else
tbuf_cur_ptr = 7;
for (j = 0; j < 4; j++) {
tbuf_dump[i*4 + j] =
ioread32(apu->md32_sysctrl +
TBUF_DBG_DAT3 - j*4);
}
}
memcpy(coredump->tbufdump, tbuf_dump, sizeof(tbuf_dump));
spin_lock_irqsave(&apu->reg_lock, flags);
/* ungate md32 cg for debug apb connection */
if (!hw_ops->cg_ungating) {
spin_unlock_irqrestore(&apu->reg_lock, flags);
WARN_ON(1);
return;
}
hw_ops->cg_ungating(apu);
/* set DBG_EN */
iowrite32(0x1, apu->md32_debug_apb + DBG_EN);
/* set DBG MODE */
iowrite32(0x0, apu->md32_debug_apb + DBG_MODE);
spin_unlock_irqrestore(&apu->reg_lock, flags);
/* ATTACH */
dbg_apb_iw(apu, DBG_ATTACH_INSTR);
/* REQUEST */
dbg_apb_iw(apu, DBG_REQUEST_INSTR);
/* Read DBG STATUS Register */
status = dbg_apb_dr(apu, DBG_STATUS_REG_INSTR);
dev_info(dev, "%s: status = 0x%x\n", __func__, status);
timeout = 0;
/* Check if RV33 go into DEBUG mode */
while ((status & 0x1) != 0x1) {
udelay(10);
/* Read DBG STATUS Register */
status = dbg_apb_dr(apu, DBG_STATUS_REG_INSTR);
if (timeout++ > 100) {
dev_info(dev, "%s: timeout\n", __func__);
break;
}
}
if ((status & 0x1) == 0x1) {
reg_dump[0] = REG_SIZE; // reg dump size
/* Read GPRs */
for (i = 1; i < 32; i++) {
val = 0x7DF01073 | ((uint32_t) i << 15);
dbg_apb_dw(apu, DBG_INSTR_REG_INSTR, val);
dbg_apb_iw(apu, DBG_EXECUTE_INSTR);
reg_dump[i] = dbg_apb_dr(apu, DBG_DATA_REG_INSTR);
}
/* Read CSRs */
for (i = 34; i < REG_SIZE/sizeof(uint32_t); i++)
reg_dump[i] = dbg_read_csr(apu, TaskContext[i]);
}
if ((status & 0x1) == 0x1) { /* Read Cache Data */
dbg_apb_dw(apu, DBG_ADDR_REG_INSTR, DRAM_DUMP_OFFSET);
ptr = (uint32_t *) coredump->ramdump;
/* read one word per loop */
for (i = 0; i < DRAM_DUMP_SIZE/sizeof(uint32_t); i++) {
dbg_apb_iw(apu, DBG_READ_DM);
data = dbg_apb_dr(apu, DBG_DATA_REG_INSTR);
ptr[i] = data;
}
}
if (!hw_ops->cg_gating) {
WARN_ON(1);
return;
}
/* freeze md32 by gating cg */
hw_ops->cg_gating(apu);
if (!hw_ops->rv_cachedump) {
WARN_ON(1);
return;
}
hw_ops->rv_cachedump(apu);
memcpy(coredump->regdump, reg_dump, sizeof(reg_dump));
dsb(SY); /* may take lots of time */
}
apu_regdump();
apusys_rv_aee_warn("APUSYS_RV", "APUSYS_RV_TIMEOUT");
dev_info(dev, "%s: done\n", __func__);
}
static irqreturn_t apu_wdt_isr(int irq, void *private_data)
{
unsigned long flags;
struct mtk_apu *apu = (struct mtk_apu *) private_data;
struct device *dev = apu->dev;
struct mtk_apu_hw_ops *hw_ops = &apu->platdata->ops;
uint32_t val;
if ((apu->platdata->flags & F_SECURE_COREDUMP)) {
apusys_rv_smc_call(dev,
MTK_APUSYS_KERNEL_OP_APUSYS_RV_CG_GATING, 0);
apusys_rv_smc_call(dev,
MTK_APUSYS_KERNEL_OP_APUSYS_RV_DISABLE_WDT_ISR, 0);
} else {
val = ioread32(apu->apu_wdt);
if (val != 0x1) {
dev_info(dev, "%s: skip abnormal isr call(status = 0x%x)\n",
__func__, val);
return IRQ_HANDLED;
}
spin_lock_irqsave(&apu->reg_lock, flags);
/* freeze md32 by turn off cg */
if (!hw_ops->cg_gating) {
spin_unlock_irqrestore(&apu->reg_lock, flags);
WARN_ON(1);
return -EINVAL;
}
hw_ops->cg_gating(apu);
/* disable apu wdt */
iowrite32(ioread32(apu->apu_wdt + 4) &
(~(0x1U << 31)), apu->apu_wdt + 4);
/* clear wdt interrupt */
iowrite32(0x1, apu->apu_wdt);
spin_unlock_irqrestore(&apu->reg_lock, flags);
}
disable_irq_nosync(apu->wdt_irq_number);
dev_info(dev, "%s: disable wdt_irq(%d)\n", __func__,
apu->wdt_irq_number);
dev_info(dev, "%s\n", __func__);
schedule_work(&(apu_coredump_work.work));
return IRQ_HANDLED;
}
static int apu_wdt_irq_register(struct platform_device *pdev,
struct mtk_apu *apu)
{
struct device *dev = apu->dev;
int ret = 0;
apu->wdt_irq_number = platform_get_irq_byname(pdev, "apu_wdt");
dev_info(dev, "%s: wdt_irq_number = %d\n", __func__, apu->wdt_irq_number);
ret = devm_request_irq(&pdev->dev, apu->wdt_irq_number, apu_wdt_isr,
irq_get_trigger_type(apu->wdt_irq_number),
"apusys_wdt", apu);
if (ret < 0)
dev_info(dev, "%s: devm_request_irq Failed to request irq %d: %d\n",
__func__, apu->wdt_irq_number, ret);
return ret;
}
int apu_excep_init(struct platform_device *pdev, struct mtk_apu *apu)
{
int ret = 0;
INIT_WORK(&(apu_coredump_work.work), &apu_coredump_work_func);
apu_coredump_work.apu = apu;
ret = apu_wdt_irq_register(pdev, apu);
if (ret < 0)
return ret;
return ret;
}
void apu_excep_remove(struct platform_device *pdev, struct mtk_apu *apu)
{
struct device *dev = apu->dev;
unsigned long flags;
if ((apu->platdata->flags & F_SECURE_COREDUMP)) {
apusys_rv_smc_call(dev,
MTK_APUSYS_KERNEL_OP_APUSYS_RV_DISABLE_WDT_ISR, 0);
apusys_rv_smc_call(dev,
MTK_APUSYS_KERNEL_OP_APUSYS_RV_CLEAR_WDT_ISR, 0);
} else {
spin_lock_irqsave(&apu->reg_lock, flags);
/* disable apu wdt */
iowrite32(ioread32(apu->apu_wdt + 4) &
(~(0x1U << 31)), apu->apu_wdt + 4);
/* clear wdt interrupt */
iowrite32(0x1, apu->apu_wdt);
spin_unlock_irqrestore(&apu->reg_lock, flags);
}
disable_irq(apu->wdt_irq_number);
dev_info(dev, "%s: disable wdt_irq(%d)\n", __func__,
apu->wdt_irq_number);
cancel_work_sync(&(apu_coredump_work.work));
}