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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
/* libhw_cr/rp2040_dma.h - Utilities for using DMA on the RP2040 (replaces <hardware/dma.h>)
*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#ifndef _LIBHW_CR_RP2040_DMA_H_
#define _LIBHW_CR_RP2040_DMA_H_
#include <stddef.h> /* for offsetof() */
#include <stdint.h> /* for uint32_t */
#include <hardware/regs/dreq.h> /* for DREQ_* for use with DMA_CTRL_TREQ_SEL() */
#include <hardware/structs/dma.h> /* for dma_hw, DMA_NUM_CHANNELS */
#include <libmisc/assert.h>
#include <libmisc/macro.h> /* for LM_FLOORLOG2() */
/* IRQ ************************************************************************/
enum dmairq {
DMAIRQ_0 = 11,
DMAIRQ_1 = 12,
};
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 before 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);
/* Control blocks *************************************************************/
bool dma_is_unsafe(volatile const void *addr);
#ifdef NDEBUG
#define dma_assert_addrs(dst, src) ((void)0)
#else
void dma_assert_addrs(volatile void *dst, volatile const void *src);
#endif
/* types =================================================*/
/* | elem | val | name */
#define READ_ADDR /*|*/volatile const void/*|*/ * /*|*/read_addr
#define WRITE_ADDR /*|*/volatile void/*|*/ * /*|*/write_addr
#define XFER_COUNT /*|*/ /*|*/uint32_t/*|*/xfer_count
#define CTRL /*|*/ /*|*/uint32_t/*|*/ctrl
/* { +0x0 ; +0x4 ; +0x8 ; +0xC (Trigger) */
struct dma_alias0 { READ_ADDR ; WRITE_ADDR ; XFER_COUNT ; CTRL ; };
struct dma_alias1 { CTRL ; READ_ADDR ; WRITE_ADDR ; XFER_COUNT ; };
struct dma_alias2 { CTRL ; XFER_COUNT ; READ_ADDR ; WRITE_ADDR ; };
struct dma_alias3 { CTRL ; WRITE_ADDR ; XFER_COUNT ; READ_ADDR ; };
struct dma_alias0_short2 { XFER_COUNT ; CTRL ; };
struct dma_alias1_short2 { WRITE_ADDR ; XFER_COUNT ; };
struct dma_alias2_short2 { READ_ADDR ; WRITE_ADDR ; };
struct dma_alias3_short2 { XFER_COUNT ; READ_ADDR ; };
struct dma_alias0_short3 { CTRL ; };
struct dma_alias1_short3 { XFER_COUNT ; };
struct dma_alias2_short3 { WRITE_ADDR ; };
struct dma_alias3_short3 { READ_ADDR ; };
#undef CTRL
#undef XFER_COUNT
#undef WRITE_ADDR
#undef READ_ADDR
/* locations =============================================*/
dma_channel_hw_t *dma_channel_hw_addr(uint channel);
#define DMA_CHAN_ADDR(CH, TYP) ((TYP *volatile)_Generic((TYP){}, \
\
struct dma_alias0: &dma_channel_hw_addr(CH)->read_addr, \
struct dma_alias1: &dma_channel_hw_addr(CH)->al1_ctrl, \
struct dma_alias2: &dma_channel_hw_addr(CH)->al2_ctrl, \
struct dma_alias3: &dma_channel_hw_addr(CH)->al3_ctrl, \
\
struct dma_alias0_short2: &dma_channel_hw_addr(CH)->transfer_count, \
struct dma_alias1_short2: &dma_channel_hw_addr(CH)->al1_write_addr, \
struct dma_alias2_short2: &dma_channel_hw_addr(CH)->al2_read_addr, \
struct dma_alias3_short2: &dma_channel_hw_addr(CH)->al3_transfer_count, \
\
struct dma_alias0_short3: &dma_channel_hw_addr(CH)->ctrl_trig, \
struct dma_alias1_short3: &dma_channel_hw_addr(CH)->al1_transfer_count_trig, \
struct dma_alias2_short3: &dma_channel_hw_addr(CH)->al2_write_addr_trig, \
struct dma_alias3_short3: &dma_channel_hw_addr(CH)->al3_read_addr_trig))
#define DMA_IS_TRIGGER(TYP, FIELD) (offsetof(TYP, FIELD) == 0xC)
#define DMA_NONTRIGGER(CH, FIELD) (DMA_CHAN_ADDR(CH, _DMA_NONTRIGGER_##FIELD)->FIELD)
#define _DMA_NONTRIGGER_read_addr struct dma_alias0
#define _DMA_NONTRIGGER_write_addr struct dma_alias0
#define _DMA_NONTRIGGER_xfer_count struct dma_alias0
#define _DMA_NONTRIGGER_ctrl struct dma_alias1
#define DMA_TRIGGER(CH, FIELD) (DMA_CHAN_ADDR(CH, _DMA_TRIGGER_##FIELD)->FIELD)
#define _DMA_TRIGGER_read_addr struct dma_alias3
#define _DMA_TRIGGER_write_addr struct dma_alias2
#define _DMA_TRIGGER_xfer_count struct dma_alias1
#define _DMA_TRIGGER_ctrl struct dma_alias0
/* block->ctrl register *******************************************************/
enum dma_transfer_size {
DMA_SIZE_8 = 0, /** Byte transfer (8 bits) */
DMA_SIZE_16 = 1, /** Half word transfer (16 bits) */
DMA_SIZE_32 = 2, /** Word transfer (32 bits) */
};
#define DMA_CTRL_ENABLE (1<<0)
#define DMA_CTRL_HI_PRIO (1<<1)
#define DMA_CTRL_DATA_SIZE(sz) ((sz)<<2)
#define DMA_CTRL_INCR_READ (1<<4)
#define DMA_CTRL_INCR_WRITE (1<<5)
#define _DMA_CTRL_RING_BITS(b) ((b)<<6)
#define _DMA_CTRL_RING_RD (0)
#define _DMA_CTRL_RING_WR (1<<10)
#define DMA_CTRL_RING(rdwr, bits) (_DMA_CTRL_RING_##rdwr | _DMA_CTRL_RING_BITS(bits))
#define DMA_CTRL_CHAIN_TO(ch) ((ch)<<11)
#define DMA_CTRL_TREQ_SEL(dreq) ((dreq)<<15)
#define DMA_CTRL_IRQ_QUIET (1<<21)
#define DMA_CTRL_BSWAP (1<<22)
#define DMA_CTRL_SNIFF_EN (1<<23)
/* Utilities to build control blocks that write to other control blocks *******/
#define DMA_CHAN_WR_XFER_COUNT(TYP) \
(sizeof(TYP)/4)
#define DMA_CHAN_WR_CTRL(TYP) ( DMA_CTRL_DATA_SIZE(DMA_SIZE_32) \
| DMA_CTRL_INCR_WRITE \
| DMA_CTRL_RING(WR, LM_FLOORLOG2(sizeof(TYP))) \
)
#endif /* _LIBHW_CR_RP2040_DMA_H_ */
|