26d1c42c79752950e4fa96fb40bdb2e25ff677ac
[ipc/ipcdev.git] / packages / ti / sdo / ipc / notifyDrivers / NotifyDriverCirc.c
1 /*
2  * Copyright (c) 2012-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  *  ======== NotifyDriverCirc.c ========
34  */
36 #include <xdc/std.h>
38 #include <xdc/runtime/Assert.h>
39 #include <xdc/runtime/Timestamp.h>
41 #include <ti/sysbios/hal/Hwi.h>
42 #include <ti/sysbios/hal/Cache.h>
44 #include <ti/sdo/ipc/interfaces/INotifyDriver.h>
46 #include "package/internal/NotifyDriverCirc.xdc.h"
48 #include <ti/sdo/ipc/_Ipc.h>
49 #include <ti/sdo/ipc/_Notify.h>
50 #include <ti/sdo/ipc/_SharedRegion.h>
51 #include <ti/sdo/utils/_MultiProc.h>
53 /* Bit mask operations */
54 #define SET_BIT(num,pos)            ((num) |= (1u << (pos)))
55 #define CLEAR_BIT(num,pos)          ((num) &= ~(1u << (pos)))
56 #define TEST_BIT(num,pos)           ((num) & (1u << (pos)))
59 /*
60  **************************************************************
61  *                       Instance functions
62  **************************************************************
63  */
65 /*
66  *  ======== NotifyDriverCirc_Instance_init ========
67  */
68 Void NotifyDriverCirc_Instance_init(NotifyDriverCirc_Object *obj,
69                                   const NotifyDriverCirc_Params *params)
70 {
71     UInt16 regionId;
72     UInt16 localIndex, remoteIndex;
73     SizeT  regionCacheSize, minAlign;
74     SizeT  ctrlSize, circBufSize, totalSelfSize;
76    /*
77     * Check whether remote proc ID has been set and isn't the same as the
78     * local proc ID
79     */
80     Assert_isTrue ((params->remoteProcId != MultiProc_INVALIDID) &&
81                    (params->remoteProcId != MultiProc_self()),
82                    ti_sdo_ipc_Ipc_A_invParam);
84     /*
85      *  Determine obj->cacheEnabled using params->cacheEnabled and SharedRegion
86      *  cache flag setting, if applicable.
87      */
88     obj->cacheEnabled = params->cacheEnabled;
89     minAlign = params->cacheLineSize;
90     if (minAlign == 0) {
91         /* Fix alignment of zero */
92         minAlign = sizeof(Ptr);
93     }
95     regionId = SharedRegion_getId(params->sharedAddr);
96     if (regionId != SharedRegion_INVALIDREGIONID) {
97         /*
98          *  Override the user cacheEnabled setting if the region
99          *  cacheEnabled is FALSE.
100          */
101         if (!SharedRegion_isCacheEnabled(regionId)) {
102             obj->cacheEnabled = FALSE;
103         }
105         regionCacheSize = SharedRegion_getCacheLineSize(regionId);
107         /*
108          *  Override the user cache line size setting if the region
109          *  cache line size is smaller.
110          */
111         if (regionCacheSize < minAlign) {
112             minAlign = regionCacheSize;
113         }
114     }
116     /* Check if shared memory base addr is aligned to cache line boundary.*/
117     Assert_isTrue ((UInt32)params->sharedAddr % minAlign == 0,
118         ti_sdo_ipc_Ipc_A_addrNotCacheAligned);
120     /*
121      *  Store all interrupt information so it may be used (if neccessary) by
122      *  the IInterrupt delegates
123      */
124     obj->intInfo.remoteIntId = params->remoteIntId;
125     obj->intInfo.localIntId  = params->localIntId;
126     obj->intInfo.intVectorId = params->intVectorId;
128     /* determine which slot to use */
129     if (params->remoteProcId > MultiProc_self()) {
130         localIndex  = 0;
131         remoteIndex = 1;
132     }
133     else {
134         localIndex  = 1;
135         remoteIndex = 0;
136     }
138     /* set the remote processor's id */
139     obj->remoteProcId = params->remoteProcId;
141     /* counters for capturing spin wait statistics */
142     obj->spinCount = 0;
143     obj->spinWaitTime = 0;
145     /* calculate the circular buffer size one-way */
146     circBufSize = _Ipc_roundup(
147         sizeof(NotifyDriverCirc_EventEntry) * NotifyDriverCirc_numMsgs,
148                minAlign);
150     /* calculate the control size one-way */
151     ctrlSize = _Ipc_roundup(sizeof(Bits32), minAlign);
153     /* calculate the total size one-way */
154     totalSelfSize =  circBufSize + (2 * ctrlSize);
156     /*
157      *  Init put/get buffer and index pointers.
158      *  These are all on different cache lines.
159      */
160     obj->putBuffer = (NotifyDriverCirc_EventEntry *)
161         ((UInt32)params->sharedAddr + (localIndex * totalSelfSize));
163     obj->putWriteIndex = (Bits32 *)((UInt32)obj->putBuffer + circBufSize);
165     obj->putReadIndex = (Bits32 *)((UInt32)obj->putWriteIndex + ctrlSize);
167     obj->getBuffer = (NotifyDriverCirc_EventEntry *)
168         ((UInt32)params->sharedAddr + (remoteIndex * totalSelfSize));
170     obj->getWriteIndex = (Bits32 *)((UInt32)obj->getBuffer + circBufSize);
172     obj->getReadIndex = (Bits32 *)((UInt32)obj->getWriteIndex + ctrlSize);
174     /*
175      *  Calculate the size for cache wb/inv in sendEvent and isr.
176      *  This size is the circular buffer + putWriteIndex.
177      *  [sizeof(EventEntry) * numMsgs] + [the sizeof(Ptr)]
178      *  aligned to a cache line.
179      */
180     obj->opCacheSize = ((UInt32)obj->putReadIndex - (UInt32)obj->putBuffer);
182     /* init the putWrite and putRead Index to 0 */
183     obj->putWriteIndex[0] = 0;
184     obj->putReadIndex[0] = 0;
186     /* cache wb the putWrite/Read Index but no need to inv them */
187     if (obj->cacheEnabled) {
188         Cache_wb(obj->putWriteIndex,
189                  sizeof(Bits32),
190                  Cache_Type_ALL, TRUE);
192         Cache_wb(obj->putReadIndex,
193                  sizeof(Bits32),
194                  Cache_Type_ALL, TRUE);
196         /* invalidate any stale data of the get buffer and indexes */
197         Cache_inv(obj->getBuffer,
198                   totalSelfSize,
199                   Cache_Type_ALL, TRUE);
200     }
202     /* Register the incoming interrupt */
203     NotifyDriverCirc_InterruptProxy_intRegister(obj->remoteProcId,
204         &(obj->intInfo), (Fxn)NotifyDriverCirc_isr, (UArg)obj);
207 /*
208  *  ======== NotifyDriverCirc_Instance_finalize ========
209  */
210 Void NotifyDriverCirc_Instance_finalize(NotifyDriverCirc_Object *obj)
212     SizeT  sizeToInv;
214     /* Unregister interrupt */
215     NotifyDriverCirc_InterruptProxy_intUnregister(obj->remoteProcId,
216         &(obj->intInfo));
218     /* cache inv the shared memory that is used for instance */
219     if (obj->cacheEnabled) {
220         if (obj->remoteProcId > MultiProc_self()) {
221             /* calculate the size of the buffer and indexes for one side */
222             sizeToInv = ((UInt32)obj->getBuffer - (UInt32)obj->putBuffer);
224             /* invalidate the shared memory for this instance */
225             Cache_inv(obj->putBuffer,
226                      (sizeToInv * 2),
227                       Cache_Type_ALL, TRUE);
228         }
229         else {
230             /* calculate the size of the buffer and indexes for one side */
231             sizeToInv = ((UInt32)obj->putBuffer - (UInt32)obj->getBuffer);
233             /* invalidate the shared memory for this instance */
234             Cache_inv(obj->getBuffer,
235                      (sizeToInv * 2),
236                       Cache_Type_ALL, TRUE);
237         }
238     }
241 /*
242  *  ======== NotifyDriverCirc_registerEvent ========
243  */
244 Void NotifyDriverCirc_registerEvent(NotifyDriverCirc_Object *obj,
245                                    UInt32 eventId)
247     UInt hwiKey;
249     /*
250      *  Disable interrupt line to ensure that isr doesn't
251      *  preempt registerEvent and encounter corrupt state
252      */
253     hwiKey = Hwi_disable();
255     /* Set the 'registered' bit */
256     SET_BIT(obj->evtRegMask, eventId);
258     /* Restore the interrupt line */
259     Hwi_restore(hwiKey);
262 /*
263  *  ======== NotifyDriverCirc_unregisterEvent ========
264  */
265 Void NotifyDriverCirc_unregisterEvent(NotifyDriverCirc_Object *obj,
266                                      UInt32 eventId)
268     UInt hwiKey;
270     /*
271      *  Disable interrupt line to ensure that isr doesn't
272      *  preempt registerEvent and encounter corrupt state
273      */
274     hwiKey = Hwi_disable();
276     /* Clear the registered bit */
277     CLEAR_BIT(obj->evtRegMask, eventId);
279     /* Restore the interrupt line */
280     Hwi_restore(hwiKey);
283 /*
284  *  ======== NotifyDriverCirc_sendEvent ========
285  */
286 Int NotifyDriverCirc_sendEvent(NotifyDriverCirc_Object *obj,
287                               UInt32                 eventId,
288                               UInt32                 payload,
289                               Bool                   waitClear)
291     Bool loop = FALSE;
292     Bool spinWait = FALSE;
293     UInt hwiKey;
294     UInt32 startWaitTime, spinWaitTime;
295     UInt32 writeIndex, readIndex;
296     NotifyDriverCirc_EventEntry *eventEntry;
298     /*
299      *  Retrieve the get Index. No need to cache inv the
300      *  readIndex until out writeIndex wraps. Only need to invalidate
301      *  once every N times [N = number of slots in buffer].
302      */
303     readIndex = obj->putReadIndex[0];
305     do {
306         /* disable interrupts */
307         hwiKey = Hwi_disable();
309         /* retrieve the put index */
310         writeIndex = obj->putWriteIndex[0];
312         /* if slot available 'break' out of loop */
313         if (((writeIndex + 1) & NotifyDriverCirc_maxIndex) != readIndex) {
314             break;
315         }
317         /* restore interrupts */
318         Hwi_restore(hwiKey);
320         /* check to make sure code has looped */
321         if (loop && !waitClear) {
322             /* if no slot available and waitClear is 'FALSE' */
323             return (Notify_E_FAIL);
324         }
326         /* start timestamp for spin wait statistics */
327         if (NotifyDriverCirc_enableStats) {
328             if (loop && !spinWait) {
329                 startWaitTime = Timestamp_get32();
330                 spinWait = TRUE;
331             }
332         }
334         /* cache invalidate the putReadIndex */
335         if (obj->cacheEnabled) {
336             Cache_inv(obj->putReadIndex,
337                       sizeof(Bits32),
338                       Cache_Type_ALL,
339                       TRUE);
340         }
342         /* re-read the get count */
343         readIndex = obj->putReadIndex[0];
345         /* convey that the code has looped around */
346         loop = TRUE;
347     } while (1);
349     /* interrupts are disabled at this point */
351     /* increment spinCount and determine the spin wait time */
352     if (NotifyDriverCirc_enableStats) {
353         if (spinWait) {
354             obj->spinCount++;
355             spinWaitTime = Timestamp_get32() - startWaitTime;
356             if (spinWaitTime > obj->spinWaitTime) {
357                 obj->spinWaitTime = spinWaitTime;
358             }
359         }
360     }
362     /* calculate the next available entry */
363     eventEntry = (NotifyDriverCirc_EventEntry *)((UInt32)obj->putBuffer +
364                  (writeIndex * sizeof(NotifyDriverCirc_EventEntry)));
366     /* Set the eventId field and payload for the entry */
367     eventEntry->eventid = eventId;
368     eventEntry->payload = payload;
370     /*
371      *  Writeback the event entry. No need to invalidate since
372      *  only one processor ever writes here. No need to wait for
373      *  cache operation since another cache operation is done below.
374      */
375     if (obj->cacheEnabled) {
376         Cache_wb(eventEntry,
377                  sizeof(NotifyDriverCirc_EventEntry),
378                  Cache_Type_ALL,
379                  FALSE);
380     }
382     /* update the putWriteIndex */
383     obj->putWriteIndex[0] = (writeIndex + 1) & NotifyDriverCirc_maxIndex;
385     /* restore interrupts */
386     Hwi_restore(hwiKey);
388     /*
389      *  Writeback the putWriteIndex.
390      *  No need to invalidate since only one processor
391      *  ever writes here.
392      */
393     if (obj->cacheEnabled) {
394         Cache_wb(obj->putWriteIndex,
395                  sizeof(Bits32),
396                  Cache_Type_ALL,
397                  TRUE);
398     }
400     /* Send an interrupt to the Remote Processor */
401     NotifyDriverCirc_InterruptProxy_intSend(obj->remoteProcId, &(obj->intInfo),
402                                             eventId);
404     return (Notify_S_SUCCESS);
407 /*
408  *  ======== NotifyDriverCirc_disable ========
409  */
410 Void NotifyDriverCirc_disable(NotifyDriverCirc_Object *obj)
412     /* Disable the incoming interrupt line */
413     NotifyDriverCirc_InterruptProxy_intDisable(obj->remoteProcId,
414                                               &(obj->intInfo));
417 /*
418  *  ======== NotifyDriverCirc_enable ========
419  */
420 Void NotifyDriverCirc_enable(NotifyDriverCirc_Object *obj)
422     /* NotifyDriverCirc_enableEvent not supported by this driver */
423     Assert_isTrue(FALSE, NotifyDriverCirc_A_notSupported);
426 /*
427  *  ======== NotifyDriverCirc_disableEvent ========
428  */
429 Void NotifyDriverCirc_disableEvent(NotifyDriverCirc_Object *obj, UInt32 eventId)
431     /* NotifyDriverCirc_disableEvent not supported by this driver */
432     Assert_isTrue(FALSE, NotifyDriverCirc_A_notSupported);
435 /*
436  *  ======== NotifyDriverCirc_enableEvent ========
437  */
438 Void NotifyDriverCirc_enableEvent(NotifyDriverCirc_Object *obj, UInt32 eventId)
440     /* Enable the incoming interrupt line */
441     NotifyDriverCirc_InterruptProxy_intEnable(obj->remoteProcId,
442                                              &(obj->intInfo));
445 /*
446  *************************************************************************
447  *                       Module functions
448  *************************************************************************
449  */
451 /*
452  *  ======== NotifyDriverCirc_sharedMemReq ========
453  */
454 SizeT NotifyDriverCirc_sharedMemReq(const NotifyDriverCirc_Params *params)
456     UInt16 regionId;
457     SizeT regionCacheSize;
458     SizeT minAlign, memReq;
460     /* Ensure that params is non-NULL */
461     Assert_isTrue(params != NULL, ti_sdo_ipc_Ipc_A_internal);
463     /*
464      *  Determine obj->cacheEnabled using params->cacheEnabled and SharedRegion
465      *  cache flag setting, if applicable.
466      */
467     minAlign = params->cacheLineSize;
468     if (minAlign == 0) {
469         /* Fix alignment of zero */
470         minAlign = sizeof(Ptr);
471     }
473     regionId = SharedRegion_getId(params->sharedAddr);
474     if (regionId != SharedRegion_INVALIDREGIONID) {
475         regionCacheSize = SharedRegion_getCacheLineSize(regionId);
476         /* Override minAlign if the region cache line size is smaller */
477         if (regionCacheSize < minAlign) {
478             minAlign = regionCacheSize;
479         }
480     }
482     /*
483      *  Amount of shared memory:
484      *  1 putBuffer with numMsgs (rounded to CLS) +
485      *  1 putWriteIndex ptr (rounded to CLS) +
486      *  1 putReadIndex put (rounded to CLS) +
487      *  1 getBuffer with numMsgs (rounded to CLS) +
488      *  1 getWriteIndex ptr (rounded to CLS) +
489      *  1 getReadIndex put (rounded to CLS) +
490      *
491      *  For CLS of 128b it is:
492      *      256b + 128b + 128b + 256b+ 128b + 128b = 1KB
493      *
494      *  Note: CLS means Cache Line Size
495      */
496     memReq = 2 *
497         ((_Ipc_roundup(sizeof(NotifyDriverCirc_EventEntry) *
498                        NotifyDriverCirc_numMsgs, minAlign))
499         + ( 2 * _Ipc_roundup(sizeof(Bits32), minAlign)));
501     return (memReq);
504 /*
505  *************************************************************************
506  *                       Internal functions
507  *************************************************************************
508  */
510 /*
511  *  ======== NotifyDriverCirc_isr ========
512  */
513 Void NotifyDriverCirc_isr(UArg arg)
515     NotifyDriverCirc_EventEntry *eventEntry;
516     NotifyDriverCirc_Object     *obj;
517     UInt32 writeIndex, readIndex;
519     obj = (NotifyDriverCirc_Object *)arg;
521     /* Make sure the NotifyDriverCirc_Object is not NULL */
522     Assert_isTrue(obj != NULL, ti_sdo_ipc_Ipc_A_internal);
524     /*
525      *  Invalidate both getBuffer getWriteIndex from cache.
526      *  Do the Cache_wait() below.
527      */
528     if (obj->cacheEnabled) {
529         Cache_inv(obj->getBuffer,
530                   obj->opCacheSize,
531                   Cache_Type_ALL,
532                   FALSE);
533         /* Reading new data instead of pulling old data from cache */
534         Cache_inv(obj->getWriteIndex,
535                   sizeof(obj->getWriteIndex),
536                   Cache_Type_ALL,
537                   FALSE);
538     }
540     /* Clear the remote interrupt */
541     NotifyDriverCirc_InterruptProxy_intClear(obj->remoteProcId,
542                                             &(obj->intInfo));
544     /* wait here to make sure inv is completely done */
545     if (obj->cacheEnabled) {
546         Cache_wait();
547     }
549     /* get the writeIndex and readIndex */
550     writeIndex = obj->getWriteIndex[0];
551     readIndex = obj->getReadIndex[0];
553     /* get the event */
554     eventEntry = &(obj->getBuffer[readIndex]);
556     /* if writeIndex != readIndex then there is an event to process */
557     while (writeIndex != readIndex) {
558         /*
559          *  Check to make sure event is registered. If the event
560          *  is not registered, the event is not processed and is lost.
561          */
562         if (TEST_BIT(obj->evtRegMask, eventEntry->eventid)) {
563             /* Execute the callback function */
564             ti_sdo_ipc_Notify_exec(obj->notifyHandle,
565                                    eventEntry->eventid,
566                                    eventEntry->payload);
567         }
569         /* update the readIndex. */
570         readIndex = ((readIndex + 1) & NotifyDriverCirc_maxIndex);
572         /* set the getReadIndex */
573         obj->getReadIndex[0] = readIndex;
575         /*
576          *  Write back the getReadIndex once every N / 4 messages.
577          *  No need to invalidate since only one processor ever
578          *  writes this. No need to wait for operation to complete
579          *  since remote core updates its readIndex at least once
580          *  every N messages and the remote core will not use a slot
581          *  until it sees that the event has been processed with this
582          *  cache wb.
583          */
584         if ((obj->cacheEnabled) &&
585             ((readIndex % NotifyDriverCirc_modIndex) == 0)) {
586             Cache_wb(obj->getReadIndex,
587                      sizeof(Bits32),
588                      Cache_Type_ALL,
589                      FALSE);
590         }
592         /* get the next event */
593         eventEntry = &(obj->getBuffer[readIndex]);
594     }
597 /*
598  *  ======== NotifyDriverCirc_setNotifyHandle ========
599  */
600 Void NotifyDriverCirc_setNotifyHandle(NotifyDriverCirc_Object *obj,
601                                      Ptr notifyHandle)
603     /* Internally used, so no Assert needed */
604     obj->notifyHandle = (ti_sdo_ipc_Notify_Handle)notifyHandle;