Commit 0c685d28 authored by Fabien Chouteau's avatar Fabien Chouteau Committed by Blue Swirl
Browse files

GRLIB UART: Add RX channel



This patch implements the RX channel of GRLIB UART with a FIFO to
improve data rate.

Signed-off-by: default avatarFabien Chouteau <chouteau@adacore.com>
Signed-off-by: default avatarBlue Swirl <blauwirbel@gmail.com>
parent fd39941a
Loading
Loading
Loading
Loading
+89 −17
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@

#include "sysbus.h"
#include "qemu-char.h"
#include "ptimer.h"

#include "trace.h"

@@ -66,6 +65,8 @@
#define SCALER_OFFSET     0x0C  /* not supported */
#define FIFO_DEBUG_OFFSET 0x10  /* not supported */

#define FIFO_LENGTH 1024

typedef struct UART {
    SysBusDevice busdev;
    MemoryRegion iomem;
@@ -77,20 +78,66 @@ typedef struct UART {
    uint32_t receive;
    uint32_t status;
    uint32_t control;

    /* FIFO */
    char buffer[FIFO_LENGTH];
    int  len;
    int  current;
} UART;

static int uart_data_to_read(UART *uart)
{
    return uart->current < uart->len;
}

static char uart_pop(UART *uart)
{
    char ret;

    if (uart->len == 0) {
        uart->status &= ~UART_DATA_READY;
        return 0;
    }

    ret = uart->buffer[uart->current++];

    if (uart->current >= uart->len) {
        /* Flush */
        uart->len     = 0;
        uart->current = 0;
    }

    if (!uart_data_to_read(uart)) {
        uart->status &= ~UART_DATA_READY;
    }

    return ret;
}

static void uart_add_to_fifo(UART          *uart,
                             const uint8_t *buffer,
                             int            length)
{
    if (uart->len + length > FIFO_LENGTH) {
        abort();
    }
    memcpy(uart->buffer + uart->len, buffer, length);
    uart->len += length;
}

static int grlib_apbuart_can_receive(void *opaque)
{
    UART *uart = opaque;

    return !!(uart->status & UART_DATA_READY);
    return FIFO_LENGTH - uart->len;
}

static void grlib_apbuart_receive(void *opaque, const uint8_t *buf, int size)
{
    UART *uart = opaque;

    uart->receive  = *buf;
    uart_add_to_fifo(uart, buf, size);

    uart->status |= UART_DATA_READY;

    if (uart->control & UART_RECEIVE_INTERRUPT) {
@@ -103,8 +150,38 @@ static void grlib_apbuart_event(void *opaque, int event)
    trace_grlib_apbuart_event(event);
}

static void
grlib_apbuart_write(void *opaque, target_phys_addr_t addr,

static uint64_t grlib_apbuart_read(void *opaque, target_phys_addr_t addr,
                                   unsigned size)
{
    UART     *uart = opaque;

    addr &= 0xff;

    /* Unit registers */
    switch (addr) {
    case DATA_OFFSET:
    case DATA_OFFSET + 3:       /* when only one byte read */
        return uart_pop(uart);

    case STATUS_OFFSET:
        /* Read Only */
        return uart->status;

    case CONTROL_OFFSET:
        return uart->control;

    case SCALER_OFFSET:
        /* Not supported */
        return 0;

    default:
        trace_grlib_apbuart_readl_unknown(addr);
        return 0;
    }
}

static void grlib_apbuart_write(void *opaque, target_phys_addr_t addr,
                                uint64_t value, unsigned size)
{
    UART          *uart = opaque;
@@ -115,6 +192,7 @@ grlib_apbuart_write(void *opaque, target_phys_addr_t addr,
    /* Unit registers */
    switch (addr) {
    case DATA_OFFSET:
    case DATA_OFFSET + 3:       /* When only one byte write */
        c = value & 0xFF;
        qemu_chr_fe_write(uart->chr, &c, 1);
        return;
@@ -124,7 +202,7 @@ grlib_apbuart_write(void *opaque, target_phys_addr_t addr,
        return;

    case CONTROL_OFFSET:
        /* Not supported */
        uart->control = value;
        return;

    case SCALER_OFFSET:
@@ -138,15 +216,9 @@ grlib_apbuart_write(void *opaque, target_phys_addr_t addr,
    trace_grlib_apbuart_writel_unknown(addr, value);
}

static bool grlib_apbuart_accepts(void *opaque, target_phys_addr_t addr,
                                  unsigned size, bool is_write)
{
    return is_write && size == 4;
}

static const MemoryRegionOps grlib_apbuart_ops = {
    .write      = grlib_apbuart_write,
    .valid.accepts = grlib_apbuart_accepts,
    .read       = grlib_apbuart_read,
    .endianness = DEVICE_NATIVE_ENDIAN,
};

+1 −0
Original line number Diff line number Diff line
@@ -351,6 +351,7 @@ grlib_irqmp_writel_unknown(uint64_t addr, uint32_t value) "addr 0x%"PRIx64" valu
# hw/grlib_apbuart.c
grlib_apbuart_event(int event) "event:%d"
grlib_apbuart_writel_unknown(uint64_t addr, uint32_t value) "addr 0x%"PRIx64" value 0x%x"
grlib_apbuart_readl_unknown(uint64_t addr) "addr 0x%"PRIx64""

# hw/leon3.c
leon3_set_irq(int intno) "Set CPU IRQ %d"