msm: 8960: Add the WCNSS restart module for 8960

This driver monitors WCNSS hardware watchdog interrupt line,
and it plugs WCNSS into Subsytem restart framework.

Acked-by: Sameer Thalappil<sameert@qca.qualcomm.com>
Signed-off-by: Ankur Nandwani <ankurn@codeaurora.org>

Conflicts:

	arch/arm/configs/msm8960_defconfig
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index c74ca7c..d6e14d1 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -55,6 +55,7 @@
 CONFIG_MSM_SUBSYSTEM_RESTART=y
 CONFIG_MSM_MODEM_8960=y
 CONFIG_MSM_BUS_SCALING=y
+CONFIG_MSM_WCNSS_SSR_8960=y
 CONFIG_MSM_WATCHDOG=y
 CONFIG_MSM_DLOAD_MODE=y
 CONFIG_MSM_SLEEP_STATS=y
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index f3c682e..58e2770 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -1417,6 +1417,14 @@
 	 lpass hardware watchdog interrupt lines and plugs into the subsystem
 	 restart and PIL drivers.
 
+config MSM_WCNSS_SSR_8960
+	tristate "MSM 8960 WCNSS restart module"
+	depends on (ARCH_MSM8960)
+	help
+	 This option enables the WCNSS restart module for MSM8960, which
+	 monitors WCNSS hardware watchdog interrupt lines and plugs WCNSS
+	 into the subsystem restart framework.
+
 config SCORPION_Uni_45nm_BUG
 	bool "Scorpion Uni 45nm(SC45U): Workaround for ICIMVAU and BPIMVA"
 	depends on ARCH_MSM7X30 || (ARCH_QSD8X50 && MSM_SOC_REV_A)
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 1c524b4..f51cd5b 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -155,6 +155,7 @@
 endif
 obj-$(CONFIG_MSM_MODEM_8960) += modem-8960.o
 obj-$(CONFIG_MSM_LPASS_8960) += lpass-8960.o
+obj-$(CONFIG_MSM_WCNSS_SSR_8960) += wcnss-ssr-8960.o
 
 ifdef CONFIG_CPU_IDLE
 	obj-$(CONFIG_ARCH_MSM8960) += cpuidle.o
diff --git a/arch/arm/mach-msm/wcnss-ssr-8960.c b/arch/arm/mach-msm/wcnss-ssr-8960.c
new file mode 100644
index 0000000..8615de5
--- /dev/null
+++ b/arch/arm/mach-msm/wcnss-ssr-8960.c
@@ -0,0 +1,183 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/interrupt.h>
+#include <linux/reboot.h>
+#include <linux/workqueue.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <mach/irqs.h>
+#include <mach/scm.h>
+#include <mach/subsystem_restart.h>
+#include <mach/subsystem_notif.h>
+#include "smd_private.h"
+#include "ramdump.h"
+
+#define MODULE_NAME			"wcnss_8960"
+
+static void riva_smsm_cb_fn(struct work_struct *);
+static DECLARE_WORK(riva_smsm_cb_work, riva_smsm_cb_fn);
+
+static void riva_fatal_fn(struct work_struct *);
+static DECLARE_WORK(riva_fatal_work, riva_fatal_fn);
+
+static void *riva_ramdump_dev;
+static int riva_crash;
+static int ss_restart_inprogress;
+
+static void riva_smsm_cb_fn(struct work_struct *work)
+{
+	pr_err("%s: Initiating subsytem restart\n", MODULE_NAME);
+	subsystem_restart("riva");
+}
+
+static void smsm_state_cb_hdlr(void *data, uint32_t old_state,
+					uint32_t new_state)
+{
+	riva_crash = true;
+	pr_err("%s: smsm state changed to smsm reset\n", MODULE_NAME);
+
+	if (ss_restart_inprogress) {
+		pr_err("%s: Ignoring smsm reset req, restart in progress\n",
+						MODULE_NAME);
+		return;
+	}
+	if (new_state & SMSM_RESET)
+		schedule_work(&riva_smsm_cb_work);
+}
+
+static void riva_fatal_fn(struct work_struct *work)
+{
+	pr_err("%s: Watchdog bite received from Riva\n", MODULE_NAME);
+	if (!ss_restart_inprogress)
+		subsystem_restart("riva");
+}
+
+static irqreturn_t riva_wdog_bite_irq_hdlr(int irq, void *dev_id)
+{
+	riva_crash = true;
+	pr_debug("%s: rxed irq[0x%x]", MODULE_NAME, irq);
+	disable_irq_nosync(RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ);
+	schedule_work(&riva_fatal_work);
+
+	return IRQ_HANDLED;
+}
+
+/* SMSM reset Riva */
+static void smsm_riva_reset(void)
+{
+	/* per SS reset request bit is not available now,
+	 * all SS host modules are setting this bit
+	 * This is still under discussion*/
+	smsm_change_state(SMSM_APPS_STATE, SMSM_RESET, SMSM_RESET);
+}
+
+/* Subsystem handlers */
+static int riva_shutdown(const struct subsys_data *subsys)
+{
+	/* TODO for phase 3 */
+	return 0;
+}
+
+static int riva_powerup(const struct subsys_data *subsys)
+{
+	/* TODO for phase 3 */
+	return 0;
+}
+
+/* RAM segments for Riva SS;
+ * We don't specify the full 5MB allocated for Riva. Only 3MB is specified */
+static struct ramdump_segment riva_segments[] = {{0x8f200000,
+						0x8f500000 - 0x8f200000} };
+
+static int riva_ramdump(int enable, const struct subsys_data *subsys)
+{
+	pr_debug("%s: enable[%d]\n", MODULE_NAME, enable);
+	if (enable)
+		return do_ramdump(riva_ramdump_dev,
+				riva_segments,
+				ARRAY_SIZE(riva_segments));
+	else
+		return 0;
+}
+
+/* Riva crash handler */
+static void riva_crash_shutdown(const struct subsys_data *subsys)
+{
+	ss_restart_inprogress = true;
+
+	pr_err("%s: crash shutdown : %d\n", MODULE_NAME, riva_crash);
+	if (riva_crash != true)
+		smsm_riva_reset();
+}
+
+static struct subsys_data riva_8960 = {
+	.name = "riva",
+	.shutdown = riva_shutdown,
+	.powerup = riva_powerup,
+	.ramdump = riva_ramdump,
+	.crash_shutdown = riva_crash_shutdown
+};
+
+static int __init riva_restart_init(void)
+{
+	return ssr_register_subsystem(&riva_8960);
+}
+
+static int __init riva_ssr_module_init(void)
+{
+	int ret;
+
+	ret = smsm_state_cb_register(SMSM_WCNSS_STATE, SMSM_RESET,
+					smsm_state_cb_hdlr, 0);
+	if (ret < 0) {
+		pr_err("%s: Unable to register smsm callback for Riva Reset!"
+				" (%d)\n", MODULE_NAME, ret);
+		goto out;
+	}
+	ret = request_irq(RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ,
+			riva_wdog_bite_irq_hdlr,
+			IRQF_TRIGGER_HIGH, "riva_wdog", NULL);
+	if (ret < 0) {
+		pr_err("%s: Unable to register RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ irq.",
+			MODULE_NAME);
+		goto out;
+	}
+	ret = riva_restart_init();
+	if (ret < 0) {
+		pr_err("%s: Unable to register with ssr. (%d)\n",
+				MODULE_NAME, ret);
+		goto out;
+	}
+	riva_ramdump_dev = create_ramdump_device("riva");
+	if (!riva_ramdump_dev) {
+		pr_err("%s: Unable to create ramdump device.\n",
+				MODULE_NAME);
+		ret = -ENOMEM;
+		goto out;
+	}
+	pr_info("%s: module initialized\n", MODULE_NAME);
+out:
+	return ret;
+}
+
+static void __exit riva_ssr_module_exit(void)
+{
+	free_irq(RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ, NULL);
+}
+
+module_init(riva_ssr_module_init);
+module_exit(riva_ssr_module_exit);
+
+MODULE_LICENSE("GPL v2");