#include "gw3323_uart.h"

//sys clock value
const u32 SYS_CLOCK_DEFINITION[8] = {
    2000000L,
    24000000L,
    48000000L,
    60000000L,
    80000000L,
    120000000L,
    147000000L,
    160000000L,
};

/**
  * @brief  Initializes the uart peripheral according to the specified
  *         parameters in the uart_init_struct.
  * @param  uartx: where x can be (0..2) to select the uartx peripheral.
  * @param  uart_init_struct: pointer to a uart_init_typedef structure that
  *         contains the configuration information for the specified UART peripheral.
  * @retval None
  */
void uart_init(uart_typedef *uartx, uart_init_typedef* uart_init_struct)
{
    uint32_t tmp_reg, sys_clk_val, baud_reg;

    //-------- disable before configuration --------//
    uartx->con &= (uint32_t)~((uint32_t)UART_CON_UTEN);
    uartx->cpnd |= (uint32_t)(UART_CPND_CRXPND | UART_CPND_CTXPND);

    //---------- uart controller register ----------//
    tmp_reg = uartx->con;

    tmp_reg &= (uint32_t)~((uint32_t)UART_CON_RXEN);
    tmp_reg &= (uint32_t)~((uint32_t)UART_CON_ONELINE);
    tmp_reg &= (uint32_t)~((uint32_t)UART_CON_CLKSRC);
    tmp_reg &= (uint32_t)~((uint32_t)UART_CON_SB2EN);

    tmp_reg |= (uint32_t)(uart_init_struct->mode |
                          uart_init_struct->one_line_enable |
                          uart_init_struct->stop_bits |
                          uart_init_struct->clock_source);
    uartx->con = tmp_reg;

    //---------- uart baud configuration ----------//
    sys_clk_val = SYS_CLOCK_DEFINITION[get_cur_sysclk()];
    if (uart_init_struct->clock_source == UART_SYSTEM_CLOCK) {
        baud_reg = (uint16_t)((sys_clk_val / uart_init_struct->baud) - 1);
    } else {        // uart_init_struct->clock_source == UART_INC_CLOCK
        if (uart_init_struct->inc_div == UART_INC_DIV1) {
            baud_reg = (uint16_t)((SYS_CLOCK_DEFINITION[1] / uart_init_struct->baud) - 1);
        } else {    //uart_init_struct->inc_div == UART_INC_DIV2
            CLKCON1 |= UART_INC_DIV2;
            baud_reg = (uint16_t)((SYS_CLOCK_DEFINITION[1] / 2 / uart_init_struct->baud) - 1);
        }
    }
    tmp_reg = (uint32_t)((baud_reg << 16) | baud_reg);
    uartx->baud = tmp_reg;
}

/**
  * @brief  Enable or disable the specified UART peripheral.
  * @param  uartx: where x can be (0..2) to select the uartx peripheral.
  * @param  state: the state of the UARTx peripheral.
  *         this parameter can be: ENABLE or DISABLE.
  * @retval None
  */
AT(.com_text.uart)
void uart_cmd(uart_typedef *uartx, FUNCTIONAL_STATE state)
{
    if (state != DISABLE) {
        uartx->con |= (uint32_t)UART_CON_UTEN;
    } else {
        uartx->con &= (uint32_t)~((uint32_t)UART_CON_UTEN);
    }
}

/**
  * @brief  Transmits data through the UARTx peripheral.
  * @param  uartx: where x can be (0..2) to select the uartx peripheral.
  * @param  data: the data to transmit.
  * @retval None
  */
AT(.com_text.uart)
void uart_send_data(uart_typedef *uartx, uint16_t data)
{
    uartx->data = (data & (uint16_t)0x1ff);
}

/**
  * @brief  Return the received data lastly by the UARTx peripheral.
  * @param  uartx: where x can be (0..2) to select the uartx peripheral.
  * @retval The received data.
  */
AT(.com_text.uart)
uint16_t uart_receive_data(uart_typedef *uartx)
{
    if (uartx->con & UART_CON_BIT9EN) {
        return (uartx->data & (uint16_t)0x1ff);
    } else {
        return (uartx->data & (uint16_t)0xff);
    }
}

/**
  * @brief  Enable or disable the specified UARTx interrupt.
  * @param  uartx: where x can be (0..2) to select the uartx peripheral.
  * @param  isr: Function to be executed for service interruption.
  * @param  pr: Priority of service interruption.
  * @param  uart_ie_bit: specifies the UART interrupt sources to be enable or disable.
  *         this parameter can be one of the following values:
  *             @arg UART_CON_RXIE: Receive single data finish interrupt enable bit.
  *             @arg UART_CON_TXIE: Transmit single data finish interrupt enable bit.
  * @param  state: the state of the UARTx peripheral.
  *         this parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void uart_pic_config(uart_typedef *uartx, isr_t isr, int pr, uint16_t uart_ie_bit, FUNCTIONAL_STATE state)
{
    if (state != DISABLE) {
        sys_irq_init(IRQ_UART_VECTOR, pr, isr);
        uartx->con |= uart_ie_bit;
    } else {
        PICEN &= ~BIT(IRQ_UART_VECTOR);
        uartx->con &= ~uart_ie_bit;
    }
}

/**
  * @brief  Check the specified UART flag is set or not.
  * @param  uartx: where x can be (0..2) to select the uartx peripheral.
  * @param  uart_flag: specifies the flag to check.
  *         this parameter can be one of the following values:
  *             @arg UART_FLAG_RX: Receive single data finish interrupt.
  *             @arg UART_FLAG_TX: Transmit single data finish interrupt.
  * @retval The state of uart_flag (SET or RESET).
  */
AT(.com_text.uart)
FLAG_STATE uart_get_flag(uart_typedef *uartx, uint16_t uart_flag)
{
    if ((uartx->con & uart_flag) != RESET) {
        return SET;
    } else {
        return RESET;
    }
}

/**
  * @brief  Clear the UART's pending flag.
  * @param  uartx: where x can be (0..2) to select the uartx peripheral.
  * @param  uart_flag: specifies the flag to check.
  *         this parameter can be one of the following values:
  *             @arg UART_FLAG_RX: Receive single data finish interrupt.
  *             @arg UART_FLAG_TX: Transmit single data finish interrupt.
  * @retval None
  */
AT(.com_text.uart)
void uart_clear_flag(uart_typedef *uartx, uint16_t uart_flag)
{
    uartx->cpnd |= (uint32_t)(uart_flag);
}
