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