1 /*
2 * Copyright (c) 2014, Mentor Graphics Corporation
3 * All rights reserved.
4 * Copyright (c) 2015 Xilinx, Inc. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 * 3. Neither the name of Mentor Graphics Corporation nor the names of its
15 * contributors may be used to endorse or promote products derived from this
16 * software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
31 #include <string.h>
32 #include "openamp/remoteproc.h"
33 #include "openamp/remoteproc_loader.h"
34 #include "openamp/rsc_table_parser.h"
35 #include "openamp/env.h"
36 #include "openamp/hil.h"
37 #include "metal/sys.h"
38 #include "metal/alloc.h"
39 #include "metal/sleep.h"
41 /**
42 * remoteproc_resource_init
43 *
44 * Initializes resources for remoteproc remote configuration. Only
45 * remoteproc remote applications are allowed to call this function.
46 *
47 * @param rsc_info - pointer to resource table info control
48 * block
49 * @param proc - pointer to the hil_proc
50 * @param channel_created - callback function for channel creation
51 * @param channel_destroyed - callback function for channel deletion
52 * @param default_cb - default callback for channel I/O
53 * @param rproc_handle - pointer to new remoteproc instance
54 * @param rpmsg_role - 1 for rpmsg master, or 0 for rpmsg slave
55 *
56 * @param returns - status of function execution
57 *
58 */
59 int remoteproc_resource_init(struct rsc_table_info *rsc_info,
60 struct hil_proc *proc,
61 rpmsg_chnl_cb_t channel_created,
62 rpmsg_chnl_cb_t channel_destroyed,
63 rpmsg_rx_cb_t default_cb,
64 struct remote_proc **rproc_handle,
65 int rpmsg_role)
66 {
68 struct remote_proc *rproc;
69 int status;
70 int remote_rpmsg_role;
72 if (!rsc_info || !proc) {
73 return RPROC_ERR_PARAM;
74 }
76 rproc = metal_allocate_memory(sizeof(struct remote_proc));
77 if (rproc) {
78 memset(rproc, 0x00, sizeof(struct remote_proc));
79 /* There can be only one master for remote configuration so use the
80 * rsvd cpu id for creating hil proc */
81 rproc->proc = proc;
82 status = hil_init_proc(proc);
83 if (!status) {
84 /* Parse resource table */
85 status =
86 handle_rsc_table(rproc, rsc_info->rsc_tab,
87 rsc_info->size);
88 if (status == RPROC_SUCCESS) {
89 /* Initialize RPMSG "messaging" component */
90 *rproc_handle = rproc;
91 remote_rpmsg_role = (rpmsg_role == RPMSG_MASTER?
92 RPMSG_REMOTE : RPMSG_MASTER);
93 status =
94 rpmsg_init(proc,
95 &rproc->rdev, channel_created,
96 channel_destroyed, default_cb,
97 remote_rpmsg_role);
98 } else {
99 status = RPROC_ERR_NO_RSC_TABLE;
100 }
101 } else {
102 status = RPROC_ERR_CPU_INIT;
103 }
104 } else {
105 status = RPROC_ERR_NO_MEM;
106 }
108 /* Cleanup in case of error */
109 if (status != RPROC_SUCCESS) {
110 *rproc_handle = 0;
111 (void)remoteproc_resource_deinit(rproc);
112 return status;
113 }
114 return status;
115 }
117 /**
118 * remoteproc_resource_deinit
119 *
120 * Uninitializes resources for remoteproc "remote" configuration.
121 *
122 * @param rproc - pointer to rproc instance
123 *
124 * @param returns - status of function execution
125 *
126 */
128 int remoteproc_resource_deinit(struct remote_proc *rproc)
129 {
130 if (rproc) {
131 if (rproc->rdev) {
132 rpmsg_deinit(rproc->rdev);
133 }
134 if (rproc->proc) {
135 hil_delete_proc(rproc->proc);
136 rproc->proc = NULL;
137 }
138 metal_free_memory(rproc);
139 }
141 return RPROC_SUCCESS;
142 }
144 /**
145 * remoteproc_init
146 *
147 * Initializes resources for remoteproc master configuration. Only
148 * remoteproc master applications are allowed to call this function.
149 *
150 * @param fw_name - name of frimware
151 * @param proc - pointer to hil_proc
152 * @param channel_created - callback function for channel creation
153 * @param channel_destroyed - callback function for channel deletion
154 * @param default_cb - default callback for channel I/O
155 * @param rproc_handle - pointer to new remoteproc instance
156 *
157 * @param returns - status of function execution
158 *
159 */
160 int remoteproc_init(char *fw_name, struct hil_proc *proc,
161 rpmsg_chnl_cb_t channel_created,
162 rpmsg_chnl_cb_t channel_destroyed, rpmsg_rx_cb_t default_cb,
163 struct remote_proc **rproc_handle)
164 {
166 struct remote_proc *rproc;
167 struct resource_table *rsc_table;
168 unsigned int fw_size, rsc_size;
169 uintptr_t fw_addr;
170 int status;
172 if (!fw_name) {
173 return RPROC_ERR_PARAM;
174 }
176 rproc = metal_allocate_memory(sizeof(struct remote_proc));
177 if (rproc) {
178 memset((void *)rproc, 0x00, sizeof(struct remote_proc));
179 /* Create proc instance */
180 status = hil_init_proc(proc);
181 if (!status) {
182 /* Retrieve firmware attributes */
183 status =
184 hil_get_firmware(fw_name, &fw_addr,
185 &fw_size);
186 if (!status) {
187 /* Initialize ELF loader - currently only ELF format is supported */
188 rproc->loader =
189 remoteproc_loader_init(ELF_LOADER);
190 if (rproc->loader) {
191 /* Attach the given firmware with the ELF parser/loader */
192 status =
193 remoteproc_loader_attach_firmware
194 (rproc->loader,
195 (void *)fw_addr);
196 } else {
197 status = RPROC_ERR_LOADER;
198 }
199 }
200 } else {
201 status = RPROC_ERR_CPU_INIT;
202 }
203 } else {
204 status = RPROC_ERR_NO_MEM;
205 }
207 if (!status) {
208 rproc->role = RPROC_MASTER;
210 /* Get resource table from firmware */
211 rsc_table =
212 remoteproc_loader_retrieve_resource_section(rproc->loader,
213 &rsc_size);
214 if (rsc_table) {
215 /* Parse resource table */
216 status = handle_rsc_table(rproc, rsc_table, rsc_size);
217 } else {
218 status = RPROC_ERR_NO_RSC_TABLE;
219 }
220 }
222 /* Cleanup in case of error */
223 if (status != RPROC_SUCCESS) {
224 (void)remoteproc_deinit(rproc);
225 return status;
226 }
228 rproc->channel_created = channel_created;
229 rproc->channel_destroyed = channel_destroyed;
230 rproc->default_cb = default_cb;
232 *rproc_handle = rproc;
234 return status;
235 }
237 /**
238 * remoteproc_deinit
239 *
240 * Uninitializes resources for remoteproc "master" configuration.
241 *
242 * @param rproc - pointer to remote proc instance
243 *
244 * @param returns - status of function execution
245 *
246 */
247 int remoteproc_deinit(struct remote_proc *rproc)
248 {
250 if (rproc) {
251 if (rproc->loader) {
252 (void)remoteproc_loader_delete(rproc->loader);
253 rproc->loader = RPROC_NULL;
254 }
255 if (rproc->proc) {
256 hil_delete_proc(rproc->proc);
257 rproc->proc = RPROC_NULL;
258 }
259 metal_free_memory(rproc);
260 }
262 return RPROC_SUCCESS;
263 }
265 /**
266 * remoteproc_boot
267 *
268 * This function loads the image on the remote processor and starts
269 * its execution from image load address.
270 *
271 * @param rproc - pointer to remoteproc instance to boot
272 *
273 * @param returns - status of function execution
274 */
275 int remoteproc_boot(struct remote_proc *rproc)
276 {
278 void *load_addr;
279 int status;
281 if (!rproc) {
282 return RPROC_ERR_PARAM;
283 }
285 /* Stop the remote CPU */
286 hil_shutdown_cpu(rproc->proc);
288 /* Load the firmware */
289 status = remoteproc_loader_load_remote_firmware(rproc->loader);
290 if (status == RPROC_SUCCESS) {
291 load_addr = remoteproc_get_load_address(rproc->loader);
292 if (load_addr != RPROC_ERR_PTR) {
293 /* Start the remote cpu */
294 status = hil_boot_cpu(rproc->proc,
295 (uintptr_t)load_addr);
296 if (status == RPROC_SUCCESS) {
297 /* Wait for remote side to come up. This delay is arbitrary and may
298 * need adjustment for different configuration of remote systems */
299 metal_sleep_usec(RPROC_BOOT_DELAY);
301 /* Initialize RPMSG "messaging" component */
303 /* It is a work-around to work with remote Linux context.
304 Since the upstream Linux rpmsg implementation always
305 assumes itself to be an rpmsg master, we initialize
306 the remote device as an rpmsg master for remote Linux
307 configuration only. */
308 #if defined (OPENAMP_REMOTE_LINUX_ENABLE)
309 status =
310 rpmsg_init(rproc->proc,
311 &rproc->rdev,
312 rproc->channel_created,
313 rproc->channel_destroyed,
314 rproc->default_cb, RPMSG_MASTER);
315 #else
316 status =
317 rpmsg_init(rproc->proc,
318 &rproc->rdev,
319 rproc->channel_created,
320 rproc->channel_destroyed,
321 rproc->default_cb, RPMSG_REMOTE);
322 #endif
323 }
324 } else {
325 status = RPROC_ERR_LOADER;
326 }
327 } else {
328 status = RPROC_ERR_LOADER;
329 }
331 return status;
332 }
334 /**
335 * remoteproc_shutdown
336 *
337 * This function shutdowns the remote execution context
338 *
339 * @param rproc - pointer to remote proc instance to shutdown
340 *
341 * @param returns - status of function execution
342 */
343 int remoteproc_shutdown(struct remote_proc *rproc)
344 {
346 if (rproc) {
347 if (rproc->proc) {
348 hil_shutdown_cpu(rproc->proc);
349 }
350 if (rproc->rdev) {
351 rpmsg_deinit(rproc->rdev);
352 rproc->rdev = RPROC_NULL;
353 rproc->proc = RPROC_NULL;
354 }
355 }
357 return RPROC_SUCCESS;
358 }