summaryrefslogtreecommitdiff
path: root/libhw/rp2040_dma.c
diff options
context:
space:
mode:
Diffstat (limited to 'libhw/rp2040_dma.c')
-rw-r--r--libhw/rp2040_dma.c56
1 files changed, 56 insertions, 0 deletions
diff --git a/libhw/rp2040_dma.c b/libhw/rp2040_dma.c
new file mode 100644
index 0000000..dfbf136
--- /dev/null
+++ b/libhw/rp2040_dma.c
@@ -0,0 +1,56 @@
+/* libhw/rp2040_dma.c - Utilities for sharing the DMA IRQs
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <stdbool.h>
+
+#include <hardware/irq.h> /* for irq_set_exclusive_handler() */
+
+#include "rp2040_dma.h"
+
+struct dmairq_handler_entry {
+ dmairq_handler_t fn;
+ void *arg;
+};
+struct dmairq_handler_entry dmairq_handlers[NUM_DMA_CHANNELS] = {0};
+
+bool dmairq_initialized[NUM_DMA_IRQS] = {0};
+
+static void dmairq_handler(void) {
+ enum dmairq irq = __get_current_exception() - VTABLE_FIRST_IRQ;
+ size_t irq_idx = irq - DMAIRQ_0;
+ assert(irq_idx < NUM_DMA_IRQS);
+
+ uint32_t regval = dma_hw->irq_ctrl[irq_idx].ints;
+ for (uint channel = 0; channel < NUM_DMA_CHANNELS; channel++) {
+ if (regval & 1u<<channel) {
+ struct dmairq_handler_entry *handler = &dmairq_handlers[channel];
+ if (handler->fn)
+ handler->fn(handler->arg, irq, channel);
+ }
+ }
+ /* acknowledge irq */
+ dma_hw->intr = regval;
+}
+
+void dmairq_set_and_enable_exclusive_handler(enum dmairq irq, uint channel, dmairq_handler_t fn, void *arg) {
+ assert(irq == DMAIRQ_0 || irq == DMAIRQ_1);
+ assert(channel < NUM_DMA_CHANNELS);
+ assert(fn);
+
+ assert(dmairq_handlers[channel].fn == NULL);
+
+ dmairq_handlers[channel].fn = fn;
+ dmairq_handlers[channel].arg = arg;
+
+ size_t irq_idx = irq - DMAIRQ_0;
+ hw_set_bits(&dma_hw->irq_ctrl[irq_idx].inte, 1u<<channel);
+
+ if (!dmairq_initialized[irq_idx]) {
+ irq_set_exclusive_handler(irq, dmairq_handler);
+ irq_set_enabled(irq, true);
+ dmairq_initialized[irq_idx] = true;
+ }
+}