diff options
Diffstat (limited to 'bsps/arm/atsam/console/console.c')
-rw-r--r-- | bsps/arm/atsam/console/console.c | 507 |
1 files changed, 264 insertions, 243 deletions
diff --git a/bsps/arm/atsam/console/console.c b/bsps/arm/atsam/console/console.c index d51d2ace7d..0f82c0c0c4 100644 --- a/bsps/arm/atsam/console/console.c +++ b/bsps/arm/atsam/console/console.c @@ -1,21 +1,35 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + /* - * Copyright (c) 2016 embedded brains GmbH. All rights reserved. + * Copyright (c) 2016 embedded brains GmbH & Co. KG * - * embedded brains GmbH - * Dornierstr. 4 - * 82178 Puchheim - * Germany - * <rtems@embedded-brains.de> + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * 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. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ #include <bsp.h> #include <bsp/irq.h> #include <bsp/fatal.h> #include <rtems/console.h> +#include <rtems/seterr.h> #include <rtems/termiostypes.h> @@ -23,274 +37,92 @@ #include <unistd.h> +#define UART_RX_DMA_BUF_SIZE 32l + +typedef struct { + char buf[UART_RX_DMA_BUF_SIZE]; + LinkedListDescriporView3 desc; +} atsam_uart_rx_dma; + typedef struct { rtems_termios_device_context base; - Usart *regs; + Uart *regs; rtems_vector_number irq; uint32_t id; bool console; + bool is_usart; #ifdef ATSAM_CONSOLE_USE_INTERRUPTS bool transmitting; + bool rx_dma_enabled; + uint32_t rx_dma_channel; + atsam_uart_rx_dma *rx_dma; + char *volatile*rx_dma_da; + char *rx_next_read_pos; #endif -} atsam_usart_context; +} atsam_uart_context; -static atsam_usart_context atsam_usart_instances[] = { +static atsam_uart_context atsam_usart_instances[] = { { - .regs = USART0, + .regs = (Uart *)USART0, .irq = USART0_IRQn, - .id = ID_USART0 + .id = ID_USART0, + .is_usart = true, } #ifdef USART1 , { - .regs = USART1, + .regs = (Uart *)USART1, .irq = USART1_IRQn, - .id = ID_USART1 + .id = ID_USART1, + .is_usart = true, } #endif #ifdef USART2 , { - .regs = USART2, + .regs = (Uart *)USART2, .irq = USART2_IRQn, - .id = ID_USART2 + .id = ID_USART2, + .is_usart = true, } #endif }; -#ifdef ATSAM_CONSOLE_USE_INTERRUPTS -static void atsam_usart_interrupt(void *arg) -{ - rtems_termios_tty *tty = arg; - atsam_usart_context *ctx = rtems_termios_get_device_context(tty); - Usart *regs = ctx->regs; - uint32_t csr = regs->US_CSR; - - while ((csr & US_CSR_RXRDY) != 0) { - char c = (char) regs->US_RHR; - - rtems_termios_enqueue_raw_characters(tty, &c, 1); - - csr = regs->US_CSR; - } - - if (ctx->transmitting && (csr & US_CSR_TXEMPTY) != 0) { - rtems_termios_dequeue_characters(tty, 1); - } -} -#endif - -static bool atsam_usart_set_attributes( - rtems_termios_device_context *base, - const struct termios *term -) -{ - atsam_usart_context *ctx = (atsam_usart_context *) base; - Usart *regs = ctx->regs; - rtems_termios_baud_t baud; - uint32_t mr; - - baud = rtems_termios_baud_to_number(term->c_ospeed); - regs->US_BRGR = (BOARD_MCK / baud) / 16; - - if ((term->c_cflag & CREAD) != 0) { - regs->US_CR = US_CR_RXEN | US_CR_TXEN; - } else { - regs->US_CR = US_CR_TXEN; - } - - mr = US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK; - - switch (term->c_cflag & CSIZE) { - case CS5: - mr |= US_MR_CHRL_5_BIT; - break; - case CS6: - mr |= US_MR_CHRL_6_BIT; - break; - case CS7: - mr |= US_MR_CHRL_7_BIT; - break; - default: - mr |= US_MR_CHRL_8_BIT; - break; - } - - if ((term->c_cflag & PARENB) != 0) { - if ((term->c_cflag & PARODD) != 0) { - mr |= US_MR_PAR_ODD; - } else { - mr |= US_MR_PAR_EVEN; - } - } else { - mr |= US_MR_PAR_NO; - } - - if ((term->c_cflag & CSTOPB) != 0) { - mr |= US_MR_NBSTOP_2_BIT; - } else { - mr |= US_MR_NBSTOP_1_BIT; - } - - regs->US_MR = mr; - - return true; -} - -static bool atsam_usart_first_open( - rtems_termios_tty *tty, - rtems_termios_device_context *base, - struct termios *term, - rtems_libio_open_close_args_t *args -) -{ - atsam_usart_context *ctx = (atsam_usart_context *) base; - Usart *regs = ctx->regs; -#ifdef ATSAM_CONSOLE_USE_INTERRUPTS - rtems_status_code sc; -#endif - - regs->US_CR = US_CR_RSTRX | US_CR_RSTTX | US_CR_RSTSTA; - regs->US_IDR = 0xffffffff; - - PMC_EnablePeripheral(ctx->id); - - rtems_termios_set_initial_baud(tty, ATSAM_CONSOLE_BAUD); - atsam_usart_set_attributes(base, term); - -#ifdef ATSAM_CONSOLE_USE_INTERRUPTS - regs->US_IER = US_IDR_RXRDY; - sc = rtems_interrupt_handler_install( - ctx->irq, - "USART", - RTEMS_INTERRUPT_SHARED, - atsam_usart_interrupt, - tty - ); - if (sc != RTEMS_SUCCESSFUL) { - return false; - } -#endif - - return true; -} - -static void atsam_usart_last_close( - rtems_termios_tty *tty, - rtems_termios_device_context *base, - rtems_libio_open_close_args_t *args -) -{ - atsam_usart_context *ctx = (atsam_usart_context *) base; - -#ifdef ATSAM_CONSOLE_USE_INTERRUPTS - rtems_interrupt_handler_remove(ctx->irq, atsam_usart_interrupt, tty); -#endif - - if (!ctx->console) { - PMC_DisablePeripheral(ctx->id); - } -} - -static void atsam_usart_write( - rtems_termios_device_context *base, - const char *buf, - size_t len -) -{ - atsam_usart_context *ctx = (atsam_usart_context *) base; - Usart *regs = ctx->regs; - -#ifdef ATSAM_CONSOLE_USE_INTERRUPTS - if (len > 0) { - ctx->transmitting = true; - regs->US_THR = buf[0]; - regs->US_IER = US_IDR_TXEMPTY; - } else { - ctx->transmitting = false; - regs->US_IDR = US_IDR_TXEMPTY; - } -#else - size_t i; - - for (i = 0; i < len; ++i) { - while ((regs->US_CSR & US_CSR_TXEMPTY) == 0) { - /* Wait */ - } - - regs->US_THR = buf[i]; - } -#endif -} - -#ifndef ATSAM_CONSOLE_USE_INTERRUPTS -static int atsam_usart_read(rtems_termios_device_context *base) -{ - atsam_usart_context *ctx = (atsam_usart_context *) base; - Usart *regs = ctx->regs; - - if ((regs->US_CSR & US_CSR_RXRDY) != 0) { - return (char) regs->US_RHR; - } else { - return -1; - } -} -#endif - -static const rtems_termios_device_handler atsam_usart_handler = { - .first_open = atsam_usart_first_open, - .last_close = atsam_usart_last_close, - .write = atsam_usart_write, - .set_attributes = atsam_usart_set_attributes, -#ifdef ATSAM_CONSOLE_USE_INTERRUPTS - .mode = TERMIOS_IRQ_DRIVEN -#else - .poll_read = atsam_usart_read, - .mode = TERMIOS_POLLED -#endif -}; - -typedef struct { - rtems_termios_device_context base; - Uart *regs; - rtems_vector_number irq; - uint32_t id; - bool console; -#ifdef ATSAM_CONSOLE_USE_INTERRUPTS - bool transmitting; -#endif -} atsam_uart_context; - static atsam_uart_context atsam_uart_instances[] = { { .regs = UART0, .irq = UART0_IRQn, - .id = ID_UART0 + .id = ID_UART0, + .is_usart = false, } #ifdef UART1 , { .regs = UART1, .irq = UART1_IRQn, - .id = ID_UART1 + .id = ID_UART1, + .is_usart = false, } #endif #ifdef UART2 , { .regs = UART2, .irq = UART2_IRQn, - .id = ID_UART2 + .id = ID_UART2, + .is_usart = false, } #endif #ifdef UART3 , { .regs = UART3, .irq = UART3_IRQn, - .id = ID_UART3 + .id = ID_UART3, + .is_usart = false, } #endif #ifdef UART4 , { .regs = UART4, .irq = UART4_IRQn, - .id = ID_UART4 + .id = ID_UART4, + .is_usart = false, } #endif }; @@ -303,16 +135,32 @@ static void atsam_uart_interrupt(void *arg) Uart *regs = ctx->regs; uint32_t sr = regs->UART_SR; - while ((sr & UART_SR_RXRDY) != 0) { - char c = (char) regs->UART_RHR; + if (!ctx->rx_dma_enabled) { + while ((sr & UART_SR_RXRDY) != 0) { + char c = (char) regs->UART_RHR; - rtems_termios_enqueue_raw_characters(tty, &c, 1); + rtems_termios_enqueue_raw_characters(tty, &c, 1); - sr = regs->UART_SR; + sr = regs->UART_SR; + } + } else { + while (*ctx->rx_dma_da != ctx->rx_next_read_pos) { + char c; + + c = *ctx->rx_next_read_pos; + + ++ctx->rx_next_read_pos; + if (ctx->rx_next_read_pos >= &ctx->rx_dma->buf[UART_RX_DMA_BUF_SIZE]) { + ctx->rx_next_read_pos = &ctx->rx_dma->buf[0]; + } + + rtems_termios_enqueue_raw_characters(tty, &c, 1); + } } - if (ctx->transmitting && (sr & UART_SR_TXEMPTY) != 0) { + while (ctx->transmitting && (sr & UART_SR_TXRDY) != 0) { rtems_termios_dequeue_characters(tty, 1); + sr = regs->UART_SR; } } #endif @@ -336,10 +184,31 @@ static bool atsam_uart_set_attributes( regs->UART_CR = UART_CR_TXEN; } - mr = UART_MR_FILTER_DISABLED | UART_MR_BRSRCCK_PERIPH_CLK; - - if ((term->c_cflag & CSIZE) != CS8) { - return false; + if (ctx->is_usart) { + mr = US_MR_USART_MODE_NORMAL | US_MR_USCLKS_MCK; + } else { + mr = UART_MR_FILTER_DISABLED | UART_MR_BRSRCCK_PERIPH_CLK; + } + + if (ctx->is_usart) { + switch (term->c_cflag & CSIZE) { + case CS5: + mr |= US_MR_CHRL_5_BIT; + break; + case CS6: + mr |= US_MR_CHRL_6_BIT; + break; + case CS7: + mr |= US_MR_CHRL_7_BIT; + break; + default: + mr |= US_MR_CHRL_8_BIT; + break; + } + } else { + if ((term->c_cflag & CSIZE) != CS8) { + return false; + } } if ((term->c_cflag & PARENB) != 0) { @@ -352,8 +221,16 @@ static bool atsam_uart_set_attributes( mr |= UART_MR_PAR_NO; } - if ((term->c_cflag & CSTOPB) != 0) { - return false; + if (ctx->is_usart) { + if ((term->c_cflag & CSTOPB) != 0) { + mr |= US_MR_NBSTOP_2_BIT; + } else { + mr |= US_MR_NBSTOP_1_BIT; + } + } else { + if ((term->c_cflag & CSTOPB) != 0) { + return false; + } } regs->UART_MR = mr; @@ -361,6 +238,118 @@ static bool atsam_uart_set_attributes( return true; } +static void atsam_uart_disable_rx_dma(atsam_uart_context *ctx) +{ + if (ctx->rx_dma) { + rtems_cache_coherent_free(ctx->rx_dma); + ctx->rx_dma = NULL; + } + + if (ctx->rx_dma_channel != XDMAD_ALLOC_FAILED) { + XDMAD_FreeChannel(&XDMAD_Instance, ctx->rx_dma_channel); + } + + ctx->rx_dma_enabled = false; +} + +static rtems_status_code atsam_uart_enable_rx_dma(atsam_uart_context *ctx) +{ + eXdmadRC rc; + int channel_id; + + if (ctx->rx_dma_enabled) { + return RTEMS_SUCCESSFUL; + } + + /* + * Make sure everything is in a clean default state so that the cleanup works + * in an error case. + */ + ctx->rx_dma = NULL; + ctx->rx_dma_channel = XDMAD_ALLOC_FAILED; + + ctx->rx_dma = rtems_cache_coherent_allocate(sizeof(*ctx->rx_dma), 0, 0); + if (ctx->rx_dma == NULL) { + atsam_uart_disable_rx_dma(ctx); + return RTEMS_NO_MEMORY; + } + + ctx->rx_next_read_pos = &ctx->rx_dma->buf[0]; + + ctx->rx_dma_channel = XDMAD_AllocateChannel( + &XDMAD_Instance, + XDMAD_TRANSFER_MEMORY, + ctx->id + ); + + if (ctx->rx_dma_channel == XDMAD_ALLOC_FAILED) { + atsam_uart_disable_rx_dma(ctx); + return RTEMS_IO_ERROR; + } + + rc = XDMAD_PrepareChannel(&XDMAD_Instance, ctx->rx_dma_channel); + if (rc != XDMAD_OK) { + atsam_uart_disable_rx_dma(ctx); + return RTEMS_IO_ERROR; + } + + channel_id = ctx->rx_dma_channel & 0xff; + ctx->rx_dma_da = + (char *volatile*) &XDMAD_Instance.pXdmacs->XDMAC_CHID[channel_id].XDMAC_CDA; + + ctx->rx_dma->desc.mbr_nda = (uint32_t)&ctx->rx_dma->desc; + ctx->rx_dma->desc.mbr_ubc = + 1 | + XDMA_UBC_NVIEW_NDV3 | + XDMA_UBC_NDE_FETCH_EN | + XDMA_UBC_NDEN_UPDATED | + XDMA_UBC_NSEN_UPDATED; + ctx->rx_dma->desc.mbr_sa = (uint32_t) &ctx->regs->UART_RHR; + ctx->rx_dma->desc.mbr_da = (uint32_t) &ctx->rx_dma->buf[0]; + ctx->rx_dma->desc.mbr_cfg = + XDMAC_CC_TYPE_PER_TRAN | + XDMAC_CC_MBSIZE_SINGLE | + XDMAC_CC_DSYNC_PER2MEM | + XDMAC_CC_SWREQ_HWR_CONNECTED | + XDMAC_CC_MEMSET_NORMAL_MODE | + XDMAC_CC_CSIZE_CHK_1 | + XDMAC_CC_DWIDTH_BYTE | + XDMAC_CC_SIF_AHB_IF1 | + XDMAC_CC_DIF_AHB_IF1 | + XDMAC_CC_SAM_FIXED_AM | + XDMAC_CC_DAM_UBS_AM | + XDMAC_CC_PERID(XDMAIF_Get_ChannelNumber(ctx->id, XDMAD_TRANSFER_RX)); + ctx->rx_dma->desc.mbr_bc = UART_RX_DMA_BUF_SIZE - 1; + ctx->rx_dma->desc.mbr_ds = 0; + ctx->rx_dma->desc.mbr_sus = 0; + ctx->rx_dma->desc.mbr_dus = 0; + + rc = XDMAD_ConfigureTransfer( + &XDMAD_Instance, + ctx->rx_dma_channel, + NULL, + XDMAC_CNDC_NDE_DSCR_FETCH_EN | + XDMAC_CNDC_NDVIEW_NDV3 | + XDMAC_CNDC_NDDUP_DST_PARAMS_UPDATED | + XDMAC_CNDC_NDSUP_SRC_PARAMS_UPDATED, + (uint32_t)&ctx->rx_dma->desc, + 0); + if (rc != XDMAD_OK) { + atsam_uart_disable_rx_dma(ctx); + return RTEMS_IO_ERROR; + } + + rc = XDMAD_StartTransfer(&XDMAD_Instance, ctx->rx_dma_channel); + if (rc != XDMAD_OK) { + atsam_uart_disable_rx_dma(ctx); + return RTEMS_IO_ERROR; + } + + ctx->rx_dma_enabled = true; + + return RTEMS_SUCCESSFUL; +} + static bool atsam_uart_first_open( rtems_termios_tty *tty, rtems_termios_device_context *base, @@ -386,7 +375,7 @@ static bool atsam_uart_first_open( regs->UART_IER = UART_IDR_RXRDY; sc = rtems_interrupt_handler_install( ctx->irq, - "UART", + ctx->is_usart ? "USART" : "UART", RTEMS_INTERRUPT_SHARED, atsam_uart_interrupt, tty @@ -411,6 +400,10 @@ static void atsam_uart_last_close( rtems_interrupt_handler_remove(ctx->irq, atsam_uart_interrupt, tty); #endif + if (ctx->rx_dma_enabled) { + atsam_uart_disable_rx_dma(ctx); + } + if (!ctx->console) { PMC_DisablePeripheral(ctx->id); } @@ -429,16 +422,16 @@ static void atsam_uart_write( if (len > 0) { ctx->transmitting = true; regs->UART_THR = buf[0]; - regs->UART_IER = UART_IDR_TXEMPTY; + regs->UART_IER = UART_IDR_TXRDY; } else { ctx->transmitting = false; - regs->UART_IDR = UART_IDR_TXEMPTY; + regs->UART_IDR = UART_IDR_TXRDY; } #else size_t i; for (i = 0; i < len; ++i) { - while ((regs->UART_SR & UART_SR_TXEMPTY) == 0) { + while ((regs->UART_SR & UART_SR_TXRDY) == 0) { /* Wait */ } @@ -461,13 +454,41 @@ static int atsam_uart_read(rtems_termios_device_context *base) } #endif +#ifdef ATSAM_CONSOLE_USE_INTERRUPTS +static int atsam_uart_ioctl( + rtems_termios_device_context *base, + ioctl_command_t request, + void *buffer +) +{ + atsam_uart_context *ctx = (atsam_uart_context *) base; + rtems_status_code sc; + + switch (request) { + case ATSAM_UART_ENABLE_RX_DMA: + sc = atsam_uart_enable_rx_dma(ctx); + if (sc != RTEMS_SUCCESSFUL) { + rtems_set_errno_and_return_minus_one(EIO); + } else { + ctx->rx_dma_enabled = true; + } + break; + default: + rtems_set_errno_and_return_minus_one(EINVAL); + } + + return 0; +} +#endif + static const rtems_termios_device_handler atsam_uart_handler = { .first_open = atsam_uart_first_open, .last_close = atsam_uart_last_close, .write = atsam_uart_write, .set_attributes = atsam_uart_set_attributes, #ifdef ATSAM_CONSOLE_USE_INTERRUPTS - .mode = TERMIOS_IRQ_DRIVEN + .mode = TERMIOS_IRQ_DRIVEN, + .ioctl = atsam_uart_ioctl, #else .poll_read = atsam_uart_read, .mode = TERMIOS_POLLED @@ -490,7 +511,7 @@ rtems_status_code console_initialize( usart[sizeof(usart) - 2] = (char) ('0' + i); rtems_termios_device_install( &usart[0], - &atsam_usart_handler, + &atsam_uart_handler, NULL, &atsam_usart_instances[i].base ); |