sam4l/
dma.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Implementation of the PDCA DMA peripheral.
6
7use crate::pm;
8use core::cell::Cell;
9use core::cmp;
10use core::sync::atomic;
11use kernel::utilities::cells::VolatileCell;
12use kernel::utilities::cells::{OptionalCell, TakeCell};
13use kernel::utilities::registers::interfaces::{Readable, Writeable};
14use kernel::utilities::registers::{register_bitfields, ReadOnly, ReadWrite, WriteOnly};
15use kernel::utilities::StaticRef;
16
17/// Memory registers for a DMA channel. Section 16.6.1 of the datasheet.
18#[repr(C)]
19#[allow(dead_code)]
20struct DMARegisters {
21    mar: ReadWrite<u32, MemoryAddress::Register>,
22    psr: VolatileCell<DMAPeripheral>,
23    _psr_padding: [u8; 3],
24    tcr: ReadWrite<u32, TransferCounter::Register>,
25    marr: ReadWrite<u32, MemoryAddressReload::Register>,
26    tcrr: ReadWrite<u32, TransferCounter::Register>,
27    cr: WriteOnly<u32, Control::Register>,
28    mr: ReadWrite<u32, Mode::Register>,
29    sr: ReadOnly<u32, Status::Register>,
30    ier: WriteOnly<u32, Interrupt::Register>,
31    idr: WriteOnly<u32, Interrupt::Register>,
32    imr: ReadOnly<u32, Interrupt::Register>,
33    isr: ReadOnly<u32, Interrupt::Register>,
34}
35
36register_bitfields![u32,
37    MemoryAddress [
38        MADDR OFFSET(0) NUMBITS(32) []
39    ],
40
41    MemoryAddressReload [
42        MARV OFFSET(0) NUMBITS(32) []
43    ],
44
45    TransferCounter [
46        /// Transfer Counter Value
47        TCV OFFSET(0) NUMBITS(16) []
48    ],
49
50    Control [
51        /// Transfer Error Clear
52        ECLR 8,
53        /// Transfer Disable
54        TDIS 1,
55        /// Transfer Enable
56        TEN 0
57    ],
58
59    Mode [
60        /// Ring Buffer
61        RING OFFSET(3) NUMBITS(1) [
62            Disable = 0,
63            Enable = 1
64        ],
65        /// Event Trigger
66        ETRIG OFFSET(2) NUMBITS(1) [
67            StartOnRequest = 0,
68            StartOnEvent = 1
69        ],
70        /// Size of Transfer
71        SIZE OFFSET(0) NUMBITS(2) [
72            Byte = 0,
73            Halfword = 1,
74            Word = 2
75        ]
76    ],
77
78    Status [
79        /// Transfer Enabled
80        TEN 0
81    ],
82
83    Interrupt [
84        /// Transfer Error
85        TERR 2,
86        /// Transfer Complete
87        TRC 1,
88        /// Reload Counter Zero
89        RCZ 0
90    ]
91];
92
93/// The PDCA's base addresses in memory (Section 7.1 of manual).
94const DMA_BASE_ADDR: usize = 0x400A2000;
95
96/// The number of bytes between each memory mapped DMA Channel (Section 16.6.1).
97const DMA_CHANNEL_SIZE: usize = 0x40;
98
99/// Shared counter that Keeps track of how many DMA channels are currently
100/// active.
101static NUM_ENABLED: atomic::AtomicUsize = atomic::AtomicUsize::new(0);
102
103/// The DMA channel number.
104///
105/// Each channel transfers data between memory and a
106/// particular peripheral function (e.g., SPI read or SPI write, but not both
107/// simultaneously). There are 16 available channels (Section 16.7).
108#[derive(Copy, Clone)]
109pub enum DMAChannelNum {
110    // Relies on the fact that assigns values 0-15 to each constructor in order
111    DMAChannel00 = 0,
112    DMAChannel01 = 1,
113    DMAChannel02 = 2,
114    DMAChannel03 = 3,
115    DMAChannel04 = 4,
116    DMAChannel05 = 5,
117    DMAChannel06 = 6,
118    DMAChannel07 = 7,
119    DMAChannel08 = 8,
120    DMAChannel09 = 9,
121    DMAChannel10 = 10,
122    DMAChannel11 = 11,
123    DMAChannel12 = 12,
124    DMAChannel13 = 13,
125    DMAChannel14 = 14,
126    DMAChannel15 = 15,
127}
128
129/// The peripheral function a channel is assigned to (Section 16.7). `*_RX`
130/// means transfer data from peripheral to memory, `*_TX` means transfer data
131/// from memory to peripheral.
132#[allow(non_camel_case_types)]
133#[derive(Copy, Clone, PartialEq)]
134#[repr(u8)]
135pub enum DMAPeripheral {
136    USART0_RX = 0,
137    USART1_RX = 1,
138    USART2_RX = 2,
139    USART3_RX = 3,
140    SPI_RX = 4,
141    TWIM0_RX = 5,
142    TWIM1_RX = 6,
143    TWIM2_RX = 7,
144    TWIM3_RX = 8,
145    TWIS0_RX = 9,
146    TWIS1_RX = 10,
147    ADCIFE_RX = 11,
148    CATB_RX = 12,
149    IISC_CH0_RX = 14,
150    IISC_CH1_RX = 15,
151    PARC_RX = 16,
152    AESA_RX = 17,
153    USART0_TX = 18,
154    USART1_TX = 19,
155    USART2_TX = 20,
156    USART3_TX = 21,
157    SPI_TX = 22,
158    TWIM0_TX = 23,
159    TWIM1_TX = 24,
160    TWIM2_TX = 25,
161    TWIM3_TX = 26,
162    TWIS0_TX = 27,
163    TWIS1_TX = 28,
164    ADCIFE_TX = 29,
165    CATB_TX = 30,
166    ABDACB_SDR0_TX = 31,
167    ABDACB_SDR1_TX = 32,
168    IISC_CH0_TX = 33,
169    IISC_CH1_TX = 34,
170    DACC_TX = 35,
171    AESA_TX = 36,
172    LCDCA_ACMDR_TX = 37,
173    LCDCA_ABMDR_TX = 38,
174}
175
176#[derive(Copy, Clone, Debug, PartialEq)]
177#[repr(u8)]
178pub enum DMAWidth {
179    ///  DMA is acting on bytes
180    Width8Bit = 0,
181    /// DMA is acting on halfwords
182    Width16Bit = 1,
183    /// DMA is acting on words
184    Width32Bit = 2,
185}
186
187pub struct DMAChannel {
188    registers: StaticRef<DMARegisters>,
189    client: OptionalCell<&'static dyn DMAClient>,
190    width: Cell<DMAWidth>,
191    enabled: Cell<bool>,
192    buffer: TakeCell<'static, [u8]>,
193}
194
195pub trait DMAClient {
196    fn transfer_done(&self, pid: DMAPeripheral);
197}
198
199impl DMAChannel {
200    pub fn new(channel: DMAChannelNum) -> DMAChannel {
201        DMAChannel {
202            registers: unsafe {
203                StaticRef::new(
204                    (DMA_BASE_ADDR + (channel as usize) * DMA_CHANNEL_SIZE) as *const DMARegisters,
205                )
206            },
207            client: OptionalCell::empty(),
208            width: Cell::new(DMAWidth::Width8Bit),
209            enabled: Cell::new(false),
210            buffer: TakeCell::empty(),
211        }
212    }
213
214    pub fn initialize(&self, client: &'static dyn DMAClient, width: DMAWidth) {
215        self.client.set(client);
216        self.width.set(width);
217    }
218
219    pub fn enable(&self) {
220        pm::enable_clock(pm::Clock::HSB(pm::HSBClock::PDCA));
221        pm::enable_clock(pm::Clock::PBB(pm::PBBClock::PDCA));
222
223        if !self.enabled.get() {
224            NUM_ENABLED.fetch_add(1, atomic::Ordering::Relaxed);
225
226            // Disable all interrupts
227            self.registers
228                .idr
229                .write(Interrupt::TERR::SET + Interrupt::TRC::SET + Interrupt::RCZ::SET);
230
231            self.enabled.set(true);
232        }
233    }
234
235    pub fn disable(&self) {
236        if self.enabled.get() {
237            let num_enabled = NUM_ENABLED.fetch_sub(1, atomic::Ordering::Relaxed);
238            if num_enabled == 1 {
239                pm::disable_clock(pm::Clock::HSB(pm::HSBClock::PDCA));
240                pm::disable_clock(pm::Clock::PBB(pm::PBBClock::PDCA));
241            }
242            self.registers.cr.write(Control::TDIS::SET);
243            self.enabled.set(false);
244        }
245    }
246
247    pub fn is_enabled(&self) -> bool {
248        self.enabled.get()
249    }
250
251    pub fn handle_interrupt(&self) {
252        self.registers
253            .idr
254            .write(Interrupt::TERR::SET + Interrupt::TRC::SET + Interrupt::RCZ::SET);
255        let channel = self.registers.psr.get();
256
257        self.client.map(|client| {
258            client.transfer_done(channel);
259        });
260    }
261
262    pub fn start_transfer(&self) {
263        self.registers.cr.write(Control::TEN::SET);
264    }
265
266    pub fn prepare_transfer(&self, pid: DMAPeripheral, buf: &'static mut [u8], mut len: usize) {
267        // TODO(alevy): take care of zero length case
268
269        let maxlen = buf.len()
270            / match self.width.get() {
271                DMAWidth::Width8Bit => 1,
272                DMAWidth::Width16Bit => 2,
273                DMAWidth::Width32Bit => 4,
274            };
275        len = cmp::min(len, maxlen);
276        self.registers
277            .mr
278            .write(Mode::SIZE.val(self.width.get() as u32));
279
280        self.registers.psr.set(pid);
281        self.registers
282            .marr
283            .write(MemoryAddressReload::MARV.val(core::ptr::from_ref::<u8>(&buf[0]) as u32));
284        self.registers
285            .tcrr
286            .write(TransferCounter::TCV.val(len as u32));
287
288        self.registers.ier.write(Interrupt::TRC::SET);
289
290        // Store the buffer reference in the TakeCell so it can be returned to
291        // the caller in `handle_interrupt`
292        self.buffer.replace(buf);
293    }
294
295    pub fn do_transfer(&self, pid: DMAPeripheral, buf: &'static mut [u8], len: usize) {
296        self.prepare_transfer(pid, buf, len);
297        self.start_transfer();
298    }
299
300    /// Aborts any current transactions and returns the buffer used in the
301    /// transaction.
302    pub fn abort_transfer(&self) -> Option<&'static mut [u8]> {
303        self.registers
304            .idr
305            .write(Interrupt::TERR::SET + Interrupt::TRC::SET + Interrupt::RCZ::SET);
306
307        // Reset counter
308        self.registers.tcr.write(TransferCounter::TCV.val(0));
309
310        self.buffer.take()
311    }
312
313    pub fn transfer_counter(&self) -> usize {
314        self.registers.tcr.read(TransferCounter::TCV) as usize
315    }
316}