blob: 03afc94c33d781db67ef6d17aaf8b151a384733a [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2011, Code Aurora Forum. 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/reboot.h>
16#include <linux/workqueue.h>
17#include <linux/io.h>
18#include <linux/jiffies.h>
19#include <linux/stringify.h>
20#include <linux/delay.h>
21#include <linux/module.h>
22#include <linux/debugfs.h>
23
24#include <mach/irqs.h>
25#include <mach/scm.h>
26#include <mach/peripheral-loader.h>
27#include <mach/subsystem_restart.h>
28#include <mach/subsystem_notif.h>
29#include <mach/irqs-8960.h>
Stepan Moskovchenko6f594732011-09-20 16:51:47 -070030#include <mach/socinfo.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070031
32#include "smd_private.h"
33#include "modem_notifier.h"
34#include "ramdump.h"
35
36static int crash_shutdown;
37
38static void modem_sw_fatal_fn(struct work_struct *work)
39{
40 uint32_t panic_smsm_states = SMSM_RESET | SMSM_SYSTEM_DOWNLOAD;
41 uint32_t reset_smsm_states = SMSM_SYSTEM_REBOOT_USR |
42 SMSM_SYSTEM_PWRDWN_USR;
43 uint32_t modem_state;
44
45 pr_err("Watchdog bite received from modem SW!\n");
46
47 modem_state = smsm_get_state(SMSM_MODEM_STATE);
48
49 if (modem_state & panic_smsm_states) {
50
51 pr_err("Modem SMSM state changed to SMSM_RESET.\n"
52 "Probable err_fatal on the modem. "
53 "Calling subsystem restart...\n");
54 subsystem_restart("modem");
55
56 } else if (modem_state & reset_smsm_states) {
57
58 pr_err("%s: User-invoked system reset/powerdown. "
59 "Resetting the SoC now.\n",
60 __func__);
61 kernel_restart(NULL);
62 } else {
63 /* TODO: Bus unlock code/sequence goes _here_ */
64 subsystem_restart("modem");
65 }
66}
67
68static void modem_fw_fatal_fn(struct work_struct *work)
69{
70 pr_err("Watchdog bite received from modem FW!\n");
71 subsystem_restart("modem");
72}
73
74static DECLARE_WORK(modem_sw_fatal_work, modem_sw_fatal_fn);
75static DECLARE_WORK(modem_fw_fatal_work, modem_fw_fatal_fn);
76
77static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
78{
79 /* Ignore if we're the one that set SMSM_RESET */
80 if (crash_shutdown)
81 return;
82
83 if (new_state & SMSM_RESET) {
84 pr_err("Modem SMSM state changed to SMSM_RESET.\n"
85 "Probable err_fatal on the modem. "
86 "Calling subsystem restart...\n");
87 subsystem_restart("modem");
88 }
89}
90
Saravana Kannan4296f372011-09-21 15:16:22 -070091#define Q6_FW_WDOG_ENABLE 0x08882024
92#define Q6_SW_WDOG_ENABLE 0x08982024
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070093static int modem_shutdown(const struct subsys_data *subsys)
94{
Saravana Kannan4296f372011-09-21 15:16:22 -070095 void __iomem *q6_fw_wdog_addr;
96 void __iomem *q6_sw_wdog_addr;
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -070097 int smsm_notif_unregistered = 0;
98
99 if (!(smsm_get_state(SMSM_MODEM_STATE) & SMSM_RESET)) {
100 smsm_state_cb_deregister(SMSM_MODEM_STATE, SMSM_RESET,
101 smsm_state_cb, 0);
102 smsm_notif_unregistered = 1;
103 smsm_reset_modem(SMSM_RESET);
104 }
105
Saravana Kannan4296f372011-09-21 15:16:22 -0700106 /*
107 * Disable the modem watchdog since it keeps running even after the
108 * modem is shutdown.
109 */
110 q6_fw_wdog_addr = ioremap_nocache(Q6_FW_WDOG_ENABLE, 4);
111 if (!q6_fw_wdog_addr)
112 return -ENOMEM;
113
114 q6_sw_wdog_addr = ioremap_nocache(Q6_SW_WDOG_ENABLE, 4);
115 if (!q6_sw_wdog_addr) {
116 iounmap(q6_fw_wdog_addr);
117 return -ENOMEM;
118 }
119
120 writel_relaxed(0x0, q6_fw_wdog_addr);
121 writel_relaxed(0x0, q6_sw_wdog_addr);
122 mb();
123 iounmap(q6_sw_wdog_addr);
124 iounmap(q6_fw_wdog_addr);
125
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700126 pil_force_shutdown("modem");
Saravana Kannan0dcf4742011-09-21 17:19:17 -0700127 pil_force_shutdown("modem_fw");
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700128 disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
129 disable_irq_nosync(Q6SW_WDOG_EXPIRED_IRQ);
130
131 if (smsm_notif_unregistered)
132 smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
133 smsm_state_cb, 0);
134
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700135 return 0;
136}
137
138static int modem_powerup(const struct subsys_data *subsys)
139{
Saravana Kannan0dcf4742011-09-21 17:19:17 -0700140 pil_force_boot("modem_fw");
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700141 pil_force_boot("modem");
142 enable_irq(Q6FW_WDOG_EXPIRED_IRQ);
143 enable_irq(Q6SW_WDOG_EXPIRED_IRQ);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700144 return 0;
145}
146
147void modem_crash_shutdown(const struct subsys_data *subsys)
148{
149 crash_shutdown = 1;
150 smsm_reset_modem(SMSM_RESET);
151}
152
153int modem_ramdump(int enable, const struct subsys_data *subsys)
154{
155 return 0;
156}
157
158static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
159{
160 int ret;
161
162 switch (irq) {
163
164 case Q6SW_WDOG_EXPIRED_IRQ:
165 ret = schedule_work(&modem_sw_fatal_work);
166 disable_irq_nosync(Q6SW_WDOG_EXPIRED_IRQ);
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700167 disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700168 break;
169 case Q6FW_WDOG_EXPIRED_IRQ:
170 ret = schedule_work(&modem_fw_fatal_work);
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700171 disable_irq_nosync(Q6SW_WDOG_EXPIRED_IRQ);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700172 disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
173 break;
174 break;
175
176 default:
177 pr_err("%s: Unknown IRQ!\n", __func__);
178 }
179
180 return IRQ_HANDLED;
181}
182
183static struct subsys_data modem_8960 = {
184 .name = "modem",
185 .shutdown = modem_shutdown,
186 .powerup = modem_powerup,
187 .ramdump = modem_ramdump,
188 .crash_shutdown = modem_crash_shutdown
189};
190
191static int modem_subsystem_restart_init(void)
192{
193 return ssr_register_subsystem(&modem_8960);
194}
195
196static int modem_debug_set(void *data, u64 val)
197{
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700198 if (val == 1)
199 subsystem_restart("modem");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700200
201 return 0;
202}
203
204static int modem_debug_get(void *data, u64 *val)
205{
206 *val = 0;
207 return 0;
208}
209
210DEFINE_SIMPLE_ATTRIBUTE(modem_debug_fops, modem_debug_get, modem_debug_set,
211 "%llu\n");
212
213static int modem_debugfs_init(void)
214{
215 struct dentry *dent;
216 dent = debugfs_create_dir("modem_debug", 0);
217
218 if (IS_ERR(dent))
219 return PTR_ERR(dent);
220
221 debugfs_create_file("reset_modem", 0644, dent, NULL,
222 &modem_debug_fops);
223 return 0;
224}
225
226static int __init modem_8960_init(void)
227{
228 int ret;
229
Stepan Moskovchenko6f594732011-09-20 16:51:47 -0700230 if (!cpu_is_msm8960())
231 return -ENODEV;
232
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700233 ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
234 smsm_state_cb, 0);
235
236 if (ret < 0)
237 pr_err("%s: Unable to register SMSM callback! (%d)\n",
238 __func__, ret);
239
240 ret = request_irq(Q6FW_WDOG_EXPIRED_IRQ, modem_wdog_bite_irq,
241 IRQF_TRIGGER_RISING, "modem_wdog_fw", NULL);
242
243 if (ret < 0) {
244 pr_err("%s: Unable to request q6fw watchdog IRQ. (%d)\n",
245 __func__, ret);
246 goto out;
247 }
248
249 ret = request_irq(Q6SW_WDOG_EXPIRED_IRQ, modem_wdog_bite_irq,
250 IRQF_TRIGGER_RISING, "modem_wdog_sw", NULL);
251
252 if (ret < 0) {
253 pr_err("%s: Unable to request q6sw watchdog IRQ. (%d)\n",
254 __func__, ret);
255 disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
256 goto out;
257 }
258
259 ret = modem_subsystem_restart_init();
260
261 if (ret < 0) {
262 pr_err("%s: Unable to reg with subsystem restart. (%d)\n",
263 __func__, ret);
264 goto out;
265 }
266
267 ret = modem_debugfs_init();
268
269 pr_info("%s: 8960 modem fatal driver init'ed.\n", __func__);
270out:
271 return ret;
272}
273
274module_init(modem_8960_init);