GRLIB UART: Add RX channel

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

Signed-off-by: Fabien Chouteau <chouteau@adacore.com>
Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
This commit is contained in:
Fabien Chouteau 2012-01-26 18:03:15 +01:00 committed by Blue Swirl
parent fd39941ac7
commit 0c685d2827
2 changed files with 90 additions and 17 deletions

View File

@ -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,
};

View File

@ -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"