]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - ipc/ipcdev.git/blob - src/ti/ipc/family/tci6638/VirtQueue.c
bf0dbbe2d5daad8ea88f1e52e013e7bf4a83b820
[ipc/ipcdev.git] / src / ti / ipc / family / tci6638 / VirtQueue.c
1 /*
2  * Copyright (c) 2011-2013, 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 <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>
65 #include <ti/sysbios/knl/Clock.h>
66 #include <ti/sysbios/family/c66/Cache.h>
67 #include <ti/sysbios/knl/Swi.h>
69 #include <ti/sdo/ipc/notifyDrivers/IInterrupt.h>
70 #include <ti/ipc/family/tci6638/Interrupt.h>
71 #include <ti/ipc/remoteproc/Resource.h>
73 #include <ti/ipc/MultiProc.h>
75 #include "package/internal/VirtQueue.xdc.h"
77 #include <string.h>
79 #include <ti/ipc/rpmsg/virtio_ring.h>
81 /* Used for defining the size of the virtqueue registry */
82 #define NUM_QUEUES                      2
84 #define DIV_ROUND_UP(n,d)   (((n) + (d) - 1) / (d))
85 #define RP_MSG_BUFS_SPACE   (VirtQueue_RP_MSG_NUM_BUFS * VirtQueue_RP_MSG_BUF_SIZE * 2)
87 /* With 256 buffers, our vring will occupy 3 pages */
88 #define RP_MSG_RING_SIZE    ((DIV_ROUND_UP(vring_size(VirtQueue_RP_MSG_NUM_BUFS, \
89                             VirtQueue_RP_MSG_VRING_ALIGN), VirtQueue_PAGE_SIZE)) * VirtQueue_PAGE_SIZE)
91 /* The total IPC space needed to communicate with a remote processor */
92 #define RPMSG_IPC_MEM   (RP_MSG_BUFS_SPACE + 2 * RP_MSG_RING_SIZE)
94 #define ID_DSP_TO_A9      0
95 #define ID_A9_TO_DSP      1
97 extern volatile cregister Uns DNUM;
99 static VirtQueue_Object *queueRegistry[NUM_QUEUES] = {NULL};
101 static inline  Void * mapPAtoVA(UInt pa)
103     return (Void *)(pa | 0x80000000);
106 static inline UInt mapVAtoPA(Void * va)
108     return ((UInt)va & 0x000fffffU) | 0xe1000000U;
111 /*
112  * ======== VirtQueue_init ========
113  */
114 Void VirtQueue_init()
116     extern cregister volatile UInt DNUM;
117     UInt16 procId;
119     /*
120      * VirtQueue_init() must be called before MultiProcSetup_init().
121      * (Check the xdc_runtime_Startup_firstFxns__A in the XDC generated code)
122      * Abort if the procId has already been set.  We must set it!
123      */
124     if (MultiProc_self() != MultiProc_INVALIDID) {
125         System_abort("VirtQueue_init(): MultiProc_self already set!");
126         return;
127     }
129     procId = DNUM + 1;
131     /* Set the local ID */
132     MultiProc_setLocalId(procId);
135 /*
136  * ======== VirtQueue_Instance_init ========
137  */
138 Void VirtQueue_Instance_init(VirtQueue_Object *vq, UInt16 remoteProcId,
139                              const VirtQueue_Params *params)
141     void *vring_phys = NULL;
142     Error_Block eb;
144     VirtQueue_module->traceBufPtr = Resource_getTraceBufPtr();
146     Error_init(&eb);
148     vq->vringPtr = Memory_calloc(NULL, sizeof(struct vring), 0, &eb);
149     Assert_isTrue((vq->vringPtr != NULL), NULL);
151     vq->callback = params->callback;
152     vq->id = VirtQueue_module->numQueues++;
153     vq->procId = remoteProcId;
154     vq->last_avail_idx = 0;
155     vq->last_used_idx = 0;
156     vq->num_free = VirtQueue_RP_MSG_NUM_BUFS;
157     vq->swiHandle = params->swiHandle;
159     switch (vq->id) {
160         case ID_DSP_TO_A9:
161             vring_phys = (struct vring *) (VirtQueue_CORE0_MEM_VRING0 +
162                 (DNUM * VirtQueue_VRING_OFFSET));
163             break;
164         case ID_A9_TO_DSP:
165             vring_phys = (struct vring *) (VirtQueue_CORE0_MEM_VRING1 +
166                 (DNUM * VirtQueue_VRING_OFFSET));
167             break;
168     }
170     Log_print3(Diags_USER1,
171             "vring: %d 0x%x (0x%x)\n", vq->id, (IArg)vring_phys,
172             RP_MSG_RING_SIZE);
174     vring_init(vq->vringPtr, VirtQueue_RP_MSG_NUM_BUFS, vring_phys, VirtQueue_RP_MSG_VRING_ALIGN);
176     queueRegistry[vq->id] = vq;
179 /*
180  * ======== VirtQueue_kick ========
181  */
182 Void VirtQueue_kick(VirtQueue_Handle vq)
184     IInterrupt_IntInfo intInfo;
185     struct vring *vring = vq->vringPtr;
187     /* For now, simply interrupt remote processor */
188     if (vring->avail->flags & VRING_AVAIL_F_NO_INTERRUPT) {
189         Log_print0(Diags_USER1,
190                 "VirtQueue_kick: no kick because of VRING_AVAIL_F_NO_INTERRUPT\n");
191         return;
192     }
194     Log_print2(Diags_USER1,
195             "VirtQueue_kick: Sending interrupt to proc %d with payload 0x%x\n",
196             (IArg)vq->procId, (IArg)vq->id);
198     intInfo.localIntId  = Interrupt_SRCS_BITPOS_CORE0;
199     Interrupt_intSend(vq->procId, &intInfo, vq->id);
202 /*
203  * ======== VirtQueue_addUsedBuf ========
204  */
205 Int VirtQueue_addUsedBuf(VirtQueue_Handle vq, Int16 head)
207     struct vring_used_elem *used;
208     struct vring *vring = vq->vringPtr;
210     if ((head > vring->num) || (head < 0)) {
211         Error_raise(NULL, Error_E_generic, 0, 0);
212     }
214     /*
215     * The virtqueue contains a ring of used buffers.  Get a pointer to the
216     * next entry in that used ring.
217     */
218     used = &vring->used->ring[vring->used->idx % vring->num];
219     used->id = head;
220     used->len = VirtQueue_RP_MSG_BUF_SIZE;
222     vring->used->idx++;
224     return (0);
227 /*
228  * ======== VirtQueue_addAvailBuf ========
229  */
230 Int VirtQueue_addAvailBuf(VirtQueue_Object *vq, Void *buf)
232     UInt16 avail;
233     struct vring *vring = vq->vringPtr;
235     if (vq->num_free == 0) {
236         /* There's no more space */
237         Error_raise(NULL, Error_E_generic, 0, 0);
238     }
240     vq->num_free--;
242     avail =  vring->avail->idx++ % vring->num;
244     vring->desc[avail].addr = mapVAtoPA(buf);
245     vring->desc[avail].len = VirtQueue_RP_MSG_BUF_SIZE;
247     return (vq->num_free);
250 /*
251  * ======== VirtQueue_getUsedBuf ========
252  */
253 Void *VirtQueue_getUsedBuf(VirtQueue_Object *vq)
255     UInt16 head;
256     Void *buf;
257     struct vring *vring = vq->vringPtr;
259     /* There's nothing available? */
260     if (vq->last_used_idx == vring->used->idx) {
261         return (NULL);
262     }
264     head = vring->used->ring[vq->last_used_idx % vring->num].id;
265     vq->last_used_idx++;
266     vq->num_free++;
268     buf = mapPAtoVA(vring->desc[head].addr);
270     return (buf);
273 /*
274  * ======== VirtQueue_getAvailBuf ========
275  */
276 Int16 VirtQueue_getAvailBuf(VirtQueue_Handle vq, Void **buf)
278     UInt16 head;
279     struct vring *vring = vq->vringPtr;
281     Log_print6(Diags_USER1, "getAvailBuf vq: 0x%x %d %d %d 0x%x 0x%x\n",
282         (IArg)vq,
283         vq->last_avail_idx, vring->avail->idx, vring->num,
284         (IArg)&vring->avail, (IArg)vring->avail);
286     /* There's nothing available? */
287     if (vq->last_avail_idx == vring->avail->idx) {
288         /* We need to know about added buffers */
289         vring->used->flags &= ~VRING_USED_F_NO_NOTIFY;
290         /* check again after setting flag */
291         if (vq->last_avail_idx == vring->avail->idx)
292             return -1;
293     }
295     /* No need to be kicked about added buffers anymore */
296     vring->used->flags |= VRING_USED_F_NO_NOTIFY;
298     /*
299      * Grab the next descriptor number they're advertising, and increment
300      * the index we've seen.
301      */
302     head = vring->avail->ring[vq->last_avail_idx++ % vring->num];
304     *buf = mapPAtoVA(vring->desc[head].addr);
306     return (head);
309 /*
310  * ======== VirtQueue_isr ========
311  * Note 'msg' is ignored: it is only used where there is a mailbox payload.
312  */
313 Void VirtQueue_isr(UArg msg)
315     VirtQueue_Object *vq;
317     Log_print1(Diags_USER1, "VirtQueue_isr received msg = 0x%x\n", msg);
319     vq = queueRegistry[0];
320     if (vq) {
321         vq->callback(vq);
322     }
323     vq = queueRegistry[1];
324     if (vq) {
325         vq->callback(vq);
326     }
329 /*
330  * ======== VirtQueue_startup ========
331  */
332 Void VirtQueue_startup(UInt16 remoteProcId, Bool isHost)
334     IInterrupt_IntInfo intInfo;
336     intInfo.intVectorId = Interrupt_DSPINT;
337     intInfo.localIntId  = Interrupt_SRCS_BITPOS_HOST;
340     /*
341      * Wait for first kick from host, which happens to coincide with the
342      * priming of host's receive buffers, indicating host is ready to send.
343      * Since interrupt is cleared, we throw away this first kick, which is
344      * OK since we don't process this in the ISR anyway.
345      */
346     Log_print0(Diags_USER1, "VirtQueue_startup: Polling for host int...\n");
347     while (!Interrupt_checkAndClear(remoteProcId, &intInfo));
349     Interrupt_intRegister(remoteProcId, &intInfo, (Fxn)VirtQueue_isr, NULL);
351     Log_print0(Diags_USER1, "Passed VirtQueue_startup\n");
354 /* By convention, Host VirtQueues host are the even number in the pair */
355 Bool VirtQueue_isSlave(VirtQueue_Handle vq)
357   return (vq->id & 0x1);
360 Bool VirtQueue_isHost(VirtQueue_Handle vq)
362   return (~(vq->id & 0x1));
365 UInt16 VirtQueue_getId(VirtQueue_Handle vq)
367   return (vq->id);
370 Swi_Handle VirtQueue_getSwiHandle(VirtQueue_Handle vq)
372   return (vq->swiHandle);
375 #define CACHE_WB_TICK_PERIOD    5
377 /*
378  * ======== VirtQueue_cacheWb ========
379  *
380  * Used for flushing SysMin trace buffer.
381  */
382 Void VirtQueue_cacheWb()
384     static UInt32 oldticks = 0;
385     UInt32 newticks;
387     newticks = Clock_getTicks();
388     if (newticks - oldticks < (UInt32)CACHE_WB_TICK_PERIOD) {
389         /* Don't keep flushing cache */
390         return;
391     }
393     oldticks = newticks;
395     /* Flush the cache of the SysMin buffer only: */
396     Assert_isTrue((VirtQueue_module->traceBufPtr != NULL), NULL);
397     Cache_wb(VirtQueue_module->traceBufPtr, SysMin_bufSize, Cache_Type_ALL,
398              FALSE);