mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-23 19:49:43 +00:00
bcm2835_aux: add emulation of BCM2835 AUX (aka UART1) block
At present only the core UART functions (data path for tx/rx) are implemented, which is enough for UEFI to boot. The following features/registers are unimplemented: * Line/modem control * Scratch register * Extra control * Baudrate * SPI interfaces Signed-off-by: Andrew Baumann <Andrew.Baumann@microsoft.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 1457467526-8840-3-git-send-email-Andrew.Baumann@microsoft.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
a2a8dfa8d8
commit
97398d900c
@ -12,6 +12,7 @@
|
||||
#include "hw/arm/bcm2835_peripherals.h"
|
||||
#include "hw/misc/bcm2835_mbox_defs.h"
|
||||
#include "hw/arm/raspi_platform.h"
|
||||
#include "sysemu/char.h"
|
||||
|
||||
/* Peripheral base address on the VC (GPU) system bus */
|
||||
#define BCM2835_VC_PERI_BASE 0x7e000000
|
||||
@ -48,6 +49,11 @@ static void bcm2835_peripherals_init(Object *obj)
|
||||
object_property_add_child(obj, "uart0", OBJECT(s->uart0), NULL);
|
||||
qdev_set_parent_bus(DEVICE(s->uart0), sysbus_get_default());
|
||||
|
||||
/* AUX / UART1 */
|
||||
object_initialize(&s->aux, sizeof(s->aux), TYPE_BCM2835_AUX);
|
||||
object_property_add_child(obj, "aux", OBJECT(&s->aux), NULL);
|
||||
qdev_set_parent_bus(DEVICE(&s->aux), sysbus_get_default());
|
||||
|
||||
/* Mailboxes */
|
||||
object_initialize(&s->mboxes, sizeof(s->mboxes), TYPE_BCM2835_MBOX);
|
||||
object_property_add_child(obj, "mbox", OBJECT(&s->mboxes), NULL);
|
||||
@ -79,6 +85,7 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
|
||||
MemoryRegion *ram;
|
||||
Error *err = NULL;
|
||||
uint32_t ram_size;
|
||||
CharDriverState *chr;
|
||||
int n;
|
||||
|
||||
obj = object_property_get_link(OBJECT(dev), "ram", &err);
|
||||
@ -131,6 +138,29 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
|
||||
qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
|
||||
INTERRUPT_UART));
|
||||
|
||||
/* AUX / UART1 */
|
||||
/* TODO: don't call qemu_char_get_next_serial() here, instead set
|
||||
* chardev properties for each uart at the board level, once pl011
|
||||
* (uart0) has been updated to avoid qemu_char_get_next_serial()
|
||||
*/
|
||||
chr = qemu_char_get_next_serial();
|
||||
if (chr == NULL) {
|
||||
chr = qemu_chr_new("bcm2835.uart1", "null", NULL);
|
||||
}
|
||||
qdev_prop_set_chr(DEVICE(&s->aux), "chardev", chr);
|
||||
|
||||
object_property_set_bool(OBJECT(&s->aux), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
memory_region_add_subregion(&s->peri_mr, UART1_OFFSET,
|
||||
sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->aux), 0));
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->aux), 0,
|
||||
qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
|
||||
INTERRUPT_AUX));
|
||||
|
||||
/* Mailboxes */
|
||||
object_property_set_bool(OBJECT(&s->mboxes), true, "realized", &err);
|
||||
if (err) {
|
||||
@ -203,6 +233,8 @@ static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data)
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = bcm2835_peripherals_realize;
|
||||
/* Reason: realize() method uses qemu_char_get_next_serial() */
|
||||
dc->cannot_instantiate_with_device_add_yet = true;
|
||||
}
|
||||
|
||||
static const TypeInfo bcm2835_peripherals_type_info = {
|
||||
|
@ -16,6 +16,7 @@ obj-$(CONFIG_SH4) += sh_serial.o
|
||||
obj-$(CONFIG_PSERIES) += spapr_vty.o
|
||||
obj-$(CONFIG_DIGIC) += digic-uart.o
|
||||
obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o
|
||||
obj-$(CONFIG_RASPI) += bcm2835_aux.o
|
||||
|
||||
common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o
|
||||
common-obj-$(CONFIG_ISA_DEBUG) += debugcon.o
|
||||
|
316
hw/char/bcm2835_aux.c
Normal file
316
hw/char/bcm2835_aux.c
Normal file
@ -0,0 +1,316 @@
|
||||
/*
|
||||
* BCM2835 (Raspberry Pi / Pi 2) Aux block (mini UART and SPI).
|
||||
* Copyright (c) 2015, Microsoft
|
||||
* Written by Andrew Baumann
|
||||
* Based on pl011.c, copyright terms below:
|
||||
*
|
||||
* Arm PrimeCell PL011 UART
|
||||
*
|
||||
* Copyright (c) 2006 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licensed under the GPL.
|
||||
*
|
||||
* At present only the core UART functions (data path for tx/rx) are
|
||||
* implemented. The following features/registers are unimplemented:
|
||||
* - Line/modem control
|
||||
* - Scratch register
|
||||
* - Extra control
|
||||
* - Baudrate
|
||||
* - SPI interfaces
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/char/bcm2835_aux.h"
|
||||
|
||||
#define AUX_IRQ 0x0
|
||||
#define AUX_ENABLES 0x4
|
||||
#define AUX_MU_IO_REG 0x40
|
||||
#define AUX_MU_IER_REG 0x44
|
||||
#define AUX_MU_IIR_REG 0x48
|
||||
#define AUX_MU_LCR_REG 0x4c
|
||||
#define AUX_MU_MCR_REG 0x50
|
||||
#define AUX_MU_LSR_REG 0x54
|
||||
#define AUX_MU_MSR_REG 0x58
|
||||
#define AUX_MU_SCRATCH 0x5c
|
||||
#define AUX_MU_CNTL_REG 0x60
|
||||
#define AUX_MU_STAT_REG 0x64
|
||||
#define AUX_MU_BAUD_REG 0x68
|
||||
|
||||
/* bits in IER/IIR registers */
|
||||
#define TX_INT 0x1
|
||||
#define RX_INT 0x2
|
||||
|
||||
static void bcm2835_aux_update(BCM2835AuxState *s)
|
||||
{
|
||||
/* signal an interrupt if either:
|
||||
* 1. rx interrupt is enabled and we have a non-empty rx fifo, or
|
||||
* 2. the tx interrupt is enabled (since we instantly drain the tx fifo)
|
||||
*/
|
||||
s->iir = 0;
|
||||
if ((s->ier & RX_INT) && s->read_count != 0) {
|
||||
s->iir |= RX_INT;
|
||||
}
|
||||
if (s->ier & TX_INT) {
|
||||
s->iir |= TX_INT;
|
||||
}
|
||||
qemu_set_irq(s->irq, s->iir != 0);
|
||||
}
|
||||
|
||||
static uint64_t bcm2835_aux_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
BCM2835AuxState *s = opaque;
|
||||
uint32_t c, res;
|
||||
|
||||
switch (offset) {
|
||||
case AUX_IRQ:
|
||||
return s->iir != 0;
|
||||
|
||||
case AUX_ENABLES:
|
||||
return 1; /* mini UART permanently enabled */
|
||||
|
||||
case AUX_MU_IO_REG:
|
||||
/* "DLAB bit set means access baudrate register" is NYI */
|
||||
c = s->read_fifo[s->read_pos];
|
||||
if (s->read_count > 0) {
|
||||
s->read_count--;
|
||||
if (++s->read_pos == BCM2835_AUX_RX_FIFO_LEN) {
|
||||
s->read_pos = 0;
|
||||
}
|
||||
}
|
||||
if (s->chr) {
|
||||
qemu_chr_accept_input(s->chr);
|
||||
}
|
||||
bcm2835_aux_update(s);
|
||||
return c;
|
||||
|
||||
case AUX_MU_IER_REG:
|
||||
/* "DLAB bit set means access baudrate register" is NYI */
|
||||
return 0xc0 | s->ier; /* FIFO enables always read 1 */
|
||||
|
||||
case AUX_MU_IIR_REG:
|
||||
res = 0xc0; /* FIFO enables */
|
||||
/* The spec is unclear on what happens when both tx and rx
|
||||
* interrupts are active, besides that this cannot occur. At
|
||||
* present, we choose to prioritise the rx interrupt, since
|
||||
* the tx fifo is always empty. */
|
||||
if (s->read_count != 0) {
|
||||
res |= 0x4;
|
||||
} else {
|
||||
res |= 0x2;
|
||||
}
|
||||
if (s->iir == 0) {
|
||||
res |= 0x1;
|
||||
}
|
||||
return res;
|
||||
|
||||
case AUX_MU_LCR_REG:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__);
|
||||
return 0;
|
||||
|
||||
case AUX_MU_MCR_REG:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__);
|
||||
return 0;
|
||||
|
||||
case AUX_MU_LSR_REG:
|
||||
res = 0x60; /* tx idle, empty */
|
||||
if (s->read_count != 0) {
|
||||
res |= 0x1;
|
||||
}
|
||||
return res;
|
||||
|
||||
case AUX_MU_MSR_REG:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MSR_REG unsupported\n", __func__);
|
||||
return 0;
|
||||
|
||||
case AUX_MU_SCRATCH:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__);
|
||||
return 0;
|
||||
|
||||
case AUX_MU_CNTL_REG:
|
||||
return 0x3; /* tx, rx enabled */
|
||||
|
||||
case AUX_MU_STAT_REG:
|
||||
res = 0x30e; /* space in the output buffer, empty tx fifo, idle tx/rx */
|
||||
if (s->read_count > 0) {
|
||||
res |= 0x1; /* data in input buffer */
|
||||
assert(s->read_count < BCM2835_AUX_RX_FIFO_LEN);
|
||||
res |= ((uint32_t)s->read_count) << 16; /* rx fifo fill level */
|
||||
}
|
||||
return res;
|
||||
|
||||
case AUX_MU_BAUD_REG:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__);
|
||||
return 0;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
|
||||
__func__, offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void bcm2835_aux_write(void *opaque, hwaddr offset, uint64_t value,
|
||||
unsigned size)
|
||||
{
|
||||
BCM2835AuxState *s = opaque;
|
||||
unsigned char ch;
|
||||
|
||||
switch (offset) {
|
||||
case AUX_ENABLES:
|
||||
if (value != 1) {
|
||||
qemu_log_mask(LOG_UNIMP, "%s: unsupported attempt to enable SPI "
|
||||
"or disable UART\n", __func__);
|
||||
}
|
||||
break;
|
||||
|
||||
case AUX_MU_IO_REG:
|
||||
/* "DLAB bit set means access baudrate register" is NYI */
|
||||
ch = value;
|
||||
if (s->chr) {
|
||||
qemu_chr_fe_write(s->chr, &ch, 1);
|
||||
}
|
||||
break;
|
||||
|
||||
case AUX_MU_IER_REG:
|
||||
/* "DLAB bit set means access baudrate register" is NYI */
|
||||
s->ier = value & (TX_INT | RX_INT);
|
||||
bcm2835_aux_update(s);
|
||||
break;
|
||||
|
||||
case AUX_MU_IIR_REG:
|
||||
if (value & 0x2) {
|
||||
s->read_count = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case AUX_MU_LCR_REG:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__);
|
||||
break;
|
||||
|
||||
case AUX_MU_MCR_REG:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__);
|
||||
break;
|
||||
|
||||
case AUX_MU_SCRATCH:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__);
|
||||
break;
|
||||
|
||||
case AUX_MU_CNTL_REG:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_CNTL_REG unsupported\n", __func__);
|
||||
break;
|
||||
|
||||
case AUX_MU_BAUD_REG:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__);
|
||||
break;
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
|
||||
__func__, offset);
|
||||
}
|
||||
|
||||
bcm2835_aux_update(s);
|
||||
}
|
||||
|
||||
static int bcm2835_aux_can_receive(void *opaque)
|
||||
{
|
||||
BCM2835AuxState *s = opaque;
|
||||
|
||||
return s->read_count < BCM2835_AUX_RX_FIFO_LEN;
|
||||
}
|
||||
|
||||
static void bcm2835_aux_put_fifo(void *opaque, uint8_t value)
|
||||
{
|
||||
BCM2835AuxState *s = opaque;
|
||||
int slot;
|
||||
|
||||
slot = s->read_pos + s->read_count;
|
||||
if (slot >= BCM2835_AUX_RX_FIFO_LEN) {
|
||||
slot -= BCM2835_AUX_RX_FIFO_LEN;
|
||||
}
|
||||
s->read_fifo[slot] = value;
|
||||
s->read_count++;
|
||||
if (s->read_count == BCM2835_AUX_RX_FIFO_LEN) {
|
||||
/* buffer full */
|
||||
}
|
||||
bcm2835_aux_update(s);
|
||||
}
|
||||
|
||||
static void bcm2835_aux_receive(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
bcm2835_aux_put_fifo(opaque, *buf);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps bcm2835_aux_ops = {
|
||||
.read = bcm2835_aux_read,
|
||||
.write = bcm2835_aux_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid.min_access_size = 4,
|
||||
.valid.max_access_size = 4,
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_bcm2835_aux = {
|
||||
.name = TYPE_BCM2835_AUX,
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT8_ARRAY(read_fifo, BCM2835AuxState,
|
||||
BCM2835_AUX_RX_FIFO_LEN),
|
||||
VMSTATE_UINT8(read_pos, BCM2835AuxState),
|
||||
VMSTATE_UINT8(read_count, BCM2835AuxState),
|
||||
VMSTATE_UINT8(ier, BCM2835AuxState),
|
||||
VMSTATE_UINT8(iir, BCM2835AuxState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void bcm2835_aux_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
BCM2835AuxState *s = BCM2835_AUX(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_aux_ops, s,
|
||||
TYPE_BCM2835_AUX, 0x100);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
}
|
||||
|
||||
static void bcm2835_aux_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
BCM2835AuxState *s = BCM2835_AUX(dev);
|
||||
|
||||
if (s->chr) {
|
||||
qemu_chr_add_handlers(s->chr, bcm2835_aux_can_receive,
|
||||
bcm2835_aux_receive, NULL, s);
|
||||
}
|
||||
}
|
||||
|
||||
static Property bcm2835_aux_props[] = {
|
||||
DEFINE_PROP_CHR("chardev", BCM2835AuxState, chr),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void bcm2835_aux_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = bcm2835_aux_realize;
|
||||
dc->vmsd = &vmstate_bcm2835_aux;
|
||||
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
|
||||
dc->props = bcm2835_aux_props;
|
||||
}
|
||||
|
||||
static const TypeInfo bcm2835_aux_info = {
|
||||
.name = TYPE_BCM2835_AUX,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(BCM2835AuxState),
|
||||
.instance_init = bcm2835_aux_init,
|
||||
.class_init = bcm2835_aux_class_init,
|
||||
};
|
||||
|
||||
static void bcm2835_aux_register_types(void)
|
||||
{
|
||||
type_register_static(&bcm2835_aux_info);
|
||||
}
|
||||
|
||||
type_init(bcm2835_aux_register_types)
|
@ -14,6 +14,7 @@
|
||||
#include "qemu-common.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/char/bcm2835_aux.h"
|
||||
#include "hw/intc/bcm2835_ic.h"
|
||||
#include "hw/misc/bcm2835_property.h"
|
||||
#include "hw/misc/bcm2835_mbox.h"
|
||||
@ -33,6 +34,7 @@ typedef struct BCM2835PeripheralState {
|
||||
qemu_irq irq, fiq;
|
||||
|
||||
SysBusDevice *uart0;
|
||||
BCM2835AuxState aux;
|
||||
BCM2835ICState ic;
|
||||
BCM2835PropertyState property;
|
||||
BCM2835MboxState mboxes;
|
||||
|
33
include/hw/char/bcm2835_aux.h
Normal file
33
include/hw/char/bcm2835_aux.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft
|
||||
* Written by Andrew Baumann
|
||||
*
|
||||
* This code is licensed under the GNU GPLv2 and later.
|
||||
*/
|
||||
|
||||
#ifndef BCM2835_AUX_H
|
||||
#define BCM2835_AUX_H
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "sysemu/char.h"
|
||||
|
||||
#define TYPE_BCM2835_AUX "bcm2835-aux"
|
||||
#define BCM2835_AUX(obj) OBJECT_CHECK(BCM2835AuxState, (obj), TYPE_BCM2835_AUX)
|
||||
|
||||
#define BCM2835_AUX_RX_FIFO_LEN 8
|
||||
|
||||
typedef struct {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
/*< public >*/
|
||||
|
||||
MemoryRegion iomem;
|
||||
CharDriverState *chr;
|
||||
qemu_irq irq;
|
||||
|
||||
uint8_t read_fifo[BCM2835_AUX_RX_FIFO_LEN];
|
||||
uint8_t read_pos, read_count;
|
||||
uint8_t ier, iir;
|
||||
} BCM2835AuxState;
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user