nrf52/
spi.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 SPI for NRF52 using EasyDMA.
6//!
7//! This file only implements support for the three SPI master (`SPIM`)
8//! peripherals, and not SPI slave (`SPIS`).
9//!
10//! Although `kernel::hil::spi::SpiMaster` is implemented for `SPIM`,
11//! only the functions marked with `x` are fully defined:
12//!
13//! * ✓ set_client
14//! * ✓ init
15//! * ✓ is_busy
16//! * ✓ read_write_bytes
17//! * write_byte
18//! * read_byte
19//! * read_write_byte
20//! * ✓ specify_chip_select
21//! * ✓ set_rate
22//! * ✓ get_rate
23//! * ✓ set_polarity
24//! * ✓ get_polarity
25//! * ✓ set_phase
26//! * ✓ get_phase
27//! * hold_low
28//! * release_low
29//!
30//! Author
31//! -------------------
32//!
33//! * Author: Jay Kickliter
34//! * Date: Sep 10, 2017
35
36use core::cell::Cell;
37use core::{cmp, ptr};
38use kernel::hil;
39use kernel::hil::gpio::Configure;
40use kernel::hil::spi::cs::ChipSelectPolar;
41use kernel::utilities::cells::{MapCell, OptionalCell, VolatileCell};
42use kernel::utilities::leasable_buffer::SubSliceMut;
43use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
44use kernel::utilities::registers::{register_bitfields, ReadWrite, WriteOnly};
45use kernel::utilities::StaticRef;
46use kernel::ErrorCode;
47use nrf5x::pinmux::Pinmux;
48
49const INSTANCES: [StaticRef<SpimRegisters>; 3] = unsafe {
50    [
51        StaticRef::new(0x40003000 as *const SpimRegisters),
52        StaticRef::new(0x40004000 as *const SpimRegisters),
53        StaticRef::new(0x40023000 as *const SpimRegisters),
54    ]
55};
56
57#[repr(C)]
58struct SpimRegisters {
59    _reserved0: [u8; 16],                            // reserved
60    tasks_start: WriteOnly<u32, TASK::Register>,     // Start SPI transaction
61    tasks_stop: WriteOnly<u32, TASK::Register>,      // Stop SPI transaction
62    _reserved1: [u8; 4],                             // reserved
63    tasks_suspend: WriteOnly<u32, TASK::Register>,   // Suspend SPI transaction
64    tasks_resume: WriteOnly<u32, TASK::Register>,    // Resume SPI transaction
65    _reserved2: [u8; 224],                           // reserved
66    events_stopped: ReadWrite<u32, EVENT::Register>, // SPI transaction has stopped
67    _reserved3: [u8; 8],                             // reserved
68    events_endrx: ReadWrite<u32, EVENT::Register>,   // End of RXD buffer reached
69    _reserved4: [u8; 4],                             // reserved
70    events_end: ReadWrite<u32, EVENT::Register>,     // End of RXD buffer and TXD buffer reached
71    _reserved5: [u8; 4],                             // reserved
72    events_endtx: ReadWrite<u32, EVENT::Register>,   // End of TXD buffer reached
73    _reserved6: [u8; 40],                            // reserved
74    events_started: ReadWrite<u32, EVENT::Register>, // Transaction started
75    _reserved7: [u8; 176],                           // reserved
76    shorts: ReadWrite<u32>,                          // Shortcut register
77    _reserved8: [u8; 256],                           // reserved
78    intenset: ReadWrite<u32, INTE::Register>,        // Enable interrupt
79    intenclr: ReadWrite<u32, INTE::Register>,        // Disable interrupt
80    _reserved9: [u8; 500],                           // reserved
81    enable: ReadWrite<u32, ENABLE::Register>,        // Enable SPIM
82    _reserved10: [u8; 4],                            // reserved
83    psel_sck: VolatileCell<Pinmux>,                  // Pin select for SCK
84    psel_mosi: VolatileCell<Pinmux>,                 // Pin select for MOSI signal
85    psel_miso: VolatileCell<Pinmux>,                 // Pin select for MISO signal
86    _reserved11: [u8; 16],                           // reserved
87    frequency: ReadWrite<u32>,                       // SPI frequency
88    _reserved12: [u8; 12],                           // reserved
89    rxd_ptr: VolatileCell<*mut u8>,                  // Data pointer
90    rxd_maxcnt: ReadWrite<u32, MAXCNT::Register>,    // Maximum number of bytes in receive buffer
91    rxd_amount: ReadWrite<u32>,                      // Number of bytes transferred
92    rxd_list: ReadWrite<u32>,                        // EasyDMA list type
93    txd_ptr: VolatileCell<*const u8>,                // Data pointer
94    txd_maxcnt: ReadWrite<u32, MAXCNT::Register>,    // Maximum number of bytes in transmit buffer
95    txd_amount: ReadWrite<u32>,                      // Number of bytes transferred
96    txd_list: ReadWrite<u32>,                        // EasyDMA list type
97    config: ReadWrite<u32, CONFIG::Register>,        // Configuration register
98    _reserved13: [u8; 104],                          // reserved
99    orc: ReadWrite<u32>,                             // Over-read character.
100}
101
102register_bitfields![u32,
103    INTE [
104        /// Write '1' to Enable interrupt on EVENTS_STOPPED event
105        STOPPED OFFSET(1) NUMBITS(1) [
106            /// Read: Disabled
107            ReadDisabled = 0,
108            /// Enable
109            Enable = 1
110        ],
111        /// Write '1' to Enable interrupt on EVENTS_ENDRX event
112        ENDRX OFFSET(4) NUMBITS(1) [
113            /// Read: Disabled
114            ReadDisabled = 0,
115            /// Enable
116            Enable = 1
117        ],
118        /// Write '1' to Enable interrupt on EVENTS_END event
119        END OFFSET(6) NUMBITS(1) [
120            /// Read: Disabled
121            ReadDisabled = 0,
122            /// Enable
123            Enable = 1
124        ],
125        /// Write '1' to Enable interrupt on EVENTS_ENDTX event
126        ENDTX OFFSET(8) NUMBITS(1) [
127            /// Read: Disabled
128            ReadDisabled = 0,
129            /// Enable
130            Enable = 1
131        ],
132        /// Write '1' to Enable interrupt on EVENTS_STARTED event
133        STARTED OFFSET(19) NUMBITS(1) [
134            /// Read: Disabled
135            ReadDisabled = 0,
136            /// Enable
137            Enable = 1
138        ]
139    ],
140    MAXCNT [
141        /// Maximum number of bytes in buffer
142        MAXCNT OFFSET(0) NUMBITS(16)
143    ],
144    CONFIG [
145        /// Bit order
146        ORDER OFFSET(0) NUMBITS(1) [
147            /// Most significant bit shifted out first
148            MostSignificantBitShiftedOutFirst = 0,
149            /// Least significant bit shifted out first
150            LeastSignificantBitShiftedOutFirst = 1
151        ],
152        /// Serial clock (SCK) phase
153        CPHA OFFSET(1) NUMBITS(1) [
154            /// Sample on leading edge of clock, shift serial data on trailing edge
155            SampleOnLeadingEdge = 0,
156            /// Sample on trailing edge of clock, shift serial data on leading edge
157            SampleOnTrailingEdge = 1
158        ],
159        /// Serial clock (SCK) polarity
160        CPOL OFFSET(2) NUMBITS(1) [
161            /// Active high
162            ActiveHigh = 0,
163            /// Active low
164            ActiveLow = 1
165        ]
166    ],
167    ENABLE [
168        ENABLE OFFSET(0) NUMBITS(4) [
169            Disable = 0,
170            Enable = 7
171        ]
172    ],
173    EVENT [
174        EVENT 0
175    ],
176    TASK [
177        TASK 0
178    ]
179];
180
181/// An enum representing all allowable `frequency` register values.
182#[repr(u32)]
183#[derive(Copy, Clone)]
184pub enum Frequency {
185    K125 = 0x02000000,
186    K250 = 0x04000000,
187    K500 = 0x08000000,
188    M1 = 0x10000000,
189    M2 = 0x20000000,
190    M4 = 0x40000000,
191    M8 = 0x80000000,
192}
193
194impl Frequency {
195    pub fn from_register(reg: u32) -> Option<Frequency> {
196        match reg {
197            0x02000000 => Some(Frequency::K125),
198            0x04000000 => Some(Frequency::K250),
199            0x08000000 => Some(Frequency::K500),
200            0x10000000 => Some(Frequency::M1),
201            0x20000000 => Some(Frequency::M2),
202            0x40000000 => Some(Frequency::M4),
203            0x80000000 => Some(Frequency::M8),
204            _ => None,
205        }
206    }
207
208    pub fn into_spi_rate(&self) -> u32 {
209        match *self {
210            Frequency::K125 => 125_000,
211            Frequency::K250 => 250_000,
212            Frequency::K500 => 500_000,
213            Frequency::M1 => 1_000_000,
214            Frequency::M2 => 2_000_000,
215            Frequency::M4 => 4_000_000,
216            Frequency::M8 => 8_000_000,
217        }
218    }
219
220    pub fn from_spi_rate(freq: u32) -> Frequency {
221        if freq < 250_000 {
222            Frequency::K125
223        } else if freq < 500_000 {
224            Frequency::K250
225        } else if freq < 1_000_000 {
226            Frequency::K500
227        } else if freq < 2_000_000 {
228            Frequency::M1
229        } else if freq < 4_000_000 {
230            Frequency::M2
231        } else if freq < 8_000_000 {
232            Frequency::M4
233        } else {
234            Frequency::M8
235        }
236    }
237}
238
239/// A SPI master device.
240///
241/// A `SPIM` instance wraps a `registers::spim::SPIM` together with
242/// addition data necessary to implement an asynchronous interface.
243pub struct SPIM<'a> {
244    registers: StaticRef<SpimRegisters>,
245    client: OptionalCell<&'a dyn hil::spi::SpiMasterClient>,
246    chip_select: OptionalCell<ChipSelectPolar<'a, crate::gpio::GPIOPin<'a>>>,
247    busy: Cell<bool>,
248    tx_buf: MapCell<SubSliceMut<'static, u8>>,
249    rx_buf: MapCell<SubSliceMut<'static, u8>>,
250    transfer_len: Cell<usize>,
251}
252
253impl<'a> SPIM<'a> {
254    pub const fn new(instance: usize) -> SPIM<'a> {
255        SPIM {
256            registers: INSTANCES[instance],
257            client: OptionalCell::empty(),
258            chip_select: OptionalCell::empty(),
259            busy: Cell::new(false),
260            tx_buf: MapCell::empty(),
261            rx_buf: MapCell::empty(),
262            transfer_len: Cell::new(0),
263        }
264    }
265
266    #[inline(never)]
267    pub fn handle_interrupt(&self) {
268        if self.registers.events_end.is_set(EVENT::EVENT) {
269            // End of RXD buffer and TXD buffer reached
270
271            if self.chip_select.is_none() {
272                debug_assert!(false, "Invariant violated. Chip-select must be Some.");
273                return;
274            }
275
276            self.chip_select.map(|cs| cs.deactivate());
277            self.registers.events_end.write(EVENT::EVENT::CLEAR);
278
279            // When we are no longer active or busy we can disable the
280            // peripheral.
281            self.disable();
282            self.busy.set(false);
283
284            self.client.map(|client| match self.tx_buf.take() {
285                None => (),
286                Some(tx_buf) => {
287                    client.read_write_done(tx_buf, self.rx_buf.take(), Ok(self.transfer_len.get()))
288                }
289            });
290        }
291
292        // Although we only configured the chip interrupt on the
293        // above 'end' event, the other event fields also get set by
294        // the chip. Let's clear those flags.
295
296        if self.registers.events_stopped.is_set(EVENT::EVENT) {
297            // SPI transaction has stopped
298            self.registers.events_stopped.write(EVENT::EVENT::CLEAR);
299        }
300
301        if self.registers.events_endrx.is_set(EVENT::EVENT) {
302            // End of RXD buffer reached
303            self.registers.events_endrx.write(EVENT::EVENT::CLEAR);
304        }
305
306        if self.registers.events_endtx.is_set(EVENT::EVENT) {
307            // End of TXD buffer reached
308            self.registers.events_endtx.write(EVENT::EVENT::CLEAR);
309        }
310
311        if self.registers.events_started.is_set(EVENT::EVENT) {
312            // Transaction started
313            self.registers.events_started.write(EVENT::EVENT::CLEAR);
314        }
315    }
316
317    /// Configures an already constructed `SPIM`.
318    pub fn configure(&self, mosi: Pinmux, miso: Pinmux, sck: Pinmux) {
319        self.registers.psel_mosi.set(mosi);
320        self.registers.psel_miso.set(miso);
321        self.registers.psel_sck.set(sck);
322    }
323
324    /// Enables `SPIM` peripheral.
325    pub fn enable(&self) {
326        self.registers.enable.write(ENABLE::ENABLE::Enable);
327    }
328
329    /// Disables `SPIM` peripheral.
330    pub fn disable(&self) {
331        self.registers.enable.write(ENABLE::ENABLE::Disable);
332    }
333
334    pub fn is_enabled(&self) -> bool {
335        self.registers.enable.matches_all(ENABLE::ENABLE::Enable)
336    }
337}
338
339impl<'a> hil::spi::SpiMaster<'a> for SPIM<'a> {
340    type ChipSelect = ChipSelectPolar<'a, crate::gpio::GPIOPin<'a>>;
341
342    fn set_client(&self, client: &'a dyn hil::spi::SpiMasterClient) {
343        self.client.set(client);
344    }
345
346    fn init(&self) -> Result<(), ErrorCode> {
347        Ok(())
348    }
349
350    fn is_busy(&self) -> bool {
351        self.busy.get()
352    }
353
354    fn read_write_bytes(
355        &self,
356        tx_buf: SubSliceMut<'static, u8>,
357        rx_buf: Option<SubSliceMut<'static, u8>>,
358    ) -> Result<
359        (),
360        (
361            ErrorCode,
362            SubSliceMut<'static, u8>,
363            Option<SubSliceMut<'static, u8>>,
364        ),
365    > {
366        debug_assert!(!self.busy.get());
367        debug_assert!(self.tx_buf.is_none());
368        debug_assert!(self.rx_buf.is_none());
369
370        // Clear (set to low) chip-select
371        if self.chip_select.is_none() {
372            return Err((ErrorCode::NODEVICE, tx_buf, rx_buf));
373        }
374        self.chip_select.map(|cs| cs.activate());
375
376        // Setup transmit data registers
377        let tx_len: u32 = tx_buf.len() as u32;
378        self.registers.txd_ptr.set(tx_buf.as_ptr());
379        self.registers.txd_maxcnt.write(MAXCNT::MAXCNT.val(tx_len));
380        self.tx_buf.replace(tx_buf);
381
382        // Setup receive data registers
383        match rx_buf {
384            None => {
385                self.registers.rxd_ptr.set(ptr::null_mut());
386                self.registers.rxd_maxcnt.write(MAXCNT::MAXCNT.val(0));
387                self.transfer_len.set(tx_len as usize);
388                self.rx_buf.take();
389            }
390            Some(mut buf) => {
391                self.registers.rxd_ptr.set(buf.as_mut_ptr());
392                let rx_len: u32 = buf.len() as u32;
393                self.registers.rxd_maxcnt.write(MAXCNT::MAXCNT.val(rx_len));
394                self.transfer_len.set(cmp::min(tx_len, rx_len) as usize);
395                self.rx_buf.put(buf);
396            }
397        }
398
399        // Start the transfer
400        self.busy.set(true);
401
402        // Start and enable the SPIM peripheral. The SPIM peripheral is only
403        // enabled when the busy flag is set.
404        self.registers.intenset.write(INTE::END::Enable);
405        self.enable();
406
407        self.registers.tasks_start.write(TASK::TASK::SET);
408        Ok(())
409    }
410
411    fn write_byte(&self, _val: u8) -> Result<(), ErrorCode> {
412        unimplemented!("SPI: Use `read_write_bytes()` instead.");
413    }
414
415    fn read_byte(&self) -> Result<u8, ErrorCode> {
416        unimplemented!("SPI: Use `read_write_bytes()` instead.");
417    }
418
419    fn read_write_byte(&self, _val: u8) -> Result<u8, ErrorCode> {
420        unimplemented!("SPI: Use `read_write_bytes()` instead.");
421    }
422
423    // Tell the SPI peripheral what to use as a chip select pin.
424    // The type of the argument is based on what makes sense for the
425    // peripheral when this trait is implemented.
426    fn specify_chip_select(&self, cs: Self::ChipSelect) -> Result<(), ErrorCode> {
427        cs.pin.make_output();
428        cs.deactivate();
429        self.chip_select.set(cs);
430        Ok(())
431    }
432
433    // Returns the actual rate set
434    fn set_rate(&self, rate: u32) -> Result<u32, ErrorCode> {
435        let f = Frequency::from_spi_rate(rate);
436        self.registers.frequency.set(f as u32);
437        Ok(f.into_spi_rate())
438    }
439
440    fn get_rate(&self) -> u32 {
441        // Reset value is a valid frequency (250kbps), so .expect
442        // should be safe here
443        let f = Frequency::from_register(self.registers.frequency.get()).unwrap(); // Unwrap fail = nrf52 unknown spi rate
444        f.into_spi_rate()
445    }
446
447    fn set_polarity(&self, polarity: hil::spi::ClockPolarity) -> Result<(), ErrorCode> {
448        let new_polarity = match polarity {
449            hil::spi::ClockPolarity::IdleLow => CONFIG::CPOL::ActiveHigh,
450            hil::spi::ClockPolarity::IdleHigh => CONFIG::CPOL::ActiveLow,
451        };
452        self.registers.config.modify(new_polarity);
453        Ok(())
454    }
455
456    fn get_polarity(&self) -> hil::spi::ClockPolarity {
457        match self.registers.config.read(CONFIG::CPOL) {
458            0 => hil::spi::ClockPolarity::IdleLow,
459            1 => hil::spi::ClockPolarity::IdleHigh,
460            _ => unreachable!(),
461        }
462    }
463
464    fn set_phase(&self, phase: hil::spi::ClockPhase) -> Result<(), ErrorCode> {
465        let new_phase = match phase {
466            hil::spi::ClockPhase::SampleLeading => CONFIG::CPHA::SampleOnLeadingEdge,
467            hil::spi::ClockPhase::SampleTrailing => CONFIG::CPHA::SampleOnTrailingEdge,
468        };
469        self.registers.config.modify(new_phase);
470        Ok(())
471    }
472
473    fn get_phase(&self) -> hil::spi::ClockPhase {
474        match self.registers.config.read(CONFIG::CPHA) {
475            0 => hil::spi::ClockPhase::SampleLeading,
476            1 => hil::spi::ClockPhase::SampleTrailing,
477            _ => unreachable!(),
478        }
479    }
480
481    // The following two trait functions are not implemented for
482    // SAM4L, and appear to not provide much functionality. Let's not
483    // bother implementing them unless needed.
484    fn hold_low(&self) {
485        unimplemented!("SPI: Use `read_write_bytes()` instead.");
486    }
487
488    fn release_low(&self) {
489        unimplemented!("SPI: Use `read_write_bytes()` instead.");
490    }
491}