pil-q6v5-lpass.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. /*
  2. * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License version 2 and
  6. * only version 2 as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. */
  13. #include <linux/init.h>
  14. #include <linux/module.h>
  15. #include <linux/platform_device.h>
  16. #include <linux/io.h>
  17. #include <linux/err.h>
  18. #include <linux/of.h>
  19. #include <linux/clk.h>
  20. #include <linux/workqueue.h>
  21. #include <linux/interrupt.h>
  22. #include <linux/delay.h>
  23. #include <linux/sysfs.h>
  24. #include <linux/of_gpio.h>
  25. #include <mach/clk.h>
  26. #include <mach/subsystem_restart.h>
  27. #include <mach/subsystem_notif.h>
  28. #include <mach/scm.h>
  29. #include <mach/ramdump.h>
  30. #include <mach/msm_smem.h>
  31. #include <mach/msm_bus_board.h>
  32. #include "peripheral-loader.h"
  33. #include "pil-q6v5.h"
  34. #include "scm-pas.h"
  35. #include "sysmon.h"
  36. #define QDSP6SS_RST_EVB 0x010
  37. #define PROXY_TIMEOUT_MS 10000
  38. static struct kobject *lpass_status;
  39. static char status[32];
  40. struct lpass_data {
  41. struct q6v5_data *q6;
  42. struct subsys_device *subsys;
  43. struct subsys_desc subsys_desc;
  44. void *ramdump_dev;
  45. struct work_struct work;
  46. void *wcnss_notif_hdle;
  47. void *modem_notif_hdle;
  48. int crash_shutdown;
  49. };
  50. #define subsys_to_drv(d) container_of(d, struct lpass_data, subsys_desc)
  51. static int pil_lpass_enable_clks(struct q6v5_data *drv)
  52. {
  53. int ret;
  54. ret = clk_reset(drv->core_clk, CLK_RESET_DEASSERT);
  55. if (ret)
  56. goto err_reset;
  57. ret = clk_prepare_enable(drv->core_clk);
  58. if (ret)
  59. goto err_core_clk;
  60. ret = clk_prepare_enable(drv->ahb_clk);
  61. if (ret)
  62. goto err_ahb_clk;
  63. ret = clk_prepare_enable(drv->axi_clk);
  64. if (ret)
  65. goto err_axi_clk;
  66. ret = clk_prepare_enable(drv->reg_clk);
  67. if (ret)
  68. goto err_reg_clk;
  69. return 0;
  70. err_reg_clk:
  71. clk_disable_unprepare(drv->axi_clk);
  72. err_axi_clk:
  73. clk_disable_unprepare(drv->ahb_clk);
  74. err_ahb_clk:
  75. clk_disable_unprepare(drv->core_clk);
  76. err_core_clk:
  77. clk_reset(drv->core_clk, CLK_RESET_ASSERT);
  78. err_reset:
  79. return ret;
  80. }
  81. static void pil_lpass_disable_clks(struct q6v5_data *drv)
  82. {
  83. clk_disable_unprepare(drv->reg_clk);
  84. clk_disable_unprepare(drv->axi_clk);
  85. clk_disable_unprepare(drv->ahb_clk);
  86. clk_disable_unprepare(drv->core_clk);
  87. clk_reset(drv->core_clk, CLK_RESET_ASSERT);
  88. }
  89. static int pil_lpass_shutdown(struct pil_desc *pil)
  90. {
  91. struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
  92. pil_q6v5_halt_axi_port(pil, drv->axi_halt_base);
  93. /*
  94. * If the shutdown function is called before the reset function, clocks
  95. * will not be enabled yet. Enable them here so that register writes
  96. * performed during the shutdown succeed.
  97. */
  98. if (drv->is_booted == false)
  99. pil_lpass_enable_clks(drv);
  100. pil_q6v5_shutdown(pil);
  101. pil_lpass_disable_clks(drv);
  102. writel_relaxed(1, drv->restart_reg);
  103. drv->is_booted = false;
  104. return 0;
  105. }
  106. static int pil_lpass_reset(struct pil_desc *pil)
  107. {
  108. struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
  109. phys_addr_t start_addr = pil_get_entry_addr(pil);
  110. int ret;
  111. /* Deassert reset to subsystem and wait for propagation */
  112. writel_relaxed(0, drv->restart_reg);
  113. mb();
  114. udelay(2);
  115. ret = pil_lpass_enable_clks(drv);
  116. if (ret)
  117. return ret;
  118. /* Program Image Address */
  119. writel_relaxed((start_addr >> 4) & 0x0FFFFFF0,
  120. drv->reg_base + QDSP6SS_RST_EVB);
  121. ret = pil_q6v5_reset(pil);
  122. if (ret) {
  123. pil_lpass_disable_clks(drv);
  124. return ret;
  125. }
  126. drv->is_booted = true;
  127. return 0;
  128. }
  129. static struct pil_reset_ops pil_lpass_ops = {
  130. .proxy_vote = pil_q6v5_make_proxy_votes,
  131. .proxy_unvote = pil_q6v5_remove_proxy_votes,
  132. .auth_and_reset = pil_lpass_reset,
  133. .shutdown = pil_lpass_shutdown,
  134. };
  135. static int pil_lpass_init_image_trusted(struct pil_desc *pil,
  136. const u8 *metadata, size_t size)
  137. {
  138. return pas_init_image(PAS_Q6, metadata, size);
  139. }
  140. static int pil_lpass_mem_setup_trusted(struct pil_desc *pil, phys_addr_t addr,
  141. size_t size)
  142. {
  143. return pas_mem_setup(PAS_Q6, addr, size);
  144. }
  145. static int pil_lpass_reset_trusted(struct pil_desc *pil)
  146. {
  147. struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
  148. int ret;
  149. ret = clk_prepare_enable(drv->axi_clk);
  150. if (ret)
  151. return ret;
  152. return pas_auth_and_reset(PAS_Q6);
  153. }
  154. static int pil_lpass_shutdown_trusted(struct pil_desc *pil)
  155. {
  156. struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
  157. int ret;
  158. ret = pas_shutdown(PAS_Q6);
  159. if (ret)
  160. return ret;
  161. clk_disable_unprepare(drv->axi_clk);
  162. return 0;
  163. }
  164. static struct pil_reset_ops pil_lpass_ops_trusted = {
  165. .init_image = pil_lpass_init_image_trusted,
  166. .mem_setup = pil_lpass_mem_setup_trusted,
  167. .proxy_vote = pil_q6v5_make_proxy_votes,
  168. .proxy_unvote = pil_q6v5_remove_proxy_votes,
  169. .auth_and_reset = pil_lpass_reset_trusted,
  170. .shutdown = pil_lpass_shutdown_trusted,
  171. };
  172. static int wcnss_notifier_cb(struct notifier_block *this, unsigned long code,
  173. void *ss_handle)
  174. {
  175. int ret;
  176. pr_debug("%s: W-Notify: event %lu\n", __func__, code);
  177. ret = sysmon_send_event(SYSMON_SS_LPASS, "wcnss", code);
  178. if (ret < 0)
  179. pr_err("%s: sysmon_send_event error %d", __func__, ret);
  180. return NOTIFY_DONE;
  181. }
  182. static struct notifier_block wnb = {
  183. .notifier_call = wcnss_notifier_cb,
  184. };
  185. static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
  186. void *ss_handle)
  187. {
  188. int ret;
  189. pr_debug("%s: M-Notify: event %lu\n", __func__, code);
  190. ret = sysmon_send_event(SYSMON_SS_LPASS, "modem", code);
  191. if (ret < 0)
  192. pr_err("%s: sysmon_send_event error %d", __func__, ret);
  193. return NOTIFY_DONE;
  194. }
  195. static struct notifier_block mnb = {
  196. .notifier_call = modem_notifier_cb,
  197. };
  198. static void adsp_log_failure_reason(void)
  199. {
  200. char *reason;
  201. char buffer[81];
  202. unsigned size;
  203. reason = smem_get_entry(SMEM_SSR_REASON_LPASS0, &size);
  204. if (!reason) {
  205. pr_err("ADSP subsystem failure reason: (unknown, smem_get_entry failed).");
  206. return;
  207. }
  208. if (reason[0] == '\0') {
  209. pr_err("ADSP subsystem failure reason: (unknown, init value found)");
  210. return;
  211. }
  212. size = min(size, sizeof(buffer) - 1);
  213. memcpy(buffer, reason, size);
  214. buffer[size] = '\0';
  215. pr_err("ADSP subsystem failure reason: %s", buffer);
  216. memset((void *)reason, 0x0, size);
  217. wmb();
  218. }
  219. static void restart_adsp(struct lpass_data *drv)
  220. {
  221. adsp_log_failure_reason();
  222. subsystem_restart_dev(drv->subsys);
  223. }
  224. static void adsp_fatal_fn(struct work_struct *work)
  225. {
  226. struct lpass_data *drv = container_of(work, struct lpass_data, work);
  227. pr_err("Watchdog bite received from ADSP!\n");
  228. restart_adsp(drv);
  229. }
  230. static irqreturn_t adsp_err_fatal_intr_handler (int irq, void *dev_id)
  231. {
  232. struct lpass_data *drv = subsys_to_drv(dev_id);
  233. /* Ignore if we're the one that set the force stop bit in the outbound
  234. * entry
  235. */
  236. if (drv->crash_shutdown)
  237. return IRQ_HANDLED;
  238. pr_err("Fatal error on the ADSP!\n");
  239. restart_adsp(drv);
  240. return IRQ_HANDLED;
  241. }
  242. #define SCM_Q6_NMI_CMD 0x1
  243. static void send_q6_nmi(void)
  244. {
  245. /* Send NMI to QDSP6 via an SCM call. */
  246. scm_call_atomic1(SCM_SVC_UTIL, SCM_Q6_NMI_CMD, 0x1);
  247. pr_debug("%s: Q6 NMI was sent.\n", __func__);
  248. }
  249. /*
  250. * The "status" file where a static variable is read from and written to.
  251. */
  252. static ssize_t adsp_state_show(struct kobject *kobj,
  253. struct kobj_attribute *attr,
  254. char *buf)
  255. {
  256. return snprintf(buf, sizeof(status), "%s\n", status);
  257. }
  258. static struct kobj_attribute adsp_state_attribute =
  259. __ATTR(status, 0444, adsp_state_show, NULL);
  260. static struct attribute *attrs[] = {
  261. &adsp_state_attribute.attr,
  262. NULL, /* need to NULL terminate the list of attributes */
  263. };
  264. static struct attribute_group attr_group = {
  265. .attrs = attrs,
  266. };
  267. static void adsp_set_state(char *state)
  268. {
  269. strlcpy(status, state, sizeof(status));
  270. sysfs_notify(lpass_status, NULL, "status");
  271. }
  272. #define subsys_to_lpass(d) container_of(d, struct lpass_data, subsys_desc)
  273. static int adsp_shutdown(const struct subsys_desc *subsys)
  274. {
  275. struct lpass_data *drv = subsys_to_lpass(subsys);
  276. send_q6_nmi();
  277. /* The write needs to go through before the q6 is shutdown. */
  278. mb();
  279. pil_shutdown(&drv->q6->desc);
  280. disable_irq_nosync(drv->subsys_desc.wdog_bite_irq);
  281. pr_debug("ADSP is Down\n");
  282. adsp_set_state("OFFLINE");
  283. return 0;
  284. }
  285. static int adsp_powerup(const struct subsys_desc *subsys)
  286. {
  287. struct lpass_data *drv = subsys_to_lpass(subsys);
  288. int ret = 0;
  289. ret = pil_boot(&drv->q6->desc);
  290. enable_irq(drv->subsys_desc.wdog_bite_irq);
  291. pr_debug("ADSP is back online\n");
  292. adsp_set_state("ONLINE");
  293. return ret;
  294. }
  295. static int adsp_ramdump(int enable, const struct subsys_desc *subsys)
  296. {
  297. struct lpass_data *drv = subsys_to_lpass(subsys);
  298. if (!enable)
  299. return 0;
  300. return pil_do_ramdump(&drv->q6->desc, drv->ramdump_dev);
  301. }
  302. static void adsp_crash_shutdown(const struct subsys_desc *subsys)
  303. {
  304. struct lpass_data *drv = subsys_to_lpass(subsys);
  305. drv->crash_shutdown = 1;
  306. gpio_set_value(subsys->force_stop_gpio, 1);
  307. send_q6_nmi();
  308. }
  309. static irqreturn_t adsp_wdog_bite_irq(int irq, void *dev_id)
  310. {
  311. struct lpass_data *drv = subsys_to_drv(dev_id);
  312. schedule_work(&drv->work);
  313. return IRQ_HANDLED;
  314. }
  315. static int lpass_start(const struct subsys_desc *desc)
  316. {
  317. struct lpass_data *drv = subsys_to_drv(desc);
  318. return pil_boot(&drv->q6->desc);
  319. }
  320. static void lpass_stop(const struct subsys_desc *desc)
  321. {
  322. struct lpass_data *drv = subsys_to_drv(desc);
  323. pil_shutdown(&drv->q6->desc);
  324. }
  325. static int __devinit pil_lpass_driver_probe(struct platform_device *pdev)
  326. {
  327. struct lpass_data *drv;
  328. struct q6v5_data *q6;
  329. struct pil_desc *desc;
  330. struct resource *res;
  331. int ret;
  332. drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
  333. if (!drv)
  334. return -ENOMEM;
  335. platform_set_drvdata(pdev, drv);
  336. q6 = pil_q6v5_init(pdev);
  337. if (IS_ERR(q6))
  338. return PTR_ERR(q6);
  339. drv->q6 = q6;
  340. desc = &q6->desc;
  341. desc->owner = THIS_MODULE;
  342. desc->proxy_timeout = PROXY_TIMEOUT_MS;
  343. res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "restart_reg");
  344. q6->restart_reg = devm_request_and_ioremap(&pdev->dev, res);
  345. if (!q6->restart_reg)
  346. return -ENOMEM;
  347. q6->core_clk = devm_clk_get(&pdev->dev, "core_clk");
  348. if (IS_ERR(q6->core_clk))
  349. return PTR_ERR(q6->core_clk);
  350. q6->ahb_clk = devm_clk_get(&pdev->dev, "iface_clk");
  351. if (IS_ERR(q6->ahb_clk))
  352. return PTR_ERR(q6->ahb_clk);
  353. q6->axi_clk = devm_clk_get(&pdev->dev, "bus_clk");
  354. if (IS_ERR(q6->axi_clk))
  355. return PTR_ERR(q6->axi_clk);
  356. q6->reg_clk = devm_clk_get(&pdev->dev, "reg_clk");
  357. if (IS_ERR(q6->reg_clk))
  358. return PTR_ERR(q6->reg_clk);
  359. if (pas_supported(PAS_Q6) > 0) {
  360. desc->ops = &pil_lpass_ops_trusted;
  361. dev_info(&pdev->dev, "using secure boot\n");
  362. } else {
  363. desc->ops = &pil_lpass_ops;
  364. dev_info(&pdev->dev, "using non-secure boot\n");
  365. }
  366. scm_pas_init(MSM_BUS_MASTER_CRYPTO_CORE0);
  367. ret = pil_desc_init(desc);
  368. if (ret)
  369. return ret;
  370. drv->subsys_desc.name = desc->name;
  371. drv->subsys_desc.owner = THIS_MODULE;
  372. drv->subsys_desc.dev = &pdev->dev;
  373. drv->subsys_desc.shutdown = adsp_shutdown;
  374. drv->subsys_desc.powerup = adsp_powerup;
  375. drv->subsys_desc.ramdump = adsp_ramdump;
  376. drv->subsys_desc.crash_shutdown = adsp_crash_shutdown;
  377. drv->subsys_desc.start = lpass_start;
  378. drv->subsys_desc.stop = lpass_stop;
  379. drv->subsys_desc.err_fatal_handler = adsp_err_fatal_intr_handler;
  380. drv->subsys_desc.wdog_bite_handler = adsp_wdog_bite_irq;
  381. INIT_WORK(&drv->work, adsp_fatal_fn);
  382. drv->ramdump_dev = create_ramdump_device("adsp", &pdev->dev);
  383. if (!drv->ramdump_dev) {
  384. ret = -ENOMEM;
  385. goto err_ramdump;
  386. }
  387. drv->subsys = subsys_register(&drv->subsys_desc);
  388. if (IS_ERR(drv->subsys)) {
  389. ret = PTR_ERR(drv->subsys);
  390. goto err_subsys;
  391. }
  392. drv->wcnss_notif_hdle = subsys_notif_register_notifier("wcnss", &wnb);
  393. if (IS_ERR(drv->wcnss_notif_hdle)) {
  394. ret = PTR_ERR(drv->wcnss_notif_hdle);
  395. goto err_notif_wcnss;
  396. }
  397. drv->modem_notif_hdle = subsys_notif_register_notifier("modem", &mnb);
  398. if (IS_ERR(drv->modem_notif_hdle)) {
  399. ret = PTR_ERR(drv->modem_notif_hdle);
  400. goto err_notif_modem;
  401. }
  402. lpass_status = kobject_create_and_add("audio_voice_service",
  403. kernel_kobj);
  404. if (!lpass_status) {
  405. pr_err("%s: kobject create failed\n", __func__);
  406. ret = -ENOMEM;
  407. goto err_create_kobj;
  408. }
  409. ret = sysfs_create_group(lpass_status, &attr_group);
  410. if (ret) {
  411. pr_err("%s: sysfs create group failed\n", __func__);
  412. goto err_kobj;
  413. }
  414. adsp_set_state("ONLINE");
  415. return 0;
  416. err_kobj:
  417. kobject_put(lpass_status);
  418. err_create_kobj:
  419. subsys_notif_unregister_notifier(drv->modem_notif_hdle, &mnb);
  420. err_notif_modem:
  421. subsys_notif_unregister_notifier(drv->wcnss_notif_hdle, &wnb);
  422. err_notif_wcnss:
  423. subsys_unregister(drv->subsys);
  424. err_subsys:
  425. destroy_ramdump_device(drv->ramdump_dev);
  426. err_ramdump:
  427. pil_desc_release(desc);
  428. return ret;
  429. }
  430. static int __devexit pil_lpass_driver_exit(struct platform_device *pdev)
  431. {
  432. struct lpass_data *drv = platform_get_drvdata(pdev);
  433. subsys_notif_unregister_notifier(drv->wcnss_notif_hdle, &wnb);
  434. subsys_notif_unregister_notifier(drv->modem_notif_hdle, &mnb);
  435. subsys_unregister(drv->subsys);
  436. destroy_ramdump_device(drv->ramdump_dev);
  437. pil_desc_release(&drv->q6->desc);
  438. sysfs_remove_group(lpass_status, &attr_group);
  439. kobject_del(lpass_status);
  440. return 0;
  441. }
  442. static struct of_device_id lpass_match_table[] = {
  443. { .compatible = "qcom,pil-q6v5-lpass" },
  444. {}
  445. };
  446. static struct platform_driver pil_lpass_driver = {
  447. .probe = pil_lpass_driver_probe,
  448. .remove = __devexit_p(pil_lpass_driver_exit),
  449. .driver = {
  450. .name = "pil-q6v5-lpass",
  451. .of_match_table = lpass_match_table,
  452. .owner = THIS_MODULE,
  453. },
  454. };
  455. static int __init pil_lpass_init(void)
  456. {
  457. return platform_driver_register(&pil_lpass_driver);
  458. }
  459. module_init(pil_lpass_init);
  460. static void __exit pil_lpass_exit(void)
  461. {
  462. platform_driver_unregister(&pil_lpass_driver);
  463. }
  464. module_exit(pil_lpass_exit);
  465. MODULE_DESCRIPTION("Support for booting low-power audio subsystems with QDSP6v5 (Hexagon) processors");
  466. MODULE_LICENSE("GPL v2");