//###########################################################################
//
// FILE:   interrupt.c
//
// TITLE:  C28x Interrupt (PIE) driver.
//
//###########################################################################
// $Copyright:
// Copyright (C) 2023 Texas Instruments Incorporated - http://www.ti.com/
//
// Redistribution and use in source and binary forms, with or without 
// modification, are permitted provided that the following conditions 
// are met:
// 
//   Redistributions of source code must retain the above copyright 
//   notice, this list of conditions and the following disclaimer.
// 
//   Redistributions in binary form must reproduce the above copyright
//   notice, this list of conditions and the following disclaimer in the 
//   documentation and/or other materials provided with the   
//   distribution.
// 
//   Neither the name of Texas Instruments Incorporated nor the names of
//   its contributors may be used to endorse or promote products derived
//   from this software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// $
//###########################################################################

#include "interrupt.h"

//*****************************************************************************
//
//! \internal
//! Clears the IFR flag in the CPU.
//!
//! \param group specifies the interrupt group to be cleared.
//!
//! This function clears the IFR flag.  This switch is needed because the
//! clearing of the IFR can only be done with a constant.
//
//*****************************************************************************
static void Interrupt_clearIFR(uint16_t group)
{
    switch(group)
    {
        case 0x0001U:
            IFR &= ~(uint16_t)0x0001U;
            break;
        case 0x0002U:
            IFR &= ~(uint16_t)0x0002U;
            break;
        case 0x0004U:
            IFR &= ~(uint16_t)0x0004U;
            break;
        case 0x0008U:
            IFR &= ~(uint16_t)0x0008U;
            break;
        case 0x0010U:
            IFR &= ~(uint16_t)0x0010U;
            break;
        case 0x0020U:
            IFR &= ~(uint16_t)0x0020U;
            break;
        case 0x0040U:
            IFR &= ~(uint16_t)0x0040U;
            break;
        case 0x0080U:
            IFR &= ~(uint16_t)0x0080U;
            break;
        case 0x0100U:
            IFR &= ~(uint16_t)0x0100U;
            break;
        case 0x0200U:
            IFR &= ~(uint16_t)0x0200U;
            break;
        case 0x0400U:
            IFR &= ~(uint16_t)0x0400U;
            break;
        case 0x0800U:
            IFR &= ~(uint16_t)0x0800U;
            break;
        case 0x1000U:
            IFR &= ~(uint16_t)0x1000U;
            break;
        case 0x2000U:
            IFR &= ~(uint16_t)0x2000U;
            break;
        case 0x4000U:
            IFR &= ~(uint16_t)0x4000U;
            break;
        case 0x8000U:
            IFR &= ~(uint16_t)0x8000U;
            break;
        default:
            //
            // Invalid group mask.
            //
            ASSERT((bool)false);
            break;
    }
}

//*****************************************************************************
//
// Interrupt_initModule
//
//*****************************************************************************
void
Interrupt_initModule(void)
{
    //
    // Disable and clear all interrupts at the CPU
    //
    (void)Interrupt_disableGlobal();
    IER = 0x0000U;
    IFR = 0x0000U;

    //
    // Clear all PIEIER registers
    //
    HWREGH(PIECTRL_BASE + PIE_O_IER1) = 0U;
    HWREGH(PIECTRL_BASE + PIE_O_IER2) = 0U;
    HWREGH(PIECTRL_BASE + PIE_O_IER3) = 0U;
    HWREGH(PIECTRL_BASE + PIE_O_IER4) = 0U;
    HWREGH(PIECTRL_BASE + PIE_O_IER5) = 0U;
    HWREGH(PIECTRL_BASE + PIE_O_IER6) = 0U;
    HWREGH(PIECTRL_BASE + PIE_O_IER7) = 0U;
    HWREGH(PIECTRL_BASE + PIE_O_IER8) = 0U;
    HWREGH(PIECTRL_BASE + PIE_O_IER9) = 0U;
    HWREGH(PIECTRL_BASE + PIE_O_IER10) = 0U;
    HWREGH(PIECTRL_BASE + PIE_O_IER11) = 0U;
    HWREGH(PIECTRL_BASE + PIE_O_IER12) = 0U;

    //
    // Clear all PIEIFR registers
    //
    HWREGH(PIECTRL_BASE + PIE_O_IFR1) = 0U;
    HWREGH(PIECTRL_BASE + PIE_O_IFR2) = 0U;
    HWREGH(PIECTRL_BASE + PIE_O_IFR3) = 0U;
    HWREGH(PIECTRL_BASE + PIE_O_IFR4) = 0U;
    HWREGH(PIECTRL_BASE + PIE_O_IFR5) = 0U;
    HWREGH(PIECTRL_BASE + PIE_O_IFR6) = 0U;
    HWREGH(PIECTRL_BASE + PIE_O_IFR7) = 0U;
    HWREGH(PIECTRL_BASE + PIE_O_IFR8) = 0U;
    HWREGH(PIECTRL_BASE + PIE_O_IFR9) = 0U;
    HWREGH(PIECTRL_BASE + PIE_O_IFR10) = 0U;
    HWREGH(PIECTRL_BASE + PIE_O_IFR11) = 0U;
    HWREGH(PIECTRL_BASE + PIE_O_IFR12) = 0U;

    //
    // Enable vector fetching from PIE block
    //
    HWREGH(PIECTRL_BASE + PIE_O_CTRL) |= PIE_CTRL_ENPIE;

}

//*****************************************************************************
//
//! The default interrupt handler.
//!
//! \return None.
//
//*****************************************************************************
__interrupt void Interrupt_defaultHandler(void)
{
    uint16_t pieVect;
    uint16_t vectID;

    //
    // Calculate the vector ID. If the vector is in the lower PIE, it's the
    // offset of the vector that was fetched (bits 7:1 of PIECTRL.PIEVECT)
    // divided by two.
    //
    pieVect = HWREGH(PIECTRL_BASE + PIE_O_CTRL);
    vectID = (pieVect & 0xFEU) >> 1U;

    //
    // If the vector is in the upper PIE, the vector ID is 128 or higher.
    //
    if(pieVect >= 0x0E00U)
    {
        vectID += 128U;
    }

    //
    // Something has gone wrong. An interrupt without a proper registered
    // handler function has occurred. To help you debug the issue, local
    // variable vectID contains the vector ID of the interrupt that occurred.
    //
    ESTOP0;
    for(;;)
    {
        ;
    }
}

//*****************************************************************************
//
//! The default illegal instruction trap interrupt handler.
//!
//! \return None.
//
//*****************************************************************************
__interrupt void Interrupt_illegalOperationHandler(void)
{
    //
    // Something has gone wrong.  The CPU has tried to execute an illegal
    // instruction, generating an illegal instruction trap (ITRAP).
    //
    ESTOP0;
    for(;;)
    {
        ;
    }
}

//*****************************************************************************
//
//! The default non-maskable interrupt handler.
//!
//! \return None.
//
//*****************************************************************************
__interrupt void Interrupt_nmiHandler(void)
{
    //
    // A non-maskable interrupt has occurred, indicating that a hardware error
    // has occurred in the system.  You can use SysCtl_getNMIFlagStatus() to
    // to read the NMIFLG register and determine what caused the NMI.
    //
    ESTOP0;
    for(;;)
    {
        ;
    }
}

//*****************************************************************************
//
// Interrupt_initVectorTable
//
//*****************************************************************************
void
Interrupt_initVectorTable(void)
{
    uint16_t i;

    EALLOW;

    //
    // We skip the first three locations because they are initialized by Boot
    // ROM with boot variables.
    //
    for(i = 3U; i < 224U; i++)
    {
        HWREG(PIEVECTTABLE_BASE + (2U * i)) =
            (uint32_t)Interrupt_defaultHandler;
    }

    //
    // NMI and ITRAP get their own handlers.
    //
    HWREG(PIEVECTTABLE_BASE + ((INT_NMI >> 16U) * 2U)) =
        (uint32_t)Interrupt_nmiHandler;
    HWREG(PIEVECTTABLE_BASE + ((INT_ILLEGAL >> 16U) * 2U)) =
        (uint32_t)Interrupt_illegalOperationHandler;

    EDIS;
}

//*****************************************************************************
//
//Interrupt_enable
//
//*****************************************************************************
void
Interrupt_enable(uint32_t interruptNumber)
{
    bool intsDisabled;
    uint16_t intGroup;
    uint16_t groupMask;
    uint16_t vectID;

    vectID = (uint16_t)(interruptNumber >> 16U);

    //
    // Globally disable interrupts but save status
    //
    intsDisabled = Interrupt_disableGlobal();

    //
    // PIE Interrupts
    //
    if(vectID >= 0x20U)
    {
        intGroup = (uint16_t)(((interruptNumber & 0xFF00UL) >> 8U) - 1U);
        groupMask = (uint16_t)1U << intGroup;

        HWREGH((PIECTRL_BASE + PIE_O_IER1 + (intGroup * 2U))) |=
            (uint16_t)1U << ((interruptNumber & 0xFFU) - 1U);

        //
        // Enable PIE Group Interrupt
        //
        IER |= groupMask;
    }

    //
    // INT13, INT14, DLOGINT, & RTOSINT
    //
    else if((vectID >= 0x0DU) && (vectID <= 0x10U))
    {
        IER |= (uint16_t)1U << (vectID - 1U);
    }
    else
    {
        //
        // Other interrupts
        //
    }

    //
    // Re-enable interrupts if they were enabled
    //
    if(!intsDisabled)
    {
        (void)Interrupt_enableGlobal();
    }
}

//*****************************************************************************
//
// Interrupt_disable
//
//*****************************************************************************
void
Interrupt_disable(uint32_t interruptNumber)
{
    bool intsDisabled;
    uint16_t intGroup;
    uint16_t groupMask;
    uint16_t vectID;

    vectID = (uint16_t)(interruptNumber >> 16U);

    intsDisabled = Interrupt_disableGlobal();

    //
    // PIE Interrupts
    //
    if(vectID >= 0x20U)
    {
        intGroup = (uint16_t)(((interruptNumber & 0xFF00UL) >> 8U) - 1U);
        groupMask = (uint16_t)1U << intGroup;

        //
        // Disable individual PIE interrupt
        //
        HWREGH((PIECTRL_BASE + PIE_O_IER1 + (intGroup * 2U))) &=
            ~(1U << ((interruptNumber & 0xFFUL) - 1U));

        //
        // Wait for any pending interrupts to get to the CPU
        //
        NOP;
        NOP;
        NOP;
        NOP;
        NOP;

        Interrupt_clearIFR(groupMask);

        //
        // Acknowledge any interrupts
        //
        HWREGH(PIECTRL_BASE + PIE_O_ACK) = groupMask;
    }

    //
    // INT13, INT14, DLOGINT, & RTOSINT
    //
    else if((vectID >= 0x0DU) && (vectID <= 0x10U))
    {
        IER &= ~((uint16_t)1U << (vectID - 1U));

        //
        // Wait for any pending interrupts to get to the CPU
        //
        NOP;
        NOP;
        NOP;
        NOP;
        NOP;

        Interrupt_clearIFR((uint16_t)1U << (vectID - 1U));
    }
    else
    {
        //
        // Other interrupts
        //
    }

    //
    // Re-enable interrupts if they were enabled
    //
    if(!intsDisabled)
    {
        (void)Interrupt_enableGlobal();
    }
}
