| /**************************************************************************** |
| * arch/arm/src/sama5/sam_irq.c |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <stdint.h> |
| #include <assert.h> |
| #include <debug.h> |
| |
| #include <nuttx/irq.h> |
| #include <nuttx/arch.h> |
| #include <arch/irq.h> |
| |
| #include "arm_internal.h" |
| |
| #ifdef CONFIG_SAMA5_PIO_IRQ |
| # include "sam_pio.h" |
| #endif |
| |
| #include "chip.h" |
| #include "mmu.h" |
| #include "cp15_cacheops.h" |
| #include "sctlr.h" |
| #include "hardware/sam_aic.h" |
| #include "hardware/sam_matrix.h" |
| #include "hardware/sam_aximx.h" |
| #include "hardware/sam_sfr.h" |
| |
| #include "sam_irq.h" |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Private types |
| ****************************************************************************/ |
| |
| typedef uint32_t *(*doirq_t)(int irq, uint32_t *regs); |
| |
| /**************************************************************************** |
| * Public Data |
| ****************************************************************************/ |
| |
| /* Symbols defined via the linker script */ |
| |
| extern uint8_t _vector_start[]; /* Beginning of vector block */ |
| extern uint8_t _vector_end[]; /* End+1 of vector block */ |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| static const uint8_t g_srctype[SCRTYPE_NTYPES] = |
| { |
| 0, 0, 1, 1, 2, 3 |
| }; |
| |
| /* This is an array of bit maps that can be used to quickly determine is the |
| * peripheral identified by its PID is served by H64MX or H32MX. Then the |
| * appropriate MATRIX SPSELR register can be consulted to determine if the |
| * peripheral interrupts are secured or not. |
| */ |
| |
| #if defined(CONFIG_SAMA5_SAIC) |
| static const uint32_t g_h64mxpids[3] = |
| { |
| H64MX_SPSELR0_PIDS, H64MX_SPSELR1_PIDS, H64MX_SPSELR2_PIDS |
| }; |
| #endif |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: sam_dumpaic |
| * |
| * Description: |
| * Dump some interesting AIC registers |
| * |
| ****************************************************************************/ |
| |
| #if defined(CONFIG_DEBUG_IRQ_INFO) |
| static void sam_dumpaic(const char *msg, uintptr_t base, int irq) |
| { |
| irqstate_t flags; |
| |
| flags = enter_critical_section(); |
| irqinfo("AIC (%s, base=%08x irq=%d):\n", msg, base, irq); |
| |
| /* Select the register set associated with this irq */ |
| |
| putreg32(irq, base + SAM_AIC_SSR_OFFSET); |
| |
| /* Then dump all of the (readable) register contents */ |
| |
| irqinfo(" SSR: %08x SMR: %08x SVR: %08x IVR: %08x\n", |
| getreg32(base + SAM_AIC_SSR_OFFSET), |
| getreg32(base + SAM_AIC_SMR_OFFSET), |
| getreg32(base + SAM_AIC_SVR_OFFSET), |
| getreg32(base + SAM_AIC_IVR_OFFSET)); |
| irqinfo(" FVR: %08x ISR: %08x\n", |
| getreg32(base + SAM_AIC_FVR_OFFSET), |
| getreg32(base + SAM_AIC_ISR_OFFSET)); |
| irqinfo(" IPR: %08x %08x %08x %08x\n", |
| getreg32(base + SAM_AIC_IPR0_OFFSET), |
| getreg32(base + SAM_AIC_IPR1_OFFSET), |
| getreg32(base + SAM_AIC_IPR2_OFFSET), |
| getreg32(base + SAM_AIC_IPR3_OFFSET)); |
| |
| /* SAMA5D4 does not have the FFSR register */ |
| |
| #if defined(SAM_AIC_FFSR) |
| irqinfo(" IMR: %08x CISR: %08x SPU: %08x FFSR: %08x\n", |
| getreg32(base + SAM_AIC_IMR_OFFSET), |
| getreg32(base + SAM_AIC_CISR_OFFSET), |
| getreg32(base + SAM_AIC_SPU_OFFSET), |
| getreg32(base + SAM_AIC_FFSR_OFFSET)); |
| #else |
| irqinfo(" IMR: %08x CISR: %08x SPU: %08x\n", |
| getreg32(base + SAM_AIC_IMR_OFFSET), |
| getreg32(base + SAM_AIC_CISR_OFFSET), |
| getreg32(base + SAM_AIC_SPU_OFFSET)); |
| #endif |
| |
| irqinfo(" DCR: %08x WPMR: %08x WPSR: %08x\n", |
| getreg32(base + SAM_AIC_DCR_OFFSET), |
| getreg32(base + SAM_AIC_WPMR_OFFSET), |
| getreg32(base + SAM_AIC_WPSR_OFFSET)); |
| |
| leave_critical_section(flags); |
| } |
| #else |
| # define sam_dumpaic(msg, base, irq) |
| #endif |
| |
| /**************************************************************************** |
| * Name: sam_vectorsize |
| * |
| * Description: |
| * Return the size of the vector data |
| * |
| ****************************************************************************/ |
| |
| static inline size_t sam_vectorsize(void) |
| { |
| return _vector_end - _vector_start; |
| } |
| |
| /**************************************************************************** |
| * Name: sam_spurious |
| * |
| * Description: |
| * Spurious interrupt handler. |
| * |
| * Paragraph 17.8.6, Spurious Interrupt: "The Advanced Interrupt Controller |
| * features protection against spurious interrupts. A spurious interrupt is |
| * defined as being the assertion of an interrupt source long enough for |
| * the AIC to assert the nIRQ, but no longer present when AIC_IVR is read. |
| * This is most prone to occur when: |
| * |
| * o An external interrupt source is programmed in level-sensitive mode |
| * and an active level occurs for only a short time. |
| * o An internal interrupt source is programmed in level sensitive and |
| * the output signal of the corresponding embedded peripheral is |
| * activated for a short time. (As in the case for the Watchdog.) |
| * o An interrupt occurs just a few cycles before the software begins to |
| * mask it, thus resulting in a pulse on the interrupt source. |
| * |
| * "The AIC detects a spurious interrupt at the time the AIC_IVR is read |
| * while no enabled interrupt source is pending. When this happens, the AIC |
| * returns the value stored by the programmer in AIC_SPU (Spurious Vector |
| * Register). The programmer must store the address of a spurious interrupt |
| * handler in AIC_SPU as part of the application, to enable an as fast as |
| * possible return to the normal execution flow. This handler writes in |
| * AIC_EOICR and performs a return from interrupt." |
| * |
| ****************************************************************************/ |
| |
| static uint32_t *sam_spurious(int irq, uint32_t *regs) |
| { |
| /* This is probably irrelevant since true vectored interrupts are not used |
| * in this implementation. The value of AIC_IVR is ignored. |
| */ |
| |
| #if defined(CONFIG_DEBUG_IRQ_INFO) |
| irqinfo("Spurious interrupt: IRQ %d\n", irq); |
| #endif |
| return regs; |
| } |
| |
| /**************************************************************************** |
| * Name: sam_fiqhandler |
| * |
| * Description: |
| * Default FIQ interrupt handler. |
| * |
| ****************************************************************************/ |
| |
| static uint32_t *sam_fiqhandler(int irq, uint32_t *regs) |
| { |
| /* Dispatch the FIQ */ |
| |
| return arm_doirq(SAM_IRQ_FIQ, regs); |
| } |
| |
| /**************************************************************************** |
| * Name: sam_aic_issecure |
| * |
| * Description: |
| * Return true if the peripheral secure. |
| * |
| * Input Parameters: |
| * PID = IRQ number |
| * |
| ****************************************************************************/ |
| |
| #if defined(CONFIG_SAMA5_SAIC) |
| static bool sam_aic_issecure(uint32_t irq) |
| { |
| uintptr_t regaddr; |
| uint32_t bit; |
| unsigned int regndx; |
| |
| /* Get the register index and bit mask */ |
| |
| regndx = (irq >> 5); |
| bit = ((uint32_t)1 << (irq & 0x1f)); |
| |
| /* Get the SPSELR register address */ |
| |
| DEBUGASSERT(regndx < 3); |
| if ((g_h64mxpids[regndx] & bit) != 0) |
| { |
| /* H64MX. Use Matrix 0 */ |
| |
| regaddr = SAM_MATRIX0_SPSELR(regndx); |
| } |
| else |
| { |
| /* H32MX. Use Matrix 1 */ |
| |
| regaddr = SAM_MATRIX1_SPSELR(regndx); |
| } |
| |
| /* Return true if the bit corresponding to this IRQ is zero */ |
| |
| return (getreg32(regaddr) & bit) == 0; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: sam_aic_redirection |
| * |
| * Description: |
| * Redirect all interrupts to the AIC. This function is only compiled if |
| * (1) the architecture supports an SAIC (CONFIG_SAMA5_HAVE_SAIC), but (2) |
| * Use of the SAIC has not been selected (!CONFIG_SAMA5_SAIC). |
| * |
| ****************************************************************************/ |
| |
| #if defined(CONFIG_SAMA5_HAVE_SAIC) && !defined(CONFIG_SAMA5_SAIC) |
| static void sam_aic_redirection(void) |
| { |
| unsigned int regval; |
| |
| /* Check if interrupts are already redirected to the AIC */ |
| |
| regval = getreg32(SAM_SFR_AICREDIR); |
| if ((regval & SFR_AICREDIR_ENABLE) == 0) |
| { |
| /* Enable redirection of all interrupts to the AIC */ |
| |
| regval = getreg32(SAM_SFR_SN1); |
| regval ^= SFR_AICREDIR_KEY; |
| regval |= SFR_AICREDIR_ENABLE; |
| putreg32(regval, SAM_SFR_AICREDIR); |
| |
| #if defined(CONFIG_DEBUG_IRQ_INFO) |
| /* Check if redirection was successfully enabled */ |
| |
| regval = getreg32(SAM_SFR_AICREDIR); |
| irqinfo("Interrupts %s redirected to the AIC\n", |
| (regval & SFR_AICREDIR_ENABLE) != 0 ? "ARE" : "NOT"); |
| #endif |
| } |
| } |
| #else |
| # define sam_aic_redirection() |
| #endif |
| |
| /**************************************************************************** |
| * Name: sam_aic_initialize |
| * |
| * Description: |
| * Initialize the AIC or the SAIC. |
| * |
| ****************************************************************************/ |
| |
| static void sam_aic_initialize(uintptr_t base) |
| { |
| int i; |
| |
| /* Unprotect SMR, SVR, SPU and DCR register */ |
| |
| putreg32(AIC_WPMR_WPKEY, base + SAM_AIC_WPMR_OFFSET); |
| |
| /* Configure the FIQ and the IRQs. */ |
| |
| for (i = 0; i < SAM_IRQ_NINT; i++) |
| { |
| /* Select the interrupt registers */ |
| |
| putreg32(i, base + SAM_AIC_SSR_OFFSET); |
| |
| /* Disable the interrupt */ |
| |
| putreg32(AIC_IDCR_INTD, base + SAM_AIC_IDCR_OFFSET); |
| |
| /* Set the (unused) FIQ/IRQ handler */ |
| |
| if (i == SAM_PID_FIQ) |
| { |
| putreg32((uint32_t)sam_fiqhandler, base + SAM_AIC_SVR_OFFSET); |
| } |
| else |
| { |
| putreg32((uint32_t)arm_doirq, base + SAM_AIC_SVR_OFFSET); |
| } |
| |
| /* Set the default interrupt priority */ |
| |
| putreg32(SAM_DEFAULT_PRIOR, base + SAM_AIC_SMR_OFFSET); |
| |
| /* Clear any pending interrupt */ |
| |
| putreg32(AIC_ICCR_INTCLR, base + SAM_AIC_ICCR_OFFSET); |
| } |
| |
| /* Set the (unused) spurious interrupt handler */ |
| |
| putreg32((uint32_t)sam_spurious, base + SAM_AIC_SPU_OFFSET); |
| |
| /* Configure debug register */ |
| |
| putreg32(AIC_DCR_PROT, base + SAM_AIC_DCR_OFFSET); |
| |
| /* Perform 8 interrupt acknowledgements by writing any value to the |
| * EOICR register. |
| */ |
| |
| for (i = 0; i < 8; i++) |
| { |
| putreg32(AIC_EOICR_ENDIT, base + SAM_AIC_EOICR_OFFSET); |
| } |
| |
| /* Restore protection and the interrupt state */ |
| |
| putreg32(AIC_WPMR_WPKEY | AIC_WPMR_WPEN, base + SAM_AIC_WPMR_OFFSET); |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: up_irqinitialize |
| * |
| * Description: |
| * This function is called by up_initialize() during the bring-up of the |
| * system. It is the responsibility of this function to but the interrupt |
| * subsystem into the working and ready state. |
| * |
| ****************************************************************************/ |
| |
| void up_irqinitialize(void) |
| { |
| #if defined(CONFIG_SAMA5_BOOT_ISRAM) || defined(CONFIG_SAMA5_BOOT_CS0FLASH) |
| size_t vectorsize; |
| #endif |
| |
| /* The following operations need to be atomic, but since this function is |
| * called early in the initialization sequence, we expect to have exclusive |
| * access to the AIC. |
| */ |
| |
| /* Redirect all interrupts to the AIC if so configured */ |
| |
| sam_aic_redirection(); |
| |
| /* Initialize the Advanced Interrupt Controller (AIC) */ |
| |
| sam_aic_initialize(SAM_AIC_VBASE); |
| |
| #if defined(CONFIG_SAMA5_SAIC) |
| /* Initialize the Secure Advanced Interrupt Controller (SAIC) */ |
| |
| sam_aic_initialize(SAM_SAIC_VBASE); |
| |
| #endif |
| |
| #if defined(CONFIG_ARCH_LOWVECTORS) |
| /* If CONFIG_ARCH_LOWVECTORS is defined, then the vectors located at the |
| * beginning of the .text region must appear at address at the address |
| * specified in the VBAR. There are three ways to accomplish this: |
| * |
| * 1. By explicitly mapping the beginning of .text region with a page |
| * table entry so that the virtual address zero maps to the beginning |
| * of the .text region. VBAR == 0x0000:0000. |
| * |
| * 2. A second way is to map the use the AXI MATRIX remap register to |
| * map physical address zero to the beginning of the text region, |
| * either internal SRAM or EBI CS 0. Then we can set an identity |
| * mapping to map the boot region at 0x0000:0000 to virtual address |
| * 0x0000:00000. VBAR == 0x0000:0000. |
| * |
| * This method is used when booting from ISRAM or NOR FLASH. In |
| * that case, vectors must lie at the beginning of NOFR FLASH. |
| * |
| * 3. Set the Cortex-A5 VBAR register so that the vector table address |
| * is moved to a location other than 0x0000:0000. |
| * |
| * This is the method used when booting from SDRAM. |
| * |
| * - When executing from NOR FLASH, the first level bootloader is supposed |
| * to provide the AXI MATRIX mapping for us at boot time base on the |
| * state of the BMS pin. However, I have found that in the test |
| * environments that I use, I cannot always be assured of that physical |
| * address mapping. |
| * |
| * - If we are executing out of ISRAM, then the SAMA5 primary bootloader |
| * probably copied us into ISRAM and set the AXI REMAP bit for us. |
| * |
| * - If we are executing from external SDRAM, then a secondary bootloader |
| * must have loaded us into SDRAM. In this case, simply set the VBAR |
| * register to the address of the vector table (not necessary at the |
| * beginning or SDRAM). |
| */ |
| |
| #if defined(CONFIG_SAMA5_BOOT_ISRAM) || defined(CONFIG_SAMA5_BOOT_CS0FLASH) |
| /* Set the vector base address register to 0x0000:0000 */ |
| |
| cp15_wrvbar(0); |
| |
| #if 0 /* Disabled on reset */ |
| /* Disable MATRIX write protection */ |
| |
| putreg32(MATRIX_WPMR_WPKEY, SAM_MATRIX_WPMR); |
| #endif |
| |
| /* Set remap state 0 if we are running from internal SRAM or from SDRAM. |
| * If we booted into NOR FLASH, then the first level bootloader should |
| * have already provided this mapping for us. |
| * |
| * This is done late in the boot sequence. Any exceptions taken before |
| * this point in time will be handled by the ROM code, not by the NuttX |
| * interrupt since which was, up to this point, uninitialized. |
| * |
| * Boot state: ROM is seen at address 0x00000000 |
| * Remap State 0: SRAM is seen at address 0x00000000 (through AHB slave |
| * interface) instead of ROM. |
| * Remap State 1: HEBI is seen at address 0x00000000 (through AHB slave |
| * interface) instead of ROM for external boot. |
| * |
| * Here we are assuming that vectors reside in the lower end of ISRAM. |
| * Hmmm... this probably does not matter since we will map a page to |
| * address 0x0000:0000 in that case anyway. |
| */ |
| |
| #ifdef ATSAMA5D3 |
| putreg32(MATRIX_MRCR_RCB0, SAM_MATRIX_MRCR); /* Enable Cortex-A5 remap */ |
| #endif |
| |
| #if defined(CONFIG_SAMA5_BOOT_ISRAM) |
| putreg32(AXIMX_REMAP_REMAP0, SAM_AXIMX_REMAP); /* Remap SRAM */ |
| #elif defined(ATSAMA5D3) /* && defined(CONFIG_SAMA5_BOOT_CS0FLASH) */ |
| putreg32(AXIMX_REMAP_REMAP1, SAM_AXIMX_REMAP); /* Remap NOR FLASH on CS0 */ |
| #endif |
| |
| /* Make sure that there is no trace of any previous mapping (here we |
| * that the L2 cache has not yet been enabled. |
| */ |
| |
| vectorsize = sam_vectorsize(); |
| cp15_invalidate_icache(0, vectorsize); |
| cp15_invalidate_dcache(0, vectorsize); |
| mmu_invalidate_region(0, vectorsize); |
| |
| #if 0 /* Disabled on reset */ |
| /* Restore MATRIX write protection */ |
| |
| putreg32(MATRIX_WPMR_WPKEY | MATRIX_WPMR_WPEN, SAM_MATRIX_WPMR); |
| #endif |
| |
| #elif defined(CONFIG_SAMA5_BOOT_SDRAM) |
| /* Set the VBAR register to the address of the vector table in SDRAM */ |
| |
| DEBUGASSERT((((uintptr_t)_vector_start) & ~VBAR_MASK) == 0); |
| cp15_wrvbar((uint32_t)_vector_start); |
| |
| #endif /* CONFIG_SAMA5_BOOT_ISRAM || CONFIG_SAMA5_BOOT_CS0FLASH */ |
| #endif /* CONFIG_ARCH_LOWVECTORS */ |
| |
| #ifndef CONFIG_SUPPRESS_INTERRUPTS |
| /* Initialize logic to support a second level of interrupt decoding for |
| * PIO pins. |
| */ |
| |
| #ifdef CONFIG_SAMA5_PIO_IRQ |
| sam_pioirqinitialize(); |
| #endif |
| |
| /* And finally, enable interrupts */ |
| |
| up_irq_enable(); |
| #endif |
| } |
| |
| /**************************************************************************** |
| * Name: arm_decodeirq, arm_decodefiq (and sam_decodeirq helper). |
| * |
| * Description: |
| * This function is called from the IRQ vector handler in arm_vectors.S. |
| * At this point, the interrupt has been taken and the registers have |
| * been saved on the stack. This function simply needs to determine the |
| * the irq number of the interrupt and then to call arm_doirq to dispatch |
| * the interrupt. |
| * |
| * Input Parameters: |
| * regs - A pointer to the register save area on the stack. |
| * |
| ****************************************************************************/ |
| |
| static uint32_t *sam_decodeirq(uintptr_t base, uint32_t *regs) |
| { |
| uint32_t irqid; |
| uint32_t ivr; |
| |
| /* Paragraph 17.8.5 Protect Mode: "The Protect Mode permits reading the |
| * Interrupt Vector Register without performing the associated automatic |
| * operations. ... Writing PROT in AIC_DCR (Debug Control Register) at |
| * 0x1 enables the Protect Mode. |
| * |
| * "When the Protect Mode is enabled, the AIC performs interrupt stacking |
| * only when a write access is performed on the AIC_IVR. Therefore, the |
| * Interrupt Service Routines must write (arbitrary data) to the AIC_IVR |
| * just after reading it. The new context of the AIC, including the |
| * value of the Interrupt Status Register (AIC_ISR), is updated with the |
| * current interrupt only when AIC_IVR is written. ..." |
| * |
| * "To summarize, in normal operating mode, the read of AIC_IVR performs |
| * the following operations within the AIC: |
| * |
| * 1. Calculates active interrupt (higher than current or spurious). |
| * 2. Determines and returns the vector of the active interrupt. |
| * 3. Memorizes the interrupt. |
| * 4. Pushes the current priority level onto the internal stack. |
| * 5. Acknowledges the interrupt. |
| * |
| * "However, while the Protect Mode is activated, only operations 1 to 3 |
| * are performed when AIC_IVR is read. Operations 4 and 5 are only |
| * performed by the AIC when AIC_IVR is written. |
| * |
| * "Software that has been written and debugged using the Protect Mode |
| * runs correctly in Normal Mode without modification. However, in Normal |
| * Mode the AIC_IVR write has no effect and can be removed to optimize the |
| * code. |
| */ |
| |
| /* Write in the IVR to support Protect Mode */ |
| |
| ivr = getreg32(base + SAM_AIC_IVR_OFFSET); |
| putreg32(ivr, base + SAM_AIC_IVR_OFFSET); |
| |
| /* Get the IRQ number from the interrupt status register. NOTE that the |
| * IRQ number is the same is the peripheral ID (PID). |
| */ |
| |
| irqid = getreg32(base + SAM_AIC_ISR_OFFSET) & AIC_ISR_MASK; |
| |
| /* Dispatch the interrupt */ |
| |
| regs = ((doirq_t)ivr)((int)irqid, regs); |
| |
| /* Acknowledge interrupt */ |
| |
| putreg32(AIC_EOICR_ENDIT, base + SAM_AIC_EOICR_OFFSET); |
| return regs; |
| } |
| |
| /* This is the entry point from the ARM IRQ vector handler */ |
| |
| uint32_t *arm_decodeirq(uint32_t *regs) |
| { |
| return sam_decodeirq(SAM_AIC_VBASE, regs); |
| } |
| |
| /* This is the entry point from the ARM FIQ vector handler */ |
| |
| uint32_t *arm_decodefiq(uint32_t *regs) |
| { |
| #if defined(CONFIG_SAMA5_SAIC) |
| uint32_t *ret; |
| |
| /* In order to distinguish a FIQ from a true secure interrupt we need to |
| * check the state of the FIQ line in the SAIC_CISR register. |
| */ |
| |
| if ((getreg32(SAM_SAIC_CISR) & AIC_CISR_NFIQ) != 0) |
| { |
| /* Handle the FIQ */ |
| |
| ret = arm_doirq(SAM_IRQ_FIQ, regs); |
| |
| /* Acknowledge interrupt */ |
| |
| putreg32(AIC_EOICR_ENDIT, SAM_SAIC_EOICR); |
| } |
| else |
| { |
| /* Handle the IRQ */ |
| |
| ret = sam_decodeirq(SAM_SAIC_VBASE, regs); |
| } |
| |
| return ret; |
| #else |
| DEBUGASSERT(false); |
| return NULL; |
| #endif |
| } |
| |
| /**************************************************************************** |
| * Name: up_disable_irq (and sam_disable_irq helper) |
| * |
| * Description: |
| * Disable the IRQ specified by 'irq' |
| * |
| ****************************************************************************/ |
| |
| static void sam_disable_irq(uintptr_t base, int irq) |
| { |
| irqstate_t flags; |
| |
| if (irq < SAM_IRQ_NINT) |
| { |
| /* These operations must be atomic */ |
| |
| flags = enter_critical_section(); |
| |
| /* Select the register set associated with this irq */ |
| |
| putreg32(irq, base + SAM_AIC_SSR_OFFSET); |
| |
| /* Disable the interrupt */ |
| |
| putreg32(AIC_IDCR_INTD, base + SAM_AIC_IDCR_OFFSET); |
| sam_dumpaic("disable", base, irq); |
| leave_critical_section(flags); |
| } |
| #ifdef CONFIG_SAMA5_PIO_IRQ |
| else |
| { |
| /* Maybe it is a (derived) PIO IRQ */ |
| |
| sam_pioirqdisable(irq); |
| } |
| #endif |
| } |
| |
| void up_disable_irq(int irq) |
| { |
| #if defined(CONFIG_SAMA5_SAIC) |
| if (sam_aic_issecure(irq)) |
| { |
| sam_disable_irq(SAM_SAIC_VBASE, irq); |
| } |
| else |
| #endif |
| { |
| sam_disable_irq(SAM_AIC_VBASE, irq); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: up_enable_irq (and sam_enable_irq helper) |
| * |
| * Description: |
| * Enable the IRQ specified by 'irq' |
| * |
| ****************************************************************************/ |
| |
| static void sam_enable_irq(uintptr_t base, int irq) |
| { |
| irqstate_t flags; |
| |
| if (irq < SAM_IRQ_NINT) |
| { |
| /* These operations must be atomic */ |
| |
| flags = enter_critical_section(); |
| |
| /* Select the register set associated with this irq */ |
| |
| putreg32(irq, base + SAM_AIC_SSR_OFFSET); |
| |
| /* Enable the interrupt */ |
| |
| putreg32(AIC_IECR_INTEN, base + SAM_AIC_IECR_OFFSET); |
| sam_dumpaic("enable", base, irq); |
| leave_critical_section(flags); |
| } |
| #ifdef CONFIG_SAMA5_PIO_IRQ |
| else |
| { |
| /* Maybe it is a (derived) PIO IRQ */ |
| |
| sam_pioirqenable(irq); |
| } |
| #endif |
| } |
| |
| void up_enable_irq(int irq) |
| { |
| #if defined(CONFIG_SAMA5_SAIC) |
| if (sam_aic_issecure(irq)) |
| { |
| sam_enable_irq(SAM_SAIC_VBASE, irq); |
| } |
| else |
| #endif |
| { |
| sam_enable_irq(SAM_AIC_VBASE, irq); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: up_prioritize_irq (and sam_prioritize_irq helper) |
| * |
| * Description: |
| * Set the priority of an IRQ. |
| * |
| * Since this API is not supported on all architectures, it should be |
| * avoided in common implementations where possible. |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_ARCH_IRQPRIO |
| static int sam_prioritize_irq(uint32_t base, int irq, int priority) |
| { |
| irqstate_t flags; |
| uint32_t regval; |
| |
| DEBUGASSERT(irq < SAM_IRQ_NINT && |
| (unsigned)priority <= AIC_SMR_PRIOR_MASK); |
| if (irq < SAM_IRQ_NINT) |
| { |
| /* These operations must be atomic */ |
| |
| flags = enter_critical_section(); |
| |
| /* Select the register set associated with this irq */ |
| |
| putreg32(irq, base + SAM_AIC_SSR_OFFSET); |
| |
| /* Unprotect and write the SMR register */ |
| |
| putreg32(AIC_WPMR_WPKEY, base + SAM_AIC_WPMR_OFFSET); |
| |
| /* Set the new priority, preserving the current srctype */ |
| |
| regval = getreg32(base + SAM_AIC_SMR_OFFSET); |
| regval &= ~AIC_SMR_PRIOR_MASK; |
| regval |= (uint32_t)priority << AIC_SMR_PRIOR_SHIFT; |
| putreg32(regval, base + SAM_AIC_SMR_OFFSET); |
| |
| /* Restore protection and the interrupt state */ |
| |
| putreg32(AIC_WPMR_WPKEY | AIC_WPMR_WPEN, base + SAM_AIC_WPMR_OFFSET); |
| sam_dumpaic("prioritize", base, irq); |
| leave_critical_section(flags); |
| } |
| |
| return OK; |
| } |
| |
| int up_prioritize_irq(int irq, int priority) |
| { |
| #if defined(CONFIG_SAMA5_SAIC) |
| if (sam_aic_issecure(irq)) |
| { |
| sam_prioritize_irq(SAM_SAIC_VBASE, irq, priority); |
| } |
| else |
| #endif |
| { |
| sam_prioritize_irq(SAM_AIC_VBASE, irq, priority); |
| } |
| |
| return OK; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: sam_irq_srctype (and _sam_irq_srctype helper) |
| * |
| * Description: |
| * irq - Identifies the IRQ source to be configured |
| * srctype - IRQ source configuration |
| * |
| ****************************************************************************/ |
| |
| static void _sam_irq_srctype(uintptr_t base, int irq, |
| enum sam_srctype_e srctype) |
| { |
| irqstate_t flags; |
| uint32_t regval; |
| |
| DEBUGASSERT(irq < SAM_IRQ_NINT && (unsigned)srctype < SCRTYPE_NTYPES); |
| |
| /* These operations must be atomic */ |
| |
| flags = enter_critical_section(); |
| |
| /* Select the register set associated with this irq */ |
| |
| putreg32(irq, base + SAM_AIC_SSR_OFFSET); |
| |
| /* Unprotect and write the SMR register */ |
| |
| putreg32(AIC_WPMR_WPKEY, base + SAM_AIC_WPMR_OFFSET); |
| |
| /* Set the new srctype, preserving the current priority */ |
| |
| regval = getreg32(base + SAM_AIC_SMR_OFFSET); |
| regval &= ~AIC_SMR_SRCTYPE_MASK; |
| regval |= (uint32_t)g_srctype[srctype] << AIC_SMR_SRCTYPE_SHIFT; |
| putreg32(regval, base + SAM_AIC_SMR_OFFSET); |
| |
| /* Restore protection and the interrupt state */ |
| |
| putreg32(AIC_WPMR_WPKEY | AIC_WPMR_WPEN, base + SAM_AIC_WPMR_OFFSET); |
| sam_dumpaic("srctype", base, irq); |
| leave_critical_section(flags); |
| } |
| |
| void sam_irq_srctype(int irq, enum sam_srctype_e srctype) |
| { |
| #if defined(CONFIG_SAMA5_SAIC) |
| if (sam_aic_issecure(irq)) |
| { |
| _sam_irq_srctype(SAM_SAIC_VBASE, irq, srctype); |
| } |
| else |
| #endif |
| { |
| _sam_irq_srctype(SAM_AIC_VBASE, irq, srctype); |
| } |
| } |