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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
//! DMA Controller
use crate::int::InterruptSource;
use crate::pac::{DMAC, DMAC0, DMAC1, DMAC2, DMAC3};
use enumflags2::{bitflags, BitFlags};
use mips_mcu::PhysicalAddress;
/// Interrupt flag or enable bits related to a specific DMA channel
#[bitflags]
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(u8)]
pub enum DmaIrq {
/// Channel Source Done
CHSD = 0x80,
/// Channel Source Half Empty
CHSH = 0x40,
/// Channel Destination Done
CHDD = 0x20,
/// Channel Destination Half Full
CHDH = 0x10,
/// Channel Block Transfer Complete
CHBC = 0x08,
/// Channel Cell Transfer Complete
CHCC = 0x04,
/// Channel Transfer Abort
CHTA = 0x02,
/// Channel Address Error
CHER = 0x01,
}
/// indicates whether the channel shall be automatically enabled after a block
/// transfer
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum XferMode {
OneShot,
Auto,
}
/// DMA Operations
///
/// This trait defines operations that can be carried out by a DMAChannel
pub trait Ops {
/// Set source address and size of source block in bytes
fn set_source(&mut self, addr: PhysicalAddress, size: usize);
/// Set destination address and size of destination block in bytes
fn set_dest(&mut self, addr: PhysicalAddress, size: usize);
/// Set cell size, i.e. number of bytes transferred per triggering event
fn set_cell_size(&mut self, size: usize);
/// Set start event source for triggering a cell transfer
fn set_start_event(&mut self, event: Option<InterruptSource>);
/// Set event for aborting a transfer
fn set_abort_event(&mut self, event: Option<InterruptSource>);
/// Set data pattern that aborts a transfer
fn set_abort_pattern(&mut self, pattern: Option<u8>);
/// Get source pointer
fn source_pointer(&self) -> PhysicalAddress;
/// Get destination pointer
fn destination_pointer(&self) -> PhysicalAddress;
/// Enable/disable individual interrupt sources
///
/// This function does not configure the interrupt controller.
fn irq_enable(&mut self, irq: BitFlags<DmaIrq>);
/// Get interrupt flags
fn irq_flags(&self) -> BitFlags<DmaIrq>;
/// Set interrupt flags
fn set_irq_flags(&mut self, flags: BitFlags<DmaIrq>);
/// Clear all interrupt flags
fn clear_all_irq_flags(&mut self) {
self.set_irq_flags(BitFlags::<DmaIrq>::default());
}
/// Enable DMA channel
///
/// # Safety
///
/// Unsafe because the DMA controller will access the memory blocks
/// specified as source and destination without any checks
unsafe fn enable(&mut self, mode: XferMode);
/// Check if a DMA channel is enabled
///
/// Can be used to poll OneShot channels for completion.
fn is_enabled(&self) -> bool;
/// Disable a DMA channel
fn disable(&mut self);
/// Force a cell transfer
fn force(&mut self);
}
pub struct DmaChannel<D> {
ch: D,
}
macro_rules! dma {
($Id:ident, $Dmac:ident) => {
impl Ops for DmaChannel<$Dmac> {
fn set_source(&mut self, addr: PhysicalAddress, size: usize) {
unsafe {
self.ch.ssa.write(|w| w.bits(addr.address() as u32));
self.ch.ssiz.write(|w| w.bits(size as u32));
}
}
fn set_dest(&mut self, addr: PhysicalAddress, size: usize) {
unsafe {
self.ch.dsa.write(|w| w.bits(addr.address() as u32));
self.ch.dsiz.write(|w| w.bits(size as u32));
}
}
fn set_cell_size(&mut self, size: usize) {
unsafe {
self.ch.csiz.write(|w| w.bits(size as u32));
}
}
fn set_start_event(&mut self, event: Option<InterruptSource>) {
match event {
Some(e) => {
self.ch
.econ
.modify(|_, w| unsafe { w.chsirq().bits(e as u8).sirqen().bit(true) });
}
None => {
self.ch.econclr.write(|w| w.sirqen().bit(true));
}
}
}
fn set_abort_event(&mut self, event: Option<InterruptSource>) {
match event {
Some(e) => {
self.ch
.econ
.modify(|_, w| unsafe { w.chairq().bits(e as u8).airqen().bit(true) });
}
None => {
self.ch.econclr.write(|w| w.airqen().bit(true));
}
}
}
fn set_abort_pattern(&mut self, pattern: Option<u8>) {
match pattern {
Some(p) => {
self.ch.dat.write(|w| unsafe { w.dchpdat().bits(p) });
self.ch.econset.write(|w| w.paten().bit(true));
}
None => {
self.ch.econclr.write(|w| w.paten().bit(true));
}
}
}
fn source_pointer(&self) -> PhysicalAddress {
PhysicalAddress::from_usize(self.ch.sptr.read().bits() as usize)
}
fn destination_pointer(&self) -> PhysicalAddress {
PhysicalAddress::from_usize(self.ch.dptr.read().bits() as usize)
}
fn irq_enable(&mut self, irq: BitFlags<DmaIrq>) {
self.ch
.int
.write(|w| unsafe { w.bits((irq.bits() as u32) << 16) });
}
fn irq_flags(&self) -> BitFlags<DmaIrq> {
let int = self.ch.int.read().bits();
unsafe { BitFlags::from_bits_unchecked(int as u8) }
}
fn set_irq_flags(&mut self, flags: BitFlags<DmaIrq>) {
self.ch.int.modify(|r, w| unsafe {
w.bits((r.bits() & 0xffff_ff00) | flags.bits() as u32)
});
}
unsafe fn enable(&mut self, mode: XferMode) {
match mode {
XferMode::OneShot => self.ch.contclr.write(|w| w.chaen().bit(true)),
XferMode::Auto => self.ch.contset.write(|w| w.chaen().bit(true)),
}
self.ch.contset.write(|w| w.chen().bit(true));
}
fn is_enabled(&self) -> bool {
self.ch.cont.read().chen().bit()
}
fn disable(&mut self) {
self.ch.contclr.write(|w| w.chen().bit(true));
while self.ch.cont.read().chbusy().bit() {}
}
fn force(&mut self) {
self.ch.econset.write(|w| w.cforce().bit(true));
}
}
impl DmaChannel<$Dmac> {
pub fn $Id(ch: $Dmac) -> Self {
// make sure that the DMA controller is enabled
unsafe {
(*DMAC::ptr()).dmaconset.write(|w| w.on().bit(true));
}
DmaChannel { ch }
}
}
};
}
dma!(channel0, DMAC0);
dma!(channel1, DMAC1);
dma!(channel2, DMAC2);
dma!(channel3, DMAC3);