//###########################################################################
//
// FILE:   ipc.h
//
// TITLE:  C28x IPC driver.
//
//###########################################################################
// $Copyright:
// Copyright (C) 2022 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.
// $
//###########################################################################

#ifndef IPC_H
#define IPC_H

//*****************************************************************************
//
// If building with a C++ compiler, make all of the definitions in this header
// have a C binding.
//
//*****************************************************************************
#ifdef __cplusplus
extern "C"
{
#endif

//*****************************************************************************
//
//! \addtogroup ipc_api IPC
//! \brief This module is used for inter-processor communications.
//! @{
//
//*****************************************************************************

#include <stdbool.h>
#include <stdint.h>
#include "debug.h"
#include "hw_memmap.h"
#include "hw_types.h"
#include "hw_ipc.h"
#include "hw_ints.h"
#include "interrupt.h"

//*****************************************************************************
//
// Defines for the APIs
//
//*****************************************************************************
//*****************************************************************************
//
// Driver configuration macros
//
//*****************************************************************************
#define IPC_MSGQ_SUPPORT       1U

//
// Number of IPC messages in circular buffer (must be interval of 2)
//
#define IPC_BUFFER_SIZE        4U

//
// Number of IPC interrupts using circular buffer (must be same number on both
// CPUs)
//
#define IPC_NUM_OF_INTERRUPTS  4U

//*****************************************************************************
//
// Values that can be passed as parameter flags in all the IPC API functions.
//
//*****************************************************************************
#ifndef IPC_FLAGS_DEFINED
#define IPC_FLAGS_DEFINED
#define IPC_NO_FLAG     0x00000000U  //!< NO FLAG
#define IPC_FLAG0       0x00000001U  //!< IPC FLAG 0
#define IPC_FLAG1       0x00000002U  //!< IPC FLAG 1
#define IPC_FLAG2       0x00000004U  //!< IPC FLAG 2
#define IPC_FLAG3       0x00000008U  //!< IPC FLAG 3
#define IPC_FLAG4       0x00000010U  //!< IPC FLAG 4
#define IPC_FLAG5       0x00000020U  //!< IPC FLAG 5
#define IPC_FLAG6       0x00000040U  //!< IPC FLAG 6
#define IPC_FLAG7       0x00000080U  //!< IPC FLAG 7
#define IPC_FLAG8       0x00000100U  //!< IPC FLAG 8
#define IPC_FLAG9       0x00000200U  //!< IPC FLAG 9
#define IPC_FLAG10      0x00000400U  //!< IPC FLAG 10
#define IPC_FLAG11      0x00000800U  //!< IPC FLAG 11
#define IPC_FLAG12      0x00001000U  //!< IPC FLAG 12
#define IPC_FLAG13      0x00002000U  //!< IPC FLAG 13
#define IPC_FLAG14      0x00004000U  //!< IPC FLAG 14
#define IPC_FLAG15      0x00008000U  //!< IPC FLAG 15
#define IPC_FLAG16      0x00010000U  //!< IPC FLAG 16
#define IPC_FLAG17      0x00020000U  //!< IPC FLAG 17
#define IPC_FLAG18      0x00040000U  //!< IPC FLAG 18
#define IPC_FLAG19      0x00080000U  //!< IPC FLAG 19
#define IPC_FLAG20      0x00100000U  //!< IPC FLAG 20
#define IPC_FLAG21      0x00200000U  //!< IPC FLAG 21
#define IPC_FLAG22      0x00400000U  //!< IPC FLAG 22
#define IPC_FLAG23      0x00800000U  //!< IPC FLAG 23
#define IPC_FLAG24      0x01000000U  //!< IPC FLAG 24
#define IPC_FLAG25      0x02000000U  //!< IPC FLAG 25
#define IPC_FLAG26      0x04000000U  //!< IPC FLAG 26
#define IPC_FLAG27      0x08000000U  //!< IPC FLAG 27
#define IPC_FLAG28      0x10000000U  //!< IPC FLAG 28
#define IPC_FLAG29      0x20000000U  //!< IPC FLAG 29
#define IPC_FLAG30      0x40000000U  //!< IPC FLAG 30
#define IPC_FLAG31      0x80000000U  //!< IPC FLAG 31
#define IPC_FLAG_ALL    0xFFFFFFFFU  //!< All IPC flags
#endif

//*****************************************************************************
//
// Values that can be passed as parameter ipcInt in
// IPC_registerInterrupt and IPC_unregisterInterrupt functions.
// Please refer to the datasheet for the actual number of interrupts available
// for each IPC instance
//
//*****************************************************************************
#define IPC_INT0        0x0U  //!< IPC Interrupt 0
#define IPC_INT1        0x1U  //!< IPC Interrupt 1
#define IPC_INT2        0x2U  //!< IPC Interrupt 2
#define IPC_INT3        0x3U  //!< IPC Interrupt 3
#define IPC_INT4        0x4U  //!< IPC Interrupt 4
#define IPC_INT5        0x5U  //!< IPC Interrupt 5
#define IPC_INT6        0x6U  //!< IPC Interrupt 6
#define IPC_INT7        0x7U  //!< IPC Interrupt 7

//*****************************************************************************
//
// Values that can be passed as parameter addrCorrEnable in
// IPC_sendCommand, IPC_readCommand, IPC_sendMessageToQueue and
// IPC_readMessageFromQueue functions.
//
//*****************************************************************************
#define IPC_ADDR_CORRECTION_ENABLE   true
#define IPC_ADDR_CORRECTION_DISABLE  false

//*****************************************************************************
//
// Values that can be passed as parameter block in
// IPC_sendMessageToQueue and IPC_readMessageFromQueue functions.
//
//*****************************************************************************
#define IPC_BLOCKING_CALL     true
#define IPC_NONBLOCKING_CALL  false



//*****************************************************************************
//
// Internal macros used for message queue implementation
//
//*****************************************************************************
#define IPC_MAX_BUFFER_INDEX    (IPC_BUFFER_SIZE - 1U)

//*****************************************************************************
//
// Enums for the APIs
//
//*****************************************************************************

//*****************************************************************************
//
//! Values that can be passed as parameter \e ipcType in all the driver
//! functions
//
//*****************************************************************************
typedef enum
{
    IPC_CPU1_L_CPU2_R,       //!< CPU1 - Local core, CPU2 - Remote core
    IPC_CPU2_L_CPU1_R,       //!< CPU2 - Local core, CPU1 - Remote core
    IPC_TOTAL_NUM
}IPC_Type_t;

//*****************************************************************************
//
// Internal structs for register and messaage queue accesses
//
//*****************************************************************************
typedef struct
{
    uint32_t IPC_ACK;
    uint32_t IPC_STS;
    uint32_t IPC_SET;
    uint32_t IPC_CLR;
    uint32_t IPC_FLG;
    uint32_t IPC_RSVDREG;
    uint32_t IPC_COUNTERL;
    uint32_t IPC_COUNTERH;
}IPC_Flag_Ctr_Reg_t;

typedef struct
{
    uint32_t IPC_SENDCOM;
    uint32_t IPC_SENDADDR;
    uint32_t IPC_SENDDATA;
    uint32_t IPC_REMOTEREPLY;
}IPC_SendCmd_Reg_t;

typedef struct
{
    uint32_t IPC_RECVCOM;
    uint32_t IPC_RECVADDR;
    uint32_t IPC_RECVDATA;
    uint32_t IPC_LOCALREPLY;
}IPC_RecvCmd_Reg_t;

typedef struct
{
    uint32_t IPC_BOOTSTS;
    uint32_t IPC_BOOTMODE;
    uint32_t IPC_FLASHCTLSEM;
}IPC_Boot_Pump_Reg_t;

#if IPC_MSGQ_SUPPORT == 1U
typedef struct
{
    uint32_t command;
    uint32_t address;
    uint32_t dataw1;
    uint32_t dataw2;
}IPC_Message_t;

typedef struct
{
    IPC_Message_t Buffer[IPC_NUM_OF_INTERRUPTS][IPC_BUFFER_SIZE];
    uint16_t      PutWriteIndex[IPC_NUM_OF_INTERRUPTS];
    uint16_t      GetReadIndex[IPC_NUM_OF_INTERRUPTS];
}IPC_PutBuffer_t;

typedef struct
{
    IPC_Message_t Buffer[IPC_NUM_OF_INTERRUPTS][IPC_BUFFER_SIZE];
    uint16_t      GetWriteIndex[IPC_NUM_OF_INTERRUPTS];
    uint16_t      PutReadIndex[IPC_NUM_OF_INTERRUPTS];
}IPC_GetBuffer_t;
#endif

//*****************************************************************************
//
// Internal struct used to store the required information regarding an IPC
// instance
//
//*****************************************************************************
typedef struct
{
    volatile IPC_Flag_Ctr_Reg_t  *IPC_Flag_Ctr_Reg;
    volatile IPC_SendCmd_Reg_t   *IPC_SendCmd_Reg;
    volatile IPC_RecvCmd_Reg_t   *IPC_RecvCmd_Reg;
    volatile IPC_Boot_Pump_Reg_t *IPC_Boot_Pump_Reg;
    uint32_t                      IPC_IntNum[8U];
    uint32_t                      IPC_MsgRam_LtoR;
    uint32_t                      IPC_MsgRam_RtoL;
    uint32_t                      IPC_Offset_Corr;
#if IPC_MSGQ_SUPPORT == 1U
    IPC_PutBuffer_t              *IPC_PutBuffer;
    IPC_GetBuffer_t              *IPC_GetBuffer;
#endif
}IPC_Instance_t;

extern const IPC_Instance_t IPC_Instance[IPC_TOTAL_NUM];

#if IPC_MSGQ_SUPPORT == 1U
//*****************************************************************************
//
// A structure that defines an IPC message queue.  These
// fields are used by the IPC drivers, and normally it is not necessary for
// user software to directly read or write fields in the table.
//
//*****************************************************************************

typedef struct
{
    IPC_Message_t *  PutBuffer;
    uint32_t         PutFlag;
    uint16_t *       PutWriteIndex;
    uint16_t *       PutReadIndex;
    IPC_Message_t *  GetBuffer;
    uint16_t *       GetWriteIndex;
    uint16_t *       GetReadIndex;
} IPC_MessageQueue_t;
#endif

//*****************************************************************************
//
//! Values that can be passed to IPC_claimFlashSemaphore() in order to claim
//! the flash semaphore.
//
//*****************************************************************************
typedef enum
{
    IPC_FLASHSEM_OWNER_CPU1 = 0x1,
    IPC_FLASHSEM_OWNER_CPU2 = 0x2
}IPC_FlashSemOwner;

//*****************************************************************************
//
// API Function prototypes
//
//*****************************************************************************

//*****************************************************************************
//
//! Local core sets Local to Remote IPC Flag
//!
//! \param ipcType is the enum corresponding to the IPC instance used
//! \param flags is the IPC flag mask for the flags being set
//!
//! This function will allow the Local core system to set the designated IPC
//! flags to send to the Remote core system. The \e flags parameter can be any
//! of the IPC flag values: \b IPC_FLAG0 - \b IPC_FLAG31.
//!
//! \return None.
//
//*****************************************************************************
static inline void
IPC_setFlagLtoR(IPC_Type_t ipcType, uint32_t flags)
{
    IPC_Instance[ipcType].IPC_Flag_Ctr_Reg->IPC_SET = flags;
}

//*****************************************************************************
//
//! Local core clears Local to Remote IPC Flag
//!
//! \param ipcType is the enum corresponding to the IPC instance used
//! \param flags is the IPC flag mask for the flags being cleared
//!
//! This function will allow the Local core system to clear the designated IPC
//! flags sent to the Remote core system. The \e flags parameter can be any
//! of the IPC flag values: \b IPC_FLAG0 - \b IPC_FLAG31.
//!
//! \return None.
//
//*****************************************************************************
static inline void
IPC_clearFlagLtoR(IPC_Type_t ipcType, uint32_t flags)
{
    IPC_Instance[ipcType].IPC_Flag_Ctr_Reg->IPC_CLR = flags;
}

//*****************************************************************************
//
//! Local core acknowledges Remote to Local IPC Flag.
//!
//! \param ipcType is the enum corresponding to the IPC instance used
//! \param flags is the IPC flag mask for the flags being acknowledged.
//!
//! This function will allow the Local core system to acknowledge/clear the IPC
//! flag set by the Remote core system. The \e flags parameter can be any of
//! the IPC flag values: \b IPC_FLAG0 - \b IPC_FLAG31.
//!
//! \return None.
//
//*****************************************************************************
static inline void
IPC_ackFlagRtoL(IPC_Type_t ipcType, uint32_t flags)
{
    IPC_Instance[ipcType].IPC_Flag_Ctr_Reg->IPC_ACK = flags;
}

//*****************************************************************************
//
//! Determines whether the given IPC flags are busy or not.
//!
//! \param ipcType is the enum corresponding to the IPC instance used
//! \param flags is the Local to Remote IPC flag masks to check the status of
//!
//! Allows the caller to determine whether the designated Local to Remote
//! IPC flags are pending. The \e flags parameter can be any of the IPC flag
//! values: \b IPC_FLAG0 - \b IPC_FLAG31.
//!
//! \return Returns \b true if the any of the designated IPC flags are busy
//! or \b false if all the designated IPC flags are free.
//
//*****************************************************************************
static inline bool
IPC_isFlagBusyLtoR(IPC_Type_t ipcType, uint32_t flags)
{
    return((IPC_Instance[ipcType].IPC_Flag_Ctr_Reg->IPC_FLG & flags) != 0U);
}

//*****************************************************************************
//
//! Determines whether the given Remote to Local IPC flags are busy or not.
//!
//! \param ipcType is the enum corresponding to the IPC instance used
//! \param flags is the Remote to Local IPC Flag masks to check the status of
//!
//! Allows the caller to determine whether the designated Remote to Local
//! IPC flags are pending. The \e flags parameter can be any of the IPC flag
//! values: \b IPC_FLAG0 - \b IPC_FLAG31.
//!
//! \return Returns \b true if the any of the designated IPC flags are busy
//! or \b false if all the designated IPC flags are free.
//
//*****************************************************************************
static inline bool
IPC_isFlagBusyRtoL(IPC_Type_t ipcType, uint32_t flags)
{
    return((IPC_Instance[ipcType].IPC_Flag_Ctr_Reg->IPC_STS & flags) != 0U);
}

//*****************************************************************************
//
//! Wait for the remote core to send a flag
//!
//! \param ipcType is the enum corresponding to the IPC instance used
//! \param flag is the Remote to Local IPC flag mask to wait for
//!
//! Allows the caller to wait for the Remote to Local flag to be send by
//! the remote core. The \e flags parameter can be any of the IPC flag
//! values: \b IPC_FLAG0 - \b IPC_FLAG31.
//!
//! \return None
//
//*****************************************************************************
static inline void
IPC_waitForFlag(IPC_Type_t ipcType, uint32_t flag)
{
    while((IPC_Instance[ipcType].IPC_Flag_Ctr_Reg->IPC_STS & flag) == 0U)
    {
    }
}

//*****************************************************************************
//
//! Wait for the IPC flag to be acknowledged
//!
//! \param ipcType is the enum corresponding to the IPC instance used
//! \param flag is the IPC flag mask for which ack is pending
//!
//! Allows the caller to wait for the IPC flag to be acknowledged by the
//! remote core. The \e flagsparameter can be any of the IPC flag values:
//! \b IPC_FLAG0 - \b IPC_FLAG31.
//!
//! \return None
//
//*****************************************************************************
static inline void
IPC_waitForAck(IPC_Type_t ipcType, uint32_t flag)
{
    while((IPC_Instance[ipcType].IPC_Flag_Ctr_Reg->IPC_FLG & flag) != 0U)
    {
    }
}

//*****************************************************************************
//
//! Synchronises the two cores
//!
//! \param ipcType is the enum corresponding to the IPC instance used
//! \param flag is the IPC flag mask with which synchronisation is done
//!
//! Allows the local and remote cores to synchronise. Neither core will return
//! from this function call before the other core enters it.
//!
//! \note Must be called with same flag mask on both the cores
//!
//! \return None
//
//*****************************************************************************
static inline void
IPC_sync(IPC_Type_t ipcType, uint32_t flag)
{
    IPC_setFlagLtoR(ipcType, flag);
    IPC_waitForFlag(ipcType, flag);
    IPC_ackFlagRtoL(ipcType, flag);
    IPC_waitForAck(ipcType, flag);
}

//*****************************************************************************
//
//! Initialize IPC
//!
//! \param ipcType is the enum corresponding to the IPC instance used
//
//! This function initializes IPC by clearing all the flags
//!
//! \return None
//
//*****************************************************************************
static inline void
IPC_init(IPC_Type_t ipcType)
{
    IPC_clearFlagLtoR(ipcType, IPC_FLAG_ALL);
}

//*****************************************************************************
//
//! Sends a command to the Remote core
//!
//! \param ipcType is the enum corresponding to the IPC instance used
//! \param flags is the IPC flag mask for the flags to be set
//! \param addrCorrEnable is the flag used to determine whether or not to
//!    convert the addr parameter to remote core's address space
//! \param command is the 32-bit command value
//! \param addr is the 32-bit address to be sent as part of command
//! \param data is the 32-bit data to be sent as part of command
//!
//! Allows the caller to send a command to the remote core. A command consists
//! of a unique command value, a 32-bit address and a 32-bit data. The function
//! also sends the designated flags to the remote core.
//! There may be differences in the address spaces of Local and Remote core.
//! For example in case of F2838X device, the address spaces of C28x core and
//! CM core are different. In case the \e addr refers to an address in the IPC
//! MSG RAM, \e addrCorrEnable param may be used to correct the address mismatch
//!
//! The \e flags parameter can be any of the IPC flag values: \b IPC_FLAG0 -
//! \b IPC_FLAG31.
//! The \e addrCorrEnable parameter can take values IPC_ADDR_CORRECTION_ENABLE
//! (converts the address to remote core's address space) or
//! IPC_ADDR_CORRECTION_DISABLE(does not modify the addr parmeter)
//!
//! The application shall use the function IPC_getResponse to read the response
//! sent by the remote core.
//!
//! \note The application is expected to wait until the the response is
//! received before sending another command.
//!
//! \note \e addrCorrEnable parameter must be kept same on the sending and
//! receiving cores
//!
//! \return Returns \b true if the command is sent properly and \b false if
//! the designated flags were busy and hence command was not sent.
//
//*****************************************************************************
extern bool
IPC_sendCommand(IPC_Type_t ipcType, uint32_t flags, bool addrCorrEnable,
                uint32_t command, uint32_t addr, uint32_t data);

//*****************************************************************************
//
//! Reads a command sent by the Remote core
//!
//! \param ipcType is the enum corresponding to the IPC instance used
//! \param flags is the IPC flag mask for the flags sent by the remote core
//! \param addrCorrEnable is the flag used to determine whether or not to
//!    convert the addr parameter to remote core's address space
//! \param command is the 32-bit pointer at which the command value is read to
//! \param addr is the 32-bit pointer at which address value is read to
//! \param data is the 32-bit pointer at which the data is read to
//!
//! Allows the caller to read a command sent by the remote core. A command
//! consists of a unique command value, a 32-bit address and a 32-bit data.
//! There may be differences in the address spaces of Local and Remote core.
//! For example in case of F2838X device, the address spaces of C28x core and
//! CM core are different. In case the \e addr refers to an address in the IPC
//! MSG RAM, \e addrCorrEnable param may be used to correct the address mismatch
//!
//! The \e flags parameter can be any of the IPC flag values: \b IPC_FLAG0 -
//! \b IPC_FLAG31.
//! The \e addrCorrEnable parameter can take values IPC_ADDR_CORRECTION_ENABLE
//! (converts the address to remote core's address space) or
//! IPC_ADDR_CORRECTION_DISABLE(does not modify the addr parmeter)
//!
//! \note The application is expected to acknowledge the flag and send a
//! response (if needed) after reading the command
//!
//! \note \e addrCorrEnable parameter must be kept same on the sending and
//! receiving cores
//!
//! \return Returns \b true if the command is read properly and \b false if
//!  the designated flags were empty and hence command was not read.
//
//*****************************************************************************
extern bool
IPC_readCommand(IPC_Type_t ipcType, uint32_t flags, bool addrCorrEnable,
                uint32_t *command, uint32_t *addr, uint32_t *data);

//*****************************************************************************
//
//! Sends the response to the command sent by remote core.
//!
//! \param ipcType is the enum corresponding to the IPC instance used
//! \param data is the 32-bit value of the response to be sent
//!
//! Allows the caller to send a response to the command previously sent by the
//! remote core
//!
//! \return None.
//
//*****************************************************************************
static inline void
IPC_sendResponse(IPC_Type_t ipcType, uint32_t data)
{
    IPC_Instance[ipcType].IPC_RecvCmd_Reg->IPC_LOCALREPLY = data;
}

//*****************************************************************************
//
//! Reads the response from the remote core.
//!
//! \param ipcType is the enum corresponding to the IPC instance used
//!
//! Allows the caller to read the response sent by the remote core to the
//! command previously sent by the local core
//!
//! \return the 32-bit value of the response.
//
//*****************************************************************************
static inline uint32_t
IPC_getResponse(IPC_Type_t ipcType)
{
    return(IPC_Instance[ipcType].IPC_SendCmd_Reg->IPC_REMOTEREPLY);
}

//*****************************************************************************
//
//! Sets the BOOTMODE register.
//!
//! \param ipcType is the enum corresponding to the IPC instance used
//! \param mode is the 32-bit value to be set
//!
//! Allows the caller to set the BOOTMODE register.
//!
//! \note This function shall be called by CPU1 only.
//!
//! \return None
//
//*****************************************************************************
static inline void
IPC_setBootMode(IPC_Type_t ipcType, uint32_t mode)
{
    ASSERT(ipcType == IPC_CPU1_L_CPU2_R);

    IPC_Instance[ipcType].IPC_Boot_Pump_Reg->IPC_BOOTMODE = mode;
}

//*****************************************************************************
//
//! Reads the BOOTMODE register.
//!
//! \param ipcType is the enum corresponding to the IPC instance used
//!
//! Allows the caller to read the BOOTMODE register.
//!
//!
//! \return 32-bit value of the BOOOTMODE register
//
//*****************************************************************************
static inline uint32_t
IPC_getBootMode(IPC_Type_t ipcType)
{
    return(IPC_Instance[ipcType].IPC_Boot_Pump_Reg->IPC_BOOTMODE);
}

//*****************************************************************************
//
//! Sets the BOOTSTS register.
//!
//! \param ipcType is the enum corresponding to the IPC instance used
//! \param status is the 32-bit value to be set
//!
//! Allows the caller to set the BOOTSTS register.
//!
//! \note This function shall be called by CPU2 and CM only
//!
//! \note This function shall be called by CPU2 only.
//!
//! \return None.
//
//*****************************************************************************
static inline void
IPC_setBootStatus(IPC_Type_t ipcType, uint32_t status)
{
    ASSERT(ipcType == IPC_CPU2_L_CPU1_R);

    IPC_Instance[ipcType].IPC_Boot_Pump_Reg->IPC_BOOTSTS = status;
}

//*****************************************************************************
//
//! Reads the BOOTSTS register.
//!
//! \param ipcType is the enum corresponding to the IPC instance used
//!
//! Allows the caller to set the BOOTMODE register.
//!
//!
//! \return 32-bit value of the BOOOTSTS register
//
//*****************************************************************************
static inline uint32_t
IPC_getBootStatus(IPC_Type_t ipcType)
{
    return(IPC_Instance[ipcType].IPC_Boot_Pump_Reg->IPC_BOOTSTS);
}

//*****************************************************************************
//
//! Reads the timestamp counter value.
//!
//! \param ipcType is the enum corresponding to the IPC instance used
//!
//! Allows the caller to read the IPC timestamp counter value.
//!
//! \return 64-bit counter value.
//
//*****************************************************************************
static inline uint64_t
IPC_getCounter(IPC_Type_t ipcType)
{
    //
    // Get the Counter High and Low values. Read to the Counter low register
    // saves the value of Counter High register.
    //
    uint32_t ctrL = IPC_Instance[ipcType].IPC_Flag_Ctr_Reg->IPC_COUNTERL;
    uint32_t ctrH = IPC_Instance[ipcType].IPC_Flag_Ctr_Reg->IPC_COUNTERH;

    //
    // Return the 64-bit value of the counter
    //
    return(((uint64_t)ctrH << 32) | ((uint64_t)ctrL));
}

//*****************************************************************************
//
//! Claim the flash pump semaphore.
//!
//! \param semOwner is the cpu claiming the flash semaphore.
//!
//! \return None.
//
//*****************************************************************************
static inline void
IPC_claimFlashSemaphore(IPC_FlashSemOwner semOwner)
{
    EALLOW;

    uint32_t semRegister = IPC_CPUXTOCPUX_BASE + IPC_O_FLASHCTLSEM;

    //
    // Block until the SEM value is 0.
    //
    while(HWREG(semRegister) != 0U);

    //
    // Block until the semaphore is claimed.
    //
    while(HWREG(semRegister) != (uint32_t)semOwner)
    {
        HWREG(semRegister) = 0x5A5A0000U | (uint32_t)semOwner;
    }
    EDIS;
}

//*****************************************************************************
//
//! Release the flash pump semaphore.
//!
//! \param pumpSemBase is the base address of the flash pump semaphore.
//!
//! \return None.
//
//*****************************************************************************
static inline void
IPC_releaseFlashSemaphore()
{
    EALLOW;
    HWREG(IPC_CPUXTOCPUX_BASE + IPC_O_FLASHCTLSEM) = 0x5A5A0000U;
    EDIS;
}
//*****************************************************************************
//
//! Registers an interrupt handler for IPC
//!
//! \param ipcType is the enum corresponding to the IPC instance used
//! \param ipcInt is the Flag number for which interrupt is being registered
//! \param pfnHandler is the pointer to ISR function
//!
//! This function registers the handler to be called when an IPC interrupt
//! occurs.  This function enables the global interrupt in the interrupt
//! controller.
//! The \e ipcInt parameter can be any of the IPC flag values:\b IPC_INT0 -
//! \b IPC_INT7. IPC_INT0 corresponds to IPC Flag 0 interrupt and so on.
//
//*****************************************************************************
extern void
IPC_registerInterrupt(IPC_Type_t ipcType, uint32_t ipcInt,
                      void (*pfnHandler)(void));

//*****************************************************************************
//
//! Unregisters an interrupt handler for IPC
//!
//! \param ipcType is the enum corresponding to the IPC instance used
//! \param ipcInt is the Flag number for which interrupt is being unregistered
//!
//! This function clears the handler to be called when an IPC interrupt
//! occurs.  This function also masks off the interrupt in the interrupt
//! controller so that the interrupt handler no longer is called.
//! The \e ipcInt parameter can be any of the IPC flag values:\b IPC_INT0 -
//! \b IPC_INT7. IPC_INT0 corresponds to IPC Flag 0 interrupt and so on.
//
//*****************************************************************************
extern void
IPC_unregisterInterrupt(IPC_Type_t ipcType, uint32_t ipcInt);

#if IPC_MSGQ_SUPPORT == 1U
//*****************************************************************************
//
//! Initializes the IPC message queue
//!
//! \param ipcType is the enum corresponding to the IPC instance used
//! \param msgQueue specifies the address of a \e IPC_MessageQueue_t instance
//! \param ipcInt_L specifies the interrupt number on the local core used by
//!                 the message queue .
//! \param ipcInt_R specifies the interrupt number on the remote core used by
//!                 the message queue.
//!
//! This function initializes the IPC message queue with circular buffer
//! and index addresses for an IPC interrupt pair. The
//! \e ipcInt_L and \e ipcInt_R parameters can be one of the following values:
//! \b IPC_INT0, \b IPC_INT1, \b IPC_INT2, \b IPC_INT3.
//!
//! \note If an interrupt is currently in use by an \e IPC_MessageQueue_t
//! instance, that particular interrupt should not be tied to a second
//! \e IPC_MessageQueue_t instance.
//!
//! \note For a particular ipcInt_L - ipcInt_R pair, there must be an instance
//! of IPC_MessageQueue_t defined and initialized on both the locakl and remote
//! systems.
//!
//! \return None.
//
//*****************************************************************************
extern void
IPC_initMessageQueue(IPC_Type_t ipcType, volatile IPC_MessageQueue_t *msgQueue,
                     uint32_t ipcInt_L, uint32_t ipcInt_R);

//*****************************************************************************
//
//! Sends a message into the messageQueue.
//!
//! \param ipcType is the enum corresponding to the IPC instance used
//! \param msgQueue specifies the address of a \e IPC_MessageQueue_t instance
//! \param addrCorrEnable is the flag used to determine whether or not to
//!                 convert the addr parameter to remote core's address space
//! \param msg specifies the address of the \e IPC_Message_t instance to be
//!                 sent to message queue.
//! \param block specifies whether to allow function to block until the buffer
//!                 has a free slot
//!
//! This function checks if there is a free slot in the message queue. If so, it
//! puts the message pointed to by \e msg into the free and sets the
//! appropriate IPC interrupt flag
//!
//! The \e addrCorrEnable parameter can take values IPC_ADDR_CORRECTION_ENABLE
//! (converts the address to remote core's address space) or
//! IPC_ADDR_CORRECTION_DISABLE(does not modify the addr parmeter)
//! The \e block parameter can be one of the following values:
//! \b IPC_BLOCKING_CALL or \b IPC_NONBLOCKING_CALL.
//!
//! \return \b false if the queue is full. \b true if the message is
//! successfully sent.
//
//*****************************************************************************
extern bool
IPC_sendMessageToQueue(IPC_Type_t ipcType,
                       volatile IPC_MessageQueue_t *msgQueue,
                       bool addrCorrEnable, IPC_Message_t *msg, bool block);

//*****************************************************************************
//
//! Reads a message from the messageQueue.
//!
//! \param ipcType is the enum corresponding to the IPC instance used
//! \param msgQueue specifies the address of a \e IPC_MessageQueue_t instance
//! \param addrCorrEnable is the flag used to determine whether or not to
//!                 convert the addr parameter to remote core's address space
//! \param msg specifies the address of the \e IPC_Message_t instance to which
//!                 the message needs to be read
//! \param block specifies whether to allow function to block until a message
//!                 is available in the message queue
//!
//! This function checks if there is a message in the message queue. If so, it
//! reads the message and writes to the address pointed to by \e msg into.
//!
//! The \e addrCorrEnable parameter can take values IPC_ADDR_CORRECTION_ENABLE
//! (converts the address to remote core's address space) or
//! IPC_ADDR_CORRECTION_DISABLE(does not modify the addr parmeter)
//! The \e block parameter can be one of the following values:
//! \b IPC_BLOCKING_CALL or \b IPC_NONBLOCKING_CALL.
//!
//! \return \b false if the queue is empty. \b true if the message successfully
//! read.
//
//*****************************************************************************
extern bool
IPC_readMessageFromQueue(IPC_Type_t ipcType,
                         volatile IPC_MessageQueue_t *msgQueue,
                         bool addrCorrEnable, IPC_Message_t *msg, bool block);
#endif
//*****************************************************************************
//
// Close the Doxygen group.
//! @}
//
//*****************************************************************************

//*****************************************************************************
//
// Mark the end of the C bindings section for C++ compilers.
//
//*****************************************************************************
#ifdef __cplusplus
}
#endif

#endif // IPC_H
