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

#ifndef ABS
    #define ABS(x) ((x) < 0 ? -(x) : (x))
#endif


#define LPRINTF_UART                        LPRINTF_UART_(LPRINTF_UART_NUM)
#define LPRINTF_UART_(num)                  LPRINTF_UART__(num)
#define LPRINTF_UART__(num)                 CHIP_REGS_UART ## num

#define LPRINTF_UART_CLK_FREQ               LPRINTF_UART_CLK_FREQ_(LPRINTF_UART_NUM)
#define LPRINTF_UART_CLK_FREQ_(num)         LPRINTF_UART_CLK_FREQ__(num)
#define LPRINTF_UART_CLK_FREQ__(num)        CHIP_CLK_ROOT_UART ## num ## _FREQ

#define LPRINTF_UART_CLK_NUM                LPRINTF_UART_CLK_NUM_(LPRINTF_UART_NUM)
#define LPRINTF_UART_CLK_NUM_(num)          LPRINTF_UART_CLK_NUM__(num)
#define LPRINTF_UART_CLK_NUM__(num)         CHIP_PER_CLK_NUM_UART ## num



int lprintf_uart_init(uint32_t baud, uint8_t databits, t_lprintf_uart_parity parity) {
    /* enable clock */
    chip_per_clk_en(LPRINTF_UART_CLK_NUM);

    /* UART enable */
    LPRINTF_UART->UCR1 = CHIP_REGFLD_UART_UCR1_UART_EN;
    /* сброс UART с ожиданием завершения сброса */
    LPRINTF_UART->UCR2 = 0;    
    while ((LPRINTF_UART->USR1 & CHIP_REGFLD_UART_UTS_SOFT_RST) != 0) {continue;}

    /* разрешение передачи и настройки параметров UART */
    LPRINTF_UART->UCR2 = CHIP_REGFLD_UART_UCR2_SRST
                        | CHIP_REGFLD_UART_UCR2_TX_EN
                        | LBITFIELD_SET(CHIP_REGFLD_UART_UCR2_WS, databits == 7 ?  CHIP_REGFLDVAL_UART_UCR2_WS_7 : CHIP_REGFLDVAL_UART_UCR2_WS_8)
                        | LBITFIELD_SET(CHIP_REGFLD_UART_UCR2_PR_EN, parity != LPRINTF_UART_PARITY_NONE)
                        | LBITFIELD_SET(CHIP_REGFLD_UART_UCR2_PR_OE, parity == CHIP_REGFLDVAL_UART_UCR2_PR_OE_ODD ?
                                                                          CHIP_REGFLDVAL_UART_UCR2_PR_OE_ODD : CHIP_REGFLDVAL_UART_UCR2_PR_OE_EVEN)
                        | CHIP_REGFLD_UART_UCR2_IRTS
        ;

    /* для простоты пределитель оставляем равным 1, а частоту уже подбираем через
     * значения UBIR и UBMR */
    LPRINTF_UART->UFCR = LBITFIELD_SET(CHIP_REGFLD_UART_UFCR_RX_TL, 1)
        | LBITFIELD_SET(CHIP_REGFLD_UART_UFCR_DTE, 0)
        | LBITFIELD_SET(CHIP_REGFLD_UART_UFCR_RF_DIV, CHIP_REGFLDVAL_UART_UFCR_RF_DIV_1)
        | LBITFIELD_SET(CHIP_REGFLD_UART_UFCR_TX_TL, 1)
        ;

    /* настройка частоты. подбор оптимального множителя дробной части, чтобы ошибка
     * отклонения частоты была минимальной. Проверяем перебором */
    float orig_fdiv = (float)CHIP_CLK_ROOT_UART2_FREQ/16/115200;
    unsigned find_mul = 1;
    unsigned find_idiv = 1;
    double find_err;

    /* верхняя граница прохода определяется, чтобы значение делителя не привысило
     * размер поля в 0xFFFF */
    for (unsigned mul = 1; mul <= 0x10000/((unsigned)orig_fdiv + 1); ++mul) {
        float fdiv_mul = orig_fdiv * mul;
        unsigned idiv_mul = (unsigned)(fdiv_mul + 0.5);
        float   check_err = ABS((float)CHIP_CLK_ROOT_UART2_FREQ * mul /(16 * idiv_mul) - baud);
        if ((mul == 1) || (check_err < find_err)) {
            find_err = check_err;
            find_mul = mul;
            find_idiv = idiv_mul;
        }
    }

    LPRINTF_UART->UBIR = find_mul - 1;
    LPRINTF_UART->UBMR = find_idiv - 1;

    /* настройка пина */
    CHIP_PIN_CONFIG(LPRINTF_UART_TX_PIN);

    return 0;
}


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

    CHIP_PIN_CONFIG_DIS(LPRINTF_UART_TX_PIN);

    LPRINTF_UART->UCR1 = 0;

    chip_per_clk_dis(LPRINTF_UART_CLK_NUM);
}


void lprintf_putchar(char c) {
    while (LPRINTF_UART->UTS & CHIP_REGFLD_UART_UTS_TX_FULL) {
        continue;
    }
    LPRINTF_UART->UTXD = c;
}

int lprintf_uart_busy(void) {
    return !(LPRINTF_UART->USR2 & CHIP_REGFLD_UART_USR2_TXDC);
}

