diff options
Diffstat (limited to 'c/src/lib/libcpu/bfin/bf52x/interrupt/interrupt.c')
-rw-r--r-- | c/src/lib/libcpu/bfin/bf52x/interrupt/interrupt.c | 643 |
1 files changed, 643 insertions, 0 deletions
diff --git a/c/src/lib/libcpu/bfin/bf52x/interrupt/interrupt.c b/c/src/lib/libcpu/bfin/bf52x/interrupt/interrupt.c new file mode 100644 index 0000000000..32f9b2401e --- /dev/null +++ b/c/src/lib/libcpu/bfin/bf52x/interrupt/interrupt.c @@ -0,0 +1,643 @@ +/** + *@file interrupt.c + * + *@brief + * - This file implements interrupt dispatcher. Most of the code is taken from + * the 533 implementation for blackfin. Since 52X supports 56 line and 2 ISR + * registers some portion is written twice. + * + * Target: TLL6527v1-0 + * Compiler: + * + * COPYRIGHT (c) 2010 by ECE Northeastern University. + * + * 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 + * + * @author Rohan Kangralkar, ECE, Northeastern University + * (kangralkar.r@husky.neu.edu) + * + * LastChange: + * $Id$ + * + */ + +#include <rtems.h> +#include <rtems/libio.h> + +#include <bsp.h> +#include <libcpu/cecRegs.h> +#include <libcpu/sicRegs.h> +#include "interrupt.h" + +#define SIC_IAR_COUNT_SET0 4 +#define SIC_IAR_BASE_ADDRESS_0 0xFFC00150 + +/** + * There are two implementations for the interrupt handler. + * 1. INTERRUPT_USE_TABLE: uses tables for finding the right ISR. + * 2. Uses link list to find the user ISR. + * + * + * 1. INTERRUPT_USE_TABLE + * Space requirement: + * - Array to hold CEC masks size: CEC_INTERRUPT_COUNT(9)*(2*int).9*2*4= 72B + * - Array to hold isr function pointers IRQ_MAX(56)*sizeof(bfin_isr_t)= 896B + * - Array for bit twidlling 32 bytes. + * - Global Mask 8 bytes. + * - Total = 1008 Bytes Aprox + * + * Time requirements + * The worst case time is about the same for jumping to the user ISR. With a + * variance of one conditional statement. + * + * 2. Using link list. + * Space requirement: + * - Array to hold CEC mask CEC_INTERRUPT_COUNT(9)*(sizeof(vectors)). + * 9*3*4= 108B + * - Array to hold isr IRQ_MAX(56)*sizeof(bfin_isr_t) The structure has + * additional pointers 56*7*4=1568B + * - Global Mask 8 bytes. + * Total = 1684. + * Time requirements + * In the worst case all the lines can be on one CEC line to 56 entries have + * to be traversed to find the right user ISR. + * But this implementation has benefit of being flexible, Providing + * additional user assigned priority. and may consume less space + * if all devices are not supported. + */ + +/** + * TODO: To place the dispatcher routine code in L1. + */ + +#if INTERRUPT_USE_TABLE + + +/****************************************************************************** + * Static variables + *****************************************************************************/ +/** + * @var sic_isr0_mask + * @brief copy of the mask of SIC ISR. The SIC ISR is cleared by the device + * the relevant SIC_ISRx bit is not cleared unless the interrupt + * service routine clears the mechanism that generated interrupt + */ +static uint32_t sic_isr0_mask = 0; + +/** + * @var sic_isr0_mask + * @brief copy of the mask of SIC ISR. The SIC ISR is cleared by the device + * the relevant SIC_ISRx bit is not cleared unless the interrupt + * service routine clears the mechanism that generated interrupt + */ +static uint32_t sic_isr1_mask = 0; + + +/** + * @var sic_isr + * @brief An array of sic register mask for each of the 16 core interrupt lines + */ +static struct { + uint32_t mask0; + uint32_t mask1; +} vectors[CEC_INTERRUPT_COUNT]; + +/** + * @var ivt + * @brief Contains a table of ISR and arguments. The ISR jumps directly to + * these ISR. + */ +static bfin_isr_t ivt[IRQ_MAX]; + +/** + * http://graphics.stanford.edu/~seander/bithacks.html for more details + */ +static const char clz_table[32] = +{ + 0, 31, 9, 30, 3, 8, 18, 29, 2, 5, 7, 14, 12, 17, + 22, 28, 1, 10, 4, 19, 6, 15, 13, 23, 11, 20, 16, + 24, 21, 25, 26, 27 +}; + +/** + * finds the first bit set from the left. look at + * http://graphics.stanford.edu/~seander/bithacks.html for more details + * @param n + * @return + */ +static unsigned long clz(unsigned long n) +{ + unsigned long c = 0x7dcd629; /* magic constant... */ + + n |= (n >> 1); + n |= (n >> 2); + n |= (n >> 4); + n |= (n >> 8); + n |= (n >> 16); + if (n == 0) return 32; + n = c + (c * n); + return 31 - clz_table[n >> 27]; /* For little endian */ +} + + + +/** + * Centralized Interrupt dispatcher routine. This routine dispatches interrupts + * to the user ISR. The priority is according to the blackfin SIC. + * The first level of priority is handled in the hardware at the core event + * controller. The second level of interrupt is handled according to the line + * number that goes in to the SIC. + * * SIC_0 has higher priority than SIC 1. + * * Inside the SIC the priority is assigned according to the line number. + * Lower the line number higher the priority. + * + * In order to change the interrupt priority we may + * 1. change the SIC IAR registers or + * 2. Assign priority and extract it inside this function and call the ISR + * according tot the priority. + * + * @param vector IVG number. + * @return + */ +static rtems_isr interruptHandler(rtems_vector_number vector) { + uint32_t mask = 0; + int id = 0; + /** + * Enable for debugging + * + * static volatile uint32_t spurious_sic0 = 0; + * static volatile uint32_t spurious_source = 0; + * static volatile uint32_t spurious_sic1 = 0; + */ + + /** + * Extract the vector number relative to the SIC start line + */ + vector -= CEC_INTERRUPT_BASE_VECTOR; + + /** + * Check for bounds + */ + if (vector >= 0 && vector < CEC_INTERRUPT_COUNT) { + + /** + * Extract information and execute ISR from SIC 0 + */ + mask = *(uint32_t volatile *) SIC_ISR & + *(uint32_t volatile *) SIC_IMASK & vectors[vector].mask0; + id = clz(mask); + if ( SIC_ISR0_MAX > id ) { + /** Parameter check */ + if( NULL != ivt[id].pFunc) { + /** Call the relevant function with argument */ + ivt[id].pFunc( ivt[id].pArg ); + } else { + /** + * spurious interrupt we should not be getting this + * spurious_sic0++; + * spurious_source = id; + */ + } + } else { + /** + * we look at SIC 1 + */ + } + + + /** + * Extract information and execute ISR from SIC 1 + */ + mask = *(uint32_t volatile *) (SIC_ISR + SIC_ISR_PITCH) & + *(uint32_t volatile *) (SIC_IMASK + SIC_IMASK_PITCH) & + vectors[vector].mask1; + id = clz(mask)+SIC_ISR0_MAX; + if ( IRQ_MAX > id ) { + /** Parameter Check */ + if( NULL != ivt[id].pFunc ) { + /** Call the relevant function with argument */ + ivt[id].pFunc( ivt[id].pArg ); + } else { + /** + * spurious interrupt we should not be getting this + * + * spurious_sic1++; + * spurious_source = id; + */ + } + } else { + /** + * we continue + */ + } + + } +} + + + +/** + * This routine registers a new ISR. It will write a new entry to the IVT table + * @param isr contains a callback function and source + * @return rtems status code + */ +rtems_status_code bfin_interrupt_register(bfin_isr_t *isr) { + rtems_interrupt_level isrLevel; + int id = 0; + int position = 0; + + /** + * Sanity Check + */ + if ( NULL == isr ){ + return RTEMS_UNSATISFIED; + } + + /** + * Sanity check. The register function should at least provide callback func + */ + if ( NULL == isr->pFunc ) { + return RTEMS_UNSATISFIED; + } + + id = isr->source; + + /** + * Parameter Check. We already have a function registered here. First + * unregister and then a new function can be allocated. + */ + if ( NULL != ivt[id].pFunc ) { + return RTEMS_UNSATISFIED; + } + + rtems_interrupt_disable(isrLevel); + /** + * Assign the new function pointer to the ISR Dispatcher + * */ + ivt[id].pFunc = isr->pFunc; + ivt[id].pArg = isr->pArg; + + + /** find out which isr mask has to be set to enable the interrupt */ + if ( SIC_ISR0_MAX > id ) { + sic_isr0_mask |= 0x1<<id; + *(uint32_t volatile *) SIC_IMASK |= 0x1<<id; + } else { + position = id - SIC_ISR0_MAX; + sic_isr1_mask |= 0x1<<position; + *(uint32_t volatile *) (SIC_IMASK + SIC_IMASK_PITCH) |= 0x1<<position; + } + + rtems_interrupt_enable(isrLevel); + + return RTEMS_SUCCESSFUL; +} + + +/** + * This function unregisters a registered interrupt handler. + * @param isr + */ +rtems_status_code bfin_interrupt_unregister(bfin_isr_t *isr) { + rtems_interrupt_level isrLevel; + int id = 0; + int position = 0; + + /** + * Sanity Check + */ + if ( NULL == isr ){ + return RTEMS_UNSATISFIED; + } + + id = isr->source; + + rtems_interrupt_disable(isrLevel); + /** + * Assign the new function pointer to the ISR Dispatcher + * */ + ivt[id].pFunc = NULL; + ivt[id].pArg = NULL; + + + /** find out which isr mask has to be set to enable the interrupt */ + if ( SIC_ISR0_MAX > id ) { + sic_isr0_mask &= ~(0x1<<id); + *(uint32_t volatile *) SIC_IMASK &= ~(0x1<<id); + } else { + position = id - SIC_ISR0_MAX; + sic_isr1_mask &= ~(0x1<<position); + *(uint32_t volatile *) (SIC_IMASK + SIC_IMASK_PITCH) &= ~(0x1<<position); + } + + rtems_interrupt_enable(isrLevel); + + return RTEMS_SUCCESSFUL; +} + + + + +/** + * blackfin interrupt initialization routine. It initializes the bfin ISR + * dispatcher. It will also create SIC CEC map which will be used for + * identifying the ISR. + */ +void bfin_interrupt_init(void) { + int source; + int vector; + uint32_t r; + int i; + int j; + + *(uint32_t volatile *) SIC_IMASK = 0; + *(uint32_t volatile *) (SIC_IMASK + SIC_IMASK_PITCH) = 0; + + memset(vectors, 0, sizeof(vectors)); + /* build mask0 showing what SIC sources drive each CEC vector */ + source = 0; + + /** + * The bf52x has 8 IAR registers but they do not have a constant pitch. + * + */ + for (i = 0; i < SIC_IAR_COUNT; i++) { + if ( SIC_IAR_COUNT_SET0 > i ) { + r = *(uint32_t volatile *) (SIC_IAR_BASE_ADDRESS + i * SIC_IAR_PITCH); + } else { + r = *(uint32_t volatile *) (SIC_IAR_BASE_ADDRESS_0 + + ((i-SIC_IAR_COUNT_SET0) * SIC_IAR_PITCH)); + } + + for (j = 0; j < 8; j++) { + vector = r & 0x0f; + if (vector >= 0 && vector < CEC_INTERRUPT_COUNT) { + /* install our local handler */ + if (vectors[vector].mask0 == 0 && vectors[vector].mask1 == 0){ + set_vector(interruptHandler, vector + CEC_INTERRUPT_BASE_VECTOR, 1); + } + if ( SIC_ISR0_MAX > source ) { + vectors[vector].mask0 |= (1 << source); + } else { + vectors[vector].mask1 |= (1 << (source - SIC_ISR0_MAX)); + } + } + r >>= 4; + source++; + } + } +} + + + + + +#else + +static struct { + uint32_t mask0; + uint32_t mask1; + bfin_isr_t *head; +} vectors[CEC_INTERRUPT_COUNT]; + +static uint32_t globalMask0; +static uint32_t globalMask1; + +static rtems_isr interruptHandler(rtems_vector_number vector) { + bfin_isr_t *isr = NULL; + uint32_t sourceMask0 = 0; + uint32_t sourceMask1 = 0; + rtems_interrupt_level isrLevel; + + rtems_interrupt_disable(isrLevel); + vector -= CEC_INTERRUPT_BASE_VECTOR; + if (vector >= 0 && vector < CEC_INTERRUPT_COUNT) { + isr = vectors[vector].head; + sourceMask0 = *(uint32_t volatile *) SIC_ISR & + *(uint32_t volatile *) SIC_IMASK; + sourceMask1 = *(uint32_t volatile *) (SIC_ISR + SIC_ISR_PITCH) & + *(uint32_t volatile *) (SIC_IMASK + SIC_IMASK_PITCH); + while (isr) { + if ((sourceMask0 & isr->mask0) || (sourceMask1 & isr->mask1)) { + isr->isr(isr->_arg); + sourceMask0 = *(uint32_t volatile *) SIC_ISR & + *(uint32_t volatile *) SIC_IMASK; + sourceMask1 = *(uint32_t volatile *) (SIC_ISR + SIC_ISR_PITCH) & + *(uint32_t volatile *) (SIC_IMASK + SIC_IMASK_PITCH); + } + isr = isr->next; + } + } + rtems_interrupt_enable(isrLevel); +} + +/** + * Initializes the interrupt module + */ +void bfin_interrupt_init(void) { + int source; + int vector; + uint32_t r; + int i; + int j; + + globalMask0 = ~(uint32_t) 0; + globalMask1 = ~(uint32_t) 0; + *(uint32_t volatile *) SIC_IMASK = 0; + *(uint32_t volatile *) (SIC_IMASK + SIC_IMASK_PITCH) = 0; + + memset(vectors, 0, sizeof(vectors)); + /* build mask0 showing what SIC sources drive each CEC vector */ + source = 0; + + /** + * The bf52x has 8 IAR registers but they do not have a constant pitch. + * + */ + for (i = 0; i < SIC_IAR_COUNT; i++) { + if ( SIC_IAR_COUNT_SET0 > i ) { + r = *(uint32_t volatile *) (SIC_IAR_BASE_ADDRESS + i * SIC_IAR_PITCH); + } else { + r = *(uint32_t volatile *) (SIC_IAR_BASE_ADDRESS_0 + + ((i-SIC_IAR_COUNT_SET0) * SIC_IAR_PITCH)); + } + for (j = 0; j < 8; j++) { + vector = r & 0x0f; + if (vector >= 0 && vector < CEC_INTERRUPT_COUNT) { + /* install our local handler */ + if (vectors[vector].mask0 == 0 && vectors[vector].mask1 == 0){ + set_vector(interruptHandler, vector + CEC_INTERRUPT_BASE_VECTOR, 1); + } + if ( SIC_ISR0_MAX > source ) { + vectors[vector].mask0 |= (1 << source); + } else { + vectors[vector].mask1 |= (1 << (source - SIC_ISR0_MAX)); + } + } + r >>= 4; + source++; + } + } +} + +/* modify SIC_IMASK based on ISR list for a particular CEC vector */ +static void setMask(uint32_t vector) { + bfin_isr_t *isr = NULL; + uint32_t mask = 0; + uint32_t r = 0; + + mask = 0; + isr = vectors[vector].head; + while (isr) { + mask |= isr->mask0; + isr = isr->next; + } + r = *(uint32_t volatile *) SIC_IMASK; + r &= ~vectors[vector].mask0; + r |= mask; + r &= globalMask0; + *(uint32_t volatile *) SIC_IMASK = r; + + + mask = 0; + isr = vectors[vector].head; + while (isr) { + mask |= isr->mask1; + isr = isr->next; + } + r = *(uint32_t volatile *) (SIC_IMASK+ SIC_IMASK_PITCH); + r &= ~vectors[vector].mask1; + r |= mask; + r &= globalMask1; + *(uint32_t volatile *) (SIC_IMASK+ SIC_IMASK_PITCH) = r; +} + +/* add an ISR to the list for whichever vector it belongs to */ +rtems_status_code 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].mask0 & (1 << isr->source) ) || \ + (vectors[isr->vector].mask1 & (1 << (isr->source - SIC_ISR0_MAX)) )) + break; + if (isr->vector < CEC_INTERRUPT_COUNT) { + isr->next = NULL; + isr->mask0 = 0; + isr->mask1 = 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; + return RTEMS_SUCCESSFUL; +} + +rtems_status_code 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); + return RTEMS_SUCCESSFUL; +} + +void bfin_interrupt_enable(bfin_isr_t *isr, bool enable) { + rtems_interrupt_level isrLevel; + + rtems_interrupt_disable(isrLevel); + if ( SIC_ISR0_MAX > isr->source ) { + isr->mask0 = enable ? (1 << isr->source) : 0; + *(uint32_t volatile *) SIC_IMASK |= isr->mask0; + } else { + isr->mask1 = enable ? (1 << (isr->source - SIC_ISR0_MAX)) : 0; + *(uint32_t volatile *) (SIC_IMASK + SIC_IMASK_PITCH) |= isr->mask1; + } + + //setMask(isr->vector); + rtems_interrupt_enable(isrLevel); +} + +void bfin_interrupt_enable_all(int source, bool enable) { + rtems_interrupt_level isrLevel; + int vector; + bfin_isr_t *walk; + + for (vector = 0; vector < CEC_INTERRUPT_COUNT; vector++) + if ( (vectors[vector].mask0 & (1 << source) ) || \ + (vectors[vector].mask1 & (1 << (source - SIC_ISR0_MAX)) )) + break; + if (vector < CEC_INTERRUPT_COUNT) { + rtems_interrupt_disable(isrLevel); + walk = vectors[vector].head; + while (walk) { + walk->mask0 = enable ? (1 << source) : 0; + walk = walk->next; + } + + walk = vectors[vector].head; + while (walk) { + walk->mask1 = enable ? (1 << (source - SIC_ISR0_MAX)) : 0; + walk = walk->next; + } + setMask(vector); + rtems_interrupt_enable(isrLevel); + } +} + +void bfin_interrupt_enable_global(int source, bool enable) { + int vector; + rtems_interrupt_level isrLevel; + + for (vector = 0; vector < CEC_INTERRUPT_COUNT; vector++) + if ( (vectors[vector].mask0 & (1 << source) ) || \ + (vectors[vector].mask1 & (1 << (source - SIC_ISR0_MAX)) )) + break; + if (vector < CEC_INTERRUPT_COUNT) { + rtems_interrupt_disable(isrLevel); + if ( SIC_ISR0_MAX > source ) { + if (enable) + globalMask0 |= 1 << source; + else + globalMask0 &= ~(1 << source); + }else { + if (enable) + globalMask1 |= 1 << (source - SIC_ISR0_MAX); + else + globalMask1 &= ~(1 << (source - SIC_ISR0_MAX)); + } + setMask(vector); + rtems_interrupt_enable(isrLevel); + } +} + +#endif |