hw/misc: add ESP32 DPORT peripheral, cache, cross-core interrupt
This commit is contained in:
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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'))
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user