1 /******************************************************************************
3 * Copyright (C) 2010 - 2014 Xilinx, Inc. All rights reserved.
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * Use of the Software is limited solely to applications:
16 * (a) running on a Xilinx device, or
17 * (b) that interact with a Xilinx device through a bus or interconnect.
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
24 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 * Except as contained in this notice, the name of the Xilinx shall not be used
28 * in advertising or otherwise to promote the sale, use or other dealings in
29 * this Software without prior written authorization from Xilinx.
31 ******************************************************************************/
32 /*****************************************************************************/
36 * @addtogroup scugic_v2_1
39 * Contains required functions for the XScuGic driver for the Interrupt
40 * Controller. See xscugic.h for a detailed description of the driver.
43 * MODIFICATION HISTORY:
45 * Ver Who Date Changes
46 * ----- ---- -------- --------------------------------------------------------
47 * 1.00a drg 01/19/10 First release
48 * 1.01a sdm 11/09/11 Changes are made in function XScuGic_CfgInitialize. Since
49 * "Config" entry is now made as pointer in the XScuGic
50 * structure, necessary changes are made.
51 * The HandlerTable can now be populated through the low
52 * level routine XScuGic_RegisterHandler added in this
53 * release. Hence necessary checks are added not to
54 * overwrite the HandlerTable entriesin function
55 * XScuGic_CfgInitialize.
56 * 1.03a srt 02/27/13 Added APIs
57 * - XScuGic_SetPriTrigTypeByDistAddr()
58 * - XScuGic_GetPriTrigTypeByDistAddr()
59 * Removed Offset calculation macros, defined in _hw.h
61 * Added support to direct interrupts to the appropriate CPU. Earlier
62 * interrupts were directed to CPU1 (hard coded). Now depending
63 * upon the CPU selected by the user (xparameters.h), interrupts
64 * will be directed to the relevant CPU. This fixes CR 699688.
66 * 1.04a hk 05/04/13 Assigned EffectiveAddr to CpuBaseAddress in
67 * XScuGic_CfgInitialize. Fix for CR#704400 to remove warnings.
68 * Moved functions XScuGic_SetPriTrigTypeByDistAddr and
69 * XScuGic_GetPriTrigTypeByDistAddr to xscugic_hw.c.
70 * This is fix for CR#705621.
71 * 1.06a asa 16/11/13 Fix for CR#749178. Assignment for EffectiveAddr
72 * in function XScuGic_CfgInitialize is removed as it was
77 ******************************************************************************/
79 /***************************** Include Files *********************************/
80 #include "xparameters.h"
81 #include "xil_types.h"
82 #include "xil_assert.h"
86 /************************** Constant Definitions *****************************/
89 /**************************** Type Definitions *******************************/
92 /***************** Macros (Inline Functions) Definitions *********************/
94 /************************** Variable Definitions *****************************/
96 /************************** Function Prototypes ******************************/
98 static void StubHandler(void *CallBackRef);
100 /*****************************************************************************/
103 * DistInit initializes the distributor of the GIC. The
104 * initialization entails:
106 * - Write the trigger mode, priority and target CPU
107 * - All interrupt sources are disabled
108 * - Enable the distributor
110 * @param InstancePtr is a pointer to the XScuGic instance.
111 * @param CpuID is the Cpu ID to be initialized.
117 ******************************************************************************/
118 static void DistInit(XScuGic *InstancePtr, u32 CpuID)
123 #warning "Building GIC for AMP"
126 * The distrubutor should not be initialized by FreeRTOS in the case of
127 * AMP -- it is assumed that Linux is the master of this device in that
133 XScuGic_DistWriteReg(InstancePtr, XSCUGIC_DIST_EN_OFFSET, 0UL);
136 * Set the security domains in the int_security registers for
137 * non-secure interrupts
138 * All are secure, so leave at the default. Set to 1 for non-secure
143 * For the Shared Peripheral Interrupts INT_ID[MAX..32], set:
147 * 1. The trigger mode in the int_config register
148 * Only write to the SPI interrupts, so start at 32
150 for (Int_Id = 32; Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS; Int_Id+=16) {
152 * Each INT_ID uses two bits, or 16 INT_ID per register
153 * Set them all to be level sensitive, active HIGH.
155 XScuGic_DistWriteReg(InstancePtr,
156 XSCUGIC_INT_CFG_OFFSET_CALC(Int_Id),
161 #define DEFAULT_PRIORITY 0xa0a0a0a0UL
162 for (Int_Id = 0; Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS; Int_Id+=4) {
164 * 2. The priority using int the priority_level register
165 * The priority_level and spi_target registers use one byte per
167 * Write a default value that can be changed elsewhere.
169 XScuGic_DistWriteReg(InstancePtr,
170 XSCUGIC_PRIORITY_OFFSET_CALC(Int_Id),
174 for (Int_Id = 32; Int_Id<XSCUGIC_MAX_NUM_INTR_INPUTS;Int_Id+=4) {
176 * 3. The CPU interface in the spi_target register
177 * Only write to the SPI interrupts, so start at 32
180 CpuID |= CpuID << 16;
182 XScuGic_DistWriteReg(InstancePtr,
183 XSCUGIC_SPI_TARGET_OFFSET_CALC(Int_Id),
187 for (Int_Id = 0; Int_Id<XSCUGIC_MAX_NUM_INTR_INPUTS;Int_Id+=32) {
189 * 4. Enable the SPI using the enable_set register. Leave all
192 XScuGic_DistWriteReg(InstancePtr,
193 XSCUGIC_ENABLE_DISABLE_OFFSET_CALC(XSCUGIC_DISABLE_OFFSET, Int_Id),
198 XScuGic_DistWriteReg(InstancePtr, XSCUGIC_DIST_EN_OFFSET,
199 XSCUGIC_EN_INT_MASK);
203 /*****************************************************************************/
206 * CPUInit initializes the CPU Interface of the GIC. The initialization entails:
208 * - Set the priority of the CPU
209 * - Enable the CPU interface
211 * @param InstancePtr is a pointer to the XScuGic instance.
217 ******************************************************************************/
218 static void CPUInit(XScuGic *InstancePtr)
221 * Program the priority mask of the CPU using the Priority mask register
223 XScuGic_CPUWriteReg(InstancePtr, XSCUGIC_CPU_PRIOR_OFFSET, 0xF0);
227 * If the CPU operates in both security domains, set parameters in the
228 * control_s register.
229 * 1. Set FIQen=1 to use FIQ for secure interrupts,
230 * 2. Program the AckCtl bit
231 * 3. Program the SBPR bit to select the binary pointer behavior
232 * 4. Set EnableS = 1 to enable secure interrupts
233 * 5. Set EnbleNS = 1 to enable non secure interrupts
237 * If the CPU operates only in the secure domain, setup the
238 * control_s register.
240 * 2. Set EnableS=1, to enable the CPU interface to signal secure interrupts.
241 * Only enable the IRQ output unless secure interrupts are needed.
243 XScuGic_CPUWriteReg(InstancePtr, XSCUGIC_CONTROL_OFFSET, 0x07);
247 /*****************************************************************************/
250 * CfgInitialize a specific interrupt controller instance/driver. The
251 * initialization entails:
253 * - Initialize fields of the XScuGic structure
254 * - Initial vector table with stub function calls
255 * - All interrupt sources are disabled
257 * @param InstancePtr is a pointer to the XScuGic instance.
258 * @param ConfigPtr is a pointer to a config table for the particular
259 * device this driver is associated with.
260 * @param EffectiveAddr is the device base address in the virtual memory
261 * address space. The caller is responsible for keeping the address
262 * mapping from EffectiveAddr to the device physical base address
263 * unchanged once this function is invoked. Unexpected errors may
264 * occur if the address mapping changes after this function is
265 * called. If address translation is not used, use
266 * Config->BaseAddress for this parameters, passing the physical
270 * - XST_SUCCESS if initialization was successful
274 ******************************************************************************/
275 int XScuGic_CfgInitialize(XScuGic *InstancePtr,
276 XScuGic_Config *ConfigPtr,
280 u8 Cpu_Id = XPAR_CPU_ID + 1;
281 (void) EffectiveAddr;
283 Xil_AssertNonvoid(InstancePtr != NULL);
284 Xil_AssertNonvoid(ConfigPtr != NULL);
286 if(InstancePtr->IsReady != XIL_COMPONENT_IS_READY) {
288 InstancePtr->IsReady = 0;
289 InstancePtr->Config = ConfigPtr;
292 for (Int_Id = 0; Int_Id<XSCUGIC_MAX_NUM_INTR_INPUTS;Int_Id++) {
294 * Initalize the handler to point to a stub to handle an
295 * interrupt which has not been connected to a handler. Only
296 * initialize it if the handler is 0 which means it was not
297 * initialized statically by the tools/user. Set the callback
298 * reference to this instance so that unhandled interrupts
301 if ((InstancePtr->Config->HandlerTable[Int_Id].Handler == 0)) {
302 InstancePtr->Config->HandlerTable[Int_Id].Handler =
305 InstancePtr->Config->HandlerTable[Int_Id].CallBackRef =
309 DistInit(InstancePtr, Cpu_Id);
310 CPUInit(InstancePtr);
312 InstancePtr->IsReady = XIL_COMPONENT_IS_READY;
318 /*****************************************************************************/
321 * Makes the connection between the Int_Id of the interrupt source and the
322 * associated handler that is to run when the interrupt is recognized. The
323 * argument provided in this call as the Callbackref is used as the argument
324 * for the handler when it is called.
326 * @param InstancePtr is a pointer to the XScuGic instance.
327 * @param Int_Id contains the ID of the interrupt source and should be
328 * in the range of 0 to XSCUGIC_MAX_NUM_INTR_INPUTS - 1
329 * @param Handler to the handler for that interrupt.
330 * @param CallBackRef is the callback reference, usually the instance
331 * pointer of the connecting driver.
335 * - XST_SUCCESS if the handler was connected correctly.
339 * WARNING: The handler provided as an argument will overwrite any handler
340 * that was previously connected.
342 ****************************************************************************/
343 int XScuGic_Connect(XScuGic *InstancePtr, u32 Int_Id,
344 Xil_InterruptHandler Handler, void *CallBackRef)
347 * Assert the arguments
349 Xil_AssertNonvoid(InstancePtr != NULL);
350 Xil_AssertNonvoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);
351 Xil_AssertNonvoid(Handler != NULL);
352 Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
355 * The Int_Id is used as an index into the table to select the proper
358 InstancePtr->Config->HandlerTable[Int_Id].Handler = Handler;
359 InstancePtr->Config->HandlerTable[Int_Id].CallBackRef = CallBackRef;
364 /*****************************************************************************/
367 * Updates the interrupt table with the Null Handler and NULL arguments at the
368 * location pointed at by the Int_Id. This effectively disconnects that interrupt
369 * source from any handler. The interrupt is disabled also.
371 * @param InstancePtr is a pointer to the XScuGic instance to be worked on.
372 * @param Int_Id contains the ID of the interrupt source and should
373 * be in the range of 0 to XSCUGIC_MAX_NUM_INTR_INPUTS - 1
379 ****************************************************************************/
380 void XScuGic_Disconnect(XScuGic *InstancePtr, u32 Int_Id)
385 * Assert the arguments
387 Xil_AssertVoid(InstancePtr != NULL);
388 Xil_AssertVoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);
389 Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
392 * The Int_Id is used to create the appropriate mask for the
393 * desired bit position. Int_Id currently limited to 0 - 31
395 Mask = 0x00000001 << (Int_Id % 32);
398 * Disable the interrupt such that it won't occur while disconnecting
399 * the handler, only disable the specified interrupt id without modifying
400 * the other interrupt ids
402 XScuGic_DistWriteReg(InstancePtr, XSCUGIC_DISABLE_OFFSET +
403 ((Int_Id / 32) * 4), Mask);
406 * Disconnect the handler and connect a stub, the callback reference
407 * must be set to this instance to allow unhandled interrupts to be
410 InstancePtr->Config->HandlerTable[Int_Id].Handler = StubHandler;
411 InstancePtr->Config->HandlerTable[Int_Id].CallBackRef = InstancePtr;
414 /*****************************************************************************/
417 * Enables the interrupt source provided as the argument Int_Id. Any pending
418 * interrupt condition for the specified Int_Id will occur after this function is
421 * @param InstancePtr is a pointer to the XScuGic instance.
422 * @param Int_Id contains the ID of the interrupt source and should be
423 * in the range of 0 to XSCUGIC_MAX_NUM_INTR_INPUTS - 1
429 ****************************************************************************/
430 void XScuGic_Enable(XScuGic *InstancePtr, u32 Int_Id)
435 * Assert the arguments
437 Xil_AssertVoid(InstancePtr != NULL);
438 Xil_AssertVoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);
439 Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
442 * The Int_Id is used to create the appropriate mask for the
443 * desired bit position. Int_Id currently limited to 0 - 31
445 Mask = 0x00000001 << (Int_Id % 32);
448 * Enable the selected interrupt source by setting the
449 * corresponding bit in the Enable Set register.
451 XScuGic_DistWriteReg(InstancePtr, XSCUGIC_ENABLE_SET_OFFSET +
452 ((Int_Id / 32) * 4), Mask);
455 /*****************************************************************************/
458 * Disables the interrupt source provided as the argument Int_Id such that the
459 * interrupt controller will not cause interrupts for the specified Int_Id. The
460 * interrupt controller will continue to hold an interrupt condition for the
461 * Int_Id, but will not cause an interrupt.
463 * @param InstancePtr is a pointer to the XScuGic instance.
464 * @param Int_Id contains the ID of the interrupt source and should be
465 * in the range of 0 to XSCUGIC_MAX_NUM_INTR_INPUTS - 1
471 ****************************************************************************/
472 void XScuGic_Disable(XScuGic *InstancePtr, u32 Int_Id)
477 * Assert the arguments
479 Xil_AssertVoid(InstancePtr != NULL);
480 Xil_AssertVoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);
481 Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
484 * The Int_Id is used to create the appropriate mask for the
485 * desired bit position. Int_Id currently limited to 0 - 31
487 Mask = 0x00000001 << (Int_Id % 32);
490 * Disable the selected interrupt source by setting the
491 * corresponding bit in the IDR.
493 XScuGic_DistWriteReg(InstancePtr, XSCUGIC_DISABLE_OFFSET +
494 ((Int_Id / 32) * 4), Mask);
497 /*****************************************************************************/
500 * Allows software to simulate an interrupt in the interrupt controller. This
501 * function will only be successful when the interrupt controller has been
502 * started in simulation mode. A simulated interrupt allows the interrupt
503 * controller to be tested without any device to drive an interrupt input
506 * @param InstancePtr is a pointer to the XScuGic instance.
507 * @param Int_Id is the software interrupt ID to simulate an interrupt.
508 * @param Cpu_Id is the list of CPUs to send the interrupt.
512 * XST_SUCCESS if successful, or XST_FAILURE if the interrupt could not be
517 ******************************************************************************/
518 int XScuGic_SoftwareIntr(XScuGic *InstancePtr, u32 Int_Id, u32 Cpu_Id)
523 * Assert the arguments
525 Xil_AssertNonvoid(InstancePtr != NULL);
526 Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
527 Xil_AssertNonvoid(Int_Id <= 15) ;
528 Xil_AssertNonvoid(Cpu_Id <= 255) ;
532 * The Int_Id is used to create the appropriate mask for the
533 * desired interrupt. Int_Id currently limited to 0 - 15
534 * Use the target list for the Cpu ID.
536 Mask = ((Cpu_Id << 16) | Int_Id) &
537 (XSCUGIC_SFI_TRIG_CPU_MASK | XSCUGIC_SFI_TRIG_INTID_MASK);
540 * Write to the Software interrupt trigger register. Use the appropriate
543 XScuGic_DistWriteReg(InstancePtr, XSCUGIC_SFI_TRIG_OFFSET, Mask);
545 /* Indicate the interrupt was successfully simulated */
550 /*****************************************************************************/
553 * A stub for the asynchronous callback. The stub is here in case the upper
554 * layers forget to set the handler.
556 * @param CallBackRef is a pointer to the upper layer callback reference
562 ******************************************************************************/
563 static void StubHandler(void *CallBackRef) {
565 * verify that the inputs are valid
567 Xil_AssertVoid(CallBackRef != NULL);
570 * Indicate another unhandled interrupt for stats
572 ((XScuGic *)CallBackRef)->UnhandledInterrupts++;
575 /****************************************************************************/
577 * Sets the interrupt priority and trigger type for the specificd IRQ source.
579 * @param InstancePtr is a pointer to the instance to be worked on.
580 * @param Int_Id is the IRQ source number to modify
581 * @param Priority is the new priority for the IRQ source. 0 is highest
582 * priority, 0xF8 (248) is lowest. There are 32 priority levels
583 * supported with a step of 8. Hence the supported priorities are
584 * 0, 8, 16, 32, 40 ..., 248.
585 * @param Trigger is the new trigger type for the IRQ source.
586 * Each bit pair describes the configuration for an INT_ID.
587 * SFI Read Only b10 always
588 * PPI Read Only depending on how the PPIs are configured.
589 * b01 Active HIGH level sensitive
590 * b11 Rising edge sensitive
591 * SPI LSB is read only.
592 * b01 Active HIGH level sensitive
593 * b11 Rising edge sensitive/
599 *****************************************************************************/
600 void XScuGic_SetPriorityTriggerType(XScuGic *InstancePtr, u32 Int_Id,
601 u8 Priority, u8 Trigger)
605 Xil_AssertVoid(InstancePtr != NULL);
606 Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
607 Xil_AssertVoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);
608 Xil_AssertVoid(Trigger <= XSCUGIC_INT_CFG_MASK);
609 Xil_AssertVoid(Priority <= XSCUGIC_MAX_INTR_PRIO_VAL);
612 * Determine the register to write to using the Int_Id.
614 RegValue = XScuGic_DistReadReg(InstancePtr,
615 XSCUGIC_PRIORITY_OFFSET_CALC(Int_Id));
618 * The priority bits are Bits 7 to 3 in GIC Priority Register. This
619 * means the number of priority levels supported are 32 and they are
620 * in steps of 8. The priorities can be 0, 8, 16, 32, 48, ... etc.
621 * The lower order 3 bits are masked before putting it in the register.
623 Priority = Priority & XSCUGIC_INTR_PRIO_MASK;
625 * Shift and Mask the correct bits for the priority and trigger in the
628 RegValue &= ~(XSCUGIC_PRIORITY_MASK << ((Int_Id%4)*8));
629 RegValue |= Priority << ((Int_Id%4)*8);
632 * Write the value back to the register.
634 XScuGic_DistWriteReg(InstancePtr, XSCUGIC_PRIORITY_OFFSET_CALC(Int_Id),
638 * Determine the register to write to using the Int_Id.
640 RegValue = XScuGic_DistReadReg(InstancePtr,
641 XSCUGIC_INT_CFG_OFFSET_CALC (Int_Id));
644 * Shift and Mask the correct bits for the priority and trigger in the
647 RegValue &= ~(XSCUGIC_INT_CFG_MASK << ((Int_Id%16)*2));
648 RegValue |= Trigger << ((Int_Id%16)*2);
651 * Write the value back to the register.
653 XScuGic_DistWriteReg(InstancePtr, XSCUGIC_INT_CFG_OFFSET_CALC(Int_Id),
658 /****************************************************************************/
660 * Gets the interrupt priority and trigger type for the specificd IRQ source.
662 * @param InstancePtr is a pointer to the instance to be worked on.
663 * @param Int_Id is the IRQ source number to modify
664 * @param Priority is a pointer to the value of the priority of the IRQ
665 * source. This is a return value.
666 * @param Trigger is pointer to the value of the trigger of the IRQ
667 * source. This is a return value.
673 *****************************************************************************/
674 void XScuGic_GetPriorityTriggerType(XScuGic *InstancePtr, u32 Int_Id,
675 u8 *Priority, u8 *Trigger)
679 Xil_AssertVoid(InstancePtr != NULL);
680 Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
681 Xil_AssertVoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);
682 Xil_AssertVoid(Priority != NULL);
683 Xil_AssertVoid(Trigger != NULL);
686 * Determine the register to read to using the Int_Id.
688 RegValue = XScuGic_DistReadReg(InstancePtr,
689 XSCUGIC_PRIORITY_OFFSET_CALC(Int_Id));
692 * Shift and Mask the correct bits for the priority and trigger in the
695 RegValue = RegValue >> ((Int_Id%4)*8);
696 *Priority = RegValue & XSCUGIC_PRIORITY_MASK;
699 * Determine the register to read to using the Int_Id.
701 RegValue = XScuGic_DistReadReg(InstancePtr,
702 XSCUGIC_INT_CFG_OFFSET_CALC (Int_Id));
705 * Shift and Mask the correct bits for the priority and trigger in the
708 RegValue = RegValue >> ((Int_Id%16)*2);
710 *Trigger = RegValue & XSCUGIC_INT_CFG_MASK;