Initial Contribution

msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142

Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/arch/arm/mach-msm/modem-8960.c b/arch/arm/mach-msm/modem-8960.c
new file mode 100644
index 0000000..907a1cf
--- /dev/null
+++ b/arch/arm/mach-msm/modem-8960.c
@@ -0,0 +1,227 @@
+/* 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/jiffies.h>
+#include <linux/stringify.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/debugfs.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/irqs-8960.h>
+
+#include "smd_private.h"
+#include "modem_notifier.h"
+#include "ramdump.h"
+
+static int crash_shutdown;
+
+static void modem_sw_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 modem_state;
+
+	pr_err("Watchdog bite received from modem SW!\n");
+
+	modem_state = smsm_get_state(SMSM_MODEM_STATE);
+
+	if (modem_state & panic_smsm_states) {
+
+		pr_err("Modem SMSM state changed to SMSM_RESET.\n"
+			"Probable err_fatal on the modem. "
+			"Calling subsystem restart...\n");
+		subsystem_restart("modem");
+
+	} else if (modem_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("modem");
+	}
+}
+
+static void modem_fw_fatal_fn(struct work_struct *work)
+{
+	pr_err("Watchdog bite received from modem FW!\n");
+	subsystem_restart("modem");
+}
+
+static DECLARE_WORK(modem_sw_fatal_work, modem_sw_fatal_fn);
+static DECLARE_WORK(modem_fw_fatal_work, modem_fw_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("Modem SMSM state changed to SMSM_RESET.\n"
+			"Probable err_fatal on the modem. "
+			"Calling subsystem restart...\n");
+		subsystem_restart("modem");
+	}
+}
+
+static int modem_shutdown(const struct subsys_data *subsys)
+{
+	/* TODO: Call into PIL to shutdown the modem */
+	return 0;
+}
+
+static int modem_powerup(const struct subsys_data *subsys)
+{
+	/* TODO: Call into PIL to powerup the modem */
+	return 0;
+}
+
+void modem_crash_shutdown(const struct subsys_data *subsys)
+{
+	crash_shutdown = 1;
+	smsm_reset_modem(SMSM_RESET);
+}
+
+int modem_ramdump(int enable, const struct subsys_data *subsys)
+{
+	return 0;
+}
+
+static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
+{
+	int ret;
+
+	switch (irq) {
+
+	case Q6SW_WDOG_EXPIRED_IRQ:
+		ret = schedule_work(&modem_sw_fatal_work);
+		disable_irq_nosync(Q6SW_WDOG_EXPIRED_IRQ);
+		break;
+	case Q6FW_WDOG_EXPIRED_IRQ:
+		ret = schedule_work(&modem_fw_fatal_work);
+		disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
+		break;
+	break;
+
+	default:
+		pr_err("%s: Unknown IRQ!\n", __func__);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static struct subsys_data modem_8960 = {
+	.name = "modem",
+	.shutdown = modem_shutdown,
+	.powerup = modem_powerup,
+	.ramdump = modem_ramdump,
+	.crash_shutdown = modem_crash_shutdown
+};
+
+static int modem_subsystem_restart_init(void)
+{
+	return ssr_register_subsystem(&modem_8960);
+}
+
+static int modem_debug_set(void *data, u64 val)
+{
+	if (val == 1) {
+		pr_info("%s: Intentionally setting the SMSM_RESET bit.\n",
+				__func__);
+		smsm_reset_modem(SMSM_RESET);
+	}
+
+	return 0;
+}
+
+static int modem_debug_get(void *data, u64 *val)
+{
+	*val = 0;
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(modem_debug_fops, modem_debug_get, modem_debug_set,
+				"%llu\n");
+
+static int modem_debugfs_init(void)
+{
+	struct dentry *dent;
+	dent = debugfs_create_dir("modem_debug", 0);
+
+	if (IS_ERR(dent))
+		return PTR_ERR(dent);
+
+	debugfs_create_file("reset_modem", 0644, dent, NULL,
+		&modem_debug_fops);
+	return 0;
+}
+
+static int __init modem_8960_init(void)
+{
+	int ret;
+
+	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(Q6FW_WDOG_EXPIRED_IRQ, modem_wdog_bite_irq,
+			IRQF_TRIGGER_RISING, "modem_wdog_fw", NULL);
+
+	if (ret < 0) {
+		pr_err("%s: Unable to request q6fw watchdog IRQ. (%d)\n",
+				__func__, ret);
+		goto out;
+	}
+
+	ret = request_irq(Q6SW_WDOG_EXPIRED_IRQ, modem_wdog_bite_irq,
+			IRQF_TRIGGER_RISING, "modem_wdog_sw", NULL);
+
+	if (ret < 0) {
+		pr_err("%s: Unable to request q6sw watchdog IRQ. (%d)\n",
+				__func__, ret);
+		disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
+		goto out;
+	}
+
+	ret = modem_subsystem_restart_init();
+
+	if (ret < 0) {
+		pr_err("%s: Unable to reg with subsystem restart. (%d)\n",
+				__func__, ret);
+		goto out;
+	}
+
+	ret = modem_debugfs_init();
+
+	pr_info("%s: 8960 modem fatal driver init'ed.\n", __func__);
+out:
+	return ret;
+}
+
+module_init(modem_8960_init);