1 // SPDX-License-Identifier: GPL-2.0-only
3 * Qualcomm Peripheral Image Loader helpers
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.
10 #include <linux/firmware.h>
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/notifier.h>
14 #include <linux/remoteproc.h>
15 #include <linux/remoteproc/qcom_rproc.h>
16 #include <linux/auxiliary_bus.h>
17 #include <linux/rpmsg/qcom_glink.h>
18 #include <linux/rpmsg/qcom_smd.h>
19 #include <linux/slab.h>
20 #include <linux/soc/qcom/mdt_loader.h>
21 #include <linux/soc/qcom/smem.h>
23 #include "remoteproc_internal.h"
24 #include "qcom_common.h"
26 #define to_glink_subdev(d) container_of(d, struct qcom_rproc_glink, subdev)
27 #define to_smd_subdev(d) container_of(d, struct qcom_rproc_subdev, subdev)
28 #define to_ssr_subdev(d) container_of(d, struct qcom_rproc_ssr, subdev)
29 #define to_pdm_subdev(d) container_of(d, struct qcom_rproc_pdm, subdev)
31 #define MAX_NUM_OF_SS 10
32 #define MAX_REGION_NAME_LENGTH 16
33 #define SBL_MINIDUMP_SMEM_ID 602
34 #define MINIDUMP_REGION_VALID ('V' << 24 | 'A' << 16 | 'L' << 8 | 'I' << 0)
35 #define MINIDUMP_SS_ENCR_DONE ('D' << 24 | 'O' << 16 | 'N' << 8 | 'E' << 0)
36 #define MINIDUMP_SS_ENABLED ('E' << 24 | 'N' << 16 | 'B' << 8 | 'L' << 0)
39 * struct minidump_region - Minidump region
40 * @name : Name of the region to be dumped
41 * @seq_num: : Use to differentiate regions with same name.
42 * @valid : This entry to be dumped (if set to 1)
43 * @address : Physical address of region to be dumped
44 * @size : Size of the region
46 struct minidump_region
{
47 char name
[MAX_REGION_NAME_LENGTH
];
55 * struct minidump_subsystem - Subsystem's SMEM Table of content
56 * @status : Subsystem toc init status
57 * @enabled : if set to 1, this region would be copied during coredump
58 * @encryption_status: Encryption status for this subsystem
59 * @encryption_required : Decides to encrypt the subsystem regions or not
60 * @region_count : Number of regions added in this subsystem toc
61 * @regions_baseptr : regions base pointer of the subsystem
63 struct minidump_subsystem
{
66 __le32 encryption_status
;
67 __le32 encryption_required
;
69 __le64 regions_baseptr
;
73 * struct minidump_global_toc - Global Table of Content
74 * @status : Global Minidump init status
75 * @md_revision : Minidump revision
76 * @enabled : Minidump enable status
77 * @subsystems : Array of subsystems toc
79 struct minidump_global_toc
{
83 struct minidump_subsystem subsystems
[MAX_NUM_OF_SS
];
86 struct qcom_ssr_subsystem
{
88 struct srcu_notifier_head notifier_list
;
89 struct list_head list
;
92 static LIST_HEAD(qcom_ssr_subsystem_list
);
93 static DEFINE_MUTEX(qcom_ssr_subsys_lock
);
95 static void qcom_minidump_cleanup(struct rproc
*rproc
)
97 struct rproc_dump_segment
*entry
, *tmp
;
99 list_for_each_entry_safe(entry
, tmp
, &rproc
->dump_segments
, node
) {
100 list_del(&entry
->node
);
106 static int qcom_add_minidump_segments(struct rproc
*rproc
, struct minidump_subsystem
*subsystem
,
107 void (*rproc_dumpfn_t
)(struct rproc
*rproc
, struct rproc_dump_segment
*segment
,
108 void *dest
, size_t offset
, size_t size
))
110 struct minidump_region __iomem
*ptr
;
111 struct minidump_region region
;
117 if (WARN_ON(!list_empty(&rproc
->dump_segments
))) {
118 dev_err(&rproc
->dev
, "dump segment list already populated\n");
122 seg_cnt
= le32_to_cpu(subsystem
->region_count
);
123 ptr
= ioremap((unsigned long)le64_to_cpu(subsystem
->regions_baseptr
),
124 seg_cnt
* sizeof(struct minidump_region
));
128 for (i
= 0; i
< seg_cnt
; i
++) {
129 memcpy_fromio(®ion
, ptr
+ i
, sizeof(region
));
130 if (le32_to_cpu(region
.valid
) == MINIDUMP_REGION_VALID
) {
131 name
= kstrndup(region
.name
, MAX_REGION_NAME_LENGTH
- 1, GFP_KERNEL
);
136 da
= le64_to_cpu(region
.address
);
137 size
= le64_to_cpu(region
.size
);
138 rproc_coredump_add_custom_segment(rproc
, da
, size
, rproc_dumpfn_t
, name
);
146 void qcom_minidump(struct rproc
*rproc
, unsigned int minidump_id
,
147 void (*rproc_dumpfn_t
)(struct rproc
*rproc
,
148 struct rproc_dump_segment
*segment
, void *dest
, size_t offset
,
152 struct minidump_subsystem
*subsystem
;
153 struct minidump_global_toc
*toc
;
155 /* Get Global minidump ToC*/
156 toc
= qcom_smem_get(QCOM_SMEM_HOST_ANY
, SBL_MINIDUMP_SMEM_ID
, NULL
);
158 /* check if global table pointer exists and init is set */
159 if (IS_ERR(toc
) || !toc
->status
) {
160 dev_err(&rproc
->dev
, "Minidump TOC not found in SMEM\n");
164 /* Get subsystem table of contents using the minidump id */
165 subsystem
= &toc
->subsystems
[minidump_id
];
168 * Collect minidump if SS ToC is valid and segment table
169 * is initialized in memory and encryption status is set.
171 if (subsystem
->regions_baseptr
== 0 ||
172 le32_to_cpu(subsystem
->status
) != 1 ||
173 le32_to_cpu(subsystem
->enabled
) != MINIDUMP_SS_ENABLED
) {
174 return rproc_coredump(rproc
);
177 if (le32_to_cpu(subsystem
->encryption_status
) != MINIDUMP_SS_ENCR_DONE
) {
178 dev_err(&rproc
->dev
, "Minidump not ready, skipping\n");
183 * Clear out the dump segments populated by parse_fw before
184 * re-populating them with minidump segments.
186 rproc_coredump_cleanup(rproc
);
188 ret
= qcom_add_minidump_segments(rproc
, subsystem
, rproc_dumpfn_t
);
190 dev_err(&rproc
->dev
, "Failed with error: %d while adding minidump entries\n", ret
);
193 rproc_coredump_using_sections(rproc
);
195 qcom_minidump_cleanup(rproc
);
197 EXPORT_SYMBOL_GPL(qcom_minidump
);
199 static int glink_subdev_start(struct rproc_subdev
*subdev
)
201 struct qcom_rproc_glink
*glink
= to_glink_subdev(subdev
);
203 glink
->edge
= qcom_glink_smem_register(glink
->dev
, glink
->node
);
205 return PTR_ERR_OR_ZERO(glink
->edge
);
208 static void glink_subdev_stop(struct rproc_subdev
*subdev
, bool crashed
)
210 struct qcom_rproc_glink
*glink
= to_glink_subdev(subdev
);
212 qcom_glink_smem_unregister(glink
->edge
);
216 static void glink_subdev_unprepare(struct rproc_subdev
*subdev
)
218 struct qcom_rproc_glink
*glink
= to_glink_subdev(subdev
);
220 qcom_glink_ssr_notify(glink
->ssr_name
);
224 * qcom_add_glink_subdev() - try to add a GLINK subdevice to rproc
225 * @rproc: rproc handle to parent the subdevice
226 * @glink: reference to a GLINK subdev context
227 * @ssr_name: identifier of the associated remoteproc for ssr notifications
229 void qcom_add_glink_subdev(struct rproc
*rproc
, struct qcom_rproc_glink
*glink
,
230 const char *ssr_name
)
232 struct device
*dev
= &rproc
->dev
;
234 glink
->node
= of_get_child_by_name(dev
->parent
->of_node
, "glink-edge");
238 glink
->ssr_name
= kstrdup_const(ssr_name
, GFP_KERNEL
);
239 if (!glink
->ssr_name
)
243 glink
->subdev
.start
= glink_subdev_start
;
244 glink
->subdev
.stop
= glink_subdev_stop
;
245 glink
->subdev
.unprepare
= glink_subdev_unprepare
;
247 rproc_add_subdev(rproc
, &glink
->subdev
);
249 EXPORT_SYMBOL_GPL(qcom_add_glink_subdev
);
252 * qcom_remove_glink_subdev() - remove a GLINK subdevice from rproc
253 * @rproc: rproc handle
254 * @glink: reference to a GLINK subdev context
256 void qcom_remove_glink_subdev(struct rproc
*rproc
, struct qcom_rproc_glink
*glink
)
261 rproc_remove_subdev(rproc
, &glink
->subdev
);
262 kfree_const(glink
->ssr_name
);
263 of_node_put(glink
->node
);
265 EXPORT_SYMBOL_GPL(qcom_remove_glink_subdev
);
268 * qcom_register_dump_segments() - register segments for coredump
269 * @rproc: remoteproc handle
270 * @fw: firmware header
272 * Register all segments of the ELF in the remoteproc coredump segment list
274 * Return: 0 on success, negative errno on failure.
276 int qcom_register_dump_segments(struct rproc
*rproc
,
277 const struct firmware
*fw
)
279 const struct elf32_phdr
*phdrs
;
280 const struct elf32_phdr
*phdr
;
281 const struct elf32_hdr
*ehdr
;
285 ehdr
= (struct elf32_hdr
*)fw
->data
;
286 phdrs
= (struct elf32_phdr
*)(ehdr
+ 1);
288 for (i
= 0; i
< ehdr
->e_phnum
; i
++) {
291 if (phdr
->p_type
!= PT_LOAD
)
294 if ((phdr
->p_flags
& QCOM_MDT_TYPE_MASK
) == QCOM_MDT_TYPE_HASH
)
300 ret
= rproc_coredump_add_segment(rproc
, phdr
->p_paddr
,
308 EXPORT_SYMBOL_GPL(qcom_register_dump_segments
);
310 static int smd_subdev_start(struct rproc_subdev
*subdev
)
312 struct qcom_rproc_subdev
*smd
= to_smd_subdev(subdev
);
314 smd
->edge
= qcom_smd_register_edge(smd
->dev
, smd
->node
);
316 return PTR_ERR_OR_ZERO(smd
->edge
);
319 static void smd_subdev_stop(struct rproc_subdev
*subdev
, bool crashed
)
321 struct qcom_rproc_subdev
*smd
= to_smd_subdev(subdev
);
323 qcom_smd_unregister_edge(smd
->edge
);
328 * qcom_add_smd_subdev() - try to add a SMD subdevice to rproc
329 * @rproc: rproc handle to parent the subdevice
330 * @smd: reference to a Qualcomm subdev context
332 void qcom_add_smd_subdev(struct rproc
*rproc
, struct qcom_rproc_subdev
*smd
)
334 struct device
*dev
= &rproc
->dev
;
336 smd
->node
= of_get_child_by_name(dev
->parent
->of_node
, "smd-edge");
341 smd
->subdev
.start
= smd_subdev_start
;
342 smd
->subdev
.stop
= smd_subdev_stop
;
344 rproc_add_subdev(rproc
, &smd
->subdev
);
346 EXPORT_SYMBOL_GPL(qcom_add_smd_subdev
);
349 * qcom_remove_smd_subdev() - remove the smd subdevice from rproc
350 * @rproc: rproc handle
351 * @smd: the SMD subdevice to remove
353 void qcom_remove_smd_subdev(struct rproc
*rproc
, struct qcom_rproc_subdev
*smd
)
358 rproc_remove_subdev(rproc
, &smd
->subdev
);
359 of_node_put(smd
->node
);
361 EXPORT_SYMBOL_GPL(qcom_remove_smd_subdev
);
363 static struct qcom_ssr_subsystem
*qcom_ssr_get_subsys(const char *name
)
365 struct qcom_ssr_subsystem
*info
;
367 mutex_lock(&qcom_ssr_subsys_lock
);
368 /* Match in the global qcom_ssr_subsystem_list with name */
369 list_for_each_entry(info
, &qcom_ssr_subsystem_list
, list
)
370 if (!strcmp(info
->name
, name
))
373 info
= kzalloc(sizeof(*info
), GFP_KERNEL
);
375 info
= ERR_PTR(-ENOMEM
);
378 info
->name
= kstrdup_const(name
, GFP_KERNEL
);
379 srcu_init_notifier_head(&info
->notifier_list
);
381 /* Add to global notification list */
382 list_add_tail(&info
->list
, &qcom_ssr_subsystem_list
);
385 mutex_unlock(&qcom_ssr_subsys_lock
);
390 * qcom_register_ssr_notifier() - register SSR notification handler
391 * @name: Subsystem's SSR name
392 * @nb: notifier_block to be invoked upon subsystem's state change
394 * This registers the @nb notifier block as part the notifier chain for a
395 * remoteproc associated with @name. The notifier block's callback
396 * will be invoked when the remote processor's SSR events occur
397 * (pre/post startup and pre/post shutdown).
399 * Return: a subsystem cookie on success, ERR_PTR on failure.
401 void *qcom_register_ssr_notifier(const char *name
, struct notifier_block
*nb
)
403 struct qcom_ssr_subsystem
*info
;
405 info
= qcom_ssr_get_subsys(name
);
409 srcu_notifier_chain_register(&info
->notifier_list
, nb
);
411 return &info
->notifier_list
;
413 EXPORT_SYMBOL_GPL(qcom_register_ssr_notifier
);
416 * qcom_unregister_ssr_notifier() - unregister SSR notification handler
417 * @notify: subsystem cookie returned from qcom_register_ssr_notifier
418 * @nb: notifier_block to unregister
420 * This function will unregister the notifier from the particular notifier
423 * Return: 0 on success, %ENOENT otherwise.
425 int qcom_unregister_ssr_notifier(void *notify
, struct notifier_block
*nb
)
427 return srcu_notifier_chain_unregister(notify
, nb
);
429 EXPORT_SYMBOL_GPL(qcom_unregister_ssr_notifier
);
431 static int ssr_notify_prepare(struct rproc_subdev
*subdev
)
433 struct qcom_rproc_ssr
*ssr
= to_ssr_subdev(subdev
);
434 struct qcom_ssr_notify_data data
= {
435 .name
= ssr
->info
->name
,
439 srcu_notifier_call_chain(&ssr
->info
->notifier_list
,
440 QCOM_SSR_BEFORE_POWERUP
, &data
);
444 static int ssr_notify_start(struct rproc_subdev
*subdev
)
446 struct qcom_rproc_ssr
*ssr
= to_ssr_subdev(subdev
);
447 struct qcom_ssr_notify_data data
= {
448 .name
= ssr
->info
->name
,
452 srcu_notifier_call_chain(&ssr
->info
->notifier_list
,
453 QCOM_SSR_AFTER_POWERUP
, &data
);
457 static void ssr_notify_stop(struct rproc_subdev
*subdev
, bool crashed
)
459 struct qcom_rproc_ssr
*ssr
= to_ssr_subdev(subdev
);
460 struct qcom_ssr_notify_data data
= {
461 .name
= ssr
->info
->name
,
465 srcu_notifier_call_chain(&ssr
->info
->notifier_list
,
466 QCOM_SSR_BEFORE_SHUTDOWN
, &data
);
469 static void ssr_notify_unprepare(struct rproc_subdev
*subdev
)
471 struct qcom_rproc_ssr
*ssr
= to_ssr_subdev(subdev
);
472 struct qcom_ssr_notify_data data
= {
473 .name
= ssr
->info
->name
,
477 srcu_notifier_call_chain(&ssr
->info
->notifier_list
,
478 QCOM_SSR_AFTER_SHUTDOWN
, &data
);
482 * qcom_add_ssr_subdev() - register subdevice as restart notification source
483 * @rproc: rproc handle
484 * @ssr: SSR subdevice handle
485 * @ssr_name: identifier to use for notifications originating from @rproc
487 * As the @ssr is registered with the @rproc SSR events will be sent to all
488 * registered listeners for the remoteproc when it's SSR events occur
489 * (pre/post startup and pre/post shutdown).
491 void qcom_add_ssr_subdev(struct rproc
*rproc
, struct qcom_rproc_ssr
*ssr
,
492 const char *ssr_name
)
494 struct qcom_ssr_subsystem
*info
;
496 info
= qcom_ssr_get_subsys(ssr_name
);
498 dev_err(&rproc
->dev
, "Failed to add ssr subdevice\n");
503 ssr
->subdev
.prepare
= ssr_notify_prepare
;
504 ssr
->subdev
.start
= ssr_notify_start
;
505 ssr
->subdev
.stop
= ssr_notify_stop
;
506 ssr
->subdev
.unprepare
= ssr_notify_unprepare
;
508 rproc_add_subdev(rproc
, &ssr
->subdev
);
510 EXPORT_SYMBOL_GPL(qcom_add_ssr_subdev
);
513 * qcom_remove_ssr_subdev() - remove subdevice as restart notification source
514 * @rproc: rproc handle
515 * @ssr: SSR subdevice handle
517 void qcom_remove_ssr_subdev(struct rproc
*rproc
, struct qcom_rproc_ssr
*ssr
)
519 rproc_remove_subdev(rproc
, &ssr
->subdev
);
522 EXPORT_SYMBOL_GPL(qcom_remove_ssr_subdev
);
524 static void pdm_dev_release(struct device
*dev
)
526 struct auxiliary_device
*adev
= to_auxiliary_dev(dev
);
531 static int pdm_notify_prepare(struct rproc_subdev
*subdev
)
533 struct qcom_rproc_pdm
*pdm
= to_pdm_subdev(subdev
);
534 struct auxiliary_device
*adev
;
537 adev
= kzalloc(sizeof(*adev
), GFP_KERNEL
);
541 adev
->dev
.parent
= pdm
->dev
;
542 adev
->dev
.release
= pdm_dev_release
;
543 adev
->name
= "pd-mapper";
544 adev
->id
= pdm
->index
;
546 ret
= auxiliary_device_init(adev
);
552 ret
= auxiliary_device_add(adev
);
554 auxiliary_device_uninit(adev
);
564 static void pdm_notify_unprepare(struct rproc_subdev
*subdev
)
566 struct qcom_rproc_pdm
*pdm
= to_pdm_subdev(subdev
);
571 auxiliary_device_delete(pdm
->adev
);
572 auxiliary_device_uninit(pdm
->adev
);
577 * qcom_add_pdm_subdev() - register PD Mapper subdevice
578 * @rproc: rproc handle
579 * @pdm: PDM subdevice handle
581 * Register @pdm so that Protection Device mapper service is started when the
582 * DSP is started too.
584 void qcom_add_pdm_subdev(struct rproc
*rproc
, struct qcom_rproc_pdm
*pdm
)
586 pdm
->dev
= &rproc
->dev
;
587 pdm
->index
= rproc
->index
;
589 pdm
->subdev
.prepare
= pdm_notify_prepare
;
590 pdm
->subdev
.unprepare
= pdm_notify_unprepare
;
592 rproc_add_subdev(rproc
, &pdm
->subdev
);
594 EXPORT_SYMBOL_GPL(qcom_add_pdm_subdev
);
597 * qcom_remove_pdm_subdev() - remove PD Mapper subdevice
598 * @rproc: rproc handle
599 * @pdm: PDM subdevice handle
601 * Remove the PD Mapper subdevice.
603 void qcom_remove_pdm_subdev(struct rproc
*rproc
, struct qcom_rproc_pdm
*pdm
)
605 rproc_remove_subdev(rproc
, &pdm
->subdev
);
607 EXPORT_SYMBOL_GPL(qcom_remove_pdm_subdev
);
609 MODULE_DESCRIPTION("Qualcomm Remoteproc helper driver");
610 MODULE_LICENSE("GPL v2");