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

#define UART_FIFO_SIZE 16

#if (LPRINTF_UART_NUM == 0) || (LPRINTF_UART_NUM == 2) || (LPRINTF_UART_NUM == 3)
    #define LPRINTF_UART_PREFIX  USART
#elif (LPRINTF_UART_NUM == 1)
    #define LPRINTF_UART_PREFIX  UART
#else
    #error Invalid uart number
#endif

#ifndef LPRINTF_UART_TX_PIN
    #error lprintf uart tx pin is not specified
#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)           LPC_ ## pref ## num

#define LPRINTF_UART_CLK_MX                 LPRINTF_UART_CLK_MX_(LPRINTF_UART_NUM)
#define LPRINTF_UART_CLK_MX_(num)           LPRINTF_UART_CLK_MX__(num)
#define LPRINTF_UART_CLK_MX__(num)          CLK_MX_UART ## num

#define LPRINTF_UART_CLK_BASE               LPRINTF_UART_CLK_BASE_(LPRINTF_UART_NUM)
#define LPRINTF_UART_CLK_BASE_(num)         LPRINTF_UART_CLK_BASE__(num)
#define LPRINTF_UART_CLK_BASE__(num)        CLK_BASE_UART ## num

#define LPRINTF_UART_CLK_BASE_CFG               LPRINTF_UART_CLK_BASE_CFG_(LPRINTF_UART_NUM)
#define LPRINTF_UART_CLK_BASE_CFG_(num)         LPRINTF_UART_CLK_BASE_CFG__(num)
#define LPRINTF_UART_CLK_BASE_CFG__(num)        LPC_BASE_UART ## num ## _CFG


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

    if (!err) {
        switch (databits) {
            case 5:
                cfg |= UART_LCR_WLEN5;
                break;
            case 6:
                cfg |= UART_LCR_WLEN6;
                break;
            case 7:
                cfg |= UART_LCR_WLEN7;
                break;
            case 8:
                cfg |= UART_LCR_WLEN8;
                break;
            default:
                err = -1;
                break;
        }
    }

    if (!err) {
        switch(parity) {
            case LPRINTF_UART_PARITY_NONE:
                cfg |= UART_LCR_PARITY_DIS;
                break;
            case LPRINTF_UART_PARITY_ODD:
                cfg |= UART_LCR_PARITY_EN | UART_LCR_PARITY_ODD;
                break;
            case LPRINTF_UART_PARITY_EVEN:
                cfg |= UART_LCR_PARITY_EN | UART_LCR_PARITY_EVEN;
                break;
            default:
                err = -2;
                break;
        }
    }

    if (!err) {
        LPC_CGU->BASE_CLK[LPRINTF_UART_CLK_BASE] = LPRINTF_UART_CLK_BASE_CFG;
        lpc_delay_clk(20);
        LPC_CCU_CFG(LPRINTF_UART_CLK_MX, 1);
        lpc_delay_clk(20);

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


        unsigned dl;
        unsigned fract_div;
        unsigned fract_mul;
        f_get_baudrate_settings(LPC_CLKF_BASE(LPRINTF_UART_CLK_BASE_CFG), baud, &dl, &fract_div, &fract_mul);

        if (fract_mul != 0) {
            LPRINTF_UART->LCR |= UART_LCR_DLAB_EN;
            LPRINTF_UART->DLL = dl & 0xFF;
            LPRINTF_UART->DLM = (dl >> 8) & 0xFF;
            LPRINTF_UART->FDR =UART_FDR_DIVADDVAL(fract_div) |
                         UART_FDR_MULVAL(fract_mul);
            LPRINTF_UART->LCR &= ~UART_LCR_DLAB_EN;
            LPRINTF_UART->FCR = UART_FCR_FIFO_EN | UART_FCR_RX_RS | UART_FCR_TX_RS;;
            LPRINTF_UART->FCR = UART_FCR_FIFO_EN;

            (void)LPRINTF_UART->IIR;
        } else {
            err = -1;
        }
    }

    if (!err) {
        CHIP_PIN_CONFIG(LPRINTF_UART_TX_PIN, 0, CHIP_PIN_MODE_FAST | CHIP_PIN_MODE_NOPULL);
    }

    return err;
}




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

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


    if (empty)
        rdy_size = UART_FIFO_SIZE;

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


void lprintf_close(void) {

    /* ждем передачи всех данных */
    while (!(LPRINTF_UART->LSR & UART_LSR_TEMT)) {
        continue;
    }

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

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



    LPC_CCU_CFG(LPRINTF_UART_CLK_MX, 0);
    LPC_CGU->BASE_CLK[LPRINTF_UART_CLK_BASE] = 1;
}
