msm: gss-8064: Add support for GSS sub-system restart
The GSS subsystem supports subsystem restart using PIL.
The subsystem restart driver registers for the smsm state
change notification and also the GSS watchdog bark
Change-Id: I89a138c9d0781d14b526752095cce4e0d2dcc326
Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org>
diff --git a/arch/arm/mach-msm/gss-8064.c b/arch/arm/mach-msm/gss-8064.c
new file mode 100644
index 0000000..1ddb7a3
--- /dev/null
+++ b/arch/arm/mach-msm/gss-8064.c
@@ -0,0 +1,259 @@
+/* Copyright (c) 2012, 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/jiffies.h>
+#include <linux/stringify.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+
+#include <mach/irqs.h>
+#include <mach/scm.h>
+#include <mach/peripheral-loader.h>
+#include <mach/subsystem_restart.h>
+#include <mach/subsystem_notif.h>
+#include <mach/socinfo.h>
+
+#include "smd_private.h"
+#include "modem_notifier.h"
+#include "ramdump.h"
+
+static struct gss_8064_data {
+ struct miscdevice gss_dev;
+ void *pil_handle;
+ void *gss_ramdump_dev;
+ void *smem_ramdump_dev;
+} gss_data;
+
+static int crash_shutdown;
+
+static void gss_fatal_fn(struct work_struct *work)
+{
+ uint32_t panic_smsm_states = SMSM_RESET | SMSM_SYSTEM_DOWNLOAD;
+ uint32_t reset_smsm_states = SMSM_SYSTEM_REBOOT_USR |
+ SMSM_SYSTEM_PWRDWN_USR;
+ uint32_t gss_state;
+
+ pr_err("Watchdog bite received from GSS!\n");
+
+ gss_state = smsm_get_state(SMSM_MODEM_STATE);
+
+ if (gss_state & panic_smsm_states) {
+
+ pr_err("GSS SMSM state changed to SMSM_RESET.\n"
+ "Probable err_fatal on the GSS. "
+ "Calling subsystem restart...\n");
+ subsystem_restart("gss");
+
+ } else if (gss_state & reset_smsm_states) {
+
+ pr_err("%s: User-invoked system reset/powerdown. "
+ "Resetting the SoC now.\n",
+ __func__);
+ kernel_restart(NULL);
+ } else {
+ /* TODO: Bus unlock code/sequence goes _here_ */
+ subsystem_restart("gss");
+ }
+}
+
+static DECLARE_WORK(gss_fatal_work, gss_fatal_fn);
+
+static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
+{
+ /* Ignore if we're the one that set SMSM_RESET */
+ if (crash_shutdown)
+ return;
+
+ if (new_state & SMSM_RESET) {
+ pr_err("GSS SMSM state changed to SMSM_RESET.\n"
+ "Probable err_fatal on the GSS. "
+ "Calling subsystem restart...\n");
+ subsystem_restart("gss");
+ }
+}
+
+#define Q6_FW_WDOG_ENABLE 0x08882024
+#define Q6_SW_WDOG_ENABLE 0x08982024
+static int gss_shutdown(const struct subsys_data *subsys)
+{
+ pil_force_shutdown("gss");
+ disable_irq_nosync(GSS_A5_WDOG_EXPIRED);
+
+ return 0;
+}
+
+static int gss_powerup(const struct subsys_data *subsys)
+{
+ pil_force_boot("gss");
+ enable_irq(GSS_A5_WDOG_EXPIRED);
+ return 0;
+}
+
+void gss_crash_shutdown(const struct subsys_data *subsys)
+{
+ crash_shutdown = 1;
+ smsm_reset_modem(SMSM_RESET);
+}
+
+/* FIXME: Get address, size from PIL */
+static struct ramdump_segment gss_segments[] = {
+ {0x89D00000 - 0x89000000}
+};
+
+static struct ramdump_segment smem_segments[] = {
+ {0x80000000, 0x00200000},
+};
+
+static int gss_ramdump(int enable,
+ const struct subsys_data *crashed_subsys)
+{
+ int ret = 0;
+
+ if (enable) {
+ ret = do_ramdump(gss_data.gss_ramdump_dev, gss_segments,
+ ARRAY_SIZE(gss_segments));
+
+ if (ret < 0) {
+ pr_err("Unable to dump gss memory (rc = %d).\n",
+ ret);
+ goto out;
+ }
+
+ ret = do_ramdump(gss_data.smem_ramdump_dev, smem_segments,
+ ARRAY_SIZE(smem_segments));
+
+ if (ret < 0) {
+ pr_err("Unable to dump smem memory (rc = %d).\n", ret);
+ goto out;
+ }
+ }
+
+out:
+ return ret;
+}
+
+static irqreturn_t gss_wdog_bite_irq(int irq, void *dev_id)
+{
+ schedule_work(&gss_fatal_work);
+ disable_irq_nosync(GSS_A5_WDOG_EXPIRED);
+
+ return IRQ_HANDLED;
+}
+
+static struct subsys_data gss_8064 = {
+ .name = "gss",
+ .shutdown = gss_shutdown,
+ .powerup = gss_powerup,
+ .ramdump = gss_ramdump,
+ .crash_shutdown = gss_crash_shutdown
+};
+
+static int gss_subsystem_restart_init(void)
+{
+ return ssr_register_subsystem(&gss_8064);
+}
+
+static int gss_open(struct inode *inode, struct file *filep)
+{
+ void *ret;
+ gss_data.pil_handle = ret = pil_get("gss");
+ if (!ret)
+ pr_debug("%s - pil_get returned NULL\n", __func__);
+ return 0;
+}
+
+static int gss_release(struct inode *inode, struct file *filep)
+{
+ pil_put(gss_data.pil_handle);
+ pr_debug("%s pil_put called on GSS\n", __func__);
+ return 0;
+}
+
+const struct file_operations gss_file_ops = {
+ .open = gss_open,
+ .release = gss_release,
+};
+
+static int __init gss_8064_init(void)
+{
+ int ret;
+
+ if (!cpu_is_apq8064())
+ return -ENODEV;
+
+ ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
+ smsm_state_cb, 0);
+
+ if (ret < 0)
+ pr_err("%s: Unable to register SMSM callback! (%d)\n",
+ __func__, ret);
+
+ ret = request_irq(GSS_A5_WDOG_EXPIRED, gss_wdog_bite_irq,
+ IRQF_TRIGGER_RISING, "gss_a5_wdog", NULL);
+
+ if (ret < 0) {
+ pr_err("%s: Unable to request gss watchdog IRQ. (%d)\n",
+ __func__, ret);
+ disable_irq_nosync(GSS_A5_WDOG_EXPIRED);
+ goto out;
+ }
+
+ ret = gss_subsystem_restart_init();
+
+ if (ret < 0) {
+ pr_err("%s: Unable to reg with subsystem restart. (%d)\n",
+ __func__, ret);
+ goto out;
+ }
+
+ gss_data.gss_dev.minor = MISC_DYNAMIC_MINOR;
+ gss_data.gss_dev.name = "gss";
+ gss_data.gss_dev.fops = &gss_file_ops;
+ ret = misc_register(&gss_data.gss_dev);
+
+ if (ret) {
+ pr_err("%s: misc_registers failed for %s (%d)", __func__,
+ gss_data.gss_dev.name, ret);
+ goto out;
+ }
+
+ gss_data.gss_ramdump_dev = create_ramdump_device("gss");
+
+ if (!gss_data.gss_ramdump_dev) {
+ pr_err("%s: Unable to create gss ramdump device. (%d)\n",
+ __func__, -ENOMEM);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ gss_data.smem_ramdump_dev = create_ramdump_device("smem");
+
+ if (!gss_data.smem_ramdump_dev) {
+ pr_err("%s: Unable to create smem ramdump device. (%d)\n",
+ __func__, -ENOMEM);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ pr_info("%s: gss fatal driver init'ed.\n", __func__);
+out:
+ return ret;
+}
+
+module_init(gss_8064_init);