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


#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_FREQ_DIV_MAX   0xFFFF
#define LPRINTF_UART_FREQ_DIV_MIN   16
#define LPRINTF_UART_FREQ_MUL       1



#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;
    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;
    }


    return  err;
}

void lprintf_close(void) {
    /* ожидание завершения передачи */
    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);
}

void lprintf_putchar(char c) {
    while ((LPRINTF_UART->SR & CHIP_REGFLD_USART_SR_TXE) == 0) {
        continue;
    }
    LPRINTF_UART->DR = LBITFIELD_SET(CHIP_REGFLD_USART_DR_VAL, c);
}

int lprintf_uart_busy(void) {
    return !(LPRINTF_UART->SR & CHIP_REGFLD_USART_SR_TC);
}
