blob: a061aa049870b942f7f1488e1467a47e87960ba8 [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;
Vikram Mulukutlad7179642011-07-18 11:13:29 -070059static void __iomem *q6_wakeup_intr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070060
61static void q6_fatal_fn(struct work_struct *work)
62{
63 pr_err("%s: Watchdog bite received from Q6!\n", MODULE_NAME);
64 subsystem_restart("lpass");
Vikram Mulukutla93510c42011-08-19 19:04:40 -070065 enable_irq(LPASS_Q6SS_WDOG_EXPIRED);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070066}
67
68static void send_q6_nmi(void)
69{
70 /* Send NMI to QDSP6 via an SCM call. */
71 uint32_t cmd = 0x1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070072
73 scm_call(SCM_SVC_UTIL, SCM_Q6_NMI_CMD,
74 &cmd, sizeof(cmd), NULL, 0);
75
76 /* Wakeup the Q6 */
Vikram Mulukutlad7179642011-07-18 11:13:29 -070077 if (q6_wakeup_intr)
78 writel_relaxed(0x2000, q6_wakeup_intr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070079 mb();
80
Vikram Mulukutla189724d2011-07-22 13:24:50 -070081 /* Q6 requires atleast 100ms to dump caches etc.*/
Vikram Mulukutlad7179642011-07-18 11:13:29 -070082 mdelay(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");
Vikram Mulukutla93510c42011-08-19 19:04:40 -0700159 enable_irq(MARM_WDOG_EXPIRED);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700160}
161
162static void modem_fatal_fn(struct work_struct *work)
163{
164 uint32_t modem_state;
165 uint32_t panic_smsm_states = SMSM_RESET | SMSM_SYSTEM_DOWNLOAD;
166 uint32_t reset_smsm_states = SMSM_SYSTEM_REBOOT_USR |
167 SMSM_SYSTEM_PWRDWN_USR;
168
169 pr_err("%s: Watchdog bite received from modem!\n", MODULE_NAME);
170
171 modem_state = smsm_get_state(SMSM_MODEM_STATE);
172 pr_err("%s: Modem SMSM state = 0x%x!", MODULE_NAME, modem_state);
173
174 if (modem_state == 0 || modem_state & panic_smsm_states) {
175
176 subsystem_restart("modem");
Vikram Mulukutla93510c42011-08-19 19:04:40 -0700177 enable_irq(MARM_WDOG_EXPIRED);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700178
179 } else if (modem_state & reset_smsm_states) {
180
181 pr_err("%s: User-invoked system reset/powerdown.",
182 MODULE_NAME);
183 do_soc_restart();
184
185 } else {
186
187 int ret;
188 void *hwio_modem_reset_addr =
189 ioremap_nocache(MODEM_HWIO_MSS_RESET_ADDR, 8);
190
191 pr_err("%s: Modem AHB locked up.\n", MODULE_NAME);
192 pr_err("%s: Trying to free up modem!\n", MODULE_NAME);
193
194 writel(0x3, hwio_modem_reset_addr);
195
196 /* If we are still alive after 6 seconds (allowing for
197 * the 5-second-delayed-panic-reboot), modem is either
198 * still wedged or SMSM didn't come through. Force panic
199 * in that case.
200 */
201 ret = schedule_delayed_work(&modem_unlock_timeout_work,
202 msecs_to_jiffies(6000));
203
204 iounmap(hwio_modem_reset_addr);
205 }
206}
207
208static int modem_notif_handler(struct notifier_block *this,
209 unsigned long code,
210 void *_cmd)
211{
212 if (code == MODEM_NOTIFIER_START_RESET) {
213
214 pr_err("%s: Modem error fatal'ed.", MODULE_NAME);
215 subsystem_restart("modem");
216 }
217 return NOTIFY_DONE;
218}
219
220static int subsys_modem_shutdown(const struct subsys_data *crashed_subsys)
221{
222 void __iomem *modem_wdog_addr;
223 int smsm_notif_unregistered = 0;
224
225 /* If the modem didn't already crash, setting SMSM_RESET
226 * here will help flush caches etc. Unregister for SMSM
227 * notifications to prevent unnecessary secondary calls to
228 * subsystem_restart.
229 */
230 if (!(smsm_get_state(SMSM_MODEM_STATE) & SMSM_RESET)) {
231 modem_unregister_notifier(&modem_notif_nb);
232 smsm_notif_unregistered = 1;
233 smsm_reset_modem(SMSM_RESET);
234 }
235
236 /* Disable the modem watchdog to allow clean modem bootup */
237 modem_wdog_addr = ioremap_nocache(MODEM_WDOG_ENABLE, 8);
238 writel_relaxed(0x0, modem_wdog_addr);
239
240 /*
241 * The write above needs to go through before the modem is
242 * powered up again (subsystem restart).
243 */
244 mb();
245 iounmap(modem_wdog_addr);
246
Vikram Mulukutla89549562011-07-28 16:10:41 -0700247 /* Wait here to allow the modem to clean up caches etc. */
248 msleep(MODEM_CLEANUP_DELAY_MS);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700249 pil_force_shutdown("modem");
250 disable_irq_nosync(MARM_WDOG_EXPIRED);
251
252 /* Re-register for SMSM notifications if necessary */
253 if (smsm_notif_unregistered)
254 modem_register_notifier(&modem_notif_nb);
255
256
257 return 0;
258}
259
260static int subsys_modem_powerup(const struct subsys_data *crashed_subsys)
261{
262 int ret;
263
264 ret = pil_force_boot("modem");
265 enable_irq(MARM_WDOG_EXPIRED);
266
267 return ret;
268}
269
270/* FIXME: Get address, size from PIL */
271static struct ramdump_segment modem_segments[] = {
272 {0x42F00000, 0x46000000 - 0x42F00000} };
273
274static int subsys_modem_ramdump(int enable,
275 const struct subsys_data *crashed_subsys)
276{
277 if (enable)
278 return do_ramdump(modem_ramdump_dev, modem_segments,
279 ARRAY_SIZE(modem_segments));
280 else
281 return 0;
282}
283
284static void subsys_modem_crash_shutdown(
285 const struct subsys_data *crashed_subsys)
286{
287 /* If modem hasn't already crashed, send SMSM_RESET. */
288 if (!(smsm_get_state(SMSM_MODEM_STATE) & SMSM_RESET)) {
289 modem_unregister_notifier(&modem_notif_nb);
290 smsm_reset_modem(SMSM_RESET);
291 }
292
Vikram Mulukutla6066a842011-09-16 11:14:50 -0700293 /* Wait to allow the modem to clean up caches etc. */
294 mdelay(5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700295}
296
297/* Non-subsystem-specific functions */
298static void do_soc_restart(void)
299{
300 pr_err("%s: Rebooting SoC..\n", MODULE_NAME);
301 kernel_restart(NULL);
302}
303
304static irqreturn_t subsys_wdog_bite_irq(int irq, void *dev_id)
305{
306 int ret;
307
308 switch (irq) {
309
310 case MARM_WDOG_EXPIRED:
311 ret = schedule_work(&modem_fatal_work);
312 disable_irq_nosync(MARM_WDOG_EXPIRED);
313 break;
314
315 case LPASS_Q6SS_WDOG_EXPIRED:
316 ret = schedule_work(&q6_fatal_work);
317 disable_irq_nosync(LPASS_Q6SS_WDOG_EXPIRED);
318 break;
319
320 default:
321 pr_err("%s: %s: Unknown IRQ!\n", MODULE_NAME, __func__);
322 }
323
324 return IRQ_HANDLED;
325}
326
327static struct subsys_data subsys_8x60_q6 = {
328 .name = "lpass",
329 .shutdown = subsys_q6_shutdown,
330 .powerup = subsys_q6_powerup,
331 .ramdump = subsys_q6_ramdump,
332 .crash_shutdown = subsys_q6_crash_shutdown
333};
334
335static struct subsys_data subsys_8x60_modem = {
336 .name = "modem",
337 .shutdown = subsys_modem_shutdown,
338 .powerup = subsys_modem_powerup,
339 .ramdump = subsys_modem_ramdump,
340 .crash_shutdown = subsys_modem_crash_shutdown
341};
342
343static int __init subsystem_restart_8x60_init(void)
344{
345 ssr_register_subsystem(&subsys_8x60_modem);
346 ssr_register_subsystem(&subsys_8x60_q6);
347
348 return 0;
349}
350
351static int __init subsystem_fatal_init(void)
352{
353 int ret;
354
355 /* Need to listen for SMSM_RESET always */
356 modem_register_notifier(&modem_notif_nb);
357
358#if defined(SUBSYS_FATAL_DEBUG)
359 schedule_delayed_work(&debug_crash_modem_work, msecs_to_jiffies(5000));
360#endif
361
362 ret = request_irq(MARM_WDOG_EXPIRED, subsys_wdog_bite_irq,
363 IRQF_TRIGGER_RISING, "modem_wdog", NULL);
364
365 if (ret < 0) {
366 pr_err("%s: Unable to request MARM_WDOG_EXPIRED irq.",
367 __func__);
368 goto out;
369 }
370
371 ret = request_irq(LPASS_Q6SS_WDOG_EXPIRED, subsys_wdog_bite_irq,
372 IRQF_TRIGGER_RISING, "q6_wdog", NULL);
373
374 if (ret < 0) {
375 pr_err("%s: Unable to request LPASS_Q6SS_WDOG_EXPIRED irq.",
376 __func__);
377 goto out;
378 }
379
Vikram Mulukutlad7179642011-07-18 11:13:29 -0700380 q6_wakeup_intr = ioremap_nocache(Q6SS_SOFT_INTR_WAKEUP, 8);
381
382 if (!q6_wakeup_intr)
383 pr_err("%s: Unable to request q6 wakeup interrupt.", __func__);
384
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700385 q6_ramdump_dev = create_ramdump_device("lpass");
386
387 if (!q6_ramdump_dev) {
388 ret = -ENOMEM;
389 goto out;
390 }
391
392 modem_ramdump_dev = create_ramdump_device("modem");
393
394 if (!modem_ramdump_dev) {
395 ret = -ENOMEM;
396 goto out;
397 }
398
399 ret = subsystem_restart_8x60_init();
400out:
401 return ret;
402}
403
404static void __exit subsystem_fatal_exit(void)
405{
406 free_irq(MARM_WDOG_EXPIRED, NULL);
407 free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL);
408}
409
410#ifdef SUBSYS_FATAL_DEBUG
411static void debug_crash_modem_fn(struct work_struct *work)
412{
413 if (reset_modem == 1)
414 smsm_reset_modem(SMSM_RESET);
415 else if (reset_modem == 2)
416 subsystem_restart("lpass");
417
418 reset_modem = 0;
419 schedule_delayed_work(&debug_crash_modem_work, msecs_to_jiffies(1000));
420}
421#endif
422
423module_init(subsystem_fatal_init);
424module_exit(subsystem_fatal_exit);