Patchwork [v3,04/20] arm: add Faraday FTDDRII030 support

login
register
mail settings
Submitter Igor Mitsyanko
Date Feb. 7, 2013, 5:26 p.m.
Message ID <CA+x0pt4-s9Sj+ZQdhZB=qg6nx7LSKC2hO_DCA-uAhZ1wT-zt1w@mail.gmail.com>
Download mbox | patch
Permalink /patch/218967/
State New
Headers show

Comments

Igor Mitsyanko - Feb. 7, 2013, 5:26 p.m.
On 02/06/2013 01:45 PM, Kuo-Jung Su wrote:

From: Kuo-Jung Su <dantesu@faraday-tech.com> <dantesu@faraday-tech.com>

The FTDDRII030 is a DDRII SDRAM controller which is responsible for
SDRAM initialization.
In QEMU we simply emualte the SDRAM enable function, neither timing parameter
nor bank setup is handled.

Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com> <dantesu@faraday-tech.com>
---
 hw/arm/Makefile.objs  |    1 +
 hw/arm/faraday_a369.c |    6 ++
 hw/arm/ftddrii030.c   |  174 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 181 insertions(+)
 create mode 100644 hw/arm/ftddrii030.c

+    dc->no_user = 1;
+}
+
+static const TypeInfo ftddrii030_info = {
+    .name          = TYPE_FTDDRII030,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Ftddrii030State),
+    .class_init    = ftddrii030_class_init,
+};
+
+static void ftddrii030_register_types(void)
+{
+    type_register_static(&ftddrii030_info);
+}
+
+type_init(ftddrii030_register_types)
Andreas Färber - Feb. 17, 2013, 6:31 p.m.
Am 07.02.2013 18:26, schrieb Igor Mitsyanko:
> On 02/06/2013 01:45 PM, Kuo-Jung Su wrote:
>> +static void ftddrii030_reset(DeviceState *ds)
>> +{
>> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
>> +    Ftddrii030State *s = FTDDRII030(FROM_SYSBUS(Ftddrii030State, busdev));
>> +    FaradayMachState *mach = s->mach;
>> +
>> +    if (!mach) {
>> +        hw_error("ftddrii030: mach is not yet initialized!\n");
>> +        exit(1);
>> +    }
>> +
>> +    if (mach->ddr_inited) {
>> +        if (mach->ahb_remapped) {
> 
> This assumes that DDRII controller will be reseted before AHBC,
> otherwise we'll end up with
> ROM and RAM mapped at 0x0 address after reset. I do not know if we can
> count on a specific device reset ordering,
> maybe someone else can clarify this.

Reset handler registration happens as part of realized property setter.
For most devices that means as part of qdev_init_nofail(), so currently
the order is predictable for those. But this will change once we delay
setting realized = true to the end of machine initialization, since then
realized = true will propagate from "/machine" object to devices in
child<> property order and along busses, as drafted by Paolo.

Andreas
Kuo-Jung Su - Feb. 18, 2013, 9:31 a.m.
2013/2/8 Igor Mitsyanko <i.mitsyanko@gmail.com>:
>
> On 02/06/2013 01:45 PM, Kuo-Jung Su wrote:
>
> From: Kuo-Jung Su <dantesu@faraday-tech.com>
>
> The FTDDRII030 is a DDRII SDRAM controller which is responsible for
> SDRAM initialization.
> In QEMU we simply emualte the SDRAM enable function, neither timing
> parameter
> nor bank setup is handled.
>
> Signed-off-by: Kuo-Jung Su <dantesu@faraday-tech.com>
> ---
>  hw/arm/Makefile.objs  |    1 +
>  hw/arm/faraday_a369.c |    6 ++
>  hw/arm/ftddrii030.c   |  174
> +++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 181 insertions(+)
>  create mode 100644 hw/arm/ftddrii030.c
>
> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> index 5825c63..fede407 100644
> --- a/hw/arm/Makefile.objs
> +++ b/hw/arm/Makefile.objs
> @@ -36,3 +36,4 @@ obj-y := $(addprefix ../,$(obj-y))
>  obj-y += faraday_a360.o faraday_a360_pmu.o
>  obj-y += faraday_a369.o faraday_a369_scu.o faraday_a369_keypad.o
>  obj-y += ftahbc020.o
> +obj-y += ftddrii030.o
> diff --git a/hw/arm/faraday_a369.c b/hw/arm/faraday_a369.c
> index ae6c445..f60bf4e 100644
> --- a/hw/arm/faraday_a369.c
> +++ b/hw/arm/faraday_a369.c
> @@ -60,6 +60,12 @@ a369_device_init(A369State *s)
>      qdev_prop_set_ptr(s->ahbc, "mach", s);
>      qdev_init_nofail(s->ahbc);
>      sysbus_mmio_map(SYS_BUS_DEVICE(s->ahbc), 0, 0x94000000);
> +
> +    /* ftddrii030 */
> +    s->ddrc = qdev_create(NULL, "ftddrii030");
> +    qdev_prop_set_ptr(s->ddrc, "mach", s);
> +    qdev_init_nofail(s->ddrc);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(s->ddrc), 0, 0x93100000);
>  }
>
>  static void
> diff --git a/hw/arm/ftddrii030.c b/hw/arm/ftddrii030.c
> new file mode 100644
> index 0000000..3f66983
> --- /dev/null
> +++ b/hw/arm/ftddrii030.c
> @@ -0,0 +1,174 @@
> +/*
> + * Faraday DDRII controller
> + *
> + * Copyright (c) 2012 Faraday Technology
> + * Written by Dante Su <dantesu@faraday-tech.com>
> + *
> + * This code is licensed under GNU GPL v2+
> + */
> +
> +#include <hw/hw.h>
> +#include <hw/sysbus.h>
> +#include <hw/devices.h>
> +#include <ui/console.h>
> +#include <qemu/timer.h>
> +#include <sysemu/sysemu.h>
> +
> +#include "faraday.h"
> +
> +#define REG_MCR             0x00    /* memory configuration register */
> +#define REG_MSR             0x04    /* memory status register */
> +
> +#define TYPE_FTDDRII030     "ftddrii030"
> +
> +typedef struct Ftddrii030State {
> +    SysBusDevice busdev;
> +    MemoryRegion iomem;
> +    void *mach;
> +    /* HW register cache */
> +    uint32_t mcr;
> +    uint32_t msr;
> +} Ftddrii030State;
> +
> +#define FTDDRII030(obj) \
> +    OBJECT_CHECK(Ftddrii030State, obj, TYPE_FTDDRII030)
> +
> +static uint64_t
> +ftddrii030_mem_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    Ftddrii030State *s = FTDDRII030(opaque);
> +    uint64_t ret = 0;
> +
> +    switch (addr) {
> +    case REG_MCR:
> +        ret = s->mcr;
> +        break;
> +    case REG_MSR:
> +        ret = s->msr;
> +        break;
> +    }
> +
> +    return ret;
> +}
> +
> +static void
> +ftddrii030_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned
> size)
> +{
> +    Ftddrii030State *s = FTDDRII030(opaque);
> +    FaradayMachState *mach = s->mach;
> +    uint32_t base;
> +
> +    if (!mach) {
> +        hw_error("ftddrii030: mach is not yet initialized!\n");
> +        exit(1);
> +    }
> +
> +    switch (addr) {
> +    case REG_MCR:
> +        s->mcr = (uint32_t)val & 0xffff;
> +        break;
> +    case REG_MSR:
> +        val = (val & 0x3f) | (s->msr & 0x100);
> +        if (!mach->ddr_inited && (val & 0x01)) {
> +            val &= 0xfffffffe;
> +            val |= 0x100;
> +            if (!mach->ahb_remapped) {
> +                /* get RAM base address */
> +                base = mach->ahb_slave6 & 0xfff00000;
> +                memory_region_add_subregion(mach->as, base,
> mach->ram_alias);
> +            } else {
>
>
> Can uboot/kernel ever perform this controller setup? If they do, then in
> case if we started QEMU with -kernel parameter
> and later got here then we already have mach->ram region mapped at 0x0 and
> next line will cause assertion. You should set
> mach->ddr_inited during machine initialization then.
>
>

Got it, thanks.

mach->ddr_inited would be updated upon direct boot mode in next version.

BTW uboot/linux-kernel would never perform DDR initialization, since
they're too huge
to fit into tiny sram.


> +                memory_region_add_subregion(mach->as, 0, mach->ram);
> +            }
> +            mach->ddr_inited = 1;
> +        }
> +        s->msr = (uint32_t)val;
> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps ftddrii030_mem_ops = {
> +    .read  = ftddrii030_mem_read,
> +    .write = ftddrii030_mem_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +};
> +
> +static void ftddrii030_reset(DeviceState *ds)
> +{
> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
> +    Ftddrii030State *s = FTDDRII030(FROM_SYSBUS(Ftddrii030State, busdev));
> +    FaradayMachState *mach = s->mach;
> +
> +    if (!mach) {
> +        hw_error("ftddrii030: mach is not yet initialized!\n");
> +        exit(1);
> +    }
> +
> +    if (mach->ddr_inited) {
> +        if (mach->ahb_remapped) {
>
>
> This assumes that DDRII controller will be reseted before AHBC, otherwise
> we'll end up with
> ROM and RAM mapped at 0x0 address after reset. I do not know if we can count
> on a specific device reset ordering,
> maybe someone else can clarify this.
>
>

The reset sequence is controlled by

static void
a369_board_reset(void *opaque)
{
    A369State *s = opaque;

    device_reset(s->scu);
    device_reset(s->ddrc);
    device_reset(s->ahbc);
    cpu_reset(CPU(s->cpu));
}

which is registered with qemu_register_reset() during a369_device_init().

> +            memory_region_del_subregion(mach->as, mach->ram);
> +        } else {
> +            memory_region_del_subregion(mach->as, mach->ram_alias);
> +        }
> +        mach->ddr_inited = 0;
> +    }
> +
> +    s->mcr = 0;
> +    s->msr = 0;
> +}
> +
> +static int ftddrii030_init(SysBusDevice *dev)
> +{
> +    Ftddrii030State *s = FTDDRII030(FROM_SYSBUS(Ftddrii030State, dev));
> +
> +    memory_region_init_io(&s->iomem,
> +                          &ftddrii030_mem_ops,
> +                          s,
> +                          TYPE_FTDDRII030,
> +                          0x1000);
> +    sysbus_init_mmio(dev, &s->iomem);
> +    return 0;
> +}
> +
> +static Property ftddrii030_properties[] = {
> +    DEFINE_PROP_PTR("mach", Ftddrii030State, mach),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static const VMStateDescription vmstate_ftddrii030 = {
> +    .name = TYPE_FTDDRII030,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .minimum_version_id_old = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32(mcr, Ftddrii030State),
> +        VMSTATE_UINT32(msr, Ftddrii030State),
> +        VMSTATE_END_OF_LIST(),
> +    }
> +};
> +
> +static void ftddrii030_class_init(ObjectClass *klass, void *data)
> +{
> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    k->init   = ftddrii030_init;
> +    dc->desc  = TYPE_FTDDRII030;
> +    dc->vmsd  = &vmstate_ftddrii030;
> +    dc->props = ftddrii030_properties;
> +    dc->reset = ftddrii030_reset;
> +    dc->no_user = 1;
> +}
> +
> +static const TypeInfo ftddrii030_info = {
> +    .name          = TYPE_FTDDRII030,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(Ftddrii030State),
> +    .class_init    = ftddrii030_class_init,
> +};
> +
> +static void ftddrii030_register_types(void)
> +{
> +    type_register_static(&ftddrii030_info);
> +}
> +
> +type_init(ftddrii030_register_types)



--
Best wishes,
Kuo-Jung Su
Kuo-Jung Su - Feb. 18, 2013, 9:37 a.m.
2013/2/18 Andreas Färber <afaerber@suse.de>:
> Am 07.02.2013 18:26, schrieb Igor Mitsyanko:
>> On 02/06/2013 01:45 PM, Kuo-Jung Su wrote:
>>> +static void ftddrii030_reset(DeviceState *ds)
>>> +{
>>> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
>>> +    Ftddrii030State *s = FTDDRII030(FROM_SYSBUS(Ftddrii030State, busdev));
>>> +    FaradayMachState *mach = s->mach;
>>> +
>>> +    if (!mach) {
>>> +        hw_error("ftddrii030: mach is not yet initialized!\n");
>>> +        exit(1);
>>> +    }
>>> +
>>> +    if (mach->ddr_inited) {
>>> +        if (mach->ahb_remapped) {
>>
>> This assumes that DDRII controller will be reseted before AHBC,
>> otherwise we'll end up with
>> ROM and RAM mapped at 0x0 address after reset. I do not know if we can
>> count on a specific device reset ordering,
>> maybe someone else can clarify this.
>
> Reset handler registration happens as part of realized property setter.
> For most devices that means as part of qdev_init_nofail(), so currently
> the order is predictable for those. But this will change once we delay
> setting realized = true to the end of machine initialization, since then
> realized = true will propagate from "/machine" object to devices in
> child<> property order and along busses, as drafted by Paolo.
>

If I register my own customized reset function with qemu_register_reset()
before calling any sysbus_create_xxxx() or qdev_create(),
would it work properlly?

> Andreas
>
> --
> SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
> GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg



--
Best wishes,
Kuo-Jung Su
Andreas Färber - Feb. 18, 2013, 9:40 a.m.
Am 18.02.2013 10:31, schrieb Kuo-Jung Su:
> 2013/2/8 Igor Mitsyanko <i.mitsyanko@gmail.com>:
>> On 02/06/2013 01:45 PM, Kuo-Jung Su wrote:
>>
>> +static void ftddrii030_reset(DeviceState *ds)
>> +{
>> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
>> +    Ftddrii030State *s = FTDDRII030(FROM_SYSBUS(Ftddrii030State, busdev));
>> +    FaradayMachState *mach = s->mach;
>> +
>> +    if (!mach) {
>> +        hw_error("ftddrii030: mach is not yet initialized!\n");
>> +        exit(1);
>> +    }
>> +
>> +    if (mach->ddr_inited) {
>> +        if (mach->ahb_remapped) {
>>
>>
>> This assumes that DDRII controller will be reseted before AHBC, otherwise
>> we'll end up with
>> ROM and RAM mapped at 0x0 address after reset. I do not know if we can count
>> on a specific device reset ordering,
>> maybe someone else can clarify this.
>>
>>
> 
> The reset sequence is controlled by
> 
> static void
> a369_board_reset(void *opaque)
> {
>     A369State *s = opaque;
> 
>     device_reset(s->scu);
>     device_reset(s->ddrc);
>     device_reset(s->ahbc);
>     cpu_reset(CPU(s->cpu));
> }
> 
> which is registered with qemu_register_reset() during a369_device_init().

This may still lead to this reset handler executing first and then the
three devices in unpredictable order again. Whether that causes any
issues remains for you to check.

sPAPR uses a QEMUMachine-level hook to override reset order of devices
and CPUs, but still among devices no guarantees are made.

Andreas
Kuo-Jung Su - Feb. 18, 2013, 9:47 a.m.
2013/2/18 Andreas Färber <afaerber@suse.de>:
> Am 18.02.2013 10:31, schrieb Kuo-Jung Su:
>> 2013/2/8 Igor Mitsyanko <i.mitsyanko@gmail.com>:
>>> On 02/06/2013 01:45 PM, Kuo-Jung Su wrote:
>>>
>>> +static void ftddrii030_reset(DeviceState *ds)
>>> +{
>>> +    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
>>> +    Ftddrii030State *s = FTDDRII030(FROM_SYSBUS(Ftddrii030State, busdev));
>>> +    FaradayMachState *mach = s->mach;
>>> +
>>> +    if (!mach) {
>>> +        hw_error("ftddrii030: mach is not yet initialized!\n");
>>> +        exit(1);
>>> +    }
>>> +
>>> +    if (mach->ddr_inited) {
>>> +        if (mach->ahb_remapped) {
>>>
>>>
>>> This assumes that DDRII controller will be reseted before AHBC, otherwise
>>> we'll end up with
>>> ROM and RAM mapped at 0x0 address after reset. I do not know if we can count
>>> on a specific device reset ordering,
>>> maybe someone else can clarify this.
>>>
>>>
>>
>> The reset sequence is controlled by
>>
>> static void
>> a369_board_reset(void *opaque)
>> {
>>     A369State *s = opaque;
>>
>>     device_reset(s->scu);
>>     device_reset(s->ddrc);
>>     device_reset(s->ahbc);
>>     cpu_reset(CPU(s->cpu));
>> }
>>
>> which is registered with qemu_register_reset() during a369_device_init().
>
> This may still lead to this reset handler executing first and then the
> three devices in unpredictable order again. Whether that causes any
> issues remains for you to check.
>
> sPAPR uses a QEMUMachine-level hook to override reset order of devices
> and CPUs, but still among devices no guarantees are made.
>

Got it, thanks

> Andreas
>
>
> --
> SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
> GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg

Patch

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 5825c63..fede407 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -36,3 +36,4 @@  obj-y := $(addprefix ../,$(obj-y))
 obj-y += faraday_a360.o faraday_a360_pmu.o
 obj-y += faraday_a369.o faraday_a369_scu.o faraday_a369_keypad.o
 obj-y += ftahbc020.o
+obj-y += ftddrii030.o
diff --git a/hw/arm/faraday_a369.c b/hw/arm/faraday_a369.c
index ae6c445..f60bf4e 100644
--- a/hw/arm/faraday_a369.c
+++ b/hw/arm/faraday_a369.c
@@ -60,6 +60,12 @@  a369_device_init(A369State *s)
     qdev_prop_set_ptr(s->ahbc, "mach", s);
     qdev_init_nofail(s->ahbc);
     sysbus_mmio_map(SYS_BUS_DEVICE(s->ahbc), 0, 0x94000000);
+
+    /* ftddrii030 */
+    s->ddrc = qdev_create(NULL, "ftddrii030");
+    qdev_prop_set_ptr(s->ddrc, "mach", s);
+    qdev_init_nofail(s->ddrc);
+    sysbus_mmio_map(SYS_BUS_DEVICE(s->ddrc), 0, 0x93100000);
 }

 static void
diff --git a/hw/arm/ftddrii030.c b/hw/arm/ftddrii030.c
new file mode 100644
index 0000000..3f66983
--- /dev/null
+++ b/hw/arm/ftddrii030.c
@@ -0,0 +1,174 @@ 
+/*
+ * Faraday DDRII controller
+ *
+ * Copyright (c) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com> <dantesu@faraday-tech.com>
+ *
+ * This code is licensed under GNU GPL v2+
+ */
+
+#include <hw/hw.h>
+#include <hw/sysbus.h>
+#include <hw/devices.h>
+#include <ui/console.h>
+#include <qemu/timer.h>
+#include <sysemu/sysemu.h>
+
+#include "faraday.h"
+
+#define REG_MCR             0x00    /* memory configuration register */
+#define REG_MSR             0x04    /* memory status register */
+
+#define TYPE_FTDDRII030     "ftddrii030"
+
+typedef struct Ftddrii030State {
+    SysBusDevice busdev;
+    MemoryRegion iomem;
+    void *mach;
+    /* HW register cache */
+    uint32_t mcr;
+    uint32_t msr;
+} Ftddrii030State;
+
+#define FTDDRII030(obj) \
+    OBJECT_CHECK(Ftddrii030State, obj, TYPE_FTDDRII030)
+
+static uint64_t
+ftddrii030_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    Ftddrii030State *s = FTDDRII030(opaque);
+    uint64_t ret = 0;
+
+    switch (addr) {
+    case REG_MCR:
+        ret = s->mcr;
+        break;
+    case REG_MSR:
+        ret = s->msr;
+        break;
+    }
+
+    return ret;
+}
+
+static void
+ftddrii030_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    Ftddrii030State *s = FTDDRII030(opaque);
+    FaradayMachState *mach = s->mach;
+    uint32_t base;
+
+    if (!mach) {
+        hw_error("ftddrii030: mach is not yet initialized!\n");
+        exit(1);
+    }
+
+    switch (addr) {
+    case REG_MCR:
+        s->mcr = (uint32_t)val & 0xffff;
+        break;
+    case REG_MSR:
+        val = (val & 0x3f) | (s->msr & 0x100);
+        if (!mach->ddr_inited && (val & 0x01)) {
+            val &= 0xfffffffe;
+            val |= 0x100;
+            if (!mach->ahb_remapped) {
+                /* get RAM base address */
+                base = mach->ahb_slave6 & 0xfff00000;
+                memory_region_add_subregion(mach->as, base, mach->ram_alias);
+            } else {


Can uboot/kernel ever perform this controller setup? If they do, then in
case if we started QEMU with -kernel parameter
and later got here then we already have mach->ram region mapped at 0x0 and
next line will cause assertion. You should set
mach->ddr_inited during machine initialization then.

 +                memory_region_add_subregion(mach->as, 0, mach->ram);
+            }
+            mach->ddr_inited = 1;
+        }
+        s->msr = (uint32_t)val;
+        break;
+    }
+}
+
+static const MemoryRegionOps ftddrii030_mem_ops = {
+    .read  = ftddrii030_mem_read,
+    .write = ftddrii030_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void ftddrii030_reset(DeviceState *ds)
+{
+    SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
+    Ftddrii030State *s = FTDDRII030(FROM_SYSBUS(Ftddrii030State, busdev));
+    FaradayMachState *mach = s->mach;
+
+    if (!mach) {
+        hw_error("ftddrii030: mach is not yet initialized!\n");
+        exit(1);
+    }
+
+    if (mach->ddr_inited) {
+        if (mach->ahb_remapped) {


This assumes that DDRII controller will be reseted before AHBC, otherwise
we'll end up with
ROM and RAM mapped at 0x0 address after reset. I do not know if we can
count on a specific device reset ordering,
maybe someone else can clarify this.

 +            memory_region_del_subregion(mach->as, mach->ram);
+        } else {
+            memory_region_del_subregion(mach->as, mach->ram_alias);
+        }
+        mach->ddr_inited = 0;
+    }
+
+    s->mcr = 0;
+    s->msr = 0;
+}
+
+static int ftddrii030_init(SysBusDevice *dev)
+{
+    Ftddrii030State *s = FTDDRII030(FROM_SYSBUS(Ftddrii030State, dev));
+
+    memory_region_init_io(&s->iomem,
+                          &ftddrii030_mem_ops,
+                          s,
+                          TYPE_FTDDRII030,
+                          0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    return 0;
+}
+
+static Property ftddrii030_properties[] = {
+    DEFINE_PROP_PTR("mach", Ftddrii030State, mach),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static const VMStateDescription vmstate_ftddrii030 = {
+    .name = TYPE_FTDDRII030,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(mcr, Ftddrii030State),
+        VMSTATE_UINT32(msr, Ftddrii030State),
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
+static void ftddrii030_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    k->init   = ftddrii030_init;
+    dc->desc  = TYPE_FTDDRII030;
+    dc->vmsd  = &vmstate_ftddrii030;
+    dc->props = ftddrii030_properties;
+    dc->reset = ftddrii030_reset;