#include "chip.h"
#include "lprintf.h"
#include "lprintf_config.h"

#define LPRINTF_UART_PREFIX  UART

#define LPRINTF_UART                        LPRINTF_UART_(LPRINTF_UART_PREFIX, LPRINTF_UART_NUM)
#define LPRINTF_UART_(pref, num)            LPRINTF_UART__(pref, num)
#define LPRINTF_UART__(pref, num)           CHIP_REGS_ ## pref ## num

#define LPRINTF_UART_PER_ID                 LPRINTF_UART_PER_ID_(LPRINTF_UART_PREFIX, LPRINTF_UART_NUM)
#define LPRINTF_UART_PER_ID_(pref, num)     LPRINTF_UART_PER_ID__(pref, num)
#define LPRINTF_UART_PER_ID__(pref, num)    CHIP_PER_ID_ ## pref ## num

#define LPRINTF_UART_CLK_FREQ               LPRINTF_UART_CLK_FREQ_(LPRINTF_UART_PREFIX, LPRINTF_UART_NUM)
#define LPRINTF_UART_CLK_FREQ_(pref, num)   LPRINTF_UART_CLK_FREQ__(pref, num)
#define LPRINTF_UART_CLK_FREQ__(pref, num)  CHIP_CLK_ ## pref ## num ## _FREQ

int lprintf_uart_init(uint32_t baud, uint8_t databits, t_lprintf_uart_parity parity) {
    int err = 0;
    uint8_t dls = 0;
    uint8_t pen = 0;
    uint8_t eps = 0;

    if (databits == 8) {
        dls = CHIP_REGFLDVAL_UART_LCR_DLS_8;
    } else if (databits == 7) {
        dls = CHIP_REGFLDVAL_UART_LCR_DLS_7;
    } else if (databits == 6) {
        dls = CHIP_REGFLDVAL_UART_LCR_DLS_6;
    } else if (databits == 5) {
        dls = CHIP_REGFLDVAL_UART_LCR_DLS_5;
    } else {
        err = -1;
    }

    if (err == 0) {
        if (parity == LPRINTF_UART_PARITY_NONE) {
            pen = 0;
            eps = 0;
        } else if (parity == LPRINTF_UART_PARITY_EVEN) {
            pen = CHIP_REGFLDVAL_UART_LCR_EPS_EVEN;
            eps = 1;
        } else if (parity == LPRINTF_UART_PARITY_ODD) {
            pen = 1;
            eps = CHIP_REGFLDVAL_UART_LCR_EPS_ODD;
        } else {
            err = -2;
        }
    }

    if (err == 0) {
        chip_per_clk_en(LPRINTF_UART_PER_ID);

        LPRINTF_UART->IER = 0; /* Disabel interrupts */
        /* Enable, reset and configure fifo */
        LPRINTF_UART->FCR = LBITFIELD_SET(CHIP_REGFLD_UART_FCR_FIFOE, 1)
                | LBITFIELD_SET(CHIP_REGFLD_UART_FCR_RFIFOR, 1)
                | LBITFIELD_SET(CHIP_REGFLD_UART_FCR_XFIFOR, 1)
                | LBITFIELD_SET(CHIP_REGFLD_UART_FCR_RT, CHIP_REGFLDVAL_UART_FCR_RT_FULL_2_CH)
                | LBITFIELD_SET(CHIP_REGFLD_UART_FCR_TFT, CHIP_REGFLDVAL_UART_FCR_TFT_1_2_FULL);

        LPRINTF_UART->MCR = 0x0;
        /* Set DLAB to change br */
        LPRINTF_UART->LCR |= CHIP_REGFLD_UART_LCR_DLAB;
        uint32_t br_div = (uint32_t)((LPRINTF_UART_CLK_FREQ + 8 * baud)/(16 * baud));
        if (br_div > 0xFFFF)
            br_div = 0xFFFF;
        if (br_div < 1)
            br_div = 1;
        LPRINTF_UART->DLL = br_div & 0xFF;
        LPRINTF_UART->DLH = (br_div >> 8) & 0xFF;
        LPRINTF_UART->LCR &= ~CHIP_REGFLD_UART_LCR_DLAB;

        LPRINTF_UART->LCR =
                LBITFIELD_SET(CHIP_REGFLD_UART_LCR_STOP, 0)
                | LBITFIELD_SET(CHIP_REGFLD_UART_LCR_DLS, dls)
                | LBITFIELD_SET(CHIP_REGFLD_UART_LCR_PEN, pen)
                | LBITFIELD_SET(CHIP_REGFLD_UART_LCR_EPS, eps)
                ;

#ifdef LPRINTF_UART_TX_PIN
        CHIP_PIN_CONFIG(LPRINTF_UART_TX_PIN);
#endif
    }


    return err;
}



void lprintf_close(void) {

    while (lprintf_uart_busy()) {
        continue;
    }
#ifdef LPRINTF_UART_TX_PIN
    CHIP_PIN_CONFIG_DIS(LPRINTF_UART_TX_PIN, CHIP_PIN_CFG_STD);
#endif
    chip_per_clk_dis(LPRINTF_UART_PER_ID);
}


void lprintf_putchar(char c) {
    while ((LPRINTF_UART->USR & CHIP_REGFLD_UART_USR_TFNF) == 0) {
        continue;
    }
    LPRINTF_UART->THR = c;
}

int lprintf_uart_busy(void) {
    return !(LPRINTF_UART->USR & CHIP_REGFLD_UART_USR_BUSY);
}
