summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/arm/lpc32xx/irq/irq.c
diff options
context:
space:
mode:
authorThomas Doerfler <Thomas.Doerfler@embedded-brains.de>2009-12-15 15:20:47 +0000
committerThomas Doerfler <Thomas.Doerfler@embedded-brains.de>2009-12-15 15:20:47 +0000
commitc468f18bb73a570bf2b3eb279a7dea60b91c3319 (patch)
treeb181297c2b4a0f8fa3edbb9987fd99a3ecc45a8b /c/src/lib/libbsp/arm/lpc32xx/irq/irq.c
parentadd support for ARM11, reimplement nested interrupts (diff)
downloadrtems-c468f18bb73a570bf2b3eb279a7dea60b91c3319.tar.bz2
add support for LPC32xx
Diffstat (limited to 'c/src/lib/libbsp/arm/lpc32xx/irq/irq.c')
-rw-r--r--c/src/lib/libbsp/arm/lpc32xx/irq/irq.c372
1 files changed, 372 insertions, 0 deletions
diff --git a/c/src/lib/libbsp/arm/lpc32xx/irq/irq.c b/c/src/lib/libbsp/arm/lpc32xx/irq/irq.c
new file mode 100644
index 0000000000..187b4b310b
--- /dev/null
+++ b/c/src/lib/libbsp/arm/lpc32xx/irq/irq.c
@@ -0,0 +1,372 @@
+/**
+ * @file
+ *
+ * @ingroup bsp_interrupt
+ *
+ * @brief Interrupt support.
+ */
+
+/*
+ * Copyright (c) 2009
+ * embedded brains GmbH
+ * Obere Lagerstr. 30
+ * D-82178 Puchheim
+ * Germany
+ * <rtems@embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ */
+
+#include <bsp.h>
+#include <bsp/irq.h>
+#include <bsp/irq-generic.h>
+#include <bsp/lpc32xx.h>
+
+/*
+ * Mask out SIC 1 and 2 IRQ request. There is no need to mask out the FIQ,
+ * since a pending FIQ would be a fatal error. The default handler will be
+ * invoked in this case.
+ */
+#define LPC32XX_MIC_STATUS_MASK (~0x3U)
+
+typedef union {
+ struct {
+ uint32_t mic;
+ uint32_t sic_1;
+ uint32_t sic_2;
+ } field;
+ uint32_t fields_table [LPC32XX_IRQ_MODULE_COUNT];
+} lpc32xx_irq_fields;
+
+typedef struct {
+ uint32_t er;
+ uint32_t rsr;
+ uint32_t sr;
+ uint32_t apr;
+ uint32_t atr;
+ uint32_t itr;
+} lpc32xx_irq_controller;
+
+static volatile lpc32xx_irq_controller *const lpc32xx_mic = (volatile lpc32xx_irq_controller *) LPC32XX_BASE_MIC;
+
+static volatile lpc32xx_irq_controller *const lpc32xx_sic_1 = (volatile lpc32xx_irq_controller *) LPC32XX_BASE_SIC_1;
+
+static volatile lpc32xx_irq_controller *const lpc32xx_sic_2 = (volatile lpc32xx_irq_controller *) LPC32XX_BASE_SIC_2;
+
+static uint8_t lpc32xx_irq_priority_table [LPC32XX_IRQ_COUNT];
+
+static lpc32xx_irq_fields lpc32xx_irq_priority_masks [LPC32XX_IRQ_PRIORITY_COUNT];
+
+static lpc32xx_irq_fields lpc32xx_irq_enable;
+
+static inline bool lpc32xx_irq_is_valid(rtems_vector_number vector)
+{
+ return vector <= BSP_INTERRUPT_VECTOR_MAX;
+}
+
+static inline bool lpc32xx_irq_priority_is_valid(unsigned priority)
+{
+ return priority <= LPC32XX_IRQ_PRIORITY_LOWEST;
+}
+
+#define LPC32XX_IRQ_BIT_OPS_DEFINE \
+ unsigned bit = index & 0x1fU; \
+ unsigned module = index >> 5
+
+#define LPC32XX_IRQ_BIT_OPS_FOR_REG_DEFINE \
+ LPC32XX_IRQ_BIT_OPS_DEFINE; \
+ unsigned module_offset = module << 14; \
+ volatile uint32_t *reg = \
+ (volatile uint32_t *) (LPC32XX_BASE_MIC + module_offset + register_offset)
+
+#define LPC32XX_IRQ_OFFSET_ER 0U
+#define LPC32XX_IRQ_OFFSET_RSR 4U
+#define LPC32XX_IRQ_OFFSET_SR 8U
+#define LPC32XX_IRQ_OFFSET_APR 12U
+#define LPC32XX_IRQ_OFFSET_ATR 16U
+#define LPC32XX_IRQ_OFFSET_ITR 20U
+
+static inline bool lpc32xx_irq_is_bit_set_in_register(unsigned index, unsigned register_offset)
+{
+ LPC32XX_IRQ_BIT_OPS_FOR_REG_DEFINE;
+
+ return *reg & (1U << bit);
+}
+
+static inline void lpc32xx_irq_set_bit_in_register(unsigned index, unsigned register_offset)
+{
+ LPC32XX_IRQ_BIT_OPS_FOR_REG_DEFINE;
+
+ *reg |= 1U << bit;
+}
+
+static inline void lpc32xx_irq_clear_bit_in_register(unsigned index, unsigned register_offset)
+{
+ LPC32XX_IRQ_BIT_OPS_FOR_REG_DEFINE;
+
+ *reg &= ~(1U << bit);
+}
+
+static inline void lpc32xx_irq_set_bit_in_field(unsigned index, lpc32xx_irq_fields *fields)
+{
+ LPC32XX_IRQ_BIT_OPS_DEFINE;
+
+ fields->fields_table [module] |= 1U << bit;
+}
+
+static inline void lpc32xx_irq_clear_bit_in_field(unsigned index, lpc32xx_irq_fields *fields)
+{
+ LPC32XX_IRQ_BIT_OPS_DEFINE;
+
+ fields->fields_table [module] &= ~bit;
+}
+
+static inline unsigned lpc32xx_irq_get_index(uint32_t val)
+{
+ uint32_t reg;
+
+ asm volatile (
+ THUMB_TO_ARM
+ "clz %1, %1\n"
+ "rsb %1, %1, #31\n"
+ ARM_TO_THUMB
+ : "=&r" (reg), "=r" (val)
+ : "1" (val)
+ );
+
+ return val;
+}
+
+void lpc32xx_irq_set_priority(rtems_vector_number vector, unsigned priority)
+{
+ if (lpc32xx_irq_is_valid(vector)) {
+ rtems_interrupt_level level;
+ unsigned i = 0;
+
+ if (priority > LPC32XX_IRQ_PRIORITY_LOWEST) {
+ priority = LPC32XX_IRQ_PRIORITY_LOWEST;
+ }
+
+ lpc32xx_irq_priority_table [vector] = (uint8_t) priority;
+
+ for (i = LPC32XX_IRQ_PRIORITY_HIGHEST; i <= priority; ++i) {
+ rtems_interrupt_disable(level);
+ lpc32xx_irq_clear_bit_in_field(vector, &lpc32xx_irq_priority_masks [i]);
+ rtems_interrupt_enable(level);
+ }
+
+ for (i = priority + 1; i <= LPC32XX_IRQ_PRIORITY_LOWEST; ++i) {
+ rtems_interrupt_disable(level);
+ lpc32xx_irq_set_bit_in_field(vector, &lpc32xx_irq_priority_masks [i]);
+ rtems_interrupt_enable(level);
+ }
+ }
+}
+
+unsigned lpc32xx_irq_get_priority(rtems_vector_number vector)
+{
+ if (lpc32xx_irq_is_valid(vector)) {
+ return lpc32xx_irq_priority_table [vector];
+ } else {
+ return LPC32XX_IRQ_PRIORITY_LOWEST;
+ }
+}
+
+void lpc32xx_irq_set_activation_polarity(rtems_vector_number vector, lpc32xx_irq_activation_polarity activation_polarity)
+{
+ if (lpc32xx_irq_is_valid(vector)) {
+ rtems_interrupt_level level;
+
+ rtems_interrupt_disable(level);
+ if (activation_polarity == LPC32XX_IRQ_ACTIVE_HIGH_OR_RISING_EDGE) {
+ lpc32xx_irq_set_bit_in_register(vector, LPC32XX_IRQ_OFFSET_APR);
+ } else {
+ lpc32xx_irq_clear_bit_in_register(vector, LPC32XX_IRQ_OFFSET_APR);
+ }
+ rtems_interrupt_enable(level);
+ }
+}
+
+lpc32xx_irq_activation_polarity lpc32xx_irq_get_activation_polarity(rtems_vector_number vector)
+{
+ if (lpc32xx_irq_is_valid(vector)) {
+ if (lpc32xx_irq_is_bit_set_in_register(vector, LPC32XX_IRQ_OFFSET_APR)) {
+ return LPC32XX_IRQ_ACTIVE_HIGH_OR_RISING_EDGE;
+ } else {
+ return LPC32XX_IRQ_ACTIVE_LOW_OR_FALLING_EDGE;
+ }
+ } else {
+ return LPC32XX_IRQ_ACTIVE_LOW_OR_FALLING_EDGE;
+ }
+}
+
+void lpc32xx_irq_set_activation_type(rtems_vector_number vector, lpc32xx_irq_activation_type activation_type)
+{
+ if (lpc32xx_irq_is_valid(vector)) {
+ rtems_interrupt_level level;
+
+ rtems_interrupt_disable(level);
+ if (activation_type == LPC32XX_IRQ_EDGE_SENSITIVE) {
+ lpc32xx_irq_set_bit_in_register(vector, LPC32XX_IRQ_OFFSET_ATR);
+ } else {
+ lpc32xx_irq_clear_bit_in_register(vector, LPC32XX_IRQ_OFFSET_ATR);
+ }
+ rtems_interrupt_enable(level);
+ }
+}
+
+lpc32xx_irq_activation_type lpc32xx_irq_get_activation_type(rtems_vector_number vector)
+{
+ if (lpc32xx_irq_is_valid(vector)) {
+ if (lpc32xx_irq_is_bit_set_in_register(vector, LPC32XX_IRQ_OFFSET_ATR)) {
+ return LPC32XX_IRQ_EDGE_SENSITIVE;
+ } else {
+ return LPC32XX_IRQ_LEVEL_SENSITIVE;
+ }
+ } else {
+ return LPC32XX_IRQ_LEVEL_SENSITIVE;
+ }
+}
+
+void bsp_interrupt_dispatch(void)
+{
+ uint32_t status = lpc32xx_mic->sr & LPC32XX_MIC_STATUS_MASK;
+ uint32_t er_mic = lpc32xx_mic->er;
+ uint32_t er_sic_1 = lpc32xx_sic_1->er;
+ uint32_t er_sic_2 = lpc32xx_sic_2->er;
+ uint32_t psr = 0;
+ lpc32xx_irq_fields *masks = NULL;
+ rtems_vector_number vector = 0;
+ unsigned priority = 0;
+
+ if (status != 0) {
+ vector = lpc32xx_irq_get_index(status);
+ } else {
+ status = lpc32xx_sic_1->sr;
+ if (status != 0) {
+ vector = lpc32xx_irq_get_index(status) + LPC32XX_IRQ_MODULE_SIC_1;
+ } else {
+ status = lpc32xx_sic_2->sr;
+ if (status != 0) {
+ vector = lpc32xx_irq_get_index(status) + LPC32XX_IRQ_MODULE_SIC_2;
+ } else {
+ return;
+ }
+ }
+ }
+
+ priority = lpc32xx_irq_priority_table [vector];
+
+ masks = &lpc32xx_irq_priority_masks [priority];
+
+ lpc32xx_mic->er = er_mic & masks->field.mic;
+ lpc32xx_sic_1->er = er_sic_1 & masks->field.sic_1;
+ lpc32xx_sic_2->er = er_sic_2 & masks->field.sic_2;
+
+ psr = arm_status_irq_enable();
+
+ bsp_interrupt_handler_dispatch(vector);
+
+ arm_status_restore(psr);
+
+ lpc32xx_mic->er = er_mic & lpc32xx_irq_enable.field.mic;
+ lpc32xx_sic_1->er = er_sic_1 & lpc32xx_irq_enable.field.sic_1;
+ lpc32xx_sic_2->er = er_sic_2 & lpc32xx_irq_enable.field.sic_2;
+}
+
+rtems_status_code bsp_interrupt_vector_enable(rtems_vector_number vector)
+{
+ if (lpc32xx_irq_is_valid(vector)) {
+ rtems_interrupt_level level;
+
+ rtems_interrupt_disable(level);
+ lpc32xx_irq_set_bit_in_register(vector, LPC32XX_IRQ_OFFSET_ER);
+ lpc32xx_irq_set_bit_in_field(vector, &lpc32xx_irq_enable);
+ rtems_interrupt_enable(level);
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+rtems_status_code bsp_interrupt_vector_disable(rtems_vector_number vector)
+{
+ if (lpc32xx_irq_is_valid(vector)) {
+ rtems_interrupt_level level;
+
+ rtems_interrupt_disable(level);
+ lpc32xx_irq_clear_bit_in_field(vector, &lpc32xx_irq_enable);
+ lpc32xx_irq_clear_bit_in_register(vector, LPC32XX_IRQ_OFFSET_ER);
+ rtems_interrupt_enable(level);
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+rtems_status_code bsp_interrupt_facility_initialize(void)
+{
+ size_t i = 0;
+
+ /* Set default priority */
+ for (i = 0; i < LPC32XX_IRQ_COUNT; ++i) {
+ lpc32xx_irq_priority_table [i] = LPC32XX_IRQ_PRIORITY_LOWEST;
+ }
+
+ /* Enable SIC 1 and 2 at all priorities */
+ for (i = 0; i < LPC32XX_IRQ_PRIORITY_COUNT; ++i) {
+ lpc32xx_irq_priority_masks [i].field.mic = 0xc0000003;
+ }
+
+ /* Disable all interrupts except SIC 1 and 2 */
+ lpc32xx_irq_enable.field.sic_2 = 0x0;
+ lpc32xx_irq_enable.field.sic_1 = 0x0;
+ lpc32xx_irq_enable.field.mic = 0xc0000003;
+ lpc32xx_sic_1->er = 0x0;
+ lpc32xx_sic_2->er = 0x0;
+ lpc32xx_mic->er = 0xc0000003;
+
+ /* Set interrupt types to IRQ */
+ lpc32xx_mic->itr = 0x0;
+ lpc32xx_sic_1->itr = 0x0;
+ lpc32xx_sic_2->itr = 0x0;
+
+ /* Set interrupt activation polarities */
+ lpc32xx_mic->apr = 0x3ff0efe0;
+ lpc32xx_sic_1->apr = 0xfbd27184;
+ lpc32xx_sic_2->apr = 0x801810c0;
+
+ /* Set interrupt activation types */
+ lpc32xx_mic->atr = 0x0;
+ lpc32xx_sic_1->atr = 0x26000;
+ lpc32xx_sic_2->atr = 0x0;
+
+ _CPU_ISR_install_vector(ARM_EXCEPTION_IRQ, arm_exc_interrupt, NULL);
+
+ return RTEMS_SUCCESSFUL;
+}
+
+void bsp_interrupt_handler_default(rtems_vector_number vector)
+{
+ printk("spurious interrupt: %u\n", vector);
+}
+
+static void lpc32xx_irq_dump_controller(volatile lpc32xx_irq_controller *controller)
+{
+ printk(
+ "er %08x\nrsr %08x\nsr %08x\napr %08x\natr %08x\nitr %08x\n",
+ controller->er,
+ controller->rsr,
+ controller->sr,
+ controller->apr,
+ controller->atr,
+ controller->itr
+ );
+}
+
+void lpc32xx_irq_dump(void)
+{
+ lpc32xx_irq_dump_controller(lpc32xx_mic);
+ lpc32xx_irq_dump_controller(lpc32xx_sic_1);
+ lpc32xx_irq_dump_controller(lpc32xx_sic_2);
+}