summaryrefslogtreecommitdiff
path: root/libhw/rp2040_dma.h
blob: e4b44ff4a9453db2526685773676ba6d43704421 (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
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
/* libhw/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_RP2040_DMA_H_
#define _LIBHW_RP2040_DMA_H_

#include <assert.h>
#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_channel_hw_t, DMA_NUM_CHANNELS */

#include <libmisc/macro.h> /* for LM_FLOORLOG2() */

/* Borrowed from <hardware/dma.h> *********************************************/

static inline dma_channel_hw_t *dma_channel_hw_addr(uint channel) {
	assert(channel < NUM_DMA_CHANNELS);
	return &dma_hw->ch[channel];
}

enum dma_channel_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)
};

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

#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)

/*                    |          elem         |     val    |  name */
#define READ_ADDR   /*|*/volatile const void/*|*/    *   /*|*/read_addr
#define WRITE_ADDR  /*|*/volatile       void/*|*/    *   /*|*/write_addr
#define TRANS_COUNT /*|*/                   /*|*/uint32_t/*|*/trans_count
#define CTRL        /*|*/                   /*|*/uint32_t/*|*/ctrl

/*                { +0x0      ; +0x4        ; +0x8        ; +0xC (Trigger) */
struct dma_alias0 { READ_ADDR ; WRITE_ADDR  ; TRANS_COUNT ; CTRL        ; };
struct dma_alias1 { CTRL      ; READ_ADDR   ; WRITE_ADDR  ; TRANS_COUNT ; };
struct dma_alias2 { CTRL      ; TRANS_COUNT ; READ_ADDR   ; WRITE_ADDR  ; };
struct dma_alias3 { CTRL      ; WRITE_ADDR  ; TRANS_COUNT ; READ_ADDR   ; };
struct dma_alias0_short2                    { TRANS_COUNT ; CTRL        ; };
struct dma_alias1_short2                    { WRITE_ADDR  ; TRANS_COUNT ; };
struct dma_alias2_short2                    { READ_ADDR   ; WRITE_ADDR  ; };
struct dma_alias3_short2                    { TRANS_COUNT ; READ_ADDR   ; };
struct dma_alias0_short3                                  { CTRL        ; };
struct dma_alias1_short3                                  { TRANS_COUNT ; };
struct dma_alias2_short3                                  { WRITE_ADDR  ; };
struct dma_alias3_short3                                  { READ_ADDR   ; };

#undef CTRL
#undef TRANS_COUNT
#undef WRITE_ADDR
#undef READ_ADDR

#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_CHAN_WR_TRANS_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))) \
                              )

#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_trans_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_trans_count    struct dma_alias1
#define _DMA_TRIGGER_ctrl           struct dma_alias0

#endif /* _LIBHW_RP2040_DMA_H_ */