/*
 * Copyright (c) 2006-2020, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2020-04-05     bigmagic     Initial version
 */

/**
 * @addtogroup ls2k
 */

/*@{*/

#include <rtthread.h>
#include <rtdevice.h>
#include <rthw.h>
#include "drv_uart.h"

#define TRUE 1
#define FALSE 0

struct rt_uart_ls2k
{
    void *base;
    rt_uint32_t IRQ;
};

static rt_err_t ls2k_uart_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
{
    struct rt_uart_ls2k *uart_dev = RT_NULL;

    RT_ASSERT(serial != RT_NULL);
    RT_ASSERT(cfg != RT_NULL);

    uart_dev = (struct rt_uart_ls2k *)serial->parent.user_data;

    UART_IER(uart_dev->base) = 0; /* clear interrupt */
    UART_FCR(uart_dev->base) = 0xc1; /* reset UART Rx/Tx */
    /* set databits, stopbits and parity. (8-bit data, 1 stopbit, no parity) */
    UART_LCR(uart_dev->base) = 0x3;
    UART_MCR(uart_dev->base) = 0x3;
    UART_LSR(uart_dev->base) = 0x60;
    UART_MSR(uart_dev->base) = 0xb0;

    return RT_EOK;
}

static rt_err_t ls2k_uart_control(struct rt_serial_device *serial, int cmd, void *arg)
{
    struct rt_uart_ls2k *uart_dev = RT_NULL;

    RT_ASSERT(serial != RT_NULL);
    uart_dev = (struct rt_uart_ls2k *)serial->parent.user_data;

    switch (cmd)
    {
    case RT_DEVICE_CTRL_CLR_INT: /* Disable RX IRQ */
        rt_hw_interrupt_mask(uart_dev->IRQ);
        break;

    case RT_DEVICE_CTRL_SET_INT: /* Enable RX IRQ */
        rt_hw_interrupt_umask(uart_dev->IRQ);
        UART_IER(uart_dev->base) |= (IER_IRxE|IER_ILE);
        break;

    default:
        break;
    }

    return RT_EOK;

}

static rt_bool_t uart_is_transmit_empty(struct rt_uart_ls2k *uart_dev)
{
    unsigned char status = UART_LSR(uart_dev->base);

    if (status & (UARTLSR_TE | UARTLSR_TFE))
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

static int ls2k_uart_putc(struct rt_serial_device *serial, char c)
{
    struct rt_uart_ls2k *uart_dev = RT_NULL;

    RT_ASSERT(serial != RT_NULL);

    uart_dev = (struct rt_uart_ls2k *)serial->parent.user_data;

    while (FALSE == uart_is_transmit_empty(uart_dev))
        ;

    UART_DAT(uart_dev->base) = c;

    return 1;
}

static int ls2k_uart_getc(struct rt_serial_device *serial)
{
    struct rt_uart_ls2k *uart_dev = RT_NULL;

    RT_ASSERT(serial != RT_NULL);

    uart_dev = (struct rt_uart_ls2k *)serial->parent.user_data;

    if (LSR_RXRDY & UART_LSR(uart_dev->base))
    {
        return UART_DAT(uart_dev->base);
    }

    return -1;
}

/* UART interrupt handler */
static void uart_irq_handler(int vector, void *param)
{
    struct rt_serial_device *serial = (struct rt_serial_device *)param;
    struct rt_uart_ls2k *uart_dev = RT_NULL;

    RT_ASSERT(serial != RT_NULL);

    uart_dev = (struct rt_uart_ls2k *)serial->parent.user_data;
    unsigned char iir = UART_IIR(uart_dev->base);

    /* Find out interrupt reason */
    if ((IIR_RXTOUT & iir) || (IIR_RXRDY & iir))
    {
        rt_interrupt_enter();
        rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);
        rt_interrupt_leave();
    }

}

static const struct rt_uart_ops ls2k_uart_ops =
{
    ls2k_uart_configure,
    ls2k_uart_control,
    ls2k_uart_putc,
    ls2k_uart_getc,
};

struct rt_uart_ls2k uart_dev0 =
{
    (void *)UARTx_BASE(0),
    LS2K_UART_0_1_2_3_IRQ,
};
struct rt_serial_device serial;


void rt_hw_uart_init(void)
{
    struct rt_uart_ls2k *uart;
    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;

    uart = &uart_dev0;

    serial.ops    = &ls2k_uart_ops;
    serial.config = config;

    rt_hw_interrupt_install(uart->IRQ, uart_irq_handler, &serial, "UART");

    /* register UART device */
    rt_hw_serial_register(&serial,
                          "uart",
                          RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
                          uart);
}
/*@}*/