blob: 096ed9ce9b5bb9499eb753630cb7ae2c73ea98aa [file] [log] [blame]
Stephen Boyd373145c2012-05-02 16:45:31 -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>
Stephen Boyd0ebf7212012-04-30 20:42:35 -070022#include <linux/err.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070023
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>
29
30#include "smd_private.h"
31#include "modem_notifier.h"
32#include "ramdump.h"
33
34#define MODEM_HWIO_MSS_RESET_ADDR 0x00902C48
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -070035#define MODULE_NAME "modem_8660"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070036#define MODEM_WDOG_ENABLE 0x10020008
Vikram Mulukutla89549562011-07-28 16:10:41 -070037#define MODEM_CLEANUP_DELAY_MS 20
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070038
39#define SUBSYS_FATAL_DEBUG
40
41#if defined(SUBSYS_FATAL_DEBUG)
42static void debug_crash_modem_fn(struct work_struct *);
43static int reset_modem;
Vikram Mulukutlaf4510d22011-12-16 11:40:21 -080044static int ignore_smsm_ack;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070045
46static DECLARE_DELAYED_WORK(debug_crash_modem_work,
47 debug_crash_modem_fn);
48
49module_param(reset_modem, int, 0644);
50#endif
51
Stephen Boyd0ebf7212012-04-30 20:42:35 -070052static struct subsys_device *modem_8660_dev;
53
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070054/* Subsystem restart: Modem data, functions */
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -070055static void *modem_ramdump_dev;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070056static void modem_fatal_fn(struct work_struct *);
57static void modem_unlock_timeout(struct work_struct *work);
58static int modem_notif_handler(struct notifier_block *this,
59 unsigned long code,
60 void *_cmd);
61static DECLARE_WORK(modem_fatal_work, modem_fatal_fn);
62static DECLARE_DELAYED_WORK(modem_unlock_timeout_work,
63 modem_unlock_timeout);
64
65static struct notifier_block modem_notif_nb = {
66 .notifier_call = modem_notif_handler,
67};
68
69static void modem_unlock_timeout(struct work_struct *work)
70{
71 void __iomem *hwio_modem_reset_addr =
72 ioremap_nocache(MODEM_HWIO_MSS_RESET_ADDR, 8);
73 pr_crit("%s: Timeout waiting for modem to unlock.\n", MODULE_NAME);
74
75 /* Set MSS_MODEM_RESET to 0x0 since the unlock didn't work */
76 writel_relaxed(0x0, hwio_modem_reset_addr);
77 /* Write needs to go through before the modem is restarted. */
78 mb();
79 iounmap(hwio_modem_reset_addr);
80
Stephen Boyd0ebf7212012-04-30 20:42:35 -070081 subsystem_restart_dev(modem_8660_dev);
Vikram Mulukutla93510c42011-08-19 19:04:40 -070082 enable_irq(MARM_WDOG_EXPIRED);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070083}
84
85static void modem_fatal_fn(struct work_struct *work)
86{
87 uint32_t modem_state;
88 uint32_t panic_smsm_states = SMSM_RESET | SMSM_SYSTEM_DOWNLOAD;
89 uint32_t reset_smsm_states = SMSM_SYSTEM_REBOOT_USR |
90 SMSM_SYSTEM_PWRDWN_USR;
91
92 pr_err("%s: Watchdog bite received from modem!\n", MODULE_NAME);
93
94 modem_state = smsm_get_state(SMSM_MODEM_STATE);
95 pr_err("%s: Modem SMSM state = 0x%x!", MODULE_NAME, modem_state);
96
97 if (modem_state == 0 || modem_state & panic_smsm_states) {
98
Stephen Boyd0ebf7212012-04-30 20:42:35 -070099 subsystem_restart_dev(modem_8660_dev);
Vikram Mulukutla93510c42011-08-19 19:04:40 -0700100 enable_irq(MARM_WDOG_EXPIRED);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700101
102 } else if (modem_state & reset_smsm_states) {
103
104 pr_err("%s: User-invoked system reset/powerdown.",
105 MODULE_NAME);
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700106 kernel_restart(NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700107
108 } else {
109
110 int ret;
111 void *hwio_modem_reset_addr =
112 ioremap_nocache(MODEM_HWIO_MSS_RESET_ADDR, 8);
113
114 pr_err("%s: Modem AHB locked up.\n", MODULE_NAME);
115 pr_err("%s: Trying to free up modem!\n", MODULE_NAME);
116
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700117 writel_relaxed(0x3, hwio_modem_reset_addr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700118
119 /* If we are still alive after 6 seconds (allowing for
120 * the 5-second-delayed-panic-reboot), modem is either
121 * still wedged or SMSM didn't come through. Force panic
122 * in that case.
123 */
124 ret = schedule_delayed_work(&modem_unlock_timeout_work,
125 msecs_to_jiffies(6000));
126
127 iounmap(hwio_modem_reset_addr);
128 }
129}
130
131static int modem_notif_handler(struct notifier_block *this,
132 unsigned long code,
133 void *_cmd)
134{
135 if (code == MODEM_NOTIFIER_START_RESET) {
Vikram Mulukutlaf4510d22011-12-16 11:40:21 -0800136 if (ignore_smsm_ack) {
137 ignore_smsm_ack = 0;
138 goto out;
139 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700140 pr_err("%s: Modem error fatal'ed.", MODULE_NAME);
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700141 subsystem_restart_dev(modem_8660_dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700142 }
Vikram Mulukutlaf4510d22011-12-16 11:40:21 -0800143out:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700144 return NOTIFY_DONE;
145}
146
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700147static int modem_shutdown(const struct subsys_desc *crashed_subsys)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700148{
149 void __iomem *modem_wdog_addr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700150
151 /* If the modem didn't already crash, setting SMSM_RESET
Vikram Mulukutlaf4510d22011-12-16 11:40:21 -0800152 * here will help flush caches etc. The ignore_smsm_ack
153 * flag is set to ignore the SMSM_RESET notification
154 * that is generated due to the modem settings its own
155 * SMSM_RESET bit in response to the apps setting the
156 * apps SMSM_RESET bit.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700157 */
158 if (!(smsm_get_state(SMSM_MODEM_STATE) & SMSM_RESET)) {
Vikram Mulukutlaf4510d22011-12-16 11:40:21 -0800159 ignore_smsm_ack = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700160 smsm_reset_modem(SMSM_RESET);
161 }
162
163 /* Disable the modem watchdog to allow clean modem bootup */
164 modem_wdog_addr = ioremap_nocache(MODEM_WDOG_ENABLE, 8);
165 writel_relaxed(0x0, modem_wdog_addr);
166
167 /*
168 * The write above needs to go through before the modem is
169 * powered up again (subsystem restart).
170 */
171 mb();
172 iounmap(modem_wdog_addr);
173
Vikram Mulukutla89549562011-07-28 16:10:41 -0700174 /* Wait here to allow the modem to clean up caches etc. */
175 msleep(MODEM_CLEANUP_DELAY_MS);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700176 pil_force_shutdown("modem");
177 disable_irq_nosync(MARM_WDOG_EXPIRED);
178
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700179
180
181 return 0;
182}
183
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700184static int modem_powerup(const struct subsys_desc *crashed_subsys)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700185{
186 int ret;
187
188 ret = pil_force_boot("modem");
189 enable_irq(MARM_WDOG_EXPIRED);
190
191 return ret;
192}
193
194/* FIXME: Get address, size from PIL */
195static struct ramdump_segment modem_segments[] = {
196 {0x42F00000, 0x46000000 - 0x42F00000} };
197
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700198static int modem_ramdump(int enable, const struct subsys_desc *crashed_subsys)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700199{
200 if (enable)
201 return do_ramdump(modem_ramdump_dev, modem_segments,
202 ARRAY_SIZE(modem_segments));
203 else
204 return 0;
205}
206
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700207static void modem_crash_shutdown(const struct subsys_desc *crashed_subsys)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700208{
209 /* If modem hasn't already crashed, send SMSM_RESET. */
210 if (!(smsm_get_state(SMSM_MODEM_STATE) & SMSM_RESET)) {
211 modem_unregister_notifier(&modem_notif_nb);
212 smsm_reset_modem(SMSM_RESET);
213 }
214
Vikram Mulukutla6066a842011-09-16 11:14:50 -0700215 /* Wait to allow the modem to clean up caches etc. */
216 mdelay(5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700217}
218
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700219static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700220{
221 int ret;
222
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700223 ret = schedule_work(&modem_fatal_work);
224 disable_irq_nosync(MARM_WDOG_EXPIRED);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700225
226 return IRQ_HANDLED;
227}
228
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700229static struct subsys_desc subsys_8660_modem = {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700230 .name = "modem",
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700231 .shutdown = modem_shutdown,
232 .powerup = modem_powerup,
233 .ramdump = modem_ramdump,
234 .crash_shutdown = modem_crash_shutdown
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700235};
236
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700237static int __init modem_8660_init(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700238{
239 int ret;
240
241 /* Need to listen for SMSM_RESET always */
242 modem_register_notifier(&modem_notif_nb);
243
244#if defined(SUBSYS_FATAL_DEBUG)
245 schedule_delayed_work(&debug_crash_modem_work, msecs_to_jiffies(5000));
246#endif
247
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700248 ret = request_irq(MARM_WDOG_EXPIRED, modem_wdog_bite_irq,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700249 IRQF_TRIGGER_RISING, "modem_wdog", NULL);
250
251 if (ret < 0) {
252 pr_err("%s: Unable to request MARM_WDOG_EXPIRED irq.",
253 __func__);
254 goto out;
255 }
256
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700257 modem_ramdump_dev = create_ramdump_device("modem");
258
259 if (!modem_ramdump_dev) {
260 ret = -ENOMEM;
261 goto out;
262 }
263
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700264 modem_8660_dev = subsys_register(&subsys_8660_modem);
265 if (IS_ERR(modem_8660_dev))
266 ret = PTR_ERR(modem_8660_dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700267out:
268 return ret;
269}
270
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700271static void __exit modem_8660_exit(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700272{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700273 subsys_unregister(modem_8660_dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700274 free_irq(MARM_WDOG_EXPIRED, NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700275}
276
277#ifdef SUBSYS_FATAL_DEBUG
278static void debug_crash_modem_fn(struct work_struct *work)
279{
280 if (reset_modem == 1)
281 smsm_reset_modem(SMSM_RESET);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700282
283 reset_modem = 0;
284 schedule_delayed_work(&debug_crash_modem_work, msecs_to_jiffies(1000));
285}
286#endif
287
Vikram Mulukutlaffa387e2011-09-13 15:14:35 -0700288module_init(modem_8660_init);
289module_exit(modem_8660_exit);
290