ENGR00156850 gpu-viv: add gpu-viv driver source
[wandboard.git] / drivers / mxc / gpu-viv / hal / kernel / gc_hal_kernel_interrupt_vg.c
blobcbc921a713b0a00b4e6ba72fa4dc9cbf6cb5aae3
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"
26 #if gcdENABLE_VG
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
40 /* Object. */
41 gcsOBJECT object;
43 /* gckVGKERNEL pointer. */
44 gckVGKERNEL kernel;
46 /* gckOS pointer. */
47 gckOS os;
49 /* Interrupt handlers. */
50 gctINTERRUPT_HANDLER handlers[32];
52 /* Main interrupt handler thread. */
53 gctTHREAD handler;
54 gctBOOL terminate;
56 /* Interrupt FIFO. */
57 gctSEMAPHORE fifoValid;
58 gctUINT32 fifo[256];
59 gctUINT fifoItems;
60 gctUINT8 head;
61 gctUINT8 tail;
63 /* Interrupt statistics. */
64 #if gcmENABLE_INTERRUPT_STATISTICS
65 gctUINT maxFifoItems;
66 gctUINT fifoOverflow;
67 gctUINT maxSimultaneous;
68 gctUINT multipleCount;
69 #endif
73 /*******************************************************************************
75 ** _ProcessInterrupt
77 ** The interrupt processor.
79 ** INPUT:
81 ** ThreadParameter
82 ** Pointer to the gckVGINTERRUPT object.
84 ** OUTPUT:
86 ** Nothing.
89 #if gcmENABLE_INTERRUPT_STATISTICS
90 static void
91 _ProcessInterrupt(
92 gckVGINTERRUPT Interrupt,
93 gctUINT_PTR TriggeredCount
95 #else
96 static void
97 _ProcessInterrupt(
98 gckVGINTERRUPT Interrupt
100 #endif
102 gceSTATUS status;
103 gctUINT32 triggered;
104 gctUINT i;
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);
114 gcmkTRACE_ZONE(
115 gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
116 "%s: triggered=0x%08X\n",
117 __FUNCTION__,
118 triggered
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;
132 #endif
134 /* Make sure we have valid handler. */
135 if (Interrupt->handlers[i] == gcvNULL)
137 gcmkTRACE(
138 gcvLEVEL_ERROR,
139 "%s: Interrupt %d isn't registered.\n",
140 __FUNCTION__, i
143 else
145 gcmkTRACE_ZONE(
146 gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
147 "%s: interrupt=%d\n",
148 __FUNCTION__,
152 /* Call the handler. */
153 status = Interrupt->handlers[i] (Interrupt->kernel);
155 if (gcmkIS_ERROR(status))
157 /* Failed to signal the semaphore. */
158 gcmkTRACE(
159 gcvLEVEL_ERROR,
160 "%s: Error %d incrementing the semaphore #%d.\n",
161 __FUNCTION__, status, i
167 /* Next interrupt. */
168 triggered >>= 1;
170 /* No more interrupts to handle? */
171 if (triggered == 0)
173 break;
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
188 ** higher priority.
190 ** INPUT:
192 ** ThreadParameter
193 ** Pointer to the gckVGINTERRUPT object.
195 ** OUTPUT:
197 ** Nothing.
200 static gctTHREADFUNCRESULT gctTHREADFUNCTYPE
201 _MainInterruptHandler(
202 gctTHREADFUNCPARAMETER ThreadParameter
205 gceSTATUS status;
206 gckVGINTERRUPT interrupt;
208 #if gcmENABLE_INTERRUPT_STATISTICS
209 gctUINT count;
210 #endif
212 /* Cast the object. */
213 interrupt = (gckVGINTERRUPT) ThreadParameter;
215 /* Enter the loop. */
216 while (gcvTRUE)
218 /* Wait for an interrupt. */
219 status = gckOS_DecrementSemaphore(interrupt->os, interrupt->fifoValid);
221 /* Error? */
222 if (gcmkIS_ERROR(status))
224 break;
227 /* System termination request? */
228 if (status == gcvSTATUS_TERMINATE)
230 break;
233 /* Driver is shutting down? */
234 if (interrupt->terminate)
236 break;
239 #if gcmENABLE_INTERRUPT_STATISTICS
240 /* Reset triggered count. */
241 count = 0;
243 /* Process the interrupt. */
244 _ProcessInterrupt(interrupt, &count);
246 /* Update conters. */
247 if (count > interrupt->maxSimultaneous)
249 interrupt->maxSimultaneous = count;
252 if (count > 1)
254 interrupt->multipleCount += 1;
256 #else
257 /* Process the interrupt. */
258 _ProcessInterrupt(interrupt);
259 #endif
262 return 0;
266 /*******************************************************************************
268 ** _StartInterruptHandler / _StopInterruptHandler
270 ** Main interrupt handler routine control.
272 ** INPUT:
274 ** ThreadParameter
275 ** Pointer to the gckVGINTERRUPT object.
277 ** OUTPUT:
279 ** Nothing.
282 static gceSTATUS
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(
306 Interrupt->os,
307 _MainInterruptHandler,
308 Interrupt,
309 &Interrupt->handler
311 #endif
313 /* Success. */
314 return gcvSTATUS_OK;
316 while (gcvFALSE);
318 /* Roll back. */
319 if (Interrupt->fifoValid != gcvNULL)
321 gcmkCHECK_STATUS(gckOS_DestroySemaphore(
322 Interrupt->os, Interrupt->fifoValid
325 Interrupt->fifoValid = gcvNULL;
328 /* Return the status. */
329 return status;
332 static gceSTATUS
333 _StopInterruptHandler(
334 gckVGINTERRUPT Interrupt
337 gceSTATUS status;
341 /* Does the thread exist? */
342 if (Interrupt->handler == gcvNULL)
344 /* The semaphore must be NULL as well. */
345 gcmkASSERT(Interrupt->fifoValid == gcvNULL);
347 /* Success. */
348 status = gcvSTATUS_OK;
349 break;
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(
365 Interrupt->os,
366 Interrupt->handler
369 /* Destroy the semaphore. */
370 gcmkERR_BREAK(gckOS_DestroySemaphore(
371 Interrupt->os, Interrupt->fifoValid
374 /* Reset handles. */
375 Interrupt->handler = gcvNULL;
376 Interrupt->fifoValid = gcvNULL;
378 while (gcvFALSE);
380 /* Return the status. */
381 return status;
385 /******************************************************************************\
386 ***************************** Interrupt Object API *****************************
387 \******************************************************************************/
389 /*******************************************************************************
391 ** gckVGINTERRUPT_Construct
393 ** Construct an interrupt object.
395 ** INPUT:
397 ** Kernel
398 ** Pointer to the gckVGKERNEL object.
400 ** OUTPUT:
402 ** Interrupt
403 ** Pointer to the new gckVGINTERRUPT object.
406 gceSTATUS
407 gckVGINTERRUPT_Construct(
408 IN gckVGKERNEL Kernel,
409 OUT gckVGINTERRUPT * Interrupt
412 gceSTATUS status;
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(
425 Kernel->os,
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;
452 /* Success. */
453 return gcvSTATUS_OK;
455 while (gcvFALSE);
457 /* Roll back. */
458 if (interrupt != gcvNULL)
460 /* Free the gckVGINTERRUPT structure. */
461 gcmkVERIFY_OK(gckOS_Free(interrupt->os, interrupt));
464 /* Return the status. */
465 return status;
469 /*******************************************************************************
471 ** gckVGINTERRUPT_Destroy
473 ** Destroy an interrupt object.
475 ** INPUT:
477 ** Interrupt
478 ** Pointer to the gckVGINTERRUPT object to destroy.
480 ** OUTPUT:
482 ** Nothing.
485 gceSTATUS
486 gckVGINTERRUPT_Destroy(
487 IN gckVGINTERRUPT Interrupt
490 gceSTATUS status;
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));
508 while (gcvFALSE);
510 gcmkFOOTER();
512 /* Return the status. */
513 return status;
517 /*******************************************************************************
519 ** gckVGINTERRUPT_DumpState
521 ** Print the current state of the interrupt manager.
523 ** INPUT:
525 ** Interrupt
526 ** Pointer to a gckVGINTERRUPT object.
528 ** OUTPUT:
530 ** Nothing.
533 #if gcvDEBUG
534 gceSTATUS
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. */
544 gcmkTRACE_ZONE(
545 gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
546 "%s: INTERRUPT OBJECT STATUS\n",
547 __FUNCTION__
550 /* Print statistics. */
551 #if gcmENABLE_INTERRUPT_STATISTICS
552 gcmkTRACE_ZONE(
553 gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
554 " Maximum number of FIFO items accumulated at a single time: %d\n",
555 Interrupt->maxFifoItems
558 gcmkTRACE_ZONE(
559 gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
560 " Interrupt FIFO overflow happened times: %d\n",
561 Interrupt->fifoOverflow
564 gcmkTRACE_ZONE(
565 gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
566 " Maximum number of interrupts simultaneously generated: %d\n",
567 Interrupt->maxSimultaneous
570 gcmkTRACE_ZONE(
571 gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
572 " Number of times when there were multiple interrupts generated: %d\n",
573 Interrupt->multipleCount
575 #endif
577 gcmkTRACE_ZONE(
578 gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
579 " The current number of entries in the FIFO: %d\n",
580 Interrupt->fifoItems
583 /* Print the FIFO contents. */
584 if (Interrupt->fifoItems != 0)
586 gctUINT8 index;
587 gctUINT8 last;
589 gcmkTRACE_ZONE(
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. */
601 index += 1;
603 gcmkTRACE_ZONE(
604 gcvLEVEL_VERBOSE, gcvZONE_COMMAND,
605 " %d: 0x%08X\n",
606 index, Interrupt->fifo[index]
611 gcmkFOOTER_NO();
612 /* Success. */
613 return gcvSTATUS_OK;
615 #endif
618 /*******************************************************************************
620 ** gckVGINTERRUPT_Enable
622 ** Enable the specified interrupt.
624 ** INPUT:
626 ** Interrupt
627 ** Pointer to a gckVGINTERRUPT object.
629 ** Id
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.
636 ** Handler
637 ** Pointer to the handler to register for the interrupt.
639 ** OUTPUT:
641 ** Nothing.
644 gceSTATUS
645 gckVGINTERRUPT_Enable(
646 IN gckVGINTERRUPT Interrupt,
647 IN OUT gctINT32_PTR Id,
648 IN gctINTERRUPT_HANDLER Handler
651 gceSTATUS status;
652 gctINT32 i;
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. */
664 if (*Id < 0)
666 /* Find the first unused interrupt handler. */
667 for (i = 0; i < gcmCOUNTOF(Interrupt->handlers); ++i)
669 if (Interrupt->handlers[i] == gcvNULL)
671 break;
675 /* No unused innterrupts? */
676 if (i == gcmCOUNTOF(Interrupt->handlers))
678 status = gcvSTATUS_OUT_OF_RESOURCES;
679 break;
682 /* Update the interrupt ID. */
683 *Id = i;
686 /* Make sure the ID is in range. */
687 else if (*Id >= gcmCOUNTOF(Interrupt->handlers))
689 status = gcvSTATUS_INVALID_ARGUMENT;
690 break;
693 /* Set interrupt handler. */
694 Interrupt->handlers[*Id] = Handler;
696 /* Success. */
697 status = gcvSTATUS_OK;
699 while (gcvFALSE);
701 gcmkFOOTER();
702 /* Return the status. */
703 return status;
707 /*******************************************************************************
709 ** gckVGINTERRUPT_Disable
711 ** Disable the specified interrupt.
713 ** INPUT:
715 ** Interrupt
716 ** Pointer to a gckVGINTERRUPT object.
718 ** Id
719 ** Interrupt number to be disabled in range 0..31.
721 ** OUTPUT:
723 ** Nothing.
726 gceSTATUS
727 gckVGINTERRUPT_Disable(
728 IN gckVGINTERRUPT Interrupt,
729 IN gctINT32 Id
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;
740 gcmkFOOTER_NO();
741 /* Success. */
742 return gcvSTATUS_OK;
746 /*******************************************************************************
748 ** gckVGINTERRUPT_Enque
750 ** Read the interrupt status register and put the value in the interrupt FIFO.
752 ** INPUT:
754 ** Interrupt
755 ** Pointer to a gckVGINTERRUPT object.
757 ** OUTPUT:
759 ** Nothing.
762 gceSTATUS
763 gckVGINTERRUPT_Enque(
764 IN gckVGINTERRUPT Interrupt
767 gceSTATUS status;
768 gctUINT32 triggered;
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? */
783 if (triggered == 0)
785 status = gcvSTATUS_NOT_OUR_INTERRUPT;
786 break;
789 /* FIFO overflow? */
790 if (Interrupt->fifoItems == gcmCOUNTOF(Interrupt->fifo))
792 #if gcmENABLE_INTERRUPT_STATISTICS
793 Interrupt->fifoOverflow += 1;
794 #endif
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;
802 else
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;
813 #endif
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);
836 #else
837 /* Process the interrupt. */
838 _ProcessInterrupt(Interrupt);
839 #endif
842 /* Set success. */
843 status = gcvSTATUS_OK;
847 while (gcvFALSE);
849 gcmkFOOTER();
850 /* Return status. */
851 return status;
854 #endif /* gcdENABLE_VG */