Commit | Line | Data |
---|---|---|
1802d0be | 1 | // SPDX-License-Identifier: GPL-2.0-only |
bde440ee BA |
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. | |
bde440ee BA |
8 | */ |
9 | ||
10 | #include <linux/firmware.h> | |
11 | #include <linux/kernel.h> | |
12 | #include <linux/module.h> | |
1e140df0 | 13 | #include <linux/notifier.h> |
bde440ee | 14 | #include <linux/remoteproc.h> |
eea07023 | 15 | #include <linux/rpmsg/qcom_glink.h> |
b90fcfcb | 16 | #include <linux/rpmsg/qcom_smd.h> |
dcb57ed4 | 17 | #include <linux/soc/qcom/mdt_loader.h> |
bde440ee BA |
18 | |
19 | #include "remoteproc_internal.h" | |
20 | #include "qcom_common.h" | |
21 | ||
eea07023 | 22 | #define to_glink_subdev(d) container_of(d, struct qcom_rproc_glink, subdev) |
b90fcfcb | 23 | #define to_smd_subdev(d) container_of(d, struct qcom_rproc_subdev, subdev) |
1e140df0 BA |
24 | #define to_ssr_subdev(d) container_of(d, struct qcom_rproc_ssr, subdev) |
25 | ||
4fec0a5a | 26 | static BLOCKING_NOTIFIER_HEAD(ssr_notifiers); |
b90fcfcb | 27 | |
6f8b0373 | 28 | static int glink_subdev_start(struct rproc_subdev *subdev) |
eea07023 BA |
29 | { |
30 | struct qcom_rproc_glink *glink = to_glink_subdev(subdev); | |
31 | ||
32 | glink->edge = qcom_glink_smem_register(glink->dev, glink->node); | |
33 | ||
a1fcc455 | 34 | return PTR_ERR_OR_ZERO(glink->edge); |
eea07023 BA |
35 | } |
36 | ||
6f8b0373 | 37 | static void glink_subdev_stop(struct rproc_subdev *subdev, bool crashed) |
eea07023 BA |
38 | { |
39 | struct qcom_rproc_glink *glink = to_glink_subdev(subdev); | |
40 | ||
41 | qcom_glink_smem_unregister(glink->edge); | |
42 | glink->edge = NULL; | |
43 | } | |
44 | ||
45 | /** | |
46 | * qcom_add_glink_subdev() - try to add a GLINK subdevice to rproc | |
47 | * @rproc: rproc handle to parent the subdevice | |
48 | * @glink: reference to a GLINK subdev context | |
49 | */ | |
50 | void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink) | |
51 | { | |
52 | struct device *dev = &rproc->dev; | |
53 | ||
54 | glink->node = of_get_child_by_name(dev->parent->of_node, "glink-edge"); | |
55 | if (!glink->node) | |
56 | return; | |
57 | ||
58 | glink->dev = dev; | |
6f8b0373 AE |
59 | glink->subdev.start = glink_subdev_start; |
60 | glink->subdev.stop = glink_subdev_stop; | |
4902676f BA |
61 | |
62 | rproc_add_subdev(rproc, &glink->subdev); | |
eea07023 BA |
63 | } |
64 | EXPORT_SYMBOL_GPL(qcom_add_glink_subdev); | |
65 | ||
66 | /** | |
67 | * qcom_remove_glink_subdev() - remove a GLINK subdevice from rproc | |
68 | * @rproc: rproc handle | |
69 | * @glink: reference to a GLINK subdev context | |
70 | */ | |
71 | void qcom_remove_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink) | |
72 | { | |
730b2ad8 SS |
73 | if (!glink->node) |
74 | return; | |
75 | ||
eea07023 BA |
76 | rproc_remove_subdev(rproc, &glink->subdev); |
77 | of_node_put(glink->node); | |
78 | } | |
79 | EXPORT_SYMBOL_GPL(qcom_remove_glink_subdev); | |
80 | ||
dcb57ed4 SJ |
81 | /** |
82 | * qcom_register_dump_segments() - register segments for coredump | |
83 | * @rproc: remoteproc handle | |
84 | * @fw: firmware header | |
85 | * | |
86 | * Register all segments of the ELF in the remoteproc coredump segment list | |
87 | * | |
88 | * Return: 0 on success, negative errno on failure. | |
89 | */ | |
90 | int qcom_register_dump_segments(struct rproc *rproc, | |
91 | const struct firmware *fw) | |
92 | { | |
93 | const struct elf32_phdr *phdrs; | |
94 | const struct elf32_phdr *phdr; | |
95 | const struct elf32_hdr *ehdr; | |
96 | int ret; | |
97 | int i; | |
98 | ||
99 | ehdr = (struct elf32_hdr *)fw->data; | |
100 | phdrs = (struct elf32_phdr *)(ehdr + 1); | |
101 | ||
102 | for (i = 0; i < ehdr->e_phnum; i++) { | |
103 | phdr = &phdrs[i]; | |
104 | ||
105 | if (phdr->p_type != PT_LOAD) | |
106 | continue; | |
107 | ||
108 | if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH) | |
109 | continue; | |
110 | ||
111 | if (!phdr->p_memsz) | |
112 | continue; | |
113 | ||
114 | ret = rproc_coredump_add_segment(rproc, phdr->p_paddr, | |
115 | phdr->p_memsz); | |
116 | if (ret) | |
117 | return ret; | |
118 | } | |
119 | ||
120 | return 0; | |
121 | } | |
122 | EXPORT_SYMBOL_GPL(qcom_register_dump_segments); | |
123 | ||
6f8b0373 | 124 | static int smd_subdev_start(struct rproc_subdev *subdev) |
b90fcfcb BA |
125 | { |
126 | struct qcom_rproc_subdev *smd = to_smd_subdev(subdev); | |
127 | ||
128 | smd->edge = qcom_smd_register_edge(smd->dev, smd->node); | |
129 | ||
c76929b3 | 130 | return PTR_ERR_OR_ZERO(smd->edge); |
b90fcfcb BA |
131 | } |
132 | ||
6f8b0373 | 133 | static void smd_subdev_stop(struct rproc_subdev *subdev, bool crashed) |
b90fcfcb BA |
134 | { |
135 | struct qcom_rproc_subdev *smd = to_smd_subdev(subdev); | |
136 | ||
137 | qcom_smd_unregister_edge(smd->edge); | |
138 | smd->edge = NULL; | |
139 | } | |
140 | ||
141 | /** | |
142 | * qcom_add_smd_subdev() - try to add a SMD subdevice to rproc | |
143 | * @rproc: rproc handle to parent the subdevice | |
144 | * @smd: reference to a Qualcomm subdev context | |
145 | */ | |
146 | void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd) | |
147 | { | |
148 | struct device *dev = &rproc->dev; | |
149 | ||
150 | smd->node = of_get_child_by_name(dev->parent->of_node, "smd-edge"); | |
151 | if (!smd->node) | |
152 | return; | |
153 | ||
154 | smd->dev = dev; | |
6f8b0373 AE |
155 | smd->subdev.start = smd_subdev_start; |
156 | smd->subdev.stop = smd_subdev_stop; | |
4902676f BA |
157 | |
158 | rproc_add_subdev(rproc, &smd->subdev); | |
b90fcfcb BA |
159 | } |
160 | EXPORT_SYMBOL_GPL(qcom_add_smd_subdev); | |
161 | ||
162 | /** | |
163 | * qcom_remove_smd_subdev() - remove the smd subdevice from rproc | |
164 | * @rproc: rproc handle | |
165 | * @smd: the SMD subdevice to remove | |
166 | */ | |
167 | void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd) | |
168 | { | |
730b2ad8 SS |
169 | if (!smd->node) |
170 | return; | |
171 | ||
b90fcfcb BA |
172 | rproc_remove_subdev(rproc, &smd->subdev); |
173 | of_node_put(smd->node); | |
174 | } | |
175 | EXPORT_SYMBOL_GPL(qcom_remove_smd_subdev); | |
176 | ||
1e140df0 BA |
177 | /** |
178 | * qcom_register_ssr_notifier() - register SSR notification handler | |
179 | * @nb: notifier_block to notify for restart notifications | |
180 | * | |
181 | * Returns 0 on success, negative errno on failure. | |
182 | * | |
183 | * This register the @notify function as handler for restart notifications. As | |
184 | * remote processors are stopped this function will be called, with the SSR | |
185 | * name passed as a parameter. | |
186 | */ | |
187 | int qcom_register_ssr_notifier(struct notifier_block *nb) | |
188 | { | |
189 | return blocking_notifier_chain_register(&ssr_notifiers, nb); | |
190 | } | |
191 | EXPORT_SYMBOL_GPL(qcom_register_ssr_notifier); | |
192 | ||
193 | /** | |
194 | * qcom_unregister_ssr_notifier() - unregister SSR notification handler | |
195 | * @nb: notifier_block to unregister | |
196 | */ | |
197 | void qcom_unregister_ssr_notifier(struct notifier_block *nb) | |
198 | { | |
199 | blocking_notifier_chain_unregister(&ssr_notifiers, nb); | |
200 | } | |
201 | EXPORT_SYMBOL_GPL(qcom_unregister_ssr_notifier); | |
202 | ||
880f5b38 | 203 | static void ssr_notify_stop(struct rproc_subdev *subdev, bool crashed) |
1e140df0 BA |
204 | { |
205 | struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev); | |
206 | ||
207 | blocking_notifier_call_chain(&ssr_notifiers, 0, (void *)ssr->name); | |
208 | } | |
209 | ||
210 | /** | |
211 | * qcom_add_ssr_subdev() - register subdevice as restart notification source | |
212 | * @rproc: rproc handle | |
213 | * @ssr: SSR subdevice handle | |
214 | * @ssr_name: identifier to use for notifications originating from @rproc | |
215 | * | |
216 | * As the @ssr is registered with the @rproc SSR events will be sent to all | |
217 | * registered listeners in the system as the remoteproc is shut down. | |
218 | */ | |
219 | void qcom_add_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr, | |
220 | const char *ssr_name) | |
221 | { | |
222 | ssr->name = ssr_name; | |
4902676f | 223 | ssr->subdev.stop = ssr_notify_stop; |
1e140df0 | 224 | |
4902676f | 225 | rproc_add_subdev(rproc, &ssr->subdev); |
1e140df0 BA |
226 | } |
227 | EXPORT_SYMBOL_GPL(qcom_add_ssr_subdev); | |
228 | ||
229 | /** | |
230 | * qcom_remove_ssr_subdev() - remove subdevice as restart notification source | |
231 | * @rproc: rproc handle | |
232 | * @ssr: SSR subdevice handle | |
233 | */ | |
234 | void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr) | |
235 | { | |
236 | rproc_remove_subdev(rproc, &ssr->subdev); | |
237 | } | |
238 | EXPORT_SYMBOL_GPL(qcom_remove_ssr_subdev); | |
239 | ||
bde440ee BA |
240 | MODULE_DESCRIPTION("Qualcomm Remoteproc helper driver"); |
241 | MODULE_LICENSE("GPL v2"); |