hw/arm: add experimental iPhone 5 A6 platform
This commit is contained in:
@@ -26,6 +26,7 @@ system_ss.add(when: 'CONFIG_DIVA_GSP', if_true: files('diva-gsp.c'))
|
||||
system_ss.add(when: 'CONFIG_AVR_USART', if_true: files('avr_usart.c'))
|
||||
system_ss.add(when: 'CONFIG_COLDFIRE', if_true: files('mcf_uart.c'))
|
||||
system_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic-uart.c'))
|
||||
system_ss.add(when: 'CONFIG_S5L8950X', if_true: files('s5l8950x-uart.c'))
|
||||
system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_uart.c'))
|
||||
system_ss.add(when: 'CONFIG_MAX78000_UART', if_true: files('max78000_uart.c'))
|
||||
system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_uart.c'))
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/core/sysbus.h"
|
||||
#include "hw/core/irq.h"
|
||||
#include "hw/core/qdev-properties-system.h"
|
||||
#include "qemu/log.h"
|
||||
#include "qemu/module.h"
|
||||
#include "chardev/char-fe.h"
|
||||
|
||||
#define TYPE_S5L8950X_UART "s5l8950x-uart"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(S5L8950XUartState, S5L8950X_UART)
|
||||
|
||||
struct S5L8950XUartState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion iomem;
|
||||
CharFrontend chr;
|
||||
|
||||
uint32_t ulcon;
|
||||
uint32_t ucon;
|
||||
uint32_t ufcon;
|
||||
uint32_t umcon;
|
||||
uint32_t ubrdiv;
|
||||
uint32_t ufracval;
|
||||
|
||||
uint8_t rx_fifo[16];
|
||||
int rx_count;
|
||||
qemu_irq irq;
|
||||
};
|
||||
|
||||
#define UART_ULCON 0x00
|
||||
#define UART_UCON 0x04
|
||||
#define UART_UFCON 0x08
|
||||
#define UART_UMCON 0x0C
|
||||
#define UART_UTRSTAT 0x10
|
||||
#define UART_UERSTAT 0x14
|
||||
#define UART_UFSTAT 0x18
|
||||
#define UART_UMSTAT 0x1C
|
||||
#define UART_UTXH 0x20
|
||||
#define UART_URXH 0x24
|
||||
#define UART_UBRDIV 0x28
|
||||
#define UART_UFRACVAL 0x2C
|
||||
|
||||
static void s5l8950x_uart_update_irq(S5L8950XUartState *s)
|
||||
{
|
||||
if (s->rx_count > 0) {
|
||||
qemu_irq_raise(s->irq);
|
||||
} else {
|
||||
qemu_irq_lower(s->irq);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t s5l8950x_uart_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
S5L8950XUartState *s = S5L8950X_UART(opaque);
|
||||
uint32_t val = 0;
|
||||
|
||||
switch (offset) {
|
||||
case UART_ULCON:
|
||||
val = s->ulcon;
|
||||
break;
|
||||
case UART_UCON:
|
||||
val = s->ucon;
|
||||
break;
|
||||
case UART_UFCON:
|
||||
val = s->ufcon;
|
||||
break;
|
||||
case UART_UMCON:
|
||||
val = s->umcon;
|
||||
break;
|
||||
case UART_UTRSTAT:
|
||||
/* Bit 0: rx ready, Bit 1: tx buf empty, Bit 2: tx empty */
|
||||
val = 0x6 | (s->rx_count > 0 ? 1 : 0);
|
||||
break;
|
||||
case UART_UERSTAT:
|
||||
val = 0;
|
||||
break;
|
||||
case UART_UFSTAT:
|
||||
/* Return rx count in low bits, no tx fifo used here */
|
||||
val = s->rx_count & 0xf;
|
||||
break;
|
||||
case UART_UMSTAT:
|
||||
val = 0;
|
||||
break;
|
||||
case UART_UTXH:
|
||||
break;
|
||||
case UART_URXH:
|
||||
if (s->rx_count > 0) {
|
||||
val = s->rx_fifo[0];
|
||||
s->rx_count--;
|
||||
memmove(s->rx_fifo, s->rx_fifo + 1, s->rx_count);
|
||||
s5l8950x_uart_update_irq(s);
|
||||
}
|
||||
break;
|
||||
case UART_UBRDIV:
|
||||
val = s->ubrdiv;
|
||||
break;
|
||||
case UART_UFRACVAL:
|
||||
val = s->ufracval;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: Unimplemented read at offset 0x%" HWADDR_PRIx "\n",
|
||||
__func__, offset);
|
||||
break;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static void s5l8950x_uart_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
|
||||
{
|
||||
S5L8950XUartState *s = S5L8950X_UART(opaque);
|
||||
unsigned char c;
|
||||
|
||||
switch (offset) {
|
||||
case UART_ULCON:
|
||||
s->ulcon = val;
|
||||
break;
|
||||
case UART_UCON:
|
||||
s->ucon = val;
|
||||
break;
|
||||
case UART_UFCON:
|
||||
s->ufcon = val;
|
||||
break;
|
||||
case UART_UMCON:
|
||||
s->umcon = val;
|
||||
break;
|
||||
case UART_UTRSTAT:
|
||||
case UART_UERSTAT:
|
||||
case UART_UFSTAT:
|
||||
case UART_UMSTAT:
|
||||
/* Read-only registers */
|
||||
break;
|
||||
case UART_UTXH:
|
||||
c = val & 0xFF;
|
||||
qemu_chr_fe_write_all(&s->chr, &c, 1);
|
||||
break;
|
||||
case UART_URXH:
|
||||
break;
|
||||
case UART_UBRDIV:
|
||||
s->ubrdiv = val;
|
||||
break;
|
||||
case UART_UFRACVAL:
|
||||
s->ufracval = val;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "%s: Unimplemented write at offset 0x%" HWADDR_PRIx "\n",
|
||||
__func__, offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps s5l8950x_uart_ops = {
|
||||
.read = s5l8950x_uart_read,
|
||||
.write = s5l8950x_uart_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4,
|
||||
},
|
||||
};
|
||||
|
||||
static int s5l8950x_uart_can_receive(void *opaque)
|
||||
{
|
||||
S5L8950XUartState *s = S5L8950X_UART(opaque);
|
||||
|
||||
return sizeof(s->rx_fifo) - s->rx_count;
|
||||
}
|
||||
|
||||
static void s5l8950x_uart_receive(void *opaque, const uint8_t *buf, int size)
|
||||
{
|
||||
S5L8950XUartState *s = S5L8950X_UART(opaque);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size && s->rx_count < sizeof(s->rx_fifo); i++) {
|
||||
s->rx_fifo[s->rx_count++] = buf[i];
|
||||
}
|
||||
s5l8950x_uart_update_irq(s);
|
||||
}
|
||||
|
||||
static void s5l8950x_uart_reset(DeviceState *dev)
|
||||
{
|
||||
S5L8950XUartState *s = S5L8950X_UART(dev);
|
||||
|
||||
s->ulcon = 0;
|
||||
s->ucon = 0;
|
||||
s->ufcon = 0;
|
||||
s->umcon = 0;
|
||||
s->ubrdiv = 0;
|
||||
s->ufracval = 0;
|
||||
s->rx_count = 0;
|
||||
s5l8950x_uart_update_irq(s);
|
||||
}
|
||||
|
||||
static void s5l8950x_uart_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
S5L8950XUartState *s = S5L8950X_UART(dev);
|
||||
|
||||
qemu_chr_fe_set_handlers(&s->chr, s5l8950x_uart_can_receive,
|
||||
s5l8950x_uart_receive, NULL, NULL,
|
||||
s, NULL, true);
|
||||
}
|
||||
|
||||
static void s5l8950x_uart_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
S5L8950XUartState *s = S5L8950X_UART(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &s5l8950x_uart_ops, s,
|
||||
TYPE_S5L8950X_UART, 0x100);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
}
|
||||
|
||||
static const Property s5l8950x_uart_properties[] = {
|
||||
DEFINE_PROP_CHR("chardev", S5L8950XUartState, chr),
|
||||
};
|
||||
|
||||
static void s5l8950x_uart_class_init(ObjectClass *klass, const void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
device_class_set_legacy_reset(dc, s5l8950x_uart_reset);
|
||||
dc->realize = s5l8950x_uart_realize;
|
||||
device_class_set_props(dc, s5l8950x_uart_properties);
|
||||
}
|
||||
|
||||
static const TypeInfo s5l8950x_uart_info = {
|
||||
.name = TYPE_S5L8950X_UART,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(S5L8950XUartState),
|
||||
.instance_init = s5l8950x_uart_init,
|
||||
.class_init = s5l8950x_uart_class_init,
|
||||
};
|
||||
|
||||
static void s5l8950x_uart_register_types(void)
|
||||
{
|
||||
type_register_static(&s5l8950x_uart_info);
|
||||
}
|
||||
|
||||
type_init(s5l8950x_uart_register_types)
|
||||
Reference in New Issue
Block a user