blob: c58b0e1024d407118f8aeb16112a413427fd54b5 [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
155static int lpass_shutdown(const struct subsys_data *subsys)
156{
157 send_q6_nmi();
158 pil_force_shutdown("q6");
159 disable_irq_nosync(LPASS_Q6SS_WDOG_EXPIRED);
160
161 return 0;
162}
163
164static int lpass_powerup(const struct subsys_data *subsys)
165{
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} };
173static int lpass_ramdump(int enable, const struct subsys_data *subsys)
174{
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
184static void lpass_crash_shutdown(const struct subsys_data *subsys)
185{
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
201static struct subsys_data lpass_8960 = {
202 .name = "lpass",
203 .shutdown = lpass_shutdown,
204 .powerup = lpass_powerup,
205 .ramdump = lpass_ramdump,
206 .crash_shutdown = lpass_crash_shutdown
207};
208
209static int __init lpass_restart_init(void)
210{
211 return ssr_register_subsystem(&lpass_8960);
212}
213
214static int __init lpass_fatal_init(void)
215{
216 int ret;
217
Swaminathan Sathappanfc137ec2011-08-16 15:18:41 -0700218 ret = smsm_state_cb_register(SMSM_Q6_STATE, SMSM_RESET,
219 lpass_smsm_state_cb, 0);
220
221 if (ret < 0)
222 pr_err("%s: Unable to register SMSM callback! (%d)\n",
223 __func__, ret);
224
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700225 ret = request_irq(LPASS_Q6SS_WDOG_EXPIRED, lpass_wdog_bite_irq,
226 IRQF_TRIGGER_RISING, "q6_wdog", NULL);
227
228 if (ret < 0) {
229 pr_err("%s: Unable to request LPASS_Q6SS_WDOG_EXPIRED irq.",
230 __func__);
231 goto out;
232 }
233 ret = lpass_restart_init();
234 if (ret < 0) {
235 pr_err("%s: Unable to reg with lpass ssr. (%d)\n",
236 __func__, ret);
237 goto out;
238 }
Swaminathan Sathappan71074552011-09-20 10:09:31 -0700239
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700240 lpass_ssr_8960.lpass_ramdump_dev = create_ramdump_device("lpass");
241
242 if (!lpass_ssr_8960.lpass_ramdump_dev) {
243 pr_err("%s: Unable to create ramdump device.\n",
244 __func__);
245 ret = -ENOMEM;
246 goto out;
247 }
Sriranjan Srikantam6b2899c2011-10-10 16:28:49 -0700248 ssr_notif_hdle = subsys_notif_register_notifier("riva",
249 &rnb);
250 if (IS_ERR(ssr_notif_hdle) < 0) {
251 ret = PTR_ERR(ssr_notif_hdle);
252 pr_err("%s: subsys_register_notifier for Riva: err = %d\n",
253 __func__, ret);
Sriranjan Srikantam6b2899c2011-10-10 16:28:49 -0700254 free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL);
255 goto out;
256 }
257
Sriranjan Srikantam3d907a62011-10-11 12:51:25 -0700258 ssr_modem_notif_hdle = subsys_notif_register_notifier("modem",
259 &mnb);
260 if (IS_ERR(ssr_modem_notif_hdle) < 0) {
261 ret = PTR_ERR(ssr_modem_notif_hdle);
262 pr_err("%s: subsys_register_notifier for Modem: err = %d\n",
263 __func__, ret);
264 subsys_notif_unregister_notifier(ssr_notif_hdle, &rnb);
Sriranjan Srikantam3d907a62011-10-11 12:51:25 -0700265 free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL);
266 goto out;
267 }
268
Rohit Vaswani56dd22a2011-11-11 16:21:28 -0800269 pr_info("%s: lpass SSR driver init'ed.\n", __func__);
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700270out:
271 return ret;
272}
273
274static void __exit lpass_fatal_exit(void)
275{
Sriranjan Srikantam6b2899c2011-10-10 16:28:49 -0700276 subsys_notif_unregister_notifier(ssr_notif_hdle, &rnb);
Sriranjan Srikantam3d907a62011-10-11 12:51:25 -0700277 subsys_notif_unregister_notifier(ssr_modem_notif_hdle, &mnb);
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700278 free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL);
279}
280
281module_init(lpass_fatal_init);
282module_exit(lpass_fatal_exit);
283
284MODULE_LICENSE("GPL v2");