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


#ifndef LPRINTF_UART_LP
    #define LPRINTF_UART_LP 0
#endif


#if LPRINTF_UART_LP
    #if ((LPRINTF_UART_NUM < 1) || (LPRINTF_UART_NUM > CHIP_DEV_LPUART_CNT))
        #error invalid lprintf LPUART number
    #endif

    #define #define LPRINTF_UART_PREFIX LPUART

    #define LPRINTF_UART_SUPPORT_PRESCALER  CHIP_LPUART_SUPPORT_FIFO(LPRINTF_UART_NUM)
    #define LPRINTF_UART_SUPPORT_FIFO       CHIP_LPUART_SUPPORT_PRESC(LPRINTF_UART_NUM)

    #define LPRINTF_UART_FREQ_DIV_MAX   0xFFFFF
    #define LPRINTF_UART_FREQ_DIV_MIN   0x300
    #define LPRINTF_UART_FREQ_MUL       256
#else
    #if ((LPRINTF_UART_NUM < 1) || (LPRINTF_UART_NUM > CHIP_DEV_UART_CNT))
        #error invalid lprintf UART number
    #endif

    #if CHIP_USART_SUPPORT_SYNC(LPRINTF_UART_NUM)
        #define LPRINTF_UART_PREFIX  USART
    #else
        #define LPRINTF_UART_PREFIX  UART
    #endif

    #define LPRINTF_UART_SUPPORT_PRESCALER  CHIP_USART_SUPPORT_FIFO(LPRINTF_UART_NUM)
    #define LPRINTF_UART_SUPPORT_FIFO       CHIP_USART_SUPPORT_PRESC(LPRINTF_UART_NUM)

    #define LPRINTF_UART_FREQ_DIV_MAX   0xFFFF
    #define LPRINTF_UART_FREQ_DIV_MIN   16
    #define LPRINTF_UART_FREQ_MUL       1
#endif


#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





#if LPRINTF_UART_SUPPORT_PRESCALER
static const uint16_t f_uart_presc_vals[] = {1, 2, 4, 6, 8, 10, 12, 16, 32, 64, 128, 256};
#endif


int lprintf_uart_init(uint32_t baud, uint8_t databits, t_lprintf_uart_parity parity) {
    int err = 0;
    unsigned char m0 = 0, m1 = 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) {
        m0 = m1 = 0;
    } else if (databits == 9) {
        m1 = 0;
        m0 = 1;
    } else if (databits == 7) {
        m1 = 1;
        m0 = 0;
    } 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 LPRINTF_UART_SUPPORT_PRESCALER
        unsigned uart_presc_code = 0;


        for (unsigned char i = 0; (i < sizeof(f_uart_presc_vals)/sizeof(f_uart_presc_vals[0]))
             && (uartdiv > LPRINTF_UART_FREQ_DIV_MAX); ++i) {
            unsigned div = uartdiv / f_uart_presc_vals[i];
            if (div <= LPRINTF_UART_FREQ_DIV_MAX) {
                uart_presc_code = (char)i;
                uartdiv = div;
            }
        }
#endif

        if (uartdiv > LPRINTF_UART_FREQ_DIV_MAX) {
            err = -3;
        } else {
#if LPRINTF_UART_SUPPORT_PRESCALER
            LBITFIELD_UPD(LPRINTF_UART->PRESC, CHIP_REGFLD_USART_PRESC_PRESCALER, uart_presc_code);
#endif
            LPRINTF_UART->BRR = uartdiv;
        }
    }

    if (!err) {
        CHIP_PIN_CONFIG(LPRINTF_UART_TX_PIN);


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

        LPRINTF_UART->CR1 = 0
#if LPRINTF_UART_SUPPORT_FIFO
                | CHIP_REGFLD_USART_CR1_FIFO_EN
#endif
                | (m1 ? CHIP_REGFLD_USART_CR1_M1 : 0)
                | (m0 ? CHIP_REGFLD_USART_CR1_M0 : 0)
                | (par_en ? CHIP_REGFLD_USART_CR1_PCE : 0)
                | (par_sel ? CHIP_REGFLD_USART_CR1_PS : 0)
                | CHIP_REGFLD_USART_CR1_TE;
        LPRINTF_UART->CR1 |= CHIP_REGFLD_USART_CR1_UE;
    }


    return  err;
}

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

    /* запрет uart и сброс регистров c сохранением только резервных полей*/
    LPRINTF_UART->CR1 = 0;
    LPRINTF_UART->CR2 &= 0x00000086;
    LPRINTF_UART->CR3 &= 0x00010000;
    LPRINTF_UART->BRR &= 0xFFFF0000;
#if LPRINTF_UART_SUPPORT_PRESCALER
    LPRINTF_UART->PRESC &= 0xFFFFFFF0;
#endif

    chip_per_clk_dis(LPRINTF_UART_PER_ID);
}

void lprintf_putchar(char c) {
    while ((LPRINTF_UART->ISR & CHIP_REGFLD_USART_ISR_TXE) == 0) {
        continue;
    }
    LPRINTF_UART->TDR = LBITFIELD_SET(CHIP_REGFLD_USART_TDR_TDR, c);
}

int lprintf_uart_busy(void) {
    return !(LPRINTF_UART->ISR & CHIP_REGFLD_USART_ISR_TC);
}
