diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-03-21 16:38:43 +0100 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-03-26 09:15:00 +0200 |
commit | e2bd1f653a3bbf969962082b9ccf1e73b0879819 (patch) | |
tree | 57a47abb16f0d4bb0536483e53bf659b5a024da2 /bsps/bfin | |
parent | bsp/pc386: Remove unused RTEMS_CPU_MODEL (diff) | |
download | rtems-e2bd1f653a3bbf969962082b9ccf1e73b0879819.tar.bz2 |
bsp/bfin: Move libcpu content to bsps
This patch is a part of the BSP source reorganization.
Update #3285.
Diffstat (limited to 'bsps/bfin')
-rw-r--r-- | bsps/bfin/TLL6527M/start/interrupt.c | 642 | ||||
-rw-r--r-- | bsps/bfin/bf537Stamp/net/ethernet.c | 880 | ||||
-rw-r--r-- | bsps/bfin/shared/dev/clock.c | 81 | ||||
-rw-r--r-- | bsps/bfin/shared/dev/rtc.c | 258 | ||||
-rw-r--r-- | bsps/bfin/shared/dev/spi.c | 240 | ||||
-rw-r--r-- | bsps/bfin/shared/dev/sport.c | 2 | ||||
-rw-r--r-- | bsps/bfin/shared/dev/timer.c | 96 | ||||
-rw-r--r-- | bsps/bfin/shared/dev/twi.c | 253 | ||||
-rw-r--r-- | bsps/bfin/shared/dev/uart.c | 528 | ||||
-rw-r--r-- | bsps/bfin/shared/interrupt.c | 196 | ||||
-rw-r--r-- | bsps/bfin/shared/mmu.c | 44 | ||||
-rw-r--r-- | bsps/bfin/shared/shared.am | 8 |
12 files changed, 3228 insertions, 0 deletions
diff --git a/bsps/bfin/TLL6527M/start/interrupt.c b/bsps/bfin/TLL6527M/start/interrupt.c new file mode 100644 index 0000000000..1b69046453 --- /dev/null +++ b/bsps/bfin/TLL6527M/start/interrupt.c @@ -0,0 +1,642 @@ +/** + *@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.org/license + * + * @author Rohan Kangralkar, ECE, Northeastern University + * (kangralkar.r@husky.neu.edu) + * + * LastChange: + */ + +#include <rtems.h> +#include <rtems/libio.h> + +#include <bsp.h> +#include <libcpu/cecRegs.h> +#include <libcpu/sicRegs.h> +#include <string.h> +#include <bsp/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/bsps/bfin/bf537Stamp/net/ethernet.c b/bsps/bfin/bf537Stamp/net/ethernet.c new file mode 100644 index 0000000000..f08ffdd63b --- /dev/null +++ b/bsps/bfin/bf537Stamp/net/ethernet.c @@ -0,0 +1,880 @@ +/* + * RTEMS network driver for Blackfin ethernet controller + * + * COPYRIGHT (c) 2008 Kallisti Labs, Los Gatos, CA, USA + * written by Allan Hessenflow <allanh@kallisti.com> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + * + */ + +#define __INSIDE_RTEMS_BSD_TCPIP_STACK__ + +#include <rtems.h> +#include <rtems/rtems_bsdnet.h> +#include <rtems/rtems/cache.h> + +#include <stdio.h> +#include <inttypes.h> +#include <string.h> + +#include <errno.h> +#include <rtems/error.h> + +#include <sys/param.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/sockio.h> + +#include <net/if.h> + +#include <netinet/in.h> +#include <netinet/if_ether.h> + +#include <libcpu/dmaRegs.h> +#include <libcpu/ethernetRegs.h> +#include <libcpu/ethernet.h> + +#if (BFIN_ETHERNET_DEBUG & BFIN_ETHERNET_DEBUG_DUMP_MBUFS) +#include <rtems/dumpbuf.h> +#endif + +/* + * Number of devices supported by this driver + */ +#ifndef N_BFIN_ETHERNET +# define N_BFIN_ETHERNET 1 +#endif + + +/* #define BFIN_IPCHECKSUMS */ + + +/* + * RTEMS event used by interrupt handler to signal daemons. + */ +#define INTERRUPT_EVENT RTEMS_EVENT_1 + +/* + * RTEMS event used to start transmit daemon. + */ +#define START_TRANSMIT_EVENT RTEMS_EVENT_2 + + +/* largest Ethernet frame MAC will handle */ +#define BFIN_ETHERNET_MAX_FRAME_LENGTH 1556 + +#if MCLBYTES < (BFIN_ETHERNET_MAX_FRAME_LENGTH + 2) +#error MCLBYTES too small +#endif + +#define BFIN_REG16(base, offset) \ + (*((uint16_t volatile *) ((char *)(base) + (offset)))) +#define BFIN_REG32(base, offset) \ + (*((uint32_t volatile *) ((char *)(base) + (offset)))) + + +#define DMA_MODE_RX (DMA_CONFIG_FLOW_DESC_LARGE | \ + (5 << DMA_CONFIG_NDSIZE_SHIFT) | \ + DMA_CONFIG_WDSIZE_32 | \ + DMA_CONFIG_WNR | \ + DMA_CONFIG_DMAEN) + +#define DMA_MODE_TX (DMA_CONFIG_FLOW_DESC_LARGE | \ + (5 << DMA_CONFIG_NDSIZE_SHIFT) | \ + DMA_CONFIG_WDSIZE_32 | \ + DMA_CONFIG_DMAEN) + +#define DMA_MODE_STATUS (DMA_CONFIG_FLOW_DESC_LARGE | \ + (5 << DMA_CONFIG_NDSIZE_SHIFT) | \ + DMA_CONFIG_DI_EN | \ + DMA_CONFIG_WDSIZE_32 | \ + DMA_CONFIG_WNR | \ + DMA_CONFIG_DMAEN) + +#define DMA_MODE_STATUS_NO_INT (DMA_CONFIG_FLOW_DESC_LARGE | \ + (5 << DMA_CONFIG_NDSIZE_SHIFT) | \ + DMA_CONFIG_WDSIZE_32 | \ + DMA_CONFIG_WNR | \ + DMA_CONFIG_DMAEN) + +#define DMA_MODE_STATUS_LAST (DMA_CONFIG_FLOW_STOP | \ + (0 << DMA_CONFIG_NDSIZE_SHIFT) | \ + DMA_CONFIG_DI_EN | \ + DMA_CONFIG_WDSIZE_32 | \ + DMA_CONFIG_WNR | \ + DMA_CONFIG_DMAEN) + +/* five 16 bit words */ +typedef struct dmaDescS { + struct dmaDescS *next; + void *addr; + uint16_t dmaConfig; +} dmaDescT; + +typedef struct { + uint32_t status; +} txStatusT; + +#ifdef BFIN_IPCHECKSUMS +typedef struct { + uint16_t ipHeaderChecksum; + uint16_t ipPayloadChecksum; + uint32_t status; +} rxStatusT; +#else +typedef struct { + uint32_t status; +} rxStatusT; +#endif + +typedef struct { + dmaDescT data; + dmaDescT status; + struct mbuf *m; +} rxPacketDescT; + +typedef struct { + dmaDescT data; + dmaDescT status; + bool inUse; + union { + uint32_t dummy; /* try to force 32 bit alignment */ + struct { + uint16_t length; + char data[BFIN_ETHERNET_MAX_FRAME_LENGTH]; + } packet; + } buffer; +} txPacketDescT; + + +/* hardware-specific storage */ +struct bfin_ethernetSoftc { + struct arpcom arpcom; /* this entry must be first */ + + uint32_t sclk; + + void *ethBase; + void *rxdmaBase; + void *txdmaBase; + + int acceptBroadcast; + + rtems_id rxDaemonTid; + rtems_id txDaemonTid; + + void *status; + int rxDescCount; + rxPacketDescT *rx; + int txDescCount; + txPacketDescT *tx; + + bool rmii; + int phyAddr; + + /* statistics */ +#ifdef BISON + unsigned long Interrupts; + unsigned long rxInterrupts; + unsigned long rxMissed; + unsigned long rxGiant; + unsigned long rxNonOctet; + unsigned long rxBadCRC; + unsigned long rxCollision; + + unsigned long txInterrupts; + unsigned long txSingleCollision; + unsigned long txMultipleCollision; + unsigned long txCollision; + unsigned long txDeferred; + unsigned long txUnderrun; + unsigned long txLateCollision; + unsigned long txExcessiveCollision; + unsigned long txExcessiveDeferral; + unsigned long txLostCarrier; + unsigned long txRawWait; +#endif +}; + +static struct bfin_ethernetSoftc ethernetSoftc[N_BFIN_ETHERNET]; + + +/* Shut down the interface. */ +static void ethernetStop(struct bfin_ethernetSoftc *sc) { + struct ifnet *ifp; + void *ethBase; + + ifp = &sc->arpcom.ac_if; + ethBase = sc->ethBase; + + ifp->if_flags &= ~IFF_RUNNING; + + /* stop the transmitter and receiver. */ + BFIN_REG32(ethBase, EMAC_OPMODE_OFFSET) &= ~(EMAC_OPMODE_TE | + EMAC_OPMODE_RE); +} + +/* Show interface statistics */ +static void bfin_ethernetStats(struct bfin_ethernetSoftc *sc) { +#ifdef BISON + printf(" Total Interrupts:%-8lu", sc->Interrupts); + printf(" Rx Interrupts:%-8lu", sc->rxInterrupts); + printf(" Giant:%-8lu", sc->rxGiant); + printf(" Non-octet:%-8lu\n", sc->rxNonOctet); + printf(" Bad CRC:%-8lu", sc->rxBadCRC); + printf(" Collision:%-8lu", sc->rxCollision); + printf(" Missed:%-8lu\n", sc->rxMissed); + + printf( " Tx Interrupts:%-8lu", sc->txInterrupts); + printf( " Deferred:%-8lu", sc->txDeferred); + printf(" Lost Carrier:%-8lu\n", sc->txLostCarrier); + printf( "Single Collisions:%-8lu", sc->txSingleCollision); + printf( "Multiple Collisions:%-8lu", sc->txMultipleCollision); + printf("Excessive Collisions:%-8lu\n", sc->txExcessiveCollision); + printf( " Total Collisions:%-8lu", sc->txCollision); + printf( " Late Collision:%-8lu", sc->txLateCollision); + printf(" Underrun:%-8lu\n", sc->txUnderrun); + printf( " Raw output wait:%-8lu\n", sc->txRawWait); +#endif /*BISON*/ +} + +void bfin_ethernet_rxdma_isr(int vector) { + struct bfin_ethernetSoftc *sc; + void *rxdmaBase; + uint16_t status; + int i; + + for (i = 0; i < N_BFIN_ETHERNET; i++) { + sc = ðernetSoftc[i]; + rxdmaBase = sc->rxdmaBase; + status = BFIN_REG16(rxdmaBase, DMA_IRQ_STATUS_OFFSET); + if (status & DMA_IRQ_STATUS_DMA_DONE) + rtems_bsdnet_event_send (sc->rxDaemonTid, INTERRUPT_EVENT); + BFIN_REG16(rxdmaBase, DMA_IRQ_STATUS_OFFSET) = status; + } +} + +void bfin_ethernet_txdma_isr(int vector) { + struct bfin_ethernetSoftc *sc; + void *txdmaBase; + uint16_t status; + int i; + + for (i = 0; i < N_BFIN_ETHERNET; i++) { + sc = ðernetSoftc[i]; + txdmaBase = sc->txdmaBase; + status = BFIN_REG16(txdmaBase, DMA_IRQ_STATUS_OFFSET); + if (status & DMA_IRQ_STATUS_DMA_DONE) + rtems_bsdnet_event_send (sc->txDaemonTid, INTERRUPT_EVENT); + BFIN_REG16(txdmaBase, DMA_IRQ_STATUS_OFFSET) = status; + } +} + +void bfin_ethernet_mac_isr(int vector) { + struct bfin_ethernetSoftc *sc; + void *ethBase; + int i; + + for (i = 0; i < N_BFIN_ETHERNET; i++) { + sc = ðernetSoftc[i]; + ethBase = sc->ethBase; + BFIN_REG32(ethBase, EMAC_SYSTAT_OFFSET) = ~(uint32_t) 0; + } +} + +static bool txFree(struct bfin_ethernetSoftc *sc, int index) { + bool freed; + txStatusT *status; + + freed = false; + if (sc->tx[index].inUse) { + status = (txStatusT *) sc->tx[index].status.addr; + rtems_cache_invalidate_multiple_data_lines(status, sizeof(*status)); + if (status->status != 0) { + /* update statistics */ + + sc->tx[index].inUse = false; + freed = true; + } + } + + return freed; +} + +static void txDaemon(void *arg) { + struct bfin_ethernetSoftc *sc; + struct ifnet *ifp; + struct mbuf *m, *first; + rtems_event_set events; + void *ethBase; + void *txdmaBase; + txStatusT *status; + int head; + int prevHead; + int tail; + int length; + char *ptr; + + sc = (struct bfin_ethernetSoftc *) arg; + ifp = &sc->arpcom.ac_if; + + ethBase = sc->ethBase; + txdmaBase = sc->txdmaBase; + head = 0; + prevHead = sc->txDescCount - 1; + tail = 0; + + while (1) { + /* wait for packet or isr */ + rtems_bsdnet_event_receive(START_TRANSMIT_EVENT | INTERRUPT_EVENT, + RTEMS_EVENT_ANY | RTEMS_WAIT, + RTEMS_NO_TIMEOUT, &events); + + /* if no descriptors are available, try to free one. To reduce + transmit latency only do one here. */ + if (sc->tx[head].inUse && txFree(sc, tail)) { + if (++tail == sc->txDescCount) + tail = 0; + } + /* send packets until the queue is empty or we run out of tx + descriptors */ + while (!sc->tx[head].inUse && (ifp->if_flags & IFF_OACTIVE)) { + /* get the next mbuf chain to transmit */ + IF_DEQUEUE(&ifp->if_snd, m); + if (m != NULL) { + /* copy packet into our buffer */ + ptr = sc->tx[head].buffer.packet.data; + length = 0; + first = m; + while (m && length <= BFIN_ETHERNET_MAX_FRAME_LENGTH) { + length += m->m_len; + if (length <= BFIN_ETHERNET_MAX_FRAME_LENGTH) + memcpy(ptr, m->m_data, m->m_len); + ptr += m->m_len; + m = m->m_next; + } + m_freem(first); /* all done with mbuf */ + if (length <= BFIN_ETHERNET_MAX_FRAME_LENGTH) { + sc->tx[head].buffer.packet.length = length; + + /* setup tx dma */ + status = (txStatusT *) sc->tx[head].status.addr; + status->status = 0; + sc->tx[head].inUse = true; + rtems_cache_flush_multiple_data_lines(status, sizeof(*status)); + + /* configure dma to stop after sending this packet */ + sc->tx[head].status.dmaConfig = DMA_MODE_STATUS_LAST; + rtems_cache_flush_multiple_data_lines( + &sc->tx[head].status.dmaConfig, + sizeof(sc->tx[head].status.dmaConfig)); + rtems_cache_flush_multiple_data_lines( + &sc->tx[head].buffer.packet, + length + sizeof(uint16_t)); + + /* modify previous descriptor to let it continue + automatically */ + sc->tx[prevHead].status.dmaConfig = DMA_MODE_STATUS; + rtems_cache_flush_multiple_data_lines( + &sc->tx[prevHead].status.dmaConfig, + sizeof(sc->tx[prevHead].status.dmaConfig)); + + /* restart dma if it stopped before the packet we just + added. this is purely to reduce transmit latency, + as it would be restarted anyway after this loop (and + needs to be, as there's a very small chance that the + dma controller had started the last status transfer + before the new dmaConfig word was written above and + is still doing that status transfer when we check the + status below. this will be caught by the check + outside the loop as that is guaranteed to run at least + once after the last dma complete interrupt. */ + if ((BFIN_REG16(txdmaBase, DMA_IRQ_STATUS_OFFSET) & + DMA_IRQ_STATUS_DMA_RUN) == 0 && + BFIN_REG32(txdmaBase, DMA_NEXT_DESC_PTR_OFFSET) != + (uint32_t) sc->tx[head].data.next) { + BFIN_REG16(txdmaBase, DMA_CONFIG_OFFSET) = DMA_MODE_TX; + BFIN_REG32(ethBase, EMAC_OPMODE_OFFSET) |= EMAC_OPMODE_TE; + } + + if (++head == sc->txDescCount) + head = 0; + if (++prevHead == sc->txDescCount) + prevHead = 0; + + /* if no descriptors are available, try to free one */ + if (sc->tx[head].inUse && txFree(sc, tail)) { + if (++tail == sc->txDescCount) + tail = 0; + } + } else { + /* dropping packet: too large */ + + } + } else { + /* no packets queued */ + ifp->if_flags &= ~IFF_OACTIVE; + } + } + + /* if dma stopped and there's more to do, restart it */ + if ((BFIN_REG16(txdmaBase, DMA_IRQ_STATUS_OFFSET) & + DMA_IRQ_STATUS_DMA_RUN) == 0 && + BFIN_REG32(txdmaBase, DMA_NEXT_DESC_PTR_OFFSET) != + (uint32_t) &sc->tx[head].data) { + BFIN_REG16(txdmaBase, DMA_CONFIG_OFFSET) = DMA_MODE_TX; + BFIN_REG32(ethBase, EMAC_OPMODE_OFFSET) |= EMAC_OPMODE_TE; + } + + /* free up any additional tx descriptors */ + while (txFree(sc, tail)) { + if (++tail == sc->txDescCount) + tail = 0; + } + } +} + + +static void rxDaemon(void *arg) { + struct bfin_ethernetSoftc *sc; + struct ifnet *ifp; + struct mbuf *m; + struct mbuf *rxPacket; + void *dataPtr; + rtems_event_set events; + struct ether_header *eh; + rxStatusT *status; + uint32_t rxStatus; + int head; + int prevHead; + int length; + void *ethBase; + void *rxdmaBase; + + sc = (struct bfin_ethernetSoftc *) arg; + rxdmaBase = sc->rxdmaBase; + ethBase = sc->ethBase; + ifp = &sc->arpcom.ac_if; + prevHead = sc->rxDescCount - 1; + head = 0; + + BFIN_REG16(rxdmaBase, DMA_CONFIG_OFFSET) = DMA_MODE_RX; + BFIN_REG32(ethBase, EMAC_OPMODE_OFFSET) |= EMAC_OPMODE_RE; + + while (1) { + status = sc->rx[head].status.addr; + rtems_cache_invalidate_multiple_data_lines(status, sizeof(*status)); + while (status->status != 0) { + if (status->status & EMAC_RX_STAT_RX_OK) { + /* get new cluster to replace this one */ + MGETHDR(m, M_WAIT, MT_DATA); + MCLGET(m, M_WAIT); + m->m_pkthdr.rcvif = ifp; + } else + m = NULL; + + rxStatus = status->status; + /* update statistics */ + + + if (m) { + /* save received packet to send up a little later */ + rxPacket = sc->rx[head].m; + dataPtr = sc->rx[head].data.addr; + + /* setup dma for new cluster */ + sc->rx[head].m = m; + sc->rx[head].data.addr = (void *) (((intptr_t) m->m_data + 3) & ~3); + /* invalidate cache for new data buffer, in case any lines + are dirty from previous owner */ + rtems_cache_invalidate_multiple_data_lines( + sc->rx[head].data.addr, + BFIN_ETHERNET_MAX_FRAME_LENGTH + 2); + } else + rxPacket = NULL; + + sc->rx[head].status.dmaConfig = DMA_MODE_STATUS_LAST; + rtems_cache_flush_multiple_data_lines(&sc->rx[head], + sizeof(sc->rx[head])); + + /* mark descriptor as empty */ + status->status = 0; + rtems_cache_flush_multiple_data_lines(&status->status, + sizeof(status->status)); + + /* allow dma to continue from previous descriptor into this + one */ + sc->rx[prevHead].status.dmaConfig = DMA_MODE_STATUS; + rtems_cache_flush_multiple_data_lines( + &sc->rx[prevHead].status.dmaConfig, + sizeof(sc->rx[prevHead].status.dmaConfig)); + + if (rxPacket) { + /* send it up */ + eh = (struct ether_header *) ((intptr_t) dataPtr + 2); + rxPacket->m_data = (caddr_t) ((intptr_t) dataPtr + 2 + 14); + length = (rxStatus & EMAC_RX_STAT_RX_FRLEN_MASK) >> + EMAC_RX_STAT_RX_FRLEN_SHIFT; + rxPacket->m_len = length - 14; + rxPacket->m_pkthdr.len = rxPacket->m_len; + /* invalidate packet buffer cache again (even though it + was invalidated prior to giving it to dma engine), + because speculative reads might cause cache lines to + be filled at any time */ + rtems_cache_invalidate_multiple_data_lines(eh, length); + ether_input(ifp, eh, rxPacket); + } + + if (++prevHead == sc->rxDescCount) + prevHead = 0; + if (++head == sc->rxDescCount) + head = 0; + status = sc->rx[head].status.addr; + rtems_cache_invalidate_multiple_data_lines(status, sizeof(*status)); + } + + /* if dma stopped before the next descriptor, restart it */ + if ((BFIN_REG16(rxdmaBase, DMA_IRQ_STATUS_OFFSET) & + DMA_IRQ_STATUS_DMA_RUN) == 0 && + BFIN_REG32(rxdmaBase, DMA_NEXT_DESC_PTR_OFFSET) != + (uint32_t) &sc->rx[head].data) { + BFIN_REG16(rxdmaBase, DMA_CONFIG_OFFSET) = DMA_MODE_RX; + } + + rtems_bsdnet_event_receive(INTERRUPT_EVENT, RTEMS_WAIT | RTEMS_EVENT_ANY, + RTEMS_NO_TIMEOUT, &events); + } + +} + +/* + ****************************************************************** + * * + * Initialization Routines * + * * + ****************************************************************** + */ + +static void resetHardware(struct bfin_ethernetSoftc *sc) { + void *ethBase; + void *rxdmaBase; + void *txdmaBase; + + ethBase = sc->ethBase; + rxdmaBase = sc->rxdmaBase; + txdmaBase = sc->txdmaBase; + BFIN_REG32(ethBase, EMAC_OPMODE_OFFSET) = 0; + BFIN_REG16(rxdmaBase, DMA_CONFIG_OFFSET) = 0; + BFIN_REG16(txdmaBase, DMA_CONFIG_OFFSET) = 0; +} + +static void initializeHardware(struct bfin_ethernetSoftc *sc) { + struct ifnet *ifp; + struct mbuf *m; + unsigned char *hwaddr; + int cacheAlignment; + int rxStatusSize; + int txStatusSize; + char *ptr; + int i; + void *ethBase; + void *rxdmaBase; + void *txdmaBase; + uint32_t divisor; + + ifp = &sc->arpcom.ac_if; + ethBase = sc->ethBase; + rxdmaBase = sc->rxdmaBase; + txdmaBase = sc->txdmaBase; + + BFIN_REG32(ethBase, EMAC_OPMODE_OFFSET) = 0; + BFIN_REG32(ethBase, EMAC_FLC_OFFSET) = 0; + divisor = (sc->sclk / 25000000) / 2 - 1; + BFIN_REG32(ethBase, EMAC_SYSCTL_OFFSET) = (divisor << + EMAC_SYSCTL_MDCDIV_SHIFT) | + EMAC_SYSCTL_RXDWA; +#ifdef BFIN_IPCHECKSUMS + BFIN_REG32(ethBase, EMAC_SYSCTL_OFFSET) |= EMAC_SYSCTL_RXCKS; +#endif + BFIN_REG32(ethBase, EMAC_SYSTAT_OFFSET) = ~(uint32_t) 0; + BFIN_REG32(ethBase, EMAC_RX_IRQE_OFFSET) = 0; + BFIN_REG32(ethBase, EMAC_RX_STKY_OFFSET) = ~(uint32_t) 0; + BFIN_REG32(ethBase, EMAC_TX_IRQE_OFFSET) = 0; + BFIN_REG32(ethBase, EMAC_TX_STKY_OFFSET) = ~(uint32_t) 0; + BFIN_REG32(ethBase, EMAC_MMC_RIRQE_OFFSET) = 0; + BFIN_REG32(ethBase, EMAC_MMC_RIRQS_OFFSET) = ~(uint32_t) 0; + BFIN_REG32(ethBase, EMAC_MMC_TIRQE_OFFSET) = 0; + BFIN_REG32(ethBase, EMAC_MMC_TIRQS_OFFSET) = ~(uint32_t) 0; + BFIN_REG32(ethBase, EMAC_MMC_CTL_OFFSET) = EMAC_MMC_CTL_MMCE | + EMAC_MMC_CTL_CCOR | + EMAC_MMC_CTL_RSTC; + BFIN_REG32(ethBase, EMAC_MMC_CTL_OFFSET) = EMAC_MMC_CTL_MMCE | + EMAC_MMC_CTL_CCOR; + + BFIN_REG16(rxdmaBase, DMA_CONFIG_OFFSET) = 0; + BFIN_REG16(txdmaBase, DMA_CONFIG_OFFSET) = 0; + BFIN_REG16(rxdmaBase, DMA_X_COUNT_OFFSET) = 0; + BFIN_REG16(txdmaBase, DMA_X_COUNT_OFFSET) = 0; + BFIN_REG16(rxdmaBase, DMA_X_MODIFY_OFFSET) = 4; + BFIN_REG16(txdmaBase, DMA_X_MODIFY_OFFSET) = 4; + BFIN_REG16(rxdmaBase, DMA_Y_COUNT_OFFSET) = 0; + BFIN_REG16(txdmaBase, DMA_Y_COUNT_OFFSET) = 0; + BFIN_REG16(rxdmaBase, DMA_Y_MODIFY_OFFSET) = 0; + BFIN_REG16(txdmaBase, DMA_Y_MODIFY_OFFSET) = 0; + BFIN_REG16(rxdmaBase, DMA_IRQ_STATUS_OFFSET) = DMA_IRQ_STATUS_DMA_ERR | + DMA_IRQ_STATUS_DMA_DONE; + + /* The status structures cannot share cache lines with anything else, + including other status structures, so we can safely manage both the + processor and DMA writing to them. So this rounds up the structure + sizes to a multiple of the cache line size. */ + cacheAlignment = (int) rtems_cache_get_data_line_size(); + if (cacheAlignment == 0) + cacheAlignment = 1; + rxStatusSize = cacheAlignment * ((sizeof(rxStatusT) + cacheAlignment - 1) / + cacheAlignment); + txStatusSize = cacheAlignment * ((sizeof(txStatusT) + cacheAlignment - 1) / + cacheAlignment); + /* Allocate enough extra to allow structures to start at cache aligned + boundary. */ + sc->status = malloc(sc->rxDescCount * rxStatusSize + + sc->txDescCount * txStatusSize + + cacheAlignment - 1, M_DEVBUF, M_NOWAIT); + sc->rx = malloc(sc->rxDescCount * sizeof(*sc->rx), M_DEVBUF, M_NOWAIT); + sc->tx = malloc(sc->txDescCount * sizeof(*sc->tx), M_DEVBUF, M_NOWAIT); + if (sc->status == NULL || sc->rx == NULL || sc->tx == NULL) + rtems_panic("No memory!\n"); + + /* Start status structures at cache aligned boundary. */ + ptr = (char *) (((intptr_t) sc->status + cacheAlignment - 1) & + ~(cacheAlignment - 1)); + memset(ptr, 0, sc->rxDescCount * rxStatusSize + + sc->txDescCount * txStatusSize); + memset(sc->rx, 0, sc->rxDescCount * sizeof(*sc->rx)); + memset(sc->tx, 0, sc->txDescCount * sizeof(*sc->tx)); + rtems_cache_flush_multiple_data_lines(ptr, sc->rxDescCount * rxStatusSize + + sc->txDescCount * txStatusSize); + for (i = 0; i < sc->rxDescCount; i++) { + MGETHDR(m, M_WAIT, MT_DATA); + MCLGET(m, M_WAIT); + m->m_pkthdr.rcvif = ifp; + sc->rx[i].m = m; + /* start dma at 32 bit boundary */ + sc->rx[i].data.addr = (void *) (((intptr_t) m->m_data + 3) & ~3); + rtems_cache_invalidate_multiple_data_lines( + sc->rx[i].data.addr, + BFIN_ETHERNET_MAX_FRAME_LENGTH + 2); + sc->rx[i].data.dmaConfig = DMA_MODE_RX; + sc->rx[i].data.next = &(sc->rx[i].status); + sc->rx[i].status.addr = ptr; + if (i < sc->rxDescCount - 1) { + sc->rx[i].status.dmaConfig = DMA_MODE_STATUS; + sc->rx[i].status.next = &(sc->rx[i + 1].data); + } else { + sc->rx[i].status.dmaConfig = DMA_MODE_STATUS_LAST; + sc->rx[i].status.next = &(sc->rx[0].data); + } + ptr += rxStatusSize; + } + rtems_cache_flush_multiple_data_lines(sc->rx, sc->rxDescCount * + sizeof(*sc->rx)); + for (i = 0; i < sc->txDescCount; i++) { + sc->tx[i].data.addr = &sc->tx[i].buffer.packet; + sc->tx[i].data.dmaConfig = DMA_MODE_TX; + sc->tx[i].data.next = &(sc->tx[i].status); + sc->tx[i].status.addr = ptr; + sc->tx[i].status.dmaConfig = DMA_MODE_STATUS_LAST; + if (i < sc->txDescCount - 1) + sc->tx[i].status.next = &(sc->tx[i + 1].data); + else + sc->tx[i].status.next = &(sc->tx[0].data); + sc->tx[i].inUse = false; + ptr += txStatusSize; + } + rtems_cache_flush_multiple_data_lines(sc->tx, sc->txDescCount * + sizeof(*sc->tx)); + + BFIN_REG32(rxdmaBase, DMA_NEXT_DESC_PTR_OFFSET) = (uint32_t) &sc->rx[0].data; + BFIN_REG32(txdmaBase, DMA_NEXT_DESC_PTR_OFFSET) = (uint32_t) &sc->tx[0].data; + + hwaddr = sc->arpcom.ac_enaddr; + BFIN_REG16(ethBase, EMAC_ADDRHI_OFFSET) = ((uint16_t) hwaddr[5] << 8) | + hwaddr[4]; + BFIN_REG32(ethBase, EMAC_ADDRLO_OFFSET) = ((uint32_t) hwaddr[3] << 24) | + ((uint32_t) hwaddr[2] << 16) | + ((uint32_t) hwaddr[1] << 8) | + hwaddr[0]; + + if (sc->acceptBroadcast) + BFIN_REG32(ethBase, EMAC_OPMODE_OFFSET) &= ~EMAC_OPMODE_DBF; + else + BFIN_REG32(ethBase, EMAC_OPMODE_OFFSET) |= EMAC_OPMODE_DBF; + +} + +/* send packet (caller provides header) */ +static void ethernetStart(struct ifnet *ifp) { + struct bfin_ethernetSoftc *sc; + + sc = ifp->if_softc; + + ifp->if_flags |= IFF_OACTIVE; + rtems_bsdnet_event_send(sc->txDaemonTid, START_TRANSMIT_EVENT); +} + +/* initialize and start the device */ +static void ethernetInit(void *arg) { + struct bfin_ethernetSoftc *sc; + struct ifnet *ifp; + void *ethBase; + + sc = arg; + ifp = &sc->arpcom.ac_if; + ethBase = sc->ethBase; + + if (sc->txDaemonTid == 0) { + initializeHardware(sc); + + /* start driver tasks */ + sc->rxDaemonTid = rtems_bsdnet_newproc("BFrx", 4096, rxDaemon, sc); + sc->txDaemonTid = rtems_bsdnet_newproc("BFtx", 4096, txDaemon, sc); + + } + + if (ifp->if_flags & IFF_PROMISC) + BFIN_REG32(ethBase, EMAC_OPMODE_OFFSET) |= EMAC_OPMODE_PR; + else + BFIN_REG32(ethBase, EMAC_OPMODE_OFFSET) &= ~EMAC_OPMODE_PR; + + /* + * Tell the world that we're running. + */ + ifp->if_flags |= IFF_RUNNING; + +} + +/* driver ioctl handler */ +static int ethernetIoctl(struct ifnet *ifp, ioctl_command_t command, + caddr_t data) { + int result; + struct bfin_ethernetSoftc *sc = ifp->if_softc; + + result = 0; + switch (command) { + case SIOCGIFADDR: + case SIOCSIFADDR: + ether_ioctl(ifp, command, data); + break; + case SIOCSIFFLAGS: + switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) { + case IFF_RUNNING: + ethernetStop(sc); + break; + case IFF_UP: + ethernetInit(sc); + break; + case IFF_UP | IFF_RUNNING: + ethernetStop(sc); + ethernetInit(sc); + break; + default: + break; + } + break; + case SIO_RTEMS_SHOW_STATS: + bfin_ethernetStats(sc); + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + default: + result = EINVAL; + break; + } + + return result; +} + +/* attach a BFIN ETHERNET driver to the system */ +int bfin_ethernet_driver_attach(struct rtems_bsdnet_ifconfig *config, + int attaching, + bfin_ethernet_configuration_t *chip) { + struct bfin_ethernetSoftc *sc; + struct ifnet *ifp; + int mtu; + int unitNumber; + char *unitName; + + if ((unitNumber = rtems_bsdnet_parse_driver_name(config, &unitName)) < 0) + return 0; + + if ((unitNumber <= 0) || (unitNumber > N_BFIN_ETHERNET)) { + printf("Bad bfin ethernet unit number %d.\n", unitNumber); + return 0; + } + sc = ðernetSoftc[unitNumber - 1]; + ifp = &sc->arpcom.ac_if; + if (ifp->if_softc != NULL) { + printf("Driver already in use.\n"); + return 0; + } + + memset(sc, 0, sizeof(*sc)); + + /* process options */ + if (config->hardware_address) + memcpy(sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN); + else + memset(sc->arpcom.ac_enaddr, 0x08, ETHER_ADDR_LEN); + if (config->mtu) + mtu = config->mtu; + else + mtu = ETHERMTU; + if (config->rbuf_count) + sc->rxDescCount = config->rbuf_count; + else + sc->rxDescCount = chip->rxDescCount; + if (config->xbuf_count) + sc->txDescCount = config->xbuf_count; + else + sc->txDescCount = chip->txDescCount; + /* minimum two of each type descriptor */ + if (sc->rxDescCount <= 1) + sc->rxDescCount = 2; + if (sc->txDescCount <= 1) + sc->txDescCount = 2; + + sc->acceptBroadcast = !config->ignore_broadcast; + + sc->sclk = chip->sclk; + sc->ethBase = chip->ethBaseAddress; + sc->rxdmaBase = chip->rxdmaBaseAddress; + sc->txdmaBase = chip->txdmaBaseAddress; + + /* make sure we should not have any interrupts asserted */ + resetHardware(sc); + + sc->rmii = (chip->phyType == rmii); + sc->phyAddr = chip->phyAddr; + + /* set up network interface values */ + ifp->if_softc = sc; + ifp->if_unit = unitNumber; + ifp->if_name = unitName; + ifp->if_mtu = mtu; + ifp->if_init = ethernetInit; + ifp->if_ioctl = ethernetIoctl; + ifp->if_start = ethernetStart; + ifp->if_output = ether_output; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; + if (ifp->if_snd.ifq_maxlen == 0) + ifp->if_snd.ifq_maxlen = ifqmaxlen; + + if_attach(ifp); + ether_ifattach(ifp); + + return 1; +} + diff --git a/bsps/bfin/shared/dev/clock.c b/bsps/bfin/shared/dev/clock.c new file mode 100644 index 0000000000..d46ab3581e --- /dev/null +++ b/bsps/bfin/shared/dev/clock.c @@ -0,0 +1,81 @@ +/* RTEMS Clock Tick Driver for Blackfin. Uses Blackfin Core Timer. + */ + +/* + * Copyright (c) 2008 Kallisti Labs, Los Gatos, CA, USA + * written by Allan Hessenflow <allanh@kallisti.com> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + + +#include <rtems.h> +#include <stdlib.h> +#include <rtems/libio.h> +#include <rtems/score/percpu.h> +#include <bsp.h> +#include <rtems/clockdrv.h> + + +#include <libcpu/cecRegs.h> +#include <libcpu/coreTimerRegs.h> + +#if (BFIN_ON_SKYEYE) +#define CLOCK_DRIVER_USE_FAST_IDLE 1 +#endif + +volatile uint32_t Clock_driver_ticks; + +void Clock_exit(void); + +static rtems_isr clockISR(rtems_vector_number vector) { + + Clock_driver_ticks += 1; + +#if CLOCK_DRIVER_USE_FAST_IDLE + do { + rtems_clock_tick(); + } while ( _Thread_Heir == _Thread_Executing && _Thread_Executing->is_idle ); +#else + rtems_clock_tick(); +#endif +} + +/* + * Clock_exit + * + * This routine allows the clock driver to exit by masking the interrupt and + * disabling the clock's counter. + */ +void Clock_exit(void) +{ + *(uint32_t volatile *) TCNTL = 0; +} + +/* + * Clock_initialize + * + * This routine initializes the clock driver. + */ +rtems_device_driver Clock_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *pargp +) +{ + Clock_driver_ticks = 0; + + set_vector(clockISR, CEC_CORE_TIMER_VECTOR, 1); + + *(uint32_t volatile *) TCNTL = TCNTL_TMPWR | TCNTL_TAUTORLD; + *(uint32_t volatile *) TSCALE = 0; + *(uint32_t volatile *) TPERIOD = CCLK / 1000000 * + rtems_configuration_get_microseconds_per_tick(); + *(uint32_t volatile *) TCNTL = TCNTL_TMPWR | TCNTL_TAUTORLD | TCNTL_TMREN; + + atexit(Clock_exit); + + return RTEMS_SUCCESSFUL; +} diff --git a/bsps/bfin/shared/dev/rtc.c b/bsps/bfin/shared/dev/rtc.c new file mode 100644 index 0000000000..fa340a3c45 --- /dev/null +++ b/bsps/bfin/shared/dev/rtc.c @@ -0,0 +1,258 @@ +/* + * Real Time Clock Driver for Blackfin + */ + +/* + * Copyright (c) 2006 by Atos Automacao Industrial Ltda. + * written by Alain Schaefer <alain.schaefer@easc.ch> + * and Antonio Giovanini <antonio@atos.com.br> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + + +#include <rtems.h> +#include <rtems/tod.h> +#include <rtems/rtc.h> +#include <rtems/libio.h> +#include <bsp.h> +#include <libcpu/rtcRegs.h> +#include <rtems/score/todimpl.h> + +/* The following are inside RTEMS -- we are violating visibility!!! + * Perhaps an API could be defined to get days since 1 Jan. + */ +extern const uint16_t _TOD_Days_to_date[2][13]; + +/* + * Prototypes and routines used below + */ +int Leap_years_until_now (int year); + +void Init_RTC(void) +{ + *((uint16_t*)RTC_PREN) = RTC_PREN_PREN; /* Enable Prescaler */ +} + +/* + * Read time from RTEMS' clock manager and set it to RTC + */ +void setRealTimeFromRTEMS (void) +{ + rtems_time_of_day time_buffer; + rtems_status_code status; + + status = rtems_clock_get_tod( &time_buffer ); + if (status == RTEMS_SUCCESSFUL){ + setRealTime(&time_buffer); + } +} + +/* + * Read real time from RTC and set it to RTEMS' clock manager + */ +void setRealTimeToRTEMS (void) +{ + rtems_time_of_day time_buffer; + + getRealTime(&time_buffer); + rtems_clock_set( &time_buffer ); +} + +/* + * Set the RTC time + */ +int setRealTime( + const rtems_time_of_day *tod +) +{ + uint32_t days; + rtems_time_of_day tod_temp; + + tod_temp = *tod; + + days = (tod_temp.year - TOD_BASE_YEAR) * 365 + \ + _TOD_Days_to_date[0][tod_temp.month] + tod_temp.day - 1; + if (tod_temp.month < 3) + days += Leap_years_until_now (tod_temp.year - 1); + else + days += Leap_years_until_now (tod_temp.year); + + *((uint32_t volatile *)RTC_STAT) = (days << RTC_STAT_DAYS_SHIFT)| + (tod_temp.hour << RTC_STAT_HOURS_SHIFT)| + (tod_temp.minute << RTC_STAT_MINUTES_SHIFT)| + tod_temp.second; + + return 0; +} + +/* + * Get the time from the RTC. + */ +void getRealTime( + rtems_time_of_day *tod +) +{ + uint32_t days, rtc_reg; + rtems_time_of_day tod_temp = { 0, 0, 0 }; + int n, Leap_year; + + rtc_reg = *((uint32_t volatile *)RTC_STAT); + + days = (rtc_reg >> RTC_STAT_DAYS_SHIFT) + 1; + + /* finding year */ + tod_temp.year = days/365 + TOD_BASE_YEAR; + if (days%365 > Leap_years_until_now (tod_temp.year - 1)) { + days = (days%365) - Leap_years_until_now (tod_temp.year - 1); + } else { + tod_temp.year--; + days = (days%365) + 365 - Leap_years_until_now (tod_temp.year - 1); + } + + /* finding month and day */ + Leap_year = (((!(tod_temp.year%4)) && (tod_temp.year%100)) || + (!(tod_temp.year%400)))?1:0; + for (n=1; n<=12; n++) { + if (days <= _TOD_Days_to_date[Leap_year][n+1]) { + tod_temp.month = n; + tod_temp.day = days - _TOD_Days_to_date[Leap_year][n]; + break; + } + } + + tod_temp.hour = (rtc_reg & RTC_STAT_HOURS_MASK) >> RTC_STAT_HOURS_SHIFT; + tod_temp.minute = (rtc_reg & RTC_STAT_MINUTES_MASK) >> RTC_STAT_MINUTES_SHIFT; + tod_temp.second = (rtc_reg & RTC_STAT_SECONDS_MASK); + tod_temp.ticks = 0; + *tod = tod_temp; +} + +/* + * Return the difference between RTC and RTEMS' clock manager time in minutes. + * If the difference is greater than 1 day, this returns 9999. + */ +int checkRealTime (void) +{ + rtems_time_of_day rtems_tod; + rtems_time_of_day rtc_tod; + uint32_t rtems_time; + uint32_t rtc_time; + + (void) rtems_clock_get_tod( &rtems_tod ); + getRealTime ( &rtc_tod ); + + rtems_time = _TOD_To_seconds( &rtems_tod ); + rtc_time = _TOD_To_seconds( &rtc_tod ); + + return rtems_time - rtc_time; +} + +int Leap_years_until_now (int year) +{ + return ((year/4 - year/100 + year/400) - + ((TOD_BASE_YEAR - 1)/4 - (TOD_BASE_YEAR - 1)/100 + + (TOD_BASE_YEAR - 1)/400)); +} + +rtems_device_driver rtc_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor_arg, + void *arg +) +{ + rtems_status_code status; + + /* + * Register and initialize the primary RTC's + */ + + status = rtems_io_register_name( RTC_DEVICE_NAME, major, 0 ); + if (status != RTEMS_SUCCESSFUL) { + rtems_fatal_error_occurred(status); + } + + Init_RTC(); + + setRealTimeToRTEMS(); + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver rtc_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_libio_rw_args_t *rw = arg; + rtems_time_of_day *tod = (rtems_time_of_day *) rw->buffer; + + rw->offset = 0; + rw->bytes_moved = 0; + + if (rw->count != sizeof( rtems_time_of_day)) { + return RTEMS_INVALID_SIZE; + } + + getRealTime( tod); + + rw->bytes_moved = rw->count; + + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver rtc_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + int rv = 0; + rtems_libio_rw_args_t *rw = arg; + const rtems_time_of_day *tod = (const rtems_time_of_day *) rw->buffer; + + rw->offset = 0; + rw->bytes_moved = 0; + + if (rw->count != sizeof( rtems_time_of_day)) { + return RTEMS_INVALID_SIZE; + } + + rv = setRealTime( tod); + if (rv != 0) { + return RTEMS_IO_ERROR; + } + + rw->bytes_moved = rw->count; + + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver rtc_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver rtc_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver rtc_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + return RTEMS_NOT_IMPLEMENTED; +} diff --git a/bsps/bfin/shared/dev/spi.c b/bsps/bfin/shared/dev/spi.c new file mode 100644 index 0000000000..e1024e8d10 --- /dev/null +++ b/bsps/bfin/shared/dev/spi.c @@ -0,0 +1,240 @@ +/* SPI driver for Blackfin + * + * Copyright (c) 2010 Kallisti Labs, Los Gatos, CA, USA + * written by Allan Hessenflow <allanh@kallisti.com> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <stdlib.h> +#include <bsp.h> +#include <rtems/error.h> +#include <rtems/bspIo.h> +#include <errno.h> +#include <rtems/libi2c.h> +#include <libcpu/spiRegs.h> +#include <libcpu/spi.h> + + +#ifndef BFIN_REG16 +#define BFIN_REG16(base, offset) \ + (*((uint16_t volatile *) ((uint8_t *)(base) + (offset)))) +#endif + + +static bfin_spi_state_t *bfin_spi; + + +void bfin_spi_isr(int v) { + bfin_spi_state_t *state; + uint16_t r; + + state = bfin_spi; + if (state->len > state->bytes_per_word) { + if (state->wr_ptr) { + if (state->bytes_per_word == 2) + r = *(uint16_t *) state->wr_ptr; + else + r = (uint16_t) *state->wr_ptr; + state->wr_ptr += state->bytes_per_word; + } else + r = state->idle_pattern; + BFIN_REG16(state->base, SPI_TDBR_OFFSET) = r; + } + state->len -= state->bytes_per_word; + if (state->len <= 0) { + /* + The transfers are done, so I don't want to kick off another + transfer or get any more interrupts. Reading the last word from + SPI_SHADOW instead of SPI_RDBR should prevent it from triggering + another transfer, but that doesn't clear the interrupt flag. I + could mask the interrupt in the SIC, but that would preclude ever + using the DMA channel that shares the interrupt independently (and + they might just share it with something more important in some other + member of the Blackfin family). And who knows what problems it + might cause in this code potentially dealing with that still pended + interrupt at the beginning of the next transfer. + + So instead I disable the SPI interface, read the data from RDBR + (thus clearing the interrupt but not triggering another transfer + since the interface is disabled), then re-eanble the interface. + This has the problem that the bf537 tri-states the SPI signals + while the interface is disabled. Either adding pull-ups on at + least the chip select signals, or using GPIOs for them so they're + not controlled by the SPI module, would be correct fixes for that + (really pull-ups/downs should be added to the SPI CLK and MOSI + signals as well to insure they cannot float into some region that + causes input structures to consume excessive power). Or they can + all be left alone, assuming that there's enough capacitance on the + lines to prevent any problems for the short time they're being left + disabled. + + An alternative approach I attempted involved switching TIMOD + between RDBR and TDBR when starting and finishing a transfer, but + I didn't get anywhere with that. In my limited testing TIMOD TDBR + wasn't behaving as I expected it to, but maybe with more + experimentation I'd find some solution there. However I'm out + of time for this project, at least for now. + */ + + BFIN_REG16(state->base, SPI_CTL_OFFSET) &= ~SPI_CTL_SPE; + r = BFIN_REG16(state->base, SPI_RDBR_OFFSET); + BFIN_REG16(state->base, SPI_CTL_OFFSET) |= SPI_CTL_SPE; + rtems_semaphore_release(state->sem); + } else + r = BFIN_REG16(state->base, SPI_RDBR_OFFSET); + + if (state->rd_ptr) { + if (state->bytes_per_word == 2) + *(uint16_t *) state->rd_ptr = r; + else + *state->rd_ptr = (uint8_t) r; + state->rd_ptr += state->bytes_per_word; + } +} + +static rtems_status_code setTFRMode(rtems_libi2c_bus_t *bus, + const rtems_libi2c_tfr_mode_t *tfrMode) { + rtems_status_code result; + bfin_spi_state_t *state; + uint32_t divisor; + uint16_t ctrl; + + result = RTEMS_SUCCESSFUL; + state = &((bfin_spi_bus_t *) bus)->p; + + if (result == RTEMS_SUCCESSFUL) { + if (tfrMode->bits_per_char != 8 && + tfrMode->bits_per_char != 16) + result = RTEMS_INVALID_NUMBER; + if (tfrMode->baudrate <= 0) + result = RTEMS_INVALID_NUMBER; + } + if (result == RTEMS_SUCCESSFUL) { + divisor = (SCLK / 2 + tfrMode->baudrate - 1) / + tfrMode->baudrate; + if (divisor < 2) + divisor = 2; + else if (divisor > 65535) + result = RTEMS_INVALID_NUMBER; + } + if (result == RTEMS_SUCCESSFUL) { + state->idle_pattern = (uint16_t) tfrMode->idle_char; + state->bytes_per_word = (tfrMode->bits_per_char > 8) ? 2 : 1; + BFIN_REG16(state->base, SPI_BAUD_OFFSET) = divisor; + ctrl = BFIN_REG16(state->base, SPI_CTL_OFFSET); + if (tfrMode->lsb_first) + ctrl |= SPI_CTL_LSBF; + else + ctrl &= ~SPI_CTL_LSBF; + if (tfrMode->bits_per_char > 8) + ctrl |= SPI_CTL_SIZE; + else + ctrl &= ~SPI_CTL_SIZE; + if (tfrMode->clock_inv) + ctrl |= SPI_CTL_CPOL; + else + ctrl &= ~SPI_CTL_CPOL; + if (tfrMode->clock_phs) + ctrl |= SPI_CTL_CPHA; + else + ctrl &= ~SPI_CTL_CPHA; + BFIN_REG16(state->base, SPI_CTL_OFFSET) = ctrl; + } + + return result; +} + +static int readWrite(rtems_libi2c_bus_t *bus, uint8_t *rdBuf, + const uint8_t *wrBuf, int len) { + rtems_status_code result; + bfin_spi_state_t *state; + uint16_t r; + + result = RTEMS_SUCCESSFUL; + state = &((bfin_spi_bus_t *) bus)->p; + + if (len) { + state->rd_ptr = rdBuf; + state->wr_ptr = wrBuf; + state->len = len; + if (state->wr_ptr) { + if (state->bytes_per_word == 2) + r = *(uint16_t *) state->wr_ptr; + else + r = (uint16_t) *state->wr_ptr; + state->wr_ptr += state->bytes_per_word; + } else + r = state->idle_pattern; + BFIN_REG16(state->base, SPI_TDBR_OFFSET) = r; + BFIN_REG16(state->base, SPI_RDBR_OFFSET); /* trigger */ + /* wait until done */ + do { + result = rtems_semaphore_obtain(state->sem, RTEMS_WAIT, 100); + } while (result == RTEMS_SUCCESSFUL && state->len > 0); + } + + return (result == RTEMS_SUCCESSFUL) ? len : -result; +} + + +rtems_status_code bfin_spi_init(rtems_libi2c_bus_t *bus) { + rtems_status_code result; + bfin_spi_state_t *state; + + state = &((bfin_spi_bus_t *) bus)->p; + + BFIN_REG16(state->base, SPI_CTL_OFFSET) = SPI_CTL_SPE | + SPI_CTL_MSTR | + SPI_CTL_CPHA | + SPI_CTL_TIMOD_RDBR; + + result = rtems_semaphore_create(rtems_build_name('s','p','i','s'), + 0, + RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE, + 0, + &state->sem); + if (result == RTEMS_SUCCESSFUL) + bfin_spi = state; /* for isr */ + + return result; +} + +rtems_status_code bfin_spi_send_start(rtems_libi2c_bus_t *bus) { + + return RTEMS_SUCCESSFUL; +} + +int bfin_spi_read_bytes(rtems_libi2c_bus_t *bus, unsigned char *buf, int len) { + + return readWrite(bus, buf, NULL, len); +} + +int bfin_spi_write_bytes(rtems_libi2c_bus_t *bus, unsigned char *buf, int len) { + + return readWrite(bus, NULL, buf, len); +} + +int bfin_spi_ioctl(rtems_libi2c_bus_t *bus, int cmd, void *arg) { + int result; + + result = -RTEMS_NOT_DEFINED; + switch(cmd) { + case RTEMS_LIBI2C_IOCTL_SET_TFRMODE: + result = -setTFRMode(bus, (const rtems_libi2c_tfr_mode_t *) arg); + break; + case RTEMS_LIBI2C_IOCTL_READ_WRITE: + result = readWrite(bus, + ((rtems_libi2c_read_write_t *) arg)->rd_buf, + ((rtems_libi2c_read_write_t *) arg)->wr_buf, + ((rtems_libi2c_read_write_t *) arg)->byte_cnt); + break; + default: + break; + } + + return result; +} + diff --git a/bsps/bfin/shared/dev/sport.c b/bsps/bfin/shared/dev/sport.c new file mode 100644 index 0000000000..6ed481b593 --- /dev/null +++ b/bsps/bfin/shared/dev/sport.c @@ -0,0 +1,2 @@ +/* placeholder */ + diff --git a/bsps/bfin/shared/dev/timer.c b/bsps/bfin/shared/dev/timer.c new file mode 100644 index 0000000000..02540fe2ce --- /dev/null +++ b/bsps/bfin/shared/dev/timer.c @@ -0,0 +1,96 @@ +/** + * @file + * @brief Timer for Blackfin + * + * This file manages the benchmark timer used by the RTEMS Timing Test + * Suite. Each measured time period is demarcated by calls to + * benchmark_timer_initialize() and benchmark_timer_read(). + * benchmark_timer_read() usually returns the number of microseconds + * since benchmark_timer_initialize() exitted. + */ + +/* + * Copyright (c) 2006 by Atos Automacao Industrial Ltda. + * written by Alain Schaefer <alain.schaefer@easc.ch> + * and Antonio Giovanini <antonio@atos.com.br> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <rtems.h> +#include <bsp.h> +#include <rtems/btimer.h> + +uint32_t Timer_interrupts; +bool benchmark_timer_find_average_overhead; + +/* + * benchmark_timer_initialize + * + * Blackfin processor has a counter for clock cycles. + */ +void benchmark_timer_initialize( void ) +{ + + /*reset counters*/ + __asm__ ("R2 = 0;"); + __asm__ ("CYCLES = R2;"); + __asm__ ("CYCLES2 = R2;"); + /*start counters*/ + __asm__ ("R2 = SYSCFG;"); + __asm__ ("BITSET(R2,1);"); + __asm__ ("SYSCFG = R2"); + +} + +/* + * The following controls the behavior of benchmark_timer_read(). + * + * AVG_OVEREHAD is the overhead for starting and stopping the timer. It + * is usually deducted from the number returned. + * + * LEAST_VALID is the lowest number this routine should trust. Numbers + * below this are "noise" and zero is returned. + */ + +#define AVG_OVERHEAD 0 /* It typically takes X.X microseconds */ + /* (Y countdowns) to start/stop the timer. */ + /* This value is in microseconds. */ +#define LEAST_VALID 1 /* Don't trust a clicks value lower than this */ + +benchmark_timer_t benchmark_timer_read( void ) +{ + uint32_t clicks; + uint32_t total; + register uint32_t cycles __asm__ ("R2"); + + /* stop counter */ + __asm__ ("R2 = SYSCFG;"); + __asm__ ("BITCLR(R2,1);"); + __asm__ ("SYSCFG = R2;"); + __asm__ ("R2 = CYCLES;"); + + + clicks = cycles; /* Clock cycles */ + + /* converting to microseconds */ + total = clicks / (CCLK/1000000); + + if ( benchmark_timer_find_average_overhead == 1 ) + return total; /* in XXX microsecond units */ + else { + if ( total < LEAST_VALID ) + return 0; /* below timer resolution */ + /* + * Somehow convert total into microseconds + */ + return (total - AVG_OVERHEAD); + } +} + +void benchmark_timer_disable_subtracting_average_overhead(bool find_flag) +{ + benchmark_timer_find_average_overhead = find_flag; +} diff --git a/bsps/bfin/shared/dev/twi.c b/bsps/bfin/shared/dev/twi.c new file mode 100644 index 0000000000..0d6e6ca0b1 --- /dev/null +++ b/bsps/bfin/shared/dev/twi.c @@ -0,0 +1,253 @@ +/* this is not much more than a shell; it does not do anything useful yet */ + +/* TWI (I2C) driver for Blackfin + * + * Copyright (c) 2008 Kallisti Labs, Los Gatos, CA, USA + * written by Allan Hessenflow <allanh@kallisti.com> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + + +#include <stdlib.h> +#include <rtems.h> + +#include <libcpu/twiRegs.h> +#include <libcpu/twi.h> + + +#ifndef N_BFIN_TWI +#define N_BFIN_TWI 1 +#endif + +#define BFIN_REG16(base, offset) \ + (*((uint16_t volatile *) ((char *)(base) + (offset)))) + + +static struct { + void *base; + rtems_id irqSem; + rtems_id mutex; + bfin_twi_callback_t callback; + void *callbackArg; + bfin_twi_request_t volatile *req; + uint8_t volatile *dataPtr; + int volatile count; + bool volatile masterActive; + rtems_status_code volatile masterResult; + bool volatile slaveActive; +} twi[N_BFIN_TWI]; + + +rtems_status_code bfin_twi_init(int channel, bfin_twi_config_t *config) { + rtems_status_code result; + void *base; + + if (channel < 0 || channel >= N_BFIN_TWI) + return RTEMS_INVALID_NUMBER; + + base = config->base; + twi[channel].base = base; + + result = rtems_semaphore_create(rtems_build_name('t','w','i','s'), + 0, + RTEMS_FIFO | + RTEMS_SIMPLE_BINARY_SEMAPHORE | + RTEMS_NO_INHERIT_PRIORITY | + RTEMS_NO_PRIORITY_CEILING | + RTEMS_LOCAL, + 0, + &twi[channel].irqSem); + result = rtems_semaphore_create(rtems_build_name('t','w','i','m'), + 1, + RTEMS_PRIORITY | + RTEMS_SIMPLE_BINARY_SEMAPHORE | + RTEMS_INHERIT_PRIORITY | + RTEMS_NO_PRIORITY_CEILING | + RTEMS_LOCAL, + 0, + &twi[channel].mutex); + BFIN_REG16(base, TWI_CONTROL_OFFSET) = + (uint16_t) (((config->sclk +9999999) / 10000000) << + TWI_CONTROL_PRESCALE_SHIFT) | + TWI_CONTROL_TWI_ENA; + BFIN_REG16(base, TWI_CLKDIV_OFFSET) = config->fast ? + ((8 << TWI_CLKDIV_CLKHI_SHIFT) | + (17 << TWI_CLKDIV_CLKLOW_SHIFT)) : + ((33 << TWI_CLKDIV_CLKHI_SHIFT) | + (67 << TWI_CLKDIV_CLKLOW_SHIFT)); + BFIN_REG16(base, TWI_SLAVE_CTL_OFFSET) = 0; + BFIN_REG16(base, TWI_MASTER_CTL_OFFSET) = config->fast ? + TWI_MASTER_CTL_FAST : + 0; + BFIN_REG16(base, TWI_SLAVE_ADDR_OFFSET) = (uint16_t) config->slave_address << + TWI_SLAVE_ADDR_SADDR_SHIFT; + BFIN_REG16(base, TWI_MASTER_STAT_OFFSET) = TWI_MASTER_STAT_BUFWRERR | + TWI_MASTER_STAT_BUFRDERR | + TWI_MASTER_STAT_DNAK | + TWI_MASTER_STAT_ANAK | + TWI_MASTER_STAT_LOSTARB; + BFIN_REG16(base, TWI_FIFO_CTL_OFFSET) = TWI_FIFO_CTL_XMTFLUSH | + TWI_FIFO_CTL_RCVFLUSH; + BFIN_REG16(base, TWI_FIFO_CTL_OFFSET) = 0; + BFIN_REG16(base, TWI_INT_STAT_OFFSET) = TWI_INT_STAT_RCVSERV | + TWI_INT_STAT_XMTSERV | + TWI_INT_STAT_MERR | + TWI_INT_STAT_MCOMP | + TWI_INT_STAT_SOVF | + TWI_INT_STAT_SERR | + TWI_INT_STAT_SCOMP | + TWI_INT_STAT_SINIT; + BFIN_REG16(base, TWI_INT_MASK_OFFSET) = TWI_INT_MASK_RCVSERVM | + TWI_INT_MASK_XMTSERVM; + + return result; +} + +rtems_status_code bfin_twi_register_callback(int channel, + bfin_twi_callback_t callback, + void *arg) { + void *base; + int level; + + if (channel < 0 || channel >= N_BFIN_TWI) + return RTEMS_INVALID_NUMBER; + + base = twi[channel].base; + if (callback == NULL) + BFIN_REG16(base, TWI_SLAVE_CTL_OFFSET) = 0; + rtems_interrupt_disable(level); + twi[channel].callback = callback; + twi[channel].callbackArg = arg; + rtems_interrupt_enable(level); + if (callback != NULL) + BFIN_REG16(base, TWI_SLAVE_CTL_OFFSET) = TWI_SLAVE_CTL_GEN | + TWI_SLAVE_CTL_SEN; + + return RTEMS_SUCCESSFUL; +} + +void bfin_twi_isr(int source) { + void *base; + int i; + uint16_t r; + uint16_t stat; + + for (i = 0; i < N_BFIN_TWI; i++) { + base = twi[i].base; + if (base) { + stat = BFIN_REG16(base, TWI_INT_STAT_OFFSET); + if (stat) { + BFIN_REG16(base, TWI_INT_STAT_OFFSET) = stat; + if ((stat & TWI_INT_STAT_SINIT) && !twi[i].slaveActive) { + twi[i].slaveActive = true; + r = BFIN_REG16(base, TWI_FIFO_CTL_OFFSET); + BFIN_REG16(base, TWI_FIFO_CTL_OFFSET) = r | TWI_FIFO_CTL_XMTFLUSH; + BFIN_REG16(base, TWI_FIFO_CTL_OFFSET) = r; + r = BFIN_REG16(base, TWI_SLAVE_CTL_OFFSET); + BFIN_REG16(base, TWI_SLAVE_CTL_OFFSET) = r | TWI_SLAVE_CTL_STDVAL; + } + if (twi[i].slaveActive) { + + + if (stat & (TWI_INT_STAT_SCOMP | TWI_INT_STAT_SERR)) { + + + r = BFIN_REG16(base, TWI_SLAVE_CTL_OFFSET); + BFIN_REG16(base, TWI_SLAVE_CTL_OFFSET) = r & ~TWI_SLAVE_CTL_STDVAL; + twi[i].slaveActive = false; + + + } + } + if (twi[i].masterActive && !twi[i].slaveActive) { + + + if (stat & (TWI_INT_STAT_MCOMP | TWI_INT_STAT_MERR)) { + if (!(stat & TWI_INT_STAT_MERR)) { + + + rtems_semaphore_release(twi[i].irqSem); + + + } else + rtems_semaphore_release(twi[i].irqSem); + } + } + } + } + } +} + +rtems_status_code bfin_twi_request(int channel, uint8_t address, + bfin_twi_request_t *request, + rtems_interval timeout) { + rtems_status_code result; + void *base; + rtems_interrupt_level level; + uint16_t r; + uint16_t masterMode; + + if (channel < 0 || channel >= N_BFIN_TWI) + return RTEMS_INVALID_NUMBER; + result = rtems_semaphore_obtain(twi[channel].mutex, + RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if (result == RTEMS_SUCCESSFUL) { + base = twi[channel].base; + twi[channel].req = request; + + if (request->write) { + twi[channel].dataPtr = request->data; + twi[channel].count = request->count; + } else + twi[channel].count = 0; + + BFIN_REG16(base, TWI_MASTER_ADDR_OFFSET) = (uint16_t) address << + TWI_MASTER_ADDR_MADDR_SHIFT; + masterMode = BFIN_REG16(base, TWI_MASTER_CTL_OFFSET); + masterMode |= (request->count << TWI_MASTER_CTL_DCNT_SHIFT); + if (request->next) + masterMode |= TWI_MASTER_CTL_RSTART; + if (!request->write) + masterMode |= TWI_MASTER_CTL_MDIR; + masterMode |= TWI_MASTER_CTL_MEN; + rtems_interrupt_disable(level); + if (!twi[channel].slaveActive) { + r = BFIN_REG16(base, TWI_FIFO_CTL_OFFSET); + BFIN_REG16(base, TWI_FIFO_CTL_OFFSET) = r | TWI_FIFO_CTL_XMTFLUSH; + BFIN_REG16(base, TWI_FIFO_CTL_OFFSET) = r; + if (request->write) { + while (twi[channel].count && + (BFIN_REG16(base, TWI_FIFO_STAT_OFFSET) & + TWI_FIFO_STAT_XMTSTAT_MASK) != + TWI_FIFO_STAT_XMTSTAT_FULL) { + BFIN_REG16(base, TWI_XMT_DATA8_OFFSET) = + (uint16_t) *twi[channel].dataPtr++; + twi[channel].count--; + } + } + twi[channel].masterActive = true; + BFIN_REG16(base, TWI_MASTER_CTL_OFFSET) = masterMode; + } else { + twi[channel].masterActive = false; + twi[channel].masterResult = -1; /* BISON (code should be equiv to lost arbitration) */ + } + rtems_interrupt_enable(level); + while (result == RTEMS_SUCCESSFUL && twi[channel].masterActive) + result = rtems_semaphore_obtain(twi[channel].irqSem, + RTEMS_WAIT, timeout); + if (result == RTEMS_SUCCESSFUL) + result = twi[channel].masterResult; + else { + /* BISON abort */ + + + + } + rtems_semaphore_release(twi[channel].mutex); + } + return result; +} + diff --git a/bsps/bfin/shared/dev/uart.c b/bsps/bfin/shared/dev/uart.c new file mode 100644 index 0000000000..18a522e121 --- /dev/null +++ b/bsps/bfin/shared/dev/uart.c @@ -0,0 +1,528 @@ +/* UART driver for Blackfin + */ + +/* + * Copyright (c) 2008 Kallisti Labs, Los Gatos, CA, USA + * written by Allan Hessenflow <allanh@kallisti.com> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <rtems.h> +#include <rtems/libio.h> +#include <rtems/termiostypes.h> +#include <termios.h> +#include <stdlib.h> + +#include <libcpu/uartRegs.h> +#include <libcpu/dmaRegs.h> +#include <libcpu/uart.h> + +/* flags */ +#define BFIN_UART_XMIT_BUSY 0x01 + +static bfin_uart_config_t *uartsConfig; + +static int pollRead(int minor) +{ + int c; + uint32_t base; + + 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].uart_useInterrupts && + *((uint16_t volatile *) (base + UART_LSR_OFFSET)) & UART_LSR_DR) + c = *((uint16_t volatile *) (base + UART_RBR_OFFSET)); + else + c = -1; + + return c; +} + +char bfin_uart_poll_read(rtems_device_minor_number minor) +{ + int c; + + do { + c = pollRead(minor); + } while (c == -1); + + return c; +} + +void bfin_uart_poll_write(int minor, char c) +{ + uint32_t base; + + base = uartsConfig->channels[minor].uart_baseAddress; + + while (!(*((uint16_t volatile *) (base + UART_LSR_OFFSET)) & UART_LSR_THRE)) + ; + *(uint16_t volatile *) (base + UART_THR_OFFSET) = c; +} + +/* + * Console Termios Support Entry Points + * + */ + +static ssize_t pollWrite(int minor, const char *buf, size_t len) +{ + size_t count; + for ( count = 0; count < len; count++ ) + bfin_uart_poll_write(minor, *buf++); + + return count; +} + +/** + * 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; + + 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; + */ + + + *(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)); + } + + *(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; + + /** + * 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 :( + */ + } +} + + +/** + * Set the UART attributes. + * @param minor + * @param termios + * @return + */ +static int setAttributes(int minor, const struct termios *termios) +{ + uint32_t base; + int baud; + uint16_t divisor; + uint16_t lcr; + + base = uartsConfig->channels[minor].uart_baseAddress; + switch (termios->c_ospeed) { + case B0: baud = 0; break; + case B50: baud = 50; break; + case B75: baud = 75; break; + case B110: baud = 110; break; + case B134: baud = 134; break; + case B150: baud = 150; break; + case B200: baud = 200; break; + case B300: baud = 300; break; + case B600: baud = 600; break; + case B1200: baud = 1200; break; + case B1800: baud = 1800; break; + case B2400: baud = 2400; break; + case B4800: baud = 4800; break; + case B9600: baud = 9600; break; + case B19200: baud = 19200; break; + case B38400: baud = 38400; break; + case B57600: baud = 57600; break; + case B115200: baud = 115200; break; + case B230400: baud = 230400; break; + case B460800: baud = 460800; break; + default: baud = -1; break; + } + 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; break; + case CS6: lcr = UART_LCR_WLS_6; break; + case CS7: lcr = UART_LCR_WLS_7; break; + default: + case CS8: lcr = UART_LCR_WLS_8; break; + } + switch (termios->c_cflag & (PARENB | PARODD)) { + case PARENB: + lcr |= UART_LCR_PEN | UART_LCR_EPS; + break; + case PARENB | PARODD: + lcr |= UART_LCR_PEN; + break; + default: + break; + } + if (termios->c_cflag & CSTOPB) + lcr |= UART_LCR_STB; + + if (baud > 0) { + divisor = (uint16_t) (uartsConfig->freq / (baud * 16)); + *(uint16_t volatile *) (base + UART_LCR_OFFSET) = lcr | 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) = lcr; + + return 0; +} + +/** + * 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; + + /** + * Sanity Check + */ + if ( + NULL == buf || NULL == channel || NULL == uartsConfig + || minor < 0 || 0 == len + ) { + return 0; + } + + channel = &(uartsConfig->channels[minor]); + + if ( NULL == channel || channel->flags & BFIN_UART_XMIT_BUSY ) { + return 0; + } + + 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; + + return 0; +} + +/** + * This function implements RX ISR + */ +void bfinUart_rxIsr(void *_arg) +{ + /** + * TODO: UART RX ISR implementation. + */ +} + +/** + * 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; + } + + 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); +} + +/** + * 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; + + /** + * Sanity Check + */ + if ( NULL == buf || 0 > minor || NULL == uartsConfig || 0 == len ) { + return 0; + } + + channel = &(uartsConfig->channels[minor]); + + /** + * Sanity Check and check for transmit busy. + */ + if ( NULL == channel || BFIN_UART_XMIT_BUSY & channel->flags ) { + return 0; + } + + 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; + + 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. + */ + } +} + +/** + * Function called during exit + */ +static 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_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 */ + }; + + /** + * 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 */ + pollRead, /* pollRead */ + uart_interruptWrite, /* write */ + setAttributes, /* setAttributes */ + NULL, /* stopRemoteTx */ + NULL, /* startRemoteTx */ + TERMIOS_IRQ_DRIVEN /* outputUsesInterrupts */ + }; + + /** + * 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); + } + + args = arg; + uartsConfig->channels[minor].termios = args->iop->data1; + + 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/bsps/bfin/shared/interrupt.c b/bsps/bfin/shared/interrupt.c new file mode 100644 index 0000000000..7fe1eb19f7 --- /dev/null +++ b/bsps/bfin/shared/interrupt.c @@ -0,0 +1,196 @@ +/* Support for Blackfin interrupt controller + * + * Copyright (c) 2008 Kallisti Labs, Los Gatos, CA, USA + * written by Allan Hessenflow <allanh@kallisti.com> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + + +#include <rtems.h> +#include <rtems/libio.h> + +#include <bsp.h> +#include <libcpu/cecRegs.h> +#include <libcpu/sicRegs.h> +#include <string.h> +#include <libcpu/interrupt.h> + + +static struct { + uint32_t mask; + bfin_isr_t *head; +} vectors[CEC_INTERRUPT_COUNT]; + +static uint32_t globalMask; + + +static rtems_isr interruptHandler(rtems_vector_number vector) { + bfin_isr_t *isr; + uint32_t sourceMask; + + vector -= CEC_INTERRUPT_BASE_VECTOR; + if (vector >= 0 && vector < CEC_INTERRUPT_COUNT) { + isr = vectors[vector].head; + sourceMask = *(uint32_t volatile *) SIC_ISR & + *(uint32_t volatile *) SIC_IMASK; + while (isr) { + if (sourceMask & isr->mask) { + isr->isr(isr->source); + sourceMask = *(uint32_t volatile *) SIC_ISR & + *(uint32_t volatile *) SIC_IMASK; + } + isr = isr->next; + } + } +} + +void bfin_interrupt_init(void) { + int source; + int vector; + uint32_t r; + int i; + int j; + + globalMask = ~(uint32_t) 0; + *(uint32_t volatile *) SIC_IMASK = 0; + memset(vectors, 0, sizeof(vectors)); + /* build mask showing what SIC sources drive each CEC vector */ + source = 0; + for (i = 0; i < SIC_IAR_COUNT; i++) { + r = *(uint32_t volatile *) (SIC_IAR_BASE_ADDRESS + i * SIC_IAR_PITCH); + for (j = 0; j < 8; j++) { + vector = r & 0x0f; + if (vector >= 0 && vector < CEC_INTERRUPT_COUNT) { + if (vectors[vector].mask == 0) + /* install our local handler */ + set_vector(interruptHandler, vector + CEC_INTERRUPT_BASE_VECTOR, 1); + vectors[vector].mask |= (1 << source); + } + r >>= 4; + source++; + } + } +} + +/* modify SIC_IMASK based on ISR list for a particular CEC vector */ +static void setMask(int vector) { + bfin_isr_t *isr; + uint32_t mask; + uint32_t r; + + mask = 0; + isr = vectors[vector].head; + while (isr) { + mask |= isr->mask; + isr = isr->next; + } + r = *(uint32_t volatile *) SIC_IMASK; + r &= ~vectors[vector].mask; + r |= mask; + r &= globalMask; + *(uint32_t volatile *) SIC_IMASK = r; +} + +/* add an ISR to the list for whichever vector it belongs to */ +void bfin_interrupt_register(bfin_isr_t *isr) { + bfin_isr_t *walk; + rtems_interrupt_level isrLevel; + + /* find the appropriate vector */ + for (isr->vector = 0; isr->vector < CEC_INTERRUPT_COUNT; isr->vector++) + if (vectors[isr->vector].mask & (1 << isr->source)) + break; + if (isr->vector < CEC_INTERRUPT_COUNT) { + isr->next = NULL; + isr->mask = 0; + rtems_interrupt_disable(isrLevel); + /* find the current end of the list */ + walk = vectors[isr->vector].head; + while (walk && walk->next) + walk = walk->next; + /* append new isr to list */ + if (walk) + walk->next = isr; + else + vectors[isr->vector].head = isr; + rtems_interrupt_enable(isrLevel); + } else + /* we failed, but make vector a legal value so other calls into + this module with this isr descriptor won't do anything bad */ + isr->vector = 0; +} + +void bfin_interrupt_unregister(bfin_isr_t *isr) { + bfin_isr_t *walk, *prev; + rtems_interrupt_level isrLevel; + + rtems_interrupt_disable(isrLevel); + walk = vectors[isr->vector].head; + prev = NULL; + /* find this isr in our list */ + while (walk && walk != isr) { + prev = walk; + walk = walk->next; + } + if (walk) { + /* if found, remove it */ + if (prev) + prev->next = walk->next; + else + vectors[isr->vector].head = walk->next; + /* fix up SIC_IMASK if necessary */ + setMask(isr->vector); + } + rtems_interrupt_enable(isrLevel); +} + +void bfin_interrupt_enable(bfin_isr_t *isr, bool enable) { + rtems_interrupt_level isrLevel; + + rtems_interrupt_disable(isrLevel); + isr->mask = enable ? (1 << isr->source) : 0; + setMask(isr->vector); + rtems_interrupt_enable(isrLevel); +} + +void bfin_interrupt_enable_all(int source, bool enable) { + rtems_interrupt_level isrLevel; + int vector; + bfin_isr_t *walk; + + for (vector = 0; vector < CEC_INTERRUPT_COUNT; vector++) + if (vectors[vector].mask & (1 << source)) + break; + if (vector < CEC_INTERRUPT_COUNT) { + rtems_interrupt_disable(isrLevel); + walk = vectors[vector].head; + while (walk) { + walk->mask = enable ? (1 << source) : 0; + walk = walk->next; + } + setMask(vector); + rtems_interrupt_enable(isrLevel); + } +} + +void bfin_interrupt_enable_global(int source, bool enable) { + int vector; + rtems_interrupt_level isrLevel; + + for (vector = 0; vector < CEC_INTERRUPT_COUNT; vector++) + if (vectors[vector].mask & (1 << source)) + break; + if (vector < CEC_INTERRUPT_COUNT) { + rtems_interrupt_disable(isrLevel); + if (enable) + globalMask |= 1 << source; + else + globalMask &= ~(1 << source); + setMask(vector); + rtems_interrupt_enable(isrLevel); + } +} + diff --git a/bsps/bfin/shared/mmu.c b/bsps/bfin/shared/mmu.c new file mode 100644 index 0000000000..bf3311b5b4 --- /dev/null +++ b/bsps/bfin/shared/mmu.c @@ -0,0 +1,44 @@ +/* Blackfin MMU Support + * + * Copyright (c) 2008 Kallisti Labs, Los Gatos, CA, USA + * written by Allan Hessenflow <allanh@kallisti.com> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + + +#include <rtems.h> + +#include <libcpu/memoryRegs.h> +#include <libcpu/mmu.h> + +/* NOTE: see notes in mmu.h */ + +void bfin_mmu_init(bfin_mmu_config_t *config) { + intptr_t addr; + intptr_t data; + int i; + + addr = (intptr_t) ICPLB_ADDR0; + data = (intptr_t) ICPLB_DATA0; + for (i = 0; i < sizeof(config->instruction) / sizeof(config->instruction[0]); + i++) { + *(uint32_t volatile *) addr = (uint32_t) config->instruction[i].address; + addr += ICPLB_ADDR_PITCH; + *(uint32_t volatile *) data = config->instruction[i].flags; + data += ICPLB_DATA_PITCH; + } + *(uint32_t volatile *) IMEM_CONTROL |= IMEM_CONTROL_ENICPLB; + addr = (intptr_t) DCPLB_ADDR0; + data = (intptr_t) DCPLB_DATA0; + for (i = 0; i < sizeof(config->data) / sizeof(config->data[0]); i++) { + *(uint32_t volatile *) addr = (uint32_t) config->data[i].address; + addr += DCPLB_ADDR_PITCH; + *(uint32_t volatile *) data = config->data[i].flags; + data += DCPLB_DATA_PITCH; + } + *(uint32_t volatile *) DMEM_CONTROL |= DMEM_CONTROL_ENDCPLB; +} + diff --git a/bsps/bfin/shared/shared.am b/bsps/bfin/shared/shared.am new file mode 100644 index 0000000000..93009c3d16 --- /dev/null +++ b/bsps/bfin/shared/shared.am @@ -0,0 +1,8 @@ +libbsp_a_SOURCES += ../../../../../../bsps/bfin/shared/mmu.c +libbsp_a_SOURCES += ../../../../../../bsps/bfin/shared/dev/clock.c +libbsp_a_SOURCES += ../../../../../../bsps/bfin/shared/dev/rtc.c +libbsp_a_SOURCES += ../../../../../../bsps/bfin/shared/dev/spi.c +libbsp_a_SOURCES += ../../../../../../bsps/bfin/shared/dev/sport.c +libbsp_a_SOURCES += ../../../../../../bsps/bfin/shared/dev/timer.c +libbsp_a_SOURCES += ../../../../../../bsps/bfin/shared/dev/twi.c +libbsp_a_SOURCES += ../../../../../../bsps/bfin/shared/dev/uart.c |