summaryrefslogtreecommitdiff
path: root/c/src/lib/libcpu/bfin/bf52x/interrupt/interrupt.c
diff options
context:
space:
mode:
Diffstat (limited to 'c/src/lib/libcpu/bfin/bf52x/interrupt/interrupt.c')
-rw-r--r--c/src/lib/libcpu/bfin/bf52x/interrupt/interrupt.c643
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