595 lines
18 KiB
C
595 lines
18 KiB
C
/****************************************************************************
|
|
* arch/arm/src/sama5/sam_irq.c
|
|
*
|
|
* Copyright (C) 2013 Gregory Nutt. All rights reserved.
|
|
* Author: Gregory Nutt <gnutt@nuttx.org>
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* 3. Neither the name NuttX nor the names of its contributors may be
|
|
* used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <stdint.h>
|
|
#include <debug.h>
|
|
|
|
#include <nuttx/irq.h>
|
|
#include <nuttx/arch.h>
|
|
#include <arch/irq.h>
|
|
|
|
#include "up_arch.h"
|
|
#include "os_internal.h"
|
|
#include "up_internal.h"
|
|
|
|
#ifdef CONFIG_PIO_IRQ
|
|
# include "sam_gpio.h"
|
|
#endif
|
|
|
|
#include "chip/sam_aic.h"
|
|
#include "chip/sam_matrix.h"
|
|
#include "chip/sam_aximx.h"
|
|
|
|
#include "sam_irq.h"
|
|
|
|
/****************************************************************************
|
|
* Definitions
|
|
****************************************************************************/
|
|
|
|
/* Enable NVIC debug features that are probably only desireable during
|
|
* bringup
|
|
*/
|
|
|
|
#undef SAM_IRQ_DEBUG
|
|
|
|
/****************************************************************************
|
|
* Public Data
|
|
****************************************************************************/
|
|
|
|
volatile uint32_t *current_regs;
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static const uint8_t g_srctype[SCRTYPE_NTYPES] =
|
|
{
|
|
0, 0, 1, 1, 2, 3
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: sam_dumpaic
|
|
*
|
|
* Description:
|
|
* Dump some interesting AIC registers
|
|
*
|
|
****************************************************************************/
|
|
|
|
#if defined(SAM_IRQ_DEBUG) && defined (CONFIG_DEBUG)
|
|
static void sam_dumpaic(const char *msg, int irq)
|
|
{
|
|
irqstate_t flags;
|
|
|
|
flags = irqsave();
|
|
slldbg("AIC (%s, irq=%d):\n", msg, irq);
|
|
|
|
/* Select the register set associated with this irq */
|
|
|
|
putreg32(irq, SAM_AIC_SSR)
|
|
|
|
/* Then dump all of the (readable) register contents */
|
|
|
|
slldbg(" SSR: %08x SMR: %08x SVR: %08x IVR: %08x\n",
|
|
getreg32(SAM_AIC_SSR), getreg32(SAM_AIC_SMR),
|
|
getreg32(SAM_AIC_SVR), getreg32(SAM_AIC_IVR));
|
|
slldbg(" FVR: %08x ISR: %08x\n",
|
|
getreg32(SAM_AIC_FVR), getreg32(SAM_AIC_ISR));
|
|
slldbg(" IPR: %08x %08x %08x %08x\n",
|
|
getreg32(SAM_AIC_IPR0), getreg32(SAM_AIC_IPR1),
|
|
getreg32(SAM_AIC_IPR2), getreg32(SAM_AIC_IPR3));
|
|
slldbg(" IMR: %08x CISR: %08x SPU: %08x FFSR: %08x\n",
|
|
getreg32(SAM_AIC_IMR), getreg32(SAM_AIC_CISR),
|
|
getreg32(SAM_AIC_SPU), getreg32(SAM_AIC_FFSR));
|
|
slldbg(" DCR: %08x WPMR: %08x WPMR: %08x\n",
|
|
getreg32(SAM_AIC_DCR), getreg32(SAM_AIC_WPMR),
|
|
getreg32(SAM_AIC_WPMR));
|
|
irqrestore(flags);
|
|
}
|
|
#else
|
|
# define sam_dumpaic(msg, irq)
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* 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 void sam_spurious(void)
|
|
{
|
|
/* This is probably irrevelant since true vectored interrupts are not used
|
|
* in this implementation. The value of AIC_IVR is ignored.
|
|
*/
|
|
|
|
lldbg("Spurious interrupt\n");
|
|
PANIC();
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: sam_fiqhandler
|
|
*
|
|
* Description:
|
|
* Default FIQ interrupt handler.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void sam_fiqhandler(void)
|
|
{
|
|
/* This is probably irrevelant since true vectored interrupts are not used
|
|
* in this implementation. The value of AIC_IVR is ignored.
|
|
*/
|
|
|
|
lldbg("FIQ\n");
|
|
PANIC();
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: sam_irqhandler
|
|
*
|
|
* Description:
|
|
* Default IRQ interrupt handler.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void sam_irqhandler( void )
|
|
{
|
|
/* This is probably irrevelant since true vectored interrupts are not used
|
|
* in this implementation. The value of AIC_IVR is ignored.
|
|
*/
|
|
|
|
lldbg("IRQ\n");
|
|
PANIC();
|
|
}
|
|
|
|
/****************************************************************************
|
|
* 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)
|
|
{
|
|
int i;
|
|
|
|
/* The following operations need to be atomic, but since this function is
|
|
* called early in the intialization sequence, we expect to have exclusive
|
|
* access to the AIC.
|
|
*/
|
|
|
|
/* Unprotect SMR, SVR, SPU and DCR register */
|
|
|
|
putreg32(AIC_WPMR_WPKEY, SAM_AIC_WPMR);
|
|
|
|
/* Configure the FIQ and the IRQs. */
|
|
|
|
for (i = 0; i < SAM_IRQ_NINT; i++)
|
|
{
|
|
/* Select the interrupt registers */
|
|
|
|
putreg32(i, SAM_AIC_SSR);
|
|
|
|
/* Disable the interrupt */
|
|
|
|
putreg32(AIC_IDCR_INTD, SAM_AIC_IDCR);
|
|
|
|
/* Set the (unused) FIQ/IRQ handler */
|
|
|
|
if (i == SAM_PID_FIQ)
|
|
{
|
|
putreg32((uint32_t)sam_fiqhandler, SAM_AIC_SVR);
|
|
}
|
|
else
|
|
{
|
|
putreg32((uint32_t)sam_irqhandler, SAM_AIC_SVR);
|
|
}
|
|
|
|
/* Set the default interrupt priority */
|
|
|
|
putreg32(SAM_DEFAULT_PRIOR, SAM_AIC_SMR);
|
|
|
|
/* Clear any pending interrupt */
|
|
|
|
putreg32(AIC_ICCR_INTCLR, SAM_AIC_ICCR);
|
|
}
|
|
|
|
/* Set the (unused) spurious interrupt handler */
|
|
|
|
putreg32((uint32_t)sam_spurious, SAM_AIC_SPU);
|
|
|
|
/* Perform 8 interrupt acknowledgements by writing any value to the
|
|
* EOICR register.
|
|
*/
|
|
|
|
for (i = 0; i < 8 ; i++)
|
|
{
|
|
putreg32(AIC_EOICR_ENDIT, SAM_AIC_EOICR);
|
|
}
|
|
|
|
/* Restore protection and the interrupt state */
|
|
|
|
putreg32(AIC_WPMR_WPKEY | AIC_WPMR_WPEN, SAM_AIC_WPMR);
|
|
|
|
#if defined(CONFIG_ARCH_LOWVECTORS) && defined(CONFIG_SAMA5_BOOT_ISRAM)
|
|
/* Disable MATRIX write protection */
|
|
|
|
#if 0 /* Disabled on reset */
|
|
putreg32(MATRIX_WPMR_WPKEY, SAM_MATRIX_WPMR);
|
|
#endif
|
|
|
|
/* Set remap state 0 if we are running from internal SRAM. 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.
|
|
*/
|
|
|
|
putreg32(MATRIX_MRCR_RCB0, SAM_MATRIX_MRCR); /* Enable remap */
|
|
putreg32(AXIMX_REMAP_REMAP0, SAM_AXIMX_REMAP); /* Remap SRAM */
|
|
|
|
/* Restore MATRIX write protection */
|
|
|
|
#if 0 /* Disabled on reset */
|
|
putreg32(MATRIX_WPMR_WPKEY | MATRIX_WPMR_WPEN, SAM_MATRIX_WPMR);
|
|
#endif
|
|
|
|
/* It might be wise to flush the instruction cache here */
|
|
#endif
|
|
|
|
/* currents_regs is non-NULL only while processing an interrupt */
|
|
|
|
current_regs = NULL;
|
|
|
|
#ifndef CONFIG_SUPPRESS_INTERRUPTS
|
|
/* Initialize logic to support a second level of interrupt decoding for
|
|
* PIO pins.
|
|
*/
|
|
|
|
#ifdef CONFIG_PIO_IRQ
|
|
sam_pioirqinitialize();
|
|
#endif
|
|
|
|
/* And finally, enable interrupts */
|
|
|
|
(void)irqenable();
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: arm_decodeirq
|
|
*
|
|
* 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 paramters:
|
|
* regs - A pointer to the register save area on the stack.
|
|
*
|
|
****************************************************************************/
|
|
|
|
uint32_t *arm_decodeirq(uint32_t *regs)
|
|
{
|
|
uint32_t regval;
|
|
|
|
/* 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 */
|
|
|
|
regval = getreg32(SAM_AIC_IVR);
|
|
putreg32(regval, SAM_AIC_IVR);
|
|
|
|
/* Get the IRQ number from the interrrupt status register. NOTE that the
|
|
* IRQ number is the same is the peripheral ID (PID).
|
|
*/
|
|
|
|
regval = getreg32(SAM_AIC_ISR) & AIC_ISR_MASK;
|
|
|
|
/* Dispatch the interrupt */
|
|
|
|
regs = arm_doirq((int)regval, regs);
|
|
|
|
/* Acknowledge interrupt */
|
|
|
|
putreg32(AIC_EOICR_ENDIT, SAM_AIC_EOICR);
|
|
return regs;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: up_disable_irq
|
|
*
|
|
* Description:
|
|
* Disable the IRQ specified by 'irq'
|
|
*
|
|
****************************************************************************/
|
|
|
|
void up_disable_irq(int irq)
|
|
{
|
|
irqstate_t flags;
|
|
|
|
if (irq < SAM_IRQ_NINT)
|
|
{
|
|
/* These operations must be atomic */
|
|
|
|
flags = irqsave();
|
|
|
|
/* Select the register set associated with this irq */
|
|
|
|
putreg32(irq, SAM_AIC_SSR);
|
|
|
|
/* Disable the interrupt */
|
|
|
|
putreg32(AIC_IDCR_INTD, SAM_AIC_IDCR);
|
|
sam_dumpaic("disable", irq);
|
|
irqrestore(flags);
|
|
}
|
|
#ifdef CONFIG_PIO_IRQ
|
|
else
|
|
{
|
|
/* Maybe it is a (derived) PIO IRQ */
|
|
|
|
sam_pioirqdisable(irq);
|
|
}
|
|
#endif
|
|
sam_dumpaic("disable", irq);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: up_enable_irq
|
|
*
|
|
* Description:
|
|
* Enable the IRQ specified by 'irq'
|
|
*
|
|
****************************************************************************/
|
|
|
|
void up_enable_irq(int irq)
|
|
{
|
|
irqstate_t flags;
|
|
|
|
if (irq < SAM_IRQ_NINT)
|
|
{
|
|
/* These operations must be atomic */
|
|
|
|
flags = irqsave();
|
|
|
|
/* Select the register set associated with this irq */
|
|
|
|
putreg32(irq, SAM_AIC_SSR);
|
|
|
|
/* Enable the interrupt */
|
|
|
|
putreg32(AIC_IECR_INTEN, SAM_AIC_IECR);
|
|
sam_dumpaic("enable", irq);
|
|
irqrestore(flags);
|
|
}
|
|
#ifdef CONFIG_PIO_IRQ
|
|
else
|
|
{
|
|
/* Maybe it is a (derived) PIO IRQ */
|
|
|
|
sam_pioirqenable(irq);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: up_maskack_irq
|
|
*
|
|
* Description:
|
|
* Mask the IRQ and acknowledge it
|
|
*
|
|
****************************************************************************/
|
|
|
|
void up_maskack_irq(int irq)
|
|
{
|
|
up_disable_irq(irq);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: up_prioritize_irq
|
|
*
|
|
* 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
|
|
int up_prioritize_irq(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 = irqsave();
|
|
|
|
/* Select the register set associated with this irq */
|
|
|
|
putreg32(irq, SAM_AIC_SSR);
|
|
|
|
/* Unprotect and write the SMR register */
|
|
|
|
putreg32(AIC_WPMR_WPKEY, SAM_AIC_WPMR);
|
|
|
|
/* Set the new priority, preserving the current srctype */
|
|
|
|
regval = getreg32(SAM_AIC_SMR);
|
|
regval &= ~AIC_SMR_PRIOR_MASK;
|
|
regval |= (uint32_t)priority << AIC_SMR_PRIOR_SHIFT;
|
|
putreg32(regval, SAM_AIC_SMR);
|
|
|
|
/* Restore protection and the interrupt state */
|
|
|
|
putreg32(AIC_WPMR_WPKEY | AIC_WPMR_WPEN, SAM_AIC_WPMR);
|
|
sam_dumpaic("prioritize", irq);
|
|
irqrestore(flags);
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: sam_irq_srctype
|
|
*
|
|
* Description:
|
|
* irq - Identifies the IRQ source to be configured
|
|
* srctype - IRQ source configuration
|
|
*
|
|
****************************************************************************/
|
|
|
|
void sam_irq_srctype(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 = irqsave();
|
|
|
|
/* Select the register set associated with this irq */
|
|
|
|
putreg32(irq, SAM_AIC_SSR);
|
|
|
|
/* Unprotect and write the SMR register */
|
|
|
|
putreg32(AIC_WPMR_WPKEY, SAM_AIC_WPMR);
|
|
|
|
/* Set the new srctype, preserving the current priority */
|
|
|
|
regval = getreg32(SAM_AIC_SMR);
|
|
regval &= ~AIC_SMR_SRCTYPE_MASK;
|
|
regval |= (uint32_t)g_srctype[srctype] << AIC_SMR_SRCTYPE_SHIFT;
|
|
putreg32(regval, SAM_AIC_SMR);
|
|
|
|
/* Restore protection and the interrupt state */
|
|
|
|
putreg32(AIC_WPMR_WPKEY | AIC_WPMR_WPEN, SAM_AIC_WPMR);
|
|
sam_dumpaic("srctype", irq);
|
|
irqrestore(flags);
|
|
}
|
|
|