/* Support for Blackfin interrupt controller * * Copyright (c) 2008 Kallisti Labs, Los Gatos, CA, USA * written by Allan Hessenflow * * 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 #include #include #include #include #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); } }