summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libcpu/bfin/interrupt/interrupt.c
diff options
context:
space:
mode:
Diffstat (limited to 'c/src/lib/libcpu/bfin/interrupt/interrupt.c')
-rw-r--r--c/src/lib/libcpu/bfin/interrupt/interrupt.c197
1 files changed, 197 insertions, 0 deletions
diff --git a/c/src/lib/libcpu/bfin/interrupt/interrupt.c b/c/src/lib/libcpu/bfin/interrupt/interrupt.c
new file mode 100644
index 0000000000..77437c4b56
--- /dev/null
+++ b/c/src/lib/libcpu/bfin/interrupt/interrupt.c
@@ -0,0 +1,197 @@
+/* Support for Blackfin interrupt controller
+ *
+ * Copyright (c) 2008 Kallisti Labs, Los Gatos, CA, USA
+ * written by Allan Hessenflow <allanh@kallisti.com>
+ *
+ * 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.
+ *
+ * $Id$
+ */
+
+
+#include <rtems.h>
+#include <rtems/libio.h>
+
+#include <bsp.h>
+#include <libcpu/cecRegs.h>
+#include <libcpu/sicRegs.h>
+#include "interrupt.h"
+
+
+static struct {
+ uint32_t mask;
+ bfin_isr_t *head;
+} vectors[CEC_INTERRUPT_COUNT];
+
+static uint32_t globalMask;
+
+
+static rtems_isr interruptHandler(rtems_vector_number vector) {
+ bfin_isr_t *isr;
+ uint32_t sourceMask;
+
+ vector -= CEC_INTERRUPT_BASE_VECTOR;
+ if (vector >= 0 && vector < CEC_INTERRUPT_COUNT) {
+ isr = vectors[vector].head;
+ sourceMask = *(uint32_t volatile *) SIC_ISR &
+ *(uint32_t volatile *) SIC_IMASK;
+ while (isr) {
+ if (sourceMask & isr->mask) {
+ isr->isr(isr->source);
+ sourceMask = *(uint32_t volatile *) SIC_ISR &
+ *(uint32_t volatile *) SIC_IMASK;
+ }
+ isr = isr->next;
+ }
+ }
+}
+
+void bfin_interrupt_init(void) {
+ int source;
+ int vector;
+ uint32_t r;
+ int i;
+ int j;
+
+ globalMask = ~(uint32_t) 0;
+ *(uint32_t volatile *) SIC_IMASK = 0;
+ memset(vectors, 0, sizeof(vectors));
+ /* build mask showing what SIC sources drive each CEC vector */
+ source = 0;
+ for (i = 0; i < SIC_IAR_COUNT; i++) {
+ r = *(uint32_t volatile *) (SIC_IAR_BASE_ADDRESS + i * SIC_IAR_PITCH);
+ for (j = 0; j < 8; j++) {
+ vector = r & 0x0f;
+ if (vector >= 0 && vector < CEC_INTERRUPT_COUNT) {
+ if (vectors[vector].mask == 0)
+ /* install our local handler */
+ set_vector(interruptHandler, vector + CEC_INTERRUPT_BASE_VECTOR, 1);
+ vectors[vector].mask |= (1 << source);
+ }
+ r >>= 4;
+ source++;
+ }
+ }
+}
+
+/* modify SIC_IMASK based on ISR list for a particular CEC vector */
+static void setMask(int vector) {
+ bfin_isr_t *isr;
+ uint32_t mask;
+ uint32_t r;
+
+ mask = 0;
+ isr = vectors[vector].head;
+ while (isr) {
+ mask |= isr->mask;
+ isr = isr->next;
+ }
+ r = *(uint32_t volatile *) SIC_IMASK;
+ r &= ~vectors[vector].mask;
+ r |= mask;
+ r &= globalMask;
+ *(uint32_t volatile *) SIC_IMASK = r;
+}
+
+/* add an ISR to the list for whichever vector it belongs to */
+void bfin_interrupt_register(bfin_isr_t *isr) {
+ bfin_isr_t *walk;
+ rtems_interrupt_level isrLevel;
+
+ /* find the appropriate vector */
+ for (isr->vector = 0; isr->vector < CEC_INTERRUPT_COUNT; isr->vector++)
+ if (vectors[isr->vector].mask & (1 << isr->source))
+ break;
+ if (isr->vector < CEC_INTERRUPT_COUNT) {
+ isr->next = NULL;
+ isr->mask = 0;
+ rtems_interrupt_disable(isrLevel);
+ /* find the current end of the list */
+ walk = vectors[isr->vector].head;
+ while (walk && walk->next)
+ walk = walk->next;
+ /* append new isr to list */
+ if (walk)
+ walk->next = isr;
+ else
+ vectors[isr->vector].head = isr;
+ rtems_interrupt_enable(isrLevel);
+ } else
+ /* we failed, but make vector a legal value so other calls into
+ this module with this isr descriptor won't do anything bad */
+ isr->vector = 0;
+}
+
+void bfin_interrupt_unregister(bfin_isr_t *isr) {
+ bfin_isr_t *walk, *prev;
+ rtems_interrupt_level isrLevel;
+
+ rtems_interrupt_disable(isrLevel);
+ walk = vectors[isr->vector].head;
+ prev = NULL;
+ /* find this isr in our list */
+ while (walk && walk != isr) {
+ prev = walk;
+ walk = walk->next;
+ }
+ if (walk) {
+ /* if found, remove it */
+ if (prev)
+ prev->next = walk->next;
+ else
+ vectors[isr->vector].head = walk->next;
+ /* fix up SIC_IMASK if necessary */
+ setMask(isr->vector);
+ }
+ rtems_interrupt_enable(isrLevel);
+}
+
+void bfin_interrupt_enable(bfin_isr_t *isr, boolean enable) {
+ rtems_interrupt_level isrLevel;
+
+ rtems_interrupt_disable(isrLevel);
+ isr->mask = enable ? (1 << isr->source) : 0;
+ setMask(isr->vector);
+ rtems_interrupt_enable(isrLevel);
+}
+
+void bfin_interrupt_enable_all(int source, boolean enable) {
+ rtems_interrupt_level isrLevel;
+ int vector;
+ bfin_isr_t *walk;
+
+ for (vector = 0; vector < CEC_INTERRUPT_COUNT; vector++)
+ if (vectors[vector].mask & (1 << source))
+ break;
+ if (vector < CEC_INTERRUPT_COUNT) {
+ rtems_interrupt_disable(isrLevel);
+ walk = vectors[vector].head;
+ while (walk) {
+ walk->mask = enable ? (1 << source) : 0;
+ walk = walk->next;
+ }
+ setMask(vector);
+ rtems_interrupt_enable(isrLevel);
+ }
+}
+
+void bfin_interrupt_enable_global(int source, boolean enable) {
+ int vector;
+ rtems_interrupt_level isrLevel;
+
+ for (vector = 0; vector < CEC_INTERRUPT_COUNT; vector++)
+ if (vectors[vector].mask & (1 << source))
+ break;
+ if (vector < CEC_INTERRUPT_COUNT) {
+ rtems_interrupt_disable(isrLevel);
+ if (enable)
+ globalMask |= 1 << source;
+ else
+ globalMask &= ~(1 << source);
+ setMask(vector);
+ rtems_interrupt_enable(isrLevel);
+ }
+}
+