diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-02-24 22:54:30 -0700 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-03-04 00:16:34 -0700 |
commit | 3f49a57b99e7fe5aafa73e70ed146d98b1ae174c (patch) | |
tree | d72daabeedecbc397cdce739639f8cf2cb947685 /libhw/rp2040_dma.c | |
parent | 7a1f764cda070de396fcb186e1f7457ff2704ba4 (diff) |
libhw: rp2040_hwspi: Use interrupts instead of busy-polling
Diffstat (limited to 'libhw/rp2040_dma.c')
-rw-r--r-- | libhw/rp2040_dma.c | 56 |
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; + } +} |