/** * * CPUInit initializes the CPU Interface of the GIC. The initialization entails: * * - Set the priority of the CPU. * - Enable the CPU interface * * @param ConfigPtr is a pointer to a config table for the particular * device this driver is associated with. * * @return None * * @note None. * ******************************************************************************/ static void CPUInit(XScuGic_Config *Config) { /* * Program the priority mask of the CPU using the Priority mask * register */ XScuGic_WriteReg(Config->CpuBaseAddress, XSCUGIC_CPU_PRIOR_OFFSET, 0xF0); /* * 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_WriteReg(Config->CpuBaseAddress, XSCUGIC_CONTROL_OFFSET, 0x07); }
static void GicCPUInit(u32 BaseAddress) { /* * Program the priority mask of the CPU using the Priority mask register */ XScuGic_WriteReg(BaseAddress, XSCUGIC_CPU_PRIOR_OFFSET, 0xF0); /* * 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. */ XScuGic_WriteReg(BaseAddress, XSCUGIC_CONTROL_OFFSET, 0x01); }
/** * Sets the interrupt priority and trigger type for the specificd IRQ source. * * @param BaseAddr is the device base address * @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 This API has the similar functionality of XScuGic_SetPriority * TriggerType() and should be used when there is no InstancePtr. * *****************************************************************************/ void XScuGic_SetPriTrigTypeByDistAddr(u32 DistBaseAddress, u32 Int_Id, u8 Priority, u8 Trigger) { u32 RegValue; u8 LocalPriority = Priority; Xil_AssertVoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS); Xil_AssertVoid(Trigger <= XSCUGIC_INT_CFG_MASK); Xil_AssertVoid(LocalPriority <= XSCUGIC_MAX_INTR_PRIO_VAL); /* * Determine the register to write to using the Int_Id. */ RegValue = XScuGic_ReadReg(DistBaseAddress, 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 & 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_WriteReg(DistBaseAddress, XSCUGIC_PRIORITY_OFFSET_CALC(Int_Id), RegValue); /* * Determine the register to write to using the Int_Id. */ RegValue = XScuGic_ReadReg(DistBaseAddress, 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_WriteReg(DistBaseAddress, XSCUGIC_INT_CFG_OFFSET_CALC(Int_Id), RegValue); }
/** * * This function is an example of how to use the interrupt controller driver * (XScuGic) and the hardware device. This function is designed to * work without any hardware devices to cause interrupts. It may not return * if the interrupt controller is not properly connected to the processor in * either software or hardware. * * This function relies on the fact that the interrupt controller hardware * has come out of the reset state such that it will allow interrupts to be * simulated by the software. * * @param CpuBaseAddress is Base Address of the Interrupt Controller * Device * * @return XST_SUCCESS to indicate success, otherwise XST_FAILURE * * @note None. * ******************************************************************************/ static int ScuGicLowLevelExample(u32 CpuBaseAddress, u32 DistBaseAddress) { GicDistInit(DistBaseAddress); GicCPUInit(CpuBaseAddress); /* * This step is processor specific, connect the handler for the * interrupt controller to the interrupt source for the processor */ SetupInterruptSystem(); /* * Enable the software interrupts only. */ XScuGic_WriteReg(DistBaseAddress, XSCUGIC_ENABLE_SET_OFFSET, 0x0000FFFF); /* * Cause (simulate) an interrupt so the handler will be called. * This is done by changing the interrupt source to be software driven, * then set a bit which simulates an interrupt. */ XScuGic_WriteReg(DistBaseAddress, XSCUGIC_SFI_TRIG_OFFSET, GIC_DEVICE_INT_MASK); /* * Wait for the interrupt to be processed, if the interrupt does not * occur this loop will wait forever */ while (1) { /* * If the interrupt occurred which is indicated by the global * variable which is set in the device driver handler, then * stop waiting */ if (InterruptProcessed != 0) { break; } } return XST_SUCCESS; }
/** * This function is the primary interrupt handler for the driver. It must be * connected to the interrupt source such that it is called when an interrupt of * the interrupt controller is active. It will resolve which interrupts are * active and enabled and call the appropriate interrupt handler. It uses * the Interrupt Type information to determine when to acknowledge the * interrupt.Highest priority interrupts are serviced first. * * This function assumes that an interrupt vector table has been previously * initialized. It does not verify that entries in the table are valid before * calling an interrupt handler. * * @param DeviceId is the unique identifier for the ScuGic device. * * @return None. * * @note None. * ******************************************************************************/ void XScuGic_DeviceInterruptHandler(void *DeviceId) { u32 InterruptID; u32 IntIDFull; XScuGic_VectorTableEntry *TablePtr; XScuGic_Config *CfgPtr; CfgPtr = &XScuGic_ConfigTable[(INTPTR )DeviceId]; /* * Read the int_ack register to identify the highest priority * interrupt ID and make sure it is valid. Reading Int_Ack will * clear the interrupt in the GIC. */ IntIDFull = XScuGic_ReadReg(CfgPtr->CpuBaseAddress, XSCUGIC_INT_ACK_OFFSET); InterruptID = IntIDFull & XSCUGIC_ACK_INTID_MASK; if(XSCUGIC_MAX_NUM_INTR_INPUTS < InterruptID){ goto IntrExit; } /* * If the interrupt is shared, do some locking here if there are * multiple processors. */ /* * If pre-eption is required: * Re-enable pre-emption by setting the CPSR I bit for non-secure , * interrupts or the F bit for secure interrupts */ /* * If we need to change security domains, issue a SMC instruction here. */ /* * Execute the ISR. Jump into the Interrupt service routine based on * the IRQSource. A software trigger is cleared by the ACK. */ TablePtr = &(CfgPtr->HandlerTable[InterruptID]); if(TablePtr != NULL) { TablePtr->Handler(TablePtr->CallBackRef); } IntrExit: /* * Write to the EOI register, we are all done here. * Let this function return, the boot code will restore the stack. */ XScuGic_WriteReg(CfgPtr->CpuBaseAddress, XSCUGIC_EOI_OFFSET, IntIDFull); /* * Return from the interrupt. Change security domains could happen * here. */ }
/** * * This function is designed to look like an interrupt handler in a device * driver. This is typically a 2nd level handler that is called from the * interrupt controller interrupt handler. This handler would typically * perform device specific processing such as reading and writing the registers * of the device to clear the interrupt condition and pass any data to an * application using the device driver. * * @param CallbackRef is passed back to the device driver's interrupt handler * by the XScuGic driver. It was given to the XScuGic driver in the * XScuGic_Connect() function call. It is typically a pointer to the * device driver instance variable if using the Xilinx Level 1 device * drivers. In this example, we do not care about the callback * reference, so we passed it a 0 when connecting the handler to the * XScuGic driver and we make no use of it here. * * @return None. * * @note None. * ******************************************************************************/ void LowInterruptHandler(void *CallbackRef) { u32 BaseAddress; u32 IntID; if (NULL == CallbackRef) { return; } BaseAddress = (u32)CallbackRef; /* * Read the int_ack register to identify the interrupt and * make sure it is valid. */ IntID = XScuGic_ReadReg(BaseAddress, XSCUGIC_INT_ACK_OFFSET) & XSCUGIC_ACK_INTID_MASK; if(XSCUGIC_MAX_NUM_INTR_INPUTS < IntID){ return; } /* * If the interrupt is shared, do some locking here if there are * multiple processors. */ /* * Execute the ISR. For this example set the global to 1. * The software trigger is cleared by the ACK. */ InterruptProcessed = 1; /* * Write to the EOI register, we are all done here. * Let this function return, the boot code will restore the stack. */ XScuGic_WriteReg(BaseAddress, XSCUGIC_EOI_OFFSET, IntID); }
/** * * DistInit 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 DistInit(XScuGic_Config *Config, u32 CpuID) { u32 Int_Id; #if USE_AMP==1 #warning "Building GIC for AMP" /* * The distrubutor should not be initialized by FreeRTOS in the case of * AMP -- it is assumed that Linux is the master of this device in that * case. */ return; #endif XScuGic_WriteReg(Config->DistBaseAddress, XSCUGIC_DIST_EN_OFFSET, 0UL); /* * 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 = 32; Int_Id<XSCUGIC_MAX_NUM_INTR_INPUTS;Int_Id+=16) { /* * Each INT_ID uses two bits, or 16 INT_ID per register * Set them all to be level sensitive, active HIGH. */ XScuGic_WriteReg(Config->DistBaseAddress, XSCUGIC_INT_CFG_OFFSET_CALC(Int_Id), 0UL); } #define DEFAULT_PRIORITY 0xa0a0a0a0UL for (Int_Id = 0; Int_Id<XSCUGIC_MAX_NUM_INTR_INPUTS;Int_Id+=4) { /* * 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_WriteReg(Config->DistBaseAddress, XSCUGIC_PRIORITY_OFFSET_CALC(Int_Id), DEFAULT_PRIORITY); } for (Int_Id = 32; Int_Id<XSCUGIC_MAX_NUM_INTR_INPUTS;Int_Id+=4) { /* * 3. The CPU interface in the spi_target register * Only write to the SPI interrupts, so start at 32 */ CpuID |= CpuID << 8; CpuID |= CpuID << 16; XScuGic_WriteReg(Config->DistBaseAddress, XSCUGIC_SPI_TARGET_OFFSET_CALC(Int_Id), CpuID); } for (Int_Id = 0; Int_Id<XSCUGIC_MAX_NUM_INTR_INPUTS;Int_Id+=32) { /* * 4. Enable the SPI using the enable_set register. Leave all disabled * for now. */ XScuGic_WriteReg(Config->DistBaseAddress, XSCUGIC_ENABLE_DISABLE_OFFSET_CALC(XSCUGIC_DISABLE_OFFSET, Int_Id), 0xFFFFFFFFUL); } XScuGic_WriteReg(Config->DistBaseAddress, XSCUGIC_DIST_EN_OFFSET, XSCUGIC_EN_INT_MASK); }
static void GicDistInit(u32 BaseAddress) { u32 Int_Id; u32 MaxInt_In; XScuGic_WriteReg(BaseAddress, XSCUGIC_DIST_EN_OFFSET, 0UL); MaxInt_In = XScuGic_ReadReg(BaseAddress, XSCUGIC_IC_TYPE_OFFSET); /* * 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 */ for(Int_Id = 32; Int_Id<XSCUGIC_MAX_NUM_INTR_INPUTS;Int_Id+=16) { /* * Each INT_ID uses two bits, or 16 INT_ID per register * Set them all to be level sensitive, active HIGH. */ XScuGic_WriteReg(BaseAddress, XSCUGIC_INT_CFG_OFFSET + (Int_Id * 4)/16, 0UL); } #define DEFAULT_PRIORITY 0xa0a0a0a0UL #define DEFAULT_TARGET 0x01010101UL for(Int_Id = 0; Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS; Int_Id+=4){ /* * 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_WriteReg(BaseAddress, XSCUGIC_PRIORITY_OFFSET +((Int_Id *4)/4), DEFAULT_PRIORITY); } for(Int_Id = 32; Int_Id<XSCUGIC_MAX_NUM_INTR_INPUTS;Int_Id+=4){ /* * 3. The CPU interface in the spi_target register */ XScuGic_WriteReg(BaseAddress, XSCUGIC_SPI_TARGET_OFFSET +((Int_Id *4)/4), DEFAULT_TARGET); } for(Int_Id = 0; Int_Id<XSCUGIC_MAX_NUM_INTR_INPUTS;Int_Id+=32){ /* * 4. Enable the SPI using the enable_set register. * Leave all disabled for now. */ XScuGic_WriteReg(BaseAddress, XSCUGIC_DISABLE_OFFSET +((Int_Id *4)/32), 0xFFFFFFFFUL); } XScuGic_WriteReg(BaseAddress, XSCUGIC_DIST_EN_OFFSET, 0x01UL); }