660 lines
17 KiB
C
660 lines
17 KiB
C
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
//
|
||
|
// adsp_excep.c-- Mediatek ADSP exception handling
|
||
|
//
|
||
|
// Copyright (c) 2018 MediaTek Inc.
|
||
|
// Author: HsinYi Chang <hsin-yi.chang@mediatek.com>
|
||
|
|
||
|
#include <linux/vmalloc.h> /* needed by vmalloc */
|
||
|
#include <linux/slab.h> /* needed by kmalloc */
|
||
|
#include <linux/sysfs.h>
|
||
|
#include <linux/device.h> /* needed by device_* */
|
||
|
#include <linux/workqueue.h>
|
||
|
#include <linux/io.h>
|
||
|
#include <linux/mutex.h>
|
||
|
#include <mt-plat/aee.h>
|
||
|
#include <mt-plat/sync_write.h>
|
||
|
#include <linux/sched_clock.h>
|
||
|
#include <linux/ratelimit.h>
|
||
|
#include "adsp_ipi.h"
|
||
|
#include "adsp_helper.h"
|
||
|
#include "adsp_clk.h"
|
||
|
#include "adsp_excep.h"
|
||
|
#include "adsp_feature_define.h"
|
||
|
#include "adsp_reserved_mem.h"
|
||
|
|
||
|
static unsigned char *adsp_ke_buffer;
|
||
|
static unsigned char *adsp_A_dump_buffer;
|
||
|
unsigned char *adsp_A_dram_dump_buffer;
|
||
|
static struct adsp_work_t adsp_aed_work;
|
||
|
static struct mutex adsp_excep_mutex;
|
||
|
static struct mutex adsp_A_excep_dump_mutex;
|
||
|
static int adsp_A_dram_dump(void);
|
||
|
static ssize_t adsp_A_ramdump(char *buf, loff_t offset, size_t size);
|
||
|
|
||
|
#define ADSP_KE_DUMP_LEN (ADSP_A_CFG_SIZE + ADSP_A_TCM_SIZE)
|
||
|
#define DRV_SetReg32(addr, val) writel(readl(addr) | (val), addr)
|
||
|
|
||
|
/* 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 adsp_core_id id)
|
||
|
{
|
||
|
struct memelfnote notes;
|
||
|
struct elf32_prstatus prstatus;
|
||
|
char cpustr[16];
|
||
|
int n;
|
||
|
|
||
|
memset(&prstatus, 0, sizeof(struct elf32_prstatus));
|
||
|
n = snprintf(cpustr, sizeof(cpustr), "CPU%d", cpu);
|
||
|
if (n < 0)
|
||
|
pr_info("%s, snprintf return error", __func__);
|
||
|
|
||
|
/* 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 (prstatus.pr_reg[15] == 0x0 && (id == ADSP_A_ID))
|
||
|
prstatus.pr_reg[15] = readl(ADSP_A_WDT_DEBUG_PC_REG);
|
||
|
|
||
|
|
||
|
nhdr->p_filesz += notesize(¬es);
|
||
|
return storenote(¬es, bufp);
|
||
|
}
|
||
|
|
||
|
void adsp_exception_header_init(void *oldbufp, enum adsp_core_id id)
|
||
|
{
|
||
|
struct elf32_phdr *nhdr, *phdr;
|
||
|
struct elf32_hdr *elf;
|
||
|
off_t offset = 0;
|
||
|
|
||
|
uint8_t *bufp = oldbufp;
|
||
|
uint32_t cpu;
|
||
|
|
||
|
/* 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;
|
||
|
|
||
|
|
||
|
phdr->p_filesz = (ADSP_A_TCM_SIZE - CRASH_MEMORY_OFFSET);
|
||
|
phdr->p_memsz = (ADSP_A_TCM_SIZE - CRASH_MEMORY_OFFSET);
|
||
|
|
||
|
|
||
|
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(¬es);
|
||
|
bufp = storenote(¬es, bufp);
|
||
|
|
||
|
/* Store pre-cpu backtrace */
|
||
|
for (cpu = 0; cpu < 1; cpu++)
|
||
|
bufp = core_write_cpu_note(cpu, nhdr, bufp, id);
|
||
|
}
|
||
|
|
||
|
static u32 copy_from_buffer(void *dest, size_t destsize, void *src,
|
||
|
size_t srcsize, u32 offset, size_t request)
|
||
|
{
|
||
|
/* if request == -1, offset == 0, copy full srcsize */
|
||
|
if (offset + request > srcsize)
|
||
|
request = srcsize - offset;
|
||
|
|
||
|
/* if destsize == -1, don't check the request size */
|
||
|
if (!dest || destsize < request) {
|
||
|
pr_warn("%s, buffer null or not enough space", __func__);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
memcpy(dest, src + offset, request);
|
||
|
|
||
|
return request;
|
||
|
}
|
||
|
|
||
|
static u32 dump_adsp_cfg_reg(void *buf, size_t size)
|
||
|
{
|
||
|
u32 clk_cfg = 0, uart_cfg = 0, n = 0;
|
||
|
|
||
|
/* record CFG registers */
|
||
|
clk_cfg = readl(ADSP_CLK_CTRL_BASE);
|
||
|
uart_cfg = readl(ADSP_UART_CTRL);
|
||
|
|
||
|
DRV_SetReg32(ADSP_CLK_CTRL_BASE, ADSP_CLK_UART_EN);
|
||
|
DRV_SetReg32(ADSP_UART_CTRL, ADSP_UART_RST_N | ADSP_UART_BCLK_CG);
|
||
|
|
||
|
n += copy_from_buffer(buf, size, ADSP_A_CFG, ADSP_A_CFG_SIZE, 0, -1);
|
||
|
|
||
|
/* Restore ADSP CLK and UART setting */
|
||
|
writel(clk_cfg, ADSP_CLK_CTRL_BASE);
|
||
|
writel(uart_cfg, ADSP_UART_CTRL);
|
||
|
|
||
|
return n;
|
||
|
}
|
||
|
|
||
|
static u32 dump_adsp_tcm(void *buf, size_t size)
|
||
|
{
|
||
|
u32 n = 0;
|
||
|
|
||
|
n += copy_from_buffer(buf, size,
|
||
|
ADSP_A_ITCM, ADSP_A_ITCM_SIZE, 0, -1);
|
||
|
n += copy_from_buffer(buf + n, size - n,
|
||
|
ADSP_A_DTCM, ADSP_A_DTCM_SIZE, 0, -1);
|
||
|
return n;
|
||
|
}
|
||
|
|
||
|
static inline u32 dump_adsp_shared_memory(void *buf, size_t size, int id)
|
||
|
{
|
||
|
void *mem_addr = adsp_get_reserve_mem_virt(id);
|
||
|
size_t mem_size = adsp_get_reserve_mem_size(id);
|
||
|
|
||
|
if (!mem_addr)
|
||
|
return 0;
|
||
|
|
||
|
return copy_from_buffer(buf, size, mem_addr, mem_size, 0, -1);
|
||
|
}
|
||
|
|
||
|
static inline u32 copy_from_adsp_shared_memory(void *buf, u32 offset,
|
||
|
size_t size, int id)
|
||
|
{
|
||
|
void *mem_addr = adsp_get_reserve_mem_virt(id);
|
||
|
size_t mem_size = adsp_get_reserve_mem_size(id);
|
||
|
|
||
|
if (!mem_addr)
|
||
|
return 0;
|
||
|
|
||
|
return copy_from_buffer(buf, -1, mem_addr, mem_size, offset, size);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* this function need ADSP to keeping awaken
|
||
|
* adsp_crash_dump: dump adsp tcm info.
|
||
|
* @param MemoryDump: adsp dump struct
|
||
|
* @param adsp_core_id: core id
|
||
|
* @return: adsp dump size
|
||
|
*/
|
||
|
static u32 adsp_crash_dump(struct MemoryDump *pMemoryDump,
|
||
|
enum adsp_core_id id)
|
||
|
{
|
||
|
u32 n = 0;
|
||
|
|
||
|
adsp_enable_dsp_clk(true);
|
||
|
mutex_lock(&adsp_sw_reset_mutex);
|
||
|
|
||
|
adsp_exception_header_init(pMemoryDump, id);
|
||
|
n += CRASH_MEMORY_HEADER_SIZE;
|
||
|
n += dump_adsp_tcm((void *)&(pMemoryDump->memory), CRASH_MEMORY_LENGTH);
|
||
|
n += CRASH_CORE_REG_SIZE;
|
||
|
n += dump_adsp_cfg_reg((void *)&(pMemoryDump->cfg_reg),
|
||
|
CRASH_CFG_REG_SIZE);
|
||
|
|
||
|
if (n != sizeof(struct MemoryDump))
|
||
|
pr_info("%s(), size not match n(%x) != MemoryDump(%zd)",
|
||
|
__func__, n, sizeof(struct MemoryDump));
|
||
|
|
||
|
mutex_unlock(&adsp_sw_reset_mutex);
|
||
|
adsp_enable_dsp_clk(false);
|
||
|
|
||
|
return n;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* generate aee argument with adsp register dump
|
||
|
* @param aed_str: exception description
|
||
|
* @param aed: struct to store argument for aee api
|
||
|
* @param id: identify adsp core id
|
||
|
*/
|
||
|
static void adsp_prepare_aed_dump(void)
|
||
|
{
|
||
|
struct MemoryDump *pMemoryDump = NULL;
|
||
|
u32 n = 0;
|
||
|
|
||
|
/*prepare adsp A db file*/
|
||
|
pMemoryDump = (struct MemoryDump *)adsp_A_dump_buffer;
|
||
|
if (!pMemoryDump) {
|
||
|
pr_debug("[ADSP AEE]MemoryDump buf is null");
|
||
|
} else {
|
||
|
memset(pMemoryDump, 0, sizeof(*pMemoryDump));
|
||
|
n = adsp_crash_dump(pMemoryDump, ADSP_A_ID);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* generate an exception according to exception type
|
||
|
* @param type: exception type
|
||
|
*/
|
||
|
void adsp_aed(enum adsp_excep_id type, enum adsp_core_id id)
|
||
|
{
|
||
|
char detail[ADSP_AED_STR_LEN];
|
||
|
struct MemoryDump *pMemoryDump;
|
||
|
struct adsp_coredump *coredump;
|
||
|
u32 n = 0;
|
||
|
char *aed_type;
|
||
|
int db_opt = DB_OPT_DEFAULT;
|
||
|
int ret = 0;
|
||
|
|
||
|
mutex_lock(&adsp_excep_mutex);
|
||
|
/* get adsp title and exception type*/
|
||
|
switch (type) {
|
||
|
case EXCEP_LOAD_FIRMWARE:
|
||
|
aed_type = "load firmware exception";
|
||
|
break;
|
||
|
case EXCEP_RESET:
|
||
|
aed_type = "reset exception";
|
||
|
break;
|
||
|
case EXCEP_BOOTUP:
|
||
|
aed_type = "boot exception";
|
||
|
break;
|
||
|
case EXCEP_RUNTIME:
|
||
|
aed_type = "runtime exception";
|
||
|
break;
|
||
|
case EXCEP_KERNEL:
|
||
|
aed_type = "kernel exception";
|
||
|
db_opt |= DB_OPT_FTRACE;
|
||
|
break;
|
||
|
default:
|
||
|
aed_type = "unknown exception";
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (type != EXCEP_LOAD_FIRMWARE) {
|
||
|
mutex_lock(&adsp_A_excep_dump_mutex);
|
||
|
adsp_prepare_aed_dump();
|
||
|
ret = adsp_A_dram_dump();
|
||
|
mutex_unlock(&adsp_A_excep_dump_mutex);
|
||
|
}
|
||
|
|
||
|
pMemoryDump = (struct MemoryDump *)adsp_A_dump_buffer;
|
||
|
coredump = adsp_get_reserve_mem_virt(ADSP_A_CORE_DUMP_MEM_ID);
|
||
|
|
||
|
/*print detail info. in kernel*/
|
||
|
n += snprintf(detail + n, ADSP_AED_STR_LEN - n, "%s %s\n",
|
||
|
adsp_core_ids[ADSP_A_ID], aed_type);
|
||
|
n += snprintf(detail + n, ADSP_AED_STR_LEN - n,
|
||
|
"adsp pc=0x%08x,exccause=0x%x,excvaddr=0x%x\n",
|
||
|
coredump->pc, coredump->exccause, coredump->excvaddr);
|
||
|
n += snprintf(detail + n, ADSP_AED_STR_LEN - n,
|
||
|
"latch pc=0x%08x,sp=0x%08x\n",
|
||
|
*((u32 *)(pMemoryDump->cfg_reg + 0x0170)),
|
||
|
*((u32 *)(pMemoryDump->cfg_reg + 0x0174)));
|
||
|
n += snprintf(detail + n, ADSP_AED_STR_LEN - n,
|
||
|
"CRDISPATCH_KEY:ADSP exception/%s\n",
|
||
|
coredump->task_name);
|
||
|
n += snprintf(detail + n, ADSP_AED_STR_LEN - n, "%s",
|
||
|
coredump->assert_log);
|
||
|
pr_debug("%s", detail);
|
||
|
|
||
|
#ifdef CFG_RECOVERY_SUPPORT
|
||
|
if (ret > 0 && /* if dram backup done, notify reset.work it's okay */
|
||
|
(atomic_read(&adsp_reset_status) == ADSP_RESET_STATUS_START ||
|
||
|
adsp_recovery_flag[ADSP_A_ID] == ADSP_RECOVERY_START)) {
|
||
|
/*complete adsp ee, if adsp reset by wdt or awake fail*/
|
||
|
pr_info("[ADSP]aed finished, complete it\n");
|
||
|
complete(&adsp_sys_reset_cp);
|
||
|
|
||
|
}
|
||
|
#endif
|
||
|
/* adsp aed api, only detail information available*/
|
||
|
aed_common_exception_api("adsp", NULL, 0, NULL, 0, detail, db_opt);
|
||
|
|
||
|
pr_info("[ADSP] adsp exception dump is done\n");
|
||
|
mutex_unlock(&adsp_excep_mutex);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* callback function for work struct
|
||
|
* generate an exception and reset adsp
|
||
|
* NOTE: this function may be blocked
|
||
|
* and should not be called in interrupt context
|
||
|
* @param ws: work struct
|
||
|
*/
|
||
|
static void adsp_aed_reset_ws(struct work_struct *ws)
|
||
|
{
|
||
|
struct adsp_work_t *sws = container_of(ws, struct adsp_work_t, work);
|
||
|
enum adsp_excep_id type = sws->flags;
|
||
|
enum adsp_core_id id = sws->id;
|
||
|
|
||
|
pr_debug("[ADSP]%s: adsp_excep_id=%u adsp_core_id=%u\n",
|
||
|
__func__, type, id);
|
||
|
adsp_reset_ready(ADSP_A_ID);
|
||
|
adsp_aed(type, id);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* schedule a work to generate an exception and reset adsp
|
||
|
* @param type: exception type
|
||
|
*/
|
||
|
void adsp_aed_reset(enum adsp_excep_id type, enum adsp_core_id id)
|
||
|
{
|
||
|
adsp_aed_work.flags = (unsigned int) type;
|
||
|
adsp_aed_work.id = (unsigned int) id;
|
||
|
queue_work(adsp_workqueue, &adsp_aed_work.work);
|
||
|
}
|
||
|
|
||
|
#if ADSP_TRAX
|
||
|
static ssize_t adsp_A_trax_show(struct file *filep, struct kobject *kobj,
|
||
|
struct bin_attribute *attr,
|
||
|
char *buf, loff_t offset, size_t size)
|
||
|
{
|
||
|
unsigned int length = 0;
|
||
|
void *trax_addr = adsp_get_reserve_mem_virt(ADSP_A_TRAX_MEM_ID);
|
||
|
|
||
|
pr_debug("[ADSP] trax initiated=%d, done=%d, length=%d, offset=%lld\n",
|
||
|
adsp_get_trax_initiated(), adsp_get_trax_done(),
|
||
|
adsp_get_trax_length(), offset);
|
||
|
|
||
|
if (adsp_get_trax_initiated() && adsp_get_trax_done()) {
|
||
|
if (offset >= 0 && offset < adsp_get_trax_length()) {
|
||
|
if ((offset + size) > adsp_get_trax_length())
|
||
|
size = adsp_get_trax_length() - offset;
|
||
|
|
||
|
memcpy(buf, trax_addr + offset, size);
|
||
|
length = size;
|
||
|
}
|
||
|
}
|
||
|
return length;
|
||
|
}
|
||
|
|
||
|
struct bin_attribute bin_attr_adsp_trax = {
|
||
|
.attr = {
|
||
|
.name = "adsp_trax",
|
||
|
.mode = 0444,
|
||
|
},
|
||
|
.size = 0,
|
||
|
.read = adsp_A_trax_show,
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
static int adsp_A_dram_dump(void)
|
||
|
{
|
||
|
unsigned int dram_total = 0;
|
||
|
void *dump_buf = NULL;
|
||
|
uint32_t n = 0;
|
||
|
|
||
|
/*wait EE dump done*/
|
||
|
#ifdef CFG_RECOVERY_SUPPORT
|
||
|
if (wdt_counter == WDT_FIRST_WAIT_COUNT ||
|
||
|
wdt_counter == WDT_LAST_WAIT_COUNT)
|
||
|
return -1;
|
||
|
#endif
|
||
|
/* all dram */
|
||
|
dram_total = adsp_get_reserve_mem_size(ADSP_A_SYSTEM_MEM_ID)
|
||
|
+ adsp_get_reserve_mem_size(ADSP_A_CORE_DUMP_MEM_ID)
|
||
|
+ adsp_get_reserve_mem_size(ADSP_A_IPI_MEM_ID)
|
||
|
+ adsp_get_reserve_mem_size(ADSP_A_LOGGER_MEM_ID);
|
||
|
|
||
|
/* allocate adsp dump dram buffer*/
|
||
|
if (adsp_A_dram_dump_buffer == NULL)
|
||
|
adsp_A_dram_dump_buffer = vmalloc(dram_total);
|
||
|
|
||
|
if (!adsp_A_dram_dump_buffer)
|
||
|
return -1;
|
||
|
|
||
|
dump_buf = adsp_A_dram_dump_buffer;
|
||
|
n += dump_adsp_shared_memory(dump_buf + n, dram_total - n,
|
||
|
ADSP_A_SYSTEM_MEM_ID);
|
||
|
n += dump_adsp_shared_memory(dump_buf + n, dram_total - n,
|
||
|
ADSP_A_CORE_DUMP_MEM_ID);
|
||
|
n += dump_adsp_shared_memory(dump_buf + n, dram_total - n,
|
||
|
ADSP_A_IPI_MEM_ID);
|
||
|
n += dump_adsp_shared_memory(dump_buf + n, dram_total - n,
|
||
|
ADSP_A_LOGGER_MEM_ID);
|
||
|
|
||
|
return n;
|
||
|
}
|
||
|
static ssize_t adsp_A_ramdump(char *buf, loff_t offset, size_t size)
|
||
|
{
|
||
|
ssize_t n = 0;
|
||
|
ssize_t threshold[5];
|
||
|
|
||
|
threshold[0] = sizeof(struct MemoryDump);
|
||
|
threshold[1] = threshold[0] +
|
||
|
adsp_get_reserve_mem_size(ADSP_A_SYSTEM_MEM_ID);
|
||
|
threshold[2] = threshold[1] +
|
||
|
adsp_get_reserve_mem_size(ADSP_A_CORE_DUMP_MEM_ID);
|
||
|
threshold[3] = threshold[2] +
|
||
|
adsp_get_reserve_mem_size(ADSP_A_IPI_MEM_ID);
|
||
|
threshold[4] = threshold[3] +
|
||
|
adsp_get_reserve_mem_size(ADSP_A_LOGGER_MEM_ID);
|
||
|
|
||
|
mutex_lock(&adsp_A_excep_dump_mutex);
|
||
|
|
||
|
if (offset >= 0 && offset < threshold[0]) {
|
||
|
n = copy_from_buffer(buf, -1, adsp_A_dump_buffer,
|
||
|
threshold[0], offset, size);
|
||
|
goto DONE;
|
||
|
}
|
||
|
|
||
|
if (adsp_A_dram_dump_buffer) { /* backup before: adsp_A_dram_dump */
|
||
|
n = copy_from_buffer(buf, -1, adsp_A_dram_dump_buffer,
|
||
|
threshold[4] - threshold[0],
|
||
|
offset - threshold[0], size);
|
||
|
|
||
|
if (n == 0) {
|
||
|
vfree(adsp_A_dram_dump_buffer);
|
||
|
adsp_A_dram_dump_buffer = NULL;
|
||
|
}
|
||
|
goto DONE;
|
||
|
}
|
||
|
|
||
|
/* not backup, dump immediately */
|
||
|
if (offset >= threshold[0] && offset < threshold[1]) {
|
||
|
n = copy_from_adsp_shared_memory(
|
||
|
buf, offset - threshold[0],
|
||
|
size, ADSP_A_SYSTEM_MEM_ID);
|
||
|
} else if (offset >= threshold[1] && offset < threshold[2]) {
|
||
|
n = copy_from_adsp_shared_memory(
|
||
|
buf, offset - threshold[1],
|
||
|
size, ADSP_A_CORE_DUMP_MEM_ID);
|
||
|
} else if (offset >= threshold[2] && offset < threshold[3]) {
|
||
|
n = copy_from_adsp_shared_memory(
|
||
|
buf, offset - threshold[2],
|
||
|
size, ADSP_A_IPI_MEM_ID);
|
||
|
} else if (offset >= threshold[3] && offset < threshold[4]) {
|
||
|
n = copy_from_adsp_shared_memory(
|
||
|
buf, offset - threshold[3],
|
||
|
size, ADSP_A_LOGGER_MEM_ID);
|
||
|
}
|
||
|
|
||
|
/* dump done*/
|
||
|
#ifdef CFG_RECOVERY_SUPPORT
|
||
|
if (!n &&
|
||
|
(atomic_read(&adsp_reset_status) == ADSP_RESET_STATUS_START ||
|
||
|
adsp_recovery_flag[ADSP_A_ID] == ADSP_RECOVERY_START)) {
|
||
|
/*complete scp ee, if scp reset by wdt or awake fail*/
|
||
|
pr_info("[ADSP]aed finished, complete it\n");
|
||
|
complete(&adsp_sys_reset_cp);
|
||
|
}
|
||
|
#endif
|
||
|
DONE:
|
||
|
mutex_unlock(&adsp_A_excep_dump_mutex);
|
||
|
return n;
|
||
|
}
|
||
|
|
||
|
static ssize_t adsp_A_dump_show(struct file *filep, struct kobject *kobj,
|
||
|
struct bin_attribute *attr,
|
||
|
char *buf, loff_t offset, size_t size)
|
||
|
{
|
||
|
return adsp_A_ramdump(buf, offset, size);
|
||
|
}
|
||
|
|
||
|
static ssize_t adsp_A_dump_ke_show(struct file *filep, struct kobject *kobj,
|
||
|
struct bin_attribute *attr,
|
||
|
char *buf, loff_t offset, size_t size)
|
||
|
{
|
||
|
if (offset == 0) /* only do ITCM/DTCM ramdump once at start */
|
||
|
adsp_prepare_aed_dump();
|
||
|
|
||
|
return adsp_A_ramdump(buf, offset, size);
|
||
|
}
|
||
|
|
||
|
static struct bin_attribute bin_attr_adsp_dump = {
|
||
|
.attr = {
|
||
|
.name = "adsp_dump",
|
||
|
.mode = 0444,
|
||
|
},
|
||
|
.size = 0,
|
||
|
.read = adsp_A_dump_show,
|
||
|
};
|
||
|
|
||
|
static struct bin_attribute bin_attr_adsp_dump_ke = {
|
||
|
.attr = {
|
||
|
.name = "adsp_dump_ke",
|
||
|
.mode = 0444,
|
||
|
},
|
||
|
.size = 0,
|
||
|
.read = adsp_A_dump_ke_show,
|
||
|
};
|
||
|
|
||
|
static struct bin_attribute *adsp_excep_bin_attrs[] = {
|
||
|
&bin_attr_adsp_dump,
|
||
|
&bin_attr_adsp_dump_ke,
|
||
|
#if ADSP_TRAX
|
||
|
&bin_attr_adsp_trax,
|
||
|
#endif
|
||
|
NULL,
|
||
|
};
|
||
|
|
||
|
struct attribute_group adsp_excep_attr_group = {
|
||
|
.bin_attrs = adsp_excep_bin_attrs,
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
* init a work struct
|
||
|
*/
|
||
|
int adsp_excep_init(void)
|
||
|
{
|
||
|
mutex_init(&adsp_excep_mutex);
|
||
|
mutex_init(&adsp_A_excep_dump_mutex);
|
||
|
|
||
|
INIT_WORK(&adsp_aed_work.work, adsp_aed_reset_ws);
|
||
|
|
||
|
/* alloc dump memory*/
|
||
|
adsp_A_dump_buffer = vmalloc(sizeof(struct MemoryDump));
|
||
|
if (!adsp_A_dump_buffer)
|
||
|
return -1;
|
||
|
|
||
|
adsp_ke_buffer = kmalloc(ADSP_KE_DUMP_LEN, GFP_KERNEL);
|
||
|
if (!adsp_ke_buffer)
|
||
|
return -1;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* cleanup adsp exception
|
||
|
*/
|
||
|
void adsp_excep_cleanup(void)
|
||
|
{
|
||
|
vfree(adsp_A_dump_buffer);
|
||
|
kfree(adsp_ke_buffer);
|
||
|
|
||
|
pr_debug("[ADSP] %s done\n", __func__);
|
||
|
}
|
||
|
|
||
|
void get_adsp_aee_buffer(unsigned long *vaddr, unsigned long *size)
|
||
|
{
|
||
|
u32 n = 0;
|
||
|
|
||
|
if (adsp_ke_buffer) {
|
||
|
adsp_enable_dsp_clk(true);
|
||
|
n += dump_adsp_cfg_reg(adsp_ke_buffer, ADSP_KE_DUMP_LEN);
|
||
|
n += dump_adsp_tcm(adsp_ke_buffer + n, ADSP_KE_DUMP_LEN - n);
|
||
|
adsp_enable_dsp_clk(false);
|
||
|
|
||
|
*vaddr = (unsigned long)adsp_ke_buffer;
|
||
|
*size = ADSP_KE_DUMP_LEN;
|
||
|
}
|
||
|
}
|
||
|
EXPORT_SYMBOL(get_adsp_aee_buffer);
|