031b5326c5319c1fb668a0cb8c39c2704629a479
[ipc/ipcdev.git] / packages / ti / ipc / family / tci6638 / 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  */
33 /** ============================================================================
34  *  @file       VirtQueue.c
35  *
36  *  @brief      Virtio Queue implementation for BIOS
37  *
38  *  Differences between BIOS version and Linux kernel (include/linux/virtio.h):
39  *  - Renamed module from virtio.h to VirtQueue.h to match the API prefixes
40  *  - XDC Standard Types and CamelCasing used
41  *  - virtio_device concept removed (i.e, assumes no containing device)
42  *  - simplified scatterlist from Linux version
43  *  - VirtQueue objects are created statically, added VirtQueue_Instance_init()
44  *    fxn to take the place of the Virtio vring_new_virtqueue() API
45  *  - The notify function is implicit in the implementation, and not provided
46  *    by the client, as it is in Linux virtio
47  *
48  *  All VirtQueue operations can be called in any context.
49  */
51 #include <xdc/std.h>
52 #include <xdc/runtime/System.h>
53 #include <xdc/runtime/Assert.h>
54 #include <xdc/runtime/Error.h>
55 #include <xdc/runtime/Memory.h>
56 #include <xdc/runtime/Log.h>
57 #include <xdc/runtime/Diags.h>
58 #include <xdc/runtime/SysMin.h>
59 #include <ti/sysbios/gates/GateAll.h>
61 #include <ti/sysbios/knl/Clock.h>
62 #include <ti/sysbios/family/c66/Cache.h>
63 #include <ti/sysbios/knl/Swi.h>
65 #include <ti/ipc/family/tci6638/Interrupt.h>
66 #include <ti/ipc/remoteproc/Resource.h>
68 #include <ti/ipc/MultiProc.h>
70 #include "package/internal/VirtQueue.xdc.h"
72 #include <string.h>
74 #include <ti/ipc/rpmsg/_VirtQueue.h>
75 #include <ti/ipc/rpmsg/virtio_ring.h>
77 /* Used for defining the size of the virtqueue registry */
78 #define NUM_QUEUES 2
80 #define DIV_ROUND_UP(n,d)   (((n) + (d) - 1) / (d))
81 #define RP_MSG_BUFS_SPACE   (VirtQueue_RP_MSG_NUM_BUFS * RPMSG_BUF_SIZE * 2)
83 /* With 256 buffers, our vring will occupy 3 pages */
84 #define RP_MSG_RING_SIZE    \
85         ((DIV_ROUND_UP(vring_size(VirtQueue_RP_MSG_NUM_BUFS, \
86         VirtQueue_RP_MSG_VRING_ALIGN), \
87         VirtQueue_PAGE_SIZE)) * VirtQueue_PAGE_SIZE)
89 /* The total IPC space needed to communicate with a remote processor */
90 #define RPMSG_IPC_MEM   (RP_MSG_BUFS_SPACE + 2 * RP_MSG_RING_SIZE)
92 #define ID_SELF_TO_HOST 0
93 #define ID_HOST_TO_SELF 1
95 extern volatile cregister UInt DNUM;
97 static VirtQueue_Object *queueRegistry[NUM_QUEUES] = {NULL};
99 static inline  Void * mapPAtoVA(UInt pa)
101     return (Void *)(pa | 0x80000000);
104 static inline UInt mapVAtoPA(Void * va)
106     return ((UInt)va & 0x000fffffU) | 0xe1000000U;
109 /*
110  * ======== VirtQueue_init ========
111  */
112 Void VirtQueue_init()
114     extern cregister volatile UInt DNUM;
115     UInt16 clusterId;
116     UInt16 procId;
118     /*  VirtQueue_init() must be called before MultiProcSetup_init().
119      *  Check the xdc_runtime_Startup_firstFxns__A array in the XDC
120      *  generated code. Abort if the procId has already been set; we
121      *  must set it!
122      */
123     if (MultiProc_self() != MultiProc_INVALIDID) {
124         System_abort("VirtQueue_init(): MultiProc_self already set!");
125         return;
126     }
128     /* clusterId is needed to support single image loading */
129     clusterId = MultiProc_getBaseIdOfCluster();
131     /* compute local procId, add one to account for HOST processor */
132     procId = clusterId + DNUM + 1;
134     /* set the local procId */
135     MultiProc_setLocalId(procId);
138 /*
139  * ======== VirtQueue_Instance_init ========
140  */
141 Int VirtQueue_Instance_init(VirtQueue_Object *vq, UInt16 remoteProcId,
142                              const VirtQueue_Params *params, Error_Block *eb)
144     void *vringAddr = NULL;
145 #if !defined(xdc_runtime_Assert_DISABLE_ALL) \
146         || !defined(xdc_runtime_Log_DISABLE_ALL)
147     UInt32 marValue;
148 #endif
150     VirtQueue_module->traceBufPtr = Resource_getTraceBufPtr();
152     /* create the thread protection gate */
153     vq->gateH = GateAll_create(NULL, eb);
154     if (Error_check(eb)) {
155         Log_error0("VirtQueue_create: could not create gate object");
156         Error_raise(NULL, Error_E_generic, 0, 0);
157         return(0);
158     }
160     vq->vringPtr = Memory_calloc(NULL, sizeof(struct vring), 0, eb);
161     Assert_isTrue((vq->vringPtr != NULL), NULL);
163     vq->callback = params->callback;
164     vq->id = params->vqId;
165     vq->procId = remoteProcId;
166     vq->last_avail_idx = 0;
167     vq->last_used_idx = 0;
168     vq->num_free = VirtQueue_RP_MSG_NUM_BUFS;
169     vq->swiHandle = params->swiHandle;
171     switch (vq->id) {
172         case ID_SELF_TO_HOST:
173         case ID_HOST_TO_SELF:
174             vringAddr = (struct vring *)Resource_getVringDA(vq->id);
175             Assert_isTrue(vringAddr != NULL, NULL);
176             /* Add per core offset: must match on host side: */
177             vringAddr = (struct vring *)((UInt32)vringAddr +
178                                          (DNUM * VirtQueue_VRING_OFFSET));
180             /* Also, assert that the vring address is non-cached: */
181 #if !defined(xdc_runtime_Assert_DISABLE_ALL) \
182         || !defined(xdc_runtime_Log_DISABLE_ALL)
183             marValue = Cache_getMar((Ptr)vringAddr);
184 #endif
185             Log_print1(Diags_USER1, "Vring cache is %s",
186                  (IArg)(marValue & 0x1 ? "enabled" : "disabled"));
187             Assert_isTrue(!(marValue & 0x1), NULL);
188             break ;
190          default:
191             Log_error1("VirtQueue_create: invalid vq->id: %d", vq->id);
192             GateAll_delete(&vq->gateH);
193             Memory_free(NULL, vq->vringPtr, sizeof(struct vring));
194             Error_raise(NULL, Error_E_generic, 0, 0);
195             return(0);
196     }
198     Log_print3(Diags_USER1, "vring: %d 0x%x (0x%x)", vq->id, (IArg)vringAddr,
199             RP_MSG_RING_SIZE);
201     vring_init(vq->vringPtr, VirtQueue_RP_MSG_NUM_BUFS, vringAddr,
202             VirtQueue_RP_MSG_VRING_ALIGN);
204     queueRegistry[vq->id] = vq;
205     return(0);
208 /*
209  * ======== VirtQueue_kick ========
210  */
211 Void VirtQueue_kick(VirtQueue_Handle vq)
213     struct vring *vring = vq->vringPtr;
214     Interrupt_IntInfo intInfo;
216     /* For now, simply interrupt remote processor */
217     if (vring->avail->flags & VRING_AVAIL_F_NO_INTERRUPT) {
218         Log_print0(Diags_USER1, "VirtQueue_kick: no kick because of "
219                 "VRING_AVAIL_F_NO_INTERRUPT");
220         return;
221     }
223     Log_print2(Diags_USER1, "VirtQueue_kick: Sending interrupt to proc %d "
224             "with payload 0x%x", (IArg)vq->procId, (IArg)vq->id);
226     intInfo.localIntId  = Interrupt_SRCS_BITPOS_CORE0;
227     Interrupt_intSend(vq->procId, &intInfo, vq->id);
230 /*
231  * ======== VirtQueue_addUsedBuf ========
232  */
233 Int VirtQueue_addUsedBuf(VirtQueue_Handle vq, Int16 head, Int len)
235     struct vring_used_elem *used;
236     struct vring *vring = vq->vringPtr;
237     IArg key;
239     key = GateAll_enter(vq->gateH);
240     if ((head > vring->num) || (head < 0)) {
241         Error_raise(NULL, Error_E_generic, 0, 0);
242     }
243     else {
244         /*
245          * The virtqueue contains a ring of used buffers.  Get a pointer to the
246          * next entry in that used ring.
247          */
248         used = &vring->used->ring[vring->used->idx % vring->num];
249         used->id = head;
250         used->len = len;
252         vring->used->idx++;
253     }
254     GateAll_leave(vq->gateH, key);
256     return (0);
259 /*
260  * ======== VirtQueue_addAvailBuf ========
261  */
262 Int VirtQueue_addAvailBuf(VirtQueue_Object *vq, Void *buf)
264     UInt16 avail;
265     struct vring *vring = vq->vringPtr;
266     IArg key;
268     key = GateAll_enter(vq->gateH);
269     if (vq->num_free == 0) {
270         /* There's no more space */
271         Error_raise(NULL, Error_E_generic, 0, 0);
272     }
273     else {
274         vq->num_free--;
276         avail =  vring->avail->idx++ % vring->num;
278         vring->desc[avail].addr = mapVAtoPA(buf);
279         vring->desc[avail].len = RPMSG_BUF_SIZE;
280     }
281     GateAll_leave(vq->gateH, key);
283     return (vq->num_free);
286 /*
287  * ======== VirtQueue_getUsedBuf ========
288  */
289 Void *VirtQueue_getUsedBuf(VirtQueue_Object *vq)
291     UInt16 head;
292     Void *buf;
293     struct vring *vring = vq->vringPtr;
294     IArg key;
296     key = GateAll_enter(vq->gateH);
297     /* There's nothing available? */
298     if (vq->last_used_idx == vring->used->idx) {
299         buf = NULL;
300     }
301     else {
302         head = vring->used->ring[vq->last_used_idx % vring->num].id;
303         vq->last_used_idx++;
304         vq->num_free++;
306         buf = mapPAtoVA(vring->desc[head].addr);
307     }
308     GateAll_leave(vq->gateH, key);
310     return (buf);
313 /*
314  * ======== VirtQueue_getAvailBuf ========
315  */
316 Int16 VirtQueue_getAvailBuf(VirtQueue_Handle vq, Void **buf, Int *len)
318     Int16 head;
319     struct vring *vring = vq->vringPtr;
320     IArg key;
322     key = GateAll_enter(vq->gateH);
323     Log_print6(Diags_USER1, "getAvailBuf vq: 0x%x %d %d %d 0x%x 0x%x",
324         (IArg)vq, (IArg)vq->last_avail_idx, (IArg)vring->avail->idx,
325         (IArg)vring->num, (IArg)&vring->avail, (IArg)vring->avail);
327     /*  Clear flag here to avoid race condition with remote processor.
328      *  This is a negative flag, clearing it means that we want to
329      *  receive an interrupt when a buffer has been added to the pool.
330      */
331     vring->used->flags &= ~VRING_USED_F_NO_NOTIFY;
333     /* There's nothing available? */
334     if (vq->last_avail_idx == vring->avail->idx) {
335         head = (-1);
336     }
337     else {
338         /* No need to be kicked about added buffers anymore */
339         vring->used->flags |= VRING_USED_F_NO_NOTIFY;
341         /*
342          * Grab the next descriptor number they're advertising, and increment
343          * the index we've seen.
344          */
345         head = vring->avail->ring[vq->last_avail_idx++ % vring->num];
347         *buf = mapPAtoVA(vring->desc[head].addr);
348         *len = vring->desc[head].len;
349     }
350     GateAll_leave(vq->gateH, key);
352     return (head);
355 /*
356  * ======== VirtQueue_isr ========
357  * Note 'msg' is ignored: it is only used where there is a mailbox payload.
358  */
359 static Void VirtQueue_isr(UArg msg)
361     VirtQueue_Object *vq;
363     Log_print1(Diags_USER1, "VirtQueue_isr received msg = 0x%x", msg);
365     vq = queueRegistry[0];
366     if (vq) {
367         vq->callback(vq);
368     }
369     vq = queueRegistry[1];
370     if (vq) {
371         vq->callback(vq);
372     }
375 /*
376  * ======== VirtQueue_startup ========
377  */
378 Void VirtQueue_startup(UInt16 remoteProcId, Bool isHost)
380     Interrupt_IntInfo intInfo;
382     intInfo.intVectorId = Interrupt_DSPINT;
383     intInfo.localIntId  = Interrupt_SRCS_BITPOS_HOST;
386     /*
387      * Wait for first kick from host, which happens to coincide with the
388      * priming of host's receive buffers, indicating host is ready to send.
389      * Since interrupt is cleared, we throw away this first kick, which is
390      * OK since we don't process this in the ISR anyway.
391      */
392     Log_print0(Diags_USER1, "VirtQueue_startup: Polling for host int...");
393     while (!Interrupt_checkAndClear(remoteProcId, &intInfo));
395     Interrupt_intRegister(remoteProcId, &intInfo, (Fxn)VirtQueue_isr, 0);
397     Log_print0(Diags_USER1, "Passed VirtQueue_startup");
400 /* By convention, Host VirtQueues host are the even number in the pair */
401 Bool VirtQueue_isSlave(VirtQueue_Handle vq)
403   return (vq->id & 0x1);
406 Bool VirtQueue_isHost(VirtQueue_Handle vq)
408   return (~(vq->id & 0x1));
411 UInt16 VirtQueue_getId(VirtQueue_Handle vq)
413   return (vq->id);
416 Swi_Handle VirtQueue_getSwiHandle(VirtQueue_Handle vq)
418   return (vq->swiHandle);
421 #define CACHE_WB_TICK_PERIOD    5
423 /*
424  * ======== VirtQueue_cacheWb ========
425  *
426  * Used for flushing SysMin trace buffer.
427  */
428 Void VirtQueue_cacheWb()
430     static UInt32 oldticks = 0;
431     UInt32 newticks;
433     newticks = Clock_getTicks();
434     if (newticks - oldticks < (UInt32)CACHE_WB_TICK_PERIOD) {
435         /* Don't keep flushing cache */
436         return;
437     }
439     oldticks = newticks;
441     /* Flush the cache of the SysMin buffer only: */
442     Assert_isTrue((VirtQueue_module->traceBufPtr != NULL), NULL);
443     Cache_wb(VirtQueue_module->traceBufPtr, SysMin_bufSize, Cache_Type_ALL,
444              FALSE);