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/xilinx-zynq | |
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/xilinx-zynq')
-rw-r--r-- | bsps/arm/xilinx-zynq/console/console-config.c | 62 | ||||
-rw-r--r-- | bsps/arm/xilinx-zynq/console/debug-console.c | 64 | ||||
-rw-r--r-- | bsps/arm/xilinx-zynq/console/zynq-uart.c | 315 |
3 files changed, 441 insertions, 0 deletions
diff --git a/bsps/arm/xilinx-zynq/console/console-config.c b/bsps/arm/xilinx-zynq/console/console-config.c new file mode 100644 index 0000000000..ce7da2f114 --- /dev/null +++ b/bsps/arm/xilinx-zynq/console/console-config.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2013, 2017 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * <info@embedded-brains.de> + * + * 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/console.h> +#include <rtems/bspIo.h> + +#include <bsp/irq.h> +#include <bsp/zynq-uart.h> + +#include <bspopts.h> + +zynq_uart_context zynq_uart_instances[2] = { + { + .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER( "Zynq UART 0" ), + .regs = (volatile struct zynq_uart *) 0xe0000000, + .irq = ZYNQ_IRQ_UART_0 + }, { + .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER( "Zynq UART 1" ), + .regs = (volatile struct zynq_uart *) 0xe0001000, + .irq = ZYNQ_IRQ_UART_1 + } +}; + +rtems_status_code console_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *arg +) +{ + size_t i; + + rtems_termios_initialize(); + + for (i = 0; i < RTEMS_ARRAY_SIZE(zynq_uart_instances); ++i) { + char uart[] = "/dev/ttySX"; + + uart[sizeof(uart) - 2] = (char) ('0' + i); + rtems_termios_device_install( + &uart[0], + &zynq_uart_handler, + NULL, + &zynq_uart_instances[i].base + ); + + if (i == BSP_CONSOLE_MINOR) { + link(&uart[0], CONSOLE_DEVICE_NAME); + } + } + + return RTEMS_SUCCESSFUL; +} diff --git a/bsps/arm/xilinx-zynq/console/debug-console.c b/bsps/arm/xilinx-zynq/console/debug-console.c new file mode 100644 index 0000000000..38c0050e90 --- /dev/null +++ b/bsps/arm/xilinx-zynq/console/debug-console.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2013, 2017 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * <rtems@embedded-brains.de> + * + * 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 <bsp/zynq-uart.h> + +#include <bspopts.h> + +static void zynq_debug_console_out(char c) +{ + rtems_termios_device_context *base = + &zynq_uart_instances[BSP_CONSOLE_MINOR].base; + + zynq_uart_write_polled(base, c); +} + +static void zynq_debug_console_init(void) +{ + rtems_termios_device_context *base = + &zynq_uart_instances[BSP_CONSOLE_MINOR].base; + + zynq_uart_initialize(base); + BSP_output_char = zynq_debug_console_out; +} + +static void zynq_debug_console_early_init(char c) +{ + rtems_termios_device_context *base = + &zynq_uart_instances[BSP_CONSOLE_MINOR].base; + + zynq_uart_initialize(base); + zynq_debug_console_out(c); +} + +static int zynq_debug_console_in(void) +{ + rtems_termios_device_context *base = + &zynq_uart_instances[BSP_CONSOLE_MINOR].base; + + return zynq_uart_read_polled(base); +} + +BSP_output_char_function_type BSP_output_char = zynq_debug_console_early_init; + +BSP_polling_getchar_function_type BSP_poll_char = zynq_debug_console_in; + +RTEMS_SYSINIT_ITEM( + zynq_debug_console_init, + RTEMS_SYSINIT_BSP_START, + RTEMS_SYSINIT_ORDER_LAST +); diff --git a/bsps/arm/xilinx-zynq/console/zynq-uart.c b/bsps/arm/xilinx-zynq/console/zynq-uart.c new file mode 100644 index 0000000000..fa91f3f46e --- /dev/null +++ b/bsps/arm/xilinx-zynq/console/zynq-uart.c @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2013, 2017 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * <info@embedded-brains.de> + * + * 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 <bsp/zynq-uart.h> +#include <bsp/zynq-uart-regs.h> +#include <bsp/irq.h> + +#include <bspopts.h> + +/* + * Make weak and let the user override. + */ +uint32_t zynq_uart_input_clock(void) __attribute__ ((weak)); + +uint32_t zynq_uart_input_clock(void) +{ + return ZYNQ_CLOCK_UART; +} + +static int zynq_cal_baud_rate(uint32_t baudrate, + uint32_t* brgr, + uint32_t* bauddiv, + uint32_t modereg) +{ + uint32_t brgr_value; /* Calculated value for baud rate generator */ + uint32_t calcbaudrate; /* Calculated baud rate */ + uint32_t bauderror; /* Diff between calculated and requested baud rate */ + uint32_t best_error = 0xFFFFFFFF; + uint32_t percenterror; + uint32_t bdiv; + uint32_t inputclk = zynq_uart_input_clock(); + + /* + * Make sure the baud rate is not impossilby large. + * Fastest possible baud rate is Input Clock / 2. + */ + if ((baudrate * 2) > inputclk) { + return -1; + } + /* + * Check whether the input clock is divided by 8 + */ + if(modereg & ZYNQ_UART_MODE_CLKS) { + inputclk = inputclk / 8; + } + + /* + * Determine the Baud divider. It can be 4to 254. + * Loop through all possible combinations + */ + for (bdiv = 4; bdiv < 255; bdiv++) { + + /* + * Calculate the value for BRGR register + */ + brgr_value = inputclk / (baudrate * (bdiv + 1)); + + /* + * Calculate the baud rate from the BRGR value + */ + calcbaudrate = inputclk/ (brgr_value * (bdiv + 1)); + + /* + * Avoid unsigned integer underflow + */ + if (baudrate > calcbaudrate) { + bauderror = baudrate - calcbaudrate; + } + else { + bauderror = calcbaudrate - baudrate; + } + + /* + * Find the calculated baud rate closest to requested baud rate. + */ + if (best_error > bauderror) { + *brgr = brgr_value; + *bauddiv = bdiv; + best_error = bauderror; + } + } + + /* + * Make sure the best error is not too large. + */ + percenterror = (best_error * 100) / baudrate; +#define XUARTPS_MAX_BAUD_ERROR_RATE 3 /* max % error allowed */ + if (XUARTPS_MAX_BAUD_ERROR_RATE < percenterror) { + return -1; + } + + return 0; +} + +void zynq_uart_initialize(rtems_termios_device_context *base) +{ + zynq_uart_context *ctx = (zynq_uart_context *) base; + volatile zynq_uart *regs = ctx->regs; + uint32_t brgr = 0x3e; + uint32_t bauddiv = 0x6; + + zynq_cal_baud_rate(ZYNQ_UART_DEFAULT_BAUD, &brgr, &bauddiv, regs->mode); + + regs->control &= ~(ZYNQ_UART_CONTROL_RXEN | ZYNQ_UART_CONTROL_TXEN); + regs->control = ZYNQ_UART_CONTROL_RXDIS + | ZYNQ_UART_CONTROL_TXDIS + | ZYNQ_UART_CONTROL_RXRES + | ZYNQ_UART_CONTROL_TXRES; + regs->mode = ZYNQ_UART_MODE_CHMODE(ZYNQ_UART_MODE_CHMODE_NORMAL) + | ZYNQ_UART_MODE_PAR(ZYNQ_UART_MODE_PAR_NONE) + | ZYNQ_UART_MODE_CHRL(ZYNQ_UART_MODE_CHRL_8); + regs->baud_rate_gen = ZYNQ_UART_BAUD_RATE_GEN_CD(brgr); + regs->baud_rate_div = ZYNQ_UART_BAUD_RATE_DIV_BDIV(bauddiv); + regs->rx_fifo_trg_lvl = ZYNQ_UART_RX_FIFO_TRG_LVL_RTRIG(0); + regs->rx_timeout = ZYNQ_UART_RX_TIMEOUT_RTO(0); + regs->control = ZYNQ_UART_CONTROL_RXEN + | ZYNQ_UART_CONTROL_TXEN + | ZYNQ_UART_CONTROL_RSTTO; +} + +#ifdef ZYNQ_CONSOLE_USE_INTERRUPTS +static void zynq_uart_interrupt(void *arg) +{ + rtems_termios_tty *tty = arg; + zynq_uart_context *ctx = rtems_termios_get_device_context(tty); + volatile zynq_uart *regs = ctx->regs; + uint32_t channel_sts; + + if ((regs->irq_sts & (ZYNQ_UART_TIMEOUT | ZYNQ_UART_RTRIG)) != 0) { + regs->irq_sts = ZYNQ_UART_TIMEOUT | ZYNQ_UART_RTRIG; + + do { + char c = (char) ZYNQ_UART_TX_RX_FIFO_FIFO_GET(regs->tx_rx_fifo); + + rtems_termios_enqueue_raw_characters(tty, &c, 1); + + channel_sts = regs->channel_sts; + } while ((channel_sts & ZYNQ_UART_CHANNEL_STS_REMPTY) == 0); + } else { + channel_sts = regs->channel_sts; + } + + if (ctx->transmitting && (channel_sts & ZYNQ_UART_CHANNEL_STS_TEMPTY) != 0) { + rtems_termios_dequeue_characters(tty, 1); + } +} +#endif + +static bool zynq_uart_first_open( + rtems_termios_tty *tty, + rtems_termios_device_context *base, + struct termios *term, + rtems_libio_open_close_args_t *args +) +{ +#ifdef ZYNQ_CONSOLE_USE_INTERRUPTS + zynq_uart_context *ctx = (zynq_uart_context *) base; + volatile zynq_uart *regs = ctx->regs; + rtems_status_code sc; +#endif + + rtems_termios_set_initial_baud(tty, ZYNQ_UART_DEFAULT_BAUD); + zynq_uart_initialize(base); + +#ifdef ZYNQ_CONSOLE_USE_INTERRUPTS + regs->rx_timeout = 32; + regs->rx_fifo_trg_lvl = ZYNQ_UART_FIFO_DEPTH / 2; + regs->irq_dis = 0xffffffff; + regs->irq_sts = 0xffffffff; + regs->irq_en = ZYNQ_UART_RTRIG | ZYNQ_UART_TIMEOUT; + sc = rtems_interrupt_handler_install( + ctx->irq, + "UART", + RTEMS_INTERRUPT_SHARED, + zynq_uart_interrupt, + tty + ); + if (sc != RTEMS_SUCCESSFUL) { + return false; + } +#endif + + return true; +} + +#ifdef ZYNQ_CONSOLE_USE_INTERRUPTS +static void zynq_uart_last_close( + rtems_termios_tty *tty, + rtems_termios_device_context *base, + rtems_libio_open_close_args_t *args +) +{ + zynq_uart_context *ctx = (zynq_uart_context *) base; + + rtems_interrupt_handler_remove(ctx->irq, zynq_uart_interrupt, tty); +} +#endif + +int zynq_uart_read_polled(rtems_termios_device_context *base) +{ + zynq_uart_context *ctx = (zynq_uart_context *) base; + volatile zynq_uart *regs = ctx->regs; + + if ((regs->channel_sts & ZYNQ_UART_CHANNEL_STS_REMPTY) != 0) { + return -1; + } else { + return ZYNQ_UART_TX_RX_FIFO_FIFO_GET(regs->tx_rx_fifo); + } +} + +void zynq_uart_write_polled( + rtems_termios_device_context *base, + char c +) +{ + zynq_uart_context *ctx = (zynq_uart_context *) base; + volatile zynq_uart *regs = ctx->regs; + + while ((regs->channel_sts & ZYNQ_UART_CHANNEL_STS_TFUL) != 0) { + /* Wait */ + } + + regs->tx_rx_fifo = ZYNQ_UART_TX_RX_FIFO_FIFO(c); +} + +static void zynq_uart_write_support( + rtems_termios_device_context *base, + const char *buf, + size_t len +) +{ +#ifdef ZYNQ_CONSOLE_USE_INTERRUPTS + zynq_uart_context *ctx = (zynq_uart_context *) base; + volatile zynq_uart *regs = ctx->regs; + + if (len > 0) { + ctx->transmitting = true; + regs->irq_sts = ZYNQ_UART_TEMPTY; + regs->irq_en = ZYNQ_UART_TEMPTY; + regs->tx_rx_fifo = ZYNQ_UART_TX_RX_FIFO_FIFO(buf[0]); + } else { + ctx->transmitting = false; + regs->irq_dis = ZYNQ_UART_TEMPTY; + } +#else + ssize_t i; + + for (i = 0; i < len; ++i) { + zynq_uart_write_polled(base, buf[i]); + } +#endif +} + +static bool zynq_uart_set_attributes( + rtems_termios_device_context *context, + const struct termios *term +) +{ +#if 0 + volatile zynq_uart *regs = zynq_uart_get_regs(minor); + uint32_t brgr = 0; + uint32_t bauddiv = 0; + int rc; + + rc = zynq_cal_baud_rate(115200, &brgr, &bauddiv, regs->mode); + if (rc != 0) + return rc; + + regs->control &= ~(ZYNQ_UART_CONTROL_RXEN | ZYNQ_UART_CONTROL_TXEN); + regs->baud_rate_gen = ZYNQ_UART_BAUD_RATE_GEN_CD(brgr); + regs->baud_rate_div = ZYNQ_UART_BAUD_RATE_DIV_BDIV(bauddiv); + regs->control |= ZYNQ_UART_CONTROL_RXEN | ZYNQ_UART_CONTROL_TXEN; + + return true; +#else + return false; +#endif +} + +const rtems_termios_device_handler zynq_uart_handler = { + .first_open = zynq_uart_first_open, + .set_attributes = zynq_uart_set_attributes, + .write = zynq_uart_write_support, +#ifdef ZYNQ_CONSOLE_USE_INTERRUPTS + .last_close = zynq_uart_last_close, + .mode = TERMIOS_IRQ_DRIVEN +#else + .poll_read = zynq_uart_read_polled, + .mode = TERMIOS_POLLED +#endif +}; + +void zynq_uart_reset_tx_flush(zynq_uart_context *ctx) +{ + volatile zynq_uart *regs = ctx->regs; + int c = 4; + + while (c-- > 0) + zynq_uart_write_polled(&ctx->base, '\r'); + + while ((regs->channel_sts & ZYNQ_UART_CHANNEL_STS_TEMPTY) == 0) { + /* Wait */ + } +} |