summaryrefslogblamecommitdiffstats
path: root/c/src/lib/libcpu/bfin/interrupt/interrupt.c
blob: 6887cca3bbe6dcefa33eec3899c43fbc0f2b84e3 (plain) (tree)
1
2
3
4
5
6
7
8
9
                                             
  




                                                                
                                         
   
 









































































































































                                                                              
                                                          







                                              
                                                         


















                                                          
                                                            
















                                                          
/*  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.org/license/LICENSE.
 */


#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, bool 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, bool 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, bool 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);
  }
}