bf752ee2a048d0637691e1d476c51f4f8739b123
[ipc/ipcdev.git] / packages / ti / ipc / family / omap54xx / 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/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/hal/Cache.h>
74 #include <ti/ipc/MultiProc.h>
76 #include <ti/ipc/rpmsg/virtio_ring.h>
77 #include <ti/pm/IpcPower.h>
78 #include <string.h>
80 #include <ti/ipc/remoteproc/Resource.h>
81 #include <ti/ipc/remoteproc/rsc_types.h>
82 #include <ti/ipc/rpmsg/_VirtQueue.h>
84 #include "InterruptProxy.h"
85 #include "VirtQueue.h"
88 /*
89  *  The following three VIRTIO_* defines must match those in
90  *  <Linux_kernel>/include/uapi/linux/virtio_config.h
91  */
92 #define VIRTIO_CONFIG_S_ACKNOWLEDGE     1
93 #define VIRTIO_CONFIG_S_DRIVER          2
94 #define VIRTIO_CONFIG_S_DRIVER_OK       4
96 #define VRING_BUFS_PRIMED  (VIRTIO_CONFIG_S_ACKNOWLEDGE | \
97                             VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK)
99 /* Used for defining the size of the virtqueue registry */
100 #define NUM_QUEUES              2
102 /* Predefined device addresses */
103 #define IPC_MEM_VRING0          0xA0000000
104 #define IPC_MEM_VRING1          0xA0004000
106 /*
107  * Size of the virtqueues (expressed in number of buffers supported,
108  * and must be power of two)
109  */
110 #define VQ_SIZE                 256
112 /*
113  * enum - Predefined Mailbox Messages
114  *
115  * @RP_MSG_MBOX_READY: informs the slave that we're up and running. will be
116  * followed by another mailbox message that carries the HOST's virtual address
117  * of the shared buffer. This would allow the HOST's drivers to send virtual
118  * addresses of the buffers.
119  *
120  * @RP_MSG_MBOX_STATE_CHANGE: informs the receiver that there is an inbound
121  * message waiting in its own receive-side vring. please note that currently
122  * this message is optional: alternatively, one can explicitly send the index
123  * of the triggered virtqueue itself. the preferred approach will be decided
124  * as we progress and experiment with those design ideas.
125  *
126  * @RP_MSG_MBOX_CRASH: this message indicates that the BIOS side is unhappy
127  *
128  * @RP_MBOX_ECHO_REQUEST: this message requests the remote processor to reply
129  * with RP_MBOX_ECHO_REPLY
130  *
131  * @RP_MBOX_ECHO_REPLY: this is a reply that is sent when RP_MBOX_ECHO_REQUEST
132  * is received.
133  *
134  * @RP_MBOX_ABORT_REQUEST:  tells the M3 to crash on demand
135  *
136  * @RP_MBOX_BOOTINIT_DONE: this message indicates the BIOS side has reached a
137  * certain state during the boot process. This message is used to inform the
138  * host that the basic BIOS initialization is done, and lets the host use this
139  * notification to perform certain actions.
140  */
141 enum {
142     RP_MSG_MBOX_READY           = (Int)0xFFFFFF00,
143     RP_MSG_MBOX_STATE_CHANGE    = (Int)0xFFFFFF01,
144     RP_MSG_MBOX_CRASH           = (Int)0xFFFFFF02,
145     RP_MBOX_ECHO_REQUEST        = (Int)0xFFFFFF03,
146     RP_MBOX_ECHO_REPLY          = (Int)0xFFFFFF04,
147     RP_MBOX_ABORT_REQUEST       = (Int)0xFFFFFF05,
148     RP_MSG_FLUSH_CACHE          = (Int)0xFFFFFF06,
149     RP_MSG_BOOTINIT_DONE        = (Int)0xFFFFFF07,
150     RP_MSG_HIBERNATION          = (Int)0xFFFFFF10,
151     RP_MSG_HIBERNATION_FORCE    = (Int)0xFFFFFF11,
152     RP_MSG_HIBERNATION_ACK      = (Int)0xFFFFFF12,
153     RP_MSG_HIBERNATION_CANCEL   = (Int)0xFFFFFF13
154 };
156 #define DIV_ROUND_UP(n,d)   (((n) + (d) - 1) / (d))
157 #define RP_MSG_NUM_BUFS     (VQ_SIZE) /* must be power of two */
158 #define RP_MSG_BUF_SIZE     (512)
159 #define RP_MSG_BUFS_SPACE   (RP_MSG_NUM_BUFS * RP_MSG_BUF_SIZE * 2)
161 #define PAGE_SIZE           (4096)
162 /*
163  * The alignment to use between consumer and producer parts of vring.
164  * Note: this is part of the "wire" protocol. If you change this, you need
165  * to update your BIOS image as well
166  */
167 #define RP_MSG_VRING_ALIGN  (4096)
169 /* With 256 buffers, our vring will occupy 3 pages */
170 #define RP_MSG_RING_SIZE    ((DIV_ROUND_UP(vring_size(RP_MSG_NUM_BUFS, \
171                             RP_MSG_VRING_ALIGN), PAGE_SIZE)) * PAGE_SIZE)
173 /* The total IPC space needed to communicate with a remote processor */
174 #define RPMSG_IPC_MEM   (RP_MSG_BUFS_SPACE + 2 * RP_MSG_RING_SIZE)
176 typedef struct VirtQueue_Object {
177     /* Id for this VirtQueue_Object */
178     UInt16                  id;
180     /* The function to call when buffers are consumed (can be NULL) */
181     VirtQueue_callback      callback;
183     /* Shared state */
184     struct vring            vring;
186     /* Number of free buffers */
187     UInt16                  num_free;
189     /* Last available index; updated by VirtQueue_getAvailBuf */
190     UInt16                  last_avail_idx;
192     /* Last available index; updated by VirtQueue_addUsedBuf */
193     UInt16                  last_used_idx;
195     /* Will eventually be used to kick remote processor */
196     UInt16                  procId;
198     /* Gate to protect from multiple threads */
199     GateHwi_Handle       gateH;
200 } VirtQueue_Object;
202 /* module diags mask */
203 Registry_Desc Registry_CURDESC;
205 static struct VirtQueue_Object *queueRegistry[NUM_QUEUES] = {NULL};
207 static UInt16 hostProcId;
209 /*!
210  * ======== _VirtQueue_init ========
211  *
212  * This function adds the VirtQueue "module" to the Registry so that
213  * DIAGS will work with this non-XDC module.
214  * Since VirtQueue_init is not called by XDC-VirtQueue module clients, this
215  * function is called in the first VirtQueue fxn called: VirtQueue_create.
216  */
217 static Void _VirtQueue_init()
219     static int initialized = 0;
221     if (!initialized) {
222         Registry_Result result;
224         /* register with xdc.runtime to get a diags mask */
225         result = Registry_addModule(&Registry_CURDESC, MODULE_NAME);
226         Assert_isTrue(result == Registry_SUCCESS, (Assert_Id)NULL);
228         initialized = 1;
229     }
232 static inline Void * mapPAtoVA(UInt pa)
234     return (Void *)((pa & 0x000fffffU) | IPC_MEM_VRING0);
237 static inline UInt mapVAtoPA(Void * va)
239     return ((UInt)va & 0x000fffffU) | 0x9cf00000U;
242 /*!
243  * ======== VirtQueue_kick ========
244  */
245 Void VirtQueue_kick(VirtQueue_Handle vq)
247     /* For now, simply interrupt remote processor */
248     if (vq->vring.avail->flags & VRING_AVAIL_F_NO_INTERRUPT) {
249         Log_print0(Diags_USER1,
250                 "VirtQueue_kick: no kick because of VRING_AVAIL_F_NO_INTERRUPT\n");
251         return;
252     }
254     Log_print2(Diags_USER1,
255             "VirtQueue_kick: Sending interrupt to proc %d with payload 0x%x\n",
256             (IArg)vq->procId, (IArg)vq->id);
257     InterruptProxy_intSend(vq->procId, vq->id);
260 /*!
261  * ======== VirtQueue_addUsedBuf ========
262  */
263 Int VirtQueue_addUsedBuf(VirtQueue_Handle vq, Int16 head, Int len)
265     struct vring_used_elem *used;
266     IArg key;
268     key = GateHwi_enter(vq->gateH);
269     if ((head > vq->vring.num) || (head < 0)) {
270         GateHwi_leave(vq->gateH, key);
271         Error_raise(NULL, Error_E_generic, 0, 0);
272     }
274     /*
275     * The virtqueue contains a ring of used buffers.  Get a pointer to the
276     * next entry in that used ring.
277     */
278     used = &vq->vring.used->ring[vq->vring.used->idx % vq->vring.num];
279     used->id = head;
280     used->len = len;
282     vq->vring.used->idx++;
283     GateHwi_leave(vq->gateH, key);
285     return (0);
288 /*!
289  * ======== VirtQueue_addAvailBuf ========
290  */
291 Int VirtQueue_addAvailBuf(VirtQueue_Object *vq, Void *buf)
293     UInt16 avail;
294     IArg key;
296     if (vq->num_free == 0) {
297         /* There's no more space */
298         Error_raise(NULL, Error_E_generic, 0, 0);
299     }
301     vq->num_free--;
303     key = GateHwi_enter(vq->gateH);
304     avail =  vq->vring.avail->idx++ % vq->vring.num;
306     vq->vring.desc[avail].addr = mapVAtoPA(buf);
307     vq->vring.desc[avail].len = RP_MSG_BUF_SIZE;
308     GateHwi_leave(vq->gateH, key);
310     return (vq->num_free);
313 /*!
314  * ======== VirtQueue_getUsedBuf ========
315  */
316 Void *VirtQueue_getUsedBuf(VirtQueue_Object *vq)
318     UInt16 head;
319     Void *buf;
320     IArg key;
322     key = GateHwi_enter(vq->gateH);
323     /* There's nothing available? */
324     if (vq->last_used_idx == vq->vring.used->idx) {
325         buf = NULL;
326     }
327     else {
328         head = vq->vring.used->ring[vq->last_used_idx % vq->vring.num].id;
329         vq->last_used_idx++;
331         buf = mapPAtoVA(vq->vring.desc[head].addr);
332     }
333     GateHwi_leave(vq->gateH, key);
335     return (buf);
338 /*!
339  * ======== VirtQueue_getAvailBuf ========
340  */
341 Int16 VirtQueue_getAvailBuf(VirtQueue_Handle vq, Void **buf, Int *len)
343     Int16 head;
344     IArg key;
346     key = GateHwi_enter(vq->gateH);
347     Log_print6(Diags_USER1, "getAvailBuf vq: 0x%x %d %d %d 0x%x 0x%x\n",
348         (IArg)vq, vq->last_avail_idx, vq->vring.avail->idx, vq->vring.num,
349         (IArg)&vq->vring.avail, (IArg)vq->vring.avail);
351     /*  Clear flag here to avoid race condition with remote processor.
352      *  This is a negative flag, clearing it means that we want to
353      *  receive an interrupt when a buffer has been added to the pool.
354      */
355     vq->vring.used->flags &= ~VRING_USED_F_NO_NOTIFY;
357     /* There's nothing available? */
358     if (vq->last_avail_idx == vq->vring.avail->idx) {
359         head = (-1);
360     }
361     else {
362         /* No need to be kicked about added buffers anymore */
363         vq->vring.used->flags |= VRING_USED_F_NO_NOTIFY;
365         /*
366          * Grab the next descriptor number they're advertising, and increment
367          * the index we've seen.
368          */
369         head = vq->vring.avail->ring[vq->last_avail_idx++ % vq->vring.num];
371         *buf = mapPAtoVA(vq->vring.desc[head].addr);
372         *len = vq->vring.desc[head].len;
373     }
374     GateHwi_leave(vq->gateH, key);
376     return (head);
379 /*!
380  * ======== VirtQueue_disableCallback ========
381  */
382 Void VirtQueue_disableCallback(VirtQueue_Object *vq)
384     /* TODO */
385     Log_print0(Diags_USER1, "VirtQueue_disableCallback called.");
388 /*!
389  * ======== VirtQueue_enableCallback ========
390  */
391 Bool VirtQueue_enableCallback(VirtQueue_Object *vq)
393     Log_print0(Diags_USER1, "VirtQueue_enableCallback called.");
395     /* TODO */
396     return (FALSE);
399 /*!
400  * ======== VirtQueue_isr ========
401  * Note 'arg' is ignored: it is the Hwi argument, not the mailbox argument.
402  */
403 Void VirtQueue_isr(UArg msg)
405     VirtQueue_Object *vq;
407     Log_print1(Diags_USER1, "VirtQueue_isr received msg = 0x%x\n", msg);
409     switch(msg) {
410         case (UInt)RP_MSG_MBOX_READY:
411             return;
413         case (UInt)RP_MBOX_ECHO_REQUEST:
414             InterruptProxy_intSend(hostProcId, (UInt)(RP_MBOX_ECHO_REPLY));
415             return;
417         case (UInt)RP_MBOX_ABORT_REQUEST:
418         {
419             /* Suppress Coverity Error: FORWARD_NULL: */
420             /* coverity[assign_zero] */
421             Fxn f = (Fxn)0x0;
422             Log_print0(Diags_USER1, "Crash on demand ...\n");
423             /* coverity[var_deref_op] */
424             f();
425         }
426         return;
428         case (UInt)RP_MSG_FLUSH_CACHE:
429             Cache_wbAll();
430             return;
432         case (UInt)RP_MSG_HIBERNATION:
433             if (IpcPower_canHibernate() == FALSE) {
434                 InterruptProxy_intSend(hostProcId,
435                         (UInt)RP_MSG_HIBERNATION_CANCEL);
436                 return;
437             }
439             /* Fall through */
440         case (UInt)RP_MSG_HIBERNATION_FORCE:
441             /* Ack request */
442             InterruptProxy_intSend(hostProcId,
443                     (UInt)RP_MSG_HIBERNATION_ACK);
444             IpcPower_suspend();
445             return;
447         default:
448             /*
449              *  If the message isn't one of the above, it's either part of the
450              *  2-message synchronization sequence or it a virtqueue message
451              */
452             break;
453     }
455     /* Don't let unknown messages to pass as a virtqueue index */
456     if (msg >= NUM_QUEUES) {
457         /* Adding print here deliberately, we should never see this */
458         System_printf("VirtQueue_isr: Invalid mailbox message 0x%x "
459                 "received\n", msg);
460         return;
461     }
463     vq = queueRegistry[msg];
464     if (vq) {
465         vq->callback(vq);
466     }
470 /*!
471  * ======== VirtQueue_create ========
472  */
473 VirtQueue_Handle VirtQueue_create(UInt16 remoteProcId, VirtQueue_Params *params,
474                                   Error_Block *eb)
476     VirtQueue_Object *vq;
477     Void *vringAddr;
479     /* Perform initialization we can't do in Instance_init (being non-XDC): */
480     _VirtQueue_init();
482     vq = Memory_alloc(NULL, sizeof(VirtQueue_Object), 0, eb);
483     if (NULL == vq) {
484         return (NULL);
485     }
487     /* Create the thread protection gate */
488     vq->gateH = GateHwi_create(NULL, eb);
489     if (Error_check(eb)) {
490         Log_error0("VirtQueue_create: could not create gate object");
491         Memory_free(NULL, vq, sizeof(VirtQueue_Object));
492         return (NULL);
493     }
495     vq->callback = params->callback;
496     vq->id = params->vqId;
497     vq->procId = remoteProcId;
498     vq->last_avail_idx = 0;
500     switch (vq->id) {
501         /* IPC transport vrings */
502         case ID_SELF_TO_HOST:
503             /* slave -> HOST */
504             vringAddr = (struct vring *) IPC_MEM_VRING0;
505             break;
506         case ID_HOST_TO_SELF:
507             /* HOST -> slave */
508             vringAddr = (struct vring *) IPC_MEM_VRING1;
509             break;
510         default:
511             GateHwi_delete(&vq->gateH);
512             Memory_free(NULL, vq, sizeof(VirtQueue_Object));
513             return (NULL);
514     }
516     Log_print3(Diags_USER1,
517             "vring: %d 0x%x (0x%x)\n", vq->id, (IArg)vringAddr,
518             RP_MSG_RING_SIZE);
520     /* See coverity related comment in vring_init() */
521     /* coverity[overrun-call] */
522     vring_init(&(vq->vring), RP_MSG_NUM_BUFS, vringAddr, RP_MSG_VRING_ALIGN);
524     /*
525      *  Don't trigger a mailbox message every time MPU makes another buffer
526      *  available
527      */
528     if (vq->procId == hostProcId) {
529         vq->vring.used->flags &= ~VRING_USED_F_NO_NOTIFY;
530     }
532     queueRegistry[vq->id] = vq;
534     return (vq);
537 /*!
538  * ======== VirtQueue_startup ========
539  */
540 Void VirtQueue_startup()
542     hostProcId      = MultiProc_getId("HOST");
544     /* Initilize the IpcPower module */
545     IpcPower_init();
547     /*
548      * Wait for HLOS (Virtio device) to indicate that priming of host's receive
549      * buffers is complete, indicating that host is ready to send.
550      *
551      * Though this is a Linux Virtio configuration status, it must be
552      * implemented by each non-Linux HLOS as well.
553      */
554     Log_print1(Diags_USER1, "VirtQueue_startup: VDEV status: 0x%x\n",
555               Resource_getVdevStatus(VIRTIO_ID_RPMSG));
556     Log_print0(Diags_USER1, "VirtQueue_startup: Polling VDEV status...\n");
557     while (Resource_getVdevStatus(VIRTIO_ID_RPMSG) != VRING_BUFS_PRIMED);
558     Log_print1(Diags_USER1, "VirtQueue_startup: VDEV status: 0x%x\n",
559               Resource_getVdevStatus(VIRTIO_ID_RPMSG));
561     InterruptProxy_intRegister(VirtQueue_isr);
562     Log_print0(Diags_USER1, "Passed VirtQueue_startup\n");
565 /*!
566  * ======== VirtQueue_postCrashToMailbox ========
567  */
568 Void VirtQueue_postCrashToMailbox(Void)
570     InterruptProxy_intSend(0, (UInt)RP_MSG_MBOX_CRASH);
573 #define CACHE_WB_TICK_PERIOD    5
575 /*!
576  * ======== VirtQueue_cacheWb ========
577  *
578  * Used for flushing SysMin trace buffer.
579  */
580 Void VirtQueue_cacheWb()
582     static UInt32 oldticks = 0;
583     UInt32 newticks;
585     newticks = Clock_getTicks();
586     if (newticks - oldticks < (UInt32)CACHE_WB_TICK_PERIOD) {
587         /* Don't keep flushing cache */
588         return;
589     }
591     oldticks = newticks;
593     /* Flush the cache */
594     Cache_wbAll();