Messages received multiple times in QNX when IPU2 + DSP1/IPU1 are active
[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>
72 #include <pthread.h>
74 /* Used for defining the size of the virtqueue registry */
75 #define NUM_QUEUES                      2
77 typedef struct VirtQueue_Object {
78     /* Id for this VirtQueue_Object */
79     UInt16                  id;
81     /* The function to call when buffers are consumed (can be NULL) */
82     VirtQueue_callback      callback;
84     /* Shared state */
85     struct vring            vring;
87     /* Number of free buffers */
88     UInt16                  num_free;
90     /* Last available index; updated by VirtQueue_getUsedBuf */
91     UInt16                  last_used_idx;
93     /* Will eventually be used to kick remote processor */
94     UInt16                  procId;
96     /* Interrupt Id for kicking remote processor */
97     UInt16                  intId;
99     /* Local virtual address for vring struct */
100     UInt32                  vaddr;
102     /* Physical address for vring struct */
103     UInt32                  paddr;
105     /* Private arg from user */
106     void *                  arg;
108     /* Mutex to protect vrings from multi-thread access */
109     pthread_mutex_t         mutex;
110 } VirtQueue_Object;
112 static UInt numQueues = 0;
113 static struct VirtQueue_Object *queueRegistry[MultiProc_MAXPROCESSORS][NUM_QUEUES];
115 static UInt32 coreIntId[MultiProc_MAXPROCESSORS];
117 static inline Void * mapPAtoVA(VirtQueue_Handle vq, UInt pa)
119     UInt offset = vq->paddr - pa;
120     return (Void *)(vq->vaddr - offset);
123 static inline UInt mapVAtoPA(VirtQueue_Handle vq, Void * va)
125     UInt offset = vq->vaddr - (UInt)va;
126     return (UInt)(vq->paddr - offset);
129 /*!
130  * ======== VirtQueue_cb ========
131  */
132 Void VirtQueue_cb(Void *buf, VirtQueue_Handle vq)
134     if (vq/* && vq->cb_enabled*/) {
135         /* Call the registered vq callback */
136         vq->callback(vq, vq->arg);
137     }
140 /*!
141  * ======== VirtQueue_kick ========
142  */
143 Void VirtQueue_kick(VirtQueue_Handle vq)
145     /*
146      * We need to expose available array entries before sending an
147      * interrupt.
148      */
149     asm("   DSB ST");
151     GT_2trace(curTrace, GT_2CLASS,
152             "VirtQueue_kick: Sending interrupt to proc %d with payload 0x%x",
153             vq->procId, vq->id);
155 #if defined (SYSLINK_USE_IPU_PM)
156     ipu_pm_restore_ctx(vq->procId);
157 #endif
158     ArchIpcInt_sendInterrupt(vq->procId, vq->intId, vq->id);
161 /*!
162  * ======== VirtQueue_addUsedBufAddr ========
163  */
164 Int VirtQueue_addUsedBufAddr(VirtQueue_Handle vq, Void *buf, UInt32 len)
166     struct vring_used_elem *used = NULL;
167     UInt16 head = 0;
168     Int status = 0;
170     if ((head > vq->vring.num) || (head < 0)) {
171         status = -1;
172         GT_setFailureReason (curTrace,
173                              GT_4CLASS,
174                              "VirtQueue_addUsedBuf",
175                              status,
176                              "head is invalid!");
177     }
178     else {
179         /*
180          * The virtqueue contains a ring of used buffers.  Get a pointer to the
181          * next entry in that used ring.
182          */
183         head = vq->vring.used->idx % vq->vring.num;
184         vq->vring.desc[head].addr = mapVAtoPA(vq, buf);
185         vq->vring.desc[head].len = len;
186         vq->vring.desc[head].flags = 0;
187         used = &vq->vring.used->ring[head];
188         used->id = head;
189         used->len = len;
191         vq->vring.used->idx++;
192     }
194     return status;
197 /*!
198  * ======== VirtQueue_addAvailBuf ========
199  */
200 Int VirtQueue_addAvailBuf(VirtQueue_Handle vq, Void *buf, UInt32 len, Int16 head)
202     UInt16 avail;
204     pthread_mutex_lock(&vq->mutex);
206     if (vq->num_free == 0) {
207         /* There's no more space */
208         GT_setFailureReason (curTrace,
209                              GT_4CLASS,
210                              "VirtQueue_addAvailBuf",
211                              (-1), // TODO: Make this a valid error code
212                              "no more space!");
214     }
215     else {
216         vq->num_free--;
218         avail = vq->vring.avail->idx % vq->vring.num;
219         vq->vring.avail->ring[avail] = head;
221         vq->vring.desc[head].addr = mapVAtoPA(vq, buf);
222         vq->vring.desc[head].len = len;
223         vq->vring.desc[head].flags = 0;
225         /*
226          * Descriptors and available array need to be set before we expose the
227          * new available array entries.
228          */
229         asm("   DMB ST");
231         vq->vring.avail->idx++;
232     }
234     pthread_mutex_unlock(&vq->mutex);
236     return (vq->num_free);
239 /*!
240  * ======== VirtQueue_getUsedBuf ========
241  */
242 Int16 VirtQueue_getUsedBuf(VirtQueue_Object *vq, Void **buf)
244     UInt16 head;
246     pthread_mutex_lock(&vq->mutex);
248     /* There's nothing available? */
249     if (vq->last_used_idx == vq->vring.used->idx) {
250         /* We need to know about added buffers */
251         vq->vring.avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT;
253         pthread_mutex_unlock(&vq->mutex);
255         return (-1);
256     }
258     /* Only get used array entries after they have been exposed. */
259     asm("   DMB");
261     /* No need to know be kicked about added buffers anymore */
262     //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.
264     head = vq->vring.used->ring[vq->last_used_idx % vq->vring.num].id;
265     vq->last_used_idx++;
266     vq->num_free++;
268     pthread_mutex_unlock(&vq->mutex);
270     *buf = mapPAtoVA(vq, vq->vring.desc[head].addr);
272     return (head);
275 /*!
276  * ======== VirtQueue_disableCallback ========
277  */
278 Void VirtQueue_disableCallback(VirtQueue_Handle vq)
280     //TODO
281     GT_0trace(curTrace, GT_3CLASS, "VirtQueue_disableCallback not supported.");
284 /*!
285  * ======== VirtQueue_enableCallback ========
286  */
287 Bool VirtQueue_enableCallback(VirtQueue_Handle vq)
289     GT_0trace(curTrace, GT_3CLASS, "VirtQueue_enableCallback not supported.");
291     //TODO
292     return (FALSE);
295 /*!
296  *  @brief      This function implements the interrupt service routine for the
297  *              interrupt received from the remote cores.
298  *
299  *  @param      refData       object to be handled in ISR
300  *
301  *  @sa         VirtQueue_cb
302  */
303 static
304 Bool
305 VirtQueue_ISR (UInt32 msg, Void * arg)
307     UInt32 procId = (UInt32)arg;
308     ProcMgr_Handle handle = NULL;
309     Int status = 0;
311     GT_2trace (curTrace, GT_ENTER, "_VirtQueue_ISR", msg, arg);
313         /* Interrupt clear is done by ArchIpcInt. */
315         switch(msg) {
316             case (UInt)RP_MBOX_ECHO_REPLY:
317                 Osal_printf ("Echo reply from %s",
318                              MultiProc_getName(procId));
319                 break;
321             case (UInt)RP_MBOX_CRASH:
322                 Osal_printf ("Crash notification for %s",
323                              MultiProc_getName(procId));
324                 status = ProcMgr_open(&handle, procId);
325                 if (status >= 0) {
326                     ProcMgr_setState(handle, ProcMgr_State_Error);
327                     ProcMgr_close(&handle);
328                 }
329                 else {
330                     Osal_printf("Failed to open ProcMgr handle");
331                 }
332                 break;
334             case (UInt)RP_MBOX_BOOTINIT_DONE:
335                 Osal_printf ("Got BootInit done from %s",
336                              MultiProc_getName(procId));
337                 // TODO: What to do with this message?
338                 break;
340             case (UInt)RP_MBOX_HIBERNATION_ACK:
341                 Osal_printf ("Got Hibernation ACK from %s",
342                              MultiProc_getName(procId));
343                 break;
345             case (UInt)RP_MBOX_HIBERNATION_CANCEL:
346                 Osal_printf ("Got Hibernation CANCEL from %s",
347                              MultiProc_getName(procId));
348                 break;
350             default:
351                 /*
352                  *  If the message isn't one of the above, it's a virtqueue
353                  *  message
354                  */
355                 if (msg%2 < NUM_QUEUES) {
356                     /* This message is for us! */
357                     VirtQueue_cb((void *)msg, queueRegistry[procId][msg%2]);
358                 }
359             break;
360         }
362     return TRUE;
365 /*!
366  * ======== VirtQueue_create ========
367  */
368 VirtQueue_Handle VirtQueue_create (VirtQueue_callback callback, UInt16 procId,
369                                    UInt16 id, UInt32 vaddr, UInt32 paddr,
370                                    UInt32 num, UInt32 align, Void *arg)
372     VirtQueue_Object *vq = NULL;
374     vq = Memory_alloc(NULL, sizeof(VirtQueue_Object), 0, NULL);
375     if (!vq) {
376         return (NULL);
377     }
379     vq->callback = callback;
380     vq->id = id;
381     numQueues++;
382     vq->procId = procId;
383     vq->intId = coreIntId[procId];
384     vq->arg = arg;
386     /* init the vring */
387     vring_init(&(vq->vring), num, (void *)vaddr, align);
389     vq->num_free = num;
390     vq->last_used_idx = 0;
391     vq->vaddr = vaddr;
392     vq->paddr = paddr;
394     vq->vring.avail->idx = 0;
395     vq->vring.used->idx = 0;
397     /* Initialize the flags */
398     vq->vring.avail->flags = 0;
399     vq->vring.used->flags = 0;
401     /* Store the VirtQueue locally */
402     if (queueRegistry[procId][vq->id%2] == NULL)
403         queueRegistry[procId][vq->id%2] = vq;
404     else {
405         Osal_printf ("VirtQueue ID %d already created", id);
406         Memory_free(NULL, vq, sizeof(VirtQueue_Object));
407         vq = NULL;
408     }
410     /* Initialize mutex */
411     pthread_mutex_init(&vq->mutex, NULL);
413     return (vq);
416 /*!
417  * ======== VirtQueue_delete ========
418 */
419 Int VirtQueue_delete (VirtQueue_Handle * vq)
421     VirtQueue_Object * obj = (VirtQueue_Object *)(*vq);
422     /* Store the VirtQueue locally */
423     queueRegistry[obj->procId][obj->id%2] = NULL;
424     Memory_free(NULL, obj, sizeof(VirtQueue_Object));
425     *vq = NULL;
426     numQueues--;
428     return 0;
431 /*!
432  * ======== VirtQueue_startup ========
433  */
434 Void VirtQueue_startup(UInt16 procId, UInt32 intId, UInt32 paddr)
436     Int32 status = 0;
437     UInt32 arg = procId;
439     coreIntId[procId] = intId;
441     /* Register for interrupts with CORE0, CORE1 messages come through CORE0 */
442     status = ArchIpcInt_interruptRegister (procId,
443                                            intId,
444                                            VirtQueue_ISR, (Ptr)arg);
445     if (status >= 0) {
446         /* Notify the remote proc that the mbox is ready */
447         status = ArchIpcInt_sendInterrupt (procId,
448                                            intId,
449                                            RP_MBOX_READY);
450     }
453 /*!
454  * ======== VirtQueue_startup ========
455  */
456 Void VirtQueue_destroy(UInt16 procId)
458     Int32 status = 0;
459     Int i = 0;
461     for (i = 0; i < NUM_QUEUES; i++) {
462         if (queueRegistry[procId][i]) {
463             VirtQueue_delete(&queueRegistry[procId][i]);
464             queueRegistry[procId][i] = NULL;
465         }
466     }
468     /* Un-register for interrupts with CORE0 */
469     status = ArchIpcInt_interruptUnregister (procId);
470     if (status < 0) {
471         GT_setFailureReason (curTrace,
472                              GT_4CLASS,
473                              "VirtQueue_destroy",
474                              status,
475                              "ArchIpcInt_interruptUnregister failed");
476     }