/* 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; } }