rp2040/
pio_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 2025.
4//
5// Author: Jason Hu <jasonhu2026@u.northwestern.edu>
6//         Anthony Alvarez <anthonyalvarez2026@u.northwestern.edu>
7
8//! SPI using the Programmable Input Output (PIO) hardware.
9use crate::clocks::{self};
10use crate::pio::{Pio, PioRxClient, PioTxClient, SMNumber, StateMachineConfiguration};
11use core::cell::Cell;
12use kernel::deferred_call::{DeferredCall, DeferredCallClient};
13use kernel::hil::spi::cs::{ChipSelectPolar, Polarity};
14use kernel::hil::spi::SpiMasterClient;
15use kernel::hil::spi::{ClockPhase, ClockPolarity};
16use kernel::utilities::cells::MapCell;
17use kernel::utilities::cells::OptionalCell;
18use kernel::utilities::leasable_buffer::SubSliceMut;
19use kernel::{hil, ErrorCode};
20
21// Since auto push / pull is set to 8 for the purposes of writing in bytes
22// rather than words, values read in have to be bitshifted by 24
23const AUTOPULL_SHIFT: usize = 24;
24
25// Frequency of system clock, for rate changes
26const SYSCLOCK_FREQ: u32 = 125_000_000;
27
28// The following programs are in PIO asm
29// SPI_CPHA0 and SPI_CPHA1 are sourced from pico examples
30// https://github.com/raspberrypi/pico-examples/blob/master/pio/spi/spi.pio
31//
32// For the idle high clock programs, we took inspiration of how Zephyr did it
33// which was the simple change of swapping when the side set pin outputs 0 or 1
34// https://github.com/zephyrproject-rtos/zephyr/blob/main/drivers/spi/spi_rpi_pico_pio.c
35//
36// for further reference consult the RP2040 datasheet chapter 3 (especially sections 3.4 and 3.6)
37// https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf
38//
39// One can compile pioasm programs locally using the official pico sdk,
40// Or you can use the following website and copy the hex output
41// https://wokwi.com/tools/pioasm
42
43// Leading edge clock phase + Idle low clock
44const SPI_CPHA0: [u16; 2] = [
45    0x6101, /*  0: out    pins, 1         side 0 [1] */
46    0x5101, /*  1: in     pins, 1         side 1 [1] */
47];
48
49// Trailing edge clock phase + Idle low clock
50const SPI_CPHA1: [u16; 3] = [
51    0x6021, /* 0: out    x, 1            side 0 */
52    0xb101, /* 1: mov    pins, x         side 1 [1] */
53    0x4001, /* 2: in     pins, 1         side 0 */
54];
55
56// Leading edge clock phase + Idle high clock
57const SPI_CPHA0_HIGH_CLOCK: [u16; 2] = [
58    0x7101, /*  0: out    pins, 1         side 1 [1] */
59    0x4101, /*  1: in     pins, 1         side 0 [1] */
60];
61
62// Trailing edge clock phase + Idle high clock
63const SPI_CPHA1_HIGH_CLOCK: [u16; 3] = [
64    0x7021, /*  0: out    x, 1            side 1 */
65    0xa101, /*  1: mov    pins, x         side 0 [1] */
66    0x5001, /*  2: in     pins, 1         side 1 */
67];
68
69/*
70Instantiation example
71let _pio_spi: &'static mut PioSpi<'static> = static_init!(
72        PioSpi,
73        PioSpi::<'static>::new(
74            &peripherals.pio0,
75            &peripherals.clocks,
76            10, // clock pin
77            11, // in pin (MISO)
78            12, // out pin (MOSI)
79            SMNumber::SM0,
80        )
81    );
82
83    // make the pio subscribe to interrupts
84    peripherals.pio0.sm(SMNumber::SM0).set_rx_client(_pio_spi);
85    peripherals.pio0.sm(SMNumber::SM0).set_tx_client(_pio_spi);
86    _pio_spi.register(); // necessary for asynchronous transactions
87
88
89By default it is in clock idle low, sample leading edge clock phase, 1 MHz clock frequency
90*/
91
92pub struct PioSpi<'a> {
93    clocks: OptionalCell<&'a clocks::Clocks>,
94    pio: &'a Pio,
95    clock_pin: u32,
96    out_pin: u32,
97    in_pin: u32,
98    sm_number: SMNumber,
99    client: OptionalCell<&'a dyn SpiMasterClient>,
100    tx_buffer: MapCell<SubSliceMut<'static, u8>>,
101    tx_position: Cell<usize>,
102    rx_buffer: MapCell<SubSliceMut<'static, u8>>,
103    rx_position: Cell<usize>,
104    len: Cell<usize>,
105    state: Cell<PioSpiState>,
106    deferred_call: DeferredCall,
107    clock_div_int: Cell<u32>,
108    clock_div_frac: Cell<u32>,
109    clock_phase: Cell<ClockPhase>,
110    clock_polarity: Cell<ClockPolarity>,
111    chip_select: OptionalCell<ChipSelectPolar<'a, crate::gpio::RPGpioPin<'a>>>,
112    hold_low: Cell<bool>,
113}
114
115#[repr(u8)]
116#[derive(Clone, Copy)]
117pub enum PioSpiState {
118    Free = 0b00,
119    Writing = 0b01,
120    Reading = 0b10,
121    ReadingWriting = 0b11,
122}
123
124impl<'a> PioSpi<'a> {
125    pub fn new(
126        pio: &'a Pio,
127        clocks: &'a clocks::Clocks,
128        clock_pin: u32,
129        in_pin: u32,
130        out_pin: u32,
131        sm_number: SMNumber,
132    ) -> Self {
133        Self {
134            clocks: OptionalCell::new(clocks),
135            pio,
136            clock_pin,
137            in_pin,
138            out_pin,
139            sm_number,
140            client: OptionalCell::empty(),
141            tx_buffer: MapCell::empty(),
142            tx_position: Cell::new(0),
143            rx_buffer: MapCell::empty(),
144            rx_position: Cell::new(0),
145            len: Cell::new(0),
146            state: Cell::new(PioSpiState::Free),
147            deferred_call: DeferredCall::new(),
148            clock_div_int: Cell::new(31u32), // defaults to 1 MHz
149            clock_div_frac: Cell::new(64u32),
150            clock_phase: Cell::new(ClockPhase::SampleLeading), // defaults to mode 0 0
151            clock_polarity: Cell::new(ClockPolarity::IdleLow),
152            chip_select: OptionalCell::empty(),
153            hold_low: Cell::new(false),
154        }
155    }
156
157    // Helper function to read and writes to and from the buffers, returns whether the operation finished
158    fn read_write_buffers(&self) -> bool {
159        let mut finished = false;
160
161        self.tx_buffer.map(|buf| {
162            let length = self.len.get();
163
164            // FIFOs are 4 units deep, so that is the max one can read/write before having to wait
165            const FIFO_DEPTH: usize = 4;
166
167            let left_to_do = self.len.get() - self.tx_position.get() + 1;
168            let run_to = if FIFO_DEPTH > left_to_do {
169                left_to_do
170            } else {
171                FIFO_DEPTH
172            };
173
174            for _i in 0..run_to {
175                let mut errors = false;
176
177                // Try to write one byte
178                if self.tx_position.get() < length {
179                    let res = self
180                        .pio
181                        .sm(self.sm_number)
182                        .push(buf[self.tx_position.get()] as u32);
183                    match res {
184                        Err(_error) => errors = true,
185                        _ => {
186                            self.tx_position.set(self.tx_position.get() + 1);
187                        }
188                    }
189                }
190
191                // Try to read one byte
192                if self.rx_position.get() < length {
193                    let data = self.pio.sm(self.sm_number).pull();
194                    match data {
195                        Ok(val) => {
196                            self.rx_buffer.map(|readbuf| {
197                                readbuf[self.rx_position.get()] = (val >> AUTOPULL_SHIFT) as u8;
198                                self.rx_position.set(self.rx_position.get() + 1);
199                            });
200                        }
201                        _ => errors = true,
202                    }
203                }
204
205                // If we are done reading and writing, then exit
206                if self.tx_position.get() >= self.len.get()
207                    && self.rx_position.get() >= self.len.get()
208                {
209                    finished = true;
210
211                    break;
212                }
213
214                // If any read/write errors, then exit and stop writing
215                if errors {
216                    break;
217                }
218            }
219        });
220
221        finished
222    }
223
224    // reset the buffers and call the SPI client if any, to finish off a transaction
225    // should only be called from the deferred call or interrupt handlers
226    fn call_client_and_clean_up(&self) {
227        self.state.set(PioSpiState::Free);
228
229        let transaction_size = self.len.get();
230
231        self.state.set(PioSpiState::Free);
232        self.len.set(0);
233        self.tx_position.set(0);
234        self.rx_position.set(0);
235
236        if !self.hold_low.get() {
237            self.set_chip_select(false);
238        }
239
240        if let Some(tx_buffer) = self.tx_buffer.take() {
241            self.client.map(|client| {
242                client.read_write_done(tx_buffer, self.rx_buffer.take(), Ok(transaction_size));
243            });
244        }
245    }
246
247    fn set_chip_select(&self, active: bool) {
248        if active {
249            self.chip_select.map(|p| match p.polarity {
250                Polarity::Low => {
251                    p.activate();
252                }
253                _ => {
254                    p.deactivate();
255                }
256            });
257        } else {
258            self.chip_select.map(|p| match p.polarity {
259                Polarity::Low => {
260                    p.deactivate();
261                }
262                _ => {
263                    p.activate();
264                }
265            });
266        }
267    }
268}
269
270impl<'a> hil::spi::SpiMaster<'a> for PioSpi<'a> {
271    type ChipSelect = ChipSelectPolar<'a, crate::gpio::RPGpioPin<'a>>;
272
273    fn init(&self) -> Result<(), ErrorCode> {
274        self.pio.init();
275
276        // the trailing phase programs have a different length
277        let mut wrap = 1;
278        let program: &[u16] = if self.clock_phase.get() == ClockPhase::SampleLeading {
279            if self.clock_polarity.get() == ClockPolarity::IdleLow {
280                &SPI_CPHA0
281            } else {
282                &SPI_CPHA0_HIGH_CLOCK
283            }
284        } else {
285            // sample trailing branch
286            wrap = 2;
287            if self.clock_polarity.get() == ClockPolarity::IdleLow {
288                &SPI_CPHA1
289            } else {
290                &SPI_CPHA1_HIGH_CLOCK
291            }
292        };
293
294        match self.pio.add_program16(None::<usize>, program) {
295            Ok(_res) => {
296                self.pio.sm(self.sm_number).exec_program(_res, true);
297            }
298            Err(_error) => return Err(ErrorCode::FAIL),
299        }
300
301        let mut custom_config = StateMachineConfiguration::default();
302
303        custom_config.div_int = self.clock_div_int.get();
304        custom_config.div_frac = self.clock_div_frac.get();
305
306        // 8 bit mode on pio
307        custom_config.in_push_threshold = 8;
308        custom_config.out_pull_threshold = 8;
309
310        custom_config.side_set_base = self.clock_pin;
311        custom_config.in_pins_base = self.in_pin;
312        custom_config.out_pins_base = self.out_pin;
313        custom_config.side_set_bit_count = 1;
314        custom_config.wrap = wrap;
315
316        // automatically push and pull from the fifos
317        custom_config.in_autopush = true;
318        custom_config.out_autopull = true;
319
320        self.pio.spi_program_init(
321            self.sm_number,
322            self.clock_pin,
323            self.in_pin,
324            self.out_pin,
325            &custom_config,
326        );
327
328        Ok(())
329    }
330
331    fn set_client(&self, client: &'a dyn SpiMasterClient) {
332        self.client.set(client);
333    }
334
335    fn is_busy(&self) -> bool {
336        match self.state.get() {
337            PioSpiState::Free => false,
338            _ => true,
339        }
340    }
341
342    fn read_write_bytes(
343        &self,
344        write_buffer: SubSliceMut<'static, u8>,
345        read_buffer: Option<SubSliceMut<'static, u8>>,
346    ) -> Result<
347        (),
348        (
349            ErrorCode,
350            SubSliceMut<'static, u8>,
351            Option<SubSliceMut<'static, u8>>,
352        ),
353    > {
354        if self.is_busy() {
355            return Err((ErrorCode::BUSY, write_buffer, read_buffer));
356        }
357
358        if write_buffer.len() < 1 {
359            return Err((ErrorCode::INVAL, write_buffer, read_buffer));
360        }
361
362        self.set_chip_select(true);
363
364        // Keep track of the new buffers
365        self.len.replace(write_buffer.len());
366        self.tx_buffer.replace(write_buffer);
367        self.tx_position.set(0);
368
369        self.state.replace(PioSpiState::Writing);
370
371        if let Some(readbuf) = read_buffer {
372            self.rx_buffer.replace(readbuf);
373            self.state.replace(PioSpiState::ReadingWriting);
374            self.rx_position.set(0);
375        }
376
377        // Begin reading/writing to/from buffers
378        let done = self.read_write_buffers();
379
380        // this call is likely coming from above, so set a deferred call if it gets done within here
381        if done {
382            self.deferred_call.set();
383        }
384
385        Ok(())
386    }
387
388    fn write_byte(&self, val: u8) -> Result<(), ErrorCode> {
389        match self.read_write_byte(val) {
390            Ok(_) => Ok(()),
391            Err(error) => Err(error),
392        }
393    }
394
395    fn read_byte(&self) -> Result<u8, ErrorCode> {
396        self.read_write_byte(0)
397    }
398
399    fn read_write_byte(&self, val: u8) -> Result<u8, ErrorCode> {
400        if self.is_busy() {
401            return Err(ErrorCode::BUSY);
402        }
403
404        self.set_chip_select(true);
405
406        let mut data: u32;
407
408        // One byte operations can be synchronous
409        match self.pio.sm(self.sm_number).push_blocking(val as u32) {
410            Err(err) => {
411                return Err(err);
412            }
413            _ => {}
414        }
415
416        data = match self.pio.sm(self.sm_number).pull_blocking() {
417            Ok(val) => val,
418            Err(error) => {
419                return Err(error);
420            }
421        };
422
423        data >>= AUTOPULL_SHIFT;
424
425        if !self.hold_low.get() {
426            self.set_chip_select(false);
427        }
428
429        Ok(data as u8)
430    }
431
432    fn specify_chip_select(&self, cs: Self::ChipSelect) -> Result<(), ErrorCode> {
433        if !self.is_busy() {
434            self.chip_select.set(cs);
435            Ok(())
436        } else {
437            Err(ErrorCode::BUSY)
438        }
439    }
440
441    fn set_rate(&self, rate: u32) -> Result<u32, ErrorCode> {
442        if rate == 0 {
443            return Err(ErrorCode::FAIL);
444        }
445
446        if self.is_busy() {
447            return Err(ErrorCode::BUSY);
448        }
449
450        let sysclock_freq = self.clocks.map_or(SYSCLOCK_FREQ, |clocks| {
451            clocks.get_frequency(clocks::Clock::System)
452        });
453
454        // Program does two instructions per every SPI clock peak
455        // Still runs at half of rate after that so multiply again by two
456        let rate = rate * 4;
457
458        // Max clock rate is the sys clock
459        if rate > sysclock_freq {
460            return Err(ErrorCode::INVAL);
461        }
462
463        let divint = sysclock_freq / rate;
464        // Div frac is in units of 1/256
465        let divfrac = (sysclock_freq % rate) * 256u32 / rate;
466
467        self.clock_div_int.replace(divint);
468        self.clock_div_frac.replace(divfrac);
469
470        // Reinit the PIO so it updates the times
471        self.pio.sm(self.sm_number).set_enabled(false);
472        self.pio
473            .sm(self.sm_number)
474            .set_clkdiv_int_frac(divint, divfrac);
475        self.pio.sm(self.sm_number).clkdiv_restart();
476        self.pio.sm(self.sm_number).set_enabled(true);
477
478        Ok(rate)
479    }
480
481    fn get_rate(&self) -> u32 {
482        let sysclock_freq = self.clocks.map_or(SYSCLOCK_FREQ, |clocks| {
483            clocks.get_frequency(clocks::Clock::Peripheral)
484        });
485
486        let divisor = self.clock_div_int.get() as f32 + (self.clock_div_frac.get() as f32 / 256f32);
487
488        if divisor == 0f32 {
489            return sysclock_freq / 65536u32;
490        }
491
492        (sysclock_freq as f32 / divisor) as u32 / 4u32
493    }
494
495    fn set_polarity(&self, polarity: ClockPolarity) -> Result<(), ErrorCode> {
496        if !self.is_busy() {
497            self.clock_polarity.replace(polarity);
498            self.init()
499        } else {
500            Err(ErrorCode::BUSY)
501        }
502    }
503
504    fn get_polarity(&self) -> ClockPolarity {
505        self.clock_polarity.get()
506    }
507
508    fn set_phase(&self, phase: ClockPhase) -> Result<(), ErrorCode> {
509        if !self.is_busy() {
510            self.clock_phase.replace(phase);
511            self.init()
512        } else {
513            Err(ErrorCode::BUSY)
514        }
515    }
516
517    fn get_phase(&self) -> ClockPhase {
518        self.clock_phase.get()
519    }
520
521    fn hold_low(&self) {
522        self.hold_low.replace(true);
523    }
524
525    fn release_low(&self) {
526        self.hold_low.replace(false);
527    }
528}
529
530impl PioTxClient for PioSpi<'_> {
531    // Buffer space availble, so send next byte
532    fn on_buffer_space_available(&self) {
533        self.tx_position.set(self.tx_position.get() + 1);
534
535        match self.state.get() {
536            PioSpiState::Writing | PioSpiState::ReadingWriting => {
537                let done = self.read_write_buffers();
538                if done {
539                    self.call_client_and_clean_up();
540                }
541            }
542            _ => {}
543        }
544    }
545}
546
547impl PioRxClient for PioSpi<'_> {
548    // Data received, so update buffer and continue reading/writing
549    fn on_data_received(&self, data: u32) {
550        let data = data >> AUTOPULL_SHIFT;
551
552        if self.len.get() > self.rx_position.get() {
553            self.rx_buffer.map(|buf| {
554                buf[self.rx_position.get()] = data as u8;
555                self.rx_position.set(self.rx_position.get() + 1);
556            });
557        }
558        match self.state.get() {
559            PioSpiState::Reading | PioSpiState::ReadingWriting => {
560                let done = self.read_write_buffers();
561                if done {
562                    self.call_client_and_clean_up();
563                }
564            }
565            _ => {}
566        }
567    }
568}
569
570impl DeferredCallClient for PioSpi<'_> {
571    // deferred call to calling the client
572    fn handle_deferred_call(&self) {
573        self.call_client_and_clean_up();
574    }
575
576    fn register(&'static self) {
577        self.deferred_call.register(self);
578    }
579}