/*
 * ESP32 UART emulation
 *
 * Copyright (c) 2019 Espressif Systems (Shanghai) Co. Ltd.
 *
 * The QEMU model of nRF51 UART by Julia Suvorova was used as a template.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 or
 * (at your option) any later version.
 */

#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "sysemu/sysemu.h"
#include "chardev/char-fe.h"
#include "hw/registerfields.h"
#include "hw/sysbus.h"
#include "hw/irq.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
#include "hw/char/esp32_uart.h"
#include "trace.h"

#include "../softmmu/simuliface.h"
#include "../xtensa/esp32-simul.h"


//static gboolean uart_transmit(void *do_not_use, GIOCondition cond, void *opaque);
void uart_receive(void *opaque, const uint8_t *buf, int size);
int uart_can_receive(void *opaque);


void esp32_uart_update_irq(ESP32UARTState *s)
{
    bool irq = false;

    uint32_t tx_empty_raw = (fifo8_num_used(&s->tx_fifo) <= s->tx_empty_threshold);
    uint32_t rx_full_raw = (fifo8_num_used(&s->rx_fifo) >= s->rx_full_threshold);
    uint32_t tx_done_raw = (fifo8_num_used(&s->tx_fifo) != 0);
    uint32_t rxfifo_tout_raw = (s->rxfifo_tout) ? 1 : 0;

    uint32_t int_raw = s->reg[R_UART_INT_RAW];
    int_raw = FIELD_DP32(int_raw, UART_INT_RAW, RXFIFO_FULL, rx_full_raw);
    int_raw = FIELD_DP32(int_raw, UART_INT_RAW, TXFIFO_EMPTY, tx_empty_raw);
    int_raw = FIELD_DP32(int_raw, UART_INT_RAW, TX_DONE, tx_done_raw);
    int_raw = FIELD_DP32(int_raw, UART_INT_RAW, RXFIFO_TOUT, rxfifo_tout_raw);
    s->reg[R_UART_INT_RAW] = int_raw;

    uint32_t int_st = s->reg[R_UART_INT_RAW] & s->reg[R_UART_INT_ENA];
    irq = int_st != 0;
    s->reg[R_UART_INT_ST] = int_st;

    qemu_set_irq(s->irq, irq);
}


void esp32_uart_set_rx_timeout(ESP32UARTState *s)
{
    if (s->rx_tout_ena) {
        int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
        int64_t rx_timeout_ns = now + s->rx_tout_thres * NANOSECONDS_PER_SECOND / s->baud_rate;
        /* If throttling is done, make sure timeout doesn't happen before more data
         * is allowed to come. Offset it by 1ms.
         */
        if (rx_timeout_ns <= s->throttle_timer.expire_time) {
            rx_timeout_ns = s->throttle_timer.expire_time + 10000000;
        }
        timer_mod_ns(&s->rx_timeout_timer, rx_timeout_ns);
    } else {
        timer_del(&s->rx_timeout_timer);
        s->rxfifo_tout = false;
    }
}


static uint8_t fifo8_peek(Fifo8 *fifo)
{
    if (fifo->num == 0) abort();
    return fifo->data[fifo->head];
}

static void uart_send_next(void* opaque)
{
    ESP32UARTState *s = ESP32_UART(opaque);
    uint8_t b = fifo8_peek( &s->tx_fifo );      // Send next byte
    //printf("%c",b);
    writeReg( s->iomem.addr & 0x000FFFFF, b );  // UART_FIFO, Memory start+0x0

    timer_mod_ns( &s->tx_timer, getQemu_ns()+s->frame_time_ns);
}

static uint64_t uart_read(void *opaque, hwaddr addr, unsigned int size)
{
    ESP32UARTState *s = ESP32_UART(opaque);
    uint64_t r = 0;

    switch (addr) {
    case A_UART_FIFO:
        if (fifo8_num_used(&s->rx_fifo) == 0) {
            r = 0xEE;
            error_report("esp_uart: read UART FIFO while it is empty");
        } else {
            r = fifo8_pop(&s->rx_fifo);
            esp32_uart_update_irq(s);
            qemu_chr_fe_accept_input(&s->chr);
        }
        break;

    case A_UART_STATUS:
        r = FIELD_DP32(r, UART_STATUS, RXFIFO_CNT, fifo8_num_used(&s->rx_fifo));
        r = FIELD_DP32(r, UART_STATUS, TXFIFO_CNT, fifo8_num_used(&s->tx_fifo));
        break;

    case A_UART_LOWPULSE:
    case A_UART_HIGHPULSE:
        r = 337;  /* FIXME: this should depend on the APB frequency */
        break;
    case A_UART_MEM_CONF:
        r = FIELD_DP32(r, UART_MEM_CONF, RX_SIZE, (unsigned char)(UART_FIFO_LENGTH/128));
        r = FIELD_DP32(r, UART_MEM_CONF, TX_SIZE,  (unsigned char)(UART_FIFO_LENGTH/128));
        break;
    case A_UART_MEM_RX_STATUS: {
        uint32_t fifo_size = fifo8_num_used(&s->rx_fifo);
        /* The software only cares about the differene between WR_ADDR and RD_ADDR;
         * to keep things simpler, set RD_ADDR to 0 and WR_ADDR to the number of bytes
         * in the FIFO. 128 is a special case — write and read pointers should be
         * the same in this case.
         */
        r = FIELD_DP32(0, UART_MEM_RX_STATUS, WR_ADDR, (fifo_size == 128) ? 0 : fifo_size);
        }
        break;
    case A_UART_DATE:
        r = 0x15122500;
        break;
    default:
        r = s->reg[addr / 4];
        break;
    }

    return r;
}

//static unsigned int uart_calc_baud(ESP32UARTState *s)
//{
//    unsigned clkdiv = (FIELD_EX32(s->reg[R_UART_CLKDIV], UART_CLKDIV, CLKDIV) << 4) +
//                          FIELD_EX32(s->reg[R_UART_CLKDIV], UART_CLKDIV, CLKDIV_FRAG);
//    unsigned baud_rate = 115200;
//    if (clkdiv != 0) {
//        /* FIXME: this should depend on the APB frequency */
//        if(FIELD_EX32(s->reg[R_UART_CONF0], UART_CONF0, TICK_REF_ALWAYS_ON)){
//            baud_rate = (unsigned) ((80000000ULL << 4) / clkdiv);
//        }
//        else{
//            baud_rate = (unsigned) ((1000000ULL << 4) / clkdiv); //REF_TICK
//        }
//    }
//    return baud_rate;
//}

static void updateBaud( ESP32UARTState *s )
{
    uint32_t baud_rate = 115273;
    uint32_t freq = s->use_apb ? esp32_soc_get_apb_freq() : 1000000;
    if( freq == 0 ) freq = 40000001;
    if( s->clkdiv ) baud_rate = (freq << 4) / s->clkdiv;

    uint32_t bitTime = 1e9/baud_rate;
    s->frame_time_ns = bitTime*10;  /// TODO: this depends on frame size
    if( s->baud_rate != baud_rate ){
        s->baud_rate = baud_rate;
        //printf("Qemu: baudrate %i %i %i %lu\n", baud_rate, s->clkdiv, freq, s->frame_time_ns ); fflush( stdout );
        writeReg( (s->iomem.addr & 0x000FFFFF)+A_UART_CLKDIV, bitTime );
    }
}


static void uart_write(void *opaque, hwaddr addr, uint64_t value, unsigned int size )
{
    ESP32UARTState *s = ESP32_UART(opaque);

    switch (addr) {
    case A_UART_FIFO:
        if (fifo8_num_free(&s->tx_fifo) == 0) {
            error_report("esp_uart: write to UART FIFO while it is full");
        } else {
            //printf("Qemu: uart_write, %lu\n", value ); fflush( stdout );
            fifo8_push( &s->tx_fifo, value );
            if( fifo8_num_used(&s->tx_fifo) == 1 ) uart_send_next( s );
            //uart_transmit(NULL, G_IO_OUT, s);
        }
        break;

    case A_UART_INT_CLR:
        s->reg[R_UART_INT_ST] &= ~((uint32_t) value);
        s->reg[addr / 4] = value;
        if (value & R_UART_INT_CLR_RXFIFO_TOUT_MASK) {
            s->rxfifo_tout = false;
        }
        break;

    case A_UART_INT_ENA: s->reg[addr / 4] = value; break;
    case A_UART_CLKDIV: {
        s->reg[addr / 4] = value;

        uint32_t clkFra = (value & 0x00F00000) >> 20;
        uint32_t clkInt = (value & 0x000FFFFF) << 4 ;
        uint32_t clkdiv = clkInt | clkFra;
        if( s->clkdiv != clkdiv ){
            s->clkdiv = clkdiv;
            updateBaud( s );
        }
        //unsigned clkdiv = (FIELD_EX32( value, UART_CLKDIV, CLKDIV) << 4)
        //                 + FIELD_EX32( value, UART_CLKDIV, CLKDIV_FRAG);
        break;
    }
    case A_UART_AUTOBAUD:
        /* If autobaud is enabled, pretend that sufficient number of edges on the RXD line
         * have been received instantly. Autobaud is only used in the ROM bootloader,
         * and it doesn't care if the result is ready immediately.
         */
        if( FIELD_EX32(value, UART_AUTOBAUD, EN) ) s->reg[R_UART_RXD_CNT] = 0x3FF;
        else                                       s->reg[R_UART_RXD_CNT] = 0;
        s->reg[addr / 4] = value;
        break;

    case A_UART_INT_RAW:
    case A_UART_INT_ST:
    case A_UART_STATUS: /* no-op */ break;
    case A_UART_CONF0:
        s->reg[addr / 4] = value;
        uint8_t use_apb = (value & 1<<27)? 1 : 0;
        if( s->use_apb != use_apb ){
            s->use_apb = use_apb;
            updateBaud( s );
        }
        //printf("Qemu: CONF0 %lu\n", value ); fflush( stdout );
        writeReg( (s->iomem.addr & 0x000FFFFF)+addr, value );
        break;
    case A_UART_CONF1:
        s->reg[addr / 4] = value;
        s->tx_empty_threshold = FIELD_EX32(s->reg[R_UART_CONF1], UART_CONF1, TXFIFO_EMPTY_THRD);
        s->rx_full_threshold = FIELD_EX32(s->reg[R_UART_CONF1], UART_CONF1, RXFIFO_FULL_THRD);
        /* On the ESP32, rx_tout_thres is in units of (bit_time * 8).
         * Note this is different on later chips.
         */
        s->rx_tout_thres = 8 * FIELD_EX32(s->reg[R_UART_CONF1], UART_CONF1, TOUT_THRD);
        s->rx_tout_ena = FIELD_EX32(s->reg[R_UART_CONF1], UART_CONF1, TOUT_EN) != 0;
        esp32_uart_set_rx_timeout(s);
        esp32_uart_update_irq(s);
        break;

    default:
        if (addr > sizeof(s->reg)) {
            error_report("esp_uart: write to addr=0x%x out of bounds\n", (uint32_t) addr);
        } else {
            s->reg[addr / 4] = value;
        }
        break;
    }
    esp32_uart_update_irq(s);
}

void uart_receive(void *opaque, const uint8_t *buf, int size)
{
    ESP32UARTState *s = ESP32_UART(opaque);

    if (size == 0) {
        return;
    }

    /* If we can receive anything: cancel any pending RX timeout timer,
     * and clear the receive timeout flag.
     */
    if (fifo8_num_free(&s->rx_fifo) > 0) {
        timer_del(&s->rx_timeout_timer);
        s->rxfifo_tout = false;
    }

    /* Move the data into the FIFO */
    for (int i = 0; i < size && fifo8_num_free(&s->rx_fifo) > 0; i++) {
        fifo8_push(&s->rx_fifo, buf[i]);
    }

    /* Receive throttling: some applications (in particular the ESP32 ROM bootloader)
     * may work incorrectly if the data comes in much faster than what UART baud rate
     * would allow. This code adds a delay every UART_FIFO_LENGTH bytes, to make the
     * average data rate match the configured baud rate.
     * This doesn't need to be very precise, so only add the delay if the FIFO is full
     * (which most likely means that more data will come).
     */
    if (fifo8_is_full(&s->rx_fifo)) {
        s->throttle_rx = true;
        const int bits_per_symbol = 10;
        int64_t throttle_time_ns = (int64_t) UART_FIFO_LENGTH * bits_per_symbol * NANOSECONDS_PER_SECOND / s->baud_rate;
        timer_mod_ns(&s->throttle_timer,
                     qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
                     throttle_time_ns);
    }
    printf("uart_receive");
    esp32_uart_set_rx_timeout(s);
    esp32_uart_update_irq(s);
}

int uart_can_receive(void *opaque)
{
    ESP32UARTState *s = ESP32_UART(opaque);
    if (s->throttle_rx) {
        return 0;
    }
    return fifo8_num_free(&s->rx_fifo);
}

static void uart_event(void *opaque, QEMUChrEvent event)
{
    /* TODO: handle UART break */
}

static void uart_tx_timer_cb(void* opaque)
{
    ESP32UARTState *s = ESP32_UART(opaque);

    fifo8_pop( &s->tx_fifo );
    if( fifo8_num_used( &s->tx_fifo ) ) uart_send_next( s );
    //printf("uart_tx_timer %lu\n", getQemu_ps() );fflush( stdout );
    esp32_uart_update_irq(s);
}

static void uart_throttle_timer_cb(void* opaque)
{
    ESP32UARTState *s = ESP32_UART(opaque);
    s->throttle_rx = false;
    qemu_chr_fe_accept_input(&s->chr);
}

static void uart_rx_timeout_timer_cb(void* opaque)
{
    ESP32UARTState *s = ESP32_UART(opaque);
    s->rxfifo_tout = true;
    esp32_uart_update_irq(s);
}

static void esp32_uart_reset(DeviceState *dev)
{
    ESP32UARTState *s = ESP32_UART(dev);

    memset(s->reg, 0, sizeof(s->reg));
    s->reg[R_UART_RXD_CNT] = 0;
    s->reg[R_UART_INT_ST] = 0;
    s->reg[R_UART_INT_RAW] = 0;
    s->reg[R_UART_INT_ENA] = 0;
    s->reg[R_UART_AUTOBAUD] = 0;
    /* Default baud rate divider after reset */
    s->reg[R_UART_CLKDIV] = FIELD_DP32(0, UART_CLKDIV, CLKDIV, 0x2B6);
    //s->reg[R_UART_CONF0] = FIELD_DP32(0 , UART_CONF0, TICK_REF_ALWAYS_ON, 1);
    //s->reg[R_UART_CONF0] = FIELD_DP32(s->reg[R_UART_CONF0] , UART_CONF0, STOP_BIT_NUM, 1);
    //s->reg[R_UART_CONF0] = FIELD_DP32(s->reg[R_UART_CONF0] , UART_CONF0, BIT_NUM, 3);

    s->use_apb = 1;
    s->clkdiv = 11104;
    s->baud_rate = 115273;
    fifo8_reset(&s->tx_fifo);
    fifo8_reset(&s->rx_fifo);
    if (s->tx_watch_handle) {
        g_source_remove(s->tx_watch_handle);
        s->tx_watch_handle = 0;
    }
    timer_del(&s->throttle_timer);
    s->throttle_rx = false;
    s->rx_tout_ena = false;
    s->tx_empty_threshold = 0;
    s->rx_full_threshold = 0;
    s->rx_tout_thres = 0;
    qemu_irq_lower(s->irq);
}

extern GMainContext *g_main_context_default_l;

//static int uart_num = 0;
static void esp32_uart_realize(DeviceState *dev, Error **errp)
{
    //ESP32UARTState *s = ESP32_UART(dev);
    //s->id = uart_num++;
    //qemu_chr_fe_set_handlers(&s->chr, uart_can_receive, uart_receive,
    //                         uart_event, NULL, s, g_main_context_default_l, true);
}


static void esp32_uart_init(Object *obj)
{
    ESP32UARTState *s = ESP32_UART(obj);
    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    ESP32UARTClass *class = ESP32_UART_GET_CLASS(obj);

    s->uart_ops = (MemoryRegionOps) {
        .read =  class->uart_read,
        .write = class->uart_write,
        .endianness = DEVICE_LITTLE_ENDIAN,
    };

    memory_region_init_io(&s->iomem, obj, &s->uart_ops, s,
                          TYPE_ESP32_UART, UART_REG_CNT * sizeof(uint32_t));
    sysbus_init_mmio(sbd, &s->iomem);
    sysbus_init_irq(sbd, &s->irq);
    fifo8_create(&s->tx_fifo, UART_FIFO_LENGTH);
    fifo8_create(&s->rx_fifo, UART_FIFO_LENGTH);
    timer_init_ns(&s->throttle_timer, QEMU_CLOCK_VIRTUAL, uart_throttle_timer_cb, s);
    timer_init_ns(&s->rx_timeout_timer, QEMU_CLOCK_VIRTUAL, uart_rx_timeout_timer_cb, s);
    timer_init_ns( &s->tx_timer, QEMU_CLOCK_VIRTUAL, uart_tx_timer_cb, s);
}


static Property esp32_uart_properties[] = {
    DEFINE_PROP_CHR("chardev", ESP32UARTState, chr),
    DEFINE_PROP_END_OF_LIST(),
};

static void esp32_uart_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);
    ESP32UARTClass *class = ESP32_UART_CLASS(klass);

    /* Populate the virtual attributes and methods here (if any) */
    class->uart_write = uart_write;
    class->uart_read = uart_read;

    dc->reset = esp32_uart_reset;
    dc->realize = esp32_uart_realize;
    device_class_set_props(dc, esp32_uart_properties);
}

static const TypeInfo esp32_uart_info = {
    .name = TYPE_ESP32_UART,
    .parent = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(ESP32UARTState),
    .instance_init = esp32_uart_init,
    .class_init = esp32_uart_class_init,
    .class_size = sizeof(ESP32UARTClass)
};

static void esp32_uart_register_types(void)
{
    type_register_static(&esp32_uart_info);
}

type_init(esp32_uart_register_types)
