blob: aae5d5abfbb438e2fa2597cc44a58a03042f9d51 [file] [log] [blame]
Rohit Vaswanid0fb4182012-03-19 18:07:59 -07001/* Copyright (c) 2012, 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/miscdevice.h>
23#include <linux/fs.h>
24
25#include <mach/irqs.h>
Rohit Vaswani974fd9e2012-05-29 17:17:11 -070026#include <mach/msm_smsm.h>
Rohit Vaswanid0fb4182012-03-19 18:07:59 -070027#include <mach/scm.h>
28#include <mach/peripheral-loader.h>
29#include <mach/subsystem_restart.h>
30#include <mach/subsystem_notif.h>
31#include <mach/socinfo.h>
32
33#include "smd_private.h"
34#include "modem_notifier.h"
35#include "ramdump.h"
36
37static struct gss_8064_data {
38 struct miscdevice gss_dev;
39 void *pil_handle;
40 void *gss_ramdump_dev;
41 void *smem_ramdump_dev;
42} gss_data;
43
44static int crash_shutdown;
45
Rohit Vaswani974fd9e2012-05-29 17:17:11 -070046#define MAX_SSR_REASON_LEN 81U
47
48static void log_gss_sfr(void)
49{
50 u32 size;
51 char *smem_reason, reason[MAX_SSR_REASON_LEN];
52
53 smem_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size);
54 if (!smem_reason || !size) {
55 pr_err("GSS subsystem failure reason: (unknown, smem_get_entry failed).\n");
56 return;
57 }
58 if (!smem_reason[0]) {
59 pr_err("GSS subsystem failure reason: (unknown, init string found).\n");
60 return;
61 }
62
63 size = min(size, MAX_SSR_REASON_LEN-1);
64 memcpy(reason, smem_reason, size);
65 reason[size] = '\0';
66 pr_err("GSS subsystem failure reason: %s.\n", reason);
67
68 smem_reason[0] = '\0';
69 wmb();
70}
71
Rohit Vaswanid0fb4182012-03-19 18:07:59 -070072static void gss_fatal_fn(struct work_struct *work)
73{
Rohit Vaswanid0fb4182012-03-19 18:07:59 -070074 pr_err("Watchdog bite received from GSS!\n");
Rohit Vaswania66023d2012-06-21 17:38:29 -070075 log_gss_sfr();
76 subsystem_restart("gss");
Rohit Vaswanid0fb4182012-03-19 18:07:59 -070077}
78
79static DECLARE_WORK(gss_fatal_work, gss_fatal_fn);
80
81static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
82{
83 /* Ignore if we're the one that set SMSM_RESET */
84 if (crash_shutdown)
85 return;
86
87 if (new_state & SMSM_RESET) {
88 pr_err("GSS SMSM state changed to SMSM_RESET.\n"
89 "Probable err_fatal on the GSS. "
90 "Calling subsystem restart...\n");
Rohit Vaswani974fd9e2012-05-29 17:17:11 -070091 log_gss_sfr();
Rohit Vaswanid0fb4182012-03-19 18:07:59 -070092 subsystem_restart("gss");
93 }
94}
95
96#define Q6_FW_WDOG_ENABLE 0x08882024
97#define Q6_SW_WDOG_ENABLE 0x08982024
98static int gss_shutdown(const struct subsys_data *subsys)
99{
100 pil_force_shutdown("gss");
101 disable_irq_nosync(GSS_A5_WDOG_EXPIRED);
102
103 return 0;
104}
105
106static int gss_powerup(const struct subsys_data *subsys)
107{
108 pil_force_boot("gss");
109 enable_irq(GSS_A5_WDOG_EXPIRED);
110 return 0;
111}
112
113void gss_crash_shutdown(const struct subsys_data *subsys)
114{
115 crash_shutdown = 1;
116 smsm_reset_modem(SMSM_RESET);
117}
118
119/* FIXME: Get address, size from PIL */
120static struct ramdump_segment gss_segments[] = {
Rohit Vaswani3e7503f2012-03-29 17:46:13 -0700121 {0x89000000, 0x00D00000}
Rohit Vaswanid0fb4182012-03-19 18:07:59 -0700122};
123
124static struct ramdump_segment smem_segments[] = {
125 {0x80000000, 0x00200000},
126};
127
128static int gss_ramdump(int enable,
129 const struct subsys_data *crashed_subsys)
130{
131 int ret = 0;
132
133 if (enable) {
134 ret = do_ramdump(gss_data.gss_ramdump_dev, gss_segments,
135 ARRAY_SIZE(gss_segments));
136
137 if (ret < 0) {
138 pr_err("Unable to dump gss memory (rc = %d).\n",
139 ret);
140 goto out;
141 }
142
143 ret = do_ramdump(gss_data.smem_ramdump_dev, smem_segments,
144 ARRAY_SIZE(smem_segments));
145
146 if (ret < 0) {
147 pr_err("Unable to dump smem memory (rc = %d).\n", ret);
148 goto out;
149 }
150 }
151
152out:
153 return ret;
154}
155
156static irqreturn_t gss_wdog_bite_irq(int irq, void *dev_id)
157{
158 schedule_work(&gss_fatal_work);
Rohit Vaswanid0fb4182012-03-19 18:07:59 -0700159
160 return IRQ_HANDLED;
161}
162
163static struct subsys_data gss_8064 = {
164 .name = "gss",
165 .shutdown = gss_shutdown,
166 .powerup = gss_powerup,
167 .ramdump = gss_ramdump,
168 .crash_shutdown = gss_crash_shutdown
169};
170
171static int gss_subsystem_restart_init(void)
172{
173 return ssr_register_subsystem(&gss_8064);
174}
175
176static int gss_open(struct inode *inode, struct file *filep)
177{
178 void *ret;
179 gss_data.pil_handle = ret = pil_get("gss");
180 if (!ret)
181 pr_debug("%s - pil_get returned NULL\n", __func__);
182 return 0;
183}
184
185static int gss_release(struct inode *inode, struct file *filep)
186{
187 pil_put(gss_data.pil_handle);
188 pr_debug("%s pil_put called on GSS\n", __func__);
189 return 0;
190}
191
192const struct file_operations gss_file_ops = {
193 .open = gss_open,
194 .release = gss_release,
195};
196
197static int __init gss_8064_init(void)
198{
199 int ret;
200
201 if (!cpu_is_apq8064())
202 return -ENODEV;
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(GSS_A5_WDOG_EXPIRED, gss_wdog_bite_irq,
212 IRQF_TRIGGER_RISING, "gss_a5_wdog", NULL);
213
214 if (ret < 0) {
215 pr_err("%s: Unable to request gss watchdog IRQ. (%d)\n",
216 __func__, ret);
217 disable_irq_nosync(GSS_A5_WDOG_EXPIRED);
218 goto out;
219 }
220
221 ret = gss_subsystem_restart_init();
222
223 if (ret < 0) {
224 pr_err("%s: Unable to reg with subsystem restart. (%d)\n",
225 __func__, ret);
226 goto out;
227 }
228
229 gss_data.gss_dev.minor = MISC_DYNAMIC_MINOR;
230 gss_data.gss_dev.name = "gss";
231 gss_data.gss_dev.fops = &gss_file_ops;
232 ret = misc_register(&gss_data.gss_dev);
233
234 if (ret) {
235 pr_err("%s: misc_registers failed for %s (%d)", __func__,
236 gss_data.gss_dev.name, ret);
237 goto out;
238 }
239
240 gss_data.gss_ramdump_dev = create_ramdump_device("gss");
241
242 if (!gss_data.gss_ramdump_dev) {
243 pr_err("%s: Unable to create gss ramdump device. (%d)\n",
244 __func__, -ENOMEM);
245 ret = -ENOMEM;
246 goto out;
247 }
248
249 gss_data.smem_ramdump_dev = create_ramdump_device("smem");
250
251 if (!gss_data.smem_ramdump_dev) {
252 pr_err("%s: Unable to create smem ramdump device. (%d)\n",
253 __func__, -ENOMEM);
254 ret = -ENOMEM;
255 goto out;
256 }
257
258 pr_info("%s: gss fatal driver init'ed.\n", __func__);
259out:
260 return ret;
261}
262
263module_init(gss_8064_init);