3c449e9d3be4b678fc5aec950546b94d77eeabc4
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/hal/Cache.h>
67 #include <ti/sysbios/knl/Swi.h>
69 #include <ti/sdo/ipc/notifyDrivers/IInterrupt.h>
70 #include <ti/sdo/ipc/family/da830/InterruptDsp.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/_VirtQueue.h>
80 #include <ti/ipc/rpmsg/virtio_ring.h>
82 /* Used for defining the size of the virtqueue registry */
83 #define NUM_QUEUES 2
85 /* Predefined device addresses */
86 #define IPU_MEM_VRING0 0xc3000000
87 #define IPU_MEM_VRING1 0xc3004000
90 #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
91 #define RP_MSG_BUFS_SPACE (VirtQueue_RP_MSG_NUM_BUFS * RPMSG_BUF_SIZE * 2)
93 /* With 256 buffers, our vring will occupy 3 pages */
94 #define RP_MSG_RING_SIZE ((DIV_ROUND_UP(vring_size(VirtQueue_RP_MSG_NUM_BUFS, \
95 VirtQueue_RP_MSG_VRING_ALIGN), VirtQueue_PAGE_SIZE)) * VirtQueue_PAGE_SIZE)
97 /* The total IPC space needed to communicate with a remote processor */
98 #define RPMSG_IPC_MEM (RP_MSG_BUFS_SPACE + 2 * RP_MSG_RING_SIZE)
100 #define ID_DSP_TO_A9 0
101 #define ID_A9_TO_DSP 1
103 #define CONSOLE_DSP_TO_A9 2
104 #define CONSOLE_A9_TO_DSP 3
106 /* TODO: do we need these to be configurable? */
107 #define DSPEVENTID 5
108 #define DSPINT 5
109 #define DSP2ARM_CHIPINT0 28
111 static VirtQueue_Object *queueRegistry[NUM_QUEUES] = {NULL};
113 static inline Void * mapPAtoVA(UInt pa)
114 {
115 return (Void *)((pa & 0x000fffffU) | 0xc3000000U);
116 }
118 static inline UInt mapVAtoPA(Void * va)
119 {
120 return ((UInt)va & 0x000fffffU) | 0xc9000000U;
121 }
123 /*
124 * ======== VirtQueue_Instance_init ========
125 */
126 Void VirtQueue_Instance_init(VirtQueue_Object *vq, UInt16 remoteProcId,
127 const VirtQueue_Params *params)
128 {
129 void *vringAddr = NULL;
130 Error_Block eb;
132 VirtQueue_module->traceBufPtr = Resource_getTraceBufPtr();
134 Error_init(&eb);
136 vq->vringPtr = Memory_calloc(NULL, sizeof(struct vring), 0, &eb);
137 Assert_isTrue((vq->vringPtr != NULL), NULL);
139 vq->callback = params->callback;
140 vq->id = params->vqId;
141 vq->procId = remoteProcId;
142 vq->last_avail_idx = 0;
143 vq->last_used_idx = 0;
144 vq->num_free = VirtQueue_RP_MSG_NUM_BUFS;
146 switch (vq->id) {
147 /* sysm3 rpmsg vrings */
148 case ID_DSP_TO_A9:
149 vringAddr = (struct vring *) IPU_MEM_VRING0;
150 break;
151 case ID_A9_TO_DSP:
152 vringAddr = (struct vring *) IPU_MEM_VRING1;
153 break;
154 }
156 Log_print3(Diags_USER1,
157 "vring: %d 0x%x (0x%x)\n", vq->id, (IArg)vringAddr,
158 RP_MSG_RING_SIZE);
160 vring_init(vq->vringPtr, VirtQueue_RP_MSG_NUM_BUFS, vringAddr, VirtQueue_RP_MSG_VRING_ALIGN);
162 queueRegistry[vq->id] = vq;
163 }
165 /*
166 * ======== VirtQueue_kick ========
167 */
168 Void VirtQueue_kick(VirtQueue_Handle vq)
169 {
170 struct vring *vring = vq->vringPtr;
171 IInterrupt_IntInfo intInfo;
173 intInfo.remoteIntId = DSP2ARM_CHIPINT0;
175 /* For now, simply interrupt remote processor */
176 if (vring->avail->flags & VRING_AVAIL_F_NO_INTERRUPT) {
177 Log_print0(Diags_USER1,
178 "VirtQueue_kick: no kick because of VRING_AVAIL_F_NO_INTERRUPT\n");
179 return;
180 }
182 Log_print2(Diags_USER1,
183 "VirtQueue_kick: Sending interrupt to proc %d with payload 0x%x\n",
184 (IArg)vq->procId, (IArg)vq->id);
185 InterruptDsp_intSend(vq->procId, &intInfo, vq->id);
186 }
188 /*
189 * ======== VirtQueue_addUsedBuf ========
190 */
191 Int VirtQueue_addUsedBuf(VirtQueue_Handle vq, Int16 head, Int len)
192 {
193 struct vring_used_elem *used;
194 struct vring *vring = vq->vringPtr;
196 if ((head > vring->num) || (head < 0)) {
197 Error_raise(NULL, Error_E_generic, 0, 0);
198 }
200 /*
201 * The virtqueue contains a ring of used buffers. Get a pointer to the
202 * next entry in that used ring.
203 */
204 used = &vring->used->ring[vring->used->idx % vring->num];
205 used->id = head;
206 used->len = len;
208 vring->used->idx++;
210 return (0);
211 }
213 /*
214 * ======== VirtQueue_addAvailBuf ========
215 */
216 Int VirtQueue_addAvailBuf(VirtQueue_Object *vq, Void *buf)
217 {
218 UInt16 avail;
219 struct vring *vring = vq->vringPtr;
221 if (vq->num_free == 0) {
222 /* There's no more space */
223 Error_raise(NULL, Error_E_generic, 0, 0);
224 }
226 vq->num_free--;
228 avail = vring->avail->idx++ % vring->num;
230 vring->desc[avail].addr = mapVAtoPA(buf);
231 vring->desc[avail].len = RPMSG_BUF_SIZE;
233 return (vq->num_free);
234 }
236 /*
237 * ======== VirtQueue_getUsedBuf ========
238 */
239 Void *VirtQueue_getUsedBuf(VirtQueue_Object *vq)
240 {
241 UInt16 head;
242 Void *buf;
243 struct vring *vring = vq->vringPtr;
245 /* There's nothing available? */
246 if (vq->last_used_idx == vring->used->idx) {
247 return (NULL);
248 }
250 head = vring->used->ring[vq->last_used_idx % vring->num].id;
251 vq->last_used_idx++;
252 vq->num_free++;
254 buf = mapPAtoVA(vring->desc[head].addr);
256 return (buf);
257 }
259 /*
260 * ======== VirtQueue_getAvailBuf ========
261 */
262 Int16 VirtQueue_getAvailBuf(VirtQueue_Handle vq, Void **buf, Int *len)
263 {
264 UInt16 head;
265 struct vring *vring = vq->vringPtr;
267 Log_print6(Diags_USER1, "getAvailBuf vq: 0x%x %d %d %d 0x%x 0x%x\n",
268 (IArg)vq,
269 vq->last_avail_idx, vring->avail->idx, vring->num,
270 (IArg)&vring->avail, (IArg)vring->avail);
272 /* There's nothing available? */
273 if (vq->last_avail_idx == vring->avail->idx) {
274 /* We need to know about added buffers */
275 vring->used->flags &= ~VRING_USED_F_NO_NOTIFY;
276 return (-1);
277 }
279 /* No need to be kicked about added buffers anymore */
280 vring->used->flags |= VRING_USED_F_NO_NOTIFY;
282 /*
283 * Grab the next descriptor number they're advertising, and increment
284 * the index we've seen.
285 */
286 head = vring->avail->ring[vq->last_avail_idx++ % vring->num];
288 *buf = mapPAtoVA(vring->desc[head].addr);
289 *len = vring->desc[head].len;
291 return (head);
292 }
294 /*
295 * ======== VirtQueue_isr ========
296 * Note 'msg' is ignored: it is only used where there is a mailbox payload.
297 */
298 Void VirtQueue_isr(UArg msg)
299 {
300 VirtQueue_Object *vq;
301 IInterrupt_IntInfo intInfo;
303 Log_print1(Diags_USER1, "VirtQueue_isr received msg = 0x%x\n", msg);
305 intInfo.localIntId = DSPEVENTID;
306 intInfo.remoteIntId = DSP2ARM_CHIPINT0;
308 InterruptDsp_intClear(msg, &intInfo);
310 vq = queueRegistry[0];
311 if (vq) {
312 vq->callback(vq);
313 }
314 vq = queueRegistry[1];
315 if (vq) {
316 vq->callback(vq);
317 }
318 }
321 /*
322 * ======== VirtQueue_startup ========
323 */
324 Void VirtQueue_startup(UInt16 remoteProcId, Bool isHost)
325 {
326 IInterrupt_IntInfo intInfo;
328 /*
329 * Wait for first kick from host, which happens to coincide with the
330 * priming of host's receive buffers, indicating host is ready to send.
331 * Since interrupt is cleared, we throw away this first kick, which is
332 * OK since we don't process this in the ISR anyway.
333 */
334 intInfo.intVectorId = DSPINT;
335 intInfo.localIntId = DSPEVENTID;
336 intInfo.remoteIntId = DSP2ARM_CHIPINT0; /* ??? don't care??? */
337 while (InterruptDsp_isIntSet(remoteProcId, &intInfo) == FALSE);
338 InterruptDsp_intClear(remoteProcId, &intInfo);
340 InterruptDsp_intRegister(remoteProcId, &intInfo, (Fxn)VirtQueue_isr, NULL);
341 Log_print0(Diags_USER1, "Passed VirtQueue_startup\n");
342 }
344 /* By convention, Host VirtQueues host are the even number in the pair */
345 Bool VirtQueue_isSlave(VirtQueue_Handle vq)
346 {
347 return (vq->id & 0x1);
348 }
350 Bool VirtQueue_isHost(VirtQueue_Handle vq)
351 {
352 return (~(vq->id & 0x1));
353 }
355 UInt16 VirtQueue_getId(VirtQueue_Handle vq)
356 {
357 return (vq->id);
358 }
360 #define CACHE_WB_TICK_PERIOD 5
362 /*
363 * ======== VirtQueue_cacheWb ========
364 *
365 * Used for flushing SysMin trace buffer.
366 */
367 Void VirtQueue_cacheWb()
368 {
369 static UInt32 oldticks = 0;
370 UInt32 newticks;
372 newticks = Clock_getTicks();
373 if (newticks - oldticks < (UInt32)CACHE_WB_TICK_PERIOD) {
374 /* Don't keep flushing cache */
375 return;
376 }
378 oldticks = newticks;
380 /* Flush the cache of the SysMin buffer only: */
381 Assert_isTrue((VirtQueue_module->traceBufPtr != NULL), NULL);
382 Cache_wb(VirtQueue_module->traceBufPtr, SysMin_bufSize, Cache_Type_ALL,
383 FALSE);
385 }