blob: 0b7b768856aa0d50b3d9c96c417a5c1b43bbd672 [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;
Vikram Mulukutlaf4510d22011-12-16 11:40:21 -080043static int ignore_smsm_ack;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070044
45static DECLARE_DELAYED_WORK(debug_crash_modem_work,
46 debug_crash_modem_fn);
47
48module_param(reset_modem, int, 0644);
49#endif
50
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070051/* Subsystem restart: Modem data, functions */
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -070052static void *modem_ramdump_dev;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070053static void modem_fatal_fn(struct work_struct *);
54static void modem_unlock_timeout(struct work_struct *work);
55static int modem_notif_handler(struct notifier_block *this,
56 unsigned long code,
57 void *_cmd);
58static DECLARE_WORK(modem_fatal_work, modem_fatal_fn);
59static DECLARE_DELAYED_WORK(modem_unlock_timeout_work,
60 modem_unlock_timeout);
61
62static struct notifier_block modem_notif_nb = {
63 .notifier_call = modem_notif_handler,
64};
65
66static void modem_unlock_timeout(struct work_struct *work)
67{
68 void __iomem *hwio_modem_reset_addr =
69 ioremap_nocache(MODEM_HWIO_MSS_RESET_ADDR, 8);
70 pr_crit("%s: Timeout waiting for modem to unlock.\n", MODULE_NAME);
71
72 /* Set MSS_MODEM_RESET to 0x0 since the unlock didn't work */
73 writel_relaxed(0x0, hwio_modem_reset_addr);
74 /* Write needs to go through before the modem is restarted. */
75 mb();
76 iounmap(hwio_modem_reset_addr);
77
78 subsystem_restart("modem");
Vikram Mulukutla93510c42011-08-19 19:04:40 -070079 enable_irq(MARM_WDOG_EXPIRED);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070080}
81
82static void modem_fatal_fn(struct work_struct *work)
83{
84 uint32_t modem_state;
85 uint32_t panic_smsm_states = SMSM_RESET | SMSM_SYSTEM_DOWNLOAD;
86 uint32_t reset_smsm_states = SMSM_SYSTEM_REBOOT_USR |
87 SMSM_SYSTEM_PWRDWN_USR;
88
89 pr_err("%s: Watchdog bite received from modem!\n", MODULE_NAME);
90
91 modem_state = smsm_get_state(SMSM_MODEM_STATE);
92 pr_err("%s: Modem SMSM state = 0x%x!", MODULE_NAME, modem_state);
93
94 if (modem_state == 0 || modem_state & panic_smsm_states) {
95
96 subsystem_restart("modem");
Vikram Mulukutla93510c42011-08-19 19:04:40 -070097 enable_irq(MARM_WDOG_EXPIRED);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070098
99 } else if (modem_state & reset_smsm_states) {
100
101 pr_err("%s: User-invoked system reset/powerdown.",
102 MODULE_NAME);
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700103 kernel_restart(NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700104
105 } else {
106
107 int ret;
108 void *hwio_modem_reset_addr =
109 ioremap_nocache(MODEM_HWIO_MSS_RESET_ADDR, 8);
110
111 pr_err("%s: Modem AHB locked up.\n", MODULE_NAME);
112 pr_err("%s: Trying to free up modem!\n", MODULE_NAME);
113
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700114 writel_relaxed(0x3, hwio_modem_reset_addr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700115
116 /* If we are still alive after 6 seconds (allowing for
117 * the 5-second-delayed-panic-reboot), modem is either
118 * still wedged or SMSM didn't come through. Force panic
119 * in that case.
120 */
121 ret = schedule_delayed_work(&modem_unlock_timeout_work,
122 msecs_to_jiffies(6000));
123
124 iounmap(hwio_modem_reset_addr);
125 }
126}
127
128static int modem_notif_handler(struct notifier_block *this,
129 unsigned long code,
130 void *_cmd)
131{
132 if (code == MODEM_NOTIFIER_START_RESET) {
Vikram Mulukutlaf4510d22011-12-16 11:40:21 -0800133 if (ignore_smsm_ack) {
134 ignore_smsm_ack = 0;
135 goto out;
136 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700137 pr_err("%s: Modem error fatal'ed.", MODULE_NAME);
138 subsystem_restart("modem");
139 }
Vikram Mulukutlaf4510d22011-12-16 11:40:21 -0800140out:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700141 return NOTIFY_DONE;
142}
143
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700144static int modem_shutdown(const struct subsys_data *crashed_subsys)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700145{
146 void __iomem *modem_wdog_addr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700147
148 /* If the modem didn't already crash, setting SMSM_RESET
Vikram Mulukutlaf4510d22011-12-16 11:40:21 -0800149 * here will help flush caches etc. The ignore_smsm_ack
150 * flag is set to ignore the SMSM_RESET notification
151 * that is generated due to the modem settings its own
152 * SMSM_RESET bit in response to the apps setting the
153 * apps SMSM_RESET bit.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700154 */
155 if (!(smsm_get_state(SMSM_MODEM_STATE) & SMSM_RESET)) {
Vikram Mulukutlaf4510d22011-12-16 11:40:21 -0800156 ignore_smsm_ack = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700157 smsm_reset_modem(SMSM_RESET);
158 }
159
160 /* Disable the modem watchdog to allow clean modem bootup */
161 modem_wdog_addr = ioremap_nocache(MODEM_WDOG_ENABLE, 8);
162 writel_relaxed(0x0, modem_wdog_addr);
163
164 /*
165 * The write above needs to go through before the modem is
166 * powered up again (subsystem restart).
167 */
168 mb();
169 iounmap(modem_wdog_addr);
170
Vikram Mulukutla89549562011-07-28 16:10:41 -0700171 /* Wait here to allow the modem to clean up caches etc. */
172 msleep(MODEM_CLEANUP_DELAY_MS);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700173 pil_force_shutdown("modem");
174 disable_irq_nosync(MARM_WDOG_EXPIRED);
175
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700176
177
178 return 0;
179}
180
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700181static int modem_powerup(const struct subsys_data *crashed_subsys)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700182{
183 int ret;
184
185 ret = pil_force_boot("modem");
186 enable_irq(MARM_WDOG_EXPIRED);
187
188 return ret;
189}
190
191/* FIXME: Get address, size from PIL */
192static struct ramdump_segment modem_segments[] = {
193 {0x42F00000, 0x46000000 - 0x42F00000} };
194
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700195static int modem_ramdump(int enable,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700196 const struct subsys_data *crashed_subsys)
197{
198 if (enable)
199 return do_ramdump(modem_ramdump_dev, modem_segments,
200 ARRAY_SIZE(modem_segments));
201 else
202 return 0;
203}
204
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700205static void modem_crash_shutdown(
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700206 const struct subsys_data *crashed_subsys)
207{
208 /* If modem hasn't already crashed, send SMSM_RESET. */
209 if (!(smsm_get_state(SMSM_MODEM_STATE) & SMSM_RESET)) {
210 modem_unregister_notifier(&modem_notif_nb);
211 smsm_reset_modem(SMSM_RESET);
212 }
213
Vikram Mulukutla6066a842011-09-16 11:14:50 -0700214 /* Wait to allow the modem to clean up caches etc. */
215 mdelay(5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700216}
217
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700218static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700219{
220 int ret;
221
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700222 ret = schedule_work(&modem_fatal_work);
223 disable_irq_nosync(MARM_WDOG_EXPIRED);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700224
225 return IRQ_HANDLED;
226}
227
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700228static struct subsys_data subsys_8660_modem = {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700229 .name = "modem",
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700230 .shutdown = modem_shutdown,
231 .powerup = modem_powerup,
232 .ramdump = modem_ramdump,
233 .crash_shutdown = modem_crash_shutdown
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700234};
235
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700236static int __init modem_8660_init(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700237{
238 int ret;
239
240 /* Need to listen for SMSM_RESET always */
241 modem_register_notifier(&modem_notif_nb);
242
243#if defined(SUBSYS_FATAL_DEBUG)
244 schedule_delayed_work(&debug_crash_modem_work, msecs_to_jiffies(5000));
245#endif
246
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700247 ret = request_irq(MARM_WDOG_EXPIRED, modem_wdog_bite_irq,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700248 IRQF_TRIGGER_RISING, "modem_wdog", NULL);
249
250 if (ret < 0) {
251 pr_err("%s: Unable to request MARM_WDOG_EXPIRED irq.",
252 __func__);
253 goto out;
254 }
255
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700256 modem_ramdump_dev = create_ramdump_device("modem");
257
258 if (!modem_ramdump_dev) {
259 ret = -ENOMEM;
260 goto out;
261 }
262
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700263 ret = ssr_register_subsystem(&subsys_8660_modem);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700264out:
265 return ret;
266}
267
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700268static void __exit modem_8660_exit(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700269{
270 free_irq(MARM_WDOG_EXPIRED, NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700271}
272
273#ifdef SUBSYS_FATAL_DEBUG
274static void debug_crash_modem_fn(struct work_struct *work)
275{
276 if (reset_modem == 1)
277 smsm_reset_modem(SMSM_RESET);
278 else if (reset_modem == 2)
279 subsystem_restart("lpass");
280
281 reset_modem = 0;
282 schedule_delayed_work(&debug_crash_modem_work, msecs_to_jiffies(1000));
283}
284#endif
285
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700286module_init(modem_8660_init);
287module_exit(modem_8660_exit);
288