On Fri, Dec 13, 2013 at 8:23 AM, Antony Pavlov <antonynpav...@gmail.com> wrote: > Signed-off-by: Antony Pavlov <antonynpav...@gmail.com> > Reviewed-by: Peter Maydell <peter.mayd...@linaro.org>
Reveiwed-by: Peter Crosthwaite <peter.crosthwa...@xilinx.com> > --- > hw/arm/digic.c | 16 ++++ > hw/char/Makefile.objs | 1 + > hw/char/digic-uart.c | 195 > +++++++++++++++++++++++++++++++++++++++++++++++++ > hw/char/digic-uart.h | 45 ++++++++++++ > include/hw/arm/digic.h | 2 + > 5 files changed, 259 insertions(+) > create mode 100644 hw/char/digic-uart.c > create mode 100644 hw/char/digic-uart.h > > diff --git a/hw/arm/digic.c b/hw/arm/digic.c > index e8eb0de..ec8c330 100644 > --- a/hw/arm/digic.c > +++ b/hw/arm/digic.c > @@ -24,6 +24,8 @@ > > #define DIGIC4_TIMER_BASE(n) (0xc0210000 + (n) * 0x100) > > +#define DIGIC_UART_BASE 0xc0800000 > + > static void digic_init(Object *obj) > { > DigicState *s = DIGIC(obj); > @@ -43,6 +45,11 @@ static void digic_init(Object *obj) > snprintf(name, DIGIC_TIMER_NAME_MLEN, "timer[%d]", i); > object_property_add_child(obj, name, OBJECT(&s->timer[i]), NULL); > } > + > + object_initialize(&s->uart, sizeof(s->uart), TYPE_DIGIC_UART); > + dev = DEVICE(&s->uart); > + qdev_set_parent_bus(dev, sysbus_get_default()); > + object_property_add_child(obj, "uart", OBJECT(&s->uart), NULL); > } > > static void digic_realize(DeviceState *dev, Error **errp) > @@ -74,6 +81,15 @@ static void digic_realize(DeviceState *dev, Error **errp) > sbd = SYS_BUS_DEVICE(&s->timer[i]); > sysbus_mmio_map(sbd, 0, DIGIC4_TIMER_BASE(i)); > } > + > + object_property_set_bool(OBJECT(&s->uart), true, "realized", &err); > + if (err != NULL) { > + error_propagate(errp, err); > + return; > + } > + > + sbd = SYS_BUS_DEVICE(&s->uart); > + sysbus_mmio_map(sbd, 0, DIGIC_UART_BASE); > } > > static void digic_class_init(ObjectClass *oc, void *data) > diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs > index cbd6a00..be2a7d9 100644 > --- a/hw/char/Makefile.objs > +++ b/hw/char/Makefile.objs > @@ -14,6 +14,7 @@ obj-$(CONFIG_COLDFIRE) += mcf_uart.o > obj-$(CONFIG_OMAP) += omap_uart.o > obj-$(CONFIG_SH4) += sh_serial.o > obj-$(CONFIG_PSERIES) += spapr_vty.o > +obj-$(CONFIG_DIGIC) += digic-uart.o > > common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o > common-obj-$(CONFIG_ISA_DEBUG) += debugcon.o > diff --git a/hw/char/digic-uart.c b/hw/char/digic-uart.c > new file mode 100644 > index 0000000..fd8e077 > --- /dev/null > +++ b/hw/char/digic-uart.c > @@ -0,0 +1,195 @@ > +/* > + * QEMU model of the Canon DIGIC UART block. > + * > + * Copyright (C) 2013 Antony Pavlov <antonynpav...@gmail.com> > + * > + * This model is based on reverse engineering efforts > + * made by CHDK (http://chdk.wikia.com) and > + * Magic Lantern (http://www.magiclantern.fm) projects > + * contributors. > + * > + * See "Serial terminal" docs here: > + * http://magiclantern.wikia.com/wiki/Register_Map#Misc_Registers > + * > + * The QEMU model of the Milkymist UART block by Michael Walle > + * is 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 as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + */ > + > +#include "hw/hw.h" > +#include "hw/sysbus.h" > +#include "sysemu/char.h" > + > +#include "hw/char/digic-uart.h" > + > +enum { > + ST_RX_RDY = (1 << 0), > + ST_TX_RDY = (1 << 1), > +}; > + > +static uint64_t digic_uart_read(void *opaque, hwaddr addr, > + unsigned size) > +{ > + DigicUartState *s = opaque; > + uint64_t ret = 0; > + > + addr >>= 2; > + > + switch (addr) { > + case R_RX: > + s->reg_st &= ~(ST_RX_RDY); > + ret = s->reg_rx; > + break; > + > + case R_ST: > + ret = s->reg_st; > + break; > + > + default: > + qemu_log_mask(LOG_UNIMP, > + "digic-uart: read access to unknown register 0x" > + TARGET_FMT_plx, addr << 2); > + } > + > + return ret; > +} > + > +static void digic_uart_write(void *opaque, hwaddr addr, uint64_t value, > + unsigned size) > +{ > + DigicUartState *s = opaque; > + unsigned char ch = value; > + > + addr >>= 2; > + > + switch (addr) { > + case R_TX: > + if (s->chr) { > + qemu_chr_fe_write_all(s->chr, &ch, 1); > + } > + break; > + > + case R_ST: > + /* > + * Ignore write to R_ST. > + * > + * The point is that this register is actively used > + * during receiving and transmitting symbols, > + * but we don't know the function of most of bits. > + * > + * Ignoring writes to R_ST is only a simplification > + * of the model. It has no perceptible side effects > + * for existing guests. > + */ > + break; > + > + default: > + qemu_log_mask(LOG_UNIMP, > + "digic-uart: write access to unknown register 0x" > + TARGET_FMT_plx, addr << 2); > + } > +} > + > +static const MemoryRegionOps uart_mmio_ops = { > + .read = digic_uart_read, > + .write = digic_uart_write, > + .valid = { > + .min_access_size = 4, > + .max_access_size = 4, > + }, > + .endianness = DEVICE_NATIVE_ENDIAN, > +}; > + > +static int uart_can_rx(void *opaque) > +{ > + DigicUartState *s = opaque; > + > + return !(s->reg_st & ST_RX_RDY); > +} > + > +static void uart_rx(void *opaque, const uint8_t *buf, int size) > +{ > + DigicUartState *s = opaque; > + > + assert(uart_can_rx(opaque)); > + > + s->reg_st |= ST_RX_RDY; > + s->reg_rx = *buf; > +} > + > +static void uart_event(void *opaque, int event) > +{ > +} > + > +static void digic_uart_reset(DeviceState *d) > +{ > + DigicUartState *s = DIGIC_UART(d); > + > + s->reg_rx = 0; > + s->reg_st = ST_TX_RDY; > +} > + > +static void digic_uart_realize(DeviceState *dev, Error **errp) > +{ > + DigicUartState *s = DIGIC_UART(dev); > + > + s->chr = qemu_char_get_next_serial(); > + if (s->chr) { > + qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); > + } > +} > + > +static void digic_uart_init(Object *obj) > +{ > + DigicUartState *s = DIGIC_UART(obj); > + > + memory_region_init_io(&s->regs_region, OBJECT(s), &uart_mmio_ops, s, > + TYPE_DIGIC_UART, 0x18); > + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->regs_region); > +} > + > +static const VMStateDescription vmstate_digic_uart = { > + .name = "digic-uart", > + .version_id = 1, > + .minimum_version_id = 1, > + .minimum_version_id_old = 1, > + .fields = (VMStateField[]) { > + VMSTATE_UINT32(reg_rx, DigicUartState), > + VMSTATE_UINT32(reg_st, DigicUartState), > + VMSTATE_END_OF_LIST() > + } > +}; > + > +static void digic_uart_class_init(ObjectClass *klass, void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + > + dc->realize = digic_uart_realize; > + dc->reset = digic_uart_reset; > + dc->vmsd = &vmstate_digic_uart; > +} > + > +static const TypeInfo digic_uart_info = { > + .name = TYPE_DIGIC_UART, > + .parent = TYPE_SYS_BUS_DEVICE, > + .instance_size = sizeof(DigicUartState), > + .instance_init = digic_uart_init, > + .class_init = digic_uart_class_init, > +}; > + > +static void digic_uart_register_types(void) > +{ > + type_register_static(&digic_uart_info); > +} > + > +type_init(digic_uart_register_types) > diff --git a/hw/char/digic-uart.h b/hw/char/digic-uart.h > new file mode 100644 > index 0000000..2a333e7 > --- /dev/null > +++ b/hw/char/digic-uart.h > @@ -0,0 +1,45 @@ > +/* > + * Canon DIGIC UART block declarations. > + * > + * Copyright (C) 2013 Antony Pavlov <antonynpav...@gmail.com> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + */ > + > +#ifndef HW_CHAR_DIGIC_UART_H > +#define HW_CHAR_DIGIC_UART_H > + > +#include "hw/sysbus.h" > +#include "qemu/typedefs.h" > + > +#define TYPE_DIGIC_UART "digic-uart" > +#define DIGIC_UART(obj) \ > + OBJECT_CHECK(DigicUartState, (obj), TYPE_DIGIC_UART) > + > +enum { > + R_TX = 0x00, > + R_RX, > + R_ST = (0x14 >> 2), > + R_MAX > +}; > + > +typedef struct DigicUartState { > + SysBusDevice parent_obj; > + > + MemoryRegion regs_region; > + CharDriverState *chr; > + > + uint32_t reg_rx; > + uint32_t reg_st; > +} DigicUartState; > + > +#endif /* HW_CHAR_DIGIC_UART_H */ > diff --git a/include/hw/arm/digic.h b/include/hw/arm/digic.h > index 177a06d..a739d6a 100644 > --- a/include/hw/arm/digic.h > +++ b/include/hw/arm/digic.h > @@ -21,6 +21,7 @@ > #include "cpu.h" > > #include "hw/timer/digic-timer.h" > +#include "hw/char/digic-uart.h" > > #define TYPE_DIGIC "digic" > > @@ -36,6 +37,7 @@ typedef struct DigicState { > ARMCPU cpu; > > DigicTimerState timer[DIGIC4_NB_TIMERS]; > + DigicUartState uart; > } DigicState; > > #endif /* HW_ARM_DIGIC_H */ > -- > 1.8.5 > >