/*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::
::   Module      :   OS Interrupt Header
::   Copyright   :   (C)2003-2009 Woodward
::   Platform(s) :   MPC5xx
::   Limitations :   MPC5xx OS
::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/

#ifndef __INTERUPT_H
#define __INTERUPT_H

/*----- INCLUDES ------------------------------------------------------------------------------------------*/
#include <TypeDefn.h>

/*----- DEFINES -------------------------------------------------------------------------------------------*/

/*----- TYPEDEFS ------------------------------------------------------------------------------------------*/

/*----- EXTERNALS -----------------------------------------------------------------------------------------*/

/*----- PROTOTYPES ----------------------------------------------------------------------------------------*/
#ifdef __ghs
/*********************************************************************
* asm void UNDISABLE(void)
* asm void DISABLE(void)
*
* Use SPR275 as a counter which records the number of times a
* DISABLE/UNDISABLE sequence has been called. Thus if DISABLE is
* called in a re-entrant module the UNDISABLE function will be able
* to unwind itself before finally re-enabling interrupts.
*
* PSEUSO CODE for DISABLE
* SPR275 += 1;
* Disable Interrupts by writing anything to SPR81
*
* PSEUSO CODE for UNDISABLE
* if (SPR275 != 0)
* {
*    SPR275 -= 1;
*    if (SPR275 == 0)
*    {
*       Re-enable interrupts by writing anything SPR80
*    }
* }
*
* ASSUMPTIONS
* Assumes that the setup code within the OSInit has zeroed SPR275
*
*********************************************************************/
asm void DISABLE(void)
{
%
    mtspr   81,r4
    mfspr   r4,275
    addi    r4,r4,1
    mtspr   275,r4
}

asm void UNDISABLE(void)
{
%lab end_undisable

    mfspr   r4,275
    cmplwi  r4,0
    beq     end_undisable
    addi    r4,r4,-1
    mtspr   275,r4
    cmplwi  r4,0
    bne     end_undisable
    mtspr   80,r4
end_undisable:
}

/* Function to set SPR275 for DISABLE/UNDISABLE use */
asm void SetupEnableDisable(void)
{
%
    li      r0,1
    mtspr   275,r0
}

asm uint4 SetupProcessState(void)
{
%
    li      r3,0
    mtspr   274, r3
}
#elif defined (__GNUC__)
static inline void DISABLE(void)
{
    asm volatile ("mtspr   81,r0;"  /* EID */
                  "mfspr   r3, 275;"
                  "addi    r3, r3,1;"
                  "mtspr   275,r3;"
                  : /* no outputs */
                  : /* no imputs */
                  : /* clobbered */ "r3");
}

static inline void UNDISABLE(void)
{
	uint4 cnt;
	
	/* Get the contents of SPR275 */
    asm volatile ("mfspr %0, 275" : "=r" (cnt));
	if (cnt)
	{
		--cnt;
		if (cnt == 0)
		{
			asm volatile ("mtspr 80, r0"); /* EIE */
		}
		/* Update SPR275 with the new count */
		asm volatile ("mtspr 275, %0" : : "r" (cnt));
	}		
}

static inline void SetupEnableDisable(void)
{
    asm volatile ("li      r3,1;"
                  "mtspr   275,r3;"
                  : /* no outputs */
                  : /* no imputs */
                  : /* clobbered */ "r3");
}

static inline void SetupProcessState(void)
{
    asm volatile ("li      r3,0;"
                  "mtspr   274,r3;"
                  : /* no outputs */
                  : /* no imputs */
                  : /* clobbered */ "r0");
}

#else
    #error Interrupt funcs need to be defined for the appropriate compiler
#endif
      

/* Returns zero if the current Process is executing from the Background or Idle */
uint4 GetProcessState(void);

#endif /* __INTERUPT_H */

/*----- END OF FILE ---------------------------------------------------------------------------------------*/
