94f210d0205b7289e275073cdb24289383c88871
[ipc/ipcdev.git] / qnx / src / ipc3x_dev / ti / syslink / ipc / hlos / knl / transports / virtio / VirtQueue.c
1 /*
2  * Copyright (c) 2011-2014, 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/rpmsg/VirtQueue.h>
52  *  @endcode
53  *
54  */
56 #include <ti/syslink/Std.h>
57 #include <ti/syslink/utils/Memory.h>
58 #include <ti/syslink/utils/Trace.h>
60 #include <_ArchIpcInt.h>
61 #include <ArchIpcInt.h>
62 #include "VirtQueue.h"
64 #include <ti/ipc/MultiProc.h>
65 #include <ti/syslink/ProcMgr.h>
67 #include <ti/syslink/utils/String.h>
69 #include "virtio_ring.h"
70 #include "_rpmsg.h"
71 #include <ipu_pm.h>
73 /* Used for defining the size of the virtqueue registry */
74 #define NUM_QUEUES                      2
76 typedef struct VirtQueue_Object {
77     /* Id for this VirtQueue_Object */
78     UInt16                  id;
80     /* The function to call when buffers are consumed (can be NULL) */
81     VirtQueue_callback      callback;
83     /* Shared state */
84     struct vring            vring;
86     /* Number of free buffers */
87     UInt16                  num_free;
89     /* Last available index; updated by VirtQueue_getUsedBuf */
90     UInt16                  last_used_idx;
92     /* Will eventually be used to kick remote processor */
93     UInt16                  procId;
95     /* Interrupt Id for kicking remote processor */
96     UInt16                  intId;
98     /* Local virtual address for vring struct */
99     UInt32                  vaddr;
101     /* Physical address for vring struct */
102     UInt32                  paddr;
104     /* Private arg from user */
105     void *                  arg;
106 } VirtQueue_Object;
108 static UInt numQueues = 0;
109 static struct VirtQueue_Object *queueRegistry[MultiProc_MAXPROCESSORS][NUM_QUEUES];
111 static UInt32 coreIntId[MultiProc_MAXPROCESSORS];
113 static inline Void * mapPAtoVA(VirtQueue_Handle vq, UInt pa)
115     UInt offset = vq->paddr - pa;
116     return (Void *)(vq->vaddr - offset);
119 static inline UInt mapVAtoPA(VirtQueue_Handle vq, Void * va)
121     UInt offset = vq->vaddr - (UInt)va;
122     return (UInt)(vq->paddr - offset);
125 /*!
126  * ======== VirtQueue_cb ========
127  */
128 Void VirtQueue_cb(Void *buf, VirtQueue_Handle vq)
130     if (vq/* && vq->cb_enabled*/) {
131         /* Call the registered vq callback */
132         vq->callback(vq, vq->arg);
133     }
136 /*!
137  * ======== VirtQueue_kick ========
138  */
139 Void VirtQueue_kick(VirtQueue_Handle vq)
141     /*
142      * We need to expose available array entries before sending an
143      * interrupt.
144      */
145     asm("   DSB ST");
147     GT_2trace(curTrace, GT_2CLASS,
148             "VirtQueue_kick: Sending interrupt to proc %d with payload 0x%x",
149             vq->procId, vq->id);
151 #if defined (SYSLINK_USE_IPU_PM)
152     ipu_pm_restore_ctx(vq->procId);
153 #endif
154     ArchIpcInt_sendInterrupt(vq->procId, vq->intId, vq->id);
157 /*!
158  * ======== VirtQueue_addUsedBufAddr ========
159  */
160 Int VirtQueue_addUsedBufAddr(VirtQueue_Handle vq, Void *buf, UInt32 len)
162     struct vring_used_elem *used = NULL;
163     UInt16 head = 0;
164     Int status = 0;
166     if ((head > vq->vring.num) || (head < 0)) {
167         status = -1;
168         GT_setFailureReason (curTrace,
169                              GT_4CLASS,
170                              "VirtQueue_addUsedBuf",
171                              status,
172                              "head is invalid!");
173     }
174     else {
175         /*
176          * The virtqueue contains a ring of used buffers.  Get a pointer to the
177          * next entry in that used ring.
178          */
179         head = vq->vring.used->idx % vq->vring.num;
180         vq->vring.desc[head].addr = mapVAtoPA(vq, buf);
181         vq->vring.desc[head].len = len;
182         vq->vring.desc[head].flags = 0;
183         used = &vq->vring.used->ring[head];
184         used->id = head;
185         used->len = len;
187         vq->vring.used->idx++;
188     }
190     return status;
193 /*!
194  * ======== VirtQueue_addAvailBuf ========
195  */
196 Int VirtQueue_addAvailBuf(VirtQueue_Handle vq, Void *buf, UInt32 len, Int16 head)
198     UInt16 avail;
200     if (vq->num_free == 0) {
201         /* There's no more space */
202         GT_setFailureReason (curTrace,
203                              GT_4CLASS,
204                              "VirtQueue_addAvailBuf",
205                              (-1), // TODO: Make this a valid error code
206                              "no more space!");
208     }
209     else {
210         vq->num_free--;
212         avail = vq->vring.avail->idx % vq->vring.num;
213         vq->vring.avail->ring[avail] = head;
215         vq->vring.desc[head].addr = mapVAtoPA(vq, buf);
216         vq->vring.desc[head].len = len;
217         vq->vring.desc[head].flags = 0;
219         /*
220          * Descriptors and available array need to be set before we expose the
221          * new available array entries.
222          */
223         asm("   DMB ST");
225         vq->vring.avail->idx++;
226     }
228     return (vq->num_free);
231 /*!
232  * ======== VirtQueue_getUsedBuf ========
233  */
234 Int16 VirtQueue_getUsedBuf(VirtQueue_Object *vq, Void **buf)
236     UInt16 head;
238     /* There's nothing available? */
239     if (vq->last_used_idx == vq->vring.used->idx) {
240         /* We need to know about added buffers */
241         vq->vring.avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT;
243         return (-1);
244     }
246     /* Only get used array entries after they have been exposed. */
247     asm("   DMB");
249     /* No need to know be kicked about added buffers anymore */
250     //vq->vring.avail->flags |= VRING_AVAIL_F_NO_INTERRUPT; // disabling for now, since there seems to be a race condition where an M3->A9 message is not detected because the interrupt isn't sent.
252     head = vq->vring.used->ring[vq->last_used_idx % vq->vring.num].id;
253     vq->last_used_idx++;
254     vq->num_free++;
256     *buf = mapPAtoVA(vq, vq->vring.desc[head].addr);
258     return (head);
261 /*!
262  * ======== VirtQueue_disableCallback ========
263  */
264 Void VirtQueue_disableCallback(VirtQueue_Handle vq)
266     //TODO
267     GT_0trace(curTrace, GT_3CLASS, "VirtQueue_disableCallback not supported.");
270 /*!
271  * ======== VirtQueue_enableCallback ========
272  */
273 Bool VirtQueue_enableCallback(VirtQueue_Handle vq)
275     GT_0trace(curTrace, GT_3CLASS, "VirtQueue_enableCallback not supported.");
277     //TODO
278     return (FALSE);
281 /*!
282  *  @brief      This function implements the interrupt service routine for the
283  *              interrupt received from the remote cores.
284  *
285  *  @param      refData       object to be handled in ISR
286  *
287  *  @sa         VirtQueue_cb
288  */
289 static
290 Bool
291 VirtQueue_ISR (UInt32 msg, Void * arg)
293     UInt32 procId = (UInt32)arg;
294     ProcMgr_Handle handle = NULL;
295     Int status = 0;
297     GT_2trace (curTrace, GT_ENTER, "_VirtQueue_ISR", msg, arg);
299         /* Interrupt clear is done by ArchIpcInt. */
301         switch(msg) {
302             case (UInt)RP_MBOX_ECHO_REPLY:
303                 Osal_printf ("Echo reply from %s",
304                              MultiProc_getName(procId));
305                 break;
307             case (UInt)RP_MBOX_CRASH:
308                 Osal_printf ("Crash notification for %s",
309                              MultiProc_getName(procId));
310                 status = ProcMgr_open(&handle, procId);
311                 if (status >= 0) {
312                     ProcMgr_setState(handle, ProcMgr_State_Error);
313                     ProcMgr_close(&handle);
314                 }
315                 else {
316                     Osal_printf("Failed to open ProcMgr handle");
317                 }
318                 break;
320             case (UInt)RP_MBOX_BOOTINIT_DONE:
321                 Osal_printf ("Got BootInit done from %s",
322                              MultiProc_getName(procId));
323                 // TODO: What to do with this message?
324                 break;
326             case (UInt)RP_MBOX_HIBERNATION_ACK:
327                 Osal_printf ("Got Hibernation ACK from %s",
328                              MultiProc_getName(procId));
329                 break;
331             case (UInt)RP_MBOX_HIBERNATION_CANCEL:
332                 Osal_printf ("Got Hibernation CANCEL from %s",
333                              MultiProc_getName(procId));
334                 break;
336             default:
337                 /*
338                  *  If the message isn't one of the above, it's a virtqueue
339                  *  message
340                  */
341                 if (msg%2 < NUM_QUEUES) {
342                     /* This message is for us! */
343                     VirtQueue_cb((void *)msg, queueRegistry[procId][msg%2]);
344                 }
345             break;
346         }
348     return TRUE;
351 /*!
352  * ======== VirtQueue_create ========
353  */
354 VirtQueue_Handle VirtQueue_create (VirtQueue_callback callback, UInt16 procId,
355                                    UInt16 id, UInt32 vaddr, UInt32 paddr,
356                                    UInt32 num, UInt32 align, Void *arg)
358     VirtQueue_Object *vq = NULL;
360     vq = Memory_alloc(NULL, sizeof(VirtQueue_Object), 0, NULL);
361     if (!vq) {
362         return (NULL);
363     }
365     vq->callback = callback;
366     vq->id = id;
367     numQueues++;
368     vq->procId = procId;
369     vq->intId = coreIntId[procId];
370     vq->arg = arg;
372     /* init the vring */
373     vring_init(&(vq->vring), num, (void *)vaddr, align);
375     vq->num_free = num;
376     vq->last_used_idx = 0;
377     vq->vaddr = vaddr;
378     vq->paddr = paddr;
380     vq->vring.avail->idx = 0;
381     vq->vring.used->idx = 0;
383     /* Initialize the flags */
384     vq->vring.avail->flags = 0;
385     vq->vring.used->flags = 0;
387     /* Store the VirtQueue locally */
388     if (queueRegistry[procId][vq->id%2] == NULL)
389         queueRegistry[procId][vq->id%2] = vq;
390     else {
391         Osal_printf ("VirtQueue ID %d already created", id);
392         Memory_free(NULL, vq, sizeof(VirtQueue_Object));
393         vq = NULL;
394     }
396     return (vq);
399 /*!
400  * ======== VirtQueue_delete ========
401 */
402 Int VirtQueue_delete (VirtQueue_Handle * vq)
404     VirtQueue_Object * obj = (VirtQueue_Object *)(*vq);
405     /* Store the VirtQueue locally */
406     queueRegistry[obj->procId][obj->id%2] = NULL;
407     Memory_free(NULL, obj, sizeof(VirtQueue_Object));
408     *vq = NULL;
409     numQueues--;
411     return 0;
414 /*!
415  * ======== VirtQueue_startup ========
416  */
417 Void VirtQueue_startup(UInt16 procId, UInt32 intId, UInt32 paddr)
419     Int32 status = 0;
420     UInt32 arg = procId;
422     coreIntId[procId] = intId;
424     /* Register for interrupts with CORE0, CORE1 messages come through CORE0 */
425     status = ArchIpcInt_interruptRegister (procId,
426                                            intId,
427                                            VirtQueue_ISR, (Ptr)arg);
428     if (status >= 0) {
429         /* Notify the remote proc that the mbox is ready */
430         status = ArchIpcInt_sendInterrupt (procId,
431                                            intId,
432                                            RP_MBOX_READY);
433     }
436 /*!
437  * ======== VirtQueue_startup ========
438  */
439 Void VirtQueue_destroy(UInt16 procId)
441     Int32 status = 0;
442     Int i = 0;
444     for (i = 0; i < NUM_QUEUES; i++) {
445         if (queueRegistry[procId][i]) {
446             VirtQueue_delete(&queueRegistry[procId][i]);
447             queueRegistry[procId][i] = NULL;
448         }
449     }
451     /* Un-register for interrupts with CORE0 */
452     status = ArchIpcInt_interruptUnregister (procId);
453     if (status < 0) {
454         GT_setFailureReason (curTrace,
455                              GT_4CLASS,
456                              "VirtQueue_destroy",
457                              status,
458                              "ArchIpcInt_interruptUnregister failed");
459     }