summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/sbc_harness/main.c3
-rw-r--r--gdb-helpers/rp2040.py112
-rw-r--r--libhw/rp2040_dma.h115
-rw-r--r--libhw/rp2040_hwspi.c131
-rw-r--r--libhw/rp2040_include/libhw/rp2040_hwspi.h17
5 files changed, 358 insertions, 20 deletions
diff --git a/cmd/sbc_harness/main.c b/cmd/sbc_harness/main.c
index a3351fc..8e2c5ee 100644
--- a/cmd/sbc_harness/main.c
+++ b/cmd/sbc_harness/main.c
@@ -174,7 +174,8 @@ COROUTINE init_cr(void *) {
16, /* PIN_MISO */
19, /* PIN_MOSI */
18, /* PIN_CLK */
- 17);/* PIN_CS */
+ 17, /* PIN_CS */
+ 0, 1, 2, 3); /* DMA channels */
w5500_init(&globals.dev_w5500, "W5500",
lo_box_rp2040_hwspi_as_spi(&globals.dev_spi),
21, /* PIN_INTR */
diff --git a/gdb-helpers/rp2040.py b/gdb-helpers/rp2040.py
index 9e10d73..983e13b 100644
--- a/gdb-helpers/rp2040.py
+++ b/gdb-helpers/rp2040.py
@@ -3,6 +3,8 @@
# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
+import typing
+
import gdb
@@ -115,6 +117,7 @@ class RP2040ShowInterrupts(gdb.Command):
def invoke(self, arg: str, from_tty: bool) -> None:
self.arm_cortex_m0plus_registers()
self.arm_cortex_m0plus_mmregisters()
+ self.rp2040_dma_mmregisters()
def arm_cortex_m0plus_mmregisters(self) -> None:
base: int = 0xE0000000
@@ -181,5 +184,114 @@ xPSR : {fmt32(psr) } {{Application,Execution,Interrupt}
)
)
+ def rp2040_dma_mmregisters(self) -> None:
+ base: int = 0x50000000
+
+ def fmt12(x: int) -> str:
+ s = fmt32(x)
+ return s[:-12] + "_" + s[-12:]
+
+ print(
+ box(
+ "RP2040 DMA memory-mapped registers",
+ f"""
+
+ 8 4 0
+ ┌──┴───┴───┤
+INTR : {fmt12(read_mmreg(base + 0x400))} Raw
+ │ │ │ │
+INTE0: {fmt12(read_mmreg(base + 0x404))} IRQ_DMA_0 Enable
+INTF0: {fmt12(read_mmreg(base + 0x408))} IRQ_DMA_0 Force
+INTS0: {fmt12(read_mmreg(base + 0x40c))} IRQ_DMA_0 Status
+ │ │ │ │
+INTE1: {fmt12(read_mmreg(base + 0x414))} IRQ_DMA_1 Enable
+INTF1: {fmt12(read_mmreg(base + 0x418))} IRQ_DMA_1 Force
+INTS1: {fmt12(read_mmreg(base + 0x41c))} IRQ_DMA_1 Status
+""",
+ )
+ )
+
RP2040ShowInterrupts()
+
+
+class RP2040ShowDMA(gdb.Command):
+ """Show the RP2040's DMA control registers."""
+
+ def __init__(self) -> None:
+ super(RP2040ShowDMA, self).__init__("rp2040-show-dma", gdb.COMMAND_USER)
+
+ def invoke(self, arg: str, from_tty: bool) -> None:
+ base: int = 0x50000000
+ u32_size: int = 4
+
+ nchan = read_mmreg(base + 0x448)
+
+ def chreg(
+ ch: int,
+ name: typing.Literal[
+ "read_addr",
+ "write_addr",
+ "trans_count",
+ "ctrl",
+ "dbg_ctdreq",
+ "dbg_tcr",
+ ],
+ ) -> int:
+ fieldcnt: int = 4 * 4
+ fieldnum: int
+ debug = False
+ match name:
+ case "read_addr":
+ fieldnum = 0
+ case "write_addr":
+ fieldnum = 1
+ case "trans_count":
+ fieldnum = 2
+ case "ctrl":
+ fieldnum = 4
+ case "dbg_ctdreq":
+ fieldnum = 0
+ debug = True
+ case "dbg_tcr":
+ fieldnum = 1
+ debug = True
+ return read_mmreg(
+ base
+ + (0x800 if debug else 0)
+ + (ch * u32_size * fieldcnt)
+ + (u32_size * fieldnum)
+ )
+
+ def ctrl(ch: int) -> str:
+ s = fmt32(chreg(ch, "ctrl"))
+ return s[:10] + "_" + s[10:]
+
+ def chaddr(ch: int, name: typing.Literal["read", "write"]) -> str:
+ val = chreg(ch, name + "_addr") # type: ignore
+ if val == 0:
+ return "NULL "
+ return f"0x{val:08x}"
+
+ ret = f"""
+ ╓sniff_enable
+ ║╓bswap
+ ║║╓irq_quiet
+ ║║║ ┌treq_sel
+ ║║║ │ ┌chain_to
+ ║║║ │ │ ╓ring_sel
+ ║║║ │ │ ║ ┌ring_size
+ ║║║ │ │ ║ │ ╓incr_write
+ busy╖ ║║║ │ │ ║ │ ║╓incr_read
+write_err╖ ║ ║║║ │ │ ║ │ ║║┌data_size
+read_err╖║ ║ ║║║ │ │ ║ │ ║║│ ╓high_priority
+ahb_err╖║║ ║ ║║║ │ │ ║ │ ║║│ ║╓enable
+ ║║║ ║ ║║║ │ │ ║ │ ║║│ ║║ trans_cnt
+ ║║║ ║ ║║║┌─┴──┐┌┴─┐║┌┴─┐║║├┐║║ read_addr write_addr cur/reload
+"""
+ for ch in range(0, nchan):
+ ret += f"{ch: 3}: {ctrl(ch)} {chaddr(ch, 'read')} {chaddr(ch, 'write')} {chreg(ch, 'trans_count')}/{chreg(ch, 'dbg_tcr')}\n"
+ print(box("RP2040 DMA channels", ret))
+
+
+RP2040ShowDMA()
diff --git a/libhw/rp2040_dma.h b/libhw/rp2040_dma.h
new file mode 100644
index 0000000..e4b44ff
--- /dev/null
+++ b/libhw/rp2040_dma.h
@@ -0,0 +1,115 @@
+/* libhw/rp2040_dma.h - Utilities for using DMA on the RP2040 (replaces <hardware/dma.h>)
+ *
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIBHW_RP2040_DMA_H_
+#define _LIBHW_RP2040_DMA_H_
+
+#include <assert.h>
+#include <stdint.h> /* for uint32_t */
+
+#include <hardware/regs/dreq.h> /* for DREQ_* for use with DMA_CTRL_TREQ_SEL() */
+#include <hardware/structs/dma.h> /* for dma_hw, dma_channel_hw_t, DMA_NUM_CHANNELS */
+
+#include <libmisc/macro.h> /* for LM_FLOORLOG2() */
+
+/* Borrowed from <hardware/dma.h> *********************************************/
+
+static inline dma_channel_hw_t *dma_channel_hw_addr(uint channel) {
+ assert(channel < NUM_DMA_CHANNELS);
+ return &dma_hw->ch[channel];
+}
+
+enum dma_channel_transfer_size {
+ DMA_SIZE_8 = 0, ///< Byte transfer (8 bits)
+ DMA_SIZE_16 = 1, ///< Half word transfer (16 bits)
+ DMA_SIZE_32 = 2 ///< Word transfer (32 bits)
+};
+
+static inline bool dma_channel_is_busy(uint channel) {
+ assert(channel < NUM_DMA_CHANNELS);
+ return dma_hw->ch[channel].al1_ctrl & DMA_CH0_CTRL_TRIG_BUSY_BITS;
+}
+
+/* Our own code ***************************************************************/
+
+#define DMA_CTRL_ENABLE (1<<0)
+#define DMA_CTRL_HI_PRIO (1<<1)
+#define DMA_CTRL_DATA_SIZE(sz) ((sz)<<2)
+#define DMA_CTRL_INCR_READ (1<<4)
+#define DMA_CTRL_INCR_WRITE (1<<5)
+#define _DMA_CTRL_RING_BITS(b) ((b)<<6)
+#define _DMA_CTRL_RING_RD (0)
+#define _DMA_CTRL_RING_WR (1<<10)
+#define DMA_CTRL_RING(rdwr, bits) (_DMA_CTRL_RING_##rdwr | _DMA_CTRL_RING_BITS(bits))
+#define DMA_CTRL_CHAIN_TO(ch) ((ch)<<11)
+#define DMA_CTRL_TREQ_SEL(dreq) ((dreq)<<15)
+#define DMA_CTRL_IRQ_QUIET (1<<21)
+#define DMA_CTRL_BSWAP (1<<22)
+#define DMA_CTRL_SNIFF_EN (1<<23)
+
+/* | elem | val | name */
+#define READ_ADDR /*|*/volatile const void/*|*/ * /*|*/read_addr
+#define WRITE_ADDR /*|*/volatile void/*|*/ * /*|*/write_addr
+#define TRANS_COUNT /*|*/ /*|*/uint32_t/*|*/trans_count
+#define CTRL /*|*/ /*|*/uint32_t/*|*/ctrl
+
+/* { +0x0 ; +0x4 ; +0x8 ; +0xC (Trigger) */
+struct dma_alias0 { READ_ADDR ; WRITE_ADDR ; TRANS_COUNT ; CTRL ; };
+struct dma_alias1 { CTRL ; READ_ADDR ; WRITE_ADDR ; TRANS_COUNT ; };
+struct dma_alias2 { CTRL ; TRANS_COUNT ; READ_ADDR ; WRITE_ADDR ; };
+struct dma_alias3 { CTRL ; WRITE_ADDR ; TRANS_COUNT ; READ_ADDR ; };
+struct dma_alias0_short2 { TRANS_COUNT ; CTRL ; };
+struct dma_alias1_short2 { WRITE_ADDR ; TRANS_COUNT ; };
+struct dma_alias2_short2 { READ_ADDR ; WRITE_ADDR ; };
+struct dma_alias3_short2 { TRANS_COUNT ; READ_ADDR ; };
+struct dma_alias0_short3 { CTRL ; };
+struct dma_alias1_short3 { TRANS_COUNT ; };
+struct dma_alias2_short3 { WRITE_ADDR ; };
+struct dma_alias3_short3 { READ_ADDR ; };
+
+#undef CTRL
+#undef TRANS_COUNT
+#undef WRITE_ADDR
+#undef READ_ADDR
+
+#define DMA_CHAN_ADDR(CH, TYP) ((TYP *volatile)_Generic((TYP){}, \
+ struct dma_alias0: &dma_channel_hw_addr(CH)->read_addr, \
+ struct dma_alias1: &dma_channel_hw_addr(CH)->al1_ctrl, \
+ struct dma_alias2: &dma_channel_hw_addr(CH)->al2_ctrl, \
+ struct dma_alias3: &dma_channel_hw_addr(CH)->al3_ctrl, \
+ struct dma_alias0_short2: &dma_channel_hw_addr(CH)->transfer_count, \
+ struct dma_alias1_short2: &dma_channel_hw_addr(CH)->al1_write_addr, \
+ struct dma_alias2_short2: &dma_channel_hw_addr(CH)->al2_read_addr, \
+ struct dma_alias3_short2: &dma_channel_hw_addr(CH)->al3_transfer_count, \
+ struct dma_alias0_short3: &dma_channel_hw_addr(CH)->ctrl_trig, \
+ struct dma_alias1_short3: &dma_channel_hw_addr(CH)->al1_transfer_count_trig, \
+ struct dma_alias2_short3: &dma_channel_hw_addr(CH)->al2_write_addr_trig, \
+ struct dma_alias3_short3: &dma_channel_hw_addr(CH)->al3_read_addr_trig))
+
+#define DMA_CHAN_WR_TRANS_COUNT(TYP) \
+ (sizeof(TYP)/4)
+
+#define DMA_CHAN_WR_CTRL(TYP) ( DMA_CTRL_DATA_SIZE(DMA_SIZE_32) \
+ | DMA_CTRL_INCR_WRITE \
+ | DMA_CTRL_RING(WR, LM_FLOORLOG2(sizeof(TYP))) \
+ )
+
+#define DMA_NONTRIGGER(CH, FIELD) (DMA_CHAN_ADDR(CH, _DMA_NONTRIGGER_##FIELD)->FIELD)
+#define _DMA_NONTRIGGER_read_addr struct dma_alias0
+#define _DMA_NONTRIGGER_write_addr struct dma_alias0
+#define _DMA_NONTRIGGER_trans_count struct dma_alias0
+#define _DMA_NONTRIGGER_ctrl struct dma_alias1
+
+#define DMA_TRIGGER(CH, FIELD) (DMA_CHAN_ADDR(CH, _DMA_TRIGGER_##FIELD)->FIELD)
+#define _DMA_TRIGGER_read_addr struct dma_alias3
+#define _DMA_TRIGGER_write_addr struct dma_alias2
+#define _DMA_TRIGGER_trans_count struct dma_alias1
+#define _DMA_TRIGGER_ctrl struct dma_alias0
+
+#endif /* _LIBHW_RP2040_DMA_H_ */
diff --git a/libhw/rp2040_hwspi.c b/libhw/rp2040_hwspi.c
index 8dd49d6..1c4e096 100644
--- a/libhw/rp2040_hwspi.c
+++ b/libhw/rp2040_hwspi.c
@@ -4,12 +4,14 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
+#include <alloca.h>
#include <inttypes.h> /* for PRIu{n} */
#include <hardware/clocks.h> /* for clock_get_hz() and clk_peri */
#include <hardware/gpio.h>
#include <hardware/spi.h>
+#include <libcr/coroutine.h>
#include <libmisc/assert.h>
#define LOG_NAME RP2040_SPI
@@ -20,6 +22,8 @@
#include <libhw/generic/alarmclock.h>
+#include "rp2040_dma.h"
+
#include "config.h"
#ifndef CONFIG_RP2040_SPI_DEBUG
@@ -46,7 +50,12 @@ void _rp2040_hwspi_init(struct rp2040_hwspi *self,
uint pin_miso,
uint pin_mosi,
uint pin_clk,
- uint pin_cs) {
+ uint pin_cs,
+ uint dma1,
+ uint dma2,
+ uint dma3,
+ uint dma4)
+{
/* Be not weary: This is but 12 lines of actual code; and many
* lines of comments and assert()s. */
spi_inst_t *inst;
@@ -58,6 +67,7 @@ void _rp2040_hwspi_init(struct rp2040_hwspi *self,
debugf("clk_peri = %"PRIu32"Hz", clk_peri_hz);
assert(baudrate_hz*2 <= clk_peri_hz);
assert_4distinct(pin_miso, pin_mosi, pin_clk, pin_cs);
+ assert_4distinct(dma1, dma2, dma3, dma4);
/* Regarding the constraints on pin assignments: see the
* RP2040 datasheet, table 2, in §1.4.3 "GPIO Functions". */
@@ -109,32 +119,121 @@ void _rp2040_hwspi_init(struct rp2040_hwspi *self,
self->min_delay_ns = min_delay_ns;
self->bogus_data = bogus_data;
self->pin_cs = pin_cs;
+ self->dma_tx_ctrl = dma1;
+ self->dma_rx_ctrl = dma2;
+ self->dma_tx_data = dma3;
+ self->dma_rx_data = dma4;
self->dead_until_ns = 0;
}
static void rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct duplex_iovec *iov, int iovcnt) {
assert(self);
- spi_inst_t *inst = self->inst;
-
- assert(inst);
+ assert(self->inst);
assert(iov);
- assert(iovcnt);
+ assert(iovcnt > 0);
+ /* At this time, I have no intention to run SPI faster than
+ * 80MHz (= 80Mb/s = 10MB/s). If we ran the CPU at just
+ * 100MHz (we'll be running it faster than that, maybe even
+ * 200MHz), that means we'd have 10 clock cycles to send each
+ * byte.
+ *
+ * This affords us substantial simplifications, like being
+ * able to afford 4-cycle changeovers between DMA blocks, and
+ * not having to worry about alignment because we can just use
+ * DMA_SIZE_8.
+ */
+
+ uint8_t bogus_rx_dst;
+
+ int pruned_iovcnt = 0;
+ for (int i = 0; i < iovcnt; i++)
+ if (iov[i].iov_len)
+ pruned_iovcnt++;
+ if (!pruned_iovcnt)
+ return;
+
+ /* For tx_data_blocks, it doesn't really matter which aliases
+ * we choose:
+ * - None of our fields can be NULL (so no
+ * false-termination).
+ * - Moving const fields first so they don't have to be
+ * re-programmed each time isn't possible for us there need
+ * to be at least 2 const fields, and we only have 1
+ * (read_addr for rx_data_blocks, and write_addr for
+ * tx_data_blocks).
+ *
+ * But for rx_data_blocks, we need ctrl to be the trigger
+ * register so that the DMA_CTRL_IRQ_QUIET flag isn't cleared
+ * before we get to the trigger; and while for tx_data_blocks
+ * it doesn't really matter, the inverse would be nice.
+ */
+ struct dma_alias1 *tx_data_blocks = alloca(sizeof(struct dma_alias1)*(pruned_iovcnt+1));
+ struct dma_alias0 *rx_data_blocks = alloca(sizeof(struct dma_alias0)*(pruned_iovcnt+1));
+
+ for (int i = 0, j = 0; i < iovcnt; i++) {
+ if (!iov[i].iov_len)
+ continue;
+ tx_data_blocks[j] = (typeof(tx_data_blocks[0])){
+ .read_addr = iov[i].iov_write_src ?: &self->bogus_data,
+ .write_addr = &spi_get_hw(self->inst)->dr,
+ .trans_count = iov[i].iov_len,
+ .ctrl = (DMA_CTRL_ENABLE
+ | DMA_CTRL_DATA_SIZE(DMA_SIZE_8)
+ | (iov[i].iov_write_src ? DMA_CTRL_INCR_READ : 0)
+ | DMA_CTRL_CHAIN_TO(self->dma_tx_ctrl)
+ | DMA_CTRL_TREQ_SEL(SPI_DREQ_NUM(self->inst, true))
+ | DMA_CTRL_IRQ_QUIET),
+ };
+ rx_data_blocks[j] = (typeof(rx_data_blocks[0])){
+ .read_addr = &spi_get_hw(self->inst)->dr,
+ .write_addr = iov[i].iov_read_dst ?: &bogus_rx_dst,
+ .trans_count = iov[i].iov_len,
+ .ctrl = (DMA_CTRL_ENABLE
+ | DMA_CTRL_DATA_SIZE(DMA_SIZE_8)
+ | (iov[i].iov_read_dst ? DMA_CTRL_INCR_WRITE : 0)
+ | DMA_CTRL_CHAIN_TO(self->dma_rx_ctrl)
+ | DMA_CTRL_TREQ_SEL(SPI_DREQ_NUM(self->inst, false))
+ | DMA_CTRL_IRQ_QUIET),
+ };
+ j++;
+ }
+ tx_data_blocks[pruned_iovcnt] = (typeof(tx_data_blocks[0])){0};
+ rx_data_blocks[pruned_iovcnt] = (typeof(rx_data_blocks[0])){0};
+
+ /* Set up ctrl. */
+ DMA_NONTRIGGER(self->dma_tx_ctrl, read_addr) = tx_data_blocks;
+ DMA_NONTRIGGER(self->dma_tx_ctrl, write_addr) = DMA_CHAN_ADDR(self->dma_tx_data, typeof(tx_data_blocks[0]));
+ DMA_NONTRIGGER(self->dma_tx_ctrl, trans_count) = DMA_CHAN_WR_TRANS_COUNT(typeof(tx_data_blocks[0]));
+ DMA_NONTRIGGER(self->dma_tx_ctrl, ctrl) = (DMA_CTRL_ENABLE
+ | DMA_CHAN_WR_CTRL(typeof(tx_data_blocks[0]))
+ | DMA_CTRL_INCR_READ
+ | DMA_CTRL_CHAIN_TO(self->dma_tx_data)
+ | DMA_CTRL_TREQ_SEL(DREQ_FORCE)
+ | DMA_CTRL_IRQ_QUIET);
+ DMA_NONTRIGGER(self->dma_rx_ctrl, read_addr) = rx_data_blocks;
+ DMA_NONTRIGGER(self->dma_rx_ctrl, write_addr) = DMA_CHAN_ADDR(self->dma_rx_data, typeof(rx_data_blocks[0]));
+ DMA_NONTRIGGER(self->dma_rx_ctrl, trans_count) = DMA_CHAN_WR_TRANS_COUNT(typeof(rx_data_blocks[0]));
+ DMA_NONTRIGGER(self->dma_rx_ctrl, ctrl) = (DMA_CTRL_ENABLE
+ | DMA_CHAN_WR_CTRL(typeof(rx_data_blocks[0]))
+ | DMA_CTRL_INCR_READ
+ | DMA_CTRL_CHAIN_TO(self->dma_rx_data)
+ | DMA_CTRL_TREQ_SEL(DREQ_FORCE)
+ | DMA_CTRL_IRQ_QUIET);
+
+ /* Run. */
uint64_t now = LO_CALL(bootclock, get_time_ns);
if (now < self->dead_until_ns)
sleep_until_ns(self->dead_until_ns);
+ /* TODO: Use interrupts instead of busy-polling. */
gpio_put(self->pin_cs, 0);
- /* TODO: Replace blocking reads+writes with DMA. */
- for (int i = 0; i < iovcnt; i++) {
- if (iov[i].iov_write_src && iov[i].iov_read_dst)
- spi_write_read_blocking(inst, iov[i].iov_write_src, iov[i].iov_read_dst, iov[i].iov_len);
- else if (iov[i].iov_write_src)
- spi_write_blocking(inst, iov[i].iov_write_src, iov[i].iov_len);
- else if (iov[i].iov_read_dst)
- spi_read_blocking(inst, self->bogus_data, iov[i].iov_read_dst, iov[i].iov_len);
- else
- assert_notreached("duplex_iovec is neither read nor write");
- }
+ dma_hw->multi_channel_trigger = (1u<<self->dma_tx_ctrl) | (1u<<self->dma_rx_ctrl);
+ while (dma_channel_is_busy(self->dma_tx_ctrl)
+ || dma_channel_is_busy(self->dma_tx_data)
+ || dma_channel_is_busy(self->dma_rx_ctrl)
+ || dma_channel_is_busy(self->dma_rx_data))
+ tight_loop_contents();
+ __compiler_memory_barrier();
gpio_put(self->pin_cs, 1);
self->dead_until_ns = LO_CALL(bootclock, get_time_ns) + self->min_delay_ns;
}
diff --git a/libhw/rp2040_include/libhw/rp2040_hwspi.h b/libhw/rp2040_include/libhw/rp2040_hwspi.h
index fef1dbd..f90c1af 100644
--- a/libhw/rp2040_include/libhw/rp2040_hwspi.h
+++ b/libhw/rp2040_include/libhw/rp2040_hwspi.h
@@ -25,6 +25,10 @@ struct rp2040_hwspi {
uint64_t min_delay_ns;
uint8_t bogus_data;
uint pin_cs;
+ uint dma_tx_data;
+ uint dma_tx_ctrl;
+ uint dma_rx_data;
+ uint dma_rx_ctrl;
/* mutable */
uint64_t dead_until_ns;
@@ -47,6 +51,7 @@ LO_IMPLEMENTATION_H(spi, struct rp2040_hwspi, rp2040_hwspi)
* @param pin_mosi : uint : pin number; 3, 7, 19, or 23 for _HWSPI_0; 11, 15, or 27 for _HWSPI_1
* @param pin_clk : uint : pin number; 2, 6, 18, or 22 for _HWSPI_0; 10, 14, or 26 for _HWSPI_1
* @param pin_cs : uint : pin number; any unused GPIO pin
+ * @param dma{1-4} : uint : DMA channel; any unused channel
*
* There is no bit-order argument; the RP2040's hardware SPI always
* uses MSB-first bit order.
@@ -76,7 +81,8 @@ LO_IMPLEMENTATION_H(spi, struct rp2040_hwspi, rp2040_hwspi)
#define rp2040_hwspi_init(self, name, \
inst_num, mode, baudrate_hz, \
min_delay_ns, bogus_data, \
- pin_miso, pin_mosi, pin_clk, pin_cs) \
+ pin_miso, pin_mosi, pin_clk, pin_cs, \
+ dma1, dma2, dma3, dma4) \
do { \
bi_decl(bi_4pins_with_names(pin_miso, name" SPI MISO", \
pin_mosi, name" SPI MOSI", \
@@ -85,7 +91,8 @@ LO_IMPLEMENTATION_H(spi, struct rp2040_hwspi, rp2040_hwspi)
_rp2040_hwspi_init(self, \
inst_num, mode, baudrate_hz, \
min_delay_ns, bogus_data, \
- pin_miso, pin_mosi, pin_clk, pin_cs); \
+ pin_miso, pin_mosi, pin_clk, pin_cs, \
+ dma1, dma2, dma3, dma4); \
} while(0)
void _rp2040_hwspi_init(struct rp2040_hwspi *self,
enum rp2040_hwspi_instance inst_num,
@@ -96,6 +103,10 @@ void _rp2040_hwspi_init(struct rp2040_hwspi *self,
uint pin_miso,
uint pin_mosi,
uint pin_clk,
- uint pin_cs);
+ uint pin_cs,
+ uint dma1,
+ uint dma2,
+ uint dma3,
+ uint dma4);
#endif /* _LIBHW_RP2040_HWSPI_H_ */