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/shared | |
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/shared')
-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 |
10 files changed, 1706 insertions, 0 deletions
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 |