blob: e528650ac8c2137238aa80cece0bb8edc95c8e52 [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
Stephen Boyd0ebf7212012-04-30 20:42:35 -070046static struct subsys_device *gss_8064_dev;
47
Rohit Vaswani974fd9e2012-05-29 17:17:11 -070048#define MAX_SSR_REASON_LEN 81U
49
50static void log_gss_sfr(void)
51{
52 u32 size;
53 char *smem_reason, reason[MAX_SSR_REASON_LEN];
54
55 smem_reason = smem_get_entry(SMEM_SSR_REASON_MSS0, &size);
56 if (!smem_reason || !size) {
57 pr_err("GSS subsystem failure reason: (unknown, smem_get_entry failed).\n");
58 return;
59 }
60 if (!smem_reason[0]) {
61 pr_err("GSS subsystem failure reason: (unknown, init string found).\n");
62 return;
63 }
64
65 size = min(size, MAX_SSR_REASON_LEN-1);
66 memcpy(reason, smem_reason, size);
67 reason[size] = '\0';
68 pr_err("GSS subsystem failure reason: %s.\n", reason);
69
70 smem_reason[0] = '\0';
71 wmb();
72}
73
Rohit Vaswanie8519592012-06-21 17:48:04 -070074static void restart_gss(void)
Rohit Vaswanid0fb4182012-03-19 18:07:59 -070075{
Rohit Vaswania66023d2012-06-21 17:38:29 -070076 log_gss_sfr();
Stephen Boyd0ebf7212012-04-30 20:42:35 -070077 subsystem_restart_dev(gss_8064_dev);
Rohit Vaswanid0fb4182012-03-19 18:07:59 -070078}
79
Rohit Vaswanid0fb4182012-03-19 18:07:59 -070080static void smsm_state_cb(void *data, uint32_t old_state, uint32_t new_state)
81{
82 /* Ignore if we're the one that set SMSM_RESET */
83 if (crash_shutdown)
84 return;
85
86 if (new_state & SMSM_RESET) {
87 pr_err("GSS SMSM state changed to SMSM_RESET.\n"
88 "Probable err_fatal on the GSS. "
89 "Calling subsystem restart...\n");
Rohit Vaswanie8519592012-06-21 17:48:04 -070090 restart_gss();
Rohit Vaswanid0fb4182012-03-19 18:07:59 -070091 }
92}
93
94#define Q6_FW_WDOG_ENABLE 0x08882024
95#define Q6_SW_WDOG_ENABLE 0x08982024
Stephen Boyd0ebf7212012-04-30 20:42:35 -070096static int gss_shutdown(const struct subsys_desc *desc)
Rohit Vaswanid0fb4182012-03-19 18:07:59 -070097{
98 pil_force_shutdown("gss");
99 disable_irq_nosync(GSS_A5_WDOG_EXPIRED);
100
101 return 0;
102}
103
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700104static int gss_powerup(const struct subsys_desc *desc)
Rohit Vaswanid0fb4182012-03-19 18:07:59 -0700105{
106 pil_force_boot("gss");
107 enable_irq(GSS_A5_WDOG_EXPIRED);
108 return 0;
109}
110
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700111void gss_crash_shutdown(const struct subsys_desc *desc)
Rohit Vaswanid0fb4182012-03-19 18:07:59 -0700112{
113 crash_shutdown = 1;
114 smsm_reset_modem(SMSM_RESET);
115}
116
117/* FIXME: Get address, size from PIL */
118static struct ramdump_segment gss_segments[] = {
Rohit Vaswani3e7503f2012-03-29 17:46:13 -0700119 {0x89000000, 0x00D00000}
Rohit Vaswanid0fb4182012-03-19 18:07:59 -0700120};
121
122static struct ramdump_segment smem_segments[] = {
123 {0x80000000, 0x00200000},
124};
125
126static int gss_ramdump(int enable,
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700127 const struct subsys_desc *crashed_subsys)
Rohit Vaswanid0fb4182012-03-19 18:07:59 -0700128{
129 int ret = 0;
130
131 if (enable) {
132 ret = do_ramdump(gss_data.gss_ramdump_dev, gss_segments,
133 ARRAY_SIZE(gss_segments));
134
135 if (ret < 0) {
136 pr_err("Unable to dump gss memory (rc = %d).\n",
137 ret);
138 goto out;
139 }
140
141 ret = do_ramdump(gss_data.smem_ramdump_dev, smem_segments,
142 ARRAY_SIZE(smem_segments));
143
144 if (ret < 0) {
145 pr_err("Unable to dump smem memory (rc = %d).\n", ret);
146 goto out;
147 }
148 }
149
150out:
151 return ret;
152}
153
154static irqreturn_t gss_wdog_bite_irq(int irq, void *dev_id)
155{
Rohit Vaswanie8519592012-06-21 17:48:04 -0700156 pr_err("Watchdog bite received from GSS!\n");
157 restart_gss();
Rohit Vaswanid0fb4182012-03-19 18:07:59 -0700158
159 return IRQ_HANDLED;
160}
161
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700162static struct subsys_desc gss_8064 = {
Rohit Vaswanid0fb4182012-03-19 18:07:59 -0700163 .name = "gss",
164 .shutdown = gss_shutdown,
165 .powerup = gss_powerup,
166 .ramdump = gss_ramdump,
167 .crash_shutdown = gss_crash_shutdown
168};
169
170static int gss_subsystem_restart_init(void)
171{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700172 gss_8064_dev = subsys_register(&gss_8064);
173 if (IS_ERR(gss_8064_dev))
174 return PTR_ERR(gss_8064_dev);
175 return 0;
Rohit Vaswanid0fb4182012-03-19 18:07:59 -0700176}
177
178static int gss_open(struct inode *inode, struct file *filep)
179{
180 void *ret;
181 gss_data.pil_handle = ret = pil_get("gss");
182 if (!ret)
183 pr_debug("%s - pil_get returned NULL\n", __func__);
184 return 0;
185}
186
187static int gss_release(struct inode *inode, struct file *filep)
188{
189 pil_put(gss_data.pil_handle);
190 pr_debug("%s pil_put called on GSS\n", __func__);
191 return 0;
192}
193
194const struct file_operations gss_file_ops = {
195 .open = gss_open,
196 .release = gss_release,
197};
198
199static int __init gss_8064_init(void)
200{
201 int ret;
202
203 if (!cpu_is_apq8064())
204 return -ENODEV;
205
206 ret = smsm_state_cb_register(SMSM_MODEM_STATE, SMSM_RESET,
207 smsm_state_cb, 0);
208
209 if (ret < 0)
210 pr_err("%s: Unable to register SMSM callback! (%d)\n",
211 __func__, ret);
212
213 ret = request_irq(GSS_A5_WDOG_EXPIRED, gss_wdog_bite_irq,
214 IRQF_TRIGGER_RISING, "gss_a5_wdog", NULL);
215
216 if (ret < 0) {
217 pr_err("%s: Unable to request gss watchdog IRQ. (%d)\n",
218 __func__, ret);
219 disable_irq_nosync(GSS_A5_WDOG_EXPIRED);
220 goto out;
221 }
222
223 ret = gss_subsystem_restart_init();
224
225 if (ret < 0) {
226 pr_err("%s: Unable to reg with subsystem restart. (%d)\n",
227 __func__, ret);
228 goto out;
229 }
230
231 gss_data.gss_dev.minor = MISC_DYNAMIC_MINOR;
232 gss_data.gss_dev.name = "gss";
233 gss_data.gss_dev.fops = &gss_file_ops;
234 ret = misc_register(&gss_data.gss_dev);
235
236 if (ret) {
237 pr_err("%s: misc_registers failed for %s (%d)", __func__,
238 gss_data.gss_dev.name, ret);
239 goto out;
240 }
241
242 gss_data.gss_ramdump_dev = create_ramdump_device("gss");
243
244 if (!gss_data.gss_ramdump_dev) {
245 pr_err("%s: Unable to create gss ramdump device. (%d)\n",
246 __func__, -ENOMEM);
247 ret = -ENOMEM;
248 goto out;
249 }
250
Stephen Boyd4b66b372012-06-28 12:32:21 -0700251 gss_data.smem_ramdump_dev = create_ramdump_device("smem-gss");
Rohit Vaswanid0fb4182012-03-19 18:07:59 -0700252
253 if (!gss_data.smem_ramdump_dev) {
254 pr_err("%s: Unable to create smem ramdump device. (%d)\n",
255 __func__, -ENOMEM);
256 ret = -ENOMEM;
257 goto out;
258 }
259
260 pr_info("%s: gss fatal driver init'ed.\n", __func__);
261out:
262 return ret;
263}
264
265module_init(gss_8064_init);