xref: /freebsd/sys/dev/pms/RefTisa/sallsdk/spc/mpi.c (revision c66ec88fed842fbaad62c30d510644ceb7bd2d71)
1 /*******************************************************************************
2 **
3 *Copyright (c) 2014 PMC-Sierra, Inc.  All rights reserved.
4 *
5 *Redistribution and use in source and binary forms, with or without modification, are permitted provided
6 *that the following conditions are met:
7 *1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
8 *following disclaimer.
9 *2. Redistributions in binary form must reproduce the above copyright notice,
10 *this list of conditions and the following disclaimer in the documentation and/or other materials provided
11 *with the distribution.
12 *
13 *THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED
14 *WARRANTIES,INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
15 *FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
16 *FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
17 *NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
18 *BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
19 *LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
20 *SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
21 
22 ********************************************************************************/
23 
24 /*******************************************************************************/
25 /*! \file mpi.c
26  *  \brief The file is a MPI Libraries to implement the MPI functions
27  *
28  * The file implements the MPI Library functions.
29  *
30  */
31 /*******************************************************************************/
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34 #include <dev/pms/config.h>
35 
36 #include <dev/pms/RefTisa/sallsdk/spc/saglobal.h>
37 
38 #ifdef SA_ENABLE_TRACE_FUNCTIONS
39 #ifdef siTraceFileID
40 #undef siTraceFileID
41 #endif
42 #define siTraceFileID 'A'
43 #endif
44 
45 #ifdef LOOPBACK_MPI
46 extern int loopback;
47 #endif
48 /*******************************************************************************/
49 
50 /*******************************************************************************/
51 /*******************************************************************************/
52 /* FUNCTIONS                                                                   */
53 /*******************************************************************************/
54 /*******************************************************************************/
55 /** \fn void mpiRequirementsGet(mpiConfig_t* config, mpiMemReq_t* memoryRequirement)
56  *  \brief Retrieves the MPI layer resource requirements
57  *  \param config            MPI configuration for the Host MPI Message Unit
58  *  \param memoryRequirement Returned data structure as defined by mpiMemReq_t
59  *                           that holds the different chunks of memory that are required
60  *
61  * The mpiRequirementsGet() function is used to determine the resource requirements
62  * for the SPC device interface
63  *
64  * Return: None
65  */
66 /*******************************************************************************/
67 void mpiRequirementsGet(mpiConfig_t* config, mpiMemReq_t* memoryRequirement)
68 {
69   bit32 qIdx, numq;
70   mpiMemReq_t* memoryMap;
71   SA_DBG2(("Entering function:mpiRequirementsGet\n"));
72   SA_ASSERT((NULL != config), "config argument cannot be null");
73 
74   memoryMap = memoryRequirement;
75   memoryMap->count = 0;
76 
77   /* MPI Memory region 0 for MSGU(AAP1) Event Log for fw */
78   memoryMap->region[memoryMap->count].numElements = 1;
79   memoryMap->region[memoryMap->count].elementSize = sizeof(bit8) * config->mainConfig.eventLogSize;
80   memoryMap->region[memoryMap->count].totalLength = sizeof(bit8) * config->mainConfig.eventLogSize;
81   memoryMap->region[memoryMap->count].alignment = 32;
82   memoryMap->region[memoryMap->count].type = AGSA_DMA_MEM;
83   SA_DBG2(("mpiRequirementsGet:eventLogSize region[%d] 0x%X\n",memoryMap->count,memoryMap->region[memoryMap->count].totalLength ));
84   memoryMap->count++;
85 
86   SA_DBG2(("mpiRequirementsGet:eventLogSize region[%d] 0x%X\n",memoryMap->count,memoryMap->region[memoryMap->count].totalLength ));
87   /* MPI Memory region 1 for IOP Event Log for fw */
88   memoryMap->region[memoryMap->count].numElements = 1;
89   memoryMap->region[memoryMap->count].elementSize = sizeof(bit8) * config->mainConfig.IOPeventLogSize;
90   memoryMap->region[memoryMap->count].totalLength = sizeof(bit8) * config->mainConfig.IOPeventLogSize;
91   memoryMap->region[memoryMap->count].alignment = 32;
92   memoryMap->region[memoryMap->count].type = AGSA_DMA_MEM;
93   SA_DBG2(("mpiRequirementsGet:IOPeventLogSize region[%d] 0x%X\n",memoryMap->count,memoryMap->region[memoryMap->count].totalLength ));
94   memoryMap->count++;
95 
96   /* MPI Memory region 2 for consumer Index of inbound queues */
97   memoryMap->region[memoryMap->count].numElements = 1;
98   memoryMap->region[memoryMap->count].elementSize = sizeof(bit32) * config->numInboundQueues;
99   memoryMap->region[memoryMap->count].totalLength = sizeof(bit32) * config->numInboundQueues;
100   memoryMap->region[memoryMap->count].alignment = 4;
101   memoryMap->region[memoryMap->count].type = AGSA_DMA_MEM;
102   SA_DBG2(("mpiRequirementsGet:numInboundQueues region[%d] 0x%X\n",memoryMap->count,memoryMap->region[memoryMap->count].totalLength ));
103   memoryMap->count++;
104 
105   /* MPI Memory region 3 for producer Index of outbound queues */
106   memoryMap->region[memoryMap->count].numElements = 1;
107   memoryMap->region[memoryMap->count].elementSize = sizeof(bit32) * config->numOutboundQueues;
108   memoryMap->region[memoryMap->count].totalLength = sizeof(bit32) * config->numOutboundQueues;
109   memoryMap->region[memoryMap->count].alignment = 4;
110   memoryMap->region[memoryMap->count].type = AGSA_DMA_MEM;
111   SA_DBG2(("mpiRequirementsGet:numOutboundQueues region[%d] 0x%X\n",memoryMap->count,memoryMap->region[memoryMap->count].totalLength ));
112   memoryMap->count++;
113 
114   /* MPI Memory regions 4, ... for the inbound queues - depends on configuration */
115   numq = 0;
116   for(qIdx = 0; qIdx < config->numInboundQueues; qIdx++)
117   {
118     if(0 != config->inboundQueues[qIdx].numElements)
119     {
120         bit32 memSize = config->inboundQueues[qIdx].numElements * config->inboundQueues[qIdx].elementSize;
121         bit32 remainder = memSize & 127;
122 
123         /* Calculate the size of this queue padded to 128 bytes */
124         if (remainder > 0)
125         {
126             memSize += (128 - remainder);
127         }
128 
129         if (numq == 0)
130         {
131             memoryMap->region[memoryMap->count].numElements = 1;
132             memoryMap->region[memoryMap->count].elementSize = memSize;
133             memoryMap->region[memoryMap->count].totalLength = memSize;
134             memoryMap->region[memoryMap->count].alignment = 128;
135             memoryMap->region[memoryMap->count].type = AGSA_CACHED_DMA_MEM;
136         }
137         else
138         {
139             memoryMap->region[memoryMap->count].elementSize += memSize;
140             memoryMap->region[memoryMap->count].totalLength += memSize;
141         }
142 
143         numq++;
144 
145         if ((0 == ((qIdx + 1) % MAX_QUEUE_EACH_MEM)) ||
146             (qIdx == (bit32)(config->numInboundQueues - 1)))
147         {
148             SA_DBG2(("mpiRequirementsGet: (inboundQueues) memoryMap->region[%d].elementSize = %d\n",
149                      memoryMap->count, memoryMap->region[memoryMap->count].elementSize));
150             SA_DBG2(("mpiRequirementsGet: (inboundQueues) memoryMap->region[%d].numElements = %d\n",
151                      memoryMap->count, memoryMap->region[memoryMap->count].numElements));
152 
153             memoryMap->count++;
154             numq = 0;
155         }
156     }
157   }
158 
159   /* MPI Memory regions for the outbound queues - depends on configuration */
160   numq = 0;
161   for(qIdx = 0; qIdx < config->numOutboundQueues; qIdx++)
162   {
163     if(0 != config->outboundQueues[qIdx].numElements)
164     {
165         bit32 memSize = config->outboundQueues[qIdx].numElements * config->outboundQueues[qIdx].elementSize;
166         bit32 remainder = memSize & 127;
167 
168         /* Calculate the size of this queue padded to 128 bytes */
169         if (remainder > 0)
170         {
171             memSize += (128 - remainder);
172         }
173 
174         if (numq == 0)
175         {
176             memoryMap->region[memoryMap->count].numElements = 1;
177             memoryMap->region[memoryMap->count].elementSize = memSize;
178             memoryMap->region[memoryMap->count].totalLength = memSize;
179             memoryMap->region[memoryMap->count].alignment = 128;
180             memoryMap->region[memoryMap->count].type = AGSA_CACHED_DMA_MEM;
181         }
182         else
183         {
184             memoryMap->region[memoryMap->count].elementSize += memSize;
185             memoryMap->region[memoryMap->count].totalLength += memSize;
186         }
187 
188         numq++;
189 
190         if ((0 == ((qIdx + 1) % MAX_QUEUE_EACH_MEM)) ||
191             (qIdx ==  (bit32)(config->numOutboundQueues - 1)))
192         {
193             SA_DBG2(("mpiRequirementsGet: (outboundQueues) memoryMap->region[%d].elementSize = %d\n",
194                      memoryMap->count, memoryMap->region[memoryMap->count].elementSize));
195             SA_DBG2(("mpiRequirementsGet: (outboundQueues) memoryMap->region[%d].numElements = %d\n",
196                      memoryMap->count, memoryMap->region[memoryMap->count].numElements));
197 
198 
199             memoryMap->count++;
200             numq = 0;
201         }
202     }
203   }
204 
205 }
206 
207 /*******************************************************************************/
208 /** \fn mpiMsgFreeGet(mpiICQueue_t *circularQ, bit16 messageSize, void** messagePtr)
209  *  \brief Retrieves a free message buffer from an inbound queue
210  *  \param circularQ    Pointer to an inbound circular queue
211  *  \param messageSize  Requested message size in bytes - only support 64 bytes/element
212  *  \param messagePtr   Pointer to the free message buffer payload (not including message header) or NULL if no free message buffers are available
213  *
214  * This function is used to retrieve a free message buffer for the given inbound queue of at least
215  * messageSize bytes.
216  * The caller can use the returned buffer to construct the message and then call mpiMsgProduce()
217  * to deliver the message to the device message unit or mpiMsgInvalidate() if the message buffer
218  * is not going to be used
219  *
220  * Return:
221  *         AGSA_RC_SUCCESS if messagePtr contains a valid message buffer pointer
222  *         AGSA_RC_FAILURE if messageSize larger than the elementSize of queue
223  *         AGSA_RC_BUSY    if there are not free message buffers (Queue full)
224  */
225 /*******************************************************************************/
226 GLOBAL FORCEINLINE
227 bit32
228 mpiMsgFreeGet(
229   mpiICQueue_t *circularQ,
230   bit16 messageSize,
231   void** messagePtr
232   )
233 {
234   bit32 offset;
235   agsaRoot_t          *agRoot=circularQ->agRoot;
236   mpiMsgHeader_t *msgHeader;
237   bit8 bcCount = 1; /* only support single buffer */
238 
239   SA_DBG4(("Entering function:mpiMsgFreeGet\n"));
240   SA_ASSERT(NULL != circularQ, "circularQ cannot be null");
241   SA_ASSERT(NULL != messagePtr, "messagePtr argument cannot be null");
242   SA_ASSERT(0 != circularQ->numElements, "The number of elements in this queue is 0");
243 
244   /* Checks is the requested message size can be allocated in this queue */
245   if(messageSize > circularQ->elementSize)
246   {
247     SA_DBG1(("mpiMsgFreeGet: Message Size (%d) is larger than Q element size (%d)\n",messageSize,circularQ->elementSize));
248     return AGSA_RC_FAILURE;
249   }
250 
251   /* Stores the new consumer index */
252   OSSA_READ_LE_32(circularQ->agRoot, &circularQ->consumerIdx, circularQ->ciPointer, 0);
253   /* if inbound queue is full, return busy */
254   /* This queue full logic may only works for bc == 1 ( == ) */
255   /* ( pi + bc ) % size > ci not fully works for bc > 1 */
256   /* To do - support bc > 1 case and wrap around case */
257   if (((circularQ->producerIdx + bcCount) % circularQ->numElements) == circularQ->consumerIdx)
258   {
259     *messagePtr = NULL;
260     smTrace(hpDBG_VERY_LOUD,"Za", (((circularQ->producerIdx & 0xFFF) << 16) |  (circularQ->consumerIdx & 0xFFF) ));
261     /* TP:Za IQ PI CI */
262     ossaHwRegRead(agRoot, MSGU_HOST_SCRATCH_PAD_0);
263     SA_DBG1(("mpiMsgFreeGet: %d + %d == %d AGSA_RC_BUSY\n",circularQ->producerIdx,bcCount,circularQ->consumerIdx));
264 
265     return AGSA_RC_BUSY;
266   }
267 
268   smTrace(hpDBG_VERY_LOUD,"Zb", (((circularQ->producerIdx & 0xFFF) << 16) |  (circularQ->consumerIdx & 0xFFF) ));
269   /* TP:Zb IQ PI CI */
270 
271 
272   /* get memory IOMB buffer address */
273   offset = circularQ->producerIdx * circularQ->elementSize;
274   /* increment to next bcCount element */
275   circularQ->producerIdx = (circularQ->producerIdx + bcCount) % circularQ->numElements;
276 
277   /* Adds that distance to the base of the region virtual address plus the message header size*/
278   msgHeader = (mpiMsgHeader_t*) (((bit8 *)(circularQ->memoryRegion.virtPtr)) + offset);
279 
280   SA_DBG3(("mpiMsgFreeGet: msgHeader = %p Offset = 0x%x\n", (void *)msgHeader, offset));
281 
282   /* Sets the message buffer in "allocated" state */
283   /* bc always is 1 for inbound queue */
284   /* temporarily store it in the native endian format, when the rest of the */
285   /* header is filled, this would be converted to Little Endian */
286   msgHeader->Header = (1<<24);
287   *messagePtr = ((bit8*)msgHeader) + sizeof(mpiMsgHeader_t);
288 
289   return AGSA_RC_SUCCESS;
290 }
291 
292 #ifdef LOOPBACK_MPI
293 GLOBAL bit32 mpiMsgFreeGetOQ(mpiOCQueue_t *circularQ, bit16 messageSize, void** messagePtr)
294 {
295   bit32 offset;
296   mpiMsgHeader_t *msgHeader;
297   bit8 bcCount = 1; /* only support single buffer */
298 
299   SA_DBG4(("Entering function:mpiMsgFreeGet\n"));
300   SA_ASSERT(NULL != circularQ, "circularQ cannot be null");
301   SA_ASSERT(NULL != messagePtr, "messagePtr argument cannot be null");
302   SA_ASSERT(0 != circularQ->numElements, "The number of elements in this queue is 0");
303 
304   /* Checks is the requested message size can be allocated in this queue */
305   if(messageSize > circularQ->elementSize)
306   {
307     SA_DBG1(("mpiMsgFreeGet: Message Size is not fit in\n"));
308     return AGSA_RC_FAILURE;
309   }
310 
311   /* Stores the new consumer index */
312   //OSSA_READ_LE_32(circularQ->agRoot, &circularQ->consumerIdx, circularQ->ciPointer, 0);
313   /* if inbound queue is full, return busy */
314   /* This queue full logic may only works for bc == 1 ( == ) */
315   /* ( pi + bc ) % size > ci not fully works for bc > 1 */
316   /* To do - support bc > 1 case and wrap around case */
317   if (((circularQ->producerIdx + bcCount) % circularQ->numElements) == circularQ->consumerIdx)
318   {
319     *messagePtr = NULL;
320     return AGSA_RC_BUSY;
321   }
322 
323   /* get memory IOMB buffer address */
324   offset = circularQ->producerIdx * circularQ->elementSize;
325   /* increment to next bcCount element */
326   circularQ->producerIdx = (circularQ->producerIdx + bcCount) % circularQ->numElements;
327 
328   /* Adds that distance to the base of the region virtual address plus the message header size*/
329   msgHeader = (mpiMsgHeader_t*) (((bit8 *)(circularQ->memoryRegion.virtPtr)) + offset);
330 
331   SA_DBG3(("mpiMsgFreeGet: msgHeader = %p Offset = 0x%x\n", (void *)msgHeader, offset));
332 
333   /* Sets the message buffer in "allocated" state */
334   /* bc always is 1 for inbound queue */
335   /* temporarily store it in the native endian format, when the rest of the */
336   /* header is filled, this would be converted to Little Endian */
337   msgHeader->Header = (1<<24);
338   *messagePtr = ((bit8*)msgHeader) + sizeof(mpiMsgHeader_t);
339 
340   return AGSA_RC_SUCCESS;
341 }
342 #endif
343 
344 /*******************************************************************************/
345 /** \fn mpiMsgProduce(mpiICQueue_t *circularQ, void *messagePtr, mpiMsgCategory_t category, bit16 opCode, bit8 responseQueue)
346  *  \brief Add a header of IOMB then send to a inbound queue and update the Producer index
347  *  \param circularQ     Pointer to an inbound queue
348  *  \param messagePtr    Pointer to the message buffer payload (not including message header))
349  *  \param category      Message category (ETHERNET, FC, SAS-SATA, SCSI)
350  *  \param opCode        Message operation code
351  *  \param responseQueue If the message requires response, this paramater indicates the outbound queue for the response
352  *
353  * This function is used to sumit a message buffer, previously obtained from  mpiMsgFreeGet()
354  * function call, to the given Inbound queue
355  *
356  * Return:
357  *         AGSA_RC_SUCCESS if the message has been posted succesfully
358  */
359 /*******************************************************************************/
360 #ifdef FAST_IO_TEST
361 GLOBAL bit32 mpiMsgPrepare(
362                        mpiICQueue_t *circularQ,
363                        void         *messagePtr,
364                        mpiMsgCategory_t category,
365                        bit16        opCode,
366                        bit8         responseQueue,
367                        bit8         hiPriority
368                        )
369 {
370   mpiMsgHeader_t *msgHeader;
371   bit32          bc;
372   bit32          Header = 0;
373   bit32          hpriority = 0;
374 
375   SA_DBG4(("Entering function:mpiMsgProduce\n"));
376   SA_ASSERT(NULL != circularQ, "circularQ argument cannot be null");
377   SA_ASSERT(NULL != messagePtr, "messagePtr argument cannot be null");
378   SA_ASSERT(0 != circularQ->numElements, "The number of elements in this queue"
379             " is 0");
380   SA_ASSERT(MPI_MAX_OUTBOUND_QUEUES > responseQueue, "oQueue ID is wrong");
381 
382   /* Obtains the address of the entire message buffer, including the header */
383   msgHeader = (mpiMsgHeader_t*)(((bit8*)messagePtr) - sizeof(mpiMsgHeader_t));
384   /* Read the BC from header, its stored in native endian format when message
385      was allocated */
386   /* intially */
387   bc = (((msgHeader->Header) >> SHIFT24) & BC_MASK);
388   SA_DBG6(("mpiMsgProduce: msgHeader bc %d\n", bc));
389   if (circularQ->priority)
390     hpriority = 1;
391 
392   /* Checks the message is in "allocated" state */
393   SA_ASSERT(0 != bc, "The message buffer is not in \"allocated\" state "
394                      "(bc == 0)");
395 
396   Header = ((V_BIT << SHIFT31) | (hpriority << SHIFT30)  |
397             ((bc & BC_MASK) << SHIFT24) |
398             ((responseQueue & OBID_MASK) << SHIFT16) |
399             ((category  & CAT_MASK) << SHIFT12 ) | (opCode & OPCODE_MASK));
400 
401   /* pre flush the IOMB cache line */
402   ossaCachePreFlush(circularQ->agRoot,
403                     (void *)circularQ->memoryRegion.appHandle,
404                     (void *)msgHeader, circularQ->elementSize * bc);
405   OSSA_WRITE_LE_32(circularQ->agRoot, msgHeader, OSSA_OFFSET_OF(mpiMsgHeader_t,
406                    Header), Header);
407   /* flush the IOMB cache line */
408   ossaCacheFlush(circularQ->agRoot, (void *)circularQ->memoryRegion.appHandle,
409                  (void *)msgHeader, circularQ->elementSize * bc);
410 
411   MPI_DEBUG_TRACE( circularQ->qNumber,
412                   ((circularQ->producerIdx << 16 ) | circularQ->consumerIdx),
413                    MPI_DEBUG_TRACE_IBQ,
414                   (void *)msgHeader,
415                   circularQ->elementSize);
416 
417   ossaLogIomb(circularQ->agRoot,
418               circularQ->qNumber,
419               TRUE,
420               (void *)msgHeader,
421               circularQ->elementSize);
422 
423   return AGSA_RC_SUCCESS;
424 } /* mpiMsgPrepare */
425 
426 GLOBAL bit32 mpiMsgProduce(
427                        mpiICQueue_t *circularQ,
428                        void         *messagePtr,
429                        mpiMsgCategory_t category,
430                        bit16        opCode,
431                        bit8         responseQueue,
432                        bit8         hiPriority
433                        )
434 {
435   bit32 ret;
436 
437   ret = mpiMsgPrepare(circularQ, messagePtr, category, opCode, responseQueue,
438                       hiPriority);
439   if (ret == AGSA_RC_SUCCESS)
440   {
441     /* update PI of inbound queue */
442     ossaHwRegWriteExt(circularQ->agRoot,
443                       circularQ->PIPCIBar,
444                       circularQ->PIPCIOffset,
445                       circularQ->producerIdx);
446   }
447   return ret;
448 }
449 
450 GLOBAL void mpiIBQMsgSend(mpiICQueue_t *circularQ)
451 {
452   ossaHwRegWriteExt(circularQ->agRoot,
453                     circularQ->PIPCIBar,
454                     circularQ->PIPCIOffset,
455                     circularQ->producerIdx);
456 }
457 #else  /* FAST_IO_TEST */
458 
459 GLOBAL FORCEINLINE
460 bit32
461 mpiMsgProduce(
462   mpiICQueue_t *circularQ,
463   void *messagePtr,
464   mpiMsgCategory_t category,
465   bit16 opCode,
466   bit8 responseQueue,
467   bit8 hiPriority
468   )
469 {
470   mpiMsgHeader_t *msgHeader;
471   bit32          bc;
472   bit32          Header = 0;
473   bit32          hpriority = 0;
474 
475 #ifdef SA_FW_TEST_BUNCH_STARTS
476 #define Need_agRootDefined 1
477 #endif /* SA_FW_TEST_BUNCH_STARTS */
478 
479 #ifdef SA_ENABLE_TRACE_FUNCTIONS
480   bit32             i;
481 #define Need_agRootDefined 1
482 #endif /* SA_ENABLE_TRACE_FUNCTIONS */
483 
484 #ifdef MPI_DEBUG_TRACE_ENABLE
485 #define Need_agRootDefined 1
486 #endif /* MPI_DEBUG_TRACE_ENABLE */
487 
488 #ifdef Need_agRootDefined
489   agsaRoot_t   *agRoot=circularQ->agRoot;
490 #ifdef SA_FW_TEST_BUNCH_STARTS
491    agsaLLRoot_t *saRoot = agNULL;
492   saRoot = agRoot->sdkData;
493 #endif /* SA_FW_TEST_BUNCH_STARTS */
494 
495 #undef Need_agRootDefined
496 #endif /* Need_agRootDefined */
497 
498   SA_DBG4(("Entering function:mpiMsgProduce\n"));
499   SA_ASSERT(NULL != circularQ, "circularQ argument cannot be null");
500   SA_ASSERT(NULL != messagePtr, "messagePtr argument cannot be null");
501   SA_ASSERT(0 != circularQ->numElements, "The number of elements in this queue is 0");
502   SA_ASSERT(MPI_MAX_OUTBOUND_QUEUES > responseQueue, "oQueue ID is wrong");
503 
504   /* REB Start extra trace */
505   smTraceFuncEnter(hpDBG_VERY_LOUD,"22");
506   /* REB End extra trace */
507 
508   /* Obtains the address of the entire message buffer, including the header */
509   msgHeader = (mpiMsgHeader_t*)(((bit8*)messagePtr) - sizeof(mpiMsgHeader_t));
510   /* Read the BC from header, its stored in native endian format when message was allocated */
511   /* intially */
512   bc = (((msgHeader->Header) >> SHIFT24) & BC_MASK);
513   SA_DBG6(("mpiMsgProduce: msgHeader bc %d\n", bc));
514   if (circularQ->priority)
515   {
516     hpriority = 1;
517   }
518 
519   /* Checks the message is in "allocated" state */
520   SA_ASSERT(0 != bc, "The message buffer is not in \"allocated\" state (bc == 0)");
521 
522   Header = ((V_BIT << SHIFT31) |
523             (hpriority << SHIFT30)  |
524             ((bc & BC_MASK) << SHIFT24) |
525             ((responseQueue & OBID_MASK) << SHIFT16) |
526             ((category  & CAT_MASK) << SHIFT12 ) |
527             (opCode & OPCODE_MASK));
528 
529   /* pre flush the cache line */
530   ossaCachePreFlush(circularQ->agRoot, (void *)circularQ->memoryRegion.appHandle, (void *)msgHeader, circularQ->elementSize * bc);
531   OSSA_WRITE_LE_32(circularQ->agRoot, msgHeader, OSSA_OFFSET_OF(mpiMsgHeader_t, Header), Header);
532   /* flush the cache line for IOMB */
533   ossaCacheFlush(circularQ->agRoot, (void *)circularQ->memoryRegion.appHandle, (void *)msgHeader, circularQ->elementSize * bc);
534 
535   MPI_DEBUG_TRACE( circularQ->qNumber,
536                   ((circularQ->producerIdx << 16 ) | circularQ->consumerIdx),
537                   MPI_DEBUG_TRACE_IBQ,
538                   (void *)msgHeader,
539                   circularQ->elementSize);
540 
541   ossaLogIomb(circularQ->agRoot,
542               circularQ->qNumber,
543               TRUE,
544               (void *)msgHeader,
545               circularQ->elementSize);
546 
547 #if defined(SALLSDK_DEBUG)
548   MPI_IBQ_IOMB_LOG(circularQ->qNumber, (void *)msgHeader, circularQ->elementSize);
549 #endif  /* SALLSDK_DEBUG */
550   /* REB Start extra trace */
551 #ifdef SA_ENABLE_TRACE_FUNCTIONS
552   smTrace(hpDBG_IOMB,"M1",circularQ->qNumber);
553  /* TP:M1 circularQ->qNumber */
554   for (i=0; i<((bit32)bc*(circularQ->elementSize/4)); i++)
555   {
556       /* The -sizeof(mpiMsgHeader_t) is to account for mpiMsgProduce adding the header to the pMessage pointer */
557       smTrace(hpDBG_IOMB,"MD",*( ((bit32 *)((bit8 *)messagePtr - sizeof(mpiMsgHeader_t))) + i));
558       /* TP:MD Inbound IOMB Dword */
559   }
560 #endif /* SA_ENABLE_TRACE_FUNCTIONS */
561 
562   /* update PI of inbound queue */
563 
564 #ifdef SA_FW_TEST_BUNCH_STARTS
565   if(saRoot->BunchStarts_Enable)
566   {
567       if (circularQ->BunchStarts_QPending == 0)
568       {
569           // store tick value for 1st deferred IO only
570           circularQ->BunchStarts_QPendingTick = saRoot->timeTick;
571       }
572       // update queue's pending count
573       circularQ->BunchStarts_QPending++;
574 
575       // update global pending count
576       saRoot->BunchStarts_Pending++;
577 
578       SA_DBG1(("mpiMsgProduce: BunchStarts - Global Pending %d\n", saRoot->BunchStarts_Pending));
579       SA_DBG1(("mpiMsgProduce: BunchStarts - QPending %d, Q-%d\n", circularQ->BunchStarts_QPending, circularQ->qNumber));
580       smTraceFuncExit(hpDBG_VERY_LOUD, 'a', "22");
581 
582       return AGSA_RC_SUCCESS;
583   }
584 
585   saRoot->BunchStarts_Pending     = 0;
586   circularQ->BunchStarts_QPending = 0;
587 #endif /* SA_FW_TEST_BUNCH_STARTS */
588   ossaHwRegWriteExt(circularQ->agRoot,
589                     circularQ->PIPCIBar,
590                     circularQ->PIPCIOffset,
591                     circularQ->producerIdx);
592 
593   smTraceFuncExit(hpDBG_VERY_LOUD, 'b', "22");
594 
595   return AGSA_RC_SUCCESS;
596 } /* mpiMsgProduce */
597 #endif /* FAST_IO_TEST */
598 
599 #ifdef SA_FW_TEST_BUNCH_STARTS
600 
601 void mpiMsgProduceBunch(  agsaLLRoot_t  *saRoot)
602 {
603   mpiICQueue_t *circularQ;
604   bit32 inq;
605 
606   for(inq=0; ((inq < saRoot->QueueConfig.numInboundQueues) && saRoot->BunchStarts_Pending); inq++)
607   {
608     circularQ= &saRoot->inboundQueue[inq];
609     /* If any pending IOs present then either process if BunchStarts_Threshold
610      * IO limit reached or if the timer has popped
611      */
612     if (circularQ->BunchStarts_QPending &&
613         ((circularQ->BunchStarts_QPending >= saRoot->BunchStarts_Threshold) ||
614          ((saRoot->timeTick - circularQ->BunchStarts_QPendingTick) >= saRoot->BunchStarts_TimeoutTicks))
615        )
616     {
617       if(circularQ->qNumber != inq)
618       {
619         SA_DBG1(("mpiMsgProduceBunch:circularQ->qNumber(%d) != inq(%d)\n",circularQ->qNumber, inq));
620       }
621 
622       SA_DBG1(("mpiMsgProduceBunch: IQ=%d, PI=%d\n", inq, circularQ->producerIdx));
623       SA_DBG1(("mpiMsgProduceBunch: Qpending=%d, TotPending=%d\n", circularQ->BunchStarts_QPending, saRoot->BunchStarts_Pending));
624 
625       ossaHwRegWriteExt(circularQ->agRoot,
626                      circularQ->PIPCIBar,
627                      circularQ->PIPCIOffset,
628                      circularQ->producerIdx);
629 
630       // update global pending count
631       saRoot->BunchStarts_Pending -= circularQ->BunchStarts_QPending;
632 
633       // clear current queue's pending count after processing
634       circularQ->BunchStarts_QPending = 0;
635       circularQ->BunchStarts_QPendingTick = saRoot->timeTick;
636     }
637   }
638 }
639 #endif /* SA_FW_TEST_BUNCH_STARTS */
640 
641 /*******************************************************************************/
642 /** \fn mpiMsgConsume(mpiOCQueue_t *circularQ, void *messagePtr1,
643  *                mpiMsgCategory_t * pCategory, bit16 * pOpCode, bit8 * pBC)
644  *  \brief Get a received message
645  *  \param circularQ   Pointer to a outbound queue
646  *  \param messagePtr1 Pointer to the returned message buffer or NULL if no valid message
647  *  \param pCategory   Pointer to Message category (ETHERNET, FC, SAS-SATA, SCSI)
648  *  \param pOpCode     Pointer to Message operation code
649  *  \param pBC         Pointer to buffer count
650  *
651  * Consume a receive message in the specified outbound queue
652  *
653  * Return:
654  *         AGSA_RC_SUCCESS if the message has been retrieved succesfully
655  *         AGSA_RC_BUSY    if the circular is empty
656  */
657 /*******************************************************************************/
658 GLOBAL FORCEINLINE
659 bit32
660 mpiMsgConsume(
661   mpiOCQueue_t       *circularQ,
662   void             ** messagePtr1,
663   mpiMsgCategory_t   *pCategory,
664   bit16              *pOpCode,
665   bit8               *pBC
666   )
667 {
668   mpiMsgHeader_t *msgHeader;
669   bit32          msgHeader_tmp;
670 
671   SA_ASSERT(NULL != circularQ, "circularQ argument cannot be null");
672   SA_ASSERT(NULL != messagePtr1, "messagePtr1 argument cannot be null");
673   SA_ASSERT(NULL != pCategory, "pCategory argument cannot be null");
674   SA_ASSERT(NULL != pOpCode, "pOpCode argument cannot be null");
675   SA_ASSERT(NULL != pBC, "pBC argument cannot be null");
676   SA_ASSERT(0 != circularQ->numElements, "The number of elements in this queue is 0");
677 
678   do
679   {
680     /* If there are not-yet-delivered messages ... */
681     if(circularQ->producerIdx != circularQ->consumerIdx)
682     {
683       /* Get the pointer to the circular queue buffer element */
684       msgHeader = (mpiMsgHeader_t*) ((bit8 *)(circularQ->memoryRegion.virtPtr) + circularQ->consumerIdx * circularQ->elementSize);
685 
686 #ifdef LOOPBACK_MPI
687       if (!loopback)
688 #endif
689       /* invalidate the cache line of IOMB */
690       ossaCacheInvalidate(circularQ->agRoot, (void *)circularQ->memoryRegion.appHandle, (void *)msgHeader, circularQ->elementSize);
691 
692 
693       /* read header */
694       OSSA_READ_LE_32(circularQ->agRoot, &msgHeader_tmp, msgHeader, 0);
695 
696       SA_DBG4(("mpiMsgConsume: process an IOMB, header=0x%x\n", msgHeader_tmp));
697 
698       SA_ASSERT(0 != (msgHeader_tmp & HEADER_BC_MASK), "The bc field in the header is 0");
699 #ifdef TEST
700       /* for debugging */
701       if (0 == (msgHeader_tmp & HEADER_BC_MASK))
702       {
703         SA_DBG1(("mpiMsgConsume: CI=%d PI=%d msgHeader=%p\n", circularQ->consumerIdx, circularQ->producerIdx, (void *)msgHeader));
704         circularQ->consumerIdx = (circularQ->consumerIdx + 1) % circularQ->numElements;
705         /* update the CI of outbound queue - skip this blank IOMB, for test only */
706         ossaHwRegWriteExt(circularQ->agRoot,
707                           circularQ->CIPCIBar,
708                           circularQ->CIPCIOffset,
709                           circularQ->consumerIdx);
710         return AGSA_RC_FAILURE;
711       }
712 #endif
713       /* get message pointer of valid entry */
714       if (0 != (msgHeader_tmp & HEADER_V_MASK))
715       {
716         SA_ASSERT(circularQ->consumerIdx <= circularQ->numElements, "Multi-buffer messages cannot wrap around");
717 
718         if (OPC_OUB_SKIP_ENTRY != (msgHeader_tmp & OPCODE_MASK))
719         {
720           /* ... return the message payload */
721           *messagePtr1 = ((bit8*)msgHeader) + sizeof(mpiMsgHeader_t);
722           *pCategory   = (mpiMsgCategory_t)(msgHeader_tmp >> SHIFT12) & CAT_MASK;
723           *pOpCode     = (bit16)(msgHeader_tmp & OPCODE_MASK);
724           *pBC         = (bit8)((msgHeader_tmp >> SHIFT24) & BC_MASK);
725 
726           /* invalidate the cache line for IOMB */
727 #ifdef LOOPBACK_MPI
728           if (!loopback)
729 #endif
730             ossaCacheInvalidate(circularQ->agRoot, (void *)circularQ->memoryRegion.appHandle, (void *)msgHeader, (*pBC - 1) * circularQ->elementSize);
731 
732 #if defined(SALLSDK_DEBUG)
733           SA_DBG3(("mpiMsgConsume: CI=%d PI=%d msgHeader=%p\n", circularQ->consumerIdx, circularQ->producerIdx, (void *)msgHeader));
734           MPI_OBQ_IOMB_LOG(circularQ->qNumber, (void *)msgHeader, circularQ->elementSize);
735 #endif
736           return AGSA_RC_SUCCESS;
737         }
738         else
739         {
740           SA_DBG3(("mpiMsgConsume: SKIP_ENTRIES_IOMB BC=%d\n", (msgHeader_tmp >> SHIFT24) & BC_MASK));
741           /* Updated comsumerIdx and skip it */
742           circularQ->consumerIdx = (circularQ->consumerIdx + ((msgHeader_tmp >> SHIFT24) & BC_MASK)) % circularQ->numElements;
743           /* clean header to 0 */
744           msgHeader_tmp = 0;
745           /*ossaSingleThreadedEnter(agRoot, LL_IOREQ_OBQ_LOCK);*/
746 
747           OSSA_WRITE_LE_32(circularQ->agRoot, msgHeader, OSSA_OFFSET_OF(mpiMsgHeader_t, Header), msgHeader_tmp);
748 
749           /* update the CI of outbound queue */
750           ossaHwRegWriteExt(circularQ->agRoot,
751                             circularQ->CIPCIBar,
752                             circularQ->CIPCIOffset,
753                             circularQ->consumerIdx);
754           /* Update the producer index */
755           OSSA_READ_LE_32(circularQ->agRoot, &circularQ->producerIdx, circularQ->piPointer, 0);
756           /*ossaSingleThreadedLeave(agRoot, LL_IOREQ_OBQ_LOCK); */
757         }
758       }
759       else
760       {
761         /* V bit is not set */
762 #if defined(SALLSDK_DEBUG)
763         agsaRoot_t *agRoot=circularQ->agRoot;
764         SA_DBG1(("mpiMsgConsume: V bit not set, PI=%d CI=%d msgHeader=%p\n",  circularQ->producerIdx, circularQ->consumerIdx,(void *)msgHeader));
765         SA_DBG1(("mpiMsgConsume: V bit not set, 0x%08X Q=%d  \n", msgHeader_tmp, circularQ->qNumber));
766 
767         MPI_DEBUG_TRACE(MPI_DEBUG_TRACE_QNUM_ERROR + circularQ->qNumber,
768                         ((circularQ->producerIdx << 16 ) | circularQ->consumerIdx),
769                           MPI_DEBUG_TRACE_OBQ,
770                          (void *)(((bit8*)msgHeader) - sizeof(mpiMsgHeader_t)),
771                           circularQ->elementSize);
772 
773         circularQ->consumerIdx = circularQ->consumerIdx % circularQ->numElements;
774         circularQ->consumerIdx ++;
775         OSSA_WRITE_LE_32(circularQ->agRoot, msgHeader, OSSA_OFFSET_OF(mpiMsgHeader_t, Header), msgHeader_tmp);
776         ossaHwRegWriteExt(agRoot,
777                           circularQ->CIPCIBar,
778                           circularQ->CIPCIOffset,
779                           circularQ->consumerIdx);
780         MPI_OBQ_IOMB_LOG(circularQ->qNumber, (void *)msgHeader, circularQ->elementSize);
781 #endif
782         SA_DBG1(("mpiMsgConsume: V bit is not set!!!!! HW CI=%d\n", ossaHwRegReadExt(circularQ->agRoot, circularQ->CIPCIBar, circularQ->CIPCIOffset) ));
783         SA_ASSERT(0, "V bit is not set");
784         return AGSA_RC_FAILURE;
785       }
786     }
787     else
788     {
789       /* Update the producer index from SPC */
790       OSSA_READ_LE_32(circularQ->agRoot, &circularQ->producerIdx, circularQ->piPointer, 0);
791     }
792   } while(circularQ->producerIdx != circularQ->consumerIdx); /* while we don't have any more not-yet-delivered message */
793 
794 #ifdef TEST
795   SA_DBG4(("mpiMsgConsume: Outbound queue is empty.\n"));
796 #endif
797 
798   /* report empty */
799   return AGSA_RC_BUSY;
800 }
801 
802 /*******************************************************************************/
803 /** \fn mpiMsgFreeSet(mpiOCQueue_t *circularQ, void *messagePtr)
804  *  \brief Returns a received message to the outbound queue
805  *  \param circularQ   Pointer to an outbound queue
806  *  \param messagePtr1 Pointer to the returned message buffer to free
807  *  \param messagePtr2 Pointer to the returned message buffer to free if bc > 1
808  *
809  * Returns consumed and processed message to the specified outbounf queue
810  *
811  * Return:
812  *         AGSA_RC_SUCCESS if the message has been returned succesfully
813  */
814 /*******************************************************************************/
815 GLOBAL FORCEINLINE
816 bit32
817 mpiMsgFreeSet(
818   mpiOCQueue_t *circularQ,
819   void *messagePtr1,
820   bit8 bc
821   )
822 {
823   mpiMsgHeader_t     *msgHeader;
824 
825   SA_DBG4(("Entering function:mpiMsgFreeSet\n"));
826   SA_ASSERT(NULL != circularQ, "circularQ argument cannot be null");
827   SA_ASSERT(NULL != messagePtr1, "messagePtr1 argument cannot be null");
828   SA_ASSERT(0 != circularQ->numElements, "The number of elements in this queue is 0");
829 
830   /* Obtains the address of the entire message buffer, including the header */
831   msgHeader = (mpiMsgHeader_t*)(((bit8*)messagePtr1) - sizeof(mpiMsgHeader_t));
832 
833   if ( ((mpiMsgHeader_t*)((bit8*)circularQ->memoryRegion.virtPtr + circularQ->consumerIdx * circularQ->elementSize)) != msgHeader)
834   {
835     /* IOMB of CI points mismatch with Message Header - should never happened */
836     SA_DBG1(("mpiMsgFreeSet: Wrong CI, Q %d ConsumeIdx = %d msgHeader 0x%08x\n",circularQ->qNumber, circularQ->consumerIdx ,msgHeader->Header));
837     SA_DBG1(("mpiMsgFreeSet: msgHeader %p != %p\n", msgHeader,((mpiMsgHeader_t*)((bit8*)circularQ->memoryRegion.virtPtr + circularQ->consumerIdx * circularQ->elementSize))));
838 
839 #ifdef LOOPBACK_MPI
840     if (!loopback)
841 #endif
842     /* Update the producer index from SPC */
843     OSSA_READ_LE_32(circularQ->agRoot, &circularQ->producerIdx, circularQ->piPointer, 0);
844 #if defined(SALLSDK_DEBUG)
845     SA_DBG3(("mpiMsgFreeSet: ProducerIdx = %d\n", circularQ->producerIdx));
846 #endif
847     return AGSA_RC_SUCCESS;
848   }
849 
850   /* ... free the circular queue buffer elements associated with the message ... */
851   /*... by incrementing the consumer index (with wrap arround) */
852   circularQ->consumerIdx = (circularQ->consumerIdx + bc) % circularQ->numElements;
853 
854   /* Invalidates this circular queue buffer element */
855 
856   msgHeader->Header &= ~HEADER_V_MASK; /* Clear Valid bit to indicate IOMB consumed by host */
857   SA_ASSERT(circularQ->consumerIdx <= circularQ->numElements, "Multi-buffer messages cannot wrap arround");
858 
859   /* update the CI of outbound queue */
860 #ifdef LOOPBACK_MPI
861   if (!loopback)
862 #endif
863   {
864   ossaHwRegWriteExt(circularQ->agRoot,
865                     circularQ->CIPCIBar,
866                     circularQ->CIPCIOffset,
867                     circularQ->consumerIdx);
868 
869   /* Update the producer index from SPC */
870   OSSA_READ_LE_32(circularQ->agRoot, &circularQ->producerIdx, circularQ->piPointer, 0);
871   }
872 #if defined(SALLSDK_DEBUG)
873   SA_DBG5(("mpiMsgFreeSet: CI=%d PI=%d\n", circularQ->consumerIdx, circularQ->producerIdx));
874 #endif
875   return AGSA_RC_SUCCESS;
876 }
877 
878 #ifdef TEST
879 GLOBAL bit32 mpiRotateQnumber(agsaRoot_t *agRoot)
880 {
881   agsaLLRoot_t *saRoot = (agsaLLRoot_t *) (agRoot->sdkData);
882   bit32        denom;
883   bit32        ret = 0;
884 
885   /* inbound queue number */
886   saRoot->IBQnumber++;
887   denom = saRoot->QueueConfig.numInboundQueues;
888   if (saRoot->IBQnumber % denom == 0) /* % Qnumber*/
889   {
890     saRoot->IBQnumber = 0;
891   }
892   SA_DBG3(("mpiRotateQnumber: IBQnumber %d\n", saRoot->IBQnumber));
893 
894   /* outbound queue number */
895   saRoot->OBQnumber++;
896   denom = saRoot->QueueConfig.numOutboundQueues;
897   if (saRoot->OBQnumber % denom == 0) /* % Qnumber*/
898   {
899     saRoot->OBQnumber = 0;
900   }
901   SA_DBG3(("mpiRotateQnumber: OBQnumber %d\n", saRoot->OBQnumber));
902 
903   ret = (saRoot->OBQnumber << SHIFT16) | saRoot->IBQnumber;
904   return ret;
905 }
906 #endif
907 
908 #ifdef LOOPBACK_MPI
909 GLOBAL bit32 mpiMsgProduceOQ(
910                        mpiOCQueue_t *circularQ,
911                        void         *messagePtr,
912                        mpiMsgCategory_t category,
913                        bit16        opCode,
914                        bit8         responseQueue,
915                        bit8         hiPriority
916                        )
917 {
918   mpiMsgHeader_t *msgHeader;
919   bit32          bc;
920   bit32          Header = 0;
921   bit32          hpriority = 0;
922 
923   SA_DBG4(("Entering function:mpiMsgProduceOQ\n"));
924   SA_ASSERT(NULL != circularQ, "circularQ argument cannot be null");
925   SA_ASSERT(NULL != messagePtr, "messagePtr argument cannot be null");
926   SA_ASSERT(0 != circularQ->numElements, "The number of elements in this queue"
927             " is 0");
928   SA_ASSERT(MPI_MAX_OUTBOUND_QUEUES > responseQueue, "oQueue ID is wrong");
929 
930   /* REB Start extra trace */
931   smTraceFuncEnter(hpDBG_VERY_LOUD, "2I");
932   /* REB End extra trace */
933 
934   /* Obtains the address of the entire message buffer, including the header */
935   msgHeader = (mpiMsgHeader_t*)(((bit8*)messagePtr) - sizeof(mpiMsgHeader_t));
936   /* Read the BC from header, its stored in native endian format when message
937      was allocated */
938   /* intially */
939   SA_DBG4(("mpiMsgProduceOQ: msgHeader %p opcode %d pi/ci %d / %d\n", msgHeader, opCode, circularQ->producerIdx, circularQ->consumerIdx));
940   bc = (((msgHeader->Header) >> SHIFT24) & BC_MASK);
941   SA_DBG6(("mpiMsgProduceOQ: msgHeader bc %d\n", bc));
942   if (circularQ->priority)
943     hpriority = 1;
944 
945   /* Checks the message is in "allocated" state */
946   SA_ASSERT(0 != bc, "The message buffer is not in \"allocated\" state "
947                      "(bc == 0)");
948 
949   Header = ((V_BIT << SHIFT31) | (hpriority << SHIFT30)  |
950             ((bc & BC_MASK) << SHIFT24) |
951             ((responseQueue & OBID_MASK) << SHIFT16) |
952             ((category  & CAT_MASK) << SHIFT12 ) | (opCode & OPCODE_MASK));
953   /* pre flush the IOMB cache line */
954   //ossaCachePreFlush(circularQ->agRoot,
955   //                  (void *)circularQ->memoryRegion.appHandle,
956   //                  (void *)msgHeader, circularQ->elementSize * bc);
957   OSSA_WRITE_LE_32(circularQ->agRoot, msgHeader, OSSA_OFFSET_OF(mpiMsgHeader_t,
958                    Header), Header);
959 
960   /* flush the IOMB cache line */
961   //ossaCacheFlush(circularQ->agRoot, (void *)circularQ->memoryRegion.appHandle,
962   //               (void *)msgHeader, circularQ->elementSize * bc);
963 
964   MPI_DEBUG_TRACE( circularQ->qNumber,
965                  ((circularQ->producerIdx << 16 ) | circularQ->consumerIdx),
966                   MPI_DEBUG_TRACE_OBQ,
967                   (void *)msgHeader,
968                   circularQ->elementSize);
969 
970   ossaLogIomb(circularQ->agRoot,
971               circularQ->qNumber,
972               TRUE,
973               (void *)msgHeader,
974               circularQ->elementSize);
975 
976   smTraceFuncExit(hpDBG_VERY_LOUD, 'a', "2I");
977   return AGSA_RC_SUCCESS;
978 } /* mpiMsgProduceOQ */
979 #endif
980 
981