8fc1c094dda77353c40db453f52a8358572cbe59
[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     /* For now, simply interrupt remote processor */
142     if (vq->vring.used->flags & VRING_USED_F_NO_NOTIFY) {
143         GT_0trace(curTrace, GT_3CLASS,
144                 "VirtQueue_kick: no kick because of VRING_USED_F_NO_NOTIFY");
145         return;
146     }
148     GT_2trace(curTrace, GT_2CLASS,
149             "VirtQueue_kick: Sending interrupt to proc %d with payload 0x%x",
150             vq->procId, vq->id);
152 #if defined (SYSLINK_USE_IPU_PM)
153     ipu_pm_restore_ctx(vq->procId);
154 #endif
155     ArchIpcInt_sendInterrupt(vq->procId, vq->intId, vq->id);
158 /*!
159  * ======== VirtQueue_addUsedBufAddr ========
160  */
161 Int VirtQueue_addUsedBufAddr(VirtQueue_Handle vq, Void *buf, UInt32 len)
163     struct vring_used_elem *used = NULL;
164     UInt16 head = 0;
165     Int status = 0;
167     if ((head > vq->vring.num) || (head < 0)) {
168         status = -1;
169         GT_setFailureReason (curTrace,
170                              GT_4CLASS,
171                              "VirtQueue_addUsedBuf",
172                              status,
173                              "head is invalid!");
174     }
175     else {
176         /*
177          * The virtqueue contains a ring of used buffers.  Get a pointer to the
178          * next entry in that used ring.
179          */
180         head = vq->vring.used->idx % vq->vring.num;
181         vq->vring.desc[head].addr = mapVAtoPA(vq, buf);
182         vq->vring.desc[head].len = len;
183         vq->vring.desc[head].flags = 0;
184         used = &vq->vring.used->ring[head];
185         used->id = head;
186         used->len = len;
188         vq->vring.used->idx++;
189     }
191     return status;
194 /*!
195  * ======== VirtQueue_addAvailBuf ========
196  */
197 Int VirtQueue_addAvailBuf(VirtQueue_Handle vq, Void *buf, UInt32 len, Int16 head)
199     UInt16 avail;
201     if (vq->num_free == 0) {
202         /* There's no more space */
203         GT_setFailureReason (curTrace,
204                              GT_4CLASS,
205                              "VirtQueue_addAvailBuf",
206                              (-1), // TODO: Make this a valid error code
207                              "no more space!");
209     }
210     else {
211         vq->num_free--;
213         avail = vq->vring.avail->idx % vq->vring.num;
214         vq->vring.avail->ring[avail] = head;
216         vq->vring.desc[head].addr = mapVAtoPA(vq, buf);
217         vq->vring.desc[head].len = len;
218         vq->vring.desc[head].flags = 0;
220         vq->vring.avail->idx++;
221     }
223     return (vq->num_free);
226 /*!
227  * ======== VirtQueue_getUsedBuf ========
228  */
229 Int16 VirtQueue_getUsedBuf(VirtQueue_Object *vq, Void **buf)
231     UInt16 head;
233     /* There's nothing available? */
234     if (vq->last_used_idx == vq->vring.used->idx) {
235         /* We need to know about added buffers */
236         vq->vring.avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT;
238         return (-1);
239     }
241     /* No need to know be kicked about added buffers anymore */
242     //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.
244     head = vq->vring.used->ring[vq->last_used_idx % vq->vring.num].id;
245     vq->last_used_idx++;
246     vq->num_free++;
248     *buf = mapPAtoVA(vq, vq->vring.desc[head].addr);
250     return (head);
253 /*!
254  * ======== VirtQueue_disableCallback ========
255  */
256 Void VirtQueue_disableCallback(VirtQueue_Handle vq)
258     //TODO
259     GT_0trace(curTrace, GT_3CLASS, "VirtQueue_disableCallback not supported.");
262 /*!
263  * ======== VirtQueue_enableCallback ========
264  */
265 Bool VirtQueue_enableCallback(VirtQueue_Handle vq)
267     GT_0trace(curTrace, GT_3CLASS, "VirtQueue_enableCallback not supported.");
269     //TODO
270     return (FALSE);
273 /*!
274  *  @brief      This function implements the interrupt service routine for the
275  *              interrupt received from the remote cores.
276  *
277  *  @param      refData       object to be handled in ISR
278  *
279  *  @sa         VirtQueue_cb
280  */
281 static
282 Bool
283 VirtQueue_ISR (UInt32 msg, Void * arg)
285     UInt32 procId = (UInt32)arg;
286     ProcMgr_Handle handle = NULL;
287     Int status = 0;
289     GT_2trace (curTrace, GT_ENTER, "_VirtQueue_ISR", msg, arg);
291         /* Interrupt clear is done by ArchIpcInt. */
293         switch(msg) {
294             case (UInt)RP_MBOX_ECHO_REPLY:
295                 Osal_printf ("Echo reply from %s",
296                              MultiProc_getName(procId));
297                 break;
299             case (UInt)RP_MBOX_CRASH:
300                 Osal_printf ("Crash notification for %s",
301                              MultiProc_getName(procId));
302                 status = ProcMgr_open(&handle, procId);
303                 if (status >= 0) {
304                     ProcMgr_setState(handle, ProcMgr_State_Error);
305                     ProcMgr_close(&handle);
306                 }
307                 else {
308                     Osal_printf("Failed to open ProcMgr handle");
309                 }
310                 break;
312             case (UInt)RP_MBOX_BOOTINIT_DONE:
313                 Osal_printf ("Got BootInit done from %s",
314                              MultiProc_getName(procId));
315                 // TODO: What to do with this message?
316                 break;
318             case (UInt)RP_MBOX_HIBERNATION_ACK:
319                 Osal_printf ("Got Hibernation ACK from %s",
320                              MultiProc_getName(procId));
321                 break;
323             case (UInt)RP_MBOX_HIBERNATION_CANCEL:
324                 Osal_printf ("Got Hibernation CANCEL from %s",
325                              MultiProc_getName(procId));
326                 break;
328             default:
329                 /*
330                  *  If the message isn't one of the above, it's a virtqueue
331                  *  message
332                  */
333                 if (msg%2 < NUM_QUEUES) {
334                     /* This message is for us! */
335                     VirtQueue_cb((void *)msg, queueRegistry[procId][msg%2]);
336                 }
337             break;
338         }
340     return TRUE;
343 /*!
344  * ======== VirtQueue_create ========
345  */
346 VirtQueue_Handle VirtQueue_create (VirtQueue_callback callback, UInt16 procId,
347                                    UInt16 id, UInt32 vaddr, UInt32 paddr,
348                                    UInt32 num, UInt32 align, Void *arg)
350     VirtQueue_Object *vq = NULL;
352     vq = Memory_alloc(NULL, sizeof(VirtQueue_Object), 0, NULL);
353     if (!vq) {
354         return (NULL);
355     }
357     vq->callback = callback;
358     vq->id = id;
359     numQueues++;
360     vq->procId = procId;
361     vq->intId = coreIntId[procId];
362     vq->arg = arg;
364     /* init the vring */
365     vring_init(&(vq->vring), num, (void *)vaddr, align);
367     vq->num_free = num;
368     vq->last_used_idx = 0;
369     vq->vaddr = vaddr;
370     vq->paddr = paddr;
372     vq->vring.avail->idx = 0;
373     vq->vring.used->idx = 0;
375     /* Initialize the flags */
376     vq->vring.avail->flags = 0;
377     vq->vring.used->flags = 0;
379     /* Store the VirtQueue locally */
380     if (queueRegistry[procId][vq->id%2] == NULL)
381         queueRegistry[procId][vq->id%2] = vq;
382     else {
383         Osal_printf ("VirtQueue ID %d already created", id);
384         Memory_free(NULL, vq, sizeof(VirtQueue_Object));
385         vq = NULL;
386     }
388     return (vq);
391 /*!
392  * ======== VirtQueue_delete ========
393 */
394 Int VirtQueue_delete (VirtQueue_Handle * vq)
396     VirtQueue_Object * obj = (VirtQueue_Object *)(*vq);
397     /* Store the VirtQueue locally */
398     queueRegistry[obj->procId][obj->id%2] = NULL;
399     Memory_free(NULL, obj, sizeof(VirtQueue_Object));
400     *vq = NULL;
401     numQueues--;
403     return 0;
406 /*!
407  * ======== VirtQueue_startup ========
408  */
409 Void VirtQueue_startup(UInt16 procId, UInt32 intId, UInt32 paddr)
411     Int32 status = 0;
412     UInt32 arg = procId;
414     coreIntId[procId] = intId;
416     /* Register for interrupts with CORE0, CORE1 messages come through CORE0 */
417     status = ArchIpcInt_interruptRegister (procId,
418                                            intId,
419                                            VirtQueue_ISR, (Ptr)arg);
420     if (status >= 0) {
421         /* Notify the remote proc that the mbox is ready */
422         status = ArchIpcInt_sendInterrupt (procId,
423                                            intId,
424                                            RP_MBOX_READY);
425     }
428 /*!
429  * ======== VirtQueue_startup ========
430  */
431 Void VirtQueue_destroy(UInt16 procId)
433     Int32 status = 0;
434     Int i = 0;
436     for (i = 0; i < NUM_QUEUES; i++) {
437         if (queueRegistry[procId][i]) {
438             VirtQueue_delete(&queueRegistry[procId][i]);
439             queueRegistry[procId][i] = NULL;
440         }
441     }
443     /* Un-register for interrupts with CORE0 */
444     status = ArchIpcInt_interruptUnregister (procId);
445     if (status < 0) {
446         GT_setFailureReason (curTrace,
447                              GT_4CLASS,
448                              "VirtQueue_destroy",
449                              status,
450                              "ArchIpcInt_interruptUnregister failed");
451     }