blob: f6afc0e5ac381a97a898c16ffbd302341dd0b155 [file] [log] [blame]
/******************************************************************************
*
* Copyright (C) 2010 - 2018 Xilinx, Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* Use of the Software is limited solely to applications:
* (a) running on a Xilinx device, or
* (b) that interact with a Xilinx device through a bus or interconnect.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of the Xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this Software without prior written authorization from Xilinx.
*
******************************************************************************/
/*****************************************************************************/
/**
*
* @file xscugic.c
* @addtogroup scugic_v3_8
* @{
*
* Contains required functions for the XScuGic driver for the Interrupt
* Controller. See xscugic.h for a detailed description of the driver.
*
* <pre>
* MODIFICATION HISTORY:
*
* Ver Who Date Changes
* ----- ---- -------- --------------------------------------------------------
* 1.00a drg 01/19/10 First release
* 1.01a sdm 11/09/11 Changes are made in function XScuGic_CfgInitialize. Since
* "Config" entry is now made as pointer in the XScuGic
* structure, necessary changes are made.
* The HandlerTable can now be populated through the low
* level routine XScuGic_RegisterHandler added in this
* release. Hence necessary checks are added not to
* overwrite the HandlerTable entriesin function
* XScuGic_CfgInitialize.
* 1.03a srt 02/27/13 Added APIs
* - XScuGic_SetPriTrigTypeByDistAddr()
* - XScuGic_GetPriTrigTypeByDistAddr()
* Removed Offset calculation macros, defined in _hw.h
* (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.06a asa 16/11/13 Fix for CR#749178. Assignment for EffectiveAddr
* in function XScuGic_CfgInitialize is removed as it was
* a bug.
* 3.00 kvn 02/13/14 Modified code for MISRA-C:2012 compliance.
* 3.01 pkp 06/19/15 Added XScuGic_InterruptMaptoCpu API for an interrupt
* target CPU mapping
* 3.02 pkp 11/09/15 Modified DistributorInit function for AMP case to add
* the current cpu to interrupt processor targets registers
* 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.3 pkp 05/12/16 Modified XScuGic_InterruptMaptoCpu to write proper value
* to interrupt target register to fix CR#951848
*
* 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.4 mus 09/08/16 Added assert to avoid invalid access of GIC from CPUID 1
* for single core zynq-7000s
* 3.5 mus 10/05/16 Modified DistributorInit function to avoid re-initialization of
* distributor,If it is already initialized by other CPU.
* 3.5 pkp 10/17/16 Modified XScuGic_InterruptMaptoCpu to correct the CPU Id value
* and properly mask interrupt target processor value to modify
* interrupt target processor register for a given interrupt ID
* and cpu ID
* 3.6 pkp 20/01/17 Added new API XScuGic_Stop to Disable distributor and
* interrupts in case they are being used only by current cpu.
* It also removes current cpu from interrupt target registers
* for all interrupts.
* 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.
* 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. It fixes CR#992490.
*
* </pre>
*
******************************************************************************/
/***************************** Include Files *********************************/
#include "xil_types.h"
#include "xil_assert.h"
#include "xscugic.h"
/************************** Constant Definitions *****************************/
/**************************** Type Definitions *******************************/
/***************** Macros (Inline Functions) Definitions *********************/
/************************** Variable Definitions *****************************/
static u32 CpuId = XPAR_CPU_ID; /**< CPU Core identifier */
/************************** Function Prototypes ******************************/
static void StubHandler(void *CallBackRef);
/*****************************************************************************/
/**
*
* DoDistributorInit initializes the distributor of the GIC. The
* initialization entails:
*
* - Write the trigger mode, priority and target CPU
* - All interrupt sources are disabled
* - Enable the distributor
*
* @param InstancePtr is a pointer to the XScuGic instance.
* @param CpuID is the Cpu ID to be initialized.
*
* @return None
*
* @note None.
*
******************************************************************************/
static void DoDistributorInit(XScuGic *InstancePtr, u32 CpuID)
{
u32 Int_Id;
u32 LocalCpuID = CpuID;
XScuGic_DistWriteReg(InstancePtr, XSCUGIC_DIST_EN_OFFSET, 0U);
/*
* Set the security domains in the int_security registers for
* non-secure interrupts
* All are secure, so leave at the default. Set to 1 for non-secure
* interrupts.
*/
/*
* For the Shared Peripheral Interrupts INT_ID[MAX..32], set:
*/
/*
* 1. The trigger mode in the int_config register
* Only write to the SPI interrupts, so start at 32
*/
for (Int_Id = 32U; Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS; Int_Id=Int_Id+16U) {
/*
* Each INT_ID uses two bits, or 16 INT_ID per register
* Set them all to be level sensitive, active HIGH.
*/
XScuGic_DistWriteReg(InstancePtr,
XSCUGIC_INT_CFG_OFFSET_CALC(Int_Id),
0U);
}
#define DEFAULT_PRIORITY 0xa0a0a0a0U
for (Int_Id = 0U; Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS; Int_Id=Int_Id+4U) {
/*
* 2. The priority using int the priority_level register
* The priority_level and spi_target registers use one byte per
* INT_ID.
* Write a default value that can be changed elsewhere.
*/
XScuGic_DistWriteReg(InstancePtr,
XSCUGIC_PRIORITY_OFFSET_CALC(Int_Id),
DEFAULT_PRIORITY);
}
for (Int_Id = 32U; Int_Id<XSCUGIC_MAX_NUM_INTR_INPUTS;Int_Id=Int_Id+4U) {
/*
* 3. The CPU interface in the spi_target register
* Only write to the SPI interrupts, so start at 32
*/
LocalCpuID |= LocalCpuID << 8U;
LocalCpuID |= LocalCpuID << 16U;
XScuGic_DistWriteReg(InstancePtr,
XSCUGIC_SPI_TARGET_OFFSET_CALC(Int_Id),
LocalCpuID);
}
for (Int_Id = 0U; Int_Id<XSCUGIC_MAX_NUM_INTR_INPUTS;Int_Id=Int_Id+32U) {
/*
* 4. Enable the SPI using the enable_set register. Leave all
* disabled for now.
*/
XScuGic_DistWriteReg(InstancePtr,
XSCUGIC_EN_DIS_OFFSET_CALC(XSCUGIC_DISABLE_OFFSET, Int_Id),
0xFFFFFFFFU);
}
XScuGic_DistWriteReg(InstancePtr, XSCUGIC_DIST_EN_OFFSET,
XSCUGIC_EN_INT_MASK);
}
/*****************************************************************************/
/**
*
* DistributorInit initializes the distributor of the GIC. It calls
* DoDistributorInit to finish the initialization.
*
* @param InstancePtr is a pointer to the XScuGic instance.
* @param CpuID is the Cpu ID to be initialized.
*
* @return None
*
* @note None.
*
******************************************************************************/
static void DistributorInit(XScuGic *InstancePtr, u32 CpuID)
{
u32 Int_Id;
u32 LocalCpuID = CpuID;
u32 RegValue;
#if USE_AMP==1 && (defined (ARMA9) || defined(__aarch64__))
#warning "Building GIC for AMP"
/*
* GIC initialization is taken care by master CPU in
* openamp configuration, so do nothing and return.
*/
return;
#endif
RegValue = XScuGic_DistReadReg(InstancePtr, XSCUGIC_DIST_EN_OFFSET);
if ((RegValue & XSCUGIC_EN_INT_MASK) == 0U) {
Xil_AssertVoid(InstancePtr != NULL);
DoDistributorInit(InstancePtr, CpuID);
return;
}
/*
* The overall distributor should not be initialized in AMP case where
* another CPU is taking care of it.
*/
LocalCpuID |= LocalCpuID << 8U;
LocalCpuID |= LocalCpuID << 16U;
for (Int_Id = 32U; Int_Id<XSCUGIC_MAX_NUM_INTR_INPUTS;Int_Id=Int_Id+4U) {
RegValue = XScuGic_DistReadReg(InstancePtr,
XSCUGIC_SPI_TARGET_OFFSET_CALC(Int_Id));
RegValue |= LocalCpuID;
XScuGic_DistWriteReg(InstancePtr,
XSCUGIC_SPI_TARGET_OFFSET_CALC(Int_Id),
RegValue);
}
}
/*****************************************************************************/
/**
*
* CPUInitialize initializes the CPU Interface of the GIC. The initialization entails:
*
* - Set the priority of the CPU
* - Enable the CPU interface
*
* @param InstancePtr is a pointer to the XScuGic instance.
*
* @return None
*
* @note None.
*
******************************************************************************/
static void CPUInitialize(XScuGic *InstancePtr)
{
/*
* Program the priority mask of the CPU using the Priority mask register
*/
XScuGic_CPUWriteReg(InstancePtr, XSCUGIC_CPU_PRIOR_OFFSET, 0xF0U);
/*
* If the CPU operates in both security domains, set parameters in the
* control_s register.
* 1. Set FIQen=1 to use FIQ for secure interrupts,
* 2. Program the AckCtl bit
* 3. Program the SBPR bit to select the binary pointer behavior
* 4. Set EnableS = 1 to enable secure interrupts
* 5. Set EnbleNS = 1 to enable non secure interrupts
*/
/*
* If the CPU operates only in the secure domain, setup the
* control_s register.
* 1. Set FIQen=1,
* 2. Set EnableS=1, to enable the CPU interface to signal secure interrupts.
* Only enable the IRQ output unless secure interrupts are needed.
*/
XScuGic_CPUWriteReg(InstancePtr, XSCUGIC_CONTROL_OFFSET, 0x07U);
}
/*****************************************************************************/
/**
*
* CfgInitialize a specific interrupt controller instance/driver. The
* initialization entails:
*
* - Initialize fields of the XScuGic structure
* - Initial vector table with stub function calls
* - All interrupt sources are disabled
*
* @param InstancePtr is a pointer to the XScuGic instance.
* @param ConfigPtr is a pointer to a config table for the particular
* device this driver is associated with.
* @param EffectiveAddr is the device base address in the virtual memory
* address space. The caller is responsible for keeping the address
* mapping from EffectiveAddr to the device physical base address
* unchanged once this function is invoked. Unexpected errors may
* occur if the address mapping changes after this function is
* called. If address translation is not used, use
* Config->BaseAddress for this parameters, passing the physical
* address instead.
*
* @return
* - XST_SUCCESS if initialization was successful
*
* @note None.
*
******************************************************************************/
s32 XScuGic_CfgInitialize(XScuGic *InstancePtr,
XScuGic_Config *ConfigPtr,
u32 EffectiveAddr)
{
u32 Int_Id;
u32 Cpu_Id = CpuId + (u32)1;
(void) EffectiveAddr;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(ConfigPtr != NULL);
/*
* Detect Zynq-7000 base silicon configuration,Dual or Single CPU.
* If it is single CPU cnfiguration then invoke assert for CPU ID=1
*/
#ifdef ARMA9
if ( XPAR_CPU_ID == 0x01 )
{
Xil_AssertNonvoid((Xil_In32(XPS_EFUSE_BASEADDR + EFUSE_STATUS_OFFSET)
& EFUSE_STATUS_CPU_MASK ) == 0);
}
#endif
if(InstancePtr->IsReady != XIL_COMPONENT_IS_READY) {
InstancePtr->IsReady = 0U;
InstancePtr->Config = ConfigPtr;
for (Int_Id = 0U; Int_Id<XSCUGIC_MAX_NUM_INTR_INPUTS;Int_Id++) {
/*
* Initalize the handler to point to a stub to handle an
* interrupt which has not been connected to a handler. Only
* initialize it if the handler is 0 which means it was not
* initialized statically by the tools/user. Set the callback
* reference to this instance so that unhandled interrupts
* can be tracked.
*/
if ((InstancePtr->Config->HandlerTable[Int_Id].Handler == NULL)) {
InstancePtr->Config->HandlerTable[Int_Id].Handler =
StubHandler;
}
InstancePtr->Config->HandlerTable[Int_Id].CallBackRef =
InstancePtr;
}
XScuGic_Stop(InstancePtr);
DistributorInit(InstancePtr, Cpu_Id);
CPUInitialize(InstancePtr);
InstancePtr->IsReady = XIL_COMPONENT_IS_READY;
}
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* Makes the connection between the Int_Id of the interrupt source and the
* associated handler that is to run when the interrupt is recognized. The
* argument provided in this call as the Callbackref is used as the argument
* for the handler when it is called.
*
* @param InstancePtr is a pointer to the XScuGic instance.
* @param Int_Id contains the ID of the interrupt source and should be
* in the range of 0 to XSCUGIC_MAX_NUM_INTR_INPUTS - 1
* @param Handler to the handler for that interrupt.
* @param CallBackRef is the callback reference, usually the instance
* pointer of the connecting driver.
*
* @return
*
* - XST_SUCCESS if the handler was connected correctly.
*
* @note
*
* WARNING: The handler provided as an argument will overwrite any handler
* that was previously connected.
*
****************************************************************************/
s32 XScuGic_Connect(XScuGic *InstancePtr, u32 Int_Id,
Xil_InterruptHandler Handler, void *CallBackRef)
{
/*
* Assert the arguments
*/
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);
Xil_AssertNonvoid(Handler != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
/*
* The Int_Id is used as an index into the table to select the proper
* handler
*/
InstancePtr->Config->HandlerTable[Int_Id].Handler = Handler;
InstancePtr->Config->HandlerTable[Int_Id].CallBackRef = CallBackRef;
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* Updates the interrupt table with the Null Handler and NULL arguments at the
* location pointed at by the Int_Id. This effectively disconnects that interrupt
* source from any handler. The interrupt is disabled also.
*
* @param InstancePtr is a pointer to the XScuGic instance to be worked on.
* @param Int_Id contains the ID of the interrupt source and should
* be in the range of 0 to XSCUGIC_MAX_NUM_INTR_INPUTS - 1
*
* @return None.
*
* @note None.
*
****************************************************************************/
void XScuGic_Disconnect(XScuGic *InstancePtr, u32 Int_Id)
{
u32 Mask;
/*
* Assert the arguments
*/
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
/*
* The Int_Id is used to create the appropriate mask for the
* desired bit position. Int_Id currently limited to 0 - 31
*/
Mask = 0x00000001U << (Int_Id % 32U);
/*
* Disable the interrupt such that it won't occur while disconnecting
* the handler, only disable the specified interrupt id without modifying
* the other interrupt ids
*/
XScuGic_DistWriteReg(InstancePtr, (u32)XSCUGIC_DISABLE_OFFSET +
((Int_Id / 32U) * 4U), Mask);
/*
* Disconnect the handler and connect a stub, the callback reference
* must be set to this instance to allow unhandled interrupts to be
* tracked
*/
InstancePtr->Config->HandlerTable[Int_Id].Handler = StubHandler;
InstancePtr->Config->HandlerTable[Int_Id].CallBackRef = InstancePtr;
}
/*****************************************************************************/
/**
*
* Enables the interrupt source provided as the argument Int_Id. Any pending
* interrupt condition for the specified Int_Id will occur after this function is
* called.
*
* @param InstancePtr is a pointer to the XScuGic instance.
* @param Int_Id contains the ID of the interrupt source and should be
* in the range of 0 to XSCUGIC_MAX_NUM_INTR_INPUTS - 1
*
* @return None.
*
* @note None.
*
****************************************************************************/
void XScuGic_Enable(XScuGic *InstancePtr, u32 Int_Id)
{
u32 Mask;
/*
* Assert the arguments
*/
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
/*
* The Int_Id is used to create the appropriate mask for the
* desired bit position. Int_Id currently limited to 0 - 31
*/
Mask = 0x00000001U << (Int_Id % 32U);
/*
* Enable the selected interrupt source by setting the
* corresponding bit in the Enable Set register.
*/
XScuGic_DistWriteReg(InstancePtr, (u32)XSCUGIC_ENABLE_SET_OFFSET +
((Int_Id / 32U) * 4U), Mask);
}
/*****************************************************************************/
/**
*
* Disables the interrupt source provided as the argument Int_Id such that the
* interrupt controller will not cause interrupts for the specified Int_Id. The
* interrupt controller will continue to hold an interrupt condition for the
* Int_Id, but will not cause an interrupt.
*
* @param InstancePtr is a pointer to the XScuGic instance.
* @param Int_Id contains the ID of the interrupt source and should be
* in the range of 0 to XSCUGIC_MAX_NUM_INTR_INPUTS - 1
*
* @return None.
*
* @note None.
*
****************************************************************************/
void XScuGic_Disable(XScuGic *InstancePtr, u32 Int_Id)
{
u32 Mask;
/*
* Assert the arguments
*/
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
/*
* The Int_Id is used to create the appropriate mask for the
* desired bit position. Int_Id currently limited to 0 - 31
*/
Mask = 0x00000001U << (Int_Id % 32U);
/*
* Disable the selected interrupt source by setting the
* corresponding bit in the IDR.
*/
XScuGic_DistWriteReg(InstancePtr, (u32)XSCUGIC_DISABLE_OFFSET +
((Int_Id / 32U) * 4U), Mask);
}
/*****************************************************************************/
/**
*
* Allows software to simulate an interrupt in the interrupt controller. This
* function will only be successful when the interrupt controller has been
* started in simulation mode. A simulated interrupt allows the interrupt
* controller to be tested without any device to drive an interrupt input
* signal into it.
*
* @param InstancePtr is a pointer to the XScuGic instance.
* @param Int_Id is the software interrupt ID to simulate an interrupt.
* @param Cpu_Id is the list of CPUs to send the interrupt.
*
* @return
*
* XST_SUCCESS if successful, or XST_FAILURE if the interrupt could not be
* simulated
*
* @note None.
*
******************************************************************************/
s32 XScuGic_SoftwareIntr(XScuGic *InstancePtr, u32 Int_Id, u32 Cpu_Id)
{
u32 Mask;
/*
* Assert the arguments
*/
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
Xil_AssertNonvoid(Int_Id <= 15U) ;
Xil_AssertNonvoid(Cpu_Id <= 255U) ;
/*
* The Int_Id is used to create the appropriate mask for the
* desired interrupt. Int_Id currently limited to 0 - 15
* Use the target list for the Cpu ID.
*/
Mask = ((Cpu_Id << 16U) | Int_Id) &
(XSCUGIC_SFI_TRIG_CPU_MASK | XSCUGIC_SFI_TRIG_INTID_MASK);
/*
* Write to the Software interrupt trigger register. Use the appropriate
* CPU Int_Id.
*/
XScuGic_DistWriteReg(InstancePtr, XSCUGIC_SFI_TRIG_OFFSET, Mask);
/* Indicate the interrupt was successfully simulated */
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* A stub for the asynchronous callback. The stub is here in case the upper
* layers forget to set the handler.
*
* @param CallBackRef is a pointer to the upper layer callback reference
*
* @return None.
*
* @note None.
*
******************************************************************************/
static void StubHandler(void *CallBackRef) {
/*
* verify that the inputs are valid
*/
Xil_AssertVoid(CallBackRef != NULL);
/*
* Indicate another unhandled interrupt for stats
*/
((XScuGic *)((void *)CallBackRef))->UnhandledInterrupts++;
}
/****************************************************************************/
/**
* Sets the interrupt priority and trigger type for the specificd IRQ source.
*
* @param InstancePtr is a pointer to the instance to be worked on.
* @param Int_Id is the IRQ source number to modify
* @param Priority is the new priority for the IRQ source. 0 is highest
* priority, 0xF8 (248) is lowest. There are 32 priority levels
* supported with a step of 8. Hence the supported priorities are
* 0, 8, 16, 32, 40 ..., 248.
* @param Trigger is the new trigger type for the IRQ source.
* Each bit pair describes the configuration for an INT_ID.
* SFI Read Only b10 always
* PPI Read Only depending on how the PPIs are configured.
* b01 Active HIGH level sensitive
* b11 Rising edge sensitive
* SPI LSB is read only.
* b01 Active HIGH level sensitive
* b11 Rising edge sensitive/
*
* @return None.
*
* @note None.
*
*****************************************************************************/
void XScuGic_SetPriorityTriggerType(XScuGic *InstancePtr, u32 Int_Id,
u8 Priority, u8 Trigger)
{
u32 RegValue;
u8 LocalPriority;
LocalPriority = Priority;
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
Xil_AssertVoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);
Xil_AssertVoid(Trigger <= (u8)XSCUGIC_INT_CFG_MASK);
Xil_AssertVoid(LocalPriority <= (u8)XSCUGIC_MAX_INTR_PRIO_VAL);
/*
* Determine the register to write to using the Int_Id.
*/
RegValue = XScuGic_DistReadReg(InstancePtr,
XSCUGIC_PRIORITY_OFFSET_CALC(Int_Id));
/*
* The priority bits are Bits 7 to 3 in GIC Priority Register. This
* means the number of priority levels supported are 32 and they are
* in steps of 8. The priorities can be 0, 8, 16, 32, 48, ... etc.
* The lower order 3 bits are masked before putting it in the register.
*/
LocalPriority = LocalPriority & (u8)XSCUGIC_INTR_PRIO_MASK;
/*
* Shift and Mask the correct bits for the priority and trigger in the
* register
*/
RegValue &= ~(XSCUGIC_PRIORITY_MASK << ((Int_Id%4U)*8U));
RegValue |= (u32)LocalPriority << ((Int_Id%4U)*8U);
/*
* Write the value back to the register.
*/
XScuGic_DistWriteReg(InstancePtr, XSCUGIC_PRIORITY_OFFSET_CALC(Int_Id),
RegValue);
/*
* Determine the register to write to using the Int_Id.
*/
RegValue = XScuGic_DistReadReg(InstancePtr,
XSCUGIC_INT_CFG_OFFSET_CALC (Int_Id));
/*
* Shift and Mask the correct bits for the priority and trigger in the
* register
*/
RegValue &= ~(XSCUGIC_INT_CFG_MASK << ((Int_Id%16U)*2U));
RegValue |= (u32)Trigger << ((Int_Id%16U)*2U);
/*
* Write the value back to the register.
*/
XScuGic_DistWriteReg(InstancePtr, XSCUGIC_INT_CFG_OFFSET_CALC(Int_Id),
RegValue);
}
/****************************************************************************/
/**
* Gets the interrupt priority and trigger type for the specificd IRQ source.
*
* @param InstancePtr is a pointer to the instance to be worked on.
* @param Int_Id is the IRQ source number to modify
* @param Priority is a pointer to the value of the priority of the IRQ
* source. This is a return value.
* @param Trigger is pointer to the value of the trigger of the IRQ
* source. This is a return value.
*
* @return None.
*
* @note None
*
*****************************************************************************/
void XScuGic_GetPriorityTriggerType(XScuGic *InstancePtr, u32 Int_Id,
u8 *Priority, u8 *Trigger)
{
u32 RegValue;
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
Xil_AssertVoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);
Xil_AssertVoid(Priority != NULL);
Xil_AssertVoid(Trigger != NULL);
/*
* Determine the register to read to using the Int_Id.
*/
RegValue = XScuGic_DistReadReg(InstancePtr,
XSCUGIC_PRIORITY_OFFSET_CALC(Int_Id));
/*
* Shift and Mask the correct bits for the priority and trigger in the
* register
*/
RegValue = RegValue >> ((Int_Id%4U)*8U);
*Priority = (u8)(RegValue & XSCUGIC_PRIORITY_MASK);
/*
* Determine the register to read to using the Int_Id.
*/
RegValue = XScuGic_DistReadReg(InstancePtr,
XSCUGIC_INT_CFG_OFFSET_CALC (Int_Id));
/*
* Shift and Mask the correct bits for the priority and trigger in the
* register
*/
RegValue = RegValue >> ((Int_Id%16U)*2U);
*Trigger = (u8)(RegValue & XSCUGIC_INT_CFG_MASK);
}
/****************************************************************************/
/**
* Sets the target CPU for the interrupt of a peripheral
*
* @param InstancePtr is a pointer to the instance to be worked on.
* @param Cpu_Id is a CPU number for which the interrupt has to be targeted
* @param Int_Id is the IRQ source number to modify
*
* @return None.
*
* @note None
*
*****************************************************************************/
void XScuGic_InterruptMaptoCpu(XScuGic *InstancePtr, u8 Cpu_Id, u32 Int_Id)
{
u32 RegValue, Offset;
RegValue = XScuGic_DistReadReg(InstancePtr,
XSCUGIC_SPI_TARGET_OFFSET_CALC(Int_Id));
Offset = (Int_Id & 0x3U);
Cpu_Id = (0x1U << Cpu_Id);
RegValue = (RegValue & (~(0xFFU << (Offset*8U))) );
RegValue |= ((Cpu_Id) << (Offset*8U));
XScuGic_DistWriteReg(InstancePtr,
XSCUGIC_SPI_TARGET_OFFSET_CALC(Int_Id),
RegValue);
}
/****************************************************************************/
/**
* Unmaps specific SPI interrupt from the target CPU
*
* @param InstancePtr is a pointer to the instance to be worked on.
* @param Cpu_Id is a CPU number from which the interrupt has to be
* unmapped
* @param Int_Id is the IRQ source number to modify
*
* @return None.
*
* @note None
*
*****************************************************************************/
void XScuGic_InterruptUnmapFromCpu(XScuGic *InstancePtr, u8 Cpu_Id, u32 Int_Id)
{
u32 RegValue;
u8 BitPos;
Xil_AssertVoid(InstancePtr != NULL);
RegValue = XScuGic_DistReadReg(InstancePtr,
XSCUGIC_SPI_TARGET_OFFSET_CALC(Int_Id));
/*
* Identify bit position corresponding to Int_Id and Cpu_Id,
* in interrupt target register and clear it
*/
BitPos = ((Int_Id % 4U) * 8U) + Cpu_Id;
RegValue &= (~ ( 1U << BitPos ));
XScuGic_DistWriteReg(InstancePtr,
XSCUGIC_SPI_TARGET_OFFSET_CALC(Int_Id),
RegValue);
}
/****************************************************************************/
/**
* Unmaps all SPI interrupts from the target CPU
*
* @param InstancePtr is a pointer to the instance to be worked on.
* @param Cpu_Id is a CPU number from which the interrupts has to be
* unmapped
*
* @return None.
*
* @note None
*
*****************************************************************************/
void XScuGic_UnmapAllInterruptsFromCpu(XScuGic *InstancePtr, u8 Cpu_Id)
{
u32 Int_Id;
u32 Target_Cpu;
u32 LocalCpuID = (1U << Cpu_Id);
Xil_AssertVoid(InstancePtr != NULL);
LocalCpuID |= LocalCpuID << 8U;
LocalCpuID |= LocalCpuID << 16U;
for (Int_Id = 32U; Int_Id<XSCUGIC_MAX_NUM_INTR_INPUTS;Int_Id=Int_Id+4U)
{
Target_Cpu = XScuGic_DistReadReg(InstancePtr,
XSCUGIC_SPI_TARGET_OFFSET_CALC(Int_Id));
/* Remove LocalCpuID from interrupt target register */
Target_Cpu &= (~LocalCpuID);
XScuGic_DistWriteReg(InstancePtr,
XSCUGIC_SPI_TARGET_OFFSET_CALC(Int_Id), Target_Cpu);
}
}
/****************************************************************************/
/**
* It checks if the interrupt target register contains all interrupts to be
* targeted for current CPU. If they are programmed to be forwarded to current
* cpu, this API disable all interrupts and disable GIC distributor.
* This API also removes current CPU from interrupt target registers for all
* interrupt.
*
* @param InstancePtr is a pointer to the instance to be worked on.
*
* @return None.
*
* @note None
*
*****************************************************************************/
void XScuGic_Stop(XScuGic *InstancePtr)
{
u32 Int_Id;
u32 RegValue;
u32 Target_Cpu;
u32 DistDisable = 1; /* To track if distributor need to be disabled or not */
u32 LocalCpuID = ((u32)0x1 << CpuId);
Xil_AssertVoid(InstancePtr != NULL);
/* If distributor is already disabled, no need to do anything */
RegValue = XScuGic_DistReadReg(InstancePtr, XSCUGIC_DIST_EN_OFFSET);
if ((RegValue & XSCUGIC_EN_INT_MASK) == 0U) {
return;
}
LocalCpuID |= LocalCpuID << 8U;
LocalCpuID |= LocalCpuID << 16U;
/*
* Check if the interrupt are targeted to current cpu only or not.
* Also remove current cpu from interrupt target register for all
* interrupts.
*/
for (Int_Id = 32U; Int_Id<XSCUGIC_MAX_NUM_INTR_INPUTS;Int_Id=Int_Id+4U) {
Target_Cpu = XScuGic_DistReadReg(InstancePtr,
XSCUGIC_SPI_TARGET_OFFSET_CALC(Int_Id));
if ((Target_Cpu != LocalCpuID) && (Target_Cpu!= 0)) {
/*
* If any other CPU is also programmed to target register, GIC
* distributor can not be disabled.
*/
DistDisable = 0;
}
/* Remove current CPU from interrupt target register */
Target_Cpu &= (~LocalCpuID);
XScuGic_DistWriteReg(InstancePtr,
XSCUGIC_SPI_TARGET_OFFSET_CALC(Int_Id), Target_Cpu);
}
/*
* If GIC distributor is safe to be disabled, disable all the interrupt
* and then disable distributor.
*/
if ( DistDisable == 1) {
for (Int_Id = 0U; Int_Id<XSCUGIC_MAX_NUM_INTR_INPUTS;Int_Id=Int_Id+32U) {
/*
* Disable all the interrupts
*/
XScuGic_DistWriteReg(InstancePtr,
XSCUGIC_EN_DIS_OFFSET_CALC(XSCUGIC_DISABLE_OFFSET,
Int_Id),
0xFFFFFFFFU);
}
XScuGic_DistWriteReg(InstancePtr, XSCUGIC_DIST_EN_OFFSET, 0U);
}
}
/****************************************************************************/
/**
* This updates the CpuId global variable.
*
* @param CpuCoreId is the CPU core number.
*
* @return None.
*
* @note None
*
*****************************************************************************/
void XScuGic_SetCpuID(u32 CpuCoreId)
{
Xil_AssertVoid(CpuCoreId <= 1U);
CpuId = CpuCoreId;
}
/****************************************************************************/
/**
* This function returns the CpuId variable.
*
* @param None.
*
* @return The CPU core number.
*
* @note None.
*
*****************************************************************************/
u32 XScuGic_GetCpuID(void)
{
return CpuId;
}
/** @} */