blob: b8305916e3fc912474a4c33c1a54aaa22644b21e [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
23#include <mach/irqs.h>
24#include <mach/scm.h>
25#include <mach/peripheral-loader.h>
26#include <mach/subsystem_restart.h>
27#include <mach/subsystem_notif.h>
28
29#include "smd_private.h"
30#include "modem_notifier.h"
31#include "ramdump.h"
32
33#define MODEM_HWIO_MSS_RESET_ADDR 0x00902C48
34#define SCM_Q6_NMI_CMD 0x1
35#define MODULE_NAME "subsystem_fatal_8x60"
36#define Q6SS_SOFT_INTR_WAKEUP 0x288A001C
37#define MODEM_WDOG_ENABLE 0x10020008
38#define Q6SS_WDOG_ENABLE 0x28882024
Vikram Mulukutla89549562011-07-28 16:10:41 -070039#define MODEM_CLEANUP_DELAY_MS 20
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070040
41#define SUBSYS_FATAL_DEBUG
42
43#if defined(SUBSYS_FATAL_DEBUG)
44static void debug_crash_modem_fn(struct work_struct *);
45static int reset_modem;
46
47static DECLARE_DELAYED_WORK(debug_crash_modem_work,
48 debug_crash_modem_fn);
49
50module_param(reset_modem, int, 0644);
51#endif
52
53static void do_soc_restart(void);
54
55/* Subsystem restart: QDSP6 data, functions */
56static void q6_fatal_fn(struct work_struct *);
57static DECLARE_WORK(q6_fatal_work, q6_fatal_fn);
58static void *q6_ramdump_dev, *modem_ramdump_dev;
59
60static void q6_fatal_fn(struct work_struct *work)
61{
62 pr_err("%s: Watchdog bite received from Q6!\n", MODULE_NAME);
63 subsystem_restart("lpass");
64}
65
66static void send_q6_nmi(void)
67{
68 /* Send NMI to QDSP6 via an SCM call. */
69 uint32_t cmd = 0x1;
70 void __iomem *q6_wakeup_intr;
71
72 scm_call(SCM_SVC_UTIL, SCM_Q6_NMI_CMD,
73 &cmd, sizeof(cmd), NULL, 0);
74
75 /* Wakeup the Q6 */
76 q6_wakeup_intr = ioremap_nocache(Q6SS_SOFT_INTR_WAKEUP, 8);
77 writel_relaxed(0x2000, q6_wakeup_intr);
78 iounmap(q6_wakeup_intr);
79 mb();
80
Vikram Mulukutla189724d2011-07-22 13:24:50 -070081 /* Q6 requires atleast 100ms to dump caches etc.*/
82 msleep(100);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070083
84 pr_info("subsystem-fatal-8x60: Q6 NMI was sent.\n");
85}
86
87int subsys_q6_shutdown(const struct subsys_data *crashed_subsys)
88{
89 void __iomem *q6_wdog_addr =
90 ioremap_nocache(Q6SS_WDOG_ENABLE, 8);
91
92 send_q6_nmi();
93 writel_relaxed(0x0, q6_wdog_addr);
94 /* The write needs to go through before the q6 is shutdown. */
95 mb();
96 iounmap(q6_wdog_addr);
97
98 pil_force_shutdown("q6");
99 disable_irq_nosync(LPASS_Q6SS_WDOG_EXPIRED);
100
101 if (get_restart_level() == RESET_SUBSYS_MIXED)
102 smsm_reset_modem(SMSM_RESET);
103
104 return 0;
105}
106
107int subsys_q6_powerup(const struct subsys_data *crashed_subsys)
108{
109 int ret = pil_force_boot("q6");
110 enable_irq(LPASS_Q6SS_WDOG_EXPIRED);
111 return ret;
112}
113
114/* FIXME: Get address, size from PIL */
115static struct ramdump_segment q6_segments[] = { {0x46700000, 0x47F00000 -
116 0x46700000}, {0x28400000, 0x12800} };
117static int subsys_q6_ramdump(int enable,
118 const struct subsys_data *crashed_subsys)
119{
120 if (enable)
121 return do_ramdump(q6_ramdump_dev, q6_segments,
122 ARRAY_SIZE(q6_segments));
123 else
124 return 0;
125}
126
127void subsys_q6_crash_shutdown(const struct subsys_data *crashed_subsys)
128{
129 send_q6_nmi();
130}
131
132/* Subsystem restart: Modem data, functions */
133static void modem_fatal_fn(struct work_struct *);
134static void modem_unlock_timeout(struct work_struct *work);
135static int modem_notif_handler(struct notifier_block *this,
136 unsigned long code,
137 void *_cmd);
138static DECLARE_WORK(modem_fatal_work, modem_fatal_fn);
139static DECLARE_DELAYED_WORK(modem_unlock_timeout_work,
140 modem_unlock_timeout);
141
142static struct notifier_block modem_notif_nb = {
143 .notifier_call = modem_notif_handler,
144};
145
146static void modem_unlock_timeout(struct work_struct *work)
147{
148 void __iomem *hwio_modem_reset_addr =
149 ioremap_nocache(MODEM_HWIO_MSS_RESET_ADDR, 8);
150 pr_crit("%s: Timeout waiting for modem to unlock.\n", MODULE_NAME);
151
152 /* Set MSS_MODEM_RESET to 0x0 since the unlock didn't work */
153 writel_relaxed(0x0, hwio_modem_reset_addr);
154 /* Write needs to go through before the modem is restarted. */
155 mb();
156 iounmap(hwio_modem_reset_addr);
157
158 subsystem_restart("modem");
159}
160
161static void modem_fatal_fn(struct work_struct *work)
162{
163 uint32_t modem_state;
164 uint32_t panic_smsm_states = SMSM_RESET | SMSM_SYSTEM_DOWNLOAD;
165 uint32_t reset_smsm_states = SMSM_SYSTEM_REBOOT_USR |
166 SMSM_SYSTEM_PWRDWN_USR;
167
168 pr_err("%s: Watchdog bite received from modem!\n", MODULE_NAME);
169
170 modem_state = smsm_get_state(SMSM_MODEM_STATE);
171 pr_err("%s: Modem SMSM state = 0x%x!", MODULE_NAME, modem_state);
172
173 if (modem_state == 0 || modem_state & panic_smsm_states) {
174
175 subsystem_restart("modem");
176
177 } else if (modem_state & reset_smsm_states) {
178
179 pr_err("%s: User-invoked system reset/powerdown.",
180 MODULE_NAME);
181 do_soc_restart();
182
183 } else {
184
185 int ret;
186 void *hwio_modem_reset_addr =
187 ioremap_nocache(MODEM_HWIO_MSS_RESET_ADDR, 8);
188
189 pr_err("%s: Modem AHB locked up.\n", MODULE_NAME);
190 pr_err("%s: Trying to free up modem!\n", MODULE_NAME);
191
192 writel(0x3, hwio_modem_reset_addr);
193
194 /* If we are still alive after 6 seconds (allowing for
195 * the 5-second-delayed-panic-reboot), modem is either
196 * still wedged or SMSM didn't come through. Force panic
197 * in that case.
198 */
199 ret = schedule_delayed_work(&modem_unlock_timeout_work,
200 msecs_to_jiffies(6000));
201
202 iounmap(hwio_modem_reset_addr);
203 }
204}
205
206static int modem_notif_handler(struct notifier_block *this,
207 unsigned long code,
208 void *_cmd)
209{
210 if (code == MODEM_NOTIFIER_START_RESET) {
211
212 pr_err("%s: Modem error fatal'ed.", MODULE_NAME);
213 subsystem_restart("modem");
214 }
215 return NOTIFY_DONE;
216}
217
218static int subsys_modem_shutdown(const struct subsys_data *crashed_subsys)
219{
220 void __iomem *modem_wdog_addr;
221 int smsm_notif_unregistered = 0;
222
223 /* If the modem didn't already crash, setting SMSM_RESET
224 * here will help flush caches etc. Unregister for SMSM
225 * notifications to prevent unnecessary secondary calls to
226 * subsystem_restart.
227 */
228 if (!(smsm_get_state(SMSM_MODEM_STATE) & SMSM_RESET)) {
229 modem_unregister_notifier(&modem_notif_nb);
230 smsm_notif_unregistered = 1;
231 smsm_reset_modem(SMSM_RESET);
232 }
233
234 /* Disable the modem watchdog to allow clean modem bootup */
235 modem_wdog_addr = ioremap_nocache(MODEM_WDOG_ENABLE, 8);
236 writel_relaxed(0x0, modem_wdog_addr);
237
238 /*
239 * The write above needs to go through before the modem is
240 * powered up again (subsystem restart).
241 */
242 mb();
243 iounmap(modem_wdog_addr);
244
Vikram Mulukutla89549562011-07-28 16:10:41 -0700245 /* Wait here to allow the modem to clean up caches etc. */
246 msleep(MODEM_CLEANUP_DELAY_MS);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700247 pil_force_shutdown("modem");
248 disable_irq_nosync(MARM_WDOG_EXPIRED);
249
250 /* Re-register for SMSM notifications if necessary */
251 if (smsm_notif_unregistered)
252 modem_register_notifier(&modem_notif_nb);
253
254
255 return 0;
256}
257
258static int subsys_modem_powerup(const struct subsys_data *crashed_subsys)
259{
260 int ret;
261
262 ret = pil_force_boot("modem");
263 enable_irq(MARM_WDOG_EXPIRED);
264
265 return ret;
266}
267
268/* FIXME: Get address, size from PIL */
269static struct ramdump_segment modem_segments[] = {
270 {0x42F00000, 0x46000000 - 0x42F00000} };
271
272static int subsys_modem_ramdump(int enable,
273 const struct subsys_data *crashed_subsys)
274{
275 if (enable)
276 return do_ramdump(modem_ramdump_dev, modem_segments,
277 ARRAY_SIZE(modem_segments));
278 else
279 return 0;
280}
281
282static void subsys_modem_crash_shutdown(
283 const struct subsys_data *crashed_subsys)
284{
285 /* If modem hasn't already crashed, send SMSM_RESET. */
286 if (!(smsm_get_state(SMSM_MODEM_STATE) & SMSM_RESET)) {
287 modem_unregister_notifier(&modem_notif_nb);
288 smsm_reset_modem(SMSM_RESET);
289 }
290
291 /* Wait for 5ms to allow the modem to clean up caches etc. */
292 usleep(5000);
293}
294
295/* Non-subsystem-specific functions */
296static void do_soc_restart(void)
297{
298 pr_err("%s: Rebooting SoC..\n", MODULE_NAME);
299 kernel_restart(NULL);
300}
301
302static irqreturn_t subsys_wdog_bite_irq(int irq, void *dev_id)
303{
304 int ret;
305
306 switch (irq) {
307
308 case MARM_WDOG_EXPIRED:
309 ret = schedule_work(&modem_fatal_work);
310 disable_irq_nosync(MARM_WDOG_EXPIRED);
311 break;
312
313 case LPASS_Q6SS_WDOG_EXPIRED:
314 ret = schedule_work(&q6_fatal_work);
315 disable_irq_nosync(LPASS_Q6SS_WDOG_EXPIRED);
316 break;
317
318 default:
319 pr_err("%s: %s: Unknown IRQ!\n", MODULE_NAME, __func__);
320 }
321
322 return IRQ_HANDLED;
323}
324
325static struct subsys_data subsys_8x60_q6 = {
326 .name = "lpass",
327 .shutdown = subsys_q6_shutdown,
328 .powerup = subsys_q6_powerup,
329 .ramdump = subsys_q6_ramdump,
330 .crash_shutdown = subsys_q6_crash_shutdown
331};
332
333static struct subsys_data subsys_8x60_modem = {
334 .name = "modem",
335 .shutdown = subsys_modem_shutdown,
336 .powerup = subsys_modem_powerup,
337 .ramdump = subsys_modem_ramdump,
338 .crash_shutdown = subsys_modem_crash_shutdown
339};
340
341static int __init subsystem_restart_8x60_init(void)
342{
343 ssr_register_subsystem(&subsys_8x60_modem);
344 ssr_register_subsystem(&subsys_8x60_q6);
345
346 return 0;
347}
348
349static int __init subsystem_fatal_init(void)
350{
351 int ret;
352
353 /* Need to listen for SMSM_RESET always */
354 modem_register_notifier(&modem_notif_nb);
355
356#if defined(SUBSYS_FATAL_DEBUG)
357 schedule_delayed_work(&debug_crash_modem_work, msecs_to_jiffies(5000));
358#endif
359
360 ret = request_irq(MARM_WDOG_EXPIRED, subsys_wdog_bite_irq,
361 IRQF_TRIGGER_RISING, "modem_wdog", NULL);
362
363 if (ret < 0) {
364 pr_err("%s: Unable to request MARM_WDOG_EXPIRED irq.",
365 __func__);
366 goto out;
367 }
368
369 ret = request_irq(LPASS_Q6SS_WDOG_EXPIRED, subsys_wdog_bite_irq,
370 IRQF_TRIGGER_RISING, "q6_wdog", NULL);
371
372 if (ret < 0) {
373 pr_err("%s: Unable to request LPASS_Q6SS_WDOG_EXPIRED irq.",
374 __func__);
375 goto out;
376 }
377
378 q6_ramdump_dev = create_ramdump_device("lpass");
379
380 if (!q6_ramdump_dev) {
381 ret = -ENOMEM;
382 goto out;
383 }
384
385 modem_ramdump_dev = create_ramdump_device("modem");
386
387 if (!modem_ramdump_dev) {
388 ret = -ENOMEM;
389 goto out;
390 }
391
392 ret = subsystem_restart_8x60_init();
393out:
394 return ret;
395}
396
397static void __exit subsystem_fatal_exit(void)
398{
399 free_irq(MARM_WDOG_EXPIRED, NULL);
400 free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL);
401}
402
403#ifdef SUBSYS_FATAL_DEBUG
404static void debug_crash_modem_fn(struct work_struct *work)
405{
406 if (reset_modem == 1)
407 smsm_reset_modem(SMSM_RESET);
408 else if (reset_modem == 2)
409 subsystem_restart("lpass");
410
411 reset_modem = 0;
412 schedule_delayed_work(&debug_crash_modem_work, msecs_to_jiffies(1000));
413}
414#endif
415
416module_init(subsystem_fatal_init);
417module_exit(subsystem_fatal_exit);