summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2025-06-29 03:26:50 -0600
committerLuke T. Shumaker <lukeshu@lukeshu.com>2025-07-03 08:07:29 -0600
commit918a80577c2f28ac88b10b199d4c799343e91434 (patch)
tree26e35ff803fd2d2e9f484addcc8e67b25020bee1
parent1d5a211e1ae3645e9c13163b76fea0a3c87f46f1 (diff)
wip pio spilukeshu/pio-spi
-rw-r--r--libhw_cr/rp2040_piospi.c54
-rw-r--r--libhw_cr/rp2040_piospi.pio41
2 files changed, 95 insertions, 0 deletions
diff --git a/libhw_cr/rp2040_piospi.c b/libhw_cr/rp2040_piospi.c
new file mode 100644
index 0000000..8bd34fb
--- /dev/null
+++ b/libhw_cr/rp2040_piospi.c
@@ -0,0 +1,54 @@
+/* libhw_cr/rp2040_piospi.c - <libhw/generic/spi.h> implementation for the RP2040's PIO
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+void _rp2040_piospi_init(struct rp2040_piospi *self,
+ enum spi_mode mode,
+ uint baudrate_hz,
+ uint64_t min_delay_ns,
+ uint8_t bogus_data,
+ uint pin_miso,
+ uint pin_mosi,
+ uint pin_clk,
+ uint pin_cs,
+ uint dma1,
+ uint dma2,
+ uint dma3,
+ uint dma4) {
+ x
+}
+
+/**
+ * At a maximum rate of sys_clk/4 bps.
+ *
+ */
+
+/**
+ *
+ * 2^32 b = 512 MiB
+ */
+size_t_and_error rp2040_piospi_readwritev(struct rp2040_piospi *self, const struct duplex_iovec *iov, int iovcnt) {
+ assert(self);
+ assert(iov);
+ assert(iovcnt > 0);
+
+ /* - The .pio file transfers data at a rate of 1/4 bit/cycle.
+ *
+ * - Even at the slow DMA_SIZE_8 (which allows us to not have
+ * to worry about alignment) goes at 8 bit/cycle; plenty
+ * fast.
+ * - If the PIO push/pull-size is 32 bits, then that gives us
+ * 32 cycles for DMA changeovers; plenty of time for
+ * separate ctrl and data channel changeovers (we don't
+ * need the complexity of toggling between 2 data channels
+ * for instaneous changeovers).
+ */
+
+ [[gnu::cleanup(heap_cleanup)]] struct dma_alias1 *tx_data_blocks = heap_alloc(pruned_iovcnt+1, struct dma_alias1);
+ [[gnu::cleanup(heap_cleanup)]] struct dma_alias0 *rx_data_blocks = heap_alloc(pruned_iovcnt+1, struct dma_alias0);
+ static_assert(!DMA_IS_TRIGGER(typeof(tx_data_blocks[0]), ctrl));
+ static_assert(DMA_IS_TRIGGER(typeof(rx_data_blocks[0]), ctrl));
+
+}
diff --git a/libhw_cr/rp2040_piospi.pio b/libhw_cr/rp2040_piospi.pio
new file mode 100644
index 0000000..6307c9b
--- /dev/null
+++ b/libhw_cr/rp2040_piospi.pio
@@ -0,0 +1,41 @@
+;;; libhw_cr/rp2040_piospi.pio - <libhw/generic/spi.h> implementation for the RP2040's PIO
+;;;
+;;; Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+;;; SPDX-License-Identifier: AGPL-3.0-or-later
+
+;;; First read a u32 bit-count, then transfer that many bits (full-duplex).
+;;;
+;;; Pin assignments:
+;;; - MOSI ; OUT pin 0
+;;; - MISO ; IN pin 0
+;;; - CLK ; SIDE pin 0
+;;; - CS ; SIDE pin 1 (low active, high idle))
+
+;;; This is all written as if clk_polarity=0 (idle low); if that is
+;;; not the case, then the clk pin can be flipped in the GPIO mux.
+
+;;; clk_phase=0 (sample on clock transition-to-active; written as "rise" here)
+;;; shift on CS fall and clock fall
+.program rp2040_piospi_mode0
+.side_set 2
+idle:
+ pull side 0b10 ; osr=(32 bits) cs=high clk=low
+ mov x osr side 0b10 ; x=osr cs=high clk=low
+active:
+ ;; 2 cycles low, 2 cycles high
+ out pins 1 side 0b00 [1] ; mosi=(1 bit) cs=low clk=low [wait=1] ; CS fall/clock fall = shift
+ in pins 1 side 0b01 ; miso=(1 bit) cs=low clk=high ; clock rise = sample
+ jmp x-- active side 0b01 ; cs=low clk=high
+
+;;; clk_phase=1 (sample on transition-to-idle
+;;; shift on clock rise
+.program rp2040_piospi_mode0
+.side_set 2
+idle:
+ pull side 0b10 ; osr=(32 bits) cs=high clk=low
+ mov x osr side 0b10 ; x=osr cs=high clk=low
+active:
+ ;; 2 cycles high, 2 cycles low
+ out pins 1 side 0b01 [1] ; mosi=(1 bit) cs=low clk=high [wait=1] ; clock rise = shift
+ in pins 1 side 0b00 ; miso=(1 bit) cs=low clk=low ; clock fall = sample
+ jmp x-- active side 0b00 ; cs=low clk=low