diff options
Diffstat (limited to 'hw')
-rw-r--r-- | hw/cc32rs512.c | 220 |
1 files changed, 215 insertions, 5 deletions
diff --git a/hw/cc32rs512.c b/hw/cc32rs512.c index 48dbd0207..8e9289dc4 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); } |