diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-19 06:28:01 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-20 13:08:32 +0200 |
commit | d7d66d7d4523b904c8ccc6aea3709dc0d5aa5bdc (patch) | |
tree | caa54b4229e86a68c84ab5961af34e087dce5302 /bsps/arm/tms570 | |
parent | bsps/powerpc: Move shared btimer support (diff) | |
download | rtems-d7d66d7d4523b904c8ccc6aea3709dc0d5aa5bdc.tar.bz2 |
bsps: Move console drivers to bsps
This patch is a part of the BSP source reorganization.
Update #3285.
Diffstat (limited to 'bsps/arm/tms570')
-rw-r--r-- | bsps/arm/tms570/console/printk-support.c | 126 | ||||
-rw-r--r-- | bsps/arm/tms570/console/tms570-sci.c | 642 |
2 files changed, 768 insertions, 0 deletions
diff --git a/bsps/arm/tms570/console/printk-support.c b/bsps/arm/tms570/console/printk-support.c new file mode 100644 index 0000000000..529c5dba20 --- /dev/null +++ b/bsps/arm/tms570/console/printk-support.c @@ -0,0 +1,126 @@ +/** + * @file printk-support.c + * + * @ingroup tms570 + * + * @brief definitions of serial line for debugging. + */ + +/* + * Copyright (c) 2014 Premysl Houdek <kom541000@gmail.com> + * + * Google Summer of Code 2014 at + * Czech Technical University in Prague + * Zikova 1903/4 + * 166 36 Praha 6 + * Czech Republic + * + * Based on LPC24xx and LPC1768 BSP + * by embedded brains GmbH and others + * + * 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/bspIo.h> +#include <rtems/sysinit.h> +#include <stdint.h> +#include <string.h> +#include <bsp/tms570-sci.h> +#include <bsp/tms570-sci-driver.h> + +#define TMS570_CONSOLE (&driver_context_table[0]) + +/** + * @brief Puts chars into peripheral + * + * debug functions always use serial dev 0 peripheral + * + * @retval Void + */ +static void tms570_debug_console_putc(char ch) +{ + tms570_sci_context *ctx = TMS570_CONSOLE; + volatile tms570_sci_t *regs = ctx->regs; + rtems_interrupt_level level; + + rtems_interrupt_disable(level); + while ( ( regs->FLR & TMS570_SCI_FLR_TXRDY ) == 0) { + rtems_interrupt_flash(level); + } + regs->TD = ch; + while ( ( regs->FLR & TMS570_SCI_FLR_TX_EMPTY ) == 0) { + rtems_interrupt_flash(level); + } + rtems_interrupt_enable(level); +} + +/** + * @brief debug console output + * + * debug functions always use serial dev 0 peripheral + * + * @retval Void + */ +static void tms570_debug_console_out(char c) +{ + tms570_debug_console_putc(c); +} + +static void tms570_debug_console_init(void) +{ + tms570_sci_context *ctx = TMS570_CONSOLE; + struct termios term; + + tms570_sci_initialize(ctx); + memset(&term, 0, sizeof(term)); + term.c_cflag = B115200; + tms570_sci_set_attributes(&ctx->base, &term); + BSP_output_char = tms570_debug_console_out; +} + +static void tms570_debug_console_early_init(char c) +{ + tms570_debug_console_init(); + tms570_debug_console_out(c); +} + +/** + * @brief debug console input + * + * debug functions always use serial dev 0 peripheral + * + * @retval x Read char + * @retval -1 No input character available + */ +static int tms570_debug_console_in( void ) +{ + tms570_sci_context *ctx = TMS570_CONSOLE; + volatile tms570_sci_t *regs = ctx->regs; + rtems_interrupt_level level; + int c; + + rtems_interrupt_disable(level); + + if ( regs->FLR & TMS570_SCI_FLR_RXRDY ) { + c = (unsigned char) regs->RD; + } else { + c = -1; + } + + rtems_interrupt_enable(level); + + return c; +} + +BSP_output_char_function_type BSP_output_char = + tms570_debug_console_early_init; + +BSP_polling_getchar_function_type BSP_poll_char = tms570_debug_console_in; + +RTEMS_SYSINIT_ITEM( + tms570_debug_console_init, + RTEMS_SYSINIT_BSP_START, + RTEMS_SYSINIT_ORDER_LAST +); diff --git a/bsps/arm/tms570/console/tms570-sci.c b/bsps/arm/tms570/console/tms570-sci.c new file mode 100644 index 0000000000..48986e9a2e --- /dev/null +++ b/bsps/arm/tms570/console/tms570-sci.c @@ -0,0 +1,642 @@ +/** + * @file tms570-sci.c + * + * @ingroup tms570 + * + * @brief Serial communication interface (SCI) functions definitions. + */ + +/* + * Copyright (c) 2014 Premysl Houdek <kom541000@gmail.com> + * + * Google Summer of Code 2014 at + * Czech Technical University in Prague + * Zikova 1903/4 + * 166 36 Praha 6 + * Czech Republic + * + * Based on LPC24xx and LPC1768 BSP + * by embedded brains GmbH and others + * + * 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 <bspopts.h> +#include <termios.h> +#include <rtems/termiostypes.h> +#include <bsp/tms570-sci.h> +#include <bsp/tms570-sci-driver.h> +#include <rtems/console.h> +#include <bsp.h> +#include <bsp/fatal.h> +#include <bsp/irq.h> + +#define TMS570_SCI_BUFFER_SIZE 1 + +/** + * @brief Table including all serial drivers + * + * Definitions of all serial drivers + */ +tms570_sci_context driver_context_table[] = { + { + .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("TMS570 SCI1"), + .device_name = "/dev/console", + /* TMS570 UART peripheral use subset of LIN registers which are equivalent + * to SCI ones + */ + .regs = (volatile tms570_sci_t *) &TMS570_LIN, + .irq = TMS570_IRQ_SCI_LEVEL_0, + }, + { + .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("TMS570 SCI2"), + .device_name = "/dev/ttyS1", + .regs = &TMS570_SCI, + .irq = TMS570_IRQ_SCI2_LEVEL_0, + } +}; + +void tms570_sci_initialize(tms570_sci_context *ctx) +{ + uint32_t rx_pin = 1 << 1; + uint32_t tx_pin = 1 << 2; + + /* Resec SCI peripheral */ + ctx->regs->GCR0 = TMS570_SCI_GCR0_RESET * 0; + ctx->regs->GCR0 = TMS570_SCI_GCR0_RESET * 1; + + /* Clear all interrupt sources */ + ctx->regs->CLEARINT = 0xffffffff; + + /* Map all interrupts to SCI INT0 line */ + ctx->regs->CLEARINTLVL = 0xffffffff; + + ctx->regs->GCR1 = TMS570_SCI_GCR1_TXENA * 0 | + TMS570_SCI_GCR1_RXENA * 0 | + TMS570_SCI_GCR1_CONT * 0 | /* continue operation when debugged */ + TMS570_SCI_GCR1_LOOP_BACK * 0 | + TMS570_SCI_GCR1_POWERDOWN * 0 | + TMS570_SCI_GCR1_SLEEP * 0 | + TMS570_SCI_GCR1_SWnRST * 0 | /* reset state */ + TMS570_SCI_GCR1_CLOCK * 1 | /* internal clock */ + TMS570_SCI_GCR1_TIMING_MODE * 1 | + TMS570_SCI_GCR1_COMM_MODE * 0; + + /* Setup connection of SCI peripheral Rx and Tx pins */ + ctx->regs->PIO0 = rx_pin * 1 | tx_pin * 1; /* Rx and Tx pins are not GPIO */ + ctx->regs->PIO3 = rx_pin * 0 | tx_pin * 0; /* Default output low */ + ctx->regs->PIO1 = rx_pin * 0 | tx_pin * 0; /* Input when not used by SCI */ + ctx->regs->PIO6 = rx_pin * 0 | tx_pin * 0; /* No open drain */ + ctx->regs->PIO7 = rx_pin * 0 | tx_pin * 0; /* Pull-up/down enabled */ + ctx->regs->PIO8 = rx_pin * 1 | tx_pin * 1; /* Select pull-up */ + + /* Bring device out of software reset */ + ctx->regs->GCR1 |= TMS570_SCI_GCR1_SWnRST; +} + +/** + * @brief Serial drivers init function + * + * Initialize all serial drivers specified in driver_context_table + * + * @param[in] major + * @param[in] minor + * @param[in] arg + * @retval RTEMS_SUCCESSFUL Initialization completed + */ +rtems_device_driver console_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + rtems_status_code sc; +#if CONSOLE_USE_INTERRUPTS + const rtems_termios_device_handler *handler = &tms570_sci_handler_interrupt; +#else + const rtems_termios_device_handler *handler = &tms570_sci_handler_polled; +#endif + + /* + * Initialize the Termios infrastructure. If Termios has already + * been initialized by another device driver, then this call will + * have no effect. + */ + rtems_termios_initialize(); + + /* Initialize each device */ + for ( + minor = 0; + minor < RTEMS_ARRAY_SIZE(driver_context_table); + ++minor + ) { + tms570_sci_context *ctx = &driver_context_table[minor]; + + tms570_sci_initialize(ctx); + + /* + * Install this device in the file system and Termios. In order + * to use the console (i.e. being able to do printf, scanf etc. + * on stdin, stdout and stderr), one device must be registered as + * "/dev/console" (CONSOLE_DEVICE_NAME). + */ + sc = rtems_termios_device_install( + ctx->device_name, + handler, + NULL, + &ctx->base + ); + if ( sc != RTEMS_SUCCESSFUL ) { + bsp_fatal(BSP_FATAL_CONSOLE_NO_DEV); + } + } + return RTEMS_SUCCESSFUL; +} + +/** + * @brief Reads chars from HW + * + * Reads chars from HW peripheral specified in driver context. + * TMS570 does not have HW buffer for serial line so this function can + * return only 0 or 1 char + * + * @param[in] ctx context of the driver + * @param[out] buf read data buffer + * @param[in] N size of buffer + * @retval x Number of read chars from peripherals + */ +static int tms570_sci_read_received_chars( + tms570_sci_context * ctx, + char * buf, + int N) +{ + if ( N < 1 ) { + return 0; + } + if ( ctx->regs->RD != 0 ) { + buf[0] = ctx->regs->RD; + return 1; + } + return 0; +} + +/** + * @brief Enables RX interrupt + * + * Enables RX interrupt source of SCI peripheral + * specified in the driver context. + * + * @param[in] ctx context of the driver + * @retval Void + */ +static void tms570_sci_enable_interrupts(tms570_sci_context * ctx) +{ + ctx->regs->SETINT = TMS570_SCI_SETINT_SET_RX_INT; +} + +/** + * @brief Disables RX interrupt + * + * Disables RX interrupt source of SCI peripheral specified in the driver + * context. + * + * @param[in] ctx context of the driver + * @retval Void + */ +static void tms570_sci_disable_interrupts(tms570_sci_context * ctx) +{ + ctx->regs->CLEARINT = TMS570_SCI_CLEARINT_CLR_RX_INT; +} + +/** + * @brief Check whether driver has put char in HW + * + * Check whether driver has put char in HW. + * This information is read from the driver context not from a peripheral. + * TMS570 does not have write data buffer asociated with SCI + * so the return can be only 0 or 1. + * + * @param[in] ctx context of the driver + * @retval x + */ +static int tms570_sci_transmitted_chars(tms570_sci_context * ctx) +{ + int ret; + + ret = ctx->tx_chars_in_hw; + if ( ret == 1 ) { + ctx->tx_chars_in_hw = 0; + return 1; + } + return ret; +} + +/** + * @brief Set attributes of the HW peripheral + * + * Sets attributes of the HW peripheral (parity, baud rate, etc.) + * + * @param[in] base context of the driver + * @param[in] t termios driver + * @retval true peripheral setting is changed + */ +bool tms570_sci_set_attributes( + rtems_termios_device_context *base, + const struct termios *t +) +{ + tms570_sci_context *ctx = (tms570_sci_context *) base; + rtems_interrupt_lock_context lock_context; + int32_t bauddiv; + int32_t baudrate; + uint32_t flr_tx_ready = TMS570_SCI_FLR_TX_EMPTY; + /* + * Test for TMS570_SCI_FLR_TXRDY is not necessary + * because both SCITD and SCITXSHF has to be empty + * to TX_EMPTY be asserted. But there is no interrupt + * option for TX_EMPTY. Polling is used isntead. + */ + + /* Baud rate */ + baudrate = rtems_termios_baud_to_number(cfgetospeed(t)); + + rtems_termios_device_lock_acquire(base, &lock_context); + + while ( (ctx->regs->GCR1 & TMS570_SCI_GCR1_TXENA) && + (ctx->regs->FLR & flr_tx_ready) != flr_tx_ready) { + /* + * There are pending characters in the hardware, + * change in the middle of the character Tx leads + * to disturb of the character and SCI engine + */ + rtems_interval tw; + + rtems_termios_device_lock_release(base, &lock_context); + + tw = rtems_clock_get_ticks_per_second(); + tw = tw * 5 / baudrate + 1; + rtems_task_wake_after( tw ); + + rtems_termios_device_lock_acquire(base, &lock_context); + } + + ctx->regs->GCR1 &= ~( TMS570_SCI_GCR1_SWnRST | TMS570_SCI_GCR1_TXENA | + TMS570_SCI_GCR1_RXENA ); + + ctx->regs->GCR1 &= ~TMS570_SCI_GCR1_STOP; /*one stop bit*/ + ctx->regs->FORMAT = TMS570_SCI_FORMAT_CHAR(0x7); + + switch ( t->c_cflag & ( PARENB|PARODD ) ) { + case ( PARENB|PARODD ): + /* Odd parity */ + ctx->regs->GCR1 &= ~TMS570_SCI_GCR1_PARITY; + ctx->regs->GCR1 |= TMS570_SCI_GCR1_PARITY_ENA; + break; + + case PARENB: + /* Even parity */ + ctx->regs->GCR1 |= TMS570_SCI_GCR1_PARITY; + ctx->regs->GCR1 |= TMS570_SCI_GCR1_PARITY_ENA; + break; + + default: + case 0: + case PARODD: + /* No Parity */ + ctx->regs->GCR1 &= ~TMS570_SCI_GCR1_PARITY_ENA; + } + + /* Apply baudrate to the hardware */ + baudrate *= 2 * 16; + bauddiv = (BSP_PLL_OUT_CLOCK + baudrate / 2) / baudrate; + ctx->regs->BRS = bauddiv; + + ctx->regs->GCR1 |= TMS570_SCI_GCR1_SWnRST | TMS570_SCI_GCR1_TXENA | + TMS570_SCI_GCR1_RXENA; + + rtems_termios_device_lock_release(base, &lock_context); + + return true; +} + +/** + * @brief sci interrupt handler + * + * Handler checks which interrupt occured and provides nessesary maintenance + * dequeue characters in termios driver whether character is send succesfully + * enqueue characters in termios driver whether character is recieved + * + * @param[in] arg rtems_termios_tty + * @retval Void + */ +static void tms570_sci_interrupt_handler(void * arg) +{ + rtems_termios_tty *tty = arg; + tms570_sci_context *ctx = rtems_termios_get_device_context(tty); + char buf[TMS570_SCI_BUFFER_SIZE]; + size_t n; + + /* + * Check if we have received something. + */ + if ( (ctx->regs->FLR & TMS570_SCI_FLR_RXRDY ) == TMS570_SCI_FLR_RXRDY ) { + n = tms570_sci_read_received_chars(ctx, buf, TMS570_SCI_BUFFER_SIZE); + if ( n > 0 ) { + /* Hand the data over to the Termios infrastructure */ + rtems_termios_enqueue_raw_characters(tty, buf, n); + } + } + /* + * Check if we have something transmitted. + */ + if ( (ctx->regs->FLR & TMS570_SCI_FLR_TXRDY ) == TMS570_SCI_FLR_TXRDY ) { + n = tms570_sci_transmitted_chars(ctx); + if ( n > 0 ) { + /* + * Notify Termios that we have transmitted some characters. It + * will call now the interrupt write function if more characters + * are ready for transmission. + */ + rtems_termios_dequeue_characters(tty, n); + } + } +} + +/** + * @brief sci write function called from interrupt + * + * Nonblocking write function. Writes characters to HW peripheral + * TMS570 does not have write data buffer asociated with SCI + * so only one character can be written. + * + * @param[in] base context of the driver + * @param[in] buf buffer of characters pending to send + * @param[in] len size of the buffer + * @retval Void + */ +static void tms570_sci_interrupt_write( + rtems_termios_device_context *base, + const char *buf, + size_t len +) +{ + tms570_sci_context *ctx = (tms570_sci_context *) base; + + if ( len > 0 ) { + /* start UART TX, this will result in an interrupt when done */ + ctx->regs->TD = *buf; + /* character written - raise count*/ + ctx->tx_chars_in_hw = 1; + /* Enable TX interrupt (interrupt is edge-triggered) */ + ctx->regs->SETINT = (1<<8); + + } else { + /* No more to send, disable TX interrupts */ + ctx->regs->CLEARINT = (1<<8); + /* Tell close that we sent everything */ + } +} + +/** + * @brief sci write function + * + * Blocking write function. Waits until HW peripheral is ready and then writes + * character to HW peripheral. Writes all characters in the buffer. + * + * @param[in] base context of the driver + * @param[in] buf buffer of characters pending to send + * @param[in] len size of the buffer + * @retval Void + */ +static void tms570_sci_poll_write( + rtems_termios_device_context *base, + const char *buf, + size_t n +) +{ + tms570_sci_context *ctx = (tms570_sci_context *) base; + size_t i; + + /* Write */ + + for ( i = 0; i < n; ++i ) { + while ( (ctx->regs->FLR & TMS570_SCI_FLR_TX_EMPTY ) == 0) { + ; + } + ctx->regs->TD = buf[i]; + } +} + +/** + * @brief See if there is recieved charakter to read + * + * read the RX flag from peripheral specified in context + * + * @param[in] ctx context of the driver + * @retval 0 No character to read + * @retval x Character ready to read + */ +static int TMS570_sci_can_read_char( + tms570_sci_context * ctx +) +{ + return ctx->regs->FLR & TMS570_SCI_FLR_RXRDY; +} + +/** + * @brief reads character from peripheral + * + * reads the recieved character from peripheral specified in context + * + * @param[in] ctx context of the driver + * @retval x Character + */ +static char TMS570_sci_read_char( + tms570_sci_context * ctx +) +{ + return ctx->regs->RD; +} + +/** + * @brief sci read function + * + * check if there is recieved character to be read and reads it. + * + * @param[in] base context of the driver + * @retval -1 No character to be read + * @retval x Read character + */ +static int tms570_sci_poll_read(rtems_termios_device_context *base) +{ + tms570_sci_context *ctx = (tms570_sci_context *) base; + + /* Check if a character is available */ + if ( TMS570_sci_can_read_char(ctx) ) { + return TMS570_sci_read_char(ctx); + } else { + return -1; + } +} + +/** + * @brief initialization of the driver + * + * initialization of the HW peripheral specified in contex of the driver. + * This function is called only once when opening the driver. + * + * @param[in] tty Termios control + * @param[in] ctx context of the driver + * @param[in] term Termios attributes + * @param[in] args + * @retval false Error occured during initialization + * @retval true Driver is open and ready + */ +static bool tms570_sci_poll_first_open( + rtems_termios_tty *tty, + rtems_termios_device_context *ctx, + struct termios *term, + rtems_libio_open_close_args_t *args +) +{ + bool ok; + + rtems_termios_set_best_baud(term, TMS570_SCI_BAUD_RATE); + ok = tms570_sci_set_attributes(ctx, term); + if ( !ok ) { + return false; + } + return true; +} + +/** + * @brief initialization of the interrupt driven driver + * + * calls tms570_sci_poll_first_open function. + * install and enables interrupts. + * + * @param[in] tty Termios control + * @param[in] base context of the driver + * @param[in] args + * @retval false Error occured during initialization + * @retval true Driver is open and ready + */ +static bool tms570_sci_interrupt_first_open( + rtems_termios_tty *tty, + rtems_termios_device_context *base, + struct termios *term, + rtems_libio_open_close_args_t *args +) +{ + tms570_sci_context *ctx = (tms570_sci_context *) base; + rtems_status_code sc; + bool ret; + + ret = tms570_sci_poll_first_open(tty, base, term, args); + if ( ret == false ) { + return false; + } + + /* Register Interrupt handler */ + sc = rtems_interrupt_handler_install(ctx->irq, + ctx->device_name, + RTEMS_INTERRUPT_SHARED, + tms570_sci_interrupt_handler, + tty + ); + if ( sc != RTEMS_SUCCESSFUL ) { + return false; + } + tms570_sci_enable_interrupts(ctx); + return true; +} + +/** + * @brief closes sci peripheral + * + * @param[in] tty Termios control + * @param[in] base context of the driver + * @param[in] args + * @retval false Error occured during initialization + * @retval true Driver is open and ready + */ +static void tms570_sci_poll_last_close( + rtems_termios_tty *tty, + rtems_termios_device_context *base, + rtems_libio_open_close_args_t *args +) +{ + ; +} + +/** + * @brief closes sci peripheral of interrupt driven driver + * + * calls tms570_sci_poll_last_close and disables interrupts + * + * @param[in] tty Termios control + * @param[in] base context of the driver + * @param[in] args + * @retval false Error occured during initialization + * @retval true Driver is open and ready + */ +static void tms570_sci_interrupt_last_close( + rtems_termios_tty *tty, + rtems_termios_device_context *base, + rtems_libio_open_close_args_t *args +) +{ + tms570_sci_context *ctx = (tms570_sci_context *) base; + rtems_interrupt_lock_context lock_context; + rtems_interval tw; + int32_t baudrate; + + /* Turn off RX interrupts */ + rtems_termios_device_lock_acquire(base, &lock_context); + tms570_sci_disable_interrupts(ctx); + rtems_termios_device_lock_release(base, &lock_context); + + tw = rtems_clock_get_ticks_per_second(); + baudrate = rtems_termios_baud_to_number(cfgetospeed(&tty->termios)); + tw = tw * 10 / baudrate + 1; + while ( ( ctx->regs->FLR & TMS570_SCI_FLR_TX_EMPTY ) == 0 ) { + rtems_task_wake_after(tw); + } + + /* uninstall ISR */ + rtems_interrupt_handler_remove(ctx->irq, tms570_sci_interrupt_handler, tty); + + tms570_sci_poll_last_close(tty, base, args); +} + +/** + * @brief Struct containing definitions of polled driver functions. + * + * Encapsulates polled driver functions. + * Use of this table is determited by not defining TMS570_USE_INTERRUPTS + */ +const rtems_termios_device_handler tms570_sci_handler_polled = { + .first_open = tms570_sci_poll_first_open, + .last_close = tms570_sci_poll_last_close, + .poll_read = tms570_sci_poll_read, + .write = tms570_sci_poll_write, + .set_attributes = tms570_sci_set_attributes, + .mode = TERMIOS_POLLED +}; + +/** + * @brief Struct containing definitions of interrupt driven driver functions. + * + * Encapsulates interrupt driven driver functions. + * Use of this table is determited by defining TMS570_USE_INTERRUPTS + */ +const rtems_termios_device_handler tms570_sci_handler_interrupt = { + .first_open = tms570_sci_interrupt_first_open, + .last_close = tms570_sci_interrupt_last_close, + .poll_read = NULL, + .write = tms570_sci_interrupt_write, + .set_attributes = tms570_sci_set_attributes, + .mode = TERMIOS_IRQ_DRIVEN +}; |