From: Kuo-Jung Su <dant...@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 <dant...@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 <dant...@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 { + 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) { + 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) -- 1.7.9.5