Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/*
 * Copyright (c) 2013-2014 Wind River Systems, Inc.
 *
 * Licensed 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.
 */

/**
 * @file
 * @brief Nvic.c - ARM CORTEX-M Series Nested Vector Interrupt Controller
 *
 * Provide an interface to the Nested Vectored Interrupt Controller found on
 * ARM Cortex-M processors.
 *
 * The API does not account for all possible usages of the NVIC, only the
 * functionalities needed by the kernel.
 *
 * The same effect can be achieved by directly writing in the registers of the
 * NVIC, with the layout available from scs.h, using the __scs.nvic data
 * structure (or hardcoded values), but these APIs are less error-prone,
 * especially for registers with multiple instances to account for potentially
 * 240 interrupt lines. If access to a missing functionality is needed, this is
 * the way to implement it.
 *
 * Supports up to 240 IRQs and 256 priority levels.
 */

#ifndef _NVIC_H_
#define _NVIC_H_

#include <arch/arm/cortex_m/scs.h>

#ifdef __cplusplus
extern "C" {
#endif

/* for assembler, only works with constants */
#define _EXC_PRIO(pri) (((pri) << (8 - CONFIG_NUM_IRQ_PRIO_BITS)) & 0xff)
#if defined(CONFIG_ZERO_LATENCY_IRQS)
#define _EXC_IRQ_DEFAULT_PRIO _EXC_PRIO(0x03)
#else
#define _EXC_IRQ_DEFAULT_PRIO _EXC_PRIO(0x02)
#endif

/* no exc #0 */
#define _EXC_RESET 1
#define _EXC_NMI 2
#define _EXC_HARD_FAULT 3
#define _EXC_MPU_FAULT 4
#define _EXC_BUS_FAULT 5
#define _EXC_USAGE_FAULT 6
/* 7-10 reserved */
#define _EXC_SVC 11
#define _EXC_DEBUG 12
/* 13 reserved */
#define _EXC_PENDSV 14
#define _EXC_SYSTICK 15
/* 16+ IRQs */

#define _NUM_EXC 16

#define NUM_IRQS_PER_REG 32
#define REG_FROM_IRQ(irq) (irq / NUM_IRQS_PER_REG)
#define BIT_FROM_IRQ(irq) (irq % NUM_IRQS_PER_REG)

#if !defined(_ASMLANGUAGE)

#include <stdint.h>
#include <misc/__assert.h>

/**
 *
 * @brief Enable an IRQ
 *
 * Enable IRQ #@a irq, which is equivalent to exception #@a irq+16
 *
 * @param irq IRQ number
 *
 * @return N/A
 */

static inline void _NvicIrqEnable(unsigned int irq)
{
	__scs.nvic.iser[REG_FROM_IRQ(irq)] = 1 << BIT_FROM_IRQ(irq);
}

/**
 *
 * @brief Find out if an IRQ is enabled
 *
 * Find out if IRQ #@a irq is enabled.
 *
 * @param irq IRQ number
 * @return 1 if IRQ is enabled, 0 otherwise
 */

static inline int _NvicIsIrqEnabled(unsigned int irq)
{
	return __scs.nvic.iser[REG_FROM_IRQ(irq)] & (1 << BIT_FROM_IRQ(irq));
}

/**
 *
 * @brief Disable an IRQ
 *
 * Disable IRQ #@a irq, which is equivalent to exception #@a irq+16
 * @param irq IRQ number
 * @return N/A
 */

static inline void _NvicIrqDisable(unsigned int irq)
{
	__scs.nvic.icer[REG_FROM_IRQ(irq)] = 1 << BIT_FROM_IRQ(irq);
}

/**
 *
 * @brief Pend an IRQ
 *
 * Pend IRQ #@a irq, which is equivalent to exception #@a irq+16. CPU will handle
 * the IRQ when interrupts are enabled and/or returning from a higher priority
 * interrupt.
 * @param irq IRQ number
 *
 * @return N/A
 */

static inline void _NvicIrqPend(unsigned int irq)
{
	__scs.nvic.ispr[REG_FROM_IRQ(irq)] = 1 << BIT_FROM_IRQ(irq);
}

/**
 *
 * @brief Find out if an IRQ is pending
 *
 * Find out if IRQ #@a irq is pending
 *
 * @param irq IRQ number
 * @return 1 if IRQ is pending, 0 otherwise
 */

static inline int _NvicIsIrqPending(unsigned int irq)
{
	return __scs.nvic.ispr[REG_FROM_IRQ(irq)] & (1 << BIT_FROM_IRQ(irq));
}

/**
 *
 * @brief Unpend an IRQ
 *
 * Unpend IRQ #@a irq, which is equivalent to exception #@a irq+16. The previously
 * pending interrupt will be ignored when either unlocking interrupts or
 * returning from a higher priority exception.
 *
 * @param irq IRQ number
 * @return N/A
 */

static inline void _NvicIrqUnpend(unsigned int irq)
{
	__scs.nvic.icpr[REG_FROM_IRQ(irq)] = 1 << BIT_FROM_IRQ(irq);
}

/**
 *
 * @brief Set priority of an IRQ
 *
 * Set priority of IRQ #@a irq to @a prio. There are 256 priority levels.
 *
 * @param irq IRQ number
 * @param prio Priority
 * @return N/A
 */

static inline void _NvicIrqPrioSet(unsigned int irq, unsigned int prio)
{
	__ASSERT(prio < 256, "invalid priority\n");
	__scs.nvic.ipr[irq] = prio;
}

/**
 *
 * @brief Get priority of an IRQ
 *
 * Get priority of IRQ #@a irq.
 *
 * @param irq IRQ number
 *
 * @return the priority level of the IRQ
 */

static inline uint32_t _NvicIrqPrioGet(unsigned int irq)
{
	return __scs.nvic.ipr[irq];
}

/**
 *
 * @brief Trigger an interrupt via software
 *
 * Trigger interrupt #@a irq. The CPU will handle the IRQ when interrupts are
 * enabled and/or returning from a higher priority interrupt.
 *
 * @param irq IRQ number
 * @return N/A
 */

static inline void _NvicSwInterruptTrigger(unsigned int irq)
{
#if defined(CONFIG_SOC_TI_LM3S6965_QEMU)
	/* the QEMU does not simulate the STIR register: this is a workaround */
	_NvicIrqPend(irq);
#else
	__scs.stir = irq;
#endif
}

#endif /* !_ASMLANGUAGE */

#ifdef __cplusplus
}
#endif

#endif /* _NVIC_H_ */