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