blob: 9a1e5651f6bebe12bd0628262f6d96c6d2aaf84a [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
39
40static void log_modem_sfr(void)
41{
42 u32 size;
43 char *smem_reason, reason[MAX_SSR_REASON_LEN];
44
45 smem_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size);
46 if (!smem_reason || !size) {
47 pr_err("modem subsystem failure reason: (unknown, smem_get_entry failed).\n");
48 return;
49 }
50 if (!smem_reason[0]) {
51 pr_err("modem subsystem failure reason: (unknown, init string found).\n");
52 return;
53 }
54
55 size = min(size, MAX_SSR_REASON_LEN-1);
56 memcpy(reason, smem_reason, size);
57 reason[size] = '\0';
58 pr_err("modem subsystem failure reason: %s.\n", reason);
59
60 smem_reason[0] = '\0';
61 wmb();
62}
63
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070064static void modem_sw_fatal_fn(struct work_struct *work)
65{
66 uint32_t panic_smsm_states = SMSM_RESET | SMSM_SYSTEM_DOWNLOAD;
67 uint32_t reset_smsm_states = SMSM_SYSTEM_REBOOT_USR |
68 SMSM_SYSTEM_PWRDWN_USR;
69 uint32_t modem_state;
70
71 pr_err("Watchdog bite received from modem SW!\n");
72
73 modem_state = smsm_get_state(SMSM_MODEM_STATE);
74
75 if (modem_state & panic_smsm_states) {
76
77 pr_err("Modem SMSM state changed to SMSM_RESET.\n"
78 "Probable err_fatal on the modem. "
79 "Calling subsystem restart...\n");
Vikram Mulukutla121a3c22012-05-01 16:08:21 -070080 log_modem_sfr();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070081 subsystem_restart("modem");
82
83 } else if (modem_state & reset_smsm_states) {
84
85 pr_err("%s: User-invoked system reset/powerdown. "
86 "Resetting the SoC now.\n",
87 __func__);
88 kernel_restart(NULL);
89 } else {
Vikram Mulukutla121a3c22012-05-01 16:08:21 -070090 log_modem_sfr();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070091 subsystem_restart("modem");
92 }
93}
94
95static void modem_fw_fatal_fn(struct work_struct *work)
96{
97 pr_err("Watchdog bite received from modem FW!\n");
Vikram Mulukutla121a3c22012-05-01 16:08:21 -070098 log_modem_sfr();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070099 subsystem_restart("modem");
100}
101
102static DECLARE_WORK(modem_sw_fatal_work, modem_sw_fatal_fn);
103static DECLARE_WORK(modem_fw_fatal_work, modem_fw_fatal_fn);
104
105static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
106{
107 /* Ignore if we're the one that set SMSM_RESET */
108 if (crash_shutdown)
109 return;
110
111 if (new_state & SMSM_RESET) {
Vikram Mulukutla121a3c22012-05-01 16:08:21 -0700112 pr_err("Probable fatal error on the modem.\n");
113 log_modem_sfr();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700114 subsystem_restart("modem");
115 }
116}
117
Saravana Kannan4296f372011-09-21 15:16:22 -0700118#define Q6_FW_WDOG_ENABLE 0x08882024
119#define Q6_SW_WDOG_ENABLE 0x08982024
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700120static int modem_shutdown(const struct subsys_data *subsys)
121{
Saravana Kannan4296f372011-09-21 15:16:22 -0700122 void __iomem *q6_fw_wdog_addr;
123 void __iomem *q6_sw_wdog_addr;
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700124
Saravana Kannan4296f372011-09-21 15:16:22 -0700125 /*
126 * Disable the modem watchdog since it keeps running even after the
127 * modem is shutdown.
128 */
129 q6_fw_wdog_addr = ioremap_nocache(Q6_FW_WDOG_ENABLE, 4);
130 if (!q6_fw_wdog_addr)
131 return -ENOMEM;
132
133 q6_sw_wdog_addr = ioremap_nocache(Q6_SW_WDOG_ENABLE, 4);
134 if (!q6_sw_wdog_addr) {
135 iounmap(q6_fw_wdog_addr);
136 return -ENOMEM;
137 }
138
139 writel_relaxed(0x0, q6_fw_wdog_addr);
140 writel_relaxed(0x0, q6_sw_wdog_addr);
141 mb();
142 iounmap(q6_sw_wdog_addr);
143 iounmap(q6_fw_wdog_addr);
144
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700145 pil_force_shutdown("modem");
Saravana Kannan0dcf4742011-09-21 17:19:17 -0700146 pil_force_shutdown("modem_fw");
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700147 disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
148 disable_irq_nosync(Q6SW_WDOG_EXPIRED_IRQ);
149
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700150 return 0;
151}
152
153static int modem_powerup(const struct subsys_data *subsys)
154{
Saravana Kannan0dcf4742011-09-21 17:19:17 -0700155 pil_force_boot("modem_fw");
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700156 pil_force_boot("modem");
157 enable_irq(Q6FW_WDOG_EXPIRED_IRQ);
158 enable_irq(Q6SW_WDOG_EXPIRED_IRQ);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700159 return 0;
160}
161
162void modem_crash_shutdown(const struct subsys_data *subsys)
163{
164 crash_shutdown = 1;
165 smsm_reset_modem(SMSM_RESET);
166}
167
Vikram Mulukutla09802372011-12-01 10:38:50 -0800168/* FIXME: Get address, size from PIL */
169static struct ramdump_segment modemsw_segments[] = {
170 {0x89000000, 0x8D400000 - 0x89000000},
171};
172
173static struct ramdump_segment modemfw_segments[] = {
174 {0x8D400000, 0x8DA00000 - 0x8D400000},
175};
176
177static struct ramdump_segment smem_segments[] = {
178 {0x80000000, 0x00200000},
179};
180
181static void *modemfw_ramdump_dev;
182static void *modemsw_ramdump_dev;
183static void *smem_ramdump_dev;
184
185static int modem_ramdump(int enable,
186 const struct subsys_data *crashed_subsys)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700187{
Vikram Mulukutla09802372011-12-01 10:38:50 -0800188 int ret = 0;
189
190 if (enable) {
191 ret = do_ramdump(modemsw_ramdump_dev, modemsw_segments,
192 ARRAY_SIZE(modemsw_segments));
193
194 if (ret < 0) {
195 pr_err("Unable to dump modem sw memory (rc = %d).\n",
196 ret);
197 goto out;
198 }
199
200 ret = do_ramdump(modemfw_ramdump_dev, modemfw_segments,
201 ARRAY_SIZE(modemfw_segments));
202
203 if (ret < 0) {
204 pr_err("Unable to dump modem fw memory (rc = %d).\n",
205 ret);
206 goto out;
207 }
208
209 ret = do_ramdump(smem_ramdump_dev, smem_segments,
210 ARRAY_SIZE(smem_segments));
211
212 if (ret < 0) {
213 pr_err("Unable to dump smem memory (rc = %d).\n", ret);
214 goto out;
215 }
216 }
217
218out:
219 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700220}
221
222static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
223{
224 int ret;
225
226 switch (irq) {
227
228 case Q6SW_WDOG_EXPIRED_IRQ:
229 ret = schedule_work(&modem_sw_fatal_work);
230 disable_irq_nosync(Q6SW_WDOG_EXPIRED_IRQ);
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700231 disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700232 break;
233 case Q6FW_WDOG_EXPIRED_IRQ:
234 ret = schedule_work(&modem_fw_fatal_work);
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700235 disable_irq_nosync(Q6SW_WDOG_EXPIRED_IRQ);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700236 disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
237 break;
238 break;
239
240 default:
241 pr_err("%s: Unknown IRQ!\n", __func__);
242 }
243
244 return IRQ_HANDLED;
245}
246
247static struct subsys_data modem_8960 = {
248 .name = "modem",
249 .shutdown = modem_shutdown,
250 .powerup = modem_powerup,
251 .ramdump = modem_ramdump,
252 .crash_shutdown = modem_crash_shutdown
253};
254
255static int modem_subsystem_restart_init(void)
256{
257 return ssr_register_subsystem(&modem_8960);
258}
259
260static int modem_debug_set(void *data, u64 val)
261{
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700262 if (val == 1)
263 subsystem_restart("modem");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700264
265 return 0;
266}
267
268static int modem_debug_get(void *data, u64 *val)
269{
270 *val = 0;
271 return 0;
272}
273
274DEFINE_SIMPLE_ATTRIBUTE(modem_debug_fops, modem_debug_get, modem_debug_set,
275 "%llu\n");
276
277static int modem_debugfs_init(void)
278{
279 struct dentry *dent;
280 dent = debugfs_create_dir("modem_debug", 0);
281
282 if (IS_ERR(dent))
283 return PTR_ERR(dent);
284
285 debugfs_create_file("reset_modem", 0644, dent, NULL,
286 &modem_debug_fops);
287 return 0;
288}
289
290static int __init modem_8960_init(void)
291{
292 int ret;
293
Rohit Vaswani56dd22a2011-11-11 16:21:28 -0800294 if (!cpu_is_msm8960() && !cpu_is_msm8930() && !cpu_is_msm9615())
Stepan Moskovchenko6f594732011-09-20 16:51:47 -0700295 return -ENODEV;
296
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700297 ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
298 smsm_state_cb, 0);
299
300 if (ret < 0)
301 pr_err("%s: Unable to register SMSM callback! (%d)\n",
302 __func__, ret);
303
304 ret = request_irq(Q6FW_WDOG_EXPIRED_IRQ, modem_wdog_bite_irq,
305 IRQF_TRIGGER_RISING, "modem_wdog_fw", NULL);
306
307 if (ret < 0) {
308 pr_err("%s: Unable to request q6fw watchdog IRQ. (%d)\n",
309 __func__, ret);
310 goto out;
311 }
312
313 ret = request_irq(Q6SW_WDOG_EXPIRED_IRQ, modem_wdog_bite_irq,
314 IRQF_TRIGGER_RISING, "modem_wdog_sw", NULL);
315
316 if (ret < 0) {
317 pr_err("%s: Unable to request q6sw watchdog IRQ. (%d)\n",
318 __func__, ret);
319 disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
320 goto out;
321 }
322
323 ret = modem_subsystem_restart_init();
324
325 if (ret < 0) {
326 pr_err("%s: Unable to reg with subsystem restart. (%d)\n",
327 __func__, ret);
328 goto out;
329 }
330
Vikram Mulukutla09802372011-12-01 10:38:50 -0800331 modemfw_ramdump_dev = create_ramdump_device("modem_fw");
332
333 if (!modemfw_ramdump_dev) {
334 pr_err("%s: Unable to create modem fw ramdump device. (%d)\n",
335 __func__, -ENOMEM);
336 ret = -ENOMEM;
337 goto out;
338 }
339
340 modemsw_ramdump_dev = create_ramdump_device("modem_sw");
341
342 if (!modemsw_ramdump_dev) {
343 pr_err("%s: Unable to create modem sw ramdump device. (%d)\n",
344 __func__, -ENOMEM);
345 ret = -ENOMEM;
346 goto out;
347 }
348
349 smem_ramdump_dev = create_ramdump_device("smem");
350
351 if (!smem_ramdump_dev) {
352 pr_err("%s: Unable to create smem ramdump device. (%d)\n",
353 __func__, -ENOMEM);
354 ret = -ENOMEM;
355 goto out;
356 }
357
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700358 ret = modem_debugfs_init();
359
Rohit Vaswani56dd22a2011-11-11 16:21:28 -0800360 pr_info("%s: modem fatal driver init'ed.\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700361out:
362 return ret;
363}
364
365module_init(modem_8960_init);