CONFIG_CRYPTO_TWOFISH=y
# CONFIG_CRYPTO_ANSI_CPRNG is not set
CONFIG_CRYPTO_DEV_TEGRA_SE=y
+CONFIG_TEGRA_NVDUMPER=y
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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
+}
+
/*
* 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)
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;
}
{
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");
--- /dev/null
+/*
+ * 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);
+}
#include <asm/cputime.h>
#include <mach/nct.h>
+#include <mach/nvdumper-footprint.h>
#include "clock.h"
#include "board.h"
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++)
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
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
*/
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();