blob: 492200763468450b3cadf52bdfd381b34a4b4a8d [file] [log] [blame]
Vikram Mulukutla21246482012-03-20 16:30:59 -07001/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
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>
Stepan Moskovchenko6f594732011-09-20 16:51:47 -070029#include <mach/socinfo.h>
Vikram Mulukutla121a3c22012-05-01 16:08:21 -070030#include <mach/msm_smsm.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
Vikram Mulukutla121a3c22012-05-01 16:08:21 -070038#define MAX_SSR_REASON_LEN 81U
Vikram Mulukutla21b95f82012-05-14 18:30:31 -070039#define Q6_FW_WDOG_ENABLE 0x08882024
40#define Q6_SW_WDOG_ENABLE 0x08982024
Vikram Mulukutla121a3c22012-05-01 16:08:21 -070041
42static void log_modem_sfr(void)
43{
44 u32 size;
45 char *smem_reason, reason[MAX_SSR_REASON_LEN];
46
47 smem_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size);
48 if (!smem_reason || !size) {
49 pr_err("modem subsystem failure reason: (unknown, smem_get_entry failed).\n");
50 return;
51 }
52 if (!smem_reason[0]) {
53 pr_err("modem subsystem failure reason: (unknown, init string found).\n");
54 return;
55 }
56
57 size = min(size, MAX_SSR_REASON_LEN-1);
58 memcpy(reason, smem_reason, size);
59 reason[size] = '\0';
60 pr_err("modem subsystem failure reason: %s.\n", reason);
61
62 smem_reason[0] = '\0';
63 wmb();
64}
65
Vikram Mulukutla21b95f82012-05-14 18:30:31 -070066static void modem_wdog_check(struct work_struct *work)
67{
68 void __iomem *q6_sw_wdog_addr;
69 u32 regval;
70
71 q6_sw_wdog_addr = ioremap_nocache(Q6_SW_WDOG_ENABLE, 4);
72 if (!q6_sw_wdog_addr)
73 panic("Unable to check modem watchdog status.\n");
74
75 regval = readl_relaxed(q6_sw_wdog_addr);
76 if (!regval) {
77 pr_err("modem-8960: Modem watchdog wasn't activated!. Restarting the modem now.\n");
78 log_modem_sfr();
79 subsystem_restart("modem");
80 }
81
82 iounmap(q6_sw_wdog_addr);
83}
84
85static DECLARE_DELAYED_WORK(modem_wdog_check_work, modem_wdog_check);
86
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070087static void modem_sw_fatal_fn(struct work_struct *work)
88{
89 uint32_t panic_smsm_states = SMSM_RESET | SMSM_SYSTEM_DOWNLOAD;
90 uint32_t reset_smsm_states = SMSM_SYSTEM_REBOOT_USR |
91 SMSM_SYSTEM_PWRDWN_USR;
92 uint32_t modem_state;
93
94 pr_err("Watchdog bite received from modem SW!\n");
95
96 modem_state = smsm_get_state(SMSM_MODEM_STATE);
97
98 if (modem_state & panic_smsm_states) {
99
100 pr_err("Modem SMSM state changed to SMSM_RESET.\n"
101 "Probable err_fatal on the modem. "
102 "Calling subsystem restart...\n");
Vikram Mulukutla121a3c22012-05-01 16:08:21 -0700103 log_modem_sfr();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700104 subsystem_restart("modem");
105
106 } else if (modem_state & reset_smsm_states) {
107
108 pr_err("%s: User-invoked system reset/powerdown. "
109 "Resetting the SoC now.\n",
110 __func__);
111 kernel_restart(NULL);
112 } else {
Vikram Mulukutla121a3c22012-05-01 16:08:21 -0700113 log_modem_sfr();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700114 subsystem_restart("modem");
115 }
116}
117
118static void modem_fw_fatal_fn(struct work_struct *work)
119{
120 pr_err("Watchdog bite received from modem FW!\n");
Vikram Mulukutla121a3c22012-05-01 16:08:21 -0700121 log_modem_sfr();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700122 subsystem_restart("modem");
123}
124
125static DECLARE_WORK(modem_sw_fatal_work, modem_sw_fatal_fn);
126static DECLARE_WORK(modem_fw_fatal_work, modem_fw_fatal_fn);
127
128static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
129{
130 /* Ignore if we're the one that set SMSM_RESET */
131 if (crash_shutdown)
132 return;
133
134 if (new_state & SMSM_RESET) {
Vikram Mulukutla121a3c22012-05-01 16:08:21 -0700135 pr_err("Probable fatal error on the modem.\n");
136 log_modem_sfr();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700137 subsystem_restart("modem");
138 }
139}
140
141static int modem_shutdown(const struct subsys_data *subsys)
142{
Saravana Kannan4296f372011-09-21 15:16:22 -0700143 void __iomem *q6_fw_wdog_addr;
144 void __iomem *q6_sw_wdog_addr;
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700145
Saravana Kannan4296f372011-09-21 15:16:22 -0700146 /*
Vikram Mulukutla21b95f82012-05-14 18:30:31 -0700147 * Cancel any pending wdog_check work items, since we're shutting
148 * down anyway.
149 */
150 cancel_delayed_work(&modem_wdog_check_work);
151
152 /*
Saravana Kannan4296f372011-09-21 15:16:22 -0700153 * Disable the modem watchdog since it keeps running even after the
154 * modem is shutdown.
155 */
156 q6_fw_wdog_addr = ioremap_nocache(Q6_FW_WDOG_ENABLE, 4);
157 if (!q6_fw_wdog_addr)
158 return -ENOMEM;
159
160 q6_sw_wdog_addr = ioremap_nocache(Q6_SW_WDOG_ENABLE, 4);
161 if (!q6_sw_wdog_addr) {
162 iounmap(q6_fw_wdog_addr);
163 return -ENOMEM;
164 }
165
166 writel_relaxed(0x0, q6_fw_wdog_addr);
167 writel_relaxed(0x0, q6_sw_wdog_addr);
168 mb();
169 iounmap(q6_sw_wdog_addr);
170 iounmap(q6_fw_wdog_addr);
171
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700172 pil_force_shutdown("modem");
Saravana Kannan0dcf4742011-09-21 17:19:17 -0700173 pil_force_shutdown("modem_fw");
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700174 disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
175 disable_irq_nosync(Q6SW_WDOG_EXPIRED_IRQ);
176
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700177 return 0;
178}
179
Vikram Mulukutla21b95f82012-05-14 18:30:31 -0700180#define MODEM_WDOG_CHECK_TIMEOUT_MS 10000
181
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700182static int modem_powerup(const struct subsys_data *subsys)
183{
Saravana Kannan0dcf4742011-09-21 17:19:17 -0700184 pil_force_boot("modem_fw");
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700185 pil_force_boot("modem");
186 enable_irq(Q6FW_WDOG_EXPIRED_IRQ);
187 enable_irq(Q6SW_WDOG_EXPIRED_IRQ);
Vikram Mulukutla21b95f82012-05-14 18:30:31 -0700188 schedule_delayed_work(&modem_wdog_check_work,
189 msecs_to_jiffies(MODEM_WDOG_CHECK_TIMEOUT_MS));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700190 return 0;
191}
192
193void modem_crash_shutdown(const struct subsys_data *subsys)
194{
195 crash_shutdown = 1;
196 smsm_reset_modem(SMSM_RESET);
197}
198
Vikram Mulukutla09802372011-12-01 10:38:50 -0800199/* FIXME: Get address, size from PIL */
200static struct ramdump_segment modemsw_segments[] = {
201 {0x89000000, 0x8D400000 - 0x89000000},
202};
203
204static struct ramdump_segment modemfw_segments[] = {
205 {0x8D400000, 0x8DA00000 - 0x8D400000},
206};
207
208static struct ramdump_segment smem_segments[] = {
209 {0x80000000, 0x00200000},
210};
211
212static void *modemfw_ramdump_dev;
213static void *modemsw_ramdump_dev;
214static void *smem_ramdump_dev;
215
216static int modem_ramdump(int enable,
217 const struct subsys_data *crashed_subsys)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700218{
Vikram Mulukutla09802372011-12-01 10:38:50 -0800219 int ret = 0;
220
221 if (enable) {
222 ret = do_ramdump(modemsw_ramdump_dev, modemsw_segments,
223 ARRAY_SIZE(modemsw_segments));
224
225 if (ret < 0) {
226 pr_err("Unable to dump modem sw memory (rc = %d).\n",
227 ret);
228 goto out;
229 }
230
231 ret = do_ramdump(modemfw_ramdump_dev, modemfw_segments,
232 ARRAY_SIZE(modemfw_segments));
233
234 if (ret < 0) {
235 pr_err("Unable to dump modem fw memory (rc = %d).\n",
236 ret);
237 goto out;
238 }
239
240 ret = do_ramdump(smem_ramdump_dev, smem_segments,
241 ARRAY_SIZE(smem_segments));
242
243 if (ret < 0) {
244 pr_err("Unable to dump smem memory (rc = %d).\n", ret);
245 goto out;
246 }
247 }
248
249out:
250 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700251}
252
253static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
254{
255 int ret;
256
257 switch (irq) {
258
259 case Q6SW_WDOG_EXPIRED_IRQ:
260 ret = schedule_work(&modem_sw_fatal_work);
261 disable_irq_nosync(Q6SW_WDOG_EXPIRED_IRQ);
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700262 disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700263 break;
264 case Q6FW_WDOG_EXPIRED_IRQ:
265 ret = schedule_work(&modem_fw_fatal_work);
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700266 disable_irq_nosync(Q6SW_WDOG_EXPIRED_IRQ);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700267 disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
268 break;
269 break;
270
271 default:
272 pr_err("%s: Unknown IRQ!\n", __func__);
273 }
274
275 return IRQ_HANDLED;
276}
277
278static struct subsys_data modem_8960 = {
279 .name = "modem",
280 .shutdown = modem_shutdown,
281 .powerup = modem_powerup,
282 .ramdump = modem_ramdump,
283 .crash_shutdown = modem_crash_shutdown
284};
285
286static int modem_subsystem_restart_init(void)
287{
288 return ssr_register_subsystem(&modem_8960);
289}
290
291static int modem_debug_set(void *data, u64 val)
292{
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700293 if (val == 1)
294 subsystem_restart("modem");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700295
296 return 0;
297}
298
299static int modem_debug_get(void *data, u64 *val)
300{
301 *val = 0;
302 return 0;
303}
304
305DEFINE_SIMPLE_ATTRIBUTE(modem_debug_fops, modem_debug_get, modem_debug_set,
306 "%llu\n");
307
308static int modem_debugfs_init(void)
309{
310 struct dentry *dent;
311 dent = debugfs_create_dir("modem_debug", 0);
312
313 if (IS_ERR(dent))
314 return PTR_ERR(dent);
315
316 debugfs_create_file("reset_modem", 0644, dent, NULL,
317 &modem_debug_fops);
318 return 0;
319}
320
321static int __init modem_8960_init(void)
322{
323 int ret;
324
Rohit Vaswani56dd22a2011-11-11 16:21:28 -0800325 if (!cpu_is_msm8960() && !cpu_is_msm8930() && !cpu_is_msm9615())
Stepan Moskovchenko6f594732011-09-20 16:51:47 -0700326 return -ENODEV;
327
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700328 ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
329 smsm_state_cb, 0);
330
331 if (ret < 0)
332 pr_err("%s: Unable to register SMSM callback! (%d)\n",
333 __func__, ret);
334
335 ret = request_irq(Q6FW_WDOG_EXPIRED_IRQ, modem_wdog_bite_irq,
336 IRQF_TRIGGER_RISING, "modem_wdog_fw", NULL);
337
338 if (ret < 0) {
339 pr_err("%s: Unable to request q6fw watchdog IRQ. (%d)\n",
340 __func__, ret);
341 goto out;
342 }
343
344 ret = request_irq(Q6SW_WDOG_EXPIRED_IRQ, modem_wdog_bite_irq,
345 IRQF_TRIGGER_RISING, "modem_wdog_sw", NULL);
346
347 if (ret < 0) {
348 pr_err("%s: Unable to request q6sw watchdog IRQ. (%d)\n",
349 __func__, ret);
350 disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
351 goto out;
352 }
353
354 ret = modem_subsystem_restart_init();
355
356 if (ret < 0) {
357 pr_err("%s: Unable to reg with subsystem restart. (%d)\n",
358 __func__, ret);
359 goto out;
360 }
361
Vikram Mulukutla09802372011-12-01 10:38:50 -0800362 modemfw_ramdump_dev = create_ramdump_device("modem_fw");
363
364 if (!modemfw_ramdump_dev) {
365 pr_err("%s: Unable to create modem fw ramdump device. (%d)\n",
366 __func__, -ENOMEM);
367 ret = -ENOMEM;
368 goto out;
369 }
370
371 modemsw_ramdump_dev = create_ramdump_device("modem_sw");
372
373 if (!modemsw_ramdump_dev) {
374 pr_err("%s: Unable to create modem sw ramdump device. (%d)\n",
375 __func__, -ENOMEM);
376 ret = -ENOMEM;
377 goto out;
378 }
379
380 smem_ramdump_dev = create_ramdump_device("smem");
381
382 if (!smem_ramdump_dev) {
383 pr_err("%s: Unable to create smem ramdump device. (%d)\n",
384 __func__, -ENOMEM);
385 ret = -ENOMEM;
386 goto out;
387 }
388
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700389 ret = modem_debugfs_init();
390
Rohit Vaswani56dd22a2011-11-11 16:21:28 -0800391 pr_info("%s: modem fatal driver init'ed.\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700392out:
393 return ret;
394}
395
396module_init(modem_8960_init);