--- /dev/null
+/*
+ * Copyright 2014 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __CORESIGHT_REGS_H
+#define __CORESIGHT_REGS_H
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#define OSLOCK_MAGIC (0xc5acce55)
+
+/* Defines are used by core-sight */
+#define CS_SJTAG_OFFSET (0x8000)
+#define SJTAG_STATUS (0x4)
+#define SJTAG_SOFT_LOCK (1<<2)
+
+/* DBG Registers */
+#define DBGWFAR (0x018) /* RW */
+#define DBGVCR (0x01c) /* RW */
+#define DBGECR (0x024) /* RW or RAZ */
+#define DBGDSCCR (0x028) /* RW or RAZ */
+#define DBGDSMCR (0x02c) /* RW or RAZ */
+#define DBGDTRRX (0x080) /* RW */
+#define DBGITR (0x084) /* RW */
+#define DBGDSCR (0x088) /* RW */
+#define DBGDTRTX (0x08c) /* RW */
+#define DBGDRCR (0x090) /* WO */
+#define DBGEACR (0x094) /* RW */
+#define DBGECCR (0x098) /* RW */
+#define DBGPCSRlo (0x0a0) /* RO */
+#define DBGCIDSR (0x0a4) /* RO */
+#define DBGVIDSR (0x0a8) /* RO */
+#define DBGPCSRhi (0x0ac) /* RO */
+#define DBGBXVR0 (0x250) /* RW */
+#define DBGBXVR1 (0x254) /* RW */
+#define DBGOSLAR (0x300) /* WO */
+#define DBGOSLSR (0x304) /* RO */
+#define DBGPRCR (0x310) /* RW */
+#define DBGPRSR (0x314) /* RO, OSLSR in ARMv8 */
+#define DBGITOCTRL (0xef8) /* WO */
+#define DBGITISR (0xefc) /* RO */
+#define DBGITCTRL (0xf00) /* RW */
+#define DBGCLAIMSET (0xfa0) /* RW */
+#define DBGCLAIMCLR (0xfa4) /* RW */
+#define DBGLAR (0xfb0) /* WO */
+#define DBGLSR (0xfb4) /* RO */
+#define DBGAUTHSTATUS (0xfb8) /* RO */
+#define DBGDEVID2 (0xfc0) /* RO */
+#define DBGDEVID1 (0xfc4) /* RO, PC offset */
+#define DBGDEVID0 (0xfc8) /* RO */
+#define DBGDEVTYPE (0xfcc) /* RO */
+
+#define MIDR (0xd00) /* RO */
+#define ID_AA64DFR0_EL1 (0xd28)
+
+/* DBG breakpoint registers (All RW) */
+#define DBGBVRn(n) (0x400 + (n * 0x10)) /* 64bit */
+#define DBGBCRn(n) (0x408 + (n * 0x10))
+/* DBG watchpoint registers (All RW) */
+#define DBGWVRn(n) (0x800 + (n * 0x10)) /* 64bit */
+#define DBGWCRn(n) (0x808 + (n * 0x10))
+
+/* DIDR or ID_AA64DFR0_EL1 bit */
+#define DEBUG_ARCH_V8 (0x6)
+
+/* MIDR bit */
+#define ARMV8_PROCESSOR (0xf << 16)
+#define ARMV8_CORTEXA53 (0xd03)
+#define ARMV8_CORTEXA57 (0xd07)
+
+/* TMC(ETB/ETF/ETR) registers */
+#define TMCRSZ (0x004)
+#define TMCSTS (0x00c)
+#define TMCRRD (0x010)
+#define TMCRRP (0x014)
+#define TMCRWP (0x018)
+#define TMCTGR (0x01c)
+#define TMCCTL (0x020)
+#define TMCRWD (0x024)
+#define TMCMODE (0x028)
+#define TMCLBUFLEVEL (0x02c)
+#define TMCCBUFLEVEL (0x030)
+#define TMCBUFWM (0x034)
+#define TMCRRPHI (0x038)
+#define TMCRWPHI (0x03c)
+#define TMCAXICTL (0x110)
+#define TMCDBALO (0x118)
+#define TMCDBAHI (0x11c)
+#define TMCFFSR (0x300)
+#define TMCFFCR (0x304)
+#define TMCPSCR (0x308)
+
+/* Coresight manager register */
+#define ITCTRL (0xf00)
+#define CLAIMSET (0xfa0)
+#define CLAIMCLR (0xfa4)
+#define LAR (0xfb0)
+#define LSR (0xfb4)
+#define AUTHSTATUS (0xfb8)
+
+/* FUNNEL configuration register */
+#define FUNCTRL (0x0)
+#define FUNPRIORCTRL (0x4)
+
+#define DBG_OFFSET (0x0)
+#define CTI_OFFSET (0x10000)
+#define PMU_OFFSET (0x20000)
+#define ETM_OFFSET (0x30000)
+#define PMUPCSRlo (0x200)
+#define PMUPCSRhi (0x204)
+
+/* ETM registers */
+#define ETMCTLR (0x004)
+#define ETMPROCSELR (0x008)
+#define ETMSTATUS (0x00c)
+#define ETMCONFIG (0x010)
+#define ETMAUXCTLR (0x018)
+#define ETMEVENTCTL0R (0x020)
+#define ETMEVENTCTL1R (0x024)
+#define ETMSTALLCTLR (0x02c)
+#define ETMTSCTLR (0x030)
+#define ETMSYNCPR (0x034)
+#define ETMCCCCTLR (0x038)
+#define ETMBBCTLR (0x03c)
+#define ETMTRACEIDR (0x040)
+#define ETMQCTRLR (0x044)
+#define ETMVICTLR (0x080)
+#define ETMVIIECTLR (0x084)
+#define ETMVISSCTLR (0x088)
+#define ETMVIPCSSCTLR (0x08c)
+#define ETMVDCTLR (0x0a0)
+#define ETMVDSACCTLR (0x0a4)
+#define ETMVDARCCTLR (0x0a8)
+#define ETMSEQEVR(n) (0x100 + (n * 4))
+#define ETMSEQRSTEVR (0x118)
+#define ETMSEQSTR (0x11c)
+#define ETMEXTINSELR (0x120)
+#define ETMCNTRLDVR(n) (0x140 + (n * 4))
+#define ETMCNTCTLR(n) (0x150 + (n * 4))
+#define ETMCNTVR(n) (0x160 + (n * 4))
+#define ETMIDR8 (0x180)
+#define ETMIDR9 (0x184)
+#define ETMID10 (0x188)
+#define ETMID11 (0x18c)
+#define ETMID12 (0x190)
+#define ETMID13 (0x194)
+#define ETMID0 (0x1e0)
+#define ETMID1 (0x1e4)
+#define ETMID2 (0x1e8)
+#define ETMID3 (0x1ec)
+#define ETMID4 (0x1f0)
+#define ETMID5 (0x1f4)
+#define ETMID6 (0x1f8)
+#define ETMID7 (0x1fc)
+#define ETMRSCTLR(n) (0x200 + (n * 4))
+#define ETMSSCCR(n) (0x280 + (n * 4))
+#define ETMSSCSR(n) (0x2a0 + (n * 4))
+#define ETMSSPCICR(n) (0x2c0 + (n * 4))
+#define ETMOSLAR (0x300)
+#define ETMOSLSR (0x304)
+#define ETMPDCR (0x310)
+#define ETMPDSR (0x314)
+#define ETMACVR(n) (0x400 + (n * 4))
+#define ETMACAT(n) (0x480 + (n * 4))
+#define ETMDVCVR(n) (0x500 + (n * 4))
+#define ETMDVCMR(n) (0x580 + (n * 4))
+#define ETMCIDCVR(n) (0x600 + (n * 4))
+#define ETMVMIDCVR(n) (0x640 + (n * 4))
+#define ETMCCIDCCTLR0 (0x680)
+#define ETMCCIDCCTLR1 (0x684)
+#define ETMVMIDCCTLR0 (0x688)
+#define ETMVMIDCCTLR1 (0x68c)
+
+#ifdef CONFIG_EXYNOS_CORESIGHT_ETM
+extern void exynos_trace_stop(void);
+#else
+#define exynos_trace_stop() do { } while(0)
+#endif
+
+#endif
#define ARM_CPU_IMP_CAVIUM 0x43
#define ARM_CPU_IMP_BRCM 0x42
#define ARM_CPU_IMP_QCOM 0x51
+#define ARM_CPU_IMP_SEC 0x53
#define ARM_CPU_IMP_NVIDIA 0x4E
#define ARM_CPU_PART_AEM_V8 0xD0F
#define ARM_CPU_PART_CORTEX_X2 0xD48
#define ARM_CPU_PART_NEOVERSE_N2 0xD49
#define ARM_CPU_PART_CORTEX_A78C 0xD4B
+#define ARM_CPU_PART_ANANKE 0xD05
+#define ARM_CPU_PART_MONGOOSE 0x001
+#define ARM_CPU_PART_MEERKAT 0x002
#define APM_CPU_PART_POTENZA 0x000
#ifdef __KERNEL__
#include <linux/types.h>
+#include <linux/blk_types.h>
+#include <linux/debug-snapshot.h>
#include <asm/byteorder.h>
#include <asm/barrier.h>
#define __raw_writeb __raw_writeb
static inline void __raw_writeb(u8 val, volatile void __iomem *addr)
{
+ dbg_snapshot_reg(0, (size_t)val, (size_t)addr, DSS_FLAG_IN);
asm volatile("strb %w0, [%1]" : : "rZ" (val), "r" (addr));
+ dbg_snapshot_reg(0, (size_t)val, (size_t)addr, DSS_FLAG_OUT);
}
#define __raw_writew __raw_writew
static inline void __raw_writew(u16 val, volatile void __iomem *addr)
{
+ dbg_snapshot_reg(0, (size_t)val, (size_t)addr, DSS_FLAG_IN);
asm volatile("strh %w0, [%1]" : : "rZ" (val), "r" (addr));
+ dbg_snapshot_reg(0, (size_t)val, (size_t)addr, DSS_FLAG_OUT);
}
#define __raw_writel __raw_writel
static inline void __raw_writel(u32 val, volatile void __iomem *addr)
{
+ dbg_snapshot_reg(0, (size_t)val, (size_t)addr, DSS_FLAG_IN);
asm volatile("str %w0, [%1]" : : "rZ" (val), "r" (addr));
+ dbg_snapshot_reg(0, (size_t)val, (size_t)addr, DSS_FLAG_OUT);
}
#define __raw_writeq __raw_writeq
static inline void __raw_writeq(u64 val, volatile void __iomem *addr)
{
+ dbg_snapshot_reg(0, (size_t)val, (size_t)addr, DSS_FLAG_IN);
asm volatile("str %x0, [%1]" : : "rZ" (val), "r" (addr));
+ dbg_snapshot_reg(0, (size_t)val, (size_t)addr, DSS_FLAG_OUT);
}
#define __raw_readb __raw_readb
static inline u8 __raw_readb(const volatile void __iomem *addr)
{
u8 val;
+
+ dbg_snapshot_reg(1, 0, (size_t)addr, DSS_FLAG_IN);
asm volatile(ALTERNATIVE("ldrb %w0, [%1]",
"ldarb %w0, [%1]",
ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE)
: "=r" (val) : "r" (addr));
+ dbg_snapshot_reg(1, (size_t)val, (size_t)addr, DSS_FLAG_OUT);
return val;
}
{
u16 val;
+ dbg_snapshot_reg(1, 0, (size_t)addr, DSS_FLAG_IN);
asm volatile(ALTERNATIVE("ldrh %w0, [%1]",
"ldarh %w0, [%1]",
ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE)
: "=r" (val) : "r" (addr));
+ dbg_snapshot_reg(1, (size_t)val, (size_t)addr, DSS_FLAG_OUT);
return val;
}
static inline u32 __raw_readl(const volatile void __iomem *addr)
{
u32 val;
+
+ dbg_snapshot_reg(1, 0, (size_t)addr, DSS_FLAG_IN);
asm volatile(ALTERNATIVE("ldr %w0, [%1]",
"ldar %w0, [%1]",
ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE)
: "=r" (val) : "r" (addr));
+ dbg_snapshot_reg(1, (size_t)val, (size_t)addr, DSS_FLAG_OUT);
return val;
}
static inline u64 __raw_readq(const volatile void __iomem *addr)
{
u64 val;
+
+ dbg_snapshot_reg(1, 0, (size_t)addr, DSS_FLAG_IN);
asm volatile(ALTERNATIVE("ldr %0, [%1]",
"ldar %0, [%1]",
ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE)
: "=r" (val) : "r" (addr));
+ dbg_snapshot_reg(1, (size_t)val, (size_t)addr, DSS_FLAG_OUT);
return val;
}
#ifdef __KERNEL__
#include <asm/ptrace.h>
+#include <linux/debug-snapshot.h>
/*
* CPU interrupt mask handling.
: "=r" (flags)
:
: "memory");
+ dbg_snapshot_irqs_disabled(0);
return flags;
}
static inline void arch_local_irq_enable(void)
{
+ dbg_snapshot_irqs_disabled(1);
asm volatile(
"msr daifclr, #2 // arch_local_irq_enable"
:
:
:
: "memory");
+ dbg_snapshot_irqs_disabled(0);
}
#define local_fiq_enable() asm("msr daifclr, #1" : : : "memory")
*/
static inline void arch_local_irq_restore(unsigned long flags)
{
+ dbg_snapshot_irqs_disabled(flags & PSR_I_BIT);
asm volatile(
"msr daif, %0 // arch_local_irq_restore"
:
#include <linux/hw_breakpoint.h>
#include <linux/personality.h>
#include <linux/notifier.h>
+#include <linux/debug-snapshot.h>
#include <trace/events/power.h>
#include <linux/percpu.h>
if (efi_enabled(EFI_RUNTIME_SERVICES))
efi_reboot(reboot_mode, NULL);
+ dbg_snapshot_post_reboot(cmd);
+
/* Now call the architecture specific reboot code. */
if (arm_pm_restart)
arm_pm_restart(reboot_mode, cmd);
top_reg = 29;
}
+ if (!user_mode(regs)) {
+ dbg_snapshot_save_context(regs);
+ /*
+ * If you want to see more kernel events after panic,
+ * you should modify dbg_snapshot_set_enable's function 2nd parameter
+ * to true.
+ */
+ dbg_snapshot_set_enable("log_kevents", false);
+ }
+
show_regs_print_info(KERN_DEFAULT);
print_symbol("pc : %s\n", regs->pc);
print_symbol("lr : %s\n", lr);
#include <linux/of.h>
#include <linux/irq_work.h>
#include <linux/kexec.h>
+#include <linux/debug-snapshot.h>
#include <asm/alternative.h>
#include <asm/atomic.h>
/*
* ipi_cpu_stop - handle IPI from smp_send_stop()
*/
-static void ipi_cpu_stop(unsigned int cpu)
+static void ipi_cpu_stop(unsigned int cpu, struct pt_regs *regs)
{
set_cpu_online(cpu, false);
local_irq_disable();
+ dbg_snapshot_save_context(regs);
+
while (1)
cpu_relax();
}
__inc_irq_stat(cpu, ipi_irqs[ipinr]);
}
+ dbg_snapshot_irq(ipinr, handle_IPI, irqs_disabled(), DSS_FLAG_IN);
+
switch (ipinr) {
case IPI_RESCHEDULE:
scheduler_ipi();
case IPI_CPU_STOP:
irq_enter();
- ipi_cpu_stop(cpu);
+ ipi_cpu_stop(cpu, regs);
irq_exit();
break;
if ((unsigned)ipinr < NR_IPI)
trace_ipi_exit_rcuidle(ipi_types[ipinr]);
+
+ dbg_snapshot_irq(ipinr, handle_IPI, irqs_disabled(), DSS_FLAG_OUT);
+
set_irq_regs(old_regs);
}
#include <linux/sizes.h>
#include <linux/syscalls.h>
#include <linux/mm_types.h>
+#include <linux/debug-snapshot.h>
#include <asm/atomic.h>
#include <asm/bug.h>
#include <linux/perf_event.h>
#include <linux/preempt.h>
#include <linux/hugetlb.h>
+#include <linux/debug-snapshot.h>
#include <asm/bug.h>
#include <asm/cmpxchg.h>
sched_idle_set_state(target_state, index);
trace_cpu_idle_rcuidle(index, dev->cpu);
+ dbg_snapshot_cpuidle(drv->states[index].desc, index, 0, DSS_FLAG_IN);
time_start = ns_to_ktime(local_clock());
stop_critical_timings();
sched_clock_idle_wakeup_event();
time_end = ns_to_ktime(local_clock());
+ dbg_snapshot_cpuidle(drv->states[index].desc, entered_state,
+ (int)ktime_to_us(ktime_sub(time_end, time_start)), DSS_FLAG_OUT);
trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu);
/* The cpu is no longer idle or about to enter idle. */
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/spinlock.h>
+#include <linux/debug-snapshot.h>
struct gpio_button_data {
const struct gpio_keys_button *button;
return;
}
+ dbg_snapshot_check_crash_key(button->code, state);
+
if (type == EV_ABS) {
if (state)
input_event(input, type, button->code, button->value);
#include <linux/clk.h>
#include <linux/smc.h>
+#include <linux/debug-snapshot.h>
#include "ufshcd.h"
#include "unipro.h"
--- /dev/null
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Exynos - Support SoC For Debug SnapShot
+ * Author: Hosung Kim <hosung0.kim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/smc.h>
+#include <linux/debug-snapshot-helper.h>
+#include <linux/debug-snapshot.h>
+#include <soc/samsung/exynos-debug.h>
+#include <soc/samsung/exynos-pmu.h>
+#include <soc/samsung/exynos-sdm.h>
+
+#ifdef CONFIG_EXYNOS_CORESIGHT_ETR
+#include <asm/core_regs.h>
+#endif
+
+#if defined(CONFIG_SEC_SIPC_MODEM_IF)
+#include <soc/samsung/exynos-modem-ctrl.h>
+#endif
+
+#if defined(CONFIG_ACPM_DVFS)
+#include <soc/samsung/acpm_ipc_ctrl.h>
+#endif
+
+static void exynos_early_panic(void *val)
+{
+}
+
+static void exynos_prepare_panic_entry(void *val)
+{
+ /* TODO: Something */
+}
+
+static void exynos_prepare_panic_exit(void *val)
+{
+#if defined(CONFIG_SEC_SIPC_MODEM_IF)
+ modem_send_panic_noti_ext();
+#endif
+#if defined(CONFIG_ACPM_DVFS)
+ acpm_stop_log();
+#endif
+}
+
+static void exynos_post_panic_entry(void *val)
+{
+ /* TODO: Something */
+
+}
+
+static void exynos_post_panic_exit(void *val)
+{
+#ifdef CONFIG_EXYNOS_SDM
+ if (dbg_snapshot_is_scratch())
+ exynos_sdm_dump_secure_region();
+#endif
+
+}
+
+static void exynos_save_context_entry(void *val)
+{
+#ifdef CONFIG_EXYNOS_CORESIGHT_ETR
+ exynos_trace_stop();
+#endif
+}
+
+static void exynos_save_context_exit(void *val)
+{
+ /* TODO: Something */
+}
+
+static void exynos_start_watchdog(void *val)
+{
+#ifdef CONFIG_EXYNOS_SNAPSHOT_WATCHDOG_RESET
+ s3c2410wdt_keepalive_emergency(true, 0);
+#endif
+}
+
+static void exynos_expire_watchdog(void *val)
+{
+#ifdef CONFIG_EXYNOS_SNAPSHOT_WATCHDOG_RESET
+ s3c2410wdt_set_emergency_reset(100, 0);
+#endif
+}
+
+static void exynos_stop_watchdog(void *val)
+{
+
+}
+
+static void exynos_kick_watchdog(void *val)
+{
+#ifdef CONFIG_EXYNOS_SNAPSHOT_WATCHDOG_RESET
+ s3c2410wdt_keepalive_emergency(false, 0);
+#endif
+}
+
+static int exynos_is_power_cpu(void *cpu)
+{
+#ifdef CONFIG_EXYNOS_PMU
+ return exynos_cpu.power_state((unsigned int)(unsigned long)cpu);
+#else
+ return 0;
+#endif
+}
+
+struct dbg_snapshot_helper_ops exynos_debug_ops = {
+ .soc_early_panic = exynos_early_panic,
+ .soc_prepare_panic_entry = exynos_prepare_panic_entry,
+ .soc_prepare_panic_exit = exynos_prepare_panic_exit,
+ .soc_post_panic_entry = exynos_post_panic_entry,
+ .soc_post_panic_exit = exynos_post_panic_exit,
+ .soc_save_context_entry = exynos_save_context_entry,
+ .soc_save_context_exit = exynos_save_context_exit,
+ .soc_start_watchdog = exynos_start_watchdog,
+ .soc_expire_watchdog = exynos_expire_watchdog,
+ .soc_stop_watchdog = exynos_stop_watchdog,
+ .soc_kick_watchdog = exynos_kick_watchdog,
+ .soc_smc_call = exynos_smc,
+ .soc_is_power_cpu = exynos_is_power_cpu,
+};
+
+void __init dbg_snapshot_soc_helper_init(void)
+{
+ dbg_snapshot_register_soc_ops(&exynos_debug_ops);
+}
#include <linux/syscore_ops.h>
#include <linux/soc/samsung/exynos-soc.h>
#include <soc/samsung/exynos-pmu.h>
+#include <linux/debug-snapshot.h>
#define S3C2410_WTCON 0x00
#define S3C2410_WTDAT 0x04
#define dbg_snapshot_check_crash_key(a,b) do { } while(0)
#endif
+#ifdef CONFIG_S3C2410_WATCHDOG
+extern int s3c2410wdt_set_emergency_stop(int index);
+extern int s3c2410wdt_set_emergency_reset(unsigned int timeout, int index);
+extern int s3c2410wdt_keepalive_emergency(bool reset, int index);
+#else
+#define s3c2410wdt_set_emergency_stop(a) (-1)
+#define s3c2410wdt_set_emergency_reset(a, b) do { } while(0)
+#define s3c2410wdt_keepalive_emergency(a, b) do { } while(0)
+#endif
+
#else
#define dbg_snapshot_acpm(a,b,c) do { } while(0)
#define dbg_snapshot_task(a,b) do { } while(0)
--- /dev/null
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Debug-SnapShot: Debug Framework for Ramdump based debugging method
+ * The original code is Exynos-Snapshot for Exynos SoC
+ *
+ * Author: Hosung Kim <hosung0.kim@samsung.com>
+ * Author: Changki Kim <changki.kim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef EXYNOS_DEBUG_H
+#define EXYNOS_DEBUG_H
+
+#ifdef CONFIG_S3C2410_WATCHDOG
+extern int s3c2410wdt_set_emergency_stop(int index);
+extern int s3c2410wdt_set_emergency_reset(unsigned int timeout, int index);
+extern int s3c2410wdt_keepalive_emergency(bool reset, int index);
+extern void s3c2410wdt_reset_confirm(unsigned long mtime, int index);
+#else
+#define s3c2410wdt_set_emergency_stop(a) (-1)
+#define s3c2410wdt_set_emergency_reset(a, b) do { } while(0)
+#define s3c2410wdt_keepalive_emergency(a, b) do { } while(0)
+#define s3c2410wdt_reset_confirm(a, b) do { } while(0)
+#endif
+
+#endif
+
+
--- /dev/null
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * EXYNOS - Security Dump Manager support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef EXYNOS_SDM_H
+#define EXYNOS_SDM_H
+
+#ifdef CONFIG_EXYNOS_SDM
+int exynos_sdm_dump_secure_region(void);
+int exynos_sdm_flush_secdram(void);
+#else
+#define exynos_sdm_dump_secure_region() do { } while(0)
+#define exynos_sdm_flush_secdram() do { } while(0)
+#endif
+
+#endif /* EXYNOS_SDM_H */
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
+#include <linux/debug-snapshot.h>
#include <trace/events/irq.h>
irqreturn_t res;
trace_irq_handler_entry(irq, action);
+ dbg_snapshot_irq(irq, (void *)action->handler, (int)irqs_disabled(), DSS_FLAG_IN);
res = action->handler(irq, action->dev_id);
+ dbg_snapshot_irq(irq, (void *)action->handler, (int)irqs_disabled(), DSS_FLAG_OUT);
trace_irq_handler_exit(irq, action, res);
if (WARN_ONCE(!irqs_disabled(),"irq %u handler %pF enabled interrupts\n",
#include <linux/bitmap.h>
#include <linux/irqdomain.h>
#include <linux/sysfs.h>
+#include <linux/debug-snapshot.h>
#include "internals.h"
bool lookup, struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
+ unsigned long long start_time;
unsigned int irq = hwirq;
int ret = 0;
+ dbg_snapshot_irq_exit_var(start_time);
irq_enter();
#ifdef CONFIG_IRQ_DOMAIN
}
irq_exit();
+ dbg_snapshot_irq_exit(irq, start_time);
set_irq_regs(old_regs);
return ret;
}
#include <linux/bug.h>
#include <linux/ratelimit.h>
#include <linux/sysfs.h>
+#include <linux/debug-snapshot.h>
#define PANIC_TIMER_STEP 100
#define PANIC_BLINK_SPD 18
this_cpu = raw_smp_processor_id();
old_cpu = atomic_cmpxchg(&panic_cpu, PANIC_CPU_INVALID, this_cpu);
- if (old_cpu != PANIC_CPU_INVALID && old_cpu != this_cpu)
+ if (old_cpu != PANIC_CPU_INVALID && old_cpu != this_cpu) {
panic_smp_self_stop();
+ }
console_verbose();
bust_spinlocks(1);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
pr_emerg("Kernel panic - not syncing: %s\n", buf);
+
+ dbg_snapshot_prepare_panic();
+ dbg_snapshot_dump_panic(buf, (size_t)strnlen(buf, sizeof(buf)));
#ifdef CONFIG_DEBUG_BUGVERBOSE
/*
* Avoid nested stack-dumping if a panic occurs during oops processing
if (!test_taint(TAINT_DIE) && oops_in_progress <= 1)
dump_stack();
#endif
+ //sysrq_sched_debug_show();
/*
* If we have crashed and we have a crash kernel loaded let it handle
printk_safe_flush_on_panic();
kmsg_dump(KMSG_DUMP_PANIC);
+ dbg_snapshot_post_panic();
+
/*
* If you doubt kdump always works fine in any situation,
* "crash_kexec_post_notifiers" offers you a chance to run
#endif
module_param_named(process, printk_process, bool, S_IRUGO | S_IWUSR);
-#ifdef CONFIG_EXYNOS_SNAPSHOT
+#ifdef CONFIG_DEBUG_SNAPSHOT
static size_t hook_size;
static char hook_text[LOG_LINE_MAX + PREFIX_MAX];
static void (*func_hook_logbuf)(const char *buf, size_t size);
msg->in_interrupt = in_interrupt() ? 1 : 0;
}
#endif
-#ifdef CONFIG_EXYNOS_SNAPSHOT
+#ifdef CONFIG_DEBUG_SNAPSHOT
if (func_hook_logbuf) {
hook_size = msg_print_text(msg,
true, hook_text, LOG_LINE_MAX + PREFIX_MAX);
#include <linux/trace_events.h>
#include <linux/suspend.h>
#include <linux/ftrace.h>
+#include <linux/debug-snapshot.h>
#include "tree.h"
#include "rcu.h"
* See Documentation/RCU/stallwarn.txt for info on how to debug
* RCU CPU stall warnings.
*/
+
+ dbg_snapshot_printkl((size_t)rsp->name, (size_t)rsp);
pr_err("INFO: %s detected stalls on CPUs/tasks:",
rsp->name);
print_cpu_stall_info_begin();
* See Documentation/RCU/stallwarn.txt for info on how to debug
* RCU CPU stall warnings.
*/
+
+ dbg_snapshot_printkl((size_t)rsp->name, (size_t)rsp);
pr_err("INFO: %s self-detected stall on CPU", rsp->name);
print_cpu_stall_info_begin();
print_cpu_stall_info(rsp, smp_processor_id());
#include <linux/profile.h>
#include <linux/security.h>
#include <linux/syscalls.h>
+#include <linux/debug-snapshot.h>
#include <asm/switch_to.h>
#include <asm/tlb.h>
rq_unlock_irq(rq, &rf);
}
+ dbg_snapshot_task(cpu, rq->curr);
balance_callback(rq);
}
#include <linux/smpboot.h>
#include <linux/tick.h>
#include <linux/irq.h>
+#include <linux/debug-snapshot.h>
#define CREATE_TRACE_POINTS
#include <trace/events/irq.h>
kstat_incr_softirqs_this_cpu(vec_nr);
trace_softirq_entry(vec_nr);
+ dbg_snapshot_irq(DSS_FLAG_SOFTIRQ, h->action, irqs_disabled(), DSS_FLAG_IN);
h->action(h);
+ dbg_snapshot_irq(DSS_FLAG_SOFTIRQ, h->action, irqs_disabled(), DSS_FLAG_OUT);
trace_softirq_exit(vec_nr);
if (unlikely(prev_count != preempt_count())) {
pr_err("huh, entered softirq %u %s %p with preempt_count %08x, exited with %08x?\n",
if (!test_and_clear_bit(TASKLET_STATE_SCHED,
&t->state))
BUG();
+ dbg_snapshot_irq(DSS_FLAG_SOFTIRQ_TASKLET,
+ t->func, irqs_disabled(), DSS_FLAG_IN);
t->func(t->data);
+ dbg_snapshot_irq(DSS_FLAG_SOFTIRQ_TASKLET,
+ t->func, irqs_disabled(), DSS_FLAG_OUT);
tasklet_unlock(t);
continue;
}
if (!test_and_clear_bit(TASKLET_STATE_SCHED,
&t->state))
BUG();
+ dbg_snapshot_irq(DSS_FLAG_SOFTIRQ_HI_TASKLET,
+ t->func, irqs_disabled(), DSS_FLAG_IN);
t->func(t->data);
+ dbg_snapshot_irq(DSS_FLAG_SOFTIRQ_HI_TASKLET,
+ t->func, irqs_disabled(), DSS_FLAG_OUT);
tasklet_unlock(t);
continue;
}
#include <linux/module.h>
#include <linux/smp.h>
#include <linux/device.h>
+#include <linux/debug-snapshot.h>
#include "tick-internal.h"
dev->retries++;
clc = ((unsigned long long) delta * dev->mult) >> dev->shift;
+ dbg_snapshot_clockevent(clc, delta, &dev->next_event);
if (dev->set_next_event((unsigned long) clc, dev) == 0)
return 0;
dev->retries++;
clc = ((unsigned long long) delta * dev->mult) >> dev->shift;
+ dbg_snapshot_clockevent(clc, delta, &dev->next_event);
return dev->set_next_event((unsigned long) clc, dev);
}
delta = max(delta, (int64_t) dev->min_delta_ns);
clc = ((unsigned long long) delta * dev->mult) >> dev->shift;
+ dbg_snapshot_clockevent(clc, delta, &dev->next_event);
rc = dev->set_next_event((unsigned long) clc, dev);
return (rc && force) ? clockevents_program_min_delta(dev) : rc;
#include <linux/timer.h>
#include <linux/freezer.h>
#include <linux/compat.h>
+#include <linux/debug-snapshot.h>
#include <linux/uaccess.h>
*/
raw_spin_unlock(&cpu_base->lock);
trace_hrtimer_expire_entry(timer, now);
+ dbg_snapshot_hrtimer(timer, now, fn, DSS_FLAG_IN);
restart = fn(timer);
+ dbg_snapshot_hrtimer(timer, now, fn, DSS_FLAG_OUT);
trace_hrtimer_expire_exit(timer);
raw_spin_lock(&cpu_base->lock);
#include <linux/slab.h>
#include <linux/compat.h>
#include <linux/random.h>
+#include <linux/debug-snapshot.h>
#include <linux/uaccess.h>
#include <asm/unistd.h>
lock_map_acquire(&lockdep_map);
trace_timer_expire_entry(timer);
+ dbg_snapshot_irq(DSS_FLAG_CALL_TIMER_FN, fn, irqs_disabled(), DSS_FLAG_IN);
fn(data);
+ dbg_snapshot_irq(DSS_FLAG_CALL_TIMER_FN, fn, irqs_disabled(), DSS_FLAG_OUT);
trace_timer_expire_exit(timer);
lock_map_release(&lockdep_map);
#include <linux/uaccess.h>
#include <linux/nmi.h>
#include <linux/kvm_para.h>
+#include <linux/debug-snapshot.h>
#include "workqueue_internal.h"
*/
lockdep_invariant_state(true);
trace_workqueue_execute_start(work);
+ dbg_snapshot_work(worker, worker->task, worker->current_func, DSS_FLAG_IN);
worker->current_func(work);
+ dbg_snapshot_work(worker, worker->task, worker->current_func, DSS_FLAG_OUT);
/*
* While we must be careful to not use "work" after this, the trace
* point will only record its address.
if the driver using a given range cannot be disabled.
If in doubt, say Y.
+
+menuconfig DEBUG_SNAPSHOT
+ bool "Debug SnapShot Support"
+ default n
+
+config DEBUG_SNAPSHOT_CALLSTACK
+ int "shown callstack level"
+ depends on DEBUG_SNAPSHOT
+ range 1 4
+ default 4
+
+config DEBUG_SNAPSHOT_IRQ_EXIT
+ bool "Enable debugging of interrupt exit event by kevent"
+ depends on DEBUG_SNAPSHOT && !DEBUG_SNAPSHOT_MINIMIZED_MODE
+ default y
+
+config DEBUG_SNAPSHOT_IRQ_EXIT_THRESHOLD
+ int "threshold of detection(microsecond)"
+ depends on DEBUG_SNAPSHOT_IRQ_EXIT && !DEBUG_SNAPSHOT_MINIMIZED_MODE
+ range 0 1000000
+ default 0
+
+config DEBUG_SNAPSHOT_IRQ_DISABLED
+ bool "Enable debugging of interrupt disable event by kevent(EXPERIMENTAL)"
+ depends on DEBUG_SNAPSHOT && !DEBUG_SNAPSHOT_MINIMIZED_MODE
+ default n
+
+config DEBUG_SNAPSHOT_SPINLOCK
+ bool "Enable debugging of spinlock event by kevent(EXPERIMENTAL)"
+ depends on DEBUG_SNAPSHOT && DEBUG_SPINLOCK && !DEBUG_SNAPSHOT_MINIMIZED_MODE
+ default y
+
+config DEBUG_SNAPSHOT_CLK
+ bool "Enable debugging of clock event by kevent(EXPERIMENTAL)"
+ depends on DEBUG_SNAPSHOT && !DEBUG_SNAPSHOT_MINIMIZED_MODE
+ default y
+
+config DEBUG_SNAPSHOT_PMU
+ bool "Enable debugging of pmu event by kevent(EXPERIMENTAL)"
+ depends on DEBUG_SNAPSHOT && !DEBUG_SNAPSHOT_MINIMIZED_MODE
+ default y
+
+config DEBUG_SNAPSHOT_FREQ
+ bool "Enable debugging of frequence event by kevent(EXPERIMENTAL)"
+ depends on DEBUG_SNAPSHOT && PM_DEVFREQ && !DEBUG_SNAPSHOT_MINIMIZED_MODE
+ default y
+
+config DEBUG_SNAPSHOT_DM
+ bool "Enable debugging of dvfs manager event by kevent(EXPERIMENTAL)"
+ depends on DEBUG_SNAPSHOT && EXYNOS_DVFS_MANAGER && !DEBUG_SNAPSHOT_MINIMIZED_MODE
+ default y
+
+config DEBUG_SNAPSHOT_HRTIMER
+ bool "Enable debugging of hrtimer by kevent"
+ depends on DEBUG_SNAPSHOT && !DEBUG_SNAPSHOT_MINIMIZED_MODE
+ default y
+
+config DEBUG_SNAPSHOT_REG
+ bool "Enable debugging of accessing register by kevent"
+ depends on DEBUG_SNAPSHOT && !DEBUG_SNAPSHOT_MINIMIZED_MODE
+ default n
+
+config DEBUG_SNAPSHOT_REGULATOR
+ bool "Enable debugging of regulator and pmic driver"
+ depends on DEBUG_SNAPSHOT && !DEBUG_SNAPSHOT_MINIMIZED_MODE
+ default y
+
+config DEBUG_SNAPSHOT_ACPM
+ bool "Enable debugging of acpm framework"
+ depends on DEBUG_SNAPSHOT && !DEBUG_SNAPSHOT_MINIMIZED_MODE
+ default y
+
+config DEBUG_SNAPSHOT_THERMAL
+ bool "Enable debugging of thermal driver"
+ depends on DEBUG_SNAPSHOT && !DEBUG_SNAPSHOT_MINIMIZED_MODE && EXYNOS_THERMAL
+ default y
+
+config DEBUG_SNAPSHOT_I2C
+ bool "Enable debugging of i2c driver"
+ depends on DEBUG_SNAPSHOT && !DEBUG_SNAPSHOT_MINIMIZED_MODE
+ default y
+
+config DEBUG_SNAPSHOT_SPI
+ bool "Enable debugging of spi driver"
+ depends on DEBUG_SNAPSHOT && !DEBUG_SNAPSHOT_MINIMIZED_MODE
+ default y
+
+config DEBUG_SNAPSHOT_PANIC_REBOOT
+ bool "Enable forced reboot after panic for ramdump"
+ depends on DEBUG_SNAPSHOT
+ default y
+
+config DEBUG_SNAPSHOT_WATCHDOG_RESET
+ bool "Support watchdog reset when hardlockup detect"
+ depends on DEBUG_SNAPSHOT && DEBUG_SNAPSHOT_PANIC_REBOOT
+ default y
+
+config DEBUG_SNAPSHOT_CRASH_KEY
+ bool "Support Crash Key to artificial panic for debugging"
+ depends on DEBUG_SNAPSHOT && KEYBOARD_GPIO
+ default y
+
+config DEBUG_SNAPSHOT_MINIMIZED_MODE
+ bool "Support minimized feature configuration"
+ depends on DEBUG_SNAPSHOT
+ default n
obj-$(CONFIG_PARMAN) += parman.o
obj-y += crypto/
+
+obj-$(CONFIG_DEBUG_SNAPSHOT) += debug-snapshot.o debug-snapshot-log.o debug-snapshot-utils.o \
+ debug-snapshot-pstore.o debug-snapshot-sysfs.o debug-snapshot-helper.o
--- /dev/null
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Debug-SnapShot: Debug Framework for Ramdump based debugging method
+ * The original code is Exynos-Snapshot for Exynos SoC
+ *
+ * Author: Hosung Kim <hosung0.kim@samsung.com>
+ * Author: Changki Kim <changki.kim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/delay.h>
+#include <linux/kallsyms.h>
+#include <linux/input.h>
+#include <linux/smc.h>
+#include <linux/bitops.h>
+#include <linux/sched/clock.h>
+#include <linux/sched/debug.h>
+#include <linux/nmi.h>
+#include <linux/init_task.h>
+#include <linux/ftrace.h>
+
+#include <asm/cputype.h>
+#include <asm/smp_plat.h>
+#include <asm/core_regs.h>
+#include <asm/cacheflush.h>
+
+#include "debug-snapshot-local.h"
+#include <linux/debug-snapshot-helper.h>
+
+static void dbg_snapshot_soc_dummy_func(void *dummy) {return;}
+static int dbg_snapshot_soc_dummy_func_int(void *dummy) {return 0;}
+static int dbg_snapshot_soc_dummy_func_smc(unsigned long dummy1,
+ unsigned long dummy2,
+ unsigned long dummy3,
+ unsigned long dummy4) {return 0;}
+
+static struct dbg_snapshot_helper_ops dss_soc_dummy_ops = {
+ .soc_early_panic = dbg_snapshot_soc_dummy_func,
+ .soc_prepare_panic_entry = dbg_snapshot_soc_dummy_func,
+ .soc_prepare_panic_exit = dbg_snapshot_soc_dummy_func,
+ .soc_save_context_entry = dbg_snapshot_soc_dummy_func,
+ .soc_save_context_exit = dbg_snapshot_soc_dummy_func,
+ .soc_post_panic_entry = dbg_snapshot_soc_dummy_func,
+ .soc_post_panic_exit = dbg_snapshot_soc_dummy_func,
+ .soc_start_watchdog = dbg_snapshot_soc_dummy_func,
+ .soc_expire_watchdog = dbg_snapshot_soc_dummy_func,
+ .soc_stop_watchdog = dbg_snapshot_soc_dummy_func,
+ .soc_kick_watchdog = dbg_snapshot_soc_dummy_func,
+ .soc_is_power_cpu = dbg_snapshot_soc_dummy_func_int,
+ .soc_smc_call = dbg_snapshot_soc_dummy_func_smc,
+};
+
+struct dbg_snapshot_helper_ops *dss_soc_ops;
+
+void __iomem *dbg_snapshot_get_base_vaddr(void)
+{
+ return (void __iomem *)(dss_base.vaddr);
+}
+
+void __iomem *dbg_snapshot_get_base_paddr(void)
+{
+ return (void __iomem *)(dss_base.paddr);
+}
+
+static void dbg_snapshot_set_core_power_stat(unsigned int val, unsigned cpu)
+{
+ if (dbg_snapshot_get_enable("header"))
+ __raw_writel(val, (dbg_snapshot_get_base_vaddr() +
+ DSS_OFFSET_CORE_POWER_STAT + cpu * 4));
+}
+
+unsigned int dbg_snapshot_get_core_panic_stat(unsigned cpu)
+{
+ if (dbg_snapshot_get_enable("header"))
+ return __raw_readl(dbg_snapshot_get_base_vaddr() +
+ DSS_OFFSET_PANIC_STAT + cpu * 4);
+ else
+ return 0;
+}
+
+void dbg_snapshot_set_core_panic_stat(unsigned int val, unsigned cpu)
+{
+ if (dbg_snapshot_get_enable("header"))
+ __raw_writel(val, (dbg_snapshot_get_base_vaddr() +
+ DSS_OFFSET_PANIC_STAT + cpu * 4));
+}
+
+static void dbg_snapshot_report_reason(unsigned int val)
+{
+ if (dbg_snapshot_get_enable("header"))
+ __raw_writel(val, dbg_snapshot_get_base_vaddr() + DSS_OFFSET_EMERGENCY_REASON);
+}
+
+void dbg_snapshot_scratch_reg(unsigned int val)
+{
+ if (dbg_snapshot_get_enable("header"))
+ __raw_writel(val, dbg_snapshot_get_base_vaddr() + DSS_OFFSET_SCRATCH);
+}
+
+bool dbg_snapshot_is_scratch(void)
+{
+ return __raw_readl(dbg_snapshot_get_base_vaddr() +
+ DSS_OFFSET_SCRATCH) == DSS_SIGN_SCRATCH;
+}
+
+unsigned long dbg_snapshot_get_last_pc_paddr(void)
+{
+ /*
+ * Basically we want to save the pc value to non-cacheable region
+ * if ESS is enabled. But we should also consider cases that are not so.
+ */
+
+ if (dbg_snapshot_get_enable("header"))
+ return ((unsigned long)dbg_snapshot_get_base_paddr() + DSS_OFFSET_CORE_LAST_PC);
+ else
+ return virt_to_phys((void *)dss_desc.hardlockup_core_pc);
+}
+
+unsigned long dbg_snapshot_get_last_pc(unsigned int cpu)
+{
+ if (dbg_snapshot_get_enable("header"))
+ return __raw_readq(dbg_snapshot_get_base_vaddr() +
+ DSS_OFFSET_CORE_LAST_PC + cpu * 8);
+ else
+ return dss_desc.hardlockup_core_pc[cpu];
+}
+
+unsigned long dbg_snapshot_get_spare_vaddr(unsigned int offset)
+{
+ return (unsigned long)(dbg_snapshot_get_base_vaddr() +
+ DSS_OFFSET_SPARE_BASE + offset);
+}
+
+unsigned long dbg_snapshot_get_spare_paddr(unsigned int offset)
+{
+ unsigned long base_vaddr = 0;
+ unsigned long base_paddr = (unsigned long)dbg_snapshot_get_base_paddr();
+
+ if (base_paddr)
+ base_vaddr = (unsigned long)(base_paddr +
+ DSS_OFFSET_SPARE_BASE + offset);
+
+ return base_vaddr;
+}
+
+unsigned int dbg_snapshot_get_item_size(char* name)
+{
+ unsigned long i;
+
+ for (i = 0; i < dss_desc.log_enable_cnt; i++) {
+ if (!strncmp(dss_items[i].name, name, strlen(name)))
+ return dss_items[i].entry.size;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(dbg_snapshot_get_item_size);
+
+unsigned long dbg_snapshot_get_item_vaddr(char *name)
+{
+ unsigned long i;
+
+ for (i = 0; i < dss_desc.log_enable_cnt; i++) {
+ if (!strncmp(dss_items[i].name, name, strlen(name)))
+ return dss_items[i].entry.vaddr;
+ }
+ return 0;
+}
+
+unsigned int dbg_snapshot_get_item_paddr(char* name)
+{
+ unsigned long i;
+
+ for (i = 0; i < dss_desc.log_enable_cnt; i++) {
+ if (!strncmp(dss_items[i].name, name, strlen(name)))
+ return dss_items[i].entry.paddr;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(dbg_snapshot_get_item_paddr);
+
+int dbg_snapshot_get_hardlockup(void)
+{
+ return dss_desc.hardlockup_detected;
+}
+EXPORT_SYMBOL(dbg_snapshot_get_hardlockup);
+
+int dbg_snapshot_set_hardlockup(int val)
+{
+ unsigned long flags;
+
+ if (unlikely(!dss_base.enabled))
+ return 0;
+
+ raw_spin_lock_irqsave(&dss_desc.ctrl_lock, flags);
+ dss_desc.hardlockup_detected = val;
+ raw_spin_unlock_irqrestore(&dss_desc.ctrl_lock, flags);
+ return 0;
+}
+EXPORT_SYMBOL(dbg_snapshot_set_hardlockup);
+
+int dbg_snapshot_early_panic(void)
+{
+ dss_soc_ops->soc_early_panic(NULL);
+ return 0;
+}
+
+int dbg_snapshot_prepare_panic(void)
+{
+ unsigned long cpu;
+
+ if (unlikely(!dss_base.enabled))
+ return 0;
+ /*
+ * kick watchdog to prevent unexpected reset during panic sequence
+ * and it prevents the hang during panic sequence by watchedog
+ */
+ dss_soc_ops->soc_start_watchdog(NULL);
+
+ dss_soc_ops->soc_prepare_panic_entry(NULL);
+
+ /* Again disable log_kevents */
+ dbg_snapshot_set_enable("log_kevents", false);
+
+ for_each_possible_cpu(cpu) {
+ if (dss_soc_ops->soc_is_power_cpu((void *)cpu))
+ dbg_snapshot_set_core_power_stat(DSS_SIGN_ALIVE, cpu);
+ else
+ dbg_snapshot_set_core_power_stat(DSS_SIGN_DEAD, cpu);
+ }
+ dss_soc_ops->soc_prepare_panic_exit(NULL);
+
+ return 0;
+}
+EXPORT_SYMBOL(dbg_snapshot_prepare_panic);
+
+int dbg_snapshot_post_panic(void)
+{
+ if (dss_base.enabled) {
+ dss_soc_ops->soc_post_panic_entry(NULL);
+
+ dbg_snapshot_recall_hardlockup_core();
+#ifdef CONFIG_DEBUG_SNAPSHOT_PMU
+ dbg_snapshot_dump_sfr();
+#endif
+ dbg_snapshot_save_context(NULL);
+ flush_cache_all();
+ dbg_snapshot_print_panic_report();
+
+ dss_soc_ops->soc_post_panic_exit(NULL);
+
+#ifdef CONFIG_DEBUG_SNAPSHOT_PANIC_REBOOT
+ if (!dss_desc.no_wdt_dev) {
+#ifdef CONFIG_DEBUG_SNAPSHOT_WATCHDOG_RESET
+ if (dss_desc.hardlockup_detected || num_online_cpus() > 1) {
+ /* for stall cpu */
+ while(1)
+ wfi();
+ }
+#endif
+ }
+#endif
+ }
+#ifdef CONFIG_DEBUG_SNAPSHOT_PANIC_REBOOT
+ arm_pm_restart(0, "panic");
+#endif
+ goto loop;
+ /* for stall cpu when not enabling panic reboot */
+loop:
+ while(1)
+ wfi();
+
+ /* Never run this function */
+ pr_emerg("debug-snapshot: %s DO NOT RUN this function (CPU:%d)\n",
+ __func__, raw_smp_processor_id());
+ return 0;
+}
+EXPORT_SYMBOL(dbg_snapshot_post_panic);
+
+int dbg_snapshot_dump_panic(char *str, size_t len)
+{
+ if (unlikely(!dss_base.enabled) ||
+ !dbg_snapshot_get_enable("header"))
+ return 0;
+
+ /* This function is only one which runs in panic funcion */
+ if (str && len && len < SZ_1K)
+ memcpy(dbg_snapshot_get_base_vaddr() + DSS_OFFSET_PANIC_STRING, str, len);
+
+ return 0;
+}
+EXPORT_SYMBOL(dbg_snapshot_dump_panic);
+
+int dbg_snapshot_post_reboot(char *cmd)
+{
+ int cpu;
+
+ if (unlikely(!dss_base.enabled))
+ return 0;
+
+ /* clear DSS_SIGN_PANIC when normal reboot */
+ for_each_possible_cpu(cpu) {
+ dbg_snapshot_set_core_panic_stat(DSS_SIGN_RESET, cpu);
+ }
+
+ dbg_snapshot_report_reason(DSS_SIGN_NORMAL_REBOOT);
+
+ if (!cmd)
+ dbg_snapshot_scratch_reg(DSS_SIGN_RESET);
+ else if (strcmp((char *)cmd, "bootloader") && strcmp((char *)cmd, "ramdump"))
+ dbg_snapshot_scratch_reg(DSS_SIGN_RESET);
+
+ pr_emerg("debug-snapshot: normal reboot done\n");
+
+ dbg_snapshot_save_context(NULL);
+ flush_cache_all();
+
+ return 0;
+}
+EXPORT_SYMBOL(dbg_snapshot_post_reboot);
+
+static int dbg_snapshot_reboot_handler(struct notifier_block *nb,
+ unsigned long l, void *p)
+{
+ if (unlikely(!dss_base.enabled))
+ return 0;
+
+ pr_emerg("debug-snapshot: normal reboot starting\n");
+
+ return 0;
+}
+
+static int dbg_snapshot_panic_handler(struct notifier_block *nb,
+ unsigned long l, void *buf)
+{
+ dbg_snapshot_report_reason(DSS_SIGN_PANIC);
+ if (unlikely(!dss_base.enabled))
+ return 0;
+
+#ifdef CONFIG_DEBUG_SNAPSHOT_PANIC_REBOOT
+ local_irq_disable();
+ pr_emerg("debug-snapshot: panic - reboot[%s]\n", __func__);
+#else
+ pr_emerg("debug-snapshot: panic - normal[%s]\n", __func__);
+#endif
+ dbg_snapshot_dump_task_info();
+ pr_emerg("linux_banner: %s\n", linux_banner);
+ flush_cache_all();
+ return 0;
+}
+
+static struct notifier_block nb_reboot_block = {
+ .notifier_call = dbg_snapshot_reboot_handler
+};
+
+static struct notifier_block nb_panic_block = {
+ .notifier_call = dbg_snapshot_panic_handler,
+};
+
+void dbg_snapshot_panic_handler_safe(void)
+{
+ char *cpu_num[SZ_16] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
+ char text[SZ_32] = "safe panic handler at cpu ";
+ int cpu = raw_smp_processor_id();
+ size_t len;
+
+ if (unlikely(!dss_base.enabled))
+ return;
+
+ strncat(text, cpu_num[cpu], 1);
+ len = strnlen(text, SZ_32);
+
+ dbg_snapshot_report_reason(DSS_SIGN_SAFE_FAULT);
+ dbg_snapshot_dump_panic(text, len);
+ dss_soc_ops->soc_expire_watchdog((void *)NULL);
+}
+
+void dbg_snapshot_register_soc_ops(struct dbg_snapshot_helper_ops *ops)
+{
+ if (ops)
+ dss_soc_ops = ops;
+}
+
+void __init dbg_snapshot_helper_init(void)
+{
+ register_reboot_notifier(&nb_reboot_block);
+ atomic_notifier_chain_register(&panic_notifier_list, &nb_panic_block);
+ dss_soc_ops = &dss_soc_dummy_ops;
+
+ /* hardlockup_detector function should be called before secondary booting */
+ dbg_snapshot_soc_helper_init();
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Debug-SnapShot: Debug Framework for Ramdump based debugging method
+ * The original code is Exynos-Snapshot for Exynos SoC
+ *
+ * Author: Hosung Kim <hosung0.kim@samsung.com>
+ * Author: Changki Kim <changki.kim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef DEBUG_SNAPSHOT_LOCAL_H
+#define DEBUG_SNAPSHOT_LOCAL_H
+#include <linux/clk-provider.h>
+#include <linux/debug-snapshot.h>
+#include <linux/debug-snapshot-soc.h>
+#include <linux/debug-snapshot-helper.h>
+
+extern void (*arm_pm_restart)(char str, const char *cmd);
+
+extern void dbg_snapshot_log_idx_init(void);
+extern void dbg_snapshot_utils_init(void);
+extern void dbg_snapshot_helper_init(void);
+extern void __iomem *dbg_snapshot_get_base_vaddr(void);
+extern void __iomem *dbg_snapshot_get_base_paddr(void);
+extern void dbg_snapshot_scratch_reg(unsigned int val);
+extern void dbg_snapshot_print_panic_report(void);
+extern void dbg_snapshot_dump_task_info(void);
+
+extern unsigned int dbg_snapshot_get_core_panic_stat(unsigned cpu);
+extern void dbg_snapshot_set_core_panic_stat(unsigned int val, unsigned cpu);
+extern void dbg_snapshot_recall_hardlockup_core(void);
+
+extern struct dbg_snapshot_helper_ops *dss_soc_ops;
+
+/* Size domain */
+#define DSS_KEEP_HEADER_SZ (SZ_256 * 3)
+#define DSS_HEADER_SZ SZ_4K
+#define DSS_MMU_REG_SZ SZ_4K
+#define DSS_CORE_REG_SZ SZ_4K
+#define DSS_DBGC_LOG_SZ SZ_4K
+#define DSS_HEADER_TOTAL_SZ (DSS_HEADER_SZ + DSS_MMU_REG_SZ + DSS_CORE_REG_SZ + DSS_DBGC_LOG_SZ)
+#define DSS_SPARE_SZ (DSS_HEADER_SIZE - DSS_HEADER_TOTAL_SZ)
+
+/* Length domain */
+#define DSS_LOG_STRING_LENGTH SZ_128
+#define DSS_MMU_REG_OFFSET SZ_512
+#define DSS_CORE_REG_OFFSET SZ_512
+#define DSS_LOG_MAX_NUM SZ_1K
+#define DSS_API_MAX_NUM SZ_2K
+#define DSS_EX_MAX_NUM SZ_8
+#define DSS_IN_MAX_NUM SZ_8
+#define DSS_CALLSTACK_MAX_NUM 4
+#define DSS_ITERATION 5
+#define DSS_NR_CPUS NR_CPUS
+#define DSS_ITEM_MAX_NUM 10
+
+/* Sign domain */
+#define DSS_SIGN_RESET 0x0
+#define DSS_SIGN_RESERVED 0x1
+#define DSS_SIGN_SCRATCH 0xD
+#define DSS_SIGN_ALIVE 0xFACE
+#define DSS_SIGN_DEAD 0xDEAD
+#define DSS_SIGN_PANIC 0xBABA
+#define DSS_SIGN_SAFE_FAULT 0xFAFA
+#define DSS_SIGN_NORMAL_REBOOT 0xCAFE
+#define DSS_SIGN_FORCE_REBOOT 0xDAFE
+#define DSS_SIGN_LOCKUP 0xDEADBEEF
+
+/* Specific Address Information */
+#define DSS_FIXED_VIRT_BASE (VMALLOC_START + 0xF6000000)
+#define DSS_OFFSET_SCRATCH (0x100)
+#define DSS_OFFSET_LAST_LOGBUF (0x200)
+#define DSS_OFFSET_EMERGENCY_REASON (0x300)
+#define DSS_OFFSET_CORE_POWER_STAT (0x400)
+#define DSS_OFFSET_PANIC_STAT (0x500)
+#define DSS_OFFSET_CORE_LAST_PC (0x600)
+
+/* S5P_VA_SS_BASE + 0xC00 -- 0xFFF is reserved */
+#define DSS_OFFSET_PANIC_STRING (0xC00)
+#define DSS_OFFSET_SPARE_BASE (DSS_HEADER_TOTAL_SZ)
+
+typedef int (*dss_initcall_t)(const struct device_node *);
+
+struct dbg_snapshot_base {
+ size_t size;
+ size_t vaddr;
+ size_t paddr;
+ unsigned int persist;
+ unsigned int enabled;
+};
+
+struct dbg_snapshot_item {
+ char *name;
+ struct dbg_snapshot_base entry;
+ unsigned char *head_ptr;
+ unsigned char *curr_ptr;
+ unsigned long long time;
+ struct vm_struct vm;
+};
+
+#ifdef CONFIG_ARM64
+struct dbg_snapshot_mmu_reg {
+ long SCTLR_EL1;
+ long TTBR0_EL1;
+ long TTBR1_EL1;
+ long TCR_EL1;
+ long ESR_EL1;
+ long FAR_EL1;
+ long CONTEXTIDR_EL1;
+ long TPIDR_EL0;
+ long TPIDRRO_EL0;
+ long TPIDR_EL1;
+ long MAIR_EL1;
+ long ELR_EL1;
+ long SP_EL0;
+};
+
+#else
+struct dbg_snapshot_mmu_reg {
+ int SCTLR;
+ int TTBR0;
+ int TTBR1;
+ int TTBCR;
+ int DACR;
+ int DFSR;
+ int DFAR;
+ int IFSR;
+ int IFAR;
+ int DAFSR;
+ int IAFSR;
+ int PMRRR;
+ int NMRRR;
+ int FCSEPID;
+ int CONTEXT;
+ int URWTPID;
+ int UROTPID;
+ int POTPIDR;
+};
+#endif
+
+struct dbg_snapshot_sfrdump {
+ char *name;
+ void __iomem *reg;
+ unsigned int phy_reg;
+ unsigned int num;
+ struct device_node *node;
+ struct list_head list;
+ bool pwr_mode;
+};
+
+struct dbg_snapshot_desc {
+ struct list_head sfrdump_list;
+ raw_spinlock_t ctrl_lock;
+ raw_spinlock_t nmi_lock;
+ unsigned int header_num;
+ unsigned int kevents_num;
+ unsigned int log_kernel_num;
+ unsigned int log_platform_num;
+ unsigned int log_sfr_num;
+ unsigned int log_pstore_num;
+ unsigned int log_etm_num;
+ unsigned int log_enable_cnt;
+
+ unsigned int callstack;
+ unsigned long hardlockup_core_mask;
+ unsigned long hardlockup_core_pc[DSS_NR_CPUS];
+ int multistage_wdt_irq;
+ int hardlockup_detected;
+ int allcorelockup_detected;
+ int no_wdt_dev;
+};
+
+struct dbg_snapshot_log {
+ struct __task_log {
+ unsigned long long time;
+ unsigned long sp;
+ struct task_struct *task;
+ char task_comm[TASK_COMM_LEN];
+ } task[DSS_NR_CPUS][DSS_LOG_MAX_NUM];
+
+ struct __work_log {
+ unsigned long long time;
+ unsigned long sp;
+ struct worker *worker;
+ char task_comm[TASK_COMM_LEN];
+ work_func_t fn;
+ int en;
+ } work[DSS_NR_CPUS][DSS_LOG_MAX_NUM];
+
+ struct __cpuidle_log {
+ unsigned long long time;
+ unsigned long sp;
+ char *modes;
+ unsigned state;
+ u32 num_online_cpus;
+ int delta;
+ int en;
+ } cpuidle[DSS_NR_CPUS][DSS_LOG_MAX_NUM];
+
+ struct __suspend_log {
+ unsigned long long time;
+ unsigned long sp;
+ void *fn;
+ struct device *dev;
+ int en;
+ int core;
+ } suspend[DSS_LOG_MAX_NUM * 4];
+
+ struct __irq_log {
+ unsigned long long time;
+ unsigned long sp;
+ int irq;
+ void *fn;
+ unsigned int preempt;
+ unsigned int val;
+ int en;
+ } irq[DSS_NR_CPUS][DSS_LOG_MAX_NUM * 2];
+
+#ifdef CONFIG_DEBUG_SNAPSHOT_IRQ_EXIT
+ struct __irq_exit_log {
+ unsigned long long time;
+ unsigned long sp;
+ unsigned long long end_time;
+ unsigned long long latency;
+ int irq;
+ } irq_exit[DSS_NR_CPUS][DSS_LOG_MAX_NUM];
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_SPINLOCK
+ struct __spinlock_log {
+ unsigned long long time;
+ unsigned long sp;
+ unsigned long long jiffies;
+ raw_spinlock_t *lock;
+#ifdef CONFIG_DEBUG_SPINLOCK
+ u16 next;
+ u16 owner;
+#endif
+ int en;
+ void *caller[DSS_CALLSTACK_MAX_NUM];
+ } spinlock[DSS_NR_CPUS][DSS_LOG_MAX_NUM];
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_IRQ_DISABLED
+ struct __irqs_disabled_log {
+ unsigned long long time;
+ unsigned long index;
+ struct task_struct *task;
+ char *task_comm;
+ void *caller[DSS_CALLSTACK_MAX_NUM];
+ } irqs_disabled[DSS_NR_CPUS][SZ_32];
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_CLK
+ struct __clk_log {
+ unsigned long long time;
+ struct clk_hw *clk;
+ const char* f_name;
+ int mode;
+ unsigned long arg;
+ } clk[DSS_LOG_MAX_NUM];
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_PMU
+ struct __pmu_log {
+ unsigned long long time;
+ unsigned int id;
+ const char* f_name;
+ int mode;
+ } pmu[DSS_LOG_MAX_NUM];
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_FREQ
+ struct __freq_log {
+ unsigned long long time;
+ int cpu;
+ char* freq_name;
+ unsigned long old_freq;
+ unsigned long target_freq;
+ int en;
+ } freq[DSS_LOG_MAX_NUM];
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_DM
+ struct __dm_log {
+ unsigned long long time;
+ int cpu;
+ int dm_num;
+ unsigned long min_freq;
+ unsigned long max_freq;
+ s32 wait_dmt;
+ s32 do_dmt;
+ } dm[DSS_LOG_MAX_NUM];
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_REG
+ struct __reg_log {
+ unsigned long long time;
+ int read;
+ size_t val;
+ size_t reg;
+ int en;
+ void *caller[DSS_CALLSTACK_MAX_NUM];
+ } reg[DSS_NR_CPUS][DSS_LOG_MAX_NUM];
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_HRTIMER
+ struct __hrtimer_log {
+ unsigned long long time;
+ unsigned long long now;
+ struct hrtimer *timer;
+ void *fn;
+ int en;
+ } hrtimers[DSS_NR_CPUS][DSS_LOG_MAX_NUM];
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_REGULATOR
+ struct __regulator_log {
+ unsigned long long time;
+ unsigned long long acpm_time;
+ int cpu;
+ char name[SZ_16];
+ unsigned int reg;
+ unsigned int voltage;
+ unsigned int raw_volt;
+ int en;
+ } regulator[DSS_LOG_MAX_NUM];
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_THERMAL
+ struct __thermal_log {
+ unsigned long long time;
+ int cpu;
+ struct exynos_tmu_platform_data *data;
+ unsigned int temp;
+ char* cooling_device;
+ unsigned int cooling_state;
+ } thermal[DSS_LOG_MAX_NUM];
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_ACPM
+ struct __acpm_log {
+ unsigned long long time;
+ unsigned long long acpm_time;
+ char log[9];
+ unsigned int data;
+ } acpm[DSS_LOG_MAX_NUM];
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_I2C
+ struct __i2c_log {
+ unsigned long long time;
+ int cpu;
+ struct i2c_adapter *adap;
+ struct i2c_msg *msgs;
+ int num;
+ int en;
+ } i2c[DSS_LOG_MAX_NUM];
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_SPI
+ struct __spi_log {
+ unsigned long long time;
+ int cpu;
+ struct spi_controller *ctlr;
+ struct spi_message *cur_msg;
+ int en;
+ } spi[DSS_LOG_MAX_NUM];
+#endif
+
+#ifndef CONFIG_DEBUG_SNAPSHOT_MINIMIZED_MODE
+ struct __clockevent_log {
+ unsigned long long time;
+ unsigned long long mct_cycle;
+ int64_t delta_ns;
+ ktime_t next_event;
+ void *caller[DSS_CALLSTACK_MAX_NUM];
+ } clockevent[DSS_NR_CPUS][DSS_LOG_MAX_NUM];
+
+ struct __printkl_log {
+ unsigned long long time;
+ int cpu;
+ size_t msg;
+ size_t val;
+ void *caller[DSS_CALLSTACK_MAX_NUM];
+ } printkl[DSS_API_MAX_NUM];
+
+ struct __printk_log {
+ unsigned long long time;
+ int cpu;
+ char log[DSS_LOG_STRING_LENGTH];
+ void *caller[DSS_CALLSTACK_MAX_NUM];
+ } printk[DSS_API_MAX_NUM];
+#endif
+};
+
+extern struct dbg_snapshot_base dss_base;
+extern struct dbg_snapshot_log *dss_log;
+extern struct dbg_snapshot_desc dss_desc;
+extern struct dbg_snapshot_item dss_items[];
+extern int dbg_snapshot_log_size;
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Debug-SnapShot: Debug Framework for Ramdump based debugging method
+ * The original code is Exynos-Snapshot for Exynos SoC
+ *
+ * Author: Hosung Kim <hosung0.kim@samsung.com>
+ * Author: Changki Kim <changki.kim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/ktime.h>
+#include <linux/kallsyms.h>
+#include <linux/platform_device.h>
+#include <linux/clk-provider.h>
+#include <linux/pstore_ram.h>
+#include <linux/sched/clock.h>
+#include <linux/ftrace.h>
+
+#include "debug-snapshot-local.h"
+#include <asm/irq.h>
+#include <asm/traps.h>
+#include <asm/hardirq.h>
+#include <asm/stacktrace.h>
+#include <linux/debug-snapshot.h>
+#include <linux/kernel_stat.h>
+#include <linux/irqnr.h>
+#include <linux/irq.h>
+#include <linux/irqdesc.h>
+
+struct dbg_snapshot_lastinfo {
+#ifdef CONFIG_DEBUG_SNAPSHOT_FREQ
+ atomic_t freq_last_idx[DSS_FLAG_END];
+#endif
+ char log[DSS_NR_CPUS][SZ_1K];
+ char *last_p[DSS_NR_CPUS];
+};
+
+struct dss_dumper {
+ bool active;
+ u32 items;
+ int init_idx;
+ int cur_idx;
+ u32 cur_cpu;
+ u32 step;
+};
+
+enum dss_kevent_flag {
+ DSS_FLAG_TASK = 1,
+ DSS_FLAG_WORK,
+ DSS_FLAG_CPUIDLE,
+ DSS_FLAG_SUSPEND,
+ DSS_FLAG_IRQ,
+ DSS_FLAG_IRQ_EXIT,
+ DSS_FLAG_SPINLOCK,
+ DSS_FLAG_IRQ_DISABLE,
+ DSS_FLAG_CLK,
+ DSS_FLAG_FREQ,
+ DSS_FLAG_REG,
+ DSS_FLAG_HRTIMER,
+ DSS_FLAG_REGULATOR,
+ DSS_FLAG_THERMAL,
+ DSS_FLAG_MAILBOX,
+ DSS_FLAG_CLOCKEVENT,
+ DSS_FLAG_PRINTK,
+ DSS_FLAG_PRINTKL,
+ DSS_FLAG_KEVENT,
+};
+
+struct dbg_snapshot_log_idx {
+ atomic_t task_log_idx[DSS_NR_CPUS];
+ atomic_t work_log_idx[DSS_NR_CPUS];
+ atomic_t cpuidle_log_idx[DSS_NR_CPUS];
+ atomic_t suspend_log_idx;
+ atomic_t irq_log_idx[DSS_NR_CPUS];
+#ifdef CONFIG_DEBUG_SNAPSHOT_SPINLOCK
+ atomic_t spinlock_log_idx[DSS_NR_CPUS];
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_IRQ_DISABLED
+ atomic_t irqs_disabled_log_idx[DSS_NR_CPUS];
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_IRQ_EXIT
+ atomic_t irq_exit_log_idx[DSS_NR_CPUS];
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_REG
+ atomic_t reg_log_idx[DSS_NR_CPUS];
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_HRTIMER
+ atomic_t hrtimer_log_idx[DSS_NR_CPUS];
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_CLK
+ atomic_t clk_log_idx;
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_PMU
+ atomic_t pmu_log_idx;
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_FREQ
+ atomic_t freq_log_idx;
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_DM
+ atomic_t dm_log_idx;
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_REGULATOR
+ atomic_t regulator_log_idx;
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_REGULATOR
+ atomic_t thermal_log_idx;
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_I2C
+ atomic_t i2c_log_idx;
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_SPI
+ atomic_t spi_log_idx;
+#endif
+#ifndef CONFIG_DEBUG_SNAPSHOT_MINIMIZED_MODE
+ atomic_t clockevent_log_idx[DSS_NR_CPUS];
+ atomic_t printkl_log_idx;
+ atomic_t printk_log_idx;
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_ACPM
+ atomic_t acpm_log_idx;
+#endif
+};
+
+int dbg_snapshot_log_size = sizeof(struct dbg_snapshot_log);
+/*
+ * including or excluding options
+ * if you want to except some interrupt, it should be written in this array
+ */
+int dss_irqlog_exlist[DSS_EX_MAX_NUM] = {
+/* interrupt number ex) 152, 153, 154, */
+ -1,
+};
+
+#ifdef CONFIG_DEBUG_SNAPSHOT_IRQ_EXIT
+int dss_irqexit_exlist[DSS_EX_MAX_NUM] = {
+/* interrupt number ex) 152, 153, 154, */
+ -1,
+};
+
+unsigned int dss_irqexit_threshold =
+ CONFIG_DEBUG_SNAPSHOT_IRQ_EXIT_THRESHOLD;
+#endif
+
+#ifdef CONFIG_DEBUG_SNAPSHOT_REG
+struct dss_reg_list {
+ size_t addr;
+ size_t size;
+};
+
+static struct dss_reg_list dss_reg_exlist[] = {
+/*
+ * if it wants to reduce effect enabled reg feautre to system,
+ * you must add these registers - mct, serial
+ * because they are called very often.
+ * physical address, size ex) {0x10C00000, 0x1000},
+ */
+ {DSS_REG_MCT_ADDR, DSS_REG_MCT_SIZE},
+ {DSS_REG_UART_ADDR, DSS_REG_UART_SIZE},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+};
+#endif
+
+#ifdef CONFIG_DEBUG_SNAPSHOT_FREQ
+static char *dss_freq_name[] = {
+ "LIT", "MID", "BIG", "INT", "MIF", "ISP", "DISP", "INTCAM", "AUD", "IVA", "SCORE", "FSYS0", "MFC", "NPU",
+};
+#endif
+
+/* Internal interface variable */
+static struct dbg_snapshot_log_idx dss_idx;
+static struct dbg_snapshot_lastinfo dss_lastinfo;
+
+void __init dbg_snapshot_log_idx_init(void)
+{
+ int i;
+
+#ifndef CONFIG_DEBUG_SNAPSHOT_MINIMIZED_MODE
+ atomic_set(&(dss_idx.printk_log_idx), -1);
+ atomic_set(&(dss_idx.printkl_log_idx), -1);
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_REGULATOR
+ atomic_set(&(dss_idx.regulator_log_idx), -1);
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_THERMAL
+ atomic_set(&(dss_idx.thermal_log_idx), -1);
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_FREQ
+ atomic_set(&(dss_idx.freq_log_idx), -1);
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_DM
+ atomic_set(&(dss_idx.dm_log_idx), -1);
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_CLK
+ atomic_set(&(dss_idx.clk_log_idx), -1);
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_PMU
+ atomic_set(&(dss_idx.pmu_log_idx), -1);
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_ACPM
+ atomic_set(&(dss_idx.acpm_log_idx), -1);
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_I2C
+ atomic_set(&(dss_idx.i2c_log_idx), -1);
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_SPI
+ atomic_set(&(dss_idx.spi_log_idx), -1);
+#endif
+ atomic_set(&(dss_idx.suspend_log_idx), -1);
+
+ for (i = 0; i < DSS_NR_CPUS; i++) {
+ atomic_set(&(dss_idx.task_log_idx[i]), -1);
+ atomic_set(&(dss_idx.work_log_idx[i]), -1);
+#ifndef CONFIG_DEBUG_SNAPSHOT_MINIMIZED_MODE
+ atomic_set(&(dss_idx.clockevent_log_idx[i]), -1);
+#endif
+ atomic_set(&(dss_idx.cpuidle_log_idx[i]), -1);
+ atomic_set(&(dss_idx.irq_log_idx[i]), -1);
+#ifdef CONFIG_DEBUG_SNAPSHOT_SPINLOCK
+ atomic_set(&(dss_idx.spinlock_log_idx[i]), -1);
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_IRQ_DISABLED
+ atomic_set(&(dss_idx.irqs_disabled_log_idx[i]), -1);
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_IRQ_EXIT
+ atomic_set(&(dss_idx.irq_exit_log_idx[i]), -1);
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_REG
+ atomic_set(&(dss_idx.reg_log_idx[i]), -1);
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_HRTIMER
+ atomic_set(&(dss_idx.hrtimer_log_idx[i]), -1);
+#endif
+ }
+}
+
+bool dbg_snapshot_dumper_one(void *v_dumper, char *line, size_t size, size_t *len)
+{
+ bool ret = false;
+ int idx, array_size;
+ unsigned int cpu, items;
+ unsigned long rem_nsec;
+ u64 ts;
+ struct dss_dumper *dumper = (struct dss_dumper *)v_dumper;
+
+ if (!line || size < SZ_128 ||
+ dumper->cur_cpu >= NR_CPUS)
+ goto out;
+
+ if (dumper->active) {
+ if (dumper->init_idx == dumper->cur_idx)
+ goto out;
+ }
+
+ cpu = dumper->cur_cpu;
+ idx = dumper->cur_idx;
+ items = dumper->items;
+
+ switch(items) {
+ case DSS_FLAG_TASK:
+ {
+ struct task_struct *task;
+ array_size = ARRAY_SIZE(dss_log->task[0]) - 1;
+ if (!dumper->active) {
+ idx = (atomic_read(&dss_idx.task_log_idx[0]) + 1) & array_size;
+ dumper->init_idx = idx;
+ dumper->active = true;
+ }
+ ts = dss_log->task[cpu][idx].time;
+ rem_nsec = do_div(ts, NSEC_PER_SEC);
+ task = dss_log->task[cpu][idx].task;
+
+ *len = snprintf(line, size, "[%8lu.%09lu][%04d:CPU%u] task_name:%16s, "
+ "task:0x%16p, stack:0x%16p, exec_start:%16llu\n",
+ (unsigned long)ts, rem_nsec / NSEC_PER_USEC, idx, cpu,
+ task->comm, task, task->stack,
+ task->se.exec_start);
+ break;
+ }
+ case DSS_FLAG_WORK:
+ {
+ char work_fn[KSYM_NAME_LEN] = {0,};
+ char *task_comm;
+ int en;
+
+ array_size = ARRAY_SIZE(dss_log->work[0]) - 1;
+ if (!dumper->active) {
+ idx = (atomic_read(&dss_idx.work_log_idx[0]) + 1) & array_size;
+ dumper->init_idx = idx;
+ dumper->active = true;
+ }
+ ts = dss_log->work[cpu][idx].time;
+ rem_nsec = do_div(ts, NSEC_PER_SEC);
+ lookup_symbol_name((unsigned long)dss_log->work[cpu][idx].fn, work_fn);
+ task_comm = dss_log->work[cpu][idx].task_comm;
+ en = dss_log->work[cpu][idx].en;
+
+ dumper->step = 6;
+ *len = snprintf(line, size, "[%8lu.%09lu][%04d:CPU%u] task_name:%16s, work_fn:%32s, %3s\n",
+ (unsigned long)ts, rem_nsec / NSEC_PER_USEC, idx, cpu,
+ task_comm, work_fn,
+ en == DSS_FLAG_IN ? "IN" : "OUT");
+ break;
+ }
+ case DSS_FLAG_CPUIDLE:
+ {
+ unsigned int delta;
+ int state, num_cpus, en;
+ char *index;
+
+ array_size = ARRAY_SIZE(dss_log->cpuidle[0]) - 1;
+ if (!dumper->active) {
+ idx = (atomic_read(&dss_idx.cpuidle_log_idx[0]) + 1) & array_size;
+ dumper->init_idx = idx;
+ dumper->active = true;
+ }
+ ts = dss_log->cpuidle[cpu][idx].time;
+ rem_nsec = do_div(ts, NSEC_PER_SEC);
+
+ index = dss_log->cpuidle[cpu][idx].modes;
+ en = dss_log->cpuidle[cpu][idx].en;
+ state = dss_log->cpuidle[cpu][idx].state;
+ num_cpus = dss_log->cpuidle[cpu][idx].num_online_cpus;
+ delta = dss_log->cpuidle[cpu][idx].delta;
+
+ *len = snprintf(line, size, "[%8lu.%09lu][%04d:CPU%u] cpuidle: %s, "
+ "state:%d, num_online_cpus:%d, stay_time:%8u, %3s\n",
+ (unsigned long)ts, rem_nsec / NSEC_PER_USEC, idx, cpu,
+ index, state, num_cpus, delta,
+ en == DSS_FLAG_IN ? "IN" : "OUT");
+ break;
+ }
+ case DSS_FLAG_SUSPEND:
+ {
+ char suspend_fn[KSYM_NAME_LEN];
+ int en;
+
+ array_size = ARRAY_SIZE(dss_log->suspend) - 1;
+ if (!dumper->active) {
+ idx = (atomic_read(&dss_idx.suspend_log_idx) + 1) & array_size;
+ dumper->init_idx = idx;
+ dumper->active = true;
+ }
+ ts = dss_log->suspend[idx].time;
+ rem_nsec = do_div(ts, NSEC_PER_SEC);
+
+ lookup_symbol_name((unsigned long)dss_log->suspend[idx].fn, suspend_fn);
+ en = dss_log->suspend[idx].en;
+
+ *len = snprintf(line, size, "[%8lu.%09lu][%04d:CPU%u] suspend_fn:%s, %3s\n",
+ (unsigned long)ts, rem_nsec / NSEC_PER_USEC, idx, cpu,
+ suspend_fn, en == DSS_FLAG_IN ? "IN" : "OUT");
+ break;
+ }
+ case DSS_FLAG_IRQ:
+ {
+ char irq_fn[KSYM_NAME_LEN];
+ int en, irq, preempt, val;
+
+ array_size = ARRAY_SIZE(dss_log->irq[0]) - 1;
+ if (!dumper->active) {
+ idx = (atomic_read(&dss_idx.irq_log_idx[0]) + 1) & array_size;
+ dumper->init_idx = idx;
+ dumper->active = true;
+ }
+ ts = dss_log->irq[cpu][idx].time;
+ rem_nsec = do_div(ts, NSEC_PER_SEC);
+
+ lookup_symbol_name((unsigned long)dss_log->irq[cpu][idx].fn, irq_fn);
+ irq = dss_log->irq[cpu][idx].irq;
+ preempt = dss_log->irq[cpu][idx].preempt;
+ val = dss_log->irq[cpu][idx].val;
+ en = dss_log->irq[cpu][idx].en;
+
+ *len = snprintf(line, size, "[%8lu.%09lu][%04d:CPU%u] irq:%6d, irq_fn:%32s, "
+ "preempt:%6d, val:%6d, %3s\n",
+ (unsigned long)ts, rem_nsec / NSEC_PER_USEC, idx, cpu,
+ irq, irq_fn, preempt, val,
+ en == DSS_FLAG_IN ? "IN" : "OUT");
+ break;
+ }
+#ifdef CONFIG_DEBUG_SNAPSHOT_IRQ_EXIT
+ case DSS_FLAG_IRQ_EXIT:
+ {
+ unsigned long end_time, latency;
+ int irq;
+
+ array_size = ARRAY_SIZE(dss_log->irq_exit[0]) - 1;
+ if (!dumper->active) {
+ idx = (atomic_read(&dss_idx.irq_exit_log_idx[0]) + 1) & array_size;
+ dumper->init_idx = idx;
+ dumper->active = true;
+ }
+ ts = dss_log->irq_exit[cpu][idx].time;
+ rem_nsec = do_div(ts, NSEC_PER_SEC);
+
+ end_time = dss_log->irq_exit[cpu][idx].end_time;
+ latency = dss_log->irq_exit[cpu][idx].latency;
+ irq = dss_log->irq_exit[cpu][idx].irq;
+
+ *len = snprintf(line, size, "[%8lu.%09lu][%04d:CPU%u] irq:%6d, "
+ "latency:%16zu, end_time:%16zu\n",
+ (unsigned long)ts, rem_nsec / NSEC_PER_USEC, idx, cpu,
+ irq, latency, end_time);
+ break;
+ }
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_SPINLOCK
+ case DSS_FLAG_SPINLOCK:
+ {
+ unsigned int jiffies_local;
+ char callstack[CONFIG_DEBUG_SNAPSHOT_CALLSTACK][KSYM_NAME_LEN];
+ int en, i;
+ u16 next, owner;
+
+ array_size = ARRAY_SIZE(dss_log->spinlock[0]) - 1;
+ if (!dumper->active) {
+ idx = (atomic_read(&dss_idx.spinlock_log_idx[0]) + 1) & array_size;
+ dumper->init_idx = idx;
+ dumper->active = true;
+ }
+ ts = dss_log->spinlock[cpu][idx].time;
+ rem_nsec = do_div(ts, NSEC_PER_SEC);
+
+ jiffies_local = dss_log->spinlock[cpu][idx].jiffies;
+ en = dss_log->spinlock[cpu][idx].en;
+ for (i = 0; i < CONFIG_DEBUG_SNAPSHOT_CALLSTACK; i++)
+ lookup_symbol_name((unsigned long)dss_log->spinlock[cpu][idx].caller[i],
+ callstack[i]);
+
+ next = dss_log->spinlock[cpu][idx].next;
+ owner = dss_log->spinlock[cpu][idx].owner;
+
+ *len = snprintf(line, size, "[%8lu.%09lu][%04d:CPU%u] next:%8x, owner:%8x jiffies:%12u, %3s\n"
+ "callstack: %s\n"
+ " %s\n"
+ " %s\n"
+ " %s\n",
+ (unsigned long)ts, rem_nsec / NSEC_PER_USEC, idx, cpu,
+ next, owner, jiffies_local,
+ en == DSS_FLAG_IN ? "IN" : "OUT",
+ callstack[0], callstack[1], callstack[2], callstack[3]);
+ break;
+ }
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_CLK
+ case DSS_FLAG_CLK:
+ {
+ const char *clk_name;
+ char clk_fn[KSYM_NAME_LEN];
+ struct clk_hw *clk;
+ int en;
+
+ array_size = ARRAY_SIZE(dss_log->clk) - 1;
+ if (!dumper->active) {
+ idx = (atomic_read(&dss_idx.clk_log_idx) + 1) & array_size;
+ dumper->init_idx = idx;
+ dumper->active = true;
+ }
+ ts = dss_log->clk[idx].time;
+ rem_nsec = do_div(ts, NSEC_PER_SEC);
+
+ clk = (struct clk_hw *)dss_log->clk[idx].clk;
+ clk_name = clk_hw_get_name(clk);
+ lookup_symbol_name((unsigned long)dss_log->clk[idx].f_name, clk_fn);
+ en = dss_log->clk[idx].mode;
+
+ *len = snprintf(line, size, "[%8lu.%09lu][%04d:CPU] clk_name:%30s, clk_fn:%30s, "
+ ", %s\n",
+ (unsigned long)ts, rem_nsec / NSEC_PER_USEC, idx,
+ clk_name, clk_fn, en == DSS_FLAG_IN ? "IN" : "OUT");
+ break;
+ }
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_FREQ
+ case DSS_FLAG_FREQ:
+ {
+ char *freq_name;
+ unsigned int on_cpu;
+ unsigned long old_freq, target_freq;
+ int en;
+
+ array_size = ARRAY_SIZE(dss_log->freq) - 1;
+ if (!dumper->active) {
+ idx = (atomic_read(&dss_idx.freq_log_idx) + 1) & array_size;
+ dumper->init_idx = idx;
+ dumper->active = true;
+ }
+ ts = dss_log->freq[idx].time;
+ rem_nsec = do_div(ts, NSEC_PER_SEC);
+
+ freq_name = dss_log->freq[idx].freq_name;
+ old_freq = dss_log->freq[idx].old_freq;
+ target_freq = dss_log->freq[idx].target_freq;
+ on_cpu = dss_log->freq[idx].cpu;
+ en = dss_log->freq[idx].en;
+
+ *len = snprintf(line, size, "[%8lu.%09lu][%04d:CPU%u] freq_name:%16s, "
+ "old_freq:%16lu, target_freq:%16lu, %3s\n",
+ (unsigned long)ts, rem_nsec / NSEC_PER_USEC, idx, on_cpu,
+ freq_name, old_freq, target_freq,
+ en == DSS_FLAG_IN ? "IN" : "OUT");
+ break;
+ }
+#endif
+#ifndef CONFIG_DEBUG_SNAPSHOT_MINIMIZED_MODE
+ case DSS_FLAG_PRINTK:
+ {
+ char *log;
+ char callstack[CONFIG_DEBUG_SNAPSHOT_CALLSTACK][KSYM_NAME_LEN];
+ unsigned int cpu;
+ int i;
+
+ array_size = ARRAY_SIZE(dss_log->printk) - 1;
+ if (!dumper->active) {
+ idx = (atomic_read(&dss_idx.printk_log_idx) + 1) & array_size;
+ dumper->init_idx = idx;
+ dumper->active = true;
+ }
+ ts = dss_log->printk[idx].time;
+ cpu = dss_log->printk[idx].cpu;
+ rem_nsec = do_div(ts, NSEC_PER_SEC);
+ log = dss_log->printk[idx].log;
+ for (i = 0; i < CONFIG_DEBUG_SNAPSHOT_CALLSTACK; i++)
+ lookup_symbol_name((unsigned long)dss_log->printk[idx].caller[i],
+ callstack[i]);
+
+ *len = snprintf(line, size, "[%8lu.%09lu][%04d:CPU%u] log:%s, callstack:%s, %s, %s, %s\n",
+ (unsigned long)ts, rem_nsec / NSEC_PER_USEC, idx, cpu,
+ log, callstack[0], callstack[1], callstack[2], callstack[3]);
+ break;
+ }
+ case DSS_FLAG_PRINTKL:
+ {
+ char callstack[CONFIG_DEBUG_SNAPSHOT_CALLSTACK][KSYM_NAME_LEN];
+ size_t msg, val;
+ unsigned int cpu;
+ int i;
+
+ array_size = ARRAY_SIZE(dss_log->printkl) - 1;
+ if (!dumper->active) {
+ idx = (atomic_read(&dss_idx.printkl_log_idx) + 1) & array_size;
+ dumper->init_idx = idx;
+ dumper->active = true;
+ }
+ ts = dss_log->printkl[idx].time;
+ cpu = dss_log->printkl[idx].cpu;
+ rem_nsec = do_div(ts, NSEC_PER_SEC);
+ msg = dss_log->printkl[idx].msg;
+ val = dss_log->printkl[idx].val;
+ for (i = 0; i < CONFIG_DEBUG_SNAPSHOT_CALLSTACK; i++)
+ lookup_symbol_name((unsigned long)dss_log->printkl[idx].caller[i],
+ callstack[i]);
+
+ *len = snprintf(line, size, "[%8lu.%09lu][%04d:CPU%u] msg:%zx, val:%zx, callstack: %s, %s, %s, %s\n",
+ (unsigned long)ts, rem_nsec / NSEC_PER_USEC, idx, cpu,
+ msg, val, callstack[0], callstack[1], callstack[2], callstack[3]);
+ break;
+ }
+#endif
+ default:
+ snprintf(line, size, "unsupported inforation to dump\n");
+ goto out;
+ }
+ if (array_size == idx)
+ dumper->cur_idx = 0;
+ else
+ dumper->cur_idx = idx + 1;
+
+ ret = true;
+out:
+ return ret;
+}
+
+#ifdef CONFIG_ARM64
+static inline unsigned long pure_arch_local_irq_save(void)
+{
+ unsigned long flags;
+
+ asm volatile(
+ "mrs %0, daif // arch_local_irq_save\n"
+ "msr daifset, #2"
+ : "=r" (flags)
+ :
+ : "memory");
+
+ return flags;
+}
+
+static inline void pure_arch_local_irq_restore(unsigned long flags)
+{
+ asm volatile(
+ "msr daif, %0 // arch_local_irq_restore"
+ :
+ : "r" (flags)
+ : "memory");
+}
+#else
+static inline unsigned long arch_local_irq_save(void)
+{
+ unsigned long flags;
+
+ asm volatile(
+ " mrs %0, cpsr @ arch_local_irq_save\n"
+ " cpsid i"
+ : "=r" (flags) : : "memory", "cc");
+ return flags;
+}
+
+static inline void arch_local_irq_restore(unsigned long flags)
+{
+ asm volatile(
+ " msr cpsr_c, %0 @ local_irq_restore"
+ :
+ : "r" (flags)
+ : "memory", "cc");
+}
+#endif
+
+void dbg_snapshot_task(int cpu, void *v_task)
+{
+ struct dbg_snapshot_item *item = &dss_items[dss_desc.kevents_num];
+
+ if (unlikely(!dss_base.enabled || !item->entry.enabled))
+ return;
+ {
+ unsigned long i = atomic_inc_return(&dss_idx.task_log_idx[cpu]) &
+ (ARRAY_SIZE(dss_log->task[0]) - 1);
+
+ dss_log->task[cpu][i].time = cpu_clock(cpu);
+ dss_log->task[cpu][i].sp = (unsigned long) current_stack_pointer;
+ dss_log->task[cpu][i].task = (struct task_struct *)v_task;
+ strncpy(dss_log->task[cpu][i].task_comm,
+ dss_log->task[cpu][i].task->comm,
+ TASK_COMM_LEN - 1);
+ }
+}
+
+void dbg_snapshot_work(void *worker, void *v_task, void *fn, int en)
+{
+ struct dbg_snapshot_item *item = &dss_items[dss_desc.kevents_num];
+
+ if (unlikely(!dss_base.enabled || !item->entry.enabled))
+ return;
+
+ {
+ int cpu = raw_smp_processor_id();
+ unsigned long i = atomic_inc_return(&dss_idx.work_log_idx[cpu]) &
+ (ARRAY_SIZE(dss_log->work[0]) - 1);
+ struct task_struct *task = (struct task_struct *)v_task;
+ dss_log->work[cpu][i].time = cpu_clock(cpu);
+ dss_log->work[cpu][i].sp = (unsigned long) current_stack_pointer;
+ dss_log->work[cpu][i].worker = (struct worker *)worker;
+ strncpy(dss_log->work[cpu][i].task_comm, task->comm, TASK_COMM_LEN - 1);
+ dss_log->work[cpu][i].fn = (work_func_t)fn;
+ dss_log->work[cpu][i].en = en;
+ }
+}
+
+void dbg_snapshot_cpuidle(char *modes, unsigned state, int diff, int en)
+{
+ struct dbg_snapshot_item *item = &dss_items[dss_desc.kevents_num];
+
+ if (unlikely(!dss_base.enabled || !item->entry.enabled))
+ return;
+ {
+ int cpu = raw_smp_processor_id();
+ unsigned long i = atomic_inc_return(&dss_idx.cpuidle_log_idx[cpu]) &
+ (ARRAY_SIZE(dss_log->cpuidle[0]) - 1);
+
+ dss_log->cpuidle[cpu][i].time = cpu_clock(cpu);
+ dss_log->cpuidle[cpu][i].modes = modes;
+ dss_log->cpuidle[cpu][i].state = state;
+ dss_log->cpuidle[cpu][i].sp = (unsigned long) current_stack_pointer;
+ dss_log->cpuidle[cpu][i].num_online_cpus = num_online_cpus();
+ dss_log->cpuidle[cpu][i].delta = diff;
+ dss_log->cpuidle[cpu][i].en = en;
+ }
+}
+
+void dbg_snapshot_suspend(void *fn, void *dev, int en)
+{
+ struct dbg_snapshot_item *item = &dss_items[dss_desc.kevents_num];
+
+ if (unlikely(!dss_base.enabled || !item->entry.enabled))
+ return;
+ {
+ int cpu = raw_smp_processor_id();
+ unsigned long i = atomic_inc_return(&dss_idx.suspend_log_idx) &
+ (ARRAY_SIZE(dss_log->suspend) - 1);
+
+ dss_log->suspend[i].time = cpu_clock(cpu);
+ dss_log->suspend[i].sp = (unsigned long) current_stack_pointer;
+ dss_log->suspend[i].fn = fn;
+ dss_log->suspend[i].dev = (struct device *)dev;
+ dss_log->suspend[i].core = cpu;
+ dss_log->suspend[i].en = en;
+ }
+}
+
+static void dbg_snapshot_print_calltrace(void)
+{
+ int i;
+
+ pr_info("\n<Call trace>\n");
+ for (i = 0; i < DSS_NR_CPUS; i++) {
+ pr_info("CPU ID: %d -----------------------------------------------\n", i);
+ pr_info("%s", dss_lastinfo.log[i]);
+ }
+}
+
+void dbg_snapshot_save_log(int cpu, unsigned long where)
+{
+ if (dss_lastinfo.last_p[cpu] == NULL)
+ dss_lastinfo.last_p[cpu] = &dss_lastinfo.log[cpu][0];
+
+ if (dss_lastinfo.last_p[cpu] > &dss_lastinfo.log[cpu][SZ_1K - SZ_128])
+ return;
+
+ *(unsigned long *)&(dss_lastinfo.last_p[cpu]) += sprintf(dss_lastinfo.last_p[cpu],
+ "[<%p>] %pS\n", (void *)where, (void *)where);
+
+}
+
+static void dbg_snapshot_get_sec(unsigned long long ts, unsigned long *sec, unsigned long *msec)
+{
+ *sec = ts / NSEC_PER_SEC;
+ *msec = (ts % NSEC_PER_SEC) / USEC_PER_MSEC;
+}
+
+static void dbg_snapshot_print_last_irq(int cpu)
+{
+ unsigned long idx, sec, msec;
+ char fn_name[KSYM_NAME_LEN];
+
+ idx = atomic_read(&dss_idx.irq_log_idx[cpu]) & (ARRAY_SIZE(dss_log->irq[0]) - 1);
+ dbg_snapshot_get_sec(dss_log->irq[cpu][idx].time, &sec, &msec);
+ lookup_symbol_name((unsigned long)dss_log->irq[cpu][idx].fn, fn_name);
+
+ pr_info("%-16s: [%4lu] %10lu.%06lu sec, %10s: %24s, %8s: %8d, %10s: %2d, %s\n",
+ ">>> last irq", idx, sec, msec,
+ "handler", fn_name,
+ "irq", dss_log->irq[cpu][idx].irq,
+ "en", dss_log->irq[cpu][idx].en,
+ (dss_log->irq[cpu][idx].en == 1) ? "[Missmatch]" : "");
+}
+
+static void dbg_snapshot_print_last_task(int cpu)
+{
+ unsigned long idx, sec, msec;
+ struct task_struct *task;
+
+ idx = atomic_read(&dss_idx.task_log_idx[cpu]) & (ARRAY_SIZE(dss_log->task[0]) - 1);
+ dbg_snapshot_get_sec(dss_log->task[cpu][idx].time, &sec, &msec);
+ task = dss_log->task[cpu][idx].task;
+
+ pr_info("%-16s: [%4lu] %10lu.%06lu sec, %10s: %24s, %8s: 0x%-16p, %10s: %16llu\n",
+ ">>> last task", idx, sec, msec,
+ "task_comm", (task) ? task->comm : "NULL",
+ "task", task,
+ "exec_start", (task) ? task->se.exec_start : 0);
+}
+
+static void dbg_snapshot_print_last_work(int cpu)
+{
+ unsigned long idx, sec, msec;
+ char fn_name[KSYM_NAME_LEN];
+
+ idx = atomic_read(&dss_idx.work_log_idx[cpu]) & (ARRAY_SIZE(dss_log->work[0]) - 1);
+ dbg_snapshot_get_sec(dss_log->work[cpu][idx].time, &sec, &msec);
+ lookup_symbol_name((unsigned long)dss_log->work[cpu][idx].fn, fn_name);
+
+ pr_info("%-16s: [%4lu] %10lu.%06lu sec, %10s: %24s, %8s: %20s, %3s: %3d %s\n",
+ ">>> last work", idx, sec, msec,
+ "task_name", dss_log->work[cpu][idx].task_comm,
+ "work_fn", fn_name,
+ "en", dss_log->work[cpu][idx].en,
+ (dss_log->work[cpu][idx].en == 1) ? "[Missmatch]" : "");
+}
+
+static void dbg_snapshot_print_last_cpuidle(int cpu)
+{
+ unsigned long idx, sec, msec;
+
+ idx = atomic_read(&dss_idx.cpuidle_log_idx[cpu]) & (ARRAY_SIZE(dss_log->cpuidle[0]) - 1);
+ dbg_snapshot_get_sec(dss_log->cpuidle[cpu][idx].time, &sec, &msec);
+
+ pr_info("%-16s: [%4lu] %10lu.%06lu sec, %10s: %24d, %8s: %4s, %6s: %3d, %12s: %2d, %3s: %3d %s\n",
+ ">>> last cpuidle", idx, sec, msec,
+ "stay time", dss_log->cpuidle[cpu][idx].delta,
+ "modes", dss_log->cpuidle[cpu][idx].modes,
+ "state", dss_log->cpuidle[cpu][idx].state,
+ "online_cpus", dss_log->cpuidle[cpu][idx].num_online_cpus,
+ "en", dss_log->cpuidle[cpu][idx].en,
+ (dss_log->cpuidle[cpu][idx].en == 1) ? "[Missmatch]" : "");
+}
+
+static void dbg_snapshot_print_lastinfo(void)
+{
+ int cpu;
+
+ pr_info("<last info>\n");
+ for (cpu = 0; cpu < DSS_NR_CPUS; cpu++) {
+ pr_info("CPU ID: %d -----------------------------------------------\n", cpu);
+ dbg_snapshot_print_last_task(cpu);
+ dbg_snapshot_print_last_work(cpu);
+ dbg_snapshot_print_last_irq(cpu);
+ dbg_snapshot_print_last_cpuidle(cpu);
+ }
+}
+
+#ifdef CONFIG_DEBUG_SNAPSHOT_REGULATOR
+void dbg_snapshot_regulator(unsigned long long timestamp, char* f_name, unsigned int addr, unsigned int volt, unsigned int rvolt, int en)
+{
+ struct dbg_snapshot_item *item = &dss_items[dss_desc.kevents_num];
+
+ if (unlikely(!dss_base.enabled || !item->entry.enabled))
+ return;
+ {
+ int cpu = raw_smp_processor_id();
+ unsigned long i = atomic_inc_return(&dss_idx.regulator_log_idx) &
+ (ARRAY_SIZE(dss_log->regulator) - 1);
+ int size = strlen(f_name);
+ if (size >= SZ_16)
+ size = SZ_16 - 1;
+ dss_log->regulator[i].time = cpu_clock(cpu);
+ dss_log->regulator[i].cpu = cpu;
+ dss_log->regulator[i].acpm_time = timestamp;
+ strncpy(dss_log->regulator[i].name, f_name, size);
+ dss_log->regulator[i].reg = addr;
+ dss_log->regulator[i].en = en;
+ dss_log->regulator[i].voltage = volt;
+ dss_log->regulator[i].raw_volt = rvolt;
+ }
+}
+#endif
+
+#ifdef CONFIG_DEBUG_SNAPSHOT_THERMAL
+void dbg_snapshot_thermal(void *data, unsigned int temp, char *name, unsigned int max_cooling)
+{
+ struct dbg_snapshot_item *item = &dss_items[dss_desc.kevents_num];
+
+ if (unlikely(!dss_base.enabled || !item->entry.enabled))
+ return;
+ {
+ int cpu = raw_smp_processor_id();
+ unsigned long i = atomic_inc_return(&dss_idx.thermal_log_idx) &
+ (ARRAY_SIZE(dss_log->thermal) - 1);
+
+ dss_log->thermal[i].time = cpu_clock(cpu);
+ dss_log->thermal[i].cpu = cpu;
+ dss_log->thermal[i].data = (struct exynos_tmu_platform_data *)data;
+ dss_log->thermal[i].temp = temp;
+ dss_log->thermal[i].cooling_device = name;
+ dss_log->thermal[i].cooling_state = max_cooling;
+ }
+}
+#endif
+
+void dbg_snapshot_irq(int irq, void *fn, unsigned int val, int en)
+{
+ struct dbg_snapshot_item *item = &dss_items[dss_desc.kevents_num];
+ unsigned long flags;
+
+ if (unlikely(!dss_base.enabled || !item->entry.enabled))
+ return;
+
+ flags = pure_arch_local_irq_save();
+ {
+ int cpu = raw_smp_processor_id();
+ unsigned long i;
+
+ for (i = 0; i < ARRAY_SIZE(dss_irqlog_exlist); i++) {
+ if (irq == dss_irqlog_exlist[i]) {
+ pure_arch_local_irq_restore(flags);
+ return;
+ }
+ }
+ i = atomic_inc_return(&dss_idx.irq_log_idx[cpu]) &
+ (ARRAY_SIZE(dss_log->irq[0]) - 1);
+
+ dss_log->irq[cpu][i].time = cpu_clock(cpu);
+ dss_log->irq[cpu][i].sp = (unsigned long) current_stack_pointer;
+ dss_log->irq[cpu][i].irq = irq;
+ dss_log->irq[cpu][i].fn = (void *)fn;
+ dss_log->irq[cpu][i].preempt = preempt_count();
+ dss_log->irq[cpu][i].val = val;
+ dss_log->irq[cpu][i].en = en;
+ }
+ pure_arch_local_irq_restore(flags);
+}
+
+#ifdef CONFIG_DEBUG_SNAPSHOT_IRQ_EXIT
+void dbg_snapshot_irq_exit(unsigned int irq, unsigned long long start_time)
+{
+ struct dbg_snapshot_item *item = &dss_items[dss_desc.kevents_num];
+ unsigned long i;
+
+ if (unlikely(!dss_base.enabled || !item->entry.enabled))
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(dss_irqexit_exlist); i++)
+ if (irq == dss_irqexit_exlist[i])
+ return;
+ {
+ int cpu = raw_smp_processor_id();
+ unsigned long long time, latency;
+
+ i = atomic_inc_return(&dss_idx.irq_exit_log_idx[cpu]) &
+ (ARRAY_SIZE(dss_log->irq_exit[0]) - 1);
+
+ time = cpu_clock(cpu);
+ latency = time - start_time;
+
+ if (unlikely(latency >
+ (dss_irqexit_threshold * 1000))) {
+ dss_log->irq_exit[cpu][i].latency = latency;
+ dss_log->irq_exit[cpu][i].sp = (unsigned long) current_stack_pointer;
+ dss_log->irq_exit[cpu][i].end_time = time;
+ dss_log->irq_exit[cpu][i].time = start_time;
+ dss_log->irq_exit[cpu][i].irq = irq;
+ } else
+ atomic_dec(&dss_idx.irq_exit_log_idx[cpu]);
+ }
+}
+#endif
+
+#ifdef CONFIG_DEBUG_SNAPSHOT_SPINLOCK
+void dbg_snapshot_spinlock(void *v_lock, int en)
+{
+ struct dbg_snapshot_item *item = &dss_items[dss_desc.kevents_num];
+
+ if (unlikely(!dss_base.enabled || !item->entry.enabled))
+ return;
+ {
+ int cpu = raw_smp_processor_id();
+ unsigned index = atomic_inc_return(&dss_idx.spinlock_log_idx[cpu]);
+ unsigned long j, i = index & (ARRAY_SIZE(dss_log->spinlock[0]) - 1);
+ raw_spinlock_t *lock = (raw_spinlock_t *)v_lock;
+#ifdef CONFIG_ARM_ARCH_TIMER
+ dss_log->spinlock[cpu][i].time = cpu_clock(cpu);
+#else
+ dss_log->spinlock[cpu][i].time = index;
+#endif
+ dss_log->spinlock[cpu][i].sp = (unsigned long) current_stack_pointer;
+ dss_log->spinlock[cpu][i].jiffies = jiffies_64;
+#ifdef CONFIG_DEBUG_SPINLOCK
+ dss_log->spinlock[cpu][i].lock = lock;
+ dss_log->spinlock[cpu][i].next = lock->raw_lock.next;
+ dss_log->spinlock[cpu][i].owner = lock->raw_lock.owner;
+#endif
+ dss_log->spinlock[cpu][i].en = en;
+
+ for (j = 0; j < dss_desc.callstack; j++) {
+ dss_log->spinlock[cpu][i].caller[j] =
+ (void *)((size_t)return_address(j + 1));
+ }
+ }
+}
+#endif
+
+#ifdef CONFIG_DEBUG_SNAPSHOT_IRQ_DISABLED
+void dbg_snapshot_irqs_disabled(unsigned long flags)
+{
+ struct dbg_snapshot_item *item = &dss_items[dss_desc.kevents_num];
+ int cpu = raw_smp_processor_id();
+
+ if (unlikely(!dss_base.enabled || !item->entry.enabled))
+ return;
+
+ if (unlikely(flags)) {
+ unsigned j, local_flags = pure_arch_local_irq_save();
+
+ /* If flags has one, it shows interrupt enable status */
+ atomic_set(&dss_idx.irqs_disabled_log_idx[cpu], -1);
+ dss_log->irqs_disabled[cpu][0].time = 0;
+ dss_log->irqs_disabled[cpu][0].index = 0;
+ dss_log->irqs_disabled[cpu][0].task = NULL;
+ dss_log->irqs_disabled[cpu][0].task_comm = NULL;
+
+ for (j = 0; j < dss_desc.callstack; j++) {
+ dss_log->irqs_disabled[cpu][0].caller[j] = NULL;
+ }
+
+ pure_arch_local_irq_restore(local_flags);
+ } else {
+ unsigned index = atomic_inc_return(&dss_idx.irqs_disabled_log_idx[cpu]);
+ unsigned long j, i = index % ARRAY_SIZE(dss_log->irqs_disabled[0]);
+
+ dss_log->irqs_disabled[cpu][0].time = jiffies_64;
+ dss_log->irqs_disabled[cpu][i].index = index;
+ dss_log->irqs_disabled[cpu][i].task = get_current();
+ dss_log->irqs_disabled[cpu][i].task_comm = get_current()->comm;
+
+ for (j = 0; j < dss_desc.callstack; j++) {
+ dss_log->irqs_disabled[cpu][i].caller[j] =
+ (void *)((size_t)return_address(j + 1));
+ }
+ }
+}
+#endif
+
+#ifdef CONFIG_DEBUG_SNAPSHOT_CLK
+void dbg_snapshot_clk(void *clock, const char *func_name, unsigned long arg, int mode)
+{
+ struct dbg_snapshot_item *item = &dss_items[dss_desc.kevents_num];
+
+ if (unlikely(!dss_base.enabled || !item->entry.enabled))
+ return;
+ {
+ int cpu = raw_smp_processor_id();
+ unsigned long i = atomic_inc_return(&dss_idx.clk_log_idx) &
+ (ARRAY_SIZE(dss_log->clk) - 1);
+
+ dss_log->clk[i].time = cpu_clock(cpu);
+ dss_log->clk[i].mode = mode;
+ dss_log->clk[i].arg = arg;
+ dss_log->clk[i].clk = (struct clk_hw *)clock;
+ dss_log->clk[i].f_name = func_name;
+ }
+}
+#endif
+
+#ifdef CONFIG_DEBUG_SNAPSHOT_PMU
+void dbg_snapshot_pmu(int id, const char *func_name, int mode)
+{
+ struct dbg_snapshot_item *item = &dss_items[dss_desc.kevents_num];
+
+ if (unlikely(!dss_base.enabled || !item->entry.enabled))
+ return;
+ {
+ int cpu = raw_smp_processor_id();
+ unsigned long i = atomic_inc_return(&dss_idx.pmu_log_idx) &
+ (ARRAY_SIZE(dss_log->pmu) - 1);
+
+ dss_log->pmu[i].time = cpu_clock(cpu);
+ dss_log->pmu[i].mode = mode;
+ dss_log->pmu[i].id = id;
+ dss_log->pmu[i].f_name = func_name;
+ }
+}
+#endif
+
+#ifdef CONFIG_DEBUG_SNAPSHOT_FREQ
+static void dbg_snapshot_print_freqinfo(void)
+{
+ unsigned long idx, sec, msec;
+ char *freq_name;
+ unsigned int i;
+ unsigned long old_freq, target_freq;
+
+ pr_info("\n<freq info>\n");
+
+ for (i = 0; i < DSS_FLAG_END; i++) {
+ idx = atomic_read(&dss_lastinfo.freq_last_idx[i]) & (ARRAY_SIZE(dss_log->freq) - 1);
+ freq_name = dss_log->freq[idx].freq_name;
+ if ((!freq_name) || strncmp(freq_name, dss_freq_name[i], strlen(dss_freq_name[i]))) {
+ pr_info("%10s: no infomation\n", dss_freq_name[i]);
+ continue;
+ }
+
+ dbg_snapshot_get_sec(dss_log->freq[idx].time, &sec, &msec);
+ old_freq = dss_log->freq[idx].old_freq;
+ target_freq = dss_log->freq[idx].target_freq;
+ pr_info("%10s: [%4lu] %10lu.%06lu sec, %12s: %6luMhz, %12s: %6luMhz, %3s: %3d %s\n",
+ freq_name, idx, sec, msec,
+ "old_freq", old_freq/1000,
+ "target_freq", target_freq/1000,
+ "en", dss_log->freq[idx].en,
+ (dss_log->freq[idx].en == 1) ? "[Missmatch]" : "");
+ }
+}
+
+void dbg_snapshot_freq(int type, unsigned long old_freq, unsigned long target_freq, int en)
+{
+ struct dbg_snapshot_item *item = &dss_items[dss_desc.kevents_num];
+
+ if (unlikely(!dss_base.enabled || !item->entry.enabled))
+ return;
+ {
+ int cpu = raw_smp_processor_id();
+ unsigned long i = atomic_inc_return(&dss_idx.freq_log_idx) &
+ (ARRAY_SIZE(dss_log->freq) - 1);
+
+ if (atomic_read(&dss_idx.freq_log_idx) > atomic_read(&dss_lastinfo.freq_last_idx[type]))
+ atomic_set(&dss_lastinfo.freq_last_idx[type], atomic_read(&dss_idx.freq_log_idx));
+
+ dss_log->freq[i].time = cpu_clock(cpu);
+ dss_log->freq[i].cpu = cpu;
+ dss_log->freq[i].freq_name = dss_freq_name[type];
+ dss_log->freq[i].old_freq = old_freq;
+ dss_log->freq[i].target_freq = target_freq;
+ dss_log->freq[i].en = en;
+ }
+}
+#endif
+
+#ifndef arch_irq_stat
+#define arch_irq_stat() 0
+#endif
+
+static void dbg_snapshot_print_irq(void)
+{
+ int i, j;
+ u64 sum = 0;
+
+ for_each_possible_cpu(i) {
+ sum += kstat_cpu_irqs_sum(i);
+ sum += arch_irq_stat_cpu(i);
+ }
+ sum += arch_irq_stat();
+
+ pr_info("\n<irq info>\n");
+ pr_info("------------------------------------------------------------------\n");
+ pr_info("\n");
+ pr_info("sum irq : %llu", (unsigned long long)sum);
+ pr_info("------------------------------------------------------------------\n");
+
+ for_each_irq_nr(j) {
+ unsigned int irq_stat = kstat_irqs(j);
+
+ if (irq_stat) {
+ struct irq_desc *desc = irq_to_desc(j);
+ const char *name;
+
+ name = desc->action ? (desc->action->name ? desc->action->name : "???") : "???";
+ pr_info("irq-%-4d : %8u %s\n", j, irq_stat, name);
+ }
+ }
+}
+
+void dbg_snapshot_print_panic_report(void)
+{
+ pr_info("============================================================\n");
+ pr_info("Panic Report\n");
+ pr_info("============================================================\n");
+ dbg_snapshot_print_lastinfo();
+#ifdef CONFIG_DEBUG_SNAPSHOT_FREQ
+ dbg_snapshot_print_freqinfo();
+#endif
+ dbg_snapshot_print_calltrace();
+ dbg_snapshot_print_irq();
+ pr_info("============================================================\n");
+}
+
+#ifdef CONFIG_DEBUG_SNAPSHOT_DM
+void dbg_snapshot_dm(int type, unsigned long min, unsigned long max, s32 wait_t, s32 t)
+{
+ struct dbg_snapshot_item *item = &dss_items[dss_desc.kevents_num];
+
+ if (unlikely(!dss_base.enabled || !item->entry.enabled))
+ return;
+ {
+ int cpu = raw_smp_processor_id();
+ unsigned long i = atomic_inc_return(&dss_idx.dm_log_idx) &
+ (ARRAY_SIZE(dss_log->dm) - 1);
+
+ dss_log->dm[i].time = cpu_clock(cpu);
+ dss_log->dm[i].cpu = cpu;
+ dss_log->dm[i].dm_num = type;
+ dss_log->dm[i].min_freq = min;
+ dss_log->dm[i].max_freq = max;
+ dss_log->dm[i].wait_dmt = wait_t;
+ dss_log->dm[i].do_dmt = t;
+ }
+}
+#endif
+
+#ifdef CONFIG_DEBUG_SNAPSHOT_HRTIMER
+void dbg_snapshot_hrtimer(void *timer, s64 *now, void *fn, int en)
+{
+ struct dbg_snapshot_item *item = &dss_items[dss_desc.kevents_num];
+
+ if (unlikely(!dss_base.enabled || !item->entry.enabled))
+ return;
+ {
+ int cpu = raw_smp_processor_id();
+ unsigned long i = atomic_inc_return(&dss_idx.hrtimer_log_idx[cpu]) &
+ (ARRAY_SIZE(dss_log->hrtimers[0]) - 1);
+
+ dss_log->hrtimers[cpu][i].time = cpu_clock(cpu);
+ dss_log->hrtimers[cpu][i].now = *now;
+ dss_log->hrtimers[cpu][i].timer = (struct hrtimer *)timer;
+ dss_log->hrtimers[cpu][i].fn = fn;
+ dss_log->hrtimers[cpu][i].en = en;
+ }
+}
+#endif
+
+#ifdef CONFIG_DEBUG_SNAPSHOT_I2C
+void dbg_snapshot_i2c(struct i2c_adapter *adap, struct i2c_msg *msgs, int num, int en)
+{
+ struct dbg_snapshot_item *item = &dss_items[dss_desc.kevents_num];
+
+ if (unlikely(!dss_base.enabled || !item->entry.enabled))
+ return;
+ {
+ int cpu = raw_smp_processor_id();
+ unsigned long i = atomic_inc_return(&dss_idx.i2c_log_idx) &
+ (ARRAY_SIZE(dss_log->i2c) - 1);
+
+ dss_log->i2c[i].time = cpu_clock(cpu);
+ dss_log->i2c[i].cpu = cpu;
+ dss_log->i2c[i].adap = adap;
+ dss_log->i2c[i].msgs = msgs;
+ dss_log->i2c[i].num = num;
+ dss_log->i2c[i].en = en;
+ }
+}
+#endif
+
+#ifdef CONFIG_DEBUG_SNAPSHOT_SPI
+void dbg_snapshot_spi(struct spi_controller *ctlr, struct spi_message *cur_msg, int en)
+{
+ struct dbg_snapshot_item *item = &dss_items[dss_desc.kevents_num];
+
+ if (unlikely(!dss_base.enabled || !item->entry.enabled))
+ return;
+ {
+ int cpu = raw_smp_processor_id();
+ unsigned long i = atomic_inc_return(&dss_idx.spi_log_idx) &
+ (ARRAY_SIZE(dss_log->spi) - 1);
+
+ dss_log->spi[i].time = cpu_clock(cpu);
+ dss_log->spi[i].cpu = cpu;
+ dss_log->spi[i].ctlr = ctlr;
+ dss_log->spi[i].cur_msg = cur_msg;
+ dss_log->spi[i].en = en;
+ }
+}
+#endif
+
+#ifdef CONFIG_DEBUG_SNAPSHOT_ACPM
+void dbg_snapshot_acpm(unsigned long long timestamp, const char *log, unsigned int data)
+{
+ struct dbg_snapshot_item *item = &dss_items[dss_desc.kevents_num];
+
+ if (unlikely(!dss_base.enabled || !item->entry.enabled))
+ return;
+ {
+ int cpu = raw_smp_processor_id();
+ unsigned long i = atomic_inc_return(&dss_idx.acpm_log_idx) &
+ (ARRAY_SIZE(dss_log->acpm) - 1);
+ int len = strlen(log);
+
+ if (len >= 9)
+ len = 9;
+
+ dss_log->acpm[i].time = cpu_clock(cpu);
+ dss_log->acpm[i].acpm_time = timestamp;
+ strncpy(dss_log->acpm[i].log, log, len);
+ dss_log->acpm[i].log[8] = '\0';
+ dss_log->acpm[i].data = data;
+ }
+}
+#endif
+
+#ifdef CONFIG_DEBUG_SNAPSHOT_REG
+static phys_addr_t virt_to_phys_high(size_t vaddr)
+{
+ phys_addr_t paddr = 0;
+ pgd_t *pgd;
+ pmd_t *pmd;
+ pte_t *pte;
+
+ if (virt_addr_valid((void *) vaddr)) {
+ paddr = virt_to_phys((void *) vaddr);
+ goto out;
+ }
+
+ pgd = pgd_offset_k(vaddr);
+ if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
+ goto out;
+
+ if (pgd_val(*pgd) & 2) {
+ paddr = pgd_val(*pgd) & SECTION_MASK;
+ goto out;
+ }
+
+ pmd = pmd_offset((pud_t *)pgd, vaddr);
+ if (pmd_none_or_clear_bad(pmd))
+ goto out;
+
+ pte = pte_offset_kernel(pmd, vaddr);
+ if (pte_none(*pte))
+ goto out;
+
+ paddr = pte_val(*pte) & PAGE_MASK;
+
+out:
+ return paddr | (vaddr & UL(SZ_4K - 1));
+}
+
+void dbg_snapshot_reg(unsigned int read, size_t val, size_t reg, int en)
+{
+ struct dbg_snapshot_item *item = &dss_items[dss_desc.kevents_num];
+ int cpu = raw_smp_processor_id();
+ unsigned long i, j;
+ size_t phys_reg, start_addr, end_addr;
+
+ if (unlikely(!dss_base.enabled || !item->entry.enabled))
+ return;
+
+ if (dss_reg_exlist[0].addr == 0)
+ return;
+
+ phys_reg = virt_to_phys_high(reg);
+ if (unlikely(!phys_reg))
+ return;
+
+ for (j = 0; j < ARRAY_SIZE(dss_reg_exlist); j++) {
+ if (dss_reg_exlist[j].addr == 0)
+ break;
+ start_addr = dss_reg_exlist[j].addr;
+ end_addr = start_addr + dss_reg_exlist[j].size;
+ if (start_addr <= phys_reg && phys_reg <= end_addr)
+ return;
+ }
+
+ i = atomic_inc_return(&dss_idx.reg_log_idx[cpu]) &
+ (ARRAY_SIZE(dss_log->reg[0]) - 1);
+
+ dss_log->reg[cpu][i].time = cpu_clock(cpu);
+ dss_log->reg[cpu][i].read = read;
+ dss_log->reg[cpu][i].val = val;
+ dss_log->reg[cpu][i].reg = phys_reg;
+ dss_log->reg[cpu][i].en = en;
+
+ for (j = 0; j < dss_desc.callstack; j++) {
+ dss_log->reg[cpu][i].caller[j] =
+ (void *)((size_t)return_address(j + 1));
+ }
+}
+#endif
+
+#ifndef CONFIG_DEBUG_SNAPSHOT_MINIMIZED_MODE
+void dbg_snapshot_clockevent(unsigned long long clc, int64_t delta, void *next_event)
+{
+ struct dbg_snapshot_item *item = &dss_items[dss_desc.kevents_num];
+
+ if (unlikely(!dss_base.enabled || !item->entry.enabled))
+ return;
+ {
+ int cpu = raw_smp_processor_id();
+ unsigned long j, i = atomic_inc_return(&dss_idx.clockevent_log_idx[cpu]) &
+ (ARRAY_SIZE(dss_log->clockevent[0]) - 1);
+
+ dss_log->clockevent[cpu][i].time = cpu_clock(cpu);
+ dss_log->clockevent[cpu][i].mct_cycle = clc;
+ dss_log->clockevent[cpu][i].delta_ns = delta;
+ dss_log->clockevent[cpu][i].next_event = *((ktime_t *)next_event);
+
+ for (j = 0; j < dss_desc.callstack; j++) {
+ dss_log->clockevent[cpu][i].caller[j] =
+ (void *)((size_t)return_address(j + 1));
+ }
+ }
+}
+
+void dbg_snapshot_printk(const char *fmt, ...)
+{
+ struct dbg_snapshot_item *item = &dss_items[dss_desc.kevents_num];
+
+ if (unlikely(!dss_base.enabled || !item->entry.enabled))
+ return;
+ {
+ int cpu = raw_smp_processor_id();
+ va_list args;
+ int ret;
+ unsigned long j, i = atomic_inc_return(&dss_idx.printk_log_idx) &
+ (ARRAY_SIZE(dss_log->printk) - 1);
+
+ va_start(args, fmt);
+ ret = vsnprintf(dss_log->printk[i].log,
+ sizeof(dss_log->printk[i].log), fmt, args);
+ va_end(args);
+
+ dss_log->printk[i].time = cpu_clock(cpu);
+ dss_log->printk[i].cpu = cpu;
+
+ for (j = 0; j < dss_desc.callstack; j++) {
+ dss_log->printk[i].caller[j] =
+ (void *)((size_t)return_address(j));
+ }
+ }
+}
+
+void dbg_snapshot_printkl(size_t msg, size_t val)
+{
+ struct dbg_snapshot_item *item = &dss_items[dss_desc.kevents_num];
+
+ if (unlikely(!dss_base.enabled || !item->entry.enabled))
+ return;
+ {
+ int cpu = raw_smp_processor_id();
+ unsigned long j, i = atomic_inc_return(&dss_idx.printkl_log_idx) &
+ (ARRAY_SIZE(dss_log->printkl) - 1);
+
+ dss_log->printkl[i].time = cpu_clock(cpu);
+ dss_log->printkl[i].cpu = cpu;
+ dss_log->printkl[i].msg = msg;
+ dss_log->printkl[i].val = val;
+
+ for (j = 0; j < dss_desc.callstack; j++) {
+ dss_log->printkl[i].caller[j] =
+ (void *)((size_t)return_address(j));
+ }
+ }
+}
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Exynos-SnapShot debugging framework for Exynos SoC
+ *
+ * Author: Hosung Kim <Hosung0.kim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/ktime.h>
+#include <linux/kallsyms.h>
+#include <linux/platform_device.h>
+#include <linux/clk-provider.h>
+#include <linux/pstore_ram.h>
+#include <linux/sched/clock.h>
+#include <linux/ftrace.h>
+
+#include "debug-snapshot-local.h"
+#include <asm/irq.h>
+#include <asm/traps.h>
+#include <asm/hardirq.h>
+#include <asm/stacktrace.h>
+#include <linux/debug-snapshot.h>
+#include <linux/kernel_stat.h>
+#include <linux/irqnr.h>
+#include <linux/irq.h>
+#include <linux/irqdesc.h>
+
+/* This defines are for PSTORE */
+#define DSS_LOGGER_LEVEL_HEADER (1)
+#define DSS_LOGGER_LEVEL_PREFIX (2)
+#define DSS_LOGGER_LEVEL_TEXT (3)
+#define DSS_LOGGER_LEVEL_MAX (4)
+#define DSS_LOGGER_SKIP_COUNT (4)
+#define DSS_LOGGER_STRING_PAD (1)
+#define DSS_LOGGER_HEADER_SIZE (68)
+
+#define DSS_LOG_ID_MAIN (0)
+#define DSS_LOG_ID_RADIO (1)
+#define DSS_LOG_ID_EVENTS (2)
+#define DSS_LOG_ID_SYSTEM (3)
+#define DSS_LOG_ID_CRASH (4)
+#define DSS_LOG_ID_KERNEL (5)
+
+typedef struct __attribute__((__packed__)) {
+ uint8_t magic;
+ uint16_t len;
+ uint16_t uid;
+ uint16_t pid;
+} dss_pmsg_log_header_t;
+
+typedef struct __attribute__((__packed__)) {
+ unsigned char id;
+ uint16_t tid;
+ int32_t tv_sec;
+ int32_t tv_nsec;
+} dss_android_log_header_t;
+
+typedef struct dss_logger {
+ uint16_t len;
+ uint16_t id;
+ uint16_t pid;
+ uint16_t tid;
+ uint16_t uid;
+ uint16_t level;
+ int32_t tv_sec;
+ int32_t tv_nsec;
+ char msg;
+ char *buffer;
+ void (*func_hook_logger)(const char*, const char*, size_t);
+} __attribute__((__packed__)) dss_logger;
+
+static dss_logger logger;
+
+void register_hook_logger(void (*func)(const char *name, const char *buf, size_t size))
+{
+ logger.func_hook_logger = func;
+ logger.buffer = vmalloc(PAGE_SIZE * 3);
+
+ if (logger.buffer)
+ pr_info("debug-snapshot: logger buffer alloc address: 0x%p\n", logger.buffer);
+}
+EXPORT_SYMBOL(register_hook_logger);
+
+static int dbg_snapshot_combine_pmsg(char *buffer, size_t count, unsigned int level)
+{
+ char *logbuf = logger.buffer;
+
+ if (!logbuf)
+ return -ENOMEM;
+
+ switch (level) {
+ case DSS_LOGGER_LEVEL_HEADER:
+ {
+ struct tm tmBuf;
+ u64 tv_kernel;
+ unsigned int logbuf_len;
+ unsigned long rem_nsec;
+
+ if (logger.id == DSS_LOG_ID_EVENTS)
+ break;
+
+ tv_kernel = local_clock();
+ rem_nsec = do_div(tv_kernel, 1000000000);
+ time_to_tm(logger.tv_sec, 0, &tmBuf);
+
+ logbuf_len = snprintf(logbuf, DSS_LOGGER_HEADER_SIZE,
+ "\n[%5lu.%06lu][%d:%16s] %02d-%02d %02d:%02d:%02d.%03d %5d %5d ",
+ (unsigned long)tv_kernel, rem_nsec / 1000,
+ raw_smp_processor_id(), current->comm,
+ tmBuf.tm_mon + 1, tmBuf.tm_mday,
+ tmBuf.tm_hour, tmBuf.tm_min, tmBuf.tm_sec,
+ logger.tv_nsec / 1000000, logger.pid, logger.tid);
+
+ logger.func_hook_logger("log_platform", logbuf, logbuf_len - 1);
+ }
+ break;
+ case DSS_LOGGER_LEVEL_PREFIX:
+ {
+ static const char *kPrioChars = "!.VDIWEFS";
+ unsigned char prio = logger.msg;
+
+ if (logger.id == DSS_LOG_ID_EVENTS)
+ break;
+
+ logbuf[0] = prio < strlen(kPrioChars) ? kPrioChars[prio] : '?';
+ logbuf[1] = ' ';
+
+ logger.func_hook_logger("log_platform", logbuf, DSS_LOGGER_LEVEL_PREFIX);
+ }
+ break;
+ case DSS_LOGGER_LEVEL_TEXT:
+ {
+ char *eatnl = buffer + count - DSS_LOGGER_STRING_PAD;
+
+ if (logger.id == DSS_LOG_ID_EVENTS)
+ break;
+ if (count == DSS_LOGGER_SKIP_COUNT && *eatnl != '\0')
+ break;
+
+ logger.func_hook_logger("log_platform", buffer, count - 1);
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+int dbg_snapshot_hook_pmsg(char *buffer, size_t count)
+{
+ dss_android_log_header_t header;
+ dss_pmsg_log_header_t pmsg_header;
+
+ if (!logger.buffer)
+ return -ENOMEM;
+
+ switch (count) {
+ case sizeof(pmsg_header):
+ memcpy((void *)&pmsg_header, buffer, count);
+ if (pmsg_header.magic != 'l') {
+ dbg_snapshot_combine_pmsg(buffer, count, DSS_LOGGER_LEVEL_TEXT);
+ } else {
+ /* save logger data */
+ logger.pid = pmsg_header.pid;
+ logger.uid = pmsg_header.uid;
+ logger.len = pmsg_header.len;
+ }
+ break;
+ case sizeof(header):
+ /* save logger data */
+ memcpy((void *)&header, buffer, count);
+ logger.id = header.id;
+ logger.tid = header.tid;
+ logger.tv_sec = header.tv_sec;
+ logger.tv_nsec = header.tv_nsec;
+ if (logger.id > 7) {
+ /* write string */
+ dbg_snapshot_combine_pmsg(buffer, count, DSS_LOGGER_LEVEL_TEXT);
+ } else {
+ /* write header */
+ dbg_snapshot_combine_pmsg(buffer, count, DSS_LOGGER_LEVEL_HEADER);
+ }
+ break;
+ case sizeof(unsigned char):
+ logger.msg = buffer[0];
+ /* write char for prefix */
+ dbg_snapshot_combine_pmsg(buffer, count, DSS_LOGGER_LEVEL_PREFIX);
+ break;
+ default:
+ /* write string */
+ dbg_snapshot_combine_pmsg(buffer, count, DSS_LOGGER_LEVEL_TEXT);
+ break;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(dbg_snapshot_hook_pmsg);
+
+/*
+ * To support pstore/pmsg/pstore_ram, following is implementation for debug-snapshot
+ * dss_ramoops platform_device is used by pstore fs.
+ */
+
+static struct ramoops_platform_data dss_ramoops_data = {
+ .record_size = SZ_512K,
+ .console_size = SZ_512K,
+ .ftrace_size = SZ_512K,
+ .pmsg_size = SZ_512K,
+ .dump_oops = 1,
+};
+
+static struct platform_device dss_ramoops = {
+ .name = "ramoops",
+ .dev = {
+ .platform_data = &dss_ramoops_data,
+ },
+};
+
+static int __init dss_pstore_init(void)
+{
+ if (dbg_snapshot_get_enable("log_pstore")) {
+ dss_ramoops_data.mem_size = dbg_snapshot_get_item_size("log_pstore");
+ dss_ramoops_data.mem_address = dbg_snapshot_get_item_paddr("log_pstore");
+ }
+ return platform_device_register(&dss_ramoops);
+}
+
+static void __exit dss_pstore_exit(void)
+{
+ platform_device_unregister(&dss_ramoops);
+}
+module_init(dss_pstore_init);
+module_exit(dss_pstore_exit);
+
+MODULE_DESCRIPTION("Exynos Snapshot pstore module");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Exynos-SnapShot debugging framework for Exynos SoC
+ *
+ * Author: Hosung Kim <Hosung0.kim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/ktime.h>
+#include <linux/kallsyms.h>
+#include <linux/platform_device.h>
+#include <linux/clk-provider.h>
+#include <linux/pstore_ram.h>
+#include <linux/sched/clock.h>
+#include <linux/ftrace.h>
+
+#include "debug-snapshot-local.h"
+#include <asm/irq.h>
+#include <asm/traps.h>
+#include <asm/hardirq.h>
+#include <asm/stacktrace.h>
+#include <linux/debug-snapshot.h>
+#include <linux/kernel_stat.h>
+#include <linux/irqnr.h>
+#include <linux/irq.h>
+#include <linux/irqdesc.h>
+
+/*
+ * sysfs implementation for debug-snapshot
+ * you can access the sysfs of debug-snapshot to /sys/devices/system/debug_snapshot
+ * path.
+ */
+static struct bus_type dss_subsys = {
+ .name = "debug-snapshot",
+ .dev_name = "debug-snapshot",
+};
+
+extern int dss_irqlog_exlist[DSS_EX_MAX_NUM];
+extern int dss_irqexit_exlist[DSS_EX_MAX_NUM];
+extern unsigned int dss_irqexit_threshold;
+
+static ssize_t dss_enable_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ struct dbg_snapshot_item *item;
+ unsigned long i;
+ ssize_t n = 0;
+
+ /* item */
+ for (i = 0; i < dss_desc.log_enable_cnt; i++) {
+ item = &dss_items[i];
+ n += scnprintf(buf + n, 24, "%-12s : %sable\n",
+ item->name, item->entry.enabled ? "en" : "dis");
+ }
+
+ /* base */
+ n += scnprintf(buf + n, 24, "%-12s : %sable\n",
+ "base", dss_base.enabled ? "en" : "dis");
+
+ return n;
+}
+
+static ssize_t dss_enable_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ int en;
+ char *name;
+
+ name = (char *)kstrndup(buf, count, GFP_KERNEL);
+ if (!name)
+ return count;
+
+ name[count - 1] = '\0';
+
+ en = dbg_snapshot_get_enable(name);
+
+ if (en == -1)
+ pr_info("echo name > enabled\n");
+ else {
+ if (en)
+ dbg_snapshot_set_enable(name, false);
+ else
+ dbg_snapshot_set_enable(name, true);
+ }
+
+ kfree(name);
+ return count;
+}
+
+static ssize_t dss_callstack_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ ssize_t n = 0;
+
+ n = scnprintf(buf, 24, "callstack depth : %d\n", dss_desc.callstack);
+
+ return n;
+}
+
+static ssize_t dss_callstack_store(struct kobject *kobj, struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long callstack;
+
+ callstack = simple_strtoul(buf, NULL, 0);
+ pr_info("callstack depth(min 1, max 4) : %lu\n", callstack);
+
+ if (callstack < 5 && callstack > 0) {
+ dss_desc.callstack = (unsigned int)callstack;
+ pr_info("success inserting %lu to callstack value\n", callstack);
+ }
+ return count;
+}
+
+static ssize_t dss_irqlog_exlist_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ unsigned long i;
+ ssize_t n = 0;
+
+ n = scnprintf(buf, 24, "excluded irq number\n");
+
+ for (i = 0; i < ARRAY_SIZE(dss_irqlog_exlist); i++) {
+ if (dss_irqlog_exlist[i] == 0)
+ break;
+ n += scnprintf(buf + n, 24, "irq num: %-4d\n", dss_irqlog_exlist[i]);
+ }
+ return n;
+}
+
+static ssize_t dss_irqlog_exlist_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long i;
+ unsigned long irq;
+
+ irq = simple_strtoul(buf, NULL, 0);
+ pr_info("irq number : %lu\n", irq);
+
+ for (i = 0; i < ARRAY_SIZE(dss_irqlog_exlist); i++) {
+ if (dss_irqlog_exlist[i] == 0)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(dss_irqlog_exlist)) {
+ pr_err("list is full\n");
+ return count;
+ }
+
+ if (irq != 0) {
+ dss_irqlog_exlist[i] = irq;
+ pr_info("success inserting %lu to list\n", irq);
+ }
+ return count;
+}
+
+#ifdef CONFIG_DEBUG_SNAPSHOT_IRQ_EXIT
+static ssize_t dss_irqexit_exlist_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ unsigned long i;
+ ssize_t n = 0;
+
+ n = scnprintf(buf, 36, "Excluded irq number\n");
+ for (i = 0; i < ARRAY_SIZE(dss_irqexit_exlist); i++) {
+ if (dss_irqexit_exlist[i] == 0)
+ break;
+ n += scnprintf(buf + n, 24, "IRQ num: %-4d\n", dss_irqexit_exlist[i]);
+ }
+ return n;
+}
+
+static ssize_t dss_irqexit_exlist_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long i;
+ unsigned long irq;
+
+ irq = simple_strtoul(buf, NULL, 0);
+ pr_info("irq number : %lu\n", irq);
+
+ for (i = 0; i < ARRAY_SIZE(dss_irqexit_exlist); i++) {
+ if (dss_irqexit_exlist[i] == 0)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(dss_irqexit_exlist)) {
+ pr_err("list is full\n");
+ return count;
+ }
+
+ if (irq != 0) {
+ dss_irqexit_exlist[i] = irq;
+ pr_info("success inserting %lu to list\n", irq);
+ }
+ return count;
+}
+
+static ssize_t dss_irqexit_threshold_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ ssize_t n;
+
+ n = scnprintf(buf, 46, "threshold : %12u us\n", dss_irqexit_threshold);
+ return n;
+}
+
+static ssize_t dss_irqexit_threshold_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+
+ val = simple_strtoul(buf, NULL, 0);
+ pr_info("threshold value : %lu\n", val);
+
+ if (val != 0) {
+ dss_irqexit_threshold = (unsigned int)val;
+ pr_info("success %lu to threshold\n", val);
+ }
+ return count;
+}
+#endif
+
+#ifdef CONFIG_DEBUG_SNAPSHOT_REG
+static ssize_t dss_reg_exlist_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ unsigned long i;
+ ssize_t n = 0;
+
+ n = scnprintf(buf, 36, "excluded register address\n");
+ for (i = 0; i < ARRAY_SIZE(dss_reg_exlist); i++) {
+ if (dss_reg_exlist[i].addr == 0)
+ break;
+ n += scnprintf(buf + n, 40, "register addr: %08zx size: %08zx\n",
+ dss_reg_exlist[i].addr, dss_reg_exlist[i].size);
+ }
+ return n;
+}
+
+static ssize_t dss_reg_exlist_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long i;
+ size_t addr;
+
+ addr = simple_strtoul(buf, NULL, 0);
+ pr_info("register addr: %zx\n", addr);
+
+ for (i = 0; i < ARRAY_SIZE(dss_reg_exlist); i++) {
+ if (dss_reg_exlist[i].addr == 0)
+ break;
+ }
+ if (addr != 0) {
+ dss_reg_exlist[i].size = SZ_4K;
+ dss_reg_exlist[i].addr = addr;
+ pr_info("success %zx to threshold\n", (addr));
+ }
+ return count;
+}
+#endif
+
+
+static struct kobj_attribute dss_enable_attr =
+__ATTR(enabled, 0644, dss_enable_show, dss_enable_store);
+
+static struct kobj_attribute dss_callstack_attr =
+__ATTR(callstack, 0644, dss_callstack_show, dss_callstack_store);
+
+static struct kobj_attribute dss_irqlog_attr =
+__ATTR(exlist_irqdisabled, 0644, dss_irqlog_exlist_show,
+ dss_irqlog_exlist_store);
+#ifdef CONFIG_DEBUG_SNAPSHOT_IRQ_EXIT
+static struct kobj_attribute dss_irqexit_attr =
+__ATTR(exlist_irqexit, 0644, dss_irqexit_exlist_show,
+ dss_irqexit_exlist_store);
+
+static struct kobj_attribute dss_irqexit_threshold_attr =
+__ATTR(threshold_irqexit, 0644, dss_irqexit_threshold_show,
+ dss_irqexit_threshold_store);
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_REG
+
+static struct kobj_attribute dss_reg_attr =
+__ATTR(exlist_reg, 0644, dss_reg_exlist_show, dss_reg_exlist_store);
+#endif
+
+static struct attribute *dss_sysfs_attrs[] = {
+ &dss_enable_attr.attr,
+ &dss_callstack_attr.attr,
+ &dss_irqlog_attr.attr,
+#ifdef CONFIG_DEBUG_SNAPSHOT_IRQ_EXIT
+ &dss_irqexit_attr.attr,
+ &dss_irqexit_threshold_attr.attr,
+#endif
+#ifdef CONFIG_DEBUG_SNAPSHOT_REG
+ &dss_reg_attr.attr,
+#endif
+ NULL,
+};
+
+static struct attribute_group dss_sysfs_group = {
+ .attrs = dss_sysfs_attrs,
+};
+
+static const struct attribute_group *dss_sysfs_groups[] = {
+ &dss_sysfs_group,
+ NULL,
+};
+
+static int __init dbg_snapshot_sysfs_init(void)
+{
+ int ret = 0;
+
+ ret = subsys_system_register(&dss_subsys, dss_sysfs_groups);
+ if (ret)
+ pr_err("fail to register debug-snapshop subsys\n");
+
+ return ret;
+}
+late_initcall(dbg_snapshot_sysfs_init);
--- /dev/null
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Debug-SnapShot: Debug Framework for Ramdump based debugging method
+ * The original code is Exynos-Snapshot for Exynos SoC
+ *
+ * Author: Hosung Kim <hosung0.kim@samsung.com>
+ * Author: Changki Kim <changki.kim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/delay.h>
+#include <linux/kallsyms.h>
+#include <linux/input.h>
+#include <linux/smc.h>
+#include <linux/bitops.h>
+#include <linux/sched/clock.h>
+#include <linux/sched/debug.h>
+#include <linux/nmi.h>
+#include <linux/init_task.h>
+#include <linux/ftrace.h>
+
+#include <asm/cputype.h>
+#include <asm/smp_plat.h>
+#include <asm/core_regs.h>
+#include <asm/cacheflush.h>
+
+#include "debug-snapshot-local.h"
+
+DEFINE_PER_CPU(struct pt_regs *, dss_core_reg);
+DEFINE_PER_CPU(struct dbg_snapshot_mmu_reg *, dss_mmu_reg);
+
+struct dbg_snapshot_allcorelockup_param {
+ unsigned long last_pc_addr;
+ unsigned long spin_pc_addr;
+} dss_allcorelockup_param;
+
+void dbg_snapshot_hook_hardlockup_entry(void *v_regs)
+{
+ int cpu = raw_smp_processor_id();
+ unsigned int val;
+
+ if (!dss_base.enabled)
+ return;
+
+ if (!dss_desc.hardlockup_core_mask) {
+ if (dss_desc.multistage_wdt_irq &&
+ !dss_desc.allcorelockup_detected) {
+ /* 1st FIQ trigger */
+ val = readl(dbg_snapshot_get_base_vaddr() +
+ DSS_OFFSET_CORE_LAST_PC + (DSS_NR_CPUS * sizeof(unsigned long)));
+ if (val == DSS_SIGN_LOCKUP || val == (DSS_SIGN_LOCKUP + 1)) {
+ dss_desc.allcorelockup_detected = true;
+ dss_desc.hardlockup_core_mask = GENMASK(DSS_NR_CPUS - 1, 0);
+ } else {
+ return;
+ }
+ }
+ }
+
+ /* re-check the cpu number which is lockup */
+ if (dss_desc.hardlockup_core_mask & BIT(cpu)) {
+ int ret;
+ unsigned long last_pc;
+ struct pt_regs *regs;
+ unsigned long timeout = USEC_PER_SEC * 3;
+
+ do {
+ /*
+ * If one cpu is occurred to lockup,
+ * others are going to output its own information
+ * without side-effect.
+ */
+ ret = do_raw_spin_trylock(&dss_desc.nmi_lock);
+ if (!ret)
+ udelay(1);
+ } while (!ret && timeout--);
+
+ last_pc = dbg_snapshot_get_last_pc(cpu);
+
+ regs = (struct pt_regs *)v_regs;
+
+ /* Replace real pc value even if it is invalid */
+ regs->pc = last_pc;
+
+ /* Then, we expect bug() function works well */
+ pr_emerg("\n--------------------------------------------------------------------------\n"
+ " Debugging Information for Hardlockup core - CPU %d"
+ "\n--------------------------------------------------------------------------\n\n", cpu);
+ }
+}
+
+void dbg_snapshot_hook_hardlockup_exit(void)
+{
+ int cpu = raw_smp_processor_id();
+
+ if (!dss_base.enabled ||
+ !dss_desc.hardlockup_core_mask ||
+ !dss_desc.allcorelockup_detected) {
+ return;
+ }
+
+ /* re-check the cpu number which is lockup */
+ if (dss_desc.hardlockup_core_mask & BIT(cpu)) {
+ /* clear bit to complete replace */
+ dss_desc.hardlockup_core_mask &= ~(BIT(cpu));
+ /*
+ * If this unlock function does not make a side-effect
+ * even it's not lock
+ */
+ do_raw_spin_unlock(&dss_desc.nmi_lock);
+ }
+}
+
+void dbg_snapshot_recall_hardlockup_core(void)
+{
+ int i;
+#ifdef SMC_CMD_KERNEL_PANIC_NOTICE
+ int ret;
+#endif
+ unsigned long cpu_mask = 0, tmp_bit = 0;
+ unsigned long last_pc_addr = 0, timeout;
+
+ if (dss_desc.allcorelockup_detected) {
+ pr_emerg("debug-snapshot: skip recall hardlockup for dump of each core\n");
+ goto out;
+ }
+
+ for (i = 0; i < DSS_NR_CPUS; i++) {
+ if (i == raw_smp_processor_id())
+ continue;
+ tmp_bit = cpu_online_mask->bits[DSS_NR_CPUS/SZ_64] & (1 << i);
+ if (tmp_bit)
+ cpu_mask |= tmp_bit;
+ }
+
+ if (!cpu_mask)
+ goto out;
+
+ last_pc_addr = dbg_snapshot_get_last_pc_paddr();
+
+ pr_emerg("debug-snapshot: core hardlockup mask information: 0x%lx\n", cpu_mask);
+ dss_desc.hardlockup_core_mask = cpu_mask;
+
+#ifdef SMC_CMD_KERNEL_PANIC_NOTICE
+ /* Setup for generating NMI interrupt to unstopped CPUs */
+ ret = dss_soc_ops->soc_smc_call(SMC_CMD_KERNEL_PANIC_NOTICE,
+ cpu_mask,
+ (unsigned long)dbg_snapshot_bug_func,
+ last_pc_addr);
+ if (ret) {
+ pr_emerg("debug-snapshot: failed to generate NMI, "
+ "not support to dump information of core\n");
+ dss_desc.hardlockup_core_mask = 0;
+ goto out;
+ }
+#endif
+ /* Wait up to 3 seconds for NMI interrupt */
+ timeout = USEC_PER_SEC * 3;
+ while (dss_desc.hardlockup_core_mask != 0 && timeout--)
+ udelay(1);
+out:
+ return;
+}
+
+void dbg_snapshot_save_system(void *unused)
+{
+ struct dbg_snapshot_mmu_reg *mmu_reg;
+
+ if (!dbg_snapshot_get_enable("header"))
+ return;
+
+ mmu_reg = per_cpu(dss_mmu_reg, raw_smp_processor_id());
+#ifdef CONFIG_ARM64
+ asm("mrs x1, SCTLR_EL1\n\t" /* SCTLR_EL1 */
+ "str x1, [%0]\n\t"
+ "mrs x1, TTBR0_EL1\n\t" /* TTBR0_EL1 */
+ "str x1, [%0,#8]\n\t"
+ "mrs x1, TTBR1_EL1\n\t" /* TTBR1_EL1 */
+ "str x1, [%0,#16]\n\t"
+ "mrs x1, TCR_EL1\n\t" /* TCR_EL1 */
+ "str x1, [%0,#24]\n\t"
+ "mrs x1, ESR_EL1\n\t" /* ESR_EL1 */
+ "str x1, [%0,#32]\n\t"
+ "mrs x1, FAR_EL1\n\t" /* FAR_EL1 */
+ "str x1, [%0,#40]\n\t"
+ /* Don't populate AFSR0_EL1 and AFSR1_EL1 */
+ "mrs x1, CONTEXTIDR_EL1\n\t" /* CONTEXTIDR_EL1 */
+ "str x1, [%0,#48]\n\t"
+ "mrs x1, TPIDR_EL0\n\t" /* TPIDR_EL0 */
+ "str x1, [%0,#56]\n\t"
+ "mrs x1, TPIDRRO_EL0\n\t" /* TPIDRRO_EL0 */
+ "str x1, [%0,#64]\n\t"
+ "mrs x1, TPIDR_EL1\n\t" /* TPIDR_EL1 */
+ "str x1, [%0,#72]\n\t"
+ "mrs x1, MAIR_EL1\n\t" /* MAIR_EL1 */
+ "str x1, [%0,#80]\n\t"
+ "mrs x1, ELR_EL1\n\t" /* ELR_EL1 */
+ "str x1, [%0, #88]\n\t"
+ "mrs x1, SP_EL0\n\t" /* SP_EL0 */
+ "str x1, [%0, #96]\n\t" : /* output */
+ : "r"(mmu_reg) /* input */
+ : "%x1", "memory" /* clobbered register */
+ );
+#else
+ asm("mrc p15, 0, r1, c1, c0, 0\n\t" /* SCTLR */
+ "str r1, [%0]\n\t"
+ "mrc p15, 0, r1, c2, c0, 0\n\t" /* TTBR0 */
+ "str r1, [%0,#4]\n\t"
+ "mrc p15, 0, r1, c2, c0,1\n\t" /* TTBR1 */
+ "str r1, [%0,#8]\n\t"
+ "mrc p15, 0, r1, c2, c0,2\n\t" /* TTBCR */
+ "str r1, [%0,#12]\n\t"
+ "mrc p15, 0, r1, c3, c0,0\n\t" /* DACR */
+ "str r1, [%0,#16]\n\t"
+ "mrc p15, 0, r1, c5, c0,0\n\t" /* DFSR */
+ "str r1, [%0,#20]\n\t"
+ "mrc p15, 0, r1, c6, c0,0\n\t" /* DFAR */
+ "str r1, [%0,#24]\n\t"
+ "mrc p15, 0, r1, c5, c0,1\n\t" /* IFSR */
+ "str r1, [%0,#28]\n\t"
+ "mrc p15, 0, r1, c6, c0,2\n\t" /* IFAR */
+ "str r1, [%0,#32]\n\t"
+ /* Don't populate DAFSR and RAFSR */
+ "mrc p15, 0, r1, c10, c2,0\n\t" /* PMRRR */
+ "str r1, [%0,#44]\n\t"
+ "mrc p15, 0, r1, c10, c2,1\n\t" /* NMRRR */
+ "str r1, [%0,#48]\n\t"
+ "mrc p15, 0, r1, c13, c0,0\n\t" /* FCSEPID */
+ "str r1, [%0,#52]\n\t"
+ "mrc p15, 0, r1, c13, c0,1\n\t" /* CONTEXT */
+ "str r1, [%0,#56]\n\t"
+ "mrc p15, 0, r1, c13, c0,2\n\t" /* URWTPID */
+ "str r1, [%0,#60]\n\t"
+ "mrc p15, 0, r1, c13, c0,3\n\t" /* UROTPID */
+ "str r1, [%0,#64]\n\t"
+ "mrc p15, 0, r1, c13, c0,4\n\t" /* POTPIDR */
+ "str r1, [%0,#68]\n\t" : /* output */
+ : "r"(mmu_reg) /* input */
+ : "%r1", "memory" /* clobbered register */
+ );
+#endif
+}
+
+int dbg_snapshot_dump(void)
+{
+ /*
+ * Output CPU Memory Error syndrome Register
+ * CPUMERRSR, L2MERRSR
+ */
+#ifdef CONFIG_ARM64
+ unsigned long reg1, reg2, reg3;
+
+ if (read_cpuid_implementor() == ARM_CPU_IMP_SEC) {
+ switch (read_cpuid_part_number()) {
+ case ARM_CPU_PART_MONGOOSE:
+ case ARM_CPU_PART_MEERKAT:
+ asm ("mrs %0, S3_1_c15_c2_0\n\t"
+ "mrs %1, S3_1_c15_c2_4\n"
+ : "=r" (reg1), "=r" (reg2));
+ pr_emerg("FEMERR0SR: %016lx, FEMERR1SR: %016lx\n", reg1, reg2);
+ asm ("mrs %0, S3_1_c15_c2_1\n\t"
+ "mrs %1, S3_1_c15_c2_5\n"
+ : "=r" (reg1), "=r" (reg2));
+ pr_emerg("LSMERR0SR: %016lx, LSMERR1SR: %016lx\n", reg1, reg2);
+ asm ("mrs %0, S3_1_c15_c2_2\n\t"
+ "mrs %1, S3_1_c15_c2_6\n"
+ : "=r" (reg1), "=r" (reg2));
+ pr_emerg("TBWMERR0SR: %016lx, TBWMERR1SR: %016lx\n", reg1, reg2);
+ asm ("mrs %0, S3_1_c15_c2_3\n\t"
+ "mrs %1, S3_1_c15_c2_7\n"
+ : "=r" (reg1), "=r" (reg2));
+ pr_emerg("L2MERR0SR: %016lx, L2MERR1SR: %016lx\n", reg1, reg2);
+
+ /* L3 MERR */
+ asm ("msr S3_1_c15_c7_1, %0\n\t"
+ "isb\n"
+ :: "r" (0));
+ asm ("mrs %0, S3_1_c15_c3_0\n\t"
+ "mrs %1, S3_1_c15_c3_4\n"
+ : "=r" (reg1), "=r" (reg2));
+ pr_emerg("BANK0 L3MERR0SR: %016lx, L3MERR1SR: %016lx\n", reg1, reg2);
+ asm ("msr S3_1_c15_c7_1, %0\n\t"
+ "isb\n"
+ :: "r" (1));
+ asm ("mrs %0, S3_1_c15_c3_0\n\t"
+ "mrs %1, S3_1_c15_c3_4\n"
+ : "=r" (reg1), "=r" (reg2));
+ pr_emerg("BANK1 L3MERR0SR: %016lx, L3MERR1SR: %016lx\n", reg1, reg2);
+ asm ("msr S3_1_c15_c7_1, %0\n\t"
+ "isb\n"
+ :: "r" (2));
+ asm ("mrs %0, S3_1_c15_c3_0\n\t"
+ "mrs %1, S3_1_c15_c3_4\n"
+ : "=r" (reg1), "=r" (reg2));
+ pr_emerg("BANK2 L3MERR0SR: %016lx, L3MERR1SR: %016lx\n", reg1, reg2);
+ asm ("msr S3_1_c15_c7_1, %0\n\t"
+ "isb\n"
+ :: "r" (3));
+ asm ("mrs %0, S3_1_c15_c3_0\n\t"
+ "mrs %1, S3_1_c15_c3_4\n"
+ : "=r" (reg1), "=r" (reg2));
+ pr_emerg("BANK3 L3MERR0SR: %016lx, L3MERR1SR: %016lx\n", reg1, reg2);
+
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch (read_cpuid_part_number()) {
+ case ARM_CPU_PART_CORTEX_A57:
+ case ARM_CPU_PART_CORTEX_A53:
+ asm ("mrs %0, S3_1_c15_c2_2\n\t"
+ "mrs %1, S3_1_c15_c2_3\n"
+ : "=r" (reg1), "=r" (reg2));
+ pr_emerg("CPUMERRSR: %016lx, L2MERRSR: %016lx\n", reg1, reg2);
+ break;
+ case ARM_CPU_PART_ANANKE:
+ asm ("HINT #16");
+ asm ("mrs %0, S3_0_c12_c1_1\n" : "=r" (reg1)); /* read DISR_EL1 */
+ pr_emerg("DISR_EL1: %016lx\n", reg1);
+
+ asm ("msr S3_0_c5_c3_1, %0\n" :: "r" (0)); /* set 1st ERRSELR_EL1 */
+
+ asm ("mrs %0, S3_0_c5_c4_2\n"
+ "mrs %1, S3_0_c5_c4_3\n"
+ "mrs %2, S3_0_c5_c5_0\n"
+ : "=r" (reg1), "=r" (reg2), "=r" (reg3));
+ pr_emerg("1st : ERXSTATUS_EL1: %016lx, ERXADDR_EL1: %016lx, "
+ "ERXMISC0_EL1: %016lx\n", reg1, reg2, reg3);
+
+ asm ("msr S3_0_c5_c3_1, %0\n" :: "r" (1)); /* set 2nd ERRSELR_EL1 */
+
+ asm ("mrs %0, S3_0_c5_c4_2\n"
+ "mrs %1, S3_0_c5_c4_3\n"
+ "mrs %2, S3_0_c5_c5_0\n"
+ : "=r" (reg1), "=r" (reg2), "=r" (reg3));
+ pr_emerg("2nd : ERXSTATUS_EL1: %016lx, ERXADDR_EL1: %016lx, "
+ "ERXMISC0_EL1: %016lx\n", reg1, reg2, reg3);
+
+ break;
+ default:
+ break;
+ }
+ }
+#else
+ unsigned long reg0;
+ asm ("mrc p15, 0, %0, c0, c0, 0\n": "=r" (reg0));
+ if (((reg0 >> 4) & 0xFFF) == 0xC0F) {
+ /* Only Cortex-A15 */
+ unsigned long reg1, reg2, reg3;
+ asm ("mrrc p15, 0, %0, %1, c15\n\t"
+ "mrrc p15, 1, %2, %3, c15\n"
+ : "=r" (reg0), "=r" (reg1),
+ "=r" (reg2), "=r" (reg3));
+ pr_emerg("CPUMERRSR: %08lx_%08lx, L2MERRSR: %08lx_%08lx\n",
+ reg1, reg0, reg3, reg2);
+ }
+#endif
+ return 0;
+}
+EXPORT_SYMBOL(dbg_snapshot_dump);
+
+int dbg_snapshot_save_core(void *v_regs)
+{
+ struct pt_regs *regs = (struct pt_regs *)v_regs;
+ struct pt_regs *core_reg =
+ per_cpu(dss_core_reg, smp_processor_id());
+
+ if(!dbg_snapshot_get_enable("header"))
+ return 0;
+
+ if (!regs) {
+ asm("str x0, [%0, #0]\n\t"
+ "mov x0, %0\n\t"
+ "str x1, [x0, #8]\n\t"
+ "str x2, [x0, #16]\n\t"
+ "str x3, [x0, #24]\n\t"
+ "str x4, [x0, #32]\n\t"
+ "str x5, [x0, #40]\n\t"
+ "str x6, [x0, #48]\n\t"
+ "str x7, [x0, #56]\n\t"
+ "str x8, [x0, #64]\n\t"
+ "str x9, [x0, #72]\n\t"
+ "str x10, [x0, #80]\n\t"
+ "str x11, [x0, #88]\n\t"
+ "str x12, [x0, #96]\n\t"
+ "str x13, [x0, #104]\n\t"
+ "str x14, [x0, #112]\n\t"
+ "str x15, [x0, #120]\n\t"
+ "str x16, [x0, #128]\n\t"
+ "str x17, [x0, #136]\n\t"
+ "str x18, [x0, #144]\n\t"
+ "str x19, [x0, #152]\n\t"
+ "str x20, [x0, #160]\n\t"
+ "str x21, [x0, #168]\n\t"
+ "str x22, [x0, #176]\n\t"
+ "str x23, [x0, #184]\n\t"
+ "str x24, [x0, #192]\n\t"
+ "str x25, [x0, #200]\n\t"
+ "str x26, [x0, #208]\n\t"
+ "str x27, [x0, #216]\n\t"
+ "str x28, [x0, #224]\n\t"
+ "str x29, [x0, #232]\n\t"
+ "str x30, [x0, #240]\n\t" :
+ : "r"(core_reg));
+ core_reg->sp = core_reg->regs[29];
+ core_reg->pc =
+ (unsigned long)(core_reg->regs[30] - sizeof(unsigned int));
+ } else {
+ memcpy(core_reg, regs, sizeof(struct user_pt_regs));
+ }
+
+ pr_emerg("debug-snapshot: core register saved(CPU:%d)\n",
+ smp_processor_id());
+ return 0;
+}
+EXPORT_SYMBOL(dbg_snapshot_save_core);
+
+int dbg_snapshot_save_context(void *v_regs)
+{
+ int cpu;
+ unsigned long flags;
+ struct pt_regs *regs = (struct pt_regs *)v_regs;
+
+ if (unlikely(!dss_base.enabled))
+ return 0;
+
+ dss_soc_ops->soc_save_context_entry(NULL);
+
+ cpu = smp_processor_id();
+ raw_spin_lock_irqsave(&dss_desc.ctrl_lock, flags);
+
+ /* If it was already saved the context information, it should be skipped */
+ if (dbg_snapshot_get_core_panic_stat(cpu) != DSS_SIGN_PANIC) {
+ dbg_snapshot_save_system(NULL);
+ dbg_snapshot_save_core(regs);
+ dbg_snapshot_dump();
+ dbg_snapshot_set_core_panic_stat(DSS_SIGN_PANIC, cpu);
+ pr_emerg("debug-snapshot: context saved(CPU:%d)\n", cpu);
+ } else
+ pr_emerg("debug-snapshot: skip context saved(CPU:%d)\n", cpu);
+
+ flush_cache_all();
+ raw_spin_unlock_irqrestore(&dss_desc.ctrl_lock, flags);
+
+ dss_soc_ops->soc_save_context_exit(NULL);
+ return 0;
+}
+EXPORT_SYMBOL(dbg_snapshot_save_context);
+
+static void dbg_snapshot_dump_one_task_info(struct task_struct *tsk, bool is_main)
+{
+ char state_array[] = {'R', 'S', 'D', 'T', 't', 'Z', 'X', 'x', 'K', 'W'};
+ unsigned char idx = 0;
+ unsigned long state = (tsk->state & TASK_REPORT) | tsk->exit_state;
+ unsigned long wchan;
+ unsigned long pc = 0;
+ char symname[KSYM_NAME_LEN];
+
+ if ((tsk == NULL) || (tsk->stack == NULL))
+ return;
+
+ pc = KSTK_EIP(tsk);
+ wchan = get_wchan(tsk);
+ if (lookup_symbol_name(wchan, symname) < 0)
+ snprintf(symname, KSYM_NAME_LEN, "%lu", wchan);
+
+ while (state) {
+ idx++;
+ state >>= 1;
+ }
+
+ /*
+ * kick watchdog to prevent unexpected reset during panic sequence
+ * and it prevents the hang during panic sequence by watchedog
+ */
+ touch_softlockup_watchdog();
+ dss_soc_ops->soc_kick_watchdog(NULL);
+
+ pr_info("%8d %8d %8d %16lld %c(%d) %3d %16zx %16zx %16zx %c %16s [%s]\n",
+ tsk->pid, (int)(tsk->utime), (int)(tsk->stime),
+ tsk->se.exec_start, state_array[idx], (int)(tsk->state),
+ task_cpu(tsk), wchan, pc, (unsigned long)tsk,
+ is_main ? '*' : ' ', tsk->comm, symname);
+
+ if (tsk->state == TASK_RUNNING
+ || tsk->state == TASK_UNINTERRUPTIBLE
+ || tsk->mm == NULL) {
+ print_worker_info(KERN_INFO, tsk);
+ show_stack(tsk, NULL);
+ pr_info("\n");
+ }
+}
+
+static inline struct task_struct *get_next_thread(struct task_struct *tsk)
+{
+ return container_of(tsk->thread_group.next,
+ struct task_struct,
+ thread_group);
+}
+
+void dbg_snapshot_dump_task_info(void)
+{
+ struct task_struct *frst_tsk;
+ struct task_struct *curr_tsk;
+ struct task_struct *frst_thr;
+ struct task_struct *curr_thr;
+
+ pr_info("\n");
+ pr_info(" current proc : %d %s\n", current->pid, current->comm);
+ pr_info(" ----------------------------------------------------------------------------------------------------------------------------\n");
+ pr_info(" pid uTime sTime exec(ns) stat cpu wchan user_pc task_struct comm sym_wchan\n");
+ pr_info(" ----------------------------------------------------------------------------------------------------------------------------\n");
+
+ /* processes */
+ frst_tsk = &init_task;
+ curr_tsk = frst_tsk;
+ while (curr_tsk != NULL) {
+ dbg_snapshot_dump_one_task_info(curr_tsk, true);
+ /* threads */
+ if (curr_tsk->thread_group.next != NULL) {
+ frst_thr = get_next_thread(curr_tsk);
+ curr_thr = frst_thr;
+ if (frst_thr != curr_tsk) {
+ while (curr_thr != NULL) {
+ dbg_snapshot_dump_one_task_info(curr_thr, false);
+ curr_thr = get_next_thread(curr_thr);
+ if (curr_thr == curr_tsk)
+ break;
+ }
+ }
+ }
+ curr_tsk = container_of(curr_tsk->tasks.next,
+ struct task_struct, tasks);
+ if (curr_tsk == frst_tsk)
+ break;
+ }
+ pr_info(" ----------------------------------------------------------------------------------------------------------------------------\n");
+}
+
+#ifdef CONFIG_DEBUG_SNAPSHOT_CRASH_KEY
+void dbg_snapshot_check_crash_key(unsigned int code, int value)
+{
+ static bool volup_p;
+ static bool voldown_p;
+ static int loopcount;
+
+ static const unsigned int VOLUME_UP = KEY_VOLUMEUP;
+ static const unsigned int VOLUME_DOWN = KEY_VOLUMEDOWN;
+
+ if (code == KEY_POWER)
+ pr_info("debug-snapshot: POWER-KEY %s\n", value ? "pressed" : "released");
+
+ /* Enter Forced Upload
+ * Hold volume down key first
+ * and then press power key twice
+ * and volume up key should not be pressed
+ */
+ if (value) {
+ if (code == VOLUME_UP)
+ volup_p = true;
+ if (code == VOLUME_DOWN)
+ voldown_p = true;
+ if (!volup_p && voldown_p) {
+ if (code == KEY_POWER) {
+ pr_info
+ ("debug-snapshot: count for entering forced upload [%d]\n",
+ ++loopcount);
+ if (loopcount == 2) {
+ panic("Crash Key");
+ }
+ }
+ }
+ } else {
+ if (code == VOLUME_UP)
+ volup_p = false;
+ if (code == VOLUME_DOWN) {
+ loopcount = 0;
+ voldown_p = false;
+ }
+ }
+}
+#endif
+
+void __init dbg_snapshot_allcorelockup_detector_init(void)
+{
+ int ret;
+
+ if (!dss_desc.multistage_wdt_irq)
+ return;
+
+ dss_allcorelockup_param.last_pc_addr = dbg_snapshot_get_last_pc_paddr();
+ dss_allcorelockup_param.spin_pc_addr = __pa_symbol(dbg_snapshot_spin_func);
+
+ __flush_dcache_area((void *)&dss_allcorelockup_param,
+ sizeof(struct dbg_snapshot_allcorelockup_param));
+
+#ifdef SMC_CMD_LOCKUP_NOTICE
+ /* Setup for generating NMI interrupt to unstopped CPUs */
+ ret = dss_soc_ops->soc_smc_call(SMC_CMD_LOCKUP_NOTICE,
+ (unsigned long)dbg_snapshot_bug_func,
+ dss_desc.multistage_wdt_irq,
+ (unsigned long)(virt_to_phys)(&dss_allcorelockup_param));
+#endif
+
+ pr_emerg("debug-snapshot: %s to register all-core lockup detector - ret: %d\n",
+ ret == 0 ? "success" : "failed", ret);
+}
+
+void __init dbg_snapshot_utils_init(void)
+{
+ size_t vaddr;
+ int i;
+
+ vaddr = dss_items[dss_desc.header_num].entry.vaddr;
+
+ for (i = 0; i < DSS_NR_CPUS; i++) {
+ per_cpu(dss_mmu_reg, i) = (struct dbg_snapshot_mmu_reg *)
+ (vaddr + DSS_HEADER_SZ +
+ i * DSS_MMU_REG_OFFSET);
+ per_cpu(dss_core_reg, i) = (struct pt_regs *)
+ (vaddr + DSS_HEADER_SZ + DSS_MMU_REG_SZ +
+ i * DSS_CORE_REG_OFFSET);
+ }
+
+ /* hardlockup_detector function should be called before secondary booting */
+ dbg_snapshot_allcorelockup_detector_init();
+}
+
+static int __init dbg_snapshot_utils_save_systems_all(void)
+{
+ smp_call_function(dbg_snapshot_save_system, NULL, 1);
+ dbg_snapshot_save_system(NULL);
+
+ return 0;
+}
+postcore_initcall(dbg_snapshot_utils_save_systems_all);
--- /dev/null
+/*
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Debug-SnapShot: Debug Framework for Ramdump based debugging method
+ * The original code is Exynos-Snapshot for Exynos SoC
+ *
+ * Author: Hosung Kim <hosung0.kim@samsung.com>
+ * Author: Changki Kim <changki.kim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/bootmem.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#include <linux/memblock.h>
+#include <linux/of_address.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/sched/clock.h>
+
+#include "debug-snapshot-local.h"
+
+extern void register_hook_logbuf(void (*)(const char *, size_t));
+extern void register_hook_logger(void (*)(const char *, const char *, size_t));
+
+struct dbg_snapshot_interface {
+ struct dbg_snapshot_log *info_event;
+ struct dbg_snapshot_item info_log[DSS_ITEM_MAX_NUM];
+};
+
+#ifdef CONFIG_DEBUG_SNAPSHOT_PMU
+struct dbg_snapshot_ops dss_ops = {
+ .pd_status = cal_pd_status,
+};
+#endif
+
+struct dbg_snapshot_item dss_items[] = {
+ {"header", {0, 0, 0, false, false}, NULL ,NULL, 0, },
+ {"log_kernel", {0, 0, 0, false, false}, NULL ,NULL, 0, },
+ {"log_platform", {0, 0, 0, false, false}, NULL ,NULL, 0, },
+ {"log_sfr", {0, 0, 0, false, false}, NULL ,NULL, 0, },
+ {"log_s2d", {0, 0, 0, true, false}, NULL, NULL, 0, },
+ {"log_cachedump", {0, 0, 0, true, false}, NULL, NULL, 0, },
+ {"log_etm", {0, 0, 0, true, false}, NULL ,NULL, 0, },
+ {"log_bcm", {0, 0, 0, false, false}, NULL ,NULL, 0, },
+ {"log_pstore", {0, 0, 0, true, false}, NULL ,NULL, 0, },
+ {"log_kevents", {0, 0, 0, false, false}, NULL ,NULL, 0, },
+};
+
+/* External interface variable for trace debugging */
+static struct dbg_snapshot_interface dss_info __attribute__ ((used));
+static struct dbg_snapshot_interface *ess_info __attribute__ ((used));
+
+struct dbg_snapshot_base dss_base;
+struct dbg_snapshot_base ess_base;
+struct dbg_snapshot_log *dss_log = NULL;
+struct dbg_snapshot_desc dss_desc;
+
+/* Variable for assigning virtual address base */
+static size_t g_dbg_snapshot_vaddr_base = DSS_FIXED_VIRT_BASE;
+
+int dbg_snapshot_set_enable(const char *name, int en)
+{
+ struct dbg_snapshot_item *item = NULL;
+ unsigned long i;
+
+ if (!strncmp(name, "base", strlen(name))) {
+ dss_base.enabled = en;
+ pr_info("debug-snapshot: %sabled\n", en ? "en" : "dis");
+ } else {
+ for (i = 0; i < ARRAY_SIZE(dss_items); i++) {
+ if (!strncmp(dss_items[i].name, name, strlen(name))) {
+ item = &dss_items[i];
+ item->entry.enabled = en;
+ item->time = local_clock();
+ pr_info("debug-snapshot: item - %s is %sabled\n",
+ name, en ? "en" : "dis");
+ break;
+ }
+ }
+ }
+ return 0;
+}
+EXPORT_SYMBOL(dbg_snapshot_set_enable);
+
+int dbg_snapshot_try_enable(const char *name, unsigned long long duration)
+{
+ struct dbg_snapshot_item *item = NULL;
+ unsigned long long time;
+ unsigned long i;
+ int ret = -1;
+
+ /* If DSS was disabled, just return */
+ if (unlikely(!dss_base.enabled) || !dbg_snapshot_get_enable("header"))
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(dss_items); i++) {
+ if (!strncmp(dss_items[i].name, name, strlen(name))) {
+ item = &dss_items[i];
+
+ /* We only interest in disabled */
+ if (!item->entry.enabled) {
+ time = local_clock() - item->time;
+ if (time > duration) {
+ item->entry.enabled = true;
+ ret = 1;
+ } else
+ ret = 0;
+ }
+ break;
+ }
+ }
+ return ret;
+}
+EXPORT_SYMBOL(dbg_snapshot_try_enable);
+
+int dbg_snapshot_get_enable(const char *name)
+{
+ struct dbg_snapshot_item *item = NULL;
+ unsigned long i;
+ int ret = 0;
+
+ if (!strncmp(name, "base", strlen(name)))
+ return dss_base.enabled;
+
+ for (i = 0; i < ARRAY_SIZE(dss_items); i++) {
+ if (!strncmp(dss_items[i].name, name, strlen(name))) {
+ item = &dss_items[i];
+ ret = item->entry.enabled;
+ break;
+ }
+ }
+ return ret;
+}
+EXPORT_SYMBOL(dbg_snapshot_get_enable);
+
+static inline int dbg_snapshot_check_eob(struct dbg_snapshot_item *item,
+ size_t size)
+{
+ size_t max, cur;
+
+ max = (size_t)(item->head_ptr + item->entry.size);
+ cur = (size_t)(item->curr_ptr + size);
+
+ if (unlikely(cur > max))
+ return -1;
+ else
+ return 0;
+}
+
+static inline void dbg_snapshot_hook_logger(const char *name,
+ const char *buf, size_t size)
+{
+ struct dbg_snapshot_item *item = &dss_items[dss_desc.log_platform_num];
+
+ if (likely(dss_base.enabled && item->entry.enabled)) {
+ if (unlikely((dbg_snapshot_check_eob(item, size))))
+ item->curr_ptr = item->head_ptr;
+ memcpy(item->curr_ptr, buf, size);
+ item->curr_ptr += size;
+ }
+}
+
+static inline void dbg_snapshot_hook_logbuf(const char *buf, size_t size)
+{
+ struct dbg_snapshot_item *item = &dss_items[dss_desc.log_kernel_num];
+
+ if (likely(dss_base.enabled && item->entry.enabled)) {
+ size_t last_buf;
+
+ if (dbg_snapshot_check_eob(item, size))
+ item->curr_ptr = item->head_ptr;
+
+ memcpy(item->curr_ptr, buf, size);
+ item->curr_ptr += size;
+ /* save the address of last_buf to physical address */
+ last_buf = (size_t)item->curr_ptr;
+
+ __raw_writel(item->entry.paddr + (last_buf - item->entry.vaddr),
+ dbg_snapshot_get_base_vaddr() + DSS_OFFSET_LAST_LOGBUF);
+ }
+}
+
+#ifdef CONFIG_DEBUG_SNAPSHOT_PMU
+static bool dbg_snapshot_check_pmu(struct dbg_snapshot_sfrdump *sfrdump,
+ const struct device_node *np)
+{
+ int ret = 0, count, i;
+ unsigned int val;
+
+ if (!sfrdump->pwr_mode)
+ return true;
+
+ count = of_property_count_u32_elems(np, "cal-pd-id");
+ for (i = 0; i < count; i++) {
+ ret = of_property_read_u32_index(np, "cal-pd-id", i, &val);
+ if (ret < 0) {
+ pr_err("failed to get pd-id - %s\n", sfrdump->name);
+ return false;
+ }
+ ret = dss_ops.pd_status(val);
+ if (ret < 0) {
+ pr_err("not powered - %s (pd-id: %d)\n", sfrdump->name, i);
+ return false;
+ }
+ }
+ return true;
+}
+
+void dbg_snapshot_dump_sfr(void)
+{
+ struct dbg_snapshot_sfrdump *sfrdump;
+ struct dbg_snapshot_item *item = &dss_items[dss_desc.log_sfr_num];
+ struct list_head *entry;
+ struct device_node *np;
+ unsigned int reg, offset, val, size;
+ int i, ret;
+ static char buf[SZ_64];
+
+ if (unlikely(!dss_base.enabled || !item->entry.enabled))
+ return;
+
+ if (list_empty(&dss_desc.sfrdump_list)) {
+ pr_emerg("debug-snapshot: %s: No information\n", __func__);
+ return;
+ }
+
+ list_for_each(entry, &dss_desc.sfrdump_list) {
+ sfrdump = list_entry(entry, struct dbg_snapshot_sfrdump, list);
+ np = of_node_get(sfrdump->node);
+ ret = dbg_snapshot_check_pmu(sfrdump, np);
+ if (!ret)
+ /* may off */
+ continue;
+
+ for (i = 0; i < sfrdump->num; i++) {
+ ret = of_property_read_u32_index(np, "addr", i, ®);
+ if (ret < 0) {
+ pr_err("debug-snapshot: failed to get address information - %s\n",
+ sfrdump->name);
+ break;
+ }
+ if (reg == 0xFFFFFFFF || reg == 0)
+ break;
+ offset = reg - sfrdump->phy_reg;
+ if (reg < offset) {
+ pr_err("debug-snapshot: invalid address information - %s: 0x%08x\n",
+ sfrdump->name, reg);
+ break;
+ }
+ val = __raw_readl(sfrdump->reg + offset);
+ snprintf(buf, SZ_64, "0x%X = 0x%0X\n",reg, val);
+ size = (unsigned int)strlen(buf);
+ if (unlikely((dbg_snapshot_check_eob(item, size))))
+ item->curr_ptr = item->head_ptr;
+ memcpy(item->curr_ptr, buf, strlen(buf));
+ item->curr_ptr += strlen(buf);
+ }
+ of_node_put(np);
+ pr_info("debug-snapshot: complete to dump %s\n", sfrdump->name);
+ }
+
+}
+
+static int dbg_snapshot_sfr_dump_init(struct device_node *np)
+{
+ struct device_node *dump_np;
+ struct dbg_snapshot_sfrdump *sfrdump;
+ char *dump_str;
+ int count, ret, i;
+ u32 phy_regs[2];
+
+ ret = of_property_count_strings(np, "sfr-dump-list");
+ if (ret < 0) {
+ pr_err("failed to get sfr-dump-list\n");
+ return ret;
+ }
+ count = ret;
+
+ INIT_LIST_HEAD(&dss_desc.sfrdump_list);
+ for (i = 0; i < count; i++) {
+ ret = of_property_read_string_index(np, "sfr-dump-list", i,
+ (const char **)&dump_str);
+ if (ret < 0) {
+ pr_err("failed to get sfr-dump-list\n");
+ continue;
+ }
+
+ dump_np = of_get_child_by_name(np, dump_str);
+ if (!dump_np) {
+ pr_err("failed to get %s node, count:%d\n", dump_str, count);
+ continue;
+ }
+
+ sfrdump = kzalloc(sizeof(struct dbg_snapshot_sfrdump), GFP_KERNEL);
+ if (!sfrdump) {
+ pr_err("failed to get memory region of dbg_snapshot_sfrdump\n");
+ of_node_put(dump_np);
+ continue;
+ }
+
+ ret = of_property_read_u32_array(dump_np, "reg", phy_regs, 2);
+ if (ret < 0) {
+ pr_err("failed to get register information\n");
+ of_node_put(dump_np);
+ kfree(sfrdump);
+ continue;
+ }
+
+ sfrdump->reg = ioremap(phy_regs[0], phy_regs[1]);
+ if (!sfrdump->reg) {
+ pr_err("failed to get i/o address %s node\n", dump_str);
+ of_node_put(dump_np);
+ kfree(sfrdump);
+ continue;
+ }
+ sfrdump->name = dump_str;
+
+ ret = of_property_count_u32_elems(dump_np, "addr");
+ if (ret < 0) {
+ pr_err("failed to get addr count\n");
+ of_node_put(dump_np);
+ kfree(sfrdump);
+ continue;
+ }
+ sfrdump->phy_reg = phy_regs[0];
+ sfrdump->num = ret;
+
+ ret = of_property_count_u32_elems(dump_np, "cal-pd-id");
+ if (ret < 0)
+ sfrdump->pwr_mode = false;
+ else
+ sfrdump->pwr_mode = true;
+
+ sfrdump->node = dump_np;
+ list_add(&sfrdump->list, &dss_desc.sfrdump_list);
+
+ pr_info("success to regsiter %s\n", sfrdump->name);
+ of_node_put(dump_np);
+ ret = 0;
+ }
+ return ret;
+}
+#endif
+
+static int __init dbg_snapshot_remap(void)
+{
+ unsigned long i, j;
+ unsigned int enabled_count = 0;
+ pgprot_t prot = __pgprot(PROT_NORMAL_NC);
+ int page_size, ret;
+ struct page *page;
+ struct page **pages;
+
+ for (i = 0; i < ARRAY_SIZE(dss_items); i++) {
+ if (dss_items[i].entry.enabled) {
+ enabled_count++;
+ page_size = dss_items[i].entry.size / PAGE_SIZE;
+ pages = kzalloc(sizeof(struct page *) * page_size, GFP_KERNEL);
+ page = phys_to_page(dss_items[i].entry.paddr);
+
+ for (j = 0; j < page_size; j++)
+ pages[j] = page++;
+
+ ret = map_vm_area(&dss_items[i].vm, prot, pages);
+ kfree(pages);
+ if (ret) {
+ pr_err("debug-snapshot: failed to mapping between virt and phys");
+ return -ENOMEM;
+ }
+
+ dss_items[i].entry.vaddr = (size_t)dss_items[i].vm.addr;
+ dss_items[i].head_ptr = (unsigned char *)dss_items[i].entry.vaddr;
+ dss_items[i].curr_ptr = (unsigned char *)dss_items[i].entry.vaddr;
+ }
+ }
+ dss_desc.log_enable_cnt = enabled_count;
+ return enabled_count;
+}
+
+static int __init dbg_snapshot_init_desc(void)
+{
+ unsigned int i, len;
+
+ /* initialize dss_desc */
+ memset((struct dbg_snapshot_desc *)&dss_desc, 0, sizeof(struct dbg_snapshot_desc));
+ dss_desc.callstack = CONFIG_DEBUG_SNAPSHOT_CALLSTACK;
+ raw_spin_lock_init(&dss_desc.ctrl_lock);
+ raw_spin_lock_init(&dss_desc.nmi_lock);
+
+ for (i = 0; i < (unsigned int)ARRAY_SIZE(dss_items); i++) {
+ len = strlen(dss_items[i].name);
+ if (!strncmp(dss_items[i].name, "header", len))
+ dss_desc.header_num = i;
+ else if (!strncmp(dss_items[i].name, "log_kevents", len))
+ dss_desc.kevents_num = i;
+ else if (!strncmp(dss_items[i].name, "log_kernel", len))
+ dss_desc.log_kernel_num = i;
+ else if (!strncmp(dss_items[i].name, "log_platform", len))
+ dss_desc.log_platform_num = i;
+ else if (!strncmp(dss_items[i].name, "log_sfr", len))
+ dss_desc.log_sfr_num = i;
+ else if (!strncmp(dss_items[i].name, "log_pstore", len))
+ dss_desc.log_pstore_num = i;
+ }
+
+#ifdef CONFIG_S3C2410_WATCHDOG
+ dss_desc.no_wdt_dev = false;
+#else
+ dss_desc.no_wdt_dev = true;
+#endif
+ return 0;
+}
+
+#ifdef CONFIG_OF_RESERVED_MEM
+static int __init dbg_snapshot_item_reserved_mem_setup(struct reserved_mem *remem)
+{
+ unsigned int i;
+
+ for (i = 0; i < (unsigned int)ARRAY_SIZE(dss_items); i++) {
+ if (strnstr(remem->name, dss_items[i].name, strlen(remem->name)))
+ break;
+ }
+
+ if (i == ARRAY_SIZE(dss_items))
+ return -ENODEV;
+
+ dss_items[i].entry.paddr = remem->base;
+ dss_items[i].entry.size = remem->size;
+ dss_items[i].entry.enabled = true;
+
+ dss_items[i].vm.phys_addr = remem->base;
+ dss_items[i].vm.addr = (void *)g_dbg_snapshot_vaddr_base;
+ dss_items[i].vm.size = remem->size;
+ dss_items[i].vm.flags = VM_NO_GUARD;
+ g_dbg_snapshot_vaddr_base += remem->size;
+
+ vm_area_add_early(&dss_items[i].vm);
+
+ if (strnstr(remem->name, "header", strlen(remem->name))) {
+ dss_base.paddr = remem->base;
+ dss_base.vaddr = (size_t)dss_items[i].vm.addr;
+ ess_base = dss_base;
+ dss_base.enabled = false;
+ }
+ dss_base.size += remem->size;
+ return 0;
+}
+
+#define DECLARE_DBG_SNAPSHOT_RESERVED_REGION(compat, name) \
+RESERVEDMEM_OF_DECLARE(name, compat#name, dbg_snapshot_item_reserved_mem_setup)
+
+DECLARE_DBG_SNAPSHOT_RESERVED_REGION("debug-snapshot,", header);
+DECLARE_DBG_SNAPSHOT_RESERVED_REGION("debug-snapshot,", log_kernel);
+DECLARE_DBG_SNAPSHOT_RESERVED_REGION("debug-snapshot,", log_platform);
+DECLARE_DBG_SNAPSHOT_RESERVED_REGION("debug-snapshot,", log_sfr);
+DECLARE_DBG_SNAPSHOT_RESERVED_REGION("debug-snapshot,", log_s2d);
+DECLARE_DBG_SNAPSHOT_RESERVED_REGION("debug-snapshot,", log_cachedump);
+DECLARE_DBG_SNAPSHOT_RESERVED_REGION("debug-snapshot,", log_etm);
+DECLARE_DBG_SNAPSHOT_RESERVED_REGION("debug-snapshot,", log_bcm);
+DECLARE_DBG_SNAPSHOT_RESERVED_REGION("debug-snapshot,", log_pstore);
+DECLARE_DBG_SNAPSHOT_RESERVED_REGION("debug-snapshot,", log_kevents);
+#endif
+
+/*
+ * ---------------------------------------------------------------------
+ * - dummy data:phy_addr, virtual_addr, buffer_size, magic_key(4K) -
+ * ---------------------------------------------------------------------
+ * - Cores MMU register(4K) -
+ * ---------------------------------------------------------------------
+ * - Cores CPU register(4K) -
+ * ---------------------------------------------------------------------
+ */
+static int __init dbg_snapshot_output(void)
+{
+ unsigned long i, size = 0;
+
+ pr_info("debug-snapshot physical / virtual memory layout:\n");
+ for (i = 0; i < ARRAY_SIZE(dss_items); i++) {
+ if (dss_items[i].entry.enabled)
+ pr_info("%-12s: phys:0x%zx / virt:0x%zx / size:0x%zx\n",
+ dss_items[i].name,
+ dss_items[i].entry.paddr,
+ dss_items[i].entry.vaddr,
+ dss_items[i].entry.size);
+ size += dss_items[i].entry.size;
+ }
+
+ pr_info("total_item_size: %ldKB, dbg_snapshot_log struct size: %dKB\n",
+ size / SZ_1K, dbg_snapshot_log_size / SZ_1K);
+
+ return 0;
+}
+
+/* Header dummy data(4K)
+ * -------------------------------------------------------------------------
+ * 0 4 8 C
+ * -------------------------------------------------------------------------
+ * 0 vaddr phy_addr size magic_code
+ * 4 Scratch_val logbuf_addr 0 0
+ * -------------------------------------------------------------------------
+*/
+
+static void __init dbg_snapshot_fixmap_header(void)
+{
+ /* fill 0 to next to header */
+ size_t vaddr, paddr, size;
+ size_t *addr;
+
+ vaddr = dss_items[dss_desc.header_num].entry.vaddr;
+ paddr = dss_items[dss_desc.header_num].entry.paddr;
+ size = dss_items[dss_desc.header_num].entry.size;
+
+ /* set to confirm debug-snapshot */
+ addr = (size_t *)vaddr;
+ memcpy(addr, &dss_base, sizeof(struct dbg_snapshot_base));
+
+ if (!dbg_snapshot_get_enable("header"))
+ return;
+
+ /* initialize kernel event to 0 except only header */
+ memset((size_t *)(vaddr + DSS_KEEP_HEADER_SZ), 0, size - DSS_KEEP_HEADER_SZ);
+}
+
+static void __init dbg_snapshot_fixmap(void)
+{
+ size_t last_buf;
+ size_t vaddr, paddr, size;
+ unsigned long i;
+
+ /* fixmap to header first */
+ dbg_snapshot_fixmap_header();
+
+ for (i = 1; i < ARRAY_SIZE(dss_items); i++) {
+ if (!dss_items[i].entry.enabled)
+ continue;
+
+ /* assign dss_item information */
+ paddr = dss_items[i].entry.paddr;
+ vaddr = dss_items[i].entry.vaddr;
+ size = dss_items[i].entry.size;
+
+ if (i == dss_desc.log_kernel_num) {
+ /* load last_buf address value(phy) by virt address */
+ last_buf = (size_t)__raw_readl(dbg_snapshot_get_base_vaddr() +
+ DSS_OFFSET_LAST_LOGBUF);
+ /* check physical address offset of kernel logbuf */
+ if (last_buf >= dss_items[i].entry.paddr &&
+ (last_buf) <= (dss_items[i].entry.paddr + dss_items[i].entry.size)) {
+ /* assumed valid address, conversion to virt */
+ dss_items[i].curr_ptr = (unsigned char *)(dss_items[i].entry.vaddr +
+ (last_buf - dss_items[i].entry.paddr));
+ } else {
+ /* invalid address, set to first line */
+ dss_items[i].curr_ptr = (unsigned char *)vaddr;
+ /* initialize logbuf to 0 */
+ memset((size_t *)vaddr, 0, size);
+ }
+ } else {
+ /* initialized log to 0 if persist == false */
+ if (!dss_items[i].entry.persist)
+ memset((size_t *)vaddr, 0, size);
+ }
+ dss_info.info_log[i - 1].name = kstrdup(dss_items[i].name, GFP_KERNEL);
+ dss_info.info_log[i - 1].head_ptr = (unsigned char *)dss_items[i].entry.vaddr;
+ dss_info.info_log[i - 1].curr_ptr = NULL;
+ dss_info.info_log[i - 1].entry.size = size;
+ }
+
+ dss_log = (struct dbg_snapshot_log *)(dss_items[dss_desc.kevents_num].entry.vaddr);
+
+ /* set fake translation to virtual address to debug trace */
+ dss_info.info_event = dss_log;
+ ess_info = &dss_info;
+
+ /* output the information of debug-snapshot */
+ dbg_snapshot_output();
+}
+
+static int dbg_snapshot_init_dt_parse(struct device_node *np)
+{
+ int ret = 0;
+ struct device_node *sfr_dump_np = of_get_child_by_name(np, "dump-info");
+
+ if (!sfr_dump_np) {
+ pr_err("debug-snapshot: failed to get dump-info node\n");
+ ret = -ENODEV;
+ } else {
+#ifdef CONFIG_DEBUG_SNAPSHOT_PMU
+ ret = dbg_snapshot_sfr_dump_init(sfr_dump_np);
+ if (ret < 0) {
+ pr_err("debug-snapshot: failed to register sfr dump node\n");
+ ret = -ENODEV;
+ of_node_put(sfr_dump_np);
+ }
+#endif
+ }
+ if (ret < 0)
+ dbg_snapshot_set_enable("log_sfr", false);
+
+ if (of_property_read_u32(np, "use_multistage_wdt_irq",
+ &dss_desc.multistage_wdt_irq)) {
+ dss_desc.multistage_wdt_irq = 0;
+ pr_err("debug-snapshot: no support multistage_wdt\n");
+ ret = -EINVAL;
+ }
+
+ of_node_put(np);
+ return ret;
+}
+
+static const struct of_device_id dss_of_match[] __initconst = {
+ { .compatible = "debug-snapshot-soc",
+ .data = dbg_snapshot_init_dt_parse},
+ {},
+};
+
+static int __init dbg_snapshot_init_dt(void)
+{
+ struct device_node *np;
+ const struct of_device_id *matched_np;
+ dss_initcall_t init_fn;
+
+ np = of_find_matching_node_and_match(NULL, dss_of_match, &matched_np);
+
+ if (!np) {
+ pr_info("debug-snapshot: couldn't find device tree file of debug-snapshot\n");
+ dbg_snapshot_set_enable("log_sfr", false);
+ return -ENODEV;
+ }
+
+ init_fn = (dss_initcall_t)matched_np->data;
+ return init_fn(np);
+}
+
+static int __init dbg_snapshot_init(void)
+{
+ dbg_snapshot_init_desc();
+ if (dbg_snapshot_remap() > 0) {
+ /*
+ * for debugging when we don't know the virtual address of pointer,
+ * In just privous the debug buffer, It is added 16byte dummy data.
+ * start address(dummy 16bytes)
+ * --> @virtual_addr | @phy_addr | @buffer_size | @magic_key(0xDBDBDBDB)
+ * And then, the debug buffer is shown.
+ */
+ dbg_snapshot_log_idx_init();
+ dbg_snapshot_fixmap();
+ dbg_snapshot_init_dt();
+ dbg_snapshot_helper_init();
+ dbg_snapshot_utils_init();
+
+ dbg_snapshot_scratch_reg(DSS_SIGN_SCRATCH);
+ dbg_snapshot_set_enable("base", true);
+
+ register_hook_logbuf(dbg_snapshot_hook_logbuf);
+ register_hook_logger(dbg_snapshot_hook_logger);
+ } else
+ pr_err("debug-snapshot: %s failed\n", __func__);
+
+ return 0;
+}
+early_initcall(dbg_snapshot_init);