| /* ------------------------------------------ |
| * Copyright (c) 2016, Synopsys, Inc. All rights reserved. |
| |
| * 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. |
| |
| * 3) Neither the name of the Synopsys, Inc., nor the names of its contributors may |
| * be used to endorse or promote products derived from this software without |
| * specific prior written permission. |
| |
| * 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 HOLDER 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. |
| * |
| * \version 2016.05 |
| * \date 2014-06-20 |
| * \author Huaqi Fang(Huaqi.Fang@synopsys.com) |
| --------------------------------------------- */ |
| |
| /** |
| * \defgroup DEVICE_DW_UART Designware UART Driver |
| * \ingroup DEVICE_DW |
| * \brief Designware UART Driver Implementation |
| */ |
| |
| /** |
| * \file |
| * \ingroup DEVICE_DW_UART |
| * \brief DesignWare UART driver implementation based on device hal layer definition (\ref dev_uart.h) |
| */ |
| #include <string.h> |
| |
| #include "inc/embARC_toolchain.h" |
| #include "inc/embARC_error.h" |
| |
| #include "inc/arc/arc_exception.h" |
| |
| #include "device/designware/uart/dw_uart_hal.h" |
| #include "device/designware/uart/dw_uart.h" |
| |
| |
| /** |
| * \name DesignWare UART Driver Macros |
| * \brief DesignWare UART driver macros used in uart driver |
| * @{ |
| */ |
| /** check expressions used in DesignWare UART driver implementation */ |
| #define DW_UART_CHECK_EXP(EXPR, ERROR_CODE) CHECK_EXP(EXPR, ercd, ERROR_CODE, error_exit) |
| |
| #ifndef DISABLE_DEVICE_OBJECT_VALID_CHECK |
| /** valid check of uart info object */ |
| #define VALID_CHK_UART_INFO_OBJECT(uartinfo_obj_ptr) { \ |
| DW_UART_CHECK_EXP((uartinfo_obj_ptr)!=NULL, E_OBJ); \ |
| DW_UART_CHECK_EXP(((uartinfo_obj_ptr)->uart_ctrl)!=NULL, E_OBJ); \ |
| } |
| #endif |
| |
| /** convert DesignWare baudrate to divisor */ |
| #define DW_UART_BAUD2DIV(perifreq, baud) ((perifreq) / ((baud)*16)) |
| |
| /** |
| * \name DesignWare UART Interrupt Callback Routine Select Marcos |
| * \brief DesignWare UART interrupt callback routines select macros definitions |
| * @{ |
| */ |
| #define DW_UART_RDY_SND (1U) /*!< ready to send callback */ |
| #define DW_UART_RDY_RCV (2U) /*!< ready to receive callback */ |
| /** @} */ |
| |
| /** @} */ |
| |
| /** |
| * \defgroup DEVICE_DW_UART_STATIC DesignWare UART Driver Static Functions |
| * \ingroup DEVICE_DW_UART |
| * \brief Static or inline functions, variables for DesignWare UART handle uart operations, |
| * only used in this file |
| * @{ |
| */ |
| const uint8_t dw_uart_databits[] = { \ |
| DW_UART_LCR_WORD_LEN5, DW_UART_LCR_WORD_LEN6, \ |
| DW_UART_LCR_WORD_LEN7, DW_UART_LCR_WORD_LEN8}; |
| const uint8_t dw_uart_parity[] = { |
| DW_UART_LCR_PARITY_NONE, DW_UART_LCR_PARITY_ODD, |
| DW_UART_LCR_PARITY_EVEN, DW_UART_LCR_PARITY_MASK, |
| DW_UART_LCR_PARITY_SPACE |
| }; |
| const uint8_t dw_uart_stopbits[] = { |
| DW_UART_LCR_1_STOP_BIT, DW_UART_LCR_1D5_STOP_BIT, |
| DW_UART_LCR_2_STOP_BIT |
| }; |
| |
| /** test whether uart is ready to send, 1 ready, 0 not ready */ |
| Inline int32_t dw_uart_putready(DW_UART_REG *uart_reg_ptr) |
| { |
| return ((uart_reg_ptr->USR & DW_UART_USR_TFNF) != 0); |
| } |
| /** test whether uart is ready to receive, 1 ready, 0 not ready */ |
| Inline int32_t dw_uart_getready(DW_UART_REG *uart_reg_ptr) |
| { |
| return ((uart_reg_ptr->USR & DW_UART_USR_RFNE) != 0); |
| } |
| /** write char to uart send fifo */ |
| Inline void dw_uart_putchar(DW_UART_REG *uart_reg_ptr, char chr) |
| { |
| uart_reg_ptr->DATA = chr; |
| } |
| /** read data from uart receive fifo, return data received */ |
| Inline int32_t dw_uart_getchar(DW_UART_REG *uart_reg_ptr) |
| { |
| return (int32_t)uart_reg_ptr->DATA; |
| } |
| /** |
| * \brief send char by uart when available, |
| * mostly used in interrupt method, non-blocked function |
| * \param[in] uart_reg_ptr uart register structure pointer |
| * \param[in] chr char to be sent |
| * \retval 0 send successfully |
| * \retval -1 not ready to send data |
| */ |
| Inline int32_t dw_uart_snd_chr(DW_UART_REG *uart_reg_ptr, char chr) |
| { |
| if (dw_uart_putready(uart_reg_ptr)) { |
| dw_uart_putchar(uart_reg_ptr, chr); |
| return 0; |
| } |
| return -1; |
| } |
| /** |
| * \brief receive one char from uart, |
| * mostly used in interrupt routine, non-blocked function |
| * \param[in] uart_reg_ptr uart register structure pointer |
| * \return data received by the uart |
| */ |
| Inline int32_t dw_uart_rcv_chr(DW_UART_REG *uart_reg_ptr) |
| { |
| return dw_uart_getchar(uart_reg_ptr); |
| } |
| /** |
| * \brief send char by uart in poll method, blocked function |
| * \param[in] uart_reg_ptr uart register structure pointer |
| * \param[in] chr char to be sent |
| */ |
| Inline void dw_uart_psnd_chr(DW_UART_REG *uart_reg_ptr, char chr) |
| { |
| /** wait until uart is ready to send */ |
| while (!dw_uart_putready(uart_reg_ptr)); /* blocked */ |
| /** send char */ |
| dw_uart_putchar(uart_reg_ptr, chr); |
| } |
| /** |
| * \brief receive one char from uart in poll method, blocked function |
| * \param[in] uart_reg_ptr uart register structure pointer |
| * \return data received by the uart |
| */ |
| Inline int32_t dw_uart_prcv_chr(DW_UART_REG *uart_reg_ptr) |
| { |
| /** wait until uart is ready to receive */ |
| while (!dw_uart_getready(uart_reg_ptr)); /* blocked */ |
| /** receive data */ |
| return dw_uart_getchar(uart_reg_ptr); |
| } |
| |
| /** Get TX FIFO Length */ |
| Inline uint32_t dw_uart_get_txfifo_len(DW_UART_REG *uart_reg_ptr) |
| { |
| uint32_t txfifolen; |
| uint32_t uart_cpr; |
| |
| uart_cpr = uart_reg_ptr->CPR; |
| if (uart_cpr & DW_UART_CPR_FIFO_STAT) { |
| txfifolen = ((uart_cpr & DW_UART_CPR_FIFO_MODE) >> DW_UART_CPR_FIFO_MODE_OFS) << 4; |
| } else { |
| txfifolen = 0; |
| } |
| |
| return txfifolen; |
| } |
| |
| /** Get RX FIFO Length */ |
| Inline uint32_t dw_uart_get_rxfifo_len(DW_UART_REG *uart_reg_ptr) |
| { |
| uint32_t rxfifolen; |
| uint32_t uart_cpr; |
| |
| uart_cpr = uart_reg_ptr->CPR; |
| if (uart_cpr & DW_UART_CPR_FIFO_STAT) { |
| rxfifolen = ((uart_cpr & DW_UART_CPR_FIFO_MODE) >> DW_UART_CPR_FIFO_MODE_OFS) << 4; |
| } else { |
| rxfifolen = 0; |
| } |
| |
| return rxfifolen; |
| } |
| |
| /** |
| * \brief set designware uart DPS value |
| * \param uart_reg_ptr uart register structure |
| * \param dps data bits/parity bit/stop bits parameter |
| * \retval 0 Set ok |
| * \retval !0 Set failed |
| */ |
| static int32_t dw_uart_set_dps(DW_UART_REG *uart_reg_ptr, UART_DPS_FORMAT *dps) |
| { |
| uint32_t dps_value = 0; |
| |
| if (dps == NULL) return -1; |
| /* data bits check */ |
| if ((dps->databits < 5) || (dps->databits > 8)) return -1; |
| /* stop bits check */ |
| if (dps->stopbits > UART_STPBITS_TWO) return -1; |
| /* parity bit type check */ |
| if (dps->parity > UART_PARITY_SPACE) return -1; |
| |
| dps_value |= (uint32_t)dw_uart_databits[dps->databits-5]; |
| dps_value |= (uint32_t)dw_uart_stopbits[dps->stopbits]; |
| dps_value |= (uint32_t)dw_uart_parity[dps->parity]; |
| |
| /* clear dps bits */ |
| uart_reg_ptr->LCR &= (~DW_UART_LCR_DPS_MASK); |
| /* set dps bits */ |
| uart_reg_ptr->LCR |= dps_value; |
| |
| return 0; |
| } |
| |
| /** |
| * \brief set designware uart baudrate |
| * \param uart_reg_ptr uart register structure |
| * \param baud_divisor uart baudrate divisor |
| */ |
| static void dw_uart_set_baud(DW_UART_REG *uart_reg_ptr, uint32_t baud_divisor) |
| { |
| /* enable uart baudrate update */ |
| uart_reg_ptr->LCR |= DW_UART_LCR_DLAB; |
| /** |
| * setting uart baudrate registers |
| */ |
| uart_reg_ptr->DATA = baud_divisor & 0xff; /*!< DLL */ |
| uart_reg_ptr->IER = (baud_divisor>>8) & 0xff; /*!< DLH */ |
| /** disable DLAB */ |
| uart_reg_ptr->LCR &= ~(DW_UART_LCR_DLAB); |
| } |
| |
| /** |
| * \brief Do uart software reset |
| * \param uart_reg_ptr uart register structure |
| */ |
| Inline void dw_uart_software_reset(DW_UART_REG *uart_reg_ptr) |
| { |
| uart_reg_ptr->SRR = DW_UART_SRR_UR|DW_UART_SRR_RFR|DW_UART_SRR_XFR; |
| while(uart_reg_ptr->USR & DW_UART_USR_BUSY); /* wait until software reset completed */ |
| } |
| |
| /** |
| * \brief set designware uart baudrate |
| * \param uart_reg_ptr uart register structure |
| * \param hwfc uart hardware flow control type |
| * \note Need to set corresponding pin functions |
| */ |
| static void dw_uart_set_hwfc(DW_UART_REG *uart_reg_ptr, UART_HW_FLOW_CONTROL hwfc) |
| { |
| if (hwfc == UART_FC_NONE) { |
| uart_reg_ptr->MCR &= ~(DW_UART_MCR_AFCE|DW_UART_MCR_RTS); |
| } |
| if ((hwfc == UART_FC_RTS) || (hwfc == UART_FC_BOTH)) { |
| uart_reg_ptr->MCR |= (DW_UART_MCR_AFCE|DW_UART_MCR_RTS); |
| } |
| if ((hwfc == UART_FC_CTS) || (hwfc == UART_FC_BOTH)) { |
| uart_reg_ptr->MCR |= (DW_UART_MCR_AFCE); |
| } |
| } |
| |
| Inline void dw_uart_set_break(DW_UART_REG *uart_reg_ptr) |
| { |
| uart_reg_ptr->LCR |= DW_UART_LCR_BREAK; |
| } |
| |
| Inline void dw_uart_clr_break(DW_UART_REG *uart_reg_ptr) |
| { |
| uart_reg_ptr->LCR &= ~DW_UART_LCR_BREAK; |
| } |
| |
| /** |
| * \brief init designware uart with selected baud |
| * \param[in] uart_reg_ptr uart register structure pointer |
| * \param[in] baud_divisor baudrate divisor |
| */ |
| static void dw_uart_init(DW_UART_REG *uart_reg_ptr, uint32_t baud_divisor, UART_DPS_FORMAT *dps, UART_HW_FLOW_CONTROL hwfc) |
| { |
| dw_uart_software_reset(uart_reg_ptr); |
| |
| dw_uart_set_hwfc(uart_reg_ptr, hwfc); |
| dw_uart_set_dps(uart_reg_ptr, dps); |
| dw_uart_set_baud(uart_reg_ptr, baud_divisor); |
| |
| uart_reg_ptr->IIR = 0x1; /** enable uart fifo (FCR IIR is the same) */ |
| uart_reg_ptr->IER = 0x0; /** disable all uart interrupt */ |
| } |
| |
| /** |
| * \brief set designware uart baudrate |
| * \param uart_info_ptr uart information structure pointer |
| */ |
| static void dw_uart_flush_output(DEV_UART_INFO *uart_info_ptr) |
| { |
| uint32_t i; |
| char *p_charbuf; |
| |
| DW_UART_CTRL *uart_ctrl_ptr = (DW_UART_CTRL_PTR)(uart_info_ptr->uart_ctrl); |
| DW_UART_REG *uart_reg_ptr = (DW_UART_REG_PTR)(uart_ctrl_ptr->dw_uart_regbase); |
| |
| if (uart_info_ptr->tx_buf.buf != NULL) { |
| p_charbuf = (char *)(uart_info_ptr->tx_buf.buf); |
| for (i = uart_info_ptr->tx_buf.ofs; i < uart_info_ptr->tx_buf.len; i ++) { |
| dw_uart_psnd_chr(uart_reg_ptr, p_charbuf[i]); |
| } |
| /* clear transmit buffer */ |
| uart_info_ptr->tx_buf.buf = NULL; |
| uart_info_ptr->tx_buf.len = 0; |
| uart_info_ptr->tx_buf.ofs = 0; |
| } |
| /* wait until transmit fifo is empty */ |
| while ((uart_reg_ptr->USR & DW_UART_USR_TFE) == 0); |
| while (uart_reg_ptr->USR & DW_UART_USR_BUSY); |
| } |
| |
| /** |
| * \brief disable designware uart send or receive interrupt |
| * \param[in] DEV_UART_INFO *uart_info_ptr |
| * \param[in] cbrtn control code of callback routine of send or receive |
| */ |
| static void dw_uart_dis_cbr(DEV_UART_INFO *uart_info_ptr, uint32_t cbrtn) |
| { |
| DW_UART_CTRL *uart_ctrl_ptr = (DW_UART_CTRL_PTR)(uart_info_ptr->uart_ctrl); |
| DW_UART_REG *uart_reg_ptr = (DW_UART_REG_PTR)(uart_ctrl_ptr->dw_uart_regbase); |
| |
| switch (cbrtn) { |
| case DW_UART_RDY_SND: |
| uart_reg_ptr->IER &= ~DW_UART_IER_XMIT_EMPTY; |
| uart_ctrl_ptr->int_status &= ~DW_UART_TXINT_ENABLE; |
| break; |
| case DW_UART_RDY_RCV: |
| uart_reg_ptr->IER &= ~DW_UART_IER_DATA_AVAIL; |
| uart_ctrl_ptr->int_status &= ~DW_UART_RXINT_ENABLE; |
| break; |
| default: |
| break; |
| } |
| if (uart_ctrl_ptr->int_status & DW_UART_GINT_ENABLE) { |
| if ((uart_ctrl_ptr->int_status & (DW_UART_RXINT_ENABLE|DW_UART_TXINT_ENABLE)) == 0) { |
| int_disable(uart_ctrl_ptr->intno); |
| uart_ctrl_ptr->int_status &= ~DW_UART_GINT_ENABLE; |
| } |
| } |
| } |
| |
| /** |
| * \brief enable DesignWare UART send or receive interrupt |
| * \param[in] DEV_UART_INFO *uart_info_ptr |
| * \param[in] cbrtn control code of callback routine of send or receive |
| */ |
| static void dw_uart_ena_cbr(DEV_UART_INFO *uart_info_ptr, uint32_t cbrtn) |
| { |
| DW_UART_CTRL *uart_ctrl_ptr = (DW_UART_CTRL_PTR)(uart_info_ptr->uart_ctrl); |
| DW_UART_REG *uart_reg_ptr = (DW_UART_REG_PTR)(uart_ctrl_ptr->dw_uart_regbase); |
| |
| switch (cbrtn) { |
| case DW_UART_RDY_SND: |
| uart_ctrl_ptr->int_status |= DW_UART_TXINT_ENABLE; |
| uart_reg_ptr->IER |= DW_UART_IER_XMIT_EMPTY; |
| break; |
| case DW_UART_RDY_RCV: |
| uart_ctrl_ptr->int_status |= DW_UART_RXINT_ENABLE; |
| uart_reg_ptr->IER |= DW_UART_IER_DATA_AVAIL; |
| break; |
| default: |
| break; |
| } |
| if ((uart_ctrl_ptr->int_status & DW_UART_GINT_ENABLE) == 0) { |
| if (uart_ctrl_ptr->int_status & (DW_UART_RXINT_ENABLE|DW_UART_TXINT_ENABLE)) { |
| uart_ctrl_ptr->int_status |= DW_UART_GINT_ENABLE; |
| int_enable(uart_ctrl_ptr->intno); |
| } |
| } |
| } |
| |
| /** |
| * \brief enable designware uart interrupt |
| * \param uart_info_ptr uart information structure pointer |
| */ |
| static void dw_uart_enable_interrupt(DEV_UART_INFO *uart_info_ptr) |
| { |
| DW_UART_CTRL *uart_ctrl_ptr = (DW_UART_CTRL_PTR)(uart_info_ptr->uart_ctrl); |
| |
| int_handler_install(uart_ctrl_ptr->intno, uart_ctrl_ptr->dw_uart_int_handler); |
| uart_ctrl_ptr->int_status |= DW_UART_GINT_ENABLE; |
| int_enable(uart_ctrl_ptr->intno); /** enable uart interrupt */ |
| } |
| /** |
| * \brief disable designware uart interrupt |
| * \param uart_info_ptr uart information structure pointer |
| */ |
| static void dw_uart_disable_interrupt(DEV_UART_INFO *uart_info_ptr) |
| { |
| DW_UART_CTRL *uart_ctrl_ptr = (DW_UART_CTRL_PTR)(uart_info_ptr->uart_ctrl); |
| |
| /** disable uart send&receive interrupt after disable uart interrupt */ |
| dw_uart_dis_cbr(uart_info_ptr, DW_UART_RDY_SND); |
| dw_uart_dis_cbr(uart_info_ptr, DW_UART_RDY_RCV); |
| /* disable uart interrupt */ |
| int_disable(uart_ctrl_ptr->intno); |
| uart_ctrl_ptr->int_status &= ~(DW_UART_GINT_ENABLE|DW_UART_TXINT_ENABLE|DW_UART_RXINT_ENABLE); |
| } |
| |
| /** enable designware uart */ |
| static void dw_uart_enable_device(DEV_UART_INFO *uart_info_ptr) |
| { |
| DW_UART_CTRL *uart_ctrl_ptr = (DW_UART_CTRL_PTR)(uart_info_ptr->uart_ctrl); |
| DW_UART_REG *uart_reg_ptr = (DW_UART_REG_PTR)(uart_ctrl_ptr->dw_uart_regbase); |
| |
| if ((uart_info_ptr->status & DEV_ENABLED) == 0) { |
| dw_uart_set_baud(uart_reg_ptr, uart_info_ptr->baudrate); |
| uart_info_ptr->status |= DEV_ENABLED; |
| } |
| } |
| |
| /** disable designware uart */ |
| static void dw_uart_disable_device(DEV_UART_INFO *uart_info_ptr) |
| { |
| DW_UART_CTRL *uart_ctrl_ptr = (DW_UART_CTRL_PTR)(uart_info_ptr->uart_ctrl); |
| DW_UART_REG *uart_reg_ptr = (DW_UART_REG_PTR)(uart_ctrl_ptr->dw_uart_regbase); |
| |
| if ((uart_info_ptr->status & DEV_ENABLED) == DEV_ENABLED) { |
| dw_uart_set_baud(uart_reg_ptr, 0); |
| uart_info_ptr->status &= ~DEV_ENABLED; |
| } |
| } |
| |
| /** abort current interrupt transmit transfer */ |
| static void dw_uart_abort_tx(DEV_UART *uart_obj) |
| { |
| DEV_UART_INFO *uart_info_ptr = &(uart_obj->uart_info); |
| DW_UART_CTRL *uart_ctrl_ptr = (DW_UART_CTRL_PTR)(uart_info_ptr->uart_ctrl); |
| |
| if (uart_ctrl_ptr->int_status & DW_UART_TXINT_ENABLE) { |
| dw_uart_dis_cbr(uart_info_ptr, DW_UART_RDY_SND); |
| uart_info_ptr->status |= DEV_IN_TX_ABRT; |
| if (uart_info_ptr->uart_cbs.tx_cb != NULL) { |
| uart_info_ptr->uart_cbs.tx_cb(uart_obj); |
| } |
| uart_info_ptr->status &= ~(DEV_IN_TX_ABRT); |
| } |
| } |
| |
| /** abort current interrupt receive transfer */ |
| static void dw_uart_abort_rx(DEV_UART *uart_obj) |
| { |
| DEV_UART_INFO *uart_info_ptr = &(uart_obj->uart_info); |
| DW_UART_CTRL *uart_ctrl_ptr = (DW_UART_CTRL_PTR)(uart_info_ptr->uart_ctrl); |
| |
| if (uart_ctrl_ptr->int_status & DW_UART_RXINT_ENABLE) { |
| dw_uart_dis_cbr(uart_info_ptr, DW_UART_RDY_RCV); |
| uart_info_ptr->status |= DEV_IN_RX_ABRT; |
| if (uart_info_ptr->uart_cbs.rx_cb != NULL) { |
| uart_info_ptr->uart_cbs.rx_cb(uart_obj); |
| } |
| uart_info_ptr->status &= ~(DEV_IN_RX_ABRT); |
| } |
| } |
| |
| /** Get available transmit fifo count */ |
| static int32_t dw_uart_get_txavail(DW_UART_CTRL *uart_ctrl_ptr) |
| { |
| int32_t tx_avail = 0; |
| DW_UART_REG *uart_reg_ptr = (DW_UART_REG *)(uart_ctrl_ptr->dw_uart_regbase); |
| |
| if (uart_ctrl_ptr->tx_fifo_len <= 1) { |
| if (dw_uart_putready(uart_reg_ptr) == 1) { |
| tx_avail = 1; |
| } else { |
| tx_avail = 0; |
| } |
| } else { |
| tx_avail = uart_ctrl_ptr->tx_fifo_len - uart_reg_ptr->TFL; |
| } |
| return tx_avail; |
| } |
| |
| /** Get available receive fifo count */ |
| static int32_t dw_uart_get_rxavail(DW_UART_CTRL *uart_ctrl_ptr) |
| { |
| int32_t rx_avail = 0; |
| DW_UART_REG *uart_reg_ptr = (DW_UART_REG *)(uart_ctrl_ptr->dw_uart_regbase); |
| |
| if (uart_ctrl_ptr->rx_fifo_len <= 1) { |
| if (dw_uart_getready(uart_reg_ptr) == 1) { |
| rx_avail = 1; |
| } else { |
| rx_avail = 0; |
| } |
| } else { |
| rx_avail = uart_reg_ptr->RFL; |
| } |
| return rx_avail; |
| } |
| |
| |
| /** @} end of group DEVICE_DW_UART_STATIC */ |
| |
| /** |
| * \brief open a designware uart device |
| * \param[in] uart_obj uart object structure pointer |
| * \param[in] baud baudrate to initialized |
| * \retval E_OK Open successfully without any issues |
| * \retval E_OPNED If device was opened before with different baudrate, then return E_OPNED |
| * \retval E_OBJ Device object is not valid |
| * \retval E_PAR Parameter is not valid |
| * \retval E_NOSPT Open settings are not supported |
| */ |
| int32_t dw_uart_open (DEV_UART *uart_obj, uint32_t baud) |
| { |
| int32_t ercd = E_OK; |
| DEV_UART_INFO *uart_info_ptr = &(uart_obj->uart_info); |
| |
| /* START ERROR CHECK */ |
| VALID_CHK_UART_INFO_OBJECT(uart_info_ptr); |
| DW_UART_CHECK_EXP(baud>0, E_PAR); |
| /* END OF ERROR CHECK */ |
| |
| uart_info_ptr->opn_cnt ++; |
| if (uart_info_ptr->opn_cnt > 1) { /* opened before */ |
| if (baud == uart_info_ptr->baudrate) { /* baudrate is the same */ |
| return E_OK; |
| } else { /* open with different baudrate */ |
| return E_OPNED; |
| } |
| } |
| |
| int32_t baud_divisor = 0; |
| |
| DW_UART_CTRL *uart_ctrl_ptr = (DW_UART_CTRL_PTR)(uart_info_ptr->uart_ctrl); |
| DW_UART_REG *uart_reg_ptr = (DW_UART_REG_PTR)(uart_ctrl_ptr->dw_uart_regbase); |
| |
| /* Get FIFO Length */ |
| uart_ctrl_ptr->tx_fifo_len = dw_uart_get_txfifo_len(uart_reg_ptr); |
| uart_ctrl_ptr->rx_fifo_len = dw_uart_get_rxfifo_len(uart_reg_ptr); |
| |
| /** init uart */ |
| uart_info_ptr->baudrate = baud; |
| baud_divisor = DW_UART_BAUD2DIV(uart_ctrl_ptr->dw_apb_bus_freq, baud); |
| uart_info_ptr->dps_format = dps_format_default; |
| uart_info_ptr->hwfc = hwfc_default; |
| dw_uart_init(uart_reg_ptr, baud_divisor, &(uart_info_ptr->dps_format), uart_info_ptr->hwfc); |
| |
| uart_info_ptr->status = DEV_ENABLED; |
| uart_info_ptr->extra = NULL; |
| |
| /** |
| * uart interrupt related init |
| */ |
| dw_uart_disable_interrupt(uart_info_ptr); |
| /** install uart interrupt into system */ |
| int_handler_install(uart_ctrl_ptr->intno, uart_ctrl_ptr->dw_uart_int_handler); |
| |
| memset(&(uart_info_ptr->tx_buf), 0, sizeof(DEV_BUFFER)); |
| memset(&(uart_info_ptr->rx_buf), 0, sizeof(DEV_BUFFER)); |
| memset(&(uart_info_ptr->uart_cbs), 0, sizeof(DEV_UART_CBS)); |
| |
| error_exit: |
| return ercd; |
| } |
| |
| /** |
| * \brief close a DesignWare UART device |
| * \param[in] uart_obj uart object structure pointer |
| * \retval E_OK Open successfully without any issues |
| * \retval E_OPNED Device is still opened, the device opn_cnt decreased by 1 |
| * \retval E_OBJ Device object is not valid |
| */ |
| int32_t dw_uart_close (DEV_UART *uart_obj) |
| { |
| int32_t ercd = E_OK; |
| DEV_UART_INFO *uart_info_ptr = &(uart_obj->uart_info); |
| |
| /* START ERROR CHECK */ |
| VALID_CHK_UART_INFO_OBJECT(uart_info_ptr); |
| DW_UART_CHECK_EXP(uart_info_ptr->opn_cnt > 0, E_OK); |
| /* END OF ERROR CHECK */ |
| |
| uart_info_ptr->opn_cnt --; |
| if (uart_info_ptr->opn_cnt == 0) { |
| dw_uart_disable_interrupt(uart_info_ptr); |
| dw_uart_abort_tx(uart_obj); |
| dw_uart_abort_rx(uart_obj); |
| dw_uart_flush_output(uart_info_ptr); |
| memset(&(uart_info_ptr->tx_buf), 0, sizeof(DEV_BUFFER)); |
| memset(&(uart_info_ptr->rx_buf), 0, sizeof(DEV_BUFFER)); |
| memset(&(uart_info_ptr->uart_cbs), 0, sizeof(DEV_UART_CBS)); |
| dw_uart_disable_device(uart_info_ptr); |
| uart_info_ptr->status = 0; |
| uart_info_ptr->extra = NULL; |
| } else { |
| ercd = E_OPNED; |
| } |
| |
| error_exit: |
| return ercd; |
| } |
| |
| /** |
| * \brief control uart by ctrl command |
| * \param[in] uart_obj uart object structure pointer |
| * \param[in] ctrl_cmd control command code to do specific uart work |
| * \param[in,out] param parameters used to control uart or return something |
| * \retval E_OK Control device successfully |
| * \retval E_CLSED Device is not opened |
| * \retval E_DIS Device is disabled |
| * \retval E_OBJ Device object is not valid or not exists |
| * \retval E_PAR Parameter is not valid for current control command |
| * \retval E_SYS Control device failed, due to hardware issues |
| * \retval E_CTX Control device failed, due to different reasons like in transfer state |
| * \retval E_NOSPT Control command is not supported or not valid |
| */ |
| int32_t dw_uart_control (DEV_UART *uart_obj, uint32_t ctrl_cmd, void *param) |
| { |
| int32_t ercd = E_OK; |
| DEV_UART_INFO *uart_info_ptr = &(uart_obj->uart_info); |
| |
| /* START ERROR CHECK */ |
| VALID_CHK_UART_INFO_OBJECT(uart_info_ptr); |
| DW_UART_CHECK_EXP(uart_info_ptr->opn_cnt > 0, E_CLSED); |
| /* END OF ERROR CHECK */ |
| |
| uint32_t val32; /** to receive unsigned int value */ |
| int32_t baud_divisor = 0; |
| DEV_BUFFER *devbuf; |
| UART_DPS_FORMAT *dps_ptr; |
| UART_HW_FLOW_CONTROL hwfc_local; |
| |
| DW_UART_CTRL *uart_ctrl_ptr = (DW_UART_CTRL_PTR)(uart_info_ptr->uart_ctrl); |
| DW_UART_REG *uart_reg_ptr = (DW_UART_REG_PTR)(uart_ctrl_ptr->dw_uart_regbase); |
| |
| /* check whether current device is disabled */ |
| if ((uart_info_ptr->status & DEV_ENABLED) == 0) { |
| /** When device is disabled, |
| * only UART_CMD_ENA_DEV, UART_CMD_DIS_DEV, UART_CMD_GET_STATUS |
| * are available, other commands will return E_SYS |
| */ |
| if ((ctrl_cmd != UART_CMD_ENA_DEV) && \ |
| (ctrl_cmd != UART_CMD_DIS_DEV) && \ |
| (ctrl_cmd != UART_CMD_GET_STATUS) ) { |
| return E_SYS; |
| } |
| } |
| |
| switch (ctrl_cmd) { |
| case UART_CMD_SET_BAUD: |
| val32 = (uint32_t)param; |
| DW_UART_CHECK_EXP(val32>0, E_PAR); |
| if (val32 != uart_info_ptr->baudrate) { |
| baud_divisor = DW_UART_BAUD2DIV(uart_ctrl_ptr->dw_apb_bus_freq, val32); |
| dw_uart_set_baud(uart_reg_ptr, baud_divisor); |
| uart_info_ptr->baudrate = val32; |
| } |
| break; |
| case UART_CMD_GET_STATUS: |
| DW_UART_CHECK_EXP((param!=NULL) && CHECK_ALIGN_4BYTES(param), E_PAR); |
| *((int32_t *)param) = uart_info_ptr->status; |
| break; |
| case UART_CMD_ENA_DEV: |
| dw_uart_enable_device(uart_info_ptr); |
| break; |
| case UART_CMD_DIS_DEV: |
| dw_uart_disable_device(uart_info_ptr); |
| break; |
| case UART_CMD_FLUSH_OUTPUT: |
| dw_uart_flush_output(uart_info_ptr); |
| break; |
| case UART_CMD_GET_RXAVAIL: |
| DW_UART_CHECK_EXP((param!=NULL) && CHECK_ALIGN_4BYTES(param), E_PAR); |
| *((int32_t *)param) = dw_uart_get_rxavail(uart_ctrl_ptr); |
| break; |
| case UART_CMD_GET_TXAVAIL: |
| DW_UART_CHECK_EXP((param!=NULL) && CHECK_ALIGN_4BYTES(param), E_PAR); |
| *((int32_t *)param) = dw_uart_get_txavail(uart_ctrl_ptr); |
| break; |
| case UART_CMD_BREAK_SET: |
| dw_uart_set_break(uart_reg_ptr); |
| break; |
| case UART_CMD_BREAK_CLR: |
| dw_uart_clr_break(uart_reg_ptr); |
| break; |
| case UART_CMD_SET_DPS_FORMAT: |
| DW_UART_CHECK_EXP(param!=NULL, E_PAR); |
| dps_ptr = (UART_DPS_FORMAT *)param; |
| if (dw_uart_set_dps(uart_reg_ptr, dps_ptr) == 0) { |
| uart_info_ptr->dps_format = *dps_ptr; |
| } else { |
| ercd = E_PAR; |
| } |
| break; |
| case UART_CMD_SET_HWFC: |
| hwfc_local = (UART_HW_FLOW_CONTROL)param; |
| DW_UART_CHECK_EXP(((hwfc_local>=UART_FC_NONE) && (hwfc_local<=UART_FC_BOTH)), E_PAR); |
| dw_uart_set_hwfc(uart_reg_ptr, hwfc_local); |
| uart_info_ptr->hwfc = hwfc_local; |
| break; |
| case UART_CMD_SET_TXCB: |
| DW_UART_CHECK_EXP(CHECK_ALIGN_4BYTES(param), E_PAR); |
| uart_info_ptr->uart_cbs.tx_cb = param; |
| break; |
| case UART_CMD_SET_RXCB: |
| DW_UART_CHECK_EXP(CHECK_ALIGN_4BYTES(param), E_PAR); |
| uart_info_ptr->uart_cbs.rx_cb = param; |
| break; |
| case UART_CMD_SET_ERRCB: |
| DW_UART_CHECK_EXP(CHECK_ALIGN_4BYTES(param), E_PAR); |
| uart_info_ptr->uart_cbs.err_cb = param; |
| break; |
| case UART_CMD_ABORT_TX: |
| dw_uart_abort_tx(uart_obj); |
| break; |
| case UART_CMD_ABORT_RX: |
| dw_uart_abort_rx(uart_obj); |
| break; |
| case UART_CMD_SET_TXINT: |
| val32 = (uint32_t)param; |
| if (val32 == 0) { |
| dw_uart_dis_cbr(uart_info_ptr, DW_UART_RDY_SND); |
| } else { |
| dw_uart_ena_cbr(uart_info_ptr, DW_UART_RDY_SND); |
| } |
| break; |
| case UART_CMD_SET_RXINT: |
| val32 = (uint32_t)param; |
| if (val32 == 0) { |
| dw_uart_dis_cbr(uart_info_ptr, DW_UART_RDY_RCV); |
| } else { |
| dw_uart_ena_cbr(uart_info_ptr, DW_UART_RDY_RCV); |
| } |
| break; |
| case UART_CMD_SET_TXINT_BUF: |
| DW_UART_CHECK_EXP(CHECK_ALIGN_4BYTES(param), E_PAR); |
| if (param != NULL) { |
| devbuf = (DEV_BUFFER *)param; |
| uart_info_ptr->tx_buf = *devbuf; |
| uart_info_ptr->tx_buf.ofs = 0; |
| } else { |
| uart_info_ptr->tx_buf.buf = NULL; |
| uart_info_ptr->tx_buf.len = 0; |
| uart_info_ptr->tx_buf.ofs = 0; |
| } |
| break; |
| case UART_CMD_SET_RXINT_BUF: |
| DW_UART_CHECK_EXP(CHECK_ALIGN_4BYTES(param), E_PAR); |
| if (param != NULL) { |
| devbuf = (DEV_BUFFER *)param; |
| uart_info_ptr->rx_buf = *devbuf; |
| uart_info_ptr->rx_buf.ofs = 0; |
| } else { |
| uart_info_ptr->rx_buf.buf = NULL; |
| uart_info_ptr->rx_buf.len = 0; |
| uart_info_ptr->rx_buf.ofs = 0; |
| } |
| break; |
| default: |
| ercd = E_NOSPT; |
| break; |
| } |
| |
| error_exit: |
| return ercd; |
| } |
| |
| /** |
| * \brief send data through DesignWare UART |
| * \param[in] uart_obj uart object structure pointer |
| * \param[in] data data that need to send (data must be char type) |
| * \param[in] len data length need to send |
| * \retval >0 Byte count that was successfully sent for poll method |
| * \retval E_OBJ Device object is not valid or not exists |
| * \retval E_PAR Parameter is not valid for current control command |
| * \retval E_SYS Can't write data to hardware due to hardware issues |
| */ |
| int32_t dw_uart_write (DEV_UART *uart_obj, const void *data, uint32_t len) |
| { |
| int32_t ercd = E_OK; |
| DEV_UART_INFO *uart_info_ptr = &(uart_obj->uart_info); |
| |
| /* START ERROR CHECK */ |
| VALID_CHK_UART_INFO_OBJECT(uart_info_ptr); |
| DW_UART_CHECK_EXP(uart_info_ptr->opn_cnt > 0, E_CLSED); |
| DW_UART_CHECK_EXP(uart_info_ptr->status & DEV_ENABLED, E_SYS); |
| DW_UART_CHECK_EXP(data!=NULL, E_PAR); |
| DW_UART_CHECK_EXP(len>0, E_PAR); |
| /* END OF ERROR CHECK */ |
| |
| int32_t i = 0; |
| const char *p_charbuf = (const char *)data; |
| |
| DW_UART_CTRL *uart_ctrl_ptr = (DW_UART_CTRL_PTR)(uart_info_ptr->uart_ctrl); |
| DW_UART_REG *uart_reg_ptr = (DW_UART_REG_PTR)(uart_ctrl_ptr->dw_uart_regbase); |
| |
| while (i < len) { |
| dw_uart_psnd_chr(uart_reg_ptr, p_charbuf[i++]); |
| } |
| ercd = i; |
| |
| error_exit: |
| return ercd; |
| } |
| |
| /** |
| * \brief read data through DesignWare UART |
| * \param[in] uart_obj uart object structure pointer |
| * \param[out] data data that need to read (data must be char type) |
| * \param[in] len data count need to read |
| * \retval >0 Byte count that was successfully sent for poll method |
| * \retval E_OBJ Device object is not valid or not exists |
| * \retval E_PAR Parameter is not valid for current control command |
| * \retval E_SYS Can't receive data from hardware due to hardware issues, such as device is disabled |
| */ |
| int32_t dw_uart_read (DEV_UART *uart_obj, void *data, uint32_t len) |
| { |
| int32_t ercd = E_OK; |
| DEV_UART_INFO *uart_info_ptr = &(uart_obj->uart_info); |
| |
| /* START ERROR CHECK */ |
| VALID_CHK_UART_INFO_OBJECT(uart_info_ptr); |
| DW_UART_CHECK_EXP(uart_info_ptr->opn_cnt > 0, E_CLSED); |
| DW_UART_CHECK_EXP(uart_info_ptr->status & DEV_ENABLED, E_SYS); |
| DW_UART_CHECK_EXP(data!=NULL, E_PAR); |
| DW_UART_CHECK_EXP(len>0, E_PAR); |
| /* END OF ERROR CHECK */ |
| |
| int32_t i = 0; |
| char *p_charbuf = (char *)data; |
| |
| DW_UART_CTRL *uart_ctrl_ptr = (DW_UART_CTRL_PTR)(uart_info_ptr->uart_ctrl); |
| DW_UART_REG *uart_reg_ptr = (DW_UART_REG_PTR)(uart_ctrl_ptr->dw_uart_regbase); |
| |
| while (i < len) { |
| p_charbuf[i++] = dw_uart_prcv_chr(uart_reg_ptr); |
| } |
| ercd = i; |
| |
| error_exit: |
| return ercd; |
| } |
| |
| /** |
| * \brief DesignWare UART interrupt processing routine |
| * \param[in] uart_obj uart object structure pointer |
| * \param[in] ptr extra information |
| */ |
| void dw_uart_isr (DEV_UART *uart_obj, void *ptr) |
| { |
| int32_t ercd = E_OK; |
| DEV_UART_INFO *uart_info_ptr = &(uart_obj->uart_info); |
| |
| /* START ERROR CHECK */ |
| VALID_CHK_UART_INFO_OBJECT(uart_info_ptr); |
| /* END OF ERROR CHECK */ |
| |
| uint32_t uart_int_status; /** uart interrupt status */ |
| volatile uint32_t temp; /** read error status to clear interrupt */ |
| DEV_BUFFER *buf_ptr; |
| char *p_charbuf; |
| |
| DW_UART_CTRL *uart_ctrl_ptr = (DW_UART_CTRL_PTR)(uart_info_ptr->uart_ctrl); |
| DW_UART_REG *uart_reg_ptr = (DW_UART_REG_PTR)(uart_ctrl_ptr->dw_uart_regbase); |
| |
| /** get uart interrupt status */ |
| uart_int_status = (uart_reg_ptr->IIR) & DW_UART_IIR_INT_ID_MASK; |
| |
| switch (uart_int_status) { |
| case DW_UART_IIR_MDM_STATUS: |
| temp = (volatile uint32_t)(uart_reg_ptr->MSR); |
| break; |
| case DW_UART_IIR_LINE_STATUS: |
| if (uart_info_ptr->uart_cbs.err_cb) { |
| uart_info_ptr->uart_cbs.err_cb(uart_info_ptr); |
| } |
| temp = (volatile uint32_t)(uart_reg_ptr->LSR); |
| break; |
| case DW_UART_IIR_XMIT_EMPTY: |
| buf_ptr = &(uart_info_ptr->tx_buf); |
| p_charbuf = (char *)buf_ptr->buf; |
| if (p_charbuf != NULL) { |
| while (dw_uart_putready(uart_reg_ptr)) { |
| dw_uart_putchar(uart_reg_ptr, p_charbuf[buf_ptr->ofs]); |
| buf_ptr->ofs ++; |
| if (buf_ptr->ofs >= buf_ptr->len) { |
| dw_uart_dis_cbr(uart_info_ptr, DW_UART_RDY_SND); |
| if (uart_info_ptr->uart_cbs.tx_cb) { |
| uart_info_ptr->uart_cbs.tx_cb(uart_obj); |
| } |
| /* clear the send buffer pointer */ |
| memset(buf_ptr, 0, sizeof(DEV_BUFFER)); |
| break; |
| } |
| } |
| } else { |
| if (uart_info_ptr->uart_cbs.tx_cb) { |
| uart_info_ptr->uart_cbs.tx_cb(uart_obj); |
| } |
| } |
| break; |
| case DW_UART_IIR_RX_TIMEOUT: |
| temp = dw_uart_getchar(uart_reg_ptr); |
| break; |
| case DW_UART_IIR_DATA_AVAIL: |
| buf_ptr = &(uart_info_ptr->rx_buf); |
| p_charbuf = (char *)buf_ptr->buf; |
| if (p_charbuf != NULL) { |
| while (dw_uart_getready(uart_reg_ptr)) { |
| p_charbuf[buf_ptr->ofs] = (char)dw_uart_getchar(uart_reg_ptr); |
| buf_ptr->ofs ++; |
| if (buf_ptr->ofs >= buf_ptr->len) { |
| dw_uart_dis_cbr(uart_info_ptr, DW_UART_RDY_RCV); |
| if (uart_info_ptr->uart_cbs.rx_cb) { |
| uart_info_ptr->uart_cbs.rx_cb(uart_obj); |
| } |
| /* clear the send buffer pointer */ |
| memset(buf_ptr, 0, sizeof(DEV_BUFFER)); |
| break; |
| } |
| } |
| } else { |
| if (uart_info_ptr->uart_cbs.rx_cb) { |
| uart_info_ptr->uart_cbs.rx_cb(uart_obj); |
| } |
| } |
| break; |
| default: |
| temp = (volatile uint32_t)(uart_reg_ptr->USR); |
| break; |
| } |
| |
| error_exit: |
| return; |
| } |