/*
 * Copyright (c) 2013-2019, Huawei Technologies Co., Ltd. All rights reserved.
 * Copyright (c) 2020, Huawei Device Co., Ltd. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 *    of conditions and the following disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "los_event.h"
#include "hisoc/uart.h"


EVENT_CB_S g_uartEvent;
#ifdef LOSCFG_PLATFORM_UART_WITHOUT_VFS
#ifdef LOSCFG_SHELL
#define UART_BUF    128
static UINT8 g_uart_buf[UART_BUF];
extern void shellCmdLineParse(CHAR c, pf_OUTPUT pf_put);
#endif
#endif
UINT8 uart_getc(void)
{
    UINT8 ch = 0;

    while (!(GET_UINT32(UART_REG_BASE + UART_USR) & 0x08)) { /*lint !e40*/
        LOS_Msleep(100);
    }
    /* receive one char */
    READ_UINT8(ch, UART_REG_BASE + UART_DR);
    return ch;
}

#if defined(LOSCFG_COREDUMP) || defined(LOSCFG_LLTSER)
UINT8 uart_getc_interrupt(void)
{
    UINT8 ch = 0;

    while (!(GET_UINT32(UART_REG_BASE + UART_USR) & 0x08)) { /*lint !e40*/
    }
    /* receive one char */
    READ_UINT8(ch, UART_REG_BASE + UART_DR);
    return ch;
}
#endif
/* send */
char uart_putc (char c)
{
    /* Wait until THRE is empyt */
    while (!(GET_UINT32(UART_REG_BASE + UART_USR) & 0x02)); /*lint !e40*/
    /* send one char */
    WRITE_UINT8(c, UART_REG_BASE + UART_DR);
    return c;
}

unsigned int g_uart_fputc_en __attribute__ ((section(".data"))) = 1;
char uart_fputc(char c, void *f)
{
    if (g_uart_fputc_en == 1) {
        if (c == '\n') {
            uart_putc('\r'); /*lint !e534*/
        }
        return (uart_putc(c));
    } else {
        return 0;
    }
}

#ifdef LOSCFG_PLATFORM_UART_WITHOUT_VFS
#ifdef LOSCFG_SHELL
static void uart_notice_adapt(void)
{
    LOS_EventWrite(&g_uartEvent, 0x112);
}

void uart_get_raw(void)
{
    UINT8 ch;
    static int cnt_ii = 0;
    if (cnt_ii == 0) {
        (VOID)memset_s(g_uart_buf, UART_BUF, 0, UART_BUF);
    }
    ch = uart_getc();
    g_uart_buf[cnt_ii] = ch;
    cnt_ii++;
    switch (cnt_ii) {
        case 1: // only one char
            if (ch != 27) {  //   un special
                uart_notice_adapt();
                cnt_ii = 0;
            }
            break;
        case 2:
            if (ch != 91) {
                uart_notice_adapt();
                cnt_ii = 0;
            }
            break;
        case 3:
            switch (ch) {
                default:
                case 'A':
                case 'B':
                case 'C':
                case 'D':
                    {
                        uart_notice_adapt();
                        cnt_ii = 0;
                    }
                    break;
                case 51:
                case 49:
                case 52:
                    break;
            }
            break;
        case 4:
            {
                uart_notice_adapt();
                cnt_ii = 0;
            }
            break;
        default:
            {
                uart_notice_adapt();
                cnt_ii = 0;
            }
            break;
    }
}
extern void dprintf(const char *fmt, ...);

static void uart_irqhandle(void)
{
    UINT8 ch;
    shellCmdLineParse(0, dprintf);
}

int uart_hwiCreate(void)
{
    UINT32 uwRet = 0;
    if (uwRet != LOS_HwiCreate(NUM_HAL_INTERRUPT_UART, 0xa0, 0, uart_irqhandle, 0)) {
        return uwRet;
    }
    uart_interrupt_unmask();
    return 0;
}

#endif
#endif
void uart_init()
{
    unsigned int temp;
    unsigned int divider;
    unsigned char dividerH, dividerL;

    /* disable UART1 FIFO */
    WRITE_UINT32(0, UART_REG_BASE + UART_FCR); /*lint !e40*/

    /* reset and enable UART1 FIFO */
    WRITE_UINT32(0x7, UART_REG_BASE + UART_FCR); /*lint !e40*/

    /* disable UART1 interrupt */
    WRITE_UINT32(0, UART_REG_BASE + UART_IER); /*lint !e40*/

    /* enable DLL and DLH */
    WRITE_UINT32(0x80, UART_REG_BASE + UART_LCR); /*lint !e40*/
    /* Caculate devide */
    temp = 16 * CONSOLE_UART_BAUDRATE;
    divider = CONFIG_UART_CLK_INPUT / temp;
    dividerH = ((divider) & 0xff00) >> 8;
    dividerL = ((divider) & 0x00ff);

    /* configure DLL and DLH */
    WRITE_UINT32(dividerL, UART_REG_BASE + UART_DLL); /*lint !e40*/
    WRITE_UINT32(dividerH, UART_REG_BASE + UART_DLH); /*lint !e40*/

    /* disable DLL and DLH */
    WRITE_UINT32(0x0, UART_REG_BASE + UART_LCR); /*lint !e40*/

    /* 8bit data, 1bit stop,even parity */
    WRITE_UINT32(0x1b, UART_REG_BASE + UART_LCR); /*lint !e40*/

    /* enable UART1 */
    WRITE_UINT32(0x1, UART_REG_BASE + UART_IER); /*lint !e40*/

    (VOID)LOS_EventInit(&g_uartEvent);
}

void uart_interrupt_unmask(void)
{
    HalIrqUnmask(NUM_HAL_INTERRUPT_UART);
}
