blob: 2baeaafeb1d32b8b20dcb0bfcbc7bf1e71c7e883 [file] [log] [blame]
Wolfgang Denkba94a1b2006-05-30 15:56:48 +02001/**
2 * @file IxQMgrDispatcher.c
3 *
4 * @author Intel Corporation
5 * @date 20-Dec-2001
6 *
7 * @brief This file contains the implementation of the Dispatcher sub component
8 *
9 *
10 * @par
11 * IXP400 SW Release version 2.0
12 *
13 * -- Copyright Notice --
14 *
15 * @par
16 * Copyright 2001-2005, Intel Corporation.
17 * All rights reserved.
18 *
19 * @par
Wolfgang Denkcb3761e2013-07-28 22:12:47 +020020 * SPDX-License-Identifier: BSD-3-Clause
Wolfgang Denkba94a1b2006-05-30 15:56:48 +020021 * @par
22 * -- End of Copyright Notice --
23*/
24
25/*
26 * User defined include files.
27 */
28#include "IxQMgr.h"
29#include "IxQMgrAqmIf_p.h"
30#include "IxQMgrQCfg_p.h"
31#include "IxQMgrDispatcher_p.h"
32#include "IxQMgrLog_p.h"
33#include "IxQMgrDefines_p.h"
34#include "IxFeatureCtrl.h"
35#include "IxOsal.h"
36
37
38
39/*
40 * #defines and macros used in this file.
41 */
42
43
44/*
45 * This constant is used to indicate the number of priority levels supported
46 */
47#define IX_QMGR_NUM_PRIORITY_LEVELS 3
48
49/*
50 * This constant is used to set the size of the array of status words
51 */
52#define MAX_Q_STATUS_WORDS 4
53
54/*
55 * This macro is used to check if a given priority is valid
56 */
57#define IX_QMGR_DISPATCHER_PRIORITY_CHECK(priority) \
58(((priority) >= IX_QMGR_Q_PRIORITY_0) && ((priority) <= IX_QMGR_Q_PRIORITY_2))
59
60/*
61 * This macto is used to check that a given interrupt source is valid
62 */
63#define IX_QMGR_DISPATCHER_SOURCE_ID_CHECK(srcSel) \
64(((srcSel) >= IX_QMGR_Q_SOURCE_ID_E) && ((srcSel) <= IX_QMGR_Q_SOURCE_ID_NOT_F))
65
66/*
67 * Number of times a dummy callback is called before logging a trace
68 * message
69 */
70#define LOG_THROTTLE_COUNT 1000000
71
72/* Priority tables limits */
73#define IX_QMGR_MIN_LOW_QUE_PRIORITY_TABLE_INDEX (0)
74#define IX_QMGR_MID_LOW_QUE_PRIORITY_TABLE_INDEX (16)
75#define IX_QMGR_MAX_LOW_QUE_PRIORITY_TABLE_INDEX (31)
76#define IX_QMGR_MIN_UPP_QUE_PRIORITY_TABLE_INDEX (32)
77#define IX_QMGR_MID_UPP_QUE_PRIORITY_TABLE_INDEX (48)
78#define IX_QMGR_MAX_UPP_QUE_PRIORITY_TABLE_INDEX (63)
79
80/*
81 * This macro is used to check if a given callback type is valid
82 */
83#define IX_QMGR_DISPATCHER_CALLBACK_TYPE_CHECK(type) \
84 (((type) >= IX_QMGR_TYPE_REALTIME_OTHER) && \
85 ((type) <= IX_QMGR_TYPE_REALTIME_SPORADIC))
86
87/*
88 * define max index in lower queue to use in loops
89 */
90#define IX_QMGR_MAX_LOW_QUE_TABLE_INDEX (31)
91
92/*
93 * Typedefs whose scope is limited to this file.
94 */
95
96/*
97 * Information on a queue needed by the Dispatcher
98 */
99typedef struct
100{
101 IxQMgrCallback callback; /* Notification callback */
102 IxQMgrCallbackId callbackId; /* Notification callback identifier */
103 unsigned dummyCallbackCount; /* Number of times runs of dummy callback */
104 IxQMgrPriority priority; /* Dispatch priority */
105 unsigned int statusWordOffset; /* Offset to the status word to check */
106 UINT32 statusMask; /* Status mask */
107 UINT32 statusCheckValue; /* Status check value */
108 UINT32 intRegCheckMask; /* Interrupt register check mask */
109} IxQMgrQInfo;
110
111/*
112 * Variable declarations global to this file. Externs are followed by
113 * statics.
114 */
115
116/*
117 * Flag to keep record of what dispatcher set in featureCtrl when ixQMgrInit()
118 * is called. This is needed because it is possible that a client might
119 * change whether the live lock prevention dispatcher is used between
120 * calls to ixQMgrInit() and ixQMgrDispatcherLoopGet().
121 */
122PRIVATE IX_STATUS ixQMgrOrigB0Dispatcher = IX_FEATURE_CTRL_COMPONENT_ENABLED;
123
124/*
125 * keep record of Q types - not in IxQMgrQInfo for performance as
126 * it is only used with ixQMgrDispatcherLoopRunB0LLP()
127 */
128PRIVATE IxQMgrType ixQMgrQTypes[IX_QMGR_MAX_NUM_QUEUES];
129
130/*
131 * This array contains a list of queue identifiers ordered by priority. The table
132 * is split logically between queue identifiers 0-31 and 32-63.
133 */
134static IxQMgrQId priorityTable[IX_QMGR_MAX_NUM_QUEUES];
135
136/*
137 * This flag indicates to the dispatcher that the priority table needs to be rebuilt.
138 */
York Sun472d5462013-04-01 11:29:11 -0700139static BOOL rebuildTable = false;
Wolfgang Denkba94a1b2006-05-30 15:56:48 +0200140
141/* Dispatcher statistics */
142static IxQMgrDispatcherStats dispatcherStats;
143
144/* Table of queue information */
145static IxQMgrQInfo dispatchQInfo[IX_QMGR_MAX_NUM_QUEUES];
146
147/* Masks use to identify the first queues in the priority tables
148* when comparing with the interrupt register
149*/
150static unsigned int lowPriorityTableFirstHalfMask;
151static unsigned int uppPriorityTableFirstHalfMask;
152
153/*
154 * Static function prototypes
155 */
156
157/*
158 * This function is the default callback for all queues
159 */
160PRIVATE void
161dummyCallback (IxQMgrQId qId,
162 IxQMgrCallbackId cbId);
163
164PRIVATE void
165ixQMgrDispatcherReBuildPriorityTable (void);
166
167/*
168 * Function definitions.
169 */
170void
171ixQMgrDispatcherInit (void)
172{
173 int i;
174 IxFeatureCtrlProductId productId = 0;
175 IxFeatureCtrlDeviceId deviceId = 0;
York Sun472d5462013-04-01 11:29:11 -0700176 BOOL stickyIntSilicon = true;
Wolfgang Denkba94a1b2006-05-30 15:56:48 +0200177
178 /* Set default priorities */
179 for (i=0; i< IX_QMGR_MAX_NUM_QUEUES; i++)
180 {
181 dispatchQInfo[i].callback = dummyCallback;
182 dispatchQInfo[i].callbackId = 0;
183 dispatchQInfo[i].dummyCallbackCount = 0;
184 dispatchQInfo[i].priority = IX_QMGR_Q_PRIORITY_2;
185 dispatchQInfo[i].statusWordOffset = 0;
186 dispatchQInfo[i].statusCheckValue = 0;
187 dispatchQInfo[i].statusMask = 0;
188 /*
189 * There are two interrupt registers, 32 bits each. One for the lower
190 * queues(0-31) and one for the upper queues(32-63). Therefore need to
191 * mod by 32 i.e the min upper queue identifier.
192 */
193 dispatchQInfo[i].intRegCheckMask = (1<<(i%(IX_QMGR_MIN_QUEUPP_QID)));
194
195 /*
196 * Set the Q types - will only be used with livelock
197 */
198 ixQMgrQTypes[i] = IX_QMGR_TYPE_REALTIME_OTHER;
199
200 /* Reset queue statistics */
201 dispatcherStats.queueStats[i].callbackCnt = 0;
202 dispatcherStats.queueStats[i].priorityChangeCnt = 0;
203 dispatcherStats.queueStats[i].intNoCallbackCnt = 0;
204 dispatcherStats.queueStats[i].intLostCallbackCnt = 0;
York Sun472d5462013-04-01 11:29:11 -0700205 dispatcherStats.queueStats[i].notificationEnabled = false;
Wolfgang Denkba94a1b2006-05-30 15:56:48 +0200206 dispatcherStats.queueStats[i].srcSel = 0;
207
208 }
209
210 /* Priority table. Order the table from queue 0 to 63 */
211 ixQMgrDispatcherReBuildPriorityTable();
212
213 /* Reset statistics */
214 dispatcherStats.loopRunCnt = 0;
215
216 /* Get the device ID for the underlying silicon */
217 deviceId = ixFeatureCtrlDeviceRead();
218
219 /* Get the product ID for the underlying silicon */
220 productId = ixFeatureCtrlProductIdRead();
221
222 /*
223 * Check featureCtrl to see if Livelock prevention is required
224 */
225 ixQMgrOrigB0Dispatcher = ixFeatureCtrlSwConfigurationCheck(
226 IX_FEATURECTRL_ORIGB0_DISPATCHER);
227
228 /*
229 * Check if the silicon supports the sticky interrupt feature.
230 * IF (IXP42X AND A0) -> No sticky interrupt feature supported
231 */
232 if ((IX_FEATURE_CTRL_DEVICE_TYPE_IXP42X ==
233 (IX_FEATURE_CTRL_DEVICE_TYPE_MASK & deviceId)) &&
234 (IX_FEATURE_CTRL_SILICON_TYPE_A0 ==
235 (IX_FEATURE_CTRL_SILICON_STEPPING_MASK & productId)))
236 {
York Sun472d5462013-04-01 11:29:11 -0700237 stickyIntSilicon = false;
Wolfgang Denkba94a1b2006-05-30 15:56:48 +0200238 }
239
240 /*
241 * IF user wants livelock prev option AND silicon supports sticky interrupt
242 * feature -> enable the sticky interrupt bit
243 */
244 if ((IX_FEATURE_CTRL_SWCONFIG_DISABLED == ixQMgrOrigB0Dispatcher) &&
245 stickyIntSilicon)
246 {
247 ixQMgrStickyInterruptRegEnable();
248 }
249}
250
251IX_STATUS
252ixQMgrDispatcherPrioritySet (IxQMgrQId qId,
253 IxQMgrPriority priority)
254{
255 int ixQMgrLockKey;
256
257 if (!ixQMgrQIsConfigured(qId))
258 {
259 return IX_QMGR_Q_NOT_CONFIGURED;
260 }
261
262 if (!IX_QMGR_DISPATCHER_PRIORITY_CHECK(priority))
263 {
264 return IX_QMGR_Q_INVALID_PRIORITY;
265 }
266
267 ixQMgrLockKey = ixOsalIrqLock();
268
269 /* Change priority */
270 dispatchQInfo[qId].priority = priority;
271 /* Set flag */
York Sun472d5462013-04-01 11:29:11 -0700272 rebuildTable = true;
Wolfgang Denkba94a1b2006-05-30 15:56:48 +0200273
274 ixOsalIrqUnlock(ixQMgrLockKey);
275
276#ifndef NDEBUG
277 /* Update statistics */
278 dispatcherStats.queueStats[qId].priorityChangeCnt++;
279#endif
280
281 return IX_SUCCESS;
282}
283
284IX_STATUS
285ixQMgrNotificationCallbackSet (IxQMgrQId qId,
286 IxQMgrCallback callback,
287 IxQMgrCallbackId callbackId)
288{
289 if (!ixQMgrQIsConfigured(qId))
290 {
291 return IX_QMGR_Q_NOT_CONFIGURED;
292 }
293
294 if (NULL == callback)
295 {
296 /* Reset to dummy callback */
297 dispatchQInfo[qId].callback = dummyCallback;
298 dispatchQInfo[qId].dummyCallbackCount = 0;
299 dispatchQInfo[qId].callbackId = 0;
300 }
301 else
302 {
303 dispatchQInfo[qId].callback = callback;
304 dispatchQInfo[qId].callbackId = callbackId;
305 }
306
307 return IX_SUCCESS;
308}
309
310IX_STATUS
311ixQMgrNotificationEnable (IxQMgrQId qId,
312 IxQMgrSourceId srcSel)
313{
314 IxQMgrQStatus qStatusOnEntry;/* The queue status on entry/exit */
315 IxQMgrQStatus qStatusOnExit; /* to this function */
316 int ixQMgrLockKey;
317
318#ifndef NDEBUG
319 if (!ixQMgrQIsConfigured (qId))
320 {
321 return IX_QMGR_Q_NOT_CONFIGURED;
322 }
323
324 if ((qId < IX_QMGR_MIN_QUEUPP_QID) &&
325 !IX_QMGR_DISPATCHER_SOURCE_ID_CHECK(srcSel))
326 {
327 /* QId 0-31 source id invalid */
328 return IX_QMGR_INVALID_INT_SOURCE_ID;
329 }
330
331 if ((IX_QMGR_Q_SOURCE_ID_NE != srcSel) &&
332 (qId >= IX_QMGR_MIN_QUEUPP_QID))
333 {
334 /*
335 * For queues 32-63 the interrupt source is fixed to the Nearly
336 * Empty status flag and therefore should have a srcSel of NE.
337 */
338 return IX_QMGR_INVALID_INT_SOURCE_ID;
339 }
340#endif
341
342#ifndef NDEBUG
York Sun472d5462013-04-01 11:29:11 -0700343 dispatcherStats.queueStats[qId].notificationEnabled = true;
Wolfgang Denkba94a1b2006-05-30 15:56:48 +0200344 dispatcherStats.queueStats[qId].srcSel = srcSel;
345#endif
346
347 /* Get the current queue status */
348 ixQMgrAqmIfQueStatRead (qId, &qStatusOnEntry);
349
350 /*
351 * Enabling interrupts results in Read-Modify-Write
352 * so need critical section
353 */
354
355 ixQMgrLockKey = ixOsalIrqLock();
356
357 /* Calculate the checkMask and checkValue for this q */
358 ixQMgrAqmIfQStatusCheckValsCalc (qId,
359 srcSel,
360 &dispatchQInfo[qId].statusWordOffset,
361 &dispatchQInfo[qId].statusCheckValue,
362 &dispatchQInfo[qId].statusMask);
363
364
Mike Williams16263082011-07-22 04:01:30 +0000365 /* Set the interrupt source is this queue is in the range 0-31 */
Wolfgang Denkba94a1b2006-05-30 15:56:48 +0200366 if (qId < IX_QMGR_MIN_QUEUPP_QID)
367 {
368 ixQMgrAqmIfIntSrcSelWrite (qId, srcSel);
369 }
370
371 /* Enable the interrupt */
372 ixQMgrAqmIfQInterruptEnable (qId);
373
374 ixOsalIrqUnlock(ixQMgrLockKey);
375
376 /* Get the current queue status */
377 ixQMgrAqmIfQueStatRead (qId, &qStatusOnExit);
378
379 /* If the status has changed return a warning */
380 if (qStatusOnEntry != qStatusOnExit)
381 {
382 return IX_QMGR_WARNING;
383 }
384
385 return IX_SUCCESS;
386}
387
388
389IX_STATUS
390ixQMgrNotificationDisable (IxQMgrQId qId)
391{
392 int ixQMgrLockKey;
393
394#ifndef NDEBUG
395 /* Validate parameters */
396 if (!ixQMgrQIsConfigured (qId))
397 {
398 return IX_QMGR_Q_NOT_CONFIGURED;
399 }
400#endif
401
402 /*
403 * Enabling interrupts results in Read-Modify-Write
404 * so need critical section
405 */
406#ifndef NDEBUG
York Sun472d5462013-04-01 11:29:11 -0700407 dispatcherStats.queueStats[qId].notificationEnabled = false;
Wolfgang Denkba94a1b2006-05-30 15:56:48 +0200408#endif
409
410 ixQMgrLockKey = ixOsalIrqLock();
411
412 ixQMgrAqmIfQInterruptDisable (qId);
413
414 ixOsalIrqUnlock(ixQMgrLockKey);
415
416 return IX_SUCCESS;
417}
418
419void
420ixQMgrStickyInterruptRegEnable(void)
421{
422 /* Use Aqm If function to set Interrupt Register0 Bit-3 */
423 ixQMgrAqmIfIntSrcSelReg0Bit3Set ();
424}
425
426#if !defined __XSCALE__ || defined __linux
427
428/* Count the number of leading zero bits in a word,
429 * and return the same value than the CLZ instruction.
430 *
431 * word (in) return value (out)
432 * 0x80000000 0
433 * 0x40000000 1
434 * ,,, ,,,
435 * 0x00000002 30
436 * 0x00000001 31
437 * 0x00000000 32
438 *
439 * The C version of this function is used as a replacement
440 * for system not providing the equivalent of the CLZ
441 * assembly language instruction.
442 *
443 * Note that this version is big-endian
444 */
445unsigned int
446ixQMgrCountLeadingZeros(UINT32 word)
447{
448 unsigned int leadingZerosCount = 0;
449
450 if (word == 0)
451 {
452 return 32;
453 }
454 /* search the first bit set by testing the MSB and shifting the input word */
455 while ((word & 0x80000000) == 0)
456 {
457 word <<= 1;
458 leadingZerosCount++;
459 }
460 return leadingZerosCount;
461}
462#endif /* not __XSCALE__ or __linux */
463
464void
465ixQMgrDispatcherLoopGet (IxQMgrDispatcherFuncPtr *qDispatcherFuncPtr)
466{
467 IxFeatureCtrlProductId productId = 0;
468 IxFeatureCtrlDeviceId deviceId = 0;
469
470 /* Get the device ID for the underlying silicon */
471 deviceId = ixFeatureCtrlDeviceRead();
472
473 /* Get the product ID for the underlying silicon */
474 productId = ixFeatureCtrlProductIdRead ();
475
476 /* IF (IXP42X AND A0 silicon) -> use ixQMgrDispatcherLoopRunA0 */
477 if ((IX_FEATURE_CTRL_DEVICE_TYPE_IXP42X ==
478 (IX_FEATURE_CTRL_DEVICE_TYPE_MASK & deviceId)) &&
479 (IX_FEATURE_CTRL_SILICON_TYPE_A0 ==
480 (IX_FEATURE_CTRL_SILICON_STEPPING_MASK & productId)))
481 {
482 /*For IXP42X A0 silicon */
483 *qDispatcherFuncPtr = &ixQMgrDispatcherLoopRunA0 ;
484 }
485 else /*For IXP42X B0 or IXP46X silicon*/
486 {
487 if (IX_FEATURE_CTRL_SWCONFIG_ENABLED == ixQMgrOrigB0Dispatcher)
488 {
489 /* Default for IXP42X B0 and IXP46X silicon */
490 *qDispatcherFuncPtr = &ixQMgrDispatcherLoopRunB0;
491 }
492 else
493 {
494 /* FeatureCtrl indicated that livelock dispatcher be used */
495 *qDispatcherFuncPtr = &ixQMgrDispatcherLoopRunB0LLP;
496 }
497 }
498}
499
500void
501ixQMgrDispatcherLoopRunA0 (IxQMgrDispatchGroup group)
502{
503 UINT32 intRegVal; /* Interrupt reg val */
504 UINT32 intRegValAfterWrite; /* Interrupt reg val after writing back */
505 UINT32 intRegCheckMask; /* Mask for checking interrupt bits */
506 UINT32 qStatusWordsB4Write[MAX_Q_STATUS_WORDS]; /* Status b4 interrupt write */
507 UINT32 qStatusWordsAfterWrite[MAX_Q_STATUS_WORDS]; /* Status after interrupt write */
508 IxQMgrQInfo *currDispatchQInfo;
509 BOOL statusChangeFlag;
510
511 int priorityTableIndex;/* Priority table index */
512 int qIndex; /* Current queue being processed */
513 int endIndex; /* Index of last queue to process */
514
515#ifndef NDEBUG
516 IX_OSAL_ASSERT((group == IX_QMGR_QUEUPP_GROUP) ||
517 (group == IX_QMGR_QUELOW_GROUP));
518#endif
519
520 /* Read Q status registers before interrupt status read/write */
521 ixQMgrAqmIfQStatusRegsRead (group, qStatusWordsB4Write);
522
523 /* Read the interrupt register */
524 ixQMgrAqmIfQInterruptRegRead (group, &intRegVal);
525
526 /* No bit set : nothing to process (the reaminder of the algorithm is
527 * based on the fact that the interrupt register value contains at
528 * least one bit set
529 */
530 if (intRegVal == 0)
531 {
532#ifndef NDEBUG
533 /* Update statistics */
534 dispatcherStats.loopRunCnt++;
535#endif
536
537 /* Rebuild the priority table if needed */
538 if (rebuildTable)
539 {
540 ixQMgrDispatcherReBuildPriorityTable ();
541 }
542
543 return;
544 }
545
546 /* Write it back to clear the interrupt */
547 ixQMgrAqmIfQInterruptRegWrite (group, intRegVal);
548
549 /* Read Q status registers after interrupt status read/write */
550 ixQMgrAqmIfQStatusRegsRead (group, qStatusWordsAfterWrite);
551
552 /* get the first queue Id from the interrupt register value */
553 qIndex = (BITS_PER_WORD - 1) - ixQMgrCountLeadingZeros(intRegVal);
554
555 /* check if any change occured during hw register modifications */
556 if (IX_QMGR_QUELOW_GROUP == group)
557 {
558 statusChangeFlag =
559 (qStatusWordsB4Write[0] != qStatusWordsAfterWrite[0]) ||
560 (qStatusWordsB4Write[1] != qStatusWordsAfterWrite[1]) ||
561 (qStatusWordsB4Write[2] != qStatusWordsAfterWrite[2]) ||
562 (qStatusWordsB4Write[3] != qStatusWordsAfterWrite[3]);
563 }
564 else
565 {
566 statusChangeFlag =
567 (qStatusWordsB4Write[0] != qStatusWordsAfterWrite[0]);
568 /* Set the queue range based on the queue group to proccess */
569 qIndex += IX_QMGR_MIN_QUEUPP_QID;
570 }
571
York Sun472d5462013-04-01 11:29:11 -0700572 if (statusChangeFlag == false)
Wolfgang Denkba94a1b2006-05-30 15:56:48 +0200573 {
574 /* check if the interrupt register contains
575 * only 1 bit set (happy day scenario)
576 */
577 currDispatchQInfo = &dispatchQInfo[qIndex];
578 if (intRegVal == currDispatchQInfo->intRegCheckMask)
579 {
580 /* only 1 queue event triggered a notification *
581 * Call the callback function for this queue
582 */
583 currDispatchQInfo->callback (qIndex,
584 currDispatchQInfo->callbackId);
585#ifndef NDEBUG
586 /* Update statistics */
587 dispatcherStats.queueStats[qIndex].callbackCnt++;
588#endif
589 }
590 else
591 {
592 /* the event is triggered by more than 1 queue,
593 * the queue search will be starting from the beginning
594 * or the middle of the priority table
595 *
596 * the serach will end when all the bits of the interrupt
597 * register are cleared. There is no need to maintain
598 * a seperate value and test it at each iteration.
599 */
600 if (IX_QMGR_QUELOW_GROUP == group)
601 {
602 /* check if any bit related to queues in the first
603 * half of the priority table is set
604 */
605 if (intRegVal & lowPriorityTableFirstHalfMask)
606 {
607 priorityTableIndex = IX_QMGR_MIN_LOW_QUE_PRIORITY_TABLE_INDEX;
608 }
609 else
610 {
611 priorityTableIndex = IX_QMGR_MID_LOW_QUE_PRIORITY_TABLE_INDEX;
612 }
613 }
614 else
615 {
616 /* check if any bit related to queues in the first
617 * half of the priority table is set
618 */
619 if (intRegVal & uppPriorityTableFirstHalfMask)
620 {
621 priorityTableIndex = IX_QMGR_MIN_UPP_QUE_PRIORITY_TABLE_INDEX;
622 }
623 else
624 {
625 priorityTableIndex = IX_QMGR_MID_UPP_QUE_PRIORITY_TABLE_INDEX;
626 }
627 }
628
629 /* iterate following the priority table until all the bits
630 * of the interrupt register are cleared.
631 */
632 do
633 {
634 qIndex = priorityTable[priorityTableIndex++];
635 currDispatchQInfo = &dispatchQInfo[qIndex];
636 intRegCheckMask = currDispatchQInfo->intRegCheckMask;
637
638 /* If this queue caused this interrupt to be raised */
639 if (intRegVal & intRegCheckMask)
640 {
641 /* Call the callback function for this queue */
642 currDispatchQInfo->callback (qIndex,
643 currDispatchQInfo->callbackId);
644#ifndef NDEBUG
645 /* Update statistics */
646 dispatcherStats.queueStats[qIndex].callbackCnt++;
647#endif
648
649 /* Clear the interrupt register bit */
650 intRegVal &= ~intRegCheckMask;
651 }
652 }
653 while(intRegVal);
654 }
655 }
656 else
657 {
658 /* A change in queue status occured during the hw interrupt
659 * register update. To maintain the interrupt consistency, it
660 * is necessary to iterate through all queues of the queue group.
661 */
662
663 /* Read interrupt status again */
664 ixQMgrAqmIfQInterruptRegRead (group, &intRegValAfterWrite);
665
666 if (IX_QMGR_QUELOW_GROUP == group)
667 {
668 priorityTableIndex = IX_QMGR_MIN_LOW_QUE_PRIORITY_TABLE_INDEX;
669 endIndex = IX_QMGR_MAX_LOW_QUE_PRIORITY_TABLE_INDEX;
670 }
671 else
672 {
673 priorityTableIndex = IX_QMGR_MIN_UPP_QUE_PRIORITY_TABLE_INDEX;
674 endIndex = IX_QMGR_MAX_UPP_QUE_PRIORITY_TABLE_INDEX;
675 }
676
677 for ( ; priorityTableIndex<=endIndex; priorityTableIndex++)
678 {
679 qIndex = priorityTable[priorityTableIndex];
680 currDispatchQInfo = &dispatchQInfo[qIndex];
681 intRegCheckMask = currDispatchQInfo->intRegCheckMask;
682
683 /* If this queue caused this interrupt to be raised */
684 if (intRegVal & intRegCheckMask)
685 {
686 /* Call the callback function for this queue */
687 currDispatchQInfo->callback (qIndex,
688 currDispatchQInfo->callbackId);
689#ifndef NDEBUG
690 /* Update statistics */
691 dispatcherStats.queueStats[qIndex].callbackCnt++;
692#endif
693
694 } /* if (intRegVal .. */
695
696 /*
697 * If interrupt bit is set in intRegValAfterWrite don't
698 * proceed as this will be caught in next interrupt
699 */
700 else if ((intRegValAfterWrite & intRegCheckMask) == 0)
701 {
702 /* Check if an interrupt was lost for this Q */
703 if (ixQMgrAqmIfQStatusCheck(qStatusWordsB4Write,
704 qStatusWordsAfterWrite,
705 currDispatchQInfo->statusWordOffset,
706 currDispatchQInfo->statusCheckValue,
707 currDispatchQInfo->statusMask))
708 {
709 /* Call the callback function for this queue */
710 currDispatchQInfo->callback (qIndex,
711 dispatchQInfo[qIndex].callbackId);
712#ifndef NDEBUG
713 /* Update statistics */
714 dispatcherStats.queueStats[qIndex].callbackCnt++;
715 dispatcherStats.queueStats[qIndex].intLostCallbackCnt++;
716#endif
717 } /* if ixQMgrAqmIfQStatusCheck(.. */
718 } /* else if ((intRegValAfterWrite ... */
719 } /* for (priorityTableIndex=0 ... */
720 }
721
722 /* Rebuild the priority table if needed */
723 if (rebuildTable)
724 {
725 ixQMgrDispatcherReBuildPriorityTable ();
726 }
727
728#ifndef NDEBUG
729 /* Update statistics */
730 dispatcherStats.loopRunCnt++;
731#endif
732}
733
734
735
736void
737ixQMgrDispatcherLoopRunB0 (IxQMgrDispatchGroup group)
738{
739 UINT32 intRegVal; /* Interrupt reg val */
740 UINT32 intRegCheckMask; /* Mask for checking interrupt bits */
741 IxQMgrQInfo *currDispatchQInfo;
742
743
744 int priorityTableIndex; /* Priority table index */
745 int qIndex; /* Current queue being processed */
746
747#ifndef NDEBUG
748 IX_OSAL_ASSERT((group == IX_QMGR_QUEUPP_GROUP) ||
749 (group == IX_QMGR_QUELOW_GROUP));
750 IX_OSAL_ASSERT((group == IX_QMGR_QUEUPP_GROUP) ||
751 (group == IX_QMGR_QUELOW_GROUP));
752#endif
753
754 /* Read the interrupt register */
755 ixQMgrAqmIfQInterruptRegRead (group, &intRegVal);
756
757
758 /* No queue has interrupt register set */
759 if (intRegVal != 0)
760 {
761
762 /* Write it back to clear the interrupt */
763 ixQMgrAqmIfQInterruptRegWrite (group, intRegVal);
764
765 /* get the first queue Id from the interrupt register value */
766 qIndex = (BITS_PER_WORD - 1) - ixQMgrCountLeadingZeros(intRegVal);
767
768 if (IX_QMGR_QUEUPP_GROUP == group)
769 {
770 /* Set the queue range based on the queue group to proccess */
771 qIndex += IX_QMGR_MIN_QUEUPP_QID;
772 }
773
774 /* check if the interrupt register contains
775 * only 1 bit set
776 * For example:
777 * intRegVal = 0x0010
778 * currDispatchQInfo->intRegCheckMask = 0x0010
York Sun472d5462013-04-01 11:29:11 -0700779 * intRegVal == currDispatchQInfo->intRegCheckMask is true.
Wolfgang Denkba94a1b2006-05-30 15:56:48 +0200780 */
781 currDispatchQInfo = &dispatchQInfo[qIndex];
782 if (intRegVal == currDispatchQInfo->intRegCheckMask)
783 {
784 /* only 1 queue event triggered a notification *
785 * Call the callback function for this queue
786 */
787 currDispatchQInfo->callback (qIndex,
788 currDispatchQInfo->callbackId);
789#ifndef NDEBUG
790 /* Update statistics */
791 dispatcherStats.queueStats[qIndex].callbackCnt++;
792#endif
793 }
794 else
795 {
796 /* the event is triggered by more than 1 queue,
797 * the queue search will be starting from the beginning
798 * or the middle of the priority table
799 *
800 * the serach will end when all the bits of the interrupt
801 * register are cleared. There is no need to maintain
802 * a seperate value and test it at each iteration.
803 */
804 if (IX_QMGR_QUELOW_GROUP == group)
805 {
806 /* check if any bit related to queues in the first
807 * half of the priority table is set
808 */
809 if (intRegVal & lowPriorityTableFirstHalfMask)
810 {
811 priorityTableIndex = IX_QMGR_MIN_LOW_QUE_PRIORITY_TABLE_INDEX;
812 }
813 else
814 {
815 priorityTableIndex = IX_QMGR_MID_LOW_QUE_PRIORITY_TABLE_INDEX;
816 }
817 }
818 else
819 {
820 /* check if any bit related to queues in the first
821 * half of the priority table is set
822 */
823 if (intRegVal & uppPriorityTableFirstHalfMask)
824 {
825 priorityTableIndex = IX_QMGR_MIN_UPP_QUE_PRIORITY_TABLE_INDEX;
826 }
827 else
828 {
829 priorityTableIndex = IX_QMGR_MID_UPP_QUE_PRIORITY_TABLE_INDEX;
830 }
831 }
832
833 /* iterate following the priority table until all the bits
834 * of the interrupt register are cleared.
835 */
836 do
837 {
838 qIndex = priorityTable[priorityTableIndex++];
839 currDispatchQInfo = &dispatchQInfo[qIndex];
840 intRegCheckMask = currDispatchQInfo->intRegCheckMask;
841
842 /* If this queue caused this interrupt to be raised */
843 if (intRegVal & intRegCheckMask)
844 {
845 /* Call the callback function for this queue */
846 currDispatchQInfo->callback (qIndex,
847 currDispatchQInfo->callbackId);
848#ifndef NDEBUG
849 /* Update statistics */
850 dispatcherStats.queueStats[qIndex].callbackCnt++;
851#endif
852
853 /* Clear the interrupt register bit */
854 intRegVal &= ~intRegCheckMask;
855 }
856 }
857 while(intRegVal);
858 } /*End of intRegVal == currDispatchQInfo->intRegCheckMask */
859 } /* End of intRegVal != 0 */
860
861#ifndef NDEBUG
862 /* Update statistics */
863 dispatcherStats.loopRunCnt++;
864#endif
865
866 /* Rebuild the priority table if needed */
867 if (rebuildTable)
868 {
869 ixQMgrDispatcherReBuildPriorityTable ();
870 }
871}
872
873void
874ixQMgrDispatcherLoopRunB0LLP (IxQMgrDispatchGroup group)
875{
876 UINT32 intRegVal =0; /* Interrupt reg val */
877 UINT32 intRegCheckMask; /* Mask for checking interrupt bits */
878 IxQMgrQInfo *currDispatchQInfo;
879
880 int priorityTableIndex; /* Priority table index */
881 int qIndex; /* Current queue being processed */
882
883 UINT32 intRegValCopy = 0;
884 UINT32 intEnableRegVal = 0;
885 UINT8 i = 0;
886
887#ifndef NDEBUG
888 IX_OSAL_ASSERT((group == IX_QMGR_QUEUPP_GROUP) ||
889 (group == IX_QMGR_QUELOW_GROUP));
890#endif
891
892 /* Read the interrupt register */
893 ixQMgrAqmIfQInterruptRegRead (group, &intRegVal);
894
895 /*
896 * mask any interrupts that are not enabled
897 */
898 ixQMgrAqmIfQInterruptEnableRegRead (group, &intEnableRegVal);
899 intRegVal &= intEnableRegVal;
900
901 /* No queue has interrupt register set */
902 if (intRegVal != 0)
903 {
904 if (IX_QMGR_QUELOW_GROUP == group)
905 {
906 /*
907 * As the sticky bit is set, the interrupt register will
908 * not clear if write back at this point because the condition
909 * has not been cleared. Take a copy and write back later after
910 * the condition has been cleared
911 */
912 intRegValCopy = intRegVal;
913 }
914 else
915 {
916 /* no sticky for upper Q's, so write back now */
917 ixQMgrAqmIfQInterruptRegWrite (group, intRegVal);
918 }
919
920 /* get the first queue Id from the interrupt register value */
921 qIndex = (BITS_PER_WORD - 1) - ixQMgrCountLeadingZeros(intRegVal);
922
923 if (IX_QMGR_QUEUPP_GROUP == group)
924 {
925 /* Set the queue range based on the queue group to proccess */
926 qIndex += IX_QMGR_MIN_QUEUPP_QID;
927 }
928
929 /* check if the interrupt register contains
930 * only 1 bit set
931 * For example:
932 * intRegVal = 0x0010
933 * currDispatchQInfo->intRegCheckMask = 0x0010
York Sun472d5462013-04-01 11:29:11 -0700934 * intRegVal == currDispatchQInfo->intRegCheckMask is true.
Wolfgang Denkba94a1b2006-05-30 15:56:48 +0200935 */
936 currDispatchQInfo = &dispatchQInfo[qIndex];
937 if (intRegVal == currDispatchQInfo->intRegCheckMask)
938 {
939
940 /*
941 * check if Q type periodic - only lower queues can
942 * have there type set to periodic
943 */
944 if (IX_QMGR_TYPE_REALTIME_PERIODIC == ixQMgrQTypes[qIndex])
945 {
946 /*
947 * Disable the notifications on any sporadics
948 */
949 for (i=0; i <= IX_QMGR_MAX_LOW_QUE_TABLE_INDEX; i++)
950 {
951 if (IX_QMGR_TYPE_REALTIME_SPORADIC == ixQMgrQTypes[i])
952 {
953 ixQMgrNotificationDisable(i);
954#ifndef NDEBUG
955 /* Update statistics */
956 dispatcherStats.queueStats[i].disableCount++;
957#endif
958 }
959 }
960 }
961
962 currDispatchQInfo->callback (qIndex,
963 currDispatchQInfo->callbackId);
964#ifndef NDEBUG
965 /* Update statistics */
966 dispatcherStats.queueStats[qIndex].callbackCnt++;
967#endif
968 }
969 else
970 {
971 /* the event is triggered by more than 1 queue,
972 * the queue search will be starting from the beginning
973 * or the middle of the priority table
974 *
975 * the serach will end when all the bits of the interrupt
976 * register are cleared. There is no need to maintain
977 * a seperate value and test it at each iteration.
978 */
979 if (IX_QMGR_QUELOW_GROUP == group)
980 {
981 /* check if any bit related to queues in the first
982 * half of the priority table is set
983 */
984 if (intRegVal & lowPriorityTableFirstHalfMask)
985 {
986 priorityTableIndex =
987 IX_QMGR_MIN_LOW_QUE_PRIORITY_TABLE_INDEX;
988 }
989 else
990 {
991 priorityTableIndex =
992 IX_QMGR_MID_LOW_QUE_PRIORITY_TABLE_INDEX;
993 }
994 }
995 else
996 {
997 /* check if any bit related to queues in the first
998 * half of the priority table is set
999 */
1000 if (intRegVal & uppPriorityTableFirstHalfMask)
1001 {
1002 priorityTableIndex =
1003 IX_QMGR_MIN_UPP_QUE_PRIORITY_TABLE_INDEX;
1004 }
1005 else
1006 {
1007 priorityTableIndex =
1008 IX_QMGR_MID_UPP_QUE_PRIORITY_TABLE_INDEX;
1009 }
1010 }
1011
1012 /* iterate following the priority table until all the bits
1013 * of the interrupt register are cleared.
1014 */
1015 do
1016 {
1017 qIndex = priorityTable[priorityTableIndex++];
1018 currDispatchQInfo = &dispatchQInfo[qIndex];
1019 intRegCheckMask = currDispatchQInfo->intRegCheckMask;
1020
1021 /* If this queue caused this interrupt to be raised */
1022 if (intRegVal & intRegCheckMask)
1023 {
1024 /*
1025 * check if Q type periodic - only lower queues can
1026 * have there type set to periodic. There can only be one
1027 * periodic queue, so the sporadics are only disabled once.
1028 */
1029 if (IX_QMGR_TYPE_REALTIME_PERIODIC == ixQMgrQTypes[qIndex])
1030 {
1031 /*
1032 * Disable the notifications on any sporadics
1033 */
1034 for (i=0; i <= IX_QMGR_MAX_LOW_QUE_TABLE_INDEX; i++)
1035 {
1036 if (IX_QMGR_TYPE_REALTIME_SPORADIC ==
1037 ixQMgrQTypes[i])
1038 {
1039 ixQMgrNotificationDisable(i);
1040 /*
1041 * remove from intRegVal as we don't want
1042 * to service any sporadics now
1043 */
1044 intRegVal &= ~dispatchQInfo[i].intRegCheckMask;
1045#ifndef NDEBUG
1046 /* Update statistics */
1047 dispatcherStats.queueStats[i].disableCount++;
1048#endif
1049 }
1050 }
1051 }
1052
1053 currDispatchQInfo->callback (qIndex,
1054 currDispatchQInfo->callbackId);
1055#ifndef NDEBUG
1056 /* Update statistics */
1057 dispatcherStats.queueStats[qIndex].callbackCnt++;
1058#endif
1059 /* Clear the interrupt register bit */
1060 intRegVal &= ~intRegCheckMask;
1061 }
1062 }
1063 while(intRegVal);
1064 } /*End of intRegVal == currDispatchQInfo->intRegCheckMask */
1065 } /* End of intRegVal != 0 */
1066
1067#ifndef NDEBUG
1068 /* Update statistics */
1069 dispatcherStats.loopRunCnt++;
1070#endif
1071
1072 if ((intRegValCopy != 0) && (IX_QMGR_QUELOW_GROUP == group))
1073 {
1074 /*
1075 * lower groups (therefore sticky) AND at least one enabled interrupt
1076 * Write back to clear the interrupt
1077 */
1078 ixQMgrAqmIfQInterruptRegWrite (IX_QMGR_QUELOW_GROUP, intRegValCopy);
1079 }
1080
1081 /* Rebuild the priority table if needed */
1082 if (rebuildTable)
1083 {
1084 ixQMgrDispatcherReBuildPriorityTable ();
1085 }
1086}
1087
1088PRIVATE void
1089ixQMgrDispatcherReBuildPriorityTable (void)
1090{
1091 UINT32 qIndex;
1092 UINT32 priority;
1093 int lowQuePriorityTableIndex = IX_QMGR_MIN_LOW_QUE_PRIORITY_TABLE_INDEX;
1094 int uppQuePriorityTableIndex = IX_QMGR_MIN_UPP_QUE_PRIORITY_TABLE_INDEX;
1095
1096 /* Reset the rebuild flag */
York Sun472d5462013-04-01 11:29:11 -07001097 rebuildTable = false;
Wolfgang Denkba94a1b2006-05-30 15:56:48 +02001098
1099 /* initialize the mak used to identify the queues in the first half
1100 * of the priority table
1101 */
1102 lowPriorityTableFirstHalfMask = 0;
1103 uppPriorityTableFirstHalfMask = 0;
1104
1105 /* For each priority level */
1106 for(priority=0; priority<IX_QMGR_NUM_PRIORITY_LEVELS; priority++)
1107 {
1108 /* Foreach low queue in this priority */
1109 for(qIndex=0; qIndex<IX_QMGR_MIN_QUEUPP_QID; qIndex++)
1110 {
1111 if (dispatchQInfo[qIndex].priority == priority)
1112 {
1113 /* build the priority table bitmask which match the
1114 * queues of the first half of the priority table
1115 */
1116 if (lowQuePriorityTableIndex < IX_QMGR_MID_LOW_QUE_PRIORITY_TABLE_INDEX)
1117 {
1118 lowPriorityTableFirstHalfMask |= dispatchQInfo[qIndex].intRegCheckMask;
1119 }
1120 /* build the priority table */
1121 priorityTable[lowQuePriorityTableIndex++] = qIndex;
1122 }
1123 }
1124 /* Foreach upp queue */
1125 for(qIndex=IX_QMGR_MIN_QUEUPP_QID; qIndex<=IX_QMGR_MAX_QID; qIndex++)
1126 {
1127 if (dispatchQInfo[qIndex].priority == priority)
1128 {
1129 /* build the priority table bitmask which match the
1130 * queues of the first half of the priority table
1131 */
1132 if (uppQuePriorityTableIndex < IX_QMGR_MID_UPP_QUE_PRIORITY_TABLE_INDEX)
1133 {
1134 uppPriorityTableFirstHalfMask |= dispatchQInfo[qIndex].intRegCheckMask;
1135 }
1136 /* build the priority table */
1137 priorityTable[uppQuePriorityTableIndex++] = qIndex;
1138 }
1139 }
1140 }
1141}
1142
1143IxQMgrDispatcherStats*
1144ixQMgrDispatcherStatsGet (void)
1145{
1146 return &dispatcherStats;
1147}
1148
1149PRIVATE void
1150dummyCallback (IxQMgrQId qId,
1151 IxQMgrCallbackId cbId)
1152{
1153 /* Throttle the trace message */
1154 if ((dispatchQInfo[qId].dummyCallbackCount % LOG_THROTTLE_COUNT) == 0)
1155 {
1156 IX_QMGR_LOG_WARNING2("--> dummyCallback: qId (%d), callbackId (%d)\n",qId,cbId);
1157 }
1158 dispatchQInfo[qId].dummyCallbackCount++;
1159
1160#ifndef NDEBUG
1161 /* Update statistcs */
1162 dispatcherStats.queueStats[qId].intNoCallbackCnt++;
1163#endif
1164}
1165void
1166ixQMgrLLPShow (int resetStats)
1167{
1168#ifndef NDEBUG
1169 UINT8 i = 0;
Wolfgang Denkba94a1b2006-05-30 15:56:48 +02001170 UINT32 intEnableRegVal = 0;
1171
1172 printf ("Livelock statistics are printed on the fly.\n");
1173 printf ("qId Type EnableCnt DisableCnt IntEnableState Callbacks\n");
1174 printf ("=== ======== ========= ========== ============== =========\n");
1175
1176 for (i=0; i<= IX_QMGR_MAX_LOW_QUE_TABLE_INDEX; i++)
1177 {
Wolfgang Denkba94a1b2006-05-30 15:56:48 +02001178 if (ixQMgrQTypes[i] != IX_QMGR_TYPE_REALTIME_OTHER)
1179 {
1180 printf (" %2d ", i);
1181
1182 if (ixQMgrQTypes[i] == IX_QMGR_TYPE_REALTIME_SPORADIC)
1183 {
1184 printf ("Sporadic");
1185 }
1186 else
1187 {
1188 printf ("Periodic");
1189 }
1190
1191
1192 ixQMgrAqmIfQInterruptEnableRegRead (IX_QMGR_QUELOW_GROUP,
1193 &intEnableRegVal);
1194
1195
1196 intEnableRegVal &= dispatchQInfo[i].intRegCheckMask;
1197 intEnableRegVal = intEnableRegVal >> i;
1198
1199 printf (" %10d %10d %10d %10d\n",
1200 dispatcherStats.queueStats[i].enableCount,
1201 dispatcherStats.queueStats[i].disableCount,
1202 intEnableRegVal,
1203 dispatcherStats.queueStats[i].callbackCnt);
1204
1205 if (resetStats)
1206 {
1207 dispatcherStats.queueStats[i].enableCount =
1208 dispatcherStats.queueStats[i].disableCount =
1209 dispatcherStats.queueStats[i].callbackCnt = 0;
1210 }
1211 }
1212 }
1213#else
1214 IX_QMGR_LOG0("Livelock Prevention statistics are only collected in debug mode\n");
1215#endif
1216}
1217
1218void
1219ixQMgrPeriodicDone (void)
1220{
1221 UINT32 i = 0;
1222 UINT32 ixQMgrLockKey = 0;
1223
1224 /*
1225 * for the lower queues
1226 */
1227 for (i=0; i <= IX_QMGR_MAX_LOW_QUE_TABLE_INDEX; i++)
1228 {
1229 /*
1230 * check for sporadics
1231 */
1232 if (IX_QMGR_TYPE_REALTIME_SPORADIC == ixQMgrQTypes[i])
1233 {
1234 /*
1235 * enable any sporadics
1236 */
1237 ixQMgrLockKey = ixOsalIrqLock();
1238 ixQMgrAqmIfQInterruptEnable(i);
1239 ixOsalIrqUnlock(ixQMgrLockKey);
1240#ifndef NDEBUG
1241 /*
1242 * Update statistics
1243 */
1244 dispatcherStats.queueStats[i].enableCount++;
York Sun472d5462013-04-01 11:29:11 -07001245 dispatcherStats.queueStats[i].notificationEnabled = true;
Wolfgang Denkba94a1b2006-05-30 15:56:48 +02001246#endif
1247 }
1248 }
1249}
1250IX_STATUS
1251ixQMgrCallbackTypeSet (IxQMgrQId qId,
1252 IxQMgrType type)
1253{
1254 UINT32 ixQMgrLockKey = 0;
1255 IxQMgrType ixQMgrOldType =0;
1256
1257#ifndef NDEBUG
1258 if (!ixQMgrQIsConfigured(qId))
1259 {
1260 return IX_QMGR_Q_NOT_CONFIGURED;
1261 }
1262 if (qId >= IX_QMGR_MIN_QUEUPP_QID)
1263 {
1264 return IX_QMGR_PARAMETER_ERROR;
1265 }
1266 if(!IX_QMGR_DISPATCHER_CALLBACK_TYPE_CHECK(type))
1267 {
1268 return IX_QMGR_PARAMETER_ERROR;
1269 }
1270#endif
1271
1272 ixQMgrOldType = ixQMgrQTypes[qId];
1273 ixQMgrQTypes[qId] = type;
1274
1275 /*
1276 * check if Q has been changed from type SPORADIC
1277 */
1278 if (IX_QMGR_TYPE_REALTIME_SPORADIC == ixQMgrOldType)
1279 {
1280 /*
1281 * previously Q was a SPORADIC, this means that LLP
1282 * might have had it disabled. enable it now.
1283 */
1284 ixQMgrLockKey = ixOsalIrqLock();
1285 ixQMgrAqmIfQInterruptEnable(qId);
1286 ixOsalIrqUnlock(ixQMgrLockKey);
1287
1288#ifndef NDEBUG
1289 /*
1290 * Update statistics
1291 */
1292 dispatcherStats.queueStats[qId].enableCount++;
1293#endif
1294 }
1295
1296 return IX_SUCCESS;
1297}
1298
1299IX_STATUS
1300ixQMgrCallbackTypeGet (IxQMgrQId qId,
1301 IxQMgrType *type)
1302{
1303#ifndef NDEBUG
1304 if (!ixQMgrQIsConfigured(qId))
1305 {
1306 return IX_QMGR_Q_NOT_CONFIGURED;
1307 }
1308 if (qId >= IX_QMGR_MIN_QUEUPP_QID)
1309 {
1310 return IX_QMGR_PARAMETER_ERROR;
1311 }
1312 if(type == NULL)
1313 {
1314 return IX_QMGR_PARAMETER_ERROR;
1315 }
1316#endif
1317
1318 *type = ixQMgrQTypes[qId];
1319 return IX_SUCCESS;
1320}