[PATCH 3/5] bsp/atsam: Fix cache / DMA handling in SPI.
Christian Mauderer
christian.mauderer at embedded-brains.de
Mon Feb 12 13:39:38 UTC 2018
This patch fixes the cache handling for the atsam SPI driver. Note that
this solution might doesn't have the best performance for small packets.
---
bsps/arm/atsam/headers.am | 1 +
bsps/arm/atsam/include/bsp/iocopy.h | 37 ++++
c/src/lib/libbsp/arm/atsam/Makefile.am | 3 +
.../arm/atsam/libraries/libchip/source/qspi.c | 36 +---
c/src/lib/libbsp/arm/atsam/spi/atsam_spi_bus.c | 205 +++++++++++++++++++--
c/src/lib/libbsp/arm/atsam/utils/iocopy.c | 49 +++++
6 files changed, 287 insertions(+), 44 deletions(-)
create mode 100644 bsps/arm/atsam/include/bsp/iocopy.h
create mode 100644 c/src/lib/libbsp/arm/atsam/utils/iocopy.c
diff --git a/bsps/arm/atsam/headers.am b/bsps/arm/atsam/headers.am
index 1ec34a2e69..70473c7ea1 100644
--- a/bsps/arm/atsam/headers.am
+++ b/bsps/arm/atsam/headers.am
@@ -11,6 +11,7 @@ include_bsp_HEADERS += ../../../../../../bsps/arm/atsam/include/bsp/atsam-clock-
include_bsp_HEADERS += ../../../../../../bsps/arm/atsam/include/bsp/atsam-i2c.h
include_bsp_HEADERS += ../../../../../../bsps/arm/atsam/include/bsp/atsam-spi.h
include_bsp_HEADERS += ../../../../../../bsps/arm/atsam/include/bsp/i2c.h
+include_bsp_HEADERS += ../../../../../../bsps/arm/atsam/include/bsp/iocopy.h
include_bsp_HEADERS += ../../../../../../bsps/arm/atsam/include/bsp/irq.h
include_bsp_HEADERS += ../../../../../../bsps/arm/atsam/include/bsp/pin-config.h
include_bsp_HEADERS += ../../../../../../bsps/arm/atsam/include/bsp/power.h
diff --git a/bsps/arm/atsam/include/bsp/iocopy.h b/bsps/arm/atsam/include/bsp/iocopy.h
new file mode 100644
index 0000000000..27e374747e
--- /dev/null
+++ b/bsps/arm/atsam/include/bsp/iocopy.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2018 embedded brains GmbH. All rights reserved.
+ *
+ * embedded brains GmbH
+ * Dornierstr. 4
+ * 82178 Puchheim
+ * Germany
+ * <rtems at embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#ifndef ATSAM_IOCOPY_H
+#define ATSAM_IOCOPY_H
+
+#include <rtems.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * Note: This functions are for copying from or to memory that is marked as
+ * Peripheral memory. In this regions a misaligned access is not allowed.
+ * Therefore memcopy would not work in all cases.
+ */
+void atsam_copy_to_io(void *dst, const void *src, size_t n);
+void atsam_copy_from_io(void *dst, const void *src, size_t n);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* ATSAM_IOCOPY_H */
diff --git a/c/src/lib/libbsp/arm/atsam/Makefile.am b/c/src/lib/libbsp/arm/atsam/Makefile.am
index 10f34dab55..aa97025568 100644
--- a/c/src/lib/libbsp/arm/atsam/Makefile.am
+++ b/c/src/lib/libbsp/arm/atsam/Makefile.am
@@ -156,6 +156,9 @@ libbsp_a_SOURCES += spi/sc16is752.c
libbsp_a_SOURCES += ../../shared/tod.c
libbsp_a_SOURCES += rtc/rtc-config.c
+# Helper functions
+libbsp_a_SOURCES += utils/iocopy.c
+
# Includes
libbsp_a_CPPFLAGS += -I$(srcdir)/../shared/CMSIS/Include
libbsp_a_CPPFLAGS += -I$(srcdir)/libraries/libboard
diff --git a/c/src/lib/libbsp/arm/atsam/libraries/libchip/source/qspi.c b/c/src/lib/libbsp/arm/atsam/libraries/libchip/source/qspi.c
index c80d0dd53a..314cfdf16c 100644
--- a/c/src/lib/libbsp/arm/atsam/libraries/libchip/source/qspi.c
+++ b/c/src/lib/libbsp/arm/atsam/libraries/libchip/source/qspi.c
@@ -75,6 +75,7 @@
#include "string.h"
#include <stdint.h>
+#include <bsp/iocopy.h>
#define SCRAMBLE_KEY 0x0BADDEAD
@@ -211,35 +212,6 @@ __STATIC_INLINE void QSPI_ScrambleData(Qspi *pQspi, uint32_t wKey,
pQspi->QSPI_SMR = (EnableFlag | (Random << 1));
}
-static void do_copy(uint8_t *dst, const uint8_t *src, size_t n, bool aligned)
-{
- if (aligned) {
- while (n > 3) {
- *(uint32_t *)dst = *(uint32_t *)src;
- dst += 4;
- src += 4;
- n -= 4;
- }
- }
-
- while (n > 0) {
- *dst = *src;
- ++dst;
- ++src;
- --n;
- }
-}
-
-static void copy_to_io(void *dst, const void *src, size_t n)
-{
- do_copy(dst, src, n, ((uintptr_t)dst) % 4 == 0);
-}
-
-static void copy_from_io(void *dst, const void *src, size_t n)
-{
- do_copy(dst, src, n, ((uintptr_t)src) % 4 == 0);
-}
-
/*----------------------------------------------------------------------------
* Exported functions
*----------------------------------------------------------------------------*/
@@ -766,9 +738,11 @@ QspidStatus_t QSPI_ReadWriteMem(Qspid_t *pQspid, Access_t const ReadWrite)
&& (ReadWrite <= WriteAccess)) ? true : false);
if (ReadWrite == WriteAccess) {
- copy_to_io(pQspiMem, pBuffer.pDataTx , pBuffer.TxDataSize);
+ atsam_copy_to_io(pQspiMem, pBuffer.pDataTx ,
+ pBuffer.TxDataSize);
} else {
- copy_from_io(pBuffer.pDataRx, pQspiMem, pBuffer.RxDataSize);
+ atsam_copy_from_io(pBuffer.pDataRx, pQspiMem,
+ pBuffer.RxDataSize);
}
memory_sync();
QSPI_EndTransfer(pQspid->pQspiHw);
diff --git a/c/src/lib/libbsp/arm/atsam/spi/atsam_spi_bus.c b/c/src/lib/libbsp/arm/atsam/spi/atsam_spi_bus.c
index 24850c8327..35f44f525d 100644
--- a/c/src/lib/libbsp/arm/atsam/spi/atsam_spi_bus.c
+++ b/c/src/lib/libbsp/arm/atsam/spi/atsam_spi_bus.c
@@ -30,6 +30,7 @@
#include <bsp/atsam-clock-config.h>
#include <bsp/atsam-spi.h>
+#include <bsp/iocopy.h>
#include <dev/spi/spi.h>
@@ -37,16 +38,33 @@
#define MAX_SPI_FREQUENCY 50000000
+#define DMA_NR_DESC_PER_DIR 3
+#define DMA_DESC_ALLIGNMENT 4
+
+#define DMA_BUF_RX 0
+#define DMA_BUF_TX 1
+#define DMA_BUF_DIRS 2
+
+struct atsam_spi_xdma_buf {
+ LinkedListDescriporView0 desc[DMA_NR_DESC_PER_DIR];
+ uint8_t leadbuf[CPU_CACHE_LINE_BYTES];
+ uint8_t trailbuf[CPU_CACHE_LINE_BYTES];
+};
+
typedef struct {
spi_bus base;
bool msg_cs_change;
const spi_ioc_transfer *msg_current;
+ const spi_ioc_transfer *msg_next;
uint32_t msg_todo;
int msg_error;
rtems_id msg_task;
Spid spi;
uint32_t dma_tx_channel;
uint32_t dma_rx_channel;
+ struct atsam_spi_xdma_buf *dma_bufs;
+ size_t leadbuf_rx_buffered_len;
+ size_t trailbuf_rx_buffered_len;
int transfer_in_progress;
bool chip_select_active;
bool chip_select_decode;
@@ -127,17 +145,155 @@ static void atsam_configure_spi(atsam_spi_bus *bus)
SPI_ConfigureNPCS(bus->spi.pSpiHw, cs, csr);
}
+static void atsam_spi_check_alignment_and_set_up_dma_descriptors(
+ atsam_spi_bus *bus,
+ struct atsam_spi_xdma_buf *buf,
+ const uint8_t *start,
+ size_t len,
+ bool tx
+)
+{
+ LinkedListDescriporView0 *curdesc = buf->desc;
+ size_t misaligned_begin;
+ size_t misaligned_end;
+ size_t len_main;
+ const uint8_t *start_main;
+ const uint8_t *start_trail;
+
+ /* Check alignments. */
+ if (len < CPU_CACHE_LINE_BYTES) {
+ misaligned_begin = len;
+ misaligned_end = 0;
+ len_main = 0;
+ } else {
+ misaligned_begin = ((uint32_t) start) % CPU_CACHE_LINE_BYTES;
+ misaligned_end = (((uint32_t) start) + len) % CPU_CACHE_LINE_BYTES;
+ len_main = len - misaligned_begin - misaligned_end;
+ }
+ start_main = start + misaligned_begin;
+ start_trail = start_main + len_main;
+
+ /* Store length for copying data back. */
+ if (!tx) {
+ bus->leadbuf_rx_buffered_len = misaligned_begin;
+ bus->trailbuf_rx_buffered_len = misaligned_end;
+ }
+
+ /* Handle misalignment on begin. */
+ if (misaligned_begin != 0) {
+ if (tx) {
+ atsam_copy_to_io(buf->leadbuf, start, misaligned_begin);
+ }
+ curdesc->mbr_nda = (uint32_t) (&curdesc[1]);
+ curdesc->mbr_ta = (uint32_t) buf->leadbuf;
+ curdesc->mbr_ubc = misaligned_begin;
+ }
+
+ /* Main part */
+ if (len_main > 0) {
+ curdesc->mbr_ubc |= tx ? XDMA_UBC_NSEN_UPDATED : XDMA_UBC_NDEN_UPDATED;
+ curdesc->mbr_ubc |= XDMA_UBC_NVIEW_NDV0;
+ curdesc->mbr_ubc |= XDMA_UBC_NDE_FETCH_EN;
+ ++curdesc;
+
+ curdesc->mbr_nda = (uint32_t) (&curdesc[1]);
+ curdesc->mbr_ta = (uint32_t) start_main;
+ curdesc->mbr_ubc = len_main;
+ if (tx) {
+ rtems_cache_flush_multiple_data_lines(start_main, len_main);
+ } else {
+ rtems_cache_invalidate_multiple_data_lines(start_main, len_main);
+ }
+ }
+
+ /* Handle misalignment on end */
+ if (misaligned_end != 0) {
+ curdesc->mbr_ubc |= tx ? XDMA_UBC_NSEN_UPDATED : XDMA_UBC_NDEN_UPDATED;
+ curdesc->mbr_ubc |= XDMA_UBC_NVIEW_NDV0;
+ curdesc->mbr_ubc |= XDMA_UBC_NDE_FETCH_EN;
+ ++curdesc;
+
+ if (tx) {
+ atsam_copy_to_io(buf->trailbuf, start_trail, misaligned_end);
+ }
+ curdesc->mbr_nda = 0;
+ curdesc->mbr_ta = (uint32_t) buf->trailbuf;
+ curdesc->mbr_ubc = misaligned_end;
+ curdesc->mbr_ubc |= XDMA_UBC_NDE_FETCH_DIS;
+ }
+}
+
+static void atsam_spi_copy_back_rx_after_dma_transfer(
+ atsam_spi_bus *bus
+)
+{
+ if (bus->leadbuf_rx_buffered_len != 0) {
+ atsam_copy_from_io(
+ bus->msg_current->rx_buf,
+ bus->dma_bufs[DMA_BUF_RX].leadbuf,
+ bus->leadbuf_rx_buffered_len
+ );
+ }
+ if (bus->trailbuf_rx_buffered_len != 0) {
+ atsam_copy_from_io(
+ bus->msg_current->rx_buf + bus->msg_current->len -
+ bus->trailbuf_rx_buffered_len,
+ bus->dma_bufs[DMA_BUF_RX].trailbuf,
+ bus->trailbuf_rx_buffered_len
+ );
+ }
+}
+
static void atsam_spi_start_dma_transfer(
atsam_spi_bus *bus,
const spi_ioc_transfer *msg
)
{
Xdmac *pXdmac = XDMAC;
+ size_t i;
+
+ atsam_spi_check_alignment_and_set_up_dma_descriptors(
+ bus,
+ &bus->dma_bufs[DMA_BUF_RX],
+ msg->rx_buf,
+ msg->len,
+ false
+ );
+ atsam_spi_check_alignment_and_set_up_dma_descriptors(
+ bus,
+ &bus->dma_bufs[DMA_BUF_TX],
+ msg->tx_buf,
+ msg->len,
+ true
+ );
+
+ XDMAC_SetDescriptorAddr(
+ pXdmac,
+ bus->dma_rx_channel,
+ (uint32_t) bus->dma_bufs[DMA_BUF_RX].desc,
+ 0
+ );
+ XDMAC_SetDescriptorControl(
+ pXdmac,
+ bus->dma_rx_channel,
+ XDMAC_CNDC_NDVIEW_NDV0 |
+ XDMAC_CNDC_NDDUP_DST_PARAMS_UPDATED |
+ XDMAC_CNDC_NDE_DSCR_FETCH_EN
+ );
+ XDMAC_SetDescriptorAddr(
+ pXdmac,
+ bus->dma_tx_channel,
+ (uint32_t) bus->dma_bufs[DMA_BUF_TX].desc,
+ 0
+ );
+ XDMAC_SetDescriptorControl(
+ pXdmac,
+ bus->dma_tx_channel,
+ XDMAC_CNDC_NDVIEW_NDV0 |
+ XDMAC_CNDC_NDSUP_SRC_PARAMS_UPDATED |
+ XDMAC_CNDC_NDE_DSCR_FETCH_EN
+ );
- XDMAC_SetDestinationAddr(pXdmac, bus->dma_rx_channel, (uint32_t)msg->rx_buf);
- XDMAC_SetSourceAddr(pXdmac, bus->dma_tx_channel, (uint32_t)msg->tx_buf);
- XDMAC_SetMicroblockControl(pXdmac, bus->dma_rx_channel, msg->len);
- XDMAC_SetMicroblockControl(pXdmac, bus->dma_tx_channel, msg->len);
XDMAC_StartTransfer(pXdmac, bus->dma_rx_channel);
XDMAC_StartTransfer(pXdmac, bus->dma_tx_channel);
}
@@ -205,11 +361,12 @@ static void atsam_spi_setup_transfer(atsam_spi_bus *bus)
}
if (msg_todo > 0) {
- const spi_ioc_transfer *msg = bus->msg_current;
+ const spi_ioc_transfer *msg = bus->msg_next;
int error;
bus->msg_cs_change = msg->cs_change;
- bus->msg_current = msg + 1;
+ bus->msg_next = msg + 1;
+ bus->msg_current = msg;
bus->msg_todo = msg_todo - 1;
error = atsam_check_configure_spi(bus, msg);
@@ -231,6 +388,7 @@ static void atsam_spi_dma_callback(uint32_t channel, void *arg)
--bus->transfer_in_progress;
if (bus->transfer_in_progress == 0) {
+ atsam_spi_copy_back_rx_after_dma_transfer(bus);
atsam_spi_setup_transfer(bus);
}
}
@@ -241,15 +399,18 @@ static int atsam_spi_transfer(
uint32_t msg_count
)
{
+ rtems_status_code sc;
atsam_spi_bus *bus = (atsam_spi_bus *)base;
bus->msg_cs_change = false;
- bus->msg_current = &msgs[0];
+ bus->msg_next = &msgs[0];
+ bus->msg_current = NULL;
bus->msg_todo = msg_count;
bus->msg_error = 0;
bus->msg_task = rtems_task_self();
atsam_spi_setup_transfer(bus);
- rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ sc = rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+ assert(sc == RTEMS_SUCCESSFUL);
return bus->msg_error;
}
@@ -281,6 +442,8 @@ static void atsam_spi_destroy(spi_bus *base)
SPI_Disable(bus->spi.pSpiHw);
PMC_DisablePeripheral(bus->spi.spiId);
+ rtems_cache_coherent_free(bus->dma_bufs);
+
spi_bus_destroy_and_free(&bus->base);
}
@@ -305,6 +468,14 @@ static void atsam_spi_init_xdma(atsam_spi_bus *bus)
uint32_t xdmaInt;
uint8_t channel;
eXdmadRC rc;
+ uint32_t xdma_cndc;
+
+ bus->dma_bufs = rtems_cache_coherent_allocate(
+ DMA_BUF_DIRS * sizeof(*(bus->dma_bufs)),
+ DMA_DESC_ALLIGNMENT,
+ 0
+ );
+ assert(bus->dma_bufs != NULL);
bus->dma_tx_channel = XDMAD_AllocateChannel(
bus->spi.pXdmad,
@@ -342,7 +513,7 @@ static void atsam_spi_init_xdma(atsam_spi_bus *bus)
rc = XDMAD_PrepareChannel(bus->spi.pXdmad, bus->dma_tx_channel);
assert(rc == XDMAD_OK);
- /* Put all interrupts on for non LLI list setup of DMA */
+ /* Put all relevant interrupts on */
xdmaInt = (
XDMAC_CIE_BIE |
XDMAC_CIE_DIE |
@@ -366,12 +537,16 @@ static void atsam_spi_init_xdma(atsam_spi_bus *bus)
XDMAC_CC_SAM_FIXED_AM |
XDMAC_CC_DAM_INCREMENTED_AM |
XDMAC_CC_PERID(channel);
+ xdma_cndc = XDMAC_CNDC_NDVIEW_NDV0 |
+ XDMAC_CNDC_NDE_DSCR_FETCH_EN |
+ XDMAC_CNDC_NDDUP_DST_PARAMS_UPDATED |
+ XDMAC_CNDC_NDSUP_SRC_PARAMS_UNCHANGED;
rc = XDMAD_ConfigureTransfer(
bus->spi.pXdmad,
bus->dma_rx_channel,
&cfg,
- 0,
- 0,
+ xdma_cndc,
+ (uint32_t) bus->dma_bufs[DMA_BUF_RX].desc,
xdmaInt
);
assert(rc == XDMAD_OK);
@@ -391,12 +566,16 @@ static void atsam_spi_init_xdma(atsam_spi_bus *bus)
XDMAC_CC_SAM_INCREMENTED_AM |
XDMAC_CC_DAM_FIXED_AM |
XDMAC_CC_PERID(channel);
+ xdma_cndc = XDMAC_CNDC_NDVIEW_NDV0 |
+ XDMAC_CNDC_NDE_DSCR_FETCH_EN |
+ XDMAC_CNDC_NDDUP_DST_PARAMS_UNCHANGED |
+ XDMAC_CNDC_NDSUP_SRC_PARAMS_UPDATED;
rc = XDMAD_ConfigureTransfer(
bus->spi.pXdmad,
bus->dma_tx_channel,
&cfg,
- 0,
- 0,
+ xdma_cndc,
+ (uint32_t) bus->dma_bufs[DMA_BUF_TX].desc,
xdmaInt
);
assert(rc == XDMAD_OK);
diff --git a/c/src/lib/libbsp/arm/atsam/utils/iocopy.c b/c/src/lib/libbsp/arm/atsam/utils/iocopy.c
new file mode 100644
index 0000000000..b91282dac6
--- /dev/null
+++ b/c/src/lib/libbsp/arm/atsam/utils/iocopy.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2018 embedded brains GmbH. All rights reserved.
+ *
+ * embedded brains GmbH
+ * Dornierstr. 4
+ * 82178 Puchheim
+ * Germany
+ * <rtems at embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <bsp/iocopy.h>
+
+static void atsam_do_copy(
+ uint8_t *dst,
+ const uint8_t *src,
+ size_t n,
+ bool aligned
+)
+{
+ if (aligned) {
+ while (n > 3) {
+ *(uint32_t *)dst = *(uint32_t *)src;
+ dst += 4;
+ src += 4;
+ n -= 4;
+ }
+ }
+
+ while (n > 0) {
+ *dst = *src;
+ ++dst;
+ ++src;
+ --n;
+ }
+}
+
+void atsam_copy_to_io(void *dst, const void *src, size_t n)
+{
+ atsam_do_copy(dst, src, n, ((uintptr_t)dst) % 4 == 0);
+}
+
+void atsam_copy_from_io(void *dst, const void *src, size_t n)
+{
+ atsam_do_copy(dst, src, n, ((uintptr_t)src) % 4 == 0);
+}
--
2.13.6
More information about the devel
mailing list