qcom_common.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Qualcomm Peripheral Image Loader helpers
  4. *
  5. * Copyright (C) 2016 Linaro Ltd
  6. * Copyright (C) 2015 Sony Mobile Communications Inc
  7. * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
  8. */
  9. #include <linux/firmware.h>
  10. #include <linux/kernel.h>
  11. #include <linux/module.h>
  12. #include <linux/notifier.h>
  13. #include <linux/remoteproc.h>
  14. #include <linux/remoteproc/qcom_rproc.h>
  15. #include <linux/auxiliary_bus.h>
  16. #include <linux/rpmsg/qcom_glink.h>
  17. #include <linux/rpmsg/qcom_smd.h>
  18. #include <linux/slab.h>
  19. #include <linux/soc/qcom/mdt_loader.h>
  20. #include <linux/soc/qcom/smem.h>
  21. #include "remoteproc_internal.h"
  22. #include "qcom_common.h"
  23. #define to_glink_subdev(d) container_of(d, struct qcom_rproc_glink, subdev)
  24. #define to_smd_subdev(d) container_of(d, struct qcom_rproc_subdev, subdev)
  25. #define to_ssr_subdev(d) container_of(d, struct qcom_rproc_ssr, subdev)
  26. #define to_pdm_subdev(d) container_of(d, struct qcom_rproc_pdm, subdev)
  27. #define MAX_NUM_OF_SS 10
  28. #define MAX_REGION_NAME_LENGTH 16
  29. #define SBL_MINIDUMP_SMEM_ID 602
  30. #define MINIDUMP_REGION_VALID ('V' << 24 | 'A' << 16 | 'L' << 8 | 'I' << 0)
  31. #define MINIDUMP_SS_ENCR_DONE ('D' << 24 | 'O' << 16 | 'N' << 8 | 'E' << 0)
  32. #define MINIDUMP_SS_ENABLED ('E' << 24 | 'N' << 16 | 'B' << 8 | 'L' << 0)
  33. /**
  34. * struct minidump_region - Minidump region
  35. * @name : Name of the region to be dumped
  36. * @seq_num: : Use to differentiate regions with same name.
  37. * @valid : This entry to be dumped (if set to 1)
  38. * @address : Physical address of region to be dumped
  39. * @size : Size of the region
  40. */
  41. struct minidump_region {
  42. char name[MAX_REGION_NAME_LENGTH];
  43. __le32 seq_num;
  44. __le32 valid;
  45. __le64 address;
  46. __le64 size;
  47. };
  48. /**
  49. * struct minidump_subsystem - Subsystem's SMEM Table of content
  50. * @status : Subsystem toc init status
  51. * @enabled : if set to 1, this region would be copied during coredump
  52. * @encryption_status: Encryption status for this subsystem
  53. * @encryption_required : Decides to encrypt the subsystem regions or not
  54. * @region_count : Number of regions added in this subsystem toc
  55. * @regions_baseptr : regions base pointer of the subsystem
  56. */
  57. struct minidump_subsystem {
  58. __le32 status;
  59. __le32 enabled;
  60. __le32 encryption_status;
  61. __le32 encryption_required;
  62. __le32 region_count;
  63. __le64 regions_baseptr;
  64. };
  65. /**
  66. * struct minidump_global_toc - Global Table of Content
  67. * @status : Global Minidump init status
  68. * @md_revision : Minidump revision
  69. * @enabled : Minidump enable status
  70. * @subsystems : Array of subsystems toc
  71. */
  72. struct minidump_global_toc {
  73. __le32 status;
  74. __le32 md_revision;
  75. __le32 enabled;
  76. struct minidump_subsystem subsystems[MAX_NUM_OF_SS];
  77. };
  78. struct qcom_ssr_subsystem {
  79. const char *name;
  80. struct srcu_notifier_head notifier_list;
  81. struct list_head list;
  82. };
  83. static LIST_HEAD(qcom_ssr_subsystem_list);
  84. static DEFINE_MUTEX(qcom_ssr_subsys_lock);
  85. static void qcom_minidump_cleanup(struct rproc *rproc)
  86. {
  87. struct rproc_dump_segment *entry, *tmp;
  88. list_for_each_entry_safe(entry, tmp, &rproc->dump_segments, node) {
  89. list_del(&entry->node);
  90. kfree(entry->priv);
  91. kfree(entry);
  92. }
  93. }
  94. static int qcom_add_minidump_segments(struct rproc *rproc, struct minidump_subsystem *subsystem,
  95. void (*rproc_dumpfn_t)(struct rproc *rproc, struct rproc_dump_segment *segment,
  96. void *dest, size_t offset, size_t size))
  97. {
  98. struct minidump_region __iomem *ptr;
  99. struct minidump_region region;
  100. int seg_cnt, i;
  101. dma_addr_t da;
  102. size_t size;
  103. char *name;
  104. if (WARN_ON(!list_empty(&rproc->dump_segments))) {
  105. dev_err(&rproc->dev, "dump segment list already populated\n");
  106. return -EUCLEAN;
  107. }
  108. seg_cnt = le32_to_cpu(subsystem->region_count);
  109. ptr = ioremap((unsigned long)le64_to_cpu(subsystem->regions_baseptr),
  110. seg_cnt * sizeof(struct minidump_region));
  111. if (!ptr)
  112. return -EFAULT;
  113. for (i = 0; i < seg_cnt; i++) {
  114. memcpy_fromio(&region, ptr + i, sizeof(region));
  115. if (le32_to_cpu(region.valid) == MINIDUMP_REGION_VALID) {
  116. name = kstrndup(region.name, MAX_REGION_NAME_LENGTH - 1, GFP_KERNEL);
  117. if (!name) {
  118. iounmap(ptr);
  119. return -ENOMEM;
  120. }
  121. da = le64_to_cpu(region.address);
  122. size = le64_to_cpu(region.size);
  123. rproc_coredump_add_custom_segment(rproc, da, size, rproc_dumpfn_t, name);
  124. }
  125. }
  126. iounmap(ptr);
  127. return 0;
  128. }
  129. void qcom_minidump(struct rproc *rproc, unsigned int minidump_id,
  130. void (*rproc_dumpfn_t)(struct rproc *rproc,
  131. struct rproc_dump_segment *segment, void *dest, size_t offset,
  132. size_t size))
  133. {
  134. int ret;
  135. struct minidump_subsystem *subsystem;
  136. struct minidump_global_toc *toc;
  137. /* Get Global minidump ToC*/
  138. toc = qcom_smem_get(QCOM_SMEM_HOST_ANY, SBL_MINIDUMP_SMEM_ID, NULL);
  139. /* check if global table pointer exists and init is set */
  140. if (IS_ERR(toc) || !toc->status) {
  141. dev_err(&rproc->dev, "Minidump TOC not found in SMEM\n");
  142. return;
  143. }
  144. /* Get subsystem table of contents using the minidump id */
  145. subsystem = &toc->subsystems[minidump_id];
  146. /**
  147. * Collect minidump if SS ToC is valid and segment table
  148. * is initialized in memory and encryption status is set.
  149. */
  150. if (subsystem->regions_baseptr == 0 ||
  151. le32_to_cpu(subsystem->status) != 1 ||
  152. le32_to_cpu(subsystem->enabled) != MINIDUMP_SS_ENABLED) {
  153. return rproc_coredump(rproc);
  154. }
  155. if (le32_to_cpu(subsystem->encryption_status) != MINIDUMP_SS_ENCR_DONE) {
  156. dev_err(&rproc->dev, "Minidump not ready, skipping\n");
  157. return;
  158. }
  159. /**
  160. * Clear out the dump segments populated by parse_fw before
  161. * re-populating them with minidump segments.
  162. */
  163. rproc_coredump_cleanup(rproc);
  164. ret = qcom_add_minidump_segments(rproc, subsystem, rproc_dumpfn_t);
  165. if (ret) {
  166. dev_err(&rproc->dev, "Failed with error: %d while adding minidump entries\n", ret);
  167. goto clean_minidump;
  168. }
  169. rproc_coredump_using_sections(rproc);
  170. clean_minidump:
  171. qcom_minidump_cleanup(rproc);
  172. }
  173. EXPORT_SYMBOL_GPL(qcom_minidump);
  174. static int glink_subdev_start(struct rproc_subdev *subdev)
  175. {
  176. struct qcom_rproc_glink *glink = to_glink_subdev(subdev);
  177. glink->edge = qcom_glink_smem_register(glink->dev, glink->node);
  178. return PTR_ERR_OR_ZERO(glink->edge);
  179. }
  180. static void glink_subdev_stop(struct rproc_subdev *subdev, bool crashed)
  181. {
  182. struct qcom_rproc_glink *glink = to_glink_subdev(subdev);
  183. qcom_glink_smem_unregister(glink->edge);
  184. glink->edge = NULL;
  185. }
  186. static void glink_subdev_unprepare(struct rproc_subdev *subdev)
  187. {
  188. struct qcom_rproc_glink *glink = to_glink_subdev(subdev);
  189. qcom_glink_ssr_notify(glink->ssr_name);
  190. }
  191. /**
  192. * qcom_add_glink_subdev() - try to add a GLINK subdevice to rproc
  193. * @rproc: rproc handle to parent the subdevice
  194. * @glink: reference to a GLINK subdev context
  195. * @ssr_name: identifier of the associated remoteproc for ssr notifications
  196. */
  197. void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink,
  198. const char *ssr_name)
  199. {
  200. struct device *dev = &rproc->dev;
  201. glink->node = of_get_child_by_name(dev->parent->of_node, "glink-edge");
  202. if (!glink->node)
  203. return;
  204. glink->ssr_name = kstrdup_const(ssr_name, GFP_KERNEL);
  205. if (!glink->ssr_name)
  206. return;
  207. glink->dev = dev;
  208. glink->subdev.start = glink_subdev_start;
  209. glink->subdev.stop = glink_subdev_stop;
  210. glink->subdev.unprepare = glink_subdev_unprepare;
  211. rproc_add_subdev(rproc, &glink->subdev);
  212. }
  213. EXPORT_SYMBOL_GPL(qcom_add_glink_subdev);
  214. /**
  215. * qcom_remove_glink_subdev() - remove a GLINK subdevice from rproc
  216. * @rproc: rproc handle
  217. * @glink: reference to a GLINK subdev context
  218. */
  219. void qcom_remove_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink)
  220. {
  221. if (!glink->node)
  222. return;
  223. rproc_remove_subdev(rproc, &glink->subdev);
  224. kfree_const(glink->ssr_name);
  225. of_node_put(glink->node);
  226. }
  227. EXPORT_SYMBOL_GPL(qcom_remove_glink_subdev);
  228. /**
  229. * qcom_register_dump_segments() - register segments for coredump
  230. * @rproc: remoteproc handle
  231. * @fw: firmware header
  232. *
  233. * Register all segments of the ELF in the remoteproc coredump segment list
  234. *
  235. * Return: 0 on success, negative errno on failure.
  236. */
  237. int qcom_register_dump_segments(struct rproc *rproc,
  238. const struct firmware *fw)
  239. {
  240. const struct elf32_phdr *phdrs;
  241. const struct elf32_phdr *phdr;
  242. const struct elf32_hdr *ehdr;
  243. int ret;
  244. int i;
  245. ehdr = (struct elf32_hdr *)fw->data;
  246. phdrs = (struct elf32_phdr *)(ehdr + 1);
  247. for (i = 0; i < ehdr->e_phnum; i++) {
  248. phdr = &phdrs[i];
  249. if (phdr->p_type != PT_LOAD)
  250. continue;
  251. if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH)
  252. continue;
  253. if (!phdr->p_memsz)
  254. continue;
  255. ret = rproc_coredump_add_segment(rproc, phdr->p_paddr,
  256. phdr->p_memsz);
  257. if (ret)
  258. return ret;
  259. }
  260. return 0;
  261. }
  262. EXPORT_SYMBOL_GPL(qcom_register_dump_segments);
  263. static int smd_subdev_start(struct rproc_subdev *subdev)
  264. {
  265. struct qcom_rproc_subdev *smd = to_smd_subdev(subdev);
  266. smd->edge = qcom_smd_register_edge(smd->dev, smd->node);
  267. return PTR_ERR_OR_ZERO(smd->edge);
  268. }
  269. static void smd_subdev_stop(struct rproc_subdev *subdev, bool crashed)
  270. {
  271. struct qcom_rproc_subdev *smd = to_smd_subdev(subdev);
  272. qcom_smd_unregister_edge(smd->edge);
  273. smd->edge = NULL;
  274. }
  275. /**
  276. * qcom_add_smd_subdev() - try to add a SMD subdevice to rproc
  277. * @rproc: rproc handle to parent the subdevice
  278. * @smd: reference to a Qualcomm subdev context
  279. */
  280. void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd)
  281. {
  282. struct device *dev = &rproc->dev;
  283. smd->node = of_get_child_by_name(dev->parent->of_node, "smd-edge");
  284. if (!smd->node)
  285. return;
  286. smd->dev = dev;
  287. smd->subdev.start = smd_subdev_start;
  288. smd->subdev.stop = smd_subdev_stop;
  289. rproc_add_subdev(rproc, &smd->subdev);
  290. }
  291. EXPORT_SYMBOL_GPL(qcom_add_smd_subdev);
  292. /**
  293. * qcom_remove_smd_subdev() - remove the smd subdevice from rproc
  294. * @rproc: rproc handle
  295. * @smd: the SMD subdevice to remove
  296. */
  297. void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd)
  298. {
  299. if (!smd->node)
  300. return;
  301. rproc_remove_subdev(rproc, &smd->subdev);
  302. of_node_put(smd->node);
  303. }
  304. EXPORT_SYMBOL_GPL(qcom_remove_smd_subdev);
  305. static struct qcom_ssr_subsystem *qcom_ssr_get_subsys(const char *name)
  306. {
  307. struct qcom_ssr_subsystem *info;
  308. mutex_lock(&qcom_ssr_subsys_lock);
  309. /* Match in the global qcom_ssr_subsystem_list with name */
  310. list_for_each_entry(info, &qcom_ssr_subsystem_list, list)
  311. if (!strcmp(info->name, name))
  312. goto out;
  313. info = kzalloc_obj(*info);
  314. if (!info) {
  315. info = ERR_PTR(-ENOMEM);
  316. goto out;
  317. }
  318. info->name = kstrdup_const(name, GFP_KERNEL);
  319. srcu_init_notifier_head(&info->notifier_list);
  320. /* Add to global notification list */
  321. list_add_tail(&info->list, &qcom_ssr_subsystem_list);
  322. out:
  323. mutex_unlock(&qcom_ssr_subsys_lock);
  324. return info;
  325. }
  326. /**
  327. * qcom_register_ssr_notifier() - register SSR notification handler
  328. * @name: Subsystem's SSR name
  329. * @nb: notifier_block to be invoked upon subsystem's state change
  330. *
  331. * This registers the @nb notifier block as part the notifier chain for a
  332. * remoteproc associated with @name. The notifier block's callback
  333. * will be invoked when the remote processor's SSR events occur
  334. * (pre/post startup and pre/post shutdown).
  335. *
  336. * Return: a subsystem cookie on success, ERR_PTR on failure.
  337. */
  338. void *qcom_register_ssr_notifier(const char *name, struct notifier_block *nb)
  339. {
  340. struct qcom_ssr_subsystem *info;
  341. info = qcom_ssr_get_subsys(name);
  342. if (IS_ERR(info))
  343. return info;
  344. srcu_notifier_chain_register(&info->notifier_list, nb);
  345. return &info->notifier_list;
  346. }
  347. EXPORT_SYMBOL_GPL(qcom_register_ssr_notifier);
  348. /**
  349. * qcom_unregister_ssr_notifier() - unregister SSR notification handler
  350. * @notify: subsystem cookie returned from qcom_register_ssr_notifier
  351. * @nb: notifier_block to unregister
  352. *
  353. * This function will unregister the notifier from the particular notifier
  354. * chain.
  355. *
  356. * Return: 0 on success, %ENOENT otherwise.
  357. */
  358. int qcom_unregister_ssr_notifier(void *notify, struct notifier_block *nb)
  359. {
  360. return srcu_notifier_chain_unregister(notify, nb);
  361. }
  362. EXPORT_SYMBOL_GPL(qcom_unregister_ssr_notifier);
  363. static int ssr_notify_prepare(struct rproc_subdev *subdev)
  364. {
  365. struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
  366. struct qcom_ssr_notify_data data = {
  367. .name = ssr->info->name,
  368. .crashed = false,
  369. };
  370. srcu_notifier_call_chain(&ssr->info->notifier_list,
  371. QCOM_SSR_BEFORE_POWERUP, &data);
  372. return 0;
  373. }
  374. static int ssr_notify_start(struct rproc_subdev *subdev)
  375. {
  376. struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
  377. struct qcom_ssr_notify_data data = {
  378. .name = ssr->info->name,
  379. .crashed = false,
  380. };
  381. srcu_notifier_call_chain(&ssr->info->notifier_list,
  382. QCOM_SSR_AFTER_POWERUP, &data);
  383. return 0;
  384. }
  385. static void ssr_notify_stop(struct rproc_subdev *subdev, bool crashed)
  386. {
  387. struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
  388. struct qcom_ssr_notify_data data = {
  389. .name = ssr->info->name,
  390. .crashed = crashed,
  391. };
  392. srcu_notifier_call_chain(&ssr->info->notifier_list,
  393. QCOM_SSR_BEFORE_SHUTDOWN, &data);
  394. }
  395. static void ssr_notify_unprepare(struct rproc_subdev *subdev)
  396. {
  397. struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
  398. struct qcom_ssr_notify_data data = {
  399. .name = ssr->info->name,
  400. .crashed = false,
  401. };
  402. srcu_notifier_call_chain(&ssr->info->notifier_list,
  403. QCOM_SSR_AFTER_SHUTDOWN, &data);
  404. }
  405. /**
  406. * qcom_add_ssr_subdev() - register subdevice as restart notification source
  407. * @rproc: rproc handle
  408. * @ssr: SSR subdevice handle
  409. * @ssr_name: identifier to use for notifications originating from @rproc
  410. *
  411. * As the @ssr is registered with the @rproc SSR events will be sent to all
  412. * registered listeners for the remoteproc when it's SSR events occur
  413. * (pre/post startup and pre/post shutdown).
  414. */
  415. void qcom_add_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr,
  416. const char *ssr_name)
  417. {
  418. struct qcom_ssr_subsystem *info;
  419. info = qcom_ssr_get_subsys(ssr_name);
  420. if (IS_ERR(info)) {
  421. dev_err(&rproc->dev, "Failed to add ssr subdevice\n");
  422. return;
  423. }
  424. ssr->info = info;
  425. ssr->subdev.prepare = ssr_notify_prepare;
  426. ssr->subdev.start = ssr_notify_start;
  427. ssr->subdev.stop = ssr_notify_stop;
  428. ssr->subdev.unprepare = ssr_notify_unprepare;
  429. rproc_add_subdev(rproc, &ssr->subdev);
  430. }
  431. EXPORT_SYMBOL_GPL(qcom_add_ssr_subdev);
  432. /**
  433. * qcom_remove_ssr_subdev() - remove subdevice as restart notification source
  434. * @rproc: rproc handle
  435. * @ssr: SSR subdevice handle
  436. */
  437. void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr)
  438. {
  439. rproc_remove_subdev(rproc, &ssr->subdev);
  440. ssr->info = NULL;
  441. }
  442. EXPORT_SYMBOL_GPL(qcom_remove_ssr_subdev);
  443. static void pdm_dev_release(struct device *dev)
  444. {
  445. struct auxiliary_device *adev = to_auxiliary_dev(dev);
  446. kfree(adev);
  447. }
  448. static int pdm_notify_prepare(struct rproc_subdev *subdev)
  449. {
  450. struct qcom_rproc_pdm *pdm = to_pdm_subdev(subdev);
  451. struct auxiliary_device *adev;
  452. int ret;
  453. adev = kzalloc_obj(*adev);
  454. if (!adev)
  455. return -ENOMEM;
  456. adev->dev.parent = pdm->dev;
  457. adev->dev.release = pdm_dev_release;
  458. adev->name = "pd-mapper";
  459. adev->id = pdm->index;
  460. ret = auxiliary_device_init(adev);
  461. if (ret) {
  462. kfree(adev);
  463. return ret;
  464. }
  465. ret = auxiliary_device_add(adev);
  466. if (ret) {
  467. auxiliary_device_uninit(adev);
  468. return ret;
  469. }
  470. pdm->adev = adev;
  471. return 0;
  472. }
  473. static void pdm_notify_unprepare(struct rproc_subdev *subdev)
  474. {
  475. struct qcom_rproc_pdm *pdm = to_pdm_subdev(subdev);
  476. if (!pdm->adev)
  477. return;
  478. auxiliary_device_delete(pdm->adev);
  479. auxiliary_device_uninit(pdm->adev);
  480. pdm->adev = NULL;
  481. }
  482. /**
  483. * qcom_add_pdm_subdev() - register PD Mapper subdevice
  484. * @rproc: rproc handle
  485. * @pdm: PDM subdevice handle
  486. *
  487. * Register @pdm so that Protection Device mapper service is started when the
  488. * DSP is started too.
  489. */
  490. void qcom_add_pdm_subdev(struct rproc *rproc, struct qcom_rproc_pdm *pdm)
  491. {
  492. pdm->dev = &rproc->dev;
  493. pdm->index = rproc->index;
  494. pdm->subdev.prepare = pdm_notify_prepare;
  495. pdm->subdev.unprepare = pdm_notify_unprepare;
  496. rproc_add_subdev(rproc, &pdm->subdev);
  497. }
  498. EXPORT_SYMBOL_GPL(qcom_add_pdm_subdev);
  499. /**
  500. * qcom_remove_pdm_subdev() - remove PD Mapper subdevice
  501. * @rproc: rproc handle
  502. * @pdm: PDM subdevice handle
  503. *
  504. * Remove the PD Mapper subdevice.
  505. */
  506. void qcom_remove_pdm_subdev(struct rproc *rproc, struct qcom_rproc_pdm *pdm)
  507. {
  508. rproc_remove_subdev(rproc, &pdm->subdev);
  509. }
  510. EXPORT_SYMBOL_GPL(qcom_remove_pdm_subdev);
  511. MODULE_DESCRIPTION("Qualcomm Remoteproc helper driver");
  512. MODULE_LICENSE("GPL v2");