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