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


#if ((LPRINTF_UART_NUM < 0) || (LPRINTF_UART_NUM >= CHIP_DEV_UART_CNT))
#error invalid lprintf UART number
#endif

#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

static int f_onfly_cnt = 0;

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

    //LPRINTF_UART->FCR = CHIP_REGFLD_UART_FCR_FIFOE | CHIP_REGFLD_UART_FCR_RFIFORST | CHIP_REGFLD_UART_FCR_TFIFORST;


    LPRINTF_UART->OSCR = 16;
    LPRINTF_UART->LCR = CHIP_REGFLD_UART_LCR_DLAB;
    uint32_t div = LPRINTF_UART_CLK_FREQ / (16 * baud);
    LPRINTF_UART->DLL = div & 0xFF;
    LPRINTF_UART->DLM = (div >> 8) & 0xFF;
    LPRINTF_UART->LCR = LBITFIELD_SET(CHIP_REGFLD_UART_LCR_EPS, parity == LPRINTF_UART_PARITY_EVEN ?
                                        CHIP_REGFLDVAL_UART_LCR_EPS_EVEN : CHIP_REGFLDVAL_UART_LCR_EPS_ODD)
                        | LBITFIELD_SET(CHIP_REGFLD_UART_LCR_PEN, parity != LPRINTF_UART_PARITY_NONE)
                        | LBITFIELD_SET(CHIP_REGFLD_UART_LCR_WLS, CHIP_REGFLDVAL_UART_LCR_WLS_8)

        ;

    LPRINTF_UART->FCR = CHIP_REGFLD_UART_FCR_FIFOE | CHIP_REGFLD_UART_FCR_RFIFORST | CHIP_REGFLD_UART_FCR_TFIFORST;

#if 0
    unsigned char len_bit = 0;
    unsigned char par_en = 0, par_sel = 0;

    chip_per_clk_en(LPRINTF_UART_PER_ID);
    chip_per_rst(LPRINTF_UART_PER_ID);

    /* настройка длины учитывает наличие бита четности */
    if (parity != LPRINTF_UART_PARITY_NONE)
        databits++;

    if (databits == 8) {
        len_bit = 0;
    } else if (databits == 9) {
        len_bit = 1;
    } else {
        err = -1;
    }

    if (!err) {
        if (parity == LPRINTF_UART_PARITY_NONE) {
            par_en = par_sel = 0;
        } else if (parity == LPRINTF_UART_PARITY_EVEN) {
            par_en = 1;
            par_sel = 0;
        } else if (parity == LPRINTF_UART_PARITY_ODD) {
            par_en = 1;
            par_sel = 1;
        } else {
            err = -2;
        }
    }

    if (!err) {
        unsigned uartdiv = (LPRINTF_UART_CLK_FREQ * LPRINTF_UART_FREQ_MUL  + baud/2) / baud;

        if (uartdiv < LPRINTF_UART_FREQ_DIV_MIN) {
            uartdiv = LPRINTF_UART_FREQ_DIV_MIN;
        }


        if (uartdiv > LPRINTF_UART_FREQ_DIV_MAX) {
            err = -3;
        } else {
            LPRINTF_UART->BRR = uartdiv;
        }
    }

    if (!err) {
        CHIP_PIN_CONFIG(LPRINTF_UART_TX_PIN);

        LPRINTF_UART->CR1 = (LPRINTF_UART->CR1 & CHIP_REGMSK_USART_CR1_RESERVED)
                            | CHIP_REGFLD_USART_CR1_UE;

        /* сброс всех битов в неиспользуемых регистрах CR3 и CR2, кроме резервных */
        LPRINTF_UART->CR3 = LPRINTF_UART->CR3 & CHIP_REGMSK_USART_CR3_RESERVED;
        LPRINTF_UART->CR2 = LPRINTF_UART->CR2 & CHIP_REGMSK_USART_CR2_RESERVED;

        LPRINTF_UART->CR1 |= LBITFIELD_SET(CHIP_REGFLD_USART_CR1_M, len_bit)
                             | LBITFIELD_SET(CHIP_REGFLD_USART_CR1_PCE, par_en)
                             | LBITFIELD_SET(CHIP_REGFLD_USART_CR1_PS, par_sel);
        LPRINTF_UART->CR1 |= CHIP_REGFLD_USART_CR1_TE;
    }
#endif

    return  err;
}

void lprintf_close(void) {
#if 0
    /* ожидание завершения передачи */
    while (lprintf_uart_busy())
        continue;

    /* запрет uart и сброс регистров c сохранением только резервных полей*/
    LPRINTF_UART->CR1 = LPRINTF_UART->CR1 & CHIP_REGMSK_USART_CR1_RESERVED;
    LPRINTF_UART->BRR = LPRINTF_UART->BRR & CHIP_REGMSK_USART_BRR_RESERVED;

    chip_per_clk_dis(LPRINTF_UART_PER_ID);
#endif
}

void lprintf_putchar(char c) {
    if (f_onfly_cnt == CHIP_DEV_UART_FIFO_SIZE) {
        while ((LPRINTF_UART->LSR & CHIP_REGFLD_UART_LSR_THRE) == 0) {
            continue;
        }
        f_onfly_cnt = 1;
    } else {
        ++f_onfly_cnt;
    }
    LPRINTF_UART->THR = c;
}

int lprintf_uart_busy(void) {
    return !(LPRINTF_UART->LSR & CHIP_REGFLD_UART_LSR_TEMPT);
}
