mako: debug: porting lge crash handler

Change-Id: I913d372131a25bb9c0d90680f5e96cebb8883055
diff --git a/arch/arm/mach-msm/include/mach/board_lge.h b/arch/arm/mach-msm/include/mach/board_lge.h
index 239dee1..833b419 100644
--- a/arch/arm/mach-msm/include/mach/board_lge.h
+++ b/arch/arm/mach-msm/include/mach/board_lge.h
@@ -23,7 +23,7 @@
 #define LGE_RAM_CONSOLE_SIZE	(124*SZ_1K * 2)
 #endif
 
-#ifdef CONFIG_LGE_HANDLE_PANIC
+#ifdef CONFIG_LGE_CRASH_HANDLER
 #define LGE_CRASH_LOG_SIZE	(4*SZ_1K)
 #endif
 
@@ -126,10 +126,10 @@
 void __init lge_add_ramconsole_devices(void);
 #endif
 
-#ifdef CONFIG_LGE_HANDLE_PANIC
+#ifdef CONFIG_LGE_CRASH_HANDLER
 void __init lge_add_panic_handler_devices(void);
-int lge_get_magic_for_subsystem(void);
-void lge_set_magic_for_subsystem(const char *subsys_name);
+int get_ssr_magic_number(void);
+void set_ssr_magic_number(const char *subsys_name);
 #endif
 
 #ifdef CONFIG_LGE_QFPROM_INTERFACE
diff --git a/arch/arm/mach-msm/include/mach/memory.h b/arch/arm/mach-msm/include/mach/memory.h
index 2596364..c0e6791 100644
--- a/arch/arm/mach-msm/include/mach/memory.h
+++ b/arch/arm/mach-msm/include/mach/memory.h
@@ -93,6 +93,10 @@
 
 #if defined(CONFIG_ARCH_MSM8X60) || defined(CONFIG_ARCH_MSM8960)
 extern void store_ttbr0(void);
+#ifdef CONFIG_LGE_CRASH_HANDLER
+extern void store_ctrl(void);
+extern void store_dac(void);
+#endif
 #define finish_arch_switch(prev)	do { store_ttbr0(); } while (0)
 #endif
 
diff --git a/arch/arm/mach-msm/include/mach/restart.h b/arch/arm/mach-msm/include/mach/restart.h
index b913e3f..4d7b558 100644
--- a/arch/arm/mach-msm/include/mach/restart.h
+++ b/arch/arm/mach-msm/include/mach/restart.h
@@ -17,6 +17,14 @@
 #define RESTART_NORMAL 0x0
 #define RESTART_DLOAD  0x1
 
+#if defined(CONFIG_LGE_CRASH_HANDLER)
+#define SUB_THD_F_PWR	0x0190
+#define SUB_THD_F_SD	0x0110
+#define SUB_UNAB_THD	0x0120
+#define SUB_RESET_SOC	0x0130
+#define SUB_UNKNOWN	0x0140
+#endif
+
 #if defined(CONFIG_MSM_NATIVE_RESTART)
 void msm_set_restart_mode(int mode);
 void msm_restart(char mode, const char *cmd);
diff --git a/arch/arm/mach-msm/lge/Kconfig b/arch/arm/mach-msm/lge/Kconfig
index 0c7e695..8c89b18 100644
--- a/arch/arm/mach-msm/lge/Kconfig
+++ b/arch/arm/mach-msm/lge/Kconfig
@@ -42,4 +42,11 @@
 	default n
 	help
 	  Saying Y here will support earjack type UART debugger cable
+
+config LGE_CRASH_HANDLER
+	tristate "Support LGE Crash Handler"
+	default n
+	help
+	  LGE Crash Handler
+
 endmenu
diff --git a/arch/arm/mach-msm/lge/Makefile b/arch/arm/mach-msm/lge/Makefile
index 336374a..9142a7d 100644
--- a/arch/arm/mach-msm/lge/Makefile
+++ b/arch/arm/mach-msm/lge/Makefile
@@ -6,4 +6,5 @@
 obj-$(CONFIG_LGE_QFPROM_INTERFACE) += lge_qfprom_access.o
 obj-$(CONFIG_LGE_QC_LCDC_LUT) += lge_qc_lcdc_luts.o
 obj-$(CONFIG_LGE_KCAL) += lge_kcal_ctrl.o
-obj-$(CONFIG_LGE_KCAL_QLUT) += kcal_qlut.o
\ No newline at end of file
+obj-$(CONFIG_LGE_KCAL_QLUT) += kcal_qlut.o
+obj-$(CONFIG_LGE_CRASH_HANDLER) += lge_panic_handler.o
diff --git a/arch/arm/mach-msm/lge/devices_lge.c b/arch/arm/mach-msm/lge/devices_lge.c
index e709b70..9186e39 100644
--- a/arch/arm/mach-msm/lge/devices_lge.c
+++ b/arch/arm/mach-msm/lge/devices_lge.c
@@ -340,7 +340,7 @@
 	struct resource* res = ram_console_resource;
 	struct membank* bank = &meminfo.bank[0];
 
-	res->start = PHYS_OFFSET + bank->size;
+	res->start = bank->start + bank->size;
 	res->end = res->start + LGE_RAM_CONSOLE_SIZE - 1;
 
 	printk(KERN_INFO "RAM CONSOLE START ADDR : %X\n", res->start);
@@ -348,9 +348,9 @@
 
 	platform_device_register(&ram_console_device);
 }
-#endif // CONFIG_ANDROID_RAM_CONSOLE
+#endif /* CONFIG_ANDROID_RAM_CONSOLE */
 
-#ifdef CONFIG_LGE_HANDLE_PANIC
+#ifdef CONFIG_LGE_CRASH_HANDLER
 static struct resource crash_log_resource[] = {
 	{
 		.name = "crash_log",
@@ -380,7 +380,7 @@
 
 	platform_device_register(&panic_handler_device);
 }
-#endif // CONFIG_LGE_HANDLE_PANIC
+#endif /* CONFIG_LGE_CRASH_HANDLER */
 
 #ifdef CONFIG_LGE_QFPROM_INTERFACE
 static struct platform_device qfprom_device = {
diff --git a/arch/arm/mach-msm/lge/lge_panic_handler.c b/arch/arm/mach-msm/lge/lge_panic_handler.c
new file mode 100644
index 0000000..ff3e355
--- /dev/null
+++ b/arch/arm/mach-msm/lge/lge_panic_handler.c
@@ -0,0 +1,322 @@
+/*
+ * arch/arm/mach-msm/lge/lge_panic_handler.c
+ *
+ * Copyright (C) 2010 LGE, Inc
+ *
+ * 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/device.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <asm/setup.h>
+#include <mach/board_lge.h>
+
+#include <mach/subsystem_restart.h>
+#ifdef CONFIG_CPU_CP15_MMU
+#include <linux/ptrace.h>
+#endif
+
+#define PANIC_HANDLER_NAME "panic-handler"
+#define PANIC_DUMP_CONSOLE 0
+#define PANIC_MAGIC_KEY    0x12345678
+#define CRASH_ARM9         0x87654321
+#define CRASH_REBOOT       0x618E1000
+
+struct crash_log_dump {
+	unsigned int magic_key;
+	unsigned int size;
+	unsigned char buffer[0];
+};
+
+static struct crash_log_dump *crash_dump_log;
+static unsigned int crash_buf_size = 0;
+static int crash_store_flag = 0;
+
+#ifdef CONFIG_CPU_CP15_MMU
+unsigned long *cpu_crash_ctx=NULL;
+#endif
+
+unsigned int msm_mmuctrl;
+unsigned int msm_mmudac;
+
+void store_ctrl(void)
+{
+	asm("mrc p15, 0, %0, c1, c0, 0\n"
+			: "=r" (msm_mmuctrl));
+}
+
+void store_dac(void)
+{
+	asm("mrc p15, 0, %0, c3, c0, 0\n"
+			: "=r" (msm_mmudac));
+}
+static DEFINE_SPINLOCK(panic_lock);
+
+static int dummy_arg;
+static int gen_bug(const char *val, struct kernel_param *kp)
+{
+	BUG();
+	return 0;
+}
+module_param_call(gen_bug, gen_bug, param_get_bool,
+		&dummy_arg, S_IWUSR | S_IRUGO);
+
+static int gen_panic(const char *val, struct kernel_param *kp)
+{
+	panic("generate test-panic");
+	return 0;
+}
+module_param_call(gen_panic, gen_panic, param_get_bool,
+		&dummy_arg, S_IWUSR | S_IRUGO);
+
+static int gen_mdm_ssr(const char *val, struct kernel_param *kp)
+{
+	subsystem_restart("external_modem");
+	return 0;
+}
+module_param_call(gen_mdm_ssr, gen_mdm_ssr, param_get_bool,
+		&dummy_arg, S_IWUSR | S_IRUGO);
+
+static int gen_modem_ssr(const char *val, struct kernel_param *kp)
+{
+	subsystem_restart("modem");
+	return 0;
+}
+module_param_call(gen_modem_ssr, gen_modem_ssr, param_get_bool,
+		&dummy_arg, S_IWUSR | S_IRUGO);
+
+static int gen_riva_ssr(const char *val, struct kernel_param *kp)
+{
+	subsystem_restart("riva");
+	return 0;
+}
+module_param_call(gen_riva_ssr, gen_riva_ssr, param_get_bool,
+		&dummy_arg, S_IWUSR | S_IRUGO);
+
+static int gen_dsps_ssr(const char *val, struct kernel_param *kp)
+{
+	subsystem_restart("dsps");
+	return 0;
+}
+module_param_call(gen_dsps_ssr, gen_dsps_ssr, param_get_bool,
+		&dummy_arg, S_IWUSR | S_IRUGO);
+
+static int gen_lpass_ssr(const char *val, struct kernel_param *kp)
+{
+	subsystem_restart("lpass");
+	return 0;
+}
+module_param_call(gen_lpass_ssr, gen_lpass_ssr, param_get_bool,
+		&dummy_arg, S_IWUSR | S_IRUGO);
+
+#define WDT0_RST        0x38
+#define WDT0_EN         0x40
+#define WDT0_BARK_TIME  0x4C
+#define WDT0_BITE_TIME  0x5C
+
+extern void __iomem *msm_timer_get_timer0_base(void);
+
+static int gen_wdt_bark(const char *val, struct kernel_param *kp)
+{
+	static void __iomem *msm_tmr0_base;
+	msm_tmr0_base = msm_timer_get_timer0_base();
+	__raw_writel(0, msm_tmr0_base + WDT0_EN);
+	__raw_writel(1, msm_tmr0_base + WDT0_RST);
+	__raw_writel(0x31F3, msm_tmr0_base + WDT0_BARK_TIME);
+	__raw_writel(5 * 0x31F3, msm_tmr0_base + WDT0_BITE_TIME);
+	__raw_writel(1, msm_tmr0_base + WDT0_EN);
+	return 0;
+}
+module_param_call(gen_wdt_bark, gen_wdt_bark, param_get_bool,
+		&dummy_arg, S_IWUSR | S_IRUGO);
+
+static int gen_hw_reset(const char *val, struct kernel_param *kp)
+{
+	static void __iomem *msm_tmr0_base;
+	msm_tmr0_base = msm_timer_get_timer0_base();
+	__raw_writel(0, msm_tmr0_base + WDT0_EN);
+	__raw_writel(1, msm_tmr0_base + WDT0_RST);
+	__raw_writel(5 * 0x31F3, msm_tmr0_base + WDT0_BARK_TIME);
+	__raw_writel(0x31F3, msm_tmr0_base + WDT0_BITE_TIME);
+	__raw_writel(1, msm_tmr0_base + WDT0_EN);
+	return 0;
+}
+module_param_call(gen_hw_reset, gen_hw_reset, param_get_bool,
+		&dummy_arg, S_IWUSR | S_IRUGO);
+
+void set_crash_store_enable(void)
+{
+	crash_store_flag = 1;
+	return;
+}
+
+void set_crash_store_disable(void)
+{
+	crash_store_flag = 0;
+	return;
+}
+
+void store_crash_log(char *p)
+{
+	if (!crash_store_flag)
+		return;
+	if (crash_dump_log->size == crash_buf_size)
+		return;
+	for ( ; *p; p++) {
+		if (*p == '[') {
+			for ( ; *p != ']'; p++)
+				;
+			p++;
+			if (*p == ' ')
+				p++;
+		}
+		if (*p == '<') {
+			for ( ; *p != '>'; p++)
+				;
+			p++;
+		}
+		crash_dump_log->buffer[crash_dump_log->size] = *p;
+		crash_dump_log->size++;
+	}
+	crash_dump_log->buffer[crash_dump_log->size] = 0;
+
+	return;
+}
+
+#ifdef CONFIG_CPU_CP15_MMU
+void lge_save_ctx(struct pt_regs* regs, unsigned int ctrl,
+		unsigned int transbase, unsigned int dac)
+{
+	/* save cpu register for simulation */
+	cpu_crash_ctx[0] = regs->ARM_r0;
+	cpu_crash_ctx[1] = regs->ARM_r1;
+	cpu_crash_ctx[2] = regs->ARM_r2;
+	cpu_crash_ctx[3] = regs->ARM_r3;
+	cpu_crash_ctx[4] = regs->ARM_r4;
+	cpu_crash_ctx[5] = regs->ARM_r5;
+	cpu_crash_ctx[6] = regs->ARM_r6;
+	cpu_crash_ctx[7] = regs->ARM_r7;
+	cpu_crash_ctx[8] = regs->ARM_r8;
+	cpu_crash_ctx[9] = regs->ARM_r9;
+	cpu_crash_ctx[10] = regs->ARM_r10;
+	cpu_crash_ctx[11] = regs->ARM_fp;
+	cpu_crash_ctx[12] = regs->ARM_ip;
+	cpu_crash_ctx[13] = regs->ARM_sp;
+	cpu_crash_ctx[14] = regs->ARM_lr;
+	cpu_crash_ctx[15] = regs->ARM_pc;
+	cpu_crash_ctx[16] = regs->ARM_cpsr;
+	/* save mmu register for simulation */
+	cpu_crash_ctx[17] = ctrl;
+	cpu_crash_ctx[18] = transbase;
+	cpu_crash_ctx[19] = dac;
+}
+#endif
+
+static int restore_crash_log(struct notifier_block *this,
+		unsigned long event, void *ptr)
+{
+	unsigned long flags;
+	crash_store_flag = 0;
+	spin_lock_irqsave(&panic_lock, flags);
+	crash_dump_log->magic_key = PANIC_MAGIC_KEY;
+	spin_unlock_irqrestore(&panic_lock, flags);
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block panic_handler_block = {
+	.notifier_call = restore_crash_log,
+};
+
+static int __init panic_handler_probe(struct platform_device *pdev)
+{
+	struct resource *res = pdev->resource;
+	size_t start;
+	size_t buffer_size;
+	void *buffer;
+	int ret = 0;
+#ifdef CONFIG_CPU_CP15_MMU
+	void *ctx_buf;
+	size_t ctx_start;
+#endif
+	if (res == NULL || pdev->num_resources != 1 ||
+			!(res->flags & IORESOURCE_MEM)) {
+		printk(KERN_ERR "lge_panic_handler: invalid resource, %p %d "
+				"flags %lx\n", res, pdev->num_resources,
+				res ? res->flags : 0);
+		return -ENXIO;
+	}
+
+	buffer_size = res->end - res->start + 1;
+	start = res->start;
+	printk(KERN_INFO "lge_panic_handler: got buffer at %zx, size %zx\n",
+			start, buffer_size);
+	buffer = ioremap(res->start, buffer_size);
+	if (buffer == NULL) {
+		printk(KERN_ERR "lge_panic_handler: failed to map memory\n");
+		return -ENOMEM;
+	}
+
+	crash_dump_log = (struct crash_log_dump *)buffer;
+	memset(crash_dump_log, 0, buffer_size);
+	crash_dump_log->magic_key = 0;
+	crash_dump_log->size = 0;
+	crash_buf_size = buffer_size - offsetof(struct crash_log_dump, buffer);
+#ifdef CONFIG_CPU_CP15_MMU
+	ctx_start = res->end + 1;
+	ctx_buf = ioremap(ctx_start,1024);
+	if (ctx_buf == NULL) {
+		printk(KERN_ERR "cpu crash ctx buffer: "
+				"failed to map memory\n");
+		return -ENOMEM;
+	}
+	cpu_crash_ctx = (unsigned long *)ctx_buf;
+#endif
+	atomic_notifier_chain_register(&panic_notifier_list,
+			&panic_handler_block);
+	return ret;
+}
+
+static int __devexit panic_handler_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct platform_driver panic_handler_driver __refdata = {
+	.probe = panic_handler_probe,
+	.remove = __devexit_p(panic_handler_remove),
+	.driver = {
+		.name = PANIC_HANDLER_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init lge_panic_handler_init(void)
+{
+	return platform_driver_register(&panic_handler_driver);
+}
+
+static void __exit lge_panic_handler_exit(void)
+{
+	platform_driver_unregister(&panic_handler_driver);
+}
+
+module_init(lge_panic_handler_init);
+module_exit(lge_panic_handler_exit);
+
+MODULE_DESCRIPTION("LGE panic handler driver");
+MODULE_AUTHOR("SungEun Kim <cleaneye.kim@lge.com>");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-msm/lge/mako/board-mako.c b/arch/arm/mach-msm/lge/mako/board-mako.c
index 468191b..7c14212 100644
--- a/arch/arm/mach-msm/lge/mako/board-mako.c
+++ b/arch/arm/mach-msm/lge/mako/board-mako.c
@@ -2357,11 +2357,10 @@
 	msm_spm_init(msm_spm_data, ARRAY_SIZE(msm_spm_data));
 	msm_spm_l2_init(msm_spm_l2_data);
 	msm_pm_init_sleep_status_data(&msm_pm_slp_sts_data);
-	/**/
 #ifdef CONFIG_ANDROID_RAM_CONSOLE
 	lge_add_ramconsole_devices();
 #endif
-#ifdef CONFIG_LGE_HANDLE_PANIC
+#ifdef CONFIG_LGE_CRASH_HANDLER
 	lge_add_panic_handler_devices();
 #endif
 
diff --git a/arch/arm/mach-msm/lpass-8960.c b/arch/arm/mach-msm/lpass-8960.c
index c58b0e1..a546ff3 100644
--- a/arch/arm/mach-msm/lpass-8960.c
+++ b/arch/arm/mach-msm/lpass-8960.c
@@ -25,6 +25,11 @@
 #include <mach/subsystem_restart.h>
 #include <mach/subsystem_notif.h>
 
+#if defined(CONFIG_LGE_CRASH_HANDLER)
+#include <mach/restart.h>
+#include <mach/board_lge.h>
+#endif
+
 #include "smd_private.h"
 #include "ramdump.h"
 #include "sysmon.h"
@@ -120,6 +125,10 @@
 	pr_err("%s %s: Watchdog bite received from Q6!\n", MODULE_NAME,
 		__func__);
 	lpass_log_failure_reason();
+#if defined(CONFIG_LGE_CRASH_HANDLER)
+	set_ssr_magic_number("lpass");
+	msm_set_restart_mode(0x6d634130);
+#endif
 	panic(MODULE_NAME ": Resetting the SoC");
 }
 
@@ -135,6 +144,10 @@
 			" new_state = 0x%x, old_state = 0x%x\n", __func__,
 			new_state, old_state);
 		lpass_log_failure_reason();
+#if defined(CONFIG_LGE_CRASH_HANDLER)
+		set_ssr_magic_number("lpass");
+		msm_set_restart_mode(0x6d634130);
+#endif
 		panic(MODULE_NAME ": Resetting the SoC");
 	}
 }
diff --git a/arch/arm/mach-msm/restart.c b/arch/arm/mach-msm/restart.c
index 7288c1d..c12f39c 100644
--- a/arch/arm/mach-msm/restart.c
+++ b/arch/arm/mach-msm/restart.c
@@ -47,6 +47,12 @@
 
 #define SCM_IO_DISABLE_PMIC_ARBITER	1
 
+#ifdef CONFIG_LGE_CRASH_HANDLER
+#define LGE_ERROR_HANDLER_MAGIC_NUM	0xA97F2C46
+#define LGE_ERROR_HANDLER_MAGIC_ADDR	0x18
+void *lge_error_handler_cookie_addr;
+#endif
+
 static int restart_mode;
 void *restart_reason;
 
@@ -80,6 +86,10 @@
 		__raw_writel(on ? 0xE47B337D : 0, dload_mode_addr);
 		__raw_writel(on ? 0xCE14091A : 0,
 		       dload_mode_addr + sizeof(unsigned int));
+#ifdef CONFIG_LGE_CRASH_HANDLER
+		__raw_writel(on ? LGE_ERROR_HANDLER_MAGIC_NUM : 0,
+				lge_error_handler_cookie_addr);
+#endif
 		mb();
 	}
 }
@@ -177,6 +187,43 @@
 	return IRQ_HANDLED;
 }
 
+#ifdef CONFIG_LGE_CRASH_HANDLER
+static int ssr_magic_number = 0;
+#define SUBSYS_NAME_MAX_LENGTH	40
+
+int get_ssr_magic_number(void)
+{
+	return ssr_magic_number;
+}
+
+void set_ssr_magic_number(const char* subsys_name)
+{
+	int i;
+	const char *subsys_list[] = {
+		"modem", "riva", "dsps", "lpass",
+		"external_modem", "gss",
+	};
+
+	ssr_magic_number = (0x6d630000 | 0x0000f000);
+
+	for (i=0; i < ARRAY_SIZE(subsys_list); i++) {
+		if (!strncmp(subsys_list[i], subsys_name,
+					SUBSYS_NAME_MAX_LENGTH)) {
+			ssr_magic_number = (0x6d630000 | ((i+1)<<12));
+			break;
+		}
+	}
+}
+
+void set_kernel_crash_magic_number(void)
+{
+	if (ssr_magic_number == 0)
+		__raw_writel(0x6d630100, restart_reason);
+	else
+		__raw_writel(restart_mode, restart_reason);
+}
+#endif /* CONFIG_LGE_CRASH_HANDLER */
+
 void msm_restart(char mode, const char *cmd)
 {
 
@@ -189,8 +236,13 @@
 	set_dload_mode(in_panic);
 
 	/* Write download mode flags if restart_mode says so */
-	if (restart_mode == RESTART_DLOAD)
+	if (restart_mode == RESTART_DLOAD) {
 		set_dload_mode(1);
+#ifdef CONFIG_LGE_CRASH_HANDLER
+		writel(0x6d63c421, restart_reason);
+		goto reset;
+#endif
+	}
 
 	/* Kill download mode if master-kill switch is set */
 	if (!download_mode)
@@ -201,6 +253,28 @@
 
 	pm8xxx_reset_pwr_off(1);
 
+#ifdef CONFIG_LGE_CRASH_HANDLER
+	if (in_panic == 1) {
+		set_kernel_crash_magic_number();
+	} else {
+		if (cmd != NULL) {
+			if (!strncmp(cmd, "bootloader", 10)) {
+				__raw_writel(0x77665500, restart_reason);
+			} else if (!strncmp(cmd, "recovery", 8)) {
+				__raw_writel(0x77665502, restart_reason);
+			} else if (!strncmp(cmd, "oem-", 4)) {
+				unsigned long code;
+				code = simple_strtoul(cmd+4, NULL, 16) & 0xff;
+				__raw_writel(0x6f656d00, restart_reason);
+			} else if (!strncmp(cmd, "recovery", 8)) {
+				__raw_writel(0x77665502, restart_reason);
+			} else {
+				__raw_writel(0x77665501, restart_reason);
+			}
+		}
+	}
+reset:
+#else
 	if (cmd != NULL) {
 		if (!strncmp(cmd, "bootloader", 10)) {
 			__raw_writel(0x77665500, restart_reason);
@@ -214,6 +288,7 @@
 			__raw_writel(0x77665501, restart_reason);
 		}
 	}
+#endif /* CONFIG_LGE_CRASH_HANDLER */
 
 	__raw_writel(0, msm_tmr0_base + WDT0_EN);
 	if (!(machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa())) {
@@ -246,6 +321,10 @@
 		pr_warn("no pmic restart interrupt specified\n");
 	}
 
+#ifdef CONFIG_LGE_CRASH_HANDLER
+	__raw_writel(0x6d63ad00, restart_reason);
+#endif
+
 	return 0;
 }
 
@@ -256,6 +335,10 @@
 #ifdef CONFIG_MSM_DLOAD_MODE
 	atomic_notifier_chain_register(&panic_notifier_list, &panic_blk);
 	dload_mode_addr = MSM_IMEM_BASE + DLOAD_MODE_ADDR;
+#ifdef CONFIG_LGE_CRASH_HANDLER
+	lge_error_handler_cookie_addr = MSM_IMEM_BASE +
+		LGE_ERROR_HANDLER_MAGIC_ADDR;
+#endif
 	set_dload_mode(download_mode);
 #endif
 	msm_tmr0_base = msm_timer_get_timer0_base();
diff --git a/arch/arm/mach-msm/subsystem_restart.c b/arch/arm/mach-msm/subsystem_restart.c
index c98a672..241fde9 100644
--- a/arch/arm/mach-msm/subsystem_restart.c
+++ b/arch/arm/mach-msm/subsystem_restart.c
@@ -34,6 +34,10 @@
 #include <mach/subsystem_notif.h>
 #include <mach/subsystem_restart.h>
 
+#if defined(CONFIG_LGE_CRASH_HANDLER)
+#include <mach/restart.h>
+#include <mach/board_lge.h>
+#endif
 #include "smd_private.h"
 
 struct subsys_soc_restart_order {
@@ -225,6 +229,9 @@
 	struct restart_log *r_log, *temp;
 	static int max_restarts_check;
 	static long max_history_time_check;
+#if defined(CONFIG_LGE_CRASH_HANDLER)
+	int ssr_magic_number = get_ssr_magic_number();
+#endif
 
 	mutex_lock(&restart_log_mutex);
 
@@ -266,10 +273,14 @@
 
 	if (time_first && n >= max_restarts_check) {
 		if ((curr_time->tv_sec - time_first->tv_sec) <
-				max_history_time_check)
+				max_history_time_check) {
+#if defined(CONFIG_LGE_CRASH_HANDLER)
+			msm_set_restart_mode(ssr_magic_number | SUB_UNAB_THD);
+#endif
 			panic("Subsystems have crashed %d times in less than "
 				"%ld seconds!", max_restarts_check,
 				max_history_time_check);
+		}
 	}
 
 out:
@@ -290,6 +301,9 @@
 	int i;
 	int restart_list_count = 0;
 
+#if defined(CONFIG_LGE_CRASH_HANDLER)
+	int ssr_magic_number = get_ssr_magic_number();
+#endif
 	if (r_work->use_restart_order)
 		soc_restart_order = subsys->restart_order;
 
@@ -324,9 +338,13 @@
 	 * sequence for these subsystems. In the latter case, panic and bail
 	 * out, since a subsystem died in its powerup sequence.
 	 */
-	if (!mutex_trylock(powerup_lock))
+	if (!mutex_trylock(powerup_lock)) {
+#if defined(CONFIG_LGE_CRASH_HANDLER)
+		msm_set_restart_mode(ssr_magic_number | SUB_THD_F_PWR);
+#endif
 		panic("%s[%p]: Subsystem died during powerup!",
 						__func__, current);
+	}
 
 	do_epoch_check(subsys);
 
@@ -351,9 +369,13 @@
 		pr_info("[%p]: Shutting down %s\n", current,
 			restart_list[i]->name);
 
-		if (restart_list[i]->shutdown(subsys) < 0)
+		if (restart_list[i]->shutdown(subsys) < 0) {
+#if defined(CONFIG_LGE_CRASH_HANDLER)
+			msm_set_restart_mode(ssr_magic_number | SUB_THD_F_SD);
+#endif
 			panic("subsys-restart: %s[%p]: Failed to shutdown %s!",
 				__func__, current, restart_list[i]->name);
+		}
 	}
 
 	_send_notification_to_order(restart_list, restart_list_count,
@@ -390,9 +412,13 @@
 		pr_info("[%p]: Powering up %s\n", current,
 					restart_list[i]->name);
 
-		if (restart_list[i]->powerup(subsys) < 0)
+		if (restart_list[i]->powerup(subsys) < 0) {
+#if defined(CONFIG_LGE_CRASH_HANDLER)
+			msm_set_restart_mode(ssr_magic_number | SUB_THD_F_PWR);
+#endif
 			panic("%s[%p]: Failed to powerup %s!", __func__,
 				current, restart_list[i]->name);
+		}
 	}
 
 	_send_notification_to_order(restart_list,
@@ -418,14 +444,21 @@
 {
 	struct restart_wq_data *data = NULL;
 	int rc;
+#if defined(CONFIG_LGE_CRASH_HANDLER)
+	int ssr_magic_number = get_ssr_magic_number();
+#endif
 
 	pr_debug("Restarting %s [level=%d]!\n", subsys->name,
 				restart_level);
 
 	data = kzalloc(sizeof(struct restart_wq_data), GFP_ATOMIC);
-	if (!data)
+	if (!data) {
+#if defined(CONFIG_LGE_CRASH_HANDLER)
+		msm_set_restart_mode(ssr_magic_number | SUB_UNAB_THD);
+#endif
 		panic("%s: Unable to allocate memory to restart %s.",
 		      __func__, subsys->name);
+	}
 
 	data->subsys = subsys;
 
@@ -438,14 +471,21 @@
 
 	INIT_WORK(&data->work, subsystem_restart_wq_func);
 	rc = queue_work(ssr_wq, &data->work);
-	if (rc < 0)
+	if (rc < 0) {
+#if defined(CONFIG_LGE_CRASH_HANDLER)
+		msm_set_restart_mode(ssr_magic_number | SUB_UNAB_THD);
+#endif
 		panic("%s: Unable to schedule work to restart %s (%d).",
 		     __func__, subsys->name, rc);
+	}
 }
 
 int subsystem_restart(const char *subsys_name)
 {
 	struct subsys_data *subsys;
+#if defined(CONFIG_LGE_CRASH_HANDLER)
+	u32 ssr_magic_number;
+#endif
 
 	if (!subsys_name) {
 		pr_err("Invalid subsystem name.\n");
@@ -465,6 +505,11 @@
 		return -EINVAL;
 	}
 
+#if defined(CONFIG_LGE_CRASH_HANDLER)
+	set_ssr_magic_number(subsys_name);
+	ssr_magic_number = get_ssr_magic_number();
+#endif
+
 	switch (restart_level) {
 
 	case RESET_SUBSYS_COUPLED:
@@ -473,11 +518,17 @@
 		break;
 
 	case RESET_SOC:
+#if defined(CONFIG_LGE_CRASH_HANDLER)
+		msm_set_restart_mode(ssr_magic_number | SUB_RESET_SOC);
+#endif
 		panic("subsys-restart: Resetting the SoC - %s crashed.",
 			subsys->name);
 		break;
 
 	default:
+#if defined(CONFIG_LGE_CRASH_HANDLER)
+		msm_set_restart_mode(ssr_magic_number | SUB_UNKNOWN);
+#endif
 		panic("subsys-restart: Unknown restart level!\n");
 	break;