#include "omw_gpio.h"
#include "omw_uart.h"

#define CLK_SWITCH_ADDR        (0x40000024)
#define UART_CLK_SEL_BIT_POS   (10)
#define UART_CLK_SEL_MSK       (3 << UART_CLK_SEL_BIT_POS)
#define UART_CLK_SEL_XTAL_VAL  (1)
#define UART_RCV_TRI  1     //0:1 character ; 1:4 characters ; 2:8 characters ; 3:14 characters

typedef struct
{
    uint32_t *uart_base_addr;
    uint8_t  *fifo_buf;
    uint16_t  fifo_sz;
    uint16_t  rd;
    uint16_t  wt;
}omw_uart_it_rx_info;

omw_uart_it_rx_info  uart_it_rx_info[OMW_MAX_UART_NUM] = {{0}};

void switch_uart_clk_src(uint32_t new_clk_val)
{
    uint32_t  clk_val = *(volatile uint32_t *)CLK_SWITCH_ADDR;

    clk_val &= (~UART_CLK_SEL_MSK);
    clk_val |= (new_clk_val << UART_CLK_SEL_BIT_POS);

    *(volatile uint32_t *)CLK_SWITCH_ADDR = clk_val;
}

void wait_uart_not_busy(uint32_t * uart_base_addr)
{
    volatile uint32_t * uart_cfg_addr = uart_base_addr;

    while(uart_cfg_addr[OMW_UART_USR] & 0x1)
    {
        if (uart_cfg_addr[OMW_UART_LSR] & 0x1)
        {
            uart_cfg_addr[OMW_UART_RBR];
        }
    }
}

void omw_uart_clk_set( OMW_UART_CLK uart_clk)
{
	SYS_CTRL->SYS_CLKSEL_b.REG_UART_CLK_SEL = uart_clk;
}

void omw_uart_set_baudrate(uint32_t * uart_base_addr, uint32_t cfg_val)
{
    volatile uint32_t * uart_cfg_addr = uart_base_addr;

    wait_uart_not_busy(uart_base_addr);

    //not busy
    uart_cfg_addr[OMW_UART_LCR] |= OMW_LCR_SET_DLAB;

    /* DLL and DLH is lower 8-bits and higher 8-bits of cfg_val.*/
    uart_cfg_addr[OMW_UART_DLL] = cfg_val & 0xff;
    uart_cfg_addr[OMW_UART_DLH] = (cfg_val >> 8) & 0xff;

    uart_cfg_addr[OMW_UART_DLF] = ((cfg_val >> 16) & 0xF);

    uart_cfg_addr[OMW_UART_LCR] &= (~OMW_LCR_SET_DLAB);

    if ((cfg_val >> 24) != 0xFF)
    {
        switch_uart_clk_src(cfg_val >> 24);
    }
}

void omw_uart_init(uint32_t * uart_base_addr, uint32_t baudrate, uint32_t rx_it_mode)
{
    volatile uint32_t * uart_cfg_addr = uart_base_addr;
    int divisor = ((omw_get_uart_clk() / baudrate) >> 4) | (0xFF << 24);
	int divf = ((((omw_get_uart_clk()<<4) / baudrate)+ 0xF)>>4)&0xF;
    divisor |= divf<<16;
    uint8_t uart_idx = (OMW_UART1 == uart_base_addr);

    RESET_UART(uart_idx);

    //make uart device in idle state before starting to config
    uart_cfg_addr[OMW_UART_LCR] = 0x83;
    uart_cfg_addr[OMW_UART_DLL] = 0x1;
    uart_cfg_addr[OMW_UART_DLH] = 0x0;
    uart_cfg_addr[OMW_UART_FCR] = 0x1;

    /////////////////start config//////////////////
    omw_uart_set_baudrate(uart_base_addr, divisor);

    //SetParity: NONE
    uart_cfg_addr[OMW_UART_LCR] &= (~OMW_LCR_PARITY_ENABLE);

    //SetWordSize: LCR_WORD_SIZE_8
    uart_cfg_addr[OMW_UART_LCR] |= OMW_LCR_WORD_SIZE_8;

    //SetStopBit: LCR_STOP_BIT_1
    uart_cfg_addr[OMW_UART_LCR] &= OMW_LCR_STOP_BIT1;

    if (!rx_it_mode)
    {
        //SetRXMode: Query mode
        uart_cfg_addr[OMW_UART_IER] &= (~OMW_IER_RDA_INT_ENABLE);
    }
    else
    {
        //SetRXMode: INT mode
        uart_cfg_addr[OMW_UART_IER] |= OMW_IER_RDA_INT_ENABLE;
    }

    //SetTXMode: Query mode
    uart_cfg_addr[OMW_UART_IER] &= (~OMW_IER_THRE_INT_ENABLE);

    //SetFIFOMode
    uart_cfg_addr[OMW_UART_FCR] =  ((UART_RCV_TRI<<6)  | 1);
}

void omw_uart_send(uint32_t * uart_base_addr, void * pdata, uint32_t len)
{
    uint8_t  *ptmp = pdata;
    uint32_t pos = 0;
    volatile uint32_t * uart_cfg_addr = uart_base_addr;

    #ifdef OMW_HAS_SCH
    __disable_irq();
    #endif

    while (pos < len)
    {
        while (!(uart_cfg_addr[OMW_UART_LSR] & OMW_LSR_TRANS_EMPTY));

        uart_cfg_addr[OMW_UART_THR] = ptmp[pos++];
    }

    #ifdef OMW_HAS_SCH
    __enable_irq();
    #endif
}

uint32_t omw_uart_rcv(uint32_t * uart_base_addr, void * pbuf, uint32_t blen)
{
    uint32_t pos   = 0;
    uint8_t  *ptmp = pbuf;

    volatile uint32_t * uart_cfg_addr = uart_base_addr;

    while ((pos < blen) && (uart_cfg_addr[OMW_UART_LSR] & OMW_LSR_DATA_READY))
    {
        ptmp[pos++] = uart_cfg_addr[OMW_UART_RBR];
    }

    return pos;
}

static void omw_uart_it_rx(omw_uart_it_rx_info * rx_info)
{
    volatile uint32_t * uart_cfg_addr = rx_info->uart_base_addr;

    while (uart_cfg_addr[OMW_UART_LSR] & OMW_LSR_DATA_READY)
    {
        uint16_t  nxt_wt = (rx_info->wt + 1) & (rx_info->fifo_sz - 1);
        uint8_t data = uart_cfg_addr[OMW_UART_RBR];

        if (rx_info->fifo_buf != NULL && nxt_wt != rx_info->rd)
        {
            rx_info->fifo_buf[rx_info->wt] = data;
            rx_info->wt = nxt_wt;
        }
    }
}

void omw_uart_it_rx_by_base_addr(uint32_t * uart_base_addr)
{
    uint32_t f_idx = OMW_GET_UART_IDX(uart_base_addr);

    omw_uart_it_rx(&uart_it_rx_info[f_idx]);
}

void omw_uart_set_rx_fifo(uint32_t * uart_base_addr, uint8_t * fifo_buf, uint16_t fifo_sz)
{
    uint32_t f_idx = OMW_GET_UART_IDX(uart_base_addr);

    uart_it_rx_info[f_idx].uart_base_addr = uart_base_addr;
    uart_it_rx_info[f_idx].fifo_buf = fifo_buf;
    uart_it_rx_info[f_idx].fifo_sz = fifo_sz;
}

uint16_t omw_uart_get_rx_fifo_wt(uint32_t *uart_base_addr)
{
    uint32_t f_idx = OMW_GET_UART_IDX(uart_base_addr);

    return uart_it_rx_info[f_idx].wt;
}

void omw_uart_set_rx_fifo_rd(uint32_t *uart_base_addr, uint16_t n_rd)
{
    uint32_t f_idx = OMW_GET_UART_IDX(uart_base_addr);

    uart_it_rx_info[f_idx].rd = n_rd;
}
