mbed library sources. Supersedes mbed-src.
Fork of mbed-dev by
Diff: targets/TARGET_NUVOTON/TARGET_M451/serial_api.c
- Revision:
- 153:9398a535854b
- Parent:
- 152:9a67f0b066fc
--- a/targets/TARGET_NUVOTON/TARGET_M451/serial_api.c Thu Dec 15 11:48:27 2016 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1071 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2015-2016 Nuvoton - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "serial_api.h" - -#if DEVICE_SERIAL - -#include "cmsis.h" -#include "mbed_error.h" -#include "mbed_assert.h" -#include "PeripheralPins.h" -#include "nu_modutil.h" -#include "nu_bitutil.h" - -#if DEVICE_SERIAL_ASYNCH -#include "dma_api.h" -#include "dma.h" -#endif - -struct nu_uart_var { - uint32_t ref_cnt; // Reference count of the H/W module - serial_t * obj; - uint32_t fifo_size_tx; - uint32_t fifo_size_rx; - void (*vec)(void); -#if DEVICE_SERIAL_ASYNCH - void (*vec_async)(void); - uint8_t pdma_perp_tx; - uint8_t pdma_perp_rx; -#endif -}; - -static void uart0_vec(void); -static void uart1_vec(void); -static void uart2_vec(void); -static void uart3_vec(void); -static void uart_irq(serial_t *obj); - -#if DEVICE_SERIAL_ASYNCH -static void uart0_vec_async(void); -static void uart1_vec_async(void); -static void uart2_vec_async(void); -static void uart3_vec_async(void); -static void uart_irq_async(serial_t *obj); - -static void uart_dma_handler_tx(uint32_t id, uint32_t event); -static void uart_dma_handler_rx(uint32_t id, uint32_t event); - -static void serial_tx_enable_interrupt(serial_t *obj, uint32_t address, uint8_t enable); -static void serial_rx_enable_interrupt(serial_t *obj, uint32_t address, uint8_t enable); -static int serial_write_async(serial_t *obj); -static int serial_read_async(serial_t *obj); - -static uint32_t serial_rx_event_check(serial_t *obj); -static uint32_t serial_tx_event_check(serial_t *obj); - -static int serial_is_tx_complete(serial_t *obj); -static void serial_tx_enable_event(serial_t *obj, int event, uint8_t enable); - -static void serial_tx_buffer_set(serial_t *obj, const void *tx, size_t length, uint8_t width); -static void serial_rx_buffer_set(serial_t *obj, void *rx, size_t length, uint8_t width); -static void serial_rx_set_char_match(serial_t *obj, uint8_t char_match); -static void serial_rx_enable_event(serial_t *obj, int event, uint8_t enable); -static int serial_is_rx_complete(serial_t *obj); - -static void serial_check_dma_usage(DMAUsage *dma_usage, int *dma_ch); -static int serial_is_irq_en(serial_t *obj, SerialIrq irq); -#endif - -static struct nu_uart_var uart0_var = { - .ref_cnt = 0, - .obj = NULL, - .fifo_size_tx = 16, - .fifo_size_rx = 16, - .vec = uart0_vec, -#if DEVICE_SERIAL_ASYNCH - .vec_async = uart0_vec_async, - .pdma_perp_tx = PDMA_UART0_TX, - .pdma_perp_rx = PDMA_UART0_RX -#endif -}; -static struct nu_uart_var uart1_var = { - .ref_cnt = 0, - .obj = NULL, - .fifo_size_tx = 16, - .fifo_size_rx = 16, - .vec = uart1_vec, -#if DEVICE_SERIAL_ASYNCH - .vec_async = uart1_vec_async, - .pdma_perp_tx = PDMA_UART1_TX, - .pdma_perp_rx = PDMA_UART1_RX -#endif -}; -static struct nu_uart_var uart2_var = { - .ref_cnt = 0, - .obj = NULL, - .fifo_size_tx = 16, - .fifo_size_rx = 16, - .vec = uart2_vec, -#if DEVICE_SERIAL_ASYNCH - .vec_async = uart2_vec_async, - .pdma_perp_tx = PDMA_UART2_TX, - .pdma_perp_rx = PDMA_UART2_RX -#endif -}; -static struct nu_uart_var uart3_var = { - .ref_cnt = 0, - .obj = NULL, - .fifo_size_tx = 16, - .fifo_size_rx = 16, - .vec = uart3_vec, -#if DEVICE_SERIAL_ASYNCH - .vec_async = uart3_vec_async, - .pdma_perp_tx = PDMA_UART3_TX, - .pdma_perp_rx = PDMA_UART3_RX -#endif -}; - - -int stdio_uart_inited = 0; -serial_t stdio_uart; -static uint32_t uart_modinit_mask = 0; - -static const struct nu_modinit_s uart_modinit_tab[] = { - {UART_0, UART0_MODULE, CLK_CLKSEL1_UARTSEL_HIRC, CLK_CLKDIV0_UART(1), UART0_RST, UART0_IRQn, &uart0_var}, - {UART_1, UART1_MODULE, CLK_CLKSEL1_UARTSEL_HIRC, CLK_CLKDIV0_UART(1), UART1_RST, UART1_IRQn, &uart1_var}, - {UART_2, UART2_MODULE, CLK_CLKSEL1_UARTSEL_HIRC, CLK_CLKDIV0_UART(1), UART2_RST, UART2_IRQn, &uart2_var}, - {UART_3, UART3_MODULE, CLK_CLKSEL1_UARTSEL_HIRC, CLK_CLKDIV0_UART(1), UART3_RST, UART3_IRQn, &uart3_var}, - - {NC, 0, 0, 0, 0, (IRQn_Type) 0, NULL} -}; - -extern void mbed_sdk_init(void); - -void serial_init(serial_t *obj, PinName tx, PinName rx) -{ - // NOTE: With armcc, serial_init() gets called from _sys_open() timing of which is before main()/mbed_sdk_init(). - mbed_sdk_init(); - - // Determine which UART_x the pins are used for - uint32_t uart_tx = pinmap_peripheral(tx, PinMap_UART_TX); - uint32_t uart_rx = pinmap_peripheral(rx, PinMap_UART_RX); - // Get the peripheral name (UART_x) from the pins and assign it to the object - obj->serial.uart = (UARTName) pinmap_merge(uart_tx, uart_rx); - MBED_ASSERT((int)obj->serial.uart != NC); - - const struct nu_modinit_s *modinit = get_modinit(obj->serial.uart, uart_modinit_tab); - MBED_ASSERT(modinit != NULL); - MBED_ASSERT(modinit->modname == obj->serial.uart); - - struct nu_uart_var *var = (struct nu_uart_var *) modinit->var; - - if (! var->ref_cnt) { - // Reset this module - SYS_ResetModule(modinit->rsetidx); - - // Select IP clock source - CLK_SetModuleClock(modinit->clkidx, modinit->clksrc, modinit->clkdiv); - // Enable IP clock - CLK_EnableModuleClock(modinit->clkidx); - - pinmap_pinout(tx, PinMap_UART_TX); - pinmap_pinout(rx, PinMap_UART_RX); - - obj->serial.pin_tx = tx; - obj->serial.pin_rx = rx; - } - var->ref_cnt ++; - - // Configure the UART module and set its baudrate - serial_baud(obj, 9600); - // Configure data bits, parity, and stop bits - serial_format(obj, 8, ParityNone, 1); - - obj->serial.vec = var->vec; - -#if DEVICE_SERIAL_ASYNCH - obj->serial.dma_usage_tx = DMA_USAGE_NEVER; - obj->serial.dma_usage_rx = DMA_USAGE_NEVER; - obj->serial.event = 0; - obj->serial.dma_chn_id_tx = DMA_ERROR_OUT_OF_CHANNELS; - obj->serial.dma_chn_id_rx = DMA_ERROR_OUT_OF_CHANNELS; -#endif - - // For stdio management - if (obj->serial.uart == STDIO_UART) { - stdio_uart_inited = 1; - memcpy(&stdio_uart, obj, sizeof(serial_t)); - } - - if (var->ref_cnt) { - // Mark this module to be inited. - int i = modinit - uart_modinit_tab; - uart_modinit_mask |= 1 << i; - } -} - -void serial_free(serial_t *obj) -{ - const struct nu_modinit_s *modinit = get_modinit(obj->serial.uart, uart_modinit_tab); - MBED_ASSERT(modinit != NULL); - MBED_ASSERT(modinit->modname == obj->serial.uart); - - struct nu_uart_var *var = (struct nu_uart_var *) modinit->var; - - var->ref_cnt --; - if (! var->ref_cnt) { -#if DEVICE_SERIAL_ASYNCH - if (obj->serial.dma_chn_id_tx != DMA_ERROR_OUT_OF_CHANNELS) { - dma_channel_free(obj->serial.dma_chn_id_tx); - obj->serial.dma_chn_id_tx = DMA_ERROR_OUT_OF_CHANNELS; - } - if (obj->serial.dma_chn_id_rx != DMA_ERROR_OUT_OF_CHANNELS) { - dma_channel_free(obj->serial.dma_chn_id_rx); - obj->serial.dma_chn_id_rx = DMA_ERROR_OUT_OF_CHANNELS; - } -#endif - - UART_Close((UART_T *) NU_MODBASE(obj->serial.uart)); - - UART_DISABLE_INT(((UART_T *) NU_MODBASE(obj->serial.uart)), (UART_INTEN_RDAIEN_Msk | UART_INTEN_THREIEN_Msk | UART_INTEN_RXTOIEN_Msk)); - NVIC_DisableIRQ(modinit->irq_n); - - // Disable IP clock - CLK_DisableModuleClock(modinit->clkidx); - } - - if (var->obj == obj) { - var->obj = NULL; - } - - if (obj->serial.uart == STDIO_UART) { - stdio_uart_inited = 0; - } - - if (! var->ref_cnt) { - // Mark this module to be deinited. - int i = modinit - uart_modinit_tab; - uart_modinit_mask &= ~(1 << i); - } -} - -void serial_baud(serial_t *obj, int baudrate) { - // Flush Tx FIFO. Otherwise, output data may get lost on this change. - while (! UART_IS_TX_EMPTY(((UART_T *) obj->serial.uart))); - - obj->serial.baudrate = baudrate; - UART_Open((UART_T *) NU_MODBASE(obj->serial.uart), baudrate); -} - -void serial_format(serial_t *obj, int data_bits, SerialParity parity, int stop_bits) { - // Flush Tx FIFO. Otherwise, output data may get lost on this change. - while (! UART_IS_TX_EMPTY(((UART_T *) obj->serial.uart))); - - // TODO: Assert for not supported parity and data bits - obj->serial.databits = data_bits; - obj->serial.parity = parity; - obj->serial.stopbits = stop_bits; - - uint32_t databits_intern = (data_bits == 5) ? UART_WORD_LEN_5 : - (data_bits == 6) ? UART_WORD_LEN_6 : - (data_bits == 7) ? UART_WORD_LEN_7 : - UART_WORD_LEN_8; - uint32_t parity_intern = (parity == ParityOdd || parity == ParityForced1) ? UART_PARITY_ODD : - (parity == ParityEven || parity == ParityForced0) ? UART_PARITY_EVEN : - UART_PARITY_NONE; - uint32_t stopbits_intern = (stop_bits == 2) ? UART_STOP_BIT_2 : UART_STOP_BIT_1; - UART_SetLine_Config((UART_T *) NU_MODBASE(obj->serial.uart), - 0, // Don't change baudrate - databits_intern, - parity_intern, - stopbits_intern); -} - -#if DEVICE_SERIAL_FC - -void serial_set_flow_control(serial_t *obj, FlowControl type, PinName rxflow, PinName txflow) -{ - UART_T *uart_base = (UART_T *) NU_MODBASE(obj->serial.uart); - - // First, disable flow control completely. - uart_base->INTEN &= ~(UART_INTEN_ATORTSEN_Msk | UART_INTEN_ATOCTSEN_Msk); - - if ((type == FlowControlRTS || type == FlowControlRTSCTS) && rxflow != NC) { - // Check if RTS pin matches. - uint32_t uart_rts = pinmap_peripheral(rxflow, PinMap_UART_RTS); - MBED_ASSERT(uart_rts == obj->serial.uart); - // Enable the pin for RTS function - pinmap_pinout(rxflow, PinMap_UART_RTS); - // nRTS pin output is high level active - uart_base->MODEM = (uart_base->MODEM & ~UART_MODEM_RTSACTLV_Msk); - uart_base->FIFO = (uart_base->FIFO & ~UART_FIFO_RTSTRGLV_Msk) | UART_FIFO_RTSTRGLV_8BYTES; - // Enable RTS - uart_base->INTEN |= UART_INTEN_ATORTSEN_Msk; - } - - if ((type == FlowControlCTS || type == FlowControlRTSCTS) && txflow != NC) { - // Check if CTS pin matches. - uint32_t uart_cts = pinmap_peripheral(txflow, PinMap_UART_CTS); - MBED_ASSERT(uart_cts == obj->serial.uart); - // Enable the pin for CTS function - pinmap_pinout(txflow, PinMap_UART_CTS); - // nCTS pin input is high level active - uart_base->MODEMSTS = (uart_base->MODEMSTS & ~UART_MODEMSTS_CTSACTLV_Msk); - // Enable CTS - uart_base->INTEN |= UART_INTEN_ATOCTSEN_Msk; - } -} - -#endif //DEVICE_SERIAL_FC - -void serial_irq_handler(serial_t *obj, uart_irq_handler handler, uint32_t id) -{ - // Flush Tx FIFO. Otherwise, output data may get lost on this change. - while (! UART_IS_TX_EMPTY(((UART_T *) obj->serial.uart))); - - const struct nu_modinit_s *modinit = get_modinit(obj->serial.uart, uart_modinit_tab); - MBED_ASSERT(modinit != NULL); - MBED_ASSERT(modinit->modname == obj->serial.uart); - - obj->serial.irq_handler = (uint32_t) handler; - obj->serial.irq_id = id; - - // Restore sync-mode vector - obj->serial.vec = ((struct nu_uart_var *) modinit->var)->vec; -} - -void serial_irq_set(serial_t *obj, SerialIrq irq, uint32_t enable) -{ - if (enable) { - const struct nu_modinit_s *modinit = get_modinit(obj->serial.uart, uart_modinit_tab); - MBED_ASSERT(modinit != NULL); - MBED_ASSERT(modinit->modname == obj->serial.uart); - - NVIC_SetVector(modinit->irq_n, (uint32_t) obj->serial.vec); - NVIC_EnableIRQ(modinit->irq_n); - - struct nu_uart_var *var = (struct nu_uart_var *) modinit->var; - // Multiple serial S/W objects for single UART H/W module possibly. - // Bind serial S/W object to UART H/W module as interrupt is enabled. - var->obj = obj; - - switch (irq) { - // NOTE: Setting inten_msk first to avoid race condition - case RxIrq: - obj->serial.inten_msk = obj->serial.inten_msk | (UART_INTEN_RDAIEN_Msk | UART_INTEN_RXTOIEN_Msk); - UART_ENABLE_INT(((UART_T *) NU_MODBASE(obj->serial.uart)), (UART_INTEN_RDAIEN_Msk | UART_INTEN_RXTOIEN_Msk)); - break; - case TxIrq: - obj->serial.inten_msk = obj->serial.inten_msk | UART_INTEN_THREIEN_Msk; - UART_ENABLE_INT(((UART_T *) NU_MODBASE(obj->serial.uart)), UART_INTEN_THREIEN_Msk); - break; - } - } else { // disable - switch (irq) { - case RxIrq: - UART_DISABLE_INT(((UART_T *) NU_MODBASE(obj->serial.uart)), (UART_INTEN_RDAIEN_Msk | UART_INTEN_RXTOIEN_Msk)); - obj->serial.inten_msk = obj->serial.inten_msk & ~(UART_INTEN_RDAIEN_Msk | UART_INTEN_RXTOIEN_Msk); - break; - case TxIrq: - UART_DISABLE_INT(((UART_T *) NU_MODBASE(obj->serial.uart)), UART_INTEN_THREIEN_Msk); - obj->serial.inten_msk = obj->serial.inten_msk & ~UART_INTEN_THREIEN_Msk; - break; - } - } -} - -int serial_getc(serial_t *obj) -{ - // TODO: Fix every byte access requires accompaniness of one interrupt. This degrades performance much. - while (! serial_readable(obj)); - int c = UART_READ(((UART_T *) NU_MODBASE(obj->serial.uart))); - - // Simulate clear of the interrupt flag - if (obj->serial.inten_msk & (UART_INTEN_RDAIEN_Msk | UART_INTEN_RXTOIEN_Msk)) { - UART_ENABLE_INT(((UART_T *) NU_MODBASE(obj->serial.uart)), (UART_INTEN_RDAIEN_Msk | UART_INTEN_RXTOIEN_Msk)); - } - - return c; -} - -void serial_putc(serial_t *obj, int c) -{ - // TODO: Fix every byte access requires accompaniness of one interrupt. This degrades performance much. - while (! serial_writable(obj)); - UART_WRITE(((UART_T *) NU_MODBASE(obj->serial.uart)), c); - - // Simulate clear of the interrupt flag - if (obj->serial.inten_msk & UART_INTEN_THREIEN_Msk) { - UART_ENABLE_INT(((UART_T *) NU_MODBASE(obj->serial.uart)), UART_INTEN_THREIEN_Msk); - } -} - -int serial_readable(serial_t *obj) -{ - //return UART_IS_RX_READY(((UART_T *) NU_MODBASE(obj->serial.uart))); - return ! UART_GET_RX_EMPTY(((UART_T *) NU_MODBASE(obj->serial.uart))); -} - -int serial_writable(serial_t *obj) -{ - return ! UART_IS_TX_FULL(((UART_T *) NU_MODBASE(obj->serial.uart))); -} - -void serial_pinout_tx(PinName tx) -{ - pinmap_pinout(tx, PinMap_UART_TX); -} - -void serial_break_set(serial_t *obj) -{ - ((UART_T *) NU_MODBASE(obj->serial.uart))->LINE |= UART_LINE_BCB_Msk; -} - -void serial_break_clear(serial_t *obj) -{ - ((UART_T *) NU_MODBASE(obj->serial.uart))->LINE &= ~UART_LINE_BCB_Msk; -} - -static void uart0_vec(void) -{ - uart_irq(uart0_var.obj); -} - -static void uart1_vec(void) -{ - uart_irq(uart1_var.obj); -} - -static void uart2_vec(void) -{ - uart_irq(uart2_var.obj); -} - -static void uart3_vec(void) -{ - uart_irq(uart3_var.obj); -} - -static void uart_irq(serial_t *obj) -{ - UART_T *uart_base = (UART_T *) NU_MODBASE(obj->serial.uart); - - if (uart_base->INTSTS & (UART_INTSTS_RDAINT_Msk | UART_INTSTS_RXTOINT_Msk)) { - // Simulate clear of the interrupt flag. Temporarily disable the interrupt here and to be recovered on next read. - UART_DISABLE_INT(uart_base, (UART_INTEN_RDAIEN_Msk | UART_INTEN_RXTOIEN_Msk)); - if (obj->serial.irq_handler) { - ((uart_irq_handler) obj->serial.irq_handler)(obj->serial.irq_id, RxIrq); - } - } - - if (uart_base->INTSTS & UART_INTSTS_THREINT_Msk) { - // Simulate clear of the interrupt flag. Temporarily disable the interrupt here and to be recovered on next write. - UART_DISABLE_INT(uart_base, UART_INTEN_THREIEN_Msk); - if (obj->serial.irq_handler) { - ((uart_irq_handler) obj->serial.irq_handler)(obj->serial.irq_id, TxIrq); - } - } - - // FIXME: Ignore all other interrupt flags. Clear them. Otherwise, program will get stuck in interrupt. - uart_base->INTSTS = uart_base->INTSTS; - uart_base->FIFOSTS = uart_base->FIFOSTS; -} - - -#if DEVICE_SERIAL_ASYNCH -int serial_tx_asynch(serial_t *obj, const void *tx, size_t tx_length, uint8_t tx_width, uint32_t handler, uint32_t event, DMAUsage hint) -{ - // NOTE: tx_width is deprecated. Assume its value is databits ceiled to the nearest number among 8, 16, and 32. - tx_width = (obj->serial.databits <= 8) ? 8 : (obj->serial.databits <= 16) ? 16 : 32; - - MBED_ASSERT(tx_width == 8 || tx_width == 16 || tx_width == 32); - - obj->serial.dma_usage_tx = hint; - serial_check_dma_usage(&obj->serial.dma_usage_tx, &obj->serial.dma_chn_id_tx); - - // UART IRQ is necessary for both interrupt way and DMA way - serial_tx_enable_event(obj, event, 1); - serial_tx_buffer_set(obj, tx, tx_length, tx_width); - //UART_HAL_DisableTransmitter(obj->serial.address); - //UART_HAL_FlushTxFifo(obj->serial.address); - //UART_HAL_EnableTransmitter(obj->serial.address); - - int n_word = 0; - if (obj->serial.dma_usage_tx == DMA_USAGE_NEVER) { - // Interrupt way - n_word = serial_write_async(obj); - serial_tx_enable_interrupt(obj, handler, 1); - } else { - // DMA way - const struct nu_modinit_s *modinit = get_modinit(obj->serial.uart, uart_modinit_tab); - MBED_ASSERT(modinit != NULL); - MBED_ASSERT(modinit->modname == obj->serial.uart); - - PDMA->CHCTL |= 1 << obj->serial.dma_chn_id_tx; // Enable this DMA channel - PDMA_SetTransferMode(obj->serial.dma_chn_id_tx, - ((struct nu_uart_var *) modinit->var)->pdma_perp_tx, // Peripheral connected to this PDMA - 0, // Scatter-gather disabled - 0); // Scatter-gather descriptor address - PDMA_SetTransferCnt(obj->serial.dma_chn_id_tx, - (tx_width == 8) ? PDMA_WIDTH_8 : (tx_width == 16) ? PDMA_WIDTH_16 : PDMA_WIDTH_32, - tx_length); - PDMA_SetTransferAddr(obj->serial.dma_chn_id_tx, - (uint32_t) tx, // NOTE: - // NUC472: End of source address - // M451: Start of source address - PDMA_SAR_INC, // Source address incremental - (uint32_t) obj->serial.uart, // Destination address - PDMA_DAR_FIX); // Destination address fixed - PDMA_SetBurstType(obj->serial.dma_chn_id_tx, - PDMA_REQ_SINGLE, // Single mode - 0); // Burst size - PDMA_EnableInt(obj->serial.dma_chn_id_tx, - PDMA_INT_TRANS_DONE); // Interrupt type - // Register DMA event handler - dma_set_handler(obj->serial.dma_chn_id_tx, (uint32_t) uart_dma_handler_tx, (uint32_t) obj, DMA_EVENT_ALL); - serial_tx_enable_interrupt(obj, handler, 1); - ((UART_T *) NU_MODBASE(obj->serial.uart))->INTEN |= UART_INTEN_TXPDMAEN_Msk; // Start DMA transfer - } - - return n_word; -} - -void serial_rx_asynch(serial_t *obj, void *rx, size_t rx_length, uint8_t rx_width, uint32_t handler, uint32_t event, uint8_t char_match, DMAUsage hint) -{ - // NOTE: rx_width is deprecated. Assume its value is databits ceiled to the nearest number among 8, 16, and 32. - rx_width = (obj->serial.databits <= 8) ? 8 : (obj->serial.databits <= 16) ? 16 : 32; - - MBED_ASSERT(rx_width == 8 || rx_width == 16 || rx_width == 32); - - obj->serial.dma_usage_rx = hint; - serial_check_dma_usage(&obj->serial.dma_usage_rx, &obj->serial.dma_chn_id_rx); - // DMA doesn't support char match, so fall back to IRQ if it is requested. - if (obj->serial.dma_usage_rx != DMA_USAGE_NEVER && - (event & SERIAL_EVENT_RX_CHARACTER_MATCH) && - char_match != SERIAL_RESERVED_CHAR_MATCH) { - obj->serial.dma_usage_rx = DMA_USAGE_NEVER; - dma_channel_free(obj->serial.dma_chn_id_rx); - obj->serial.dma_chn_id_rx = DMA_ERROR_OUT_OF_CHANNELS; - } - - // UART IRQ is necessary for both interrupt way and DMA way - serial_rx_enable_event(obj, event, 1); - serial_rx_buffer_set(obj, rx, rx_length, rx_width); - serial_rx_set_char_match(obj, char_match); - //UART_HAL_DisableReceiver(obj->serial.address); - //UART_HAL_FlushRxFifo(obj->serial.address); - //UART_HAL_EnableReceiver(obj->serial.address); - - if (obj->serial.dma_usage_rx == DMA_USAGE_NEVER) { - // Interrupt way - serial_rx_enable_interrupt(obj, handler, 1); - } else { - // DMA way - const struct nu_modinit_s *modinit = get_modinit(obj->serial.uart, uart_modinit_tab); - MBED_ASSERT(modinit != NULL); - MBED_ASSERT(modinit->modname == obj->serial.uart); - - PDMA->CHCTL |= 1 << obj->serial.dma_chn_id_rx; // Enable this DMA channel - PDMA_SetTransferMode(obj->serial.dma_chn_id_rx, - ((struct nu_uart_var *) modinit->var)->pdma_perp_rx, // Peripheral connected to this PDMA - 0, // Scatter-gather disabled - 0); // Scatter-gather descriptor address - PDMA_SetTransferCnt(obj->serial.dma_chn_id_rx, - (rx_width == 8) ? PDMA_WIDTH_8 : (rx_width == 16) ? PDMA_WIDTH_16 : PDMA_WIDTH_32, - rx_length); - PDMA_SetTransferAddr(obj->serial.dma_chn_id_rx, - (uint32_t) obj->serial.uart, // Source address - PDMA_SAR_FIX, // Source address fixed - (uint32_t) rx, // NOTE: - // NUC472: End of destination address - // M451: Start of destination address - PDMA_DAR_INC); // Destination address incremental - PDMA_SetBurstType(obj->serial.dma_chn_id_rx, - PDMA_REQ_SINGLE, // Single mode - 0); // Burst size - PDMA_EnableInt(obj->serial.dma_chn_id_rx, - PDMA_INT_TRANS_DONE); // Interrupt type - // Register DMA event handler - dma_set_handler(obj->serial.dma_chn_id_rx, (uint32_t) uart_dma_handler_rx, (uint32_t) obj, DMA_EVENT_ALL); - serial_rx_enable_interrupt(obj, handler, 1); - ((UART_T *) NU_MODBASE(obj->serial.uart))->INTEN |= UART_INTEN_RXPDMAEN_Msk; // Start DMA transfer - } -} - -void serial_tx_abort_asynch(serial_t *obj) -{ - // Flush Tx FIFO. Otherwise, output data may get lost on this change. - while (! UART_IS_TX_EMPTY(((UART_T *) obj->serial.uart))); - - if (obj->serial.dma_usage_tx != DMA_USAGE_NEVER) { - if (obj->serial.dma_chn_id_tx != DMA_ERROR_OUT_OF_CHANNELS) { - PDMA_DisableInt(obj->serial.dma_chn_id_tx, PDMA_INT_TRANS_DONE); - // FIXME: On NUC472, next PDMA transfer will fail with PDMA_STOP() called. Cause is unknown. - //PDMA_STOP(obj->serial.dma_chn_id_tx); - PDMA->CHCTL &= ~(1 << obj->serial.dma_chn_id_tx); - } - UART_DISABLE_INT(((UART_T *) NU_MODBASE(obj->serial.uart)), UART_INTEN_TXPDMAEN_Msk); - } - - // Necessary for both interrupt way and DMA way - serial_irq_set(obj, TxIrq, 0); - // FIXME: more complete abort operation - //UART_HAL_DisableTransmitter(obj->serial.serial.address); - //UART_HAL_FlushTxFifo(obj->serial.serial.address); -} - -void serial_rx_abort_asynch(serial_t *obj) -{ - if (obj->serial.dma_usage_rx != DMA_USAGE_NEVER) { - if (obj->serial.dma_chn_id_rx != DMA_ERROR_OUT_OF_CHANNELS) { - PDMA_DisableInt(obj->serial.dma_chn_id_rx, PDMA_INT_TRANS_DONE); - // FIXME: On NUC472, next PDMA transfer will fail with PDMA_STOP() called. Cause is unknown. - //PDMA_STOP(obj->serial.dma_chn_id_rx); - PDMA->CHCTL &= ~(1 << obj->serial.dma_chn_id_rx); - } - UART_DISABLE_INT(((UART_T *) NU_MODBASE(obj->serial.uart)), UART_INTEN_RXPDMAEN_Msk); - } - - // Necessary for both interrupt way and DMA way - serial_irq_set(obj, RxIrq, 0); - // FIXME: more complete abort operation - //UART_HAL_DisableReceiver(obj->serial.serial.address); - //UART_HAL_FlushRxFifo(obj->serial.serial.address); -} - -uint8_t serial_tx_active(serial_t *obj) -{ - return serial_is_irq_en(obj, TxIrq); -} - -uint8_t serial_rx_active(serial_t *obj) -{ - return serial_is_irq_en(obj, RxIrq); -} - -int serial_irq_handler_asynch(serial_t *obj) -{ - int event_rx = 0; - int event_tx = 0; - - // Necessary for both interrupt way and DMA way - if (serial_is_irq_en(obj, RxIrq)) { - event_rx = serial_rx_event_check(obj); - if (event_rx) { - serial_rx_abort_asynch(obj); - } - } - - if (serial_is_irq_en(obj, TxIrq)) { - event_tx = serial_tx_event_check(obj); - if (event_tx) { - serial_tx_abort_asynch(obj); - } - } - - return (obj->serial.event & (event_rx | event_tx)); -} - -int serial_allow_powerdown(void) -{ - uint32_t modinit_mask = uart_modinit_mask; - while (modinit_mask) { - int uart_idx = nu_ctz(modinit_mask); - const struct nu_modinit_s *modinit = uart_modinit_tab + uart_idx; - if (modinit->modname != NC) { - UART_T *uart_base = (UART_T *) NU_MODBASE(modinit->modname); - // Disallow entering power-down mode if Tx FIFO has data to flush - if (! UART_IS_TX_EMPTY((uart_base))) { - return 0; - } - // Disallow entering power-down mode if async Rx transfer (not PDMA) is on-going - if (uart_base->INTEN & (UART_INTEN_RDAIEN_Msk | UART_INTEN_RXTOIEN_Msk)) { - return 0; - } - // Disallow entering power-down mode if async Rx transfer (PDMA) is on-going - if (uart_base->INTEN & UART_INTEN_RXPDMAEN_Msk) { - return 0; - } - } - modinit_mask &= ~(1 << uart_idx); - } - - return 1; -} - -static void uart0_vec_async(void) -{ - uart_irq_async(uart0_var.obj); -} - -static void uart1_vec_async(void) -{ - uart_irq_async(uart1_var.obj); -} - -static void uart2_vec_async(void) -{ - uart_irq_async(uart2_var.obj); -} - -static void uart3_vec_async(void) -{ - uart_irq_async(uart3_var.obj); -} - -static void uart_irq_async(serial_t *obj) -{ - if (serial_is_irq_en(obj, RxIrq)) { - (*obj->serial.irq_handler_rx_async)(); - } - if (serial_is_irq_en(obj, TxIrq)) { - (*obj->serial.irq_handler_tx_async)(); - } -} - -static void serial_rx_set_char_match(serial_t *obj, uint8_t char_match) -{ - obj->char_match = char_match; - obj->char_found = 0; -} - -static void serial_tx_enable_event(serial_t *obj, int event, uint8_t enable) -{ - obj->serial.event &= ~SERIAL_EVENT_TX_MASK; - obj->serial.event |= (event & SERIAL_EVENT_TX_MASK); - - //if (event & SERIAL_EVENT_TX_COMPLETE) { - //} -} - -static void serial_rx_enable_event(serial_t *obj, int event, uint8_t enable) -{ - obj->serial.event &= ~SERIAL_EVENT_RX_MASK; - obj->serial.event |= (event & SERIAL_EVENT_RX_MASK); - - //if (event & SERIAL_EVENT_RX_COMPLETE) { - //} - //if (event & SERIAL_EVENT_RX_OVERRUN_ERROR) { - //} - if (event & SERIAL_EVENT_RX_FRAMING_ERROR) { - UART_ENABLE_INT(((UART_T *) NU_MODBASE(obj->serial.uart)), UART_INTEN_RLSIEN_Msk); - } - if (event & SERIAL_EVENT_RX_PARITY_ERROR) { - UART_ENABLE_INT(((UART_T *) NU_MODBASE(obj->serial.uart)), UART_INTEN_RLSIEN_Msk); - } - if (event & SERIAL_EVENT_RX_OVERFLOW) { - UART_ENABLE_INT(((UART_T *) NU_MODBASE(obj->serial.uart)), UART_INTEN_BUFERRIEN_Msk); - } - //if (event & SERIAL_EVENT_RX_CHARACTER_MATCH) { - //} -} - -static int serial_is_tx_complete(serial_t *obj) -{ - // NOTE: Exclude tx fifo empty check due to no such interrupt on DMA way - //return (obj->tx_buff.pos == obj->tx_buff.length) && UART_GET_TX_EMPTY(((UART_T *) NU_MODBASE(obj->serial.uart))); - // FIXME: Premature abort??? - return (obj->tx_buff.pos == obj->tx_buff.length); -} - -static int serial_is_rx_complete(serial_t *obj) -{ - //return (obj->rx_buff.pos == obj->rx_buff.length) && UART_GET_RX_EMPTY(((UART_T *) NU_MODBASE(obj->serial.uart))); - return (obj->rx_buff.pos == obj->rx_buff.length); -} - -static uint32_t serial_tx_event_check(serial_t *obj) -{ - UART_T *uart_base = (UART_T *) NU_MODBASE(obj->serial.uart); - - if (uart_base->INTSTS & UART_INTSTS_THREINT_Msk) { - // Simulate clear of the interrupt flag. Temporarily disable the interrupt here and to be recovered on next write. - UART_DISABLE_INT(uart_base, UART_INTEN_THREIEN_Msk); - } - - uint32_t event = 0; - - if (obj->serial.dma_usage_tx == DMA_USAGE_NEVER) { - serial_write_async(obj); - } - - if (serial_is_tx_complete(obj)) { - event |= SERIAL_EVENT_TX_COMPLETE; - } - - return event; -} - -static uint32_t serial_rx_event_check(serial_t *obj) -{ - UART_T *uart_base = (UART_T *) NU_MODBASE(obj->serial.uart); - - if (uart_base->INTSTS & (UART_INTSTS_RDAINT_Msk | UART_INTSTS_RXTOINT_Msk)) { - // Simulate clear of the interrupt flag. Temporarily disable the interrupt here and to be recovered on next read. - UART_DISABLE_INT(uart_base, (UART_INTEN_RDAIEN_Msk | UART_INTEN_RXTOIEN_Msk)); - } - - uint32_t event = 0; - - if (uart_base->FIFOSTS & UART_FIFOSTS_BIF_Msk) { - uart_base->FIFOSTS = UART_FIFOSTS_BIF_Msk; - } - if (uart_base->FIFOSTS & UART_FIFOSTS_FEF_Msk) { - uart_base->FIFOSTS = UART_FIFOSTS_FEF_Msk; - event |= SERIAL_EVENT_RX_FRAMING_ERROR; - } - if (uart_base->FIFOSTS & UART_FIFOSTS_PEF_Msk) { - uart_base->FIFOSTS = UART_FIFOSTS_PEF_Msk; - event |= SERIAL_EVENT_RX_PARITY_ERROR; - } - - if (uart_base->FIFOSTS & UART_FIFOSTS_RXOVIF_Msk) { - uart_base->FIFOSTS = UART_FIFOSTS_RXOVIF_Msk; - event |= SERIAL_EVENT_RX_OVERFLOW; - } - - if (obj->serial.dma_usage_rx == DMA_USAGE_NEVER) { - serial_read_async(obj); - } - - if (serial_is_rx_complete(obj)) { - event |= SERIAL_EVENT_RX_COMPLETE; - } - if ((obj->char_match != SERIAL_RESERVED_CHAR_MATCH) && obj->char_found) { - event |= SERIAL_EVENT_RX_CHARACTER_MATCH; - // FIXME: Timing to reset char_found? - //obj->char_found = 0; - } - - return event; -} - -static void uart_dma_handler_tx(uint32_t id, uint32_t event_dma) -{ - serial_t *obj = (serial_t *) id; - - // FIXME: Pass this error to caller - if (event_dma & DMA_EVENT_ABORT) { - } - // Expect UART IRQ will catch this transfer done event - if (event_dma & DMA_EVENT_TRANSFER_DONE) { - obj->tx_buff.pos = obj->tx_buff.length; - } - // FIXME: Pass this error to caller - if (event_dma & DMA_EVENT_TIMEOUT) { - } - - uart_irq_async(obj); -} - -static void uart_dma_handler_rx(uint32_t id, uint32_t event_dma) -{ - serial_t *obj = (serial_t *) id; - - // FIXME: Pass this error to caller - if (event_dma & DMA_EVENT_ABORT) { - } - // Expect UART IRQ will catch this transfer done event - if (event_dma & DMA_EVENT_TRANSFER_DONE) { - obj->rx_buff.pos = obj->rx_buff.length; - } - // FIXME: Pass this error to caller - if (event_dma & DMA_EVENT_TIMEOUT) { - } - - uart_irq_async(obj); -} - -static int serial_write_async(serial_t *obj) -{ - const struct nu_modinit_s *modinit = get_modinit(obj->serial.uart, uart_modinit_tab); - MBED_ASSERT(modinit != NULL); - MBED_ASSERT(modinit->modname == obj->serial.uart); - - UART_T *uart_base = (UART_T *) NU_MODBASE(obj->serial.uart); - - uint32_t tx_fifo_max = ((struct nu_uart_var *) modinit->var)->fifo_size_tx; - uint32_t tx_fifo_busy = (uart_base->FIFOSTS & UART_FIFOSTS_TXPTR_Msk) >> UART_FIFOSTS_TXPTR_Pos; - if (uart_base->FIFOSTS & UART_FIFOSTS_TXFULL_Msk) { - tx_fifo_busy = tx_fifo_max; - } - uint32_t tx_fifo_free = tx_fifo_max - tx_fifo_busy; - if (tx_fifo_free == 0) { - // Simulate clear of the interrupt flag - if (obj->serial.inten_msk & UART_INTEN_THREIEN_Msk) { - UART_ENABLE_INT(((UART_T *) NU_MODBASE(obj->serial.uart)), UART_INTEN_THREIEN_Msk); - } - return 0; - } - - uint32_t bytes_per_word = obj->tx_buff.width / 8; - - uint8_t *tx = (uint8_t *)(obj->tx_buff.buffer) + bytes_per_word * obj->tx_buff.pos; - int n_words = 0; - while (obj->tx_buff.pos < obj->tx_buff.length && tx_fifo_free >= bytes_per_word) { - switch (bytes_per_word) { - case 4: - UART_WRITE(((UART_T *) NU_MODBASE(obj->serial.uart)), *tx ++); - UART_WRITE(((UART_T *) NU_MODBASE(obj->serial.uart)), *tx ++); - case 2: - UART_WRITE(((UART_T *) NU_MODBASE(obj->serial.uart)), *tx ++); - case 1: - UART_WRITE(((UART_T *) NU_MODBASE(obj->serial.uart)), *tx ++); - } - - n_words ++; - tx_fifo_free -= bytes_per_word; - obj->tx_buff.pos ++; - } - - if (n_words) { - // Simulate clear of the interrupt flag - if (obj->serial.inten_msk & UART_INTEN_THREIEN_Msk) { - UART_ENABLE_INT(((UART_T *) NU_MODBASE(obj->serial.uart)), UART_INTEN_THREIEN_Msk); - } - } - - return n_words; -} - -static int serial_read_async(serial_t *obj) -{ - const struct nu_modinit_s *modinit = get_modinit(obj->serial.uart, uart_modinit_tab); - MBED_ASSERT(modinit != NULL); - MBED_ASSERT(modinit->modname == obj->serial.uart); - - uint32_t rx_fifo_busy = (((UART_T *) NU_MODBASE(obj->serial.uart))->FIFOSTS & UART_FIFOSTS_RXPTR_Msk) >> UART_FIFOSTS_RXPTR_Pos; - //uint32_t rx_fifo_free = ((struct nu_uart_var *) modinit->var)->fifo_size_rx - rx_fifo_busy; - //if (rx_fifo_free == 0) { - // return 0; - //} - - uint32_t bytes_per_word = obj->rx_buff.width / 8; - - uint8_t *rx = (uint8_t *)(obj->rx_buff.buffer) + bytes_per_word * obj->rx_buff.pos; - int n_words = 0; - while (obj->rx_buff.pos < obj->rx_buff.length && rx_fifo_busy >= bytes_per_word) { - switch (bytes_per_word) { - case 4: - *rx ++ = UART_READ(((UART_T *) NU_MODBASE(obj->serial.uart))); - *rx ++ = UART_READ(((UART_T *) NU_MODBASE(obj->serial.uart))); - case 2: - *rx ++ = UART_READ(((UART_T *) NU_MODBASE(obj->serial.uart))); - case 1: - *rx ++ = UART_READ(((UART_T *) NU_MODBASE(obj->serial.uart))); - } - - n_words ++; - rx_fifo_busy -= bytes_per_word; - obj->rx_buff.pos ++; - - if ((obj->serial.event & SERIAL_EVENT_RX_CHARACTER_MATCH) && - obj->char_match != SERIAL_RESERVED_CHAR_MATCH) { - uint8_t *rx_cmp = rx; - switch (bytes_per_word) { - case 4: - rx_cmp -= 2; - case 2: - rx_cmp --; - case 1: - rx_cmp --; - } - if (*rx_cmp == obj->char_match) { - obj->char_found = 1; - break; - } - } - } - - if (n_words) { - // Simulate clear of the interrupt flag - if (obj->serial.inten_msk & (UART_INTEN_RDAIEN_Msk | UART_INTEN_RXTOIEN_Msk)) { - UART_ENABLE_INT(((UART_T *) NU_MODBASE(obj->serial.uart)), (UART_INTEN_RDAIEN_Msk | UART_INTEN_RXTOIEN_Msk)); - } - } - - return n_words; -} - -static void serial_tx_buffer_set(serial_t *obj, const void *tx, size_t length, uint8_t width) -{ - obj->tx_buff.buffer = (void *) tx; - obj->tx_buff.length = length; - obj->tx_buff.pos = 0; - obj->tx_buff.width = width; -} - -static void serial_rx_buffer_set(serial_t *obj, void *rx, size_t length, uint8_t width) -{ - obj->rx_buff.buffer = rx; - obj->rx_buff.length = length; - obj->rx_buff.pos = 0; - obj->rx_buff.width = width; -} - -static void serial_tx_enable_interrupt(serial_t *obj, uint32_t handler, uint8_t enable) -{ - const struct nu_modinit_s *modinit = get_modinit(obj->serial.uart, uart_modinit_tab); - MBED_ASSERT(modinit != NULL); - MBED_ASSERT(modinit->modname == obj->serial.uart); - - // Necessary for both interrupt way and DMA way - struct nu_uart_var *var = (struct nu_uart_var *) modinit->var; - // With our own async vector, tx/rx handlers can be different. - obj->serial.vec = var->vec_async; - obj->serial.irq_handler_tx_async = (void (*)(void)) handler; - serial_irq_set(obj, TxIrq, enable); -} - -static void serial_rx_enable_interrupt(serial_t *obj, uint32_t handler, uint8_t enable) -{ - const struct nu_modinit_s *modinit = get_modinit(obj->serial.uart, uart_modinit_tab); - MBED_ASSERT(modinit != NULL); - MBED_ASSERT(modinit->modname == obj->serial.uart); - - // Necessary for both interrupt way and DMA way - struct nu_uart_var *var = (struct nu_uart_var *) modinit->var; - // With our own async vector, tx/rx handlers can be different. - obj->serial.vec = var->vec_async; - obj->serial.irq_handler_rx_async = (void (*) (void)) handler; - serial_irq_set(obj, RxIrq, enable); -} - -static void serial_check_dma_usage(DMAUsage *dma_usage, int *dma_ch) -{ - if (*dma_usage != DMA_USAGE_NEVER) { - if (*dma_ch == DMA_ERROR_OUT_OF_CHANNELS) { - *dma_ch = dma_channel_allocate(DMA_CAP_NONE); - } - if (*dma_ch == DMA_ERROR_OUT_OF_CHANNELS) { - *dma_usage = DMA_USAGE_NEVER; - } - } - else { - dma_channel_free(*dma_ch); - *dma_ch = DMA_ERROR_OUT_OF_CHANNELS; - } -} - -static int serial_is_irq_en(serial_t *obj, SerialIrq irq) -{ - int inten_msk = 0; - - switch (irq) { - case RxIrq: - inten_msk = obj->serial.inten_msk & (UART_INTEN_RDAIEN_Msk | UART_INTEN_RXTOIEN_Msk); - break; - case TxIrq: - inten_msk = obj->serial.inten_msk & UART_INTEN_THREIEN_Msk; - break; - } - - return !! inten_msk; -} - -#endif // #if DEVICE_SERIAL_ASYNCH -#endif // #if DEVICE_SERIAL