blob: 02078dde95c7a6a869e11b56e1c4a7d96a7a075d [file] [log] [blame]
/*
* Copyright (c) 2018 MediaTek Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <arch/mmu.h>
#include <platform/mtk_wdt.h>
#include <ram_console.h>
#include <stdio.h>
#include <stdlib.h> // for ALIGN()
#include <string.h>
#include <sys/types.h>
#define RAM_CONSOLE_SIG (0x43474244) /* DBGC */
#define MOD "RAM_CONSOLE"
struct ram_console_buffer {
u32 sig;
/* for size comptible */
u32 off_pl;
u32 off_lpl;
u32 sz_pl;
u32 off_lk;
u32 off_llk; /* last lk */
u32 sz_lk;
u32 padding[2]; /* size = 2 * 16 = 32 byte */
u32 dump_step;
u32 sz_buffer;
u32 off_linux;
u32 off_console;
u32 padding2[3];
};
#define RAM_CONSOLE_PL_SIZE 3
struct reboot_reason_pl {
u32 wdt_status;
u32 last_func[RAM_CONSOLE_PL_SIZE];
};
#define RAM_CONSOLE_LK_SIZE 16
struct reboot_reason_lk {
u32 last_func[RAM_CONSOLE_LK_SIZE];
};
struct reboot_reason_kernel {
u32 fiq_step;
/* 0xaeedeadX: X=1 (HWT), X=2 (KE), X=3 (nested panic) */
/* details see enum AEE_EXP_TYPE_NUM in ram_console_common.h */
u32 exp_type;
u32 others[0];
};
static struct ram_console_buffer *ram_console = NULL;
static uint32_t ram_console_size;
#define U_VAR(type) rc_##type
static unsigned int U_VAR(wdt_status), U_VAR(fiq_step), U_VAR(exp_type);
static bool U_VAR(set_flag) = false;
static void ram_console_ptr_init(void)
{
if (ram_console && ram_console_size) {
LOG("%s. already init start: 0x%zx, size: 0x%x, sig: 0x%x\n",
MOD, (size_t)ram_console, ram_console_size, ram_console->sig);
return;
}
ram_console = (struct ram_console_buffer *)PA_TO_VA((paddr_t)RAM_CONSOLE_DEF_ADDR);
ram_console_size = RAM_CONSOLE_DEF_SIZE;
LOG("%s. pa start: 0x%zx, size: 0x%x\n", MOD, (size_t)RAM_CONSOLE_DEF_ADDR, RAM_CONSOLE_DEF_SIZE);
}
int sLOG(const char *fmt, ...)
{
va_list args;
static int pos = 0;
va_start(args, fmt);
if (pos < SZLOG - 1) /* vsnprintf bug */
pos += vsnprintf(logbuf + pos, SZLOG - pos - 1, fmt, args);
va_end(args);
return 0;
}
void pl_ram_console_init(void)
{
int i;
struct reboot_reason_pl *rr_pl;
ram_console = (struct ram_console_buffer *)PA_TO_VA((paddr_t)RAM_CONSOLE_DEF_ADDR);
ram_console_size = RAM_CONSOLE_DEF_SIZE;
LOG("pl_ram_console_init ram_console pa: 0x%zx, va:0x%zx, size: 0x%x\n",
(size_t)RAM_CONSOLE_DEF_ADDR, (size_t)ram_console, RAM_CONSOLE_DEF_SIZE);
if (ram_console->sig == RAM_CONSOLE_SIG && ram_console->sz_pl == sizeof(struct reboot_reason_pl)
&& ram_console->off_pl + ALIGN(ram_console->sz_pl, 64) == ram_console->off_lpl) {
rr_pl = (void *)ram_console + ram_console->off_pl;
for (i = 0; i < RAM_CONSOLE_PL_SIZE; i++) {
LOG("0x%x ", rr_pl->last_func[i]);
}
LOG("\n");
memcpy((void *)ram_console + ram_console->off_lpl, (void *)ram_console + ram_console->off_pl,
ram_console->sz_pl);
} else {
memset(ram_console, 0, ram_console_size);
ram_console->sig = RAM_CONSOLE_SIG;
ram_console->off_pl = sizeof(struct ram_console_buffer);
ram_console->sz_pl = sizeof(struct reboot_reason_pl);
ram_console->off_lpl = ram_console->off_pl + ALIGN(ram_console->sz_pl, 64);
LOG("pl_ram_console_init first init\n");
}
}
void ram_console_init(void)
{
int i;
struct reboot_reason_lk *rr_lk;
ram_console_ptr_init();
if (ram_console) {
LOG("%s. start: 0x%lx, size: 0x%x\n", MOD, (size_t)ram_console, ram_console_size);
} else {
LOG("%s. sig not match\n", MOD);
return;
}
ram_console->off_lk = ram_console->off_lpl + ALIGN(ram_console->sz_pl, 64);;
ram_console->off_llk = ram_console->off_lk + ALIGN(sizeof(struct reboot_reason_lk), 64);
ram_console->sz_lk = sizeof(struct reboot_reason_lk);
if (ram_console->sz_lk == sizeof(struct reboot_reason_lk) && (ram_console->off_lk +
ram_console->sz_lk == ram_console->off_llk)) {
LOG("%s. lk last status: ", MOD);
rr_lk = (void *)ram_console + ram_console->off_lk;
for (i = 0; i < RAM_CONSOLE_LK_SIZE; i++) {
LOG("0x%x ", rr_lk->last_func[i]);
}
LOG("\n");
memcpy((void *)ram_console + ram_console->off_llk, (void *)ram_console +
ram_console->off_lk, ram_console->sz_lk);
} else {
LOG("%s. lk size mismatch %x + %x != %x\n", MOD, ram_console->sz_lk,
ram_console->off_lk, ram_console->off_llk);
ram_console->sz_lk = sizeof(struct reboot_reason_lk);
}
ram_console->off_linux = ram_console->off_llk + ALIGN(ram_console->sz_lk, 64);
}
void ram_console_reboot_reason_save(u32 rgu_status)
{
struct reboot_reason_pl *rr_pl;
if (ram_console) {
rr_pl = (void *)ram_console + ram_console->off_pl;
rr_pl->wdt_status = rgu_status;
LOG("%s wdt status (0x%x)=0x%x\n", MOD, rr_pl->wdt_status, rgu_status);
}
}
//#define RE_BOOT_BY_WDT_SW 2
#define RE_BOOT_NORMAL_BOOT 0
#define RE_BOOT_BY_EINT 256/*we can find the definition from preloader ,this value should sync with preloader incase issue happened*/
#define RE_BOOT_BY_SYSRST 512/*we can find the definition from preloader ,this value should sync with preloader incase issue happened*/
#ifdef MTK_PMIC_FULL_RESET
#define RE_BOOT_FULL_PMIC 0x800
#endif
int ram_console_reboot_by_mrdump_key(void)
{
unsigned int wdt_status;
wdt_status = ((struct reboot_reason_pl *)((void *)ram_console + ram_console->off_pl))->wdt_status;
return ((wdt_status & RE_BOOT_BY_EINT)|(wdt_status & RE_BOOT_BY_SYSRST))?true:false;
}
bool ram_console_should_restore(unsigned char *ram_console_ptr)
{
unsigned int fiq_step, wdt_status, exp_type;
struct ram_console_buffer *tmp_ram_console = (struct ram_console_buffer *)ram_console_ptr;
if (U_VAR(set_flag) && tmp_ram_console && tmp_ram_console->off_linux &&
(tmp_ram_console->off_linux == (tmp_ram_console->off_llk +
ALIGN(tmp_ram_console->sz_lk, 64))) &&
(tmp_ram_console->off_pl == sizeof(struct ram_console_buffer))) {
wdt_status = ((struct reboot_reason_pl*)((void*)tmp_ram_console +
tmp_ram_console->off_pl))->wdt_status;
fiq_step = ((struct reboot_reason_kernel*)((void*)tmp_ram_console +
tmp_ram_console->off_linux))->fiq_step;
exp_type = RAM_CONSOLE_EXP_TYPE_DEC(((struct reboot_reason_kernel *)((void *)tmp_ram_console
+ tmp_ram_console->off_linux))->exp_type);
if ((wdt_status != 0 && wdt_status != U_VAR(wdt_status)) ||
(fiq_step != 0 && fiq_step != U_VAR(fiq_step)) ||
(exp_type != 0 && exp_type != U_VAR(exp_type))) {
return true;
}
}
return false;
}
#ifdef MTK_PMIC_FULL_RESET
bool ram_console_reboot_by_cold_reset(void)
{
unsigned int wdt_status;
wdt_status = ((struct reboot_reason_pl *)((void *)ram_console + ram_console->off_pl))->wdt_status;
return (wdt_status & RE_BOOT_FULL_PMIC) ? true : false;
}
#endif
int ram_console_get_wdt_status(unsigned int *wdt_status)
{
if (wdt_status && U_VAR(set_flag)) {
*wdt_status = U_VAR(wdt_status);
return true;
}
return false;
}
int ram_console_get_fiq_step(unsigned int *fiq_step)
{
if (fiq_step && U_VAR(set_flag)) {
*fiq_step = U_VAR(fiq_step);
return true;
}
return false;
}
int ram_console_get_exp_type(unsigned int *exp_type)
{
if (exp_type && U_VAR(set_flag)) {
*exp_type = U_VAR(exp_type);
return true;
}
return false;
}
int ram_console_set_exp_type(unsigned int exp_type)
{
bool ret = false;
if (ram_console && ram_console->off_linux &&
(ram_console->off_linux == (ram_console->off_llk + ALIGN(ram_console->sz_lk, 64))) &&
(ram_console->off_pl == sizeof(struct ram_console_buffer))) {
if (exp_type < 16) {
exp_type = exp_type ^ RAM_CONSOLE_EXP_TYPE_MAGIC;
((struct reboot_reason_kernel *)((void *)ram_console + ram_console->off_linux))->exp_type = exp_type;
ret = true;
} else {
LOG("%s. set exp type failed: off_linux:0x%x, off_llk:0x%x, off_pl:0x%x, exp type:%d\n",
MOD, ram_console->off_linux, ram_console->off_llk, ram_console->off_pl, exp_type);
}
}
return ret;
}
int ram_console_is_abnormal_boot(void)
{
unsigned int fiq_step, wdt_status, exp_type;
int reinit_flag = 0;
if (!ram_console) {
ram_console_ptr_init();
reinit_flag = 1;
}
if (ram_console && ram_console->off_linux &&
(ram_console->off_linux == (ram_console->off_llk + ALIGN(ram_console->sz_lk, 64))) &&
(ram_console->off_pl == sizeof(struct ram_console_buffer))) {
wdt_status = ((struct reboot_reason_pl *)((void *)ram_console + ram_console->off_pl))->wdt_status;
fiq_step = ((struct reboot_reason_kernel *)((void *)ram_console + ram_console->off_linux))->fiq_step;
exp_type = ((struct reboot_reason_kernel *)((void *)ram_console + ram_console->off_linux))->exp_type;
LOG("%s. wdt_status 0x%x, fiq_step 0x%x, exp_type 0x%x\n", MOD, wdt_status, fiq_step, RAM_CONSOLE_EXP_TYPE_DEC(exp_type));
if (fiq_step != 0 && (exp_type ^ RAM_CONSOLE_EXP_TYPE_MAGIC) >= 16) {
fiq_step = 0;
((struct reboot_reason_kernel *)((void *)ram_console + ram_console->off_linux))->fiq_step = fiq_step;
}
U_VAR(wdt_status) = wdt_status;
U_VAR(fiq_step) = fiq_step;
U_VAR(exp_type) = RAM_CONSOLE_EXP_TYPE_DEC(exp_type);
U_VAR(set_flag) = true;
LOG("%s. set reboot reason info done\n", MOD);
if ((wdt_status == RE_BOOT_BY_WDT_SW && fiq_step == 0 && U_VAR(exp_type) == 0) /* adb reboot */
#ifdef MTK_PMIC_FULL_RESET
|| (wdt_status == RE_BOOT_FULL_PMIC && fiq_step == 0) /* full pmic reset */
#endif
|| (wdt_status == RE_BOOT_NORMAL_BOOT)) /* power off->on */
return false;
else
return true;
} else {
if (ram_console) {
LOG("%s. set reboot reason info failed: off_linux:0x%x, off_llk:0x%x, off_pl:0x%x, reinit flag:%d\n",
MOD, ram_console->off_linux, ram_console->off_llk, ram_console->off_pl, reinit_flag);
} else {
LOG("%s. ram console buffer NULL\n", MOD);
}
}
return false;
}
void ram_console_lk_save(unsigned int val, int index)
{
struct reboot_reason_lk *rr_lk;
if (ram_console && ram_console->off_lk < ram_console_size) {
rr_lk = (void *)ram_console + ram_console->off_lk;
if (index < RAM_CONSOLE_LK_SIZE)
rr_lk->last_func[index] = val;
}
}
void ram_console_addr_size(unsigned long *addr, unsigned long *size)
{
*addr = (unsigned long)ram_console;
*size = ram_console_size;
}
void ram_console_set_dump_step(unsigned int step)
{
if (ram_console) {
ram_console->dump_step = step;
}
}
int ram_console_get_dump_step(void)
{
if (ram_console)
return ram_console->dump_step;
else {
LOG("%s. ram_console not ready\n", MOD);
return 0;
}
}