1 /****************************************************************************
3 * Copyright (C) 2005 - 2014 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 *****************************************************************************/
22 #include "gc_hal_kernel_precomp.h"
26 /******************************************************************************\
27 *********************** Support Functions and Definitions **********************
28 \******************************************************************************/
30 /* Interruot statistics will be accumulated if not zero. */
31 #define gcmENABLE_INTERRUPT_STATISTICS 0
33 #define _GC_OBJ_ZONE gcvZONE_INTERRUPT
35 /* Object structure. */
36 struct _gckVGINTERRUPT
41 /* gckVGKERNEL pointer. */
47 /* Interrupt handlers. */
48 gctINTERRUPT_HANDLER handlers[32];
50 /* Main interrupt handler thread. */
55 gctSEMAPHORE fifoValid;
61 /* Interrupt statistics. */
62 #if gcmENABLE_INTERRUPT_STATISTICS
65 gctUINT maxSimultaneous;
66 gctUINT multipleCount;
71 /*******************************************************************************
75 ** The interrupt processor.
80 ** Pointer to the gckVGINTERRUPT object.
87 #if gcmENABLE_INTERRUPT_STATISTICS
90 gckVGINTERRUPT Interrupt,
91 gctUINT_PTR TriggeredCount
96 gckVGINTERRUPT Interrupt
104 /* Advance to the next entry. */
105 Interrupt->tail += 1;
106 Interrupt->fifoItems -= 1;
108 /* Get the interrupt value. */
109 triggered = Interrupt->fifo[Interrupt->tail];
110 gcmkASSERT(triggered != 0);
113 gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
114 "%s: triggered=0x%08X\n",
119 /* Walk through all possible interrupts. */
120 for (i = 0; i < gcmSIZEOF(Interrupt->handlers); i += 1)
122 /* Test if interrupt happened. */
123 if ((triggered & 1) == 1)
125 #if gcmENABLE_INTERRUPT_STATISTICS
126 if (TriggeredCount != gcvNULL)
128 (* TriggeredCount) += 1;
132 /* Make sure we have valid handler. */
133 if (Interrupt->handlers[i] == gcvNULL)
137 "%s: Interrupt %d isn't registered.\n",
144 gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
145 "%s: interrupt=%d\n",
150 /* Call the handler. */
151 status = Interrupt->handlers[i] (Interrupt->kernel);
153 if (gcmkIS_ERROR(status))
155 /* Failed to signal the semaphore. */
158 "%s: Error %d incrementing the semaphore #%d.\n",
159 __FUNCTION__, status, i
165 /* Next interrupt. */
168 /* No more interrupts to handle? */
177 /*******************************************************************************
179 ** _MainInterruptHandler
181 ** The main interrupt thread serves the interrupt FIFO and calls registered
182 ** handlers for the interrupts that occured. The handlers are called in the
183 ** sequence interrupts occured with the exception when multiple interrupts
184 ** occured at the same time. In that case the handler calls are "sorted" by
185 ** the interrupt number therefore giving the interrupts with lower numbers
191 ** Pointer to the gckVGINTERRUPT object.
198 static gctTHREADFUNCRESULT gctTHREADFUNCTYPE
199 _MainInterruptHandler(
200 gctTHREADFUNCPARAMETER ThreadParameter
204 gckVGINTERRUPT interrupt;
206 #if gcmENABLE_INTERRUPT_STATISTICS
210 /* Cast the object. */
211 interrupt = (gckVGINTERRUPT) ThreadParameter;
213 /* Enter the loop. */
216 /* Wait for an interrupt. */
217 status = gckOS_DecrementSemaphore(interrupt->os, interrupt->fifoValid);
220 if (gcmkIS_ERROR(status))
225 /* System termination request? */
226 if (status == gcvSTATUS_TERMINATE)
231 /* Driver is shutting down? */
232 if (interrupt->terminate)
237 #if gcmENABLE_INTERRUPT_STATISTICS
238 /* Reset triggered count. */
241 /* Process the interrupt. */
242 _ProcessInterrupt(interrupt, &count);
244 /* Update conters. */
245 if (count > interrupt->maxSimultaneous)
247 interrupt->maxSimultaneous = count;
252 interrupt->multipleCount += 1;
255 /* Process the interrupt. */
256 _ProcessInterrupt(interrupt);
264 /*******************************************************************************
266 ** _StartInterruptHandler / _StopInterruptHandler
268 ** Main interrupt handler routine control.
273 ** Pointer to the gckVGINTERRUPT object.
281 _StartInterruptHandler(
282 gckVGINTERRUPT Interrupt
285 gceSTATUS status, last;
289 /* Objects must not be already created. */
290 gcmkASSERT(Interrupt->fifoValid == gcvNULL);
291 gcmkASSERT(Interrupt->handler == gcvNULL);
293 /* Reset the termination request. */
294 Interrupt->terminate = gcvFALSE;
296 #if !gcdENABLE_INFINITE_SPEED_HW
297 /* Construct the fifo semaphore. */
298 gcmkERR_BREAK(gckOS_CreateSemaphoreVG(
299 Interrupt->os, &Interrupt->fifoValid
302 /* Start the interrupt handler thread. */
303 gcmkERR_BREAK(gckOS_StartThread(
305 _MainInterruptHandler,
317 if (Interrupt->fifoValid != gcvNULL)
319 gcmkCHECK_STATUS(gckOS_DestroySemaphore(
320 Interrupt->os, Interrupt->fifoValid
323 Interrupt->fifoValid = gcvNULL;
326 /* Return the status. */
331 _StopInterruptHandler(
332 gckVGINTERRUPT Interrupt
339 /* Does the thread exist? */
340 if (Interrupt->handler == gcvNULL)
342 /* The semaphore must be NULL as well. */
343 gcmkASSERT(Interrupt->fifoValid == gcvNULL);
346 status = gcvSTATUS_OK;
350 /* The semaphore must exist as well. */
351 gcmkASSERT(Interrupt->fifoValid != gcvNULL);
353 /* Set the termination request. */
354 Interrupt->terminate = gcvTRUE;
356 /* Unlock the thread. */
357 gcmkERR_BREAK(gckOS_IncrementSemaphore(
358 Interrupt->os, Interrupt->fifoValid
361 /* Wait until the thread quits. */
362 gcmkERR_BREAK(gckOS_StopThread(
367 /* Destroy the semaphore. */
368 gcmkERR_BREAK(gckOS_DestroySemaphore(
369 Interrupt->os, Interrupt->fifoValid
373 Interrupt->handler = gcvNULL;
374 Interrupt->fifoValid = gcvNULL;
378 /* Return the status. */
383 /******************************************************************************\
384 ***************************** Interrupt Object API *****************************
385 \******************************************************************************/
387 /*******************************************************************************
389 ** gckVGINTERRUPT_Construct
391 ** Construct an interrupt object.
396 ** Pointer to the gckVGKERNEL object.
401 ** Pointer to the new gckVGINTERRUPT object.
405 gckVGINTERRUPT_Construct(
406 IN gckVGKERNEL Kernel,
407 OUT gckVGINTERRUPT * Interrupt
411 gckVGINTERRUPT interrupt = gcvNULL;
413 gcmkHEADER_ARG("Kernel=0x%x Interrupt=0x%x", Kernel, Interrupt);
415 /* Verify argeuments. */
416 gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
417 gcmkVERIFY_ARGUMENT(Interrupt != gcvNULL);
421 /* Allocate the gckVGINTERRUPT structure. */
422 gcmkERR_BREAK(gckOS_Allocate(
424 gcmSIZEOF(struct _gckVGINTERRUPT),
425 (gctPOINTER *) &interrupt
428 /* Reset the object data. */
429 gcmkVERIFY_OK(gckOS_ZeroMemory(
430 interrupt, gcmSIZEOF(struct _gckVGINTERRUPT)
433 /* Initialize the object. */
434 interrupt->object.type = gcvOBJ_INTERRUPT;
436 /* Initialize the object pointers. */
437 interrupt->kernel = Kernel;
438 interrupt->os = Kernel->os;
440 /* Initialize the current FIFO position. */
441 interrupt->head = (gctUINT8)~0;
442 interrupt->tail = (gctUINT8)~0;
444 /* Start the thread. */
445 gcmkERR_BREAK(_StartInterruptHandler(interrupt));
447 /* Return interrupt object. */
448 *Interrupt = interrupt;
450 gcmkFOOTER_ARG("*Interrup=0x%x", *Interrupt);
457 if (interrupt != gcvNULL)
459 /* Free the gckVGINTERRUPT structure. */
460 gcmkVERIFY_OK(gckOS_Free(interrupt->os, interrupt));
465 /* Return the status. */
470 /*******************************************************************************
472 ** gckVGINTERRUPT_Destroy
474 ** Destroy an interrupt object.
479 ** Pointer to the gckVGINTERRUPT object to destroy.
487 gckVGINTERRUPT_Destroy(
488 IN gckVGINTERRUPT Interrupt
493 gcmkHEADER_ARG("Interrupt=0x%x", Interrupt);
495 /* Verify the arguments. */
496 gcmkVERIFY_OBJECT(Interrupt, gcvOBJ_INTERRUPT);
500 /* Stop the interrupt thread. */
501 gcmkERR_BREAK(_StopInterruptHandler(Interrupt));
503 /* Mark the object as unknown. */
504 Interrupt->object.type = gcvOBJ_UNKNOWN;
506 /* Free the gckVGINTERRUPT structure. */
507 gcmkERR_BREAK(gckOS_Free(Interrupt->os, Interrupt));
513 /* Return the status. */
518 /*******************************************************************************
520 ** gckVGINTERRUPT_DumpState
522 ** Print the current state of the interrupt manager.
527 ** Pointer to a gckVGINTERRUPT object.
536 gckVGINTERRUPT_DumpState(
537 IN gckVGINTERRUPT Interrupt
540 gcmkHEADER_ARG("Interrupt=0x%x", Interrupt);
541 /* Verify the arguments. */
542 gcmkVERIFY_OBJECT(Interrupt, gcvOBJ_INTERRUPT);
544 /* Print the header. */
546 gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
547 "%s: INTERRUPT OBJECT STATUS\n",
551 /* Print statistics. */
552 #if gcmENABLE_INTERRUPT_STATISTICS
554 gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
555 " Maximum number of FIFO items accumulated at a single time: %d\n",
556 Interrupt->maxFifoItems
560 gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
561 " Interrupt FIFO overflow happened times: %d\n",
562 Interrupt->fifoOverflow
566 gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
567 " Maximum number of interrupts simultaneously generated: %d\n",
568 Interrupt->maxSimultaneous
572 gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
573 " Number of times when there were multiple interrupts generated: %d\n",
574 Interrupt->multipleCount
579 gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
580 " The current number of entries in the FIFO: %d\n",
584 /* Print the FIFO contents. */
585 if (Interrupt->fifoItems != 0)
591 gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
592 " FIFO current contents:\n"
595 /* Get the current pointers. */
596 index = Interrupt->tail;
597 last = Interrupt->head;
599 while (index != last)
601 /* Advance to the next entry. */
605 gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
607 index, Interrupt->fifo[index]
619 /*******************************************************************************
621 ** gckVGINTERRUPT_Enable
623 ** Enable the specified interrupt.
628 ** Pointer to a gckVGINTERRUPT object.
631 ** Pointer to the variable that holds the interrupt number to be
632 ** registered in range 0..31.
633 ** If the value is less then 0, gckVGINTERRUPT_Enable will attempt
634 ** to find an unused interrupt. If such interrupt is found, the number
635 ** will be assigned to the variable if the functuion call succeedes.
638 ** Pointer to the handler to register for the interrupt.
646 gckVGINTERRUPT_Enable(
647 IN gckVGINTERRUPT Interrupt,
648 IN OUT gctINT32_PTR Id,
649 IN gctINTERRUPT_HANDLER Handler
655 gcmkHEADER_ARG("Interrupt=0x%x Id=0x%x Handler=0x%x", Interrupt, Id, Handler);
657 /* Verify the arguments. */
658 gcmkVERIFY_OBJECT(Interrupt, gcvOBJ_INTERRUPT);
659 gcmkVERIFY_ARGUMENT(Id != gcvNULL);
660 gcmkVERIFY_ARGUMENT(Handler != gcvNULL);
664 /* See if we need to allocate an ID. */
667 /* Find the first unused interrupt handler. */
668 for (i = 0; i < gcmCOUNTOF(Interrupt->handlers); ++i)
670 if (Interrupt->handlers[i] == gcvNULL)
676 /* No unused innterrupts? */
677 if (i == gcmCOUNTOF(Interrupt->handlers))
679 status = gcvSTATUS_OUT_OF_RESOURCES;
683 /* Update the interrupt ID. */
687 /* Make sure the ID is in range. */
688 else if (*Id >= gcmCOUNTOF(Interrupt->handlers))
690 status = gcvSTATUS_INVALID_ARGUMENT;
694 /* Set interrupt handler. */
695 Interrupt->handlers[*Id] = Handler;
698 status = gcvSTATUS_OK;
703 /* Return the status. */
708 /*******************************************************************************
710 ** gckVGINTERRUPT_Disable
712 ** Disable the specified interrupt.
717 ** Pointer to a gckVGINTERRUPT object.
720 ** Interrupt number to be disabled in range 0..31.
728 gckVGINTERRUPT_Disable(
729 IN gckVGINTERRUPT Interrupt,
733 gcmkHEADER_ARG("Interrupt=0x%x Id=0x%x", Interrupt, Id);
734 /* Verify the arguments. */
735 gcmkVERIFY_OBJECT(Interrupt, gcvOBJ_INTERRUPT);
736 gcmkVERIFY_ARGUMENT((Id >= 0) && (Id < gcmCOUNTOF(Interrupt->handlers)));
738 /* Reset interrupt handler. */
739 Interrupt->handlers[Id] = gcvNULL;
747 /*******************************************************************************
749 ** gckVGINTERRUPT_Enque
751 ** Read the interrupt status register and put the value in the interrupt FIFO.
756 ** Pointer to a gckVGINTERRUPT object.
765 gckVGINTERRUPT_Enque(
766 IN gckVGINTERRUPT Interrupt
770 gckVGINTERRUPT_Enque(
771 IN gckVGINTERRUPT Interrupt,
773 OUT gctSEMAPHORE *Semaphore
780 gcmkHEADER_ARG("Interrupt=0x%x", Interrupt);
782 /* Verify the arguments. */
783 gcmkVERIFY_OBJECT(Interrupt, gcvOBJ_INTERRUPT);
787 *Semaphore = gcvNULL;
792 /* Read interrupt status register. */
793 gcmkERR_BREAK(gckVGHARDWARE_ReadInterrupt(
794 Interrupt->kernel->hardware, &triggered
797 /* Mask out TS overflow interrupt */
798 triggered &= 0xfffffffe;
800 /* No interrupts to process? */
803 status = gcvSTATUS_NOT_OUR_INTERRUPT;
808 if (Interrupt->fifoItems == gcmCOUNTOF(Interrupt->fifo))
810 #if gcmENABLE_INTERRUPT_STATISTICS
811 Interrupt->fifoOverflow += 1;
814 /* OR the interrupt with the last value in the FIFO. */
815 Interrupt->fifo[Interrupt->head] |= triggered;
817 /* Success (kind of). */
818 status = gcvSTATUS_OK;
822 /* Advance to the next entry. */
823 Interrupt->head += 1;
824 Interrupt->fifoItems += 1;
826 #if gcmENABLE_INTERRUPT_STATISTICS
827 if (Interrupt->fifoItems > Interrupt->maxFifoItems)
829 Interrupt->maxFifoItems = Interrupt->fifoItems;
833 /* Set the new value. */
834 Interrupt->fifo[Interrupt->head] = triggered;
837 /* Increment the FIFO semaphore. */
838 gcmkERR_BREAK(gckOS_IncrementSemaphore(
839 Interrupt->os, Interrupt->fifoValid
843 *Semaphore = Interrupt->fifoValid;
846 /* Windows kills our threads prematurely when the application
847 exists. Verify here that the thread is still alive. */
848 status = gckOS_VerifyThread(Interrupt->os, Interrupt->handler);
850 /* Has the thread been prematurely terminated? */
851 if (status != gcvSTATUS_OK)
853 /* Process all accumulated interrupts. */
854 while (Interrupt->head != Interrupt->tail)
856 #if gcmENABLE_INTERRUPT_STATISTICS
857 /* Process the interrupt. */
858 _ProcessInterrupt(Interrupt, gcvNULL);
860 /* Process the interrupt. */
861 _ProcessInterrupt(Interrupt);
866 status = gcvSTATUS_OK;
877 #endif /* gcdENABLE_VG */