summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libcpu/bfin
diff options
context:
space:
mode:
Diffstat (limited to 'c/src/lib/libcpu/bfin')
-rw-r--r--c/src/lib/libcpu/bfin/ChangeLog14
-rw-r--r--c/src/lib/libcpu/bfin/Makefile.am27
-rw-r--r--c/src/lib/libcpu/bfin/bf52x/include/bf52x.h133
-rw-r--r--c/src/lib/libcpu/bfin/bf52x/interrupt/interrupt.c643
-rw-r--r--c/src/lib/libcpu/bfin/bf52x/interrupt/interrupt.h146
-rw-r--r--c/src/lib/libcpu/bfin/configure.ac4
-rw-r--r--c/src/lib/libcpu/bfin/include/sicRegs.h8
-rw-r--r--c/src/lib/libcpu/bfin/preinstall.am23
-rw-r--r--c/src/lib/libcpu/bfin/serial/uart.c520
-rw-r--r--c/src/lib/libcpu/bfin/serial/uart.h113
10 files changed, 1467 insertions, 164 deletions
diff --git a/c/src/lib/libcpu/bfin/ChangeLog b/c/src/lib/libcpu/bfin/ChangeLog
index 305088920d..102622b88d 100644
--- a/c/src/lib/libcpu/bfin/ChangeLog
+++ b/c/src/lib/libcpu/bfin/ChangeLog
@@ -1,3 +1,17 @@
+2011-04-20 Rohan Kangralkar <rkangral@ece.neu.edu>
+
+ PR 1781/bsps
+ * bf52x/include: Added additional MMR.
+ * bf52x/interrupt: The BF52X processors have a different
+ System interrupt controller than present in the 53X range of
+ processors. The 52X have 8 interrupt assignment registers. The
+ implementation uses tables to increase predictability.
+ * serial/uart.?: Added DMA based and interrupt based transfer
+ support. The uart code used a single ISR for TX and RX and tried
+ to identify and multiplex inside the ISR. In the new code the
+ type of interrupt is identified by the central ISR dispatcher
+ bf52x/interrupt or interrupt/. This simplifies the UART ISR.
+
2011-02-11 Ralf Corsépius <ralf.corsepius@rtems.org>
* timer/timer.c:
diff --git a/c/src/lib/libcpu/bfin/Makefile.am b/c/src/lib/libcpu/bfin/Makefile.am
index 3eb65cce94..6e9fb3ec32 100644
--- a/c/src/lib/libcpu/bfin/Makefile.am
+++ b/c/src/lib/libcpu/bfin/Makefile.am
@@ -10,9 +10,31 @@ EXTRA_DIST =
noinst_PROGRAMS =
+include_bspdir = $(includedir)/bsp
include_libcpudir = $(includedir)/libcpu
+
+include_bsp_HEADERS =
include_libcpu_HEADERS =
+
+############
+# Start of bf52x files
+if bf52x
+
+include_HEADERS = bf52x/include/bf52x.h
+
+## INTERRUPT
+include_bsp_HEADERS += bf52x/interrupt/interrupt.h
+noinst_PROGRAMS += bf52x/interrupt.rel
+bf52x_interrupt_rel_SOURCES = bf52x/interrupt/interrupt.c \
+ bf52x/interrupt/interrupt.h
+bf52x_interrupt_rel_CPPFLAGS = $(AM_CPPFLAGS)
+bf52x_interrupt_rel_LDFLAGS = $(RTEMS_RELLDFLAGS)
+
+endif
+# endof bf52x
+############
+
include_libcpu_HEADERS += include/bf533.h
include_libcpu_HEADERS += include/bf537.h
include_libcpu_HEADERS += include/cecRegs.h
@@ -47,12 +69,17 @@ mmu_rel_SOURCES = mmu/mmu.c
mmu_rel_CPPFLAGS = $(AM_CPPFLAGS)
mmu_rel_LDFLAGS = $(RTEMS_RELLDFLAGS)
+if bf52x
+
+else
include_libcpu_HEADERS += interrupt/interrupt.h
noinst_PROGRAMS += interrupt.rel
interrupt_rel_SOURCES = interrupt/interrupt.c
interrupt_rel_CPPFLAGS = $(AM_CPPFLAGS)
interrupt_rel_LDFLAGS = $(RTEMS_RELLDFLAGS)
+endif
+
noinst_PROGRAMS += clock.rel
clock_rel_SOURCES = clock/clock.c
clock_rel_CPPFLAGS = $(AM_CPPFLAGS)
diff --git a/c/src/lib/libcpu/bfin/bf52x/include/bf52x.h b/c/src/lib/libcpu/bfin/bf52x/include/bf52x.h
new file mode 100644
index 0000000000..460fd388bd
--- /dev/null
+++ b/c/src/lib/libcpu/bfin/bf52x/include/bf52x.h
@@ -0,0 +1,133 @@
+/**
+ *@file bf52x.h
+ *
+ *@brief
+ * - This file provides the register address for the 52X model. The file is
+ * based on the 533 implementation with some addition to support 52X range of
+ * processors.
+ *
+ * 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$
+ *
+ */
+
+#ifndef _BF52X_H_
+#define _BF52X_H_
+
+/* register (or register block) addresses */
+
+#define SIC_BASE_ADDRESS 0xffc00100
+#define WDOG_BASE_ADDRESS 0xffc00200
+#define RTC_BASE_ADDRESS 0xffc00300
+#define UART0_BASE_ADDRESS 0xffc00400
+#define UART1_BASE_ADDRESS 0xffc02000
+#define SPI_BASE_ADDRESS 0xffc00500
+#define TIMER_BASE_ADDRESS 0xffc00600
+#define TIMER_CHANNELS 3
+#define TIMER_PITCH 0x10
+#define TIMER0_BASE_ADDRESS 0xffc00600
+#define TIMER1_BASE_ADDRESS 0xffc00610
+#define TIMER2_BASE_ADDRESS 0xffc00620
+#define TIMER_ENABLE 0xffc00640
+#define TIMER_DISABLE 0xffc00644
+#define TIMER_STATUS 0xffc00648
+#define PORTFIO_BASE_ADDRESS 0xffc00700
+#define SPORT0_BASE_ADDRESS 0xffc00800
+#define SPORT1_BASE_ADDRESS 0xffc00900
+#define EBIU_BASE_ADDRESS 0xffc00a00
+#define DMA_TC_PER 0xffc00b0c
+#define DMA_TC_CNT 0xffc00b10
+#define DMA_BASE_ADDRESS 0xffc00c00
+#define DMA_CHANNELS 8
+#define DMA_PITCH 0x40
+#define DMA0_BASE_ADDRESS 0xffc00c00
+#define DMA1_BASE_ADDRESS 0xffc00c40
+#define DMA2_BASE_ADDRESS 0xffc00c80
+#define DMA3_BASE_ADDRESS 0xffc00cc0
+#define DMA4_BASE_ADDRESS 0xffc00d00
+#define DMA5_BASE_ADDRESS 0xffc00d40
+#define DMA6_BASE_ADDRESS 0xffc00d80
+#define DMA7_BASE_ADDRESS 0xffc00dc0
+#define DMA8_BASE_ADDRESS 0xffc00e00
+#define DMA9_BASE_ADDRESS 0xffc00e40
+#define DMA10_BASE_ADDRESS 0xffc00e80
+#define DMA11_BASE_ADDRESS 0xffc00ec0
+#define MDMA_BASE_ADDRESS 0xffc00e00
+#define MDMA_CHANNELS 2
+#define MDMA_D_S 0x40
+#define MDMA_PITCH 0x80
+#define MDMA0D_BASE_ADDRESS 0xffc00e00
+#define MDMA0S_BASE_ADDRESS 0xffc00e40
+#define MDMA1D_BASE_ADDRESS 0xffc00e80
+#define MDMA1S_BASE_ADDRESS 0xffc00ec0
+#define PPI_BASE_ADDRESS 0xffc01000
+
+
+/* register fields */
+
+#define DMA_TC_PER_MDMA_ROUND_ROBIN_PERIOD_MASK 0xf800
+#define DMA_TC_PER_MDMA_ROUND_ROBIN_PERIOD_SHIFT 11
+#define DMA_TC_PER_DAB_TRAFFIC_PERIOD_MASK 0x0700
+#define DMA_TC_PER_DAB_TRAFFIC_PERIOD_SHIFT 8
+#define DMA_TC_PER_DEB_TRAFFIC_PERIOD_MASK 0x00f0
+#define DMA_TC_PER_DEB_TRAFFIC_PERIOD_SHIFT 4
+#define DMA_TC_PER_DCB_TRAFFIC_PERIOD_MASK 0x000f
+#define DMA_TC_PER_DCB_TRAFFIC_PERIOD_SHIFT 0
+
+#define DMA_TC_CNT_MDMA_ROUND_ROBIN_COUNT_MASK 0xf800
+#define DMA_TC_CNT_MDMA_ROUND_ROBIN_COUNT_SHIFT 11
+#define DMA_TC_CNT_DAB_TRAFFIC_COUNT_MASK 0x0700
+#define DMA_TC_CNT_DAB_TRAFFIC_COUNT_SHIFT 8
+#define DMA_TC_CNT_DEB_TRAFFIC_COUNT_MASK 0x00f0
+#define DMA_TC_CNT_DEB_TRAFFIC_COUNT_SHIFT 4
+#define DMA_TC_CNT_DCB_TRAFFIC_COUNT_MASK 0x000f
+#define DMA_TC_CNT_DCB_TRAFFIC_COUNT_SHIFT 0
+
+#define TIMER_ENABLE_TIMEN2 0x0004
+#define TIMER_ENABLE_TIMEN1 0x0002
+#define TIMER_ENABLE_TIMEN0 0x0001
+
+#define TIMER_DISABLE_TIMDIS2 0x0004
+#define TIMER_DISABLE_TIMDIS1 0x0002
+#define TIMER_DISABLE_TIMDIS0 0x0001
+
+#define TIMER_STATUS_TRUN2 0x00004000
+#define TIMER_STATUS_TRUN1 0x00002000
+#define TIMER_STATUS_TRUN0 0x00001000
+#define TIMER_STATUS_TOVF_ERR2 0x00000040
+#define TIMER_STATUS_TOVF_ERR1 0x00000020
+#define TIMER_STATUS_TOVF_ERR0 0x00000010
+#define TIMER_STATUS_TIMIL2 0x00000004
+#define TIMER_STATUS_TIMIL1 0x00000002
+#define TIMER_STATUS_TIMIL0 0x00000001
+
+/* Core Event Controller vectors */
+
+#define CEC_EMULATION_VECTOR 0
+#define CEC_RESET_VECTOR 1
+#define CEC_NMI_VECTOR 2
+#define CEC_EXCEPTIONS_VECTOR 3
+#define CEC_HARDWARE_ERROR_VECTOR 5
+#define CEC_CORE_TIMER_VECTOR 6
+#define CEC_INTERRUPT_BASE_VECTOR 7
+#define CEC_INTERRUPT_COUNT 9
+
+
+/* System Interrupt Controller vectors */
+
+#define SIC_IAR_COUNT 8
+
+#endif /* _BF52X_H_ */
+
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
diff --git a/c/src/lib/libcpu/bfin/bf52x/interrupt/interrupt.h b/c/src/lib/libcpu/bfin/bf52x/interrupt/interrupt.h
new file mode 100644
index 0000000000..2e9c269318
--- /dev/null
+++ b/c/src/lib/libcpu/bfin/bf52x/interrupt/interrupt.h
@@ -0,0 +1,146 @@
+/**
+ *@file interrupt.h
+ *
+ *@brief
+ * - This file implements interrupt dispatcher. The init 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$
+ *
+ */
+
+#ifndef _BFIN_INTERRUPT_H_
+#define _BFIN_INTERRUPT_H_
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** The type of interrupts handled by the SIC
+ */
+typedef enum {
+ IRQ_PLL_WAKEUP_INTERRUPT, /* 0 */
+ IRQ_DMA_ERROR_0, /* 1 */
+ IRQ_DMAR0_BLOCK_INTERRUPT, /* 2 */
+ IRQ_DMAR1_BLOCK_INTERRUPT, /* 3 */
+ IRQ_DMAR0_OVERFLOW_ERROR, /* 4 */
+ IRQ_DMAR1_OVERFLOW_ERROR, /* 5 */
+ IRQ_PPI_STATUS, /* 6 */
+ IRQ_MAC_STATUS, /* 7 */
+ IRQ_SPORT0_STATUS, /* 8 */
+ IRQ_SPORT1_STATUS, /* 9 */
+ IRQ_RESERVED_10, /* 10 */
+ IRQ_RESERVED_11, /* 11 */
+ IRQ_UART0_STATUS, /* 12 */
+ IRQ_UART1_STATUS, /* 13 */
+ IRQ_REAL_TIME_CLOCK, /* 14 */
+ IRQ_DMA0_PPI_NFC, /* 15 */
+ IRQ_DMA3_SPORT0_RX, /* 16 */
+ IRQ_DMA4_SPORT0_TX, /* 17 */
+ IRQ_DMA5_SPORT1_RX, /* 18 */
+ IRQ_DMA6_SPORT1_TX, /* 19 */
+ IRQ_TWI_INTERRUPT, /* 20 */
+ IRQ_DMA7_SPI, /* 21 */
+ IRQ_DMA8_UART0_RX, /* 22 */
+ IRQ_DMA9_UART0_TX, /* 23 */
+ IRQ_DMA10_UART1_RX, /* 24 */
+ IRQ_DMA11_UART1_TX, /* 25 */
+ IRQ_OTP, /* 26 */
+ IRQ_GP_COUNTER, /* 27 */
+ IRQ_DMA1_MAC_RX_HOSTDP, /* 28 */
+ IRQ_PORT_H_INTERRUPT_A, /* 29 */
+ IRQ_DMA2_MAC_TX_NFC, /* 30 */
+ IRQ_PORT_H_INTERRUPT_B, /* 31 */
+ SIC_ISR0_MAX, /* 32 ***/
+ IRQ_TIMER0 = SIC_ISR0_MAX, /* 32 */
+ IRQ_TIMER1, /* 33 */
+ IRQ_TIMER2, /* 34 */
+ IRQ_TIMER3, /* 35 */
+ IRQ_TIMER4, /* 36 */
+ IRQ_TIMER5, /* 37 */
+ IRQ_TIMER6, /* 38 */
+ IRQ_TIMER7, /* 39 */
+ IRQ_PORT_G_INTERRUPT_A, /* 40 */
+ IRQ_PORT_G_INTERRUPT_B, /* 41 */
+ IRQ_MDMA0_STREAM_0_INTERRUPT, /* 42 */
+ IRQ_MDMA1_STREAM_0_INTERRUPT, /* 43 */
+ IRQ_SOFTWARE_WATCHDOG_INTERRUPT, /* 44 */
+ IRQ_PORT_F_INTERRUPT_A, /* 45 */
+ IRQ_PORT_F_INTERRUPT_B, /* 46 */
+ IRQ_SPI_STATUS, /* 47 */
+ IRQ_NFC_STATUS, /* 48 */
+ IRQ_HOSTDP_STATUS, /* 49 */
+ IRQ_HOREAD_DONE_INTERRUPT, /* 50 */
+ IRQ_RESERVED_19, /* 51 */
+ IRQ_USB_INT0_INTERRUPT, /* 52 */
+ IRQ_USB_INT1_INTERRUPT, /* 53 */
+ IRQ_USB_INT2_INTERRUPT, /* 54 */
+ IRQ_USB_DMAINT, /* 55 */
+ IRQ_MAX, /* 56 */
+} e_isr_t;
+
+
+
+
+/* source is the source to the SIC (the bit number in SIC_ISR). isr is
+ the function that will be called when the interrupt is active. */
+typedef struct bfin_isr_s {
+#if INTERRUPT_USE_TABLE
+ e_isr_t source;
+ void (*pFunc)(void *arg);
+ void *pArg;
+ int priority; /** not used */
+#else
+ int source;
+ void (*isr)(void *arg);
+ void *_arg;
+ /* the following are for internal use only */
+ uint32_t mask0;
+ uint32_t mask1;
+ uint32_t vector;
+ struct bfin_isr_s *next;
+#endif
+} bfin_isr_t;
+
+/**
+ * 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);
+
+/**
+ * This function unregisters a registered interrupt handler.
+ * @param isr
+ */
+rtems_status_code bfin_interrupt_unregister(bfin_isr_t *isr);
+
+/**
+ * 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);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BFIN_INTERRUPT_H_ */
+
diff --git a/c/src/lib/libcpu/bfin/configure.ac b/c/src/lib/libcpu/bfin/configure.ac
index 0a5e88214d..0fa523fcd1 100644
--- a/c/src/lib/libcpu/bfin/configure.ac
+++ b/c/src/lib/libcpu/bfin/configure.ac
@@ -24,6 +24,10 @@ RTEMS_PROG_CCAS
RTEMS_CHECK_NETWORKING
AM_CONDITIONAL(HAS_NETWORKING,test "$HAS_NETWORKING" = "yes")
+# AM_CONDITIONAL(shared, test "$RTEMS_CPU_MODEL" = "bf52x")
+AM_CONDITIONAL(bf52x, test "$RTEMS_CPU_MODEL" = "bf52x")
+
+
RTEMS_AMPOLISH3
# Explicitly list all Makefiles here
diff --git a/c/src/lib/libcpu/bfin/include/sicRegs.h b/c/src/lib/libcpu/bfin/include/sicRegs.h
index 185bd5f09a..0c52622318 100644
--- a/c/src/lib/libcpu/bfin/include/sicRegs.h
+++ b/c/src/lib/libcpu/bfin/include/sicRegs.h
@@ -16,8 +16,14 @@
/* register addresses */
#define SIC_IMASK (SIC_BASE_ADDRESS + 0x000c)
+#define SIC_IMASK_PITCH (0x40)
+
+#define SIC_ISR (SIC_BASE_ADDRESS + 0x0020)
+#define SIC_ISR_PITCH (0x40)
+
#define SIC_IAR_BASE_ADDRESS (SIC_BASE_ADDRESS + 0x0010)
#define SIC_IAR_PITCH 0x04
+
#define SIC_IAR0 (SIC_BASE_ADDRESS + 0x0010)
#if SIC_IAR_COUNT > 1
#define SIC_IAR1 (SIC_BASE_ADDRESS + 0x0014)
@@ -28,7 +34,7 @@
#if SIC_IAR_COUNT > 3
#define SIC_IAR3 (SIC_BASE_ADDRESS + 0x001c)
#endif
-#define SIC_ISR (SIC_BASE_ADDRESS + 0x0020)
+
#define SIC_IWR (SIC_BASE_ADDRESS + 0x0024)
diff --git a/c/src/lib/libcpu/bfin/preinstall.am b/c/src/lib/libcpu/bfin/preinstall.am
index b14978e4ff..a8b2fa979b 100644
--- a/c/src/lib/libcpu/bfin/preinstall.am
+++ b/c/src/lib/libcpu/bfin/preinstall.am
@@ -13,11 +13,30 @@ all-am: $(PREINSTALL_FILES)
PREINSTALL_FILES =
CLEANFILES = $(PREINSTALL_FILES)
+$(PROJECT_INCLUDE)/$(dirstamp):
+ @$(MKDIR_P) $(PROJECT_INCLUDE)
+ @: > $(PROJECT_INCLUDE)/$(dirstamp)
+PREINSTALL_DIRS += $(PROJECT_INCLUDE)/$(dirstamp)
+
+$(PROJECT_INCLUDE)/bsp/$(dirstamp):
+ @$(MKDIR_P) $(PROJECT_INCLUDE)/bsp
+ @: > $(PROJECT_INCLUDE)/bsp/$(dirstamp)
+PREINSTALL_DIRS += $(PROJECT_INCLUDE)/bsp/$(dirstamp)
+
$(PROJECT_INCLUDE)/libcpu/$(dirstamp):
@$(MKDIR_P) $(PROJECT_INCLUDE)/libcpu
@: > $(PROJECT_INCLUDE)/libcpu/$(dirstamp)
PREINSTALL_DIRS += $(PROJECT_INCLUDE)/libcpu/$(dirstamp)
+if bf52x
+$(PROJECT_INCLUDE)/bf52x.h: bf52x/include/bf52x.h $(PROJECT_INCLUDE)/$(dirstamp)
+ $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bf52x.h
+PREINSTALL_FILES += $(PROJECT_INCLUDE)/bf52x.h
+
+$(PROJECT_INCLUDE)/bsp/interrupt.h: bf52x/interrupt/interrupt.h $(PROJECT_INCLUDE)/bsp/$(dirstamp)
+ $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/interrupt.h
+PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/interrupt.h
+endif
$(PROJECT_INCLUDE)/libcpu/bf533.h: include/bf533.h $(PROJECT_INCLUDE)/libcpu/$(dirstamp)
$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/libcpu/bf533.h
PREINSTALL_FILES += $(PROJECT_INCLUDE)/libcpu/bf533.h
@@ -102,10 +121,12 @@ $(PROJECT_INCLUDE)/libcpu/mmu.h: mmu/mmu.h $(PROJECT_INCLUDE)/libcpu/$(dirstamp)
$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/libcpu/mmu.h
PREINSTALL_FILES += $(PROJECT_INCLUDE)/libcpu/mmu.h
+if bf52x
+else
$(PROJECT_INCLUDE)/libcpu/interrupt.h: interrupt/interrupt.h $(PROJECT_INCLUDE)/libcpu/$(dirstamp)
$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/libcpu/interrupt.h
PREINSTALL_FILES += $(PROJECT_INCLUDE)/libcpu/interrupt.h
-
+endif
$(PROJECT_INCLUDE)/libcpu/uart.h: serial/uart.h $(PROJECT_INCLUDE)/libcpu/$(dirstamp)
$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/libcpu/uart.h
PREINSTALL_FILES += $(PROJECT_INCLUDE)/libcpu/uart.h
diff --git a/c/src/lib/libcpu/bfin/serial/uart.c b/c/src/lib/libcpu/bfin/serial/uart.c
index 0f9ab0f673..2135f7531f 100644
--- a/c/src/lib/libcpu/bfin/serial/uart.c
+++ b/c/src/lib/libcpu/bfin/serial/uart.c
@@ -7,6 +7,9 @@
* found in the file LICENSE in this distribution or at
* http://www.rtems.com/license/LICENSE.
*
+ * Modified:
+ * $ $Author$ Added interrupt support and DMA support
+ *
* $Id$
*/
@@ -18,9 +21,9 @@
#include <stdlib.h>
#include <libcpu/uartRegs.h>
+#include <libcpu/dmaRegs.h>
#include "uart.h"
-
/* flags */
#define BFIN_UART_XMIT_BUSY 0x01
@@ -28,45 +31,16 @@
static bfin_uart_config_t *uartsConfig;
-static void initializeHardware(int minor) {
- uint16_t divisor;
- char *base;
- uint16_t r;
-
- base = uartsConfig->channels[minor].base_address;
-
- *(uint16_t volatile *) (base + UART_IER_OFFSET) = 0;
-
- if (uartsConfig->channels[minor].force_baud)
- divisor = (uint16_t) (uartsConfig->freq /
- (uartsConfig->channels[minor].force_baud * 16));
- else
- divisor = (uint16_t) (uartsConfig->freq / (9600 * 16));
- *(uint16_t volatile *) (base + UART_LCR_OFFSET) = UART_LCR_DLAB;
- *(uint16_t volatile *) (base + UART_DLL_OFFSET) = (divisor & 0xff);
- *(uint16_t volatile *) (base + UART_DLH_OFFSET) = ((divisor >> 8) & 0xff);
-
- *(uint16_t volatile *) (base + UART_LCR_OFFSET) = UART_LCR_WLS_8;
-
- *(uint16_t volatile *) (base + UART_GCTL_OFFSET) = UART_GCTL_UCEN;
-
- r = *(uint16_t volatile *) (base + UART_LSR_OFFSET);
- r = *(uint16_t volatile *) (base + UART_RBR_OFFSET);
- r = *(uint16_t volatile *) (base + UART_IIR_OFFSET);
-
- return;
-}
-
static int pollRead(int minor) {
int c;
- char *base;
+ uint32_t base;
- base = uartsConfig->channels[minor].base_address;
+ base = uartsConfig->channels[minor].uart_baseAddress;
/* check to see if driver is using interrupts so this call will be
harmless (though non-functional) in case some debug code tries to
use it */
- if (!uartsConfig->channels[minor].use_interrupts &&
+ if (!uartsConfig->channels[minor].uart_useInterrupts &&
*((uint16_t volatile *) (base + UART_LSR_OFFSET)) & UART_LSR_DR)
c = *((uint16_t volatile *) (base + UART_RBR_OFFSET));
else
@@ -75,7 +49,7 @@ static int pollRead(int minor) {
return c;
}
-char bfin_uart_poll_read(int minor) {
+char bfin_uart_poll_read(rtems_device_minor_number minor) {
int c;
do {
@@ -86,9 +60,9 @@ char bfin_uart_poll_read(int minor) {
}
void bfin_uart_poll_write(int minor, char c) {
- char *base;
+ uint32_t base;
- base = uartsConfig->channels[minor].base_address;
+ base = uartsConfig->channels[minor].uart_baseAddress;
while (!(*((uint16_t volatile *) (base + UART_LSR_OFFSET)) & UART_LSR_THRE))
;
@@ -157,44 +131,86 @@ static ssize_t pollWrite(int minor, const char *buf, size_t len) {
return count;
}
-static void enableInterrupts(int minor) {
- char *base;
- base = uartsConfig->channels[minor].base_address;
+/**
+ * Routine to initialize the hardware. It initialize the DMA,
+ * interrupt if required.
+ * @param channel channel information
+ */
+static void initializeHardware(bfin_uart_channel_t *channel) {
+ uint16_t divisor = 0;
+ uint32_t base = 0;
+ uint32_t tx_dma_base = 0;
- *(uint16_t volatile *) (base + UART_IER_OFFSET) = UART_IER_ETBEI |
- UART_IER_ERBFI;
-}
+ if ( NULL == channel ) {
+ return;
+ }
+
+ base = channel->uart_baseAddress;
+ tx_dma_base = channel->uart_txDmaBaseAddress;
+ /**
+ * RX based DMA and interrupt is not supported yet
+ * uint32_t tx_dma_base = 0;
+ *
+ * rx_dma_base = channel->uart_rxDmaBaseAddress;
+ */
-static void disableAllInterrupts(void) {
- int i;
- char *base;
- for (i = 0; i < uartsConfig->num_channels; i++) {
- base = uartsConfig->channels[i].base_address;
- *(uint16_t volatile *) (base + UART_IER_OFFSET) = 0;
+ *(uint16_t volatile *) (base + UART_IER_OFFSET) = 0;
+
+ if ( 0 != channel->uart_baud) {
+ divisor = (uint16_t) (uartsConfig->freq /
+ (channel->uart_baud * 16));
+ } else {
+ divisor = (uint16_t) (uartsConfig->freq / (9600 * 16));
}
-}
-static ssize_t interruptWrite(int minor, const char *buf, size_t len) {
- char *base;
+ *(uint16_t volatile *) (base + UART_LCR_OFFSET) = UART_LCR_DLAB;
+ *(uint16_t volatile *) (base + UART_DLL_OFFSET) = (divisor & 0xff);
+ *(uint16_t volatile *) (base + UART_DLH_OFFSET) = ((divisor >> 8) & 0xff);
- base = uartsConfig->channels[minor].base_address;
+ *(uint16_t volatile *) (base + UART_LCR_OFFSET) = UART_LCR_WLS_8;
- uartsConfig->channels[minor].flags |= BFIN_UART_XMIT_BUSY;
- *(uint16_t volatile *) (base + UART_THR_OFFSET) = *buf;
+ *(uint16_t volatile *) (base + UART_GCTL_OFFSET) = UART_GCTL_UCEN;
- /* one byte written */
- return 1;
+ /**
+ * To clear previous status
+ * divisor is a temp variable here
+ */
+ divisor = *(uint16_t volatile *) (base + UART_LSR_OFFSET);
+ divisor = *(uint16_t volatile *) (base + UART_RBR_OFFSET);
+ divisor = *(uint16_t volatile *) (base + UART_IIR_OFFSET);
+
+ if ( channel->uart_useDma ) {
+ *(uint16_t volatile *)(tx_dma_base + DMA_CONFIG_OFFSET) = 0;
+ *(uint16_t volatile *)(tx_dma_base + DMA_CONFIG_OFFSET) = DMA_CONFIG_DI_EN
+ | DMA_CONFIG_SYNC ;
+ *(uint16_t volatile *)(tx_dma_base + DMA_IRQ_STATUS_OFFSET) |=
+ DMA_IRQ_STATUS_DMA_DONE | DMA_IRQ_STATUS_DMA_ERR;
+
+ } else {
+ /**
+ * We use polling or interrupts only sending one char at a time :(
+ */
+ }
+
+ return;
}
+
+/**
+ * Set the UART attributes.
+ * @param minor
+ * @param termios
+ * @return
+ */
static int setAttributes(int minor, const struct termios *termios) {
- char *base;
+ uint32_t base;
int baud;
uint16_t divisor;
uint16_t lcr;
- base = uartsConfig->channels[minor].base_address;
+ base = uartsConfig->channels[minor].uart_baseAddress;
switch (termios->c_cflag & CBAUD) {
case B0:
baud = 0;
@@ -260,8 +276,8 @@ static int setAttributes(int minor, const struct termios *termios) {
baud = -1;
break;
}
- if (baud > 0 && uartsConfig->channels[minor].force_baud)
- baud = uartsConfig->channels[minor].force_baud;
+ if (baud > 0 && uartsConfig->channels[minor].uart_baud)
+ baud = uartsConfig->channels[minor].uart_baud;
switch (termios->c_cflag & CSIZE) {
case CS5:
lcr = UART_LCR_WLS_5;
@@ -282,8 +298,8 @@ static int setAttributes(int minor, const struct termios *termios) {
lcr |= UART_LCR_PEN | UART_LCR_EPS;
break;
case PARENB | PARODD:
- lcr |= UART_LCR_PEN;
- break;
+ lcr |= UART_LCR_PEN;
+ break;
default:
break;
}
@@ -301,114 +317,326 @@ static int setAttributes(int minor, const struct termios *termios) {
return 0;
}
-void bfin_uart_isr(int source) {
- int i;
- char *base;
- uint16_t uartStat;
- char c;
- uint8_t uartLSR;
-
- /* Just use one ISR and check for all UART interrupt sources in it.
- This is less efficient than making use of the vector to narrow down
- the things we need to check, but not all Blackfins separate the
- UART interrupt sources in the same ways. This way we don't have
- to make this code dependent on the type of Blackfin. */
- for (i = 0; i < uartsConfig->num_channels; i++) {
- if (uartsConfig->channels[i].use_interrupts) {
- base = uartsConfig->channels[i].base_address;
- uartStat = *(uint16_t volatile *) (base + UART_IIR_OFFSET);
- if ((uartStat & UART_IIR_NINT) == 0) {
- switch (uartStat & UART_IIR_STATUS_MASK) {
- case UART_IIR_STATUS_THRE:
- if (uartsConfig->channels[i].termios &&
- (uartsConfig->channels[i].flags & BFIN_UART_XMIT_BUSY)) {
- uartsConfig->channels[i].flags &= ~BFIN_UART_XMIT_BUSY;
- rtems_termios_dequeue_characters(uartsConfig->channels[i].termios,
- 1);
- }
- break;
- case UART_IIR_STATUS_RDR:
- c = *(uint16_t volatile *) (base + UART_RBR_OFFSET);
- if (uartsConfig->channels[i].termios)
- rtems_termios_enqueue_raw_characters(
- uartsConfig->channels[i].termios, &c, 1);
- break;
- case UART_IIR_STATUS_LS:
- uartLSR = *(uint16_t volatile *) (base + UART_LSR_OFFSET);
- /* break, framing error, parity error, or overrun error
- has been detected */
- break;
- default:
- break;
- }
- }
- }
+/**
+ * Interrupt based uart tx routine. The routine writes one character at a time.
+ *
+ * @param minor Minor number to indicate uart number
+ * @param buf Character buffer which stores characters to be transmitted.
+ * @param len Length of buffer to be transmitted.
+ * @return
+ */
+static ssize_t uart_interruptWrite(int minor, const char *buf, size_t len) {
+ uint32_t base = 0;
+ bfin_uart_channel_t* channel = NULL;
+ rtems_interrupt_level isrLevel;
+
+ /**
+ * Sanity Check
+ */
+ if (NULL == buf || NULL == channel || NULL == uartsConfig || minor < 0) {
+ return 0;
}
+
+ channel = &(uartsConfig->channels[minor]);
+
+ if ( NULL == channel || channel->flags & BFIN_UART_XMIT_BUSY ) {
+ return 0;
+ }
+
+ rtems_interrupt_disable(isrLevel);
+
+ base = channel->uart_baseAddress;
+
+ channel->flags |= BFIN_UART_XMIT_BUSY;
+ channel->length = 1;
+ *(uint16_t volatile *) (base + UART_THR_OFFSET) = *buf;
+ *(uint16_t volatile *) (base + UART_IER_OFFSET) = UART_IER_ETBEI;
+
+ rtems_interrupt_enable(isrLevel);
+
+ return 0;
}
-rtems_status_code bfin_uart_initialize(rtems_device_major_number major,
- bfin_uart_config_t *config) {
- rtems_status_code status;
- int i;
+/**
+* This function implements RX ISR
+*/
+void bfinUart_rxIsr(void *_arg)
+{
+ /**
+ * TODO: UART RX ISR implementation.
+ */
- status = RTEMS_SUCCESSFUL;
+}
- rtems_termios_initialize();
- /*
- * Register Device Names
+/**
+ * This function implements TX ISR. The function gets called when the TX FIFO is
+ * empty. It clears the interrupt and dequeues the character. It only tx one
+ * character at a time.
+ *
+ * TODO: error handling.
+ * @param _arg gets the channel information.
+ */
+void bfinUart_txIsr(void *_arg) {
+ bfin_uart_channel_t* channel = NULL;
+ uint32_t base = 0;
+
+ /**
+ * Sanity check
*/
+ if (NULL == _arg) {
+ /** It should never be NULL */
+ return;
+ }
- uartsConfig = config;
- for (i = 0; i < config->num_channels; i++) {
- config->channels[i].termios = NULL;
- config->channels[i].flags = 0;
- initializeHardware(i);
- status = rtems_io_register_name(config->channels[i].name, major, i);
+ channel = (bfin_uart_channel_t *) _arg;
+
+ base = channel->uart_baseAddress;
+
+ *(uint16_t volatile *) (base + UART_IER_OFFSET) &= ~UART_IER_ETBEI;
+ channel->flags &= ~BFIN_UART_XMIT_BUSY;
+
+ rtems_termios_dequeue_characters(channel->termios, channel->length);
+
+ return;
+}
+
+
+
+
+/**
+ * interrupt based DMA write Routine. It configure the DMA to write len bytes.
+ * The DMA supports 64K data only.
+ *
+ * @param minor Identification number of the UART.
+ * @param buf Character buffer pointer
+ * @param len length of data items to be written
+ * @return data already written
+ */
+static ssize_t uart_DmaWrite(int minor, const char *buf, size_t len) {
+ uint32_t base = 0;
+ bfin_uart_channel_t* channel = NULL;
+ uint32_t tx_dma_base = 0;
+ rtems_interrupt_level isrLevel;
+
+ /**
+ * Sanity Check
+ */
+ if ( NULL == buf || 0 > minor || NULL == uartsConfig ) {
+ return 0;
+ }
+
+ channel = &(uartsConfig->channels[minor]);
+
+ /**
+ * Sanity Check and check for transmit busy.
+ */
+ if ( NULL == channel || BFIN_UART_XMIT_BUSY & channel->flags ) {
+ return 0;
+ }
+
+ rtems_interrupt_disable(isrLevel);
+
+ base = channel->uart_baseAddress;
+ tx_dma_base = channel->uart_txDmaBaseAddress;
+
+ channel->flags |= BFIN_UART_XMIT_BUSY;
+ channel->length = len;
+
+ *(uint16_t volatile *) (tx_dma_base + DMA_CONFIG_OFFSET) &= ~DMA_CONFIG_DMAEN;
+ *(uint32_t volatile *) (tx_dma_base + DMA_START_ADDR_OFFSET) = (uint32_t)buf;
+ *(uint16_t volatile *) (tx_dma_base + DMA_X_COUNT_OFFSET) = channel->length;
+ *(uint16_t volatile *) (tx_dma_base + DMA_X_MODIFY_OFFSET) = 1;
+ *(uint16_t volatile *) (tx_dma_base + DMA_CONFIG_OFFSET) |= DMA_CONFIG_DMAEN;
+ *(uint16_t volatile *) (base + UART_IER_OFFSET) = UART_IER_ETBEI;
+
+ rtems_interrupt_enable(isrLevel);
+
+ return 0;
+}
+
+
+/**
+ * RX DMA ISR.
+ * The polling route is used for receiving the characters. This is a place
+ * holder for future implementation.
+ * @param _arg
+ */
+void bfinUart_rxDmaIsr(void *_arg) {
+/**
+ * TODO: Implementation of RX DMA
+ */
+}
+
+/**
+ * This function implements TX dma ISR. It clears the IRQ and dequeues a char
+ * The channel argument will have the base address. Since there are two uart
+ * and both the uarts can use the same tx dma isr.
+ *
+ * TODO: 1. Error checking 2. sending correct length ie after looking at the
+ * number of elements the uart transmitted.
+ *
+ * @param _arg argument passed to the interrupt handler. It contains the
+ * channel argument.
+ */
+void bfinUart_txDmaIsr(void *_arg) {
+ bfin_uart_channel_t* channel = NULL;
+ uint32_t tx_dma_base = 0;
+
+ /**
+ * Sanity check
+ */
+ if (NULL == _arg) {
+ /** It should never be NULL */
+ return;
+ }
+
+ channel = (bfin_uart_channel_t *) _arg;
+
+ tx_dma_base = channel->uart_txDmaBaseAddress;
+
+ if ((*(uint16_t volatile *) (tx_dma_base + DMA_IRQ_STATUS_OFFSET)
+ & DMA_IRQ_STATUS_DMA_DONE)) {
+
+ *(uint16_t volatile *) (tx_dma_base + DMA_IRQ_STATUS_OFFSET)
+ |= DMA_IRQ_STATUS_DMA_DONE | DMA_IRQ_STATUS_DMA_ERR;
+ channel->flags &= ~BFIN_UART_XMIT_BUSY;
+ rtems_termios_dequeue_characters(channel->termios, channel->length);
+ } else {
+ /* UART DMA did not generate interrupt.
+ * This routine must not be called.
+ */
}
- return RTEMS_SUCCESSFUL;
+ return;
+}
+
+/**
+ * Function called during exit
+ */
+void uart_exit(void)
+{
+ /**
+ * TODO: Flushing of quques
+ */
+
}
+/**
+ * Opens the device in different modes. The supported modes are
+ * 1. Polling
+ * 2. Interrupt
+ * 3. DMA
+ * At exit the uart_Exit function will be called to flush the device.
+ *
+ * @param major Major number of the device
+ * @param minor Minor number of the device
+ * @param arg
+ * @return
+ */
rtems_device_driver bfin_uart_open(rtems_device_major_number major,
- rtems_device_minor_number minor,
- void *arg) {
- rtems_status_code sc;
- rtems_libio_open_close_args_t *args;
+ rtems_device_minor_number minor, void *arg) {
+ rtems_status_code sc = RTEMS_NOT_DEFINED;;
+ rtems_libio_open_close_args_t *args = NULL;
+
+ /**
+ * Callback function for polling
+ */
static const rtems_termios_callbacks pollCallbacks = {
- NULL, /* firstOpen */
- NULL, /* lastClose */
- pollRead, /* pollRead */
- pollWrite, /* write */
- setAttributes, /* setAttributes */
- NULL, /* stopRemoteTx */
- NULL, /* startRemoteTx */
- TERMIOS_POLLED /* outputUsesInterrupts */
+ NULL, /* firstOpen */
+ NULL, /* lastClose */
+ pollRead, /* pollRead */
+ pollWrite, /* write */
+ setAttributes, /* setAttributes */
+ NULL, /* stopRemoteTx */
+ NULL, /* startRemoteTx */
+ TERMIOS_POLLED /* outputUsesInterrupts */
};
+
+ /**
+ * Callback function for interrupt based transfers without DMA.
+ * We use interrupts for writing only. For reading we use polling.
+ */
static const rtems_termios_callbacks interruptCallbacks = {
- NULL, /* firstOpen */
- NULL, /* lastClose */
- NULL, /* pollRead */
- interruptWrite, /* write */
- setAttributes, /* setAttributes */
- NULL, /* stopRemoteTx */
- NULL, /* startRemoteTx */
- TERMIOS_IRQ_DRIVEN /* outputUsesInterrupts */
+ NULL, /* firstOpen */
+ NULL, /* lastClose */
+ pollRead, /* pollRead */
+ uart_interruptWrite, /* write */
+ setAttributes, /* setAttributes */
+ NULL, /* stopRemoteTx */
+ NULL, /* startRemoteTx */
+ TERMIOS_IRQ_DRIVEN /* outputUsesInterrupts */
};
- if (uartsConfig == NULL || minor < 0 || minor >= uartsConfig->num_channels)
+ /**
+ * Callback function for interrupt based DMA transfers.
+ * We use interrupts for writing only. For reading we use polling.
+ */
+ static const rtems_termios_callbacks interruptDmaCallbacks = {
+ NULL, /* firstOpen */
+ NULL, /* lastClose */
+ NULL, /* pollRead */
+ uart_DmaWrite, /* write */
+ setAttributes, /* setAttributes */
+ NULL, /* stopRemoteTx */
+ NULL, /* startRemoteTx */
+ TERMIOS_IRQ_DRIVEN /* outputUsesInterrupts */
+ };
+
+
+ if ( NULL == uartsConfig || 0 > minor || minor >= uartsConfig->num_channels) {
return RTEMS_INVALID_NUMBER;
+ }
+
+ /**
+ * Opens device for handling uart send request either by
+ * 1. interrupt with DMA
+ * 2. interrupt based
+ * 3. Polling
+ */
+ if ( uartsConfig->channels[minor].uart_useDma ) {
+ sc = rtems_termios_open(major, minor, arg, &interruptDmaCallbacks);
+ } else {
+ sc = rtems_termios_open(major, minor, arg,
+ uartsConfig->channels[minor].uart_useInterrupts ?
+ &interruptCallbacks : &pollCallbacks);
+ }
- sc = rtems_termios_open(major, minor, arg,
- uartsConfig->channels[minor].use_interrupts ?
- &interruptCallbacks : &pollCallbacks);
args = arg;
uartsConfig->channels[minor].termios = args->iop->data1;
- if (uartsConfig->channels[minor].use_interrupts)
- enableInterrupts(minor);
- atexit(disableAllInterrupts);
+ atexit(uart_exit);
return sc;
}
+
+/**
+* Uart initialization function.
+* @param major major number of the device
+* @param config configuration parameters
+* @return rtems status code
+*/
+rtems_status_code bfin_uart_initialize(rtems_device_major_number major,
+ bfin_uart_config_t *config) {
+ rtems_status_code sc = RTEMS_NOT_DEFINED;
+ int i = 0;
+
+ rtems_termios_initialize();
+
+ /*
+ * Register Device Names
+ */
+ uartsConfig = config;
+ for (i = 0; i < config->num_channels; i++) {
+ config->channels[i].termios = NULL;
+ config->channels[i].flags = 0;
+ initializeHardware(&(config->channels[i]));
+ sc = rtems_io_register_name(config->channels[i].name, major, i);
+ if (RTEMS_SUCCESSFUL != sc) {
+ return sc;
+ }
+ }
+
+ return sc;
+}
diff --git a/c/src/lib/libcpu/bfin/serial/uart.h b/c/src/lib/libcpu/bfin/serial/uart.h
index 87e493bf6d..fbedaf6537 100644
--- a/c/src/lib/libcpu/bfin/serial/uart.h
+++ b/c/src/lib/libcpu/bfin/serial/uart.h
@@ -8,52 +8,133 @@
* found in the file LICENSE in this distribution or at
* http://www.rtems.com/license/LICENSE.
*
+ * Modified:
+ * $Author$ Added interrupt support and DMA support
+ *
* $Id$
*/
-#ifndef _uart_h_
-#define _uart_h_
+
+#ifndef _UART_H_
+#define _UART_H_
#ifdef __cplusplus
extern "C" {
#endif
-
+/** bfin_uart_channel object
+ */
typedef struct {
- const char *name;
- void *base_address;
- bool use_interrupts;
- int force_baud;
- /* the following are for internal use */
- void *termios;
- uint8_t volatile flags;
+ const char *name; /** Holds name of the device */
+ uint32_t uart_baseAddress; /** UART base address */
+ uint32_t uart_rxDmaBaseAddress; /** RX DMA base address */
+ uint32_t uart_txDmaBaseAddress; /** TX DMA base address */
+ bool uart_useInterrupts; /** are interrupts used */
+ bool uart_useDma; /** is dma used */
+ int uart_baud; /** baud rate, 0 for default */
+
+ void *termios; /** termios associated */
+ uint8_t volatile flags; /** flags for internal use */
+ uint16_t length; /** length for internal use */
} bfin_uart_channel_t;
+
typedef struct {
uint32_t freq;
int num_channels;
bfin_uart_channel_t *channels;
} bfin_uart_config_t;
+/**
+ * @param base_address defines the UART base address
+ * @param source defines the source that caused the interrupt. This argument
+ * will help us in identifying if Rx or TX caused the interrupt.
+ */
+typedef struct {
+ uint32_t base_address;
+ int source;
+} bfin_uart_arg_t;
+
-char bfin_uart_poll_read(int minor);
+
+char bfin_uart_poll_read(rtems_device_minor_number minor);
void bfin_uart_poll_write(int minor, char c);
+
+/**
+* Uart initialization function.
+* @param major major number of the device
+* @param config configuration parameters
+* @return rtems status code
+*/
rtems_status_code bfin_uart_initialize(rtems_device_major_number major,
- bfin_uart_config_t *config);
+ bfin_uart_config_t *config);
+
+
+/**
+ * Opens the device in different modes. The supported modes are
+ * 1. Polling
+ * 2. Interrupt
+ * 3. DMA
+ * At exit the uart_Exit function will be called to flush the device.
+ *
+ * @param major Major number of the device
+ * @param minor Minor number of the device
+ * @param arg
+ * @return
+ */
rtems_device_driver bfin_uart_open(rtems_device_major_number major,
- rtems_device_minor_number minor,
- void *arg);
+ rtems_device_minor_number minor, void *arg);
+
+
+
+/**
+ * This function implements TX dma ISR. It clears the IRQ and dequeues a char
+ * The channel argument will have the base address. Since there are two uart
+ * and both the uarts can use the same tx dma isr.
+ *
+ * TODO: 1. Error checking 2. sending correct length ie after looking at the
+ * number of elements the uart transmitted.
+ *
+ * @param _arg argument passed to the interrupt handler. It contains the
+ * channel argument.
+ */
+void bfinUart_txDmaIsr(void *_arg);
+
+
+
+/**
+ * RX DMA ISR.
+ * The polling route is used for receiving the characters. This is a place
+ * holder for future implementation.
+ * @param _arg
+ */
+void bfinUart_rxDmaIsr(void *_arg);
+
+
+/**
+ * This function implements TX ISR. The function gets called when the TX FIFO is
+ * empty. It clears the interrupt and dequeues the character. It only tx one
+ * character at a time.
+ *
+ * TODO: error handling.
+ * @param _arg gets the channel information.
+ */
+void bfinUart_txIsr(void *_arg);
+
-void bfin_uart_isr(int source);
+/**
+* This function implements RX ISR
+*/
+void bfinUart_rxIsr(void *_arg);
#ifdef __cplusplus
}
#endif
-#endif /* _uart_h_ */
+#endif /* _UART_H_ */