omapl138: VirtQueue: Fixup mapVAtoPA() and mapPAtoVA()
[ipc/ipcdev.git] / packages / ti / ipc / family / omapl138 / VirtQueue.c
1 /*
2  * Copyright (c) 2011-2018, 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 <ti/trace/SysMin.h>
64 #include <ti/sysbios/gates/GateAll.h>
66 #include <ti/sysbios/knl/Clock.h>
67 #include <ti/sysbios/family/c64p/Cache.h>
68 #include <ti/sysbios/knl/Swi.h>
70 #include <ti/sdo/ipc/notifyDrivers/IInterrupt.h>
71 #include <ti/sdo/ipc/family/da830/InterruptDsp.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 #define CONSOLE_DSP_TO_A9 2
100 #define CONSOLE_A9_TO_DSP 3
102 /* TODO: do we need these to be configurable? */
103 #define DSPEVENTID              5
104 #define DSPINT                  5
105 #define DSP2ARM_CHIPINT0        28
107 static VirtQueue_Object *queueRegistry[NUM_QUEUES] = {NULL};
109 static inline Void * mapPAtoVA(UInt pa)
111     return (Void *)(pa);
114 static inline UInt mapVAtoPA(Void * va)
116     return (UInt)va;
119 /*
120  * ======== VirtQueue_Instance_init ========
121  */
122 Int VirtQueue_Instance_init(VirtQueue_Object *vq, UInt16 remoteProcId,
123                              const VirtQueue_Params *params, Error_Block *eb)
126     void *vringAddr = NULL;
127     Cache_Mar         marValue;
129     VirtQueue_module->traceBufPtr = Resource_getTraceBufPtr();
131     /* Create the thread protection gate */
132     vq->gateH = GateAll_create(NULL, eb);
133     if (Error_check(eb)) {
134         Log_error0("VirtQueue_create: could not create gate object");
135         Error_raise(NULL, Error_E_generic, 0, 0);
136         return(0);
137     }
139     vq->vringPtr = Memory_calloc(NULL, sizeof(struct vring), 0, eb);
140     Assert_isTrue((vq->vringPtr != NULL), NULL);
143     vq->callback = params->callback;
144     vq->id = params->vqId;
145     vq->procId = remoteProcId;
146     vq->last_avail_idx = 0;
147     vq->last_used_idx = 0;
148     vq->num_free = VirtQueue_RP_MSG_NUM_BUFS;
150     switch (vq->id) {
151         case ID_DSP_TO_A9:
152         case ID_A9_TO_DSP:
153             vringAddr = (struct vring *)Resource_getVringDA(vq->id);
154             Assert_isTrue(vringAddr != NULL, NULL);
156             /* Also, assert that the vring address is non-cached: */
157             marValue = Cache_getMar((Ptr)vringAddr);
158             Log_print1(Diags_USER1, "Vring cache is %s",
159                  (IArg)(marValue == Cache_Mar_ENABLE? "enabled" : "disabled"));
160             Assert_isTrue(marValue == Cache_Mar_DISABLE, NULL);
161             break;
162          default:
163             Log_error1("VirtQueue_create: invalid vq->id: %d", vq->id);
164             GateAll_delete(&vq->gateH);
165             Memory_free(NULL, vq->vringPtr, sizeof(struct vring));
166             Error_raise(NULL, Error_E_generic, 0, 0);
167             return(0);
168     }
170     Log_print3(Diags_USER1,
171             "vring: %d 0x%x (0x%x)\n", vq->id, (IArg)vringAddr,
172             RP_MSG_RING_SIZE);
174     vring_init(vq->vringPtr, VirtQueue_RP_MSG_NUM_BUFS, vringAddr, VirtQueue_RP_MSG_VRING_ALIGN);
176     queueRegistry[vq->id] = vq;
177     return(0);
180 /*
181  * ======== VirtQueue_kick ========
182  */
183 Void VirtQueue_kick(VirtQueue_Handle vq)
185     struct vring *vring = vq->vringPtr;
186     IInterrupt_IntInfo intInfo;
188     intInfo.remoteIntId = DSP2ARM_CHIPINT0;
190     /* For now, simply interrupt remote processor */
191     if (vring->avail->flags & VRING_AVAIL_F_NO_INTERRUPT) {
192         Log_print0(Diags_USER1,
193                 "VirtQueue_kick: no kick because of VRING_AVAIL_F_NO_INTERRUPT\n");
194         return;
195     }
197     Log_print2(Diags_USER1,
198             "VirtQueue_kick: Sending interrupt to proc %d with payload 0x%x\n",
199             (IArg)vq->procId, (IArg)vq->id);
200     InterruptDsp_intSend(vq->procId, &intInfo, vq->id);
203 /*
204  * ======== VirtQueue_addUsedBuf ========
205  */
206 Int VirtQueue_addUsedBuf(VirtQueue_Handle vq, Int16 head, Int len)
208     struct vring_used_elem *used;
209     struct vring *vring = vq->vringPtr;
210     IArg key;
212     key = GateAll_enter(vq->gateH);
213     if ((head > vring->num) || (head < 0)) {
214         Error_raise(NULL, Error_E_generic, 0, 0);
215     }
216     else {
217         /*
218          * The virtqueue contains a ring of used buffers.  Get a pointer to the
219          * next entry in that used ring.
220          */
221         used = &vring->used->ring[vring->used->idx % vring->num];
222         used->id = head;
223         used->len = len;
225         vring->used->idx++;
226     }
227     GateAll_leave(vq->gateH, key);
229     return (0);
232 /*
233  * ======== VirtQueue_addAvailBuf ========
234  */
235 Int VirtQueue_addAvailBuf(VirtQueue_Object *vq, Void *buf)
237     UInt16 avail;
238     struct vring *vring = vq->vringPtr;
239     IArg key;
241     key = GateAll_enter(vq->gateH);
242     if (vq->num_free == 0) {
243         /* There's no more space */
244         Error_raise(NULL, Error_E_generic, 0, 0);
245     }
246     else {
247         vq->num_free--;
249         avail =  vring->avail->idx++ % vring->num;
251         vring->desc[avail].addr = mapVAtoPA(buf);
252         vring->desc[avail].len = RPMSG_BUF_SIZE;
253     }
254     GateAll_leave(vq->gateH, key);
256     return (vq->num_free);
259 /*
260  * ======== VirtQueue_getUsedBuf ========
261  */
262 Void *VirtQueue_getUsedBuf(VirtQueue_Object *vq)
264     UInt16 head;
265     Void *buf;
266     struct vring *vring = vq->vringPtr;
267     IArg key;
269     key = GateAll_enter(vq->gateH);
270     /* There's nothing available? */
271     if (vq->last_used_idx == vring->used->idx) {
272         buf = NULL;
273     }
274     else {
275         head = vring->used->ring[vq->last_used_idx % vring->num].id;
276         vq->last_used_idx++;
277         vq->num_free++;
279         buf = mapPAtoVA(vring->desc[head].addr);
280     }
281     GateAll_leave(vq->gateH, key);
283     return (buf);
286 /*
287  * ======== VirtQueue_getAvailBuf ========
288  */
289 Int16 VirtQueue_getAvailBuf(VirtQueue_Handle vq, Void **buf, Int *len)
291     Int16 head;
292     struct vring *vring = vq->vringPtr;
293     IArg key;
295     key = GateAll_enter(vq->gateH);
296     Log_print6(Diags_USER1, "getAvailBuf vq: 0x%x %d %d %d 0x%x 0x%x\n",
297     (IArg)vq,
298         vq->last_avail_idx, vring->avail->idx, vring->num,
299         (IArg)&vring->avail, (IArg)vring->avail);
301     /*  Clear flag here to avoid race condition with remote processor.
302      *  This is a negative flag, clearing it means that we want to
303      *  receive an interrupt when a buffer has been added to the pool.
304      */
305     vring->used->flags &= ~VRING_USED_F_NO_NOTIFY;
307     /* There's nothing available? */
308     if (vq->last_avail_idx == vring->avail->idx) {
309         head = (-1);
310     }
311     else {
312         /* No need to be kicked about added buffers anymore */
313         vring->used->flags |= VRING_USED_F_NO_NOTIFY;
315         /*
316          * Grab the next descriptor number they're advertising, and increment
317          * the index we've seen.
318          */
319         head = vring->avail->ring[vq->last_avail_idx++ % vring->num];
321         *buf = mapPAtoVA(vring->desc[head].addr);
322         *len = vring->desc[head].len;
323     }
324     GateAll_leave(vq->gateH, key);
326     return (head);
329 /*
330  * ======== VirtQueue_isr ========
331  * Note 'msg' is ignored: it is only used where there is a mailbox payload.
332  */
333 Void VirtQueue_isr(UArg msg)
335     VirtQueue_Object *vq;
336     IInterrupt_IntInfo intInfo;
338     Log_print1(Diags_USER1, "VirtQueue_isr received msg = 0x%x\n", msg);
340     intInfo.localIntId = DSPEVENTID;
341     intInfo.remoteIntId = DSP2ARM_CHIPINT0;
343     InterruptDsp_intClear(msg, &intInfo);
345     vq = queueRegistry[0];
346     if (vq) {
347         vq->callback(vq);
348     }
349     vq = queueRegistry[1];
350     if (vq) {
351        vq->callback(vq);
352     }
356 /*
357  * ======== VirtQueue_startup ========
358  */
359 Void VirtQueue_startup(UInt16 remoteProcId, Bool isHost)
361     IInterrupt_IntInfo intInfo;
363     /*
364      * Wait for first kick from host, which happens to coincide with the
365      * priming of host's receive buffers, indicating host is ready to send.
366      * Since interrupt is cleared, we throw away this first kick, which is
367      * OK since we don't process this in the ISR anyway.
368      */
369     intInfo.intVectorId = DSPINT;
370     intInfo.localIntId = DSPEVENTID;
371     intInfo.remoteIntId = DSP2ARM_CHIPINT0;  /* ??? don't care??? */
372     while (InterruptDsp_isIntSet(remoteProcId, &intInfo) == FALSE);
373     InterruptDsp_intClear(remoteProcId, &intInfo);
375     InterruptDsp_intRegister(remoteProcId, &intInfo, (Fxn)VirtQueue_isr, 0);
376     Log_print0(Diags_USER1, "Passed VirtQueue_startup\n");
379 /* By convention, Host VirtQueues host are the even number in the pair */
380 Bool VirtQueue_isSlave(VirtQueue_Handle vq)
382   return (vq->id & 0x1);
385 Bool VirtQueue_isHost(VirtQueue_Handle vq)
387   return (~(vq->id & 0x1));
390 UInt16 VirtQueue_getId(VirtQueue_Handle vq)
392   return (vq->id);
395 #define CACHE_WB_TICK_PERIOD    5
397 /*
398  * ======== VirtQueue_cacheWb ========
399  *
400  * Used for flushing SysMin trace buffer.
401  */
402 Void VirtQueue_cacheWb()
404     static UInt32 oldticks = 0;
405     UInt32 newticks;
407     newticks = Clock_getTicks();
408     if (newticks - oldticks < (UInt32)CACHE_WB_TICK_PERIOD) {
409         /* Don't keep flushing cache */
410         return;
411     }
413     oldticks = newticks;
415     /* Flush the cache of the SysMin buffer only: */
416     Assert_isTrue((VirtQueue_module->traceBufPtr != NULL), NULL);
417     Cache_wb(VirtQueue_module->traceBufPtr, SysMin_bufSize, Cache_Type_ALL,
418              FALSE);