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 "openamp/remoteproc.h"
49 #include <metal/io.h>
50 #include <metal/alloc.h>
51 #include <metal/device.h>
52 #include <metal/shmem.h>
53 #include <metal/utilities.h>
54 #include <metal/time.h>
56 #define DEFAULT_VRING_MEM_SIZE 0x10000
58 /*--------------------------- Globals ---------------------------------- */
59 static METAL_DECLARE_LIST (procs);
61 #if defined (OPENAMP_BENCHMARK_ENABLE)
63 unsigned long long boot_time_stamp;
64 unsigned long long shutdown_time_stamp;
66 #endif
68 metal_phys_addr_t hil_generic_start_paddr = 0;
69 struct metal_io_region hil_shm_generic_io = {
70 0,
71 &hil_generic_start_paddr,
72 (size_t)(-1),
73 (sizeof(metal_phys_addr_t) << 3),
74 (metal_phys_addr_t)(-1),
75 0,
76 {NULL},
77 };
79 struct metal_io_region hil_devmem_generic_io = {
80 0,
81 &hil_generic_start_paddr,
82 (size_t)(-1),
83 (sizeof(metal_phys_addr_t) << 3),
84 (metal_phys_addr_t)(-1),
85 METAL_UNCACHED | METAL_SHARED_MEM,
86 {NULL},
87 };
89 struct hil_proc *hil_create_proc(struct hil_platform_ops *ops,
90 unsigned long cpu_id, void *pdata)
91 {
92 struct hil_proc *proc = 0;
93 int i;
95 proc = metal_allocate_memory(sizeof(struct hil_proc));
96 if (!proc)
97 return NULL;
98 memset(proc, 0, sizeof(struct hil_proc));
100 proc->ops = ops;
101 proc->num_chnls = 1;
102 proc->cpu_id = cpu_id;
103 proc->pdata = pdata;
105 /* Setup generic shared memory I/O region */
106 proc->sh_buff.io = &hil_shm_generic_io;
107 /* Setup generic vrings I/O region */
108 for (i = 0; i < HIL_MAX_NUM_VRINGS; i++)
109 proc->vdev.vring_info[i].io = &hil_devmem_generic_io;
111 metal_mutex_init(&proc->lock);
112 metal_list_add_tail(&procs, &proc->node);
114 return proc;
115 }
117 /**
118 * hil_delete_proc
119 *
120 * This function deletes the given proc instance and frees the
121 * associated resources.
122 *
123 * @param proc - pointer to hil remote_proc instance
124 *
125 */
126 void hil_delete_proc(struct hil_proc *proc)
127 {
128 struct metal_list *node;
129 struct metal_device *dev;
130 struct metal_io_region *io;
131 struct proc_vring *vring;
132 int i;
133 metal_list_for_each(&procs, node) {
134 if (proc ==
135 metal_container_of(node, struct hil_proc, node)) {
136 metal_list_del(&proc->node);
137 metal_mutex_acquire(&proc->lock);
138 proc->ops->release(proc);
139 /* Close shmem device */
140 dev = proc->sh_buff.dev;
141 io = proc->sh_buff.io;
142 if (dev) {
143 metal_device_close(dev);
144 } else if (io && io->ops.close) {
145 io->ops.close(io);
146 }
148 /* Close vring device */
149 for (i = 0; i < HIL_MAX_NUM_VRINGS; i++) {
150 vring = &proc->vdev.vring_info[i];
151 dev = vring->dev;
152 io = vring->io;
153 if (dev) {
154 metal_device_close(dev);
155 } if (io && io->ops.close) {
156 io->ops.close(io);
157 }
158 }
160 metal_mutex_release(&proc->lock);
161 metal_mutex_deinit(&proc->lock);
162 metal_free_memory(proc);
163 return;
164 }
165 }
166 }
168 int hil_init_proc(struct hil_proc *proc)
169 {
170 int ret = 0;
171 if (!proc->is_initialized && proc->ops->initialize) {
172 ret = proc->ops->initialize(proc);
173 if (!ret)
174 proc->is_initialized = 1;
175 else
176 return -1;
177 }
178 return 0;
179 }
181 /**
182 * hil_get_chnl_info
183 *
184 * This function returns channels info for given proc.
185 *
186 * @param proc - pointer to proc info struct
187 * @param num_chnls - pointer to integer variable to hold
188 * number of available channels
189 *
190 * @return - pointer to channel info control block
191 *
192 */
193 struct proc_chnl *hil_get_chnl_info(struct hil_proc *proc, int *num_chnls)
194 {
195 *num_chnls = proc->num_chnls;
196 return (proc->chnls);
197 }
199 void hil_notified(struct hil_proc *proc, uint32_t notifyid)
200 {
201 struct proc_vdev *pvdev = &proc->vdev;
202 struct fw_rsc_vdev *vdev_rsc = pvdev->vdev_info;
203 int i;
204 if (vdev_rsc->status & VIRTIO_CONFIG_STATUS_NEEDS_RESET) {
205 if (pvdev->rst_cb)
206 pvdev->rst_cb(proc, 0);
207 } else {
208 for(i = 0; i < (int)pvdev->num_vrings; i++) {
209 struct fw_rsc_vdev_vring *vring_rsc;
210 vring_rsc = &vdev_rsc->vring[i];
211 if (notifyid == (uint32_t)(-1) ||
212 notifyid == vring_rsc->notifyid)
213 virtqueue_notification(
214 pvdev->vring_info[i].vq);
215 }
216 }
217 }
219 /**
220 * hil_get_vdev_info
221 *
222 * This function return virtio device for remote core.
223 *
224 * @param proc - pointer to remote proc
225 *
226 * @return - pointer to virtio HW device.
227 *
228 */
230 struct proc_vdev *hil_get_vdev_info(struct hil_proc *proc)
231 {
232 return (&proc->vdev);
234 }
236 /**
237 * hil_get_vring_info
238 *
239 * This function returns vring_info_table. The caller will use
240 * this table to get the vring HW info which will be subsequently
241 * used to create virtqueues.
242 *
243 * @param vdev - pointer to virtio HW device
244 * @param num_vrings - pointer to hold number of vrings
245 *
246 * @return - pointer to vring hardware info table
247 */
248 struct proc_vring *hil_get_vring_info(struct proc_vdev *vdev, int *num_vrings)
249 {
250 struct fw_rsc_vdev *vdev_rsc;
251 struct fw_rsc_vdev_vring *vring_rsc;
252 struct proc_vring *vring;
253 int i;
255 vdev_rsc = vdev->vdev_info;
256 if (vdev_rsc) {
257 vring = &vdev->vring_info[0];
258 for (i = 0; i < vdev_rsc->num_of_vrings; i++) {
259 /* Initialize vring with vring resource */
260 vring_rsc = &vdev_rsc->vring[i];
261 vring[i].num_descs = vring_rsc->num;
262 vring[i].align = vring_rsc->align;
263 /* Enable acccess to vring memory region */
264 vring[i].vaddr =
265 metal_io_mem_map(
266 (metal_phys_addr_t)vring_rsc->da,
267 vring[i].io,
268 vring_size(vring_rsc->num,
269 vring_rsc->align));
270 }
271 }
272 *num_vrings = vdev->num_vrings;
273 return (vdev->vring_info);
275 }
277 /**
278 * hil_get_shm_info
279 *
280 * This function returns shared memory info control block. The caller
281 * will use this information to create and manage memory buffers for
282 * vring descriptor table.
283 *
284 * @param proc - pointer to proc instance
285 *
286 * @return - pointer to shared memory region used for buffers
287 *
288 */
289 struct proc_shm *hil_get_shm_info(struct hil_proc *proc)
290 {
291 return (&proc->sh_buff);
292 }
294 void hil_free_vqs(struct virtio_device *vdev)
295 {
296 struct hil_proc *proc = vdev->device;
297 struct proc_vdev *pvdev = &proc->vdev;
298 int num_vrings = (int)pvdev->num_vrings;
299 int i;
301 metal_mutex_acquire(&proc->lock);
302 for(i = 0; i < num_vrings; i++) {
303 struct proc_vring *pvring = &pvdev->vring_info[i];
304 struct virtqueue *vq = pvring->vq;
305 if (vq) {
306 virtqueue_free(vq);
307 pvring->vq = 0;
308 }
309 }
310 metal_mutex_release(&proc->lock);
311 }
313 int hil_enable_vdev_notification(struct hil_proc *proc, int id)
314 {
315 /* We only support single vdev in hil_proc */
316 (void)id;
317 if (!proc)
318 return -1;
319 if (proc->ops->enable_interrupt)
320 proc->ops->enable_interrupt(&proc->vdev.intr_info);
321 return 0;
322 }
324 /**
325 * hil_enable_vring_notifications()
326 *
327 * This function is called after successful creation of virtqueues.
328 * This function saves queue handle in the vring_info_table which
329 * will be used during interrupt handling .This function setups
330 * interrupt handlers.
331 *
332 * @param vring_index - index to vring HW table
333 * @param vq - pointer to virtqueue to save in vring HW table
334 *
335 * @return - execution status
336 */
337 int hil_enable_vring_notifications(int vring_index, struct virtqueue *vq)
338 {
339 struct hil_proc *proc_hw = (struct hil_proc *)vq->vq_dev->device;
340 struct proc_vring *vring_hw = &proc_hw->vdev.vring_info[vring_index];
341 /* Save virtqueue pointer for later reference */
342 vring_hw->vq = vq;
344 if (proc_hw->ops->enable_interrupt) {
345 proc_hw->ops->enable_interrupt(&vring_hw->intr_info);
346 }
348 return 0;
349 }
351 /**
352 * hil_vdev_notify()
353 *
354 * This function generates IPI to let the other side know that there is
355 * update in the vritio dev configs
356 *
357 * @param vdev - pointer to the viritio device
358 *
359 */
360 void hil_vdev_notify(struct virtio_device *vdev)
361 {
362 struct hil_proc *proc = vdev->device;
363 struct proc_vdev *pvdev = &proc->vdev;
365 if (proc->ops->notify) {
366 proc->ops->notify(proc, &pvdev->intr_info);
367 }
368 }
370 /**
371 * hil_vring_notify()
372 *
373 * This function generates IPI to let the other side know that there is
374 * job available for it. The required information to achieve this, like interrupt
375 * vector, CPU id etc is be obtained from the proc_vring table.
376 *
377 * @param vq - pointer to virtqueue
378 *
379 */
380 void hil_vring_notify(struct virtqueue *vq)
381 {
382 struct hil_proc *proc_hw = (struct hil_proc *)vq->vq_dev->device;
383 struct proc_vring *vring_hw =
384 &proc_hw->vdev.vring_info[vq->vq_queue_index];
386 if (proc_hw->ops->notify) {
387 proc_hw->ops->notify(proc_hw, &vring_hw->intr_info);
388 }
389 }
391 /**
392 * hil_get_status
393 *
394 * This function is used to check if the given core is up and running.
395 * This call will return after it is confirmed that remote core has
396 * started.
397 *
398 * @param proc - pointer to proc instance
399 *
400 * @return - execution status
401 */
402 int hil_get_status(struct hil_proc *proc)
403 {
404 (void)proc;
406 /* For future use only. */
407 return 0;
408 }
410 /**
411 * hil_set_status
412 *
413 * This function is used to update the status
414 * of the given core i.e it is ready for IPC.
415 *
416 * @param proc - pointer to remote proc
417 *
418 * @return - execution status
419 */
420 int hil_set_status(struct hil_proc *proc)
421 {
422 (void)proc;
424 /* For future use only. */
425 return 0;
426 }
428 /**
429 * hil_boot_cpu
430 *
431 * This function boots the remote processor.
432 *
433 * @param proc - pointer to remote proc
434 * @param start_addr - start address of remote cpu
435 *
436 * @return - execution status
437 */
438 int hil_boot_cpu(struct hil_proc *proc, unsigned int start_addr)
439 {
441 if (proc->ops->boot_cpu) {
442 proc->ops->boot_cpu(proc, start_addr);
443 }
444 #if defined (OPENAMP_BENCHMARK_ENABLE)
445 boot_time_stamp = metal_get_timestamp();
446 #endif
448 return 0;
449 }
451 /**
452 * hil_shutdown_cpu
453 *
454 * This function shutdowns the remote processor
455 *
456 * @param proc - pointer to remote proc
457 *
458 */
459 void hil_shutdown_cpu(struct hil_proc *proc)
460 {
461 if (proc->ops->shutdown_cpu) {
462 proc->ops->shutdown_cpu(proc);
463 }
464 #if defined (OPENAMP_BENCHMARK_ENABLE)
465 shutdown_time_stamp = metal_get_timestamp();
466 #endif
467 }
469 /**
470 * hil_get_firmware
471 *
472 * This function returns address and size of given firmware name passed as
473 * parameter.
474 *
475 * @param fw_name - name of the firmware
476 * @param start_addr - pointer t hold start address of firmware
477 * @param size - pointer to hold size of firmware
478 *
479 * returns - status of function execution
480 *
481 */
482 int hil_get_firmware(char *fw_name, uintptr_t *start_addr,
483 unsigned int *size)
484 {
485 return (config_get_firmware(fw_name, start_addr, size));
486 }
488 int hil_poll (struct hil_proc *proc, int nonblock)
489 {
490 return proc->ops->poll(proc, nonblock);
491 }
493 int hil_set_shm (struct hil_proc *proc,
494 const char *bus_name, const char *name,
495 metal_phys_addr_t paddr, size_t size)
496 {
497 struct metal_device *dev;
498 struct metal_io_region *io;
499 int ret;
500 if (!proc)
501 return -1;
502 if (name && bus_name) {
503 ret = metal_device_open(bus_name, name, &dev);
504 if (ret)
505 return ret;
506 io = metal_device_io_region(dev, 0);
507 if (!io)
508 return -1;
509 proc->sh_buff.io = io;
510 proc->sh_buff.dev = dev;
511 } else if (name) {
512 ret = metal_shmem_open(name, size, &io);
513 if (ret)
514 return ret;
515 proc->sh_buff.io = io;
516 }
517 if (!paddr && io) {
518 proc->sh_buff.start_paddr = io->physmap[0];
519 proc->sh_buff.start_addr = io->virt;
520 } else {
521 proc->sh_buff.start_paddr = paddr;
522 }
523 if (!size && io)
524 proc->sh_buff.size = io->size;
525 else
526 proc->sh_buff.size = size;
528 metal_io_mem_map(proc->sh_buff.start_paddr, proc->sh_buff.io,
529 proc->sh_buff.size);
530 return 0;
531 }
533 int hil_set_vring (struct hil_proc *proc, int index,
534 const char *bus_name, const char *name)
535 {
536 struct metal_device *dev;
537 struct metal_io_region *io;
538 struct proc_vring *vring;
539 int ret;
541 if (!proc)
542 return -1;
543 if (index >= HIL_MAX_NUM_VRINGS)
544 return -1;
545 vring = &proc->vdev.vring_info[index];
546 if (name && bus_name) {
547 ret = metal_device_open(bus_name, name, &dev);
548 if (ret)
549 return ret;
550 io = metal_device_io_region(dev, 0);
551 if (!io)
552 return -1;
553 vring->io = io;
554 vring->dev = dev;
555 } else if (name) {
556 ret = metal_shmem_open(name, DEFAULT_VRING_MEM_SIZE, &io);
557 if (ret)
558 return ret;
559 vring->io = io;
560 }
562 return 0;
563 }
565 int hil_set_vdev_ipi (struct hil_proc *proc, int index,
566 unsigned int irq, void *data)
567 {
568 struct proc_intr *vring_intr;
570 /* As we support only one vdev for now */
571 (void)index;
573 if (!proc)
574 return -1;
575 vring_intr = &proc->vdev.intr_info;
576 vring_intr->vect_id = irq;
577 vring_intr->data = data;
578 return 0;
579 }
581 int hil_set_vring_ipi (struct hil_proc *proc, int index,
582 unsigned int irq, void *data)
583 {
584 struct proc_intr *vring_intr;
586 if (!proc)
587 return -1;
588 vring_intr = &proc->vdev.vring_info[index].intr_info;
589 vring_intr->vect_id = irq;
590 vring_intr->data = data;
591 return 0;
592 }
594 int hil_set_rpmsg_channel (struct hil_proc *proc, int index,
595 char *name)
596 {
597 if (!proc)
598 return -1;
599 if (index >= HIL_MAX_NUM_CHANNELS)
600 return -1;
601 strcpy(proc->chnls[index].name, name);
602 return 0;
603 }
605 int hil_set_vdev_rst_cb (struct hil_proc *proc, int index,
606 hil_proc_vdev_rst_cb_t cb)
607 {
608 (void)index;
609 proc->vdev.rst_cb = cb;
610 return 0;
611 }