summaryrefslogtreecommitdiff
path: root/libhw_cr
diff options
context:
space:
mode:
Diffstat (limited to 'libhw_cr')
-rw-r--r--libhw_cr/host_net.c1
-rw-r--r--libhw_cr/rp2040_dma.c84
-rw-r--r--libhw_cr/rp2040_dma.h7
-rw-r--r--libhw_cr/rp2040_hwspi.c36
-rw-r--r--libhw_cr/w5500.c1
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);