diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-07-03 08:00:50 -0600 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-07-03 08:00:50 -0600 |
commit | 1d5a211e1ae3645e9c13163b76fea0a3c87f46f1 (patch) | |
tree | 610128434e35b9b6028609d7cb5d67bcd13b3379 /libhw_cr | |
parent | 1be02f3e2379b2cd06dbae775504948ff37bf89a (diff) | |
parent | ad2ef1642096665be998e83f9b6c4b7de308b644 (diff) |
Diffstat (limited to 'libhw_cr')
-rw-r--r-- | libhw_cr/host_net.c | 1 | ||||
-rw-r--r-- | libhw_cr/rp2040_dma.c | 84 | ||||
-rw-r--r-- | libhw_cr/rp2040_dma.h | 7 | ||||
-rw-r--r-- | libhw_cr/rp2040_hwspi.c | 36 | ||||
-rw-r--r-- | libhw_cr/w5500.c | 1 |
5 files changed, 123 insertions, 6 deletions
diff --git a/libhw_cr/host_net.c b/libhw_cr/host_net.c index e68ccf8..71a5c37 100644 --- a/libhw_cr/host_net.c +++ b/libhw_cr/host_net.c @@ -38,7 +38,6 @@ LO_IMPLEMENTATION_C(net_stream_listener, struct hostnet_tcp_listener, hostnet_tc LO_IMPLEMENTATION_STATIC(io_reader, struct _hostnet_tcp_conn, hostnet_tcp); LO_IMPLEMENTATION_STATIC(io_writer, struct _hostnet_tcp_conn, hostnet_tcp); -LO_IMPLEMENTATION_STATIC(io_readwriter, struct _hostnet_tcp_conn, hostnet_tcp); LO_IMPLEMENTATION_STATIC(io_closer, struct _hostnet_tcp_conn, hostnet_tcp); LO_IMPLEMENTATION_STATIC(io_bidi_closer, struct _hostnet_tcp_conn, hostnet_tcp); LO_IMPLEMENTATION_STATIC(net_stream_conn, struct _hostnet_tcp_conn, hostnet_tcp); diff --git a/libhw_cr/rp2040_dma.c b/libhw_cr/rp2040_dma.c index 9c2c9ce..7b78535 100644 --- a/libhw_cr/rp2040_dma.c +++ b/libhw_cr/rp2040_dma.c @@ -9,6 +9,8 @@ #include <hardware/irq.h> /* for irq_set_exclusive_handler() */ +#include <libmisc/log.h> + #include "rp2040_dma.h" /* static_assert("rp2040_dma.h" == <hardware/irq.h>); */ @@ -24,6 +26,88 @@ dma_channel_hw_t *dma_channel_hw_addr(uint channel) { /* Our own code ***************************************************************/ +typedef uint8_t addr_flag_t; +#define ADDR_FLAG_UNMAPPED ((addr_flag_t)(1<<0)) +#define ADDR_FLAG_UNSAFE ((addr_flag_t)(1<<1)) +#define ADDR_FLAG_RD_OK ((addr_flag_t)(1<<2)) +#define ADDR_FLAG_WR_OK ((addr_flag_t)(1<<3)) +#define ADDR_FLAG_NEEDS_DREQ ((addr_flag_t)(1<<4)) + +static addr_flag_t dma_classify_addr(volatile const void *_addr) { + uintptr_t addr = (uintptr_t)_addr; + switch (addr >> 28) { + case 0x0: /* ROM */ + if (addr < 0x4000) + return ADDR_FLAG_RD_OK; + return ADDR_FLAG_UNMAPPED; + case 0x1: /* XIP */ + switch ((addr >> 24)&0xf) { + case 0x0: case 0x1: case 0x2: case 0x3: /* not safe for DMA */ + return ADDR_FLAG_UNSAFE; + case 0x4: /* CTRL registers */ + if (addr < 0x14000020) + return ADDR_FLAG_RD_OK | ADDR_FLAG_WR_OK; + return ADDR_FLAG_UNMAPPED; + case 0x5: /* SRAM */ + if (addr < 0x15004000) + return ADDR_FLAG_RD_OK | ADDR_FLAG_WR_OK; + return ADDR_FLAG_UNMAPPED; + case 0x8: /* SSI registers */ + if (addr < 0x18000064) + return ADDR_FLAG_RD_OK | ADDR_FLAG_WR_OK; + if (0x180000f0 <= addr && addr < 0x180000fc) + return ADDR_FLAG_RD_OK | ADDR_FLAG_WR_OK; + return ADDR_FLAG_UNMAPPED; + } + return ADDR_FLAG_UNMAPPED; + case 0x2: /* SRAM */ + if ((addr & 0xfeffffff) < 0x20040000) /* banks 0-3 striped/unstriped depending on bit */ + return ADDR_FLAG_RD_OK | ADDR_FLAG_WR_OK; + if (addr < 0x20042000) /* banks 4-5 */ + return ADDR_FLAG_RD_OK | ADDR_FLAG_WR_OK; + return ADDR_FLAG_UNMAPPED; + case 0x4: /* APB Peripherals */ + /* TODO */ + return ADDR_FLAG_RD_OK | ADDR_FLAG_WR_OK; + case 0x5: /* AHB-Lite Peripherals */ + /* TODO */ + return ADDR_FLAG_RD_OK | ADDR_FLAG_WR_OK; + case 0xd: /* IOPORT Registers */ + /* TODO */ + return ADDR_FLAG_RD_OK | ADDR_FLAG_WR_OK; + case 0xe: /* Cortex-M0+ internal registers */ + /* TODO */ + return ADDR_FLAG_RD_OK | ADDR_FLAG_WR_OK; + } + return ADDR_FLAG_UNMAPPED; +} + +bool dma_is_unsafe(volatile const void *addr) { + return dma_classify_addr(addr) & ADDR_FLAG_UNSAFE; +} + +#ifndef NDEBUG +void dma_assert_addrs(volatile void *dst, volatile const void *src) { + addr_flag_t dst_flags = dma_classify_addr(dst); + addr_flag_t src_flags = dma_classify_addr(src); + bool bad = false; + if (!(dst_flags & ADDR_FLAG_WR_OK)) { + log_n_errorln(ASSERT, "dma_assert_addrs(", (ptr, dst), ", ", (ptr, src), "): invalid destination"); + bad = true; + } + if (!(src_flags & ADDR_FLAG_RD_OK)) { + log_n_errorln(ASSERT, "dma_assert_addrs(", (ptr, dst), ", ", (ptr, src), "): invalid source"); + bad = true; + } + if (!bad && (dst_flags & ADDR_FLAG_NEEDS_DREQ && src_flags & ADDR_FLAG_NEEDS_DREQ) ) { + log_n_errorln(ASSERT, "dma_assert_addrs(", (ptr, dst), ", ", (ptr, src), "): source and destination both required DREQs"); + bad = true; + } + if (bad) + __lm_abort(); +} +#endif + struct dmairq_handler_entry { dmairq_handler_t fn; void *arg; diff --git a/libhw_cr/rp2040_dma.h b/libhw_cr/rp2040_dma.h index b382952..1392e1f 100644 --- a/libhw_cr/rp2040_dma.h +++ b/libhw_cr/rp2040_dma.h @@ -41,6 +41,13 @@ void dmairq_set_and_enable_exclusive_handler(enum dmairq irq, uint channel, dmai /* Control blocks *************************************************************/ +bool dma_is_unsafe(volatile const void *addr); +#ifdef NDEBUG +#define dma_assert_addrs(dst, src) ((void)0) +#else +void dma_assert_addrs(volatile void *dst, volatile const void *src); +#endif + /* types =================================================*/ /* | elem | val | name */ diff --git a/libhw_cr/rp2040_hwspi.c b/libhw_cr/rp2040_hwspi.c index 54a94af..f667332 100644 --- a/libhw_cr/rp2040_hwspi.c +++ b/libhw_cr/rp2040_hwspi.c @@ -27,6 +27,9 @@ #ifndef CONFIG_RP2040_SPI_DEBUG #error config.h must define CONFIG_RP2040_SPI_DEBUG (bool) #endif +#ifndef CONFIG_RP2040_SPI_MAX_DMABUF + #error config.h must define CONFIG_RP2040_SPI_DEBUG (non-negative integer) +#endif LO_IMPLEMENTATION_C(io_duplex_readwriter, struct rp2040_hwspi, rp2040_hwspi); LO_IMPLEMENTATION_C(spi, struct rp2040_hwspi, rp2040_hwspi); @@ -173,13 +176,18 @@ size_t_and_error rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct uint8_t bogus_rx_dst; size_t count = 0; + size_t unsafe_count = 0; int pruned_iovcnt = 0; for (int i = 0; i < iovcnt; i++) { + if (!iov[i].iov_len) + continue; + pruned_iovcnt++; count += iov[i].iov_len; - if (iov[i].iov_len) - pruned_iovcnt++; + if (dma_is_unsafe(iov[i].iov_write_from)) + unsafe_count += iov[i].iov_len; } assert(count); + assert(unsafe_count <= CONFIG_RP2040_SPI_MAX_DMABUF); assert(((spi_hw_t *)self->inst)->sr == 0b11); @@ -228,11 +236,24 @@ size_t_and_error rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct static_assert(DMA_IS_TRIGGER(typeof(rx_data_blocks[0]), ctrl)); /* avoid needing to set IRQ_QUIET in the null-trigger block */ /* Core data blocks. */ + [[gnu::cleanup(heap_cleanup)]] void *dmabuf = NULL; + if (unsafe_count) + dmabuf = heap_alloc(unsafe_count, char); + size_t dmabuf_pos = 0; for (int i = 0, j = 0; i < iovcnt; i++) { if (!iov[i].iov_len) continue; + + const void *write_from = iov[i].iov_write_from; + if (write_from == IOVEC_DISCARD) + write_from = &self->bogus_data; + else if (dma_is_unsafe(write_from)) { + memcpy(dmabuf+dmabuf_pos, write_from, iov[i].iov_len); + write_from = dmabuf+dmabuf_pos; + dmabuf_pos += iov[i].iov_len; + } tx_data_blocks[j] = (typeof(tx_data_blocks[0])){ - .read_addr = (iov[i].iov_write_from != IOVEC_DISCARD) ? iov[i].iov_write_from : &self->bogus_data, + .read_addr = write_from, .write_addr = &spi_get_hw(self->inst)->dr, .xfer_count = iov[i].iov_len, .ctrl = (DMA_CTRL_ENABLE @@ -241,9 +262,14 @@ size_t_and_error rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct | ((j+1 < pruned_iovcnt) ? DMA_CTRL_CHAIN_TO(self->dma_tx_ctrl) : 0) | DMA_CTRL_TREQ_SEL(SPI_DREQ_NUM(self->inst, true))), }; + dma_assert_addrs(tx_data_blocks[j].write_addr, tx_data_blocks[j].read_addr); + + void *read_to = iov[i].iov_read_to; + if (read_to == IOVEC_DISCARD) + read_to = &bogus_rx_dst; rx_data_blocks[j] = (typeof(rx_data_blocks[0])){ .read_addr = &spi_get_hw(self->inst)->dr, - .write_addr = (iov[i].iov_read_to != IOVEC_DISCARD) ? iov[i].iov_read_to : &bogus_rx_dst, + .write_addr = read_to, .xfer_count = iov[i].iov_len, .ctrl = (DMA_CTRL_ENABLE | DMA_CTRL_DATA_SIZE(DMA_SIZE_8) @@ -252,6 +278,8 @@ size_t_and_error rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct | DMA_CTRL_TREQ_SEL(SPI_DREQ_NUM(self->inst, false)) | DMA_CTRL_IRQ_QUIET), }; + dma_assert_addrs(rx_data_blocks[j].write_addr, rx_data_blocks[j].read_addr); + j++; } /* Null-trigger (generate IRQ). */ diff --git a/libhw_cr/w5500.c b/libhw_cr/w5500.c index dc11ef9..5189512 100644 --- a/libhw_cr/w5500.c +++ b/libhw_cr/w5500.c @@ -132,7 +132,6 @@ LO_IMPLEMENTATION_STATIC(net_stream_listener, struct _w5500_socket, w5500_tcplis LO_IMPLEMENTATION_STATIC(io_reader, struct _w5500_socket, w5500_tcp); LO_IMPLEMENTATION_STATIC(io_writer, struct _w5500_socket, w5500_tcp); -LO_IMPLEMENTATION_STATIC(io_readwriter, struct _w5500_socket, w5500_tcp); LO_IMPLEMENTATION_STATIC(io_closer, struct _w5500_socket, w5500_tcp); LO_IMPLEMENTATION_STATIC(io_bidi_closer, struct _w5500_socket, w5500_tcp); LO_IMPLEMENTATION_STATIC(net_stream_conn, struct _w5500_socket, w5500_tcp); |