/* ------------------------------------------ | |
* 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; | |
} |