blob: 19711a2aafffe37c7dbae37d87b6de4614e0070a [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
23#include <mach/irqs.h>
24#include <mach/scm.h>
25#include <mach/peripheral-loader.h>
26#include <mach/subsystem_restart.h>
27#include <mach/subsystem_notif.h>
28
29#include "smd_private.h"
30#include "modem_notifier.h"
31#include "ramdump.h"
32
33#define MODEM_HWIO_MSS_RESET_ADDR 0x00902C48
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -070034#define MODULE_NAME "modem_8660"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070035#define MODEM_WDOG_ENABLE 0x10020008
Vikram Mulukutla89549562011-07-28 16:10:41 -070036#define MODEM_CLEANUP_DELAY_MS 20
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070037
38#define SUBSYS_FATAL_DEBUG
39
40#if defined(SUBSYS_FATAL_DEBUG)
41static void debug_crash_modem_fn(struct work_struct *);
42static int reset_modem;
43
44static DECLARE_DELAYED_WORK(debug_crash_modem_work,
45 debug_crash_modem_fn);
46
47module_param(reset_modem, int, 0644);
48#endif
49
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070050/* Subsystem restart: Modem data, functions */
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -070051static void *modem_ramdump_dev;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070052static void modem_fatal_fn(struct work_struct *);
53static void modem_unlock_timeout(struct work_struct *work);
54static int modem_notif_handler(struct notifier_block *this,
55 unsigned long code,
56 void *_cmd);
57static DECLARE_WORK(modem_fatal_work, modem_fatal_fn);
58static DECLARE_DELAYED_WORK(modem_unlock_timeout_work,
59 modem_unlock_timeout);
60
61static struct notifier_block modem_notif_nb = {
62 .notifier_call = modem_notif_handler,
63};
64
65static void modem_unlock_timeout(struct work_struct *work)
66{
67 void __iomem *hwio_modem_reset_addr =
68 ioremap_nocache(MODEM_HWIO_MSS_RESET_ADDR, 8);
69 pr_crit("%s: Timeout waiting for modem to unlock.\n", MODULE_NAME);
70
71 /* Set MSS_MODEM_RESET to 0x0 since the unlock didn't work */
72 writel_relaxed(0x0, hwio_modem_reset_addr);
73 /* Write needs to go through before the modem is restarted. */
74 mb();
75 iounmap(hwio_modem_reset_addr);
76
77 subsystem_restart("modem");
Vikram Mulukutla93510c42011-08-19 19:04:40 -070078 enable_irq(MARM_WDOG_EXPIRED);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070079}
80
81static void modem_fatal_fn(struct work_struct *work)
82{
83 uint32_t modem_state;
84 uint32_t panic_smsm_states = SMSM_RESET | SMSM_SYSTEM_DOWNLOAD;
85 uint32_t reset_smsm_states = SMSM_SYSTEM_REBOOT_USR |
86 SMSM_SYSTEM_PWRDWN_USR;
87
88 pr_err("%s: Watchdog bite received from modem!\n", MODULE_NAME);
89
90 modem_state = smsm_get_state(SMSM_MODEM_STATE);
91 pr_err("%s: Modem SMSM state = 0x%x!", MODULE_NAME, modem_state);
92
93 if (modem_state == 0 || modem_state & panic_smsm_states) {
94
95 subsystem_restart("modem");
Vikram Mulukutla93510c42011-08-19 19:04:40 -070096 enable_irq(MARM_WDOG_EXPIRED);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070097
98 } else if (modem_state & reset_smsm_states) {
99
100 pr_err("%s: User-invoked system reset/powerdown.",
101 MODULE_NAME);
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700102 kernel_restart(NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700103
104 } else {
105
106 int ret;
107 void *hwio_modem_reset_addr =
108 ioremap_nocache(MODEM_HWIO_MSS_RESET_ADDR, 8);
109
110 pr_err("%s: Modem AHB locked up.\n", MODULE_NAME);
111 pr_err("%s: Trying to free up modem!\n", MODULE_NAME);
112
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700113 writel_relaxed(0x3, hwio_modem_reset_addr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700114
115 /* If we are still alive after 6 seconds (allowing for
116 * the 5-second-delayed-panic-reboot), modem is either
117 * still wedged or SMSM didn't come through. Force panic
118 * in that case.
119 */
120 ret = schedule_delayed_work(&modem_unlock_timeout_work,
121 msecs_to_jiffies(6000));
122
123 iounmap(hwio_modem_reset_addr);
124 }
125}
126
127static int modem_notif_handler(struct notifier_block *this,
128 unsigned long code,
129 void *_cmd)
130{
131 if (code == MODEM_NOTIFIER_START_RESET) {
132
133 pr_err("%s: Modem error fatal'ed.", MODULE_NAME);
134 subsystem_restart("modem");
135 }
136 return NOTIFY_DONE;
137}
138
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700139static int modem_shutdown(const struct subsys_data *crashed_subsys)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700140{
141 void __iomem *modem_wdog_addr;
142 int smsm_notif_unregistered = 0;
143
144 /* If the modem didn't already crash, setting SMSM_RESET
145 * here will help flush caches etc. Unregister for SMSM
146 * notifications to prevent unnecessary secondary calls to
147 * subsystem_restart.
148 */
149 if (!(smsm_get_state(SMSM_MODEM_STATE) & SMSM_RESET)) {
150 modem_unregister_notifier(&modem_notif_nb);
151 smsm_notif_unregistered = 1;
152 smsm_reset_modem(SMSM_RESET);
153 }
154
155 /* Disable the modem watchdog to allow clean modem bootup */
156 modem_wdog_addr = ioremap_nocache(MODEM_WDOG_ENABLE, 8);
157 writel_relaxed(0x0, modem_wdog_addr);
158
159 /*
160 * The write above needs to go through before the modem is
161 * powered up again (subsystem restart).
162 */
163 mb();
164 iounmap(modem_wdog_addr);
165
Vikram Mulukutla89549562011-07-28 16:10:41 -0700166 /* Wait here to allow the modem to clean up caches etc. */
167 msleep(MODEM_CLEANUP_DELAY_MS);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700168 pil_force_shutdown("modem");
169 disable_irq_nosync(MARM_WDOG_EXPIRED);
170
171 /* Re-register for SMSM notifications if necessary */
172 if (smsm_notif_unregistered)
173 modem_register_notifier(&modem_notif_nb);
174
175
176 return 0;
177}
178
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700179static int modem_powerup(const struct subsys_data *crashed_subsys)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700180{
181 int ret;
182
183 ret = pil_force_boot("modem");
184 enable_irq(MARM_WDOG_EXPIRED);
185
186 return ret;
187}
188
189/* FIXME: Get address, size from PIL */
190static struct ramdump_segment modem_segments[] = {
191 {0x42F00000, 0x46000000 - 0x42F00000} };
192
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700193static int modem_ramdump(int enable,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700194 const struct subsys_data *crashed_subsys)
195{
196 if (enable)
197 return do_ramdump(modem_ramdump_dev, modem_segments,
198 ARRAY_SIZE(modem_segments));
199 else
200 return 0;
201}
202
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700203static void modem_crash_shutdown(
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700204 const struct subsys_data *crashed_subsys)
205{
206 /* If modem hasn't already crashed, send SMSM_RESET. */
207 if (!(smsm_get_state(SMSM_MODEM_STATE) & SMSM_RESET)) {
208 modem_unregister_notifier(&modem_notif_nb);
209 smsm_reset_modem(SMSM_RESET);
210 }
211
Vikram Mulukutla6066a842011-09-16 11:14:50 -0700212 /* Wait to allow the modem to clean up caches etc. */
213 mdelay(5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700214}
215
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700216static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700217{
218 int ret;
219
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700220 ret = schedule_work(&modem_fatal_work);
221 disable_irq_nosync(MARM_WDOG_EXPIRED);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700222
223 return IRQ_HANDLED;
224}
225
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700226static struct subsys_data subsys_8660_modem = {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700227 .name = "modem",
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700228 .shutdown = modem_shutdown,
229 .powerup = modem_powerup,
230 .ramdump = modem_ramdump,
231 .crash_shutdown = modem_crash_shutdown
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700232};
233
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700234static int __init modem_8660_init(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700235{
236 int ret;
237
238 /* Need to listen for SMSM_RESET always */
239 modem_register_notifier(&modem_notif_nb);
240
241#if defined(SUBSYS_FATAL_DEBUG)
242 schedule_delayed_work(&debug_crash_modem_work, msecs_to_jiffies(5000));
243#endif
244
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700245 ret = request_irq(MARM_WDOG_EXPIRED, modem_wdog_bite_irq,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700246 IRQF_TRIGGER_RISING, "modem_wdog", NULL);
247
248 if (ret < 0) {
249 pr_err("%s: Unable to request MARM_WDOG_EXPIRED irq.",
250 __func__);
251 goto out;
252 }
253
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700254 modem_ramdump_dev = create_ramdump_device("modem");
255
256 if (!modem_ramdump_dev) {
257 ret = -ENOMEM;
258 goto out;
259 }
260
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700261 ret = ssr_register_subsystem(&subsys_8660_modem);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700262out:
263 return ret;
264}
265
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700266static void __exit modem_8660_exit(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700267{
268 free_irq(MARM_WDOG_EXPIRED, NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700269}
270
271#ifdef SUBSYS_FATAL_DEBUG
272static void debug_crash_modem_fn(struct work_struct *work)
273{
274 if (reset_modem == 1)
275 smsm_reset_modem(SMSM_RESET);
276 else if (reset_modem == 2)
277 subsystem_restart("lpass");
278
279 reset_modem = 0;
280 schedule_delayed_work(&debug_crash_modem_work, msecs_to_jiffies(1000));
281}
282#endif
283
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700284module_init(modem_8660_init);
285module_exit(modem_8660_exit);
286