1 /*
2 * Copyright (c) 2014, Mentor Graphics Corporation
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. Neither the name of Mentor Graphics Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
30 /**************************************************************************
31 * FILE NAME
32 *
33 * hil.c
34 *
35 * COMPONENT
36 *
37 * OpenAMP Stack.
38 *
39 * DESCRIPTION
40 *
41 * This file is implementation of generic part of HIL.
42 *
43 *
44 *
45 **************************************************************************/
47 #include "openamp/hil.h"
48 #include <metal/io.h>
49 #include <metal/alloc.h>
50 #include <metal/device.h>
51 #include <metal/shmem.h>
52 #include <metal/utilities.h>
54 #define DEFAULT_VRING_MEM_SIZE 0x10000
56 /*--------------------------- Globals ---------------------------------- */
57 static METAL_DECLARE_LIST (procs);
59 #if defined (OPENAMP_BENCHMARK_ENABLE)
61 unsigned long long boot_time_stamp;
62 unsigned long long shutdown_time_stamp;
64 #endif
66 metal_phys_addr_t hil_generic_start_paddr = 0;
67 struct metal_io_region hil_shm_generic_io = {
68 0,
69 &hil_generic_start_paddr,
70 (size_t)(-1),
71 (sizeof(metal_phys_addr_t) << 3),
72 (metal_phys_addr_t)(-1),
73 0,
74 {NULL},
75 };
77 struct metal_io_region hil_devmem_generic_io = {
78 0,
79 &hil_generic_start_paddr,
80 (size_t)(-1),
81 (sizeof(metal_phys_addr_t) << 3),
82 (metal_phys_addr_t)(-1),
83 METAL_UNCACHED | METAL_SHARED_MEM,
84 {NULL},
85 };
87 struct hil_proc *hil_create_proc(struct hil_platform_ops *ops,
88 unsigned long cpu_id, void *pdata)
89 {
90 struct hil_proc *proc = 0;
91 int i;
93 proc = metal_allocate_memory(sizeof(struct hil_proc));
94 if (!proc)
95 return NULL;
96 memset(proc, 0, sizeof(struct hil_proc));
98 proc->ops = ops;
99 proc->num_chnls = 1;
100 proc->cpu_id = cpu_id;
101 proc->pdata = pdata;
103 /* Setup generic shared memory I/O region */
104 proc->sh_buff.io = &hil_shm_generic_io;
105 /* Setup generic vrings I/O region */
106 for (i = 0; i < HIL_MAX_NUM_VRINGS; i++)
107 proc->vdev.vring_info[i].io = &hil_devmem_generic_io;
109 metal_list_add_tail(&procs, &proc->node);
111 return proc;
112 }
114 /**
115 * hil_delete_proc
116 *
117 * This function deletes the given proc instance and frees the
118 * associated resources.
119 *
120 * @param proc - pointer to hil remote_proc instance
121 *
122 */
123 void hil_delete_proc(struct hil_proc *proc)
124 {
125 struct metal_list *node;
126 struct metal_device *dev;
127 struct metal_io_region *io;
128 struct proc_vring *vring;
129 int i;
130 metal_list_for_each(&procs, node) {
131 if (proc ==
132 metal_container_of(node, struct hil_proc, node)) {
133 metal_list_del(&proc->node);
134 proc->ops->release(proc);
135 /* Close shmem device */
136 dev = proc->sh_buff.dev;
137 io = proc->sh_buff.io;
138 if (dev) {
139 metal_device_close(dev);
140 } else if (io && io->ops.close) {
141 io->ops.close(io);
142 }
144 /* Close vring device */
145 for (i = 0; i < HIL_MAX_NUM_VRINGS; i++) {
146 vring = &proc->vdev.vring_info[i];
147 dev = vring->dev;
148 io = vring->io;
149 if (dev) {
150 metal_device_close(dev);
151 } if (io && io->ops.close) {
152 io->ops.close(io);
153 }
154 }
156 metal_free_memory(proc);
157 return;
158 }
159 }
160 }
162 int hil_init_proc(struct hil_proc *proc)
163 {
164 int ret = 0;
165 if (!proc->is_initialized && proc->ops->initialize) {
166 ret = proc->ops->initialize(proc);
167 if (!ret)
168 proc->is_initialized = 1;
169 else
170 return -1;
171 }
172 return 0;
173 }
174 /**
175 * hil_isr()
176 *
177 * This function is called when interrupt is received for the vring.
178 * This function gets the corresponding virtqueue and generates
179 * call back for it.
180 *
181 * @param vring_hw - pointer to vring control block
182 *
183 */
184 void hil_isr(struct proc_vring *vring_hw)
185 {
186 virtqueue_notification(vring_hw->vq);
187 }
189 /**
190 * hil_get_chnl_info
191 *
192 * This function returns channels info for given proc.
193 *
194 * @param proc - pointer to proc info struct
195 * @param num_chnls - pointer to integer variable to hold
196 * number of available channels
197 *
198 * @return - pointer to channel info control block
199 *
200 */
201 struct proc_chnl *hil_get_chnl_info(struct hil_proc *proc, int *num_chnls)
202 {
203 *num_chnls = proc->num_chnls;
204 return (proc->chnls);
205 }
207 /**
208 * hil_get_vdev_info
209 *
210 * This function return virtio device for remote core.
211 *
212 * @param proc - pointer to remote proc
213 *
214 * @return - pointer to virtio HW device.
215 *
216 */
218 struct proc_vdev *hil_get_vdev_info(struct hil_proc *proc)
219 {
220 return (&proc->vdev);
222 }
224 /**
225 * hil_get_vring_info
226 *
227 * This function returns vring_info_table. The caller will use
228 * this table to get the vring HW info which will be subsequently
229 * used to create virtqueues.
230 *
231 * @param vdev - pointer to virtio HW device
232 * @param num_vrings - pointer to hold number of vrings
233 *
234 * @return - pointer to vring hardware info table
235 */
236 struct proc_vring *hil_get_vring_info(struct proc_vdev *vdev, int *num_vrings)
237 {
239 *num_vrings = vdev->num_vrings;
240 return (vdev->vring_info);
242 }
244 /**
245 * hil_get_shm_info
246 *
247 * This function returns shared memory info control block. The caller
248 * will use this information to create and manage memory buffers for
249 * vring descriptor table.
250 *
251 * @param proc - pointer to proc instance
252 *
253 * @return - pointer to shared memory region used for buffers
254 *
255 */
256 struct proc_shm *hil_get_shm_info(struct hil_proc *proc)
257 {
258 return (&proc->sh_buff);
259 }
261 /**
262 * hil_enable_vring_notifications()
263 *
264 * This function is called after successful creation of virtqueues.
265 * This function saves queue handle in the vring_info_table which
266 * will be used during interrupt handling .This function setups
267 * interrupt handlers.
268 *
269 * @param vring_index - index to vring HW table
270 * @param vq - pointer to virtqueue to save in vring HW table
271 *
272 * @return - execution status
273 */
274 int hil_enable_vring_notifications(int vring_index, struct virtqueue *vq)
275 {
276 struct hil_proc *proc_hw = (struct hil_proc *)vq->vq_dev->device;
277 struct proc_vring *vring_hw = &proc_hw->vdev.vring_info[vring_index];
278 /* Save virtqueue pointer for later reference */
279 vring_hw->vq = vq;
281 if (proc_hw->ops->enable_interrupt) {
282 proc_hw->ops->enable_interrupt(vring_hw);
283 }
285 return 0;
286 }
288 /**
289 * hil_vring_notify()
290 *
291 * This function generates IPI to let the other side know that there is
292 * job available for it. The required information to achieve this, like interrupt
293 * vector, CPU id etc is be obtained from the proc_vring table.
294 *
295 * @param vq - pointer to virtqueue
296 *
297 */
298 void hil_vring_notify(struct virtqueue *vq)
299 {
300 struct hil_proc *proc_hw = (struct hil_proc *)vq->vq_dev->device;
301 struct proc_vring *vring_hw =
302 &proc_hw->vdev.vring_info[vq->vq_queue_index];
304 if (proc_hw->ops->notify) {
305 proc_hw->ops->notify(proc_hw, &vring_hw->intr_info);
306 }
307 }
309 /**
310 * hil_get_status
311 *
312 * This function is used to check if the given core is up and running.
313 * This call will return after it is confirmed that remote core has
314 * started.
315 *
316 * @param proc - pointer to proc instance
317 *
318 * @return - execution status
319 */
320 int hil_get_status(struct hil_proc *proc)
321 {
322 (void)proc;
324 /* For future use only. */
325 return 0;
326 }
328 /**
329 * hil_set_status
330 *
331 * This function is used to update the status
332 * of the given core i.e it is ready for IPC.
333 *
334 * @param proc - pointer to remote proc
335 *
336 * @return - execution status
337 */
338 int hil_set_status(struct hil_proc *proc)
339 {
340 (void)proc;
342 /* For future use only. */
343 return 0;
344 }
346 /**
347 * hil_boot_cpu
348 *
349 * This function boots the remote processor.
350 *
351 * @param proc - pointer to remote proc
352 * @param start_addr - start address of remote cpu
353 *
354 * @return - execution status
355 */
356 int hil_boot_cpu(struct hil_proc *proc, unsigned int start_addr)
357 {
359 if (proc->ops->boot_cpu) {
360 proc->ops->boot_cpu(proc, start_addr);
361 }
362 #if defined (OPENAMP_BENCHMARK_ENABLE)
363 boot_time_stamp = env_get_timestamp();
364 #endif
366 return 0;
367 }
369 /**
370 * hil_shutdown_cpu
371 *
372 * This function shutdowns the remote processor
373 *
374 * @param proc - pointer to remote proc
375 *
376 */
377 void hil_shutdown_cpu(struct hil_proc *proc)
378 {
379 if (proc->ops->shutdown_cpu) {
380 proc->ops->shutdown_cpu(proc);
381 }
382 #if defined (OPENAMP_BENCHMARK_ENABLE)
383 shutdown_time_stamp = env_get_timestamp();
384 #endif
385 }
387 /**
388 * hil_get_firmware
389 *
390 * This function returns address and size of given firmware name passed as
391 * parameter.
392 *
393 * @param fw_name - name of the firmware
394 * @param start_addr - pointer t hold start address of firmware
395 * @param size - pointer to hold size of firmware
396 *
397 * returns - status of function execution
398 *
399 */
400 int hil_get_firmware(char *fw_name, uintptr_t *start_addr,
401 unsigned int *size)
402 {
403 return (config_get_firmware(fw_name, start_addr, size));
404 }
406 int hil_poll (struct hil_proc *proc, int nonblock)
407 {
408 return proc->ops->poll(proc, nonblock);
409 }
411 int hil_set_shm (struct hil_proc *proc,
412 const char *bus_name, const char *name,
413 metal_phys_addr_t paddr, size_t size)
414 {
415 struct metal_device *dev;
416 struct metal_io_region *io;
417 int ret;
418 if (!proc)
419 return -1;
420 if (name && bus_name) {
421 ret = metal_device_open(bus_name, name, &dev);
422 if (ret)
423 return ret;
424 io = metal_device_io_region(dev, 0);
425 if (!io)
426 return -1;
427 proc->sh_buff.io = io;
428 proc->sh_buff.dev = dev;
429 } else if (name) {
430 ret = metal_shmem_open(name, size, &io);
431 if (ret)
432 return ret;
433 proc->sh_buff.io = io;
434 }
435 if (!paddr && io) {
436 proc->sh_buff.start_paddr = io->physmap[0];
437 proc->sh_buff.start_addr = io->virt;
438 } else {
439 proc->sh_buff.start_paddr = paddr;
440 }
441 if (!size && io)
442 proc->sh_buff.size = io->size;
443 else
444 proc->sh_buff.size = size;
446 metal_io_mem_map(proc->sh_buff.start_paddr, proc->sh_buff.io,
447 proc->sh_buff.size);
448 return 0;
449 }
451 int hil_set_vring (struct hil_proc *proc, int index,
452 const char *bus_name, const char *name)
453 {
454 struct metal_device *dev;
455 struct metal_io_region *io;
456 struct proc_vring *vring;
457 int ret;
459 if (!proc)
460 return -1;
461 if (index >= HIL_MAX_NUM_VRINGS)
462 return -1;
463 vring = &proc->vdev.vring_info[index];
464 if (name && bus_name) {
465 ret = metal_device_open(bus_name, name, &dev);
466 if (ret)
467 return ret;
468 io = metal_device_io_region(dev, 0);
469 if (!io)
470 return -1;
471 vring->io = io;
472 vring->dev = dev;
473 } else if (name) {
474 ret = metal_shmem_open(name, DEFAULT_VRING_MEM_SIZE, &io);
475 if (ret)
476 return ret;
477 vring->io = io;
478 }
480 return 0;
481 }
483 int hil_set_ipi (struct hil_proc *proc, int index,
484 unsigned int irq, void *data)
485 {
486 struct proc_intr *vring_intr;
488 if (!proc)
489 return -1;
490 vring_intr = &proc->vdev.vring_info[index].intr_info;
491 vring_intr->vect_id = irq;
492 vring_intr->data = data;
493 return 0;
494 }
496 int hil_set_rpmsg_channel (struct hil_proc *proc, int index,
497 char *name)
498 {
499 if (!proc)
500 return -1;
501 if (index >= HIL_MAX_NUM_CHANNELS)
502 return -1;
503 strcpy(proc->chnls[index].name, name);
504 return 0;
505 }