]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/commitdiff
nvdumper: add nvdumper support in kernel
authorYifei Wan <ywan@nvidia.com>
Fri, 6 Jun 2014 01:36:58 +0000 (20:36 -0500)
committerHarshada Kale <hkale@nvidia.com>
Tue, 10 Jun 2014 14:49:58 +0000 (07:49 -0700)
Change-Id: Ib59f88b171f12efc28985f42cf3d349374a371b8
Signed-off-by: Yifei Wan <ywan@nvidia.com>
Reviewed-on: http://git-master/r/419803
GVS: Gerrit_Virtual_Submit
Reviewed-by: Mark Peters <mpeters@nvidia.com>
Reviewed-by: Mitch Luban <mluban@nvidia.com>
arch/arm/configs/tegra12_android_defconfig
arch/arm/mach-tegra/Makefile
arch/arm/mach-tegra/include/mach/nvdumper-footprint.h [new file with mode: 0644]
arch/arm/mach-tegra/include/mach/nvdumper.h [new file with mode: 0644]
arch/arm/mach-tegra/nvdumper-footprint.c [new file with mode: 0644]
arch/arm/mach-tegra/nvdumper.c
arch/arm/mach-tegra/nvdumper_regdump.c [new file with mode: 0644]
arch/arm/mach-tegra/tegra12_emc.c
kernel/panic.c

index a4e07dc2ed2cb96d0547d3ffcd227c5ec6d2844a..337ba582c8e1b3f483783d83cf0754ce0281ab23 100644 (file)
@@ -622,3 +622,4 @@ CONFIG_CRYPTO_TEST=m
 CONFIG_CRYPTO_TWOFISH=y
 # CONFIG_CRYPTO_ANSI_CPRNG is not set
 CONFIG_CRYPTO_DEV_TEGRA_SE=y
+CONFIG_TEGRA_NVDUMPER=y
index b6b97c59efbb7bd4540d87bccfd83c644205f334..d92bae788ee90c134c695e9fcae99d871e80edb2 100644 (file)
@@ -302,3 +302,7 @@ obj-${CONFIG_MACH_ROTH}                 += board-roth-kbc.o
 obj-${CONFIG_MACH_ROTH}                 += board-roth-sensors.o
 obj-${CONFIG_MACH_ROTH}                 += board-roth-fan.o
 obj-${CONFIG_MACH_ROTH}                 += board-roth-leds.o
+
+obj-${CONFIG_TEGRA_NVDUMPER}            += nvdumper.o
+obj-${CONFIG_TEGRA_NVDUMPER}            += nvdumper_regdump.o
+obj-${CONFIG_TEGRA_NVDUMPER}            += nvdumper-footprint.o
diff --git a/arch/arm/mach-tegra/include/mach/nvdumper-footprint.h b/arch/arm/mach-tegra/include/mach/nvdumper-footprint.h
new file mode 100644 (file)
index 0000000..7d79455
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * arch/arm64/mach-tegra/include/mach/nvdumper-footprint.h
+ *
+ * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __MACH_TEGRA_NVFOOTPRINT_H
+#define __MACH_TEGRA_NVFOOTPRINT_H
+
+/*
+ * The below debug footprint structure is used to
+ * save debug information to.
+ */
+#define DBG_FOOTPRINT_NAME_LEN 16
+struct dbg_footprint_element_cpu {
+       volatile char name[DBG_FOOTPRINT_NAME_LEN];
+       volatile uint32_t data[NR_CPUS];/* NR_CPUS is defined in config file. */
+};
+
+struct dbg_footprint_element_default {
+       volatile char name[DBG_FOOTPRINT_NAME_LEN];
+       volatile uint32_t data[1];
+};
+
+#define DECLARE_DBG_FOOTPRINT_DATA_CPU(name) \
+       struct dbg_footprint_element_cpu name
+#define DECLARE_DBG_FOOTPRINT_DATA(name) \
+       struct dbg_footprint_element_default name
+
+struct dbg_footprint_data_cpu {
+       /* CPU related */
+       DECLARE_DBG_FOOTPRINT_DATA_CPU(kernel_footprint_cpu);
+       DECLARE_DBG_FOOTPRINT_DATA_CPU(exit_counter_from_cpu);
+       DECLARE_DBG_FOOTPRINT_DATA_CPU(reset_vector_for_cpu);
+       DECLARE_DBG_FOOTPRINT_DATA_CPU(cpu_reset_vector_address);
+       DECLARE_DBG_FOOTPRINT_DATA_CPU(cpu_reset_vector_address_value);
+       DECLARE_DBG_FOOTPRINT_DATA_CPU(cpu_frequency);
+       DECLARE_DBG_FOOTPRINT_DATA_CPU(acpuclk_set_rate_footprint_cpu);
+       DECLARE_DBG_FOOTPRINT_DATA_CPU(cpu_prev_frequency);
+       DECLARE_DBG_FOOTPRINT_DATA_CPU(cpu_new_frequency);
+       DECLARE_DBG_FOOTPRINT_DATA_CPU(cpu_hotplug_on);
+};
+
+struct dbg_footprint_data_soc {
+       /* SOC */
+       DECLARE_DBG_FOOTPRINT_DATA(emc_frequency);
+       DECLARE_DBG_FOOTPRINT_DATA(lp_state_current);
+       DECLARE_DBG_FOOTPRINT_DATA(lp_state_prev);
+       DECLARE_DBG_FOOTPRINT_DATA(lp_state_next);
+       DECLARE_DBG_FOOTPRINT_DATA(core_voltage);
+       DECLARE_DBG_FOOTPRINT_DATA(soc_voltage);
+       DECLARE_DBG_FOOTPRINT_DATA(gpu_voltage);
+       DECLARE_DBG_FOOTPRINT_DATA(mem_voltage);
+
+       /* customized debug purpose */
+       DECLARE_DBG_FOOTPRINT_DATA(debug_data);
+
+       /* Please add more if necessary */
+};
+
+#define INIT_DBG_FOOTPRINT(var, element, stringname) do {\
+               memset((uint32_t*)var.element.data, 0, sizeof(var.element.data)); \
+               if (strlen(stringname) < DBG_FOOTPRINT_NAME_LEN - 1) \
+                       strcpy((char*)var.element.name, stringname); \
+               else \
+                       strcpy((char*)var.element.name, "error in name"); \
+       } while (0)
+
+#define GET_DBG_FP_DATA_CPU(element, core) (dbp_fp_cpu.element.data[core])
+#define SET_DBG_FP_DATA_CPU(element, core, value) \
+       dbp_fp_cpu.element.data[core] = (uint32_t) value;
+#define GET_DBG_FP_DATA_DEFAULT(element) (dbp_fp_soc.element.data[0])
+#define SET_DBG_FP_DATA_DEFAULT(element, value) \
+       dbp_fp_soc.element.data[0] = (uint32_t) value;
+
+#define CPU_CORE_COUNT_LIMIT_CHECK(c) do { \
+               if (unlikely(c >= num_possible_cpus())) { \
+                       pr_err("Only %d cores but try to access core %d\n", \
+                               num_possible_cpus(), c); \
+                       return; \
+               } \
+       } while (0)
+
+void nvdumper_dbg_footprint_init(void);
+void nvdumper_dbg_footprint_exit(void);
+void dbg_set_kernel_footprint_cpu(int core, uint32_t value);
+void dbg_set_exit_counter_from_cpu(int core, uint32_t value);
+void dbg_set_reset_vector_for_cpu(int core, uint32_t value);
+void dbg_set_cpu_reset_vector_address(int core, uint32_t value);
+void dbg_set_cpu_frequency(int core, uint32_t value);
+void dbg_set_acpuclk_set_rate_footprint_cpu(int core, uint32_t value);
+void dbg_set_cpu_prev_frequency(int core, uint32_t value);
+void dbg_set_cpu_new_frequency(int core, uint32_t value);
+void dbg_set_cpu_hotplug_on(int core, uint32_t value);
+void dbg_set_emc_frequency(uint32_t value);
+void dbg_set_lp_state_current(uint32_t value);
+void dbg_set_lp_state_prev(uint32_t value);
+void dbg_set_lp_state_next(uint32_t value);
+void dbg_set_core_voltage(uint32_t value);
+void dbg_set_soc_voltage(uint32_t value);
+void dbg_set_gpu_voltage(uint32_t value);
+void dbg_set_mem_voltage(uint32_t value);
+void dbg_set_debug_data(uint32_t value);
+
+#endif
diff --git a/arch/arm/mach-tegra/include/mach/nvdumper.h b/arch/arm/mach-tegra/include/mach/nvdumper.h
new file mode 100644 (file)
index 0000000..0c89e45
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * arch/arm64/mach-tegra/include/mach/nvdumper.h
+ *
+ * Copyright (c) 2011-2014, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __MACH_TEGRA_NVDUMPER_H
+#define __MACH_TEGRA_NVDUMPER_H
+
+struct mode_regs_t {
+       unsigned long sp_svc;
+       unsigned long lr_svc;
+       unsigned long spsr_svc;
+       /* 0x17 */
+       unsigned long sp_abt;
+       unsigned long lr_abt;
+       unsigned long spsr_abt;
+       /* 0x1b */
+       unsigned long sp_und;
+       unsigned long lr_und;
+       unsigned long spsr_und;
+       /* 0x12 */
+       unsigned long sp_irq;
+       unsigned long lr_irq;
+       unsigned long spsr_irq;
+       /* 0x11 */
+       unsigned long r8_fiq;
+       unsigned long r9_fiq;
+       unsigned long r10_fiq;
+       unsigned long r11_fiq;
+       unsigned long r12_fiq;
+       unsigned long sp_fiq;
+       unsigned long lr_fiq;
+       unsigned long spsr_fiq;
+       /* 0x1f: sys/usr */
+       unsigned long r8_usr;
+       unsigned long r9_usr;
+       unsigned long r10_usr;
+       unsigned long r11_usr;
+       unsigned long r12_usr;
+       unsigned long sp_usr;
+       unsigned long lr_usr;
+};
+
+struct cp15_regs_t {
+       unsigned int SCTLR;
+       unsigned int TTBR0;
+       unsigned int TTBR1;
+       unsigned int TTBCR;
+       unsigned int DACR;
+       unsigned int DFSR;
+       unsigned int IFSR;
+       unsigned int ADFSR;
+       unsigned int AIFSR;
+       unsigned int DFAR;
+       unsigned int IFAR;
+       unsigned int PAR;
+       unsigned int TLPLDR;
+       unsigned int PRRR;
+       unsigned int NRRR;
+       unsigned int CONTEXTIDR;
+       unsigned int TPIDRURW;
+       unsigned int TPIDRURO;
+       unsigned int TPIDRPRW;
+       unsigned int CFGBASEADDREG;
+};
+
+struct nvdumper_cpu_data_t {
+       bool is_online;
+       struct pt_regs pt_regs;
+       struct mode_regs_t mode_regs;
+       struct cp15_regs_t cp15_regs;
+       struct task_struct *current_task;
+};
+
+
+int nvdumper_regdump_init(void);
+void nvdumper_regdump_exit(void);
+void nvdumper_crash_setup_regs(void);
+void nvdumper_print_data(void);
+
+#endif
diff --git a/arch/arm/mach-tegra/nvdumper-footprint.c b/arch/arm/mach-tegra/nvdumper-footprint.c
new file mode 100644 (file)
index 0000000..16b289d
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * arch/arm64/mach-tegra/nvdumper-footprint.c
+ *
+ * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/threads.h>
+#include <linux/string.h>
+#include <linux/cpumask.h>
+#include <asm/ptrace.h>
+#include <mach/nvdumper-footprint.h>
+
+struct dbg_footprint_data_cpu dbp_fp_cpu;
+struct dbg_footprint_data_soc dbp_fp_soc;
+
+void nvdumper_dbg_footprint_init(void)
+{
+       /* Set names */
+       INIT_DBG_FOOTPRINT(dbp_fp_cpu, kernel_footprint_cpu, "KernelFPCpu");
+       INIT_DBG_FOOTPRINT(dbp_fp_cpu, exit_counter_from_cpu,
+               "CpuExitCounter");
+       INIT_DBG_FOOTPRINT(dbp_fp_cpu, reset_vector_for_cpu, "ResetVector");
+       INIT_DBG_FOOTPRINT(dbp_fp_cpu, cpu_reset_vector_address,
+               "RstVectorAddr");
+       INIT_DBG_FOOTPRINT(dbp_fp_cpu, cpu_reset_vector_address_value,
+               "RstVectAddrVal");
+       INIT_DBG_FOOTPRINT(dbp_fp_cpu, cpu_frequency, "CpuFreq");
+       INIT_DBG_FOOTPRINT(dbp_fp_cpu, acpuclk_set_rate_footprint_cpu,
+               "acpuclk");
+       INIT_DBG_FOOTPRINT(dbp_fp_cpu, cpu_prev_frequency, "CpuPrevFreq");
+       INIT_DBG_FOOTPRINT(dbp_fp_cpu, cpu_new_frequency, "CpuNewFreq");
+       INIT_DBG_FOOTPRINT(dbp_fp_cpu, cpu_hotplug_on, "CpuHotplug");
+
+       INIT_DBG_FOOTPRINT(dbp_fp_soc, emc_frequency, "EmcFreq");
+       INIT_DBG_FOOTPRINT(dbp_fp_soc, lp_state_current, "LPStateCur");
+       INIT_DBG_FOOTPRINT(dbp_fp_soc, lp_state_prev, "LPStatePrev");
+       INIT_DBG_FOOTPRINT(dbp_fp_soc, lp_state_next, "LPStateNext");
+       INIT_DBG_FOOTPRINT(dbp_fp_soc, core_voltage, "CoreVol");
+       INIT_DBG_FOOTPRINT(dbp_fp_soc, soc_voltage, "SocVol");
+       INIT_DBG_FOOTPRINT(dbp_fp_soc, gpu_voltage, "GpuVol");
+       INIT_DBG_FOOTPRINT(dbp_fp_soc, mem_voltage, "MemVol");
+
+       INIT_DBG_FOOTPRINT(dbp_fp_soc, debug_data, "DebugData");
+}
+
+void nvdumper_dbg_footprint_exit(void)
+{
+
+}
+
+void dbg_set_kernel_footprint_cpu(int core, uint32_t value)
+{
+       CPU_CORE_COUNT_LIMIT_CHECK(core);
+
+       SET_DBG_FP_DATA_CPU(kernel_footprint_cpu, core, value);
+}
+
+void dbg_set_exit_counter_from_cpu(int core, uint32_t value)
+{
+       CPU_CORE_COUNT_LIMIT_CHECK(core);
+
+       SET_DBG_FP_DATA_CPU(exit_counter_from_cpu, core, value);
+}
+
+void dbg_set_reset_vector_for_cpu(int core, uint32_t value)
+{
+       CPU_CORE_COUNT_LIMIT_CHECK(core);
+
+       SET_DBG_FP_DATA_CPU(reset_vector_for_cpu, core, value);
+}
+
+void dbg_set_cpu_reset_vector_address(int core, uint32_t value)
+{
+       CPU_CORE_COUNT_LIMIT_CHECK(core);
+
+       SET_DBG_FP_DATA_CPU(cpu_reset_vector_address, core, value);
+}
+
+void dbg_set_cpu_frequency(int core, uint32_t value)
+{
+       CPU_CORE_COUNT_LIMIT_CHECK(core);
+
+       SET_DBG_FP_DATA_CPU(cpu_frequency, core, value);
+}
+
+void dbg_set_acpuclk_set_rate_footprint_cpu(int core, uint32_t value)
+{
+       CPU_CORE_COUNT_LIMIT_CHECK(core);
+
+       SET_DBG_FP_DATA_CPU(acpuclk_set_rate_footprint_cpu, core, value);
+}
+
+void dbg_set_cpu_prev_frequency(int core, uint32_t value)
+{
+       CPU_CORE_COUNT_LIMIT_CHECK(core);
+
+       SET_DBG_FP_DATA_CPU(cpu_prev_frequency, core, value);
+}
+
+void dbg_set_cpu_new_frequency(int core, uint32_t value)
+{
+       CPU_CORE_COUNT_LIMIT_CHECK(core);
+
+       SET_DBG_FP_DATA_CPU(cpu_new_frequency, core, value);
+}
+
+void dbg_set_cpu_hotplug_on(int core, uint32_t value)
+{
+       CPU_CORE_COUNT_LIMIT_CHECK(core);
+
+       SET_DBG_FP_DATA_CPU(cpu_hotplug_on, core, value);
+}
+
+void dbg_set_emc_frequency(uint32_t value)
+{
+       SET_DBG_FP_DATA_DEFAULT(emc_frequency, value);
+}
+
+void dbg_set_lp_state_current(uint32_t value)
+{
+       SET_DBG_FP_DATA_DEFAULT(lp_state_current, value);
+}
+
+void dbg_set_lp_state_prev(uint32_t value)
+{
+       SET_DBG_FP_DATA_DEFAULT(lp_state_prev, value);
+}
+
+void dbg_set_lp_state_next(uint32_t value)
+{
+       SET_DBG_FP_DATA_DEFAULT(lp_state_next, value);
+}
+
+void dbg_set_core_voltage(uint32_t value)
+{
+       SET_DBG_FP_DATA_DEFAULT(core_voltage, value);
+}
+
+void dbg_set_soc_voltage(uint32_t value)
+{
+       SET_DBG_FP_DATA_DEFAULT(soc_voltage, value);
+}
+
+void dbg_set_gpu_voltage(uint32_t value)
+{
+       SET_DBG_FP_DATA_DEFAULT(gpu_voltage, value);
+}
+
+void dbg_set_mem_voltage(uint32_t value)
+{
+       SET_DBG_FP_DATA_DEFAULT(mem_voltage, value);
+}
+
+void dbg_set_debug_data(uint32_t value)
+{
+       SET_DBG_FP_DATA_DEFAULT(debug_data, value);
+}
+
index 0b83613365d8c6213ce94130e7aadaac92193520..c5f952b4a605ee755ac5a83a195017be25a0a6c5 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * arch/arm/mach-tegra/nvdumper.c
  *
- * Copyright (C) 2011 NVIDIA Corporation
+ * Copyright (c) 2011-2014, NVIDIA CORPORATION.  All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/reboot.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
 #include "board.h"
+#include <mach/nvdumper.h>
+#include <mach/nvdumper-footprint.h>
+
+#ifdef CONFIG_TEGRA_USE_NCT
+#include <mach/nct.h>
+#endif
 
 #define NVDUMPER_CLEAN 0xf000caf3U
 #define NVDUMPER_DIRTY 0xdeadbeefU
 
+#define RW_MODE (S_IWUSR | S_IRUGO)
+
 static uint32_t *nvdumper_ptr;
 
 static int get_dirty_state(void)
@@ -50,7 +60,7 @@ static void set_dirty_state(int dirty)
 static int nvdumper_reboot_cb(struct notifier_block *nb,
                unsigned long event, void *unused)
 {
-       printk(KERN_INFO "nvdumper: rebooting cleanly.\n");
+       pr_info("nvdumper: rebooting cleanly.\n");
        set_dirty_state(0);
        return NOTIFY_DONE;
 }
@@ -63,44 +73,95 @@ static int __init nvdumper_init(void)
 {
        int ret, dirty;
 
+#ifdef CONFIG_TEGRA_USE_NCT
+       union nct_item_type *item;
+#endif
+
        if (!nvdumper_reserved) {
-               printk(KERN_INFO "nvdumper: not configured\n");
+               pr_info("nvdumper: not configured\n");
                return -ENOTSUPP;
        }
        nvdumper_ptr = ioremap_nocache(nvdumper_reserved,
                        NVDUMPER_RESERVED_SIZE);
        if (!nvdumper_ptr) {
-               printk(KERN_INFO "nvdumper: failed to ioremap memory "
-                       "at 0x%08lx\n", nvdumper_reserved);
+               pr_info("nvdumper: failed to ioremap memory at 0x%08lx\n",
+                               nvdumper_reserved);
                return -EIO;
        }
        ret = register_reboot_notifier(&nvdumper_reboot_notifier);
        if (ret)
-               return ret;
+               goto err_out1;
+
+       ret = nvdumper_regdump_init();
+       if (ret)
+               goto err_out2;
+
+       nvdumper_dbg_footprint_init();
+
        dirty = get_dirty_state();
        switch (dirty) {
        case 0:
-               printk(KERN_INFO "nvdumper: last reboot was clean\n");
+               pr_info("nvdumper: last reboot was clean\n");
                break;
        case 1:
-               printk(KERN_INFO "nvdumper: last reboot was dirty\n");
+               pr_info("nvdumper: last reboot was dirty\n");
                break;
        default:
-               printk(KERN_INFO "nvdumper: last reboot was unknown\n");
+               pr_info("nvdumper: last reboot was unknown\n");
                break;
        }
+#ifdef CONFIG_TEGRA_USE_NCT
+       item = kzalloc(sizeof(*item), GFP_KERNEL);
+       if (!item) {
+               pr_err("failed to allocate memory\n");
+               goto err_out3;
+       }
+
+       ret = tegra_nct_read_item(NCT_ID_RAMDUMP, item);
+       if (ret < 0) {
+               pr_err("%s: NCT read failure. Set to dirty\n", __func__);
+               kfree(item);
+               set_dirty_state(1);
+               goto err_out3;
+       }
+
+       pr_info("%s: RAMDUMP flag(%d) from NCT\n",
+                       __func__, item->ramdump.flag);
+       if (item->ramdump.flag == 1)
+               set_dirty_state(1);
+       else
+               set_dirty_state(0);
+
+       kfree(item);
+
+       return 0;
+
+err_out3:
+
+#else
        set_dirty_state(1);
        return 0;
+#endif
+
+err_out2:
+       unregister_reboot_notifier(&nvdumper_reboot_notifier);
+err_out1:
+       iounmap(nvdumper_ptr);
+
+       return ret;
+
 }
 
 static void __exit nvdumper_exit(void)
 {
+       nvdumper_regdump_exit();
+       nvdumper_dbg_footprint_exit();
        unregister_reboot_notifier(&nvdumper_reboot_notifier);
        set_dirty_state(0);
        iounmap(nvdumper_ptr);
 }
 
-module_init(nvdumper_init);
+arch_initcall(nvdumper_init);
 module_exit(nvdumper_exit);
 
 MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-tegra/nvdumper_regdump.c b/arch/arm/mach-tegra/nvdumper_regdump.c
new file mode 100644 (file)
index 0000000..637b169
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ * arch/arm64/mach-tegra/nvdumper_regdump.c
+ *
+ * Copyright (c) 2011-2014, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/kdebug.h>
+#include <linux/notifier.h>
+#include <linux/sched.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/cacheflush.h>
+
+#include <mach/nvdumper.h>
+
+#define DEBUG_REGDUMP 1
+
+static int max_cpus;
+
+struct nvdumper_cpu_data_t *nvdumper_cpu_data;
+
+static dma_addr_t nvdumper_p;
+
+void __naked save_mode_regs(struct mode_regs_t *regs)
+{
+       asm volatile (
+               "mrs    r1, cpsr\n"
+               "msr    cpsr_c, #0xd3 @(SVC_MODE | PSR_I_BIT | PSR_F_BIT)\n"
+               "stmia  r0!, {r13 - r14}\n"
+               "mrs    r2, spsr\n"
+               "msr    cpsr_c, #0xd7 @(ABT_MODE | PSR_I_BIT | PSR_F_BIT)\n"
+               "stmia  r0!, {r2, r13 - r14}\n"
+               "mrs    r2, spsr\n"
+               "msr    cpsr_c, #0xdb @(UND_MODE | PSR_I_BIT | PSR_F_BIT)\n"
+               "stmia  r0!, {r2, r13 - r14}\n"
+               "mrs    r2, spsr\n"
+               "msr    cpsr_c, #0xd2 @(IRQ_MODE | PSR_I_BIT | PSR_F_BIT)\n"
+               "stmia  r0!, {r2, r13 - r14}\n"
+               "mrs    r2, spsr\n"
+               "msr    cpsr_c, #0xd1 @(FIQ_MODE | PSR_I_BIT | PSR_F_BIT)\n"
+               "stmia  r0!, {r2, r8 - r14}\n"
+               "mrs    r2, spsr\n"
+               "msr    cpsr_c, #0xdf @(SYS_MODE | PSR_I_BIT | PSR_F_BIT)\n"
+               "stmia  r0!, {r2, r8 - r14}\n"
+               "msr    cpsr_c, r1\n"
+               "bx        lr\n");
+}
+
+void save_cp15_regs(struct cp15_regs_t *cp15_regs)
+{
+       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, c5, c0, 1\n\t"        /* IFSR */
+               "str r1, [%0,#24]\n\t"
+               "mrc    p15, 0, r1, c5, c1, 0\n\t"        /* ADFSR */
+               "str r1, [%0,#28]\n\t"
+               "mrc    p15, 0, r1, c5, c1, 1\n\t"        /* AIFSR */
+               "str r1, [%0,#32]\n\t"
+               "mrc    p15, 0, r1, c6, c0, 0\n\t"        /* DFAR */
+               "str r1, [%0,#36]\n\t"
+               "mrc    p15, 0, r1, c6, c0, 2\n\t"        /* IFAR */
+               "str r1, [%0,#40]\n\t"
+               "mrc    p15, 0, r1, c7, c4, 0\n\t"        /* PAR */
+               "str r1, [%0,#44]\n\t"
+               //"mrc    p15, 0, r1, c10, c0, 0\n\t"        /* TLPLDR */
+               //"str r1, [%0,#48]\n\t"
+               "mrc    p15, 0, r1, c10, c2, 0\n\t"        /* PRRR */
+               "str r1, [%0,#52]\n\t"
+               "mrc    p15, 0, r1, c10, c2, 1\n\t"        /* NRRR */
+               "str r1, [%0,#56]\n\t"
+               "mrc    p15, 0, r1, c13, c0, 1\n\t"        /* CONTEXTIDR */
+               "str r1, [%0,#60]\n\t"
+               "mrc    p15, 0, r1, c13, c0, 2\n\t"        /* TPIDRURW */
+               "str r1, [%0,#64]\n\t"
+               "mrc    p15, 0, r1, c13, c0, 3\n\t"        /* TPIDRURO */
+               "str r1, [%0,#68]\n\t"
+               "mrc    p15, 0, r1, c13, c0, 4\n\t"        /* TPIDRPRW */
+               "str r1, [%0,#72]\n\t"
+               "mrc    p15, 4, r1, c15, c0, 0\n\t"        /* CFGBASEADDREG */
+               "str r1, [%0,#76]\n\t" :                /* output */
+               : "r"(cp15_regs)                        /* input */
+               : "%r1", "memory"                        /* clobbered register */
+       );
+}
+
+void nvdumper_save_regs(void *data)
+{
+       int id = smp_processor_id();
+       nvdumper_cpu_data[id].current_task = current_thread_info()->task;
+       nvdumper_cpu_data[id].is_online = true;
+
+       __asm__ __volatile__ (
+               "stmia        %[regs_base], {r0-r12}\n\t"
+               "mov        %[_ARM_sp], sp\n\t"
+               "str        lr, %[_ARM_lr]\n\t"
+               "adr        %[_ARM_pc], 1f\n\t"
+               "mrs        %[_ARM_cpsr], cpsr\n\t"
+               "1:"
+               : [_ARM_pc] "=r" (nvdumper_cpu_data[id].pt_regs.ARM_pc),
+               [_ARM_cpsr] "=r" (nvdumper_cpu_data[id].pt_regs.ARM_cpsr),
+               [_ARM_sp] "=r" (nvdumper_cpu_data[id].pt_regs.ARM_sp),
+               [_ARM_lr] "=o" (nvdumper_cpu_data[id].pt_regs.ARM_lr)
+               : [regs_base] "r" (&nvdumper_cpu_data[id].pt_regs.ARM_r0)
+               : "memory"
+       );
+
+       save_mode_regs(&nvdumper_cpu_data[id].mode_regs);
+
+       save_cp15_regs(&nvdumper_cpu_data[id].cp15_regs);
+}
+
+void nvdumper_crash_setup_regs(void)
+{
+       pr_info("entering nvdumper_crash_setup_regs\n");
+       on_each_cpu(nvdumper_save_regs, NULL, 1);
+}
+
+void print_cpu_data(int id)
+{
+       struct pt_regs *pt_regs = &nvdumper_cpu_data[id].pt_regs;
+       struct mode_regs_t *mode_regs = &nvdumper_cpu_data[id].mode_regs;
+       struct cp15_regs_t *cp15_regs = &nvdumper_cpu_data[id].cp15_regs;
+
+       pr_info("------------------------------------------------\n");
+
+       pr_info("CPU%d Status: %s\n", id,
+
+       nvdumper_cpu_data[id].is_online ? "online" : "offline");
+
+       pr_info("current task: %p\n", nvdumper_cpu_data[id].current_task);
+
+       if (nvdumper_cpu_data[id].is_online) {
+               pr_info("ARM_r0   = 0x%08lx\n", pt_regs->ARM_r0);
+               pr_info("ARM_r1   = 0x%08lx\n", pt_regs->ARM_r1);
+               pr_info("ARM_r2   = 0x%08lx\n", pt_regs->ARM_r2);
+               pr_info("ARM_r3   = 0x%08lx\n", pt_regs->ARM_r3);
+               pr_info("ARM_r4   = 0x%08lx\n", pt_regs->ARM_r4);
+               pr_info("ARM_r5   = 0x%08lx\n", pt_regs->ARM_r5);
+               pr_info("ARM_r6   = 0x%08lx\n", pt_regs->ARM_r6);
+               pr_info("ARM_r7   = 0x%08lx\n", pt_regs->ARM_r7);
+               pr_info("ARM_r8   = 0x%08lx\n", pt_regs->ARM_r8);
+               pr_info("ARM_r9   = 0x%08lx\n", pt_regs->ARM_r9);
+               pr_info("ARM_r10  = 0x%08lx\n", pt_regs->ARM_r10);
+               pr_info("ARM_fp   = 0x%08lx\n", pt_regs->ARM_fp);
+               pr_info("ARM_ip   = 0x%08lx\n", pt_regs->ARM_ip);
+               pr_info("ARM_sp   = 0x%08lx\n", pt_regs->ARM_sp);
+               pr_info("ARM_lr   = 0x%08lx\n", pt_regs->ARM_lr);
+               pr_info("ARM_pc   = 0x%08lx\n", pt_regs->ARM_pc);
+               pr_info("ARM_cpsr = 0x%08lx\n", pt_regs->ARM_cpsr);
+
+               pr_info("sp_svc   = 0x%08lx\n", mode_regs->sp_svc);
+               pr_info("lr_svc   = 0x%08lx\n", mode_regs->lr_svc);
+               pr_info("spsr_svc = 0x%08lx\n", mode_regs->spsr_svc);
+               pr_info("sp_abt   = 0x%08lx\n", mode_regs->sp_abt);
+               pr_info("lr_abt   = 0x%08lx\n", mode_regs->lr_abt);
+               pr_info("spsr_abt = 0x%08lx\n", mode_regs->spsr_abt);
+               pr_info("sp_und   = 0x%08lx\n", mode_regs->sp_und);
+               pr_info("lr_und   = 0x%08lx\n", mode_regs->lr_und);
+               pr_info("spsr_und = 0x%08lx\n", mode_regs->spsr_und);
+               pr_info("sp_irq   = 0x%08lx\n", mode_regs->sp_irq);
+               pr_info("lr_irq   = 0x%08lx\n", mode_regs->lr_irq);
+               pr_info("spsr_irq = 0x%08lx\n", mode_regs->spsr_irq);
+               pr_info("r8_fiq   = 0x%08lx\n", mode_regs->r8_fiq);
+               pr_info("r9_fiq   = 0x%08lx\n", mode_regs->r9_fiq);
+               pr_info("r10_fiq  = 0x%08lx\n", mode_regs->r10_fiq);
+               pr_info("r11_fiq  = 0x%08lx\n", mode_regs->r11_fiq);
+               pr_info("r12_fiq  = 0x%08lx\n", mode_regs->r12_fiq);
+               pr_info("sp_fiq   = 0x%08lx\n", mode_regs->sp_fiq);
+               pr_info("lr_fiq   = 0x%08lx\n", mode_regs->lr_fiq);
+               pr_info("spsr_fiq = 0x%08lx\n", mode_regs->spsr_fiq);
+               pr_info("r8_usr   = 0x%08lx\n", mode_regs->r8_usr);
+               pr_info("r9_usr   = 0x%08lx\n", mode_regs->r9_usr);
+               pr_info("r10_usr  = 0x%08lx\n", mode_regs->r10_usr);
+               pr_info("r11_usr  = 0x%08lx\n", mode_regs->r11_usr);
+               pr_info("r12_usr  = 0x%08lx\n", mode_regs->r12_usr);
+               pr_info("sp_usr   = 0x%08lx\n", mode_regs->sp_usr);
+               pr_info("lr_usr   = 0x%08lx\n", mode_regs->lr_usr);
+
+               pr_info("SCTLR    = 0x%08x\n", cp15_regs->SCTLR);
+               pr_info("TTBR0    = 0x%08x\n", cp15_regs->TTBR0);
+               pr_info("TTBR1    = 0x%08x\n", cp15_regs->TTBR1);
+               pr_info("TTBCR    = 0x%08x\n", cp15_regs->TTBCR);
+               pr_info("DACR     = 0x%08x\n", cp15_regs->DACR);
+               pr_info("DFSR     = 0x%08x\n", cp15_regs->DFSR);
+               pr_info("IFSR     = 0x%08x\n", cp15_regs->IFSR);
+               pr_info("ADFSR    = 0x%08x\n", cp15_regs->ADFSR);
+               pr_info("AIFSR    = 0x%08x\n", cp15_regs->AIFSR);
+               pr_info("DFAR     = 0x%08x\n", cp15_regs->DFAR);
+               pr_info("IFAR     = 0x%08x\n", cp15_regs->IFAR);
+               pr_info("PAR      = 0x%08x\n", cp15_regs->PAR);
+               pr_info("TLPLDR   = 0x%08x\n", cp15_regs->TLPLDR);
+               pr_info("PRRR     = 0x%08x\n", cp15_regs->PRRR);
+               pr_info("NRRR     = 0x%08x\n", cp15_regs->NRRR);
+               pr_info("CONTEXTIDR = 0x%08x\n", cp15_regs->CONTEXTIDR);
+               pr_info("TPIDRURW = 0x%08x\n", cp15_regs->TPIDRURW);
+               pr_info("TPIDRURO = 0x%08x\n", cp15_regs->TPIDRURO);
+               pr_info("TPIDRPRW = 0x%08x\n", cp15_regs->TPIDRPRW);
+               pr_info("CFGBASEADDREG = 0x%08x\n", cp15_regs->CFGBASEADDREG);
+       }
+}
+
+void nvdumper_print_data(void)
+{
+       int id;
+
+       for_each_present_cpu(id)
+               print_cpu_data(id);
+}
+
+int nvdumper_die_handler(struct notifier_block *nb, unsigned long reason,
+                                       void *data)
+{
+       nvdumper_crash_setup_regs();
+       return NOTIFY_OK;
+}
+
+static int nvdumper_panic_handler(struct notifier_block *this,
+                                       unsigned long event, void *unused)
+{
+#if DEBUG_REGDUMP
+       nvdumper_print_data();
+#endif
+       flush_cache_all();
+
+       return NOTIFY_OK;
+}
+
+struct notifier_block nvdumper_die_notifier = {
+       .notifier_call = nvdumper_die_handler,
+       .priority      = INT_MAX-1, /* priority: INT_MAX >= x >= 0 */
+};
+
+static struct notifier_block nvdumper_panic_notifier = {
+       .notifier_call = nvdumper_panic_handler,
+       .priority      = INT_MAX-1, /* priority: INT_MAX >= x >= 0 */
+};
+
+int nvdumper_regdump_init(void)
+{
+       int ret;
+
+       max_cpus = num_possible_cpus();
+
+       nvdumper_cpu_data = dma_alloc_coherent(NULL,
+               sizeof(struct nvdumper_cpu_data_t) * max_cpus,
+               &nvdumper_p, 0);
+       if (!nvdumper_cpu_data) {
+               pr_err("%s: can not allocate bounce buffer\n", __func__);
+
+               return -ENOMEM;
+       }
+
+       ret = register_die_notifier(&nvdumper_die_notifier);
+       if (ret != 0) {
+               pr_err("%s: registering die notifier failed with err=%d\n",
+                               __func__, ret);
+               goto err_out1;
+       }
+
+       ret = atomic_notifier_chain_register(&panic_notifier_list,
+               &nvdumper_panic_notifier);
+       if (ret != 0) {
+               pr_err("%s: unable to register a panic notifier (err=%d)\n",
+                               __func__, ret);
+               goto err_out2;
+       }
+
+       return ret;
+
+err_out2:
+       unregister_die_notifier(&nvdumper_die_notifier);
+
+err_out1:
+       if (nvdumper_cpu_data)
+               dma_free_coherent(NULL,
+                               sizeof(struct nvdumper_cpu_data_t) * max_cpus,
+                               nvdumper_cpu_data, nvdumper_p);
+
+       return ret;
+}
+
+void nvdumper_regdump_exit(void)
+{
+       dma_free_coherent(NULL, sizeof(struct nvdumper_cpu_data_t) * max_cpus,
+                       nvdumper_cpu_data, nvdumper_p);
+       unregister_die_notifier(&nvdumper_die_notifier);
+       atomic_notifier_chain_unregister(&panic_notifier_list,
+                       &nvdumper_panic_notifier);
+}
index 7c2c70db43b7f31c9968dd47ae18066f749eebe7..678ffa8f3ccebdb4dd3c219a055a524e8ae22965 100644 (file)
@@ -36,6 +36,7 @@
 #include <asm/cputime.h>
 
 #include <mach/nct.h>
+#include <mach/nvdumper-footprint.h>
 
 #include "clock.h"
 #include "board.h"
@@ -900,6 +901,11 @@ static noinline void emc_set_clock(const struct tegra12_emc_table *next_timing,
           change EMC clock source register wait for clk change completion */
        do_clock_change(clk_setting);
 
+#ifdef CONFIG_TEGRA_NVDUMPER
+       /* set debug footprint */
+       dbg_set_emc_frequency(clk_setting);
+#endif
+
        /* 14.2 program burst_up_down registers if emc rate is going up */
        if (next_timing->rate > last_timing->rate) {
                for (i = 0; i < next_timing->burst_up_down_regs_num; i++)
index 126b2ef2eb618bcd39f827ab43a4162092f70284..df3a06a32496e379068e16338af2d800c9bca217 100644 (file)
@@ -63,6 +63,11 @@ void __weak panic_smp_self_stop(void)
                cpu_relax();
 }
 
+#ifdef CONFIG_TEGRA_NVDUMPER
+#include <mach/nvdumper.h>
+static int is_oops_called;
+#endif /* CONFIG_TEGRA_NVDUMPER */
+
 /**
  *     panic - halt the system
  *     @fmt: The text string to print
@@ -79,6 +84,12 @@ void panic(const char *fmt, ...)
        long i, i_next = 0;
        int state = 0;
 
+#ifdef CONFIG_TEGRA_NVDUMPER
+       /* if panic is called directly */
+       if (!is_oops_called)
+               nvdumper_crash_setup_regs();
+#endif
+
        /*
         * Disable local interrupts. This will prevent panic_smp_self_stop
         * from deadlocking the first cpu that invokes the panic, since
@@ -356,6 +367,10 @@ int oops_may_print(void)
  */
 void oops_enter(void)
 {
+#ifdef CONFIG_TEGRA_NVDUMPER
+       is_oops_called = 1;
+#endif
+
        tracing_off();
        /* can't trust the integrity of the kernel anymore: */
        debug_locks_off();