blob: 5eccf0669d511d1742b226386f654edface74700 [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"
34#define Q6SS_SOFT_INTR_WAKEUP 0x28800024
35
36/* Subsystem restart: QDSP6 data, functions */
37static void lpass_fatal_fn(struct work_struct *);
38static DECLARE_WORK(lpass_fatal_work, lpass_fatal_fn);
Swaminathan Sathappan71074552011-09-20 10:09:31 -070039void __iomem *q6_wakeup_intr;
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -070040struct lpass_ssr {
41 void *lpass_ramdump_dev;
42} lpass_ssr;
43
44static struct lpass_ssr lpass_ssr_8960;
Swaminathan Sathappanfc137ec2011-08-16 15:18:41 -070045static int q6_crash_shutdown;
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -070046
Sriranjan Srikantam6b2899c2011-10-10 16:28:49 -070047static int riva_notifier_cb(struct notifier_block *this, unsigned long code,
48 void *ss_handle)
49{
50 int ret;
51 switch (code) {
52 case SUBSYS_BEFORE_SHUTDOWN:
53 pr_debug("%s: R-Notify: Shutdown started\n", __func__);
54 ret = sysmon_send_event(SYSMON_SS_LPASS, "wcnss",
55 SUBSYS_BEFORE_SHUTDOWN);
56 if (ret < 0)
57 pr_err("%s: sysmon_send_event error %d", __func__,
58 ret);
59 break;
60 }
61 return NOTIFY_DONE;
62}
63
64static void *ssr_notif_hdle;
65static struct notifier_block rnb = {
66 .notifier_call = riva_notifier_cb,
67};
68
Sriranjan Srikantam3d907a62011-10-11 12:51:25 -070069static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
70 void *ss_handle)
71{
72 int ret;
73 switch (code) {
74 case SUBSYS_BEFORE_SHUTDOWN:
75 pr_debug("%s: M-Notify: Shutdown started\n", __func__);
76 ret = sysmon_send_event(SYSMON_SS_LPASS, "modem",
77 SUBSYS_BEFORE_SHUTDOWN);
78 if (ret < 0)
79 pr_err("%s: sysmon_send_event error %d", __func__,
80 ret);
81 break;
82 }
83 return NOTIFY_DONE;
84}
85
86static void *ssr_modem_notif_hdle;
87static struct notifier_block mnb = {
88 .notifier_call = modem_notifier_cb,
89};
90
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -070091static void lpass_fatal_fn(struct work_struct *work)
92{
Swaminathan Sathappanfc137ec2011-08-16 15:18:41 -070093 pr_err("%s %s: Watchdog bite received from Q6!\n", MODULE_NAME,
94 __func__);
Swaminathan Sathappan00f766d2011-09-29 15:35:55 -070095 panic(MODULE_NAME ": Resetting the SoC");
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -070096}
97
Swaminathan Sathappanfc137ec2011-08-16 15:18:41 -070098static void lpass_smsm_state_cb(void *data, uint32_t old_state,
99 uint32_t new_state)
100{
101 /* Ignore if we're the one that set SMSM_RESET */
102 if (q6_crash_shutdown)
103 return;
104
105 if (new_state & SMSM_RESET) {
106 pr_err("%s: LPASS SMSM state changed to SMSM_RESET,"
107 " new_state = 0x%x, old_state = 0x%x\n", __func__,
108 new_state, old_state);
Swaminathan Sathappan00f766d2011-09-29 15:35:55 -0700109 panic(MODULE_NAME ": Resetting the SoC");
Swaminathan Sathappanfc137ec2011-08-16 15:18:41 -0700110 }
111}
112
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700113static void send_q6_nmi(void)
114{
115 /* Send NMI to QDSP6 via an SCM call. */
116 uint32_t cmd = 0x1;
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700117
118 scm_call(SCM_SVC_UTIL, SCM_Q6_NMI_CMD,
119 &cmd, sizeof(cmd), NULL, 0);
120
121 /* Wakeup the Q6 */
Swaminathan Sathappan71074552011-09-20 10:09:31 -0700122 if (q6_wakeup_intr)
123 writel_relaxed(0x01, q6_wakeup_intr);
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700124 mb();
125
126 /* Q6 requires worstcase 100ms to dump caches etc.*/
Swaminathan Sathappan71074552011-09-20 10:09:31 -0700127 mdelay(100);
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700128 pr_debug("%s: Q6 NMI was sent.\n", __func__);
129}
130
131static int lpass_shutdown(const struct subsys_data *subsys)
132{
133 send_q6_nmi();
134 pil_force_shutdown("q6");
135 disable_irq_nosync(LPASS_Q6SS_WDOG_EXPIRED);
136
137 return 0;
138}
139
140static int lpass_powerup(const struct subsys_data *subsys)
141{
142 int ret = pil_force_boot("q6");
143 enable_irq(LPASS_Q6SS_WDOG_EXPIRED);
144 return ret;
145}
146/* RAM segments - address and size for 8960 */
147static struct ramdump_segment q6_segments[] = { {0x8da00000, 0x8f200000 -
148 0x8da00000}, {0x28400000, 0x20000} };
149static int lpass_ramdump(int enable, const struct subsys_data *subsys)
150{
151 pr_debug("%s: enable[%d]\n", __func__, enable);
152 if (enable)
153 return do_ramdump(lpass_ssr_8960.lpass_ramdump_dev,
154 q6_segments,
155 ARRAY_SIZE(q6_segments));
156 else
157 return 0;
158}
159
160static void lpass_crash_shutdown(const struct subsys_data *subsys)
161{
Swaminathan Sathappanfc137ec2011-08-16 15:18:41 -0700162 q6_crash_shutdown = 1;
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700163 send_q6_nmi();
164}
165
166static irqreturn_t lpass_wdog_bite_irq(int irq, void *dev_id)
167{
168 int ret;
169
170 pr_debug("%s: rxed irq[0x%x]", __func__, irq);
171 disable_irq_nosync(LPASS_Q6SS_WDOG_EXPIRED);
172 ret = schedule_work(&lpass_fatal_work);
173
174 return IRQ_HANDLED;
175}
176
177static struct subsys_data lpass_8960 = {
178 .name = "lpass",
179 .shutdown = lpass_shutdown,
180 .powerup = lpass_powerup,
181 .ramdump = lpass_ramdump,
182 .crash_shutdown = lpass_crash_shutdown
183};
184
185static int __init lpass_restart_init(void)
186{
187 return ssr_register_subsystem(&lpass_8960);
188}
189
190static int __init lpass_fatal_init(void)
191{
192 int ret;
193
Swaminathan Sathappanfc137ec2011-08-16 15:18:41 -0700194 ret = smsm_state_cb_register(SMSM_Q6_STATE, SMSM_RESET,
195 lpass_smsm_state_cb, 0);
196
197 if (ret < 0)
198 pr_err("%s: Unable to register SMSM callback! (%d)\n",
199 __func__, ret);
200
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700201 ret = request_irq(LPASS_Q6SS_WDOG_EXPIRED, lpass_wdog_bite_irq,
202 IRQF_TRIGGER_RISING, "q6_wdog", NULL);
203
204 if (ret < 0) {
205 pr_err("%s: Unable to request LPASS_Q6SS_WDOG_EXPIRED irq.",
206 __func__);
207 goto out;
208 }
209 ret = lpass_restart_init();
210 if (ret < 0) {
211 pr_err("%s: Unable to reg with lpass ssr. (%d)\n",
212 __func__, ret);
213 goto out;
214 }
Swaminathan Sathappan71074552011-09-20 10:09:31 -0700215 q6_wakeup_intr = ioremap_nocache(Q6SS_SOFT_INTR_WAKEUP, 8);
216 if (!q6_wakeup_intr)
217 pr_err("%s: Unable to request q6 wakeup interrupt\n", __func__);
218
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700219 lpass_ssr_8960.lpass_ramdump_dev = create_ramdump_device("lpass");
220
221 if (!lpass_ssr_8960.lpass_ramdump_dev) {
222 pr_err("%s: Unable to create ramdump device.\n",
223 __func__);
224 ret = -ENOMEM;
225 goto out;
226 }
Sriranjan Srikantam6b2899c2011-10-10 16:28:49 -0700227 ssr_notif_hdle = subsys_notif_register_notifier("riva",
228 &rnb);
229 if (IS_ERR(ssr_notif_hdle) < 0) {
230 ret = PTR_ERR(ssr_notif_hdle);
231 pr_err("%s: subsys_register_notifier for Riva: err = %d\n",
232 __func__, ret);
233 iounmap(q6_wakeup_intr);
234 free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL);
235 goto out;
236 }
237
Sriranjan Srikantam3d907a62011-10-11 12:51:25 -0700238 ssr_modem_notif_hdle = subsys_notif_register_notifier("modem",
239 &mnb);
240 if (IS_ERR(ssr_modem_notif_hdle) < 0) {
241 ret = PTR_ERR(ssr_modem_notif_hdle);
242 pr_err("%s: subsys_register_notifier for Modem: err = %d\n",
243 __func__, ret);
244 subsys_notif_unregister_notifier(ssr_notif_hdle, &rnb);
245 iounmap(q6_wakeup_intr);
246 free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL);
247 goto out;
248 }
249
Rohit Vaswani56dd22a2011-11-11 16:21:28 -0800250 pr_info("%s: lpass SSR driver init'ed.\n", __func__);
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700251out:
252 return ret;
253}
254
255static void __exit lpass_fatal_exit(void)
256{
Sriranjan Srikantam6b2899c2011-10-10 16:28:49 -0700257 subsys_notif_unregister_notifier(ssr_notif_hdle, &rnb);
Sriranjan Srikantam3d907a62011-10-11 12:51:25 -0700258 subsys_notif_unregister_notifier(ssr_modem_notif_hdle, &mnb);
Swaminathan Sathappan71074552011-09-20 10:09:31 -0700259 iounmap(q6_wakeup_intr);
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700260 free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL);
261}
262
263module_init(lpass_fatal_init);
264module_exit(lpass_fatal_exit);
265
266MODULE_LICENSE("GPL v2");