blob: f05bcdbd3369ce4962b5e30bb6eff35c7a0d8723 [file] [log] [blame]
Seemanta Dutta4e2d49c2013-04-05 16:28:11 -07001/* Copyright (c) 2012,2013 The Linux Foundation. All rights reserved.
Stephen Boydbdb53f32012-06-05 18:39:47 -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/init.h>
14#include <linux/module.h>
15#include <linux/platform_device.h>
16#include <linux/regulator/consumer.h>
17#include <linux/err.h>
18#include <linux/clk.h>
19#include <linux/io.h>
Stephen Boyda1cf76b2012-06-13 12:05:35 -070020#include <linux/interrupt.h>
21#include <linux/workqueue.h>
22#include <linux/delay.h>
Stephen Boydbdb53f32012-06-05 18:39:47 -070023
Stephen Boyda1cf76b2012-06-13 12:05:35 -070024#include <mach/scm.h>
Stephen Boyda1cf76b2012-06-13 12:05:35 -070025#include <mach/subsystem_restart.h>
26#include <mach/subsystem_notif.h>
Seemanta Dutta4e2d49c2013-04-05 16:28:11 -070027#include <mach/ramdump.h>
Stephen Boyda1cf76b2012-06-13 12:05:35 -070028
29#include "smd_private.h"
Stephen Boyda1cf76b2012-06-13 12:05:35 -070030#include "sysmon.h"
Stephen Boydbdb53f32012-06-05 18:39:47 -070031#include "peripheral-loader.h"
32#include "pil-q6v4.h"
33#include "scm-pas.h"
34
Stephen Boyda1cf76b2012-06-13 12:05:35 -070035struct lpass_q6v4 {
36 struct q6v4_data q6;
37 void *riva_notif_hdle;
38 void *modem_notif_hdle;
39 struct subsys_device *subsys;
40 struct subsys_desc subsys_desc;
41 int crash_shutdown;
42 void *ramdump_dev;
43 struct work_struct work;
44 int loadable;
45};
46
Stephen Boydbdb53f32012-06-05 18:39:47 -070047static int pil_q6v4_lpass_boot(struct pil_desc *pil)
48{
49 struct q6v4_data *drv = pil_to_q6v4_data(pil);
50 int err;
51
52 err = pil_q6v4_power_up(drv);
53 if (err)
54 return err;
55
56 return pil_q6v4_boot(pil);
57}
58
59static int pil_q6v4_lpass_shutdown(struct pil_desc *pil)
60{
61 struct q6v4_data *drv = pil_to_q6v4_data(pil);
62 int ret;
63
64 ret = pil_q6v4_shutdown(pil);
65 if (ret)
66 return ret;
67 pil_q6v4_power_down(drv);
68 return 0;
69}
70
71static struct pil_reset_ops pil_q6v4_lpass_ops = {
Stephen Boydbdb53f32012-06-05 18:39:47 -070072 .auth_and_reset = pil_q6v4_lpass_boot,
73 .shutdown = pil_q6v4_lpass_shutdown,
74 .proxy_vote = pil_q6v4_make_proxy_votes,
75 .proxy_unvote = pil_q6v4_remove_proxy_votes,
76};
77
78static struct pil_reset_ops pil_q6v4_lpass_ops_trusted = {
79 .init_image = pil_q6v4_init_image_trusted,
80 .auth_and_reset = pil_q6v4_boot_trusted,
81 .shutdown = pil_q6v4_shutdown_trusted,
82 .proxy_vote = pil_q6v4_make_proxy_votes,
83 .proxy_unvote = pil_q6v4_remove_proxy_votes,
84};
85
Stephen Boyda1cf76b2012-06-13 12:05:35 -070086static int riva_notifier_cb(struct notifier_block *this, unsigned long code,
87 void *ss_handle)
88{
89 int ret;
90 switch (code) {
91 case SUBSYS_BEFORE_SHUTDOWN:
92 pr_debug("%s: R-Notify: Shutdown started\n", __func__);
93 ret = sysmon_send_event(SYSMON_SS_LPASS, "wcnss",
94 SUBSYS_BEFORE_SHUTDOWN);
95 if (ret < 0)
96 pr_err("%s: sysmon_send_event error %d", __func__, ret);
97 break;
98 }
99 return NOTIFY_DONE;
100}
101
102static struct notifier_block rnb = {
103 .notifier_call = riva_notifier_cb,
104};
105
106static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
107 void *ss_handle)
108{
109 int ret;
110 switch (code) {
111 case SUBSYS_BEFORE_SHUTDOWN:
112 pr_debug("%s: M-Notify: Shutdown started\n", __func__);
113 ret = sysmon_send_event(SYSMON_SS_LPASS, "modem",
114 SUBSYS_BEFORE_SHUTDOWN);
115 if (ret < 0)
116 pr_err("%s: sysmon_send_event error %d", __func__, ret);
117 break;
118 }
119 return NOTIFY_DONE;
120}
121
122static struct notifier_block mnb = {
123 .notifier_call = modem_notifier_cb,
124};
125
126static void lpass_log_failure_reason(void)
127{
128 char *reason;
129 char buffer[81];
130 unsigned size;
131
132 reason = smem_get_entry(SMEM_SSR_REASON_LPASS0, &size);
133
134 if (!reason) {
135 pr_err("LPASS subsystem failure reason: (unknown, smem_get_entry failed).");
136 return;
137 }
138
139 if (reason[0] == '\0') {
140 pr_err("LPASS subsystem failure reason: (unknown, init value found)");
141 return;
142 }
143
144 size = min(size, sizeof(buffer) - 1);
145 memcpy(buffer, reason, size);
146 buffer[size] = '\0';
147 pr_err("LPASS subsystem failure reason: %s", buffer);
148 memset((void *)reason, 0x0, size);
149 wmb();
150}
151
152static void lpass_fatal_fn(struct work_struct *work)
153{
154 pr_err("Watchdog bite received from lpass Q6!\n");
155 lpass_log_failure_reason();
156 panic("Q6 Resetting the SoC");
157}
158
159static void lpass_smsm_state_cb(void *data, uint32_t old_state,
160 uint32_t new_state)
161{
162 struct lpass_q6v4 *drv = data;
163
164 /* Ignore if we're the one that set SMSM_RESET */
165 if (drv->crash_shutdown)
166 return;
167
168 if (new_state & SMSM_RESET) {
169 pr_err("%s: LPASS SMSM state changed to SMSM_RESET, new_state = %#x, old_state = %#x\n",
170 __func__, new_state, old_state);
171 lpass_log_failure_reason();
172 panic("Q6 Resetting the SoC");
173 }
174}
175
176#define SCM_Q6_NMI_CMD 0x1
177
178static void send_q6_nmi(void)
179{
180 /* Send NMI to QDSP6 via an SCM call. */
181 uint32_t cmd = 0x1;
182
183 scm_call(SCM_SVC_UTIL, SCM_Q6_NMI_CMD,
184 &cmd, sizeof(cmd), NULL, 0);
185
186 /* Q6 requires worstcase 100ms to dump caches etc.*/
187 mdelay(100);
188 pr_debug("%s: Q6 NMI was sent.\n", __func__);
189}
190
191#define subsys_to_lpass(d) container_of(d, struct lpass_q6v4, subsys_desc)
192
Stephen Boyd3e4e9752012-06-27 12:46:32 -0700193static int lpass_start(const struct subsys_desc *desc)
194{
195 struct lpass_q6v4 *drv = subsys_to_lpass(desc);
196
Stephen Boyde83a0a22012-06-29 13:51:27 -0700197 if (drv->loadable)
198 return pil_boot(&drv->q6.desc);
Stephen Boyd3e4e9752012-06-27 12:46:32 -0700199 return 0;
200}
201
202static void lpass_stop(const struct subsys_desc *desc)
203{
204 struct lpass_q6v4 *drv = subsys_to_lpass(desc);
Stephen Boyde83a0a22012-06-29 13:51:27 -0700205
Stephen Boyd3e4e9752012-06-27 12:46:32 -0700206 if (drv->loadable)
Stephen Boyde83a0a22012-06-29 13:51:27 -0700207 pil_shutdown(&drv->q6.desc);
Stephen Boyd3e4e9752012-06-27 12:46:32 -0700208}
209
Stephen Boyda1cf76b2012-06-13 12:05:35 -0700210static int lpass_shutdown(const struct subsys_desc *subsys)
211{
212 struct lpass_q6v4 *drv = subsys_to_lpass(subsys);
213
214 send_q6_nmi();
215 if (drv->loadable)
Stephen Boyde83a0a22012-06-29 13:51:27 -0700216 pil_shutdown(&drv->q6.desc);
Stephen Boyda1cf76b2012-06-13 12:05:35 -0700217 disable_irq_nosync(drv->q6.wdog_irq);
218
219 return 0;
220}
221
222static int lpass_powerup(const struct subsys_desc *subsys)
223{
224 struct lpass_q6v4 *drv = subsys_to_lpass(subsys);
225 int ret = 0;
226
227 if (drv->loadable)
Stephen Boyde83a0a22012-06-29 13:51:27 -0700228 ret = pil_boot(&drv->q6.desc);
Stephen Boyda1cf76b2012-06-13 12:05:35 -0700229 enable_irq(drv->q6.wdog_irq);
230
231 return ret;
232}
233
Stephen Boyda1cf76b2012-06-13 12:05:35 -0700234static int lpass_ramdump(int enable, const struct subsys_desc *subsys)
235{
236 struct lpass_q6v4 *drv = subsys_to_lpass(subsys);
237
238 if (!enable)
239 return 0;
Stephen Boyd5eb17ce2012-11-29 15:34:21 -0800240
241 return pil_do_ramdump(&drv->q6.desc, drv->ramdump_dev);
Stephen Boyda1cf76b2012-06-13 12:05:35 -0700242}
243
244static void lpass_crash_shutdown(const struct subsys_desc *subsys)
245{
246 struct lpass_q6v4 *drv = subsys_to_lpass(subsys);
247
248 drv->crash_shutdown = 1;
249 send_q6_nmi();
250}
251
252static irqreturn_t lpass_wdog_bite_irq(int irq, void *dev_id)
253{
254 struct lpass_q6v4 *drv = dev_id;
255
256 disable_irq_nosync(drv->q6.wdog_irq);
257 schedule_work(&drv->work);
258
259 return IRQ_HANDLED;
260}
261
Stephen Boydbdb53f32012-06-05 18:39:47 -0700262static int __devinit pil_q6v4_lpass_driver_probe(struct platform_device *pdev)
263{
264 const struct pil_q6v4_pdata *pdata = pdev->dev.platform_data;
Stephen Boyda1cf76b2012-06-13 12:05:35 -0700265 struct lpass_q6v4 *drv;
266 struct q6v4_data *q6;
Stephen Boydbdb53f32012-06-05 18:39:47 -0700267 struct pil_desc *desc;
268 struct resource *res;
Stephen Boyda1cf76b2012-06-13 12:05:35 -0700269 int ret;
Stephen Boydbdb53f32012-06-05 18:39:47 -0700270
271 drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
272 if (!drv)
273 return -ENOMEM;
274 platform_set_drvdata(pdev, drv);
Stephen Boyda1cf76b2012-06-13 12:05:35 -0700275 q6 = &drv->q6;
276 desc = &q6->desc;
Stephen Boydbdb53f32012-06-05 18:39:47 -0700277
Stephen Boyda1cf76b2012-06-13 12:05:35 -0700278 q6->wdog_irq = platform_get_irq(pdev, 0);
279 if (q6->wdog_irq < 0)
280 return q6->wdog_irq;
Stephen Boydbdb53f32012-06-05 18:39:47 -0700281
Stephen Boyda1cf76b2012-06-13 12:05:35 -0700282 drv->loadable = !!pdata; /* No pdata = don't use PIL */
283 if (drv->loadable) {
284 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Stephen Boydf8f89282012-07-16 18:05:48 -0700285 q6->base = devm_request_and_ioremap(&pdev->dev, res);
Stephen Boyda1cf76b2012-06-13 12:05:35 -0700286 if (!q6->base)
287 return -ENOMEM;
Stephen Boydbdb53f32012-06-05 18:39:47 -0700288
Stephen Boyda1cf76b2012-06-13 12:05:35 -0700289 q6->vreg = devm_regulator_get(&pdev->dev, "core_vdd");
290 if (IS_ERR(q6->vreg))
291 return PTR_ERR(q6->vreg);
Stephen Boydbdb53f32012-06-05 18:39:47 -0700292
Stephen Boyda1cf76b2012-06-13 12:05:35 -0700293 q6->xo = devm_clk_get(&pdev->dev, "xo");
294 if (IS_ERR(q6->xo))
295 return PTR_ERR(q6->xo);
Stephen Boydbdb53f32012-06-05 18:39:47 -0700296
Stephen Boyda1cf76b2012-06-13 12:05:35 -0700297 desc->name = pdata->name;
298 desc->dev = &pdev->dev;
299 desc->owner = THIS_MODULE;
300 desc->proxy_timeout = 10000;
301 pil_q6v4_init(q6, pdata);
302
303 if (pas_supported(pdata->pas_id) > 0) {
304 desc->ops = &pil_q6v4_lpass_ops_trusted;
305 dev_info(&pdev->dev, "using secure boot\n");
306 } else {
307 desc->ops = &pil_q6v4_lpass_ops;
308 dev_info(&pdev->dev, "using non-secure boot\n");
309 }
310
Stephen Boyde83a0a22012-06-29 13:51:27 -0700311 ret = pil_desc_init(desc);
312 if (ret)
313 return ret;
Stephen Boydbdb53f32012-06-05 18:39:47 -0700314 }
315
Stephen Boyd77db8bb2012-06-27 15:15:16 -0700316 drv->subsys_desc.name = "adsp";
Stephen Boyd3e4e9752012-06-27 12:46:32 -0700317 drv->subsys_desc.dev = &pdev->dev;
318 drv->subsys_desc.owner = THIS_MODULE;
319 drv->subsys_desc.start = lpass_start;
320 drv->subsys_desc.stop = lpass_stop;
Stephen Boyda1cf76b2012-06-13 12:05:35 -0700321 drv->subsys_desc.shutdown = lpass_shutdown;
322 drv->subsys_desc.powerup = lpass_powerup;
323 drv->subsys_desc.ramdump = lpass_ramdump;
324 drv->subsys_desc.crash_shutdown = lpass_crash_shutdown;
325
326 INIT_WORK(&drv->work, lpass_fatal_fn);
327
Stephen Boydc1a72612012-07-05 14:07:35 -0700328 drv->ramdump_dev = create_ramdump_device("lpass", &pdev->dev);
Stephen Boyda1cf76b2012-06-13 12:05:35 -0700329 if (!drv->ramdump_dev) {
330 ret = -ENOMEM;
331 goto err_ramdump;
332 }
333
334 drv->subsys = subsys_register(&drv->subsys_desc);
335 if (IS_ERR(drv->subsys)) {
336 ret = PTR_ERR(drv->subsys);
337 goto err_subsys;
338 }
Stephen Boyd43b380a2012-09-21 17:34:24 -0700339 if (!drv->loadable)
340 subsys_default_online(drv->subsys);
Stephen Boyda1cf76b2012-06-13 12:05:35 -0700341
342 ret = devm_request_irq(&pdev->dev, q6->wdog_irq, lpass_wdog_bite_irq,
343 IRQF_TRIGGER_RISING, dev_name(&pdev->dev), drv);
344 if (ret)
345 goto err_irq;
346
347 ret = smsm_state_cb_register(SMSM_Q6_STATE, SMSM_RESET,
348 lpass_smsm_state_cb, drv);
349 if (ret < 0)
350 goto err_smsm;
351
352 drv->riva_notif_hdle = subsys_notif_register_notifier("riva", &rnb);
353 if (IS_ERR(drv->riva_notif_hdle)) {
354 ret = PTR_ERR(drv->riva_notif_hdle);
355 goto err_notif_riva;
356 }
357
358 drv->modem_notif_hdle = subsys_notif_register_notifier("modem", &mnb);
359 if (IS_ERR(drv->modem_notif_hdle)) {
360 ret = PTR_ERR(drv->modem_notif_hdle);
361 goto err_notif_modem;
362 }
Stephen Boydbdb53f32012-06-05 18:39:47 -0700363 return 0;
Stephen Boyda1cf76b2012-06-13 12:05:35 -0700364err_notif_modem:
365 subsys_notif_unregister_notifier(drv->riva_notif_hdle, &rnb);
366err_notif_riva:
367 smsm_state_cb_deregister(SMSM_Q6_STATE, SMSM_RESET,
368 lpass_smsm_state_cb, drv);
369err_smsm:
370err_irq:
371 subsys_unregister(drv->subsys);
372err_subsys:
373 destroy_ramdump_device(drv->ramdump_dev);
374err_ramdump:
375 if (drv->loadable)
Stephen Boyde83a0a22012-06-29 13:51:27 -0700376 pil_desc_release(desc);
Stephen Boyda1cf76b2012-06-13 12:05:35 -0700377 return ret;
Stephen Boydbdb53f32012-06-05 18:39:47 -0700378}
379
380static int __devexit pil_q6v4_lpass_driver_exit(struct platform_device *pdev)
381{
Stephen Boyda1cf76b2012-06-13 12:05:35 -0700382 struct lpass_q6v4 *drv = platform_get_drvdata(pdev);
383 subsys_notif_unregister_notifier(drv->riva_notif_hdle, &rnb);
384 subsys_notif_unregister_notifier(drv->modem_notif_hdle, &mnb);
385 smsm_state_cb_deregister(SMSM_Q6_STATE, SMSM_RESET,
386 lpass_smsm_state_cb, drv);
387 subsys_unregister(drv->subsys);
388 destroy_ramdump_device(drv->ramdump_dev);
389 if (drv->loadable)
Stephen Boyde83a0a22012-06-29 13:51:27 -0700390 pil_desc_release(&drv->q6.desc);
Stephen Boydbdb53f32012-06-05 18:39:47 -0700391 return 0;
392}
393
394static struct platform_driver pil_q6v4_lpass_driver = {
395 .probe = pil_q6v4_lpass_driver_probe,
396 .remove = __devexit_p(pil_q6v4_lpass_driver_exit),
397 .driver = {
398 .name = "pil-q6v4-lpass",
399 .owner = THIS_MODULE,
400 },
401};
402
403static int __init pil_q6v4_lpass_init(void)
404{
405 return platform_driver_register(&pil_q6v4_lpass_driver);
406}
407module_init(pil_q6v4_lpass_init);
408
409static void __exit pil_q6v4_lpass_exit(void)
410{
411 platform_driver_unregister(&pil_q6v4_lpass_driver);
412}
413module_exit(pil_q6v4_lpass_exit);
414
415MODULE_DESCRIPTION("Support for booting QDSP6v4 (Hexagon) processors");
416MODULE_LICENSE("GPL v2");