bsp/bfin: Move libcpu content to bsps

This patch is a part of the BSP source reorganization.

Update #3285.
This commit is contained in:
Sebastian Huber
2018-03-21 16:38:43 +01:00
parent 96400050ed
commit e2bd1f653a
21 changed files with 15 additions and 157 deletions

View File

@@ -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;
}

258
bsps/bfin/shared/dev/rtc.c Normal file
View File

@@ -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;
}

240
bsps/bfin/shared/dev/spi.c Normal file
View File

@@ -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;
}

View File

@@ -0,0 +1,2 @@
/* placeholder */

View File

@@ -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;
}

253
bsps/bfin/shared/dev/twi.c Normal file
View File

@@ -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;
}

528
bsps/bfin/shared/dev/uart.c Normal file
View File

@@ -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;
}