blob: fec578f32f3b9039a1b379bd84e7d051e4fb48c2 [file] [log] [blame]
Vikram Mulukutla74f95262012-06-25 10:21:41 -07001/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/kernel.h>
14#include <linux/interrupt.h>
15#include <linux/module.h>
16#include <linux/err.h>
17
18#include <mach/subsystem_restart.h>
19#include <mach/msm_smsm.h>
20
21static int crash_shutdown;
22static struct subsys_device *modem_ssr_dev;
23
24#define MAX_SSR_REASON_LEN 81U
25#define Q6SS_WDOG_ENABLE 0xFC802004
26#define MSS_Q6SS_WDOG_EXP_IRQ 56
27
28static void log_modem_sfr(void)
29{
30 u32 size;
31 char *smem_reason, reason[MAX_SSR_REASON_LEN];
32
33 smem_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size);
34 if (!smem_reason || !size) {
35 pr_err("modem subsystem failure reason: (unknown, smem_get_entry failed).\n");
36 return;
37 }
38 if (!smem_reason[0]) {
39 pr_err("modem subsystem failure reason: (unknown, empty string found).\n");
40 return;
41 }
42
43 strlcpy(reason, smem_reason, min(size, sizeof(reason)));
44 pr_err("modem subsystem failure reason: %s.\n", reason);
45
46 smem_reason[0] = '\0';
47 wmb();
48}
49
50static void restart_modem(void)
51{
52 log_modem_sfr();
53 subsystem_restart("modem");
54}
55
56static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
57{
58 /* Ignore if we're the one that set SMSM_RESET */
59 if (crash_shutdown)
60 return;
61
62 if (new_state & SMSM_RESET) {
63 pr_err("Probable fatal error on the modem.\n");
64 restart_modem();
65 }
66}
67
68static int modem_shutdown(const struct subsys_desc *subsys)
69{
70 return 0;
71}
72
73static int modem_powerup(const struct subsys_desc *subsys)
74{
75 return 0;
76}
77
78void modem_crash_shutdown(const struct subsys_desc *subsys)
79{
80 crash_shutdown = 1;
81 smsm_reset_modem(SMSM_RESET);
82}
83
84static int modem_ramdump(int enable,
85 const struct subsys_desc *crashed_subsys)
86{
87 return 0;
88}
89
90static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
91{
92 pr_err("Watchdog bite received from modem software!\n");
93 restart_modem();
94 return IRQ_HANDLED;
95}
96
97static struct subsys_desc modem_8974 = {
98 .name = "modem",
99 .shutdown = modem_shutdown,
100 .powerup = modem_powerup,
101 .ramdump = modem_ramdump,
102 .crash_shutdown = modem_crash_shutdown
103};
104
105static int __init modem_8974_init(void)
106{
107 int ret;
108
109 ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
110 smsm_state_cb, 0);
111
112 if (ret < 0) {
113 pr_err("%s: Unable to register SMSM callback! (%d)\n",
114 __func__, ret);
115 goto out;
116 }
117
118 ret = request_irq(MSS_Q6SS_WDOG_EXP_IRQ, modem_wdog_bite_irq,
119 IRQF_TRIGGER_RISING, "modem_wdog_sw", NULL);
120
121 if (ret < 0) {
122 pr_err("%s: Unable to request q6sw watchdog IRQ. (%d)\n",
123 __func__, ret);
124 goto out;
125 }
126
127 modem_ssr_dev = subsys_register(&modem_8974);
128
129 if (IS_ERR_OR_NULL(modem_ssr_dev)) {
130 pr_err("%s: Unable to reg with subsystem restart. (%ld)\n",
131 __func__, PTR_ERR(modem_ssr_dev));
132 ret = PTR_ERR(modem_ssr_dev);
133 goto out;
134 }
135
136 pr_info("%s: modem subsystem restart driver init'ed.\n", __func__);
137out:
138 return ret;
139}
140
141arch_initcall(modem_8974_init);