[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)
114 {
115 UInt offset = vq->paddr - pa;
116 return (Void *)(vq->vaddr - offset);
117 }
119 static inline UInt mapVAtoPA(VirtQueue_Handle vq, Void * va)
120 {
121 UInt offset = vq->vaddr - (UInt)va;
122 return (UInt)(vq->paddr - offset);
123 }
125 /*!
126 * ======== VirtQueue_cb ========
127 */
128 Void VirtQueue_cb(Void *buf, VirtQueue_Handle vq)
129 {
130 if (vq/* && vq->cb_enabled*/) {
131 /* Call the registered vq callback */
132 vq->callback(vq, vq->arg);
133 }
134 }
136 /*!
137 * ======== VirtQueue_kick ========
138 */
139 Void VirtQueue_kick(VirtQueue_Handle vq)
140 {
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);
155 }
157 /*!
158 * ======== VirtQueue_addUsedBufAddr ========
159 */
160 Int VirtQueue_addUsedBufAddr(VirtQueue_Handle vq, Void *buf, UInt32 len)
161 {
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;
191 }
193 /*!
194 * ======== VirtQueue_addAvailBuf ========
195 */
196 Int VirtQueue_addAvailBuf(VirtQueue_Handle vq, Void *buf, UInt32 len, Int16 head)
197 {
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);
229 }
231 /*!
232 * ======== VirtQueue_getUsedBuf ========
233 */
234 Int16 VirtQueue_getUsedBuf(VirtQueue_Object *vq, Void **buf)
235 {
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);
259 }
261 /*!
262 * ======== VirtQueue_disableCallback ========
263 */
264 Void VirtQueue_disableCallback(VirtQueue_Handle vq)
265 {
266 //TODO
267 GT_0trace(curTrace, GT_3CLASS, "VirtQueue_disableCallback not supported.");
268 }
270 /*!
271 * ======== VirtQueue_enableCallback ========
272 */
273 Bool VirtQueue_enableCallback(VirtQueue_Handle vq)
274 {
275 GT_0trace(curTrace, GT_3CLASS, "VirtQueue_enableCallback not supported.");
277 //TODO
278 return (FALSE);
279 }
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)
292 {
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;
349 }
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)
357 {
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);
397 }
399 /*!
400 * ======== VirtQueue_delete ========
401 */
402 Int VirtQueue_delete (VirtQueue_Handle * vq)
403 {
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;
412 }
414 /*!
415 * ======== VirtQueue_startup ========
416 */
417 Void VirtQueue_startup(UInt16 procId, UInt32 intId, UInt32 paddr)
418 {
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 }
434 }
436 /*!
437 * ======== VirtQueue_startup ========
438 */
439 Void VirtQueue_destroy(UInt16 procId)
440 {
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 }
460 }