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

#define UART_FIFO_SIZE      CHIP_UART0_TX_FIFO_SIZE

#include "lpc_uart_baudrate.inc"


int lprintf_uart_init(uint32_t baud, uint8_t databits, t_lprintf_uart_parity parity) {

    int err = 0;
    unsigned cfg = 0;
    chip_per_clk_en(CHIP_PER_ID_USART0);



    if (databits == 5) {
        cfg |= LBITFIELD_SET(CHIP_REGFLD_USART0_LCR_WLS, CHIP_REGFLDVAL_USART0_LCR_WLS_5);
    } else if (databits == 6) {
        cfg |= LBITFIELD_SET(CHIP_REGFLD_USART0_LCR_WLS, CHIP_REGFLDVAL_USART0_LCR_WLS_6);
    } else if (databits == 7) {
        cfg |= LBITFIELD_SET(CHIP_REGFLD_USART0_LCR_WLS, CHIP_REGFLDVAL_USART0_LCR_WLS_7);
    } else if (databits == 8) {
        cfg |= LBITFIELD_SET(CHIP_REGFLD_USART0_LCR_WLS, CHIP_REGFLDVAL_USART0_LCR_WLS_8);
    } else {
        err = LPRINTF_ERR_UART_INVALID_BITLEN;
    }

    if (!err) {
        if (parity == LPRINTF_UART_PARITY_NONE) {
            cfg |= LBITFIELD_SET(CHIP_REGFLD_USART0_LCR_PE, 0);
        } else if (parity == LPRINTF_UART_PARITY_ODD) {
            cfg |= LBITFIELD_SET(CHIP_REGFLD_USART0_LCR_PE, 1)
                    | LBITFIELD_SET(CHIP_REGFLD_USART0_LCR_PS, CHIP_REGFLDVAL_USART0_LCR_PS_ODD);
        } else if (parity == LPRINTF_UART_PARITY_EVEN) {
            cfg |= LBITFIELD_SET(CHIP_REGFLD_USART0_LCR_PE, 1)
                    | LBITFIELD_SET(CHIP_REGFLD_USART0_LCR_PS, CHIP_REGFLDVAL_USART0_LCR_PS_EVEN);
        } else {
            err = LPRINTF_ERR_UART_INVALID_PARITY;
        }
    }

    if (!err) {
        unsigned dl;
        unsigned fract_div;
        unsigned fract_mul;

        CHIP_REGS_USART0->LCR = cfg;
        CHIP_REGS_USART0->IER = 0;

        f_get_baudrate_settings(CHIP_CLK_USART0_FREQ, baud, &dl, &fract_div, &fract_mul );

        if (fract_mul != 0) {
            CHIP_REGS_USART0->LCR |= CHIP_REGFLD_USART0_LCR_DLAB;
            CHIP_REGS_USART0->DLL = dl & 0xFF;
            CHIP_REGS_USART0->DLM = (dl >> 8) & 0xFF;
            CHIP_REGS_USART0->FDR = LBITFIELD_SET(CHIP_REGFLD_USART0_FDR_DIVADD_VAL, fract_div)
                    | LBITFIELD_SET(CHIP_REGFLD_USART0_FDR_MUL_VAL, fract_mul);
            CHIP_REGS_USART0->LCR &= ~CHIP_REGFLD_USART0_LCR_DLAB;
            CHIP_REGS_USART0->FCR = CHIP_REGFLD_USART0_FCR_FIFO_EN
                    | CHIP_REGFLD_USART0_FCR_RX_FIFO_RES
                    | CHIP_REGFLD_USART0_FCR_TX_FIFO_RES;;
            CHIP_REGS_USART0->FCR = CHIP_REGFLD_USART0_FCR_FIFO_EN;

            (void)CHIP_REGS_USART0->IIR;
        } else {
            err = LPRINTF_ERR_UART_INVALID_BAUDRATE;
        }
    }


    if (!err) {        
        CHIP_PIN_CONFIG(LPRINTF_UART_TX_PIN);
    }
    return err;
}




void lprintf_putchar(char c) {
    static uint8_t rdy_size = UART_FIFO_SIZE;
    static uint8_t empty;

    do {
        empty = CHIP_REGS_USART0->LSR & CHIP_REGFLD_USART0_LSR_THRE;
    } while (!empty && (rdy_size==0));


    if (empty)
        rdy_size = UART_FIFO_SIZE;

    CHIP_REGS_USART0->THR = c;
    rdy_size--;
}

void lprintf_close(void) {
    /* ждем передачи всех данных */
    while (!(CHIP_REGS_USART0->LSR & CHIP_REGFLD_USART0_LSR_TEMT)) {}

    /* настройка пина в функцию по-умолчанию */
    CHIP_PIN_CONFIG(CHIP_PIN_ID(CHIP_PIN_ID_PORT(LPRINTF_UART_TX_PIN), CHIP_PIN_ID_PIN(LPRINTF_UART_TX_PIN), 0));

    /* возвращаем значения по умолчанию */
    CHIP_REGS_USART0->LCR = CHIP_REGFLD_USART0_LCR_DLAB;
    CHIP_REGS_USART0->DLL = 1;
    CHIP_REGS_USART0->DLM = 0;
    CHIP_REGS_USART0->FDR = 0x10;
    CHIP_REGS_USART0->LCR = 0;
    CHIP_REGS_USART0->IER = 0;
    CHIP_REGS_USART0->FCR = 0;


    chip_per_clk_dis(CHIP_PER_ID_USART0);
}
