unplugged-kernel/drivers/misc/mediatek/scp/cm4/v01/scp_excep.c

803 lines
20 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/vmalloc.h> /* needed by vmalloc */
#include <linux/sysfs.h>
#include <linux/device.h> /* needed by device_* */
#include <linux/workqueue.h>
#include <linux/io.h>
#include <linux/mutex.h>
#if IS_ENABLED(CONFIG_MTK_AEE_FEATURE)
#include <mt-plat/aee.h>
#endif
#include <linux/sched_clock.h>
#include <linux/ratelimit.h>
#include <linux/delay.h>
#include "scp_ipi.h"
#include "scp_helper.h"
#include "scp_excep.h"
#include "scp_feature_define.h"
#include "scp_l1c.h"
struct scp_aed_cfg {
int *log;
int log_size;
int *phy;
int phy_size;
char *detail;
struct MemoryDump *pMemoryDump;
int memory_dump_size;
};
struct scp_status_reg {
unsigned int pc;
unsigned int lr;
unsigned int psp;
unsigned int sp;
unsigned int m2h;
unsigned int h2m;
};
static unsigned char *scp_A_detail_buffer;
static unsigned char *scp_A_dump_buffer;
static unsigned char *scp_A_dump_buffer_last;
static unsigned int scp_A_dump_length;
static unsigned int scp_A_task_context_addr;
static struct scp_work_struct scp_aed_work;
static struct scp_status_reg scp_A_aee_status;
static struct mutex scp_excep_mutex;
static struct mutex scp_A_excep_dump_mutex;
int scp_ee_enable;
/* An ELF note in memory */
struct memelfnote {
const char *name;
int type;
unsigned int datasz;
void *data;
};
static int notesize(struct memelfnote *en)
{
int sz;
sz = sizeof(struct elf32_note);
sz += roundup((strlen(en->name) + 1), 4);
sz += roundup(en->datasz, 4);
return sz;
}
static uint8_t *storenote(struct memelfnote *men, uint8_t *bufp)
{
struct elf32_note en;
en.n_namesz = strlen(men->name) + 1;
en.n_descsz = men->datasz;
en.n_type = men->type;
memcpy(bufp, &en, sizeof(en));
bufp += sizeof(en);
memcpy(bufp, men->name, en.n_namesz);
bufp += en.n_namesz;
bufp = (uint8_t *) roundup((unsigned long)bufp, 4);
memcpy(bufp, men->data, men->datasz);
bufp += men->datasz;
bufp = (uint8_t *) roundup((unsigned long)bufp, 4);
return bufp;
}
static uint8_t *core_write_cpu_note(int cpu, struct elf32_phdr *nhdr,
uint8_t *bufp, enum scp_core_id id)
{
struct memelfnote notes;
struct elf32_prstatus prstatus;
char cpustr[16];
memset(&prstatus, 0, sizeof(struct elf32_prstatus));
snprintf(cpustr, sizeof(cpustr), "CPU%d", cpu);
/* set up the process status */
notes.name = cpustr;
notes.type = NT_PRSTATUS;
notes.datasz = sizeof(struct elf32_prstatus);
notes.data = &prstatus;
prstatus.pr_pid = cpu + 1;
if (scp_A_task_context_addr && (id == SCP_A_ID)) {
memcpy_from_scp((void *)&(prstatus.pr_reg),
(void *)(SCP_TCM + scp_A_task_context_addr),
sizeof(prstatus.pr_reg));
}
if (prstatus.pr_reg[15] == 0x0 && (id == SCP_A_ID))
prstatus.pr_reg[15] = readl(SCP_A_DEBUG_PC_REG);
if (prstatus.pr_reg[14] == 0x0 && (id == SCP_A_ID))
prstatus.pr_reg[14] = readl(SCP_A_DEBUG_LR_REG);
if (prstatus.pr_reg[13] == 0x0 && (id == SCP_A_ID))
prstatus.pr_reg[13] = readl(SCP_A_DEBUG_PSP_REG);
nhdr->p_filesz += notesize(&notes);
return storenote(&notes, bufp);
}
void exception_header_init(void *oldbufp, enum scp_core_id id)
{
struct elf32_phdr *nhdr, *phdr;
struct elf32_hdr *elf;
off_t offset = 0;
uint8_t *bufp = oldbufp;
uint32_t cpu;
uint32_t dram_size = 0;
/* NT_PRPSINFO */
struct elf32_prpsinfo prpsinfo;
struct memelfnote notes;
elf = (struct elf32_hdr *) bufp;
bufp += sizeof(struct elf32_hdr);
offset += sizeof(struct elf32_hdr);
elf_setup_eident(elf->e_ident, ELFCLASS32);
/*setup elf header*/
elf->e_type = ET_CORE;
elf->e_machine = EM_ARM;
elf->e_version = EV_CURRENT;
elf->e_entry = 0;
elf->e_phoff = sizeof(struct elf32_hdr);
elf->e_shoff = 0;
elf->e_flags = ELF_CORE_EFLAGS;
elf->e_ehsize = sizeof(struct elf32_hdr);
elf->e_phentsize = sizeof(struct elf32_phdr);
elf->e_phnum = 2;
elf->e_shentsize = 0;
elf->e_shnum = 0;
elf->e_shstrndx = 0;
nhdr = (struct elf32_phdr *) bufp;
bufp += sizeof(struct elf32_phdr);
offset += sizeof(struct elf32_phdr);
memset(nhdr, 0, sizeof(struct elf32_phdr));
nhdr->p_type = PT_NOTE;
phdr = (struct elf32_phdr *) bufp;
bufp += sizeof(struct elf32_phdr);
offset += sizeof(struct elf32_phdr);
phdr->p_flags = PF_R|PF_W|PF_X;
phdr->p_offset = CRASH_MEMORY_HEADER_SIZE;
phdr->p_vaddr = CRASH_MEMORY_OFFSET;
phdr->p_paddr = CRASH_MEMORY_OFFSET;
#if SCP_RECOVERY_SUPPORT
if ((int)scp_region_info_copy.ap_dram_size > 0)
dram_size = scp_region_info_copy.ap_dram_size;
#endif
phdr->p_filesz = CRASH_MEMORY_LENGTH + roundup(dram_size, 4);
phdr->p_memsz = CRASH_MEMORY_LENGTH + roundup(dram_size, 4);
phdr->p_align = 0;
phdr->p_type = PT_LOAD;
nhdr->p_offset = offset;
/* set up the process info */
notes.name = CORE_STR;
notes.type = NT_PRPSINFO;
notes.datasz = sizeof(struct elf32_prpsinfo);
notes.data = &prpsinfo;
memset(&prpsinfo, 0, sizeof(struct elf32_prpsinfo));
prpsinfo.pr_state = 0;
prpsinfo.pr_sname = 'R';
prpsinfo.pr_zomb = 0;
prpsinfo.pr_gid = prpsinfo.pr_uid = 0x0;
strlcpy(prpsinfo.pr_fname, "freertos8",
sizeof(prpsinfo.pr_fname));
strlcpy(prpsinfo.pr_psargs, "freertos8", ELF_PRARGSZ);
nhdr->p_filesz += notesize(&notes);
bufp = storenote(&notes, bufp);
/* Store pre-cpu backtrace */
for (cpu = 0; cpu < 1; cpu++)
bufp = core_write_cpu_note(cpu, nhdr, bufp, id);
}
/*dump scp reg*/
void scp_reg_copy(void *bufp)
{
struct scp_reg_dump_list *scp_reg;
uint32_t tmp;
scp_reg = (struct scp_reg_dump_list *) bufp;
/*setup scp reg*/
scp_reg->scp_reg_magic = 0xDEADBEEF;
scp_reg->ap_resource = readl(SCP_AP_RESOURCE);
scp_reg->bus_resource = readl(SCP_BUS_RESOURCE);
scp_reg->slp_protect = readl(SCP_SLP_PROTECT_CFG);
scp_reg->cpu_sleep_status = readl(SCP_CPU_SLEEP_STATUS);
scp_reg->clk_sw_sel = readl(SCP_CLK_SW_SEL);
scp_reg->clk_enable = readl(SCP_CLK_ENABLE);
scp_reg->clk_high_core = readl(SCP_CLK_HIGH_CORE_CG);
scp_reg->debug_wdt_sp = readl(SCP_WDT_SP);
scp_reg->debug_wdt_lr = readl(SCP_WDT_LR);
scp_reg->debug_wdt_psp = readl(SCP_WDT_PSP);
scp_reg->debug_wdt_pc = readl(SCP_WDT_PC);
scp_reg->debug_addr_s2r = readl(SCP_DEBUG_ADDR_S2R);
scp_reg->debug_addr_dma = readl(SCP_DEBUG_ADDR_DMA);
scp_reg->debug_addr_spi0 = readl(SCP_DEBUG_ADDR_SPI0);
scp_reg->debug_addr_spi1 = readl(SCP_DEBUG_ADDR_SPI1);
scp_reg->debug_addr_spi2 = readl(SCP_DEBUG_ADDR_SPI2);
scp_reg->debug_bus_status = readl(SCP_DEBUG_BUS_STATUS);
/* scp_reg->debug_infra_mon = readl(SCP_SYS_INFRA_MON); */
tmp = readl(SCP_BUS_CTRL)&(~dbg_irq_info_sel_mask);
writel(tmp | (0 << dbg_irq_info_sel_shift), SCP_BUS_CTRL);
scp_reg->infra_addr_latch = readl(SCP_DEBUG_IRQ_INFO);
writel(tmp | (1 << dbg_irq_info_sel_shift), SCP_BUS_CTRL);
scp_reg->ddebug_latch = readl(SCP_DEBUG_IRQ_INFO);
writel(tmp | (2 << dbg_irq_info_sel_shift), SCP_BUS_CTRL);
scp_reg->pdebug_latch = readl(SCP_DEBUG_IRQ_INFO);
writel(tmp | (3 << dbg_irq_info_sel_shift), SCP_BUS_CTRL);
scp_reg->pc_value = readl(SCP_DEBUG_IRQ_INFO);
scp_reg->scp_reg_magic_end = 0xDEADBEEF;
}
/*dump scp l1c header*/
void scp_sub_header_init(void *bufp)
{
struct scp_dump_header_list *scp_sub_head;
scp_sub_head = (struct scp_dump_header_list *) bufp;
/*setup scp reg*/
scp_sub_head->scp_head_magic = 0xDEADBEEF;
#if SCP_RECOVERY_SUPPORT
memcpy(&scp_sub_head->scp_region_info,
&scp_region_info_copy, sizeof(scp_region_info_copy));
#endif
scp_sub_head->scp_head_magic_end = 0xDEADBEEF;
}
/*
* return last lr for debugging
*/
uint32_t scp_dump_lr(void)
{
if (is_scp_ready(SCP_A_ID))
return readl(SCP_A_DEBUG_LR_REG);
else
return 0xFFFFFFFF;
}
/*
* return last pc for debugging
*/
uint32_t scp_dump_pc(void)
{
if (is_scp_ready(SCP_A_ID))
return readl(SCP_A_DEBUG_PC_REG);
else
return 0xFFFFFFFF;
}
/*
* dump scp register for debugging
*/
void scp_A_dump_regs(void)
{
uint32_t tmp;
if (is_scp_ready(SCP_A_ID)) {
pr_debug("[SCP]ready PC:0x%x,LR:0x%x,PSP:0x%x,SP:0x%x\n"
, readl(SCP_A_DEBUG_PC_REG), readl(SCP_A_DEBUG_LR_REG)
, readl(SCP_A_DEBUG_PSP_REG), readl(SCP_A_DEBUG_SP_REG));
} else {
pr_debug("[SCP]not ready PC:0x%x,LR:0x%x,PSP:0x%x,SP:0x%x\n"
, readl(SCP_A_DEBUG_PC_REG), readl(SCP_A_DEBUG_LR_REG)
, readl(SCP_A_DEBUG_PSP_REG), readl(SCP_A_DEBUG_SP_REG));
}
pr_debug("[SCP]GIPC 0x%x\n", readl(SCP_GIPC_IN_REG));
pr_debug("[SCP]BUS_CTRL 0x%x\n", readl(SCP_BUS_CTRL));
pr_debug("[SCP]SLEEP_STATUS 0x%x\n", readl(SCP_CPU_SLEEP_STATUS));
pr_debug("[SCP]INFRA_STATUS 0x%x\n", readl(INFRA_CTRL_STATUS));
pr_debug("[SCP]IRQ_STATUS 0x%x\n", readl(SCP_INTC_IRQ_STATUS));
pr_debug("[SCP]IRQ_ENABLE 0x%x\n", readl(SCP_INTC_IRQ_ENABLE));
pr_debug("[SCP]IRQ_SLEEP 0x%x\n", readl(SCP_INTC_IRQ_SLEEP));
pr_debug("[SCP]IRQ_STATUS_MSB 0x%x\n", readl(SCP_INTC_IRQ_STATUS_MSB));
pr_debug("[SCP]IRQ_ENABLE_MSB 0x%x\n", readl(SCP_INTC_IRQ_ENABLE_MSB));
pr_debug("[SCP]IRQ_SLEEP_MSB 0x%x\n", readl(SCP_INTC_IRQ_SLEEP_MSB));
pr_debug("[SCP]CLK_CTRL_SEL 0x%x\n", readl(SCP_CLK_SW_SEL));
pr_debug("[SCP]CLK_ENABLE 0x%x\n", readl(SCP_CLK_ENABLE));
pr_debug("[SCP]SLEEP_DEBUG 0x%x\n", readl(SCP_A_SLEEP_DEBUG_REG));
tmp = readl(SCP_BUS_CTRL)&(~dbg_irq_info_sel_mask);
writel(tmp | (0 << dbg_irq_info_sel_shift), SCP_BUS_CTRL);
pr_debug("[SCP]BUS:INFRA LATCH, 0x%x\n", readl(SCP_DEBUG_IRQ_INFO));
writel(tmp | (1 << dbg_irq_info_sel_shift), SCP_BUS_CTRL);
pr_debug("[SCP]BUS:DCACHE LATCH, 0x%x\n", readl(SCP_DEBUG_IRQ_INFO));
writel(tmp | (2 << dbg_irq_info_sel_shift), SCP_BUS_CTRL);
pr_debug("[SCP]BUS:ICACHE LATCH, 0x%x\n", readl(SCP_DEBUG_IRQ_INFO));
writel(tmp | (3 << dbg_irq_info_sel_shift), SCP_BUS_CTRL);
pr_debug("[SCP]BUS:PC LATCH, 0x%x\n", readl(SCP_DEBUG_IRQ_INFO));
}
/*
* save scp register when scp crash
* these data will be used to generate EE
*/
void scp_aee_last_reg(void)
{
pr_debug("[SCP] %s begins\n", __func__);
scp_A_aee_status.pc = readl(SCP_A_DEBUG_PC_REG);
scp_A_aee_status.lr = readl(SCP_A_DEBUG_LR_REG);
scp_A_aee_status.psp = readl(SCP_A_DEBUG_PSP_REG);
scp_A_aee_status.sp = readl(SCP_A_DEBUG_SP_REG);
scp_A_aee_status.m2h = readl(SCP_A_TO_HOST_REG);
scp_A_aee_status.h2m = readl(SCP_GIPC_IN_REG);
pr_debug("[SCP] %s ends\n", __func__);
}
/*
* this function need SCP to keeping awaken
* scp_crash_dump: dump scp tcm info.
* @param MemoryDump: scp dump struct
* @param scp_core_id: core id
* @return: scp dump size
*/
static unsigned int scp_crash_dump(struct MemoryDump *pMemoryDump,
enum scp_core_id id)
{
unsigned int lock;
unsigned int *reg;
unsigned int scp_dump_size;
unsigned int scp_awake_fail_flag;
#if SCP_RECOVERY_SUPPORT
uint32_t dram_start = 0;
#endif
uint32_t dram_size = 0;
/*flag use to indicate scp awake success or not*/
scp_awake_fail_flag = 0;
/*check SRAM lock ,awake scp*/
if (scp_awake_lock(id) == -1) {
pr_err("[SCP] %s: awake scp fail, scp id=%u\n", __func__, id);
scp_awake_fail_flag = 1;
}
reg = (unsigned int *) (scpreg.cfg + SCP_LOCK_OFS);
lock = *reg;
*reg &= ~SCP_TCM_LOCK_BIT;
dsb(SY);
if ((*reg & SCP_TCM_LOCK_BIT)) {
pr_debug("[SCP]unlock failed, skip dump\n");
return 0;
}
#if SCP_RECOVERY_SUPPORT
/* L1C support? */
if ((int)(scp_region_info_copy.ap_dram_size) <= 0) {
scp_dump_size = sizeof(struct MemoryDump) + CRASH_MEMORY_LENGTH;
} else {
dram_start = scp_region_info_copy.ap_dram_start;
dram_size = scp_region_info_copy.ap_dram_size;
scp_dump_size = sizeof(struct MemoryDump) + CRASH_MEMORY_LENGTH +
roundup(dram_size, 4);
}
#else
scp_dump_size = 0;
#endif
exception_header_init(pMemoryDump, id);
/* init sub header*/
scp_sub_header_init(&(pMemoryDump->scp_dump_header));
memcpy_from_scp((void *)&(pMemoryDump->memory),
(void *)(SCP_TCM + CRASH_MEMORY_OFFSET),
(SCP_A_TCM_SIZE - CRASH_MEMORY_OFFSET));
/* L1C support? */
if (dram_size) {
/* l1c enable,flua l1c */
scp_l1c_flua(SCP_DL1C);
scp_l1c_flua(SCP_IL1C);
udelay(10);
#if SCP_RECOVERY_SUPPORT
pr_debug("scp:scp_l1c_start_virt %p\n",
scp_l1c_start_virt);
/* copy dram data*/
memcpy((void *)&(pMemoryDump->scp_reg_dump),
scp_l1c_start_virt, dram_size);
/* dump scp reg */
#endif
scp_reg_copy((void *)(&pMemoryDump->scp_reg_dump) +
roundup(dram_size, 4));
} else {
/* dump scp reg */
scp_reg_copy(&(pMemoryDump->scp_reg_dump));
}
*reg = lock;
dsb(SY);
/*check SRAM unlock*/
if (scp_awake_fail_flag != 1) {
if (scp_awake_unlock(id) == -1)
pr_debug("[SCP]%s awake unlock fail, scp id=%u\n",
__func__, id);
}
return scp_dump_size;
}
/*
* generate aee argument without dump scp register
* @param aed_str: exception description
* @param aed: struct to store argument for aee api
*/
static void scp_prepare_aed(char *aed_str, struct scp_aed_cfg *aed)
{
char *detail, *log;
u8 *phy;
u32 log_size, phy_size;
int ret = 0;
pr_debug("[SCP] %s begins\n", __func__);
aed->detail = NULL;
detail = scp_A_detail_buffer;
if (!detail)
return;
memset(detail, 0, SCP_AED_STR_LEN);
ret = snprintf(detail, SCP_AED_STR_LEN, "%s\n", aed_str);
if (ret < 0)
pr_err("[SCP] detail msg is truncated\n");
detail[SCP_AED_STR_LEN - 1] = '\0';
log_size = 0;
log = NULL;
phy_size = 0;
phy = NULL;
aed->log = (int *)log;
aed->log_size = log_size;
aed->phy = (int *)phy;
aed->phy_size = phy_size;
aed->detail = detail;
pr_debug("[SCP] %s ends\n", __func__);
}
/*
* generate aee argument with scp register dump
* @param aed_str: exception description
* @param aed: struct to store argument for aee api
* @param id: identify scp core id
*/
static void scp_prepare_aed_dump(char *aed_str,
struct scp_aed_cfg *aed,
enum scp_core_id id)
{
u8 *scp_detail;
u8 *scp_dump_ptr;
int ret = 0;
u32 memory_dump_size;
struct MemoryDump *pMemoryDump = NULL;
char *scp_A_log = NULL;
pr_debug("[SCP] %s begins:%s\n", __func__, aed_str);
scp_aee_last_reg();
scp_A_log = scp_get_last_log(SCP_A_ID);
/* prepare scp aee detail information */
scp_detail = scp_A_detail_buffer;
if (!scp_detail) {
pr_err("[SCP AEE]detail buf is null\n");
} else {
/* prepare scp aee detail information*/
memset(scp_detail, 0, SCP_AED_STR_LEN);
ret = snprintf(scp_detail, SCP_AED_STR_LEN,
"%s\nscp_A pc=0x%08x, lr=0x%08x, psp=0x%08x, sp=0x%08x\n"
"last log:\n%s",
aed_str, scp_A_aee_status.pc,
scp_A_aee_status.lr,
scp_A_aee_status.psp,
scp_A_aee_status.sp,
scp_A_log);
if (ret < 0)
pr_err("[SCP AEE] detail buf msg is truncated\n");
scp_detail[SCP_AED_STR_LEN - 1] = '\0';
}
/*prepare scp A db file*/
memory_dump_size = 0;
scp_dump_ptr = scp_A_dump_buffer_last;
if (!scp_dump_ptr) {
pr_err("[SCP AEE]MemoryDump buf is null, size=0x%x\n",
memory_dump_size);
memory_dump_size = 0;
} else {
pr_debug("[SCP AEE]scp A dump ptr:%p\n", scp_dump_ptr);
pMemoryDump = (struct MemoryDump *) scp_dump_ptr;
memset(pMemoryDump, 0x0, sizeof(*pMemoryDump) + CRASH_MEMORY_LENGTH);
memory_dump_size = scp_crash_dump(pMemoryDump, SCP_A_ID);
}
/* scp_dump_buffer_set */
mutex_lock(&scp_A_excep_dump_mutex);
scp_A_dump_buffer_last = scp_A_dump_buffer;
scp_A_dump_buffer = scp_dump_ptr;
scp_A_dump_length = memory_dump_size;
mutex_unlock(&scp_A_excep_dump_mutex);
aed->log = NULL;
aed->log_size = 0;
aed->phy = NULL;
aed->phy_size = 0;
aed->detail = scp_detail;
aed->pMemoryDump = NULL;
aed->memory_dump_size = 0;
pr_debug("[SCP] %s ends\n", __func__);
}
/*
* generate an exception according to exception type
* @param type: exception type
*/
void scp_aed(enum scp_excep_id type, enum scp_core_id id)
{
struct scp_aed_cfg aed;
char *scp_aed_title;
mutex_lock(&scp_excep_mutex);
/* get scp title and exception type*/
switch (type) {
case EXCEP_LOAD_FIRMWARE:
scp_prepare_aed("scp firmware load exception", &aed);
if (id == SCP_A_ID)
scp_aed_title = "SCP_A load firmware exception";
else
scp_aed_title = "SCP load firmware exception";
break;
case EXCEP_RESET:
if (id == SCP_A_ID)
scp_aed_title = "SCP_A reset exception";
else
scp_aed_title = "SCP reset exception";
break;
case EXCEP_BOOTUP:
if (id == SCP_A_ID)
scp_aed_title = "SCP_A boot exception";
else
scp_aed_title = "SCP boot exception";
scp_get_log(id);
break;
case EXCEP_RUNTIME:
if (id == SCP_A_ID)
scp_aed_title = "SCP_A runtime exception";
else
scp_aed_title = "SCP runtime exception";
scp_get_log(id);
break;
default:
scp_prepare_aed("scp unknown exception", &aed);
if (id == SCP_A_ID)
scp_aed_title = "SCP_A unknown exception";
else
scp_aed_title = "SCP unknown exception";
break;
}
/*print scp message*/
pr_debug("scp_aed_title=%s", scp_aed_title);
if (type != EXCEP_LOAD_FIRMWARE)
scp_prepare_aed_dump(scp_aed_title, &aed, id);
/*print detail info. in kernel*/
pr_debug("%s", aed.detail);
#if IS_ENABLED(CONFIG_MTK_AEE_FEATURE)
/* scp aed api, only detail information available*/
aed_common_exception_api("scp", NULL, 0, NULL, 0,
aed.detail, DB_OPT_DEFAULT);
#endif
pr_debug("[SCP] scp exception dump is done\n");
mutex_unlock(&scp_excep_mutex);
}
/*
* generate an exception and reset scp right now
* NOTE: this function may be blocked and
* should not be called in interrupt context
* @param type: exception type
*/
void scp_aed_reset_inplace(enum scp_excep_id type, enum scp_core_id id)
{
pr_debug("[SCP] %s begins\n", __func__);
if (scp_ee_enable)
scp_aed(type, id);
else
pr_debug("[SCP]ee disable value=%d\n", scp_ee_enable);
#if (SCP_RECOVERY_SUPPORT == 0)
/* workaround for QA, not reset SCP in WDT */
if (type == EXCEP_RUNTIME)
return;
#endif
#if SCP_RECOVERY_SUPPORT
if (atomic_read(&scp_reset_status) == RESET_STATUS_START) {
/*complete scp ee, if scp reset by wdt or awake fail*/
pr_debug("[SCP]aed finished, complete it\n");
complete(&scp_sys_reset_cp);
}
#endif
}
/*
* callback function for work struct
* generate an exception and reset scp
* NOTE: this function may be blocked
* and should not be called in interrupt context
* @param ws: work struct
*/
static void scp_aed_reset_ws(struct work_struct *ws)
{
struct scp_work_struct *sws = container_of(ws,
struct scp_work_struct, work);
enum scp_excep_id type = (enum scp_excep_id) sws->flags;
enum scp_core_id id = (enum scp_core_id) sws->id;
pr_debug("[SCP] %s begins: scp_excep_id=%u scp_core_id=%u\n",
__func__, type, id);
scp_aed_reset_inplace(type, id);
}
/*
* schedule a work to generate an exception and reset scp
* @param type: exception type
*/
void scp_aed_reset(enum scp_excep_id type, enum scp_core_id id)
{
scp_aed_work.flags = (unsigned int) type;
scp_aed_work.id = (unsigned int) id;
scp_schedule_work(&scp_aed_work);
}
static ssize_t scp_A_dump_show(struct file *filep,
struct kobject *kobj, struct bin_attribute *attr,
char *buf, loff_t offset, size_t size)
{
unsigned int length = 0;
mutex_lock(&scp_A_excep_dump_mutex);
if (offset >= 0 && offset < scp_A_dump_length) {
if ((offset + size) > scp_A_dump_length)
size = scp_A_dump_length - offset;
memcpy(buf, scp_A_dump_buffer + offset, size);
length = size;
}
mutex_unlock(&scp_A_excep_dump_mutex);
return length;
}
struct bin_attribute bin_attr_scp_dump = {
.attr = {
.name = "scp_dump",
.mode = 0444,
},
.size = 0,
.read = scp_A_dump_show,
};
/*
* init a work struct
*/
int scp_excep_init(void)
{
int dram_size = 0;
mutex_init(&scp_excep_mutex);
mutex_init(&scp_A_excep_dump_mutex);
INIT_WORK(&scp_aed_work.work, scp_aed_reset_ws);
#if SCP_RECOVERY_SUPPORT
/* support L1C or not? */
if ((int)(scp_region_info->ap_dram_size) > 0)
dram_size = scp_region_info->ap_dram_size;
#else
dram_size = 0;
#endif
/* alloc dump memory */
scp_A_detail_buffer = vmalloc(SCP_AED_STR_LEN);
if (!scp_A_detail_buffer)
return -1;
scp_A_dump_buffer = vmalloc(sizeof(struct MemoryDump) +
CRASH_MEMORY_LENGTH + roundup(dram_size, 4));
if (!scp_A_dump_buffer)
goto _err;
scp_A_dump_buffer_last = vmalloc(sizeof(struct MemoryDump) +
CRASH_MEMORY_LENGTH + roundup(dram_size, 4));
if (!scp_A_dump_buffer_last)
goto _err1;
/* init global values */
scp_A_dump_length = 0;
/* 1: ee on, 0: ee disable */
scp_ee_enable = 1;
return 0;
_err1:
vfree(scp_A_dump_buffer);
_err:
vfree(scp_A_detail_buffer);
return -1;
}
/******************************************************************************
* This function is called in the interrupt context. Note that scp_region_info
* was initialized in scp_region_info_init() which must be called before this
* function is called.
*****************************************************************************/
void scp_ram_dump_init(void)
{
#if SCP_RECOVERY_SUPPORT
scp_A_task_context_addr = scp_region_info->TaskContext_ptr;
pr_debug("[SCP] get scp_A_task_context_addr: 0x%x\n",
scp_A_task_context_addr);
#endif
}
/*
* cleanup scp exception
*/
void scp_excep_cleanup(void)
{
vfree(scp_A_detail_buffer);
vfree(scp_A_dump_buffer_last);
vfree(scp_A_dump_buffer);
scp_A_task_context_addr = 0;
pr_debug("[SCP] %s ends\n", __func__);
}