qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH v7 4/6] hw/arm/digic: add UART support


From: Peter Crosthwaite
Subject: Re: [Qemu-devel] [PATCH v7 4/6] hw/arm/digic: add UART support
Date: Fri, 13 Dec 2013 09:25:07 +1000

On Fri, Dec 13, 2013 at 8:23 AM, Antony Pavlov <address@hidden> wrote:
> Signed-off-by: Antony Pavlov <address@hidden>
> Reviewed-by: Peter Maydell <address@hidden>

Reveiwed-by: Peter Crosthwaite <address@hidden>

> ---
>  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 <address@hidden>
> + *
> + * 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 <address@hidden>
> + *
> + * 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
>
>



reply via email to

[Prev in Thread] Current Thread [Next in Thread]