diff options
Diffstat (limited to 'c')
-rw-r--r-- | c/src/lib/libcpu/bfin/ChangeLog | 14 | ||||
-rw-r--r-- | c/src/lib/libcpu/bfin/Makefile.am | 27 | ||||
-rw-r--r-- | c/src/lib/libcpu/bfin/bf52x/include/bf52x.h | 133 | ||||
-rw-r--r-- | c/src/lib/libcpu/bfin/bf52x/interrupt/interrupt.c | 643 | ||||
-rw-r--r-- | c/src/lib/libcpu/bfin/bf52x/interrupt/interrupt.h | 146 | ||||
-rw-r--r-- | c/src/lib/libcpu/bfin/configure.ac | 4 | ||||
-rw-r--r-- | c/src/lib/libcpu/bfin/include/sicRegs.h | 8 | ||||
-rw-r--r-- | c/src/lib/libcpu/bfin/preinstall.am | 23 | ||||
-rw-r--r-- | c/src/lib/libcpu/bfin/serial/uart.c | 520 | ||||
-rw-r--r-- | c/src/lib/libcpu/bfin/serial/uart.h | 113 |
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_ */ |