blob: fd7b7b55fdc9ead7602faed864aea30e8499d00f [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 Mulukutla773c1b32012-06-05 18:44:35 -070066static void restart_modem(void)
67{
68 log_modem_sfr();
69 subsystem_restart("modem");
70}
71
Vikram Mulukutla21b95f82012-05-14 18:30:31 -070072static void modem_wdog_check(struct work_struct *work)
73{
74 void __iomem *q6_sw_wdog_addr;
75 u32 regval;
76
77 q6_sw_wdog_addr = ioremap_nocache(Q6_SW_WDOG_ENABLE, 4);
78 if (!q6_sw_wdog_addr)
79 panic("Unable to check modem watchdog status.\n");
80
81 regval = readl_relaxed(q6_sw_wdog_addr);
82 if (!regval) {
83 pr_err("modem-8960: Modem watchdog wasn't activated!. Restarting the modem now.\n");
Vikram Mulukutla773c1b32012-06-05 18:44:35 -070084 restart_modem();
Vikram Mulukutla21b95f82012-05-14 18:30:31 -070085 }
86
87 iounmap(q6_sw_wdog_addr);
88}
89
90static DECLARE_DELAYED_WORK(modem_wdog_check_work, modem_wdog_check);
91
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070092static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
93{
94 /* Ignore if we're the one that set SMSM_RESET */
95 if (crash_shutdown)
96 return;
97
98 if (new_state & SMSM_RESET) {
Vikram Mulukutla121a3c22012-05-01 16:08:21 -070099 pr_err("Probable fatal error on the modem.\n");
Vikram Mulukutla773c1b32012-06-05 18:44:35 -0700100 restart_modem();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700101 }
102}
103
104static int modem_shutdown(const struct subsys_data *subsys)
105{
Saravana Kannan4296f372011-09-21 15:16:22 -0700106 void __iomem *q6_fw_wdog_addr;
107 void __iomem *q6_sw_wdog_addr;
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700108
Saravana Kannan4296f372011-09-21 15:16:22 -0700109 /*
Vikram Mulukutla21b95f82012-05-14 18:30:31 -0700110 * Cancel any pending wdog_check work items, since we're shutting
111 * down anyway.
112 */
113 cancel_delayed_work(&modem_wdog_check_work);
114
115 /*
Saravana Kannan4296f372011-09-21 15:16:22 -0700116 * Disable the modem watchdog since it keeps running even after the
117 * modem is shutdown.
118 */
119 q6_fw_wdog_addr = ioremap_nocache(Q6_FW_WDOG_ENABLE, 4);
120 if (!q6_fw_wdog_addr)
121 return -ENOMEM;
122
123 q6_sw_wdog_addr = ioremap_nocache(Q6_SW_WDOG_ENABLE, 4);
124 if (!q6_sw_wdog_addr) {
125 iounmap(q6_fw_wdog_addr);
126 return -ENOMEM;
127 }
128
129 writel_relaxed(0x0, q6_fw_wdog_addr);
130 writel_relaxed(0x0, q6_sw_wdog_addr);
131 mb();
132 iounmap(q6_sw_wdog_addr);
133 iounmap(q6_fw_wdog_addr);
134
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700135 pil_force_shutdown("modem");
Saravana Kannan0dcf4742011-09-21 17:19:17 -0700136 pil_force_shutdown("modem_fw");
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700137 disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
138 disable_irq_nosync(Q6SW_WDOG_EXPIRED_IRQ);
139
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700140 return 0;
141}
142
Vikram Mulukutla21b95f82012-05-14 18:30:31 -0700143#define MODEM_WDOG_CHECK_TIMEOUT_MS 10000
144
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700145static int modem_powerup(const struct subsys_data *subsys)
146{
Saravana Kannan0dcf4742011-09-21 17:19:17 -0700147 pil_force_boot("modem_fw");
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700148 pil_force_boot("modem");
149 enable_irq(Q6FW_WDOG_EXPIRED_IRQ);
150 enable_irq(Q6SW_WDOG_EXPIRED_IRQ);
Vikram Mulukutla21b95f82012-05-14 18:30:31 -0700151 schedule_delayed_work(&modem_wdog_check_work,
152 msecs_to_jiffies(MODEM_WDOG_CHECK_TIMEOUT_MS));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700153 return 0;
154}
155
156void modem_crash_shutdown(const struct subsys_data *subsys)
157{
158 crash_shutdown = 1;
159 smsm_reset_modem(SMSM_RESET);
160}
161
Vikram Mulukutla09802372011-12-01 10:38:50 -0800162/* FIXME: Get address, size from PIL */
163static struct ramdump_segment modemsw_segments[] = {
164 {0x89000000, 0x8D400000 - 0x89000000},
165};
166
167static struct ramdump_segment modemfw_segments[] = {
168 {0x8D400000, 0x8DA00000 - 0x8D400000},
169};
170
171static struct ramdump_segment smem_segments[] = {
172 {0x80000000, 0x00200000},
173};
174
175static void *modemfw_ramdump_dev;
176static void *modemsw_ramdump_dev;
177static void *smem_ramdump_dev;
178
179static int modem_ramdump(int enable,
180 const struct subsys_data *crashed_subsys)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700181{
Vikram Mulukutla09802372011-12-01 10:38:50 -0800182 int ret = 0;
183
184 if (enable) {
185 ret = do_ramdump(modemsw_ramdump_dev, modemsw_segments,
186 ARRAY_SIZE(modemsw_segments));
187
188 if (ret < 0) {
189 pr_err("Unable to dump modem sw memory (rc = %d).\n",
190 ret);
191 goto out;
192 }
193
194 ret = do_ramdump(modemfw_ramdump_dev, modemfw_segments,
195 ARRAY_SIZE(modemfw_segments));
196
197 if (ret < 0) {
198 pr_err("Unable to dump modem fw memory (rc = %d).\n",
199 ret);
200 goto out;
201 }
202
203 ret = do_ramdump(smem_ramdump_dev, smem_segments,
204 ARRAY_SIZE(smem_segments));
205
206 if (ret < 0) {
207 pr_err("Unable to dump smem memory (rc = %d).\n", ret);
208 goto out;
209 }
210 }
211
212out:
213 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700214}
215
216static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
217{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700218 switch (irq) {
219
220 case Q6SW_WDOG_EXPIRED_IRQ:
Vikram Mulukutla773c1b32012-06-05 18:44:35 -0700221 pr_err("Watchdog bite received from modem software!\n");
222 restart_modem();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700223 break;
224 case Q6FW_WDOG_EXPIRED_IRQ:
Vikram Mulukutla773c1b32012-06-05 18:44:35 -0700225 pr_err("Watchdog bite received from modem firmware!\n");
226 restart_modem();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700227 break;
228 break;
229
230 default:
231 pr_err("%s: Unknown IRQ!\n", __func__);
232 }
233
234 return IRQ_HANDLED;
235}
236
237static struct subsys_data modem_8960 = {
238 .name = "modem",
239 .shutdown = modem_shutdown,
240 .powerup = modem_powerup,
241 .ramdump = modem_ramdump,
242 .crash_shutdown = modem_crash_shutdown
243};
244
245static int modem_subsystem_restart_init(void)
246{
247 return ssr_register_subsystem(&modem_8960);
248}
249
250static int modem_debug_set(void *data, u64 val)
251{
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700252 if (val == 1)
253 subsystem_restart("modem");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700254
255 return 0;
256}
257
258static int modem_debug_get(void *data, u64 *val)
259{
260 *val = 0;
261 return 0;
262}
263
264DEFINE_SIMPLE_ATTRIBUTE(modem_debug_fops, modem_debug_get, modem_debug_set,
265 "%llu\n");
266
267static int modem_debugfs_init(void)
268{
269 struct dentry *dent;
270 dent = debugfs_create_dir("modem_debug", 0);
271
272 if (IS_ERR(dent))
273 return PTR_ERR(dent);
274
275 debugfs_create_file("reset_modem", 0644, dent, NULL,
276 &modem_debug_fops);
277 return 0;
278}
279
280static int __init modem_8960_init(void)
281{
282 int ret;
283
Stepan Moskovchenko0df9bb22012-07-06 18:19:15 -0700284 if (!cpu_is_msm8960() && !cpu_is_msm8930() && !cpu_is_msm8930aa() &&
285 !cpu_is_msm9615() && !cpu_is_msm8627())
Stepan Moskovchenko6f594732011-09-20 16:51:47 -0700286 return -ENODEV;
287
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700288 ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
289 smsm_state_cb, 0);
290
291 if (ret < 0)
292 pr_err("%s: Unable to register SMSM callback! (%d)\n",
293 __func__, ret);
294
295 ret = request_irq(Q6FW_WDOG_EXPIRED_IRQ, modem_wdog_bite_irq,
296 IRQF_TRIGGER_RISING, "modem_wdog_fw", NULL);
297
298 if (ret < 0) {
299 pr_err("%s: Unable to request q6fw watchdog IRQ. (%d)\n",
300 __func__, ret);
301 goto out;
302 }
303
304 ret = request_irq(Q6SW_WDOG_EXPIRED_IRQ, modem_wdog_bite_irq,
305 IRQF_TRIGGER_RISING, "modem_wdog_sw", NULL);
306
307 if (ret < 0) {
308 pr_err("%s: Unable to request q6sw watchdog IRQ. (%d)\n",
309 __func__, ret);
310 disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
311 goto out;
312 }
313
314 ret = modem_subsystem_restart_init();
315
316 if (ret < 0) {
317 pr_err("%s: Unable to reg with subsystem restart. (%d)\n",
318 __func__, ret);
319 goto out;
320 }
321
Vikram Mulukutla09802372011-12-01 10:38:50 -0800322 modemfw_ramdump_dev = create_ramdump_device("modem_fw");
323
324 if (!modemfw_ramdump_dev) {
325 pr_err("%s: Unable to create modem fw ramdump device. (%d)\n",
326 __func__, -ENOMEM);
327 ret = -ENOMEM;
328 goto out;
329 }
330
331 modemsw_ramdump_dev = create_ramdump_device("modem_sw");
332
333 if (!modemsw_ramdump_dev) {
334 pr_err("%s: Unable to create modem sw ramdump device. (%d)\n",
335 __func__, -ENOMEM);
336 ret = -ENOMEM;
337 goto out;
338 }
339
Stephen Boyd4b66b372012-06-28 12:32:21 -0700340 smem_ramdump_dev = create_ramdump_device("smem-modem");
Vikram Mulukutla09802372011-12-01 10:38:50 -0800341
342 if (!smem_ramdump_dev) {
343 pr_err("%s: Unable to create smem ramdump device. (%d)\n",
344 __func__, -ENOMEM);
345 ret = -ENOMEM;
346 goto out;
347 }
348
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700349 ret = modem_debugfs_init();
350
Rohit Vaswani56dd22a2011-11-11 16:21:28 -0800351 pr_info("%s: modem fatal driver init'ed.\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700352out:
353 return ret;
354}
355
356module_init(modem_8960_init);