//###########################################################################
// FILE:   interrupt.c
// TITLE:  Driver for the NVIC Interrupt Controller.
//###########################################################################
// $TI Release: F28M35x Support Library v201 $
// $Release Date: Fri Jun  7 10:51:13 CDT 2013 $
//###########################################################################

//*****************************************************************************
//! \addtogroup interrupt_api
//! @{
//*****************************************************************************

#include "inc/hw_ints.h"
#include "inc/hw_nvic.h"
#include "inc/hw_types.h"
#include "driverlib/cpu.h"
#include "driverlib/debug.h"
#include "driverlib/interrupt.h"

#ifndef USE_RTOS
extern void (*g_pfnVectors[])(void);
#endif

//*****************************************************************************
// This is a mapping between priority grouping encodings and the number of
// preemption priority bits.
//*****************************************************************************
static const unsigned long g_pulPriority[] =
{
    NVIC_APINT_PRIGROUP_0_8, NVIC_APINT_PRIGROUP_1_7, NVIC_APINT_PRIGROUP_2_6,
    NVIC_APINT_PRIGROUP_3_5, NVIC_APINT_PRIGROUP_4_4, NVIC_APINT_PRIGROUP_5_3,
    NVIC_APINT_PRIGROUP_6_2, NVIC_APINT_PRIGROUP_7_1
};

//*****************************************************************************
// This is a mapping between interrupt number and the register that contains
// the priority encoding for that interrupt.
//*****************************************************************************
static const unsigned long g_pulRegs[] =
{
    0, NVIC_SYS_PRI1, NVIC_SYS_PRI2, NVIC_SYS_PRI3, NVIC_PRI0, NVIC_PRI1,
    NVIC_PRI2, NVIC_PRI3, NVIC_PRI4, NVIC_PRI5, NVIC_PRI6, NVIC_PRI7,
    NVIC_PRI8, NVIC_PRI9, NVIC_PRI10, NVIC_PRI11, NVIC_PRI12, NVIC_PRI13
};

//*****************************************************************************
//! \internal
//! The default interrupt handler.
//!
//! This is the default interrupt handler for all interrupts.  It simply loops
//! forever so that the system state is preserved for observation by a
//! debugger.  Since interrupts should be disabled before unregistering the
//! corresponding handler, this should never be called.
//!
//! \return None.
//*****************************************************************************
static void
IntDefaultHandler(void)
{
    // Go into an infinite loop.
    while(1)
    {
    }
}

//*****************************************************************************
// The processor vector table.
// This contains a list of the handlers for the various interrupt sources in
// the system.  The layout of this list is defined by the hardware; assertion
// of an interrupt causes the processor to start executing directly at the
// address given in the corresponding location in this list.
//*****************************************************************************
#ifndef USE_RTOS
#pragma DATA_ALIGN(g_pfnRAMVectors, 1024)
#pragma DATA_SECTION(g_pfnRAMVectors, ".vtable")
void (*g_pfnRAMVectors[NUM_INTERRUPTS]) (void);
#endif

//*****************************************************************************
//! Enables the processor interrupt.
//!
//! Allows the processor to respond to interrupts.  This does not affect the
//! set of interrupts enabled in the interrupt controller; it just gates the
//! single interrupt from the controller to the processor.
//!
//! \note Previously, this function had no return value.  As such, it was
//! possible to include <tt>interrupt.h</tt> and call this function without
//! having included <tt>hw_types.h</tt>.  Now that the return is a
//! <tt>tBoolean</tt>, a compiler error will occur in this case.  The solution
//! is to include <tt>hw_types.h</tt> before including <tt>interrupt.h</tt>.
//!
//! \return Returns \b true if interrupts were disabled when the function was
//! called or \b false if they were initially enabled.
//*****************************************************************************
tBoolean
IntMasterEnable(void)
{
    // Enable processor interrupts.
    return(CPUcpsie());
}

//*****************************************************************************
//! Disables the processor interrupt.
//!
//! Prevents the processor from receiving interrupts.  This does not affect the
//! set of interrupts enabled in the interrupt controller; it just gates the
//! single interrupt from the controller to the processor.
//!
//! \note Previously, this function had no return value.  As such, it was
//! possible to include <tt>interrupt.h</tt> and call this function without
//! having included <tt>hw_types.h</tt>.  Now that the return is a
//! <tt>tBoolean</tt>, a compiler error will occur in this case.  The solution
//! is to include <tt>hw_types.h</tt> before including <tt>interrupt.h</tt>.
//!
//! \return Returns \b true if interrupts were already disabled when the
//! function was called or \b false if they were initially enabled.
//*****************************************************************************
tBoolean
IntMasterDisable(void)
{
    // Disable processor interrupts.
    return(CPUcpsid());
}

//*****************************************************************************
//! Points NVIC to Vector Table in Flash
//!
//! 
//!
//! \return None.
//*****************************************************************************
void
IntFlashVTable(void)
{
#ifndef USE_RTOS
    // See if the RAM vector table has been initialized.
    if(HWREG(NVIC_VTABLE) != (unsigned long)g_pfnVectors)
    {
        // Point NVIC at the RAM vector table.
        HWREG(NVIC_VTABLE) = (unsigned long)g_pfnVectors;
    }
#else
#endif
}

//*****************************************************************************
//! Unregisters the function to be called when an interrupt occurs.
//!
//! \param ulInterrupt specifies the interrupt in question.
//!
//! This function is used to indicate that no handler should be called when the
//! given interrupt is asserted to the processor.  The interrupt source will be
//! automatically disabled (via IntDisable()) if necessary.
//!
//! \sa IntRegister() for important information about registering interrupt
//! handlers.
//!
//! \return None.
//*****************************************************************************
void
IntRAMVTable(void)
{
#ifndef USE_RTOS
    // See if the RAM vector table has been initialized.
    if(HWREG(NVIC_VTABLE) != (unsigned long)g_pfnRAMVectors)
    {
        // Point NVIC at the RAM vector table.
        HWREG(NVIC_VTABLE) = (unsigned long)g_pfnRAMVectors;
    }
#else
#endif
}


//*****************************************************************************
//
//! Registers a function to be called when an interrupt occurs.
//!
//! \param ulInterrupt specifies the interrupt in question.
//! \param pfnHandler is a pointer to the function to be called.
//!
//! This function is used to specify the handler function to be called when the
//! given interrupt is asserted to the processor.  When the interrupt occurs,
//! if it is enabled (via IntEnable()), the handler function will be called in
//! interrupt context.  Since the handler function can preempt other code, care
//! must be taken to protect memory or peripherals that are accessed by the
//! handler and other non-handler code.
//!
//! \note The use of this function (directly or indirectly via a peripheral
//! driver interrupt register function) moves the interrupt vector table from
//! flash to SRAM.  Therefore, care must be taken when linking the application
//! to ensure that the SRAM vector table is located at the beginning of SRAM;
//! otherwise NVIC will not look in the correct portion of memory for the
//! vector table (it requires the vector table be on a 1 kB memory alignment).
//! Normally, the SRAM vector table is so placed via the use of linker scripts;
//! some tool chains, such as the evaluation version of RV-MDK, do not support
//! linker scripts and therefore will not produce a valid executable.  See the
//! discussion of compile-time versus run-time interrupt handler registration
//! in the introduction to this chapter.
//!
//! \return None.
//
//*****************************************************************************
void
IntRegister(unsigned long ulInterrupt, void (*pfnHandler)(void))
{
#ifndef USE_RTOS
    unsigned long ulIdx;

    //
    // Check the arguments.
    //
    ASSERT(ulInterrupt < NUM_INTERRUPTS);

    //
    // Make sure that the RAM vector table is correctly aligned.
    //
    ASSERT(((unsigned long)g_pfnRAMVectors & 0x000003ff) == 0);

    //
    // See if the RAM vector table has been initialized.
    //
    if(HWREG(NVIC_VTABLE) != (unsigned long)g_pfnRAMVectors)
    {
        //
        // Copy the vector table from FLASH vector table to the RAM vector
        // table.
        //
        for(ulIdx = 0; ulIdx < NUM_INTERRUPTS; ulIdx++)
        {
            g_pfnRAMVectors[ulIdx] = g_pfnVectors[ulIdx];
        }

        //
        // Point NVIC at the RAM vector table.
        //
        HWREG(NVIC_VTABLE) = (unsigned long)g_pfnRAMVectors;
    }

    //
    // Save the interrupt handler.
    //
    g_pfnRAMVectors[ulInterrupt] = pfnHandler;
#else
#endif
}

//*****************************************************************************
//
//! Unregisters the function to be called when an interrupt occurs.
//!
//! \param ulInterrupt specifies the interrupt in question.
//!
//! This function is used to indicate that no handler should be called when the
//! given interrupt is asserted to the processor.  The interrupt source will be
//! automatically disabled (via IntDisable()) if necessary.
//!
//! \sa IntRegister() for important information about registering interrupt
//! handlers.
//!
//! \return None.
//
//*****************************************************************************
void
IntUnregister(unsigned long ulInterrupt)
{
#ifndef USE_RTOS
    //
    // Check the arguments.
    //
    ASSERT(ulInterrupt < NUM_INTERRUPTS);

    //
    // Reset the interrupt handler.
    //
    g_pfnRAMVectors[ulInterrupt] = IntDefaultHandler;
#else
#endif
}
//*****************************************************************************
//! Sets the priority grouping of the interrupt controller.
//!
//! \param ulBits specifies the number of bits of preemptable priority.
//!
//! This function specifies the split between preemptable priority levels and
//! subpriority levels in the interrupt priority specification.  The range of
//! the grouping values are dependent upon the hardware implementation; on
//! the Concerto family, three bits are available for hardware interrupt
//! prioritization and therefore priority grouping values of three through
//! seven have the same effect.
//!
//! \return None.
//*****************************************************************************
void
IntPriorityGroupingSet(unsigned long ulBits)
{
    // Check the arguments.
    ASSERT(ulBits < NUM_PRIORITY);

    // Set the priority grouping.
    HWREG(NVIC_APINT) = NVIC_APINT_VECTKEY | g_pulPriority[ulBits];
}

//*****************************************************************************
//! Gets the priority grouping of the interrupt controller.
//!
//! This function returns the split between preemptable priority levels and
//! subpriority levels in the interrupt priority specification.
//!
//! \return The number of bits of preemptable priority.
//*****************************************************************************
unsigned long
IntPriorityGroupingGet(void)
{
    unsigned long ulLoop, ulValue;

    // Read the priority grouping.
    ulValue = HWREG(NVIC_APINT) & NVIC_APINT_PRIGROUP_M;

    // Loop through the priority grouping values.
    for(ulLoop = 0; ulLoop < NUM_PRIORITY; ulLoop++)
    {
        // Stop looping if this value matches.
        if(ulValue == g_pulPriority[ulLoop])
        {
            break;
        }
    }

    // Return the number of priority bits.
    return(ulLoop);
}

//*****************************************************************************
//! Sets the priority of an interrupt.
//!
//! \param ulInterrupt specifies the interrupt in question.
//! \param ucPriority specifies the priority of the interrupt.
//!
//! This function is used to set the priority of an interrupt.  When multiple
//! interrupts are asserted simultaneously, the ones with the highest priority
//! are processed before the lower priority interrupts.  Smaller numbers
//! correspond to higher interrupt priorities; priority 0 is the highest
//! interrupt priority.
//!
//! The hardware priority mechanism will only look at the upper N bits of the
//! priority level (where N is 3 for the Concerto family), so any
//! prioritization must be performed in those bits.  The remaining bits can be
//! used to sub-prioritize the interrupt sources, and may be used by the
//! hardware priority mechanism on a future part.  This arrangement allows
//! priorities to migrate to different NVIC implementations without changing
//! the gross prioritization of the interrupts.
//!
//! \return None.
//*****************************************************************************
void
IntPrioritySet(unsigned long ulInterrupt, unsigned char ucPriority)
{
    unsigned long ulTemp;

    // Check the arguments.
    ASSERT((ulInterrupt >= 4) && (ulInterrupt < NUM_INTERRUPTS));

    // Set the interrupt priority.
    ulTemp = HWREG(g_pulRegs[ulInterrupt >> 2]);
    ulTemp &= ~(0xFF << (8 * (ulInterrupt & 3)));
    ulTemp |= ucPriority << (8 * (ulInterrupt & 3));
    HWREG(g_pulRegs[ulInterrupt >> 2]) = ulTemp;
}

//*****************************************************************************
//! Gets the priority of an interrupt.
//!
//! \param ulInterrupt specifies the interrupt in question.
//!
//! This function gets the priority of an interrupt.  See IntPrioritySet() for
//! a definition of the priority value.
//!
//! \return Returns the interrupt priority, or -1 if an invalid interrupt was
//! specified.
//*****************************************************************************
long
IntPriorityGet(unsigned long ulInterrupt)
{
    // Check the arguments.
    ASSERT((ulInterrupt >= 4) && (ulInterrupt < NUM_INTERRUPTS));

    // Return the interrupt priority.
    return((HWREG(g_pulRegs[ulInterrupt >> 2]) >> (8 * (ulInterrupt & 3)))
           & 0xFF);
}

//*****************************************************************************
//! Enables an interrupt.
//!
//! \param ulInterrupt specifies the interrupt to be enabled.
//!
//! The specified interrupt is enabled in the interrupt controller.  Other
//! enables for the interrupt (such as at the peripheral level) are unaffected
//! by this function.
//!
//! \return None.
//*****************************************************************************
void
IntEnable(unsigned long ulInterrupt)
{
    // Check the arguments.
    ASSERT(ulInterrupt < NUM_INTERRUPTS);

    // Determine the interrupt to enable.
    if(ulInterrupt == FAULT_MPU)
    {
        // Enable the MemManage interrupt.
        HWREG(NVIC_SYS_HND_CTRL) |= NVIC_SYS_HND_CTRL_MEM;
    }
    else if(ulInterrupt == FAULT_BUS)
    {
        // Enable the bus fault interrupt.
        HWREG(NVIC_SYS_HND_CTRL) |= NVIC_SYS_HND_CTRL_BUS;
    }
    else if(ulInterrupt == FAULT_USAGE)
    {
        // Enable the usage fault interrupt.
        HWREG(NVIC_SYS_HND_CTRL) |= NVIC_SYS_HND_CTRL_USAGE;
    }
    else if(ulInterrupt == FAULT_SYSTICK)
    {
        // Enable the System Tick interrupt.
        HWREG(NVIC_ST_CTRL) |= NVIC_ST_CTRL_INTEN;
    }
    else if((ulInterrupt >= 16) && (ulInterrupt <= 47))
    {
        // Enable the general interrupt.
        HWREG(NVIC_EN0) = 1 << (ulInterrupt - 16);
    }
    else if((ulInterrupt >= 48) && (ulInterrupt <= 79))
    {
        // Enable the general interrupt.
        HWREG(NVIC_EN1) = 1 << (ulInterrupt - 48);
    }
    else if(ulInterrupt >= 80)
    {
        // Enable the general interrupt.
        HWREG(NVIC_EN2) = 1 << (ulInterrupt - 80);
    }
}

//*****************************************************************************
//! Disables an interrupt.
//!
//! \param ulInterrupt specifies the interrupt to be disabled.
//!
//! The specified interrupt is disabled in the interrupt controller.  Other
//! enables for the interrupt (such as at the peripheral level) are unaffected
//! by this function.
//!
//! \return None.
//*****************************************************************************
void
IntDisable(unsigned long ulInterrupt)
{
    // Check the arguments.
    ASSERT(ulInterrupt < NUM_INTERRUPTS);

    // Determine the interrupt to disable.
    if(ulInterrupt == FAULT_MPU)
    {
        // Disable the MemManage interrupt.
        HWREG(NVIC_SYS_HND_CTRL) &= ~(NVIC_SYS_HND_CTRL_MEM);
    }
    else if(ulInterrupt == FAULT_BUS)
    {
        // Disable the bus fault interrupt.
        HWREG(NVIC_SYS_HND_CTRL) &= ~(NVIC_SYS_HND_CTRL_BUS);
    }
    else if(ulInterrupt == FAULT_USAGE)
    {
        // Disable the usage fault interrupt.
        HWREG(NVIC_SYS_HND_CTRL) &= ~(NVIC_SYS_HND_CTRL_USAGE);
    }
    else if(ulInterrupt == FAULT_SYSTICK)
    {
        // Disable the System Tick interrupt.
        HWREG(NVIC_ST_CTRL) &= ~(NVIC_ST_CTRL_INTEN);
    }
    else if((ulInterrupt >= 16) && (ulInterrupt <= 47))
    {
        // Disable the general interrupt.
        HWREG(NVIC_DIS0) = 1 << (ulInterrupt - 16);
    }
    else if((ulInterrupt >= 48) && (ulInterrupt <= 79))
    {
        // Enable the general interrupt.
        HWREG(NVIC_DIS1) = 1 << (ulInterrupt - 48);
    }
    else if(ulInterrupt >= 80)
    {
        // Enable the general interrupt.
        HWREG(NVIC_DIS2) = 1 << (ulInterrupt - 80);
    }
}

//*****************************************************************************
//! Pends an interrupt.
//!
//! \param ulInterrupt specifies the interrupt to be pended.
//!
//! The specified interrupt is pended in the interrupt controller.  This will
//! cause the interrupt controller to execute the corresponding interrupt
//! handler at the next available time, based on the current interrupt state
//! priorities.  For example, if called by a higher priority interrupt handler,
//! the specified interrupt handler will not be called until after the current
//! interrupt handler has completed execution.  The interrupt must have been
//! enabled for it to be called.
//!
//! \return None.
//*****************************************************************************
void
IntPendSet(unsigned long ulInterrupt)
{
    // Check the arguments.
    ASSERT(ulInterrupt < NUM_INTERRUPTS);

    // Determine the interrupt to pend.
    if(ulInterrupt == FAULT_NMI)
    {
        // Pend the NMI interrupt.
        HWREG(NVIC_INT_CTRL) |= NVIC_INT_CTRL_NMI_SET;
    }
    else if(ulInterrupt == FAULT_PENDSV)
    {
        // Pend the PendSV interrupt.
        HWREG(NVIC_INT_CTRL) |= NVIC_INT_CTRL_PEND_SV;
    }
    else if(ulInterrupt == FAULT_SYSTICK)
    {
        // Pend the SysTick interrupt.
        HWREG(NVIC_INT_CTRL) |= NVIC_INT_CTRL_PENDSTSET;
    }
    else if((ulInterrupt >= 16) && (ulInterrupt <= 47))
    {
        // Pend the general interrupt.
        HWREG(NVIC_PEND0) = 1 << (ulInterrupt - 16);
    }
    else if((ulInterrupt >= 48) && (ulInterrupt <= 79))
    {
        // Enable the general interrupt.
        HWREG(NVIC_PEND1) = 1 << (ulInterrupt - 48);
    }
    else if(ulInterrupt >= 80)
    {
        // Enable the general interrupt.
        HWREG(NVIC_PEND2) = 1 << (ulInterrupt - 80);
    }
}

//*****************************************************************************
//! Unpends an interrupt.
//!
//! \param ulInterrupt specifies the interrupt to be unpended.
//!
//! The specified interrupt is unpended in the interrupt controller.  This will
//! cause any previously generated interrupts that have not been handled yet
//! (due to higher priority interrupts or the interrupt no having been enabled
//! yet) to be discarded.
//!
//! \return None.
//*****************************************************************************
void
IntPendClear(unsigned long ulInterrupt)
{
    // Check the arguments.
    ASSERT(ulInterrupt < NUM_INTERRUPTS);

    // Determine the interrupt to unpend.
    if(ulInterrupt == FAULT_PENDSV)
    {
        // Unpend the PendSV interrupt.
        HWREG(NVIC_INT_CTRL) |= NVIC_INT_CTRL_UNPEND_SV;
    }
    else if(ulInterrupt == FAULT_SYSTICK)
    {
        // Unpend the SysTick interrupt.
        HWREG(NVIC_INT_CTRL) |= NVIC_INT_CTRL_PENDSTCLR;
    }
    else if((ulInterrupt >= 16) && (ulInterrupt <= 47))
    {
        // Unpend the general interrupt.
        HWREG(NVIC_UNPEND0) = 1 << (ulInterrupt - 16);
    }
    else if((ulInterrupt >= 48) && (ulInterrupt <= 79))
    {
        // Enable the general interrupt.
        HWREG(NVIC_UNPEND1) = 1 << (ulInterrupt - 48);
    }
    else if(ulInterrupt >= 80)
    {
        // Enable the general interrupt.
        HWREG(NVIC_UNPEND2) = 1 << (ulInterrupt - 80);
    }
}

//*****************************************************************************
//! Sets the priority masking level
//!
//! \param ulPriorityMask is the priority level that will be masked.
//!
//! This function sets the interrupt priority masking level so that all
//! interrupts at the specified or lesser priority level is masked.  This
//! can be used to globally disable a set of interrupts with priority below
//! a predetermined threshold.  A value of 0 disables priority
//! masking.
//!
//! Smaller numbers correspond to higher interrupt priorities.  So for example
//! a priority level mask of 4 will allow interrupts of priority level 0-3,
//! and interrupts with a numerical priority of 4 and greater will be blocked.
//!
//! The hardware priority mechanism will only look at the upper N bits of the
//! priority level (where N is 3 for the Concerto family), so any
//! prioritization must be performed in those bits.
//!
//! \return None.
//*****************************************************************************
void
IntPriorityMaskSet(unsigned long ulPriorityMask)
{
    CPUbasepriSet(ulPriorityMask);
}

//*****************************************************************************
//! Gets the priority masking level
//!
//! This function gets the current setting of the interrupt priority masking
//! level.  The value returned is the priority level such that all interrupts
//! of that and lesser priority are masked.  A value of 0 means that priority
//! masking is disabled.
//!
//! Smaller numbers correspond to higher interrupt priorities.  So for example
//! a priority level mask of 4 will allow interrupts of priority level 0-3,
//! and interrupts with a numerical priority of 4 and greater will be blocked.
//!
//! The hardware priority mechanism will only look at the upper N bits of the
//! priority level (where N is 3 for the Concerto family), so any
//! prioritization must be performed in those bits.
//!
//! \return Returns the value of the interrupt priority level mask.
//*****************************************************************************
unsigned long
IntPriorityMaskGet(void)
{
    return(CPUbasepriGet());
}

//*****************************************************************************
// Close the Doxygen group.
//! @}
//*****************************************************************************




