blob: 7345a89511927bf6c4dd39872c21d01aef46c850 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
2 *
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>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070030
31#include "smd_private.h"
32#include "modem_notifier.h"
33#include "ramdump.h"
34
35static int crash_shutdown;
36
37static void modem_sw_fatal_fn(struct work_struct *work)
38{
39 uint32_t panic_smsm_states = SMSM_RESET | SMSM_SYSTEM_DOWNLOAD;
40 uint32_t reset_smsm_states = SMSM_SYSTEM_REBOOT_USR |
41 SMSM_SYSTEM_PWRDWN_USR;
42 uint32_t modem_state;
43
44 pr_err("Watchdog bite received from modem SW!\n");
45
46 modem_state = smsm_get_state(SMSM_MODEM_STATE);
47
48 if (modem_state & panic_smsm_states) {
49
50 pr_err("Modem SMSM state changed to SMSM_RESET.\n"
51 "Probable err_fatal on the modem. "
52 "Calling subsystem restart...\n");
53 subsystem_restart("modem");
54
55 } else if (modem_state & reset_smsm_states) {
56
57 pr_err("%s: User-invoked system reset/powerdown. "
58 "Resetting the SoC now.\n",
59 __func__);
60 kernel_restart(NULL);
61 } else {
62 /* TODO: Bus unlock code/sequence goes _here_ */
63 subsystem_restart("modem");
64 }
65}
66
67static void modem_fw_fatal_fn(struct work_struct *work)
68{
69 pr_err("Watchdog bite received from modem FW!\n");
70 subsystem_restart("modem");
71}
72
73static DECLARE_WORK(modem_sw_fatal_work, modem_sw_fatal_fn);
74static DECLARE_WORK(modem_fw_fatal_work, modem_fw_fatal_fn);
75
76static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
77{
78 /* Ignore if we're the one that set SMSM_RESET */
79 if (crash_shutdown)
80 return;
81
82 if (new_state & SMSM_RESET) {
83 pr_err("Modem SMSM state changed to SMSM_RESET.\n"
84 "Probable err_fatal on the modem. "
85 "Calling subsystem restart...\n");
86 subsystem_restart("modem");
87 }
88}
89
Saravana Kannan4296f372011-09-21 15:16:22 -070090#define Q6_FW_WDOG_ENABLE 0x08882024
91#define Q6_SW_WDOG_ENABLE 0x08982024
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070092static int modem_shutdown(const struct subsys_data *subsys)
93{
Saravana Kannan4296f372011-09-21 15:16:22 -070094 void __iomem *q6_fw_wdog_addr;
95 void __iomem *q6_sw_wdog_addr;
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -070096 int smsm_notif_unregistered = 0;
97
98 if (!(smsm_get_state(SMSM_MODEM_STATE) & SMSM_RESET)) {
99 smsm_state_cb_deregister(SMSM_MODEM_STATE, SMSM_RESET,
100 smsm_state_cb, 0);
101 smsm_notif_unregistered = 1;
102 smsm_reset_modem(SMSM_RESET);
103 }
104
Saravana Kannan4296f372011-09-21 15:16:22 -0700105 /*
106 * Disable the modem watchdog since it keeps running even after the
107 * modem is shutdown.
108 */
109 q6_fw_wdog_addr = ioremap_nocache(Q6_FW_WDOG_ENABLE, 4);
110 if (!q6_fw_wdog_addr)
111 return -ENOMEM;
112
113 q6_sw_wdog_addr = ioremap_nocache(Q6_SW_WDOG_ENABLE, 4);
114 if (!q6_sw_wdog_addr) {
115 iounmap(q6_fw_wdog_addr);
116 return -ENOMEM;
117 }
118
119 writel_relaxed(0x0, q6_fw_wdog_addr);
120 writel_relaxed(0x0, q6_sw_wdog_addr);
121 mb();
122 iounmap(q6_sw_wdog_addr);
123 iounmap(q6_fw_wdog_addr);
124
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700125 pil_force_shutdown("modem");
Saravana Kannan0dcf4742011-09-21 17:19:17 -0700126 pil_force_shutdown("modem_fw");
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700127 disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
128 disable_irq_nosync(Q6SW_WDOG_EXPIRED_IRQ);
129
130 if (smsm_notif_unregistered)
131 smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
132 smsm_state_cb, 0);
133
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700134 return 0;
135}
136
137static int modem_powerup(const struct subsys_data *subsys)
138{
Saravana Kannan0dcf4742011-09-21 17:19:17 -0700139 pil_force_boot("modem_fw");
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700140 pil_force_boot("modem");
141 enable_irq(Q6FW_WDOG_EXPIRED_IRQ);
142 enable_irq(Q6SW_WDOG_EXPIRED_IRQ);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700143 return 0;
144}
145
146void modem_crash_shutdown(const struct subsys_data *subsys)
147{
148 crash_shutdown = 1;
149 smsm_reset_modem(SMSM_RESET);
150}
151
Vikram Mulukutla09802372011-12-01 10:38:50 -0800152/* FIXME: Get address, size from PIL */
153static struct ramdump_segment modemsw_segments[] = {
154 {0x89000000, 0x8D400000 - 0x89000000},
155};
156
157static struct ramdump_segment modemfw_segments[] = {
158 {0x8D400000, 0x8DA00000 - 0x8D400000},
159};
160
161static struct ramdump_segment smem_segments[] = {
162 {0x80000000, 0x00200000},
163};
164
165static void *modemfw_ramdump_dev;
166static void *modemsw_ramdump_dev;
167static void *smem_ramdump_dev;
168
169static int modem_ramdump(int enable,
170 const struct subsys_data *crashed_subsys)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700171{
Vikram Mulukutla09802372011-12-01 10:38:50 -0800172 int ret = 0;
173
174 if (enable) {
175 ret = do_ramdump(modemsw_ramdump_dev, modemsw_segments,
176 ARRAY_SIZE(modemsw_segments));
177
178 if (ret < 0) {
179 pr_err("Unable to dump modem sw memory (rc = %d).\n",
180 ret);
181 goto out;
182 }
183
184 ret = do_ramdump(modemfw_ramdump_dev, modemfw_segments,
185 ARRAY_SIZE(modemfw_segments));
186
187 if (ret < 0) {
188 pr_err("Unable to dump modem fw memory (rc = %d).\n",
189 ret);
190 goto out;
191 }
192
193 ret = do_ramdump(smem_ramdump_dev, smem_segments,
194 ARRAY_SIZE(smem_segments));
195
196 if (ret < 0) {
197 pr_err("Unable to dump smem memory (rc = %d).\n", ret);
198 goto out;
199 }
200 }
201
202out:
203 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700204}
205
206static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
207{
208 int ret;
209
210 switch (irq) {
211
212 case Q6SW_WDOG_EXPIRED_IRQ:
213 ret = schedule_work(&modem_sw_fatal_work);
214 disable_irq_nosync(Q6SW_WDOG_EXPIRED_IRQ);
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700215 disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700216 break;
217 case Q6FW_WDOG_EXPIRED_IRQ:
218 ret = schedule_work(&modem_fw_fatal_work);
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700219 disable_irq_nosync(Q6SW_WDOG_EXPIRED_IRQ);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700220 disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
221 break;
222 break;
223
224 default:
225 pr_err("%s: Unknown IRQ!\n", __func__);
226 }
227
228 return IRQ_HANDLED;
229}
230
231static struct subsys_data modem_8960 = {
232 .name = "modem",
233 .shutdown = modem_shutdown,
234 .powerup = modem_powerup,
235 .ramdump = modem_ramdump,
236 .crash_shutdown = modem_crash_shutdown
237};
238
239static int modem_subsystem_restart_init(void)
240{
241 return ssr_register_subsystem(&modem_8960);
242}
243
244static int modem_debug_set(void *data, u64 val)
245{
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700246 if (val == 1)
247 subsystem_restart("modem");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700248
249 return 0;
250}
251
252static int modem_debug_get(void *data, u64 *val)
253{
254 *val = 0;
255 return 0;
256}
257
258DEFINE_SIMPLE_ATTRIBUTE(modem_debug_fops, modem_debug_get, modem_debug_set,
259 "%llu\n");
260
261static int modem_debugfs_init(void)
262{
263 struct dentry *dent;
264 dent = debugfs_create_dir("modem_debug", 0);
265
266 if (IS_ERR(dent))
267 return PTR_ERR(dent);
268
269 debugfs_create_file("reset_modem", 0644, dent, NULL,
270 &modem_debug_fops);
271 return 0;
272}
273
274static int __init modem_8960_init(void)
275{
276 int ret;
277
Rohit Vaswani56dd22a2011-11-11 16:21:28 -0800278 if (!cpu_is_msm8960() && !cpu_is_msm8930() && !cpu_is_msm9615())
Stepan Moskovchenko6f594732011-09-20 16:51:47 -0700279 return -ENODEV;
280
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700281 ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
282 smsm_state_cb, 0);
283
284 if (ret < 0)
285 pr_err("%s: Unable to register SMSM callback! (%d)\n",
286 __func__, ret);
287
288 ret = request_irq(Q6FW_WDOG_EXPIRED_IRQ, modem_wdog_bite_irq,
289 IRQF_TRIGGER_RISING, "modem_wdog_fw", NULL);
290
291 if (ret < 0) {
292 pr_err("%s: Unable to request q6fw watchdog IRQ. (%d)\n",
293 __func__, ret);
294 goto out;
295 }
296
297 ret = request_irq(Q6SW_WDOG_EXPIRED_IRQ, modem_wdog_bite_irq,
298 IRQF_TRIGGER_RISING, "modem_wdog_sw", NULL);
299
300 if (ret < 0) {
301 pr_err("%s: Unable to request q6sw watchdog IRQ. (%d)\n",
302 __func__, ret);
303 disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
304 goto out;
305 }
306
307 ret = modem_subsystem_restart_init();
308
309 if (ret < 0) {
310 pr_err("%s: Unable to reg with subsystem restart. (%d)\n",
311 __func__, ret);
312 goto out;
313 }
314
Vikram Mulukutla09802372011-12-01 10:38:50 -0800315 modemfw_ramdump_dev = create_ramdump_device("modem_fw");
316
317 if (!modemfw_ramdump_dev) {
318 pr_err("%s: Unable to create modem fw ramdump device. (%d)\n",
319 __func__, -ENOMEM);
320 ret = -ENOMEM;
321 goto out;
322 }
323
324 modemsw_ramdump_dev = create_ramdump_device("modem_sw");
325
326 if (!modemsw_ramdump_dev) {
327 pr_err("%s: Unable to create modem sw ramdump device. (%d)\n",
328 __func__, -ENOMEM);
329 ret = -ENOMEM;
330 goto out;
331 }
332
333 smem_ramdump_dev = create_ramdump_device("smem");
334
335 if (!smem_ramdump_dev) {
336 pr_err("%s: Unable to create smem ramdump device. (%d)\n",
337 __func__, -ENOMEM);
338 ret = -ENOMEM;
339 goto out;
340 }
341
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700342 ret = modem_debugfs_init();
343
Rohit Vaswani56dd22a2011-11-11 16:21:28 -0800344 pr_info("%s: modem fatal driver init'ed.\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700345out:
346 return ret;
347}
348
349module_init(modem_8960_init);