[COMMON] lib: dss: add to support Debug-SnapShot(DSS)
authorDonghyeok Choe <d7271.choe@samsung.com>
Sun, 13 May 2018 06:21:23 +0000 (15:21 +0900)
committerCosmin Tanislav <demonsingur@gmail.com>
Mon, 22 Apr 2024 17:21:55 +0000 (20:21 +0300)
Change-Id: I28ab91356cc2852a38ee6b7c08d1bcb837d6e8ca
Signed-off-by: Donghyeok Choe <d7271.choe@samsung.com>
36 files changed:
arch/arm64/include/asm/core_regs.h [new file with mode: 0644]
arch/arm64/include/asm/cputype.h
arch/arm64/include/asm/io.h
arch/arm64/include/asm/irqflags.h
arch/arm64/kernel/process.c
arch/arm64/kernel/smp.c
arch/arm64/kernel/traps.c
arch/arm64/mm/fault.c
drivers/cpuidle/cpuidle.c
drivers/input/keyboard/gpio_keys.c
drivers/scsi/ufs/ufs-exynos-dbg.c
drivers/soc/samsung/debug/exynos-helper.c [new file with mode: 0644]
drivers/watchdog/s3c2410_wdt.c
include/linux/debug-snapshot.h
include/soc/samsung/exynos-debug.h [new file with mode: 0644]
include/soc/samsung/exynos-sdm.h [new file with mode: 0644]
kernel/irq/handle.c
kernel/irq/irqdesc.c
kernel/panic.c
kernel/printk/printk.c
kernel/rcu/tree.c
kernel/sched/core.c
kernel/softirq.c
kernel/time/clockevents.c
kernel/time/hrtimer.c
kernel/time/timer.c
kernel/workqueue.c
lib/Kconfig.debug
lib/Makefile
lib/debug-snapshot-helper.c [new file with mode: 0644]
lib/debug-snapshot-local.h [new file with mode: 0644]
lib/debug-snapshot-log.c [new file with mode: 0644]
lib/debug-snapshot-pstore.c [new file with mode: 0644]
lib/debug-snapshot-sysfs.c [new file with mode: 0644]
lib/debug-snapshot-utils.c [new file with mode: 0644]
lib/debug-snapshot.c [new file with mode: 0644]

diff --git a/arch/arm64/include/asm/core_regs.h b/arch/arm64/include/asm/core_regs.h
new file mode 100644 (file)
index 0000000..8630dbf
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * 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
index 401088d9cd827adad4c23a5137c5e5e5438c5985..dce7626829a27242598d28a6c0fc67ec1f232eb9 100644 (file)
@@ -75,6 +75,7 @@
 #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
@@ -97,6 +98,9 @@
 #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
 
index 49bb9a020a09aeba74c0eacf0e43ca668ed4339a..7a63d3f7f6413670de4e65c03858bc539171ea4f 100644 (file)
@@ -22,6 +22,8 @@
 #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;
 }
 
@@ -76,10 +89,12 @@ static inline u16 __raw_readw(const volatile void __iomem *addr)
 {
        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;
 }
 
@@ -87,10 +102,13 @@ static inline u16 __raw_readw(const volatile void __iomem *addr)
 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;
 }
 
@@ -98,10 +116,13 @@ static inline u32 __raw_readl(const volatile void __iomem *addr)
 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;
 }
 
index 8c581281fa12ca350ee0a3cb478211f0d03f0f4c..39cce7d84c7f5ed17eeef2570e12b1bd0219455b 100644 (file)
@@ -19,6 +19,7 @@
 #ifdef __KERNEL__
 
 #include <asm/ptrace.h>
+#include <linux/debug-snapshot.h>
 
 /*
  * CPU interrupt mask handling.
@@ -32,11 +33,13 @@ static inline unsigned long arch_local_irq_save(void)
                : "=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"
                :
@@ -51,6 +54,7 @@ static inline void arch_local_irq_disable(void)
                :
                :
                : "memory");
+       dbg_snapshot_irqs_disabled(0);
 }
 
 #define local_fiq_enable()     asm("msr        daifclr, #1" : : : "memory")
@@ -78,6 +82,7 @@ static inline unsigned long arch_local_save_flags(void)
  */
 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"
        :
index 8008b25ffa9e18c14fff43b60a879e7eebdacfd9..334f33513a44b2e16687c70de859bec45afab87e 100644 (file)
@@ -47,6 +47,7 @@
 #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>
 
@@ -157,6 +158,8 @@ void machine_restart(char *cmd)
        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);
@@ -249,6 +252,16 @@ void __show_regs(struct pt_regs *regs)
                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);
index cfb5a6ad78653f2282aba525af3c0bbeb8ce3062..93f224eeb122ed35ccf44ebe759ad27f67239931 100644 (file)
@@ -40,6 +40,7 @@
 #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>
@@ -795,12 +796,14 @@ void arch_irq_work_raise(void)
 /*
  * 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();
 }
@@ -841,6 +844,8 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
                __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();
@@ -854,7 +859,7 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
 
        case IPI_CPU_STOP:
                irq_enter();
-               ipi_cpu_stop(cpu);
+               ipi_cpu_stop(cpu, regs);
                irq_exit();
                break;
 
@@ -898,6 +903,9 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
 
        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);
 }
 
index ee19e6463e5a4d9219a9bd515ec51a6a88b427d2..9bb27278ab43f7f72cd8c58370df22a1ea23eba8 100644 (file)
@@ -35,6 +35,7 @@
 #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>
index d191b046d4c186255a2926addc8c1749b981d983..bf3e581e7f585e9f4630fe14f4e31f7f710be642 100644 (file)
@@ -32,6 +32,7 @@
 #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>
index ecf18feff16b35fed28ec1e3b0dc4b2768583b05..4f7078f00ac5ba183660b5a080bb289c6ceb2ec7 100644 (file)
@@ -216,6 +216,7 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
        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();
@@ -224,6 +225,8 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
 
        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. */
index e9f0ebf3267add21d8c372a7853312fa1998a7e6..39b91d6dfb5cfe500fb71b0457b6d81bf98c5f4c 100644 (file)
@@ -30,6 +30,7 @@
 #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;
@@ -371,6 +372,8 @@ static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
                return;
        }
 
+       dbg_snapshot_check_crash_key(button->code, state);
+
        if (type == EV_ABS) {
                if (state)
                        input_event(input, type, button->code, button->value);
index 1d7de9973abcc2275bce1196ad2e3c79411c14c5..a23e2a4fe87024097c67bcb0d828a632597ffbf7 100644 (file)
@@ -9,6 +9,7 @@
 
 #include <linux/clk.h>
 #include <linux/smc.h>
+#include <linux/debug-snapshot.h>
 
 #include "ufshcd.h"
 #include "unipro.h"
diff --git a/drivers/soc/samsung/debug/exynos-helper.c b/drivers/soc/samsung/debug/exynos-helper.c
new file mode 100644 (file)
index 0000000..c7ce6b4
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * 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);
+}
index b327c423357f9ae794653e4fcdaba455b0b39972..e53f4af3e0ea09d063a69945c6b4ca743fd408a0 100644 (file)
@@ -39,6 +39,7 @@
 #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
index 468c40861e93bb810c4c4e668d517dcc0bf95999..8869bbfcd6c2293d3355c8e3a71e792ae49b16fb 100644 (file)
@@ -161,6 +161,16 @@ void dbg_snapshot_check_crash_key(unsigned int code, int value);
 #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)
diff --git a/include/soc/samsung/exynos-debug.h b/include/soc/samsung/exynos-debug.h
new file mode 100644 (file)
index 0000000..aaaca6f
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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
+
+
diff --git a/include/soc/samsung/exynos-sdm.h b/include/soc/samsung/exynos-sdm.h
new file mode 100644 (file)
index 0000000..86d2696
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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 */
index 1bc4dcc489d0b32c6fed28459aa9e79e171c65a0..312f35b5260f3d7fb0e688436107892eea24abb2 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/sched.h>
 #include <linux/interrupt.h>
 #include <linux/kernel_stat.h>
+#include <linux/debug-snapshot.h>
 
 #include <trace/events/irq.h>
 
@@ -144,7 +145,9 @@ irqreturn_t __handle_irq_event_percpu(struct irq_desc *desc, unsigned int *flags
                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",
index 92784b29056492613212af1ab12bda971c15489f..a7bda29ba4c6d58f093b807edd80182ede1f27a8 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/bitmap.h>
 #include <linux/irqdomain.h>
 #include <linux/sysfs.h>
+#include <linux/debug-snapshot.h>
 
 #include "internals.h"
 
@@ -636,9 +637,11 @@ int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
                        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
@@ -658,6 +661,7 @@ int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
        }
 
        irq_exit();
+       dbg_snapshot_irq_exit(irq, start_time);
        set_irq_regs(old_regs);
        return ret;
 }
index 41b9b6028a46914246a67892b1f63ab44d8b8577..286b99c687a5b6c67195856ad1d8bd43e5d3f96e 100644 (file)
@@ -29,6 +29,7 @@
 #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
@@ -230,8 +231,9 @@ void panic(const char *fmt, ...)
        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);
@@ -239,6 +241,9 @@ void panic(const char *fmt, ...)
        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
@@ -246,6 +251,7 @@ void panic(const char *fmt, ...)
        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
@@ -284,6 +290,8 @@ void panic(const char *fmt, ...)
        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
index 1f51b29bdb155f3230e8846892ceac49e91467e8..5a3bb51dfbe2c6192672aa9b56b89fc4e5f0714e 100644 (file)
@@ -589,7 +589,7 @@ static size_t print_process(const struct printk_log *msg, char *buf)
 #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);
@@ -713,7 +713,7 @@ static int log_store(int facility, int level,
                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);
index fb051fa99b67acd716c6902e4c20117843443d59..b64605215bfd66d012eb1c816f6b9f0131eee2ea 100644 (file)
@@ -58,6 +58,7 @@
 #include <linux/trace_events.h>
 #include <linux/suspend.h>
 #include <linux/ftrace.h>
+#include <linux/debug-snapshot.h>
 
 #include "tree.h"
 #include "rcu.h"
@@ -1458,6 +1459,8 @@ static void print_other_cpu_stall(struct rcu_state *rsp, unsigned long gpnum)
         * 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();
@@ -1526,6 +1529,8 @@ static void print_cpu_stall(struct rcu_state *rsp)
         * 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());
index 45eb7ea8f61cbcecf67949cfeb46238ccfc94cfd..51b15b32cc7b1dcb7ef84491faf39e2581257fce 100644 (file)
@@ -26,6 +26,7 @@
 #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>
@@ -3460,6 +3461,7 @@ static void __sched notrace __schedule(bool preempt)
                rq_unlock_irq(rq, &rf);
        }
 
+       dbg_snapshot_task(cpu, rq->curr);
        balance_callback(rq);
 }
 
index a4c87cf27f9da9bb30e292d3091f3103a7a3a2da..4a4223189c82eddd19c16b35082845050122defc 100644 (file)
@@ -26,6 +26,7 @@
 #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>
@@ -285,7 +286,9 @@ restart:
                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",
@@ -510,7 +513,11 @@ static __latent_entropy void tasklet_action(struct softirq_action *a)
                                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;
                        }
@@ -546,7 +553,11 @@ static __latent_entropy void tasklet_hi_action(struct softirq_action *a)
                                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;
                        }
index 4237e0744e26bd276de92ca083a44676c9ede240..3d2baafdbac60266cb86236a8cb12399a81e9f06 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/module.h>
 #include <linux/smp.h>
 #include <linux/device.h>
+#include <linux/debug-snapshot.h>
 
 #include "tick-internal.h"
 
@@ -253,6 +254,7 @@ static int clockevents_program_min_delta(struct clock_event_device *dev)
 
                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;
 
@@ -290,6 +292,7 @@ static int clockevents_program_min_delta(struct clock_event_device *dev)
 
        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);
 }
 
@@ -336,6 +339,7 @@ int clockevents_program_event(struct clock_event_device *dev, ktime_t expires,
        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;
index 7d96314acdf9728f185acfbaa5763cb82049013a..7383e80cc42386552424a851787430d837444474 100644 (file)
@@ -52,6 +52,7 @@
 #include <linux/timer.h>
 #include <linux/freezer.h>
 #include <linux/compat.h>
+#include <linux/debug-snapshot.h>
 
 #include <linux/uaccess.h>
 
@@ -1259,7 +1260,9 @@ static void __run_hrtimer(struct hrtimer_cpu_base *cpu_base,
         */
        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);
 
index 99f885d2904ac693bc0abbd1bc3cbda7c98e669d..68fbd3e67e56dd7c46f9d87daebfbbb23c83a4b9 100644 (file)
@@ -45,6 +45,7 @@
 #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>
@@ -1277,7 +1278,9 @@ static void call_timer_fn(struct timer_list *timer, void (*fn)(unsigned long),
        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);
index 7931dc2d5c97ec5a184859d510b59f0c0e7030be..c1b867c93ddddcbffb73be27254544a8db3decc3 100644 (file)
@@ -50,6 +50,7 @@
 #include <linux/uaccess.h>
 #include <linux/nmi.h>
 #include <linux/kvm_para.h>
+#include <linux/debug-snapshot.h>
 
 #include "workqueue_internal.h"
 
@@ -2134,7 +2135,9 @@ __acquires(&pool->lock)
         */
        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.
index 4e5f1aa049d71f87c97eed585268633fa03c1474..523d83b8111c643e25593af1ef2a2a992ca5a687 100644 (file)
@@ -1989,3 +1989,109 @@ config IO_STRICT_DEVMEM
          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
index be6401a29033aeac9ffeadf3bef5ceb28869a583..54b18b8ca141795511a23a4075c2a085a40bdd3e 100644 (file)
@@ -264,3 +264,6 @@ obj-$(CONFIG_SBITMAP) += sbitmap.o
 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
diff --git a/lib/debug-snapshot-helper.c b/lib/debug-snapshot-helper.c
new file mode 100644 (file)
index 0000000..89e665b
--- /dev/null
@@ -0,0 +1,400 @@
+/*
+ * 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();
+}
diff --git a/lib/debug-snapshot-local.h b/lib/debug-snapshot-local.h
new file mode 100644 (file)
index 0000000..186a744
--- /dev/null
@@ -0,0 +1,394 @@
+/*
+ * 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
diff --git a/lib/debug-snapshot-log.c b/lib/debug-snapshot-log.c
new file mode 100644 (file)
index 0000000..845090a
--- /dev/null
@@ -0,0 +1,1423 @@
+/*
+ * 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
diff --git a/lib/debug-snapshot-pstore.c b/lib/debug-snapshot-pstore.c
new file mode 100644 (file)
index 0000000..fdaee98
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * 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");
diff --git a/lib/debug-snapshot-sysfs.c b/lib/debug-snapshot-sysfs.c
new file mode 100644 (file)
index 0000000..17c5c09
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ * 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);
diff --git a/lib/debug-snapshot-utils.c b/lib/debug-snapshot-utils.c
new file mode 100644 (file)
index 0000000..f64c0f7
--- /dev/null
@@ -0,0 +1,646 @@
+/*
+ * 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);
diff --git a/lib/debug-snapshot.c b/lib/debug-snapshot.c
new file mode 100644 (file)
index 0000000..8eee4b1
--- /dev/null
@@ -0,0 +1,671 @@
+/*
+ * 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, &reg);
+                       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);