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");