blob: 73b9b1fa1fc4a14d1cfdb3d94e63814a9cba41f5 [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
Stephen Boyd0ebf7212012-04-30 20:42:35 -070038static struct subsys_device *modem_8960_dev;
39
Vikram Mulukutla121a3c22012-05-01 16:08:21 -070040#define MAX_SSR_REASON_LEN 81U
Vikram Mulukutla21b95f82012-05-14 18:30:31 -070041#define Q6_FW_WDOG_ENABLE 0x08882024
42#define Q6_SW_WDOG_ENABLE 0x08982024
Vikram Mulukutla121a3c22012-05-01 16:08:21 -070043
44static void log_modem_sfr(void)
45{
46 u32 size;
47 char *smem_reason, reason[MAX_SSR_REASON_LEN];
48
49 smem_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size);
50 if (!smem_reason || !size) {
51 pr_err("modem subsystem failure reason: (unknown, smem_get_entry failed).\n");
52 return;
53 }
54 if (!smem_reason[0]) {
55 pr_err("modem subsystem failure reason: (unknown, init string found).\n");
56 return;
57 }
58
59 size = min(size, MAX_SSR_REASON_LEN-1);
60 memcpy(reason, smem_reason, size);
61 reason[size] = '\0';
62 pr_err("modem subsystem failure reason: %s.\n", reason);
63
64 smem_reason[0] = '\0';
65 wmb();
66}
67
Vikram Mulukutla773c1b32012-06-05 18:44:35 -070068static void restart_modem(void)
69{
70 log_modem_sfr();
Stephen Boyd0ebf7212012-04-30 20:42:35 -070071 subsystem_restart_dev(modem_8960_dev);
Vikram Mulukutla773c1b32012-06-05 18:44:35 -070072}
73
Vikram Mulukutla21b95f82012-05-14 18:30:31 -070074static void modem_wdog_check(struct work_struct *work)
75{
76 void __iomem *q6_sw_wdog_addr;
77 u32 regval;
78
79 q6_sw_wdog_addr = ioremap_nocache(Q6_SW_WDOG_ENABLE, 4);
80 if (!q6_sw_wdog_addr)
81 panic("Unable to check modem watchdog status.\n");
82
83 regval = readl_relaxed(q6_sw_wdog_addr);
84 if (!regval) {
85 pr_err("modem-8960: Modem watchdog wasn't activated!. Restarting the modem now.\n");
Vikram Mulukutla773c1b32012-06-05 18:44:35 -070086 restart_modem();
Vikram Mulukutla21b95f82012-05-14 18:30:31 -070087 }
88
89 iounmap(q6_sw_wdog_addr);
90}
91
92static DECLARE_DELAYED_WORK(modem_wdog_check_work, modem_wdog_check);
93
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070094static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
95{
96 /* Ignore if we're the one that set SMSM_RESET */
97 if (crash_shutdown)
98 return;
99
100 if (new_state & SMSM_RESET) {
Vikram Mulukutla121a3c22012-05-01 16:08:21 -0700101 pr_err("Probable fatal error on the modem.\n");
Vikram Mulukutla773c1b32012-06-05 18:44:35 -0700102 restart_modem();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700103 }
104}
105
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700106static int modem_shutdown(const struct subsys_desc *subsys)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700107{
Saravana Kannan4296f372011-09-21 15:16:22 -0700108 void __iomem *q6_fw_wdog_addr;
109 void __iomem *q6_sw_wdog_addr;
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700110
Saravana Kannan4296f372011-09-21 15:16:22 -0700111 /*
Vikram Mulukutla21b95f82012-05-14 18:30:31 -0700112 * Cancel any pending wdog_check work items, since we're shutting
113 * down anyway.
114 */
115 cancel_delayed_work(&modem_wdog_check_work);
116
117 /*
Saravana Kannan4296f372011-09-21 15:16:22 -0700118 * Disable the modem watchdog since it keeps running even after the
119 * modem is shutdown.
120 */
121 q6_fw_wdog_addr = ioremap_nocache(Q6_FW_WDOG_ENABLE, 4);
122 if (!q6_fw_wdog_addr)
123 return -ENOMEM;
124
125 q6_sw_wdog_addr = ioremap_nocache(Q6_SW_WDOG_ENABLE, 4);
126 if (!q6_sw_wdog_addr) {
127 iounmap(q6_fw_wdog_addr);
128 return -ENOMEM;
129 }
130
131 writel_relaxed(0x0, q6_fw_wdog_addr);
132 writel_relaxed(0x0, q6_sw_wdog_addr);
133 mb();
134 iounmap(q6_sw_wdog_addr);
135 iounmap(q6_fw_wdog_addr);
136
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700137 pil_force_shutdown("modem");
Saravana Kannan0dcf4742011-09-21 17:19:17 -0700138 pil_force_shutdown("modem_fw");
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700139 disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
140 disable_irq_nosync(Q6SW_WDOG_EXPIRED_IRQ);
141
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700142 return 0;
143}
144
Vikram Mulukutla21b95f82012-05-14 18:30:31 -0700145#define MODEM_WDOG_CHECK_TIMEOUT_MS 10000
146
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700147static int modem_powerup(const struct subsys_desc *subsys)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700148{
Saravana Kannan0dcf4742011-09-21 17:19:17 -0700149 pil_force_boot("modem_fw");
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700150 pil_force_boot("modem");
151 enable_irq(Q6FW_WDOG_EXPIRED_IRQ);
152 enable_irq(Q6SW_WDOG_EXPIRED_IRQ);
Vikram Mulukutla21b95f82012-05-14 18:30:31 -0700153 schedule_delayed_work(&modem_wdog_check_work,
154 msecs_to_jiffies(MODEM_WDOG_CHECK_TIMEOUT_MS));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700155 return 0;
156}
157
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700158void modem_crash_shutdown(const struct subsys_desc *subsys)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700159{
160 crash_shutdown = 1;
161 smsm_reset_modem(SMSM_RESET);
162}
163
Vikram Mulukutla09802372011-12-01 10:38:50 -0800164/* FIXME: Get address, size from PIL */
165static struct ramdump_segment modemsw_segments[] = {
166 {0x89000000, 0x8D400000 - 0x89000000},
167};
168
169static struct ramdump_segment modemfw_segments[] = {
170 {0x8D400000, 0x8DA00000 - 0x8D400000},
171};
172
173static struct ramdump_segment smem_segments[] = {
174 {0x80000000, 0x00200000},
175};
176
177static void *modemfw_ramdump_dev;
178static void *modemsw_ramdump_dev;
179static void *smem_ramdump_dev;
180
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700181static int modem_ramdump(int enable, const struct subsys_desc *crashed_subsys)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700182{
Vikram Mulukutla09802372011-12-01 10:38:50 -0800183 int ret = 0;
184
185 if (enable) {
186 ret = do_ramdump(modemsw_ramdump_dev, modemsw_segments,
187 ARRAY_SIZE(modemsw_segments));
188
189 if (ret < 0) {
190 pr_err("Unable to dump modem sw memory (rc = %d).\n",
191 ret);
192 goto out;
193 }
194
195 ret = do_ramdump(modemfw_ramdump_dev, modemfw_segments,
196 ARRAY_SIZE(modemfw_segments));
197
198 if (ret < 0) {
199 pr_err("Unable to dump modem fw memory (rc = %d).\n",
200 ret);
201 goto out;
202 }
203
204 ret = do_ramdump(smem_ramdump_dev, smem_segments,
205 ARRAY_SIZE(smem_segments));
206
207 if (ret < 0) {
208 pr_err("Unable to dump smem memory (rc = %d).\n", ret);
209 goto out;
210 }
211 }
212
213out:
214 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700215}
216
217static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
218{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700219 switch (irq) {
220
221 case Q6SW_WDOG_EXPIRED_IRQ:
Vikram Mulukutla773c1b32012-06-05 18:44:35 -0700222 pr_err("Watchdog bite received from modem software!\n");
223 restart_modem();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700224 break;
225 case Q6FW_WDOG_EXPIRED_IRQ:
Vikram Mulukutla773c1b32012-06-05 18:44:35 -0700226 pr_err("Watchdog bite received from modem firmware!\n");
227 restart_modem();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700228 break;
229 break;
230
231 default:
232 pr_err("%s: Unknown IRQ!\n", __func__);
233 }
234
235 return IRQ_HANDLED;
236}
237
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700238static struct subsys_desc modem_8960 = {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700239 .name = "modem",
240 .shutdown = modem_shutdown,
241 .powerup = modem_powerup,
242 .ramdump = modem_ramdump,
243 .crash_shutdown = modem_crash_shutdown
244};
245
246static int modem_subsystem_restart_init(void)
247{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700248 modem_8960_dev = subsys_register(&modem_8960);
249 if (IS_ERR(modem_8960_dev))
250 return PTR_ERR(modem_8960_dev);
251 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700252}
253
254static int modem_debug_set(void *data, u64 val)
255{
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700256 if (val == 1)
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700257 subsystem_restart_dev(modem_8960_dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700258
259 return 0;
260}
261
262static int modem_debug_get(void *data, u64 *val)
263{
264 *val = 0;
265 return 0;
266}
267
268DEFINE_SIMPLE_ATTRIBUTE(modem_debug_fops, modem_debug_get, modem_debug_set,
269 "%llu\n");
270
271static int modem_debugfs_init(void)
272{
273 struct dentry *dent;
274 dent = debugfs_create_dir("modem_debug", 0);
275
276 if (IS_ERR(dent))
277 return PTR_ERR(dent);
278
279 debugfs_create_file("reset_modem", 0644, dent, NULL,
280 &modem_debug_fops);
281 return 0;
282}
283
284static int __init modem_8960_init(void)
285{
286 int ret;
287
Stepan Moskovchenko0df9bb22012-07-06 18:19:15 -0700288 if (!cpu_is_msm8960() && !cpu_is_msm8930() && !cpu_is_msm8930aa() &&
289 !cpu_is_msm9615() && !cpu_is_msm8627())
Stepan Moskovchenko6f594732011-09-20 16:51:47 -0700290 return -ENODEV;
291
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700292 ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
293 smsm_state_cb, 0);
294
295 if (ret < 0)
296 pr_err("%s: Unable to register SMSM callback! (%d)\n",
297 __func__, ret);
298
299 ret = request_irq(Q6FW_WDOG_EXPIRED_IRQ, modem_wdog_bite_irq,
300 IRQF_TRIGGER_RISING, "modem_wdog_fw", NULL);
301
302 if (ret < 0) {
303 pr_err("%s: Unable to request q6fw watchdog IRQ. (%d)\n",
304 __func__, ret);
305 goto out;
306 }
307
308 ret = request_irq(Q6SW_WDOG_EXPIRED_IRQ, modem_wdog_bite_irq,
309 IRQF_TRIGGER_RISING, "modem_wdog_sw", NULL);
310
311 if (ret < 0) {
312 pr_err("%s: Unable to request q6sw watchdog IRQ. (%d)\n",
313 __func__, ret);
314 disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
315 goto out;
316 }
317
318 ret = modem_subsystem_restart_init();
319
320 if (ret < 0) {
321 pr_err("%s: Unable to reg with subsystem restart. (%d)\n",
322 __func__, ret);
323 goto out;
324 }
325
Vikram Mulukutla09802372011-12-01 10:38:50 -0800326 modemfw_ramdump_dev = create_ramdump_device("modem_fw");
327
328 if (!modemfw_ramdump_dev) {
329 pr_err("%s: Unable to create modem fw ramdump device. (%d)\n",
330 __func__, -ENOMEM);
331 ret = -ENOMEM;
332 goto out;
333 }
334
335 modemsw_ramdump_dev = create_ramdump_device("modem_sw");
336
337 if (!modemsw_ramdump_dev) {
338 pr_err("%s: Unable to create modem sw ramdump device. (%d)\n",
339 __func__, -ENOMEM);
340 ret = -ENOMEM;
341 goto out;
342 }
343
Stephen Boyd4b66b372012-06-28 12:32:21 -0700344 smem_ramdump_dev = create_ramdump_device("smem-modem");
Vikram Mulukutla09802372011-12-01 10:38:50 -0800345
346 if (!smem_ramdump_dev) {
347 pr_err("%s: Unable to create smem ramdump device. (%d)\n",
348 __func__, -ENOMEM);
349 ret = -ENOMEM;
350 goto out;
351 }
352
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700353 ret = modem_debugfs_init();
354
Rohit Vaswani56dd22a2011-11-11 16:21:28 -0800355 pr_info("%s: modem fatal driver init'ed.\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700356out:
357 return ret;
358}
359
360module_init(modem_8960_init);