/* * Copyright (c) 2023, ArtInChip Technology Co., Ltd * * SPDX-License-Identifier: Apache-2.0 * * Authors: matteo */ #include "aic_core.h" #include "aic_reboot_reason.h" #include "hal_wri.h" /* Register of WRI */ #define WRI_RST_FLAG (WRI_BASE + 0x0) #define WRI_BOOT_INFO (WRI_BASE + 0x100) #define WRI_SYS_BAK (WRI_BASE + 0x104) #define WRI_VERSION (WRI_BASE + 0xFFC) #define WRI_FLAG_CMP_RST BIT(12) #define WRI_FLAG_TSEN_RST BIT(11) #define WRI_FLAG_WDT_RST BIT(10) #define WRI_FLAG_DM_RST BIT(9) #define WRI_FLAG_EXT_RST BIT(8) #define WRI_FLAG_RTC_RST BIT(1) #define WRI_FLAG_SYS_POR BIT(0) #define WRI_REBOOT_REASON_MASK GENMASK(7, 4) #define WRI_REBOOT_REASON_SHIFT 4 struct reboot_info { u32 inited; enum aic_reboot_reason reason; enum aic_reboot_reason sw_reason; enum aic_warm_reset_type hw_reason; }; static struct reboot_info g_last_reboot = {0}; enum aic_warm_reset_type aic_wr_type_get(void) { u32 val = 0; u16 wr_bit[WRI_TYPE_MAX] = { WRI_FLAG_SYS_POR, WRI_FLAG_RTC_RST, WRI_FLAG_EXT_RST, WRI_FLAG_DM_RST, WRI_FLAG_WDT_RST, WRI_FLAG_TSEN_RST, WRI_FLAG_CMP_RST}; s32 i; if (g_last_reboot.inited) return g_last_reboot.hw_reason; val = readl(WRI_RST_FLAG); if (!val) return WRI_TYPE_POR; writel(val, WRI_RST_FLAG); /* clear the flag */ for (i = WRI_TYPE_MAX - 1; i >= 0; i--) { if (val & wr_bit[i]) { g_last_reboot.hw_reason = (enum aic_warm_reset_type)i; return g_last_reboot.hw_reason; } } pr_warn("Invalid warm reset flag: %#x\n", val); return WRI_TYPE_POR; } enum aic_reboot_reason aic_judge_reboot_reason(enum aic_warm_reset_type hw, u32 sw) { enum aic_reboot_reason r = (enum aic_reboot_reason)sw; /* First, check the software-triggered reboot */ if (hw == WRI_TYPE_WDT) { printf("Reboot action: Watchdog-Reset, reason: "); switch (sw) { case REBOOT_REASON_UPGRADE: printf("Upgrade-Mode\n"); break; case REBOOT_REASON_CMD_REBOOT: printf("Command-Reboot\n"); break; case REBOOT_REASON_SW_LOCKUP: printf("Software-Lockup\n"); break; case REBOOT_REASON_HW_LOCKUP: printf("Hardware-Lockup\n"); break; case REBOOT_REASON_PANIC: printf("Kernel-Panic\n"); break; case REBOOT_REASON_RAMDUMP: printf("Ramdump\n"); break; default: printf("Unknown(%d)\n", r); break; } return r; } if (r == REBOOT_REASON_CMD_SHUTDOWN) { printf("Reboot reason: Command-Poweroff\n"); return r; } if (r == REBOOT_REASON_SUSPEND) { pr_info("Reboot reason: Suspend\n"); return r; } /* Second, check the hardware-triggered reboot */ if (r == REBOOT_REASON_COLD) { if (hw == WRI_TYPE_POR) { printf("Startup reason: Power-On-Reset\n"); return (enum aic_reboot_reason)sw; } printf("Reboot action: Warm-Reset, reason: "); switch (hw) { case WRI_TYPE_RTC: printf("RTC-Power-Down\n"); r = REBOOT_REASON_RTC; break; case WRI_TYPE_EXT: printf("External-Reset\n"); r = REBOOT_REASON_EXTEND; break; case WRI_TYPE_DM: printf("JTAG-Reset\n"); r = REBOOT_REASON_JTAG; break; case WRI_TYPE_TSEN: printf("OTP-Reset\n"); r = REBOOT_REASON_OTP; break; case WRI_TYPE_CMP: printf("Undervoltage-Reset\n"); r = REBOOT_REASON_UNDER_VOL; break; default: printf("Unknown(%d)\n", hw); break; } return r; } pr_warn("Unknow reboot reason: %d - %d\n", hw, sw); return r; } #if defined(AIC_WRI_DRV_V12) void aic_set_reboot_reason(enum aic_reboot_reason r) { writel_bits(r, WRI_REBOOT_REASON_MASK, WRI_REBOOT_REASON_SHIFT, WRI_SYS_BAK); if (r <= (WRI_REBOOT_REASON_MASK >> WRI_REBOOT_REASON_SHIFT)) pr_debug("Set reboot reason %d\n", r); } enum aic_reboot_reason aic_get_reboot_reason(void) { u32 val = 0; enum aic_warm_reset_type hw = aic_wr_type_get(); if (g_last_reboot.inited) return g_last_reboot.reason; val = readl_bits(WRI_REBOOT_REASON_MASK, WRI_REBOOT_REASON_SHIFT, WRI_SYS_BAK); if (val) aic_set_reboot_reason(REBOOT_REASON_COLD); pr_debug("Last reboot info: hw %d, sw %d\n", hw, val); g_last_reboot.reason = aic_judge_reboot_reason(hw, val); g_last_reboot.inited = 1; return g_last_reboot.reason; } /* Convert and print the time info from a GTC counter. It's useful when print the time before console is ready. */ void aic_show_gtc_time(char *tag, u32 val) { u32 sec, msec; u32 cnt = val; if (!cnt) cnt = readl(GTC_BASE + 0x8); sec = cnt / 4000000; msec = (cnt % 4000000) / 4 / 1000; printf("[%s] time: %d.%03d sec\n", tag ? tag : "GTC", sec, msec); } void aic_show_startup_time(u32 val) { u32 cnt = readl(GTC_BASE + 0x8); aic_show_gtc_time("Startup", cnt); } #endif