[PATCH rtems-libbsd 3/7] busdma: Option to round to cache lines on sync
Christian Mauderer
christian.mauderer at embedded-brains.de
Fri Jul 17 05:56:16 UTC 2020
Some targets support only flushing or invalidating complete cache lines.
In this cases misaligned buffers might lead to unexpected results. This
patch adds a flag that allows drivers to signal to the bus dma driver
that it is OK to round a buffer to the next full cache line. That's for
example necessary if a driver wants to send out 14 byte via a USB DMA.
Only the driver knows whether these 14 bytes are located in an otherwise
unused cache line aligned buffer.
---
freebsd/sys/dev/usb/usb_busdma.c | 31 ++++++++++++++++++++
freebsd/sys/dev/usb/usb_busdma.h | 5 ++++
freebsd/sys/dev/usb/usb_transfer.c | 20 +++++++++++++
freebsd/sys/dev/usb/usbdi.h | 4 +++
freebsd/sys/sys/bus_dma.h | 6 ++++
rtemsbsd/include/machine/rtems-bsd-bus-dma.h | 3 ++
rtemsbsd/rtems/rtems-kernel-bus-dma-mbuf.c | 3 ++
rtemsbsd/rtems/rtems-kernel-bus-dma.c | 9 ++++++
8 files changed, 81 insertions(+)
diff --git a/freebsd/sys/dev/usb/usb_busdma.c b/freebsd/sys/dev/usb/usb_busdma.c
index dc52fe15..b74f0823 100644
--- a/freebsd/sys/dev/usb/usb_busdma.c
+++ b/freebsd/sys/dev/usb/usb_busdma.c
@@ -67,6 +67,9 @@
#include <dev/usb/usb_controller.h>
#include <dev/usb/usb_bus.h>
#endif /* USB_GLOBAL_INCLUDE_FILE */
+#ifdef __rtems__
+#include <machine/rtems-bsd-cache.h>
+#endif /* __rtems__ */
#if USB_HAVE_BUSDMA
static void usb_dma_tag_create(struct usb_dma_tag *, usb_size_t, usb_size_t);
@@ -543,6 +546,15 @@ usb_pc_alloc_mem(struct usb_page_cache *pc, struct usb_page *pg,
uptag = pc->tag_parent;
+#if defined(__rtems__) && defined(CPU_DATA_CACHE_ALIGNMENT)
+ while (align % CPU_DATA_CACHE_ALIGNMENT != 0) {
+ align *= 2;
+ }
+ if (size % CPU_DATA_CACHE_ALIGNMENT != 0) {
+ size = (size + (CPU_DATA_CACHE_ALIGNMENT - 1)) &
+ ~(CPU_DATA_CACHE_ALIGNMENT - 1);
+ }
+#endif /* __rtems__ */
if (align != 1) {
/*
* The alignment must be greater or equal to the
@@ -605,7 +617,12 @@ usb_pc_alloc_mem(struct usb_page_cache *pc, struct usb_page *pg,
/* load memory into DMA */
err = bus_dmamap_load(
utag->tag, map, ptr, size, &usb_pc_alloc_mem_cb,
+#if defined(__rtems__) && CPU_DATA_CACHE_ALIGNMENT
+ pc, (BUS_DMA_WAITOK | BUS_DMA_COHERENT |
+ BUS_DMA_DO_CACHE_LINE_BLOW_UP));
+#else /* __rtems__ */
pc, (BUS_DMA_WAITOK | BUS_DMA_COHERENT));
+#endif /* __rtems__ */
if (err == EINPROGRESS) {
cv_wait(uptag->cv, uptag->mtx);
@@ -662,6 +679,12 @@ usb_pc_free_mem(struct usb_page_cache *pc)
uint8_t
usb_pc_load_mem(struct usb_page_cache *pc, usb_size_t size, uint8_t sync)
{
+#ifdef __rtems__
+ int flags;
+
+ flags = pc->dma_do_cache_line_blow_up ?
+ BUS_DMA_DO_CACHE_LINE_BLOW_UP : 0;
+#endif /* __rtems__ */
/* setup page cache */
pc->page_offset_buf = 0;
pc->page_offset_end = size;
@@ -687,7 +710,11 @@ usb_pc_load_mem(struct usb_page_cache *pc, usb_size_t size, uint8_t sync)
*/
err = bus_dmamap_load(
pc->tag, pc->map, pc->buffer, size,
+#ifndef __rtems__
&usb_pc_alloc_mem_cb, pc, BUS_DMA_WAITOK);
+#else /* __rtems__ */
+ &usb_pc_alloc_mem_cb, pc, BUS_DMA_WAITOK | flags);
+#endif /* __rtems__ */
if (err == EINPROGRESS) {
cv_wait(uptag->cv, uptag->mtx);
err = 0;
@@ -709,7 +736,11 @@ usb_pc_load_mem(struct usb_page_cache *pc, usb_size_t size, uint8_t sync)
*/
if (bus_dmamap_load(
pc->tag, pc->map, pc->buffer, size,
+#ifndef __rtems__
&usb_pc_load_mem_cb, pc, BUS_DMA_WAITOK)) {
+#else /* __rtems__ */
+ &usb_pc_load_mem_cb, pc, BUS_DMA_WAITOK | flags)) {
+#endif /* __rtems__ */
}
}
} else {
diff --git a/freebsd/sys/dev/usb/usb_busdma.h b/freebsd/sys/dev/usb/usb_busdma.h
index d75e67ee..03eca30d 100644
--- a/freebsd/sys/dev/usb/usb_busdma.h
+++ b/freebsd/sys/dev/usb/usb_busdma.h
@@ -102,6 +102,11 @@ struct usb_page_cache {
* from the memory. Else write. */
uint8_t ismultiseg:1; /* set if we can have multiple
* segments */
+#ifdef __rtems__
+ uint8_t dma_do_cache_line_blow_up:1;
+ /* set if it is OK to align the buffer
+ * start and end to next cache line */
+#endif /* __rtems__ */
#endif
};
diff --git a/freebsd/sys/dev/usb/usb_transfer.c b/freebsd/sys/dev/usb/usb_transfer.c
index 3b67c20c..ee1e4c28 100644
--- a/freebsd/sys/dev/usb/usb_transfer.c
+++ b/freebsd/sys/dev/usb/usb_transfer.c
@@ -2195,6 +2195,9 @@ usbd_xfer_set_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex,
/* set virtual address to load and length */
xfer->frbuffers[frindex].buffer = ptr;
usbd_xfer_set_frame_len(xfer, frindex, len);
+#ifdef __rtems__
+ xfer->frbuffers[frindex].dma_do_cache_line_blow_up = 0;
+#endif /* __rtems__ */
}
void
@@ -2209,6 +2212,23 @@ usbd_xfer_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex,
*len = xfer->frlengths[frindex];
}
+#ifdef __rtems__
+/*------------------------------------------------------------------------*
+ * usbd_xfer_frame_allow_cache_line_blow_up
+ *
+ * Set a flag that the buffer start and end belonging to this frame can be
+ * aligned to the next cache line on sync and flush.
+ *------------------------------------------------------------------------*/
+void
+usbd_xfer_frame_allow_cache_line_blow_up(struct usb_xfer *xfer,
+ usb_frcount_t frindex)
+{
+ KASSERT(frindex < xfer->max_frame_count, ("frame index overflow"));
+
+ xfer->frbuffers[frindex].dma_do_cache_line_blow_up = 1;
+}
+
+#endif /* __rtems__ */
/*------------------------------------------------------------------------*
* usbd_xfer_old_frame_length
*
diff --git a/freebsd/sys/dev/usb/usbdi.h b/freebsd/sys/dev/usb/usbdi.h
index 0a393844..834810a8 100644
--- a/freebsd/sys/dev/usb/usbdi.h
+++ b/freebsd/sys/dev/usb/usbdi.h
@@ -640,6 +640,10 @@ void usbd_xfer_set_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex,
void *ptr, usb_frlength_t len);
void usbd_xfer_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex,
void **ptr, int *len);
+#ifdef __rtems__
+void usbd_xfer_frame_allow_cache_line_blow_up(struct usb_xfer *xfer,
+ usb_frcount_t frindex);
+#endif /* __rtems__ */
void usbd_xfer_set_frame_offset(struct usb_xfer *xfer, usb_frlength_t offset,
usb_frcount_t frindex);
usb_frlength_t usbd_xfer_max_len(struct usb_xfer *xfer);
diff --git a/freebsd/sys/sys/bus_dma.h b/freebsd/sys/sys/bus_dma.h
index e99d8ece..d5a08be0 100644
--- a/freebsd/sys/sys/bus_dma.h
+++ b/freebsd/sys/sys/bus_dma.h
@@ -107,6 +107,12 @@
#define BUS_DMA_KEEP_PG_OFFSET 0x400
#define BUS_DMA_LOAD_MBUF 0x800
+#ifdef __rtems__
+/*
+ * Hint that the start address and size can be aligned to the next cache line.
+ */
+#define BUS_DMA_DO_CACHE_LINE_BLOW_UP 0x80000000
+#endif /* __rtems__ */
/* Forwards needed by prototypes below. */
union ccb;
diff --git a/rtemsbsd/include/machine/rtems-bsd-bus-dma.h b/rtemsbsd/include/machine/rtems-bsd-bus-dma.h
index ac970537..e9566882 100644
--- a/rtemsbsd/include/machine/rtems-bsd-bus-dma.h
+++ b/rtemsbsd/include/machine/rtems-bsd-bus-dma.h
@@ -75,6 +75,9 @@ struct bus_dma_tag {
struct bus_dmamap {
void *buffer_begin;
bus_size_t buffer_size;
+ int flags;
+/* OK to flush / invalidate the complete cache line */
+#define DMAMAP_CACHE_ALIGNED (1 << 0)
};
int
diff --git a/rtemsbsd/rtems/rtems-kernel-bus-dma-mbuf.c b/rtemsbsd/rtems/rtems-kernel-bus-dma-mbuf.c
index c435fd74..56ead4ee 100644
--- a/rtemsbsd/rtems/rtems-kernel-bus-dma-mbuf.c
+++ b/rtemsbsd/rtems/rtems-kernel-bus-dma-mbuf.c
@@ -69,6 +69,9 @@ bus_dmamap_load_mbuf(bus_dma_tag_t dmat, bus_dmamap_t map,
int first = 1;
bus_addr_t lastaddr = 0;
struct mbuf *m;
+ if ((flags & BUS_DMA_LOAD_MBUF) != 0) {
+ map->flags |= DMAMAP_CACHE_ALIGNED;
+ }
for (m = m0; m != NULL && error == 0; m = m->m_next) {
if (m->m_len > 0) {
diff --git a/rtemsbsd/rtems/rtems-kernel-bus-dma.c b/rtemsbsd/rtems/rtems-kernel-bus-dma.c
index 8c15e92b..4dc634f3 100644
--- a/rtemsbsd/rtems/rtems-kernel-bus-dma.c
+++ b/rtemsbsd/rtems/rtems-kernel-bus-dma.c
@@ -365,9 +365,13 @@ bus_dmamap_load(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf,
map->buffer_begin = buf;
map->buffer_size = buflen;
+ if ((flags & BUS_DMA_DO_CACHE_LINE_BLOW_UP) != 0) {
+ map->flags |= DMAMAP_CACHE_ALIGNED;
+ }
lastaddr = (vm_offset_t)0;
nsegs = 0;
+
error = bus_dmamap_load_buffer(dmat, dm_segments, buf, buflen,
NULL, flags, &lastaddr, &nsegs, 1);
@@ -397,6 +401,11 @@ bus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op)
uintptr_t begin = (uintptr_t) map->buffer_begin;
uintptr_t end = begin + size;
+ if ((map->flags & DMAMAP_CACHE_ALIGNED) != 0) {
+ begin &= ~CLMASK;
+ end = (end + CLMASK) & ~CLMASK;
+ size = end - begin;
+ }
if ((op & BUS_DMASYNC_PREWRITE) != 0 && (op & BUS_DMASYNC_PREREAD) == 0) {
rtems_cache_flush_multiple_data_lines((void *) begin, size);
}
--
2.26.2
More information about the devel
mailing list