From ccdda32084e89108a536dab3a1e1ff83401b8a38 Mon Sep 17 00:00:00 2001 From: Omar Chebib Date: Mon, 10 Mar 2025 18:05:42 +0800 Subject: [PATCH] hw/timer: fix reset/default value for register SYSTIMER_CONF on ESP targets * Fixes https://github.com/espressif/qemu/issues/120 --- hw/timer/esp32c3_systimer.c | 9 ++++++++- hw/timer/esp_systimer.c | 22 +++++++++++++++++++--- include/hw/timer/esp_systimer.h | 3 ++- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/hw/timer/esp32c3_systimer.c b/hw/timer/esp32c3_systimer.c index 6f13890441..92c23e3cd1 100644 --- a/hw/timer/esp32c3_systimer.c +++ b/hw/timer/esp32c3_systimer.c @@ -20,13 +20,20 @@ static uint64_t esp32c3_systimer_read(void *opaque, hwaddr addr, unsigned int si uint64_t r = 0; switch (addr) { + case A_SYSTIMER_CONF: + /* On the C3, bit 27 is always 0, bit 25 is always high */ + r = class->parent_systimer_read(opaque, addr, size); + r &= ~BIT(27); + r |= BIT(25); + break; + case A_SYSTIMER_REAL_TARGET0_LO: case A_SYSTIMER_REAL_TARGET0_HI: case A_SYSTIMER_REAL_TARGET1_LO: case A_SYSTIMER_REAL_TARGET1_HI: case A_SYSTIMER_REAL_TARGET2_LO: case A_SYSTIMER_REAL_TARGET2_HI: - /* These registers are not supported on the C3 hardware, so nothing! */ + /* These registers are not supported on the C3 hardware, do nothing! */ break; default: diff --git a/hw/timer/esp_systimer.c b/hw/timer/esp_systimer.c index cef60bb5b4..3daff32dc9 100644 --- a/hw/timer/esp_systimer.c +++ b/hw/timer/esp_systimer.c @@ -381,8 +381,10 @@ static void esp_systimer_conf_set(ESPSysTimerState *s, uint64_t value) } s->counter[0].enabled = (value & R_SYSTIMER_CONF_TIMER_UNIT0_WORK_EN_MASK) ? 1 : 0; s->counter[1].enabled = (value & R_SYSTIMER_CONF_TIMER_UNIT1_WORK_EN_MASK) ? 1 : 0; - s->counter[0].enabled_on_stall = (value & R_SYSTIMER_CONF_TIMER_UNIT0_CORE0_STALL_EN_MASK) ? 1 : 0; - s->counter[1].enabled_on_stall = (value & R_SYSTIMER_CONF_TIMER_UNIT1_CORE0_STALL_EN_MASK) ? 1 : 0; + s->counter[0].core0_stall_en = (value & R_SYSTIMER_CONF_TIMER_UNIT0_CORE0_STALL_EN_MASK) ? 1 : 0; + s->counter[1].core0_stall_en = (value & R_SYSTIMER_CONF_TIMER_UNIT1_CORE0_STALL_EN_MASK) ? 1 : 0; + s->counter[0].core1_stall_en = (value & R_SYSTIMER_CONF_TIMER_UNIT0_CORE1_STALL_EN_MASK) ? 1 : 0; + s->counter[1].core1_stall_en = (value & R_SYSTIMER_CONF_TIMER_UNIT1_CORE1_STALL_EN_MASK) ? 1 : 0; /* If the state of the counter changed while one of the comparator depends on it, reload it */ for (int i = 0; i < ESP_SYSTIMER_COMP_COUNT; i++) { @@ -558,10 +560,22 @@ static void esp_systimer_cb(void* opaque) } +static void esp_systimer_default_conf(ESPSysTimerState *s) +{ + /* According to the TRM, on dual-core targets, counter 1 stalls with core 1 */ + s->counter[1].core0_stall_en = true; + s->counter[1].core1_stall_en = true; + /* Counter 0 is marked as enabled (no comparator enabled though) */ + s->counter[0].enabled = true; + s->conf = R_SYSTIMER_CONF_TIMER_UNIT0_WORK_EN_MASK | + R_SYSTIMER_CONF_TIMER_UNIT1_CORE0_STALL_EN_MASK | + R_SYSTIMER_CONF_TIMER_UNIT1_CORE1_STALL_EN_MASK; +} + + static void esp_systimer_reset(DeviceState* ts) { ESPSysTimerState *s = ESP_SYSTIMER(ts); - s->conf = 0; for (int i = 0; i < ESP_SYSTIMER_COMP_COUNT; i++) { ESPSysTimerComp* comp = &s->comparators[i]; QEMUTimer qtimer = comp->qtimer; @@ -579,6 +593,7 @@ static void esp_systimer_reset(DeviceState* ts) comp->irq = irq; comp->systimer = s; } + esp_systimer_default_conf(s); } @@ -602,6 +617,7 @@ static void esp_systimer_init(Object *obj) sysbus_init_irq(sbd, &s->comparators[i].irq); timer_init_ns(&s->comparators[i].qtimer, QEMU_CLOCK_VIRTUAL, esp_systimer_cb, &s->comparators[i]); } + esp_systimer_default_conf(s); } diff --git a/include/hw/timer/esp_systimer.h b/include/hw/timer/esp_systimer.h index 9fd1b80619..d8e2955ab9 100644 --- a/include/hw/timer/esp_systimer.h +++ b/include/hw/timer/esp_systimer.h @@ -69,7 +69,8 @@ static inline void systimer_set_reg(uint64_t* reg, uint64_t value, uint64_t mask typedef struct { bool enabled; /* Enabled on CPU stall */ - bool enabled_on_stall; + bool core0_stall_en; + bool core1_stall_en; uint64_t value; // Internal counter that should be updated as often as possible uint64_t toload; // Counter that can be loaded by the guest program uint64_t flushed; // Mirror of the internal counter that can be seen by the guest program