1 /****************************************************************************
3 * Copyright (C) 2005 - 2011 by Vivante Corp.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the license, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 *****************************************************************************/
24 #include "gc_hal_kernel_precomp.h"
28 /******************************************************************************\
29 *********************** Support Functions and Definitions **********************
30 \******************************************************************************/
32 /* Interruot statistics will be accumulated if not zero. */
33 #define gcmENABLE_INTERRUPT_STATISTICS 0
35 #define _GC_OBJ_ZONE gcvZONE_INTERRUPT
37 /* Object structure. */
38 struct _gckVGINTERRUPT
43 /* gckVGKERNEL pointer. */
49 /* Interrupt handlers. */
50 gctINTERRUPT_HANDLER handlers
[32];
52 /* Main interrupt handler thread. */
57 gctSEMAPHORE fifoValid
;
63 /* Interrupt statistics. */
64 #if gcmENABLE_INTERRUPT_STATISTICS
67 gctUINT maxSimultaneous
;
68 gctUINT multipleCount
;
73 /*******************************************************************************
77 ** The interrupt processor.
82 ** Pointer to the gckVGINTERRUPT object.
89 #if gcmENABLE_INTERRUPT_STATISTICS
92 gckVGINTERRUPT Interrupt
,
93 gctUINT_PTR TriggeredCount
98 gckVGINTERRUPT Interrupt
106 /* Advance to the next entry. */
107 Interrupt
->tail
+= 1;
108 Interrupt
->fifoItems
-= 1;
110 /* Get the interrupt value. */
111 triggered
= Interrupt
->fifo
[Interrupt
->tail
];
112 gcmkASSERT(triggered
!= 0);
115 gcvLEVEL_VERBOSE
, gcvZONE_COMMAND
,
116 "%s: triggered=0x%08X\n",
121 /* Walk through all possible interrupts. */
122 for (i
= 0; i
< gcmSIZEOF(Interrupt
->handlers
); i
+= 1)
124 /* Test if interrupt happened. */
125 if ((triggered
& 1) == 1)
127 #if gcmENABLE_INTERRUPT_STATISTICS
128 if (TriggeredCount
!= gcvNULL
)
130 (* TriggeredCount
) += 1;
134 /* Make sure we have valid handler. */
135 if (Interrupt
->handlers
[i
] == gcvNULL
)
139 "%s: Interrupt %d isn't registered.\n",
146 gcvLEVEL_VERBOSE
, gcvZONE_COMMAND
,
147 "%s: interrupt=%d\n",
152 /* Call the handler. */
153 status
= Interrupt
->handlers
[i
] (Interrupt
->kernel
);
155 if (gcmkIS_ERROR(status
))
157 /* Failed to signal the semaphore. */
160 "%s: Error %d incrementing the semaphore #%d.\n",
161 __FUNCTION__
, status
, i
167 /* Next interrupt. */
170 /* No more interrupts to handle? */
179 /*******************************************************************************
181 ** _MainInterruptHandler
183 ** The main interrupt thread serves the interrupt FIFO and calls registered
184 ** handlers for the interrupts that occured. The handlers are called in the
185 ** sequence interrupts occured with the exception when multiple interrupts
186 ** occured at the same time. In that case the handler calls are "sorted" by
187 ** the interrupt number therefore giving the interrupts with lower numbers
193 ** Pointer to the gckVGINTERRUPT object.
200 static gctTHREADFUNCRESULT gctTHREADFUNCTYPE
201 _MainInterruptHandler(
202 gctTHREADFUNCPARAMETER ThreadParameter
206 gckVGINTERRUPT interrupt
;
208 #if gcmENABLE_INTERRUPT_STATISTICS
212 /* Cast the object. */
213 interrupt
= (gckVGINTERRUPT
) ThreadParameter
;
215 /* Enter the loop. */
218 /* Wait for an interrupt. */
219 status
= gckOS_DecrementSemaphore(interrupt
->os
, interrupt
->fifoValid
);
222 if (gcmkIS_ERROR(status
))
227 /* System termination request? */
228 if (status
== gcvSTATUS_TERMINATE
)
233 /* Driver is shutting down? */
234 if (interrupt
->terminate
)
239 #if gcmENABLE_INTERRUPT_STATISTICS
240 /* Reset triggered count. */
243 /* Process the interrupt. */
244 _ProcessInterrupt(interrupt
, &count
);
246 /* Update conters. */
247 if (count
> interrupt
->maxSimultaneous
)
249 interrupt
->maxSimultaneous
= count
;
254 interrupt
->multipleCount
+= 1;
257 /* Process the interrupt. */
258 _ProcessInterrupt(interrupt
);
266 /*******************************************************************************
268 ** _StartInterruptHandler / _StopInterruptHandler
270 ** Main interrupt handler routine control.
275 ** Pointer to the gckVGINTERRUPT object.
283 _StartInterruptHandler(
284 gckVGINTERRUPT Interrupt
287 gceSTATUS status
, last
;
291 /* Objects must not be already created. */
292 gcmkASSERT(Interrupt
->fifoValid
== gcvNULL
);
293 gcmkASSERT(Interrupt
->handler
== gcvNULL
);
295 /* Reset the termination request. */
296 Interrupt
->terminate
= gcvFALSE
;
298 #if !gcdENABLE_INFINITE_SPEED_HW
299 /* Construct the fifo semaphore. */
300 gcmkERR_BREAK(gckOS_CreateSemaphoreVG(
301 Interrupt
->os
, &Interrupt
->fifoValid
304 /* Start the interrupt handler thread. */
305 gcmkERR_BREAK(gckOS_StartThread(
307 _MainInterruptHandler
,
319 if (Interrupt
->fifoValid
!= gcvNULL
)
321 gcmkCHECK_STATUS(gckOS_DestroySemaphore(
322 Interrupt
->os
, Interrupt
->fifoValid
325 Interrupt
->fifoValid
= gcvNULL
;
328 /* Return the status. */
333 _StopInterruptHandler(
334 gckVGINTERRUPT Interrupt
341 /* Does the thread exist? */
342 if (Interrupt
->handler
== gcvNULL
)
344 /* The semaphore must be NULL as well. */
345 gcmkASSERT(Interrupt
->fifoValid
== gcvNULL
);
348 status
= gcvSTATUS_OK
;
352 /* The semaphore must exist as well. */
353 gcmkASSERT(Interrupt
->fifoValid
!= gcvNULL
);
355 /* Set the termination request. */
356 Interrupt
->terminate
= gcvTRUE
;
358 /* Unlock the thread. */
359 gcmkERR_BREAK(gckOS_IncrementSemaphore(
360 Interrupt
->os
, Interrupt
->fifoValid
363 /* Wait until the thread quits. */
364 gcmkERR_BREAK(gckOS_StopThread(
369 /* Destroy the semaphore. */
370 gcmkERR_BREAK(gckOS_DestroySemaphore(
371 Interrupt
->os
, Interrupt
->fifoValid
375 Interrupt
->handler
= gcvNULL
;
376 Interrupt
->fifoValid
= gcvNULL
;
380 /* Return the status. */
385 /******************************************************************************\
386 ***************************** Interrupt Object API *****************************
387 \******************************************************************************/
389 /*******************************************************************************
391 ** gckVGINTERRUPT_Construct
393 ** Construct an interrupt object.
398 ** Pointer to the gckVGKERNEL object.
403 ** Pointer to the new gckVGINTERRUPT object.
407 gckVGINTERRUPT_Construct(
408 IN gckVGKERNEL Kernel
,
409 OUT gckVGINTERRUPT
* Interrupt
413 gckVGINTERRUPT interrupt
= gcvNULL
;
415 gcmkHEADER_ARG("Kernel=0x%x Interrupt=0x%x", Kernel
, Interrupt
);
417 /* Verify argeuments. */
418 gcmkVERIFY_OBJECT(Kernel
, gcvOBJ_KERNEL
);
419 gcmkVERIFY_ARGUMENT(Interrupt
!= gcvNULL
);
423 /* Allocate the gckVGINTERRUPT structure. */
424 gcmkERR_BREAK(gckOS_Allocate(
426 gcmSIZEOF(struct _gckVGINTERRUPT
),
427 (gctPOINTER
*) &interrupt
430 /* Reset the object data. */
431 gcmkVERIFY_OK(gckOS_ZeroMemory(
432 interrupt
, gcmSIZEOF(struct _gckVGINTERRUPT
)
435 /* Initialize the object. */
436 interrupt
->object
.type
= gcvOBJ_INTERRUPT
;
438 /* Initialize the object pointers. */
439 interrupt
->kernel
= Kernel
;
440 interrupt
->os
= Kernel
->os
;
442 /* Initialize the current FIFO position. */
443 interrupt
->head
= (gctUINT8
)~0;
444 interrupt
->tail
= (gctUINT8
)~0;
446 /* Start the thread. */
447 gcmkERR_BREAK(_StartInterruptHandler(interrupt
));
449 /* Return interrupt object. */
450 *Interrupt
= interrupt
;
458 if (interrupt
!= gcvNULL
)
460 /* Free the gckVGINTERRUPT structure. */
461 gcmkVERIFY_OK(gckOS_Free(interrupt
->os
, interrupt
));
464 /* Return the status. */
469 /*******************************************************************************
471 ** gckVGINTERRUPT_Destroy
473 ** Destroy an interrupt object.
478 ** Pointer to the gckVGINTERRUPT object to destroy.
486 gckVGINTERRUPT_Destroy(
487 IN gckVGINTERRUPT Interrupt
492 gcmkHEADER_ARG("Interrupt=0x%x", Interrupt
);
494 /* Verify the arguments. */
495 gcmkVERIFY_OBJECT(Interrupt
, gcvOBJ_INTERRUPT
);
499 /* Stop the interrupt thread. */
500 gcmkERR_BREAK(_StopInterruptHandler(Interrupt
));
502 /* Mark the object as unknown. */
503 Interrupt
->object
.type
= gcvOBJ_UNKNOWN
;
505 /* Free the gckVGINTERRUPT structure. */
506 gcmkERR_BREAK(gckOS_Free(Interrupt
->os
, Interrupt
));
512 /* Return the status. */
517 /*******************************************************************************
519 ** gckVGINTERRUPT_DumpState
521 ** Print the current state of the interrupt manager.
526 ** Pointer to a gckVGINTERRUPT object.
535 gckVGINTERRUPT_DumpState(
536 IN gckVGINTERRUPT Interrupt
539 gcmkHEADER_ARG("Interrupt=0x%x", Interrupt
);
540 /* Verify the arguments. */
541 gcmkVERIFY_OBJECT(Interrupt
, gcvOBJ_INTERRUPT
);
543 /* Print the header. */
545 gcvLEVEL_VERBOSE
, gcvZONE_COMMAND
,
546 "%s: INTERRUPT OBJECT STATUS\n",
550 /* Print statistics. */
551 #if gcmENABLE_INTERRUPT_STATISTICS
553 gcvLEVEL_VERBOSE
, gcvZONE_COMMAND
,
554 " Maximum number of FIFO items accumulated at a single time: %d\n",
555 Interrupt
->maxFifoItems
559 gcvLEVEL_VERBOSE
, gcvZONE_COMMAND
,
560 " Interrupt FIFO overflow happened times: %d\n",
561 Interrupt
->fifoOverflow
565 gcvLEVEL_VERBOSE
, gcvZONE_COMMAND
,
566 " Maximum number of interrupts simultaneously generated: %d\n",
567 Interrupt
->maxSimultaneous
571 gcvLEVEL_VERBOSE
, gcvZONE_COMMAND
,
572 " Number of times when there were multiple interrupts generated: %d\n",
573 Interrupt
->multipleCount
578 gcvLEVEL_VERBOSE
, gcvZONE_COMMAND
,
579 " The current number of entries in the FIFO: %d\n",
583 /* Print the FIFO contents. */
584 if (Interrupt
->fifoItems
!= 0)
590 gcvLEVEL_VERBOSE
, gcvZONE_COMMAND
,
591 " FIFO current contents:\n"
594 /* Get the current pointers. */
595 index
= Interrupt
->tail
;
596 last
= Interrupt
->head
;
598 while (index
!= last
)
600 /* Advance to the next entry. */
604 gcvLEVEL_VERBOSE
, gcvZONE_COMMAND
,
606 index
, Interrupt
->fifo
[index
]
618 /*******************************************************************************
620 ** gckVGINTERRUPT_Enable
622 ** Enable the specified interrupt.
627 ** Pointer to a gckVGINTERRUPT object.
630 ** Pointer to the variable that holds the interrupt number to be
631 ** registered in range 0..31.
632 ** If the value is less then 0, gckVGINTERRUPT_Enable will attempt
633 ** to find an unused interrupt. If such interrupt is found, the number
634 ** will be assigned to the variable if the functuion call succeedes.
637 ** Pointer to the handler to register for the interrupt.
645 gckVGINTERRUPT_Enable(
646 IN gckVGINTERRUPT Interrupt
,
647 IN OUT gctINT32_PTR Id
,
648 IN gctINTERRUPT_HANDLER Handler
654 gcmkHEADER_ARG("Interrupt=0x%x Id=0x%x Handler=0x%x", Interrupt
, Id
, Handler
);
656 /* Verify the arguments. */
657 gcmkVERIFY_OBJECT(Interrupt
, gcvOBJ_INTERRUPT
);
658 gcmkVERIFY_ARGUMENT(Id
!= gcvNULL
);
659 gcmkVERIFY_ARGUMENT(Handler
!= gcvNULL
);
663 /* See if we need to allocate an ID. */
666 /* Find the first unused interrupt handler. */
667 for (i
= 0; i
< gcmCOUNTOF(Interrupt
->handlers
); ++i
)
669 if (Interrupt
->handlers
[i
] == gcvNULL
)
675 /* No unused innterrupts? */
676 if (i
== gcmCOUNTOF(Interrupt
->handlers
))
678 status
= gcvSTATUS_OUT_OF_RESOURCES
;
682 /* Update the interrupt ID. */
686 /* Make sure the ID is in range. */
687 else if (*Id
>= gcmCOUNTOF(Interrupt
->handlers
))
689 status
= gcvSTATUS_INVALID_ARGUMENT
;
693 /* Set interrupt handler. */
694 Interrupt
->handlers
[*Id
] = Handler
;
697 status
= gcvSTATUS_OK
;
702 /* Return the status. */
707 /*******************************************************************************
709 ** gckVGINTERRUPT_Disable
711 ** Disable the specified interrupt.
716 ** Pointer to a gckVGINTERRUPT object.
719 ** Interrupt number to be disabled in range 0..31.
727 gckVGINTERRUPT_Disable(
728 IN gckVGINTERRUPT Interrupt
,
732 gcmkHEADER_ARG("Interrupt=0x%x Id=0x%x", Interrupt
, Id
);
733 /* Verify the arguments. */
734 gcmkVERIFY_OBJECT(Interrupt
, gcvOBJ_INTERRUPT
);
735 gcmkVERIFY_ARGUMENT((Id
>= 0) && (Id
< gcmCOUNTOF(Interrupt
->handlers
)));
737 /* Reset interrupt handler. */
738 Interrupt
->handlers
[Id
] = gcvNULL
;
746 /*******************************************************************************
748 ** gckVGINTERRUPT_Enque
750 ** Read the interrupt status register and put the value in the interrupt FIFO.
755 ** Pointer to a gckVGINTERRUPT object.
763 gckVGINTERRUPT_Enque(
764 IN gckVGINTERRUPT Interrupt
770 gcmkHEADER_ARG("Interrupt=0x%x", Interrupt
);
772 /* Verify the arguments. */
773 gcmkVERIFY_OBJECT(Interrupt
, gcvOBJ_INTERRUPT
);
777 /* Read interrupt status register. */
778 gcmkERR_BREAK(gckVGHARDWARE_ReadInterrupt(
779 Interrupt
->kernel
->hardware
, &triggered
782 /* No interrupts to process? */
785 status
= gcvSTATUS_NOT_OUR_INTERRUPT
;
790 if (Interrupt
->fifoItems
== gcmCOUNTOF(Interrupt
->fifo
))
792 #if gcmENABLE_INTERRUPT_STATISTICS
793 Interrupt
->fifoOverflow
+= 1;
796 /* OR the interrupt with the last value in the FIFO. */
797 Interrupt
->fifo
[Interrupt
->head
] |= triggered
;
799 /* Success (kind of). */
800 status
= gcvSTATUS_OK
;
804 /* Advance to the next entry. */
805 Interrupt
->head
+= 1;
806 Interrupt
->fifoItems
+= 1;
808 #if gcmENABLE_INTERRUPT_STATISTICS
809 if (Interrupt
->fifoItems
> Interrupt
->maxFifoItems
)
811 Interrupt
->maxFifoItems
= Interrupt
->fifoItems
;
815 /* Set the new value. */
816 Interrupt
->fifo
[Interrupt
->head
] = triggered
;
818 /* Increment the FIFO semaphore. */
819 gcmkERR_BREAK(gckOS_IncrementSemaphore(
820 Interrupt
->os
, Interrupt
->fifoValid
823 /* Windows kills our threads prematurely when the application
824 exists. Verify here that the thread is still alive. */
825 status
= gckOS_VerifyThread(Interrupt
->os
, Interrupt
->handler
);
827 /* Has the thread been prematurely terminated? */
828 if (status
!= gcvSTATUS_OK
)
830 /* Process all accumulated interrupts. */
831 while (Interrupt
->head
!= Interrupt
->tail
)
833 #if gcmENABLE_INTERRUPT_STATISTICS
834 /* Process the interrupt. */
835 _ProcessInterrupt(Interrupt
, gcvNULL
);
837 /* Process the interrupt. */
838 _ProcessInterrupt(Interrupt
);
843 status
= gcvSTATUS_OK
;
854 #endif /* gcdENABLE_VG */