blob: cda32375ff07fa1c0b82f7aefa3baf8d6be43bd5 [file] [log] [blame]
Duy Truonge833aca2013-02-12 13:35:08 -08001/* Copyright (c) 2011-2012, The Linux Foundation. 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
41
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();
Stephen Boyd0ebf7212012-04-30 20:42:35 -070069 subsystem_restart_dev(modem_8960_dev);
Vikram Mulukutla773c1b32012-06-05 18:44:35 -070070}
71
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070072static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
73{
74 /* Ignore if we're the one that set SMSM_RESET */
75 if (crash_shutdown)
76 return;
77
78 if (new_state & SMSM_RESET) {
Vikram Mulukutla121a3c22012-05-01 16:08:21 -070079 pr_err("Probable fatal error on the modem.\n");
Vikram Mulukutla773c1b32012-06-05 18:44:35 -070080 restart_modem();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070081 }
82}
83
Vikram Mulukutlaaf1a4ec2012-07-16 13:27:25 -070084#define Q6_FW_WDOG_ENABLE 0x08882024
85#define Q6_SW_WDOG_ENABLE 0x08982024
86
Stephen Boyd0ebf7212012-04-30 20:42:35 -070087static int modem_shutdown(const struct subsys_desc *subsys)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070088{
Saravana Kannan4296f372011-09-21 15:16:22 -070089 void __iomem *q6_fw_wdog_addr;
90 void __iomem *q6_sw_wdog_addr;
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -070091
Saravana Kannan4296f372011-09-21 15:16:22 -070092 /*
93 * Disable the modem watchdog since it keeps running even after the
94 * modem is shutdown.
95 */
96 q6_fw_wdog_addr = ioremap_nocache(Q6_FW_WDOG_ENABLE, 4);
97 if (!q6_fw_wdog_addr)
98 return -ENOMEM;
99
100 q6_sw_wdog_addr = ioremap_nocache(Q6_SW_WDOG_ENABLE, 4);
101 if (!q6_sw_wdog_addr) {
102 iounmap(q6_fw_wdog_addr);
103 return -ENOMEM;
104 }
105
106 writel_relaxed(0x0, q6_fw_wdog_addr);
107 writel_relaxed(0x0, q6_sw_wdog_addr);
108 mb();
109 iounmap(q6_sw_wdog_addr);
110 iounmap(q6_fw_wdog_addr);
111
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700112 pil_force_shutdown("modem");
Saravana Kannan0dcf4742011-09-21 17:19:17 -0700113 pil_force_shutdown("modem_fw");
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700114 disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
115 disable_irq_nosync(Q6SW_WDOG_EXPIRED_IRQ);
116
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700117 return 0;
118}
119
Vikram Mulukutla21b95f82012-05-14 18:30:31 -0700120#define MODEM_WDOG_CHECK_TIMEOUT_MS 10000
121
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700122static int modem_powerup(const struct subsys_desc *subsys)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700123{
Saravana Kannan0dcf4742011-09-21 17:19:17 -0700124 pil_force_boot("modem_fw");
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700125 pil_force_boot("modem");
126 enable_irq(Q6FW_WDOG_EXPIRED_IRQ);
127 enable_irq(Q6SW_WDOG_EXPIRED_IRQ);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700128 return 0;
129}
130
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700131void modem_crash_shutdown(const struct subsys_desc *subsys)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700132{
133 crash_shutdown = 1;
134 smsm_reset_modem(SMSM_RESET);
135}
136
Vikram Mulukutla09802372011-12-01 10:38:50 -0800137/* FIXME: Get address, size from PIL */
138static struct ramdump_segment modemsw_segments[] = {
139 {0x89000000, 0x8D400000 - 0x89000000},
140};
141
142static struct ramdump_segment modemfw_segments[] = {
143 {0x8D400000, 0x8DA00000 - 0x8D400000},
144};
145
146static struct ramdump_segment smem_segments[] = {
147 {0x80000000, 0x00200000},
148};
149
150static void *modemfw_ramdump_dev;
151static void *modemsw_ramdump_dev;
152static void *smem_ramdump_dev;
153
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700154static int modem_ramdump(int enable, const struct subsys_desc *crashed_subsys)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700155{
Vikram Mulukutla09802372011-12-01 10:38:50 -0800156 int ret = 0;
157
158 if (enable) {
159 ret = do_ramdump(modemsw_ramdump_dev, modemsw_segments,
160 ARRAY_SIZE(modemsw_segments));
161
162 if (ret < 0) {
163 pr_err("Unable to dump modem sw memory (rc = %d).\n",
164 ret);
165 goto out;
166 }
167
168 ret = do_ramdump(modemfw_ramdump_dev, modemfw_segments,
169 ARRAY_SIZE(modemfw_segments));
170
171 if (ret < 0) {
172 pr_err("Unable to dump modem fw memory (rc = %d).\n",
173 ret);
174 goto out;
175 }
176
177 ret = do_ramdump(smem_ramdump_dev, smem_segments,
178 ARRAY_SIZE(smem_segments));
179
180 if (ret < 0) {
181 pr_err("Unable to dump smem memory (rc = %d).\n", ret);
182 goto out;
183 }
184 }
185
186out:
187 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700188}
189
190static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
191{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700192 switch (irq) {
193
194 case Q6SW_WDOG_EXPIRED_IRQ:
Vikram Mulukutla773c1b32012-06-05 18:44:35 -0700195 pr_err("Watchdog bite received from modem software!\n");
196 restart_modem();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700197 break;
198 case Q6FW_WDOG_EXPIRED_IRQ:
Vikram Mulukutla773c1b32012-06-05 18:44:35 -0700199 pr_err("Watchdog bite received from modem firmware!\n");
200 restart_modem();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700201 break;
202 break;
203
204 default:
205 pr_err("%s: Unknown IRQ!\n", __func__);
206 }
207
208 return IRQ_HANDLED;
209}
210
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700211static struct subsys_desc modem_8960 = {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700212 .name = "modem",
213 .shutdown = modem_shutdown,
214 .powerup = modem_powerup,
215 .ramdump = modem_ramdump,
216 .crash_shutdown = modem_crash_shutdown
217};
218
219static int modem_subsystem_restart_init(void)
220{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700221 modem_8960_dev = subsys_register(&modem_8960);
222 if (IS_ERR(modem_8960_dev))
223 return PTR_ERR(modem_8960_dev);
224 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700225}
226
227static int modem_debug_set(void *data, u64 val)
228{
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700229 if (val == 1)
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700230 subsystem_restart_dev(modem_8960_dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700231
232 return 0;
233}
234
235static int modem_debug_get(void *data, u64 *val)
236{
237 *val = 0;
238 return 0;
239}
240
241DEFINE_SIMPLE_ATTRIBUTE(modem_debug_fops, modem_debug_get, modem_debug_set,
242 "%llu\n");
243
244static int modem_debugfs_init(void)
245{
246 struct dentry *dent;
247 dent = debugfs_create_dir("modem_debug", 0);
248
249 if (IS_ERR(dent))
250 return PTR_ERR(dent);
251
252 debugfs_create_file("reset_modem", 0644, dent, NULL,
253 &modem_debug_fops);
254 return 0;
255}
256
257static int __init modem_8960_init(void)
258{
259 int ret;
260
Jay Chokshi66592fc2013-01-17 15:14:46 -0800261 if (soc_class_is_apq8064())
Stepan Moskovchenko6f594732011-09-20 16:51:47 -0700262 return -ENODEV;
263
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700264 ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
265 smsm_state_cb, 0);
266
267 if (ret < 0)
268 pr_err("%s: Unable to register SMSM callback! (%d)\n",
269 __func__, ret);
270
271 ret = request_irq(Q6FW_WDOG_EXPIRED_IRQ, modem_wdog_bite_irq,
272 IRQF_TRIGGER_RISING, "modem_wdog_fw", NULL);
273
274 if (ret < 0) {
275 pr_err("%s: Unable to request q6fw watchdog IRQ. (%d)\n",
276 __func__, ret);
277 goto out;
278 }
279
280 ret = request_irq(Q6SW_WDOG_EXPIRED_IRQ, modem_wdog_bite_irq,
281 IRQF_TRIGGER_RISING, "modem_wdog_sw", NULL);
282
283 if (ret < 0) {
284 pr_err("%s: Unable to request q6sw watchdog IRQ. (%d)\n",
285 __func__, ret);
286 disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
287 goto out;
288 }
289
290 ret = modem_subsystem_restart_init();
291
292 if (ret < 0) {
293 pr_err("%s: Unable to reg with subsystem restart. (%d)\n",
294 __func__, ret);
295 goto out;
296 }
297
Vikram Mulukutla09802372011-12-01 10:38:50 -0800298 modemfw_ramdump_dev = create_ramdump_device("modem_fw");
299
300 if (!modemfw_ramdump_dev) {
301 pr_err("%s: Unable to create modem fw ramdump device. (%d)\n",
302 __func__, -ENOMEM);
303 ret = -ENOMEM;
304 goto out;
305 }
306
307 modemsw_ramdump_dev = create_ramdump_device("modem_sw");
308
309 if (!modemsw_ramdump_dev) {
310 pr_err("%s: Unable to create modem sw ramdump device. (%d)\n",
311 __func__, -ENOMEM);
312 ret = -ENOMEM;
313 goto out;
314 }
315
Stephen Boyd4b66b372012-06-28 12:32:21 -0700316 smem_ramdump_dev = create_ramdump_device("smem-modem");
Vikram Mulukutla09802372011-12-01 10:38:50 -0800317
318 if (!smem_ramdump_dev) {
319 pr_err("%s: Unable to create smem ramdump device. (%d)\n",
320 __func__, -ENOMEM);
321 ret = -ENOMEM;
322 goto out;
323 }
324
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700325 ret = modem_debugfs_init();
326
Rohit Vaswani56dd22a2011-11-11 16:21:28 -0800327 pr_info("%s: modem fatal driver init'ed.\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700328out:
329 return ret;
330}
331
332module_init(modem_8960_init);