1 /*
2 * Copyright (c) 2011-2013, Texas Instruments Incorporated
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
7 * are met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * * Neither the name of Texas Instruments Incorporated nor the names of
17 * its contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 /** ============================================================================
33 * @file VirtQueue.c
34 *
35 * @brief Virtio Queue implementation for BIOS
36 *
37 * Differences between BIOS version and Linux kernel (include/linux/virtio.h):
38 * - Renamed module from virtio.h to VirtQueue_Object.h to match the API prefixes;
39 * - BIOS (XDC) types and CamelCasing used;
40 * - virtio_device concept removed (i.e, assumes no containing device);
41 * - simplified scatterlist from Linux version;
42 * - VirtQueue_Objects are created statically here, so just added a VirtQueue_Object_init()
43 * fxn to take the place of the Virtio vring_new_virtqueue() API;
44 * - The notify function is implicit in the implementation, and not provided
45 * by the client, as it is in Linux virtio.
46 *
47 * All VirtQueue operations can be called in any context.
48 *
49 * The virtio header should be included in an application as follows:
50 * @code
51 * #include <ti/ipc/family/vayu/VirtQueue.h>
52 * @endcode
53 *
54 */
56 /* this define must precede inclusion of any xdc header file */
57 #define Registry_CURDESC ti_ipc_family_vayu__Desc
58 #define MODULE_NAME "ti.ipc.family.vayu.VirtQueue"
60 #include <xdc/std.h>
61 #include <xdc/runtime/System.h>
62 #include <xdc/runtime/Assert.h>
63 #include <xdc/runtime/Error.h>
64 #include <xdc/runtime/Memory.h>
65 #include <xdc/runtime/Registry.h>
66 #include <xdc/runtime/Log.h>
67 #include <xdc/runtime/Diags.h>
69 #include <ti/sysbios/hal/Hwi.h>
70 #include <ti/sysbios/knl/Clock.h>
71 #include <ti/sysbios/gates/GateHwi.h>
72 #include <ti/sysbios/BIOS.h>
73 #include <ti/sysbios/hal/Cache.h>
75 #include <ti/ipc/MultiProc.h>
77 #include <ti/ipc/rpmsg/virtio_ring.h>
78 #ifndef DSP
79 #include <ti/pm/IpcPower.h>
80 #endif
81 #include <string.h>
83 #include <ti/ipc/remoteproc/Resource.h>
84 #include <ti/ipc/remoteproc/rsc_types.h>
85 #include <ti/ipc/rpmsg/_VirtQueue.h>
87 #include <ti/sdo/ipc/notifyDrivers/IInterrupt.h>
88 #include "InterruptProxy.h"
89 #include "VirtQueue.h"
92 /* Used for defining the size of the virtqueue registry */
93 #define NUM_QUEUES 4
95 /* Predefined device addresses */
96 #ifdef DSP
97 #define IPC_MEM_VRING0 0xA0000000
98 #define IPC_MEM_VRING1 0xA0004000
99 #define IPC_MEM_VRING2 0xA0008000
100 #define IPC_MEM_VRING3 0xA000c000
101 #else
102 #define IPC_MEM_VRING0 0x60000000
103 #define IPC_MEM_VRING1 0x60004000
104 #define IPC_MEM_VRING2 0x60008000
105 #define IPC_MEM_VRING3 0x6000c000
106 #endif
108 /*
109 * Sizes of the virtqueues (expressed in number of buffers supported,
110 * and must be power of two)
111 */
112 #define VQ0_SIZE 256
113 #define VQ1_SIZE 256
114 #define VQ2_SIZE 256
115 #define VQ3_SIZE 256
117 /*
118 * enum - Predefined Mailbox Messages
119 *
120 * @RP_MSG_MBOX_READY: informs the M3's that we're up and running. will be
121 * followed by another mailbox message that carries the A9's virtual address
122 * of the shared buffer. This would allow the A9's drivers to send virtual
123 * addresses of the buffers.
124 *
125 * @RP_MSG_MBOX_STATE_CHANGE: informs the receiver that there is an inbound
126 * message waiting in its own receive-side vring. please note that currently
127 * this message is optional: alternatively, one can explicitly send the index
128 * of the triggered virtqueue itself. the preferred approach will be decided
129 * as we progress and experiment with those design ideas.
130 *
131 * @RP_MSG_MBOX_CRASH: this message indicates that the BIOS side is unhappy
132 *
133 * @RP_MBOX_ECHO_REQUEST: this message requests the remote processor to reply
134 * with RP_MBOX_ECHO_REPLY
135 *
136 * @RP_MBOX_ECHO_REPLY: this is a reply that is sent when RP_MBOX_ECHO_REQUEST
137 * is received.
138 *
139 * @RP_MBOX_ABORT_REQUEST: tells the M3 to crash on demand
140 *
141 * @RP_MBOX_BOOTINIT_DONE: this message indicates the BIOS side has reached a
142 * certain state during the boot process. This message is used to inform the
143 * host that the basic BIOS initialization is done, and lets the host use this
144 * notification to perform certain actions.
145 */
146 enum {
147 RP_MSG_MBOX_READY = (Int)0xFFFFFF00,
148 RP_MSG_MBOX_STATE_CHANGE = (Int)0xFFFFFF01,
149 RP_MSG_MBOX_CRASH = (Int)0xFFFFFF02,
150 RP_MBOX_ECHO_REQUEST = (Int)0xFFFFFF03,
151 RP_MBOX_ECHO_REPLY = (Int)0xFFFFFF04,
152 RP_MBOX_ABORT_REQUEST = (Int)0xFFFFFF05,
153 RP_MSG_FLUSH_CACHE = (Int)0xFFFFFF06,
154 RP_MSG_BOOTINIT_DONE = (Int)0xFFFFFF07,
155 RP_MSG_HIBERNATION = (Int)0xFFFFFF10,
156 RP_MSG_HIBERNATION_FORCE = (Int)0xFFFFFF11,
157 RP_MSG_HIBERNATION_ACK = (Int)0xFFFFFF12,
158 RP_MSG_HIBERNATION_CANCEL = (Int)0xFFFFFF13
159 };
161 #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
162 #define RP_MSG_NUM_BUFS (VQ0_SIZE) /* must be power of two */
163 #define RP_MSG_BUF_SIZE (512)
164 #define RP_MSG_BUFS_SPACE (RP_MSG_NUM_BUFS * RP_MSG_BUF_SIZE * 2)
166 #define PAGE_SIZE (4096)
167 /*
168 * The alignment to use between consumer and producer parts of vring.
169 * Note: this is part of the "wire" protocol. If you change this, you need
170 * to update your BIOS image as well
171 */
172 #define RP_MSG_VRING_ALIGN (4096)
174 /* With 256 buffers, our vring will occupy 3 pages */
175 #define RP_MSG_RING_SIZE ((DIV_ROUND_UP(vring_size(RP_MSG_NUM_BUFS, \
176 RP_MSG_VRING_ALIGN), PAGE_SIZE)) * PAGE_SIZE)
178 /* The total IPC space needed to communicate with a remote processor */
179 #define RPMSG_IPC_MEM (RP_MSG_BUFS_SPACE + 2 * RP_MSG_RING_SIZE)
181 #define ID_SYSM3_TO_A9 ID_SELF_TO_A9
182 #define ID_A9_TO_SYSM3 ID_A9_TO_SELF
183 #define ID_DSP_TO_A9 ID_SELF_TO_A9
184 #define ID_A9_TO_DSP ID_A9_TO_SELF
185 #define ID_APPM3_TO_A9 2
186 #define ID_A9_TO_APPM3 3
188 typedef struct VirtQueue_Object {
189 /* Id for this VirtQueue_Object */
190 UInt16 id;
192 /* The function to call when buffers are consumed (can be NULL) */
193 VirtQueue_callback callback;
195 /* Shared state */
196 struct vring vring;
198 /* Number of free buffers */
199 UInt16 num_free;
201 /* Last available index; updated by VirtQueue_getAvailBuf */
202 UInt16 last_avail_idx;
204 /* Last available index; updated by VirtQueue_addUsedBuf */
205 UInt16 last_used_idx;
207 /* Will eventually be used to kick remote processor */
208 UInt16 procId;
210 /* Gate to protect from multiple threads */
211 GateHwi_Handle gateH;
212 } VirtQueue_Object;
214 /* module diags mask */
215 Registry_Desc Registry_CURDESC;
217 static struct VirtQueue_Object *queueRegistry[NUM_QUEUES] = {NULL};
219 static UInt16 hostProcId;
220 #ifndef SMP
221 static UInt16 dsp1ProcId;
222 static UInt16 sysm3ProcId;
223 static UInt16 appm3ProcId;
224 #endif
226 #define DSPEVENTID 5
227 IInterrupt_IntInfo intInfo;
229 #if defined(M3_ONLY) && !defined(SMP)
230 extern Void OffloadM3_init();
231 extern Int OffloadM3_processSysM3Tasks(UArg msg);
232 #endif
234 /*!
235 * ======== _VirtQueue_init ========
236 *
237 * This function adds the VirtQueue "module" to the Registry so that
238 * DIAGS will work with this non-XDC module.
239 * Since VirtQueue_init is not called by XDC-VirtQueue module clients, this
240 * function is called in the first VirtQueue fxn called: VirtQueue_create.
241 */
242 static Void _VirtQueue_init()
243 {
244 static int initialized = 0;
246 if (!initialized) {
247 Registry_Result result;
249 /* register with xdc.runtime to get a diags mask */
250 result = Registry_addModule(&Registry_CURDESC, MODULE_NAME);
251 Assert_isTrue(result == Registry_SUCCESS, (Assert_Id)NULL);
253 initialized = 1;
254 }
255 }
257 static inline Void * mapPAtoVA(UInt pa)
258 {
259 return (Void *)((pa & 0x000fffffU) | IPC_MEM_VRING0);
260 }
262 static inline UInt mapVAtoPA(Void * va)
263 {
264 return ((UInt)va & 0x000fffffU) | 0x9cf00000U;
265 }
267 /*!
268 * ======== VirtQueue_kick ========
269 */
270 Void VirtQueue_kick(VirtQueue_Handle vq)
271 {
272 /* For now, simply interrupt remote processor */
273 if (vq->vring.avail->flags & VRING_AVAIL_F_NO_INTERRUPT) {
274 Log_print0(Diags_USER1,
275 "VirtQueue_kick: no kick because of VRING_AVAIL_F_NO_INTERRUPT\n");
276 return;
277 }
279 Log_print2(Diags_USER1,
280 "VirtQueue_kick: Sending interrupt to proc %d with payload 0x%x\n",
281 (IArg)vq->procId, (IArg)vq->id);
282 InterruptProxy_intSend(vq->procId, NULL, vq->id);
283 }
285 /*!
286 * ======== VirtQueue_addUsedBuf ========
287 */
288 Int VirtQueue_addUsedBuf(VirtQueue_Handle vq, Int16 head, Int len)
289 {
290 struct vring_used_elem *used;
291 IArg key;
293 key = GateHwi_enter(vq->gateH);
294 if ((head > vq->vring.num) || (head < 0)) {
295 GateHwi_leave(vq->gateH, key);
296 Error_raise(NULL, Error_E_generic, 0, 0);
297 }
299 /*
300 * The virtqueue contains a ring of used buffers. Get a pointer to the
301 * next entry in that used ring.
302 */
303 used = &vq->vring.used->ring[vq->vring.used->idx % vq->vring.num];
304 used->id = head;
305 used->len = len;
307 vq->vring.used->idx++;
308 GateHwi_leave(vq->gateH, key);
310 return (0);
311 }
313 /*!
314 * ======== VirtQueue_addAvailBuf ========
315 */
316 Int VirtQueue_addAvailBuf(VirtQueue_Object *vq, Void *buf)
317 {
318 UInt16 avail;
319 IArg key;
321 if (vq->num_free == 0) {
322 /* There's no more space */
323 Error_raise(NULL, Error_E_generic, 0, 0);
324 }
326 vq->num_free--;
328 key = GateHwi_enter(vq->gateH);
329 avail = vq->vring.avail->idx++ % vq->vring.num;
331 vq->vring.desc[avail].addr = mapVAtoPA(buf);
332 vq->vring.desc[avail].len = RP_MSG_BUF_SIZE;
333 GateHwi_leave(vq->gateH, key);
335 return (vq->num_free);
336 }
338 /*!
339 * ======== VirtQueue_getUsedBuf ========
340 */
341 Void *VirtQueue_getUsedBuf(VirtQueue_Object *vq)
342 {
343 UInt16 head;
344 Void *buf;
345 IArg key;
347 key = GateHwi_enter(vq->gateH);
348 /* There's nothing available? */
349 if (vq->last_used_idx == vq->vring.used->idx) {
350 buf = NULL;
351 }
352 else {
353 head = vq->vring.used->ring[vq->last_used_idx % vq->vring.num].id;
354 vq->last_used_idx++;
356 buf = mapPAtoVA(vq->vring.desc[head].addr);
357 }
358 GateHwi_leave(vq->gateH, key);
360 return (buf);
361 }
363 /*!
364 * ======== VirtQueue_getAvailBuf ========
365 */
366 Int16 VirtQueue_getAvailBuf(VirtQueue_Handle vq, Void **buf, Int *len)
367 {
368 Int16 head;
369 IArg key;
371 key = GateHwi_enter(vq->gateH);
372 Log_print6(Diags_USER1, "getAvailBuf vq: 0x%x %d %d %d 0x%x 0x%x\n",
373 (IArg)vq, vq->last_avail_idx, vq->vring.avail->idx, vq->vring.num,
374 (IArg)&vq->vring.avail, (IArg)vq->vring.avail);
376 /* Clear flag here to avoid race condition with remote processor.
377 * This is a negative flag, clearing it means that we want to
378 * receive an interrupt when a buffer has been added to the pool.
379 */
380 vq->vring.used->flags &= ~VRING_USED_F_NO_NOTIFY;
382 /* There's nothing available? */
383 if (vq->last_avail_idx == vq->vring.avail->idx) {
384 head = (-1);
385 }
386 else {
387 /* No need to be kicked about added buffers anymore */
388 vq->vring.used->flags |= VRING_USED_F_NO_NOTIFY;
390 /*
391 * Grab the next descriptor number they're advertising, and increment
392 * the index we've seen.
393 */
394 head = vq->vring.avail->ring[vq->last_avail_idx++ % vq->vring.num];
396 *buf = mapPAtoVA(vq->vring.desc[head].addr);
397 *len = vq->vring.desc[head].len;
398 }
399 GateHwi_leave(vq->gateH, key);
401 return (head);
402 }
404 /*!
405 * ======== VirtQueue_disableCallback ========
406 */
407 Void VirtQueue_disableCallback(VirtQueue_Object *vq)
408 {
409 /* TODO */
410 Log_print0(Diags_USER1, "VirtQueue_disableCallback called.");
411 }
413 /*!
414 * ======== VirtQueue_enableCallback ========
415 */
416 Bool VirtQueue_enableCallback(VirtQueue_Object *vq)
417 {
418 Log_print0(Diags_USER1, "VirtQueue_enableCallback called.");
420 /* TODO */
421 return (FALSE);
422 }
424 /*!
425 * ======== VirtQueue_isr ========
426 * Note 'arg' is ignored: it is the Hwi argument, not the mailbox argument.
427 */
428 Void VirtQueue_isr(UArg msg)
429 {
430 VirtQueue_Object *vq;
432 msg = InterruptProxy_intClear(hostProcId, &intInfo);
434 Log_print1(Diags_USER1, "VirtQueue_isr received msg = 0x%x\n", msg);
436 #ifndef SMP
437 if (MultiProc_self() == sysm3ProcId || MultiProc_self() == dsp1ProcId) {
438 #endif
439 switch(msg) {
440 case (UInt)RP_MSG_MBOX_READY:
441 return;
443 case (UInt)RP_MBOX_ECHO_REQUEST:
444 InterruptProxy_intSend(hostProcId, NULL,
445 (UInt)(RP_MBOX_ECHO_REPLY));
446 return;
448 case (UInt)RP_MBOX_ABORT_REQUEST:
449 {
450 /* Suppress Coverity Error: FORWARD_NULL: */
451 /* coverity[assign_zero] */
452 Fxn f = (Fxn)0x0;
453 Log_print0(Diags_USER1, "Crash on demand ...\n");
454 /* coverity[var_deref_op] */
455 f();
456 }
457 return;
459 case (UInt)RP_MSG_FLUSH_CACHE:
460 Cache_wbAll();
461 return;
463 #ifndef DSP
464 case (UInt)RP_MSG_HIBERNATION:
465 if (IpcPower_canHibernate() == FALSE) {
466 InterruptProxy_intSend(hostProcId, NULL,
467 (UInt)RP_MSG_HIBERNATION_CANCEL);
468 return;
469 }
471 /* Fall through */
472 case (UInt)RP_MSG_HIBERNATION_FORCE:
473 #ifndef SMP
474 /* Core0 should notify Core1 */
475 if (MultiProc_self() == sysm3ProcId) {
476 InterruptProxy_intSend(appm3ProcId, NULL,
477 (UInt)(RP_MSG_HIBERNATION));
478 }
479 #endif
480 /* Ack request */
481 InterruptProxy_intSend(hostProcId, NULL,
482 (UInt)RP_MSG_HIBERNATION_ACK);
483 IpcPower_suspend();
484 return;
485 #endif
486 default:
487 #if defined(M3_ONLY) && !defined(SMP)
488 /* Check and process any Inter-M3 Offload messages */
489 if (OffloadM3_processSysM3Tasks(msg))
490 return;
491 #endif
493 /*
494 * If the message isn't one of the above, it's either part of the
495 * 2-message synchronization sequence or it a virtqueue message
496 */
497 break;
498 }
499 #ifndef SMP
500 }
501 else if (msg & 0xFFFF0000) {
502 #ifndef DSP
503 if (msg == (UInt)RP_MSG_HIBERNATION) {
504 IpcPower_suspend();
505 }
506 #endif
507 return;
508 }
510 if (MultiProc_self() == sysm3ProcId && (msg == ID_A9_TO_APPM3 || msg == ID_APPM3_TO_A9)) {
511 InterruptProxy_intSend(appm3ProcId, NULL, (UInt)msg);
512 }
513 else {
514 #endif
515 /* Don't let unknown messages to pass as a virtqueue index */
516 if (msg >= NUM_QUEUES) {
517 /* Adding print here deliberately, we should never see this */
518 System_printf("VirtQueue_isr: Invalid mailbox message 0x%x "
519 "received\n", msg);
520 return;
521 }
523 vq = queueRegistry[msg];
524 if (vq) {
525 vq->callback(vq);
526 }
527 #ifndef SMP
528 }
529 #endif
530 }
533 /*!
534 * ======== VirtQueue_create ========
535 */
536 VirtQueue_Handle VirtQueue_create(UInt16 remoteProcId, VirtQueue_Params *params,
537 Error_Block *eb)
538 {
539 VirtQueue_Object *vq;
540 Void *vringAddr;
542 /* Perform initialization we can't do in Instance_init (being non-XDC): */
543 _VirtQueue_init();
545 vq = Memory_alloc(NULL, sizeof(VirtQueue_Object), 0, eb);
546 if (NULL == vq) {
547 return (NULL);
548 }
550 /* Create the thread protection gate */
551 vq->gateH = GateHwi_create(NULL, eb);
552 if (Error_check(eb)) {
553 Log_error0("VirtQueue_create: could not create gate object");
554 Memory_free(NULL, vq, sizeof(VirtQueue_Object));
555 return (NULL);
556 }
558 vq->callback = params->callback;
559 vq->id = params->vqId;
560 vq->procId = remoteProcId;
561 vq->last_avail_idx = 0;
563 #ifndef SMP
564 if (MultiProc_self() == appm3ProcId) {
565 /* vqindices that belong to AppM3 should be big so they don't
566 * collide with SysM3's virtqueues */
567 vq->id += 2;
568 }
569 #endif
571 switch (vq->id) {
572 /* IPC transport vrings */
573 case ID_SELF_TO_A9:
574 /* IPU/DSP -> A9 */
575 vringAddr = (struct vring *) IPC_MEM_VRING0;
576 break;
577 case ID_A9_TO_SELF:
578 /* A9 -> IPU/DSP */
579 vringAddr = (struct vring *) IPC_MEM_VRING1;
580 break;
581 #ifndef SMP
582 case ID_APPM3_TO_A9:
583 /* APPM3 -> A9 */
584 vringAddr = (struct vring *) IPC_MEM_VRING2;
585 break;
586 case ID_A9_TO_APPM3:
587 /* A9 -> APPM3 */
588 vringAddr = (struct vring *) IPC_MEM_VRING3;
589 break;
590 #endif
591 default:
592 GateHwi_delete(&vq->gateH);
593 Memory_free(NULL, vq, sizeof(VirtQueue_Object));
594 return (NULL);
595 }
597 Log_print3(Diags_USER1,
598 "vring: %d 0x%x (0x%x)\n", vq->id, (IArg)vringAddr,
599 RP_MSG_RING_SIZE);
601 /* See coverity related comment in vring_init() */
602 /* coverity[overrun-call] */
603 vring_init(&(vq->vring), RP_MSG_NUM_BUFS, vringAddr, RP_MSG_VRING_ALIGN);
605 /*
606 * Don't trigger a mailbox message every time MPU makes another buffer
607 * available
608 */
609 if (vq->procId == hostProcId) {
610 vq->vring.used->flags &= ~VRING_USED_F_NO_NOTIFY;
611 }
613 queueRegistry[vq->id] = vq;
615 return (vq);
616 }
618 /*
619 * The following three VIRTIO_* defines must match those in
620 * <Linux_kernel>/include/uapi/linux/virtio_config.h
621 */
622 #define VIRTIO_CONFIG_S_ACKNOWLEDGE 1
623 #define VIRTIO_CONFIG_S_DRIVER 2
624 #define VIRTIO_CONFIG_S_DRIVER_OK 4
626 #define VRING_BUFS_PRIMED (VIRTIO_CONFIG_S_ACKNOWLEDGE | \
627 VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK)
629 /*!
630 * ======== VirtQueue_startup ========
631 */
632 Void VirtQueue_startup()
633 {
634 hostProcId = MultiProc_getId("HOST");
635 #ifndef SMP
636 dsp1ProcId = MultiProc_getId("DSP1");
637 sysm3ProcId = MultiProc_getId("CORE0");
638 appm3ProcId = MultiProc_getId("CORE1");
639 #endif
641 #ifdef DSP
642 intInfo.intVectorId = DSPEVENTID;
643 #else
644 /* Initilize the IpcPower module */
645 IpcPower_init();
646 #endif
648 /*
649 * Wait for HLOS (Virtio device) to indicate that priming of host's receive
650 * buffers is complete, indicating that host is ready to send.
651 *
652 * Though this is a Linux Virtio configuration status, it must be
653 * implemented by each non-Linux HLOS as well.
654 */
655 Log_print1(Diags_USER1, "VirtQueue_startup: VDEV status: 0x%x\n",
656 Resource_getVdevStatus(VIRTIO_ID_RPMSG));
657 Log_print0(Diags_USER1, "VirtQueue_startup: Polling VDEV status...\n");
658 while (Resource_getVdevStatus(VIRTIO_ID_RPMSG) != VRING_BUFS_PRIMED);
659 Log_print1(Diags_USER1, "VirtQueue_startup: VDEV status: 0x%x\n",
660 Resource_getVdevStatus(VIRTIO_ID_RPMSG));
662 InterruptProxy_intRegister(hostProcId, &intInfo, (Fxn)VirtQueue_isr, NULL);
663 Log_print0(Diags_USER1, "Passed VirtQueue_startup\n");
664 }
666 /*!
667 * ======== VirtQueue_postCrashToMailbox ========
668 */
669 Void VirtQueue_postCrashToMailbox(Void)
670 {
671 InterruptProxy_intSend(0, NULL, (UInt)RP_MSG_MBOX_CRASH);
672 }
674 #define CACHE_WB_TICK_PERIOD 5
676 /*!
677 * ======== VirtQueue_cacheWb ========
678 *
679 * Used for flushing SysMin trace buffer.
680 */
681 Void VirtQueue_cacheWb()
682 {
683 static UInt32 oldticks = 0;
684 UInt32 newticks;
686 newticks = Clock_getTicks();
687 if (newticks - oldticks < (UInt32)CACHE_WB_TICK_PERIOD) {
688 /* Don't keep flushing cache */
689 return;
690 }
692 oldticks = newticks;
694 /* Flush the cache */
695 Cache_wbAll();
696 }