summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbuild-aux/stack.c.gen5
-rw-r--r--libhw/CMakeLists.txt1
-rw-r--r--libhw/rp2040_dma.c56
-rw-r--r--libhw/rp2040_dma.h23
-rw-r--r--libhw/rp2040_hwspi.c21
-rw-r--r--libhw/rp2040_include/libhw/rp2040_hwspi.h2
6 files changed, 95 insertions, 13 deletions
diff --git a/build-aux/stack.c.gen b/build-aux/stack.c.gen
index 66d837a..60f51fe 100755
--- a/build-aux/stack.c.gen
+++ b/build-aux/stack.c.gen
@@ -505,6 +505,7 @@ class LibHWPlugin:
"hostclock_handle_sig_alarm",
"hostnet_handle_sig_io",
"gpioirq_handler",
+ "dmairq_handler",
]
def extra_nodes(self) -> typing.Collection[Node]:
@@ -527,6 +528,10 @@ class LibHWPlugin:
return [
"w5500_intrhandler",
], False
+ if "/rp2040_dmairq.c:" in loc and "handler->fn" in line:
+ return [
+ "rp2040_hwspi_intrhandler",
+ ], False
return None
def skip_call(self, chain: list[str], call: str) -> bool:
diff --git a/libhw/CMakeLists.txt b/libhw/CMakeLists.txt
index bd92e04..242a3fa 100644
--- a/libhw/CMakeLists.txt
+++ b/libhw/CMakeLists.txt
@@ -14,6 +14,7 @@ if (PICO_PLATFORM STREQUAL "rp2040")
libcr_ipc
)
target_sources(libhw INTERFACE
+ rp2040_dma.c
rp2040_gpioirq.c
rp2040_hwspi.c
rp2040_hwtimer.c
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;
+ }
+}
diff --git a/libhw/rp2040_dma.h b/libhw/rp2040_dma.h
index e4b44ff..c8d69b1 100644
--- a/libhw/rp2040_dma.h
+++ b/libhw/rp2040_dma.h
@@ -31,13 +31,26 @@ enum dma_channel_transfer_size {
DMA_SIZE_32 = 2 ///< Word transfer (32 bits)
};
-static inline bool dma_channel_is_busy(uint channel) {
- assert(channel < NUM_DMA_CHANNELS);
- return dma_hw->ch[channel].al1_ctrl & DMA_CH0_CTRL_TRIG_BUSY_BITS;
-}
-
/* Our own code ***************************************************************/
+enum dmairq {
+ DMAIRQ_0 = DMA_IRQ_0,
+ DMAIRQ_1 = DMA_IRQ_1,
+};
+
+typedef void (*dmairq_handler_t)(void *arg, enum dmairq irq, uint channel);
+
+/**
+ * Register `fn(arg, ...)` to be called when `channel` completes or
+ * has a NULL trigger (depending on the channel's configuration).
+ *
+ * Your handler does not need to acknowledge the IRQ; that will be
+ * done for you after your handler is called.
+ *
+ * It is illegal to enable the same channel on more than one IRQ.
+ */
+void dmairq_set_and_enable_exclusive_handler(enum dmairq irq, uint channel, dmairq_handler_t fn, void *arg);
+
#define DMA_CTRL_ENABLE (1<<0)
#define DMA_CTRL_HI_PRIO (1<<1)
#define DMA_CTRL_DATA_SIZE(sz) ((sz)<<2)
diff --git a/libhw/rp2040_hwspi.c b/libhw/rp2040_hwspi.c
index 1c4e096..f747b1e 100644
--- a/libhw/rp2040_hwspi.c
+++ b/libhw/rp2040_hwspi.c
@@ -33,6 +33,12 @@
LO_IMPLEMENTATION_C(io_duplex_readwriter, struct rp2040_hwspi, rp2040_hwspi, static)
LO_IMPLEMENTATION_C(spi, struct rp2040_hwspi, rp2040_hwspi, static)
+static void rp2040_hwspi_intrhandler(void *_self, enum dmairq LM_UNUSED(irq), uint LM_UNUSED(channel)) {
+ struct rp2040_hwspi *self = _self;
+ gpio_put(self->pin_cs, 1);
+ cr_sema_signal_from_intrhandler(&self->sema);
+}
+
#define assert_4distinct(a, b, c, d) \
assert(a != b); \
assert(a != c); \
@@ -124,6 +130,10 @@ void _rp2040_hwspi_init(struct rp2040_hwspi *self,
self->dma_tx_data = dma3;
self->dma_rx_data = dma4;
self->dead_until_ns = 0;
+ self->sema = (cr_sema_t){0};
+
+ /* Initialize the interrupt handler. */
+ dmairq_set_and_enable_exclusive_handler(DMAIRQ_0, self->dma_rx_data, rp2040_hwspi_intrhandler, self);
}
static void rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct duplex_iovec *iov, int iovcnt) {
@@ -225,15 +235,10 @@ static void rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct dupl
uint64_t now = LO_CALL(bootclock, get_time_ns);
if (now < self->dead_until_ns)
sleep_until_ns(self->dead_until_ns);
- /* TODO: Use interrupts instead of busy-polling. */
+ bool saved = cr_save_and_disable_interrupts();
gpio_put(self->pin_cs, 0);
dma_hw->multi_channel_trigger = (1u<<self->dma_tx_ctrl) | (1u<<self->dma_rx_ctrl);
- while (dma_channel_is_busy(self->dma_tx_ctrl)
- || dma_channel_is_busy(self->dma_tx_data)
- || dma_channel_is_busy(self->dma_rx_ctrl)
- || dma_channel_is_busy(self->dma_rx_data))
- tight_loop_contents();
- __compiler_memory_barrier();
- gpio_put(self->pin_cs, 1);
+ cr_restore_interrupts(saved);
+ cr_sema_wait(&self->sema);
self->dead_until_ns = LO_CALL(bootclock, get_time_ns) + self->min_delay_ns;
}
diff --git a/libhw/rp2040_include/libhw/rp2040_hwspi.h b/libhw/rp2040_include/libhw/rp2040_hwspi.h
index a76a2c8..eb54cdc 100644
--- a/libhw/rp2040_include/libhw/rp2040_hwspi.h
+++ b/libhw/rp2040_include/libhw/rp2040_hwspi.h
@@ -9,6 +9,7 @@
#include <pico/binary_info.h> /* for bi_* */
+#include <libcr_ipc/sema.h>
#include <libmisc/private.h>
#include <libhw/generic/spi.h>
@@ -32,6 +33,7 @@ struct rp2040_hwspi {
/* mutable */
uint64_t dead_until_ns;
+ cr_sema_t sema;
END_PRIVATE(LIBHW_RP2040_HWSPI_H)
};
LO_IMPLEMENTATION_H(io_duplex_readwriter, struct rp2040_hwspi, rp2040_hwspi)