blob: 8baa800e8a20b16f4ee9b382db7a5fd2b686fcd7 [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>
29#include <mach/irqs-8960.h>
30
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
90static int modem_shutdown(const struct subsys_data *subsys)
91{
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -070092 int smsm_notif_unregistered = 0;
93
94 if (!(smsm_get_state(SMSM_MODEM_STATE) & SMSM_RESET)) {
95 smsm_state_cb_deregister(SMSM_MODEM_STATE, SMSM_RESET,
96 smsm_state_cb, 0);
97 smsm_notif_unregistered = 1;
98 smsm_reset_modem(SMSM_RESET);
99 }
100
101 pil_force_shutdown("modem");
102 disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
103 disable_irq_nosync(Q6SW_WDOG_EXPIRED_IRQ);
104
105 if (smsm_notif_unregistered)
106 smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
107 smsm_state_cb, 0);
108
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700109 return 0;
110}
111
112static int modem_powerup(const struct subsys_data *subsys)
113{
114 /* TODO: Call into PIL to powerup the modem */
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700115 pil_force_boot("modem");
116 enable_irq(Q6FW_WDOG_EXPIRED_IRQ);
117 enable_irq(Q6SW_WDOG_EXPIRED_IRQ);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700118 return 0;
119}
120
121void modem_crash_shutdown(const struct subsys_data *subsys)
122{
123 crash_shutdown = 1;
124 smsm_reset_modem(SMSM_RESET);
125}
126
127int modem_ramdump(int enable, const struct subsys_data *subsys)
128{
129 return 0;
130}
131
132static irqreturn_t modem_wdog_bite_irq(int irq, void *dev_id)
133{
134 int ret;
135
136 switch (irq) {
137
138 case Q6SW_WDOG_EXPIRED_IRQ:
139 ret = schedule_work(&modem_sw_fatal_work);
140 disable_irq_nosync(Q6SW_WDOG_EXPIRED_IRQ);
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700141 disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700142 break;
143 case Q6FW_WDOG_EXPIRED_IRQ:
144 ret = schedule_work(&modem_fw_fatal_work);
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700145 disable_irq_nosync(Q6SW_WDOG_EXPIRED_IRQ);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700146 disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
147 break;
148 break;
149
150 default:
151 pr_err("%s: Unknown IRQ!\n", __func__);
152 }
153
154 return IRQ_HANDLED;
155}
156
157static struct subsys_data modem_8960 = {
158 .name = "modem",
159 .shutdown = modem_shutdown,
160 .powerup = modem_powerup,
161 .ramdump = modem_ramdump,
162 .crash_shutdown = modem_crash_shutdown
163};
164
165static int modem_subsystem_restart_init(void)
166{
167 return ssr_register_subsystem(&modem_8960);
168}
169
170static int modem_debug_set(void *data, u64 val)
171{
Vikram Mulukutla976ff0a2011-08-31 12:06:53 -0700172 if (val == 1)
173 subsystem_restart("modem");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700174
175 return 0;
176}
177
178static int modem_debug_get(void *data, u64 *val)
179{
180 *val = 0;
181 return 0;
182}
183
184DEFINE_SIMPLE_ATTRIBUTE(modem_debug_fops, modem_debug_get, modem_debug_set,
185 "%llu\n");
186
187static int modem_debugfs_init(void)
188{
189 struct dentry *dent;
190 dent = debugfs_create_dir("modem_debug", 0);
191
192 if (IS_ERR(dent))
193 return PTR_ERR(dent);
194
195 debugfs_create_file("reset_modem", 0644, dent, NULL,
196 &modem_debug_fops);
197 return 0;
198}
199
200static int __init modem_8960_init(void)
201{
202 int ret;
203
204 ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
205 smsm_state_cb, 0);
206
207 if (ret < 0)
208 pr_err("%s: Unable to register SMSM callback! (%d)\n",
209 __func__, ret);
210
211 ret = request_irq(Q6FW_WDOG_EXPIRED_IRQ, modem_wdog_bite_irq,
212 IRQF_TRIGGER_RISING, "modem_wdog_fw", NULL);
213
214 if (ret < 0) {
215 pr_err("%s: Unable to request q6fw watchdog IRQ. (%d)\n",
216 __func__, ret);
217 goto out;
218 }
219
220 ret = request_irq(Q6SW_WDOG_EXPIRED_IRQ, modem_wdog_bite_irq,
221 IRQF_TRIGGER_RISING, "modem_wdog_sw", NULL);
222
223 if (ret < 0) {
224 pr_err("%s: Unable to request q6sw watchdog IRQ. (%d)\n",
225 __func__, ret);
226 disable_irq_nosync(Q6FW_WDOG_EXPIRED_IRQ);
227 goto out;
228 }
229
230 ret = modem_subsystem_restart_init();
231
232 if (ret < 0) {
233 pr_err("%s: Unable to reg with subsystem restart. (%d)\n",
234 __func__, ret);
235 goto out;
236 }
237
238 ret = modem_debugfs_init();
239
240 pr_info("%s: 8960 modem fatal driver init'ed.\n", __func__);
241out:
242 return ret;
243}
244
245module_init(modem_8960_init);