/***************************************************************************//**
 * @file    drv_usart.c
 * @brief   USART driver of RT-Thread RTOS for EFM32
 *  COPYRIGHT (C) 2012, RT-Thread Development Team
 * @author  onelife
 * @version 1.0
 *******************************************************************************
 * @section License
 * The license and distribution terms for this file may be found in the file
 *  LICENSE in this distribution or at http://www.rt-thread.org/license/LICENSE
 *******************************************************************************
 * @section Change Logs of serial.c
 * Date         Author      Notes
 * 2009-02-05   Bernard     first version
 * 2009-10-25   Bernard     fix rt_serial_read bug when there is no data in the
 *  buffer.
 * 2010-03-29   Bernard     cleanup code.
 *
 * @section Change Logs
 * Date         Author      Notes
 * 2010-12-22   onelife     Initial creation for EFM32
 * 2011-01-17   onelife     Merge with serial.c
 * 2011-05-06   onelife     Add sync mode (SPI) support
 * 2011-06-14   onelife     Fix a bug of TX by DMA
 * 2011-06-16   onelife     Modify init function for EFM32 library v2.0.0
 *  upgrading
 * 2011-07-07   onelife     Modify write function to avoid sleep in ISR
 * 2011-07-26   onelife     Add lock (semaphore) to prevent simultaneously
 *  access
 * 2011-11-29   onelife     Modify init function for EFM32 library v2.2.2
 *  upgrading
 * 2011-12-09   onelife     Add giant gecko support
 * 2011-12-09   onelife     Add UART module support
 * 2011-12-20   onelife     Add 9-bit SPI mode support
 * 2011-12-20   onelife     Change SPI write format (same as SPI read)
 * 2011-12-20   onelife     Change USART status format
 * 2011-12-27	onelife		Utilize "USART_PRESENT", "USART_COUNT",
 *  "UART_PRESENT" and "UART_COUNT"
 * 2012-05-16	onelife		Fix a bug in rt_hw_usart_init()
 ******************************************************************************/

/***************************************************************************//**
 * @addtogroup efm32
 * @{
 ******************************************************************************/

/* Includes ------------------------------------------------------------------*/
#include "board.h"
#include "hdl_interrupt.h"
#include "drv_usart.h"

#if (defined(RT_USING_USART0) || defined(RT_USING_USART1) || \
    defined(RT_USING_USART2) || defined(RT_USING_UART0) || \
    defined(RT_USING_UART1))
 #if ((defined(RT_USING_USART0) || defined(RT_USING_USART1) || \
    defined(RT_USING_USART2)) && !defined(USART_PRESENT))
 #error "USART module is not available"
 #endif
 #if ((defined(RT_USING_UART0) || defined(RT_USING_UART1)) && \
    !defined(UART_PRESENT))
 #error "UART module is not available"
 #endif
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
#ifdef RT_USART_DEBUG
#define usart_debug(format,args...)         rt_kprintf(format, ##args)
#else
#define usart_debug(format,args...)
#endif

/* Private variables ---------------------------------------------------------*/
#ifdef RT_USING_USART0
 #if (RT_USING_USART0 >= EFM32_USART_LOCATION_COUNT)
    #error "Wrong location number"
 #endif
    struct rt_device usart0_device;
    static struct rt_semaphore usart0_lock;
#endif

#ifdef RT_USING_USART1
 #if (USART_COUNT <= 1)
    #error "Wrong unit number"
 #endif
 #if (RT_USING_USART1 >= EFM32_USART_LOCATION_COUNT)
    #error "Wrong location number"
 #endif
    struct rt_device usart1_device;
    static struct rt_semaphore usart1_lock;
#endif

#ifdef RT_USING_USART2
 #if (USART_COUNT <= 2)
    #error "Wrong unit number"
 #endif
 #if (RT_USING_USART2 >= EFM32_USART_LOCATION_COUNT)
    #error "Wrong location number"
 #endif
    struct rt_device usart2_device;
    static struct rt_semaphore usart2_lock;
#endif

#ifdef RT_USING_UART0
 #if (RT_USING_UART0 >= EFM32_UART_LOCATION_COUNT)
    #error "Wrong location number"
 #endif
    struct rt_device uart0_device;
    static struct rt_semaphore uart0_lock;
#endif

#ifdef RT_USING_UART1
 #if (UART_COUNT <= 1)
    #error "Wrong unit number"
 #endif
 #if (RT_USING_UART1 >= EFM32_UART_LOCATION_COUNT)
    #error "Wrong location number"
 #endif
    struct rt_device uart1_device;
    static struct rt_semaphore uart1_lock;
#endif

/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/***************************************************************************//**
 * @brief
 *   Initialize USART device
 *
 * @details
 *
 * @note
 *
 * @param[in] dev
 *   Pointer to device descriptor
 *
 * @return
 *   Error code
 ******************************************************************************/
static rt_err_t rt_usart_init (rt_device_t dev)
{
    struct efm32_usart_device_t *usart;

    usart = (struct efm32_usart_device_t *)(dev->user_data);

    if (!(dev->flag & RT_DEVICE_FLAG_ACTIVATED))
    {
        if (dev->flag & RT_DEVICE_FLAG_DMA_TX)
        {
            struct efm32_usart_dma_mode_t *dma_tx;

            dma_tx = (struct efm32_usart_dma_mode_t *)(usart->tx_mode);

            usart->state |= USART_STATE_RX_BUSY;
        }

        if (dev->flag & RT_DEVICE_FLAG_INT_RX)
        {
            struct efm32_usart_int_mode_t *int_rx;

            int_rx = (struct efm32_usart_int_mode_t *)(usart->rx_mode);

            int_rx->data_ptr = RT_NULL;
        }

        /* Enable USART */
        USART_Enable(usart->usart_device, usartEnable);

        dev->flag |= RT_DEVICE_FLAG_ACTIVATED;
    }

    return RT_EOK;
}

/***************************************************************************//**
 * @brief
 *   Open USART device
 *
 * @details
 *
 * @note
 *
 * @param[in] dev
 *   Pointer to device descriptor
 *
 * @param[in] oflag
 *   Device open flag
 *
 * @return
 *   Error code
 ******************************************************************************/
static rt_err_t rt_usart_open(rt_device_t dev, rt_uint16_t oflag)
{
    RT_ASSERT(dev != RT_NULL);

    struct efm32_usart_device_t *usart;

    usart = (struct efm32_usart_device_t *)(dev->user_data);

    if (dev->flag & RT_DEVICE_FLAG_INT_RX)
    {
        IRQn_Type                   rxIrq;

        //if (usart->state & USART_STATE_CONSOLE)
        {   /* Allocate new RX buffer */
            struct efm32_usart_int_mode_t   *int_mode;

            int_mode = (struct efm32_usart_int_mode_t *)(usart->rx_mode);

            if ((int_mode->data_ptr = rt_malloc(USART_RX_BUFFER_SIZE)) == RT_NULL)
            {
                usart_debug("USART%d err: no mem for RX BUF\n", usart->unit);
                return -RT_ENOMEM;
            }
            rt_memset(int_mode->data_ptr, 0, USART_RX_BUFFER_SIZE);
            int_mode->data_size = USART_RX_BUFFER_SIZE;
            int_mode->read_index = 0;
            int_mode->save_index = 0;
        }

        /* Enable RX interrupt */
#if defined(UART_PRESENT)
        if (usart->state & USART_STATE_ASYNC_ONLY)
        {
            usart->usart_device->IEN    = UART_IEN_RXDATAV;
        }
        else
#endif
        {
            usart->usart_device->IEN    = USART_IEN_RXDATAV;
        }

        /* Enable IRQ */
        switch (usart->unit)
        {
        case 0:
#if defined(UART_PRESENT)
            if (usart->state & USART_STATE_ASYNC_ONLY)
            {
                rxIrq   = UART0_RX_IRQn;
            }
            else
#endif
            {
                rxIrq   = USART0_RX_IRQn;
            }
            break;
#if ((defined(USART_PRESENT) && (USART_COUNT > 1)) || \
    (defined(UART_PRESENT) && (UART_COUNT > 1)))
        case 1:
 #if (defined(UART_PRESENT) && (UART_COUNT > 1))
            if (usart->state & USART_STATE_ASYNC_ONLY)
            {
                rxIrq   = UART1_RX_IRQn;
            }
            else
 #endif
            {
                rxIrq   = USART1_RX_IRQn;
            }
            break;
#endif
#if ((defined(USART_PRESENT) && (USART_COUNT > 2)) || \
    (defined(UART_PRESENT) && (UART_COUNT > 2)))
        case 2:
 #if (defined(UART_PRESENT) && (UART_COUNT > 2))
            if (usart->state & USART_STATE_ASYNC_ONLY)
            {
                rxIrq   = UART2_RX_IRQn;
            }
            else
 #endif
            {
                rxIrq   = USART2_RX_IRQn;
            }
            break;
#endif
        }
        if (oflag != RT_DEVICE_OFLAG_WRONLY)
        {
            NVIC_ClearPendingIRQ(rxIrq);
            NVIC_SetPriority(rxIrq, EFM32_IRQ_PRI_DEFAULT);
            NVIC_EnableIRQ(rxIrq);
        }
    }

    /* Clear Flag */
#if defined(UART_PRESENT)
    if (usart->state & USART_STATE_ASYNC_ONLY)
    {
        usart->usart_device->IFC    = _UART_IFC_MASK;
    }
    else
#endif
    {
        usart->usart_device->IFC    = _USART_IFC_MASK;
    }

    if ((dev->flag & RT_DEVICE_FLAG_DMA_TX) && (oflag != RT_DEVICE_OFLAG_RDONLY))
    {
        /* DMA IRQ is enabled by DMA_Init() */
        NVIC_SetPriority(DMA_IRQn, EFM32_IRQ_PRI_DEFAULT);
    }

    usart->counter++;
    usart_debug("USART%d: Open with flag %x\n", usart->unit, oflag);
    return RT_EOK;
}

/***************************************************************************//**
 * @brief
 *   Close USART device
 *
 * @details
 *
 * @note
 *
 * @param[in] dev
 *   Pointer to device descriptor
 *
 * @return
 *   Error code
 ******************************************************************************/
static rt_err_t rt_usart_close(rt_device_t dev)
{
    RT_ASSERT(dev != RT_NULL);

    struct efm32_usart_device_t *usart;

    usart = (struct efm32_usart_device_t *)(dev->user_data);

    if (--usart->counter == 0)
    {
        if (dev->flag & RT_DEVICE_FLAG_INT_RX)
        {
            struct efm32_usart_int_mode_t *int_rx;

            int_rx = (struct efm32_usart_int_mode_t *)usart->rx_mode;

            rt_free(int_rx->data_ptr);
            int_rx->data_ptr = RT_NULL;
        }
    }

    return RT_EOK;
}

/***************************************************************************//**
 * @brief
 *  Read from USART device
 *
 * @details
 *
 * @note
 *  9-bit SPI mode and SPI slave mode is untested
 *
 * @param[in] dev
 *  Pointer to device descriptor
 *
 * @param[in] pos
 *  Offset
 *
 * @param[in] buffer
 *  Poniter to the buffer
 *
 * @param[in] size
 *  Buffer size in byte
 *
 * @return
 *  Number of read bytes
 ******************************************************************************/
static rt_size_t rt_usart_read (
    rt_device_t     dev,
    rt_off_t        pos,
    void            *buffer,
    rt_size_t       size)
{
    rt_err_t    err_code;
    struct efm32_usart_device_t *usart;
    rt_size_t   read_len, len;
    rt_uint8_t  *ptr;
    rt_uint32_t rx_flag, tx_flag, b8_flag;

    usart = (struct efm32_usart_device_t *)(dev->user_data);
#if defined(UART_PRESENT)
    if (usart->state & USART_STATE_ASYNC_ONLY)
    {
        rx_flag = UART_STATUS_RXDATAV;
        tx_flag = UART_STATUS_TXBL;
        b8_flag = UART_CTRL_BIT8DV;
    }
    else
#endif
    {
        rx_flag = USART_STATUS_RXDATAV;
        tx_flag = USART_STATUS_TXBL;
        b8_flag = USART_CTRL_BIT8DV;
    }

    /* Lock device */
    if (rt_hw_interrupt_check())
    {
        err_code = rt_sem_take(usart->lock, RT_WAITING_NO);
    }
    else
    {
        err_code = rt_sem_take(usart->lock, RT_WAITING_FOREVER);
    }
    if (err_code != RT_EOK)
    {
        rt_set_errno(err_code);
        return 0;
    }

    if (dev->flag & RT_DEVICE_FLAG_INT_RX)
    {
        len = size;
        ptr = buffer;
        /* interrupt mode Rx */
        while (len)
        {
            rt_base_t level;
            struct efm32_usart_int_mode_t *int_rx;

            int_rx = (struct efm32_usart_int_mode_t *)\
                (((struct efm32_usart_device_t *)(dev->user_data))->rx_mode);

            /* disable interrupt */
            level = rt_hw_interrupt_disable();

            if (int_rx->read_index != int_rx->save_index)
            {
                /* read a character */
                *ptr++ = int_rx->data_ptr[int_rx->read_index];
                len--;

                /* move to next position */
                int_rx->read_index ++;
                if (int_rx->read_index >= USART_RX_BUFFER_SIZE)
                {
                    int_rx->read_index = 0;
                }
            }
            else
            {
                /* set error code */
                err_code = -RT_EEMPTY;

                /* enable interrupt */
                rt_hw_interrupt_enable(level);
                break;
            }

            /* enable interrupt */
            rt_hw_interrupt_enable(level);
        }

        read_len = (rt_uint32_t)ptr - (rt_uint32_t)buffer;
    }
    else
    {
        if (usart->state & USART_STATE_SYNC)
        {
            /* SPI read */
            rt_uint8_t inst_len = *((rt_uint8_t *)buffer);
            rt_uint8_t *inst_ptr = (rt_uint8_t *)(buffer + 1);
            rt_uint8_t *rx_buf = *((rt_uint8_t **)(buffer + inst_len + 1));
            rt_off_t i;

            ptr = inst_ptr;
            len = inst_len;
            /* Write instructions */
            if (len)
            {
                if (usart->state & USART_STATE_9BIT)
                {
                    usart->usart_device->CTRL &= ~b8_flag;
                }
                while (len)
                {
                    while (!(usart->usart_device->STATUS & tx_flag));
                    usart->usart_device->TXDATA = (rt_uint32_t)*(ptr++);
                    len--;
                }
                if (usart->state & USART_STATE_9BIT)
                {
                    usart->usart_device->CTRL |= b8_flag;
                }
            }

            /* Flushing RX */
            usart->usart_device->CMD = USART_CMD_CLEARRX;
            /* Skip some bytes if necessary */
            for (i = 0; i < pos; i++)
            {
                /* dummy write */
                while (!(usart->usart_device->STATUS & tx_flag));
                usart->usart_device->TXDATA = (rt_uint32_t)0xff;
                /* dummy read */
                while (!(usart->usart_device->STATUS & rx_flag));
                *((rt_uint32_t *)0x00) = usart->usart_device->RXDATA;
            }

            ptr = rx_buf;
            len = size;
            /* Read data */
            while (len)
            {
                /* dummy write */
                while (!(usart->usart_device->STATUS & tx_flag));
                usart->usart_device->TXDATA = (rt_uint32_t)0xff;
                /* read a byte of data */
                while (!(usart->usart_device->STATUS & rx_flag));
                *(ptr++) = usart->usart_device->RXDATA & 0xff;
                len--;
            }
        }
        else
        {
            ptr = buffer;
            len = size;
            /* polling mode */
            while (len)
            {
                while (usart->usart_device->STATUS & rx_flag)
                {
                    *(ptr++) = usart->usart_device->RXDATA & 0xff;
                }
                len--;
            }
        }

        read_len = size - len;
    }

    /* Unlock device */
    rt_sem_release(usart->lock);

    /* set error code */
    rt_set_errno(err_code);

    return read_len;
}

/***************************************************************************//**
 * @brief
 *   Write to USART device
 *
 * @details
 *
 * @note
 *
 * @param[in] dev
 *   Pointer to device descriptor
 *
 * @param[in] pos
 *   Offset
 *
 * @param[in] buffer
 *   Poniter to the buffer
 *
 * @param[in] size
 *   Buffer size in byte
 *
 * @return
 *   Number of written bytes
 ******************************************************************************/
static rt_size_t rt_usart_write (
    rt_device_t     dev,
    rt_off_t        pos,
    const void*     buffer,
    rt_size_t       size)
{
    rt_err_t err_code;
    struct efm32_usart_device_t* usart = (struct efm32_usart_device_t*)(dev->user_data);
    rt_size_t   read_len, len;
    rt_uint8_t  *ptr;
    rt_size_t   write_size = 0;
    rt_uint32_t tx_flag, b8_flag;

#if defined(UART_PRESENT)
    if (usart->state & USART_STATE_ASYNC_ONLY)
    {
        tx_flag = UART_STATUS_TXBL;
        b8_flag = UART_CTRL_BIT8DV;
    }
    else
#endif
    {
        tx_flag = USART_STATUS_TXBL;
        b8_flag = USART_CTRL_BIT8DV;
    }

    /* Lock device */
    if (rt_hw_interrupt_check())
    {
        err_code = rt_sem_take(usart->lock, RT_WAITING_NO);
    }
    else
    {
        err_code = rt_sem_take(usart->lock, RT_WAITING_FOREVER);
    }
    if (err_code != RT_EOK)
    {
        rt_set_errno(err_code);
        return 0;
    }

    if (usart->state & USART_STATE_SYNC)
    {   /* SPI write */
        rt_uint8_t inst_len     = *((rt_uint8_t *)buffer);
        rt_uint8_t *inst_ptr    = (rt_uint8_t *)(buffer + 1);
        rt_uint8_t *tx_buf      = *((rt_uint8_t **)(buffer + inst_len + 1));

        ptr = inst_ptr;
        len = inst_len;
        /* Write instructions */
        if (len)
        {
            if (usart->state & USART_STATE_9BIT)
            {
                usart->usart_device->CTRL &= ~b8_flag;
            }
            if ((dev->flag & RT_DEVICE_FLAG_DMA_TX) && (len > 2))
            {   /* DMA mode Tx */
                struct efm32_usart_dma_mode_t *dma_tx;

                usart_debug("USART: DMA TX INS (%d)\n", len);
                dma_tx = (struct efm32_usart_dma_mode_t *)(usart->tx_mode);
                dma_tx->data_ptr = (rt_uint32_t *)ptr;
                dma_tx->data_size = len;

                usart->state |= USART_STATE_TX_BUSY;
                DMA_ActivateBasic(
                    dma_tx->dma_channel,
                    true,
                    false,
                    (void *)&(usart->usart_device->TXDATA),
                    (void *)ptr,
                    (rt_uint32_t)(len - 1));
                /* Wait, otherwise the TX buffer is overwrite */
                // TODO: This function blocks the process => goto low power mode?
        //      if (usart->state & USART_STATE_CONSOLE)
        //      {
                    while(usart->state & USART_STATE_TX_BUSY);
        //      }
        //      else
        //      {
        //          while(usart->state & USART_STATE_TX_BUSY)
        //          {
        //              rt_thread_sleep(USART_WAIT_TIME_TX);
        //          }
        //      }
            }
            else
            {   /* polling mode */
                usart_debug("USART: Polling TX INS (%d)\n", len);
                while (len)
                {
                    while (!(usart->usart_device->STATUS & tx_flag));
                    usart->usart_device->TXDATA = (rt_uint32_t)*(ptr++);
                    len--;
                }
            }
            if (usart->state & USART_STATE_9BIT)
            {
                usart->usart_device->CTRL |= b8_flag;
            }
        }

        ptr = tx_buf;
    }
    else
    {
        ptr = (rt_uint8_t *)buffer;
    }

    len = size;
    /* Write data */
    if (dev->flag & RT_DEVICE_FLAG_STREAM)
    {
        if (*(ptr + len - 1) == '\n')
        {
            *(ptr + len - 1) = '\r';
            *(ptr + len++) = '\n';
            *(ptr + len) = 0;
        }
    }
    if ((dev->flag & RT_DEVICE_FLAG_DMA_TX) && (len > 2))
    {   /* DMA mode Tx */
        struct efm32_usart_dma_mode_t *dma_tx;

        usart_debug("USART: DMA TX data (%d)\n", len);
        dma_tx = (struct efm32_usart_dma_mode_t *)(usart->tx_mode);
        dma_tx->data_ptr = (rt_uint32_t *)ptr;
        dma_tx->data_size = len;

        usart->state |= USART_STATE_TX_BUSY;
        DMA_ActivateBasic(
            dma_tx->dma_channel,
            true,
            false,
            (void *)&(usart->usart_device->TXDATA),
            (void *)ptr,
            (rt_uint32_t)(len - 1));

        /* Wait, otherwise the TX buffer is overwrite */
        // TODO: This function blocks the process => goto low power mode?
//      if (usart->state & USART_STATE_CONSOLE)
//      {
            while(usart->state & USART_STATE_TX_BUSY);
//      }
//      else
//      {
//          while(usart->state & USART_STATE_TX_BUSY)
//          {
//              rt_thread_sleep(USART_WAIT_TIME_TX);
//          }
//      }
        write_size = size;
    }
    else
    {   /* polling mode */
        usart_debug("USART: Polling TX data (%d)\n", len);
        while (len)
        {
            while (!(usart->usart_device->STATUS & tx_flag));
            usart->usart_device->TXDATA = (rt_uint32_t)*(ptr++);
            len--;
        }

        write_size = size - len;
    }

    /* Unlock device */
    rt_sem_release(usart->lock);

    /* set error code */
    rt_set_errno(err_code);

    return write_size;
}

/***************************************************************************//**
* @brief
*   Configure USART device
*
* @details
*
* @note
*
* @param[in] dev
*   Pointer to device descriptor
*
* @param[in] cmd
*   IIC control command
*
* @param[in] args
*   Arguments
*
* @return
*   Error code
******************************************************************************/
static rt_err_t rt_usart_control (
    rt_device_t     dev,
    rt_uint8_t      cmd,
    void            *args)
{
    RT_ASSERT(dev != RT_NULL);

    rt_err_t    err_code;
    struct efm32_usart_device_t *usart;

    usart = (struct efm32_usart_device_t *)(dev->user_data);

    /* Lock device */
    if (rt_hw_interrupt_check())
    {
        err_code = rt_sem_take(usart->lock, RT_WAITING_NO);
    }
    else
    {
        err_code = rt_sem_take(usart->lock, RT_WAITING_FOREVER);
    }
    if (err_code != RT_EOK)
    {
        return err_code;
    }

    switch (cmd)
    {
    case RT_DEVICE_CTRL_SUSPEND:
        /* Suspend device */
        dev->flag |= RT_DEVICE_FLAG_SUSPENDED;
        USART_Enable(usart->usart_device, usartDisable);
        break;

    case RT_DEVICE_CTRL_RESUME:
        /* Resume device */
        dev->flag &= ~RT_DEVICE_FLAG_SUSPENDED;
        USART_Enable(usart->usart_device, usartEnable);
        break;

    case RT_DEVICE_CTRL_USART_RBUFFER:
        /* Set RX buffer */
        {
            struct efm32_usart_int_mode_t *int_rx;
            rt_uint8_t size;

            int_rx = (struct efm32_usart_int_mode_t *)(usart->rx_mode);
            size = (rt_uint8_t)((rt_uint32_t)args & 0xFFUL);

            /* Free previous RX buffer */
            if (int_rx->data_ptr != RT_NULL)
            {
                if (size == 0)
                {   /* Free RX buffer */
                    rt_free(int_rx->data_ptr);
                    int_rx->data_ptr = RT_NULL;
                }
                else if (size != int_rx->data_size)
                {
                    /* Re-allocate RX buffer */
                    if ((int_rx->data_ptr = rt_realloc(int_rx->data_ptr, size)) \
                        == RT_NULL)
                    {
                        usart_debug("USART%d err: no mem for RX BUF\n", usart->unit);
                        err_code = -RT_ENOMEM;
                        break;
                    }
                    // TODO: Is the following line necessary?
                    //rt_memset(int_rx->data_ptr, 0, size);
                }
            }
            else
            {
                /* Allocate new RX buffer */
                if ((int_rx->data_ptr = rt_malloc(size)) == RT_NULL)
                {
                    usart_debug("USART%d err: no mem for RX BUF\n", usart->unit);
                    err_code = -RT_ENOMEM;
                    break;
                }
            }
            int_rx->data_size = size;
            int_rx->read_index = 0;
            int_rx->save_index = 0;
        }
        break;

    }

    /* Unlock device */
    rt_sem_release(usart->lock);

    return err_code;
}

/***************************************************************************//**
 * @brief
 *  USART RX data valid interrupt handler
 *
 * @details
 *
 * @note
 *  9-bit SPI mode has not implemented yet and SPI slave mode is untested
 *
 * @param[in] dev
 *  Pointer to device descriptor
 ******************************************************************************/
void rt_hw_usart_rx_isr(rt_device_t dev)
{
    struct efm32_usart_device_t     *usart;
    struct efm32_usart_int_mode_t   *int_rx;
    rt_uint32_t                     flag;

    /* interrupt mode receive */
    RT_ASSERT(dev->flag & RT_DEVICE_FLAG_INT_RX);
    usart = (struct efm32_usart_device_t *)(dev->user_data);
    int_rx = (struct efm32_usart_int_mode_t *)(usart->rx_mode);
    RT_ASSERT(int_rx->data_ptr != RT_NULL);
#if defined(UART_PRESENT)
    if (usart->state & USART_STATE_ASYNC_ONLY)
    {
        flag = UART_STATUS_RXDATAV;
    }
    else
#endif
    {
        flag = USART_STATUS_RXDATAV;
    }

    /* Set status */
    usart->state |= USART_STATE_RX_BUSY;

    /* save into rx buffer */
    while (usart->usart_device->STATUS & flag)
    {
        rt_base_t level;

        /* disable interrupt */
        level = rt_hw_interrupt_disable();

        /* save character */
        int_rx->data_ptr[int_rx->save_index] = \
            (rt_uint8_t)(usart->usart_device->RXDATA & 0xFFUL);
        int_rx->save_index ++;
        if (int_rx->save_index >= USART_RX_BUFFER_SIZE)
            int_rx->save_index = 0;

        /* if the next position is read index, discard this 'read char' */
        if (int_rx->save_index == int_rx->read_index)
        {
            int_rx->read_index ++;
            if (int_rx->read_index >= USART_RX_BUFFER_SIZE)
            {
                int_rx->read_index = 0;
            }
        }

        /* enable interrupt */
        rt_hw_interrupt_enable(level);
    }

    /* invoke callback */
    if (dev->rx_indicate != RT_NULL)
    {
        rt_size_t rx_length;

        /* get rx length */
        rx_length = int_rx->read_index > int_rx->save_index ?
            USART_RX_BUFFER_SIZE - int_rx->read_index + int_rx->save_index : \
            int_rx->save_index - int_rx->read_index;

        dev->rx_indicate(dev, rx_length);
    }
}

/***************************************************************************//**
 * @brief
 *  DMA for USART TX interrupt handler
 *
 * @details
 *
 * @note
 *
 * @param[in] dev
 *  Pointer to device descriptor
 ******************************************************************************/
void rt_hw_usart_dma_tx_isr(rt_device_t dev)
{
    /* DMA mode receive */
    struct efm32_usart_device_t     *usart;
    struct efm32_usart_dma_mode_t   *dma_tx;

    RT_ASSERT(dev->flag & RT_DEVICE_FLAG_DMA_TX);

    usart = (struct efm32_usart_device_t *)(dev->user_data);
    dma_tx = (struct efm32_usart_dma_mode_t *)(usart->tx_mode);

    /* invoke call to notify tx complete */
    if (dev->tx_complete != RT_NULL)
    {
        dev->tx_complete(dev, dma_tx->data_ptr);
    }

    /* Set status */
    usart->state &= ~(rt_uint32_t)USART_STATE_TX_BUSY;
}

/***************************************************************************//**
* @brief
*   Register USART device
*
* @details
*
* @note
*
* @param[in] device
*   Pointer to device descriptor
*
* @param[in] name
*   Device name
*
* @param[in] flag
*   Configuration flags
*
* @param[in] usart
*   Pointer to USART device descriptor
*
* @return
*   Error code
******************************************************************************/
rt_err_t rt_hw_usart_register(
    rt_device_t     device,
    const char      *name,
    rt_uint32_t     flag,
    struct efm32_usart_device_t *usart)
{
    RT_ASSERT(device != RT_NULL);

    if ((flag & RT_DEVICE_FLAG_DMA_RX) ||
        (flag & RT_DEVICE_FLAG_INT_TX))
    {
        RT_ASSERT(0);
    }

    if (usart->state & USART_STATE_SYNC)
    {
        device->type    = RT_Device_Class_SPIBUS;
    }
    else
    {
        device->type    = RT_Device_Class_Char;
    }
    device->rx_indicate = RT_NULL;
    device->tx_complete = RT_NULL;
    device->init        = rt_usart_init;
    device->open        = rt_usart_open;
    device->close       = rt_usart_close;
    device->read        = rt_usart_read;
    device->write       = rt_usart_write;
    device->control     = rt_usart_control;
    device->user_data   = usart;

    /* register a character device */
    return rt_device_register(device, name, RT_DEVICE_FLAG_RDWR | flag);
}

/***************************************************************************//**
* @brief
*   Initialize the specified USART unit
*
* @details
*
* @note
*
* @param[in] device
*   Pointer to device descriptor
*
* @param[in] unitNumber
*   Unit number
*
* @param[in] location
*   Pin location number
*
* @param[in] flag
*   Configuration flag
*
* @param[in] dmaChannel
*   DMA channel number for TX
*
* @param[in] console
*   Indicate if using as console
*
* @return
*   Pointer to USART device
******************************************************************************/
static struct efm32_usart_device_t *rt_hw_usart_unit_init(
    rt_device_t device,
    rt_uint8_t  unitNumber,
    rt_uint8_t  location,
    rt_uint32_t flag,
    rt_uint32_t dmaChannel,
    rt_uint8_t  config)
{
    struct efm32_usart_device_t     *usart;
    struct efm32_usart_dma_mode_t   *dma_mode;
    DMA_CB_TypeDef                  *callback;
    CMU_Clock_TypeDef               usartClock;
    rt_uint32_t                     txDmaSelect;
    GPIO_Port_TypeDef               port_tx, port_rx, port_clk, port_cs;
    rt_uint32_t                     pin_tx, pin_rx, pin_clk, pin_cs;
    efm32_irq_hook_init_t           hook;

    do
    {
        /* Allocate device */
        usart = rt_malloc(sizeof(struct efm32_usart_device_t));
        if (usart == RT_NULL)
        {
            usart_debug("USART%d err: no mem\n", usart->unit);
            break;
        }
        usart->counter  = 0;
        usart->unit     = unitNumber;
        usart->state    = config;
        usart->tx_mode  = RT_NULL;
        usart->rx_mode  = RT_NULL;

        /* Allocate TX */
        dma_mode = RT_NULL;
        if (flag & RT_DEVICE_FLAG_DMA_TX)
        {
            usart->tx_mode = dma_mode = rt_malloc(sizeof(struct efm32_usart_dma_mode_t));
            if (dma_mode == RT_NULL)
            {
                usart_debug("USART%d err: no mem for DMA TX\n", usart->unit);
                break;
            }
            dma_mode->dma_channel = dmaChannel;
        }

        /* Allocate RX */
        if (flag & RT_DEVICE_FLAG_INT_RX)
        {
            usart->rx_mode = rt_malloc(sizeof(struct efm32_usart_int_mode_t));
            if (usart->rx_mode == RT_NULL)
            {
                usart_debug("USART%d err: no mem for INT RX\n", usart->unit);
                break;
            }
        }

        /* Initialization */
#if defined(UART_PRESENT)
        if ((!(config & USART_STATE_ASYNC_ONLY) && (unitNumber >= USART_COUNT)) || \
            ((config & USART_STATE_ASYNC_ONLY) && (unitNumber >= UART_COUNT)))
#else
        if (unitNumber >= USART_COUNT)
#endif
        {
            break;
        }
        switch (unitNumber)
        {
        case 0:
#if defined(UART_PRESENT)
            if (config & USART_STATE_ASYNC_ONLY)
            {
                usart->usart_device = UART0;
                usartClock          = (CMU_Clock_TypeDef)cmuClock_UART0;
                txDmaSelect         = DMAREQ_UART0_TXBL;
                port_tx             = AF_UART0_TX_PORT(location);
                pin_tx              = AF_UART0_TX_PIN(location);
                port_rx             = AF_UART0_RX_PORT(location);
                pin_rx              = AF_UART0_RX_PIN(location);
            }
            else
 #endif
            {
                usart->usart_device = USART0;
                usartClock          = (CMU_Clock_TypeDef)cmuClock_USART0;
                txDmaSelect         = DMAREQ_USART0_TXBL;
                port_tx             = AF_USART0_TX_PORT(location);
                pin_tx              = AF_USART0_TX_PIN(location);
                port_rx             = AF_USART0_RX_PORT(location);
                pin_rx              = AF_USART0_RX_PIN(location);
                port_clk            = AF_USART0_CLK_PORT(location);
                pin_clk             = AF_USART0_CLK_PIN(location);
                port_cs             = AF_USART0_CS_PORT(location);
                pin_cs              = AF_USART0_CS_PIN(location);
            }
            break;
#if ((defined(USART_PRESENT) && (USART_COUNT > 1)) || \
    (defined(UART_PRESENT) && (UART_COUNT > 1)))
        case 1:
 #if (defined(UART_PRESENT) && (UART_COUNT > 1))
            if (config & USART_STATE_ASYNC_ONLY)
            {
                usart->usart_device = UART1;
                usartClock          = (CMU_Clock_TypeDef)cmuClock_UART1;
                txDmaSelect         = DMAREQ_UART1_TXBL;
                port_tx             = AF_UART1_TX_PORT(location);
                pin_tx              = AF_UART1_TX_PIN(location);
                port_rx             = AF_UART1_RX_PORT(location);
                pin_rx              = AF_UART1_RX_PIN(location);
            }
            else
 #endif
            {
                usart->usart_device = USART1;
                usartClock          = (CMU_Clock_TypeDef)cmuClock_USART1;
                txDmaSelect         = DMAREQ_USART1_TXBL;
                port_tx             = AF_USART1_TX_PORT(location);
                pin_tx              = AF_USART1_TX_PIN(location);
                port_rx             = AF_USART1_RX_PORT(location);
                pin_rx              = AF_USART1_RX_PIN(location);
                port_clk            = AF_USART1_CLK_PORT(location);
                pin_clk             = AF_USART1_CLK_PIN(location);
                port_cs             = AF_USART1_CS_PORT(location);
                pin_cs              = AF_USART1_CS_PIN(location);
            }
            break;
#endif
#if ((defined(USART_PRESENT) && (USART_COUNT > 2)) || \
    (defined(UART_PRESENT) && (UART_COUNT > 2)))
        case 2:
 #if (defined(UART_PRESENT) && (UART_COUNT > 2))
            if (config & USART_STATE_ASYNC_ONLY)
            {
                usart->usart_device = UART2;
                usartClock          = (CMU_Clock_TypeDef)cmuClock_UART2;
                txDmaSelect         = DMAREQ_UART2_TXBL;
                port_tx             = AF_UART2_TX_PORT(location);
                pin_tx              = AF_UART2_TX_PIN(location);
                port_rx             = AF_UART2_RX_PORT(location);
                pin_rx              = AF_UART2_RX_PIN(location);
            }
            else
 #endif
            {
                usart->usart_device = USART2;
                usartClock          = (CMU_Clock_TypeDef)cmuClock_USART2;
                txDmaSelect         = DMAREQ_USART2_TXBL;
                port_tx             = AF_USART2_TX_PORT(location);
                pin_tx              = AF_USART2_TX_PIN(location);
                port_rx             = AF_USART2_RX_PORT(location);
                pin_rx              = AF_USART2_RX_PIN(location);
                port_clk            = AF_USART2_CLK_PORT(location);
                pin_clk             = AF_USART2_CLK_PIN(location);
                port_cs             = AF_USART2_CS_PORT(location);
                pin_cs              = AF_USART2_CS_PIN(location);
            }
            break;
#endif
        default:
            break;
        }

        /* Enable USART clock */
        CMU_ClockEnable(usartClock, true);

        /* Config GPIO */
        GPIO_PinModeSet(
            port_tx,
            pin_tx,
            gpioModePushPull,
            0);
        GPIO_PinModeSet(
            port_rx,
            pin_rx,
            gpioModeInputPull,
            1);
        if (config & USART_STATE_SYNC)
        {
            GPIO_PinModeSet(
                port_clk,
                pin_clk,
                gpioModePushPull,
                0);
        }
        if (config & USART_STATE_AUTOCS)
        {
            GPIO_PinModeSet(
                port_cs,
                pin_cs,
                gpioModePushPull,
                1);
        }

        /* Config interrupt and NVIC */
        if (flag & RT_DEVICE_FLAG_INT_RX)
        {
            hook.type       = efm32_irq_type_usart;
            hook.unit       = unitNumber * 2 + 1;
#if defined(UART_PRESENT)
            if (config & USART_STATE_ASYNC_ONLY)
            {
                hook.unit += USART_COUNT * 2;
            }
#endif
            hook.cbFunc     = rt_hw_usart_rx_isr;
            hook.userPtr    = device;
            efm32_irq_hook_register(&hook);
        }

        /* Config DMA */
        if (flag & RT_DEVICE_FLAG_DMA_TX)
        {
            DMA_CfgChannel_TypeDef  chnlCfg;
            DMA_CfgDescr_TypeDef    descrCfg;

            hook.type           = efm32_irq_type_dma;
            hook.unit           = dmaChannel;
            hook.cbFunc         = rt_hw_usart_dma_tx_isr;
            hook.userPtr        = device;
            efm32_irq_hook_register(&hook);

            callback = (DMA_CB_TypeDef *)rt_malloc(sizeof(DMA_CB_TypeDef));
            if (callback == RT_NULL)
            {
                usart_debug("USART%d err: no mem for callback\n", usart->unit);
                break;
            }
            callback->cbFunc    = DMA_IRQHandler_All;
            callback->userPtr   = RT_NULL;
            callback->primary   = 0;

            /* Setting up DMA channel */
            chnlCfg.highPri     = false;    /* Can't use with peripherals */
            chnlCfg.enableInt   = true;     /* Interrupt for callback function */
            chnlCfg.select      = txDmaSelect;
            chnlCfg.cb          = callback;
            DMA_CfgChannel(dmaChannel, &chnlCfg);

            /* Setting up DMA channel descriptor */
            descrCfg.dstInc     = dmaDataIncNone;
            descrCfg.srcInc     = dmaDataInc1;
            descrCfg.size       = dmaDataSize1;
            descrCfg.arbRate    = dmaArbitrate1;
            descrCfg.hprot      = 0;
            DMA_CfgDescr(dmaChannel, true, &descrCfg);
        }

        /* Init specified USART unit */
        if (config & USART_STATE_SYNC)
        {
            USART_InitSync_TypeDef init_sync = USART_INITSYNC_DEFAULT;

            init_sync.enable        = usartEnable;
            init_sync.refFreq       = 0;
            init_sync.baudrate      = SPI_BAUDRATE;
            if (config & USART_STATE_9BIT)
            {
                init_sync.databits  = usartDatabits9;
            }
            else
            {
                init_sync.databits  = usartDatabits8;
            }
            if (config & USART_STATE_MASTER)
            {
                init_sync.master    = true;
            }
            else
            {
                init_sync.master    = false;
            }
            init_sync.msbf          = true;

            switch (USART_CLK_MODE_GET(config))
            {
            case 0:
                init_sync.clockMode = usartClockMode0;
                break;
            case 1:
                init_sync.clockMode = usartClockMode1;
                break;
            case 2:
                init_sync.clockMode = usartClockMode2;
                break;
            case 3:
                init_sync.clockMode = usartClockMode3;
                break;
            }
            USART_InitSync(usart->usart_device, &init_sync);
        }
        else
        {
            USART_InitAsync_TypeDef init_async = USART_INITASYNC_DEFAULT;

            init_async.enable       = usartEnable;
            init_async.refFreq      = 0;
            init_async.baudrate     = UART_BAUDRATE;
            init_async.oversampling = USART_CTRL_OVS_X4;
            init_async.databits     = USART_FRAME_DATABITS_EIGHT;
            init_async.parity       = USART_FRAME_PARITY_NONE;
            init_async.stopbits     = USART_FRAME_STOPBITS_ONE;
            USART_InitAsync(usart->usart_device, &init_async);
        }

        /* Enable RX and TX pins and set location */
        usart->usart_device->ROUTE = USART_ROUTE_RXPEN | USART_ROUTE_TXPEN | \
                        (location << _USART_ROUTE_LOCATION_SHIFT);
        if (config & USART_STATE_SYNC)
        {
            usart->usart_device->ROUTE |= USART_ROUTE_CLKPEN;
        }
        if (config & USART_STATE_AUTOCS)
        {
            usart->usart_device->ROUTE |= USART_ROUTE_CSPEN;
            if (config & USART_STATE_MASTER)
            {
                usart->usart_device->CTRL |= USART_CTRL_AUTOCS;
            }
        }

        /* Clear RX/TX buffers */
        usart->usart_device->CMD = USART_CMD_CLEARRX | USART_CMD_CLEARTX;

        return usart;
    } while(0);

    if (usart->rx_mode)
    {
        rt_free(usart->rx_mode);
    }
    if (usart->tx_mode)
    {
        rt_free(usart->tx_mode);
    }
    if (usart)
    {
        rt_free(usart);
    }
    if (callback)
    {
        rt_free(callback);
    }

#if defined(UART_PRESENT)
    if (config & USART_STATE_ASYNC_ONLY)
    {
        usart_debug("UART%d err: init failed!\n", unitNumber);
    }
    else
#endif
    {
        usart_debug("USART%d err: init failed!\n", unitNumber);
    }
    return RT_NULL;
}

/***************************************************************************//**
* @brief
*   Initialize all USART module related hardware and register USART device to
* kernel
*
* @details
*
* @note
******************************************************************************/
void rt_hw_usart_init(void)
{
    struct efm32_usart_device_t *usart;
    rt_uint32_t                 flag;
    rt_uint8_t                  config;

    do
    {
#if (defined(USART_PRESENT) && defined(RT_USING_USART0))
        config = 0x00;
        flag = RT_DEVICE_FLAG_RDWR;

 #ifdef RT_USART0_SYNC_MODE
        config |= USART_STATE_SYNC;
        config |= (RT_USART0_SYNC_MODE & SYNC_SETTING_MASK) << SYNC_SETTING_SHIFT;
  #if (!((RT_USART0_SYNC_MODE << SYNC_SETTING_SHIFT) & USART_STATE_MASTER))
         flag |= RT_DEVICE_FLAG_INT_RX;
  #endif
 #else
         flag |= RT_DEVICE_FLAG_INT_RX;
 #endif

 #if (RT_CONSOLE_DEVICE == EFM_USART0)
        config |= USART_STATE_CONSOLE;
        flag |= RT_DEVICE_FLAG_STREAM;
 #endif

 #ifdef RT_USART0_USING_DMA
        RT_ASSERT(RT_USART0_USING_DMA < DMA_CHAN_COUNT);
        flag |= RT_DEVICE_FLAG_DMA_TX;
 #else
       #define RT_USART0_USING_DMA EFM32_NO_DMA
 #endif

        /* Initialize and Register usart0 */
        if ((usart = rt_hw_usart_unit_init(
            &usart0_device,
            0,
            RT_USING_USART0,
            flag,
            RT_USART0_USING_DMA,
            config)) != RT_NULL)
        {
            rt_hw_usart_register(&usart0_device, RT_USART0_NAME, flag, usart);
        }
        else
        {
            break;
        }
        /* Initialize lock for usart0 */
        usart->lock = &usart0_lock;
        if (rt_sem_init(usart->lock, RT_USART0_NAME, 1, RT_IPC_FLAG_FIFO) != RT_EOK)
        {
            break;
        }
#endif

#if (defined(USART_PRESENT) && (USART_COUNT > 1) && defined(RT_USING_USART1))
        config = 0x00;
        flag = RT_DEVICE_FLAG_RDWR;

 #ifdef RT_USART1_SYNC_MODE
         config |= USART_STATE_SYNC;
         config |= (RT_USART1_SYNC_MODE & SYNC_SETTING_MASK) << SYNC_SETTING_SHIFT;
  #if (!((RT_USART1_SYNC_MODE << SYNC_SETTING_SHIFT) & USART_STATE_MASTER))
         flag |= RT_DEVICE_FLAG_INT_RX;
  #endif
 #else
         flag |= RT_DEVICE_FLAG_INT_RX;
 #endif

 #if (RT_CONSOLE_DEVICE == EFM_USART1)
        config |= USART_STATE_CONSOLE;
        flag |= RT_DEVICE_FLAG_STREAM;
 #endif

 #ifdef RT_USART1_USING_DMA
        RT_ASSERT(RT_USART1_USING_DMA < DMA_CHAN_COUNT);
        flag |= RT_DEVICE_FLAG_DMA_TX;
 #else

       #define RT_USART1_USING_DMA EFM32_NO_DMA
 #endif

        /* Initialize and Register usart1 */
        if ((usart = rt_hw_usart_unit_init(
            &usart1_device,
            1,
            RT_USING_USART1,
            flag,
            RT_USART1_USING_DMA,
            config)) != RT_NULL)
        {
            rt_hw_usart_register(&usart1_device, RT_USART1_NAME, flag, usart);
        }
        else
        {
            break;
        }
        /* Initialize lock for usart1 */
        usart->lock = &usart1_lock;
        if (rt_sem_init(usart->lock, RT_USART1_NAME, 1, RT_IPC_FLAG_FIFO) != RT_EOK)
        {
            break;
        }
#endif

#if (defined(USART_PRESENT) && (USART_COUNT > 2) && defined(RT_USING_USART2))
        config = 0x00;
        flag = RT_DEVICE_FLAG_RDWR;

 #ifdef RT_USART2_SYNC_MODE
         config |= USART_STATE_SYNC;
         config |= (RT_USART2_SYNC_MODE & SYNC_SETTING_MASK) << SYNC_SETTING_SHIFT;
  #if (!((RT_USART2_SYNC_MODE << SYNC_SETTING_SHIFT) & USART_STATE_MASTER))
        flag |= RT_DEVICE_FLAG_INT_RX;
  #endif
 #else
        flag |= RT_DEVICE_FLAG_INT_RX;
 #endif

#if (RT_CONSOLE_DEVICE == EFM_USART2)
        config |= USART_STATE_CONSOLE;
        flag |= RT_DEVICE_FLAG_STREAM;
#endif

 #ifdef RT_USART2_USING_DMA
        RT_ASSERT(RT_USART2_USING_DMA < DMA_CHAN_COUNT);
        flag |= RT_DEVICE_FLAG_DMA_TX;

 #else

       #define RT_USART2_USING_DMA EFM32_NO_DMA
 #endif

        /* Initialize and Register usart2 */
        if ((usart = rt_hw_usart_unit_init(
            &usart2_device,
            2,
            RT_USING_USART2,
            flag,
            RT_USART2_USING_DMA,
            config)) != RT_NULL)
        {
            rt_hw_usart_register(&usart2_device, RT_USART2_NAME, flag, usart);
        }
        else
        {
            break;
        }
        /* Initialize lock for usart2 */
        usart->lock = &usart2_lock;
        if (rt_sem_init(usart->lock, RT_USART2_NAME, 1, RT_IPC_FLAG_FIFO) != RT_EOK)
        {
            break;
        }
#endif

#if (defined(UART_PRESENT) && defined(RT_USING_UART0))
        config = USART_STATE_ASYNC_ONLY;
        flag = RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX;

 #if (RT_CONSOLE_DEVICE == EFM_UART0)
        config |= USART_STATE_CONSOLE;
        flag |= RT_DEVICE_FLAG_STREAM;
 #endif

 #ifdef RT_UART0_USING_DMA
        RT_ASSERT(RT_UART0_USING_DMA < DMA_CHAN_COUNT);
        flag |= RT_DEVICE_FLAG_DMA_TX;
 #else
       #define RT_UART0_USING_DMA EFM32_NO_DMA
 #endif

        /* Initialize and Register uart0 */
        if ((usart = rt_hw_usart_unit_init(
            &uart0_device,
            0,
            RT_USING_UART0,
            flag,
            RT_UART0_USING_DMA,
            config)) != RT_NULL)
        {
            rt_hw_usart_register(&uart0_device, RT_UART0_NAME, flag, usart);
        }
        else
        {
            break;
        }
        /* Initialize lock for uart0 */
        usart->lock = &uart0_lock;
        if (rt_sem_init(usart->lock, RT_UART0_NAME, 1, RT_IPC_FLAG_FIFO) != RT_EOK)
        {
            break;
        }
#endif

#if (defined(UART_PRESENT) && (UART_COUNT > 1) && defined(RT_USING_UART1))
        config = USART_STATE_ASYNC_ONLY;
        flag = RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX;

 #if (RT_CONSOLE_DEVICE == EFM_UART1)
        config |= USART_STATE_CONSOLE;
        flag |= RT_DEVICE_FLAG_STREAM;
 #endif

 #ifdef RT_UART1_USING_DMA
        RT_ASSERT(RT_UART1_USING_DMA < DMA_CHAN_COUNT);
        flag |= RT_DEVICE_FLAG_DMA_TX;
 #else
       #define RT_UART1_USING_DMA EFM32_NO_DMA
 #endif

        /* Initialize and Register uart1 */
        if ((usart = rt_hw_usart_unit_init(
            &uart1_device,
            1,
            RT_USING_UART1,
            flag,
            RT_UART1_USING_DMA,
            config)) != RT_NULL)
        {
            rt_hw_usart_register(&uart1_device, RT_UART1_NAME, flag, usart);
        }
        else
        {
            break;
        }
        /* Initialize lock for uart1 */
        usart->lock = &uart1_lock;
        if (rt_sem_init(usart->lock, RT_UART1_NAME, 1, RT_IPC_FLAG_FIFO) != RT_EOK)
        {
            break;
        }
#endif

        usart_debug("USART: H/W init OK!\n");
        return;
    } while (0);

    rt_kprintf("USART: H/W init failed!\n");
}

#endif /* (defined(RT_USING_USART0) || defined(RT_USING_USART1) || \
    defined(RT_USING_USART2) || defined(RT_USING_UART0) || \
    defined(RT_USING_UART1)) */
/***************************************************************************//**
 * @}
 ******************************************************************************/
