980ce50db33b725177a3a736394b5ae3e9a6a8fc
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);
205 }
207 /*
208 * ======== NotifyDriverCirc_Instance_finalize ========
209 */
210 Void NotifyDriverCirc_Instance_finalize(NotifyDriverCirc_Object *obj)
211 {
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 }
239 }
241 /*
242 * ======== NotifyDriverCirc_registerEvent ========
243 */
244 Void NotifyDriverCirc_registerEvent(NotifyDriverCirc_Object *obj,
245 UInt32 eventId)
246 {
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);
260 }
262 /*
263 * ======== NotifyDriverCirc_unregisterEvent ========
264 */
265 Void NotifyDriverCirc_unregisterEvent(NotifyDriverCirc_Object *obj,
266 UInt32 eventId)
267 {
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);
281 }
283 /*
284 * ======== NotifyDriverCirc_sendEvent ========
285 */
286 Int NotifyDriverCirc_sendEvent(NotifyDriverCirc_Object *obj,
287 UInt32 eventId,
288 UInt32 payload,
289 Bool waitClear)
290 {
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);
405 }
407 /*
408 * ======== NotifyDriverCirc_disable ========
409 */
410 Void NotifyDriverCirc_disable(NotifyDriverCirc_Object *obj)
411 {
412 /* Disable the incoming interrupt line */
413 NotifyDriverCirc_InterruptProxy_intDisable(obj->remoteProcId,
414 &(obj->intInfo));
415 }
417 /*
418 * ======== NotifyDriverCirc_enable ========
419 */
420 Void NotifyDriverCirc_enable(NotifyDriverCirc_Object *obj)
421 {
422 /* NotifyDriverCirc_enableEvent not supported by this driver */
423 Assert_isTrue(FALSE, NotifyDriverCirc_A_notSupported);
424 }
426 /*
427 * ======== NotifyDriverCirc_disableEvent ========
428 */
429 Void NotifyDriverCirc_disableEvent(NotifyDriverCirc_Object *obj, UInt32 eventId)
430 {
431 /* NotifyDriverCirc_disableEvent not supported by this driver */
432 Assert_isTrue(FALSE, NotifyDriverCirc_A_notSupported);
433 }
435 /*
436 * ======== NotifyDriverCirc_enableEvent ========
437 */
438 Void NotifyDriverCirc_enableEvent(NotifyDriverCirc_Object *obj, UInt32 eventId)
439 {
440 /* Enable the incoming interrupt line */
441 NotifyDriverCirc_InterruptProxy_intEnable(obj->remoteProcId,
442 &(obj->intInfo));
443 }
445 /*
446 *************************************************************************
447 * Module functions
448 *************************************************************************
449 */
451 /*
452 * ======== NotifyDriverCirc_sharedMemReq ========
453 */
454 SizeT NotifyDriverCirc_sharedMemReq(const NotifyDriverCirc_Params *params)
455 {
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);
502 }
504 /*
505 *************************************************************************
506 * Internal functions
507 *************************************************************************
508 */
510 /*
511 * ======== NotifyDriverCirc_isr ========
512 */
513 Void NotifyDriverCirc_isr(UArg arg)
514 {
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 }
535 /* Clear the remote interrupt */
536 NotifyDriverCirc_InterruptProxy_intClear(obj->remoteProcId,
537 &(obj->intInfo));
539 /* wait here to make sure inv is completely done */
540 if (obj->cacheEnabled) {
541 Cache_wait();
542 }
544 /* get the writeIndex and readIndex */
545 writeIndex = obj->getWriteIndex[0];
546 readIndex = obj->getReadIndex[0];
548 /* get the event */
549 eventEntry = &(obj->getBuffer[readIndex]);
551 /* if writeIndex != readIndex then there is an event to process */
552 while (writeIndex != readIndex) {
553 /*
554 * Check to make sure event is registered. If the event
555 * is not registered, the event is not processed and is lost.
556 */
557 if (TEST_BIT(obj->evtRegMask, eventEntry->eventid)) {
558 /* Execute the callback function */
559 ti_sdo_ipc_Notify_exec(obj->notifyHandle,
560 eventEntry->eventid,
561 eventEntry->payload);
562 }
564 /* update the readIndex. */
565 readIndex = ((readIndex + 1) & NotifyDriverCirc_maxIndex);
567 /* set the getReadIndex */
568 obj->getReadIndex[0] = readIndex;
570 /*
571 * Write back the getReadIndex once every N / 4 messages.
572 * No need to invalidate since only one processor ever
573 * writes this. No need to wait for operation to complete
574 * since remote core updates its readIndex at least once
575 * every N messages and the remote core will not use a slot
576 * until it sees that the event has been processed with this
577 * cache wb.
578 */
579 if ((obj->cacheEnabled) &&
580 ((readIndex % NotifyDriverCirc_modIndex) == 0)) {
581 Cache_wb(obj->getReadIndex,
582 sizeof(Bits32),
583 Cache_Type_ALL,
584 FALSE);
585 }
587 /* get the next event */
588 eventEntry = &(obj->getBuffer[readIndex]);
589 }
590 }
592 /*
593 * ======== NotifyDriverCirc_setNotifyHandle ========
594 */
595 Void NotifyDriverCirc_setNotifyHandle(NotifyDriverCirc_Object *obj,
596 Ptr notifyHandle)
597 {
598 /* Internally used, so no Assert needed */
599 obj->notifyHandle = (ti_sdo_ipc_Notify_Handle)notifyHandle;
600 }