blob: 7775740f9400c3838c241f51fef4d85bafb6ebef [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"
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -070034
35/* Subsystem restart: QDSP6 data, functions */
36static void lpass_fatal_fn(struct work_struct *);
37static DECLARE_WORK(lpass_fatal_work, lpass_fatal_fn);
38struct lpass_ssr {
39 void *lpass_ramdump_dev;
40} lpass_ssr;
41
42static struct lpass_ssr lpass_ssr_8960;
Swaminathan Sathappanfc137ec2011-08-16 15:18:41 -070043static int q6_crash_shutdown;
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -070044
Sriranjan Srikantam6b2899c2011-10-10 16:28:49 -070045static int riva_notifier_cb(struct notifier_block *this, unsigned long code,
46 void *ss_handle)
47{
48 int ret;
49 switch (code) {
50 case SUBSYS_BEFORE_SHUTDOWN:
51 pr_debug("%s: R-Notify: Shutdown started\n", __func__);
52 ret = sysmon_send_event(SYSMON_SS_LPASS, "wcnss",
53 SUBSYS_BEFORE_SHUTDOWN);
54 if (ret < 0)
55 pr_err("%s: sysmon_send_event error %d", __func__,
56 ret);
57 break;
58 }
59 return NOTIFY_DONE;
60}
61
62static void *ssr_notif_hdle;
63static struct notifier_block rnb = {
64 .notifier_call = riva_notifier_cb,
65};
66
Sriranjan Srikantam3d907a62011-10-11 12:51:25 -070067static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
68 void *ss_handle)
69{
70 int ret;
71 switch (code) {
72 case SUBSYS_BEFORE_SHUTDOWN:
73 pr_debug("%s: M-Notify: Shutdown started\n", __func__);
74 ret = sysmon_send_event(SYSMON_SS_LPASS, "modem",
75 SUBSYS_BEFORE_SHUTDOWN);
76 if (ret < 0)
77 pr_err("%s: sysmon_send_event error %d", __func__,
78 ret);
79 break;
80 }
81 return NOTIFY_DONE;
82}
83
84static void *ssr_modem_notif_hdle;
85static struct notifier_block mnb = {
86 .notifier_call = modem_notifier_cb,
87};
88
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -070089static void lpass_fatal_fn(struct work_struct *work)
90{
Swaminathan Sathappanfc137ec2011-08-16 15:18:41 -070091 pr_err("%s %s: Watchdog bite received from Q6!\n", MODULE_NAME,
92 __func__);
Swaminathan Sathappan00f766d2011-09-29 15:35:55 -070093 panic(MODULE_NAME ": Resetting the SoC");
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -070094}
95
Swaminathan Sathappanfc137ec2011-08-16 15:18:41 -070096static void lpass_smsm_state_cb(void *data, uint32_t old_state,
97 uint32_t new_state)
98{
99 /* Ignore if we're the one that set SMSM_RESET */
100 if (q6_crash_shutdown)
101 return;
102
103 if (new_state & SMSM_RESET) {
104 pr_err("%s: LPASS SMSM state changed to SMSM_RESET,"
105 " new_state = 0x%x, old_state = 0x%x\n", __func__,
106 new_state, old_state);
Swaminathan Sathappan00f766d2011-09-29 15:35:55 -0700107 panic(MODULE_NAME ": Resetting the SoC");
Swaminathan Sathappanfc137ec2011-08-16 15:18:41 -0700108 }
109}
110
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700111static void send_q6_nmi(void)
112{
113 /* Send NMI to QDSP6 via an SCM call. */
114 uint32_t cmd = 0x1;
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700115
116 scm_call(SCM_SVC_UTIL, SCM_Q6_NMI_CMD,
117 &cmd, sizeof(cmd), NULL, 0);
118
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700119 /* Q6 requires worstcase 100ms to dump caches etc.*/
Swaminathan Sathappan71074552011-09-20 10:09:31 -0700120 mdelay(100);
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700121 pr_debug("%s: Q6 NMI was sent.\n", __func__);
122}
123
124static int lpass_shutdown(const struct subsys_data *subsys)
125{
126 send_q6_nmi();
127 pil_force_shutdown("q6");
128 disable_irq_nosync(LPASS_Q6SS_WDOG_EXPIRED);
129
130 return 0;
131}
132
133static int lpass_powerup(const struct subsys_data *subsys)
134{
135 int ret = pil_force_boot("q6");
136 enable_irq(LPASS_Q6SS_WDOG_EXPIRED);
137 return ret;
138}
139/* RAM segments - address and size for 8960 */
140static struct ramdump_segment q6_segments[] = { {0x8da00000, 0x8f200000 -
141 0x8da00000}, {0x28400000, 0x20000} };
142static int lpass_ramdump(int enable, const struct subsys_data *subsys)
143{
144 pr_debug("%s: enable[%d]\n", __func__, enable);
145 if (enable)
146 return do_ramdump(lpass_ssr_8960.lpass_ramdump_dev,
147 q6_segments,
148 ARRAY_SIZE(q6_segments));
149 else
150 return 0;
151}
152
153static void lpass_crash_shutdown(const struct subsys_data *subsys)
154{
Swaminathan Sathappanfc137ec2011-08-16 15:18:41 -0700155 q6_crash_shutdown = 1;
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700156 send_q6_nmi();
157}
158
159static irqreturn_t lpass_wdog_bite_irq(int irq, void *dev_id)
160{
161 int ret;
162
163 pr_debug("%s: rxed irq[0x%x]", __func__, irq);
164 disable_irq_nosync(LPASS_Q6SS_WDOG_EXPIRED);
165 ret = schedule_work(&lpass_fatal_work);
166
167 return IRQ_HANDLED;
168}
169
170static struct subsys_data lpass_8960 = {
171 .name = "lpass",
172 .shutdown = lpass_shutdown,
173 .powerup = lpass_powerup,
174 .ramdump = lpass_ramdump,
175 .crash_shutdown = lpass_crash_shutdown
176};
177
178static int __init lpass_restart_init(void)
179{
180 return ssr_register_subsystem(&lpass_8960);
181}
182
183static int __init lpass_fatal_init(void)
184{
185 int ret;
186
Swaminathan Sathappanfc137ec2011-08-16 15:18:41 -0700187 ret = smsm_state_cb_register(SMSM_Q6_STATE, SMSM_RESET,
188 lpass_smsm_state_cb, 0);
189
190 if (ret < 0)
191 pr_err("%s: Unable to register SMSM callback! (%d)\n",
192 __func__, ret);
193
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700194 ret = request_irq(LPASS_Q6SS_WDOG_EXPIRED, lpass_wdog_bite_irq,
195 IRQF_TRIGGER_RISING, "q6_wdog", NULL);
196
197 if (ret < 0) {
198 pr_err("%s: Unable to request LPASS_Q6SS_WDOG_EXPIRED irq.",
199 __func__);
200 goto out;
201 }
202 ret = lpass_restart_init();
203 if (ret < 0) {
204 pr_err("%s: Unable to reg with lpass ssr. (%d)\n",
205 __func__, ret);
206 goto out;
207 }
Swaminathan Sathappan71074552011-09-20 10:09:31 -0700208
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700209 lpass_ssr_8960.lpass_ramdump_dev = create_ramdump_device("lpass");
210
211 if (!lpass_ssr_8960.lpass_ramdump_dev) {
212 pr_err("%s: Unable to create ramdump device.\n",
213 __func__);
214 ret = -ENOMEM;
215 goto out;
216 }
Sriranjan Srikantam6b2899c2011-10-10 16:28:49 -0700217 ssr_notif_hdle = subsys_notif_register_notifier("riva",
218 &rnb);
219 if (IS_ERR(ssr_notif_hdle) < 0) {
220 ret = PTR_ERR(ssr_notif_hdle);
221 pr_err("%s: subsys_register_notifier for Riva: err = %d\n",
222 __func__, ret);
Sriranjan Srikantam6b2899c2011-10-10 16:28:49 -0700223 free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL);
224 goto out;
225 }
226
Sriranjan Srikantam3d907a62011-10-11 12:51:25 -0700227 ssr_modem_notif_hdle = subsys_notif_register_notifier("modem",
228 &mnb);
229 if (IS_ERR(ssr_modem_notif_hdle) < 0) {
230 ret = PTR_ERR(ssr_modem_notif_hdle);
231 pr_err("%s: subsys_register_notifier for Modem: err = %d\n",
232 __func__, ret);
233 subsys_notif_unregister_notifier(ssr_notif_hdle, &rnb);
Sriranjan Srikantam3d907a62011-10-11 12:51:25 -0700234 free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL);
235 goto out;
236 }
237
Rohit Vaswani56dd22a2011-11-11 16:21:28 -0800238 pr_info("%s: lpass SSR driver init'ed.\n", __func__);
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700239out:
240 return ret;
241}
242
243static void __exit lpass_fatal_exit(void)
244{
Sriranjan Srikantam6b2899c2011-10-10 16:28:49 -0700245 subsys_notif_unregister_notifier(ssr_notif_hdle, &rnb);
Sriranjan Srikantam3d907a62011-10-11 12:51:25 -0700246 subsys_notif_unregister_notifier(ssr_modem_notif_hdle, &mnb);
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700247 free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL);
248}
249
250module_init(lpass_fatal_init);
251module_exit(lpass_fatal_exit);
252
253MODULE_LICENSE("GPL v2");