diff --git a/hw/misc/esp32_crosscore_int.c b/hw/misc/esp32_crosscore_int.c new file mode 100644 index 0000000000..9b2c4fc55f --- /dev/null +++ b/hw/misc/esp32_crosscore_int.c @@ -0,0 +1,91 @@ +/* + * ESP32 Cross-core interrupt + * + * Copyright (c) 2019 Espressif Systems (Shanghai) Co. Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "hw/sysbus.h" +#include "hw/irq.h" +#include "hw/registerfields.h" +#include "hw/qdev-properties.h" +#include "hw/misc/esp32_reg.h" +#include "hw/misc/esp32_crosscore_int.h" + + +static uint64_t esp32_crosscore_int_read(void *opaque, hwaddr addr, unsigned int size) +{ + return 0; +} + +static void esp32_crosscore_int_write(void *opaque, hwaddr addr, + uint64_t value, unsigned int size) +{ + Esp32CrosscoreInt *s = ESP32_CROSSCORE_INT(opaque); + int index = addr / 4; + assert(index < s->n_irqs); + qemu_set_irq(s->irqs[index], value & 0x1); +} + +static const MemoryRegionOps esp32_crosscore_int_ops = { + .read = esp32_crosscore_int_read, + .write = esp32_crosscore_int_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void esp32_crosscore_int_realize(DeviceState *dev, Error **errp) +{ + Esp32CrosscoreInt *s = ESP32_CROSSCORE_INT(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + s->irqs = g_malloc0_n(s->n_irqs, sizeof(qemu_irq)); + assert(s->irqs); + for (int i = 0; i < s->n_irqs; ++i) { + sysbus_init_irq(sbd, &s->irqs[i]); + } + + memory_region_init_io(&s->iomem, OBJECT(dev), &esp32_crosscore_int_ops, s, + TYPE_ESP32_CROSSCORE_INT, + s->n_irqs * sizeof(uint32_t)); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void esp32_crosscore_int_init(Object *obj) +{ + +} + +static Property esp32_crosscore_int_properties[] = { + DEFINE_PROP_INT32("n_irqs", Esp32CrosscoreInt, n_irqs, 4), + DEFINE_PROP_END_OF_LIST(), +}; + +static void esp32_crosscore_int_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = esp32_crosscore_int_realize; + device_class_set_props(dc, esp32_crosscore_int_properties); +} + +static const TypeInfo esp32_crosscore_int_info = { + .name = TYPE_ESP32_CROSSCORE_INT, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Esp32CrosscoreInt), + .instance_init = esp32_crosscore_int_init, + .class_init = esp32_crosscore_int_class_init +}; + +static void esp32_crosscore_int_register_types(void) +{ + type_register_static(&esp32_crosscore_int_info); +} + +type_init(esp32_crosscore_int_register_types) diff --git a/hw/misc/esp32_dport.c b/hw/misc/esp32_dport.c new file mode 100644 index 0000000000..ee3e68bb58 --- /dev/null +++ b/hw/misc/esp32_dport.c @@ -0,0 +1,476 @@ +/* + * ESP32 "DPORT" device + * + * Copyright (c) 2019 Espressif Systems (Shanghai) Co. Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" +#include "hw/registerfields.h" +#include "hw/boards.h" +#include "hw/misc/esp32_reg.h" +#include "hw/misc/esp32_dport.h" + +#include "hw/misc/esp32_flash_enc.h" +#include "hw/nvram/esp32_efuse.h" + + +#define ESP32_DPORT_SIZE (DR_REG_DPORT_APB_BASE - DR_REG_DPORT_BASE) + +#define MMU_RANGE_SIZE (ESP32_CACHE_PAGES_PER_REGION * sizeof(uint32_t)) +#define MMU_RANGE_LAST (MMU_RANGE_SIZE - sizeof(uint32_t)) + +#define PRO_DROM0_MMU_FIRST (DR_REG_FLASH_MMU_TABLE_PRO - DR_REG_DPORT_BASE) +#define PRO_DROM0_MMU_LAST (PRO_DROM0_MMU_FIRST + MMU_RANGE_LAST) +#define PRO_IRAM0_MMU_FIRST (DR_REG_FLASH_MMU_TABLE_PRO - DR_REG_DPORT_BASE + MMU_RANGE_SIZE) +#define PRO_IRAM0_MMU_LAST (PRO_IRAM0_MMU_FIRST + MMU_RANGE_LAST) +#define APP_DROM0_MMU_FIRST (DR_REG_FLASH_MMU_TABLE_APP - DR_REG_DPORT_BASE) +#define APP_DROM0_MMU_LAST (APP_DROM0_MMU_FIRST + MMU_RANGE_LAST) +#define APP_IRAM0_MMU_FIRST (DR_REG_FLASH_MMU_TABLE_APP - DR_REG_DPORT_BASE + MMU_RANGE_SIZE) +#define APP_IRAM0_MMU_LAST (APP_IRAM0_MMU_FIRST + MMU_RANGE_LAST) +#define MMU_ENTRY_MASK 0x1ff + +static void esp32_cache_state_update(Esp32CacheState* cs); +static void esp32_cache_data_sync(Esp32CacheRegionState* crs); +static void esp32_cache_invalidate_all_entries(Esp32CacheRegionState* crs); + +static inline uint32_t get_mmu_entry(Esp32CacheRegionState* crs, hwaddr base, hwaddr addr) +{ + return crs->mmu_table[(addr - base)/sizeof(uint32_t)] & MMU_ENTRY_MASK; +} + +static inline void set_mmu_entry(Esp32CacheRegionState* crs, hwaddr base, hwaddr addr, uint64_t val) +{ + uint32_t old_val = crs->mmu_table[(addr - base)/sizeof(uint32_t)]; + if (val != old_val) { + crs->mmu_table[(addr - base)/sizeof(uint32_t)] = (val & MMU_ENTRY_MASK) | ESP32_CACHE_MMU_ENTRY_CHANGED; + } +} + +static uint64_t esp32_dport_read(void *opaque, hwaddr addr, unsigned int size) +{ + Esp32DportState *s = ESP32_DPORT(opaque); + uint64_t r = 0; + switch (addr) { + case A_DPORT_APPCPU_RESET: + r = s->appcpu_reset_state; + break; + case A_DPORT_APPCPU_CLK: + r = s->appcpu_clkgate_state; + break; + case A_DPORT_APPCPU_RUNSTALL: + r = s->appcpu_stall_state; + break; + case A_DPORT_APPCPU_BOOT_ADDR: + r = s->appcpu_boot_addr; + break; + case A_DPORT_CPU_PER_CONF: + r = s->cpuperiod_sel; + break; + case A_DPORT_PRO_CACHE_CTRL: + r = s->cache_state[0].cache_ctrl_reg; + break; + case A_DPORT_PRO_CACHE_CTRL1: + r = s->cache_state[0].cache_ctrl1_reg; + break; + case A_DPORT_APP_CACHE_CTRL: + r = s->cache_state[1].cache_ctrl_reg; + break; + case A_DPORT_APP_CACHE_CTRL1: + r = s->cache_state[1].cache_ctrl1_reg; + break; + case A_DPORT_PRO_DCACHE_DBUG0: + case A_DPORT_APP_DCACHE_DBUG0: + /* in idle state */ + r = FIELD_DP32(0, DPORT_PRO_DCACHE_DBUG0, CACHE_STATE, 1); + break; + case A_DPORT_CACHE_IA_INT_EN: + r = s->cache_ill_trap_en_reg; + break; + case A_DPORT_PRO_DCACHE_DBUG3: + r = 0; + r = FIELD_DP32(r, DPORT_PRO_DCACHE_DBUG3, IA_INT_DROM0, s->cache_state[0].drom0.illegal_access_status); + r = FIELD_DP32(r, DPORT_PRO_DCACHE_DBUG3, IA_INT_IRAM0, s->cache_state[0].iram0.illegal_access_status); + break; + case A_DPORT_APP_DCACHE_DBUG3: + r = 0; + r = FIELD_DP32(r, DPORT_APP_DCACHE_DBUG3, IA_INT_DROM0, s->cache_state[1].drom0.illegal_access_status); + r = FIELD_DP32(r, DPORT_APP_DCACHE_DBUG3, IA_INT_IRAM0, s->cache_state[1].iram0.illegal_access_status); + break; + case PRO_DROM0_MMU_FIRST ... PRO_DROM0_MMU_LAST: + r = get_mmu_entry(&s->cache_state[0].drom0, PRO_DROM0_MMU_FIRST, addr); + break; + case PRO_IRAM0_MMU_FIRST ... PRO_IRAM0_MMU_LAST: + r = get_mmu_entry(&s->cache_state[0].iram0, PRO_IRAM0_MMU_FIRST, addr); + break; + case APP_DROM0_MMU_FIRST ... APP_DROM0_MMU_LAST: + r = get_mmu_entry(&s->cache_state[1].drom0, APP_DROM0_MMU_FIRST, addr); + break; + case APP_IRAM0_MMU_FIRST ... APP_IRAM0_MMU_LAST: + r = get_mmu_entry(&s->cache_state[1].iram0, APP_IRAM0_MMU_FIRST, addr); + break; + case A_DPORT_SLAVE_SPI_CONFIG: + r = s->slave_spi_config_reg; + break; + } + + return r; +} + +static void esp32_dport_write(void *opaque, hwaddr addr, + uint64_t value, unsigned int size) +{ + Esp32DportState *s = ESP32_DPORT(opaque); + bool old_state; + uint32_t old_val; + switch (addr) { + case A_DPORT_APPCPU_RESET: + old_state = s->appcpu_reset_state; + s->appcpu_reset_state = value & 1; + if (old_state && !s->appcpu_reset_state) { + qemu_irq_pulse(s->appcpu_reset_req); + } + break; + case A_DPORT_APPCPU_CLK: + s->appcpu_clkgate_state = value & 1; + qemu_set_irq(s->appcpu_stall_req, s->appcpu_stall_state || !s->appcpu_clkgate_state); + break; + case A_DPORT_APPCPU_RUNSTALL: + s->appcpu_stall_state = value & 1; + qemu_set_irq(s->appcpu_stall_req, s->appcpu_stall_state || !s->appcpu_clkgate_state); + break; + case A_DPORT_APPCPU_BOOT_ADDR: + s->appcpu_boot_addr = value; + break; + case A_DPORT_CPU_PER_CONF: + s->cpuperiod_sel = value & R_DPORT_CPU_PER_CONF_CPUPERIOD_SEL_MASK; + qemu_irq_pulse(s->clk_update_req); + break; + case A_DPORT_PRO_CACHE_CTRL: + if (FIELD_EX32(value, DPORT_PRO_CACHE_CTRL, CACHE_FLUSH_ENA)) { + value |= R_DPORT_PRO_CACHE_CTRL_CACHE_FLUSH_DONE_MASK; + value &= ~R_DPORT_PRO_CACHE_CTRL_CACHE_FLUSH_ENA_MASK; + esp32_cache_invalidate_all_entries(&s->cache_state[0].drom0); + esp32_cache_data_sync(&s->cache_state[0].drom0); + esp32_cache_invalidate_all_entries(&s->cache_state[0].iram0); + esp32_cache_data_sync(&s->cache_state[0].iram0); + } + old_val = s->cache_state[0].cache_ctrl_reg; + s->cache_state[0].cache_ctrl_reg = value; + if (value != old_val) { + esp32_cache_state_update(&s->cache_state[0]); + } + break; + case A_DPORT_PRO_CACHE_CTRL1: + old_val = s->cache_state[0].cache_ctrl1_reg; + s->cache_state[0].cache_ctrl1_reg = value; + if (value != old_val) { + esp32_cache_state_update(&s->cache_state[0]); + } + break; + case A_DPORT_APP_CACHE_CTRL: + if (FIELD_EX32(value, DPORT_APP_CACHE_CTRL, CACHE_FLUSH_ENA)) { + value |= R_DPORT_APP_CACHE_CTRL_CACHE_FLUSH_DONE_MASK; + value &= ~R_DPORT_APP_CACHE_CTRL_CACHE_FLUSH_ENA_MASK; + esp32_cache_invalidate_all_entries(&s->cache_state[1].drom0); + esp32_cache_data_sync(&s->cache_state[1].drom0); + esp32_cache_invalidate_all_entries(&s->cache_state[1].iram0); + esp32_cache_data_sync(&s->cache_state[1].iram0); + } + old_val = s->cache_state[1].cache_ctrl_reg; + s->cache_state[1].cache_ctrl_reg = value; + if (value != old_val) { + esp32_cache_state_update(&s->cache_state[1]); + } + break; + case A_DPORT_APP_CACHE_CTRL1: + old_val = s->cache_state[1].cache_ctrl1_reg; + s->cache_state[1].cache_ctrl1_reg = value; + if (value != old_val) { + esp32_cache_state_update(&s->cache_state[1]); + } + break; + case A_DPORT_CACHE_IA_INT_EN: + s->cache_ill_trap_en_reg = value; + s->cache_state[0].drom0.illegal_access_trap_en = (FIELD_EX32(value, DPORT_CACHE_IA_INT_EN, IA_INT_PRO_DROM0)); + s->cache_state[0].iram0.illegal_access_trap_en = (FIELD_EX32(value, DPORT_CACHE_IA_INT_EN, IA_INT_PRO_IRAM0)); + s->cache_state[1].drom0.illegal_access_trap_en = (FIELD_EX32(value, DPORT_CACHE_IA_INT_EN, IA_INT_APP_DROM0)); + s->cache_state[1].iram0.illegal_access_trap_en = (FIELD_EX32(value, DPORT_CACHE_IA_INT_EN, IA_INT_APP_IRAM0)); + s->cache_state[0].dram1.illegal_access_trap_en = (FIELD_EX32(value, DPORT_CACHE_IA_INT_EN, IA_INT_PRO_DRAM1)); + s->cache_state[1].dram1.illegal_access_trap_en = (FIELD_EX32(value, DPORT_CACHE_IA_INT_EN, IA_INT_APP_DRAM1)); + break; + case PRO_DROM0_MMU_FIRST ... PRO_DROM0_MMU_LAST: + set_mmu_entry(&s->cache_state[0].drom0, PRO_DROM0_MMU_FIRST, addr, value); + break; + case PRO_IRAM0_MMU_FIRST ... PRO_IRAM0_MMU_LAST: + set_mmu_entry(&s->cache_state[0].iram0, PRO_IRAM0_MMU_FIRST, addr, value); + break; + case APP_DROM0_MMU_FIRST ... APP_DROM0_MMU_LAST: + set_mmu_entry(&s->cache_state[1].drom0, APP_DROM0_MMU_FIRST, addr, value); + break; + case APP_IRAM0_MMU_FIRST ... APP_IRAM0_MMU_LAST: + set_mmu_entry(&s->cache_state[1].iram0, APP_IRAM0_MMU_FIRST, addr, value); + break; + case A_DPORT_SLAVE_SPI_CONFIG: + s->slave_spi_config_reg = value; + qemu_set_irq(s->flash_enc_en_gpio, FIELD_EX32(value, DPORT_SLAVE_SPI_CONFIG, SLAVE_SPI_ENCRYPT_ENABLE)); + qemu_set_irq(s->flash_dec_en_gpio, FIELD_EX32(value, DPORT_SLAVE_SPI_CONFIG, SLAVE_SPI_DECRYPT_ENABLE)); + break; + } +} + +static const MemoryRegionOps esp32_dport_ops = { + .read = esp32_dport_read, + .write = esp32_dport_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void esp32_cache_data_sync(Esp32CacheRegionState* crs) +{ + if (crs->cache->dport->flash_blk == NULL) { + return; + } + + Esp32FlashEncryptionState * flash_enc = esp32_flash_encryption_find(); + bool decrypt = (flash_enc != NULL && esp32_flash_decryption_enabled(flash_enc)); + + uint8_t* cache_data = (uint8_t*) memory_region_get_ram_ptr(&crs->mem); + for (int i = 0; i < ESP32_CACHE_PAGES_PER_REGION; ++i) { + uint32_t* cache_page = (uint32_t*) (cache_data + i * ESP32_CACHE_PAGE_SIZE); + uint32_t mmu_entry = crs->mmu_table[i]; + if (!(mmu_entry & ESP32_CACHE_MMU_ENTRY_CHANGED)) { + continue; + } + mmu_entry &= MMU_ENTRY_MASK; + if (mmu_entry & ESP32_CACHE_MMU_INVALID_VAL) { + uint32_t fill_val = crs->illegal_access_retval; + for (int word = 0; word < ESP32_CACHE_PAGE_SIZE / sizeof(uint32_t); ++word) { + cache_page[word] = fill_val; + } + } else { + uint32_t phys_addr = mmu_entry * ESP32_CACHE_PAGE_SIZE; + blk_pread(crs->cache->dport->flash_blk, phys_addr, ESP32_CACHE_PAGE_SIZE, cache_page, 0); + if (decrypt) { + esp32_flash_decrypt_inplace(flash_enc, phys_addr, cache_page, ESP32_CACHE_PAGE_SIZE/4); + } + } + crs->mmu_table[i] &= ~ESP32_CACHE_MMU_ENTRY_CHANGED; + } + memory_region_flush_rom_device(&crs->mem, 0, ESP32_CACHE_REGION_SIZE); +} + +static void esp32_cache_invalidate_all_entries(Esp32CacheRegionState* crs) +{ + for (int i = 0; i < ESP32_CACHE_PAGES_PER_REGION; ++i) { + crs->mmu_table[i] |= ESP32_CACHE_MMU_ENTRY_CHANGED; + } +} + +static void esp32_cache_state_update(Esp32CacheState* cs) +{ + bool cache_enabled = FIELD_EX32(cs->cache_ctrl_reg, DPORT_PRO_CACHE_CTRL, CACHE_ENA) != 0; + + bool drom0_enabled = cache_enabled && + FIELD_EX32(cs->cache_ctrl1_reg, DPORT_PRO_CACHE_CTRL1, MASK_DROM0) == 0; + if (!cs->drom0.mem.enabled && drom0_enabled) { + esp32_cache_data_sync(&cs->drom0); + } + memory_region_set_enabled(&cs->drom0.mem, drom0_enabled); + + bool iram0_enabled = cache_enabled && + FIELD_EX32(cs->cache_ctrl1_reg, DPORT_PRO_CACHE_CTRL1, MASK_IRAM0) == 0; + if (!cs->iram0.mem.enabled && iram0_enabled) { + esp32_cache_data_sync(&cs->iram0); + } + memory_region_set_enabled(&cs->iram0.mem, iram0_enabled); + + if (cs->dport->has_psram) { + bool dram1_enabled = cache_enabled && + FIELD_EX32(cs->cache_ctrl1_reg, DPORT_PRO_CACHE_CTRL1, MASK_DRAM1) == 0; + memory_region_set_enabled(&cs->dram1.mem, dram1_enabled); + } +} + +static void esp32_cache_region_reset(Esp32CacheRegionState *crs) +{ + for (int i = 0; i < ESP32_CACHE_PAGES_PER_REGION; ++i) { + crs->mmu_table[i] = ESP32_CACHE_MMU_ENTRY_CHANGED; + } + crs->illegal_access_trap_en = false; +} + +static void esp32_cache_reset(Esp32CacheState *cs) +{ + esp32_cache_region_reset(&cs->drom0); + esp32_cache_region_reset(&cs->iram0); +} + +static uint64_t esp32_cache_ill_read(void *opaque, hwaddr addr, unsigned int size) +{ + Esp32CacheRegionState *crs = (Esp32CacheRegionState*) opaque; + uint32_t ill_data[] = { crs->illegal_access_retval, crs->illegal_access_retval }; + uint32_t result; + memcpy(&result, ((uint8_t*) ill_data) + (addr % 4), size); + if (crs->illegal_access_trap_en) { + crs->illegal_access_status = true; + qemu_irq_raise(crs->cache->dport->cache_ill_irq); + } + return result; +} + +static void esp32_cache_ill_write(void *opaque, hwaddr addr, + uint64_t value, unsigned int size) +{ +} + +static bool esp32_cache_ill_accepts(void *opaque, hwaddr addr, + unsigned size, bool is_write, + MemTxAttrs attrs) +{ + return !is_write; +} + + +void esp32_dport_clear_ill_trap_state(Esp32DportState* s) +{ + s->cache_state[0].drom0.illegal_access_status = false; + s->cache_state[1].drom0.illegal_access_status = false; + s->cache_state[0].iram0.illegal_access_status = false; + s->cache_state[1].iram0.illegal_access_status = false; + s->cache_state[0].dram1.illegal_access_status = false; + s->cache_state[1].dram1.illegal_access_status = false; + qemu_irq_lower(s->cache_ill_irq); +} + +static const MemoryRegionOps esp32_cache_ops = { + .write = NULL, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.accepts = esp32_cache_ill_accepts, +}; + + +static const MemoryRegionOps esp32_cache_ill_trap_ops = { + .read = esp32_cache_ill_read, + .write = esp32_cache_ill_write, +}; + +static void esp32_dport_reset(DeviceState *dev) +{ + Esp32DportState *s = ESP32_DPORT(dev); + + s->appcpu_boot_addr = 0; + s->appcpu_clkgate_state = false; + s->appcpu_reset_state = true; + s->appcpu_stall_state = false; + s->cache_ill_trap_en_reg = 0; + esp32_cache_reset(&s->cache_state[0]); + esp32_cache_reset(&s->cache_state[1]); + qemu_irq_lower(s->appcpu_stall_req); +} + +static void esp32_dport_realize(DeviceState *dev, Error **errp) +{ + Esp32DportState *s = ESP32_DPORT(dev); + MachineState *ms = MACHINE(qdev_get_machine()); + + s->cpu_count = ms->smp.cpus; +} + +static void esp32_cache_init_region(Esp32DportState *ds, + Esp32CacheState *cs, + Esp32CacheRegionState *crs, + Esp32CacheRegionType type, + const char* name, hwaddr base, + uint32_t illegal_access_retval) +{ + char desc[16]; + crs->cache = cs; + crs->type = type; + crs->base = base; + crs->illegal_access_retval = illegal_access_retval; + snprintf(desc, sizeof(desc), "cpu%d-%s", cs->core_id, name); + if (type == ESP32_DCACHE_PSRAM) { + memory_region_init_alias(&crs->mem, OBJECT(cs->dport), desc, + &ds->psram, 0, ESP32_CACHE_REGION_SIZE); + } else { + memory_region_init_rom_device(&crs->mem, OBJECT(cs->dport), + &esp32_cache_ops, crs, + desc, ESP32_CACHE_REGION_SIZE, &error_abort); + } + + snprintf(desc, sizeof(desc), "cpu%d-%s-ill", cs->core_id, name); + memory_region_init_io(&crs->illegal_access_trap_mem, OBJECT(cs->dport), + &esp32_cache_ill_trap_ops, crs, + desc, ESP32_CACHE_REGION_SIZE); +} + +static void esp32_dport_init(Object *obj) +{ + Esp32DportState *s = ESP32_DPORT(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->iomem, obj, &esp32_dport_ops, s, + TYPE_ESP32_DPORT, ESP32_DPORT_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + + memory_region_init_ram(&s->psram, obj, "psram", ESP32_CACHE_REGION_SIZE, &error_fatal); + + for (int i = 0; i < ESP32_CPU_COUNT; ++i) { + Esp32CacheState* cs = &s->cache_state[i]; + cs->core_id = i; + cs->dport = s; + esp32_cache_init_region(s, cs, &cs->drom0, ESP32_DCACHE_FLASH, "drom0", + 0x3F400000, 0xbaadbaad); + esp32_cache_init_region(s, cs, &cs->iram0, ESP32_ICACHE_FLASH, "iram0", + 0x40000000, 0x00000000); + esp32_cache_init_region(s, cs, &cs->dram1, ESP32_DCACHE_PSRAM, "dram1", + 0x3F800000, 0xbaadbaad); + } + + qdev_init_gpio_out_named(DEVICE(sbd), &s->appcpu_stall_req, ESP32_DPORT_APPCPU_STALL_GPIO, 1); + qdev_init_gpio_out_named(DEVICE(sbd), &s->appcpu_reset_req, ESP32_DPORT_APPCPU_RESET_GPIO, 1); + qdev_init_gpio_out_named(DEVICE(sbd), &s->clk_update_req, ESP32_DPORT_CLK_UPDATE_GPIO, 1); + qdev_init_gpio_out_named(DEVICE(sbd), &s->cache_ill_irq, ESP32_DPORT_CACHE_ILL_IRQ_GPIO, 1); + qdev_init_gpio_out_named(DEVICE(sbd), &s->flash_enc_en_gpio, ESP32_DPORT_FLASH_ENC_EN_GPIO, 1); + qdev_init_gpio_out_named(DEVICE(sbd), &s->flash_dec_en_gpio, ESP32_DPORT_FLASH_DEC_EN_GPIO, 1); +} + +static Property esp32_dport_properties[] = { + DEFINE_PROP_DRIVE("flash", Esp32DportState, flash_blk), + DEFINE_PROP_BOOL("has_psram", Esp32DportState, has_psram, false), + DEFINE_PROP_END_OF_LIST(), +}; + +static void esp32_dport_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->legacy_reset = esp32_dport_reset; + dc->realize = esp32_dport_realize; + device_class_set_props(dc, esp32_dport_properties); +} + +static const TypeInfo esp32_dport_info = { + .name = TYPE_ESP32_DPORT, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Esp32DportState), + .instance_init = esp32_dport_init, + .class_init = esp32_dport_class_init +}; + +static void esp32_dport_register_types(void) +{ + type_register_static(&esp32_dport_info); +} + +type_init(esp32_dport_register_types) diff --git a/hw/misc/meson.build b/hw/misc/meson.build index d02d96e403..8a7ec9068f 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -136,6 +136,10 @@ system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( system_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-sysreg.c')) system_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_rng.c')) +system_ss.add(when: 'CONFIG_XTENSA_ESP32', if_true: files( + 'esp32_crosscore_int.c', + 'esp32_dport.c', +)) system_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_ahb_apb_pnp.c')) diff --git a/include/hw/misc/esp32_crosscore_int.h b/include/hw/misc/esp32_crosscore_int.h new file mode 100644 index 0000000000..35c8739dda --- /dev/null +++ b/include/hw/misc/esp32_crosscore_int.h @@ -0,0 +1,17 @@ +#pragma once + +#include "hw/hw.h" +#include "hw/registerfields.h" +#include "hw/sysbus.h" +#include "sysemu/block-backend.h" + + +#define TYPE_ESP32_CROSSCORE_INT "misc.esp32.crosscoreint" +#define ESP32_CROSSCORE_INT(obj) OBJECT_CHECK(Esp32CrosscoreInt, (obj), TYPE_ESP32_CROSSCORE_INT) + +typedef struct Esp32CrosscoreInt { + SysBusDevice parent_obj; + MemoryRegion iomem; + int n_irqs; + qemu_irq *irqs; +} Esp32CrosscoreInt; diff --git a/include/hw/misc/esp32_dport.h b/include/hw/misc/esp32_dport.h new file mode 100644 index 0000000000..3dfc0ab4ce --- /dev/null +++ b/include/hw/misc/esp32_dport.h @@ -0,0 +1,178 @@ +#pragma once + +#include "hw/hw.h" +#include "hw/registerfields.h" +#include "hw/sysbus.h" +#include "sysemu/block-backend.h" +#include "hw/misc/esp32_flash_enc.h" + +typedef struct Esp32DportState Esp32DportState; +typedef struct Esp32CacheState Esp32CacheState; + +#define TYPE_ESP32_DPORT "misc.esp32.dport" +#define ESP32_DPORT(obj) OBJECT_CHECK(Esp32DportState, (obj), TYPE_ESP32_DPORT) + +#define ESP32_CACHE_PAGE_SIZE 0x10000 +#define ESP32_CACHE_PAGES_PER_REGION 64 +#define ESP32_CACHE_REGION_SIZE (ESP32_CACHE_PAGE_SIZE * ESP32_CACHE_PAGES_PER_REGION) +#define ESP32_CACHE_MMU_INVALID_VAL 0x100 +#define ESP32_CACHE_MMU_ENTRY_CHANGED 0x200 /* not a hardware flag; used here to check if the page data needs to be updated */ +#define ESP32_CACHE_MAX_PHYS_PAGES 0x100 + +typedef enum Esp32CacheRegionType { + ESP32_DCACHE_FLASH, + ESP32_ICACHE_FLASH, + ESP32_DCACHE_PSRAM, +} Esp32CacheRegionType; + +typedef struct Esp32CacheRegionState { + Esp32CacheState* cache; + MemoryRegion mem; + MemoryRegion illegal_access_trap_mem; + Esp32CacheRegionType type; + hwaddr base; + uint32_t illegal_access_retval; + bool illegal_access_trap_en; + bool illegal_access_status; + uint16_t mmu_table[ESP32_CACHE_PAGES_PER_REGION]; +} Esp32CacheRegionState; + +typedef struct Esp32CacheState { + Esp32DportState* dport; + int core_id; + + uint32_t cache_ctrl_reg; + uint32_t cache_ctrl1_reg; + /* Using only the first 4MB range. + * TODO: add memory regions for other ports: iram1, irom0 + */ + Esp32CacheRegionState iram0; + Esp32CacheRegionState drom0; + Esp32CacheRegionState dram1; /* PSRAM */ +} Esp32CacheState; + +typedef struct Esp32DportState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + bool has_psram; + int cpu_count; + Esp32CacheState cache_state[ESP32_CPU_COUNT]; + MemoryRegion psram; /* Shared between the CPUs: the actual memory region for PSRAM */ + BlockBackend *flash_blk; + qemu_irq appcpu_stall_req; + qemu_irq appcpu_reset_req; + qemu_irq clk_update_req; + qemu_irq cache_ill_irq; + qemu_irq flash_enc_en_gpio; + qemu_irq flash_dec_en_gpio; + + bool appcpu_reset_state; + bool appcpu_stall_state; + bool appcpu_clkgate_state; + uint32_t appcpu_boot_addr; + uint32_t cpuperiod_sel; + uint32_t cache_ill_trap_en_reg; + uint32_t slave_spi_config_reg; + +} Esp32DportState; + +void esp32_dport_clear_ill_trap_state(Esp32DportState* s); + +#define ESP32_DPORT_APPCPU_STALL_GPIO "appcpu-stall" +#define ESP32_DPORT_APPCPU_RESET_GPIO "appcpu-reset" +#define ESP32_DPORT_CLK_UPDATE_GPIO "clk-update" +#define ESP32_DPORT_CACHE_ILL_IRQ_GPIO "cache-ill-irq" +#define ESP32_DPORT_FLASH_ENC_EN_GPIO "flash-enc-en" +#define ESP32_DPORT_FLASH_DEC_EN_GPIO "flash-dec-en" + + +REG32(DPORT_APPCPU_RESET, 0x2c) +REG32(DPORT_APPCPU_CLK, 0x30) +REG32(DPORT_APPCPU_RUNSTALL, 0x34) +REG32(DPORT_APPCPU_BOOT_ADDR, 0x38) + +REG32(DPORT_CPU_PER_CONF, 0x3c) + FIELD(DPORT_CPU_PER_CONF, CPUPERIOD_SEL, 0, 2) + +REG32(DPORT_PRO_CACHE_CTRL, 0x40) + FIELD(DPORT_PRO_CACHE_CTRL, CACHE_FLUSH_DONE, 5, 1) + FIELD(DPORT_PRO_CACHE_CTRL, CACHE_FLUSH_ENA, 4, 1) + FIELD(DPORT_PRO_CACHE_CTRL, CACHE_ENA, 3, 1) + +REG32(DPORT_PRO_CACHE_CTRL1, 0x44) + FIELD(DPORT_PRO_CACHE_CTRL1, MMU_IA_CLR, 13, 1) + FIELD(DPORT_PRO_CACHE_CTRL1, MASK_OPSDRAM, 5, 1) + FIELD(DPORT_PRO_CACHE_CTRL1, MASK_DROM0, 4, 1) + FIELD(DPORT_PRO_CACHE_CTRL1, MASK_DRAM1, 3, 1) + FIELD(DPORT_PRO_CACHE_CTRL1, MASK_IROM0, 2, 1) + FIELD(DPORT_PRO_CACHE_CTRL1, MASK_IRAM1, 1, 1) + FIELD(DPORT_PRO_CACHE_CTRL1, MASK_IRAM0, 0, 1) + +REG32(DPORT_APP_CACHE_CTRL, 0x58) + FIELD(DPORT_APP_CACHE_CTRL, CACHE_FLUSH_DONE, 5, 1) + FIELD(DPORT_APP_CACHE_CTRL, CACHE_FLUSH_ENA, 4, 1) + FIELD(DPORT_APP_CACHE_CTRL, CACHE_ENA, 3, 1) + +REG32(DPORT_APP_CACHE_CTRL1, 0x5C) + FIELD(DPORT_APP_CACHE_CTRL1, MMU_IA_CLR, 13, 1) + FIELD(DPORT_APP_CACHE_CTRL1, MASK_OPSDRAM, 5, 1) + FIELD(DPORT_APP_CACHE_CTRL1, MASK_DROM0, 4, 1) + FIELD(DPORT_APP_CACHE_CTRL1, MASK_DRAM1, 3, 1) + FIELD(DPORT_APP_CACHE_CTRL1, MASK_IROM0, 2, 1) + FIELD(DPORT_APP_CACHE_CTRL1, MASK_IRAM1, 1, 1) + FIELD(DPORT_APP_CACHE_CTRL1, MASK_IRAM0, 0, 1) + +REG32(DPORT_SLAVE_SPI_CONFIG, 0xC8) + FIELD(DPORT_SLAVE_SPI_CONFIG, SLAVE_SPI_ENCRYPT_ENABLE, 8, 1) + FIELD(DPORT_SLAVE_SPI_CONFIG, SLAVE_SPI_DECRYPT_ENABLE, 12, 1) + +REG32(DPORT_CPU_INTR_FROM_CPU_0, 0xdc) +REG32(DPORT_CPU_INTR_FROM_CPU_1, 0xe0) +REG32(DPORT_CPU_INTR_FROM_CPU_2, 0xe4) +REG32(DPORT_CPU_INTR_FROM_CPU_3, 0xe8) + +REG32(DPORT_PRO_MAC_INTR_MAP, 0x104) +REG32(DPORT_APP_MAC_INTR_MAP, 0x218) + +REG32(DPORT_PRO_DCACHE_DBUG0, 0x3f0) + FIELD(DPORT_PRO_DCACHE_DBUG0, CACHE_STATE, 7, 12) + +REG32(DPORT_PRO_DCACHE_DBUG3, 0x3FC) + FIELD(DPORT_PRO_DCACHE_DBUG3, IA_INT_OPPOSITE, 9, 1) + FIELD(DPORT_PRO_DCACHE_DBUG3, IA_INT_DRAM1, 10, 1) + FIELD(DPORT_PRO_DCACHE_DBUG3, IA_INT_IROM0, 11, 1) + FIELD(DPORT_PRO_DCACHE_DBUG3, IA_INT_IRAM1, 12, 1) + FIELD(DPORT_PRO_DCACHE_DBUG3, IA_INT_IRAM0, 13, 1) + FIELD(DPORT_PRO_DCACHE_DBUG3, IA_INT_DROM0, 14, 1) + +REG32(DPORT_APP_DCACHE_DBUG0, 0x418) + FIELD(DPORT_APP_DCACHE_DBUG0, CACHE_STATE, 7, 12) + +REG32(DPORT_APP_DCACHE_DBUG3, 0x424) + FIELD(DPORT_APP_DCACHE_DBUG3, IA_INT_OPPOSITE, 9, 1) + FIELD(DPORT_APP_DCACHE_DBUG3, IA_INT_DRAM1, 10, 1) + FIELD(DPORT_APP_DCACHE_DBUG3, IA_INT_IROM0, 11, 1) + FIELD(DPORT_APP_DCACHE_DBUG3, IA_INT_IRAM1, 12, 1) + FIELD(DPORT_APP_DCACHE_DBUG3, IA_INT_IRAM0, 13, 1) + FIELD(DPORT_APP_DCACHE_DBUG3, IA_INT_DROM0, 14, 1) + +REG32(DPORT_CACHE_IA_INT_EN, 0x5A0) + FIELD(DPORT_CACHE_IA_INT_EN, IA_INT_PRO_OPPOSITE, 19, 1) + FIELD(DPORT_CACHE_IA_INT_EN, IA_INT_PRO_DRAM1, 18, 1) + FIELD(DPORT_CACHE_IA_INT_EN, IA_INT_PRO_IROM0, 17, 1) + FIELD(DPORT_CACHE_IA_INT_EN, IA_INT_PRO_IRAM1, 16, 1) + FIELD(DPORT_CACHE_IA_INT_EN, IA_INT_PRO_IRAM0, 15, 1) + FIELD(DPORT_CACHE_IA_INT_EN, IA_INT_PRO_DROM0, 14, 1) + FIELD(DPORT_CACHE_IA_INT_EN, IA_INT_APP_OPPOSITE, 5, 1) + FIELD(DPORT_CACHE_IA_INT_EN, IA_INT_APP_DRAM1, 4, 1) + FIELD(DPORT_CACHE_IA_INT_EN, IA_INT_APP_IROM0, 3, 1) + FIELD(DPORT_CACHE_IA_INT_EN, IA_INT_APP_IRAM1, 2, 1) + FIELD(DPORT_CACHE_IA_INT_EN, IA_INT_APP_IRAM0, 1, 1) + FIELD(DPORT_CACHE_IA_INT_EN, IA_INT_APP_DROM0, 0, 1) + + +#define ESP32_DPORT_PRO_INTMATRIX_BASE A_DPORT_PRO_MAC_INTR_MAP +#define ESP32_DPORT_APP_INTMATRIX_BASE A_DPORT_APP_MAC_INTR_MAP +#define ESP32_DPORT_CROSSCORE_INT_BASE A_DPORT_CPU_INTR_FROM_CPU_0 +