t
Fork of mbed-dev by
targets/TARGET_Maxim/TARGET_MAX32625/mxc/uart.c
- Committer:
- amithy
- Date:
- 2017-11-09
- Revision:
- 178:c26431f84b0d
- Parent:
- 150:02e0a0aed4ec
File content as of revision 178:c26431f84b0d:
/******************************************************************************* * Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the name of Maxim Integrated * Products, Inc. shall not be used except as stated in the Maxim Integrated * Products, Inc. Branding Policy. * * The mere transfer of this software does not imply any licenses * of trade secrets, proprietary technology, copyrights, patents, * trademarks, maskwork rights, or any other form of intellectual * property whatsoever. Maxim Integrated Products, Inc. retains all * ownership rights. * * $Date: 2016-07-28 15:01:10 -0500 (Thu, 28 Jul 2016) $ * $Revision: 23823 $ * ******************************************************************************/ /** * @file uart.c * @brief UART diver source. */ /***** Includes *****/ #include <string.h> #include "mxc_assert.h" #include "mxc_lock.h" #include "mxc_sys.h" #include "uart.h" /***** Definitions *****/ #define UART_ERRORS (MXC_F_UART_INTEN_RX_FIFO_OVERFLOW | \ MXC_F_UART_INTEN_RX_FRAMING_ERR | \ MXC_F_UART_INTEN_RX_PARITY_ERR) #define UART_READ_INTS (MXC_F_UART_INTEN_RX_FIFO_AF | \ MXC_F_UART_INTEN_RX_FIFO_NOT_EMPTY | \ MXC_F_UART_INTEN_RX_STALLED | \ UART_ERRORS) #define UART_WRITE_INTS (MXC_F_UART_INTEN_TX_UNSTALLED | \ MXC_F_UART_INTEN_TX_FIFO_AE) #define UART_RXFIFO_USABLE (MXC_UART_FIFO_DEPTH-3) /***** Globals *****/ // Saves the state of the non-blocking read requests static uart_req_t *rx_states[MXC_CFG_UART_INSTANCES]; // Saves the state of the non-blocking write requests static uart_req_t *tx_states[MXC_CFG_UART_INSTANCES]; /***** Functions *****/ static void UART_WriteHandler(mxc_uart_regs_t *uart, uart_req_t *req, int uart_num); static void UART_ReadHandler(mxc_uart_regs_t *uart, uart_req_t *req, int uart_num, uint32_t flags); /******************************************************************************/ int UART_Init(mxc_uart_regs_t *uart, const uart_cfg_t *cfg, const sys_cfg_uart_t *sys_cfg) { int err; int uart_num; uint32_t uart_clk; uint8_t baud_shift; uint16_t baud_div; uint32_t baud, diff_baud; uint32_t baud_1, diff_baud_1; // Check the input parameters uart_num = MXC_UART_GET_IDX(uart); MXC_ASSERT(uart_num >= 0); // Set system level configurations if(sys_cfg != NULL) { if ((err = SYS_UART_Init(uart, cfg, sys_cfg)) != E_NO_ERROR) { return err; } } // Initialize state pointers rx_states[uart_num] = NULL; tx_states[uart_num] = NULL; // Drain FIFOs and enable UART uart->ctrl = 0; uart->ctrl = (MXC_F_UART_CTRL_UART_EN | MXC_F_UART_CTRL_TX_FIFO_EN | MXC_F_UART_CTRL_RX_FIFO_EN | (UART_RXFIFO_USABLE << MXC_F_UART_CTRL_RTS_LEVEL_POS)); // Configure data size, stop bit, parity, cts, and rts uart->ctrl |= ((cfg->size << MXC_F_UART_CTRL_DATA_SIZE_POS) | (cfg->extra_stop << MXC_F_UART_CTRL_EXTRA_STOP_POS) | (cfg->parity << MXC_F_UART_CTRL_PARITY_POS) | (cfg->cts << MXC_F_UART_CTRL_CTS_EN_POS) | (cfg->rts << MXC_F_UART_CTRL_RTS_EN_POS)); // Configure the baud rate and divisor uart_clk = SYS_UART_GetFreq(uart); MXC_ASSERT(uart_clk > 0); baud_shift = 2; baud_div = (uart_clk / (cfg->baud * 4)); // Can not support higher frequencies if(!baud_div) { return E_NOT_SUPPORTED; } // Decrease the divisor if baud_div is overflowing while(baud_div > 0xFF) { if(baud_shift == 0) { return E_NOT_SUPPORTED; } baud_shift--; baud_div = (uart_clk / (cfg->baud * (16 >> baud_shift))); } // Adjust baud_div so we don't overflow with the calculations below if(baud_div == 0xFF) { baud_div = 0xFE; } if(baud_div == 0) { baud_div = 1; } // Figure out if the truncation increased the error baud = (uart_clk / (baud_div * (16 >> baud_shift))); baud_1 = (uart_clk / ((baud_div+1) * (16 >> baud_shift))); if(cfg->baud > baud) { diff_baud = cfg->baud - baud; } else { diff_baud = baud - cfg->baud; } if(cfg->baud > baud_1) { diff_baud_1 = cfg->baud - baud_1; } else { diff_baud_1 = baud_1 - cfg->baud; } if(diff_baud < diff_baud_1) { uart->baud = ((baud_div & MXC_F_UART_BAUD_BAUD_DIVISOR) | (baud_shift << MXC_F_UART_BAUD_BAUD_MODE_POS)); } else { uart->baud = (((baud_div+1) & MXC_F_UART_BAUD_BAUD_DIVISOR) | (baud_shift << MXC_F_UART_BAUD_BAUD_MODE_POS)); } return E_NO_ERROR; } /******************************************************************************/ int UART_Shutdown(mxc_uart_regs_t *uart) { int uart_num, err; uart_req_t *temp_req; uart_num = MXC_UART_GET_IDX(uart); MXC_ASSERT(uart_num >= 0); // Disable and clear interrupts uart->inten = 0; uart->intfl = uart->intfl; // Disable UART and FIFOS uart->ctrl &= ~(MXC_F_UART_CTRL_UART_EN | MXC_F_UART_CTRL_TX_FIFO_EN | MXC_F_UART_CTRL_RX_FIFO_EN); // Call all of the pending callbacks for this UART if(rx_states[uart_num] != NULL) { // Save the request temp_req = rx_states[uart_num]; // Unlock this UART to read mxc_free_lock((uint32_t*)&rx_states[uart_num]); // Callback if not NULL if(temp_req->callback != NULL) { temp_req->callback(temp_req, E_SHUTDOWN); } } if(tx_states[uart_num] != NULL) { // Save the request temp_req = tx_states[uart_num]; // Unlock this UART to write mxc_free_lock((uint32_t*)&tx_states[uart_num]); // Callback if not NULL if(temp_req->callback != NULL) { temp_req->callback(temp_req, E_SHUTDOWN); } } // Clears system level configurations if ((err = SYS_UART_Shutdown(uart)) != E_NO_ERROR) { return err; } return E_NO_ERROR; } /******************************************************************************/ int UART_Write(mxc_uart_regs_t *uart, uint8_t* data, int len) { int num, uart_num; mxc_uart_fifo_regs_t *fifo; uart_num = MXC_UART_GET_IDX(uart); MXC_ASSERT(uart_num >= 0); if(data == NULL) { return E_NULL_PTR; } // Make sure the UART has been initialized if(!(uart->ctrl & MXC_F_UART_CTRL_UART_EN)) { return E_UNINITIALIZED; } if(!(len > 0)) { return E_NO_ERROR; } // Lock this UART from writing while(mxc_get_lock((uint32_t*)&tx_states[uart_num], 1) != E_NO_ERROR) {} // Get the FIFO for this UART fifo = MXC_UART_GET_FIFO(uart_num); num = 0; while(num < len) { // Wait for TXFIFO to not be full while((uart->tx_fifo_ctrl & MXC_F_UART_TX_FIFO_CTRL_FIFO_ENTRY) == MXC_F_UART_TX_FIFO_CTRL_FIFO_ENTRY) {} // Write the data to the FIFO #if(MXC_UART_REV == 0) uart->intfl = MXC_F_UART_INTFL_TX_DONE; #endif fifo->tx = data[num++]; } // Unlock this UART to write mxc_free_lock((uint32_t*)&tx_states[uart_num]); return num; } /******************************************************************************/ int UART_Read(mxc_uart_regs_t *uart, uint8_t* data, int len, int *num) { int num_local, remain, uart_num; mxc_uart_fifo_regs_t *fifo; uart_num = MXC_UART_GET_IDX(uart); MXC_ASSERT(uart_num >= 0); if(data == NULL) { return E_NULL_PTR; } // Make sure the UART has been initialized if(!(uart->ctrl & MXC_F_UART_CTRL_UART_EN)) { return E_UNINITIALIZED; } if(!(len > 0)) { return E_NO_ERROR; } // Lock this UART from reading while(mxc_get_lock((uint32_t*)&rx_states[uart_num], 1) != E_NO_ERROR) {} // Get the FIFO for this UART fifo = MXC_UART_GET_FIFO(uart_num); num_local = 0; remain = len; while(remain) { // Save the data in the FIFO while((uart->rx_fifo_ctrl & MXC_F_UART_RX_FIFO_CTRL_FIFO_ENTRY) && remain) { data[num_local] = fifo->rx; num_local++; remain--; } // Break if there is an error if(uart->intfl & UART_ERRORS) { break; } } // Save the number of bytes read if pointer is valid if(num != NULL) { *num = num_local; } // Check for errors if(uart->intfl & MXC_F_UART_INTFL_RX_FIFO_OVERFLOW) { // Clear errors and return error code uart->intfl = UART_ERRORS; // Unlock this UART to read mxc_free_lock((uint32_t*)&rx_states[uart_num]); return E_OVERFLOW; } else if(uart->intfl & (MXC_F_UART_INTFL_RX_FRAMING_ERR | MXC_F_UART_INTFL_RX_PARITY_ERR)) { // Clear errors and return error code uart->intfl = UART_ERRORS; // Unlock this UART to read mxc_free_lock((uint32_t*)&rx_states[uart_num]); return E_COMM_ERR; } // Unlock this UART to read mxc_free_lock((uint32_t*)&rx_states[uart_num]); return num_local; } /******************************************************************************/ int UART_WriteAsync(mxc_uart_regs_t *uart, uart_req_t *req) { int uart_num = MXC_UART_GET_IDX(uart); MXC_ASSERT(uart_num >= 0); // Check the input parameters if(req->data == NULL) { return E_NULL_PTR; } // Make sure the UART has been initialized if(!(uart->ctrl & MXC_F_UART_CTRL_UART_EN)) { return E_UNINITIALIZED; } if(!(req->len > 0)) { return E_NO_ERROR; } // Attempt to register this write request if(mxc_get_lock((uint32_t*)&tx_states[uart_num], (uint32_t)req) != E_NO_ERROR) { return E_BUSY; } // Clear the number of bytes counter req->num = 0; // Start the write UART_WriteHandler(uart, req, uart_num); return E_NO_ERROR; } /******************************************************************************/ int UART_ReadAsync(mxc_uart_regs_t *uart, uart_req_t *req) { int uart_num; uint32_t flags; uart_num = MXC_UART_GET_IDX(uart); MXC_ASSERT(uart_num >= 0); if(req->data == NULL) { return E_NULL_PTR; } // Make sure the UART has been initialized if(!(uart->ctrl & MXC_F_UART_CTRL_UART_EN)) { return E_UNINITIALIZED; } if(!(req->len > 0)) { return E_NO_ERROR; } // Attempt to register this write request if(mxc_get_lock((uint32_t*)&rx_states[uart_num], (uint32_t)req) != E_NO_ERROR) { return E_BUSY; } // Clear the number of bytes counter req->num = 0; // Start the read flags = uart->intfl; uart->intfl = flags; UART_ReadHandler(uart, req, uart_num, flags); return E_NO_ERROR; } /******************************************************************************/ int UART_AbortAsync(uart_req_t *req) { int uart_num; // Figure out if this was a read or write request, find the request, set to NULL for(uart_num = 0; uart_num < MXC_CFG_UART_INSTANCES; uart_num++) { if(req == rx_states[uart_num]) { // Disable read interrupts, clear flags. MXC_UART_GET_UART(uart_num)->inten &= ~UART_READ_INTS; MXC_UART_GET_UART(uart_num)->intfl = UART_READ_INTS; // Unlock this UART to read mxc_free_lock((uint32_t*)&rx_states[uart_num]); // Callback if not NULL if(req->callback != NULL) { req->callback(req, E_ABORT); } return E_NO_ERROR; } if(req == tx_states[uart_num]) { // Disable write interrupts, clear flags. MXC_UART_GET_UART(uart_num)->inten &= ~(UART_WRITE_INTS); MXC_UART_GET_UART(uart_num)->intfl = UART_WRITE_INTS; // Unlock this UART to write mxc_free_lock((uint32_t*)&tx_states[uart_num]); // Callback if not NULL if(req->callback != NULL) { req->callback(req, E_ABORT); } return E_NO_ERROR; } } return E_BAD_PARAM; } /******************************************************************************/ void UART_Handler(mxc_uart_regs_t *uart) { int uart_num; uint32_t flags; uart_num = MXC_UART_GET_IDX(uart); MXC_ASSERT(uart_num >= 0); flags = uart->intfl; uart->intfl = flags; // Figure out if this UART has an active Read request if((rx_states[uart_num] != NULL) && (flags & UART_READ_INTS)) { UART_ReadHandler(uart, rx_states[uart_num], uart_num, flags); } // Figure out if this UART has an active Write request if((tx_states[uart_num] != NULL) && (flags & (UART_WRITE_INTS))) { UART_WriteHandler(uart, tx_states[uart_num], uart_num); } } /******************************************************************************/ int UART_Busy(mxc_uart_regs_t *uart) { int uart_num = MXC_UART_GET_IDX(uart); MXC_ASSERT(uart_num >= 0); // Check to see if there are any ongoing transactions or if the UART is disabled if(((tx_states[uart_num] == NULL) && !(uart->tx_fifo_ctrl & MXC_F_UART_TX_FIFO_CTRL_FIFO_ENTRY) && #if(MXC_UART_REV == 0) (uart->intfl & MXC_F_UART_INTFL_TX_DONE)) || #else (uart->idle & MXC_F_UART_IDLE_TX_RX_IDLE)) || #endif !(uart->ctrl & MXC_F_UART_CTRL_UART_EN)) { return E_NO_ERROR; } return E_BUSY; } /******************************************************************************/ int UART_PrepForSleep(mxc_uart_regs_t *uart) { if(UART_Busy(uart) != E_NO_ERROR) { return E_BUSY; } // Leave read interrupts enabled, if already enabled uart->inten &= UART_READ_INTS; return E_NO_ERROR; } /******************************************************************************/ static void UART_WriteHandler(mxc_uart_regs_t *uart, uart_req_t *req, int uart_num) { int avail, remain; mxc_uart_fifo_regs_t *fifo; // Disable write interrupts uart->inten &= ~(UART_WRITE_INTS); // Get the FIFO for this UART fifo = MXC_UART_GET_FIFO(uart_num); // Refill the TX FIFO avail = UART_NumWriteAvail(uart); remain = req->len - req->num; while(avail && remain) { // Write the data to the FIFO #if(MXC_UART_REV == 0) uart->intfl = MXC_F_UART_INTFL_TX_DONE; #endif fifo->tx = req->data[req->num++]; remain--; avail--; } // All of the bytes have been written to the FIFO if(!remain) { // Unlock this UART to write mxc_free_lock((uint32_t*)&tx_states[uart_num]); if(req->callback != NULL) { req->callback(req, E_NO_ERROR); } } else { // Interrupt when there is one byte left in the TXFIFO uart->tx_fifo_ctrl = ((MXC_UART_FIFO_DEPTH - 1) << MXC_F_UART_TX_FIFO_CTRL_FIFO_AE_LVL_POS); // Enable almost empty interrupt uart->inten |= (MXC_F_UART_INTEN_TX_FIFO_AE); } } /******************************************************************************/ static void UART_ReadHandler(mxc_uart_regs_t *uart, uart_req_t *req, int uart_num, uint32_t flags) { int avail, remain; mxc_uart_fifo_regs_t *fifo; // Disable interrupts uart->inten &= ~UART_READ_INTS; // Get the FIFO for this UART, uart_num fifo = MXC_UART_GET_FIFO(uart_num); // Save the data in the FIFO while we still need data avail = UART_NumReadAvail(uart); remain = req->len - req->num; while(avail && remain) { req->data[req->num++] = fifo->rx; remain--; avail--; } // Check for errors if(flags & MXC_F_UART_INTFL_RX_FIFO_OVERFLOW) { // Unlock this UART to read mxc_free_lock((uint32_t*)&rx_states[uart_num]); if(req->callback != NULL) { req->callback(req, E_OVERFLOW); } return; } if(flags & (MXC_F_UART_INTFL_RX_FRAMING_ERR | MXC_F_UART_INTFL_RX_PARITY_ERR)) { // Unlock this UART to read mxc_free_lock((uint32_t*)&rx_states[uart_num]); if(req->callback != NULL) { req->callback(req, E_COMM_ERR); } return; } // Check to see if we're done receiving if(remain == 0) { // Unlock this UART to read mxc_free_lock((uint32_t*)&rx_states[uart_num]); if(req->callback != NULL) { req->callback(req, E_NO_ERROR); } return; } if(remain == 1) { uart->inten |= (MXC_F_UART_INTEN_RX_FIFO_NOT_EMPTY | UART_ERRORS); } else { // Set the RX FIFO AF threshold if(remain < UART_RXFIFO_USABLE) { uart->rx_fifo_ctrl = ((remain - 1) << MXC_F_UART_RX_FIFO_CTRL_FIFO_AF_LVL_POS); } else { uart->rx_fifo_ctrl = (UART_RXFIFO_USABLE << MXC_F_UART_RX_FIFO_CTRL_FIFO_AF_LVL_POS); } uart->inten |= (MXC_F_UART_INTEN_RX_FIFO_AF | UART_ERRORS); } }