blob: b714a7f0ceef574899717309ad42af0f13bc520b [file] [log] [blame]
Sriranjan Srikantam6b2899c2011-10-10 16:28:49 -07001/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -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/delay.h>
19#include <linux/module.h>
Sriranjan Srikantam6b2899c2011-10-10 16:28:49 -070020#include <linux/err.h>
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -070021
22#include <mach/irqs.h>
23#include <mach/scm.h>
24#include <mach/peripheral-loader.h>
25#include <mach/subsystem_restart.h>
26#include <mach/subsystem_notif.h>
27
28#include "smd_private.h"
29#include "ramdump.h"
Sriranjan Srikantam6b2899c2011-10-10 16:28:49 -070030#include "sysmon.h"
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -070031
32#define SCM_Q6_NMI_CMD 0x1
33#define MODULE_NAME "lpass_8960"
Ravishankar Sarawadide251162012-03-23 12:47:03 -070034#define MAX_BUF_SIZE 0x51
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -070035
36/* Subsystem restart: QDSP6 data, functions */
37static void lpass_fatal_fn(struct work_struct *);
38static DECLARE_WORK(lpass_fatal_work, lpass_fatal_fn);
39struct lpass_ssr {
40 void *lpass_ramdump_dev;
41} lpass_ssr;
42
43static struct lpass_ssr lpass_ssr_8960;
Swaminathan Sathappanfc137ec2011-08-16 15:18:41 -070044static int q6_crash_shutdown;
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -070045
Sriranjan Srikantam6b2899c2011-10-10 16:28:49 -070046static int riva_notifier_cb(struct notifier_block *this, unsigned long code,
47 void *ss_handle)
48{
49 int ret;
50 switch (code) {
51 case SUBSYS_BEFORE_SHUTDOWN:
52 pr_debug("%s: R-Notify: Shutdown started\n", __func__);
53 ret = sysmon_send_event(SYSMON_SS_LPASS, "wcnss",
54 SUBSYS_BEFORE_SHUTDOWN);
55 if (ret < 0)
56 pr_err("%s: sysmon_send_event error %d", __func__,
57 ret);
58 break;
59 }
60 return NOTIFY_DONE;
61}
62
63static void *ssr_notif_hdle;
64static struct notifier_block rnb = {
65 .notifier_call = riva_notifier_cb,
66};
67
Sriranjan Srikantam3d907a62011-10-11 12:51:25 -070068static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
69 void *ss_handle)
70{
71 int ret;
72 switch (code) {
73 case SUBSYS_BEFORE_SHUTDOWN:
74 pr_debug("%s: M-Notify: Shutdown started\n", __func__);
75 ret = sysmon_send_event(SYSMON_SS_LPASS, "modem",
76 SUBSYS_BEFORE_SHUTDOWN);
77 if (ret < 0)
78 pr_err("%s: sysmon_send_event error %d", __func__,
79 ret);
80 break;
81 }
82 return NOTIFY_DONE;
83}
84
85static void *ssr_modem_notif_hdle;
86static struct notifier_block mnb = {
87 .notifier_call = modem_notifier_cb,
88};
89
Ravishankar Sarawadide251162012-03-23 12:47:03 -070090static void lpass_log_failure_reason(void)
91{
92 char *reason;
93 char buffer[MAX_BUF_SIZE];
94 unsigned size;
95
96 reason = smem_get_entry(SMEM_SSR_REASON_LPASS0, &size);
97
98 if (!reason) {
99 pr_err("%s: subsystem failure reason: (unknown, smem_get_entry failed).",
100 MODULE_NAME);
101 return;
102 }
103
104 if (reason[0] == '\0') {
105 pr_err("%s: subsystem failure reason: (unknown, init value found)",
106 MODULE_NAME);
107 return;
108 }
109
110 size = size < MAX_BUF_SIZE ? size : (MAX_BUF_SIZE-1);
111 memcpy(buffer, reason, size);
112 buffer[size] = '\0';
113 pr_err("%s: subsystem failure reason: %s", MODULE_NAME, buffer);
114 memset((void *)reason, 0x0, size);
115 wmb();
116}
117
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700118static void lpass_fatal_fn(struct work_struct *work)
119{
Swaminathan Sathappanfc137ec2011-08-16 15:18:41 -0700120 pr_err("%s %s: Watchdog bite received from Q6!\n", MODULE_NAME,
121 __func__);
Ravishankar Sarawadide251162012-03-23 12:47:03 -0700122 lpass_log_failure_reason();
Swaminathan Sathappan00f766d2011-09-29 15:35:55 -0700123 panic(MODULE_NAME ": Resetting the SoC");
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700124}
125
Swaminathan Sathappanfc137ec2011-08-16 15:18:41 -0700126static void lpass_smsm_state_cb(void *data, uint32_t old_state,
127 uint32_t new_state)
128{
129 /* Ignore if we're the one that set SMSM_RESET */
130 if (q6_crash_shutdown)
131 return;
132
133 if (new_state & SMSM_RESET) {
134 pr_err("%s: LPASS SMSM state changed to SMSM_RESET,"
135 " new_state = 0x%x, old_state = 0x%x\n", __func__,
136 new_state, old_state);
Ravishankar Sarawadide251162012-03-23 12:47:03 -0700137 lpass_log_failure_reason();
Swaminathan Sathappan00f766d2011-09-29 15:35:55 -0700138 panic(MODULE_NAME ": Resetting the SoC");
Swaminathan Sathappanfc137ec2011-08-16 15:18:41 -0700139 }
140}
141
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700142static void send_q6_nmi(void)
143{
144 /* Send NMI to QDSP6 via an SCM call. */
145 uint32_t cmd = 0x1;
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700146
147 scm_call(SCM_SVC_UTIL, SCM_Q6_NMI_CMD,
148 &cmd, sizeof(cmd), NULL, 0);
149
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700150 /* Q6 requires worstcase 100ms to dump caches etc.*/
Swaminathan Sathappan71074552011-09-20 10:09:31 -0700151 mdelay(100);
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700152 pr_debug("%s: Q6 NMI was sent.\n", __func__);
153}
154
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700155static int lpass_shutdown(const struct subsys_desc *subsys)
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700156{
157 send_q6_nmi();
158 pil_force_shutdown("q6");
159 disable_irq_nosync(LPASS_Q6SS_WDOG_EXPIRED);
160
161 return 0;
162}
163
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700164static int lpass_powerup(const struct subsys_desc *subsys)
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700165{
166 int ret = pil_force_boot("q6");
167 enable_irq(LPASS_Q6SS_WDOG_EXPIRED);
168 return ret;
169}
170/* RAM segments - address and size for 8960 */
171static struct ramdump_segment q6_segments[] = { {0x8da00000, 0x8f200000 -
172 0x8da00000}, {0x28400000, 0x20000} };
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700173static int lpass_ramdump(int enable, const struct subsys_desc *subsys)
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700174{
175 pr_debug("%s: enable[%d]\n", __func__, enable);
176 if (enable)
177 return do_ramdump(lpass_ssr_8960.lpass_ramdump_dev,
178 q6_segments,
179 ARRAY_SIZE(q6_segments));
180 else
181 return 0;
182}
183
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700184static void lpass_crash_shutdown(const struct subsys_desc *subsys)
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700185{
Swaminathan Sathappanfc137ec2011-08-16 15:18:41 -0700186 q6_crash_shutdown = 1;
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700187 send_q6_nmi();
188}
189
190static irqreturn_t lpass_wdog_bite_irq(int irq, void *dev_id)
191{
192 int ret;
193
194 pr_debug("%s: rxed irq[0x%x]", __func__, irq);
195 disable_irq_nosync(LPASS_Q6SS_WDOG_EXPIRED);
196 ret = schedule_work(&lpass_fatal_work);
197
198 return IRQ_HANDLED;
199}
200
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700201static struct subsys_device *lpass_8960_dev;
202
203static struct subsys_desc lpass_8960 = {
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700204 .name = "lpass",
205 .shutdown = lpass_shutdown,
206 .powerup = lpass_powerup,
207 .ramdump = lpass_ramdump,
208 .crash_shutdown = lpass_crash_shutdown
209};
210
211static int __init lpass_restart_init(void)
212{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700213 lpass_8960_dev = subsys_register(&lpass_8960);
214 if (IS_ERR(lpass_8960_dev))
215 return PTR_ERR(lpass_8960_dev);
216 return 0;
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700217}
218
219static int __init lpass_fatal_init(void)
220{
221 int ret;
222
Swaminathan Sathappanfc137ec2011-08-16 15:18:41 -0700223 ret = smsm_state_cb_register(SMSM_Q6_STATE, SMSM_RESET,
224 lpass_smsm_state_cb, 0);
225
226 if (ret < 0)
227 pr_err("%s: Unable to register SMSM callback! (%d)\n",
228 __func__, ret);
229
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700230 ret = request_irq(LPASS_Q6SS_WDOG_EXPIRED, lpass_wdog_bite_irq,
231 IRQF_TRIGGER_RISING, "q6_wdog", NULL);
232
233 if (ret < 0) {
234 pr_err("%s: Unable to request LPASS_Q6SS_WDOG_EXPIRED irq.",
235 __func__);
236 goto out;
237 }
238 ret = lpass_restart_init();
239 if (ret < 0) {
240 pr_err("%s: Unable to reg with lpass ssr. (%d)\n",
241 __func__, ret);
242 goto out;
243 }
Swaminathan Sathappan71074552011-09-20 10:09:31 -0700244
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700245 lpass_ssr_8960.lpass_ramdump_dev = create_ramdump_device("lpass");
246
247 if (!lpass_ssr_8960.lpass_ramdump_dev) {
248 pr_err("%s: Unable to create ramdump device.\n",
249 __func__);
250 ret = -ENOMEM;
251 goto out;
252 }
Sriranjan Srikantam6b2899c2011-10-10 16:28:49 -0700253 ssr_notif_hdle = subsys_notif_register_notifier("riva",
254 &rnb);
255 if (IS_ERR(ssr_notif_hdle) < 0) {
256 ret = PTR_ERR(ssr_notif_hdle);
257 pr_err("%s: subsys_register_notifier for Riva: err = %d\n",
258 __func__, ret);
Sriranjan Srikantam6b2899c2011-10-10 16:28:49 -0700259 free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL);
260 goto out;
261 }
262
Sriranjan Srikantam3d907a62011-10-11 12:51:25 -0700263 ssr_modem_notif_hdle = subsys_notif_register_notifier("modem",
264 &mnb);
265 if (IS_ERR(ssr_modem_notif_hdle) < 0) {
266 ret = PTR_ERR(ssr_modem_notif_hdle);
267 pr_err("%s: subsys_register_notifier for Modem: err = %d\n",
268 __func__, ret);
269 subsys_notif_unregister_notifier(ssr_notif_hdle, &rnb);
Sriranjan Srikantam3d907a62011-10-11 12:51:25 -0700270 free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL);
271 goto out;
272 }
273
Rohit Vaswani56dd22a2011-11-11 16:21:28 -0800274 pr_info("%s: lpass SSR driver init'ed.\n", __func__);
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700275out:
276 return ret;
277}
278
279static void __exit lpass_fatal_exit(void)
280{
Sriranjan Srikantam6b2899c2011-10-10 16:28:49 -0700281 subsys_notif_unregister_notifier(ssr_notif_hdle, &rnb);
Sriranjan Srikantam3d907a62011-10-11 12:51:25 -0700282 subsys_notif_unregister_notifier(ssr_modem_notif_hdle, &mnb);
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700283 subsys_unregister(lpass_8960_dev);
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700284 free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL);
285}
286
287module_init(lpass_fatal_init);
288module_exit(lpass_fatal_exit);
289
290MODULE_LICENSE("GPL v2");