summaryrefslogtreecommitdiff
path: root/libhw/rp2040_dma.c
blob: dfbf136380d3e3054311bd53631eca794dc91bad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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;
	}
}