blob: 11b9092bfb2262419d37f28e1f644567a564f2ec [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
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -070069static void lpass_fatal_fn(struct work_struct *work)
70{
Swaminathan Sathappanfc137ec2011-08-16 15:18:41 -070071 pr_err("%s %s: Watchdog bite received from Q6!\n", MODULE_NAME,
72 __func__);
Swaminathan Sathappan00f766d2011-09-29 15:35:55 -070073 panic(MODULE_NAME ": Resetting the SoC");
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -070074}
75
Swaminathan Sathappanfc137ec2011-08-16 15:18:41 -070076static void lpass_smsm_state_cb(void *data, uint32_t old_state,
77 uint32_t new_state)
78{
79 /* Ignore if we're the one that set SMSM_RESET */
80 if (q6_crash_shutdown)
81 return;
82
83 if (new_state & SMSM_RESET) {
84 pr_err("%s: LPASS SMSM state changed to SMSM_RESET,"
85 " new_state = 0x%x, old_state = 0x%x\n", __func__,
86 new_state, old_state);
Swaminathan Sathappan00f766d2011-09-29 15:35:55 -070087 panic(MODULE_NAME ": Resetting the SoC");
Swaminathan Sathappanfc137ec2011-08-16 15:18:41 -070088 }
89}
90
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -070091static void send_q6_nmi(void)
92{
93 /* Send NMI to QDSP6 via an SCM call. */
94 uint32_t cmd = 0x1;
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -070095
96 scm_call(SCM_SVC_UTIL, SCM_Q6_NMI_CMD,
97 &cmd, sizeof(cmd), NULL, 0);
98
99 /* Wakeup the Q6 */
Swaminathan Sathappan71074552011-09-20 10:09:31 -0700100 if (q6_wakeup_intr)
101 writel_relaxed(0x01, q6_wakeup_intr);
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700102 mb();
103
104 /* Q6 requires worstcase 100ms to dump caches etc.*/
Swaminathan Sathappan71074552011-09-20 10:09:31 -0700105 mdelay(100);
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700106 pr_debug("%s: Q6 NMI was sent.\n", __func__);
107}
108
109static int lpass_shutdown(const struct subsys_data *subsys)
110{
111 send_q6_nmi();
112 pil_force_shutdown("q6");
113 disable_irq_nosync(LPASS_Q6SS_WDOG_EXPIRED);
114
115 return 0;
116}
117
118static int lpass_powerup(const struct subsys_data *subsys)
119{
120 int ret = pil_force_boot("q6");
121 enable_irq(LPASS_Q6SS_WDOG_EXPIRED);
122 return ret;
123}
124/* RAM segments - address and size for 8960 */
125static struct ramdump_segment q6_segments[] = { {0x8da00000, 0x8f200000 -
126 0x8da00000}, {0x28400000, 0x20000} };
127static int lpass_ramdump(int enable, const struct subsys_data *subsys)
128{
129 pr_debug("%s: enable[%d]\n", __func__, enable);
130 if (enable)
131 return do_ramdump(lpass_ssr_8960.lpass_ramdump_dev,
132 q6_segments,
133 ARRAY_SIZE(q6_segments));
134 else
135 return 0;
136}
137
138static void lpass_crash_shutdown(const struct subsys_data *subsys)
139{
Swaminathan Sathappanfc137ec2011-08-16 15:18:41 -0700140 q6_crash_shutdown = 1;
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700141 send_q6_nmi();
142}
143
144static irqreturn_t lpass_wdog_bite_irq(int irq, void *dev_id)
145{
146 int ret;
147
148 pr_debug("%s: rxed irq[0x%x]", __func__, irq);
149 disable_irq_nosync(LPASS_Q6SS_WDOG_EXPIRED);
150 ret = schedule_work(&lpass_fatal_work);
151
152 return IRQ_HANDLED;
153}
154
155static struct subsys_data lpass_8960 = {
156 .name = "lpass",
157 .shutdown = lpass_shutdown,
158 .powerup = lpass_powerup,
159 .ramdump = lpass_ramdump,
160 .crash_shutdown = lpass_crash_shutdown
161};
162
163static int __init lpass_restart_init(void)
164{
165 return ssr_register_subsystem(&lpass_8960);
166}
167
168static int __init lpass_fatal_init(void)
169{
170 int ret;
171
Swaminathan Sathappanfc137ec2011-08-16 15:18:41 -0700172 ret = smsm_state_cb_register(SMSM_Q6_STATE, SMSM_RESET,
173 lpass_smsm_state_cb, 0);
174
175 if (ret < 0)
176 pr_err("%s: Unable to register SMSM callback! (%d)\n",
177 __func__, ret);
178
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700179 ret = request_irq(LPASS_Q6SS_WDOG_EXPIRED, lpass_wdog_bite_irq,
180 IRQF_TRIGGER_RISING, "q6_wdog", NULL);
181
182 if (ret < 0) {
183 pr_err("%s: Unable to request LPASS_Q6SS_WDOG_EXPIRED irq.",
184 __func__);
185 goto out;
186 }
187 ret = lpass_restart_init();
188 if (ret < 0) {
189 pr_err("%s: Unable to reg with lpass ssr. (%d)\n",
190 __func__, ret);
191 goto out;
192 }
Swaminathan Sathappan71074552011-09-20 10:09:31 -0700193 q6_wakeup_intr = ioremap_nocache(Q6SS_SOFT_INTR_WAKEUP, 8);
194 if (!q6_wakeup_intr)
195 pr_err("%s: Unable to request q6 wakeup interrupt\n", __func__);
196
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700197 lpass_ssr_8960.lpass_ramdump_dev = create_ramdump_device("lpass");
198
199 if (!lpass_ssr_8960.lpass_ramdump_dev) {
200 pr_err("%s: Unable to create ramdump device.\n",
201 __func__);
202 ret = -ENOMEM;
203 goto out;
204 }
Sriranjan Srikantam6b2899c2011-10-10 16:28:49 -0700205 ssr_notif_hdle = subsys_notif_register_notifier("riva",
206 &rnb);
207 if (IS_ERR(ssr_notif_hdle) < 0) {
208 ret = PTR_ERR(ssr_notif_hdle);
209 pr_err("%s: subsys_register_notifier for Riva: err = %d\n",
210 __func__, ret);
211 iounmap(q6_wakeup_intr);
212 free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL);
213 goto out;
214 }
215
Rohit Vaswani56dd22a2011-11-11 16:21:28 -0800216 pr_info("%s: lpass SSR driver init'ed.\n", __func__);
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700217out:
218 return ret;
219}
220
221static void __exit lpass_fatal_exit(void)
222{
Sriranjan Srikantam6b2899c2011-10-10 16:28:49 -0700223 subsys_notif_unregister_notifier(ssr_notif_hdle, &rnb);
Swaminathan Sathappan71074552011-09-20 10:09:31 -0700224 iounmap(q6_wakeup_intr);
Bharath Ramachandramurthyff061c72011-06-27 14:22:33 -0700225 free_irq(LPASS_Q6SS_WDOG_EXPIRED, NULL);
226}
227
228module_init(lpass_fatal_init);
229module_exit(lpass_fatal_exit);
230
231MODULE_LICENSE("GPL v2");