/******************************************************************************
* Copyright (C) 2010 - 2021 Xilinx, Inc.  All rights reserved.
* SPDX-License-Identifier: MIT
******************************************************************************/

/*****************************************************************************/
/**
*
* @file xscugic.h
* @addtogroup scugic_v4_7
* @{
* @details
*
* The generic interrupt controller driver component.
*
* The interrupt controller driver uses the idea of priority for the various
* handlers. Priority is an integer within the range of 1 and 31 inclusive with
* default of 1 being the highest priority interrupt source. The priorities
* of the various sources can be dynamically altered as needed through
* hardware configuration.
*
* The generic interrupt controller supports the following
* features:
*
*   - specific individual interrupt enabling/disabling
*   - specific individual interrupt acknowledging
*   - attaching specific callback function to handle interrupt source
*   - assigning desired priority to interrupt source if default is not
*     acceptable.
*
* Details about connecting the interrupt handler of the driver are contained
* in the source file specific to interrupt processing, xscugic_intr.c.
*
* This driver is intended to be RTOS and processor independent.  It works with
* physical addresses only.  Any needs for dynamic memory management, threads
* or thread mutual exclusion, virtual memory, or cache control must be
* satisfied by the layer above this driver.
*
* <b>Interrupt Vector Tables</b>
*
* The device ID of the interrupt controller device is used by the driver as a
* direct index into the configuration data table. The user should populate the
* vector table with handlers and callbacks at run-time using the
* XScuGic_Connect() and XScuGic_Disconnect() functions.
*
* Each vector table entry corresponds to a device that can generate an
* interrupt. Each entry contains an interrupt handler function and an
* argument to be passed to the handler when an interrupt occurs.  The
* user must use XScuGic_Connect() when the interrupt handler takes an
* argument other than the base address.
*
* <b>Nested Interrupts Processing</b>
*
* Nested interrupts are not supported by this driver.
*
* NOTE:
* The generic interrupt controller is not a part of the snoop control unit
* as indicated by the prefix "scu" in the name of the driver.
* It is an independent module in APU.
*
* <pre>
* MODIFICATION HISTORY:
*
* Ver   Who  Date     Changes
* ----- ---- -------- ---------------------------------------------------------
* 1.00a drg  01/19/00 First release
* 1.01a sdm  11/09/11 The XScuGic and XScuGic_Config structures have changed.
*		      The HandlerTable (of type XScuGic_VectorTableEntry) is
*		      moved to XScuGic_Config structure from XScuGic structure.
*
*		      The "Config" entry in XScuGic structure is made as
*		      pointer for better efficiency.
*
*		      A new file named as xscugic_hw.c is now added. It is
*		      to implement low level driver routines without using
*		      any xscugic instance pointer. They are useful when the
*		      user wants to use xscugic through device id or
*		      base address. The driver routines provided are explained
*		      below.
*		      XScuGic_DeviceInitialize that takes device id as
*		      argument and initializes the device (without calling
*		      XScuGic_CfgInitialize).
*		      XScuGic_DeviceInterruptHandler that takes device id
*		      as argument and calls appropriate handlers from the
*		      HandlerTable.
*		      XScuGic_RegisterHandler that registers a new handler
*		      by taking xscugic hardware base address as argument.
*		      LookupConfigByBaseAddress is used to return the
*		      corresponding config structure from XScuGic_ConfigTable
*		      based on the scugic base address passed.
* 1.02a sdm  12/20/11 Removed AckBeforeService from the XScuGic_Config
*		      structure.
* 1.03a srt  02/27/13 Moved Offset calculation macros from *.c and *_hw.c to
*		      *_hw.h
*		      Added APIs
*			- XScuGic_SetPriTrigTypeByDistAddr()
*			- XScuGic_GetPriTrigTypeByDistAddr()
*		      (CR 702687)
*			Added support to direct interrupts to the appropriate CPU. Earlier
*			  interrupts were directed to CPU1 (hard coded). Now depending
*			  upon the CPU selected by the user (xparameters.h), interrupts
*			  will be directed to the relevant CPU. This fixes CR 699688.
* 1.04a hk   05/04/13 Assigned EffectiveAddr to CpuBaseAddress in
*			  XScuGic_CfgInitialize. Fix for CR#704400 to remove warnings.
*			  Moved functions XScuGic_SetPriTrigTypeByDistAddr and
*             XScuGic_GetPriTrigTypeByDistAddr to xscugic_hw.c.
*			  This is fix for CR#705621.
* 1.05a hk   06/26/13 Modified tcl to export external interrupts correctly to
*                     xparameters.h. Fix for CR's 690505, 708928 & 719359.
* 2.0   adk  12/10/13 Updated as per the New Tcl API's
* 2.1   adk  25/04/14 Fixed the CR:789373 changes are made in the driver tcl file.
* 3.00  kvn  02/13/15 Modified code for MISRA-C:2012 compliance.
* 3.2   asa  02/29/16 Modified DistributorInit function for Zynq AMP case. The
*			  distributor is left uninitialized for Zynq AMP. It is assumed
*             that the distributor will be initialized by Linux master. However
*             for CortexR5 case, the earlier code is left unchanged where the
*             the interrupt processor target registers in the distributor is
*             initialized with the corresponding CPU ID on which the application
*             built over the scugic driver runs.
*             These changes fix CR#937243.
*
* 3.4   asa  04/07/16 Created a new static function DoDistributorInit to simplify
*            the flow and avoid code duplication. Changes are made for
*            USE_AMP use case for R5. In a scenario (in R5 split mode) when
*            one R5 is operating with A53 in open amp config and other
*            R5 running baremetal app, the existing code
*            had the potential to stop the whole AMP solution to work (if
*            for some reason the R5 running the baremetal app tasked to
*            initialize the Distributor hangs or crashes before initializing).
*            Changes are made so that the R5 under AMP first checks if
*            the distributor is enabled or not and if not, it does the
*            standard Distributor initialization.
*            This fixes the CR#952962.
* 3.6   ms   01/23/17 Modified xil_printf statement in main function for all
*                     examples to ensure that "Successfully ran" and "Failed"
*                     strings are available in all examples. This is a fix
*                     for CR-965028.
*       kvn  02/17/17 Add support for changing GIC CPU master at run time.
*       kvn  02/28/17 Make the CpuId as static variable and Added new
*                     XScugiC_GetCpuId to access CpuId.
*       ms   03/17/17 Added readme.txt file in examples folder for doxygen
*                     generation.
* 3.7   ms   04/11/17 Modified tcl file to add suffix U for all macro
*                     definitions of scugic in xparameters.h
* 3.8   mus  07/05/17 Updated scugic.tcl to add support for interrupts connected
*                     through util_reduced_vector IP(OR gate)
*       mus  07/05/17 Updated xdefine_zynq_canonical_xpars proc to initialize
*                     the HandlerTable in XScuGic_ConfigTable to 0, it removes
*                     the compilation warning in xscugic_g.c. Fix for CR#978736.
*       mus  07/25/17 Updated xdefine_gic_params proc to export correct canonical
*                     definitions for pl to ps interrupts.Fix for CR#980534
* 3.9   mus  02/21/18 Added new API's XScuGic_UnmapAllInterruptsFromCpu and
*                     XScuGic_InterruptUnmapFromCpu, These API's can be used
*                     by applications to unmap specific/all interrupts from
*                     target CPU.
* 3.10  aru  08/23/18 Resolved MISRA-C:2012 compliance mandatory violations
* 4.0   mus  11/22/18 Fixed bugs in software interrupt generation through
*                      XScuGic_SoftwareIntr API
* 4.1   asa  03/30/19 Made changes not to direct each interrupt to all
*                     available CPUs by default. This was breaking AMP
*                     behavior. Instead every time an interrupt enable
*                     request is received, the interrupt was mapped to
*                     the respective CPU. There were several other changes
*                     made to implement this. This set of changes was to
*                     fix CR-1024716.
* 4.1   mus  06/19/19 Added API's XScuGic_MarkCoreAsleep and
*                     XScuGic_MarkCoreAwake to mark processor core as
*                     asleep or awake. Fix for CR#1027220.
* 4.5   asa  03/07/21 Included a header file xil_spinlock.h to ensure that
*                     GIC driver can use newly introduced spinlock
*                     functionality.
* 4.6	sk   08/05/21 Fix scugic misrac violations.
* 4.7   dp   11/22/21 Added new API XScuGic_IsInitialized() to check and return
*                     the GIC initialization status.
*
* </pre>
*
******************************************************************************/

#ifndef XSCUGIC_H /* prevent circular inclusions */
#define XSCUGIC_H /* by using protection macros */

#ifdef __cplusplus
extern "C" {
#endif


/***************************** Include Files *********************************/

#ifndef __rtems__
#include "xstatus.h"
#include "xil_io.h"
#endif /* __rtems__ */
#include "xscugic_hw.h"
#ifndef __rtems__
#include "xil_exception.h"
#include "xil_spinlock.h"
#else /* __rtems__ */
#include <xil-compat-lwip.h>
#endif /* __rtems__ */

/************************** Constant Definitions *****************************/

#define EFUSE_STATUS_OFFSET   0x10
#define EFUSE_STATUS_CPU_MASK 0x80

#if !defined (ARMR5) && !defined (__aarch64__) && !defined (ARMA53_32)
#define ARMA9
#endif

#define XSCUGIC500_DCTLR_ARE_NS_ENABLE  0x20
#define XSCUGIC500_DCTLR_ARE_S_ENABLE  0x10
/**************************** Type Definitions *******************************/

/* The following data type defines each entry in an interrupt vector table.
 * The callback reference is the base address of the interrupting device
 * for the low level driver and an instance pointer for the high level driver.
 */
typedef struct
{
	Xil_InterruptHandler Handler;
	void *CallBackRef;
} XScuGic_VectorTableEntry;

/**
 * This typedef contains configuration information for the device.
 */
typedef struct
{
	u16 DeviceId;		/**< Unique ID  of device */
	u32 CpuBaseAddress;	/**< CPU Interface Register base address */
	u32 DistBaseAddress;	/**< Distributor Register base address */
	XScuGic_VectorTableEntry HandlerTable[XSCUGIC_MAX_NUM_INTR_INPUTS];/**<
				 Vector table of interrupt handlers */
} XScuGic_Config;

/**
 * The XScuGic driver instance data. The user is required to allocate a
 * variable of this type for every intc device in the system. A pointer
 * to a variable of this type is then passed to the driver API functions.
 */
typedef struct
{
	XScuGic_Config *Config;  /**< Configuration table entry */
	u32 IsReady;		 /**< Device is initialized and ready */
	u32 UnhandledInterrupts; /**< Intc Statistics */
} XScuGic;

/************************** Variable Definitions *****************************/

extern XScuGic_Config XScuGic_ConfigTable[];	/**< Config table */

/***************** Macros (Inline Functions) Definitions *********************/

/****************************************************************************/
/**
*
* Write the given CPU Interface register
*
* @param    InstancePtr is a pointer to the instance to be worked on.
* @param    RegOffset is the register offset to be written
* @param    Data is the 32-bit value to write to the register
*
* @return   None.
*
* @note
* C-style signature:
*    void XScuGic_CPUWriteReg(XScuGic *InstancePtr, u32 RegOffset, u32 Data)
*
*****************************************************************************/
#define XScuGic_CPUWriteReg(InstancePtr, RegOffset, Data) \
(XScuGic_WriteReg(((InstancePtr)->Config->CpuBaseAddress), (RegOffset), \
					((u32)(Data))))

/****************************************************************************/
/**
*
* Read the given CPU Interface register
*
* @param    InstancePtr is a pointer to the instance to be worked on.
* @param    RegOffset is the register offset to be read
*
* @return   The 32-bit value of the register
*
* @note
* C-style signature:
*    u32 XScuGic_CPUReadReg(XScuGic *InstancePtr, u32 RegOffset)
*
*****************************************************************************/
#define XScuGic_CPUReadReg(InstancePtr, RegOffset) \
	(XScuGic_ReadReg(((InstancePtr)->Config->CpuBaseAddress), (RegOffset)))

/****************************************************************************/
/**
*
* Write the given Distributor Interface register
*
* @param    InstancePtr is a pointer to the instance to be worked on.
* @param    RegOffset is the register offset to be written
* @param    Data is the 32-bit value to write to the register
*
* @return   None.
*
* @note
* C-style signature:
*    void XScuGic_DistWriteReg(XScuGic *InstancePtr, u32 RegOffset, u32 Data)
*
*****************************************************************************/
#define XScuGic_DistWriteReg(InstancePtr, RegOffset, Data) \
(XScuGic_WriteReg(((InstancePtr)->Config->DistBaseAddress), (RegOffset), \
					((u32)(Data))))

/****************************************************************************/
/**
*
* Read the given Distributor Interface register
*
* @param    InstancePtr is a pointer to the instance to be worked on.
* @param    RegOffset is the register offset to be read
*
* @return   The 32-bit value of the register
*
* @note
* C-style signature:
*    u32 XScuGic_DistReadReg(XScuGic *InstancePtr, u32 RegOffset)
*
*****************************************************************************/
#define XScuGic_DistReadReg(InstancePtr, RegOffset) \
(XScuGic_ReadReg(((InstancePtr)->Config->DistBaseAddress), (RegOffset)))

/****************************************************************************/
/**
*
* Write the given ReDistributor Interface register
*
* @param    InstancePtr is a pointer to the instance to be worked on.
* @param    RegOffset is the register offset to be written
* @param    Data is the 32-bit value to write to the register
*
* @return   None.
*
* @note
* C-style signature:
*    void XScuGic_DistWriteReg(XScuGic *InstancePtr, u32 RegOffset, u32 Data)
*
*****************************************************************************/
#define XScuGic_ReDistWriteReg(InstancePtr, RegOffset, Data) \
(XScuGic_WriteReg(((InstancePtr)->Config->DistBaseAddress)+ \
				   XSCUGIC_RDIST_OFFSET, (RegOffset), ((u32)(Data))))

/****************************************************************************/
/**
*
* Read the given ReDistributor Interface register
*
* @param    InstancePtr is a pointer to the instance to be worked on.
* @param    RegOffset is the register offset to be read
*
* @return   The 32-bit value of the register
*
* @note
* C-style signature:
*    u32 XScuGic_DistReadReg(XScuGic *InstancePtr, u32 RegOffset)
*
*****************************************************************************/
#define XScuGic_ReDistReadReg(InstancePtr, RegOffset) \
(XScuGic_ReadReg((((InstancePtr)->Config->DistBaseAddress)+ \
XSCUGIC_RDIST_OFFSET), (RegOffset)))

/****************************************************************************/
/**
*
* Write the given ReDistributor SGI PPI Interface register
*
* @param    InstancePtr is a pointer to the instance to be worked on.
* @param    RegOffset is the register offset to be written
* @param    Data is the 32-bit value to write to the register
*
* @return   None.
*
* @note
* C-style signature:
*    void XScuGic_DistWriteReg(XScuGic *InstancePtr, u32 RegOffset, u32 Data)
*
*****************************************************************************/
#define XScuGic_ReDistSGIPPIWriteReg(InstancePtr, RegOffset, Data) \
(XScuGic_WriteReg(((InstancePtr)->Config->DistBaseAddress)+ \
				   XSCUGIC_RDIST_SGI_PPI_OFFSET, (RegOffset), ((u32)(Data))))

/****************************************************************************/
/**
*
* Read the given ReDistributor SGI PPI Interface register
*
* @param    InstancePtr is a pointer to the instance to be worked on.
* @param    RegOffset is the register offset to be read
*
* @return   The 32-bit value of the register
*
* @note
* C-style signature:
*    u32 XScuGic_DistReadReg(XScuGic *InstancePtr, u32 RegOffset)
*
*****************************************************************************/
#define XScuGic_ReDistSGIPPIReadReg(InstancePtr, RegOffset) \
(XScuGic_ReadReg((((InstancePtr)->Config->DistBaseAddress)+ \
					XSCUGIC_RDIST_SGI_PPI_OFFSET), (RegOffset)))

/****************************************************************************/
/**
* This function enables system register interface for GIC CPU Interface
*
* @param	value to be written
*
* @return	None.
*
* @note        None.
*
*****************************************************************************/
#define XScuGic_Enable_SystemReg_CPU_Interface_EL3() mtcp(S3_6_C12_C12_5, 0xF);
#define XScuGic_Enable_SystemReg_CPU_Interface_EL1() mtcp(S3_0_C12_C12_5, 0xF);
/****************************************************************************/
/**
* This function enables Grou0 interrupts
*
* @param	None.
*
* @return	None.
*
* @note        None.
*
*****************************************************************************/
#define XScuGic_Enable_Group0_Interrupts() mtcp(S3_0_C12_C12_6,0x1);
/****************************************************************************/
/**
* This function enables Group1 interrupts
*
* @param	None.
*
* @return	None.
*
* @note        None.
*
*****************************************************************************/
#if defined (__aarch64__)
#if EL1_NONSECURE
#define XScuGic_Enable_Group1_Interrupts() \
		mtcp (S3_0_C12_C12_7, 0x1 | mfcp(S3_0_C12_C12_7) );
#else
#define XScuGic_Enable_Group1_Interrupts() \
		mtcp (S3_6_C12_C12_7, 0x1 | mfcp(S3_6_C12_C12_7) );
#endif
#endif
/****************************************************************************/
/**
* This function writes to ICC_SGI0R_EL1
*
* @param	value to be written
*
* @return	None.
*
* @note     None.
*
*****************************************************************************/
#define XScuGic_WriteICC_SGI0R_EL1(val) mtcp(S3_0_C12_C11_7,val)

/****************************************************************************/
/**
* This function writes to ICC_SGI1R_EL1
*
* @param	value to be written
*
* @return	None.
*
* @note        None.
*
*****************************************************************************/
#define XScuGic_WriteICC_SGI1R_EL1(val) mtcp(S3_0_C12_C11_5,val)

/****************************************************************************/
/**
* This function reads ICC_SGI1R_EL1 register
*
* @param	None
*
* @return	Value of ICC_SGI1R_EL1 register
*
* @note        None.
*
*****************************************************************************/
#define XScuGic_ReadICC_SGI1R_EL1() mfcp(S3_0_C12_C11_5)
/****************************************************************************/
/**
* This function sets interrupt priority filter
*
* @param	None.
*
* @return	None.
*
* @note        None.
*
*****************************************************************************/
#define XScuGic_set_priority_filter(val)  __asm__ __volatile__("msr  S3_0_C4_C6_0,%0"  : : "r" (val))
/****************************************************************************/
/**
* This function returns interrupt id of highest priority pending interrupt
*
* @param	None.
*
* @return	None.
*
* @note        None.
*
*****************************************************************************/
#if defined (__aarch64__)
#if EL3
#define XScuGic_get_IntID()  mfcp(S3_0_C12_C8_0)
#else
#define XScuGic_get_IntID()  mfcp(S3_0_C12_C12_0)
#endif
#endif
/****************************************************************************/
/**
* This function acks the interrupt
*
* @param	None.
*
* @return	None.
*
* @note        None.
*
*****************************************************************************/
#if  defined (__aarch64__)
#if EL3
#define XScuGic_ack_Int(val)   mtcp(S3_0_C12_C8_1,val)
#else
#define XScuGic_ack_Int(val)   mtcp(S3_0_C12_C12_1,val)
#endif
#endif
/****************************************************************************/
/**
* This macro returns bit position for the specific interrupt's trigger type
* configuration within GICR_ICFGR0/GICR_ICFGR1 register
*
* @param	None.
*
* @return	None.
*
* @note        None.
*
*****************************************************************************/
#define XScuGic_Get_Rdist_Int_Trigger_Index(IntrId)  (((Int_Id%16) & 0x1f) << 2) +1
/************************** Function Prototypes ******************************/

/*
 * Required functions in xscugic.c
 */

s32  XScuGic_Connect(XScuGic *InstancePtr, u32 Int_Id,
			Xil_InterruptHandler Handler, void *CallBackRef);
void XScuGic_Disconnect(XScuGic *InstancePtr, u32 Int_Id);

void XScuGic_Enable(XScuGic *InstancePtr, u32 Int_Id);
void XScuGic_Disable(XScuGic *InstancePtr, u32 Int_Id);

s32  XScuGic_CfgInitialize(XScuGic *InstancePtr, XScuGic_Config *ConfigPtr,
							u32 EffectiveAddr);

s32  XScuGic_SoftwareIntr(XScuGic *InstancePtr, u32 Int_Id, u32 Cpu_Identifier);

void XScuGic_GetPriorityTriggerType(XScuGic *InstancePtr, u32 Int_Id,
					u8 *Priority, u8 *Trigger);
void XScuGic_SetPriorityTriggerType(XScuGic *InstancePtr, u32 Int_Id,
					u8 Priority, u8 Trigger);
void XScuGic_InterruptMaptoCpu(XScuGic *InstancePtr, u8 Cpu_Identifier, u32 Int_Id);
void XScuGic_InterruptUnmapFromCpu(XScuGic *InstancePtr, u8 Cpu_Identifier, u32 Int_Id);
void XScuGic_UnmapAllInterruptsFromCpu(XScuGic *InstancePtr, u8 Cpu_Identifier);
void XScuGic_Stop(XScuGic *InstancePtr);
void XScuGic_SetCpuID(u32 CpuCoreId);
u32 XScuGic_GetCpuID(void);
u8 XScuGic_IsInitialized(u32 DeviceId);
/*
 * Initialization functions in xscugic_sinit.c
 */
XScuGic_Config *XScuGic_LookupConfig(u16 DeviceId);
XScuGic_Config *XScuGic_LookupConfigBaseAddr(UINTPTR BaseAddress);

/*
 * Interrupt functions in xscugic_intr.c
 */
void XScuGic_InterruptHandler(XScuGic *InstancePtr);

/*
 * Self-test functions in xscugic_selftest.c
 */
s32  XScuGic_SelfTest(XScuGic *InstancePtr);

#if defined (GICv3)
void XScuGic_MarkCoreAsleep(XScuGic *InstancePtr);
void XScuGic_MarkCoreAwake(XScuGic *InstancePtr);
#endif
#ifdef __cplusplus
}
#endif

#endif            /* end of protection macro */
/** @} */
