Skip to content

Commit 40edcca

Browse files
committed
hw/riscv: fix interrupts being lost or delayed when MIE=0 on the ESP32-C3
1 parent 00b2b75 commit 40edcca

4 files changed

Lines changed: 77 additions & 239 deletions

File tree

hw/riscv/esp32c3_intmatrix.c

Lines changed: 40 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,6 @@
3030
#define SET_BIT(reg, bit) do { (reg) |= BIT(bit); } while(0)
3131

3232

33-
static void esp32c3_do_int(ESP32C3IntMatrixState *s, int line)
34-
{
35-
qemu_irq_pulse(s->out_irqs[line]);
36-
}
37-
38-
3933
static int esp32c3_get_output_line_level(ESP32C3IntMatrixState *s, int line)
4034
{
4135
int level_shared = 0;
@@ -51,27 +45,37 @@ static int esp32c3_get_output_line_level(ESP32C3IntMatrixState *s, int line)
5145
return level_shared;
5246
}
5347

54-
/**
55-
* Try to clear the given interrupt from the pending bitmap.
56-
* If the signal is shared with other interrupt sources, make sure all of them are low (0)
57-
* before clearing the pending IRQ from the bitmap.
58-
*/
59-
static void esp32c3_intmatrix_clear_pending(ESP32C3IntMatrixState *s, int line)
48+
static bool esp32c3_intmatrix_line_should_assert(ESP32C3IntMatrixState *s, int line)
6049
{
61-
/* Check if another GPIO IRQ is sharing the same output line. They must all be low before
62-
* clearing the pending bit. This is due to the fact that output lines
63-
* can be shared on the ESP32-C3 */
64-
const int output_level = esp32c3_get_output_line_level(s, line);
65-
if (!output_level) {
66-
/* The output interrupt line is 0, so we can clear the pending flag */
67-
CLEAR_BIT(s->irq_pending, line);
50+
if (line == 0) {
51+
return false;
6852
}
53+
54+
return BIT_SET(s->irq_enabled, line) &&
55+
esp32c3_get_output_line_level(s, line) != 0 &&
56+
(s->irq_prio[line] >= s->irq_thres);
6957
}
7058

59+
static void esp32c3_intmatrix_update_line(ESP32C3IntMatrixState *s, int line)
60+
{
61+
if (line == 0) {
62+
return;
63+
}
7164

72-
static inline bool esp32c3_intmatrix_can_trigger(ESP32C3IntMatrixState *s)
65+
const bool assert_line = esp32c3_intmatrix_line_should_assert(s, line);
66+
67+
if (assert_line) {
68+
qemu_irq_raise(s->out_irqs[line]);
69+
} else {
70+
qemu_irq_lower(s->out_irqs[line]);
71+
}
72+
}
73+
74+
static void esp32c3_intmatrix_refresh_all(ESP32C3IntMatrixState *s)
7375
{
74-
return esp_cpu_accept_interrupts(s->cpu);
76+
for (uint32_t i = 1; i <= ESP32C3_CPU_INT_COUNT; i++) {
77+
esp32c3_intmatrix_update_line(s, i);
78+
}
7579
}
7680

7781

@@ -80,7 +84,7 @@ static void esp32c3_intmatrix_irq_handler(void *opaque, int n, int level)
8084
ESP32C3IntMatrixState *s = ESP32C3_INTMATRIX(opaque);
8185

8286
/* Update the level mirror */
83-
assert(n <= ESP32C3_INT_MATRIX_INPUTS);
87+
assert(n < ESP32C3_INT_MATRIX_INPUTS);
8488

8589
/* Make sure level is 0 or 1 */
8690
level = level ? 1 : 0;
@@ -94,128 +98,13 @@ static void esp32c3_intmatrix_irq_handler(void *opaque, int n, int level)
9498
CLEAR_BIT(s->irq_levels, n);
9599
}
96100

97-
const int line = s->irq_map[n];
98-
99-
/* If the line is not enable, don't do anything special, the level has been recorded already.
100-
* Don't do anything if the line is at the same level as before */
101-
if ((s->irq_enabled & BIT(line)) == 0 || former_level == level) {
102-
return;
103-
}
104-
105-
/* If the new level is high, check that the priority is equal or bigger than the threshold.
106-
* If that's the case, we can execute the interrupt, else, mark it as pending. */
107-
if (level == 1) {
108-
#if INTMATRIX_DEBUG
109-
info_report("\x1b[31m[INTMATRIX] IRQ %d priority set to %d, CPU threshold %d \x1b[0m\n",
110-
line, s->irq_prio[line], s->irq_thres);
111-
#endif
112-
113-
if (s->irq_prio[line] >= s->irq_thres && esp32c3_intmatrix_can_trigger(s)) {
114-
esp32c3_do_int(s, line);
115-
} else {
116-
SET_BIT(s->irq_pending, line);
117-
}
118-
} else if (BIT_SET(s->irq_pending, line)) {
119-
esp32c3_intmatrix_clear_pending(s, line);
120-
}
121-
}
122-
123-
124-
static void esp32c3_intmatrix_irq_prio_changed(ESP32C3IntMatrixState* s, uint32_t line, uint8_t priority)
125-
{
126-
const bool accept = esp32c3_intmatrix_can_trigger(s);
127-
128-
if (accept && priority >= s->irq_thres && BIT_SET(s->irq_pending, line)) {
129-
/* No need to clear the pending bit here. As soon as the interrupt source will be ACK by the
130-
* software, its level will be update, as well as its pending state. */
131-
esp32c3_do_int(s, line);
132-
}
133-
}
134-
135-
136-
static void esp32c3_intmatrix_core_prio_changed(ESP32C3IntMatrixState* s, uint64_t new_cpu_priority)
137-
{
138-
uint64_t pending = s->irq_pending;
139-
const bool accept = esp32c3_intmatrix_can_trigger(s);
140-
141-
if (pending && accept) {
142-
int64_t priority = -1;
143-
uint_fast32_t line = 0;
144-
145-
/* Clear all the interrupts that have a lower priority than the new CPU threshold */
146-
for (uint_fast32_t i = 1; i <= ESP32C3_CPU_INT_COUNT; i++) {
147-
148-
const uint64_t line_prio = s->irq_prio[i];
149-
if (line_prio < new_cpu_priority) {
150-
CLEAR_BIT(pending, i);
151-
}
152-
}
153-
154-
/* No high level interrupt pending? */
155-
if (pending == 0) {
156-
return;
157-
}
158-
159-
/* Look for the highest priority pending interrupt */
160-
for (uint_fast32_t i = 1; i <= ESP32C3_CPU_INT_COUNT; i++) {
161-
const int64_t line_prio = (int64_t) s->irq_prio[i];
162-
if (BIT_SET(pending, i) && line_prio > priority) {
163-
priority = line_prio;
164-
line = i;
165-
}
166-
}
167-
168-
/* Make sure a line was selected with its new priority */
169-
assert(line != 0);
170-
assert(priority >= new_cpu_priority);
171-
/* No need to clear the pending bit here. As soon as the interrupt source will be ACK by the
172-
* software, its level will be update, as well as its pending state. */
173-
esp32c3_do_int(s, line);
174-
}
175-
}
176-
177-
178-
/**
179-
* This function is called when the status (enabled/disabled) of a line has just been changed.
180-
* It will update the pending IRQ map.
181-
*/
182-
static void esp32c3_intmatrix_irq_status_changed(ESP32C3IntMatrixState* s, uint32_t line, int enabled)
183-
{
184-
const bool accept = esp32c3_intmatrix_can_trigger(s);
185-
186-
if (!enabled) {
187-
188-
/* IRQ has just been disabled, if any interrupt is pending, clear it */
189-
CLEAR_BIT(s->irq_pending, line);
190-
191-
} else if (esp32c3_get_output_line_level(s, line)) {
192-
193-
/* IRQ has just been re-enabled, we have to check if any interrupt source is mapped to it, and
194-
* if that's the case, check if their level is high, as we would need to potentially trigger an
195-
* interrupt. */
196-
SET_BIT(s->irq_pending, line);
197-
198-
if (accept) {
199-
/* If the CPU can accept interrupt, trigger an interrupt now */
200-
esp32c3_do_int(s, line);
201-
}
101+
/* Nothing to do if the level is unchanged. */
102+
if (former_level != level) {
103+
const int line = s->irq_map[n];
104+
esp32c3_intmatrix_update_line(s, line);
202105
}
203106
}
204107

205-
206-
/**
207-
* Callback invoked by the CPU as soon as interrupts are re-enabled
208-
*/
209-
static bool esp32c3_intmatrix_mie_enabled(void* opaque)
210-
{
211-
ESP32C3IntMatrixState *s = ESP32C3_INTMATRIX(opaque);
212-
/* We need to check if any interrupt is pending and trigger it. We have such function already, triggered when
213-
* the core priority changes, let's reuse this function by giving the same core priority */
214-
esp32c3_intmatrix_core_prio_changed(s, s->irq_thres);
215-
return s->irq_pending != 0;
216-
}
217-
218-
219108
static uint64_t esp32c3_intmatrix_read(void* opaque, hwaddr addr, unsigned int size)
220109
{
221110
ESP32C3IntMatrixState *s = ESP32C3_INTMATRIX(opaque);
@@ -251,11 +140,16 @@ static void esp32c3_intmatrix_write(void* opaque, hwaddr addr, uint64_t value, u
251140
const uint32_t index = addr / sizeof(uint32_t);
252141

253142
if (index < ESP32C3_INT_MATRIX_INPUTS) {
254-
255-
s->irq_map[index] = (value & 0x1f);
143+
const uint8_t old_line = s->irq_map[index];
144+
const uint8_t new_line = (value & 0x1f);
145+
s->irq_map[index] = new_line;
256146
#if INTMATRIX_DEBUG
257147
info_report("\x1b[31m[INTMATRIX] Mapping interrupt %d to CPU line %d\x1b[0m\n", index, s->irq_map[index]);
258148
#endif
149+
if (old_line != new_line) {
150+
esp32c3_intmatrix_update_line(s, old_line);
151+
esp32c3_intmatrix_update_line(s, new_line);
152+
}
259153

260154
} else if (index >= ESP32C3_INTMATRIX_IO_PRIO_START && index < ESP32C3_INTMATRIX_IO_PRIO_END) {
261155

@@ -266,7 +160,7 @@ static void esp32c3_intmatrix_write(void* opaque, hwaddr addr, uint64_t value, u
266160
info_report("\x1b[31m[INTMATRIX] Priority of line %d set to %d\x1b[0m\n", line, priority);
267161
#endif
268162
/* Check if the new priority interrupts the CPU */
269-
esp32c3_intmatrix_irq_prio_changed(s, line, priority);
163+
esp32c3_intmatrix_update_line(s, line);
270164

271165
} else if (index == ESP32C3_INTMATRIX_IO_THRESH_REG) {
272166

@@ -289,7 +183,7 @@ static void esp32c3_intmatrix_write(void* opaque, hwaddr addr, uint64_t value, u
289183
#if INTMATRIX_DEBUG
290184
info_report("\x1b[31m[INTMATRIX] Setting CPU IRQ threshold to %d\x1b[0m", priority);
291185
#endif
292-
esp32c3_intmatrix_core_prio_changed(s, priority);
186+
esp32c3_intmatrix_refresh_all(s);
293187
}
294188

295189
} else if (index == ESP32C3_INTMATRIX_IO_ENABLE_REG) {
@@ -302,7 +196,7 @@ static void esp32c3_intmatrix_write(void* opaque, hwaddr addr, uint64_t value, u
302196
const int new_st = value & BIT(i);
303197
const int old_st = prev & BIT(i);
304198
if (new_st != old_st) {
305-
esp32c3_intmatrix_irq_status_changed(s, i, new_st ? 1 : 0);
199+
esp32c3_intmatrix_update_line(s, i);
306200
}
307201
}
308202
} else if (index == ESP32C3_INTMATRIX_IO_TYPE_REG) {
@@ -334,7 +228,6 @@ static void esp32c3_intmatrix_reset_hold(Object *obj, ResetType type)
334228
memset(s->irq_map, 0, sizeof(s->irq_map));
335229
memset(s->irq_prio, 0, sizeof(s->irq_prio));
336230
s->irq_thres = 0;
337-
s->irq_pending = 0;
338231
s->irq_levels = 0;
339232
s->irq_trigger = 0;
340233
s->irq_enabled = 0;
@@ -349,15 +242,7 @@ static void esp32c3_intmatrix_reset_hold(Object *obj, ResetType type)
349242

350243
static void esp32c3_intmatrix_realize(DeviceState *dev, Error **errp)
351244
{
352-
ESP32C3IntMatrixState *s = ESP32C3_INTMATRIX(dev);
353-
EspRISCVCPU *cpu = s->cpu;
354-
EspRISCVCPUClass *cpu_klass = ESP_CPU_GET_CLASS(cpu);
355-
356245
esp32c3_intmatrix_reset_hold(OBJECT(dev), RESET_TYPE_COLD);
357-
358-
/* Register MIE callback */
359-
assert(cpu);
360-
cpu_klass->esp_cpu_register_mie_callback(cpu, esp32c3_intmatrix_mie_enabled, s);
361246
}
362247

363248

include/hw/riscv/esp32c3_intmatrix.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,6 @@ typedef struct ESP32C3IntMatrixState {
7575
uint8_t irq_prio[ESP32C3_CPU_INT_COUNT + 1];
7676
/* Current priority threshold of the CPU interrupts */
7777
uint8_t irq_thres;
78-
/* Keep a bitmap of the pending interrupts */
79-
uint64_t irq_pending;
8078
/* Bitmap that records the enabled/disabled interrupts */
8179
uint64_t irq_enabled;
8280
/* Bitmap that records the type of trigger for interrupts */

0 commit comments

Comments
 (0)