VirtQueue: OMAP5: Poll for VDEV status in VirtQueue_startup()
[ipc/ipcdev.git] / packages / ti / ipc / family / omap54xx / VirtQueue.c
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/omap54xx/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.omap54xx.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 #include <ti/pm/IpcPower.h>
79 #include <string.h>
81 #include <ti/ipc/remoteproc/Resource.h>
82 #include <ti/ipc/remoteproc/rsc_types.h>
83 #include <ti/ipc/rpmsg/_VirtQueue.h>
85 #include "InterruptProxy.h"
86 #include "VirtQueue.h"
89 /*
90  *  The following three VIRTIO_* defines must match those in
91  *  <Linux_kernel>/include/uapi/linux/virtio_config.h
92  */
93 #define VIRTIO_CONFIG_S_ACKNOWLEDGE     1
94 #define VIRTIO_CONFIG_S_DRIVER          2
95 #define VIRTIO_CONFIG_S_DRIVER_OK       4
97 #define VRING_BUFS_PRIMED  (VIRTIO_CONFIG_S_ACKNOWLEDGE | \
98                             VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK)
100 /* Used for defining the size of the virtqueue registry */
101 #define NUM_QUEUES              4
103 /* Predefined device addresses */
104 #ifndef DSPC674
105 #define IPC_MEM_VRING0          0xA0000000
106 #define IPC_MEM_VRING1          0xA0004000
107 #else
108 #define IPC_MEM_VRING0          0x9FB00000
109 #define IPC_MEM_VRING1          0x9FB04000
110 #endif
111 #define IPC_MEM_VRING2          0xA0008000
112 #define IPC_MEM_VRING3          0xA000c000
114 /*
115  * Sizes of the virtqueues (expressed in number of buffers supported,
116  * and must be power of two)
117  */
118 #define VQ0_SIZE                256
119 #define VQ1_SIZE                256
120 #define VQ2_SIZE                256
121 #define VQ3_SIZE                256
123 /*
124  * enum - Predefined Mailbox Messages
125  *
126  * @RP_MSG_MBOX_READY: informs the M3's that we're up and running. will be
127  * followed by another mailbox message that carries the A9's virtual address
128  * of the shared buffer. This would allow the A9's drivers to send virtual
129  * addresses of the buffers.
130  *
131  * @RP_MSG_MBOX_STATE_CHANGE: informs the receiver that there is an inbound
132  * message waiting in its own receive-side vring. please note that currently
133  * this message is optional: alternatively, one can explicitly send the index
134  * of the triggered virtqueue itself. the preferred approach will be decided
135  * as we progress and experiment with those design ideas.
136  *
137  * @RP_MSG_MBOX_CRASH: this message indicates that the BIOS side is unhappy
138  *
139  * @RP_MBOX_ECHO_REQUEST: this message requests the remote processor to reply
140  * with RP_MBOX_ECHO_REPLY
141  *
142  * @RP_MBOX_ECHO_REPLY: this is a reply that is sent when RP_MBOX_ECHO_REQUEST
143  * is received.
144  *
145  * @RP_MBOX_ABORT_REQUEST:  tells the M3 to crash on demand
146  *
147  * @RP_MBOX_BOOTINIT_DONE: this message indicates the BIOS side has reached a
148  * certain state during the boot process. This message is used to inform the
149  * host that the basic BIOS initialization is done, and lets the host use this
150  * notification to perform certain actions.
151  */
152 enum {
153     RP_MSG_MBOX_READY           = (Int)0xFFFFFF00,
154     RP_MSG_MBOX_STATE_CHANGE    = (Int)0xFFFFFF01,
155     RP_MSG_MBOX_CRASH           = (Int)0xFFFFFF02,
156     RP_MBOX_ECHO_REQUEST        = (Int)0xFFFFFF03,
157     RP_MBOX_ECHO_REPLY          = (Int)0xFFFFFF04,
158     RP_MBOX_ABORT_REQUEST       = (Int)0xFFFFFF05,
159     RP_MSG_FLUSH_CACHE          = (Int)0xFFFFFF06,
160     RP_MSG_BOOTINIT_DONE        = (Int)0xFFFFFF07,
161     RP_MSG_HIBERNATION          = (Int)0xFFFFFF10,
162     RP_MSG_HIBERNATION_FORCE    = (Int)0xFFFFFF11,
163     RP_MSG_HIBERNATION_ACK      = (Int)0xFFFFFF12,
164     RP_MSG_HIBERNATION_CANCEL   = (Int)0xFFFFFF13
165 };
167 #define DIV_ROUND_UP(n,d)   (((n) + (d) - 1) / (d))
168 #define RP_MSG_NUM_BUFS     (VQ0_SIZE) /* must be power of two */
169 #define RP_MSG_BUF_SIZE     (512)
170 #define RP_MSG_BUFS_SPACE   (RP_MSG_NUM_BUFS * RP_MSG_BUF_SIZE * 2)
172 #define PAGE_SIZE           (4096)
173 /*
174  * The alignment to use between consumer and producer parts of vring.
175  * Note: this is part of the "wire" protocol. If you change this, you need
176  * to update your BIOS image as well
177  */
178 #define RP_MSG_VRING_ALIGN  (4096)
180 /* With 256 buffers, our vring will occupy 3 pages */
181 #define RP_MSG_RING_SIZE    ((DIV_ROUND_UP(vring_size(RP_MSG_NUM_BUFS, \
182                             RP_MSG_VRING_ALIGN), PAGE_SIZE)) * PAGE_SIZE)
184 /* The total IPC space needed to communicate with a remote processor */
185 #define RPMSG_IPC_MEM   (RP_MSG_BUFS_SPACE + 2 * RP_MSG_RING_SIZE)
187 #define ID_SYSM3_TO_A9      ID_SELF_TO_A9
188 #define ID_A9_TO_SYSM3      ID_A9_TO_SELF
189 #define ID_DSP_TO_A9        ID_SELF_TO_A9
190 #define ID_A9_TO_DSP        ID_A9_TO_SELF
191 #define ID_APPM3_TO_A9      2
192 #define ID_A9_TO_APPM3      3
194 typedef struct VirtQueue_Object {
195     /* Id for this VirtQueue_Object */
196     UInt16                  id;
198     /* The function to call when buffers are consumed (can be NULL) */
199     VirtQueue_callback      callback;
201     /* Shared state */
202     struct vring            vring;
204     /* Number of free buffers */
205     UInt16                  num_free;
207     /* Last available index; updated by VirtQueue_getAvailBuf */
208     UInt16                  last_avail_idx;
210     /* Last available index; updated by VirtQueue_addUsedBuf */
211     UInt16                  last_used_idx;
213     /* Will eventually be used to kick remote processor */
214     UInt16                  procId;
216     /* Gate to protect from multiple threads */
217     GateHwi_Handle       gateH;
218 } VirtQueue_Object;
220 /* module diags mask */
221 Registry_Desc Registry_CURDESC;
223 static struct VirtQueue_Object *queueRegistry[NUM_QUEUES] = {NULL};
225 static UInt16 hostProcId;
226 #ifndef SMP
227 static UInt16 dspProcId;
228 static UInt16 sysm3ProcId;
229 static UInt16 appm3ProcId;
230 #endif
232 #if defined(M3_ONLY) && !defined(SMP)
233 extern Void OffloadM3_init();
234 extern Int OffloadM3_processSysM3Tasks(UArg msg);
235 #endif
237 /*!
238  * ======== _VirtQueue_init ========
239  *
240  * This function adds the VirtQueue "module" to the Registry so that
241  * DIAGS will work with this non-XDC module.
242  * Since VirtQueue_init is not called by XDC-VirtQueue module clients, this
243  * function is called in the first VirtQueue fxn called: VirtQueue_create.
244  */
245 static Void _VirtQueue_init()
247     static int initialized = 0;
249     if (!initialized) {
250         Registry_Result result;
252         /* register with xdc.runtime to get a diags mask */
253         result = Registry_addModule(&Registry_CURDESC, MODULE_NAME);
254         Assert_isTrue(result == Registry_SUCCESS, (Assert_Id)NULL);
256         initialized = 1;
257     }
260 static inline Void * mapPAtoVA(UInt pa)
262     return (Void *)((pa & 0x000fffffU) | IPC_MEM_VRING0);
265 static inline UInt mapVAtoPA(Void * va)
267     return ((UInt)va & 0x000fffffU) | 0x9cf00000U;
270 /*!
271  * ======== VirtQueue_kick ========
272  */
273 Void VirtQueue_kick(VirtQueue_Handle vq)
275     /* For now, simply interrupt remote processor */
276     if (vq->vring.avail->flags & VRING_AVAIL_F_NO_INTERRUPT) {
277         Log_print0(Diags_USER1,
278                 "VirtQueue_kick: no kick because of VRING_AVAIL_F_NO_INTERRUPT\n");
279         return;
280     }
282     Log_print2(Diags_USER1,
283             "VirtQueue_kick: Sending interrupt to proc %d with payload 0x%x\n",
284             (IArg)vq->procId, (IArg)vq->id);
285     InterruptProxy_intSend(vq->procId, vq->id);
288 /*!
289  * ======== VirtQueue_addUsedBuf ========
290  */
291 Int VirtQueue_addUsedBuf(VirtQueue_Handle vq, Int16 head, Int len)
293     struct vring_used_elem *used;
294     IArg key;
296     key = GateHwi_enter(vq->gateH);
297     if ((head > vq->vring.num) || (head < 0)) {
298         GateHwi_leave(vq->gateH, key);
299         Error_raise(NULL, Error_E_generic, 0, 0);
300     }
302     /*
303     * The virtqueue contains a ring of used buffers.  Get a pointer to the
304     * next entry in that used ring.
305     */
306     used = &vq->vring.used->ring[vq->vring.used->idx % vq->vring.num];
307     used->id = head;
308     used->len = len;
310     vq->vring.used->idx++;
311     GateHwi_leave(vq->gateH, key);
313     return (0);
316 /*!
317  * ======== VirtQueue_addAvailBuf ========
318  */
319 Int VirtQueue_addAvailBuf(VirtQueue_Object *vq, Void *buf)
321     UInt16 avail;
322     IArg key;
324     if (vq->num_free == 0) {
325         /* There's no more space */
326         Error_raise(NULL, Error_E_generic, 0, 0);
327     }
329     vq->num_free--;
331     key = GateHwi_enter(vq->gateH);
332     avail =  vq->vring.avail->idx++ % vq->vring.num;
334     vq->vring.desc[avail].addr = mapVAtoPA(buf);
335     vq->vring.desc[avail].len = RP_MSG_BUF_SIZE;
336     GateHwi_leave(vq->gateH, key);
338     return (vq->num_free);
341 /*!
342  * ======== VirtQueue_getUsedBuf ========
343  */
344 Void *VirtQueue_getUsedBuf(VirtQueue_Object *vq)
346     UInt16 head;
347     Void *buf;
348     IArg key;
350     key = GateHwi_enter(vq->gateH);
351     /* There's nothing available? */
352     if (vq->last_used_idx == vq->vring.used->idx) {
353         buf = NULL;
354     }
355     else {
356         head = vq->vring.used->ring[vq->last_used_idx % vq->vring.num].id;
357         vq->last_used_idx++;
359         buf = mapPAtoVA(vq->vring.desc[head].addr);
360     }
361     GateHwi_leave(vq->gateH, key);
363     return (buf);
366 /*!
367  * ======== VirtQueue_getAvailBuf ========
368  */
369 Int16 VirtQueue_getAvailBuf(VirtQueue_Handle vq, Void **buf, Int *len)
371     Int16 head;
372     IArg key;
374     key = GateHwi_enter(vq->gateH);
375     Log_print6(Diags_USER1, "getAvailBuf vq: 0x%x %d %d %d 0x%x 0x%x\n",
376         (IArg)vq, vq->last_avail_idx, vq->vring.avail->idx, vq->vring.num,
377         (IArg)&vq->vring.avail, (IArg)vq->vring.avail);
379     /* There's nothing available? */
380     if (vq->last_avail_idx == vq->vring.avail->idx) {
381         /* We need to know about added buffers */
382         vq->vring.used->flags &= ~VRING_USED_F_NO_NOTIFY;
383         head = (-1);
384     }
385     else {
386         /*
387          * Grab the next descriptor number they're advertising, and increment
388          * the index we've seen.
389          */
390         head = vq->vring.avail->ring[vq->last_avail_idx++ % vq->vring.num];
392         *buf = mapPAtoVA(vq->vring.desc[head].addr);
393         *len = vq->vring.desc[head].len;
394     }
395     GateHwi_leave(vq->gateH, key);
397     return (head);
400 /*!
401  * ======== VirtQueue_disableCallback ========
402  */
403 Void VirtQueue_disableCallback(VirtQueue_Object *vq)
405     /* TODO */
406     Log_print0(Diags_USER1, "VirtQueue_disableCallback called.");
409 /*!
410  * ======== VirtQueue_enableCallback ========
411  */
412 Bool VirtQueue_enableCallback(VirtQueue_Object *vq)
414     Log_print0(Diags_USER1, "VirtQueue_enableCallback called.");
416     /* TODO */
417     return (FALSE);
420 /*!
421  * ======== VirtQueue_isr ========
422  * Note 'arg' is ignored: it is the Hwi argument, not the mailbox argument.
423  */
424 Void VirtQueue_isr(UArg msg)
426     VirtQueue_Object *vq;
428     Log_print1(Diags_USER1, "VirtQueue_isr received msg = 0x%x\n", msg);
430 #ifndef SMP
431     if (MultiProc_self() == sysm3ProcId || MultiProc_self() == dspProcId) {
432 #endif
433         switch(msg) {
434             case (UInt)RP_MSG_MBOX_READY:
435                 return;
437             case (UInt)RP_MBOX_ECHO_REQUEST:
438                 InterruptProxy_intSend(hostProcId, (UInt)(RP_MBOX_ECHO_REPLY));
439                 return;
441             case (UInt)RP_MBOX_ABORT_REQUEST:
442                 {
443                     /* Suppress Coverity Error: FORWARD_NULL: */
444                     /* coverity[assign_zero] */
445                     Fxn f = (Fxn)0x0;
446                     Log_print0(Diags_USER1, "Crash on demand ...\n");
447                     /* coverity[var_deref_op] */
448                     f();
449                 }
450                 return;
452             case (UInt)RP_MSG_FLUSH_CACHE:
453                 Cache_wbAll();
454                 return;
456 #ifndef DSPC674
457             case (UInt)RP_MSG_HIBERNATION:
458                 if (IpcPower_canHibernate() == FALSE) {
459                     InterruptProxy_intSend(hostProcId,
460                                         (UInt)RP_MSG_HIBERNATION_CANCEL);
461                     return;
462                 }
464             /* Fall through */
465             case (UInt)RP_MSG_HIBERNATION_FORCE:
466 #ifndef SMP
467                 /* Core0 should notify Core1 */
468                 if (MultiProc_self() == sysm3ProcId) {
469                     InterruptProxy_intSend(appm3ProcId,
470                                            (UInt)(RP_MSG_HIBERNATION));
471                 }
472 #endif
473                 /* Ack request */
474                 InterruptProxy_intSend(hostProcId,
475                                     (UInt)RP_MSG_HIBERNATION_ACK);
476                 IpcPower_suspend();
477                 return;
478 #endif
479             default:
480 #if defined(M3_ONLY) && !defined(SMP)
481                 /* Check and process any Inter-M3 Offload messages */
482                 if (OffloadM3_processSysM3Tasks(msg))
483                     return;
484 #endif
486                 /*
487                  *  If the message isn't one of the above, it's either part of the
488                  *  2-message synchronization sequence or it a virtqueue message
489                  */
490                 break;
491         }
492 #ifndef SMP
493     }
494 #ifndef DSPC674
495     else if (msg & 0xFFFF0000) {
496         if (msg == (UInt)RP_MSG_HIBERNATION) {
497             IpcPower_suspend();
498         }
499         return;
500     }
502     if (MultiProc_self() == sysm3ProcId && (msg == ID_A9_TO_APPM3 || msg == ID_APPM3_TO_A9)) {
503         InterruptProxy_intSend(appm3ProcId, (UInt)msg);
504     }
505     else {
506 #endif
507 #endif
508         /* Don't let unknown messages to pass as a virtqueue index */
509         if (msg >= NUM_QUEUES) {
510             /* Adding print here deliberately, we should never see this */
511             System_printf("VirtQueue_isr: Invalid mailbox message 0x%x "
512                           "received\n", msg);
513             return;
514         }
516         vq = queueRegistry[msg];
517         if (vq) {
518             vq->callback(vq);
519         }
520 #ifndef SMP
521 #ifndef DSPC674
522     }
523 #endif
524 #endif
528 /*!
529  * ======== VirtQueue_create ========
530  */
531 VirtQueue_Handle VirtQueue_create(UInt16 remoteProcId, VirtQueue_Params *params,
532                                   Error_Block *eb)
534     VirtQueue_Object *vq;
535     Void *vringAddr;
537     /* Perform initialization we can't do in Instance_init (being non-XDC): */
538     _VirtQueue_init();
540     vq = Memory_alloc(NULL, sizeof(VirtQueue_Object), 0, eb);
541     if (NULL == vq) {
542         return (NULL);
543     }
545     /* Create the thread protection gate */
546     vq->gateH = GateHwi_create(NULL, eb);
547     if (Error_check(eb)) {
548         Log_error0("VirtQueue_create: could not create gate object");
549         Memory_free(NULL, vq, sizeof(VirtQueue_Object));
550         return (NULL);
551     }
553     vq->callback = params->callback;
554     vq->id = params->vqId;
555     vq->procId = remoteProcId;
556     vq->last_avail_idx = 0;
558 #ifndef SMP
559     if (MultiProc_self() == appm3ProcId) {
560         /* vqindices that belong to AppM3 should be big so they don't
561          * collide with SysM3's virtqueues */
562          vq->id += 2;
563     }
564 #endif
566     switch (vq->id) {
567         /* IPC transport vrings */
568         case ID_SELF_TO_A9:
569             /* IPU/DSP -> A9 */
570             vringAddr = (struct vring *) IPC_MEM_VRING0;
571             break;
572         case ID_A9_TO_SELF:
573             /* A9 -> IPU/DSP */
574             vringAddr = (struct vring *) IPC_MEM_VRING1;
575             break;
576 #ifndef SMP
577         case ID_APPM3_TO_A9:
578             /* APPM3 -> A9 */
579             vringAddr = (struct vring *) IPC_MEM_VRING2;
580             break;
581         case ID_A9_TO_APPM3:
582             /* A9 -> APPM3 */
583             vringAddr = (struct vring *) IPC_MEM_VRING3;
584             break;
585 #endif
586         default:
587             GateHwi_delete(&vq->gateH);
588             Memory_free(NULL, vq, sizeof(VirtQueue_Object));
589             return (NULL);
590     }
592     Log_print3(Diags_USER1,
593             "vring: %d 0x%x (0x%x)\n", vq->id, (IArg)vringAddr,
594             RP_MSG_RING_SIZE);
596     /* See coverity related comment in vring_init() */
597     /* coverity[overrun-call] */
598     vring_init(&(vq->vring), RP_MSG_NUM_BUFS, vringAddr, RP_MSG_VRING_ALIGN);
600     /*
601      *  Don't trigger a mailbox message every time MPU makes another buffer
602      *  available
603      */
604     if (vq->procId == hostProcId) {
605         vq->vring.used->flags &= ~VRING_USED_F_NO_NOTIFY;
606     }
608     queueRegistry[vq->id] = vq;
610     return (vq);
613 /*!
614  * ======== VirtQueue_startup ========
615  */
616 Void VirtQueue_startup()
618     hostProcId      = MultiProc_getId("HOST");
619 #ifndef SMP
620     dspProcId       = MultiProc_getId("DSP");
621     sysm3ProcId     = MultiProc_getId("CORE0");
622     appm3ProcId     = MultiProc_getId("CORE1");
623 #endif
625 #ifndef DSPC674
626     /* Initilize the IpcPower module */
627     IpcPower_init();
628 #endif
630 #if defined(M3_ONLY) && !defined(SMP)
631     if (MultiProc_self() == sysm3ProcId) {
632         OffloadM3_init();
633     }
634 #endif
636     /*
637      * Wait for HLOS (Virtio device) to indicate that priming of host's receive
638      * buffers is complete, indicating that host is ready to send.
639      *
640      * Though this is a Linux Virtio configuration status, it must be
641      * implemented by each non-Linux HLOS as well.
642      */
643     Log_print1(Diags_USER1, "VirtQueue_startup: VDEV status: 0x%x\n",
644               Resource_getVdevStatus(VIRTIO_ID_RPMSG));
645     Log_print0(Diags_USER1, "VirtQueue_startup: Polling VDEV status...\n");
646     while (Resource_getVdevStatus(VIRTIO_ID_RPMSG) != VRING_BUFS_PRIMED);
647     Log_print1(Diags_USER1, "VirtQueue_startup: VDEV status: 0x%x\n",
648               Resource_getVdevStatus(VIRTIO_ID_RPMSG));
650     InterruptProxy_intRegister(VirtQueue_isr);
651     Log_print0(Diags_USER1, "Passed VirtQueue_startup\n");
654 /*!
655  * ======== VirtQueue_postCrashToMailbox ========
656  */
657 Void VirtQueue_postCrashToMailbox(Void)
659     InterruptProxy_intSend(0, (UInt)RP_MSG_MBOX_CRASH);
662 #define CACHE_WB_TICK_PERIOD    5
664 /*!
665  * ======== VirtQueue_cacheWb ========
666  *
667  * Used for flushing SysMin trace buffer.
668  */
669 Void VirtQueue_cacheWb()
671     static UInt32 oldticks = 0;
672     UInt32 newticks;
674     newticks = Clock_getTicks();
675     if (newticks - oldticks < (UInt32)CACHE_WB_TICK_PERIOD) {
676         /* Don't keep flushing cache */
677         return;
678     }
680     oldticks = newticks;
682     /* Flush the cache */
683     Cache_wbAll();