286cb5f53fe793be50e1f80f7bae62d482176328
[ipc/ipcdev.git] / packages / ti / ipc / family / tci6614 / VirtQueue.c
1 /*
2  * Copyright (c) 2011-2015 Texas Instruments Incorporated - http://www.ti.com
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 <xdc/std.h>
57 #include <xdc/runtime/System.h>
58 #include <xdc/runtime/Assert.h>
59 #include <xdc/runtime/Error.h>
60 #include <xdc/runtime/Memory.h>
61 #include <xdc/runtime/Log.h>
62 #include <xdc/runtime/Diags.h>
63 #include <xdc/runtime/SysMin.h>
64 #include <ti/sysbios/gates/GateAll.h>
66 #include <ti/sysbios/knl/Clock.h>
67 #include <ti/sysbios/family/c66/Cache.h>
68 #include <ti/sysbios/knl/Swi.h>
70 #include <ti/sdo/ipc/notifyDrivers/IInterrupt.h>
71 #include <ti/ipc/family/tci6614/Interrupt.h>
72 #include <ti/ipc/remoteproc/Resource.h>
74 #include <ti/ipc/MultiProc.h>
76 #include "package/internal/VirtQueue.xdc.h"
78 #include <string.h>
80 #include <ti/ipc/rpmsg/_VirtQueue.h>
81 #include <ti/ipc/rpmsg/virtio_ring.h>
83 /* Used for defining the size of the virtqueue registry */
84 #define NUM_QUEUES                      2
86 #define DIV_ROUND_UP(n,d)   (((n) + (d) - 1) / (d))
87 #define RP_MSG_BUFS_SPACE   (VirtQueue_RP_MSG_NUM_BUFS * RPMSG_BUF_SIZE * 2)
89 /* With 256 buffers, our vring will occupy 3 pages */
90 #define RP_MSG_RING_SIZE    ((DIV_ROUND_UP(vring_size(VirtQueue_RP_MSG_NUM_BUFS, \
91                             VirtQueue_RP_MSG_VRING_ALIGN), VirtQueue_PAGE_SIZE)) * VirtQueue_PAGE_SIZE)
93 /* The total IPC space needed to communicate with a remote processor */
94 #define RPMSG_IPC_MEM   (RP_MSG_BUFS_SPACE + 2 * RP_MSG_RING_SIZE)
96 #define ID_DSP_TO_A9      0
97 #define ID_A9_TO_DSP      1
99 extern volatile cregister UInt DNUM;
101 static VirtQueue_Object *queueRegistry[NUM_QUEUES] = {NULL};
103 /* static inline */ Void * mapPAtoVA(UInt pa)
105     Void *va;
106     UInt offset;
108     offset = (DNUM * VirtQueue_VRING_OFFSET);
109     va = (Void *)((pa & 0x000fffffU) | offset | 0xa0000000U);
111     return va;
114 static inline UInt mapVAtoPA(Void * va)
116     return ((UInt)va & 0x000fffffU) | 0xe1000000U;
120 /*
121  * ======== VirtQueue_init ========
122  */
123 Void VirtQueue_init()
125     extern cregister volatile UInt DNUM;
126     UInt16 procId;
128     /*
129      * VirtQueue_init() must be called before MultiProcSetup_init().
130      * (Check the xdc_runtime_Startup_firstFxns__A in the XDC generated code)
131      * Abort if the procId has already been set.  We must set it!
132      */
133     if (MultiProc_self() != MultiProc_INVALIDID) {
134         System_abort("VirtQueue_init(): MultiProc_self already set!");
135         return;
136     }
138     procId = DNUM + 1;
140     /* Set the local ID */
141     MultiProc_setLocalId(procId);
144 /*
145  * ======== VirtQueue_Instance_init ========
146  */
147 Int VirtQueue_Instance_init(VirtQueue_Object *vq, UInt16 remoteProcId,
148                              const VirtQueue_Params *params, Error_Block *eb)
150     void *vringAddr = NULL;
152     VirtQueue_module->traceBufPtr = Resource_getTraceBufPtr();
153     /* Create the thread protection gate */
154     vq->gateH = GateAll_create(NULL, eb);
155     if (Error_check(eb)) {
156         Log_error0("VirtQueue_create: could not create gate object");
157         Error_raise(NULL, Error_E_generic, 0, 0);
158         return(0);
159     }
161     vq->vringPtr = Memory_calloc(NULL, sizeof(struct vring), 0, eb);
162     Assert_isTrue((vq->vringPtr != NULL), NULL);
164     vq->callback = params->callback;
165     vq->id = params->vqId;
166     vq->procId = remoteProcId;
167     vq->last_avail_idx = 0;
168     vq->last_used_idx = 0;
169     vq->num_free = VirtQueue_RP_MSG_NUM_BUFS;
170     vq->swiHandle = params->swiHandle;
172     switch (vq->id) {
173         case ID_DSP_TO_A9:
174             vringAddr = (struct vring *) (VirtQueue_CORE0_MEM_VRING0 +
175                 (DNUM * VirtQueue_VRING_OFFSET));
176             break;
177         case ID_A9_TO_DSP:
178             vringAddr = (struct vring *) (VirtQueue_CORE0_MEM_VRING1 +
179                 (DNUM * VirtQueue_VRING_OFFSET));
180             break;
181          default:
182             Log_error1("VirtQueue_create: invalid vq->id: %d", vq->id);
183             GateAll_delete(&vq->gateH);
184             Memory_free(NULL, vq->vringPtr, sizeof(struct vring));
185             Error_raise(NULL, Error_E_generic, 0, 0);
186             return(0);
187     }
189     Log_print3(Diags_USER1,
190             "vring: %d 0x%x (0x%x)\n", vq->id, (IArg)vringAddr,
191             RP_MSG_RING_SIZE);
193     vring_init(vq->vringPtr, VirtQueue_RP_MSG_NUM_BUFS, vringAddr, VirtQueue_RP_MSG_VRING_ALIGN);
195     queueRegistry[vq->id] = vq;
196     return(0);
199 /*
200  * ======== VirtQueue_kick ========
201  */
202 Void VirtQueue_kick(VirtQueue_Handle vq)
204     struct vring *vring = vq->vringPtr;
205     IInterrupt_IntInfo intInfo;
207     /* For now, simply interrupt remote processor */
208     if (vring->avail->flags & VRING_AVAIL_F_NO_INTERRUPT) {
209         Log_print0(Diags_USER1,
210                 "VirtQueue_kick: no kick because of VRING_AVAIL_F_NO_INTERRUPT\n");
211         return;
212     }
214     Log_print2(Diags_USER1,
215             "VirtQueue_kick: Sending interrupt to proc %d with payload 0x%x\n",
216             (IArg)vq->procId, (IArg)vq->id);
218     intInfo.localIntId  = Interrupt_SRCS_BITPOS_CORE0;
219     Interrupt_intSend(vq->procId, &intInfo, vq->id);
222 /*
223  * ======== VirtQueue_addUsedBuf ========
224  */
225 Int VirtQueue_addUsedBuf(VirtQueue_Handle vq, Int16 head, Int len)
227     struct vring_used_elem *used;
228     struct vring *vring = vq->vringPtr;
229     IArg key;
231     key = GateAll_enter(vq->gateH);
232     if ((head > vring->num) || (head < 0)) {
233         Error_raise(NULL, Error_E_generic, 0, 0);
234     }
235     else {
236         /*
237          * The virtqueue contains a ring of used buffers.  Get a pointer to the
238          * next entry in that used ring.
239          */
240         used = &vring->used->ring[vring->used->idx % vring->num];
241         used->id = head;
242         used->len = len;
244         vring->used->idx++;
245     }
246     GateAll_leave(vq->gateH, key);
248     return (0);
251 /*
252  * ======== VirtQueue_addAvailBuf ========
253  */
254 Int VirtQueue_addAvailBuf(VirtQueue_Object *vq, Void *buf)
256     UInt16 avail;
257     struct vring *vring = vq->vringPtr;
258     IArg key;
260     key = GateAll_enter(vq->gateH);
261     if (vq->num_free == 0) {
262         /* There's no more space */
263         Error_raise(NULL, Error_E_generic, 0, 0);
264     }
265     else {
266         vq->num_free--;
268         avail =  vring->avail->idx++ % vring->num;
270         vring->desc[avail].addr = mapVAtoPA(buf);
271         vring->desc[avail].len = RPMSG_BUF_SIZE;
272     }
273     GateAll_leave(vq->gateH, key);
275     return (vq->num_free);
278 /*
279  * ======== VirtQueue_getUsedBuf ========
280  */
281 Void *VirtQueue_getUsedBuf(VirtQueue_Object *vq)
283     UInt16 head;
284     Void *buf;
285     struct vring *vring = vq->vringPtr;
286     IArg key;
288     key = GateAll_enter(vq->gateH);
289     /* There's nothing available? */
290     if (vq->last_used_idx == vring->used->idx) {
291         buf = NULL;
292     }
293     else {
294         head = vring->used->ring[vq->last_used_idx % vring->num].id;
295         vq->last_used_idx++;
296         vq->num_free++;
298         buf = mapPAtoVA(vring->desc[head].addr);
299     }
300     GateAll_leave(vq->gateH, key);
302     return (buf);
305 /*
306  * ======== VirtQueue_getAvailBuf ========
307  */
308 Int16 VirtQueue_getAvailBuf(VirtQueue_Handle vq, Void **buf, Int *len)
310     Int16 head;
311     struct vring *vring = vq->vringPtr;
312     IArg key;
314     key = GateAll_enter(vq->gateH);
315     Log_print6(Diags_USER1, "getAvailBuf vq: 0x%x %d %d %d 0x%x 0x%x\n",
316         (IArg)vq,
317         vq->last_avail_idx, vring->avail->idx, vring->num,
318         (IArg)&vring->avail, (IArg)vring->avail);
320     /*  Clear flag here to avoid race condition with remote processor.
321      *  This is a negative flag, clearing it means that we want to
322      *  receive an interrupt when a buffer has been added to the pool.
323      */
324     vring->used->flags &= ~VRING_USED_F_NO_NOTIFY;
326     /* There's nothing available? */
327     if (vq->last_avail_idx == vring->avail->idx) {
328         head = (-1);
329     }
330     else {
331         /* No need to be kicked about added buffers anymore */
332         vring->used->flags |= VRING_USED_F_NO_NOTIFY;
334         /*
335          * Grab the next descriptor number they're advertising, and increment
336          * the index we've seen.
337          */
338         head = vring->avail->ring[vq->last_avail_idx++ % vring->num];
340         *buf = mapPAtoVA(vring->desc[head].addr);
341         *len = vring->desc[head].len;
342     }
343     GateAll_leave(vq->gateH, key);
345     return (head);
348 /*
349  * ======== VirtQueue_isr ========
350  * Note 'msg' is ignored: it is only used where there is a mailbox payload.
351  */
352 static Void VirtQueue_isr(UArg msg)
354     VirtQueue_Object *vq;
356     Log_print1(Diags_USER1, "VirtQueue_isr received msg = 0x%x\n", msg);
358     vq = queueRegistry[0];
359     if (vq) {
360         vq->callback(vq);
361     }
362     vq = queueRegistry[1];
363     if (vq) {
364         vq->callback(vq);
365     }
368 /*
369  * ======== VirtQueue_startup ========
370  */
371 Void VirtQueue_startup(UInt16 remoteProcId, Bool isHost)
373     IInterrupt_IntInfo intInfo;
375     intInfo.intVectorId = Interrupt_DSPINT;
376     intInfo.localIntId  = Interrupt_SRCS_BITPOS_HOST;
379     /*
380      * Wait for first kick from host, which happens to coincide with the
381      * priming of host's receive buffers, indicating host is ready to send.
382      * Since interrupt is cleared, we throw away this first kick, which is
383      * OK since we don't process this in the ISR anyway.
384      */
385     Log_print0(Diags_USER1, "VirtQueue_startup: Polling for host int...\n");
386     while (!Interrupt_checkAndClear(remoteProcId, &intInfo));
388     Interrupt_intRegister(remoteProcId, &intInfo, (Fxn)VirtQueue_isr, 0);
390     Log_print0(Diags_USER1, "Passed VirtQueue_startup\n");
393 /* By convention, Host VirtQueues host are the even number in the pair */
394 Bool VirtQueue_isSlave(VirtQueue_Handle vq)
396   return (vq->id & 0x1);
399 Bool VirtQueue_isHost(VirtQueue_Handle vq)
401   return (~(vq->id & 0x1));
404 UInt16 VirtQueue_getId(VirtQueue_Handle vq)
406   return (vq->id);
409 Swi_Handle VirtQueue_getSwiHandle(VirtQueue_Handle vq)
411   return (vq->swiHandle);
414 #define CACHE_WB_TICK_PERIOD    5
416 /*
417  * ======== VirtQueue_cacheWb ========
418  *
419  * Used for flushing SysMin trace buffer.
420  */
421 Void VirtQueue_cacheWb()
423     static UInt32 oldticks = 0;
424     UInt32 newticks;
426     newticks = Clock_getTicks();
427     if (newticks - oldticks < (UInt32)CACHE_WB_TICK_PERIOD) {
428         /* Don't keep flushing cache */
429         return;
430     }
432     oldticks = newticks;
434     /* Flush the cache of the SysMin buffer only: */
435     Assert_isTrue((VirtQueue_module->traceBufPtr != NULL), NULL);
436     Cache_wb(VirtQueue_module->traceBufPtr, SysMin_bufSize, Cache_Type_ALL,
437              FALSE);