410 lines
12 KiB
C
410 lines
12 KiB
C
/****************************************************************************
|
|
* arch/arm/src/tms570/tms570_irq.c
|
|
*
|
|
* Copyright (C) 2015-2016 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 <errno.h>
|
|
#include <assert.h>
|
|
#include <debug.h>
|
|
|
|
#include <nuttx/irq.h>
|
|
#include <nuttx/arch.h>
|
|
#include <arch/irq.h>
|
|
|
|
#include "up_arch.h"
|
|
#include "up_internal.h"
|
|
|
|
#include "chip/tms570_vim.h"
|
|
#include "tms570_gio.h"
|
|
#include "tms570_esm.h"
|
|
#include "tms570_irq.h"
|
|
|
|
/****************************************************************************
|
|
* Public Data
|
|
****************************************************************************/
|
|
|
|
/* g_current_regs[] holds a references to the current interrupt level
|
|
* register storage structure. If is non-NULL only during interrupt
|
|
* processing. Access to g_current_regs[] must be through the macro
|
|
* CURRENT_REGS for portability.
|
|
*/
|
|
|
|
volatile uint32_t *g_current_regs[1];
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: tms570_error_handler
|
|
****************************************************************************/
|
|
|
|
static void tms570_error_handler(void)
|
|
{
|
|
PANIC();
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: up_irqinitialize
|
|
|
|
* The device supports three different possibilities for software to handle
|
|
* interrupts:
|
|
*
|
|
* 1. Index interrupts mode (compatible with TMS470R1x legacy code),
|
|
* 2. Register vectored interrupts (automatically provide vector address
|
|
* to application)
|
|
* 3. Hardware vectored interrupts (automatically dispatch to ISR, IRQ
|
|
* only)
|
|
*
|
|
* Only the indexed mode is supported here: After the interrupt is received
|
|
* by the CPU, the CPU branches to 0x18 (IRQ) or 0x1C (FIQ) to execute the
|
|
* main ISR. The main ISR routine reads the offset register (IRQINDEX,
|
|
* FIQINDEX) to determine the source of the interrupt.
|
|
*
|
|
* To use mode 2), it would only be necessary to initialize the VIM_RAM.
|
|
* To use mode 3), it would be necessary to initialize the VIM_RAM and also
|
|
* to set the vector enable (VE) bit in the CP15 R1 register. This bit is
|
|
* zero on reset so that the default state after reset is backward
|
|
* compatible to earlier ARM CPU.
|
|
*
|
|
****************************************************************************/
|
|
|
|
void up_irqinitialize(void)
|
|
{
|
|
FAR uintptr_t *vimram;
|
|
int i;
|
|
|
|
/* Colorize the interrupt stack for debug purposes */
|
|
|
|
#if defined(CONFIG_STACK_COLORATION) && CONFIG_ARCH_INTERRUPTSTACK > 3
|
|
size_t intstack_size = (CONFIG_ARCH_INTERRUPTSTACK & ~3);
|
|
up_stack_color((FAR void *)((uintptr_t)&g_intstackbase - intstack_size),
|
|
intstack_size);
|
|
#endif
|
|
|
|
/* Initialize VIM RAM vectors. These vectors are not used in the current
|
|
* interrupt handler logic.
|
|
*/
|
|
|
|
vimram = (FAR uintptr_t *)TMS570_VIMRAM_BASE;
|
|
for (i = 0; i < (TMS570_IRQ_NCHANNELS + 1); i++)
|
|
{
|
|
*vimram++ = (uintptr_t)tms570_error_handler;
|
|
}
|
|
|
|
/* Set Fall-Back Address Parity Error Register (also not used) */
|
|
|
|
putreg32((uint32_t)tms570_error_handler, TMS570_VIM_FBPARERR);
|
|
|
|
/* Assign all interrupt requests to the VIM channel of the same value.
|
|
* NOTE: Nothing need be done. That is the power-on default mapping.
|
|
*/
|
|
|
|
/* Assign all channels to IRQs */
|
|
|
|
putreg32(0, TMS570_VIM_FIRQPR0);
|
|
putreg32(0, TMS570_VIM_FIRQPR1);
|
|
putreg32(0, TMS570_VIM_FIRQPR2);
|
|
#ifdef TMS570_VIM_FIRQPR3
|
|
putreg32(0, TMS570_VIM_FIRQPR3);
|
|
#endif
|
|
|
|
/* Disable all interrupts */
|
|
|
|
putreg32(0xfffffffc, TMS570_VIM_REQENACLR0);
|
|
putreg32(0xffffffff, TMS570_VIM_REQENACLR1);
|
|
putreg32(0xffffffff, TMS570_VIM_REQENACLR2);
|
|
#ifdef TMS570_VIM_REQENACLR3
|
|
putreg32(0xffffffff, TMS570_VIM_REQENACLR3);
|
|
#endif
|
|
|
|
/* currents_regs is non-NULL only while processing an interrupt */
|
|
|
|
CURRENT_REGS = NULL;
|
|
|
|
#ifdef CONFIG_ARMV7R_HAVE_DECODEFIQ
|
|
/* By default, interrupt CHAN0 is mapped to ESM (Error Signal Module)
|
|
* high level interrupt and CHAN1 is reserved for other NMI. For safety
|
|
* reasons, these two channels are mapped to FIQ only and can NOT be
|
|
* disabled through ENABLE registers.
|
|
*/
|
|
|
|
#endif
|
|
|
|
#ifndef CONFIG_SUPPRESS_INTERRUPTS
|
|
#ifdef CONFIG_TMS570_GIO_IRQ
|
|
/* Initialize logic to support a second level of interrupt decoding for
|
|
* GIO pins.
|
|
*/
|
|
|
|
tms570_gioirq_initialize();
|
|
#endif
|
|
|
|
/* Attach and enable ESM interrupts. The high level interrupt is really
|
|
* an NMI.
|
|
*/
|
|
|
|
(void)irq_attach(TMS570_REQ_ESMHIGH, tms570_esm_interrupt);
|
|
(void)irq_attach(TMS570_REQ_ESMLO, tms570_esm_interrupt);
|
|
up_enable_irq(TMS570_REQ_ESMHIGH);
|
|
up_enable_irq(TMS570_REQ_ESMLO);
|
|
|
|
/* And finally, enable interrupts globally */
|
|
|
|
up_irq_enable();
|
|
#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 parameters:
|
|
* regs - A pointer to the register save area on the stack.
|
|
*
|
|
****************************************************************************/
|
|
|
|
uint32_t *arm_decodeirq(uint32_t *regs)
|
|
{
|
|
int vector;
|
|
|
|
/* Check for a VRAM parity error.
|
|
*
|
|
* REVISIT: This is not to critical in this implementation since VIM RAM
|
|
* is not used.
|
|
*/
|
|
|
|
/* Get the interrupting vector number from the IRQINDEX register. Zero,
|
|
* the "phantom" vector will returned.
|
|
*/
|
|
|
|
vector = getreg32(TMS570_VIM_IRQINDEX) & VIM_IRQINDEX_MASK;
|
|
if (vector > 0)
|
|
{
|
|
/* Dispatch the interrupt. NOTE that the IRQ number is the vector
|
|
* number offset by one to skip over the "phantom" vector.
|
|
*/
|
|
|
|
regs = arm_doirq(vector - 1, regs);
|
|
}
|
|
|
|
return regs;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: arm_decodefiq
|
|
*
|
|
* Description:
|
|
* This function is called from the FIQ 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.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_ARMV7R_HAVE_DECODEFIQ
|
|
uint32_t *arm_decodefiq(FAR uint32_t *regs)
|
|
{
|
|
int vector;
|
|
|
|
/* Check for a VRAM parity error.
|
|
*
|
|
* REVISIT: This is not to critical in this implementation since VIM RAM
|
|
* is not used.
|
|
*/
|
|
|
|
/* Get the interrupting vector number from the FIQINDEX register. Zero,
|
|
* the "phantom" vector will returned.
|
|
*/
|
|
|
|
vector = getreg32(TMS570_VIM_FIQINDEX) & VIM_FIQINDEX_MASK;
|
|
if (vector > 0)
|
|
{
|
|
/* Dispatch the interrupt. NOTE that the IRQ number is the vector
|
|
* number offset by one to skip over the "phantom" vector.
|
|
*/
|
|
|
|
regs = arm_doirq(vector - 1, regs);
|
|
}
|
|
|
|
return regs;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: up_disable_irq
|
|
*
|
|
* Description:
|
|
* Disable the IRQ or FIQ specified by 'channel'
|
|
*
|
|
****************************************************************************/
|
|
|
|
void up_disable_irq(int channel)
|
|
{
|
|
uintptr_t regaddr;
|
|
uint32_t regval;
|
|
uint32_t bitmask;
|
|
unsigned int regndx;
|
|
|
|
DEBUGASSERT(channel >= 0 && channel < TMS570_IRQ_NCHANNELS);
|
|
|
|
/* Offset to account for the "phantom" vector */
|
|
|
|
channel++;
|
|
regndx = VIM_REGNDX(channel);
|
|
channel = VIM_REGBIT(channel);
|
|
bitmask = (1 << channel);
|
|
|
|
/* Disable the IRQ/FIQ by setting the corresponding REQENACLR bit. */
|
|
|
|
regaddr = TMS570_VIM_REQENACLR(regndx);
|
|
regval = getreg32(regaddr);
|
|
regaddr |= bitmask;
|
|
putreg32(regval, regaddr);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: up_enable_irq
|
|
*
|
|
* Description:
|
|
* Enable the IRQ specified by 'channel'
|
|
*
|
|
****************************************************************************/
|
|
|
|
void up_enable_irq(int channel)
|
|
{
|
|
uintptr_t regaddr;
|
|
uint32_t regval;
|
|
uint32_t bitmask;
|
|
unsigned int regndx;
|
|
|
|
DEBUGASSERT(channel >= 0 && channel < TMS570_IRQ_NCHANNELS);
|
|
|
|
/* Offset to account for the "phantom" vector */
|
|
|
|
channel++;
|
|
regndx = VIM_REGNDX(channel);
|
|
channel = VIM_REGBIT(channel);
|
|
bitmask = (1 << channel);
|
|
|
|
#ifdef CONFIG_ARMV7R_HAVE_DECODEFIQ
|
|
/* Select IRQ (vs FIQ) by clearing the corresponding FIRQPR bit */
|
|
|
|
regaddr = TMS570_VIM_FIRQPR(regndx);
|
|
regval = getreg32(regaddr);
|
|
regaddr &= ~bitmask;
|
|
putreg32(regval, regaddr);
|
|
#endif
|
|
|
|
/* Enable the IRQ by setting the corresponding REQENASET bit. */
|
|
|
|
regaddr = TMS570_VIM_REQENASET(regndx);
|
|
regval = getreg32(regaddr);
|
|
regaddr |= bitmask;
|
|
putreg32(regval, regaddr);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: up_enable_fiq
|
|
*
|
|
* Description:
|
|
* Enable the FIQ specified by 'channel'
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifdef CONFIG_ARMV7R_HAVE_DECODEFIQ
|
|
void up_enable_fiq(int channel)
|
|
{
|
|
uintptr_t regaddr;
|
|
uint32_t regval;
|
|
uint32_t bitmask;
|
|
unsigned int regndx;
|
|
|
|
DEBUGASSERT(channel >= 0 && channel < TMS570_IRQ_NCHANNELS);
|
|
|
|
/* Offset to account for the "phantom" vector */
|
|
|
|
channel++;
|
|
regndx = VIM_REGNDX(channel);
|
|
channel = VIM_REGBIT(channel);
|
|
bitmask = (1 << channel);
|
|
|
|
/* Select FIQ (vs IRQ) by setting the corresponding FIRQPR bit */
|
|
|
|
regaddr = TMS570_VIM_FIRQPR(regndx);
|
|
regval = getreg32(regaddr);
|
|
regaddr &= ~bitmask;
|
|
putreg32(regval, regaddr);
|
|
|
|
/* Enable the FIQ by setting the corresponding REQENASET bit. */
|
|
|
|
regaddr = TMS570_VIM_REQENASET(regndx);
|
|
regval = getreg32(regaddr);
|
|
regaddr |= bitmask;
|
|
putreg32(regval, regaddr);
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* Name: up_ack_irq
|
|
*
|
|
* Description:
|
|
* Acknowledge the IRQ
|
|
*
|
|
****************************************************************************/
|
|
|
|
void up_ack_irq(int irq)
|
|
{
|
|
}
|