rt-thread/bsp/synopsys/embarc/device/designware/uart/dw_uart.c

957 lines
29 KiB
C

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