aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2012-03-04 18:17:53 +0100
committerHarald Welte <laforge@gnumonks.org>2012-03-05 01:03:57 +0100
commit0aafdf2366e12d767555c6d25719a4fd34eef839 (patch)
treed193c05d00928f30fd846e8176d4aca026d78216
parenta4fb8f496a49976e8b6669a427d518c4dc98268d (diff)
cc32rs512: add simulation of the built-in flash controllerHEADmaster
-rw-r--r--hw/cc32rs512.c220
1 files changed, 215 insertions, 5 deletions
diff --git a/hw/cc32rs512.c b/hw/cc32rs512.c
index 48dbd02..8e9289d 100644
--- a/hw/cc32rs512.c
+++ b/hw/cc32rs512.c
@@ -23,6 +23,201 @@
#include "boards.h"
#include "exec-memory.h"
+/***********************************************************************
+ * Flash controller
+ ***********************************************************************/
+
+enum cc32_flcon_reg {
+ FLCON = 0x00,
+ FLSDP1 = 0x04,
+ FLSDP2 = 0x08,
+ FLSTS = 0x0C,
+ FLBUF = 0x10,
+};
+
+#define NUM_FL_REGS ((FLBUF>>2) + 4)
+
+enum write_state {
+ WRST_NONE,
+ WRST_SDP1_55,
+ WRST_SDP1_AA,
+ WRST_ERASE,
+ WRST_PROGRAM,
+};
+
+typedef struct cc32_flcon_state {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ qemu_irq irq;
+
+ uint32_t regs[NUM_FL_REGS];
+ enum write_state wrst;
+ uint8_t *storage;
+ uint8_t *ram_storage;
+} cc32_flcon_state;
+
+static void cc32_flcon_update(cc32_flcon_state *s)
+{
+ if (s->regs[FLSTS>>2] & 1)
+ qemu_set_irq(s->irq, 1);
+ else
+ qemu_set_irq(s->irq, 0);
+}
+
+static uint64_t cc32_flcon_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
+{
+ cc32_flcon_state *s = (cc32_flcon_state *)opaque;
+
+ return s->regs[offset>>2];
+}
+
+static void cc32_flcon_write(void *opaque, target_phys_addr_t offset,
+ uint64_t value, unsigned size)
+{
+ cc32_flcon_state *s = (cc32_flcon_state *)opaque;
+
+ s->regs[offset>>2] = value;
+
+ switch (offset) {
+ case FLSDP1:
+ switch (value) {
+ case 0x55:
+ s->wrst = WRST_SDP1_55;
+ break;
+ case 0xAA:
+ s->wrst = WRST_SDP1_AA;
+ break;
+ default:
+ /* Signal incorrect operation */
+ s->regs[FLSTS>>2] |= (1 << 1);
+ s->wrst = WRST_NONE;
+ break;
+ }
+ break;
+ case FLSDP2:
+ if (s->wrst == WRST_SDP1_55 && (value == 0xAA)) {
+ s->wrst = WRST_ERASE;
+ /* actual ERASE will be performed once the program
+ * writes a 0xFF to any address within the flash page */
+ } else if ((s->wrst == WRST_SDP1_AA) && (value == 0x55)) {
+ s->wrst = WRST_PROGRAM;
+ /* actual PROGRAM will be performed once the program
+ * writes a 0x00 to any address within the flash page */
+ } else {
+ /* Signal incorrect operation */
+ s->regs[FLSTS>>2] |= (1 << 1);
+ s->wrst = WRST_NONE;
+ break;
+ }
+ break;
+ case FLSTS:
+ /* clear bits */
+ s->regs[offset>>2] &= ~(value & 3);
+ break;
+ }
+
+ cc32_flcon_update(s);
+}
+
+/* callback for user writes to the flash memory region */
+static void cc32_flash_write(void *opaque, target_phys_addr_t offset,
+ uint64_t value, unsigned size)
+{
+ cc32_flcon_state *s = (cc32_flcon_state *)opaque;
+ uint32_t epsize = (s->regs[FLCON>>2] >> 3);
+ uint32_t page_size, mask;
+
+ if (epsize == 0) {
+ page_size = 512;
+ mask = 0xfffffe00;
+ } else {
+ page_size = 256;
+ mask = 0xffffff00;
+ }
+ /* FIXME: check for 0x00 / 0xFF as value */
+
+ switch (s->wrst) {
+ case WRST_ERASE:
+ fprintf(stderr, "cc32-flcon: Erasing %u at offset 0x%06x\n",
+ page_size, offset & mask);
+ memset(s->storage + (offset & mask), 0xFF, page_size);
+ s->regs[FLSTS>>2] |= 1;
+ s->wrst = WRST_NONE;
+ break;
+ case WRST_PROGRAM:
+ fprintf(stderr, "cc32-flcon: Programming %u at offset 0x%06x\n",
+ page_size, offset & mask);
+ memcpy(s->storage + (offset & mask),
+ s->ram_storage + ((s->regs[FLBUF>>2] & 0xffffff) - 0xC0000),
+ page_size);
+ s->regs[FLSTS>>2] |= 1;
+ s->wrst = WRST_NONE;
+ break;
+ default:
+ break;
+ }
+
+ cc32_flcon_update(s);
+}
+
+
+/* MMIO interface of the FLCON */
+static const MemoryRegionOps cc32_flcon_ops = {
+ .read = cc32_flcon_read,
+ .write = cc32_flcon_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+/* Flash memory writes for memory_region_init_rom_device() */
+static const MemoryRegionOps cc32_flash_ops = {
+ .write = cc32_flash_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int cc32_flcon_init(SysBusDevice *dev)
+{
+ cc32_flcon_state *s = FROM_SYSBUS(cc32_flcon_state, dev);
+
+ sysbus_init_irq(dev, &s->irq);
+
+ memory_region_init_io(&s->iomem, &cc32_flcon_ops, s, "cc32-flcon", NUM_FL_REGS<<2);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ return 0;
+}
+
+static void cc32_flcon_reset(DeviceState *d)
+{
+ cc32_flcon_state *s = container_of(d, cc32_flcon_state, busdev.qdev);
+
+ s->regs[FLBUF>>2] = 0xC0000 + (16*1024) - 512;
+}
+
+static void cc32_flcon_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = cc32_flcon_init;
+ dc->reset = cc32_flcon_reset;
+}
+
+static TypeInfo cc32_flcon_info = {
+ .name = "cc32-flcon",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(cc32_flcon_state),
+ .class_init = cc32_flcon_class_init,
+};
+
+/***********************************************************************
+ * System controller
+ ***********************************************************************/
+
enum cc32_sysc_reg {
SCCM0 = 0x00,
SCSYS = 0x04,
@@ -42,7 +237,7 @@ enum cc32_sysc_reg {
SCCM4 = 0x7C,
};
-#define NUM_REGS (SCCM4 + 4)
+#define NUM_REGS ((SCCM4>>2) + 4)
typedef struct cc32_sysc_state
{
@@ -105,6 +300,7 @@ static void cc32_sysc_write(void *opaque, target_phys_addr_t offset,
switch (offset) {
case SCINTEN:
s->irq_enabled = value;
+ fprintf(stderr, "cc32-sysc: INTEN = 0x%08x\n", s->irq_enabled);
break;
}
cc32_sysc_update(s);
@@ -123,7 +319,7 @@ static int cc32_sysc_init(SysBusDevice *dev)
qdev_init_gpio_in(&dev->qdev, cc32_sysc_set_irq, 32);
sysbus_init_irq(dev, &s->parent_irq);
sysbus_init_irq(dev, &s->parent_fiq);
- memory_region_init_io(&s->iomem, &cc32_sysc_ops, s, "cc32-sysc", 8);
+ memory_region_init_io(&s->iomem, &cc32_sysc_ops, s, "cc32-sysc", NUM_REGS<<2);
sysbus_init_mmio(dev, &s->iomem);
return 0;
@@ -156,11 +352,16 @@ static TypeInfo cc32_sysc_info = {
static void cc32_register_types(void)
{
type_register_static(&cc32_sysc_info);
+ type_register_static(&cc32_flcon_info);
}
type_init(cc32_register_types)
+/***********************************************************************
+ * SoC core
+ ***********************************************************************/
+
struct arm_boot_info cc32rs512_binfo;
@@ -177,6 +378,7 @@ static void cc32rs512_init(ram_addr_t ram_size,
qemu_irq *cpu_irq;
qemu_irq pic[32];
DeviceState *dev;
+ cc32_flcon_state *fl;
int i;
if (!cpu_model)
@@ -187,13 +389,12 @@ static void cc32rs512_init(ram_addr_t ram_size,
exit(1);
}
- memory_region_init_ram(flash, "cc32rs512.flash", 512*1024);
- memory_region_add_subregion(sysmem, 0, flash);
-
memory_region_init_ram(ram, "cc32rs512.ram", 16*1024);
+ vmstate_register_ram_global(ram);
memory_region_add_subregion(sysmem, 0xc0000, ram);
memory_region_init_ram(rsa_ram, "cc32rs512.rsa_ram", 2*1024);
+ vmstate_register_ram_global(rsa_ram);
memory_region_add_subregion(sysmem, 0xd4000, rsa_ram);
cc32rs512_binfo.ram_size = ram_size;
@@ -209,6 +410,15 @@ static void cc32rs512_init(ram_addr_t ram_size,
sysbus_create_simple("cc32-iso-slave", 0x0F8800, pic[8]);
+ dev = sysbus_create_simple("cc32-flcon", 0x0F2000, pic[6]);
+ fl = container_of(dev, cc32_flcon_state, busdev.qdev);
+ memory_region_init_rom_device(flash, &cc32_flash_ops, fl,
+ "cc32rs512.flash", 512*1024);
+ vmstate_register_ram_global(flash);
+ fl->storage = memory_region_get_ram_ptr(flash);
+ fl->ram_storage = memory_region_get_ram_ptr(ram);
+ memory_region_add_subregion(sysmem, 0, flash);
+
arm_load_kernel(env, &cc32rs512_binfo);
}